极海国产芯|CubeMX配置F103xE_CAN,兼容APM32E103xE双CAN的修改操作
本帖最后由 一个人破 于 2022-11-25 17:06 编辑#申请原创# 文档说明:STM32F103xE芯片只有一个CAN,而APM32E103xE是有2个CAN,且相互独立。因此使用CubeMX生成的F103xE_CAN程序(HAL库),无法满足APM32E103xE的CAN2使用。故建立文档,进行程序修改说明,以供电子同胞们参考学习使用。
(我还是不习惯这个编辑器,后面的图片我就不发了。会在文章的最后放上PDF教程文件和程序包,方便你们观看和参考)
1)PDF教程文件:attach://2028402.pdf
2)程序包(分2个,有文件大小限制):
attach://2026898.rarattach://2026912.rar
一、使用CubeMX生成STM32F103VET6的CAN工程
1、创建工程
https://bbs.21ic.com/forum.php?mod=image&aid=2026708&size=300x300&key=2f99767a87296145&nocache=yes&type=fixnone
2、选择STM32F103VET6型号
https://bbs.21ic.com/forum.php?mod=image&aid=2026710&size=300x300&key=dad2eda82e755779&nocache=yes&type=fixnone
3、配置系统主频
我时钟源使用外部晶振HSE8MHz,经过9倍频,PLL72MHz
https://bbs.21ic.com/forum.php?mod=image&aid=2026712&size=300x300&key=0967cce5d9dd9a77&nocache=yes&type=fixnonehttps://bbs.21ic.com/forum.php?mod=image&aid=2026714&size=300x300&key=8ff25867426f6788&nocache=yes&type=fixnone
4、配置CAN
STM32F103xE芯片只有一个CAN,所以CubeMX只能先配置一个CAN,即CAN1。
a、使能接收中断
b、CAN1引脚我使用PB8和PB9
c、波特率配置500Kbps
https://bbs.21ic.com/forum.php?mod=image&aid=2026716&size=300x300&key=77ad8ce0b4152de9&nocache=yes&type=fixnonehttps://bbs.21ic.com/forum.php?mod=image&aid=2026718&size=300x300&key=71f6ba15631076c5&nocache=yes&type=fixnonehttps://bbs.21ic.com/forum.php?mod=image&aid=2026720&size=300x300&key=3df022b8a781297e&nocache=yes&type=fixnone
5、配置串口,便于查看是否收发成功
这里我用的是串口1,记得使能串口接收中断
https://bbs.21ic.com/forum.php?mod=image&aid=2026722&size=300x300&key=9879485dc576b033&nocache=yes&type=fixnone
6、配置按键
配置2个IO当按键,作为CAN的收发开关。这里我用的是PC1、PC2,输入上拉模式。
https://bbs.21ic.com/forum.php?mod=image&aid=2026724&size=300x300&key=8c8c7502890b210d&nocache=yes&type=fixnone
7、选择一个调试模式
CubeMX默认配置的是No Debug,最好根据自身需求选择一个调试模式。我选的是Serial Wire(SW)
https://bbs.21ic.com/forum.php?mod=image&aid=2026726&size=300x300&key=57f41dbbd2cb6814&nocache=yes&type=fixnone
8、生成工程文件
a、我使用的是MDK软件,选择MDK-ARM
b、勾选单独生成.c/.h,便于修改调试
c、点击GENERATE CODE,生成工程
9、工程备份
打开工程存储的位置。万事备份,小心无大错。
二、工程过渡修改
刚生成的工程还不适合我们直接调试。为了便于串口输出调试,我们需要修改串口重定义。
在usart.c文件中加入以下代码
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE * f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1,&ch, 1, 0xffff);
return ch;
}
在usart.h文件中加入头文件
三、添加CAN2配置
STM32F103VET6是只有1个CAN,所以工程里只配置了1个CAN。如果我们想要使用APM32E103VET6的CAN2功能,就需要添加CAN2的相关配置。
大致有4个部分:CAN2基地址、时钟、引脚、中断。
1、添加CAN2初始化函数
a、can.c文件:更改并添加CAN2结构体定义
b、can.c文件:更改并添加CAN2初始化函数
* USER CODE END 0 */
CAN_HandleTypeDef hcan1;
CAN_HandleTypeDef hcan2;
/* CAN init function */
void MX_CAN1_Init(void)
{
/* USER CODE BEGIN CAN_Init 0 */
/* USER CODE END CAN_Init 0 */
/* USER CODE BEGIN CAN_Init 1 */
/* USER CODE END CAN_Init 1 */
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 12;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_3TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = DISABLE;
hcan1.Init.AutoWakeUp = ENABLE;
hcan1.Init.AutoRetransmission = ENABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN CAN_Init 2 */
/* USER CODE END CAN_Init 2 */
}
void MX_CAN2_Init(void)
{
/* USER CODE BEGIN CAN_Init 0 */
/* USER CODE END CAN_Init 0 */
/* USER CODE BEGIN CAN_Init 1 */
/* USER CODE END CAN_Init 1 */
hcan2.Instance = CAN2;
hcan2.Init.Prescaler = 12;
hcan2.Init.Mode = CAN_MODE_NORMAL;
hcan2.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan2.Init.TimeSeg1 = CAN_BS1_3TQ;
hcan2.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan2.Init.TimeTriggeredMode = DISABLE;
hcan2.Init.AutoBusOff = DISABLE;
hcan2.Init.AutoWakeUp = ENABLE;
hcan2.Init.AutoRetransmission = ENABLE;
hcan2.Init.ReceiveFifoLocked = DISABLE;
hcan2.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN CAN_Init 2 */
/* USER CODE END CAN_Init 2 */
}
c、在can.h文件中做好函数声明,main.c中做好函数初始化。
d、CAN2之前是没有定义的,因此需要加宏定义
跳转“CAN1”,复制并添加CAN2定义
跳转“CAN1_BASE”,复制并添加CAN2_BASE定义
添加CAN2部分,其实就是改HAL库的过程。我们只需要知道,对所有寄存器的操作都是对地址的操作,那就很好理解了。在极海官网可以下载最新版的E103xE参考手册,对照手册修改相关寄存器的地址就OK了。
参考手册资料:
2、HAL_CAN_Msp初始化函数中添加CAN2部分
返回can.c文件,对Msp初始化函数进行修改,添加CAN2部分的判断。CAN2的引脚我复用了PB5和PB6。
修改前
修改后(添加CAN2部分后的代码)
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspInit 0 */
/* USER CODE END CAN1_MspInit 0 */
/* CAN1 clock enable */
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**CAN1 GPIO Configuration
PB8 ------> CAN1_RX
PB9 ------> CAN1_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
__HAL_AFIO_REMAP_CAN1_2();
/* CAN1 interrupt Init */
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
/* USER CODE BEGIN CAN1_MspInit 1 */
/* USER CODE END CAN1_MspInit 1 */
}
else if(canHandle->Instance==CAN2)
{
/* USER CODE BEGIN CAN2_MspInit 0 */
/* USER CODE END CAN2_MspInit 0 */
/* CAN2 clock enable */
__HAL_RCC_CAN2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**CAN2 GPIO Configuration
PB5 ------> CAN2_RX
PB6 ------> CAN2_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
__HAL_AFIO_REMAP_CAN2_2();
/* CAN2 interrupt Init */
HAL_NVIC_SetPriority(CAN2_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(CAN2_RX0_IRQn);
/* USER CODE BEGIN CAN2_MspInit 1 */
/* USER CODE END CAN2_MspInit 1 */
}
}
软件会提示3个部分没有定义:时钟、复用引脚、中断
3、添加CAN2时钟宏定义
a、跳转“__HAL_RCC_CAN1_CLK_ENABLE“,复制并定义CAN2时钟使能函数
每个CAN1字眼都改为CAN2
b、跳转“RCC_APB1ENR_CAN1EN”,复制并定义CAN2时钟的偏移量
通过参考手册可知,CAN2时钟是bit26
修改前:
修改后:警报消除
4、添加CAN2引脚复用的宏定义
a、跳转“__HAL_AFIO_REMAP_CAN1_2”,复制并定义CAN2引脚复用
b、跳转“AFIO_MAPR_CAN_REMAP_REMAP2”,复制并定义CAN2引脚复用
通过手册可知,CAN2RMP为bit22
c、跳转“AFIO_MAPR_CAN_REMAP”,复制并定义CAN2引脚复用
修改前
修改后:警报消除
5、添加CAN2接收中断
通过参考手册,查看中断向量表
在文件”startup_stm32f103xe.s“添加3处中断内容
在文件”stm32f103xe.h“添加1处中断内容
在文件”stm32f1xx_it“中添加CAN2接收中断的函数
文件”startup_stm32f103xe.s“
文件”stm32f103xe.h“
在文件“stm32f1xx_it”中添加CAN2接收中断的函数
修改前:
修改后:
6、过滤器配置
a、在“can.c“文件,添加过滤器函数
b、在“can.h“做好函数申明
c、在“main.c”初始化
void CAN1_Filter_Init(CAN_HandleTypeDef* canHandle)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterActivation = ENABLE;//打开过滤器
sFilterConfig.FilterBank = 1;//过滤器0 这里可设0-13
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;//采用掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;//采用32位掩码模式
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;//采用FIFO0
sFilterConfig.FilterIdHigh = 0x0000;//设置过滤器掩码高16位
sFilterConfig.FilterIdLow = 0x0000;//设置过滤器掩码低16位
sFilterConfig.FilterMaskIdHigh = 0x0000;//设置过滤器掩码高16位
sFilterConfig.FilterMaskIdLow = 0x0000;//设置过滤器掩码低16位
if(HAL_CAN_ConfigFilter(canHandle,&sFilterConfig) != HAL_OK)//初始化过滤器
{
Error_Handler();
}
if(HAL_CAN_Start(canHandle) != HAL_OK)//打开can
{
Error_Handler();
}
if(HAL_CAN_ActivateNotification(canHandle,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//开启接收中断
{
Error_Handler();
}
}
void CAN2_Filter_Init(CAN_HandleTypeDef* canHandle)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterActivation = ENABLE;//打开过滤器
sFilterConfig.FilterBank = 1; //过滤器13 这里可设0-13
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;//采用掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;//采用32位掩码模式
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;//采用FIFO1
sFilterConfig.FilterIdHigh = 0x0000;//设置过滤器掩码高16位
sFilterConfig.FilterIdLow = 0x0000;//设置过滤器掩码低16位
sFilterConfig.FilterMaskIdHigh = 0x0000;//设置过滤器掩码高16位
sFilterConfig.FilterMaskIdLow = 0x0000;//设置过滤器掩码低16位
if(HAL_CAN_ConfigFilter(canHandle,&sFilterConfig) != HAL_OK)//初始化过滤器
{
Error_Handler();
}
if(HAL_CAN_Start(canHandle) != HAL_OK)//打开can
{
Error_Handler();
}
if(HAL_CAN_ActivateNotification(canHandle,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//开启接收中断
{
Error_Handler();
}
}
在can.h做好函数申明
在main.c初始化
7、CAN数据传输的准备
在main.c添加数据变量和传输验证
CAN_TxHeaderTypeDef Can_Tx;
CAN_RxHeaderTypeDef Can_Rx;
uint8_t tdata = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
uint32_t pTxMailbox = 0;
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
uint8_t aRxData, i;
Can_Rx.StdId = 0xFFF;
Can_Rx.ExtId = 0xFFF;
Can_Rx.IDE = CAN_ID_EXT;//CAN_ID_STD 标准帧CAN_ID_EXT 扩展帧
Can_Rx.RTR = CAN_RTR_DATA;
Can_Rx.DLC = 8;
if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &Can_Rx, aRxData) == HAL_OK)
{
if(hcan->Instance ==CAN1)
{
printf("1Get Rx Message Success\r\n\r\n");
// HAL_CAN_AddTxMessage(&hcan1,&Can_Tx,tdata,&pTxMailbox);
}
else
{
printf("2Get Rx Message Success\r\n\r\n");
// HAL_CAN_AddTxMessage(&hcan2,&Can_Tx,tdata,&pTxMailbox);
}
}
}
8、注释掉自动生成的CAN2宏
”stm32f1xx_hal_can.c“文件,2处
”stm32f1xx_hal_can.h“文件,1处
9、配置按键与CAN收发逻辑
PC1拉低,CAN1发送,CAN2接收。
PC2拉低,CAN2发送,CAN1接收。
main.c添加按键引脚定义:
#define KEY1_BUTTON_PIN GPIO_PIN_1
#define KEY1_BUTTON_GPIO_PORT GPIOC
#define KEY1_BUTTON_GPIO_CLK RCM_APB2_PERIPH_GPIOC
#define KEY2_BUTTON_PIN GPIO_PIN_2
#define KEY2_BUTTON_GPIO_PORT GPIOC
#define KEY2_BUTTON_GPIO_CLK RCM_APB2_PERIPH_GPIOC
主循环中添加按键判断语句:
if (HAL_GPIO_ReadPin(KEY1_BUTTON_GPIO_PORT,KEY1_BUTTON_PIN) == RESET)
{
printf("CAN1 Transmit !\r\n");
HAL_CAN_AddTxMessage(&hcan1,&Can_Tx,tdata,&pTxMailbox);
while(HAL_GPIO_ReadPin(KEY1_BUTTON_GPIO_PORT,KEY1_BUTTON_PIN)== RESET);
}
if (HAL_GPIO_ReadPin(KEY2_BUTTON_GPIO_PORT,KEY2_BUTTON_PIN) == RESET)
{
printf("CAN2 Transmit !\r\n");
HAL_CAN_AddTxMessage(&hcan2,&Can_Tx,tdata,&pTxMailbox);
while(HAL_GPIO_ReadPin(KEY2_BUTTON_GPIO_PORT,KEY2_BUTTON_PIN)== RESET);
}
四、硬件连接
使用2个CAN收发器,分别连接CAN1(PB8、PB9)与CAN2(PB5、PB6)
CANL与CANL相连,CANH与CANH相连
3.3V供电,记得共地
CAN收发器
五、测试验证
PC1拉低,CAN1发送,CAN2接收,测试正常。
PC2拉低,CAN2发送,CAN1接收,测试正常。
好文,点赞 WuKaiLi 发表于 2022-11-25 15:00
好文,点赞
惊现大佬 将PDF教程文件更新:添加目录,调整排版
页:
[1]