CAN基础知识介绍

CAN介绍

什么是CAN

CAN(Controller Area Network),是ISO国际标准化的串行通信协议。为了满足汽车产业的“减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”的需求。

  • 低速CAN(ISO11519)通信速率10~125Kbps,总线长度可达1000米。

  • 高速CAN(经典CAN)(ISO11898)通信速率125Kbps~1Mbps,总线长度≤40米。

  • CAN FD通信速率可达5Mbps,并且兼容经典CAN,遵循ISO11898-1 做数据收发。

CAN总线通讯网络

  • 一般CAN总线包含CAN控制器(开发板内CAN外设)、CAN收发器(CAH收发器芯片:TAJ1050、TAJ1042、SIT1050T)、CAN_High和CAN_Low

  • CAN总线由两根线(CAN_High、CAN_Low)组成的,允许挂载多个设备节点(一般低速CAN20个,高速CAN30个,一般取决于CAN收发器的芯片)。

闭环总线网络(高速CAN)

开环总线网络(低速CAN)

CAN总线特点

  • 多主控制:每个设备都可以主动发送数据。

  • 系统的柔软性:没有类似地址的信息,添加设备不改变原来总线的状态。

  • 通信速度:速度快,距离远。

  • 具有错误检测和错误通知和错误恢复功能。

  • 故障封闭:判断故障类型,并且进行隔离。

  • 连接节点多:CAN协议中,我们可以挂载多个节点(每个节点由CAN控制器和CAN收发器组成) ,通过总线来实现节点通讯,与其他协议不同的是,CAN协议不对节点的地址进行编码,而是对节点的数据内容进行编码。

CAN应用场景

CAN总线协议已经广泛应用在汽车电子、工业自动化、船舶、医疗设备、工业设备等方面。

CAN物理层

CAN物理层特性

  • CAN使用差分信号(CAN_High - CAN_Low)进行数据传输,根据CAN_High和CAN_Low上的电位差来判断总线电平。

  • 总线电平分为显性电平(逻辑0)和隐性电平(逻辑1)。

  • 显性电平具有优先权(即当某个发送方发送一个显性电平,那么总线都是逻辑0)。发送方通过使总线电平发生变化,将消息发给接收方。

  • CAN总线电平与CAN_High和CAN_Low的关系:

CAN数据传输

发送数据:控制器发送一个逻辑信号,收发器将这个逻辑信号变成差分信号传送到总线中。

接收数据:收发器将差分信号转化为逻辑信号,即0或1的二进制编码;

CAN协议层

CAN帧种类介绍

CAN总线以“帧”形式进行通信。CAN协议定义了5种类型的帧,分别是数据帧、遥控帧、错误帧、过载帧和间隔帧,其中数据帧最常用。

帧类型

帧作用

数据帧

用于发送单元向接收单元传输数据的帧

遥控帧

用于接收单元向具有相同ID的发送单元请求数据的帧

错误帧

用于当检测出错误时向其他单元通知错误的帧

过载帧

用于接收单元通知其他尚未做好接收准备的帧

间隔帧

用于将数据帧以及遥控帧与前面的帧分离开来的帧

CAN的其他帧的分析方法与数据帧基本一样,我们只要搞懂对数据帧就基本可以搞懂CAN的其他帧了。

CAN数据帧介绍

数据帧由7段组成,分为标准帧(CAN2.0A)和扩展帧(CAN2.0B),这两者的区别主要体现在仲裁段和控制段。

  • 帧起始:表示数据帧开始的段,一位显性信号(逻辑0)。

  • 仲裁段:表示该帧优先级的段,优先级。

  • 控制段:表示数据的字节数即保留位的段。

  • 数据段:数据的内容,一帧可发送0~8字节数。

  • CRC段:检查帧的传输错误的段。

  • ACK段:表示确认正常接收的段。

  • 帧结束:表示数据帧结束的段,7位隐性信号(逻辑1)。

标准数据帧

  • ID(标识符位):优先级。

  • RTR(远程发送请求位):0是数据帧,1是遥控帧。

  • IDE(扩展标识符位):用于区别是标准帧还是扩展帧,1是扩展帧,0是标准帧。

  • DLC(数据长度编码位):告知该数据帧的长度。

  • Data Field:数据长度,0~8字节。

  • CRC:循环校验序列。

  • DEL:界定符。

  • ACK:发送单元发送数据时将该位拉高,其他接收单元接收数据时将该位拉低。

扩展数据帧

  • ID(标识符位):优先级。

  • RTR(远程发送请求位):0是数据帧,1是遥控帧。

  • SRR:用在扩展格式,替代RTR,无实质作用。

  • IDE(扩展标识符位):用于区别是标准帧还是扩展帧,1是扩展帧,0是标准帧。

  • DLC(数据长度编码位):告知该数据帧的长度。

  • Data Field:数据长度,0~8字节。

  • CRC:循环校验序列。

  • DEL:界定符。

  • ACK:发送单元发送数据时将该位拉高,其他接收单元接收数据时将该位拉低。

CAN时序介绍

CAN总线以“位同步”机制实现对电平的正确采样。位数据都由四段组成:同步段(SS)、传播时间段(PTS)、相位缓冲段1(PBS1)和相位缓冲段2(PBS2),每段又由多个位时序Tq组成。

  • 同步段(SS):1Tq

  • 传播时间段(PTS):1~8Tq

  • 相位缓冲段1(PBS1):1~8Tq

  • 相位缓冲段2(PBS2):2~8q

  • 采样点:读取总线电平,并将读取到的电平作为位值的点,如该位数据的采样点读取的电平是显性电平。

  • 注意:当节点监测到总线上信号的跳变在SS段范围内,表示节点与总线的时序是同步,此时采样点的电平即该位的电平。

  • 根据位时序,就可以计算CAN通信的波特率?。

数据同步过程

CAN为了实现对总线电平信号的正确采样,会进行数据同步,数据同步分为硬件同步和软件同步。

硬件同步

节点通过CAN总线发送数据,一开始发送帧起始信号。总线上其他节点会检测帧起始信号在不在位数据的SS段内,来判断内部时序是否与总线同步。

如果不在SS段内,这种情况下,采样点获得的电平状态时不正确的。所以节点会使用硬件同步方式来进行调整,把自己的SS段平移到检测到边沿(帧起始信号是从隐性电平到显性电平的,具有一个下降沿)的地方,从而获得同步,同步情况下,采样点获得的电平状态才是正确的。

数据硬件同步前

数据硬件同步后

再同步

再同步利用普通数据位的边沿信号(帧起始信号时特殊的边沿信号)进行同步。再同步的方式分为两种情况:超前和滞后,即边沿信号与SS段的相对位置。

再同步时,PSB1和PSB2增加或者减少的时间称为PJW(再同步补偿宽度),一般是1~4个Tq。限定了SJW值后,再同步时,不能增加(修改)限定长度的SJW值。SJW值较大时,吸收误差能力更强,但是通讯速度会下降。

边沿信号滞后

边沿信号超前

CAN总线仲裁(优先级决定)

CAN总线处于空闲状态时,最先开始发送消息的单元获得发送权。当多个单元同时开始发送时,从仲裁段(报文ID)的第一位开始仲裁,连续输出显性电平最多的单元可继续发送,即首先出现隐性电平的单元失去对总线的占有权而变为接收单元。(即报文ID越小的单元,它的优先级越高)

在总线仲裁中失利的单元(即由发送转变成接收的单元)会自动检测总线空闲,在第一时间再次尝试发送。

STM32的CAN控制器介绍

CAN控制器介绍

STM32的CAN控制器(bxCAN)支持CAN2.0A和CAN2.0B Active版本协议。CAN2.0A只能处理标准数据帧且扩展帧的内容会识别错误,而CAN2.0B Active可以处理标准数据帧和扩展数据帧。CAN2.0B Passive只能处理标准数据帧且扩展数据帧的内容会忽略。

CAN控制器主要特点

  • 波特率最高可达1Mbps。

  • 支持时间出发通信(CAN的硬件内部定时器可以在TX/RX的帧起始位的采样点位置产生时间戳)。

  • 具有3级发送邮箱。

  • 具有3级深度的2个接收FIFO。

  • 可变的过滤器组(最多28个),STM32F103xxxx只有14个。

CAN控制器工作模式

CAN控制器的工作模式有三种:初始化模式、正常模式和睡眠模式。

CAN控制器的测试模式

CAN控制器的测试模式有三种:静默模式、环回模式和环回静默模式。(在初始化模式下可以配置CAN控制器的测试模式)

CAN控制器的框图

STM32F103xxxx没有CAN2(从)。

CAN内核(①)

包含各种控制/状态/配置寄存器,可以配置模式、波特率灯。

发送邮箱(②)

用来缓存待发送的报文,最多可以缓存3个报文,并且包含发送调度,可以决定哪个报文(报文ID)先发送。

发送处理

发送优先级由邮箱中报文的标识符(ID)决定,标识符数值越低有最高优先级,如果标识符的值相同,邮箱小的先发送。

接收FIFO(③)

当有效报文通过接收过滤器后就进入FIFO,可以在FIFO里读取报文。

接收处理

有效报文指的是(数据帧知道EOF段的最后一位都没有错误),且通过过滤器组队标识符过滤。

接收过滤器(④)

当总线上报文数据量很大时,总线上的设备会频繁获取报文,占用CPU。过滤器的作用是选择性接收有效报文,减轻系统的负担,根据滤波器的配置将报文分别送往FIFO0或FIFO1。

过滤器的作用

每个过滤器组都有两个32位寄存器CAN_FxR1和CAN_FxR2。过滤器组的功能(与寄存器的位宽和寄存器的选择模式有关)不同,寄存器的作用不尽相同。

位宽

寄存器位宽可以设置32位或者16位,寄存器存储的内容就有所区别。(STDID:标准ID,EXTID:扩展ID)

过滤器组寄存器

32位

16位(寄存器由两部分组成)

CAN_FxR1

STDID[10:0]、EXTID[17:0]、IDE、RTR

STDID[10:0]、EXTID[17:15]、IDE、RTR

CAN_FxR2

STDID[10:0]、EXTID[17:0]、IDE、RTR

STDID[10:0]、EXTID[17:15]、IDE、RTR

工作模式

工作模式可以设置标识符屏蔽模式或标识符列表模式,寄存器内容的功能就有所区别。

  • 标识符屏蔽模式:它把可接收报文 ID 的某几位作为列表,这几位被称为屏蔽位,可以把它理解成关键字搜索,只要屏蔽位(关键字)相同,就符合要求,报文就会被保存到接收 FIFO。

  • 标识符列表模式:它把要接收报文的 ID 列成一个表,要求报文 ID 与列表中的某一个标识符完全相同才可以接收,可以理解为白名单管理。

作用

通常32位用来筛选扩展数据帧,16位用来筛选标准数据帧。

模式

说明

32位标识符屏蔽模式

CAN_FxR1 存储 ID,CAN_FxR2 存储哪个位必须要与 CAN_FxR1 中的ID 一致。(过滤出1组符合条件的报文)

32 位标识符列表模式

CAN_FxR1和 CAN_FxR2 各存储 1个ID(过滤出2个符合条件的报文)

16 位标识符屏蔽模式

CAN_FxR1低 16 位存储 ID,高 16 位存储哪个位必须要与低 16 位的ID一致;

CAN_FxR2低 16 位存储 ID,高 16 位存储哪个位必须要与低 16 位的ID一致。(过滤出2组符合条件的报文)

16 位标识符模式

CAN_FxR1和 CAN_FxR2 各存储2个ID(过滤出4个符合条件的报文)

举例:使用32位掩码模式过滤出一组符合条件的报文

设置过滤器组0为32位屏蔽位模式(过滤出一组符合条件的报文)

bit

31~24

23~16

15~8

7~0

ID

CAN_F0R1

(0xFFFF0000)

1111 1111

1111 1111

0000 0000

0000 0000

屏蔽位

CAN_F0R1

(0xFF00FF00)

1111 1111

0000 0000

1111 1111

0000 0000

映像

STID[10:3]

STID[2:0]、EXID[17:13]

EXID[12:5]

EXID[4:0]、IDE、RTR、0

过滤出ID

1111 1111

xxxx xxxx

0000 0000

xxxx xxxx

  • 屏蔽位寄存器中位值为1,表示ID要必须匹配;位值为0则不关心。

  • 在使能过滤器的情况下,总线上广播的报文ID与过滤器的配置都不匹配,CAN控制器会丢弃该报文,不会进入到接收FIFO中。

  • 注意:标识符选择位IDE和帧类型RTR需要一致,不同过滤器组的工作模式可以设置不同。

CAN控制器的位时序

STM32的CAN控制器的位时序分为三段:同步段(SYNC_SEG)、时间段1(BS1 = PTS + PBS1)、时间段2(BS2)。(主要用来计算波特率,CAN通信双方的波特率保持一致才能通信成功)

CAN相关寄存器介绍

寄存器

名称

作用

CAN_MCR

CAN主控制寄存器

主要负责CAN工作模式的配置

CAN_MSR

CAN主状态寄存器

主要用来设置CAN的当前状态

CAN_TSR

CAN发送状态寄存器

主要用来确定报文是否发送出去

CAN_RF0R

CAN接收FIFO 0寄存器

主要用来获取FIFO 0的报文数目

CAN_RF1R

CAN接收FIFO 1寄存器

主要用来获取FIFO 1的报文数目

CAN_IER

CAN中断使能寄存器

主要用来使能CAN的某些中断位

CAN_ESR

CAN错误状态寄存器

主要用来接收一些错误信息

CAN_BTR

位时序寄存器

用来设置分频/TBS1/TBS2/TSWJ 等参数,设置测试模式

CAN_(T/R)IxR

标识符寄存器

存放(待发送/接收)的报文ID、扩展ID、IDE位及RTR位

CAN_(T/R)DTxR

数据长度和时间戳寄存器

存放(待发送/接收)报文的DLC段

CAN_(T/R)DIxR

低位数据寄存器

存放(待发送/接收)报文数据段的Data0~Data3的内容

CAN_(T/R)DHxR

高位数据寄存器

存放 (待发送/接收)报文数据段的Data4~Data7的内容

CAN_FM1R

过滤器模式寄存器

用于设置各过滤器组的选择模式

CAN_FS1R

过滤器位宽寄存器

用于设置各过滤器组的位宽

CAN_FFA1R

FIFO关联寄存器

用于设置报文通过过滤器后,被存入的FIFO

CAN_FA1R

过滤器激活寄存器

用于开启对应的过滤器组

CAN_FxR(1/2)

过滤器组x寄存器

根据位宽和模式设置不同,CAN FXR1和FXR2功能不同

CAN相关HAL库驱动介绍

STM32的hal库关于CAN的结构体

CAN_InitTypeDef

CAN初始化结构体

typedef struct
{uint32_t Prescaler;  /* 预分频 */uint32_t Mode;  /* 工作模式 */ uint32_t SyncJumpWidth;  /* 再次同步跳跃宽度 */uint32_t TimeSeg1;  /* 时间段1(BS1)长度 */ uint32_t TimeSeg2;  /* 时间段2(BS2)长度 */ FunctionalState TimeTriggeredMode;  /* 时间触发通信模式 */FunctionalState AutoBusOff;  /* 总线自动关闭模式 */FunctionalState AutoWakeUp;  /* 自动唤醒 */ FunctionalState AutoRetransmission;  /* 自动重传 */ FunctionalState ReceiveFifoLocked;  /* 接收FIFO锁定 */FunctionalState TransmitFifoPriority;  /* 传输FIFO优先级 */ } CAN_InitTypeDef;

CAN_FilterTypeDef

CAN过滤器配置结构体。

typedef struct
{uint32_t FilterIdHigh;  /* ID高字节 */          uint32_t FilterIdLow;  /* ID低字节 */           uint32_t FilterMaskIdHigh;  /* 屏蔽位高字节 */      uint32_t FilterMaskIdLow;  /*屏蔽位低字节 */       uint32_t FilterFIFOAssignment;  /* 过滤器关联FIFO */  uint32_t FilterBank;  /* 选择过滤器组 */          uint32_t FilterMode;  /* 过滤器模式 */          uint32_t FilterScale;  /* 过滤器位宽 */           uint32_t FilterActivation;  /* 过滤器使能 */      uint32_t SlaveStartFilterBank;  /* 从CAN选择启动过滤器组(单CAN情况下,该成员没有意义) */  } CAN_FilterTypeDef;

过滤器配置模式

CAN_FxR1[31:16]

CAN_FxR1[15:0]

CAN_FxR2[31:16]

CAN_FxR2[15:.0]

32位标识符屏蔽模式

FilterIdHigh

FilterIdLow

FilterMaskIdHigh

FilterMaskIdLow

32 位标识符列表模式

FilterIdHigh

FilterIdLow

FilterMaskIdHigh

FilterMaskIdLow

16 位标识符屏蔽模式

FilterMaskIdLow

FilterIdLow

FilterMaskIdHigh

FilterIdHigh

16 位标识符模式

FilterMaskIdLow

FilterIdLow

FilterMaskIdHigh

FilterIdHigh

需要结合印象来赋值:

  • 32位位宽:STID[10:3]、STID[2:0]、EXID[17:13]、EXID[12:5]、EXID[4:0]、IDE、ETR、0

  • 16位位宽:STID[10:3]、STID[2:0]、RTR、IDE、EXID[17:15]

32位标识符列表模式过滤器配置代码

32位标识符屏蔽模式过滤器配置代码

CAN_TxHeaderTypeDef

CAN发送消息结构体。

typedef struct
{uint32_t StdId;  /* 标准标识符(当CAN数据帧是扩展数据帧时,该成员无意义) */ uint32_t ExtId;  /* 扩展标识符(当CAN数据帧是标准数据帧时,该成员无意义) */ uint32_t IDE;  /* 帧格式(该成员决定标准数据帧或扩展数据帧) */ uint32_t RTR;  /* 帧类型(该成员决定数据帧或遥控帧) */ uint32_t DLC;  /* 数据长度 */ FunctionalState TransmitGlobalTime;  /* 发送时间标记(时间戳) */} CAN_TxHeaderTypeDef;

CAN_RxHeaderTypeDef

CAN接收消息结构体。

typedef struct
{uint32_t StdId;  /* 标准标识符(当CAN数据帧是扩展帧时,该成员无意义) */ uint32_t ExtId;  /* 扩展标识符(当CAN数据帧是标准帧时,该成员无意义) */ uint32_t IDE;  /* 帧格式(该成员决定标准帧或扩展帧) */ uint32_t RTR;  /* 帧类型(该成员决定数据帧或遥控帧) */ uint32_t DLC;  /* 数据长度 */ uint32_t Timestamp;  /* 时间戳 */ uint32_t FilterMatchIndex;  /* 过滤器号 */ } CAN_RxHeaderTypeDef;

STM32的hal库关于CAN的函数

__HAL_RCC_CAN1_CLK_ENABLE()

使能CAN时钟。(使用STM32CubeMX会自动配置)

HAL_CAN_Init()

初始化CAN。(使用STM32CubeMX会自动配置)

HAL_CAN_ConfigFilter()

配置CAN接收过滤器。(将配置好的接收过滤器结构体与CAN外设连接起来)

原型:
HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig)

参数:
CAN_HandleTypeDef *hcan:CAN句柄
CAN_FilterTypeDef *sFilterConfig:接收过滤器结构体地址

实例:
HAL_CAN_ConfigFilter(&hcan,&myCan_filter); /* 配置CAN外设的接收过滤器 */

HAL_CAN_Start()

启动CAN外设。

原型:
HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan)

参数:
CAN_HandleTypeDef *hcan:CAN句柄

实例:
HAL_CAN_Start(&hcan); /* 启动CAN外设 */

HAL_CAN_ActivateNotification()

使能CAN中断。(该函数会调用__HAL_CAN_ENABLE_IT())

HAL_CAN_AddTxMessage()

添加数据到某个发送邮箱。

原型:
HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox)

参数:
CAN_HandleTypeDef *hcan:CAN句柄
CAN_TxHeaderTypeDef *pHeader:CAN发送结构体地址
uint8_t aData[]:待发送数据
uint32_t *pTxMailbo:发送邮箱

实例:
CAN_TxHeaderTypeDef myCan_tx; /* CAN发送结构体 */
uint32_t tx_mail = CAN_TX_MAILBOX0; /* 数据发送邮箱为邮箱0 */
uint8_t buf[8] = "zhu_tou";

HAL_CAN_AddTxMessage(&hcan,&myCan_tx,buf,&tx_mail); /* 从buf中发送数据到邮箱0 */

HAL_CAN_GetTxMailboxesFreeLevel()

获取发送邮箱的状态。

原型:
uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan)

参数:
CAN_HandleTypeDef *hcan:CAN句柄

实例:
while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 3); /* 等待数据发送完毕:当发送邮箱的3个邮箱都为空时,返回值为3 */

HAL_CAN_GetRxMessage()

从接收FIFO接收数据。

原型:
HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[])

参数:
CAN_HandleTypeDef *hcan:CAN句柄
uint32_t RxFifo:接收FIFO
CAN_RxHeaderTypeDef *pHeader:CAN接收结构体地址
uint8_t aData[]:接收数据存储地址

实例:
CAN_RxHeaderTypeDef myCan_rx; /* CAN接收结构体 */
uint8_t rcvbuf[8] = {0};

HAL_CAN_GetRxMessage(&hcan,CAN_RX_FIFO0,&myCan_rx,buf); /* 从接收FIFO 0接收消息到buf中 */

HAL_CAN_GetRxFifoFillLevel()

获取接收FIFO的状态。

原型:
uint32_t HAL_CAN_GetRxFifoFillLevel(CAN_HandleTypeDef *hcan, uint32_t RxFifo)

参数:
CAN_HandleTypeDef *hcan:CAN句柄
uint32_t RxFifo:接收FIFO

实例:
/* 判断接收FIFO 0是否为空 */
if(HAL_CAN_GetRxFifoFillLevel(&hcan,CAN_RX_FIFO0) == 0){
return 0; /*为空返回0*/
}

CAN基本使用步骤

  1. CAN外设初始化MX_CAN_Init()

  • 配置波特率、CAN功能、接收过滤器等等。

  1. 使能CAN时钟和初始化CAN相关引脚HAL_CAN_MspInit()

  • 使用STM32CubeMX会自动配置。

  1. CAN数据的接收和发送

  • CAN数据的接收:HAL_CAN_AddTxMessage()

  • 获取发送邮箱的状态:HAL_CAN_GetTxMailboxesFreeLevel()

  • CAN数据的发送:HAL_CAN_GetRxMessage()

  • 获取接收FIFO的状态:HAL_CAN_GetRxFifoFillLevel()

  1. 使用CAN的中断(可选)

  • 使能CAN的相关中断:__HAL_CAN_ENABLE_IT()

  • 配置NVIC

  • 编写中断服务函数

CAN实验1(使用回环模式实现自发自收)

通过CAN总线将数据自发自收,每按下一次按键就发送一次数据,并将接收的数据显示在串口上。(接收过滤器接收所有报文,不进行筛选)

使用STM32CubeMX创建工程

配置SYS

配置RCC

配置GPIO

PA0(按键)配置成输入引脚。

配置串口信息(UART1)

配置CAN

配置工程名称、工程路径

选择固件库

生成工程

使用MicroLIB库

can.c文件编写

  1. CAN外设初始化:主要配置波特率、CAN功能、接收过滤器

  1. CAN发送数据函数

  1. CAN接收数据函数

/* USER CODE BEGIN Header */
/********************************************************************************* @file    can.c* @brief   This file provides code for the configuration*          of the CAN instances.******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/#include "can.h"
#include <stdio.h>/* USER CODE BEGIN 0 */CAN_TxHeaderTypeDef myCan_tx;  /* CAN发送结构体 */
CAN_RxHeaderTypeDef myCan_rx;  /* CAN接收结构体 *//* USER CODE END 0 */CAN_HandleTypeDef hcan;/* CAN外设初始化 */
void MX_CAN_Init(void)
{/* USER CODE BEGIN CAN_Init 0 *//* USER CODE END CAN_Init 0 *//* USER CODE BEGIN CAN_Init 1 *//* USER CODE END CAN_Init 1 */hcan.Instance = CAN1;hcan.Init.Mode = CAN_MODE_LOOPBACK;  /* 环回模式:自发自收 *//* 波特率相关 */hcan.Init.Prescaler = 4;  /* 分频系数 */hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;  /* 重新同步跳跃宽度 */hcan.Init.TimeSeg1 = CAN_BS1_9TQ;  /* 时间段1 */hcan.Init.TimeSeg2 = CAN_BS2_8TQ;  /* 时间段2 *//* CAN功能设置 */hcan.Init.TimeTriggeredMode = DISABLE;  /* 禁止时间触发通信 */hcan.Init.AutoBusOff = DISABLE;  /* 禁止自动离线管理 */hcan.Init.AutoWakeUp = DISABLE;  /* 禁止自动唤醒 */hcan.Init.AutoRetransmission = DISABLE;  /* 禁止自动重发 */hcan.Init.ReceiveFifoLocked = DISABLE;  /* 禁止接收FIFO锁定 */hcan.Init.TransmitFifoPriority = DISABLE;  /* 禁止发送FIFO优先级 */if (HAL_CAN_Init(&hcan) != HAL_OK){Error_Handler();}/* USER CODE BEGIN CAN_Init 2 *//* 配置接收过滤器 */CAN_FilterTypeDef myCan_filter;  /* CAN过滤器结构体 */myCan_filter.FilterMode = CAN_FILTERMODE_IDMASK;  /* 工作模式:屏蔽位模式 */myCan_filter.FilterScale = CAN_FILTERSCALE_32BIT;  /* 32位位宽 *//* 过滤器接收所有报文,不进行筛选 */myCan_filter.FilterIdHigh = 0;myCan_filter.FilterIdLow = 0;myCan_filter.FilterMaskIdHigh = 0;myCan_filter.FilterMaskIdLow = 0;myCan_filter.FilterBank = 0;  /* 选择过滤器组 */myCan_filter.FilterFIFOAssignment = CAN_FilterFIFO0;  /* 过滤器关联FIFO 0 */myCan_filter.FilterActivation = CAN_FILTER_ENABLE;  /* 使能过滤器 */HAL_CAN_ConfigFilter(&hcan,&myCan_filter);  /* 配置CAN外设的接收过滤器 *//* USER CODE END CAN_Init 2 */}/* 使能CAN时钟和初始化相关引脚 */
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(canHandle->Instance==CAN1){/* USER CODE BEGIN CAN1_MspInit 0 *//* USER CODE END CAN1_MspInit 0 *//* CAN1 clock enable */__HAL_RCC_CAN1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**CAN GPIO ConfigurationPA11     ------> CAN_RXPA12     ------> CAN_TX*/GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN CAN1_MspInit 1 *//* USER CODE END CAN1_MspInit 1 */}
}void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{if(canHandle->Instance==CAN1){/* USER CODE BEGIN CAN1_MspDeInit 0 *//* USER CODE END CAN1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_CAN1_CLK_DISABLE();/**CAN GPIO ConfigurationPA11     ------> CAN_RXPA12     ------> CAN_TX*/HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);/* USER CODE BEGIN CAN1_MspDeInit 1 *//* USER CODE END CAN1_MspDeInit 1 */}
}/* USER CODE BEGIN 1 *//* 发送消息数据函数 */
uint8_t can_send_message(uint32_t id,uint8_t *buf,uint8_t len)
{uint32_t tx_mail;  /* 数据发送邮箱 */myCan_tx.RTR = CAN_RTR_DATA;  /* 帧类型为数据帧 */myCan_tx.IDE = CAN_ID_EXT;  /* 帧格式为扩展数据帧 */myCan_tx.ExtId = id;  /* 扩展数据帧ID */myCan_tx.DLC = len;  /* 数据长度 *//*如果三个发送邮箱都阻塞了就一直等待直到其中某个邮箱空闲*/while((HAL_CAN_GetTxMailboxesFreeLevel(&hcan) == 0)){ printf("没有邮箱空闲\r\n");HAL_Delay(1000);}/* 哪个邮箱空闲,就选哪个邮箱作为发送邮箱 */if ((hcan.Instance->TSR & CAN_TSR_TME0) != 0U){tx_mail = CAN_TX_MAILBOX0;} else if ((hcan.Instance->TSR & CAN_TSR_TME1) != 0U) {tx_mail = CAN_TX_MAILBOX1;} else if ((hcan.Instance->TSR & CAN_TSR_TME2) != RESET) {tx_mail = CAN_TX_MAILBOX2;}/* 从buf中发送消息 */if(HAL_CAN_AddTxMessage(&hcan,&myCan_tx,buf,&tx_mail) != HAL_OK){return 1;}  /* 等待数据发送完毕:当发送邮箱的3个邮箱都为空时,返回值为3 */while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 3);printf("邮箱为空\r\n");return 0;
}/*接收数据函数*/
uint8_t can_receive_message(uint8_t *buf)
{/* 判断接收FIFO 0是否为空 */if(HAL_CAN_GetRxFifoFillLevel(&hcan,CAN_RX_FIFO0) == 0){return 0;  /*为空返回0*/} HAL_CAN_GetRxMessage(&hcan,CAN_RX_FIFO0,&myCan_rx,buf);  /* 接收消息到buf中 */return myCan_rx.DLC;  /*不为空返回数据长度*/
}/* USER CODE END 1 */

can.h文件编写

/* USER CODE BEGIN Header */
/********************************************************************************* @file    can.h* @brief   This file contains all the function prototypes for*          the can.c file******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __CAN_H__
#define __CAN_H__#ifdef __cplusplus
extern "C" {
#endif/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes *//* USER CODE END Includes */extern CAN_HandleTypeDef hcan;/* USER CODE BEGIN Private defines *//* USER CODE END Private defines */void MX_CAN_Init(void);/* USER CODE BEGIN Prototypes *//* 发送消息数据函数 */
uint8_t can_send_message(uint32_t id,uint8_t *buf,uint8_t len);/*接收数据函数*/
uint8_t can_receive_message(uint8_t *buf);/* USER CODE END Prototypes */#ifdef __cplusplus
}
#endif#endif /* __CAN_H__ */

main.c文件编写

  1. 启动CAN外设

  1. 发送数据

  1. 接收数据

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "can.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */#include <stdio.h>/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */uint8_t rcvlen = 0;
uint8_t rcvbuf[8] = {0};
uint8_t can = 0;
uint8_t sendbuf[8] = "zhu_tou";  /* 重写stdio.h文件中的prinft()里的fputc()函数 */
int fputc(int my_data,FILE *p)
{unsigned char temp = my_data;/* 改写后,使用printf()函数会将数据通过串口一发送出去 */HAL_UART_Transmit(&huart1,&temp,1,0xffff);  /* 0xfffff为最大超时时间 */return my_data;
}/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
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_CAN_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */printf("haozige\r\n");  /* 确认串口是否启动 */HAL_CAN_Start(&hcan);  /* 启动CAN外设 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){  /* 当按键被按下*/HAL_Delay(50);  //按键防抖if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){//HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);if(can_send_message(0x12345678,sendbuf,8)){printf("发送失败\r\n");}  /* CAN发送数据,一次发送8字节 */}}rcvlen = can_receive_message(rcvbuf);  /* CAN接收数据 */if(rcvlen){printf("%s\r\n",rcvbuf);  /* 把接收到的数据打印到串口上 */}HAL_Delay(100);}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

STM32之bxCAN相关推荐

  1. python脚本计算STM32的bxCAN的波特率

    CAN波特率的计算公式: baudRate = pClk / (brp[9:0]+1)/(1+(ts1[3:0]+1)+(ts2[2:0]+1)) 采样点 = (1+ts1[3:0]+1) / (1+ ...

  2. STM32 CAN过滤器

    目录 1 前言 2 几个重要的概念 2.1 为什么要过滤器? 2.2 两种过滤模式(列表模式与掩码模式) 2.3 验证码与屏蔽码 2.4 列表模式与掩码模式的对比 2.5 标准CAN ID与扩展CAN ...

  3. 基于STM32的CAN总线通信学习笔记

    转自:https://blog.csdn.net/ludaoyi88/article/details/53350077 基于STM32的CAN总线通信学习笔记 本文主要简单介绍CAN总线的相关概念,以 ...

  4. STM32必备知识点(面试和工作用的到)

    STM32必备知识点(面试和工作用的到) 文章目录 STM32必备知识点(面试和工作用的到) 前言 嵌入式C基础 一.位操作 1. 不改变其他位的值的状况下,对某几个位进行设值 2.移位操作提高代码的 ...

  5. STM32 CAN过滤器配置详解

    1 前言 STM32F10X的bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B. 在CAN协议里,报文的标识符不代表节点的地址,而是和报文的 ...

  6. STM32的CAN开发注意事项整理收集

    一.STM32的bxCAN控制器 bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B.它的设计目标是,以最小的CPU负荷来高效处理大量收到的报 ...

  7. CAN通信稳定性开发分析

    本篇文章从嵌入式工程师的角度,借鉴了一些成熟的通信概念与架构,从包括硬件,软件各个角度分析嵌入式程序如何实现稳定CAN通信的难点以及方案,将相关的难点分别进行梳理,读者在完整读完这篇文章之后能够对实现 ...

  8. CAN通信详解(全)

    本章我们将向大家介绍如何使用STM32自带的CAN控制器来实现两个开发板之间的CAN通讯,并将结果显示在TFTLCD模块上.本章分为如下几个部分: 30.1 CAN简介 30.2 硬件设计 30.3 ...

  9. STM32--CAN ID过滤器分析

    1 前言 在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的内容相关的.因此,发送者以广播的形式把报文发送给所有的接收者.节点在接收报文时,根据标识符(CAN ID)的值决定软件是否需要该报文 ...

最新文章

  1. 机器学习数据不平衡不均衡处理之SMOTE算法实现
  2. (传送门)linux命令总结dd命令详解
  3. 2014.11 总结
  4. Apache Flink Meetup · 上海站,超强数据湖干货等你!
  5. OC系列foundation Kit基础-NSString
  6. js生成验证码并且验证
  7. RedHat6.2 x86手动配置LNMP环境
  8. Linux的tomcat日志分割,linux下tomcat日志分割
  9. IE10-浏览器实现placeholder效果
  10. 华为Java开发编程军规,谁违反谁走
  11. NOIP模拟题——tractor
  12. C语言printf函数格式化打印之长整型
  13. MySQL入门笔记整理
  14. 魔性计时器html6,最近抖音很火的6首BGM,太有魔性了!
  15. Electron 自定义托盘实战——桌面计算器
  16. EUI-64生成IPv6地址
  17. golang获取时间所在周的起止时间
  18. 【AT91SAM7X-EK开发板】系统时钟的配置
  19. Unity使用C#网络下载用户头像
  20. MyEclipse安装Vue

热门文章

  1. E Enigmatic Partition 2020牛客暑期多校训练营(第八场)
  2. 自锁时间电路plc_PLC中有自锁功能,请问自锁使什么意思?
  3. 浏览器的缓存机制 优点 缺点 协商缓存和强缓存 浏览器缓存过程 如何判断强缓存是否过期
  4. python调用固高GSN运动控制卡dll
  5. Linux 您未安装Flash Player 或者版本过低
  6. 使用shiro的会话管理和redis缓存管理来构建登录模块spring+struts+hibernate(SSH)
  7. 统计源期刊《国际医学放射学杂志》
  8. 让电脑注销的c语言程序,C语言实现系统关机注销功能
  9. 【论文阅读】SML:标准最大logits
  10. 微信小程序敏感词过滤