一、介绍
CAN运用与汽车计算机系统和嵌入式公用控制局域网的标准总线。
CAN不是以时钟信号来同步的,它是一种异步通讯,只具有CAN_H,CAN_L两条信号,共同构成一组差分信号线,以差分信号的形式进行通讯。
1、闭环总线网络
特点:高速,短距离。总线最大长度40m, 通讯速度最高位1Mbps,要求总线两端各一个120欧姆的电阻。
2、开环总线网络
特点:低速、远距离
最大传输距离1Km,最高通讯速率为125kbps,两根总线式独立的,不形成闭环,要求每根总线上各串联一个2.2K欧姆的电阻。
CAN通讯点由一个CAN控制器及CAN收发器组成,控制器与收发器通过CAN_Tx,CAN_Rx信号线连接,收发器与CAN总线之间使用CAN_H,CAN_L信号线连接。其中CAN_Tx及CAN_Rx使用普通的类似与TTL逻辑信号,而CAN_H,CAN_L是一对差分信号线。
3、CAN的四种模式
各个工作模式介绍如下:
正常模式
正常模式下就是一个正常的 CAN 节点,可以向总线发送数据和接收数据。
静默模式
静默模式下,它自己的输出端的逻辑 0 数据会直接传输到它自己的输入端,逻辑
1 可以被发送到总线,所以它不能向总线发送显性位(逻辑 0),只能发送隐性位(逻 辑 1)。输入端可以从总线接收内容。由于它只可发送的隐性位不会强制影响总线
的状态,所以把它称为静默模式。这种模式一般用于监测,它可以用于分析总线
上的流量,但又不会因为发送显性位而影响总线。
回环模式
回环模式下,它自己的输出端的所有内容都直接传输到自己的输入端,输出端的
内容同时也会被传输到总线上,即也可使用总线监测它的发送内容。输入端只接
收自己发送端的内容,不接收来自总线上的内容。使用回环模式可以进行自检。
回环静默模式
回环静默模式是以上两种模式的结合,自己的输出端的所有内容都直接传输到自
己的输入端,并且不会向总线发送显性位影响总线,不能通过总线监测它的发送
内容。输入端只接收自己发送端的内容,不接收来自总线上的内容。这种方式可
以在“热自检”时使用,即自我检查的时候,不会干扰总线。
4、STM32 CAN框架
5、筛选器
图 24-5 中的 CAN 外设框图,在标号处的是 CAN 外设的验收筛选器,一共有 28 个
筛选器组,每个筛选器组有 2 个寄存器,CAN1 和 CAN2 共用的筛选器的。
在 CAN 协议中,消息的标识符与节点地址无关,但与消息内容有关。因此,发送节点将
报文广播给所有接收器时,接收节点会根据报文标识符的值来确定软件是否需要该消息,为了简化软件的工作,STM32 的 CAN 外设接收报文前会先使用验收筛选器检查,只接收需要的报文到 FIFO 中。
过滤的方法分为以下两种模式:
(1) 标识符列表模式,它把要接收报文的 ID 列成一个表,要求报文 ID 与列表中的某
一个标识符完全相同才可以接收,可以理解为白名单管理。
(2) 掩码模式,它把可接收报文 ID 的某几位作为列表,这几位被称为掩码,可以把它
理解成关键字搜索,只要掩码(关键字)相同,就符合要求,报文就会被保存到接
收 FIFO。
二、遇到的问题
1、发送单包数据,我想都非常简答,例子一大堆包括,demo。但是发送多包数据怎么处理?
2、发送多包数据,网上基本上就两种说发
*两个数据之间加延时
*查询邮箱是否满了
HAL_CAN_GetTxMailboxesFreeLevel()
对比上面两种方法,为了效率我们选择第二种
三、配置与代码
直接上cubemx配置
直接生成后,cubemx没有设置过滤器,需要自己添加,结合网上各资料总结代码如下
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_CAN1_Init();
/* USER CODE BEGIN 2 */
CAN_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
uint8_t TxData[100] ;
for(uint8_t i=0;i<100;i++)
TxData[i]=i;
CAN_SendStdMsg(&hcan1,TxData,sizeof(TxData));
HAL_Delay(100);
}
/* USER CODE END 3 */
}
bsp_can.c
#include "bsp_can.h"
/// CAN过滤器寄存器位宽类型定义
typedef union
{
__IO uint32_t value;
struct
{
uint8_t REV : 1; ///< [0] :未使用
uint8_t RTR : 1; ///< [1] : RTR(数据帧或远程帧标志位)
uint8_t IDE : 1; ///< [2] : IDE(标准帧或扩展帧标志位)
uint32_t EXID : 18; ///< [21:3] : 存放扩展帧ID
uint16_t STID : 11; ///< [31:22]: 存放标准帧ID
} Sub;
} CAN_FilterRegTypeDef;
#define CAN_BASE_ID 0 ///< CAN标准ID,最大11位,也就是0x7FF
#define CAN_FILTER_MODE_MASK_ENABLE 1 ///< CAN过滤器模式选择:=0:列表模式 =1:屏蔽模式
#define CAN_ID_TYPE_STD_ENABLE 1 ///< CAN过滤ID类型选择:=1:标准ID,=0:扩展ID
void CAN_Filter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
CAN_FilterRegTypeDef IDH = {
0};
CAN_FilterRegTypeDef IDL = {
0};
#if CAN_ID_TYPE_STD_ENABLE
IDH.Sub.STID = (CAN_BASE_ID >> 16) & 0xFFFF; // 标准ID高16位
IDL.Sub.STID = (CAN_BASE_ID & 0xFFFF); // 标准ID低16位
#else
IDH.Sub.EXID = (CAN_BASE_ID >> 16) & 0xFFFF; // 扩展ID高16位
IDL.Sub.EXID = (CAN_BASE_ID & 0xFFFF); // 扩展ID低16位
IDL.Sub.IDE = 1; // 扩展帧标志位置位
#endif
sFilterConfig.FilterBank = 0; // 设置过滤器组编号
#if CAN_FILTER_MODE_MASK_ENABLE
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
#else
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
#endif
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽
sFilterConfig.FilterIdHigh = IDH.value; // 标识符寄存器一ID高十六位,放入扩展帧位
sFilterConfig.FilterIdLow = IDL.value; // 标识符寄存器一ID低十六位,放入扩展帧位
sFilterConfig.FilterMaskIdHigh = IDH.value; // 标识符寄存器二ID高十六位,放入扩展帧位
sFilterConfig.FilterMaskIdLow = IDL.value; // 标识符寄存器二ID低十六位,放入扩展帧位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器组关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
sFilterConfig.SlaveStartFilterBank = 14; // 设置从CAN的起始过滤器编号,本单片机只有一个CAN,顾此参数无效
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
void CAN_Init(void)
{
CAN_Filter_Config();
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // 使能CAN接收中断
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *canHandle)
{
static CAN_RxPacketTypeDef packet;
// CAN数据接收
if (canHandle->Instance == hcan1.Instance)
{
if (HAL_CAN_GetRxMessage(canHandle, CAN_RX_FIFO0, &packet.hdr, packet.payload) == HAL_OK) // 获得接收到的数据头和数据
{
// printf("\r\n\r\n\r\n################### CAN RECV ###################\r\n");
// printf("STID:0x%X\r\n",packet.hdr.StdId);
// printf("EXID:0x%X\r\n",packet.hdr.ExtId);
// printf("DLC :%d\r\n", packet.hdr.DLC);