STM32之bxCAN
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基本使用步骤
CAN外设初始化MX_CAN_Init()
配置波特率、CAN功能、接收过滤器等等。
使能CAN时钟和初始化CAN相关引脚HAL_CAN_MspInit()
使用STM32CubeMX会自动配置。
CAN数据的接收和发送
CAN数据的接收:HAL_CAN_AddTxMessage()
获取发送邮箱的状态:HAL_CAN_GetTxMailboxesFreeLevel()
CAN数据的发送:HAL_CAN_GetRxMessage()
获取接收FIFO的状态:HAL_CAN_GetRxFifoFillLevel()
使用CAN的中断(可选)
使能CAN的相关中断:__HAL_CAN_ENABLE_IT()
配置NVIC
编写中断服务函数
CAN实验1(使用回环模式实现自发自收)
通过CAN总线将数据自发自收,每按下一次按键就发送一次数据,并将接收的数据显示在串口上。(接收过滤器接收所有报文,不进行筛选)
使用STM32CubeMX创建工程
配置SYS
配置RCC
配置GPIO
PA0(按键)配置成输入引脚。
配置串口信息(UART1)
配置CAN
配置工程名称、工程路径
选择固件库
生成工程
使用MicroLIB库
can.c文件编写
CAN外设初始化:主要配置波特率、CAN功能、接收过滤器
CAN发送数据函数
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文件编写
启动CAN外设
发送数据
接收数据
/* 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相关推荐
- 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+ ...
- STM32 CAN过滤器
目录 1 前言 2 几个重要的概念 2.1 为什么要过滤器? 2.2 两种过滤模式(列表模式与掩码模式) 2.3 验证码与屏蔽码 2.4 列表模式与掩码模式的对比 2.5 标准CAN ID与扩展CAN ...
- 基于STM32的CAN总线通信学习笔记
转自:https://blog.csdn.net/ludaoyi88/article/details/53350077 基于STM32的CAN总线通信学习笔记 本文主要简单介绍CAN总线的相关概念,以 ...
- STM32必备知识点(面试和工作用的到)
STM32必备知识点(面试和工作用的到) 文章目录 STM32必备知识点(面试和工作用的到) 前言 嵌入式C基础 一.位操作 1. 不改变其他位的值的状况下,对某几个位进行设值 2.移位操作提高代码的 ...
- STM32 CAN过滤器配置详解
1 前言 STM32F10X的bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B. 在CAN协议里,报文的标识符不代表节点的地址,而是和报文的 ...
- STM32的CAN开发注意事项整理收集
一.STM32的bxCAN控制器 bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B.它的设计目标是,以最小的CPU负荷来高效处理大量收到的报 ...
- CAN通信稳定性开发分析
本篇文章从嵌入式工程师的角度,借鉴了一些成熟的通信概念与架构,从包括硬件,软件各个角度分析嵌入式程序如何实现稳定CAN通信的难点以及方案,将相关的难点分别进行梳理,读者在完整读完这篇文章之后能够对实现 ...
- CAN通信详解(全)
本章我们将向大家介绍如何使用STM32自带的CAN控制器来实现两个开发板之间的CAN通讯,并将结果显示在TFTLCD模块上.本章分为如下几个部分: 30.1 CAN简介 30.2 硬件设计 30.3 ...
- STM32--CAN ID过滤器分析
1 前言 在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的内容相关的.因此,发送者以广播的形式把报文发送给所有的接收者.节点在接收报文时,根据标识符(CAN ID)的值决定软件是否需要该报文 ...
最新文章
- 机器学习数据不平衡不均衡处理之SMOTE算法实现
- (传送门)linux命令总结dd命令详解
- 2014.11 总结
- Apache Flink Meetup · 上海站,超强数据湖干货等你!
- OC系列foundation Kit基础-NSString
- js生成验证码并且验证
- RedHat6.2 x86手动配置LNMP环境
- Linux的tomcat日志分割,linux下tomcat日志分割
- IE10-浏览器实现placeholder效果
- 华为Java开发编程军规,谁违反谁走
- NOIP模拟题——tractor
- C语言printf函数格式化打印之长整型
- MySQL入门笔记整理
- 魔性计时器html6,最近抖音很火的6首BGM,太有魔性了!
- Electron 自定义托盘实战——桌面计算器
- EUI-64生成IPv6地址
- golang获取时间所在周的起止时间
- 【AT91SAM7X-EK开发板】系统时钟的配置
- Unity使用C#网络下载用户头像
- MyEclipse安装Vue
热门文章
- E Enigmatic Partition 2020牛客暑期多校训练营(第八场)
- 自锁时间电路plc_PLC中有自锁功能,请问自锁使什么意思?
- 浏览器的缓存机制 优点 缺点 协商缓存和强缓存 浏览器缓存过程 如何判断强缓存是否过期
- python调用固高GSN运动控制卡dll
- Linux 您未安装Flash Player 或者版本过低
- 使用shiro的会话管理和redis缓存管理来构建登录模块spring+struts+hibernate(SSH)
- 统计源期刊《国际医学放射学杂志》
- 让电脑注销的c语言程序,C语言实现系统关机注销功能
- 【论文阅读】SML:标准最大logits
- 微信小程序敏感词过滤