通信协议与接口知识参考文章:
【通信理论知识】数据传送的方式:串/并行;传输方向:单工、半/全双工;传输方式:同步/异步
【串口通信详解】USART/UART、RS232、RS485标准接口与协议特点解析
【同步串行通信接口】IIC(Inter-Integrated Circuit)通信总线协议详解
【通信总线协议】SPI串行外设接口通信详解
【CAN总线协议】CAN通信入门总览:常见协议优劣、CAN应用、协议组成与标准、传输原理的实现、仲裁机制、传输与时序初探
【CAN总线协议】CAN接收报文(协议层:帧的五个种类、仲裁机制、错误的种类、位填充与位时序、同步方法;)
进阶:
【通信模块】简单玩转WiFi模块(ESP32、ESP8266)
【通信模块】WiFi&Bluetooth简介与对比
【通信模块】LoRa与LoRaWAN简介
【通信协议】硬件通信协议速率对比
目录
AT32 MCU CAN 介绍
- 整体功能介绍
- CAN 发送流程
2.1 CAN 发送步骤
2.2 标志位和操作位说明- CAN 接收流程
3.1 CAN 常用接收步骤
3.2 用户不参与操作时硬件流程
3.3 标志位和操作位说明- 过滤器
4.1 过滤器的位宽
4.2 过滤器模式- CAN 波特率及采样点计算
5.1 波特率计算公式
典例
5.2 采样点计算公式
典例
5.3 波特率计算工具(AT32专用)初始化配置及收发源码
AT32 MCU CAN 介绍
主要介绍 AT32 CAN 的主要设计结构和使用,介绍了 AT32 CAN 的正常通信流程,包括发送流程、接收流程、报文过滤、波特率及采样点设置等。其他 AT32 CAN 相关设计,例如错误管理、中断管理等,请参考 RM 相关章节.
AT32 的 CAN 支持标准 CAN 协议 2.0A 和 2.0B,且在兼容标准 CAN 协议的基础上增加了一些功能和可配置选项。
CAN 2.0A 和 2.0B 差别:
前者仅支持 11bit ID,即只支持标准帧;后者支持 11bit/29bit ID,即支持标准帧和扩展帧。
1. 整体功能介绍
随着 CAN 网络节点和报文数量的增加,需要一个增强的过滤机制处理各种类型的报文,减少接收报文的处理时间,采用 FIFO 的方案,使得 CPU 可以长时间处理应用层任务而不会丢失报文。同时发送报文由硬件控制发送优先级顺序。
基于以上考虑,CAN 控制器提供 28 组位宽可配置的标识符过滤器组,2 个接收 FIFO,每个 FIFO 都可以存放 3 个完整的报文。共有 3 个发送邮箱,发送调度器决定发送优先级顺序。整个收发过程完全由硬件管理,无需占用 CPU 资源。
2. CAN 发送流程
2.1 CAN 发送步骤
用户使用时只需操作 1)~3),而 4)~7)由硬件自动完成,无需用户代码参与,不占用 CPU 资源。
1)程序选择 1 个空置的邮箱(发送邮箱空标志 TMxEF=1)
2)将需要发送的报文写入对应的空邮箱。报文内容包含: ID、帧类型、数据长度和发送数据等
3)请求发送:将 CAN_TMIx 的 TMSR 位置 1
4)邮箱挂号(等待成为最高优先级)
5)预定发送(等待总线空闲)
6)发送
7)邮箱空置
注:以上步骤 1)~7)只简单介绍正常发送流程,下图中还包含取消发送、发送失败、自动/不自动重传等情况,
可参考 RM 文件报文发送一节,这里不再详述。
2.2 标志位和操作位说明
下图 10 中标志位和操作位说明如下:
TMxTCF: 请求完成标志位(发送/中止请求)
TMxTSF: 发送成功标志位
TMxEF: 发送邮箱空标志位
TMSR: 请求发送
TMxCT: 中止发送
PRSFEN: 禁止自动重传(PRSFEN =1 时,禁止自动重传;PRSFEN=0 时,自动重传直到发送成功)
3. CAN 接收流程
3.1 CAN 常用接收步骤
CAN 常用接收流程如下,即下图的**“空”和“挂号_1”两个状态间循环**:
1)FIFO 空
2)收到有效报文
3)进入“挂号_1”状态(FIFO 内有 1 条有效报文的状态)
4)读取有效报文:读取接收邮箱寄存器(CAN_RFIx,CAN_RFCx,CAN_RFDTLx,CAN_RFDTHx)。
5)释放邮箱:CAN_RFx 寄存器 RFxR 位置 1。
注:用户使用时只需操作 4)~5)。1)~3)由硬件自动完成,无需用户代码参与,不占用 CPU 资源。
有效报文:
当报文被正确接收(直到 EOF 域的最后一位都没有错误),且通过了标识符过滤,那么该报文被认为是有效报文。过滤器相关介绍见下一节。
3.2 用户不参与操作时硬件流程
而如果接收过程中用户不参与操作(即不去读取有效报文和释放邮箱),硬件流程如下:
1)收到有效报文
2)进入“挂号_1”状态(FIFO 内有 1 条有效报文的状态)
3)收到有效报文
4)进入“挂号_2”状态(FIFO 内有 2 条有效报文的状态)
5)收到有效报文
6)进入“挂号_3”状态(FIFO 内有 3 条有效报文的状态)
7)收到有效报文
8)进入“溢出”状态(FIFO 内有 3 条有效报文,丢失了一条报文,溢出标志置起)
3.3 标志位和操作位说明
下图中标志位和操作位说明如下:
RFxMN: FIFO 内有效报文数量(取值 0~3)
RFxOF: 溢出标志位
RFxR: 释放邮箱
4. 过滤器
在 CAN 协议里,报文的 ID 不代表节点的地址,而是跟报文的内容相关的。
因此,发送者以广播的形式把报文发送给所有的接收者。
节点在接收报文时,根据 ID 的值决定软件是否需要该报文;
如果需要,就存到接收 FIFO 里,用户可通过软件读取接收邮箱寄存器获取该报文;
如果不需要,报文就被丢弃且无需软件的干预。
为满足这一需求,AT32 CAN 控制器为应用程序提供了 28 个硬件过滤器组(AT32F435 系列有 28 个过滤器组,0~27;但 AT32F403A 等系列只有 14 个过滤器组,0~13。具体请参考相应型号的 RM),以便只接收那些软件需要的报文。用户配置好需要的 ID 后,整个过滤过程无需软件参与,不占用CPU 资源。
4.1 过滤器的位宽
每个过滤器组由 2 个 32bit 的寄存器,CAN_FiFB1 和 CAN_FiFB2 组成。
通过配置 CAN_FBWCFG寄存器的 FBWSELx 位,可以设置 2 个 16 位宽或者 1 个 32 位宽的过滤器。
32 位宽的过滤器寄存器 CAN_FiFBx 包括:一组 SID[10:0]、EID[17:0]、IDT 和 RTR 位。
16 位宽的过滤器寄存器 CAN_FiFBx 包括:两组 SID[10:0]、IDT、RTR 和 EID[17:15]位。
4.2 过滤器模式
通过设置 CAN_FMCFG 寄存器的 FMSELx 位可以设置过滤器寄存器工作在标识符掩码模式或者标识符列表模式。
掩码模式用来指定 ID 的哪些位需要与预设 ID 相同,哪些位无需比较;
列表模式表示 ID的每个位都必须与预设 ID 一致。
两种模式与过滤器位宽配合使用,可以有以下四种过滤方式:
更多 CAN 过滤器说明,例如 CAN 过滤器匹配序号,优先级规则等可参考 RM 文件报文过滤一节,这里不再详述。
过滤器配置流程见后文案例介绍 – CAN 接收过滤器使用。
5. CAN 波特率及采样点计算
如前文 CAN 位格式一节所述,CAN 的一个 bit 被分为几段。
其中第一段同步段(SYNC_SEG)固定为 1Tq,1Tq 的长度由 CAN 位时序寄存器 (CAN_BTMG)的分频系数 BRDIV[11:0]位定义;
位段1(BSEG1)通过配置 CAN 位时序寄存器的 BTS1[3:0]位,可设定为 1~16Tq;
位段 2(BSEG2)通过配置 CAN 位时序寄存器的 BTS2[2:0]位,可设定为 1~8Tq。
用户通过配置 CAN 时序寄存器,可设置 CAN 波特率和采样点,整个 CAN 总线上各节点的波特率和采样点一致最佳,不过由于各节点主频可能不一样,所以比较难保证波特率和采样点均一致。
用户使用时应首先保证波特率一致,采样点尽量保持在较小的偏差范围内,这样 CAN 总线可以支持更多的节点和更长的线路。
5.1 波特率计算公式
其中
典例
bsp 例程 project\at_start_f437\examples\can\communication_mode
5.2 采样点计算公式
典例
同上bsp例程
关于采样点设置,CAN 协议并没有明确规定,但根据各厂商 CAN 设备使用习惯,采样点设置建议如下:
5.3 波特率计算工具(AT32专用)
为方便用户波特率设定,AT32制造厂商雅特力制作了一个 AT 专用波特率计算工具;
点击进入官网即可免费下载(技术开发与支持->Tool->下拉最底下):
雅特力科技 : 32位微控制器的创新领导者! (arterytek.com)
使用步骤如下:
1) 波特率设定:高速 CAN 波特率最大为 1M,各厂商 CAN 设备常用波特率为 125K、250K、333K、500K、1M 等。用户可根据需要设定波特率。参考下图 16“波特率(Kbit/S)”。
2) CAN 时钟源频率设定:参考下图 16“PCLK1(MHZ)”。
3) 采样位置设置:设置完波特率后,计算工具会自动填入一个推荐的采样位置值。
若实际项目中无具体限定,可保持默认设定;
若项目中有具体限定,根据需求更改即可。参考下图 16“采样位置(%)”。
4) 波特率偏差设定:建议在不勾选“允许波特率偏差”项,仅在没有符合要求的计算结果时,再勾选此项。
由于同一 CAN 网络的节点波特率有误差会增大通信错误几率,建议“偏差”值设置尽量小。
参考下图 16“允许波特率偏差”和“偏差”。
5) 波特率配置结果选择:根据以上设定即可计算出多组结果。
在页面左下角选择一项计算结果,即会在页面右下角显示对应软件代码配置,点击“复制全部”即可获得对应代码。
初始化配置及收发源码
/**
**************************************************************************
* @file main.c
* @brief main program
**************************************************************************
* Copyright notice & Disclaimer
*
* The software Board Support Package (BSP) that is made available to
* download from Artery official website is the copyrighted work of Artery.
* Artery authorizes customers to use, copy, and distribute the BSP
* software and its related documentation for the purpose of design and
* development in conjunction with Artery microcontrollers. Use of the
* software is governed by this copyright notice and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
* GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
* TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
* STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
* INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
*
**************************************************************************
*/
#include "at32a423_board.h"
#include "at32a423_clock.h"
/** @addtogroup AT32A423_periph_examples
* @{
*/
/** @addtogroup 423_CAN_communication_mode CAN_communication_mode
* @{
*/
/**
* @brief can gpio config
* @param none
* @retval none
*/
static void can_gpio_config(void)
{
gpio_init_type gpio_init_struct;
/* enable the gpio clock */
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_init_struct);
/* configure the can tx, rx pin */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_pins = GPIO_PINS_9 | GPIO_PINS_8;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOB, &gpio_init_struct);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE9, GPIO_MUX_9);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE8, GPIO_MUX_9);
}
/**
* @brief can configiguration.
* @param none
* @retval none
*/
static void can_configuration(void)
{
can_base_type can_base_struct;
can_baudrate_type can_baudrate_struct;
can_filter_init_type can_filter_init_struct;
/* enable the can clock */
crm_periph_clock_enable(CRM_CAN1_PERIPH_CLOCK, TRUE);
/* can base init */
can_default_para_init(&can_base_struct);
can_base_struct.mode_selection = CAN_MODE_COMMUNICATE;
can_base_struct.ttc_enable = FALSE;
can_base_struct.aebo_enable = TRUE;
can_base_struct.aed_enable = TRUE;
can_base_struct.prsf_enable = FALSE;
can_base_struct.mdrsel_selection = CAN_DISCARDING_FIRST_RECEIVED;
can_base_struct.mmssr_selection = CAN_SENDING_BY_ID;
can_base_init(CAN1, &can_base_struct);
/* can baudrate, set boudrate = pclk/(baudrate_div *(1 + bts1_size + bts2_size)) */
can_baudrate_struct.baudrate_div = 6;
can_baudrate_struct.rsaw_size = CAN_RSAW_3TQ;
can_baudrate_struct.bts1_size = CAN_BTS1_8TQ;
can_baudrate_struct.bts2_size = CAN_BTS2_3TQ;
can_baudrate_set(CAN1, &can_baudrate_struct);
/* can filter init */
can_filter_init_struct.filter_activate_enable = TRUE;
can_filter_init_struct.filter_mode = CAN_FILTER_MODE_ID_MASK;
can_filter_init_struct.filter_fifo = CAN_FILTER_FIFO0;
can_filter_init_struct.filter_number = 0;
can_filter_init_struct.filter_bit = CAN_FILTER_32BIT;
can_filter_init_struct.filter_id_high = 0;
can_filter_init_struct.filter_id_low = 0;
can_filter_init_struct.filter_mask_high = 0;
can_filter_init_struct.filter_mask_low = 0;
can_filter_init(CAN1, &can_filter_init_struct);
/* can interrupt config */
nvic_irq_enable(CAN1_SE_IRQn, 0x00, 0x00);
nvic_irq_enable(CAN1_RX0_IRQn, 0x00, 0x00);
can_interrupt_enable(CAN1, CAN_RF0MIEN_INT, TRUE);
/* error interrupt enable */
can_interrupt_enable(CAN1, CAN_ETRIEN_INT, TRUE);
can_interrupt_enable(CAN1, CAN_EOIEN_INT, TRUE);
}
/**
* @brief can transmit data
* @param none
* @retval none
*/
static void can_transmit_data(void)
{
uint8_t transmit_mailbox;
can_tx_message_type tx_message_struct;
tx_message_struct.standard_id = 0x400;
tx_message_struct.extended_id = 0;
tx_message_struct.id_type = CAN_ID_STANDARD;
tx_message_struct.frame_type = CAN_TFT_DATA;
tx_message_struct.dlc = 8;
tx_message_struct.data[0] = 0x11;
tx_message_struct.data[1] = 0x22;
tx_message_struct.data[2] = 0x33;
tx_message_struct.data[3] = 0x44;
tx_message_struct.data[4] = 0x55;
tx_message_struct.data[5] = 0x66;
tx_message_struct.data[6] = 0x77;
tx_message_struct.data[7] = 0x88;
transmit_mailbox = can_message_transmit(CAN1, &tx_message_struct);
while(can_transmit_status_get(CAN1, (can_tx_mailbox_num_type)transmit_mailbox) != CAN_TX_STATUS_SUCCESSFUL);
}
/**
* @brief can1 interrupt function rx0
* @param none
* @retval none
*/
void CAN1_RX0_IRQHandler(void)
{
can_rx_message_type rx_message_struct;
if(can_interrupt_flag_get(CAN1,CAN_RF0MN_FLAG) != RESET)
{
can_message_receive(CAN1, CAN_RX_FIFO0, &rx_message_struct);
if(rx_message_struct.standard_id == 0x400)
at32_led_toggle(LED2);
else
at32_led_toggle(LED3);
}
}
/**
* @brief can1 interrupt function se
* @param none
* @retval none
*/
void CAN1_SE_IRQHandler(void)
{
if(can_interrupt_flag_get(CAN1,CAN_ETR_FLAG) != RESET)
{
can_flag_clear(CAN1, CAN_ETR_FLAG);
}
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
system_clock_config();
at32_board_init();
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
can_gpio_config();
can_configuration();
while(1)
{
can_transmit_data();
at32_led_toggle(LED4);
delay_sec(1);
}
}
/**
* @}
*/
/**
* @}
*/