细说STM32F407单片机轮询方式CAN通信
目录
一、项目介绍
二、项目配置
1、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator
2、CAN1
(1)Bit Timings Parameters组,位时序参数
(2)Basic Parameters组,基本参数
(3)Advanced Parameters组,高级参数
三、软件设计
1、KEYLED
2、can.h
3、can.c
4、main.c
四、下载与调试
本文旨在以案例说明STM32F407单片机轮询方式CAN通信的方法。本文仍然使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。需要参考本文作者的其他文章:
参考文章1:细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV_stm32f407 spiflash驱动程序-CSDN博客 https://wenchm.blog.csdn.net/article/details/144587209
参考文章2:细说STM32F407单片机CAN基础知识及其HAL驱动程序-CSDN博客 https://wenchm.blog.csdn.net/article/details/144769950
一、项目介绍
本文实例的动作说明:使用开发板上的按键S2、S3,按下S2后,发送一个数据帧,按下S3后,发送一个遥控帧。开发板上的LED1、LED2,依次响应按键的动作。按下S6后,开发板复位。
[S2]KeyUp = Send a Data Frame. LED1 ON
[S3]KeyDown= Send a Remote Frame. LED2 ON
示例还有以下功能:
- 使用CAN测试模式中的回环模式,进行自发自收的测试。
- 设置筛选器组,只接收消息ID为奇数的消息。
- 使用轮询方式接收数据。
开发板上CAN收发器型号是VD230。相关跳线的操作在这里省略10000字。有关CAN的原理图如下:
二、项目配置
1、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator
与参考文章1相同或近似。其中的时钟在本例中,HCLK=100MHz,PCLK1=25MHz,PCLK2=50MHz。
2、CAN1
CAN1的参数设置分为3个部分:
(1)Bit Timings Parameters组,位时序参数
位时序和波特率的原理详见参考文章2,这里设置的参数如下:
- Prescaler,预分频系数,这里设置为5,可设置范围是1~1024。CAN1的时钟频率由PCLK1经过分频后得到,本例中PCLK1=25MHz,经过5分频后,=5MHz。
- Time Quantum,时间片。在设置预分频系数后,时间片会被自动计算:tq=1/(5×1000000)=200ns。
- Time Quantam in Bit Segment 1,位段1的时间片个数为m,范围为1~16,本例设置为4。
- Time Quantam in Bit Segment 2,位段2的时间片个数为n,范围为1~8,本例设置为3。
- ReSynchronization Jump Width(SJW),再同步跳转宽度,设置范围为1~4,本例设置为1。CAN通信的波特率由同步段、BS1、BS2的时间片个数决定,波特率计算公式如下:Baudrate=1/(1+m+n)=1s/(8×200ns)=625kbit/s。
注意,STM32F407的CAN控制器在闭环CAN网络中波特率范围是125kbit/s~1Mbit/s,如果计算的实际波特率不在这个范围内,则需要调整分频系数或各位段的时间片个数。
(2)Basic Parameters组,基本参数
基本参数与CAN主控制寄存器CAN_MCR中的一些位对应,对CAN模块的一些特性进行设置。
- Time Triggered Communication Mode(TTCM位),时间触发通信模式。设置为Disable表示禁止时间触发通信模式,若启用TTCM,则在发送或接收消息时,会加上一个内部计数器的计数值。
- Automatic Bus-Off Management(ABOM位),自动的总线关闭管理。设置为Disable表示不使用自动的总线关闭。
- Automatic Wake-Up Mode(AWUM位),自动唤醒模式。这个参数用于控制CAN模块在睡眠模式下接收消息时的行为,如果设置为Enable,则表示只要接收消息,就通过硬件自动退出睡眠模式。
- Automatic Retransmission(NART位),自动重发。若设置为Enable,CAN模块将自动重发消息,直到发送成功为止。若设置为Disable,则无论发送结果如何,消息只发送一次。这个设定值实际是对NART位值取反,因为NART表示禁止自动重发。
- Receive Fifo Locked Mode(RFLM位),接收FIFO锁定模式。若设置为Disable,表示FIFO上溢不锁定,下一条新消息覆盖前一条消息。若设置为Enable,则表示上溢后锁定,丢弃下一条新消息。
- Transmit Fifo Priority(TXFP位),发送FIFO优先级。若设置为Disable,表示消息优先级由标识符决定;若设置为Enable,表示优先级由请求顺序决定。
(3)Advanced Parameters组,高级参数
- Operating Mode,用于设置CAN模块的工作模式,有4种工作模式可选,即正常(Normal)、静默(Silent)、回环(Loopback)、回环静默(Loopback combined with Silent)。其中,后3种是CAN模块的测试模式。本例设置为Loopback,使用其自发自收功能进行CAN收发功能的测试。
本例使用轮询方式测试CAN模块的数据发送和接收功能,所以不开启CAN1的任何中断。
三、软件设计
1、KEYLED
本例的项目中要使用KEYLED,其中的keyled.c和keyled.h的使用方法与参考文章1相同。
2、can.h
/* USER CODE BEGIN Prototypes */
HAL_StatusTypeDef CAN_SetFilters();
void CAN_TestPoll(uint8_t msgID,uint8_t frameType);
/* USER CODE END Prototypes */
3、can.c
/* USER CODE BEGIN 0 */
#include <stdio.h>
/* USER CODE END 0 */
/* USER CODE BEGIN 1 */
//设置筛选器,要在完成CAN初始化之后调用此函数
HAL_StatusTypeDef CAN_SetFilters()
{CAN_FilterTypeDef canFilter;//筛选器结构体变量// Configure the CAN FiltercanFilter.FilterBank = 0; //筛选器组编号canFilter.FilterMode = CAN_FILTERMODE_IDMASK; //ID掩码模式canFilter.FilterScale = CAN_FILTERSCALE_32BIT; //32位长度//设置1:接收所有帧
// canFilter.FilterIdHigh = 0x0000; //CAN_FxR1 的高16位
// canFilter.FilterIdLow = 0x0000; //CAN_FxR1 的低16位
// canFilter.FilterMaskIdHigh = 0x0000; //CAN_FxR2 的高16位,所有位任意
// canFilter.FilterMaskIdLow = 0x0000; //CAN_FxR2 的低16位,所有位任意//设置2:只接收stdID为奇数的帧canFilter.FilterIdHigh = 0x0020; //CAN_FxR1 的高16位canFilter.FilterIdLow = 0x0000; //CAN_FxR1 的低16位canFilter.FilterMaskIdHigh = 0x0020; //CAN_FxR2 的高16位canFilter.FilterMaskIdLow = 0x0000; //CAN_FxR2 的低16位canFilter.FilterFIFOAssignment = CAN_RX_FIFO0; //应用于FIFO0canFilter.FilterActivation = ENABLE; //使用筛选器canFilter.SlaveStartFilterBank = 14; //从CAN控制器筛选器起始的BankHAL_StatusTypeDef result=HAL_CAN_ConfigFilter(&hcan1,&canFilter);return result;
}void CAN_TestPoll(uint8_t msgID, uint8_t frameType)
{
//1. 要发送的数据uint8_t TxData[8]; //发送数据缓存区,最多8字节TxData[0]=msgID;TxData[1]=msgID+11;CAN_TxHeaderTypeDef TxHeader; //发送消息的结构体变量TxHeader.StdId = msgID; //stdIDTxHeader.RTR = frameType; //数据帧或遥控帧,CAN_RTR_DATA或CAN_RTR_REMOTETxHeader.IDE = CAN_ID_STD; //标准格式TxHeader.DLC = 2; //数据字节数TxHeader.TransmitGlobalTime = DISABLE;//禁用时间戳while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) < 1) {} //等待有可用的发送邮箱uint32_t TxMailbox; //临时变量,用于返回使用的邮箱编号/* 将消息发送到邮箱 */if(HAL_CAN_AddTxMessage(&hcan1,&TxHeader,TxData,&TxMailbox) != HAL_OK){printf("Transmit error.\r\n");return;}printf("Send MsgID= %X\r\n",msgID);//2. 轮询方式接收消息/* 等待邮箱发送完毕,也就是等待空闲邮箱个数恢复为3 */while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3) {}CAN_RxHeaderTypeDef RxHeader; //接收消息的结构体变量uint8_t RxData[8]; //接收数据缓存区HAL_Delay(1);if(HAL_CAN_GetRxFifoFillLevel(&hcan1,CAN_RX_FIFO0) != 1){printf("Message is not received.\r\n");return;}printf("Message is received.\r\n");if(HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&RxHeader,RxData) == HAL_OK){printf("StdID= %lX\r\n",RxHeader.StdId);printf("RTR(0=Data,2=Remote)= %lX\r\n",RxHeader.RTR);printf("IDE(0=Std,4=Ext)= %lX\r\n",RxHeader.IDE);printf("DLC(Data length)= %lX\r\n",RxHeader.DLC);//数据帧,显示数据内容,遥控帧没有数据if (TxHeader.RTR == CAN_RTR_DATA){printf("Data[0]= %X\r\n",RxData[0]);printf("Data[1]= %X\r\n",RxData[1]);}}
}
/* USER CODE END 1 */
在文件can.h中有两个自定义函数,其中函数CAN_SetFilters()用于筛选器设置。
4、main.c
/* USER CODE BEGIN Includes */
#include "keyled.h"
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */printf("Demo18_1_CAN:CAN Polling.\r\n");printf("Test mode:Loopback.\r\n");if (CAN_SetFilters() == HAL_OK) //设置筛选器printf("ID Filter: Only Odd IDs.\r\n");if (HAL_CAN_Start(&hcan1) == HAL_OK) //启动CAN1模块printf("CAN is started.\r\n");printf("[S2]KeyUp = Send a Data Frame.\r\n");printf("[S3]KeyDown= Send a Remote Frame.\r\n");// MCU output low level LED light is onLED1_OFF();LED2_OFF();/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */uint8_t msgID=1;while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */KEYS curKey=ScanPressedKey(KEY_WAIT_ALWAYS);if (curKey==KEY_UP){CAN_TestPoll(msgID++,CAN_RTR_DATA); //发送数据帧LED1_ON();LED2_OFF();}else if (curKey==KEY_DOWN){CAN_TestPoll(msgID++,CAN_RTR_REMOTE); //发送遥控帧LED1_OFF();LED2_ON();}printf("** Reselect menu or reset **\r\n");HAL_Delay(500);}/* USER CODE END 3 */
在进入while循环之前,显示了菜单提示信息。
在while()循环中,检测按键输入,当KeyUp键按下时,调用函数CAN_TestPoll()测试发送数据帧,当KeyDown键按下时,调用函数CAN_TestPoll()测试发送遥控帧。函数CAN_TestPoll()是在文件can.c中实现的自定义函数。 函数CAN_TestPoll()用于测试CAN1模块在轮询方式下的数据发送和接收。
/* USER CODE BEGIN 4 *///串口打印
int __io_putchar(int ch)
{HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);return ch;
}
/* USER CODE END 4 */
四、下载与调试
下载后,自动显示:
按下S2键,可以按下多次:
按下S3键,可以按下多次: