文章目录
- STM32-CAN
- 1. CAN基础知识
- 2. CAN协议
- 2.1. CAN协议与ISO/OSI基本参照模型的关系
- 2.2. CAN协议及标准规格
- 2.3. CAN协议
- 2.3.1. 帧的种类
- 2.3.2. 数据帧
- 2.3.3. 遥控帧
- 2.3.4. 错误帧
- 2.3.5. 过载帧
- 2.3.6. 间隔帧
- 2.4. 优先级的决定
- 2.5. 位填充
- 2.6. 错误的种类
- 2.7. 位时序
- 3. CAN控制器
- 4. CAN相关寄存器
- 5. CAN相关HAL库驱动
- 6. 代码实现
STM32-CAN
1. CAN基础知识
-
CAN简介:
Controller Area Network
,是ISO国际标准化的串行通信协议。在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。为适应减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”的需要,1986年德国电气商博世公司开发出面向汽车的CAN通信协议。此后,CAN通过ISO11898及ISO11519进行了标准。现在,CAN的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面。现场总线是当今自动化领域技术发展的热点之一,被誉为自动化领域的计算机局域网。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有力的技术支持。 -
CAN协议的特点:
- 多主控制: 在总线空闲时,所有单元都可以发送消息,而两个以上的单元同时发送消息时,根据标识符(ID)决定优先级。ID并不是表示发送的目的地址,而是表示访问总线的消息的优先级。两个以上的单元同时发送消息时,对各消息ID的每个位进行逐个仲裁比较。仲裁获胜的单元可继续发送消息,仲裁失利的单元则立即停止发送而进行接收工作。
- 系统的柔软性: 与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。
- 通信速度快,距离远: 最高1Mbps(距离小于40m),最远可达10km(速率低于5Kbps)。
- 具有错误检测、错误通知和错误恢复功能: 所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其它所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断的重新发送此消息直到成功发送为止(错误恢复功能)。
- 故障封闭功能: CAN可以判断出错误的类型是总线上暂时的数据错误(如外部噪声)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续错误时,可将引起此故障的单元从总线上隔离出去。
- 连接节点多: CAN总线是可以同时连接多个单元的总线,可连接的单元总数理论上是没有限制的,但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加。
-
CAN应用场景:
CAN总线协议已广泛应用在汽车电子、工业自动化、船舶、医疗设备、工业设备等方面。
车载网络构想:
-
CAN标准:
ISO11898
标准(高速 CAN)和ISO11519-2
标准(低速 CAN)总线拓扑图:
-
CAN物理层:
-
CAN收发器芯片:
-
CAN错误状态:
-
主动错误状态: 主动错误状态是可以正常参加总线通信的状态。处于主动错误状态的单元检测出错误时,输出主动错误标志。
-
被动错误状态: 被动错误状态是易引起错误的状态。处于被动错误状态的单元虽能参加总线通信,但为不妨碍其它单元通信,接收时不能积极地发送错误通知。处于被动错误状态的单元即使检测出错误,而其它处于主动错误状态的单元如果没发现错误,整个总线也被认为是没有错误的。处于被动错误状态的单元检测出错误时,输出被动错误标志。另外,处于被动错误状态的单元在发送结束后不能马上再次开始发送。在开始下次发送前,在间隔帧期间内必须插入“延迟传送”(8个位的隐性位)。
-
总线关闭态: 总线关闭态是不能参加总线上通信的状态。信息的接收和发送均被禁止。
这些状态依靠发送错误计数和接收错误计数来管理,根据计数值决定进入何种状态。
- 错误计数值:
-
2. CAN协议
2.1. CAN协议与ISO/OSI基本参照模型的关系
-
CAN协议与ISO/OSI基本参照模型:
数据链路层分为MAC子层和LLC子层,MAC子层是CAN协议的核心部分。数据链路层的功能是将物理层收到的信号组织成有意义的消息,并提供传送错误控制等传输控制的流程。具体地说,就是消息的帧化、仲裁、应答、错误的检测或报告。数据链路层的功能通常在CAN控制器的硬件中执行。
在物理层定义了信号实际的发送方式、位时序、位的编码方式及同步的步骤。但具体地说,信号电平、通信速度、采样点、驱动器和总线的电气特性、连接器的形态等均未定义。这些必须由用户根据系统需求自行确定。
2.2. CAN协议及标准规格
2.3. CAN协议
2.3.1. 帧的种类
通信的实现依靠五种帧:数据帧、遥控帧、错误帧、过载帧、帧间隔。
另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有 11 个位的标识符(ID),扩展格式有 29 个位的 ID。
帧类型 | 帧作用 |
---|---|
数据帧(Data Frame) | 用于发送单元向接收单元传输数据的帧 |
遥控帧(Remote Frame) | 用于接收单元向具有相同ID的发送单元请求数据的帧 |
错误帧(Error Frame) | 用于当检测出错误时向其他单元通知错误的帧 |
过载帧(Overload Frame) | 用于接收单元通知其尚未做好接收准备的帧 |
间隔帧(Inter Frame Space) | 用于将数据帧 及遥控帧与前面的帧分离开来的帧 |
2.3.2. 数据帧
-
数据帧的构成:
数据帧由7段构成:
帧起始:表示数据帧开始的段。
仲裁段:表示该帧优先级的段。
控制段:表示数据的字节数及保留位的段。
数据段:数据的内容,可发送 0~8 个字节的数据。
CRC段:检查帧的传输错误的段。
ACK段:表示确认正常接收的段。
帧结束:表示数据帧结束的段。
2.3.3. 遥控帧
-
遥控帧的构成:
遥控帧由6段组成:
帧起始: 表示帧开始的段。
仲裁段: 表示该帧优先级的段。
控制段: 表示数据的字节数及保留位的段。
CRC段: 表示帧传输错误的段。
ACK段: 表示确认正常接收的段。
帧结束: 表示遥控帧结束的段。
-
数据帧与遥控帧的不同:
- 遥控帧的
RTR
位为隐性位,没有数据段 - 没有数据段的数据帧和遥控帧可通过RTR位区别
- 遥控帧的
-
遥控帧的数据长度码以所请求数据帧的数据长度码表示
-
没有数据帧的数据段可用于各单元的定期连接确认/应答、或仲裁段本身带有实质性信息的情况
2.3.4. 错误帧
用于在接收和发送消息时检测出错误通知错误的帧。错误帧由错误标志和错误界定符构成。
- 错误标志:
- 主动错误标志(处于主动错误状态的单元检测出错误时输出的错误标志):6个位的显性位
- 被动错误标志(处于被动错误状态的单元检测出错误时输出的错误标志):6个位的隐性位
- 错误界定符:
- 错误界定符由8个位的隐性位构成。
2.3.5. 过载帧
用于接收单元通知其尚未完成接收准备的帧。过载帧由过载标志和过载界定符构成。
- 过载标志:
- 6个位的显性位。
- 过载标志的构成与主动错误标志的构成相同。
- 过载界定符:
- 8个位的隐性位。
- 过载界定符由8个位的隐性位构成。
2.3.6. 间隔帧
帧间隔是用于分隔数据帧和遥控帧的帧。数据帧和遥控帧可通过帧间隔将本帧与前面的任何帧分开。
过载帧和错误帧不能插入帧间隔。
- 间隔:
- 3个位的隐性位。
- 延迟传送:
- 8个位的隐性位。
- 只在处于被动错误状态的单元刚发送一个消息后的帧间隔中包含的段。
- 总线空闲:
- 隐性电平,无长度限制。
- 本状态下,可视为总线空闲,要发送的单元可开始访问总线。
2.4. 优先级的决定
在总线空闲态,最先开始发送消息的单元获得发送权。
多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。
-
数据帧和遥控帧的优先级:
具有相同ID的数据帧和遥控帧在总线上竞争时,仲裁段的最后一位(
RTR
)为显性位的数据帧具有优先权,可继续发送。
-
标准格式和拓展格式的优先级:
标准格式ID与具有相同ID的遥控帧或者拓展格式的数据帧在总线上竞争时,标准格式的
RTR
位为显性的具有优先权,可继续发送。
2.5. 位填充
位填充是为防止突发错误而设定的功能。当同样的电平持续5位时则添加一个位的反型数据。
-
发送单元的工作:
在发送数据帧和遥控帧时,SOF~CRC段间的数据,相同电平如果持续5位,在下一位(第6个位)则要插入1位与前5位反型的电平。
-
接收单元的工作:
在接收数据帧和遥控帧时,SOF~CRC段间的数据,相同电平如果持续5位,需要删除下一位(第6个位)再接收。如果这个第6个位的电平与前5位相同,将被视为错误并发送错误帧。
2.6. 错误的种类
一共有5种错误:位错误、填充错误、CRC错误、格式错误、ACK错误。
- 错误帧的输出位置:
2.7. 位时序
由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位可分为4段。
- 同步段(SS)
- 传播时间段(PTS)
- 相位缓冲段1(PBS1)
- 相位缓冲段2(PBS2)
这些段又由可称为Time Quantum
(Tq) 的最小时间单位构成。
1位分为4个段,每个段又由若干个Tq构成,这称为位时序。
1个位的构成:
-
CAN总线上取的同步的方法:
CAN协议的通信方法为NRZ(
Non-Return Zero
)方法。各个位的开头或者结尾都没有附件同步信号。发送单元以与位时序同步的方式开始发送数据。接收单元根据总线上电平的变化进行同步接收工作。但是,发送单元和接收单元存在的时钟频率误差及传输路径上的相位延迟会引起同步偏差。因此,接收单元通过硬件同步或再同步的方式调整时序进行接收。-
硬件同步:
-
再同步:
-
调整同步的规则:
- 1个位中只进行一次同步调整。
- 只有当上次采样点的总线值和边沿后的总线值不同时,该边沿才能用于调整同步。
- 在总线空闲且存在隐性电平到显性电平的边沿时,则一定要进行硬件同步。
- 在总线非空闲时检测到的隐性电平到显性电平的边沿如果满足条件(1)和(2),将进行再同步。但还要满足下面条件。
- 发送单元观测到自身输出的显性电平有延迟时不进行再同步。
- 发送单元在帧起始到仲裁段有多个单元同时发送的情况下,对延迟边沿不进行再同步。
-
3. CAN控制器
-
bxCAN介绍:
-
CAN控制器工作模式:
CAN控制器的工作模式有三种:初始化模式、正常模式和睡眠模式。
-
CAN控制器测试模式:
CAN控制器的测试模式有三种:静默模式、环回模式和环回静默模式。
-
CAN控制器框图:
发送处理流程:
接收处理流程:
接收过滤器:
- CAN控制器位时序:
4. CAN相关寄存器
寄存器 | 名称 | 作用 |
---|---|---|
CAN_MCR | CAN主控制寄存器 | 主要负责CAN工作模式的配置 |
CAN_BTR | 位时序寄存器 | 用来设置分频/TBS1/TBS2/TSWJ等参数,设置测试模式 |
CAN_(T/R)IxR | 标识符寄存器 | 存放(待发送/接收)的报文ID、扩展ID、IDE位及RTR位 |
CAN_(T/R)DTxR | 数据长度和时间戳寄存器 | 存放(待发送/接收)报文的DLC段 |
CAN_(T/R)DLxR | 低位数据寄存器 | 存放 (待发送/接收)报文数据段的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功能不同 |
5. CAN相关HAL库驱动
-
CAN主控制寄存器 (
CAN_MCR
)
-
CAN位时序寄存器 (
CAN_BTR
)
-
CAN发送/接收邮箱标识符寄存器 (
CAN_(T/R)IxR
)
-
CAN发送邮箱数据长度和时间截寄存器 (
CAN_TDTxR
)
-
CAN发送邮箱低字节数据寄存器 (
CAN_TDLxR
)
-
CAN过滤器模式寄存器 (
CAN_FM1R
)
-
CAN过滤器位宽寄存器 (
CAN_FS1R
)
-
CAN过滤器FIFO关联寄存器 (
CAN_FFA1R
)
-
CAN过滤器激活寄存器 (
CAN_FA1R
)
-
CAN过滤器组x寄存器 (
CAN_FxR(1/2)
)
6. 代码实现
-
功能: 通过 KEY_UP 按键选择 CAN 的工作模式(正常模式/环回模式),然后通过 KEY0 控制数据发送,接着查询是否有数据接收到,假如接收到数据,就将接收到的数据显示在 LCD 模块上。如果是环回模式,我们不需要 2 个开发板。如果是正常模式,我们就需要 2 个战舰开发板,并且将他们的 CAN 接口对接起来,然后一个开发板发送数据,另外一个开发板将接收到的数据显示在 LCD 模块上。
-
CAN初始化函数
void can_init(void) {CAN_FilterTypeDef can_filterconfig = {0}; // 初始化CAN过滤器配置结构体,将所有成员初始化为0g_can1_handler.Instance = CAN1; // 选择CAN1作为CAN控制器实例g_can1_handler.Init.Mode = CAN_MODE_LOOPBACK; // 设置CAN工作模式为环回模式(自发自收),用于调试g_can1_handler.Init.Prescaler = 4; // 设置CAN分频系数为4,以降低输入时钟频率g_can1_handler.Init.TimeSeg1 = CAN_BS1_9TQ; // 设置时间段1为9个时间量(TQ)g_can1_handler.Init.TimeSeg2 = CAN_BS2_8TQ; // 设置时间段2为8个时间量(TQ)g_can1_handler.Init.SyncJumpWidth = CAN_SJW_1TQ; // 设置重新同步跳跃宽度为1个时间量(TQ)g_can1_handler.Init.AutoBusOff = DISABLE; // 禁止自动离线管理g_can1_handler.Init.AutoRetransmission = DISABLE; // 禁止自动重发消息g_can1_handler.Init.AutoWakeUp = DISABLE; // 禁止自动唤醒功能g_can1_handler.Init.ReceiveFifoLocked = DISABLE; // 禁止接收FIFO锁定g_can1_handler.Init.TimeTriggeredMode = DISABLE; // 禁止时间触发通信模式g_can1_handler.Init.TransmitFifoPriority = DISABLE; // 禁止发送FIFO优先级HAL_CAN_Init(&g_can1_handler); // 初始化CAN控制器can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK; // 设置CAN过滤器模式为ID掩码模式can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT; // 设置CAN过滤器规模为32位can_filterconfig.FilterIdHigh = 0; // 设置过滤器ID高位为0can_filterconfig.FilterIdLow = 0; // 设置过滤器ID低位为0can_filterconfig.FilterMaskIdHigh = 0; // 设置过滤器掩码ID高位为0can_filterconfig.FilterMaskIdLow = 0; // 设置过滤器掩码ID低位为0can_filterconfig.FilterBank = 0; // 设置过滤器组为0can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0; // 设置过滤器FIFO分配为FIFO 0can_filterconfig.FilterActivation = CAN_FILTER_ENABLE; // 启用过滤器can_filterconfig.SlaveStartFilterBank = 14; // 设置从过滤器组起始号为14HAL_CAN_ConfigFilter(&g_can1_handler, &can_filterconfig); // 配置CAN过滤器HAL_CAN_Start(&g_can1_handler); // 启动CAN控制器 }
1. 配置CAN控制器实例
g_can1_handler.Instance = CAN1; g_can1_handler.Init.Mode = CAN_MODE_LOOPBACK; // 工作模式设置:环回模式,自发自收
g_can1_handler.Instance = CAN1;
:选择CAN1作为CAN控制器实例。g_can1_handler.Init.Mode = CAN_MODE_LOOPBACK;
:将CAN设置为环回模式,这意味着控制器会自发自收,用于调试。
2. 配置CAN时间和同步参数
g_can1_handler.Init.Prescaler = 4; // 分频系数 g_can1_handler.Init.TimeSeg1 = CAN_BS1_9TQ; // 时间段1 g_can1_handler.Init.TimeSeg2 = CAN_BS2_8TQ; // 时间段2 g_can1_handler.Init.SyncJumpWidth = CAN_SJW_1TQ; // 重新同步跳跃宽度
g_can1_handler.Init.Prescaler = 4;
:设置分频系数为4,以降低输入时钟频率。g_can1_handler.Init.TimeSeg1 = CAN_BS1_9TQ;
:设置时间段1为9个时间量(TQ)。g_can1_handler.Init.TimeSeg2 = CAN_BS2_8TQ;
:设置时间段2为8个时间量(TQ)。g_can1_handler.Init.SyncJumpWidth = CAN_SJW_1TQ;
: 设置同步跳跃宽度为1个时间量(TQ)。
3. 配置CAN功能
g_can1_handler.Init.AutoBusOff = DISABLE; // 禁止自动离线管理 g_can1_handler.Init.AutoRetransmission = DISABLE; // 禁止自动重发 g_can1_handler.Init.AutoWakeUp = DISABLE; // 禁止自动唤醒 g_can1_handler.Init.ReceiveFifoLocked = DISABLE; // 禁止接收FIFO锁定 g_can1_handler.Init.TimeTriggeredMode = DISABLE; // 禁止时间触发通信模式 g_can1_handler.Init.TransmitFifoPriority = DISABLE;// 禁止发送FIFO优先级
g_can1_handler.Init.AutoBusOff = DISABLE;
:禁止自动离线管理。g_can1_handler.Init.AutoRetransmission = DISABLE;
:禁止自动重发消息。g_can1_handler.Init.AutoWakeUp = DISABLE;
:禁止自动唤醒功能。g_can1_handler.Init.ReceiveFifoLocked = DISABLE;
:禁止接收FIFO锁定。g_can1_handler.Init.TimeTriggeredMode = DISABLE;
:禁止时间触发通信模式。g_can1_handler.Init.TransmitFifoPriority = DISABLE;
:禁止发送FIFO优先级。
4. 初始化CAN控制器
HAL_CAN_Init(&g_can1_handler); //调用HAL_CAN_Init函数来初始化CAN控制器
5. 配置CAN过滤器
can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK; //设置CAN过滤器模式为ID掩码模式 can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置CAN过滤器规模为32位can_filterconfig.FilterIdHigh = 0; // 设置过滤器ID高位为0 can_filterconfig.FilterIdLow = 0; // 设置过滤器ID低位为0 can_filterconfig.FilterMaskIdHigh = 0; // 设置过滤器掩码ID高位为0 can_filterconfig.FilterMaskIdLow = 0; // 设置过滤器掩码ID低位为0can_filterconfig.FilterBank = 0; // 设置过滤器组为0 can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0;//设置过滤器FIFO分配为FIFO 0 can_filterconfig.FilterActivation = CAN_FILTER_ENABLE; // 启用过滤器 can_filterconfig.SlaveStartFilterBank = 14; // 设置从过滤器组起始号为14 HAL_CAN_ConfigFilter(&g_can1_handler, &can_filterconfig); // 配置CAN过滤器
can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
:设置过滤器模式为ID掩码模式。can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
:设置过滤器规模为32位。can_filterconfig.FilterIdHigh = 0;
和can_filterconfig.FilterIdLow = 0;
:设置过滤器ID高位和低位为0。can_filterconfig.FilterMaskIdHigh = 0;
和can_filterconfig.FilterMaskIdLow = 0;
:设置过滤器掩码ID高位和低位为0。can_filterconfig.FilterBank = 0;
:设置过滤器组为0。can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0;
:设置过滤器FIFO分配为FIFO 0。can_filterconfig.FilterActivation = CAN_FILTER_ENABLE;
:启用过滤器。can_filterconfig.SlaveStartFilterBank = 14;
:设置从过滤器组起始号为14。
然后调用
HAL_CAN_ConfigFilter
函数来配置过滤器。6. 启动CAN控制器
HAL_CAN_Start(&g_can1_handler); //调用HAL_CAN_Start函数来启动CAN控制器
具体操作寄存器:
-
MSP回调函数
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan) {CAN_TX_GPIO_CLK_ENABLE(); // 启用CAN发送引脚的GPIO时钟CAN_RX_GPIO_CLK_ENABLE(); // 启用CAN接收引脚的GPIO时钟__HAL_RCC_CAN1_CLK_ENABLE(); // 启用CAN1控制器的时钟GPIO_InitTypeDef gpio_init_struct; // 定义一个GPIO初始化结构体// 配置CAN发送引脚gpio_init_struct.Pin = CAN_TX_GPIO_PIN; // 设置引脚号gpio_init_struct.Mode = GPIO_MODE_AF_PP; // 设置为复用推挽输出模式gpio_init_struct.Pull = GPIO_PULLUP; // 上拉电阻gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(CAN_TX_GPIO_PORT, &gpio_init_struct); // 初始化CAN发送引脚的GPIO配置// 配置CAN接收引脚gpio_init_struct.Pin = CAN_RX_GPIO_PIN; // 设置引脚号gpio_init_struct.Mode = GPIO_MODE_AF_INPUT; // 设置为复用输入模式HAL_GPIO_Init(CAN_RX_GPIO_PORT, &gpio_init_struct); // 初始化CAN接收引脚的GPIO配置 }
1. 配置CAN发送引脚(TX):
gpio_init_struct.Pin = CAN_TX_GPIO_PIN; // 设置引脚号 gpio_init_struct.Mode = GPIO_MODE_AF_PP; // 设置为复用推挽输出模式 gpio_init_struct.Pull = GPIO_PULLUP; // 上拉电阻 gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速 HAL_GPIO_Init(CAN_TX_GPIO_PORT, &gpio_init_struct); // 初始化CAN发送引脚的GPIO配置
gpio_init_struct.Pin = CAN_TX_GPIO_PIN
:指定要配置的GPIO引脚为CAN发送引脚。gpio_init_struct.Mode = GPIO_MODE_AF_PP
:将引脚模式设置为复用推挽输出模式,用于CAN发送。gpio_init_struct.Pull = GPIO_PULLUP
:使能上拉电阻,保持引脚在空闲状态时为高电平。gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH
:将引脚速度设置为高频率,以支持高速数据传输。HAL_GPIO_Init(CAN_TX_GPIO_PORT, &gpio_init_struct)
:使用以上配置初始化CAN发送引脚的GPIO。
2. 配置CAN接收引脚(RX):
gpio_init_struct.Pin = CAN_RX_GPIO_PIN; // 设置引脚号 gpio_init_struct.Mode = GPIO_MODE_AF_INPUT; // 设置为复用输入模式 HAL_GPIO_Init(CAN_RX_GPIO_PORT, &gpio_init_struct); // 初始化CAN接收引脚的GPIO配置
gpio_init_struct.Pin = CAN_RX_GPIO_PIN
:指定要配置的GPIO引脚为CAN接收引脚。gpio_init_struct.Mode = GPIO_MODE_AF_INPUT
:将引脚模式设置为复用输入模式,用于CAN接收。
-
发送消息数据函数
void can_send_message(uint32_t id, uint8_t *buf, uint8_t len) {uint32_t tx_mail = CAN_TX_MAILBOX0; // 定义并初始化发送邮箱为邮箱0g_can1_txheader.ExtId = id; // 设置扩展ID为传入的id参数g_can1_txheader.DLC = len; // 设置数据长度代码(DLC)为传入的len参数g_can1_txheader.IDE = CAN_ID_EXT; // 设置标识符扩展(IDE)为扩展帧g_can1_txheader.RTR = CAN_RTR_DATA; // 设置远程传输请求(RTR)为数据帧HAL_CAN_AddTxMessage(&g_can1_handler, &g_can1_txheader, buf, &tx_mail); // 通过CAN控制器发送消息while(HAL_CAN_GetTxMailboxesFreeLevel(&g_can1_handler) != 3); // 等待所有发送邮箱为空 }
1. 定义并初始化发送邮箱:
uint32_t tx_mail = CAN_TX_MAILBOX0; // 定义并初始化发送邮箱为邮箱0
- 定义一个32位无符号整数
tx_mail
并将其初始化为CAN_TX_MAILBOX0
,表示使用第一个发送邮箱。
2. 设置CAN消息头:
g_can1_txheader.ExtId = id; // 设置扩展ID为传入的id参数 g_can1_txheader.DLC = len; // 设置数据长度代码(DLC)为传入的len参数 g_can1_txheader.IDE = CAN_ID_EXT; // 设置标识符扩展(IDE)为扩展帧 g_can1_txheader.RTR = CAN_RTR_DATA; // 设置远程传输请求(RTR)为数据帧
g_can1_txheader.ExtId = id
:将传入的id
参数设置为CAN消息的扩展ID。g_can1_txheader.DLC = len
:将传入的len
参数设置为CAN消息的数据长度代码(DLC)。g_can1_txheader.IDE = CAN_ID_EXT
:将CAN消息的标识符扩展(IDE)设置为扩展帧,表示使用29位ID。g_can1_txheader.RTR = CAN_RTR_DATA
:将CAN消息的远程传输请求(RTR)设置为数据帧。
3. 发送CAN消息:
HAL_CAN_AddTxMessage(&g_can1_handler, &g_can1_txheader, buf, &tx_mail); // 通过CAN控制器发送消息
- 调用
HAL_CAN_AddTxMessage
函数,通过CAN控制器发送消息:&g_can1_handler
:指向CAN控制器的句柄。&g_can1_txheader
:指向消息头的指针。buf
:指向要发送的数据缓冲区。&tx_mail
:指向发送邮箱的指针。
4. 等待发送完成:
while(HAL_CAN_GetTxMailboxesFreeLevel(&g_can1_handler) != 3); // 等待所有发送邮箱为空
- 使用
while
循环等待所有发送邮箱为空:HAL_CAN_GetTxMailboxesFreeLevel(&g_can1_handler)
:获取当前空闲的发送邮箱数量。!= 3
:如果空闲的发送邮箱数量不等于3,则继续等待。这表示所有发送邮箱(CAN_TX_MAILBOX0
,CAN_TX_MAILBOX1
,CAN_TX_MAILBOX2
)都空闲。
总结:
这段代码主要通过配置CAN消息的头部(包括ID、长度、帧类型等)并调用相应的库函数发送消息,最后等待所有发送邮箱都空闲,以确保消息成功发送。
- 定义一个32位无符号整数
-
接收数据函数
uint8_t can_receive_message(uint8_t *buf) {if(HAL_CAN_GetRxFifoFillLevel(&g_can1_handler, CAN_RX_FIFO0) == 0){return 0;}HAL_CAN_GetRxMessage(&g_can1_handler, CAN_RX_FIFO0, &g_can1_rxheader, buf);return g_can1_rxheader.DLC; }
1. 检查接收FIFO队列是否有消息:
if(HAL_CAN_GetRxFifoFillLevel(&g_can1_handler, CAN_RX_FIFO0) == 0) // 检查接收FIFO0队列是否为空 {return 0; // 如果接收FIFO0队列为空,返回0,表示没有消息 }
HAL_CAN_GetRxFifoFillLevel(&g_can1_handler, CAN_RX_FIFO0)
:调用库函数检查接收FIFO0队列的填充级别。接收FIFO0队列为空,即没有消息。&g_can1_handler
:指向CAN控制器的句柄。CAN_RX_FIFO0
:指定接收FIFO0队列。
2. 从接收FIFO0队列中获取消息:
HAL_CAN_GetRxMessage(&g_can1_handler, CAN_RX_FIFO0, &g_can1_rxheader, buf); // 从接收FIFO0队列中获取消息,并存储在buf中
HAL_CAN_GetRxMessage(&g_can1_handler, CAN_RX_FIFO0, &g_can1_rxheader, buf)
:调用库函数从接收FIFO0队列中获取消息。&g_can1_handler
:指向CAN控制器的句柄。CAN_RX_FIFO0
:指定接收FIFO0队列。&g_can1_rxheader
:指向接收消息头部的指针。buf
:指向接收消息数据缓冲区的指针。
3. 返回消息的长度代码(DLC):
return g_can1_rxheader.DLC; // 返回消息的长度代码(DLC)
g_can1_rxheader.DLC
:获取接收消息头部的长度代码(DLC)。
总结:
这段代码用于接收通过CAN(控制器局域网络)发送的消息,并将其存储在指定的缓冲区中。它首先检查接收FIFO(先入先出)队列中是否有消息,如果有消息,函数从FIFO队列中提取消息,并返回消息的长度代码(DLC)。
声明:资料来源(战舰STM32F103ZET6开发板资源包)
- Cortex-M3权威指南(中文).pdf
- STM32F10xxx参考手册_V10(中文版).pdf
- STM32F103 战舰开发指南V1.3.pdf
- STM32F103ZET6(中文版).pdf
- 战舰V4 硬件参考手册_V1.0.pdf