CAN通信
文章目录
- 接线方式
- 电平格式
- 收发模式
- 常规模式(多用)
- 回环模式
- 静默模式
- 回环静默模式
- CubeMX
- 开启
- 发送
- 筛选器
- 四种筛选模式
- 接收
接线方式
- 总线接法
- 每个设备引I出两根线,一根是CAN_H,一根是CANL(当然也有单线CAN但速度低);不同设备的CAN_H接到一起,CAN_L接到一起
- CAN_H和CAN_L用120Ω电阻连接在一起,防止信号不稳定,响应速度变慢等问题
电平格式
- 差分信号传输,在长距离传输时可保证信号质量
- 码元1被编码成CAN_H与CAN_L电平相同,称为隐性电平,一般两根线电平均为2.5V
- 码元0被编码成CAN_H与CAN_L电平不同,称为显性电平,一般CAN_H为3.5V,CAN_L为1.5V
收发模式
常规模式(多用)
- 向总线发送
- 向总线接收
回环模式
- 向总线和本机发送
- 不从总线接收仅从本机接收
静默模式
- 不向总线发送仅向本机发送
- 从总线和本机接收
回环静默模式
- 不向总线发送
- 不向总线发送
- 自收自发
- 一般用于自检
CubeMX
- CAN挂载在APB1的外设上
- 最主要是配置波特率,等于时钟频率/分频/3个time之和
f407主频168M,apb1外设42M
- 时间触发模式:会自动为报文生成时间戳
- 自动总线关闭管理
- 自动唤醒模式:使能后,当CAN外设在休眠状态时如果CAN总线有数据,则自动唤醒CAN外设。
- 自动重发:使能后,如果因为仲裁失败(总线冲突)或是其他原因导致发送失败,会自动重发。建议使能。
- 接收FIFO锁定模式:如果使能,当接收FIFO满时,下一条数据会被丢失。如果不使能,则覆盖前面的数据。
- 发送FIFO优先级:当发送邮箱中同时有多个帧,是按照先进先出的顺序发送还是按照ID的优先级发送。如果不使能,则按照ID优先级发送。
记得打开中断
开启
! ! !记得开启can! ! !
HAL_CAN_Start(CAN_HandleTypeDef *hcan)
如果用中断
__HAL_CAN_ENABLE_IT()
发送
- CAN协议总共定义了5种类型的帧,但我们能人为发送的其实只有数据帧和遥控帧。如果我们要发送数据帧,则还要定义数据的长度。
发送用到的结构体如下:
typedef struct
{uint32_t StdId; //标准IDuint32_t ExtId; //扩展IDuint32_t IDE; //用来决定报文是使用标准ID还是扩准IDuint32_t RTR; //用来决定报文是数据帧要是遥控帧uint32_t DLC; //数据长度,取值为0-8FunctionalState TransmitGlobalTime;
//最后这个是时间触发模式用的,开启后会自动把时间戳添加到最后两字节的数据中。目前没有用到,选择 DISABLE
} CAN_TxHeaderTypeDef;
- StdId:如果将要发送的报文使用标准ID,那么这个成员便记录标准ID的值
- 取值:
0x0 ~ 0x7FF
- 取值:
- ExtId:如果将要发送的报文使用扩展ID,那么这个成员便记录扩展ID的值
- 取值:
0x0 ~ 0x1FFFFFFF
- 取值:
- IDE:用来决定报文使用标准ID还是扩准ID
- 取值:
CAN_ID_STD
或CAN_ID_EXT
- 取值:
- RTR:用来决定报文是数据帧要是遥控帧
- 取值:
CAN_RTR_DATA
或CAN_RTR_REMOTE
- 取值:
- DLC:用来记录数据帧的数据长度,单位字节(如果要发送的是遥控帧,该成员中的内容不起作用)
- 取值:
0 ~ 8
- 取值:
- TransmitGlobalTime:目前没有用到,选择
DISABLE
- 取值:
ENABLE
或DISABLE
- 取值:
结构体中的 .IDE 成员是用来决定报文是使用标准ID还是扩准ID,如果这个成员等于 CAN_ID_STD ,也就是使用标准ID,此时 .ExtId 中的内容就不起作用了;反之亦然。
发送函数
HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox);
*hcan
:can的句柄,由CubeMX自动帮我们定义*pHeader
:发送结构体aData[]
:要发送的数据*pTxMailbox
:发送这条报文用的是哪个邮箱,这个作为输出参数。(因为STM32的CAN外设拥有三个发送邮箱,库函数会帮我们找到空的邮箱并把一帧报文装进去,这个参数便记录用到的邮箱号,具体怎么用目前还不清楚。因为CAN总线可能拥挤或是报文被抢占,因此数据并不是填到FIFO中就能马上发出去的,而是要等到合适的时机才能发出去)
筛选器
- 不配置筛选器就无法接收数据
- CAN总线上可以挂载多个节点,需要忽略掉无关ID的报文。筛选器用来筛选不同ID的报文,只有通过筛选的报文才会被存入接收FIFO中。
- 对于CAN1、CAN2一同使用的时候,两个CAN共用28个筛选器组。如果只使用了CAN1,则只有14个筛选器组能够使用。 一个筛选器组共有两个32位的寄存器
四种筛选模式
- 32位掩码模式
此模式下筛选寄存器1 (CAN_FiR1
) 用来存放ID,筛选寄存器2 (CAN_FiR2
) 用来存放此ID的掩码。掩码为1的位一定要匹配ID所对应的位,掩码为0的位则ID此位为1还是0都可以。如果收到匹配成功的ID,则放入接收FIFO。 - 32位列表模式
列表模式就相当于白名单,只有收到与白名单一模一样的ID时才能通过匹配。此模式下筛选寄存器1存储一条ID,筛选寄存器2存储另一条ID。也就是说此模式可以存放两条32位的白名单。 - 16位掩码模式
此模式与前面的32位掩码模式类似,只不过筛选码从原来的32位变成了16位,对扩展ID筛选能力变弱了。但这种模式可以存放两条ID及两条掩码。 - 16位列表模式
此模式可以存放4条16位的ID。
结构体
typedef struct
{uint32_t FilterIdHigh; //CAN_FiR1寄存器的高16位uint32_t FilterIdLow; //CAN_FiR1寄存器的低16位uint32_t FilterMaskIdHigh; //CAN_FiR2寄存器的高16位uint32_t FilterMaskIdLow; //CAN_FiR2寄存器的低16位uint32_t FilterFIFOAssignment; //通过筛选器的报文存在FIFO0还是FIFO1中uint32_t FilterBank; //此次配置用的是哪个筛选器。用单CAN的取值为0-13,双CAN为 0 ~ 27uint32_t FilterMode; //掩码模式或列表模式,取值:CAN_FILTERMODE_IDMASK 或 CAN_FILTERMODE_IDLISTuint32_t FilterScale; //32位或16位,取值:CAN_FILTERSCALE_16BIT 或 CAN_FILTERSCALE_32BITuint32_t FilterActivation; //使能或失能此筛选器uint32_t SlaveStartFilterBank; //CAN1和CAN2一起用的时候,为CAN2分配筛选器的个数
} CAN_FilterTypeDef;
填好筛选器结构体,然后调用下面这个函数即可生效:
HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig);
接收
- CAN的接收通常是使用中断方式来实现(因为没有DMA,而查询法又难以保证实时性)
- 打开CAN的FIFO消息挂起中断请求(也就是CAN外设的中断使能位)。
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
//等效于
__HAL_CAN_ENABLE_IT(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
- 当CAN收到了符合筛选器的报文时,就会触发这个中断,我们便可以在这个中断回调函数中接收并处理收到的报文。(由于FIFO0和FIFO1用到的中断函数是独立的,因此这里的回调函数也是不一样的,要看清楚是FIFO0的还是1的)
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &can_Rx, recvBuf);
}
接收用到的结构体如下:
不过这个结构体并不需要我们来赋值,而是作为接收函数的输出参数。
typedef struct
{uint32_t StdId; uint32_t ExtId; uint32_t IDE; uint32_t RTR; uint32_t DLC; uint32_t Timestamp; uint32_t FilterMatchIndex;
} CAN_RxHeaderTypeDef;
- Timestamp :只有使能了时间触发模式才有用,记录时间戳
- FilterMatchIndex :这条报文被接收是通过哪个筛选器
接收函数
HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);
*hcan
:can的句柄,由CubeMX自动帮我们定义RxFifo
:接收FIFO号。参数:CAN_RX_FIFO0
或CAN_RX_FIFO1
pHeader
:接收结构体,这里作为输出参数aData[]
:接收数组,这里作为输出参数