当前位置: 代码网 > 科技>电脑产品>交换机 > STM32开发(五)STM32F103 通信 —— CAN通信编程详解

STM32开发(五)STM32F103 通信 —— CAN通信编程详解

2024年08月01日 交换机 我要评论
USE_HAL_CAN_REGISTER_CALLBACKS宏定义为0,则调用HAL_CAN_RxFifo0MsgPendingCallback回调函数(这个函数是弱化函数,重构该函数之后就会调用重构函数)(2)在HAL_CAN_IRQHandler函数中判断中断标志位为CAN_IT_RX_FIFO0_MSG_PENDING(挂起中断,在初始化中使能挂起中断)CAN接收中断包括:挂起中断(只要有信息就触发中断)、满中断(FIFO都满了触发中断)、溢出中断(只有FIFO都满后还接收到数据就会触发中断)

👈  🏡  👉



一、基础知识点

了解can通讯协议以及can 协议及标准规格 。本实验是基于stm32f103开发的can通信,来一起研究下stm32数据手册 中can的特色
准备好了吗?开始我的show time。


二、开发环境

1、硬件开发准备

主控:stm32f103zet6
can收发器:tja1040t
在这里插入图片描述

2、软件开发准备

软件开发使用虚拟机 + vscode + stm32cube 开发stm32,在虚拟机中直接完成编译下载。
该部分可参考:


三、stm32cubemx相关配置

1、stm32cubemx基本配置

本实验基于cubemx详解构建基本框架 进行开发。

2、stm32cubemx can相关配置

(1)时钟配置
由于can在apb1时钟线上,apb1时钟配置36m
在这里插入图片描述
(2)配置can参数

  • 开启主can配置
    在这里插入图片描述
  • 位时序配置
    位时序顾名思义就是传输一个位的时序(如0或1)。位时序结构:同步段(sync_seg)、时间段1(bs1)、时间段2(bs2)
    在这里插入图片描述假j把can的时钟配置为500khz
    (1)将系统时间36m进行4分频,则36m/4 = 9m
    (2)位时序中同步段(sync_seg)固定1tq;stm32时间段1(bs1)包含两部分:传播时间段和相位缓冲时间段1,可以分配11tq;时间段2(bs2)包含相位缓冲时间段2,可以分配6tq。
    这样1位由18个 tq 构成,则9m/18 = 500k
    按照以上的配置可以实现can 500k通信。

在这里插入图片描述

  • 基本模式配置
    根据自己需要进行配置,这里实验都不需要,直接disable关掉
    在这里插入图片描述
    自动重发数据:若使能,数据出错了可以重新发送数据
    接收fifo锁定模式:若使能,fifo数据不可以重叠,更替
    发送fifo优先级:若关闭,就按照邮箱的优先级来发送数据;若使能,就按照自己设定的优先级发送。

  • can工作模式配置
    在这里插入图片描述
    模式选择:正常模式、静默模式、环回模式、环回静默模式。
    实验选用正常模式、环回模式测试can通信。

  • 中断模式配置
    在nvic settings选项卡中将can接收中断使能打开
    在这里插入图片描述
    设置中断优先级
    在这里插入图片描述


四、vscode代码讲解

1、构建一个can相关结构体

//定义结构体类型
typedef struct
{
  uint32_t can_work_mode;         // can 工作模式

  uint8_t tx_buff[8];             // 发送缓存
  uint8_t rx_buff[8];             // 接收缓存
  
  void (*mycan_init)(void);        // can 初始化             
  uint8_t (*mycan_send_message)(uint8_t *p_tx_buff, uint32_t *pmycan_mailbox_num);  // 发送信息
  void (*mycan_recevie_message)(uint8_t *p_rx_buff);                                // 接收信息

  uint8_t rx_status_flag;         // 接收标志位
} mycan_t;

2、定义can结构体

mycan_t mycan ={
    can_mode_normal,            // 正常接收发送模式

    {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77},
    {0},

    mycan_init,                     
    mycan_send_message,
    mycan_recevie_message,

    false            // 默认没有接收到信息
};

3、初始化can
(1)can过滤器配置
filterbank:要配置的过滤器0(芯片一共14个,0-13)
filtermode:选用标识符屏蔽模式(可以接收一组id),若选择列表模式,只能接收一个特定的id
filterfifoassignment:将配置的过滤器0关联到fifo0
filteractivation:激活过滤器,若不激活接收不到任何数据
(2)使能接收挂起中断
在stm32cubemx里面是时钟了接收的总中断,这里使能的是总中断下的挂起中断
can接收中断包括:挂起中断(只要有信息就触发中断)、满中断(fifo都满了触发中断)、溢出中断(只有fifo都满后还接收到数据就会触发中断)
(3)启动can

  void mycan_init(void)
  {
    can_filtertypedef mycan_filter;

    // 配置过滤器
    mycan_filter.filteridhigh         = 0x34;                 // 过滤器需要过滤高id
    mycan_filter.filteridlow          = 0x00;                 // 过滤器需要过滤低id
    mycan_filter.filtermaskidhigh     = 0x00;                 // 过滤器掩码 '0'位不限制 
    mycan_filter.filtermaskidlow      = 0x00;                 // 过滤器掩码 '0'位不限制
    mycan_filter.filterfifoassignment = can_filter_fifo0;     // 挂在过滤器fifo0
    mycan_filter.filterbank           = 0;                    // 过滤器0
    mycan_filter.filtermode           = can_filtermode_idmask; // id掩码模式
    mycan_filter.filterscale          = can_filterscale_16bit; // 16位过滤器
    mycan_filter.filteractivation     = can_filter_enable;     // 激活过滤器
    mycan_filter.slavestartfilterbank = 14;                  

    // 配置过滤器
    if (hal_can_configfilter(&hcan, &mycan_filter) != hal_ok)
    {
        printf("dwb --- can配置过滤器失败\n");
        system.error_handler();
    }

    // 使能fifo接收到一个新报文中断
    if(hal_can_activatenotification(&hcan, can_it_rx_fifo0_msg_pending) != hal_ok)
    {
        printf("dwb --- can使能接收挂起中断失败\n");
        system.error_handler();
    }

    if(hal_can_start(&hcan) != hal_ok)
    {
        printf("dwb --- can开启失败\n");
        system.error_handler();
    }

    printf("dwb --- can配置并开启成功!\n");
  }    

4、can发送
(1)can发送数据时序配置
定义发送时序参数,通过hal_can_addtxmessage函数发送数据到邮箱
(2)等待发送数据成功
延时1s时间,1s内反复通过hal_can_gettxmailboxesfreelevel函数检查空邮箱的个数。如果空邮箱个数等于3,则说明数据已经发送成功。

  uint8_t mycan_send_message(uint8_t *p_tx_buff, uint32_t *pmycan_mailbox_num)
  {
      can_txheadertypedef mycan_txheader;

      // 配置发送头
      mycan_txheader.stdid              = 0x34;                    // 发送设备标准id
      mycan_txheader.extid              = 0x00;                    // 扩展id
      mycan_txheader.ide                = can_id_std;              // can标准id模式
      mycan_txheader.rtr                = can_rtr_data;            // 数据帧
      mycan_txheader.dlc                = 8;                       // 传输长度8
      mycan_txheader.transmitglobaltime = disable;                 // 时间戳 不使能

      // 发送数据到邮箱并判断状态
      if(hal_can_addtxmessage(&hcan, &mycan_txheader, p_tx_buff, pmycan_mailbox_num) != hal_ok)
      {
          printf("dwb --- 发送数据到邮箱失败\n");
          return send_date_fail;
      }

      uint8_t rtc_seconds_t = myrtc.pmyrtc_current_time->seconds+1;
      do
      {
        if(rtc_seconds_t == myrtc.pmyrtc_current_time->seconds)
        {
            printf("dwb --- 数据未发出 \n");
            return send_date_fail;
        }
      } while (hal_can_gettxmailboxesfreelevel(&hcan) != 3);

      printf("dwb --- 数据发送成功 \n\r");
      return send_date_success;
  }

5、主函数中调用发送接收函数
(1)调用结构体can发送函数成员进行数据发送
(2)通过rx_status_flag标识符判断是否接收到数据,后调用mycan_recevie_message接收

    res = mycan.mycan_send_message(mycan.tx_buff, &mailbox_num);
    printf("dwb --- mailbox_num = %ld\n\r", mailbox_num);
    if(!res && true == mycan.rx_status_flag){
        mycan.mycan_recevie_message(mycan.rx_buff);
        mycan.rx_status_flag = false;
    }

6、can接收中断函数
在初始化中can使能接收挂起中断。当有接收到数据就会调用中断函数
__weak void hal_can_rxfifo0msgpendingcallback(can_handletypedef *hcan)

这个函数是弱函数,直接重构就好了。

void hal_can_rxfifo0msgpendingcallback(can_handletypedef *hcan_t)
{
  can_rxheadertypedef pmycan_tx_head;
  // hal_statustypedef hal_can_getrxmessage(can_handletypedef *hcan, uint32_t rxfifo, can_rxheadertypedef *pheader, uint8_t adata[]);
  if (hal_can_getrxmessage(&hcan, can_rx_fifo0, &pmycan_tx_head, mycan.rx_buff) == hal_ok)
    mycan.rx_status_flag = true;
}

调用hal_can_getrxmessage函数接收数据,这里数据从can_rx_fifo0中读取。
为什么是fifo0呢?因为在初始化过滤器的时候将其关联到fifo0上。

解析接收的过程
中断初始化中,使能usb_lp_can1_rx0_irqn can接收中断

static void mx_nvic_init(void)
{
  /* rtc_alarm_irqn interrupt configuration */
  hal_nvic_setpriority(rtc_alarm_irqn, 1, 0);
  hal_nvic_enableirq(rtc_alarm_irqn);
  /* usb_lp_can1_rx0_irqn interrupt configuration */
  hal_nvic_setpriority(usb_lp_can1_rx0_irqn, 2, 0);
  hal_nvic_enableirq(usb_lp_can1_rx0_irqn);            // can接收总中断使能
}

can初始化中使能接收挂起中断

    // 使能fifo接收到一个新报文中断
    if(hal_can_activatenotification(&hcan, can_it_rx_fifo0_msg_pending) != hal_ok)
    {
        printf("dwb --- can使能接收挂起中断失败\n");
        system.error_handler();
    }

can接收数据时,
(1)触发usb_lp_can1_rx0_irqhandler回调函数

void usb_lp_can1_rx0_irqhandler(void)
{
  /* user code begin usb_lp_can1_rx0_irqn 0 */

  /* user code end usb_lp_can1_rx0_irqn 0 */
  hal_can_irqhandler(&hcan);
  /* user code begin usb_lp_can1_rx0_irqn 1 */

  /* user code end usb_lp_can1_rx0_irqn 1 */
}

(2)在hal_can_irqhandler函数中判断中断标志位为can_it_rx_fifo0_msg_pending(挂起中断,在初始化中使能挂起中断)
use_hal_can_register_callbacks宏定义为0,则调用hal_can_rxfifo0msgpendingcallback回调函数(这个函数是弱化函数,重构该函数之后就会调用重构函数)

void hal_can_irqhandler(can_handletypedef *hcan)
{
    ......
  /* receive fifo 0 message pending interrupt management *********************/
  if ((interrupts & can_it_rx_fifo0_msg_pending) != 0u)
  {
    /* check if message is still pending */
    if ((hcan->instance->rf0r & can_rf0r_fmp0) != 0u)
    {
      /* receive fifo 0 message pending callback */
#if use_hal_can_register_callbacks == 1
      /* call registered callback*/
      hcan->rxfifo0msgpendingcallback(hcan);
#else
      /* call weak (surcharged) callback */
      hal_can_rxfifo0msgpendingcallback(hcan);
#endif /* use_hal_can_register_callbacks */
    }
  }
    ......
}

五、结果演示

can 内部回环测试

代码设置回环测试,can自发自收。
在这里插入图片描述

串口打印发送成功后接收到的数据内容以及发送邮箱号。
在这里插入图片描述

can 正常模式测试

代码模式配置为正常模式
在这里插入图片描述

两块板子can相互通信背景:用另一块stm32开发板上的can通信与本实验中的板子can(打印信息有dwb)通信。
实验板子can发送(左图),stm32开发板can接收(右图)。两个板子canh对应相连;canl对应相连。
在这里插入图片描述
实验板子can接收(左图),stm32开发板can发送(右图)。两个板子canh对应相连;canl对应相连。
在这里插入图片描述

使用adalm2000分析工具解析can时序

整体波形:
在这里插入图片描述
开始帧(1位)
右下角,传输1位的时间为1.998μs,和软件里配置的时间1999.99ns时间一致(500000hz)
在这里插入图片描述
设备id位(标准帧id 11位)
解析出来的配置为0x34与软件配置一致(00000110100)
注:由于位补充(在发送数据帧和遥控帧时, sof~crc 段间的数据,相同电平如果持续 5 位,在下一个位(第 6 个位)则要插入 1 位与前 5 位反型的电平)的原因,中间有插入一个补充位1(绿色1)
在这里插入图片描述
rtr(1位数据帧)、ide(1位标准id模式)、rb0(保留位)、数据长度码(8位)
由于连续5位0,则中间添加补充位1
在这里插入图片描述
数据(8个字节)
在这里插入图片描述
crc(校验位15位)、crc d(crc 界定符(用于分隔的位)1位)、ack(用来确认是否正常接收2位)
在这里插入图片描述
结束帧
在这里插入图片描述


六、代码下载

stm32基础(五)stm32f103 can通信代码

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com