UART
I2C
SPI
介绍
CAN(Controller Area Network)接口是一种常用于嵌入式系统中的通信接口,尤其在汽车和工业领域。它由Robert Bosch GmbH在1986年开发,旨在提供一种可靠、高效、灵活的车载通信网络。CAN总线的主要
特点包括:
- 多主通信:CAN总线上的所有节点都可以主动发送数据,没有固定的主节点。
- 消息优先级:通过消息的标识符(ID)来设定优先级,ID越低,优先级越高。
- 非破坏性仲裁:当多个节点同时尝试发送数据时,CAN总线使用非破坏性仲裁机制来确定哪个消息可以继续传输。
- 自动错误处理:CAN总线具有错误检测和容错机制,包括位错误检测、帧检验、循环冗余检验(CRC)和自动重传。
- 高噪声免疫性:由于其差分信号的特点,CAN总线对电磁干扰有较高的抵抗能力。
- 数据传输速率:CAN总线支持从1 kbps到1 Mbps不等的数据传输速率。
- 通信距离:在不使用中继器的情况下,CAN总线可以支持最长40米(在1 Mbps速率下)的通信距离。
- 低成本:与其他通信协议相比,CAN总线接口的硬件实现成本较低。
补充
两根通信线(CAN_H、CAN_L),线路少,无需共地
差分信号通信,抗干扰能力强
高速CAN(ISO11898):125k~1Mbps, <40m
低速CAN(ISO11519):10k~125kbps, <1km
异步,无需时钟线,通信速率由设备各自约定
半双工,可挂载多设备,多设备同时发送数据时通过仲裁判断先后顺序
11位/29位报文ID,用于区分消息功能,同时决定优先级
可配置1~8字节的有效载荷
可实现广播式和请求式两种传输方式
应答、CRC校验、位填充、位同步、错误处理等特性
CAN接口通常由CAN控制器和CAN收发器组成。
CAN控制器负责处理数据帧的封装、解析和错误处理等逻辑,而CAN收发器则负责将控制器发出的信号转换为适合在物理总线上传输的差分信号,或者将物理总线上的差分信号转换为控制器能够处理的信号。
硬件电路
CAN电平标准
CAN总线采用差分信号,即两线电压差(VCAN_H-VCAN_L)传输数据位
高速CAN规定:
电压差为0V时表示逻辑1(隐性电平)
电压差为2V时表示逻辑0(显性电平)
低速CAN规定:
电压差为-1.5V时表示逻辑1(隐性电平)
电压差为3V时表示逻辑0(显性电平)
帧类型
-
数据帧(Data Frame):
- 标准数据帧:使用11位标识符,用于传输数据。
- 扩展数据帧:使用29位标识符,提供更多的标识符可能性,也用于传输数据。
-
遥控帧(Remote Transmission Request, RTR):
- 标准遥控帧:使用11位标识符,请求发送具有特定标识符的数据帧。
- 扩展遥控帧:使用29位标识符,请求发送具有特定标识符的数据帧。
-
错误帧(Error Frame):
- 由任何检测到错误的节点发送,用于指示总线上发生了错误。
-
过载帧(Overload Frame):
- 用于在需要更多时间来处理接收到的数据帧时,延长下一个数据帧的发送。
-
帧间隔(Interframe Space):
- 在数据帧和遥控帧之间的最小时间间隔,确保总线上的节点有足够的时间来处理接收到的帧。
数据帧
遥控帧
遥控帧无数据段,RTR为隐性电平1,其他部分与数据帧相同
错误帧
总线上所有设备都会监督总线的数据,一旦发现“位错误”或“填充错误”或“CRC错误”或“格式错误”或“应答错误” ,这些设备便会发出错误帧来破坏数据,同时终止当前的发送设备
过载帧
当接收方收到大量数据而无法处理时,其可以发出过载帧,延缓发送方的数据发送,以平衡总线负载,避免数据丢失
帧间隔
将数据帧和远程帧与前面的帧分离开
注意
位填充
数据传输时两个设备做了什么
在CAN(Controller Area Network)总线中,当两个节点(例如,节点A和节点B)进行通信时,它们各自扮演不同的角色:一个是发送节点,另一个是接收节点。以下是两个节点在数据帧传输过程中各自所做的具体工作:
发送节点(节点A):
-
构造数据帧:
- 节点A将需要发送的数据组织成数据帧格式,包括帧起始(SOF)、仲裁域(标识符和RTR位)、控制域(DLC)、数据域(实际数据)、CRC域(CRC序列)和帧结束(EOF)。
-
发送数据帧:
- 节点A将数据帧发送到CAN总线上。它首先监测总线是否空闲,然后在总线空闲时开始发送数据帧。
-
仲裁:
- 如果多个节点同时尝试发送数据,节点A会参与仲裁过程。如果节点A的标识符具有最高的优先级(最低的数值),它将赢得仲裁并继续发送数据帧。
-
等待确认:
- 在发送完数据帧的ACK槽期间,节点A监听总线,以确定是否有接收节点发送了确认响应(ACK)。
-
处理错误:
- 如果在传输过程中检测到错误(例如,在ACK槽中没有收到确认),节点A会根据错误类型和错误计数器采取相应的错误处理措施,可能会重传数据帧。
接收节点(节点B):
-
监听总线:
- 节点B持续监听总线上的信号,等待数据帧的到来。
-
识别标识符:
- 当节点B检测到帧起始(SOF)时,它会开始解析仲裁域,识别标识符,以确定是否应该接收这个数据帧。
-
接收数据帧:
- 如果数据帧的标识符与节点B的接收过滤器匹配,节点B将继续接收数据帧的剩余部分,包括控制域、数据域、CRC域等。
-
验证数据帧:
- 节点B计算数据帧的CRC,并将其与接收到的CRC序列进行比较,以验证数据在传输过程中没有发生错误。
-
发送确认:
- 如果数据帧验证无误,节点B在ACK槽期间向总线发送一个显性位(逻辑“0”),以向发送节点(节点A)确认数据帧已被正确接收。
-
处理数据:
- 一旦数据帧被正确接收和确认,节点B可以将数据传递给应用程序进行处理。
各个版本协议
CAN(Controller Area Network)是一种广泛使用的、基于消息的通信协议,主要用于汽车和其他嵌入式系统中。随着技术的发展,CAN协议也经历了多次更新和扩展,主要包括CAN 2.0A、CAN 2.0B和CAN FD三个标准。
-
CAN 2.0A
- CAN 2.0A是CAN协议的一个版本,也被称为标准帧格式。
- 它支持11位标识符,这意味着可以有最多2048个不同的消息标识符。
- CAN 2.0A通常用于较为简单的网络,其中节点数量和消息优先级不是特别高。
-
CAN 2.0B
- CAN 2.0B是CAN协议的另一个版本,也被称为扩展帧格式。
- 它支持29位标识符,这意味着可以有最多约536870912个不同的消息标识符。
- CAN 2.0B通过增加额外的标识符位数,提供了更高的消息标识符数量,从而能够支持更复杂的网络和更多的节点。
-
CAN FD(Flexible Data-Rate)
- CAN FD是CAN协议的一个扩展,旨在提高数据传输速率和数据长度。
- 它支持两种不同的数据传输速率:在仲裁阶段使用传统的CAN速率,而在数据阶段可以使用高达8Mbps的速率。
- CAN FD还支持更长的数据字段,可以达到64字节,而传统的CAN协议最多只能支持8字节。
- CAN FD向后兼容CAN 2.0A和CAN 2.0B,但在使用更高速率和更长数据长度时需要网络中的所有节点都支持CAN FD。
最大时钟速度
CAN标准理论上的最大速度:
-
CAN 2.0A/CAN 2.0B
- CAN 2.0A和CAN 2.0B是CAN协议的基础版本,它们在物理层和数据链路层上有所不同,但理论上支持的最大通信速度相同。
- 传统上,CAN 2.0A/B的通信速度通常限制在1 Mbps(兆比特每秒)以内,这是大多数CAN控制器和收发器的标准速度。
- 然而,一些高性能的CAN控制器和收发器可能支持更高的速度,例如2 Mbps或更高,但这通常需要优化的硬件设计和严格的位定时参数配置。
-
CAN FD(Flexible Data-Rate)
- CAN FD是CAN协议的一个扩展,旨在提高数据传输速率和数据长度。
- 在仲裁阶段,CAN FD仍然支持与传统CAN相同的标准速度,通常也是1 Mbps。
- 在数据阶段,CAN FD可以支持更高的速度,理论上可以达到5 Mbps甚至8 Mbps,但实际应用中的速度取决于具体的硬件支持和网络配置。
stm32CudeMX配置CAN
-
Bit Timings Parameters(位定时参数)
- Prescaler (for Time Quantum)(时间量子预分频器): 用于确定CAN模块的时间量子(Time Quantum)的长度,通过这个预分频器可以设置CAN时钟的频率。
- Time Quantum(时间量子): 是CAN总线上的基本时间单位,用于定义位时间。
- Time Quanta in Bit Segment 1(位段1中的时间量子): 定义一个CAN位中的第一个时间段(例如,同步段、传播时间段)的长度。
- Time Quanta in Bit Segment 2(位段2中的时间量子): 定义一个CAN位中的第二个时间段(例如,相位缓冲段1)的长度。
- Time for one Bit(一个位的时间): 定义一个完整的CAN位的时间长度。
- Baud Rate(波特率): 定义CAN通信的速率,即每秒钟传输的位数。
- ReSynchronization Jump Width(重新同步跳变宽度): 定义在CAN位时间内,重新同步跳变的最大宽度。
-
Basic Parameters(基本参数)
- Time Triggered Communication Mode(时间触发通信模式): 如果启用,CAN模块将工作在时间触发通信模式,这种模式下所有的通信都是基于时间的,可以提高通信的确定性和可靠性。
- Automatic Bus-Off Management(自动总线关闭管理): 如果启用,当CAN节点检测到错误过多时,会自动进入总线关闭状态,并在一定条件满足后自动尝试重新连接到总线。
- Automatic Wake-Up Mode(自动唤醒模式): 如果启用,当CAN模块检测到总线活动时,会自动唤醒微控制器。
- Automatic Retransmission(自动重传模式): 如果启用,当CAN消息发送失败时,模块会自动重传消息。
- Receive Fifo Locked Mode(接收FIFO锁定模式): 如果启用,当FIFO溢出时,锁定的消息不会被覆盖,而是保持不变。
- Transmit Fifo Priority(发送FIFO优先级): 定义发送FIFO中消息的发送顺序,可以设置为优先级或FIFO模式。
-
Advanced Parameters(高级参数)
- Test Mode(测试模式): 允许用户将CAN模块设置为不同的测试模式,用于诊断和测试目的。
模式(Normal :正常 Loop back :回环 Silent :静默 Loop back combined with Silent :回环静默)
正常模式:CAN外设正常地向CAN总线发送数据并从CAN总线上接收数据。
回环模式:CAN外设正常向CAN总线发送数据,同时接收自己发送的数据,但不从CAN总线上接收数据。在学习CAN外设的时候非常有用,特别是在没有专门的USB转CAN模块也没有两块开发板的时候。
静默模式:CAN外设不向CAN总线发送数据,仅从CAN总线上接收数据,但不会应答。一般用于检测CAN总线的流量。
静默回环模式:CAN外设不会往CAN总线收发数据,仅给自己发送。一般用于自检。
函数解释
//启动CAN模块,使其进入正常工作模式。
HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan);
//停止CAN模块,使其不再接收或发送数据。
HAL_StatusTypeDef HAL_CAN_Stop(CAN_HandleTypeDef *hcan);
//请求CAN模块进入睡眠模式,以降低功耗。
HAL_StatusTypeDef HAL_CAN_RequestSleep(CAN_HandleTypeDef *hcan);
//将CAN模块从睡眠模式唤醒。
HAL_StatusTypeDef HAL_CAN_WakeUp(CAN_HandleTypeDef *hcan);
//检查CAN模块是否处于活动睡眠模式。
uint32_t HAL_CAN_IsSleepActive(const CAN_HandleTypeDef *hcan);
//向CAN模块的发送邮箱添加一条消息。
HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, const CAN_TxHeaderTypeDef *pHeader,
const uint8_t aData[], uint32_t *pTxMailbox);
//取消正在发送的消息。
HAL_StatusTypeDef HAL_CAN_AbortTxRequest(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes);
//获取空闲的发送邮箱数量。
uint32_t HAL_CAN_GetTxMailboxesFreeLevel(const CAN_HandleTypeDef *hcan);
//检查是否有消息正在发送邮箱中等待发送。
uint32_t HAL_CAN_IsTxMessagePending(const CAN_HandleTypeDef *hcan, uint32_t TxMailboxes);
//获取发送消息的时间戳。
uint32_t HAL_CAN_GetTxTimestamp(const CAN_HandleTypeDef *hcan, uint32_t TxMailbox);
//从接收FIFO中读取一条消息。
HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo,
CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);
//获取接收FIFO中的消息数量
uint32_t HAL_CAN_GetRxFifoFillLevel(const CAN_HandleTypeDef *hcan, uint32_t RxFifo);
每个函数的解释:
*HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef hcan);
启动CAN模块,使其进入正常工作模式。
参数 hcan 是指向CAN_HandleTypeDef结构的指针,该结构包含CAN模块的配置和状态信息。
*HAL_StatusTypeDef HAL_CAN_Stop(CAN_HandleTypeDef hcan);
停止CAN模块,使其不再接收或发送数据。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
*HAL_StatusTypeDef HAL_CAN_RequestSleep(CAN_HandleTypeDef hcan);
请求CAN模块进入睡眠模式,以降低功耗。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
*HAL_StatusTypeDef HAL_CAN_WakeUp(CAN_HandleTypeDef hcan);
将CAN模块从睡眠模式唤醒。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
*uint32_t HAL_CAN_IsSleepActive(const CAN_HandleTypeDef hcan);
检查CAN模块是否处于活动睡眠模式。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
返回值:如果CAN模块处于睡眠模式,则返回非零值,否则返回0。
**HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, const CAN_TxHeaderTypeDef pHeader, const uint8_t aData[], uint32_t pTxMailbox);
向CAN模块的发送邮箱添加一条消息。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
参数 pHeader 是指向CAN_TxHeaderTypeDef结构的指针,该结构包含要发送消息的头部信息。
参数 aData 是指向要发送的数据的指针。
参数 pTxMailbox 是指向用于存储发送邮箱编号的变量的指针。
返回值:操作状态(HAL_OK, HAL_ERROR等)。
*HAL_StatusTypeDef HAL_CAN_AbortTxRequest(CAN_HandleTypeDef hcan, uint32_t TxMailboxes);
取消正在发送的消息。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
参数 TxMailboxes 是要取消的消息所在的发送邮箱的位掩码。
返回值:操作状态(HAL_OK, HAL_ERROR等)。
*uint32_t HAL_CAN_GetTxMailboxesFreeLevel(const CAN_HandleTypeDef hcan);
获取空闲的发送邮箱数量。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
返回值:空闲发送邮箱的数量。
*uint32_t HAL_CAN_IsTxMessagePending(const CAN_HandleTypeDef hcan, uint32_t TxMailboxes);
检查是否有消息正在发送邮箱中等待发送。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
参数 TxMailboxes 是要检查的发送邮箱的位掩码。
返回值:如果有消息正在等待发送,则返回非零值,否则返回0。
*uint32_t HAL_CAN_GetTxTimestamp(const CAN_HandleTypeDef hcan, uint32_t TxMailbox);
获取发送消息的时间戳。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
参数 TxMailbox 是要获取时间戳的发送邮箱的编号。
返回值:发送消息的时间戳。
**HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef pHeader, uint8_t aData[]);
从接收FIFO中读取一条消息。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
参数 RxFifo 是要读取的接收FIFO的编号。
参数 pHeader 是指向CAN_RxHeaderTypeDef结构的指针,该结构用于存储接收消息的头部信息。
参数 aData 是用于存储接收数据的数组。
返回值:操作状态(HAL_OK, HAL_ERROR等)。
*uint32_t HAL_CAN_GetRxFifoFillLevel(const CAN_HandleTypeDef hcan, uint32_t RxFifo);
获取接收FIFO中的消息数量。
参数 hcan 是指向CAN_HandleTypeDef结构的指针。
参数 RxFifo 是要检查的接收FIFO的编号。
返回值:接收FIFO中的消息数量。
基本步骤:
初始化CAN接口
-
配置CAN模式和波特率:
- 使用
HAL_CAN_Init()
函数来初始化CAN接口。 - 通过
CAN_HandleTypeDef
结构体配置CAN的工作模式,如正常模式、测试模式等,并设置适当的波特率。
- 使用
-
配置过滤器:
- 设置CAN过滤器以过滤不必要的报文。这可以通过
HAL_CAN_ConfigFilter()
函数来完成。
- 设置CAN过滤器以过滤不必要的报文。这可以通过
发送数据
-
创建CAN报文:
- 使用
CAN_TxHeaderTypeDef
结构体定义要发送的报文的头部信息,包括标识符、数据长度、帧类型等。
- 使用
-
填充数据:
- 准备要发送的数据,通常存储在一个数组中。
-
发送报文:
- 调用
HAL_CAN_AddTxMessage()
函数,将报文添加到发送队列中。 - 如果发送成功,该函数会返回
HAL_OK
。
- 调用
接收数据
-
接收中断:
- 使能CAN中断,并配置中断处理函数。在中断处理函数中,调用
HAL_CAN_IRQHandler()
。
- 使能CAN中断,并配置中断处理函数。在中断处理函数中,调用
-
读取数据:
- 在中断处理函数或主循环中,使用
HAL_CAN_GetRxMessage()
函数来读取接收到的报文。 - 读取到的数据存储在事先定义的数组中。
- 在中断处理函数或主循环中,使用
-
处理数据:
- 根据应用程序的需求处理接收到的数据。
RxFifo 参数通常是一个枚举值
区别
CAN_RX_FIFO0
:通常用于存储高优先级的消息。这意味着如果CAN消息在硬件筛选过程中被认为具有较高的优先级,它们将被放入FIFO 0。CAN_RX_FIFO1
:用于存储低优先级的消息。那些在筛选过程中被认为优先级较低的消息将被放入FIFO 1。
筛选器(不设置不可接收数据)
(filters)是用于过滤接收到的消息的重要功能。CAN控制器通常包含多个筛选器,允许您根据特定的标准(如标识符)来选择性地接收消息。这有助于减少CPU的处理负担,因为只有符合特定条件的消息才会触发中断或被传送到应用程序。
STM32微控制器的CAN模块通常具有多个筛选器,每个筛选器都可以独立配置。筛选器可以根据以下标准来过滤消息:
-
标识符(ID):筛选器可以基于标准标识符(11位)或扩展标识符(29位)来过滤消息。
-
远程传输请求(RTR):筛选器可以区分数据帧和远程帧。
-
IDE位:筛选器可以根据标识符类型(标准或扩展)来过滤消息。
在STM32的HAL库中,您可以使用以下函数来配置CAN筛选器:
-
HAL_CAN_ConfigFilter()
:用于配置一个或多个筛选器。 -
HAL_CAN_ActivateNotification()
:用于启用与筛选器相关的中断。
CAN_HandleTypeDef hcan;
CAN_FilterTypeDef sFilterConfig;
// 初始化CAN
hcan.Instance = CAN1;
hcan.Init.Mode = CAN_MODE_NORMAL;
// ... 其他初始化设置
// 初始化CAN并启用中断
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
// 初始化失败的处理
}
// 配置筛选器
sFilterConfig.FilterBank = 0; // 使用筛选器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 筛选器模式为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 使用32位筛选器
sFilterConfig.FilterIdHigh = 0x0000; // 筛选器ID高16位
sFilterConfig.FilterIdLow = 0x0000; // 筛选器ID低16位
sFilterConfig.FilterMaskIdHigh = 0x0000; // 掩码ID高16位
sFilterConfig.FilterMaskIdLow = 0x0000; // 掩码ID低16位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 将筛选器关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 启用筛选器
// 设置筛选器
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
// 筛选器配置失败的处理
}
{
- FilterIdHigh :CAN_FiR1寄存器的高16位,用于填写筛选码。具体的格式要根据16位、32位;掩码模式、列表模式来确定。
- 取值: 0x0 ~ 0xFFFF
- FilterIdLow :CAN_FiR1寄存器的低16位
- FilterMaskIdHigh :CAN_FiR2寄存器的高16位
- FilterMaskIdLow :CAN_FiR2寄存器的低16位
- FilterFIFOAssignment :通过筛选器的报文存在FIFO0还是FIFO1中
- 取值:CAN_FILTER_FIFO0 或 CAN_FILTER_FIFO1
- FilterBank :本次配置的筛选器号
- 取值:对于单CAN为 0 ~ 13;对于双CAN为 0 ~ 27
- FilterMode :筛选模式,掩码模式或列表模式。
- 取值:CAN_FILTERMODE_IDMASK 或 CAN_FILTERMODE_IDMASK
- FilterScale :筛选码大小,16位或32位。
- 取值:CAN_FILTERSCALE_16BIT 或 CAN_FILTERSCALE_32BIT
- FilterActivation :使能或失能此筛选器。
- 取值:CAN_FILTER_DISABLE 或 CAN_FILTER_ENABLE
- SlaveStartFilterBank :为从CAN(CAN2)分配的筛选器个数。如果只使用单个CAN,可忽略此成员
- 取值: 0 ~ 27
原文链接:https://blog.csdn.net/jdhfusk/article/details/121748853
}
实现收发数据
思路
定义一个按键,串口,can
按键点击开始发发完再接收,将数据通过串口发给电脑
1定义函数
/**
* CAN 发送一组数据
* 数据 长度
*/
uint8_t can_send_msg( uint8_t *data, uint8_t len )
{
//发的结构体
CAN_TxHeaderTypeDef can_tx;
can_tx.DLC=len; // 数据长度代码
can_tx.ExtId=0x00;// 扩展标识符
can_tx.IDE=CAN_ID_EXT;// 标识符类型为扩展帧 //标识符的长度
can_tx.RTR=CAN_RTR_DATA;// 远程传输请求为数据帧
can_tx.StdId=0x12;// 标准标识符 //can的ID
//can_tx.TransmitGlobalTime=ENABLE;// 使用全局时间 ENABLE开启 DISABLE关闭 时间戳
uint32_t as=CAN_TX_MAILBOX0;
if(HAL_CAN_AddTxMessage(&hcan,&can_tx,data,&as)==HAL_OK){
int as1=HAL_CAN_GetTxMailboxesFreeLevel(&hcan);
char aa[28];
sprintf(aa,"数据发送成功,邮箱个数为:%d\n",as1);
HAL_UART_Transmit(&huart1,(uint8_t*)aa,27,50);
}
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 3); /* 等待发送完成,所有邮箱为空 */
return 0;
}
/**
* CAN 接收数据查询
// 数据
*/
void can_receive_msg( uint8_t *data )
{
//接收的结构体
// 声明一个变量来存储接收到的消息的头部信息
CAN_RxHeaderTypeDef can_rx;
//can_rx.DLC // 数据长度
//can_rx.ExtId //扩展标识符
//can_rx.FilterMatchIndex// 过滤器匹配索引
//can_rx.IDE // 标识符类型为扩展帧
//can_rx.RTR // 远程传输请求为数据帧
//can_rx.StdId; // 标准标识符
//can_rx.Timestamp ; // 时间戳
if(HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &can_rx, data)== HAL_OK)
{
char aa[28]={"接收成功\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)aa,9,50);
}else{
char aa[28]={"接收失败\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)aa,9,50);
}
}
2初始化过滤器
CAN_FilterTypeDef sFilterConfig;
//配置CAN过滤器
sFilterConfig.FilterBank = 0; /* 过滤器0 */
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000; /* 32位ID */
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; /* 32位MASK */
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; /* 过滤器0关联到FIFO0 */
sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; /* 激活滤波器0 */
sFilterConfig.SlaveStartFilterBank = 14;
// 过滤器配置
if ( HAL_CAN_ConfigFilter( &hcan, &sFilterConfig ) != HAL_OK )
{
return 2;
}
// 启动CAN外围设备
if ( HAL_CAN_Start( &hcan ) != HAL_OK )
{
return 3;
}
3检测按键收发
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)== GPIO_PIN_SET){
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==HAL_OK){
char a[]={"按键按下了\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)a,11,50);
uint8_t tx_data[8] = {"ABCDEFGH"}; // 消息数据
uint8_t rx_data[8]; // 接收数据缓冲区
can_send_msg( tx_data, 8 );
if(HAL_CAN_GetRxFifoFillLevel(&hcan ,CAN_RX_FIFO0)){
can_receive_msg( rx_data );
}else{
char aa[28]={"无数据\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)aa,9,50);
}
HAL_Delay(500);
HAL_UART_Transmit(&huart1,rx_data,8,500);
}
}
全部在主文件中
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 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 */
/**
* CAN 发送一组数据
* 数据 长度
*/
uint8_t can_send_msg( uint8_t *data, uint8_t len )
{
//发的结构体
CAN_TxHeaderTypeDef can_tx;
can_tx.DLC=len; // 数据长度代码
can_tx.ExtId=0x00;// 扩展标识符
can_tx.IDE=CAN_ID_EXT;// 标识符类型为扩展帧 //标识符的长度
can_tx.RTR=CAN_RTR_DATA;// 远程传输请求为数据帧
can_tx.StdId=0x12;// 标准标识符 //can的ID
//can_tx.TransmitGlobalTime=ENABLE;// 使用全局时间 ENABLE开启 DISABLE关闭 时间戳
uint32_t as=CAN_TX_MAILBOX0;
if(HAL_CAN_AddTxMessage(&hcan,&can_tx,data,&as)==HAL_OK){
int as1=HAL_CAN_GetTxMailboxesFreeLevel(&hcan);
char aa[28];
sprintf(aa,"数据发送成功,邮箱个数为:%d\n",as1);
HAL_UART_Transmit(&huart1,(uint8_t*)aa,27,50);
}
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 3); /* 等待发送完成,所有邮箱为空 */
return 0;
}
/**
* CAN 接收数据查询
// 数据
*/
void can_receive_msg( uint8_t *data )
{
//接收的结构体
// 声明一个变量来存储接收到的消息的头部信息
CAN_RxHeaderTypeDef can_rx;
//can_rx.DLC // 数据长度
//can_rx.ExtId //扩展标识符
//can_rx.FilterMatchIndex// 过滤器匹配索引
//can_rx.IDE // 标识符类型为扩展帧
//can_rx.RTR // 远程传输请求为数据帧
//can_rx.StdId; // 标准标识符
//can_rx.Timestamp ; // 时间戳
if(HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &can_rx, data)== HAL_OK)
{
char aa[28]={"接收成功\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)aa,9,50);
}else{
char aa[28]={"接收失败\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)aa,9,50);
}
}
/* 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 */
CAN_FilterTypeDef sFilterConfig;
//配置CAN过滤器
sFilterConfig.FilterBank = 0; /* 过滤器0 */
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000; /* 32位ID */
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; /* 32位MASK */
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; /* 过滤器0关联到FIFO0 */
sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; /* 激活滤波器0 */
sFilterConfig.SlaveStartFilterBank = 14;
// 过滤器配置
if ( HAL_CAN_ConfigFilter( &hcan, &sFilterConfig ) != HAL_OK )
{
return 2;
}
// 启动CAN外围设备
if ( HAL_CAN_Start( &hcan ) != HAL_OK )
{
return 3;
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)== GPIO_PIN_SET){
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==HAL_OK){
char a[]={"按键按下了\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)a,11,50);
uint8_t tx_data[8] = {"ABCDEFGH"}; // 消息数据
uint8_t rx_data[8]; // 接收数据缓冲区
can_send_msg( tx_data, 8 );
if(HAL_CAN_GetRxFifoFillLevel(&hcan ,CAN_RX_FIFO0)){
can_receive_msg( rx_data );
}else{
char aa[28]={"无数据\n"};
HAL_UART_Transmit(&huart1,(uint8_t*)aa,9,50);
}
HAL_Delay(500);
HAL_UART_Transmit(&huart1,rx_data,8,500);
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* 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_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
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_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != 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 */
个人主页有项目文件资料免费自行下载
还会总一篇STM32CudeMX的全过程
此篇重点介绍概念