STM32系列文章(1):STM32CubeMX配置CAN总线通信
STM32CubeMX 配置
首先打开STM32CubeMX 选择芯片型号建立工程,我这里选择STM32F03ZET6(正点原子战舰V3) 具体新建工程的过程不再赘述,太基础。
首先配置System Core目录下的SYS和RCC选项卡如下图:
(1)配置SYS下的Debug为Serial Wire
(2)配置RCC下的HSE(高速时钟)为外部晶振Crystal/Ceramic Resonator,具体根据板子的硬件配置设置
(3)使能CAN总线并配置相关参数
在此说明CAN总线相关参数的配置方法,在F103ZET6中CAN是挂载在APB1下的低速外设,时钟36MHz,参数中的Prescaler(for Time Quantum)是指预分频系数,跟CAN总线的波特率相关,我这里设置为36分频,如此一来时钟就是1MHz,每一个周期为1us(即后面参数的1个Time是1us)方便计算,Time Quanta in Bit Segment 1和Time Quanta in Bit Segment 2 也是影响波特率的两个参数,具体计算如下:
Bound=1/(1*Tq+Tqbs1+Tqbs2)
其中Tqbs1代表的就是Time Quanta in Bit Segment 1所对应的时间,Tqbs2代表的就是Time Quanta in Bit Segment 2所对应的时间,Tq代表的是1个Time的时间。
所以以我的配置举例:1/(1us+4us+5us)=100K,这就是我的波特率。
另外顺带提一下因为我只有一块战舰V3,MIni板上的CAN没有引出,所以我设置Advenced Parameter下的Operating Mode 为Loopback,即回环模式(自发自收)。记得打开NVIC setting中的接收中断,不然是没法接收数据的
(4)串口配置(用于上位机显示收发数据方便看到效果)
配置串口模式为Asynchronous,硬件流控制Disable就可以,波特率字宽等参数默认即可,另外打开NVIC Setting下面的 USART1 global interruput 方便接收来自上位机的数据:
既然讲到串口就顺便提一下DMA吧,打开DMA可以大大节约CPU资源,减轻CPU负担。在DMA Setting选项卡下Add两个DMA通道(USART1_RX和USART1_TX),Mode暂都设置为Normal模式,当然也可以设置为Circle模式,那样DMA就会不停的持续疯狂往外发,在本测试中是不需要的。Data Width默认Byte.如下图:
至此串口和CAN 总线的设置已经完毕。
(5)时钟树配置
(6)设置项目名称和文件路径和IDE
IDE根据自己使用的开发工具选择,我这里使用Keil V5,勾选Copy only the necessry library files,这样生成的工程内只包含配置用到的相关库文件没有冗余。勾选Generate peripheral initialization as a pair of ‘.c/.h’file per peripheral,此选项的作用是会把每一个外设的相关配置代码分别生成不同的源文件和头文件,工程更加清洁。
(7)生成代码
直接GENERATE CODE 即可
生成完成后可以打开文件夹看到生成的文件目录,或者直接打开工程都可以。
代码增加和修改
(1)增加部分CAN总线的收发函数和过滤函数
打开工程目录下的Application/User下的can.c文件, 在最后的
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
之间增加自己的代码, 否则如果STM32CubeMX 配置出错需要修改配置重新生成代码的时候,就会覆盖掉这部分代码,相当于白写,所以一定要写在这两个注释中间切记 !!
这里我把我的代码贴出来供大家使用, 发现bug请告诉我谢谢 !
void CAN_FilterInit(CAN_HandleTypeDef *hcan)
{
CAN_FilterTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.FilterMode = CAN_FILTERMODE_IDMASK;
CAN_FilterInitStructure.FilterScale = CAN_FILTERSCALE_32BIT;
CAN_FilterInitStructure.FilterIdHigh = 0x2460;
CAN_FilterInitStructure.FilterIdLow = 0x0000;
CAN_FilterInitStructure.FilterMaskIdHigh = 0xF0E0;
CAN_FilterInitStructure.FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.FilterBank = 0;
CAN_FilterInitStructure.FilterFIFOAssignment = CAN_RX_FIFO0;
CAN_FilterInitStructure.FilterActivation = ENABLE;
CAN_FilterInitStructure.SlaveStartFilterBank = 14;
if(HAL_CAN_ConfigFilter(hcan,&CAN_FilterInitStructure) != HAL_OK)
{
Error_Handler();
}
if(HAL_CAN_ActivateNotification(hcan,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
if(HAL_CAN_Start(hcan) != HAL_OK)
{
Error_Handler();
}
}
并且记得在can.h中申明一下.
(2) CAN 总线的收发函数串口的接收发送完成回调函数我直接放在main.c里面了,这里也贴出来给大家参考(也要记得在main.c靠前的部分申明一下):
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//CAN Receive
{
CAN_RxHeaderTypeDef RX_Header;
HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO0,&RX_Header,RX_Data);
HAL_CAN_ActivateNotification(hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
}
void CAN_Send_Msg(CAN_HandleTypeDef *hcan,uint8_t *msg,uint8_t len,uint32_t id)//CAN Transmit
{
CAN_TxHeaderTypeDef Tx_Header;
uint32_t TxMailBox;
Tx_Header.StdId = id;
Tx_Header.IDE = CAN_ID_STD;
Tx_Header.RTR = CAN_RTR_DATA;
Tx_Header.DLC = len;
HAL_CAN_AddTxMessage(hcan,&Tx_Header,msg,&TxMailBox);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart1)
{
HAL_UART_Receive_DMA(&huart1,(uint8_t*)TX_Data,sizeof(TX_Data));
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart1)
{
CAN_Send_Msg(&hcan,TX_Data,sizeof(TX_Data),0x123);
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)RX_Data,sizeof(RX_Data));
}
}
(3)此外记得在Main函数的开头处开启串口DMA接收和发送,并定义相关变量
uint8_t RX_Data[8];
uint8_t TX_Data[8];
CAN_TxHeaderTypeDef TxHeader;
CAN_FilterInit(&hcan);
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)RX_Data,sizeof(RX_Data));
HAL_UART_Receive_DMA(&huart1,(uint8_t*)TX_Data,sizeof(TX_Data));
此测试实现的功能是:接收来自串口的数据,然后通过CAN总线发送出去,因为配置了回环模式,会收到一模一样的CAN数据输入,接收到数据后再通过串口DMA方式发出去上位机显示,类似串口的回音实验,只不过中间又串了一个CAN线,哈哈哈有点绕我也是够了…
编译下载后效果如图
结束,累了,拜拜