STM32CubeMX | 36 - 使用CAN总线进行双板通信(TJA1050)

最新更新时间:2021-07-30来源: eefocus关键字:STM32CubeMX  CAN总线

本篇详细的记录了如何使用STM32CubeMX配置 STM32F407ZGT6 的硬件CAN接口与另一个开发板之间通信。


1. 准备工作

硬件准备

  • 开发板

首先需要准备一个开发板,这里我准备的是STM32F407ZGT6的开发板,称之为 1# 实验板。

  • CAN收发器

开发板板载一块CAN收发器TJA1050,如图中红框所示:

软件准备

需要准备一份 TJA1050 的数据手册。


实验说明

本实验中还需要另外准备一块具备CAN收发功能的开发板,这里我使用STM32F767开发板,其板载CAN收发器也是TJA 1050,称之为 2# 实验板:

两个开发板之间的连接方式如下:

这样就形成了一个CAN总线的闭环通信网络,最高通信速度可达 1M bps/s:

2. 使用STM32CubeMX生成工程

选择芯片型号

打开STM32CubeMX,打开MCU选择器:

搜索并选中芯片STM32F407ZGT6:

配置时钟源

  • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;

  • 如果使用默认内部时钟(HSI),这一步可以略过;

这里我都使用外部时钟:

调试选项配置

默认没有配置下载引脚,烧录之后下载器将无法再检测到,这里我使用ST-Link,所以配置为SW选项:

配置串口

开发板板载了一个CH340换串口,连接到USART1,但是引脚不是默认引脚,需要手动修改。

接下来开始配置USART1:

配置CAN外设

CAN收发器

开发板上CAN收发器(TJA1050)的原理图如下:

其中CAN_TX 和 CAN_RX 连接到CAN1外设:

网络标号引脚
CAN_TXPA12
CAN_RXPA11

正点原子该款开发板上的PA12和PA11被复用,需要使用跳线帽来选择连接到CAN收发器。

配置CAN1控制器

选中CAN1,点击使能“Master Mode”,在右边即可看到CAN1控制器的默认GPIO,与原理图上连接CAN收发器的引脚一致,无需修改:

配置CAN控制只需配置波特率,一般为500KHz,最高1MHz,其它保持默认即可。

CAN总线的波特率比较特别,串口协议的波特率只支持一个确定值,而CAN总线的波特率支持一个较宽的范围,这也使得CAN总线的抗噪声性能大大增强。

CAN总线的波特率计算方式如下:

① 确定CAN外设连接的外设总线时钟PCLK1

此处CAN1连接到APB1外设总线上,在配置HCLK=168Mhz的基础上,PCLK=42Mhz。

② 确定分频系数

此处将PCLK1进行7分频,为 42Mhz / 7 = 6Mhz,所以设置CAN1外设的分频系数为7:

③ 配置位段时序

CAN协议的每一个数据位都分为许多时间段,如图:

  • 同步段(SYNC_SEG):位变化应该在此时间段内发生,只有一个时间片的固定长度(1 x tq);

  • 位段1(BS1):定义采样点的位置,其持续长度可以在 1 到 16 个Tq之间调整;

  • 位段2(BS2):定义发送点的位置,其持续长度可以在 1 到 8 个Tq之间调整;

  • 同步跳转宽度(SJW):定义位段加长或缩短的上限,它可以在 1 到 4 个Tq之间调整;

目标波特率是500khz,设:
B S 1 + B S 2 + S J W = T BS1+BS2+SJW = T BS1+BS2+SJW=T
根据:
6 M h z / T = 6000 k h z / T = 500 k h z 6Mhz/T = 6000khz / T = 500khz 6Mhz/T=6000khz/T=500khz
计算出:
T = 12 T = 12 T=12
最后在BS1、BS2、SJW的每个范围内,调整出和为12即可,本文配置如下:

使能CAN1控制器接收中断

配置时钟树

STM32F407ZGT6的最高主频到168M,使HCLK = 168Mhz即可:
在这里插入图片描述

生成工程设置

代码生成设置

最后设置生成独立的初始化文件:

生成代码

点击GENERATE CODE即可生成MDK-V5工程:

4. 编写收发测试程序

4.1. 重定向printf到串口1

/* USER CODE BEGIN 1 */

#if 1

#include


int fputc(int ch, FILE *stream)

{

    /* 堵塞判断串口是否发送完成 */

    while((USART1->SR & 0X40) == 0);


    /* 串口发送完成,将该字符发送 */

    USART1->DR = (uint8_t) ch;


    return ch;

}

#endif

/* USER CODE END 1 */


具体参考这篇博客:STM32CubeMX_09 | 重定向printf函数到串口输出的多种方法


4.2. 编写CAN1收发测试代码

①编写CAN过滤器配置函数

在 CAN 协议中,发送节点将报文广播给所有接收器。而接收节点会根据报文标识符的值来确定节点是否需要该消息,为了简化软件的工作, STM32 的 CAN 外设接收报文前会先使用过滤器检查,只接收需要的报文到 FIFO 中。


STM32的CAN控制器一共有 28 个过滤器,CAN1 和 CAN2 共用这些过滤器。


CAN过滤器结构体定义在stm32f4xx_hal_can.h文件中,在main.c中编写CAN过滤器配置函数(不进行任何过滤):


/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

/* CAN过滤器配置函数 */

static void CANFilter_Config(void)

{

    CAN_FilterTypeDef  sFilterConfig;

    

    sFilterConfig.FilterBank = 0;                       //CAN过滤器编号,范围0-27

    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;   //CAN过滤器模式,掩码模式或列表模式

    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;  //CAN过滤器尺度,16位或32位

    sFilterConfig.FilterIdHigh = 0x000 << 5; //32位下,存储要过滤ID的高16位

    sFilterConfig.FilterIdLow = 0x0000; //32位下,存储要过滤ID的低16位

    sFilterConfig.FilterMaskIdHigh = 0x0000; //掩码模式下,存储的是掩码

    sFilterConfig.FilterMaskIdLow = 0x0000;

    sFilterConfig.FilterFIFOAssignment = 0; //报文通过过滤器的匹配后,存储到哪个FIFO

    sFilterConfig.FilterActivation = ENABLE;    //激活过滤器

    sFilterConfig.SlaveStartFilterBank = 0;

    

    if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {

        printf("CAN Filter Config Fail!rn");

        Error_Handler();

    }

    printf("CAN Filter Config Success!rn");


}

/* USER CODE END 0 */


其中,不同配置模式下四个数据成员内容对应的含义:

② 定义接收和发送消息变量

在main.c文件中定义CAN接收和发送消息变量:


/* Private variables ---------------------------------------------------------*/


/* USER CODE BEGIN PV */

static CAN_TxHeaderTypeDef        TxMessage;    //CAN发送的消息的消息头

static CAN_RxHeaderTypeDef        RxMessage;    //CAN接收的消息的消息头

/* USER CODE END PV */


③ 编写CAN接收中断处理函数

在main.c文件的最后编写CAN接收中断处理函数:


/* USER CODE BEGIN 4 */

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)

{

    uint8_t  data[8];

    HAL_StatusTypeDef status;

    

    if (hcan == &hcan1) {

        status = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);

        if (HAL_OK == status){                             

            printf("--->Data Receieve!rn");

            printf("RxMessage.StdId is %#xrn",  RxMessage.StdId);

            printf("data[0] is 0x%02xrn", data[0]);

            printf("data[1] is 0x%02xrn", data[1]);

            printf("data[2] is 0x%02xrn", data[2]);

            printf("data[3] is 0x%02xrn", data[3]);

            printf("<---rn");

            

        }

    }

}

/* USER CODE END 4 */


④ 编写CAN发送测试数据函数

/* CAN 发送数据测试函数 */

void CAN1_Send_Test()

{

    uint8_t data[4] = {0x01, 0x02, 0x03, 0x04};

    

    TxMessage.IDE = CAN_ID_STD;     //设置ID类型

TxMessage.StdId = 0x222;        //设置ID号

    TxMessage.RTR = CAN_RTR_DATA;   //设置传送数据帧

TxMessage.DLC = 4;              //设置数据长度

    

if (HAL_CAN_AddTxMessage(&hcan1, &TxMessage, data, (uint32_t*)CAN_TX_MAILBOX0) != HAL_OK) {

        printf("CAN send test data fail!rn");

        Error_Handler();

    }

    printf("CAN send test data success!rn");

}


⑤ 编写初始化函数

修改main函数,在其中配置CAN滤波器、启动CAN控制器、使能CAN控制器接收中断:


 /* USER CODE BEGIN 2 */

    printf("----- CAN Test Board #1 -----rn");

    

    /* 1. CAN Filter Config */

    CANFilter_Config();

    

    /* 2. CAN Start */

    if (HAL_CAN_Start(&hcan1) != HAL_OK) {

        printf("CAN Start Failrn");

        Error_Handler();

    }

    printf("CAN Start Successrn");

    

    /* 3. Enable CAN RX Interrupt */

    if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {

        printf("CAN_IT_RX_FIFO0_MSG_PENDING Enable Failrn");

        Error_Handler();

    }

    printf("CAN_IT_RX_FIFO0_MSG_PENDING Enable Successrn");

    

  /* USER CODE END 2 */


⑥ 在main函数中循环发送测试数据

在while(1)中循环发送测试数据函数:


  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

    /* USER CODE END WHILE */


    /* USER CODE BEGIN 3 */

      /* 4. Send Data */

      CAN1_Send_Test();

      

      HAL_Delay(5000);


  }

  /* USER CODE END 3 */


至此,1#测试板的配置和代码编写完成。


5. 编写2#测试板的代码

第二块板的流程和第一块板的流程几乎相同,有几点需要改动。


① STM32F767的 PCLK1 = 54Mhz,所以要配置CAN控制器的分频系数为9,其余参数不变:


② STM32F767开发板默认CAN控制器的接收引脚不匹配,需要修改到PA11:

③ 在代码中将打印信息修改为2#实验板:

④ 在代码中修改发送数据包的ID号,为1#实验板的ID:

其余地方一样。

6. 实验结果

将两个程序分别编译、下载到开发板中,使用串口助手查看结果。

1#测试板(ID:0x111)的串口打印日志为:

2#测试板(ID:0x222)的串口打印日志为:


关键字:STM32CubeMX  CAN总线 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic543344.html

上一篇:mbedtls | 10 - 数字证书及 X.509 证书标准
下一篇:使用Keil MDK以及标准外设库创建STM32工程

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

玩转STM32CubeMX | 跑马灯
如下:3.软件设计3.1 STM32CubeMX设置➡️RCC设置外接HSE,时钟设置为72M➡️PC0和PC2设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平➡️输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码3.2 MDK-ARM软件编程➡️在gpio.c文件中可以看到PC0/PC1管脚的初始化函数1void MX_GPIO_Init(void)2{3  GPIO_InitTypeDef GPIO
发表于 2021-08-31
玩转<font color='red'>STM32CubeMX</font> | 跑马灯
玩转STM32CubeMX | DAC数模转换
* USART1串口* DAC_OUT1(PA4)* K_UP和K_DOWN按键3.软件设计3.1 STM32CubeMX设置➡️ RCC设置外接HSE,时钟设置为72M➡️ PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平➡️ USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位➡️ PA0设置为GPIO输入模式、下拉模式;PE3设置为GPIO输入模式、上拉模式➡️ 激活DAC_OUT1,关闭输出缓冲,不使用触发功能➡️输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as
发表于 2021-08-31
玩转<font color='red'>STM32CubeMX</font> | DAC数模转换
玩转STM32CubeMX | SPI总线
的时候必须要有4K以上的SRAM(可以开辟4K的缓冲区)。W25Q64的擦写周期多达10万次,具有20年的数据保存期限。下表是W25QXX的常用命令表2.硬件设计D1指示灯用来提示系统运行状态,K_UP按键用来控制W25Q64数据写入,K_DOWN按键用来控制W25Q64数据读取,串口1用来打印写入和读取的数据信息*指示灯D1*USART1串口*W25Q64*K_UP和K_DOWN按键3.软件设计3.1 STM32CubeMX设置➡️ RCC设置外接HSE,时钟设置为72M➡️ PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平➡️ USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit
发表于 2021-08-31
玩转<font color='red'>STM32CubeMX</font> | SPI总线
玩转STM32CubeMX | 红外遥控
是将遥控发射器发来的红外光信好转换成电信号,再放大、限幅、 检波、整形,形成遥控指令脉冲,输出至遥控微处理器。由于红外接收头在没有脉冲的时候为高电平,当收到脉冲的时候为低电平,所以可以通过外部中断的下降沿触发中断,在中断内通过计算高电平时间来判断接收到的数据是0还是1。2.硬件设计D1指示灯用来提示系统运行状态,红外遥控器用来发射红外键值的编码信号,通过红外接收头进行解码,并将解码后的数据通过串口1打印输出指示灯D1登录后复制 USART1串口红外外遥控器和红外接收头TIM7(提供us延时)3.软件设计3.1 STM32CubeMX设置➡️ RCC设置外接HSE,时钟设置为72M➡️ PC0设置为GPIO推挽输出模式、上拉
发表于 2021-08-31
玩转<font color='red'>STM32CubeMX</font> | 红外遥控
玩转STM32CubeMX | 开发环境
2.STM32CubeMX软件简介及安装2.1 STM32CubeMX软件简介STM32CubeMX是ST意法半导体推出的STM32芯片图形化配置工具,目的就是为了方便开发者,允许用户使用图形化向导生成C 初始化代码,可以大大减轻开发工作,时间和费用,提高开发效率。STM32CubeMX几乎覆盖了STM32 全系列芯片。在CubeMX上,通过傻瓜化的操作便能实现相关配置,最终能够生成C语言代码,支持多种工具链,比如MDK、IAR For ARM、TrueStudio等,省去了我们配置各种外设的时间,大大的节省了时间2.2 STM32CubeMX软件获取通过STM32CubeMX的官网获取软件 http://www.st.com/en
发表于 2021-08-31
玩转<font color='red'>STM32CubeMX</font> | 开发环境
玩转STM32CubeMX | 待机唤醒
使用的外设的时钟本实验仅对STM32的最低功耗模式(即待机模式)来做介绍。待机模式可实现STM32的最低功耗,该模式实在CM3深睡眠模式时关闭电压调节器,整个1.8V供电区域被断电,PLL/HSI/HSE振荡器也被断电,SRAM和寄存器内容丢失,仅备份的寄存器和待机电路维持供电下图为STM32进入及退出待机模式的条件:2.硬件设计本实验用D1指示灯提示系统正常运行,指示灯熄灭表示进入待机模式,K_UP按键用来唤醒待机模式,并使用串口1打印相关调试信息*指示灯D1*USART1串口*K_UP按键3.软件设计3.1 STM32CubeMX设置➡️ RCC设置外接HSE,时钟设置为72M➡️ PC0设置为GPIO推挽输出模式、上拉、高速
发表于 2021-08-31
玩转<font color='red'>STM32CubeMX</font> | 待机唤醒
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2021 EEWORLD.com.cn, Inc. All rights reserved