stm32_HAL_CAN(特点:广播试,检测线路是否空,id有优先级)

UART

I2C

SPI

介绍

CAN(Controller Area Network)接口是一种常用于嵌入式系统中的通信接口,尤其在汽车和工业领域。它由Robert Bosch GmbH在1986年开发,旨在提供一种可靠、高效、灵活的车载通信网络。CAN总线的主要

特点包括:

  1. 多主通信:CAN总线上的所有节点都可以主动发送数据,没有固定的主节点。
  2. 消息优先级:通过消息的标识符(ID)来设定优先级,ID越低,优先级越高。
  3. 非破坏性仲裁:当多个节点同时尝试发送数据时,CAN总线使用非破坏性仲裁机制来确定哪个消息可以继续传输。
  4. 自动错误处理:CAN总线具有错误检测和容错机制,包括位错误检测、帧检验、循环冗余检验(CRC)和自动重传。
  5. 高噪声免疫性:由于其差分信号的特点,CAN总线对电磁干扰有较高的抵抗能力。
  6. 数据传输速率:CAN总线支持从1 kbps到1 Mbps不等的数据传输速率。
  7. 通信距离:在不使用中继器的情况下,CAN总线可以支持最长40米(在1 Mbps速率下)的通信距离。
  8. 低成本:与其他通信协议相比,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(显性电平)

帧类型

        

  1. 数据帧(Data Frame):

    • 标准数据帧:使用11位标识符,用于传输数据。
    • 扩展数据帧:使用29位标识符,提供更多的标识符可能性,也用于传输数据。
  2. 遥控帧(Remote Transmission Request, RTR):

    • 标准遥控帧:使用11位标识符,请求发送具有特定标识符的数据帧。
    • 扩展遥控帧:使用29位标识符,请求发送具有特定标识符的数据帧。
  3. 错误帧(Error Frame):

    • 由任何检测到错误的节点发送,用于指示总线上发生了错误。
  4. 过载帧(Overload Frame):

    • 用于在需要更多时间来处理接收到的数据帧时,延长下一个数据帧的发送。
  5. 帧间隔(Interframe Space):

    • 在数据帧和遥控帧之间的最小时间间隔,确保总线上的节点有足够的时间来处理接收到的帧。

数据帧

遥控帧

遥控帧无数据段,RTR为隐性电平1,其他部分与数据帧相同

错误帧

总线上所有设备都会监督总线的数据,一旦发现“位错误”或“填充错误”或“CRC错误”或“格式错误”或“应答错误” ,这些设备便会发出错误帧来破坏数据,同时终止当前的发送设备

过载帧

当接收方收到大量数据而无法处理时,其可以发出过载帧,延缓发送方的数据发送,以平衡总线负载,避免数据丢失

帧间隔

将数据帧和远程帧与前面的帧分离开



注意

位填充

数据传输时两个设备做了什么

在CAN(Controller Area Network)总线中,当两个节点(例如,节点A和节点B)进行通信时,它们各自扮演不同的角色:一个是发送节点,另一个是接收节点。以下是两个节点在数据帧传输过程中各自所做的具体工作:

发送节点(节点A):

  1. 构造数据帧

    • 节点A将需要发送的数据组织成数据帧格式,包括帧起始(SOF)、仲裁域(标识符和RTR位)、控制域(DLC)、数据域(实际数据)、CRC域(CRC序列)和帧结束(EOF)。
  2. 发送数据帧

    • 节点A将数据帧发送到CAN总线上。它首先监测总线是否空闲,然后在总线空闲时开始发送数据帧。
  3. 仲裁

    • 如果多个节点同时尝试发送数据,节点A会参与仲裁过程。如果节点A的标识符具有最高的优先级(最低的数值),它将赢得仲裁并继续发送数据帧。
  4. 等待确认

    • 在发送完数据帧的ACK槽期间,节点A监听总线,以确定是否有接收节点发送了确认响应(ACK)。
  5. 处理错误

    • 如果在传输过程中检测到错误(例如,在ACK槽中没有收到确认),节点A会根据错误类型和错误计数器采取相应的错误处理措施,可能会重传数据帧。

接收节点(节点B):

  1. 监听总线

    • 节点B持续监听总线上的信号,等待数据帧的到来。
  2. 识别标识符

    • 当节点B检测到帧起始(SOF)时,它会开始解析仲裁域,识别标识符,以确定是否应该接收这个数据帧。
  3. 接收数据帧

    • 如果数据帧的标识符与节点B的接收过滤器匹配,节点B将继续接收数据帧的剩余部分,包括控制域、数据域、CRC域等。
  4. 验证数据帧

    • 节点B计算数据帧的CRC,并将其与接收到的CRC序列进行比较,以验证数据在传输过程中没有发生错误。
  5. 发送确认

    • 如果数据帧验证无误,节点B在ACK槽期间向总线发送一个显性位(逻辑“0”),以向发送节点(节点A)确认数据帧已被正确接收。
  6. 处理数据

    • 一旦数据帧被正确接收和确认,节点B可以将数据传递给应用程序进行处理。

各个版本协议

CAN(Controller Area Network)是一种广泛使用的、基于消息的通信协议,主要用于汽车和其他嵌入式系统中。随着技术的发展,CAN协议也经历了多次更新和扩展,主要包括CAN 2.0A、CAN 2.0B和CAN FD三个标准。

  1. CAN 2.0A

    • CAN 2.0A是CAN协议的一个版本,也被称为标准帧格式。
    • 它支持11位标识符,这意味着可以有最多2048个不同的消息标识符。
    • CAN 2.0A通常用于较为简单的网络,其中节点数量和消息优先级不是特别高。
  2. CAN 2.0B

    • CAN 2.0B是CAN协议的另一个版本,也被称为扩展帧格式。
    • 它支持29位标识符,这意味着可以有最多约536870912个不同的消息标识符。
    • CAN 2.0B通过增加额外的标识符位数,提供了更高的消息标识符数量,从而能够支持更复杂的网络和更多的节点。
  3. 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标准理论上的最大速度:

  1. CAN 2.0A/CAN 2.0B

    • CAN 2.0A和CAN 2.0B是CAN协议的基础版本,它们在物理层和数据链路层上有所不同,但理论上支持的最大通信速度相同。
    • 传统上,CAN 2.0A/B的通信速度通常限制在1 Mbps(兆比特每秒)以内,这是大多数CAN控制器和收发器的标准速度。
    • 然而,一些高性能的CAN控制器和收发器可能支持更高的速度,例如2 Mbps或更高,但这通常需要优化的硬件设计和严格的位定时参数配置。
  2. CAN FD(Flexible Data-Rate)

    • CAN FD是CAN协议的一个扩展,旨在提高数据传输速率和数据长度。
    • 在仲裁阶段,CAN FD仍然支持与传统CAN相同的标准速度,通常也是1 Mbps。
    • 在数据阶段,CAN FD可以支持更高的速度,理论上可以达到5 Mbps甚至8 Mbps,但实际应用中的速度取决于具体的硬件支持和网络配置。

stm32CudeMX配置CAN

  1. 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位时间内,重新同步跳变的最大宽度。
  2. 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模式。
  3. 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接口

  1. 配置CAN模式和波特率

    • 使用HAL_CAN_Init()函数来初始化CAN接口。
    • 通过CAN_HandleTypeDef结构体配置CAN的工作模式,如正常模式、测试模式等,并设置适当的波特率。
  2. 配置过滤器

    • 设置CAN过滤器以过滤不必要的报文。这可以通过HAL_CAN_ConfigFilter()函数来完成。

发送数据

  1. 创建CAN报文

    • 使用CAN_TxHeaderTypeDef结构体定义要发送的报文的头部信息,包括标识符、数据长度、帧类型等。
  2. 填充数据

    • 准备要发送的数据,通常存储在一个数组中。
  3. 发送报文

    • 调用HAL_CAN_AddTxMessage()函数,将报文添加到发送队列中。
    • 如果发送成功,该函数会返回HAL_OK

接收数据

  1. 接收中断

    • 使能CAN中断,并配置中断处理函数。在中断处理函数中,调用HAL_CAN_IRQHandler()
  2. 读取数据

    • 在中断处理函数或主循环中,使用HAL_CAN_GetRxMessage()函数来读取接收到的报文。
    • 读取到的数据存储在事先定义的数组中。
  3. 处理数据

    • 根据应用程序的需求处理接收到的数据。

RxFifo 参数通常是一个枚举值

区别

  • CAN_RX_FIFO0:通常用于存储高优先级的消息。这意味着如果CAN消息在硬件筛选过程中被认为具有较高的优先级,它们将被放入FIFO 0。
  • CAN_RX_FIFO1:用于存储低优先级的消息。那些在筛选过程中被认为优先级较低的消息将被放入FIFO 1。

筛选器(不设置不可接收数据)

(filters)是用于过滤接收到的消息的重要功能。CAN控制器通常包含多个筛选器,允许您根据特定的标准(如标识符)来选择性地接收消息。这有助于减少CPU的处理负担,因为只有符合特定条件的消息才会触发中断或被传送到应用程序。

STM32微控制器的CAN模块通常具有多个筛选器,每个筛选器都可以独立配置。筛选器可以根据以下标准来过滤消息:

  1. 标识符(ID):筛选器可以基于标准标识符(11位)或扩展标识符(29位)来过滤消息。

  2. 远程传输请求(RTR):筛选器可以区分数据帧和远程帧。

  3. 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的全过程

此篇重点介绍概念

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/706440.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flask基础2-Jinja2模板

目录 1.介绍 2.模板传参 1.变量传参 2.表达式 3.控制语句 4.过滤器 5.自定义过滤器 6.测试器 7.块和继承 flask基础1 1.介绍 Jinja2:是Python的Web项目中被广泛应用的模板引擎,是由Python实现的模板语言,Jinja2 的作者也是 Flask 的作 者。他的设计思想来源于Django的模…

Windows本地使用SSH连接VM虚拟机

WIN10 VM17.5 Ubuntu:20.04 1.网路设置 1)选择编辑->更改设置 配置完成 2.修改了服务器文件&#xff0c;修改sshd配置&#xff0c;在此文件下/etc/ssh/sshd_config&#xff0c;以下为比较重要的配置 PasswordAuthentication yes PermitRootLogin yes PubkeyAuthenticat…

华为昇腾异构计算架构CANN及AI芯片简介

异构计算架构CANN 异构计算架构CANN&#xff08;Compute Architecture for Neural Networks&#xff09;是华为针对AI场景推出的异构计算架构&#xff0c;向上支持多种AI框架&#xff0c;包括MindSpore、PyTorch、TensorFlow等&#xff0c;向下服务AI处理器与编程&#xff0c;…

ISCC2024 winterbegins

首先&#xff0c;用IDA打开程序&#xff0c;看到一大堆while(1)又套着while(1)的结构&#xff0c;肯定是控制流平坦化了&#xff0c;我们可以使用IDA插件d810去掉。 现在程序就好看多了。如果IDA没有显示这堆中文字符串&#xff0c;可以考虑使用IDA8.3&#xff0c;就可以显示…

MyBatis 动态 SQL 的详细内容讲解

1. MyBatis 动态 SQL 的详细内容讲解 文章目录 1. MyBatis 动态 SQL 的详细内容讲解2. 准备工作3. if 标签4. where 标签5. trim 标签6. set 标签7. choose when otherwise 标签8. foreach 标签8.1 批量删除8.2 批量添加 9. SQL 标签与 include 标签10. 总结&#xff1a;11. 最…

【介绍下ERP,什么是ERP?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

问题:棕色试剂瓶用于盛装见光易分解的试剂或溶剂。 #其他#学习方法#微信

问题&#xff1a;棕色试剂瓶用于盛装见光易分解的试剂或溶剂。 A、正确 B、错误 参考答案如图所示

机器学习第四十三周周报 aGNN

文章目录 week43 aGNN摘要Abstract1. 题目2. Abstract3. 网络架构3.1 aGNN3.1.1 输入与输出模块3.1.2 嵌入层3.1.3编码器解码器模块&#xff1a;带有多头注意力层的GCN 3.2 可释性模型&#xff1a;SHAP 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1 实验区域以及场…

CentOS手工升级curl记

笔者一台服务器装有 CentOS 7.9 系统&#xff0c;运行 curl -V 查询 curl 的版本是 7.29&#xff0c;这个老版本的 curl 不支持 HTTP/2 协议。为了使 curl 能连接HTTP/2&#xff0c;curl 必须升级到至少7.46.0版本以上。查询 curl的官网得知当前最新版本是 8.8.0&#xff0c;然…

wireshark抓包ssl数据出现ignored unknown record的原因

文章目录 前言一、出现原因二、wireshark抓包分析Ignored Unknown RecordTCP segment of a reassembled PDU 总结 前言 使用下面这个例子来观察记录层数据大于TCP MSS时用wireshark抓包出现ignored unknown record的情况并分析原因。 c语言利用openssl实现简单客户端和服务端&…

每日复盘-2024060123

今日关注&#xff1a; 这几天市场打板情绪环境转好&#xff0c;轻仓试错 20240613 六日涨幅最大: ------1--------301036--------- 双乐股份 五日涨幅最大: ------1--------301036--------- 双乐股份 四日涨幅最大: ------1--------301036--------- 双乐股份 三日涨幅最大: --…

CANable USB转CAN适配器固件的安装(ubuntu20.04)

CANable USB转CAN适配器固件和驱动的安装(以candlelight和pcan为例)ubuntu20.04 candlelight candlelight是什么 Candlelight是CANable设备的固件之一&#xff0c;用于将CANable设备转换为本机CAN设备&#xff0c;无需使用slcand。它允许CANable设备直接在Linux系统上以本机C…

【智能算法应用】基于A星算法求解六边形栅格地图路径规划

目录 1.算法原理2.结果展示3.参考文献4.代码获取 1.算法原理 精准导航&#xff1a;用A*算法优化栅格地图的路径规划【附Matlab代码】 六边形栅格地图 分析一下地图&#xff1a; 六边形栅格地图上移动可以看做6领域运动&#xff0c;偶数列与奇数列移动方式有所差异&#xff0…

Jetson Linux 上安装ZMQ

1. 安装ZMQ 框架 apt-get install libzmq3-dev 2. 或者自己build ZMQ https://github.com/zeromq/libzmq.git 参考官网教程 3. 安装CPPZMQ CPPZMQ 是ZMQ 的友好的C封装&#xff0c;只需要一个zmq.hpp 头文件即可 git clone https://github.com/zeromq/cppzmq.git cd cppz…

文件没有权限问题:cannot create /opt/apollo/neo/data/log/monitor.log: Permission denied

问题描述 执行 aem bootstrap start --plus 命令启动 Dreamview 提示错误&#xff1a; /bin/sh: 1: cannot create /opt/apollo/neo/data/log/monitor.log: Permission denied [ERROR] Failed to start Dreamview. Please check /opt/apollo/neo/data/log/dreamview.log or /op…

.net 调用海康SDK以及常见的坑解释

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 在工控领域,很多时候需要…

内网穿透的新标杆:神卓互联发布 V9.0 极速版本

在当今数字化时代&#xff0c;企业对于高效、安全的内网穿透解决方案的需求愈发迫切。神卓互联 V9.0 极速版本的惊艳发布&#xff0c;为企业提供了更为强大、稳定的内网穿透服务&#xff0c;有力地推动了企业实现更便捷的远程办公和数据共享。接下来&#xff0c;让我们详细探究…

Kubeadm 1.24.x安装教程

第一章 基本环境配置 1.1 配置hosts 所有节点 修改/etc/hosts如下&#xff1a; 192.168.0.93 W31-ywzt-SIT-1 192.168.0.94 W31-ywzt-SIT-2 1.2 CentOS 7安装yum源如下 所有节点 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo…

Beego 使用教程 9:ORM 操作数据库(上)

beego 是一个用于Go编程语言的开源、高性能的 web 框架 beego 被用于在Go语言中企业应用程序的快速开发&#xff0c;包括RESTful API、web应用程序和后端服务。它的灵感来源于Tornado&#xff0c; Sinatra 和 Flask beego 官网&#xff1a;http://beego.gocn.vip/ 上面的 be…

学习笔记丨嵌入式BI分析的12个关键功能

编者注&#xff1a;以下内容节选编译自嵌入式分析厂商Qrvey发表的《What is Embedded Analytics?》&#xff08;什么是嵌入式分析&#xff09;一文&#xff0c;作者为Qrvey产品市场主管Brian Dreyer。 什么是嵌入式分析&#xff1f; 嵌入式分析是指能够将数据分析的特性和功…