STM32F103有1个CAN控制器,可以配合逻辑分析仪来测试CAN和PC的通信。在此之前先学习一下CAN的基础知识。
CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平(0),CAN_H和 CAN_L之差为 2.5V左右;隐性电平(1),二者必居其一。而隐性电平对应逻辑 1 CAN_H和 CAN_L之差为 0V。发送方通过使总线电平发生变化,将消息发送给接收方。在总线上显性电平具有优先权,只要有一个单元输出显性电平,总线上即为显性电平。而隐形电平则具有包容意味,只有所有的单元都输出隐性电平,总线上才为隐性电平(显性电平比隐性电平更强)
这里要注意,和常规理解不同,显性电平为0。
还有一点,CAN总线的起止端都有一个120欧的匹配电阻
除了正常工作模式外,还有3种测试模式:
- 静默模式(接收自己发出去的数据和总线数据,不发送)
- 环回模式(发送数据到总线,但不接收总线数据,只接收自己Tx到总线的那份数据)
- 环回静默模式(不发送数据到总线,不接收总线数据,仅接收自己Tx的数据)
这里还有个很重要的知识点,就是CAN总线的波特率,计算方式比较复杂,结合CubeMX上的CAN设置来看
有4个参数和波特率有关,Prescaler
,TQBS1
,TQBS2
和SJW
APB1的频率是36MHz,分频系数取9的话,则一个TQ为1(36/9) =250ns,之后*(TQBS1+TQBS2+SJW),则CAN的位时间为2000ns,相应的位波特率为500Kbps(记住这个值,后面很重要)
HAL_CAN_Start()
和HAL_CAN_AddTxMessage()
这两个方法。还要利用CAN_TxHeaderTypeDef
这个结构体。 CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8]={0x10, 0x20, 0x30, 0x40, 0x54, 0x65, 0x66, 0x99}; //测试发送这8个字节
uint32_t TxMailbox = 0;
TxHeader.StdId = 0x198;
TxHeader.ExtId = 0x999;
TxHeader.IDE = 0;
TxHeader.RTR = 0;
TxHeader.DLC = 8;
然后在进入while(1)
循环前,开启CAN通道
HAL_CAN_Start(&hcan); //F103ZET只有一个CAN,所以无需写序号
while循环中加入下列代码
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin); //LED1闪烁表示程序在正常运行
HAL_Delay(500);
HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox);
编译烧写后,将Saleae的USB Logic Analyzer的CH0连到开发板的CANL,并将分析仪的地线和开发板的某个GND相连,千万不能忘的是设置波特率为500Kbit/s。点击软件的Start后,可以看到捕获的波形
放大右下角来看
就是我们发送的数据以及CAN Identifier,测试通过!
2. 接收功能测试
CAN的接收比发送要复杂,CAN总线协议是有邮箱机制的,就是收到的报文是需要放到邮箱里,需要配置过滤器(Filter),关于过滤器的配置,可以在
stm32f1xx_hal_can.h
中找到CAN_FilterTypeDef
结构体的各个成员定义,一般会在can.c
中手动将这个结构体的各个成员进行初始化。
CAN接收函数使用HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, RxData)
,接收到的数据存到RxData中。
由于我手头上只有一块开发板,无法向CAN接收器发送数据,所以接收测试无法做,但可以使用测试模式回环即loopback
来测试接收功能,即发数据不进入总线,直接接收回来。
3. 回环测试
在can.c
里将hcan.Init.Mode = CAN_MODE_NORMAL
改为hcan.Init.Mode = CAN_MODE_LOOPBACK
,即进入了回环模式。想法是通过串口将送回去的RxData
打印出来
将过滤器初始化后,需要在原来的main.c代码中添加接收器结构体的初始化
CAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[8];
RxHeader.StdId = 0x234;
RxHeader.ExtId = 0x234;
RxHeader.IDE = 0;
RxHeader.RTR = 0;
RxHeader.DLC = 8;
while(1)循环中在原来的代码后加入以下
HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, RxData);
printf("RX of the CAN data is: %02X %02X %02X %02X %02X %02X %02X %02X\r\n", RxData[0], RxData[1], RxData[2], RxData[3], RxData[4], RxData[5], RxData[6], RxData[7]);
这样在发送出去以后马上就能回到RxData中,打开串口调试助手可以看到
证明发送出去的数据都可以通过串口打印出来,测试通过。
同时通过逻辑分析仪也还是能看到CAN模块的发送数据。