一个人破 发表于 2022-11-23 22:39

极海国产芯|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

好文,点赞

一个人破 发表于 2022-11-25 15:49

WuKaiLi 发表于 2022-11-25 15:00
好文,点赞

惊现大佬

一个人破 发表于 2022-11-25 17:07

将PDF教程文件更新:添加目录,调整排版
页: [1]
查看完整版本: 极海国产芯|CubeMX配置F103xE_CAN,兼容APM32E103xE双CAN的修改操作