历史上的今天
返回首页

历史上的今天

今天是:2025年07月30日(星期三)

正在发生

2021年07月30日 | STM32CubeMX | 36 - 使用CAN总线进行双板通信(TJA1050)

2021-07-30 来源:eefocus

本篇详细的记录了如何使用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)的串口打印日志为:


推荐阅读

史海拾趣

博通集成(BEKEN)公司的发展小趣事

为了加快市场拓展和技术创新步伐,博通集成积极寻求与产业链上下游企业的战略合作。公司与多家知名企业建立了紧密的合作关系,共同推动无线通讯技术的发展和应用。通过战略合作,博通集成不仅获得了更多的市场资源和技术支持,还实现了与合作伙伴的共赢发展。

Benchmarq Microelectronics Inc公司的发展小趣事

在半导体行业中,供应链的稳定性和可靠性对企业的发展至关重要。Benchmarq Microelectronics Inc深知这一点,因此始终注重供应链管理和合作伙伴关系的建立。公司与多家知名供应商建立了长期稳定的合作关系,确保原材料的稳定供应和质量可控。同时,公司还加强库存管理和物流配送,确保产品能够及时、准确地送达客户手中。这些努力不仅提升了公司的运营效率和市场竞争力,还为客户提供了更加便捷、高效的服务。

Axiohm公司的发展小趣事

随着环保意识的日益增强,Axiohm公司积极响应国家绿色发展的号召,将环保理念融入产品设计和生产过程中。公司投入大量资金研发环保材料和生产工艺,成功推出了一系列绿色电子产品。这些产品不仅符合环保标准,还具有良好的性能价格比,受到了消费者的广泛好评。Axiohm因此赢得了业界的赞誉和政府的支持。

Digitron公司的发展小趣事

Digitron公司非常重视产品的品质和售后服务。公司建立了严格的质量控制体系,确保每一台出厂的设备都符合高标准的质量要求。同时,Digitron公司还提供全方位的售后服务,包括技术支持、维修保养等,确保客户在使用过程中得到及时、有效的帮助。这种对品质的坚持和对客户的关怀使Digitron公司在客户中树立了良好的口碑。

富瀚(Fullhan)公司的发展小趣事
集成了过载保护、短路保护、缺相保护、逆相保护等多种功能于一体,满足不同场景下的保护需求。
Gaomi Xinghe Electronics公司的发展小趣事

背景:在21世纪初,Galaxy公司凭借其在电子产品领域的深厚积累,决定进一步拓展国际市场。通过详细的市场调研,公司发现欧洲市场潜力巨大,特别是对高质量电子产品的需求日益增长。

行动:于是,Galaxy在波兰Cracow成立了欧洲分公司,地处欧洲中部,这一地理位置使其能够很好地覆盖整个欧洲大陆,包括东部和西部的国家。该分公司不仅作为销售中心,还承担起返修件回收处理的重任,确保客户能够享受到无时区差别的销售和维修服务。

成果:这一战略部署显著提升了Galaxy在欧洲市场的知名度和竞争力,市场份额逐年攀升,为公司的全球化进程奠定了坚实基础。

问答坊 | AI 解惑

硅技术引领汽车设计时代

摘  要:随着科技的不断向前发展,汽车电子化程度也越来越高,半导体技术也随之崛起。本文详尽的描述了硅技术的进步,微控制器在汽车应用上的发展以及硅产品在汽车网络所发挥的巨大潜力。最后作者希望汽车制造商和半导体生产商能够密切合作为 ...…

查看全部问答>

FOCS在煤调自动化系统中的应用

湘潭钢铁集团公司(以下简称湘钢)煤气调度系统在改造前使用的都是 型淘汰仪表,截至改造前安装的 /0 块仪表因!电缆等原因已全部瘫痪。“六五”以来湘钢经过几次大的改造煤气用户大量增加,煤气测量点由原来的 12 多点已增至近/22 点,显然现有的煤 ...…

查看全部问答>

建议一点

建议把资料共享区和技术交流区分开,通常下资料的都只是灌水,和技术交流混在一起感觉不便于聚集人气!…

查看全部问答>

2009年基本仪器和主要元器件清单20090827

本帖最后由 paulhyde 于 2014-9-15 09:44 编辑 2009年基本仪器和主要元器件清单20090827  …

查看全部问答>

vxworks辅助时钟定时问题,急急急急

sysAuxClkRateSet(int rate)函数中,rate只能设成(2,4,8,16,32,64,128,..,1024等等),我想精确定时到1ms或5ms、10ms该怎么办,或者有其它方法吗,请大家帮忙!…

查看全部问答>

自己动手创建一个基于万利STM32板的IAR工程

    圈圈前面几个STM32的程序是直接拿例子来改的,但我们总不能每次都拿别人的例子来改吧?我们要学会如何自己来创建一个属于自己的IAR工程。    首先启动IAR开发环境。如果你的设置是在启动时出现Em ...…

查看全部问答>

TDK 8G U盘到啦!晒一下!

祝我们的论坛越办越好!! [ 本帖最后由 anananjjj 于 2012-4-23 13:16 编辑 ]…

查看全部问答>

关于PID运算,一种10个时钟周期的另一种算法。

首先是@dontium 。共同讨论C2000系列中的CLA的一个计算方法。 图片截取自网络。其中第一项是比例项,第二项是积分项,第三项是微分项。对应的系数是kp,ki,kd。然后根据这个表达式的恒等变换可以得到最后一个表达式。如果计算第一个表达式。每次 ...…

查看全部问答>

AD转换检测的问题

AD转换时怎么检测上升沿下降沿,我是想让电压上升的时候让灯亮着,下降的时候灯灭,电压变化范围在1.2V~2.0V,是把先后采集到的数据进行比较吗,怎么把采集到的电压值储存起来再比较 …

查看全部问答>