22、STM32 CAN总线应用与软件配置详解

STM32 CAN总线应用与软件配置详解

1. CAN总线消息格式

CAN总线非扩展消息格式包含多个字段,各字段的位长度和描述如下表所示:
| 字段名称 | 位长度 | 描述 |
| — | — | — |
| SOF | 1 | 帧起始 |
| ID | 11 | 消息ID/优先级 |
| RTR | 1 | 远程传输请求:数据帧为0,远程请求为1 |
| IDE | 1 | 标识符扩展:11位格式为0,29位为1 |
| Reserved | 1 | 必须为0 |
| DLC | 4 | 数据长度码:0到8字节 |
| Data | 0 - 64 | 传输的数据(DLC设置长度) |
| CRC | 15 | 循环冗余校验 |
| CRC delimiter | 1 | 必须为1(隐性) |
| ACK slot | 1 | 发送器发送1(隐性),接收器响应0(显性) |
| ACK delimiter | 1 | 必须为1(隐性) |
| EOF | 1 | 帧结束:1(隐性) |

2. STM32的局限性

STM32 CAN总线外设使用SRAM存储消息,但STM32F103 MCU的设计使得CAN和USB共享同一内存区域,因此CAN和USB不能同时使用。虽然可以禁用一个设备来使用另一个,但这并不实际,所以演示将使用UART进行通信。

3. 演示设置

本次演示将实现三个假设的电子单元(EU):
1. EU1 :仪表盘控制单元(具有UART接口),提供灯光控制并从后部的EU3读取温度。
2. EU2 :后部控制器单元,负责停车灯、信号灯和刹车灯。
3. EU3 :前部控制器单元,负责前部停车灯和信号灯。

完整演示需要三个STM32 MCU,但如果只有两个,也可以部分演示操作,不过三个效果最佳。必要时可以省略前部的EU2。

4. 软件构建

演示的软件目录位于:

$ cd stm32f103c8t6/rtos/can

重新编译的命令如下:

$ make clobber
$ make

这将编译出三个可执行文件:

$ ls *.elf
front.elf    main.elf    rear.elf
5. UART接口

EU1是主控制单元,模拟位于假设车辆仪表盘后面的设备(模块main.c)。演示配置的串口参数如下,必须与minicom或你选择的终端程序一致:
- 波特率:115,200
- 数据位:8
- 奇偶校验:无
- 停止位:1
- 硬件流控制:RTS/CTS

硬件流控制需要将RTS和CTS线连接到TTL串口适配器和STM32,连接后,终端程序也必须配置为使用硬件流控制。如果任何细节不正确,将无法进行通信。如果流控制因某种原因无法工作,可能会出现数据丢失和乱码。

如果硬件流控制有问题或TTL串口适配器缺少所需的RTS/CTS信号,可以将main.c中的以下源行:

open_uart(1,115200,"8N1","rw",1,1);

改为:

open_uart(1,9600,"8N1","rw",0,0);

重新编译后,如果仍有数据丢失,可进一步降低波特率。否则,9,600的较低波特率应该没问题。

6. MCU烧录

本次演示需要烧录三个MCU:
1. EU1:main.c
2. EU2:front.c
3. EU3:rear.c

烧录设备的命令如下(构建后):

$ make flash
$ make flash_front
$ make flash_rear
7. 演示总线

为了简化操作,本次演示使用带有4.7 kohm上拉电阻的短单总线CAN(SWCAN)。在这种配置下,每个STM32 MCU的CAN_RX和CAN_TX线连接在一起并连接到公共总线。通常,这些连接会连接到像PCA82C251这样的CAN总线驱动芯片,具有单独的RX和TX连接。

电阻R1是建立隐性状态所需的上拉电阻,由于MCU是3.3伏设备,隐性状态接近+3.3伏。由于上拉电阻,我们将CAN_TX的GPIO配置为GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN(强调开漏)。其他MCU共享总线和接地,不要忘记将这些接地连接在一起,一个MCU发送的任何消息都会被其他所有MCU接收。

8. 会话运行

连接好所有设备后,将USB-TTL串口设备连接到PC并启动minicom(或等效程序),然后给面包板供电。主MCU应通过串口链接做出如下响应:

Welcome to minicom 2.7
OPTIONS:
Compiled on Sep 17 2016, 05:53:15.
Port /dev/cu.usbserial-A703CYQ5, 20:38:01
Press Meta-Z for help on special keys
Car simulation begun.
Menu:
  L - Turn on left signals
  R - Turn on right signals
  P - Turn on parking lights
  B - Activate brake lights
  Lower case the above to turn OFF
  V - Verbose mode (show received messages)
CAN Console Ready:
> _

串口链接显示一个菜单,允许你控制假设车辆的各种功能。如果一切正常,前后MCU板载LED(PC13)应大约每秒闪烁一次。如果看到它闪烁两次后停止,这表明总线连接有问题,需要重新检查接线。

以下是一些操作示例:
- 按大写“P”打开停车灯,控制台将返回温度:

CAN Console Ready:
> P
> Temperature: +24.73 C
>

按小写“p”关闭停车灯。
- 按大写“B”打开刹车灯,按小写“b”关闭。
- 按大写“L”或“R”分别打开左或右转向灯,按小写“l”或“r”关闭。也可以通过同时启用左右转向灯来打开四闪灯。
- 先按大写“L”打开左转向灯,再按大写“B”打开刹车灯,此时左转向灯闪烁,右后灯保持亮起表示刹车灯。关闭转向灯后,两个后刹车灯应保持亮起。

9. CAN消息

消息主要从主EU1发送到前后单元。每次灯光请求后,主单元还会向后部发送消息请求温度(设置RTR位请求回复)。当后部单元收到RTR标志为真的消息时,它会读取温度并将其传输到总线。所有其他单元都可以读取此消息,但只有主单元使用该信息。其他消息用于启用/禁用特定灯光,为了使信号灯同步闪烁,还会发送闪烁消息。

10. 同步性

信号灯闪烁时,人眼看起来非常同步,但实际使用示波器测量,信号灯的开关时间变化约为±850 μs,并且会随时间变化。所有MCU同时将消息接收到其外设中,但FreeRTOS进行抢占式调度,由于使用1 ms的滴答,计时可能会有最多1 ms的偏差。

对于工厂控制应用,如果需要更高的精度,可以采取以下方法:
- 增加定时器滴答频率(减少时间片)。
- 在应用程序软件中进行其他改进,例如ISR例程可以通知等待的任务。

具体方法取决于问题的重要性以及你愿意付出的努力。

11. CAN总线软件初始化

项目源模块位于以下目录:

$ cd ~/stm32f103c8t6/rtos/can

CAN总线外设设置中最具挑战性的部分是其配置和初始化。以下是 initialize_can() 函数的代码:

0090: void
0091: initialize_can(bool nart,bool locked,bool altcfg) {
0092:
0093:   rcc_periph_clock_enable(RCC_AFIO);
0094:   rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_CAN1EN);
0095:
0096:   /******************************************************
0097:    * When:
0098:    *    altcfg     CAN_RX=PB8,  CAN_TX=PB9
0099:    *    !altcfg    CAN_RX=PA11, CAN_TX=PA12
0100:    *****************************************************/
0101:   if ( altcfg ) {
0102:       rcc_periph_clock_enable(RCC_GPIOB);
0103:       gpio_set_mode(GPIOB,GPIO_MODE_OUTPUT_50_MHZ,
                GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN,
                GPIO_CAN_PB_TX);
0104:       gpio_set_mode(GPIOB,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,
                GPIO_CAN_PB_RX);
0105:
0106:       gpio_primary_remap(   // Map CAN1 to use PB8/PB9
0107:               AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, // Optional
0108:               AFIO_MAPR_CAN1_REMAP_PORTB);
0109:   } else  {
0110:       rcc_periph_clock_enable(RCC_GPIOA);
0111:       gpio_set_mode(GPIOA,GPIO_MODE_OUTPUT_50_MHZ,
                GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN,GPIO_CAN_TX);
0112:       gpio_set_mode(GPIOA,GPIO_MODE_INPUT,
                GPIO_CNF_INPUT_FLOAT,GPIO_CAN_RX);
0113:
0114:       gpio_primary_remap( // Map CAN1 to use PA11/PA12
0115:           AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, // Optional
0116:       AFIO_MAPR_CAN1_REMAP_PORTA);
0117:   }
0118:
0119:   can_reset(CAN1);
0120:   can_init(
0121:       CAN1,
0122:       false,    // ttcm=off
0123:       false,    // auto bus off management
0124:       true,     // Automatic wakeup mode.
0125:       nart,     // No automatic retransmission.
0126:       locked,   // Receive FIFO locked mode
0127:       false,    // Transmit FIFO priority (msg id)
0128:       PARM_SJW, // Resynch time quanta jump width (0..3)
0129:       PARM_TS1, // segment 1 time quanta width
0130:       PARM_TS2, // Time segment 2 time quanta width
0131:       PARM_BRP, // Baud rate prescaler for 33.333 kbs
0132:       false,    // Loopback
0133:       false);   // Silent
0134:
0135:   can_filter_id_mask_16bit_init(
0137:       0,                      // Filter bank 0
0138:       0x000 << 5, 0x001 << 5,    // LSB == 0
0139:       0x000 << 5, 0x001 << 5,    // Not used
0140:       0,                      // FIFO 0
0141:       true);
0142:
0143:   can_filter_id_mask_16bit_init(
0145:       1,                      // Filter bank 1
0146:       0x010 << 5, 0x001 << 5,    // LSB == 1 (no match)
0147:       0x001 << 5, 0x001 << 5,    // Match when odd
0148:       1,                      // FIFO 1
0149:       true);
0150:
0151:   canrxq = xQueueCreate(33,sizeof(struct s_canmsg));
0152:
0153:   nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
0154:   nvic_enable_irq(NVIC_CAN_RX1_IRQ);
0155:   can_enable_irq(CAN1,CAN_IER_FMPIE0|CAN_IER_FMPIE1);
0156:
0157:   xTaskCreate(can_rx_task,"canrx",400,NULL,
            configMAX_PRIORITIES-1,NULL);
0158: }

该函数按以下基本步骤进行初始化:
1. 启用AFIO子系统的时钟(第93行),以便选择用于CAN总线端口的GPIO。
2. 启用CAN总线外设的时钟(第94行),这是外设正常工作所必需的。
3. 根据布尔参数 altcfg 选择的配置,启用相应GPIO的时钟(第102或110行)。
4. 为CAN_TX选择GPIO输出模式(第103或111行)。
5. 为CAN_RX选择GPIO输入模式(第104或112行)。
6. 为CAN_TX和CAN_RX线选择AFIO映射(第106 - 108行或第114 - 116行)。
7. 调用 libopencm3 例程 can_reset() 初始化和配置CAN总线外设(第119 - 133行)。
8. 配置CAN过滤器组0(第135 - 141行),以确定某些消息的去向。
9. 配置CAN过滤器组1(第143 - 149行),以确定其他消息的去向。
10. 创建一个名为 canrxq 的FreeRTOS接收队列(第151行)。
11. 启用STM32 NVIC的两个中断通道(第153和154行)。
12. 为CAN总线外设的FIFO 0和FIFO 1启用FIFO消息挂起中断(第155行)。
13. 最后,创建一个接收任务(第157行)。

12. can_init() 函数参数详解

can_init() 函数由 libopencm3 提供,需要多个参数来配置设备,各参数如下:
| 参数 | 说明 |
| — | — |
| CAN1 | 指示使用哪个外设,对于STM32F103C8T6只有一个可用。 |
| false | 表示不使用时间触发通信模式。 |
| false | 表示不使用自动总线关闭模式(如果发生太多错误,总线会自动禁用)。 |
| true | 表示如果MCU进入睡眠状态,希望自动唤醒模式。 |
| nart | 当为 true 时,表示检测到错误时,CAN外设不自动重传。 |
| locked | 当为 true 时,接收FIFO满时,新消息不会取代现有消息(FIFO被锁定);为 false 时,FIFO满时新消息可以取代现有消息。 |
| false | 表示根据消息ID为传出消息分配优先级,否则按时间顺序传输消息。 |
| PARM_SJW | 定义CAN同步参数。 |
| PARM_TS1 | 定义CAN同步参数。 |
| PARM_TS2 | 定义CAN同步参数。 |
| PARM_BRP | 在 canmsgs.h 中声明为78,使有效波特率为33.333 kbs。 |
| false | 禁用外设的回环功能。 |
| false | 表示以“正常模式”运行,在静默模式下,外设可以接收远程数据但不能发起消息。 |

13. CAN接收过滤器

CAN总线外设能够过滤感兴趣的消息。想象一个连接了大量设备的总线,会接收到大量的消息流量,通常并非每个节点都对所有消息感兴趣,处理每条消息会消耗CPU资源。

CAN外设支持两个FIFO队列来接收消息,通过过滤,本演示将偶数消息ID的消息安排到FIFO 0,奇数消息ID的消息安排到FIFO 1,这是通过配置过滤器组0和1实现的。

调用 can_filter_id_mask_16bit_init() (第135 - 141行)将一组消息安排到FIFO 0(第140行),此例中的参数2声明了过滤器组0的配置(第137行),最后一个参数 true 表示启用过滤器。

参数3(第138行)和4(第139行)定义了实际的过滤器ID值和要使用的位掩码,这是16位过滤器,但过滤器宽度为32位,因此使用两个相同的过滤器:
- 0x000 是应用掩码后要匹配的结果ID。
- 0x001 是在比较之前应用于ID的位掩码。

两个参数都必须左移5位,以便将11位标识符左对齐到16位字段中。

在第二个配置的过滤器(第143 - 149行)中,有相同的掩码值( 0x001 ),但比较两个不同的ID值:
- 0x010 是“不匹配”的ID。
- 0x001 是掩码后的奇数ID。

14. 总结

通过本次演示,我们了解了CAN总线的基本概念和一个演示电路。运行演示证明了使用短CAN消息可以对其他MCU进行近乎实时的控制,同时也证明了共享总线的概念,其中没有主从设备。最后,我们看到STM32能够在单总线或差分总线模式下应用CAN通信(差分模式需要驱动芯片的帮助)。

整个过程的流程可以用以下mermaid流程图表示:

graph LR
    A[开始] --> B[软件构建]
    B --> C[UART接口配置]
    C --> D[MCU烧录]
    D --> E[演示总线连接]
    E --> F[会话运行]
    F --> G[CAN消息交互]
    G --> H[同步性处理]
    H --> I[CAN总线软件初始化]
    I --> J[CAN接收过滤器配置]
    J --> K[结束]

对于想要深入了解CAN总线应用的开发者来说,可以根据上述内容进一步探索和实践,通过调整参数和优化代码,实现更复杂和高效的CAN总线通信系统。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值