CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试
1. STM32CubeMX配置
CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz
激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式)
CAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps
激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯
2. MDK-ARM编程
2.1 几个重要的结构体
主控寄存器 CAN_MCR:负责管理 CAN 的工作模式
typedef struct { uint32_t Prescaler; /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/ uint32_t Mode; /* 配置 CAN 的工作模式,回环或正常模式 */ uint32_t SyncJumpWidth; /* 配置 SJW 极限值 */ uint32_t TimeSeg1; /* 配置 BS1 段长度 */ uint32_t TimeSeg2; /* 配置 BS2 段长度 */ FunctionalState TimeTriggeredMode; /* 是否使能 TTCM 时间触发功能 */ FunctionalState AutoBusOff; /* 是否使能 ABOM 自动离线管理功能 */ FunctionalState AutoWakeUp; /* 是否使能 AWUM 自动唤醒功能 */ FunctionalState AutoRetransmission; /* 是否使能 NART 自动重传功能 */ FunctionalState ReceiveFifoLocked; /* 是否使能 RFLM 锁定 FIFO 功能 */ FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */ } CAN_InitTypeDef;
发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量
typedef struct { uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */ uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */ uint32_t IDE; /* 存储 IDE 扩展标志 */ uint32_t RTR; /* 存储 RTR 远程帧标志 */ uint32_t DLC; /* 存储报文数据段的长度,0-8 */ FunctionalState TransmitGlobalTime; } CAN_TxHeaderTypeDef; typedef struct { uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */ uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */ uint32_t IDE; /* 存储 IDE 扩展标志 */ uint32_t RTR; /* 存储 RTR 远程帧标志 */ uint32_t DLC; /* 存储报文数据段的长度,0-8 */ uint32_t Timestamp; uint32_t FilterMatchIndex; } CAN_RxHeaderTypeDef;过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加
typedef struct { uint32_t FilterIdHigh; /*CAN_FxR1 寄存器的高 16 位 */ uint32_t FilterIdLow; /*CAN_FxR1 寄存器的低 16 位 */ uint32_t FilterMaskIdHigh; /*CAN_FxR2 寄存器的高 16 位 */ uint32_t FilterMaskIdLow; /*CAN_FxR2 寄存器的低 16 位 */ uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */ uint32_t FilterBank; /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */ uint32_t FilterMode; /* 筛选器模式 */ uint32_t FilterScale; /* 设置筛选器的尺度 */ uint32_t FilterActivation; /* 是否使能本筛选器 */ uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */ } CAN_FilterTypeDef;
2.2 程序编写
生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用
//下面的设置只使能了FIFO0,并不过滤任何消息 void CAN_Filter_Config(){ CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank = 0; //筛选器编号, CAN1是0-13, CAN2是14-27 sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式 sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位 sFilterConfig.FilterIdHigh = 0X0000; //过滤器ID高16位,即CAN_FxR1寄存器的高16位 sFilterConfig.FilterIdLow = 0X0000; //过滤器ID低16位,即CAN_FxR1寄存器的低16位 sFilterConfig.FilterMaskIdHigh = 0X0000; //过滤器掩码高16位,即CAN_FxR2寄存器的高16位 sFilterConfig.FilterMaskIdLow = 0X0000; //过滤器掩码低16位,即CAN_FxR2寄存器的低16位 sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO sFilterConfig.FilterActivation = ENABLE; //是否使能本筛选器 sFilterConfig.SlaveStartFilterBank = 14; //指定为CAN1分配多少个滤波器组 if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) { Error_Handler(); } }编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12
uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){ uint16_t i = 0; uint32_t txMailBox; uint8_t send_buf[8]; txHeader.StdId = 12; txHeader.ExtId = 12; txHeader.IDE = CAN_ID_STD; txHeader.RTR = CAN_RTR_DATA; txHeader.DLC = len; for(i = 0; i < len; i++) send_buf[i] = msg[i]; if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK) return 1; return 0; } uint8_t CAN1_Recv_Msg(uint8_t *buf){ uint16_t i = 0; HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf); if(rxHeader.IDE == CAN_ID_STD) printf("StdId ID: %d ", rxHeader.StdId); else printf("ExtId ID: %d ", rxHeader.ExtId); printf("CAN IDE: %d ", rxHeader.IDE); printf("CAN RTR: %d ", rxHeader.RTR); printf("CAN DLC: %d ", rxHeader.DLC); printf("Recv Data: "); for(i = 0; i < rxHeader.DLC; i++) printf("%c",buf[i]); printf(" "); return rxHeader.DLC; }默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加
void MX_CAN1_Init(void){ ...... /* USER CODE BEGIN CAN1_Init 2 */ CAN_Filter_Config(); HAL_CAN_Start(&hcan1); /* USER CODE END CAN1_Init 2 */ }主函数main.c中,代码如下
int main(void){ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_CAN1_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ uint8_t ret,i; printf("CAN Testing....! "); uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75}; uint8_t rxdata[8]; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1); HAL_Delay(1000); printf("Start Send data... "); ret = CAN1_Send_Msg(txdata, 8); if(ret == 0) printf("CAN Send success! "); else printf("CAN Send failed! "); CAN1_Recv_Msg(rxdata); printf("+++++++++++++++++++++++++++++++ "); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
3. 下载测试
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据
全部0条评论
快来发表一下你的评论吧 !