CAN总线结构:

图1 CAN总线结构
闭环结构:总线两端各接一个120欧姆的电阻,是高速短距离CAN网络,速率为125kbit/s - 1Mbit/s。1Mbit/s时通信距离最长是40m。
开环结构:两根信号线独立,各自串一个2.2k电阻,低速远距离,最高到125kbit\s,在40kbit/s是最长距离是1KM。
CAN网络的逻辑电平和平时的不一致。
| 电平 | 高速CAN | 低速CAN |
| 显性电平(0) | UCAN_H – UCAN_L= 2V | UCAN_H – UCAN_L = 3V |
| 隐性电平(1) | UCAN_H – UCAN_L = 0V | UCAN_H – UCAN_L = - 1.5V |
表1 显隐电平
位时序:
CAN网络上没有主从机之分,都是节点,同一时刻只能有一个节点发送数据其余接收,没有地址概念,按照帧发送数据,每个节点都可以接收数据,可以根据ID过滤数据。没有同步的时钟线,所以需要规定波特率。
位时序CAN总线上一个节点采集一个位数据的时序,通过位时序CAN总线可以位同步消除传输误差。

图2 位时序
同步段需要检测一个位跳变,固定时间是一个 。BS1是
,定义了采样点位置,BS1结束时采样,m可以自动加长以补偿频率差异导致的正相位偏移。BS2一般是1-8个
,定义了发送点的位置,可自动缩短补偿负相位偏移,CAN控制器可以自动对位时序实现再同步,此时可以修改图中的m和n,一般修改1-4个
。波特率是
。
STM32把传播时间段和相位缓冲段1合并在一起,形成了新的时间段1,在图2 中没有体现传播时间段;没有SS同步段的设置,因为SS段恒等于1。
帧类型:
若干个位时序组成了帧。
数据帧、遥控帧、错误帧、过载帧、帧间空间。
重点学习数据帧和遥控帧。
数据帧:标准的数据帧和遥控帧都有11位的ID。数据帧传输带有ID的0-8字节数据,遥控帧只有ID,没有数据,用于请求数据。

图3 数据帧
帧起始:SOF (Start Of Frame),显性电平0,图3中 SOF 标识的1是位数,不是实际的帧数据。
仲裁段:标准ID(11位)+RTR(Remote Transmit Request)
多个节点一同发送数据时根据仲裁段谁先出现显性电平(逻辑0)决定谁占用总线,如果两节点发送ID相同,则根据最后的RTR位(用于区分数据帧和遥控帧,数据帧为显性电平逻辑0,遥控帧为隐形电平逻辑1)裁决,所以具有相同ID的遥控帧和数据帧同时发送时,数据帧优先级高。
IDE(Identify Extern)是指示帧是否为标准格式,标准格式为显性电平逻辑0。RB0是保留位总为显性电平逻辑0,DLC Data Length Code是数据长度编码4位,遥控帧DLC总是0。
数据段:只有数据帧有。
CRC段前15位是检验码,最后一位总是隐形电平逻辑1,是CRC段的界定符。
ACK段包含一个ACK位和一个ACK段标识符
EOF连续7个隐形电平,表示帧结束。
扩展帧:
仲裁段的ID是分开的,两部分。
SRR是RTR占位符,隐形电平逻辑1,真正的RTR位在扩展ID后边。IDE在扩展帧中是隐形电平逻辑1。
RB1和RB0是保留位显性电平逻辑0。
总线仲裁规则:
1.空闲时谁先发送谁优先
2.同时ID显性电平先出现者优先
STM32CubeMX设置:
以72M主频的F103C8T6为例:APB1是36Mhz,选9分频后是4Mhz,一个时间片就是250ns。我们如果设波特率为500KBit/s,那么就设置BS1为4,BS2为3(这两个决定CAN的波特率)。其他时间默认即可。因为只有一个最小系统板,也没有CAN收发器,所以设置自发自收Test Mode:Loopback。

图4 Cube MX 设置
如果使用中断接收的话需要把中断打开:

图5 CAN中断
STM32F103C8T6只有一个CAN控制器,我们只关心接收时的中断所以使用RX0这个中断就行。CAN TX initerruputs是发送完成中断。CAN SCE interrupts是CAN错误中断。
代码:
在can.h中添加以下声明
//can.h
void CANFilter_Config(void);
void CAN1_Send_Test(void);
在can.c中需要配置过滤器、实现发送函数、实现接收中断回调函数
//can.c
static CAN_TxHeaderTypeDef TxMessage;//发送数据头,里边有ID信息
static CAN_RxHeaderTypeDef RxMessage;//接收数据头,里边有ID信息
//配置过滤器函数:(没有进行任何的过滤)
void CANFilter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
/* Filter configuration Error */
printf("ConfigFilter is Failed\n");
Error_Handler();
}
printf("ConfigFilter is success\n");
}
void CAN1_Send_Test(void)
{
uint32_t CAN_TX_BOX=0;//必须自己定义,用宏会硬件报错
uint8_t data[8] ={0x01,0x02,0x03,0x04};
TxMessage.IDE= CAN_ID_STD;//标准ID
TxMessage.StdId = 0x2222;//自己定的ID
TxMessage.RTR =CAN_RTR_DATA;//它是区别数据帧和远程帧的标志。数据帧显性
TxMessage.TransmitGlobalTime = DISABLE;
TxMessage.DLC = 4;//数据长度
if (HAL_CAN_AddTxMessage(&hcan,&TxMessage, data,&CAN_TX_BOX) != HAL_OK){
printf("CAN send test data fail! \r\n");
Error_Handler();
}
}
//接收回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
uint8_t data[8];
HAL_StatusTypeDef status;
if (hcan->Instance == NULL){
return;
}
//接收数据主要就是下边的函数,注意FIFO是在过滤器中定义的那个
status = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);
if (status == HAL_OK) {
printf("---> Data Received!\n");
printf("RxMessage.StdId is 0x%08X\n", RxMessage.StdId);
printf("data[0] is 0x%02X\n", data[0]);
printf("data[1] is 0x%02X\n", data[1]);
printf("data[2] is 0x%02X\n", data[2]);
printf("data[3] is 0x%02X\n", data[3]);
printf("<---\n");
} else {
printf("Failed to get CAN message, status: 0x%08X\n", status);
}
}
将下面代码放到main.c文件中的MX_CAN_Init();函数之后,即可1s中接收到一次CAN信号,并且通过串口打印(假设printf已经重定向)
//main.c
CANFilter_Config();
if(HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK){
printf("CAN_IT_RX_FIFO0_MSG_PENDING Fail\r\n");
Error_Handler();
}
printf("CAN_IT_RX_FIFO0_MSG_PENDING success\r\n");
//使能CAN总线
if(HAL_CAN_Start(&hcan) !=HAL_OK){
printf("CAN start Fail\r\n");
Error_Handler();
}
printf("CAN start success\r\n");
while (1)
{
CAN1_Send_Test();
HAL_Delay(1000);
}
过滤器:
cubeMX没有为我们生成过滤器代码,所以我们必须自己写一个,过滤器决定了你想收到哪个ID的信息。
FilterBank 是因为某些高端型号芯片会有两个CAN控制器芯片,主CAN控制器和从CAN控制器,丛CAN想用主CAN必须打开。我们F103C8T6就只有一个CAN控制器,所以就填0。
FilterMode 过滤器是工作在列表模式还是掩码模式。
列表模式类似路由器的白名单模式。
掩码模式类似子网掩码。下边两个FilterMask参数中的位如果是1则和FilterId严格匹配才可通过,如果FilterMask的位是0则被过滤ID对应位无论是什么均可通过(这个位不再关系不代表整个ID就通过过滤器了)。
FilterFIFOAssignment 需要指定FIFO0
sFilterConfig.SlaveStartFilterBank = 14;还是因为高端芯片的事,0-13是给主CAN的,14-27给从CAN,这里设成14代表主CAN使用0-13。
参考文献:
初学stm32 --- CAN_stm32 can-CSDN博客

1784

被折叠的 条评论
为什么被折叠?



