基本帧结构


CAN帧数据详细如上图所示。
1.起始位
在I2C,SPI,USART通信协议中都有起始位。通信线静默状态(无数据时),总线上的电平状态为静默状态。
表示开始就要打破静默状态,总线上的节点知道通信要开始了。启动相应的接收准备。表示帧开始传输

2.标识符 ID
在I2C协议中 发送起始位后,需要发送目标节点的地址,表示数据的归属。
在CAN中没有目标节点地址,ID代表着这条数据的归属。
例如 ,ID0x 01 表示温度。那么所有关心温度的节点都可以识别到这条消息。
根本上来说CAN的数据传输是一种广播,ID 区分了广播的数据和意义。总线上的节点根据ID来接收或者丢弃。
如果是I2C要进行广播,显然要把所有的节点的地址都发一次
同理消息ID有仲裁的消息,在I2C中一样。回读自己发送的ID,一旦不同则仲裁出局,根本上来说是线与。

3.远程传输请求
简单说就是,发起请求。有点像I2C中的read/Write。需要写数据的时候,是要携带数据的。read的时候不需要带数据,告诉对方是read。
发起请求标志。接收到这个请求的节点会回馈这个请求,发送相应的报文。

4.R1,R0
预留,以后CAN协议要扩展,先留下两个坑位,实现兼容。

5.数据长度
有效载荷 数据的长度,也就是一帧数据所携带的真正内容。

6.CRC校验
1-5步骤的数据进行CRC校验

7.ACK
ACK必定是一个非静默电平。表示有人应答。

9.EOF
结束

理解上述基本帧后,再去看扩展帧。这里不叙述扩展帧。

1.邮箱
为什么叫邮箱
CAN 帧的定义实际上是CAN协议的数据链路层的定义。定义了一帧数据的结构,有开头有结尾。所以收发数据的时候,按照这个定义来组包->发送,接收->解包。那么这包就是这里的帧。显然可以定义一种数据结构来表示帧数据,邮箱就是这个数据结构,而且它是硬件实现的寄存器组。多个寄存器按照CAN逻辑链路的定义组成一组。这一组就叫做一个邮箱,邮箱对应的是一帧数据。
把邮箱进行标号,就有邮箱1,邮箱2,邮箱3。邮箱之间的数据排列如果是队列形式的那么就可以叫做FIFO。用于存储接收帧的邮箱组就叫做接收FIFO。

2.筛选器
CAN传输数据是用广播的方式,所有连接在总线上的节点都可以接受到发送端的数据。
所以只要总线上有数据传输,那么就一直接收。这个时候需要判别出哪些数据时本节点需要的。
显然一般的方法是把CAN一帧的数据读进CPU,对比一下ID是不是我要。这很费事。
所以,在硬件上设计了一个对比机制,根本上解放了CPU。CPU只要处理需要的数据。
这个对比机制也就是筛选机制就是 筛选器,筛选的主体就是ID。

筛选器的根本就是队ID处理
a.完整的ID筛选
ID11位完全符合,才会接收入邮箱
b.掩码筛选
ID 与运算 掩码 =掩码 ,表示通过
当然分扩展和基础帧

筛选器的部分应该就没啥问题了。
在CAN模块的框图中,数据流动的箭头表示。通过筛选器的数据才会进入到接收FIFO

CAN的工作模式

有三种主要的工作模式: 初始化、 正常和睡眠。
CAN通信是广播发送,所以相对来说,每个节点都需要接收数据。
CAN不带时钟信号线,所以CAN的节点需要在每次显性电平的跳变沿进行同步和补偿。每个节点的接收模块是比较“忙的”
总线形式是所有节点都连接在一起,向一条马路一样如果有问题,会影响到整条通路的状况。
1.有节能的需求,应为CAN控制器会比较忙。
2.减少节点出错,发送错误的数据

CAN控制器
初始化状态:这个状态允许对控制寄存器进行配置
正常状态:寄存器不能进行配置
睡眠状态:节能


SLAK,INAK 组合用来表示当前状态
SLEEP 睡眠请求
INRQ 初始化请求

  1. ACK = 硬件通过将 CAN_MSR 寄存器的 INAK 或 SLAK 位置 1 来确认请求的等待状态
  2. SYNC = bxCAN 等待 CAN 总线变为空闲(即在 CANRX 上监测到连续 11 个隐性位)的状态
    切换的逻辑如上图所示。

发送处理

发送就是按照CAN帧的定义把相应的数据装填到 数据结构(邮箱)中。然后拍一下邮箱的屁股,说走吧
数据的装填 :标识符、数据长度代码 (DLC) 和数据本身
显然我们只能往一个空的邮箱里面写数据。
然后把 配置 TXRQ: 发送邮箱请求 (Transmit mailbox request) 就让它发送。
如果这个时候,发送正在排队,可以根据ID优先级和邮箱号的顺序进行。

接收处理

为了接收 CAN 消息,提供了构成 FIFO 的三个邮箱。为了节约 CPU 负载,简化软件并保证数据一致性, FIFO 完全由硬件进行管理。应用程序通过 FIFO 输出邮箱访问 FIFO 中所存储的消息。
当消息依据 CAN 协议正确接收(直到 EOF 字段的倒数第二位都没有发送错误) 并且成功通过标识符筛选后,该消息将视为有效

接收到的数据会放入接收FIFO,这个和所有带FIFO的通信模块一样,FIFO有很多状态
接收到一个消息,接收到两个,接收FIFO满了,FIFO上溢了。
都会有相应的状态寄存器表示,如果对应的中断使能打开还可以产生中断,这样CPU可以计时的介入处理任务。

中断处理

不得不说STM32系列为什么这么流行,他的手册写的非常好,容易理解。
上图是中断
发送中断可由以下事件产生:
— 发送邮箱 0 变为空, CAN_TSR 寄存器的 RQCP0 位置 1。
— 发送邮箱 1 变为空, CAN_TSR 寄存器的 RQCP1 位置 1。
— 发送邮箱 2 变为空, CAN_TSR 寄存器的 RQCP2 位置 1。
● FIFO 0 中断可由以下事件产生:
— 接收到新消息, CAN_RF0R 寄存器的 FMP0 位不是“00”。
— FIFO0 满, CAN_RF0R 寄存器的 FULL0 位置 1。
— FIFO0 上溢, CAN_RF0R 寄存器的 FOVR0 位置 1。
●FIFO 1 中断可由以下事件产生:
— 接收到新消息, CAN_RF1R 寄存器的 FMP1 位不是“00”。
— FIFO1 满, CAN_RF1R 寄存器的 FULL1 位置 1。
— FIFO1 上溢, CAN_RF1R 寄存器的 FOVR1 位置 1。
● 错误和状态改变中断可由以下事件产生:
— 错误状况,有关错误状况的更多详细信息,请参见 CAN 错误状态寄存器
(CAN_ESR)。
— 唤醒状况, CAN Rx 信号上监测到 SOF。
— 进入睡眠模式。

模块代码,先分析官方的CAN代码,初始化流程
以下代码用作分析

static void CAN_Config(void)
{GPIO_InitTypeDef  GPIO_InitStructure;/* CAN GPIOs configuration **************************************************//* Enable GPIO clock */RCC_AHB1PeriphClockCmd(CAN_GPIO_CLK, ENABLE);/* Connect CAN pins to AF9 */GPIO_PinAFConfig(CAN_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT);GPIO_PinAFConfig(CAN_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT); /* Configure CAN RX and TX pins */GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN | CAN_TX_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;GPIO_Init(CAN_GPIO_PORT, &GPIO_InitStructure);/* CAN configuration ********************************************************/  /* Enable CAN clock */RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);/* CAN register init */CAN_DeInit(CANx);/* CAN cell init */CAN_InitStructure.CAN_TTCM = DISABLE;CAN_InitStructure.CAN_ABOM = DISABLE;CAN_InitStructure.CAN_AWUM = DISABLE;CAN_InitStructure.CAN_NART = DISABLE;CAN_InitStructure.CAN_RFLM = DISABLE;CAN_InitStructure.CAN_TXFP = DISABLE;CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;/* CAN Baudrate = 1 MBps (CAN clocked at 30 MHz) */CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq;CAN_InitStructure.CAN_Prescaler = 2;CAN_Init(CANx, &CAN_InitStructure);/* CAN filter init */CAN_FilterInitStructure.CAN_FilterNumber = 0;CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;CAN_FilterInit(&CAN_FilterInitStructure);/* Transmit Structure preparation */TxMessage.StdId = 0x321;TxMessage.ExtId = 0x01;TxMessage.RTR = CAN_RTR_DATA;TxMessage.IDE = CAN_ID_STD;TxMessage.DLC = 1;/* Enable FIFO 0 message pending Interrupt */CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}

流程还是比较清晰的
1.配置CAN模块的GPIO 输出引脚
2.配置CAN时钟模块,初始化 CAN cell (CAN模块的控制部分,模块框图右边)
3.CAN filter 过滤器的配置
4 .Transmit Structure 传输数据的配置

CAN_InitStruct.CAN_ABOM =  DISABLE;

CAN_InitStruct.CAN_AWUM =  DISABLE;

 CAN_InitStruct.CAN_BS1  =  CAN_BS1_5tq;CAN_InitStruct.CAN_BS2  =  CAN_BS2_4tq;

BS1,BS2段所占用的Tq 长度。

 CAN_InitStruct.CAN_Mode =  CAN_Mode_Normal;

正常模式 (CAN_Mode_Normal)、回环模式 (CAN_Mode_LoopBack)、静默模式 (CAN_Mode_Silent) 以及回环静默模式(CAN_Mode_Silent_LoopBack)

CAN_InitStruct.CAN_NART =  DISABLE;

 CAN_InitStruct.CAN_RFLM =  DISABLE;

CAN_InitStruct.CAN_SJW =   CAN_SJW_4tq;

CAN_InitStruct.CAN_TTCM =  DISABLE;

 CAN_InitStruct.CAN_TXFP =  DISABLE;

CAN_InitStruct.CAN_Prescaler

F407 APB2 总线时钟频率最大为 42MHz , CAN_Prescaler在此基础上进行分频 得到时间份额 Tq

波特率 为: [ clk(ABP1) /CAN_Prescaler ] / [ 1(SS) + BS1+BS2 ]

实验

在没有CAN收发器的开发板上,缺少物理电平转换。那么可以用示波器或者逻辑分析仪来观察CAN_TX的变化

设置预分频为42,Tq = 42M/42 = 1M

CAN_InitStruct.CAN_BS1  =  CAN_BS1_5tq;
CAN_InitStruct.CAN_BS2  =  CAN_BS2_4tq;

波特率 为: [ clk(ABP1) /CAN_Prescaler ] / [ 1(SS) + BS1+BS2 ] =100Kbps
ACK 在没有其他设备的情况下是 1 ,隐性
代码如下

STM32F407
/*PA11  CAN1_RX
PA12  CAN1_TX*/void app_can_init(void)
{GPIO_InitTypeDef  GPIO_InitStruct;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF ;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP ;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;GPIO_InitStruct.GPIO_PuPd =GPIO_PuPd_UP  ;GPIO_InitStruct.GPIO_Speed =GPIO_Fast_Speed  ;GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1);app_can_config();
}void app_can_config(void)
{CAN_InitTypeDef  CAN_InitStruct;CAN_FilterInitTypeDef CAN_FilterInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);CAN_InitStruct.CAN_ABOM =  DISABLE;CAN_InitStruct.CAN_AWUM =  DISABLE;CAN_InitStruct.CAN_BS1  =  CAN_BS1_5tq;CAN_InitStruct.CAN_BS2  =  CAN_BS2_4tq;CAN_InitStruct.CAN_Mode =  CAN_Mode_LoopBack;CAN_InitStruct.CAN_NART =  DISABLE;CAN_InitStruct.CAN_Prescaler = 42;CAN_InitStruct.CAN_RFLM =  DISABLE;CAN_InitStruct.CAN_SJW =   CAN_SJW_4tq;CAN_InitStruct.CAN_TTCM =  DISABLE;CAN_InitStruct.CAN_TXFP =  DISABLE;CAN_Init(CAN1, & CAN_InitStruct);CAN_FilterInitStructure.CAN_FilterNumber=14;                       //筛选器组14CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;  //工作在掩码模式CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;    //筛选器位宽为单个32位。CAN_FilterInitStructure.CAN_FilterIdHigh= ((((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;       //要筛选的ID高位 CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要筛选的ID低位 CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF;         //筛选器高16位每位必须匹配CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF;            //筛选器低16位每位必须匹配CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;             //筛选器被关联到FIFO0CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;         //使能筛选器CAN_FilterInit(&CAN_FilterInitStructure);}void app_can_send(void)
{   CanTxMsg TxMessage;TxMessage.Data[0]= 170; /* 0xAA*/TxMessage.DLC = 1;TxMessage.ExtId = 0x00;TxMessage.IDE  = CAN_ID_STD;TxMessage.RTR  = CAN_RTR_DATA;TxMessage.StdId = 0x1B5;CAN_Transmit(CAN1,&TxMessage);
}

波特率测量如下
CAN包数据如下

我们可以看到事实上携带的有效 数据只有1 byte 也就是 8 bit 约为 80us,一包数据下图大概有530us
所以有效载荷 80/530 = 15%

最高8byte的时候 880/(530+807) = 58%
所以can基本帧有效载荷率大概在15~58% 应为再同步会插入一些同步位,所以是大概值。

分析一下 CAN的发送,数据的的发送是按照固定的格式 前面我们理解为邮箱
发送函数如下所示

uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage)
{uint8_t transmit_mailbox = 0;/* Select one empty transmit mailbox */if ((CANx->TSR&CAN_TSR_TME0) == CAN_TSR_TME0){transmit_mailbox = 0;}else if ((CANx->TSR&CAN_TSR_TME1) == CAN_TSR_TME1){transmit_mailbox = 1;}else if ((CANx->TSR&CAN_TSR_TME2) == CAN_TSR_TME2){transmit_mailbox = 2;}else{transmit_mailbox = CAN_TxStatus_NoMailBox;}if (transmit_mailbox != CAN_TxStatus_NoMailBox){/* Set up the Id */CANx->sTxMailBox[transmit_mailbox].TIR &= TMIDxR_TXRQ;if (TxMessage->IDE == CAN_Id_Standard){assert_param(IS_CAN_STDID(TxMessage->StdId));  CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->StdId << 21) | \TxMessage->RTR);}else{assert_param(IS_CAN_EXTID(TxMessage->ExtId));CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->ExtId << 3) | \TxMessage->IDE | \TxMessage->RTR);}/* Set up the DLC */TxMessage->DLC &= (uint8_t)0x0000000F;CANx->sTxMailBox[transmit_mailbox].TDTR &= (uint32_t)0xFFFFFFF0;CANx->sTxMailBox[transmit_mailbox].TDTR |= TxMessage->DLC;/* Set up the data field */CANx->sTxMailBox[transmit_mailbox].TDLR = (((uint32_t)TxMessage->Data[3] << 24) | ((uint32_t)TxMessage->Data[2] << 16) |((uint32_t)TxMessage->Data[1] << 8) | ((uint32_t)TxMessage->Data[0]));CANx->sTxMailBox[transmit_mailbox].TDHR = (((uint32_t)TxMessage->Data[7] << 24) | ((uint32_t)TxMessage->Data[6] << 16) |((uint32_t)TxMessage->Data[5] << 8) |((uint32_t)TxMessage->Data[4]));/* Request transmission */CANx->sTxMailBox[transmit_mailbox].TIR |= TMIDxR_TXRQ;}return transmit_mailbox;
}

主要是通过结构体 CanTxMsg* TxMessage,按照成员的定义把数据装入发送邮箱。
那么接收的过程也应该是一样的

void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage)

其他系列的MCU原理也类似。

STM32 CAN笔记(一)相关推荐

  1. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

  2. STM32工作笔记0031---基于STM32F103C8自己实现的跑马灯实验_使用STLINK调试

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 网上买的8块一个开发版,简单的STM32F103C8,64kbflash,中容量版,这个容量 好像 ...

  3. STM32复习笔记(五) —— GPIO锁定机制

    STM32复习笔记(五) -- GPIO锁定机制 1.GPIO 锁定机制 锁定机制可以锁定 IO 口的配置,锁定一个端口位之后,直至下一次复位之前,将不能再更改端口位的配置 2.本例程软件设计思路 1 ...

  4. STM32学习笔记 | 引起电源和系统异常复位的原因

    关注+星标公众号,不错过精彩内容 每一块处理器都有复位的功能,不同处理器复位的类型可能有差异,引起复位的原因也可能有多种. STM32的复位功能非常强大,可通过软件.硬件和一些事件触发系统复位,而且通 ...

  5. 《STM32学习笔记》4——核心功能电路与编程(下)

    接上文,文中的图片,大多数来自视频的截图(来自洋桃电子). 欢迎大家批评指正! STM32学习笔记-专栏 文章目录 一.蜂鸣器驱动 1.蜂鸣器介绍 2.蜂鸣器电路 3.蜂鸣器程序 二. MIDI 音乐 ...

  6. 【STM32学习笔记-点亮LED灯】

    STM32学习笔记-点亮LED灯 文章目录 STM32学习笔记-点亮LED灯 一.原理图分析 二.代码分析 1.mian函数 2.led.c函数 3.led.h函数 4.函数文件整理 5.LED_In ...

  7. STM32学习笔记(四)丨TIM定时器及其应用(定时中断、内外时钟源选择)

    本篇文章包含的内容 一.TIM 定时器 1.1 TIM 定时器简介 1.2 TIM 定时器类型及其工作原理简介 1.2.1 基本定时器工作原理及其结构 1.2.2 通用定时器工作原理及其结构 1.2. ...

  8. micropython STM32移植笔记(一)

    micropython STM32移植笔记(一) 首先,我是一个搞硬件的,说白了是做电路的,呵呵... 一直想学一些软件,只会搞硬件的工程师是没有灵魂的,画了7年的PCB板卡了,但是每次调试硬件都要求 ...

  9. STM32学习笔记(六)丨TIM定时器及其应用(输入捕获丨测量PWM波形的频率和占空比)

    本篇文章包含的内容 一.输入捕获 1.1 输入捕获简介 1.2 输入捕获通道的工作原理 1.3 输入捕获的主从触发模式 1.4 输入捕获和PWMI结构 二.频率的测量方法 2.1 测频法 2.2 测周 ...

  10. STM32学习笔记(15)——SPI协议

    STM32学习笔记(15)--SPI协议 一.SPI协议简介 1. 物理层 2. 协议层 (1) 通讯的开始与停止 (2)时钟极性CPOL.时钟相位CPHA 二.STM32的SPI外设 1. 通讯引脚 ...

最新文章

  1. 分治习题--九章算法培训课第三章笔记
  2. java中菜单分几级_JAVA构造多级菜单
  3. 房地产还有最后十年机会 抓紧时间转型
  4. Linux Shell脚本去掉几类常见文件中的注释
  5. Android ViewPager
  6. 云海技术u盘怎么恢复成普通盘_BITLOCKER加密中断数据无法读取恢复一例
  7. mysql是一个_Mysql
  8. Linux中的端口大全
  9. openlayers根据坐标定位_车辆定位技术概述
  10. 计算机术语hpp,HPP(计算机术语)_百度百科
  11. js动态显示时间和日期
  12. Python 基础变量声明
  13. 转 爬虫与反爬虫套路
  14. java罗马帝国下载,Java程序设计2020满分完整版考 试题库大全
  15. 计算机共享的媒体设备,多台计算机、手机和平板电脑中共享对文件和媒体的访问时怎么做...
  16. Java程序员掉发系列——程序员的成长之路
  17. kafka-eagle详细安装配置图文教程
  18. 20个最受欢迎商务旅游城市:纽约连续四年拿第一,上海第四
  19. CC00155.bigdatajava——|JavaMySQL.高级.V27|——|MySQL.v28|锁分类|
  20. 大神们都是如何在时间序列中进行特征提取的?看完就懂了!

热门文章

  1. Dinic求最大流/最小割
  2. 递归方法求最大公约数,求最小公倍数
  3. 入耳式蓝牙耳机哪个牌子好用?四款高品质音乐游戏耳机
  4. 安全灵活,华为云桌面成为数字化办公最佳搭档
  5. 协方差与皮尔森相关性系数
  6. 20200928 006.简单错误如何处理_守破离学习法_程序员修炼手册
  7. Flex Programming Tricks 1
  8. 【思考14】量化交易回测中,关于涨跌停的处理方式
  9. python 涨停统计_python+tushare获取股票和基金每日涨跌停价格
  10. Vue+Element 实现订单列表【管理端】02