当前位置: 首页 > news >正文

STM的CAN通信学习

 显性电平:0    隐性电平:1

一、帧结构

1.帧类型

1)数据帧:发送设备主动发送数据(广播式)

2)请求帧:接收设备主动请求数据(请求式)

2.帧结构

1)标准帧

1位SOF+11位ID+1位RTR+1位IDE+1位r0+4位DLC+0~64位Data+15位CRC效验码+1位CRC界定符+1位ACK槽+1位ACK界定符+7位结束符

  • 1位  SOF:帧起始,表示后面一段波形为传输的数据位,显性0
  • 11位  ID:标识符,区分功能,同时决定优先级
  • 1位  RTR:远程请求位,区分数据帧(显性0)和遥控帧(隐性1)
  • 1位  IDE:扩展标志位,区分标准格式(0)和扩展格式(1)
  • 1位  r0/r1:保留位,为后续协议升级留下空间(显性0)
  • 4位  DLC:数据长度,指示数据段Data有几个字节
  • 0~64位  Data:数据段的1~8个字节有效数据
  • 15位 CRC:循环冗余校验,校验数据是否正确
  • 1位 CRC界定符:隐性1
  • 1位 ACK:应答位,判断数据有没有被接收方接收(显性0收到数据,隐形1没收到数据)
  • 1位 ACK界定符:为应答位前后发送方和接收方释放总线留下时间(隐性1)
  • 7位 EOF:帧结束,表示数据位已经传输完毕(隐性电平1)

2)扩展帧

11位ID结束后,加1位SRR和1位IDE。其中SRR是代替RTR,协议升级时留下的无意义位。IDE是隐性电平1,表示这是扩展帧。其余与标准帧设定一致。

4.关于位填充

位填充规则:发送方每发送5个相同电平后,自动追加一个相反电平的填充位,接收方检测到填充位时,会自动移除填充位,恢复原始数据

位填充例子:

二、仲裁

1.先占先得

2.非破坏性仲裁

仲裁段就是ID号+RTR位,ID号越小,仲裁的优先级越高,填充位不会影响仲裁,如果ID号一致,数据帧的优先级高于遥控桢

如下图所示,单元一和单元二同时开始波形, 根据线与特性到标红的位置,总线呈现显性0,单元二回读为0,与发出0一致,单元一回读为0,与发出的1不一致。此时,单元一仲裁失败,转为接收状态。

三、STM32的CAN外设

1.STM32的默认CAN复用端口:

PA11——CAN_RX

PA12——CAN_TX

2.CAN收发报文

  • CAN_RX写入指令到控制器,接收过滤器过滤所接收到的指令,再放入指定的FIFO队列(队列遵循先进先出),接收的队列一共有两个,可以指定两个队列的优先级。每一个队列共有三个邮箱,邮箱如果满了,接收的数据就会溢出。接收流程:接收到一个报文→匹配过滤器后进入FIFO 0或FIFO 1→CPU读取

  • CAN_TX发送指令时,也有三个发送邮箱,三个发送邮箱可以按照指定顺序进行发送,如先进先出或者ID优先级发送。ID优先级一般是ID号小的先发送。发送流程:选择一个空置邮箱——写入报文——请求发送
  • NART:置1,关闭自动重传,CAN报文只被发送1次;置0,自动重传,直到发送成功
  • TXFP:置1,优先级由发送请求的顺序来决定,先请求的先发送;置0,优先级由报文标识符(报文ID)来决定,标识符值小的先发送(标识符值相等时,邮箱号小的报文先发送)
  • RFLM:置1,接收FIFO锁定,FIFO溢出时,新收到的报文会被丢弃;置0,禁用FIFO锁定,FIFO溢出时,FIFO中最后收到的报文被新报文覆盖

3.标识符过滤器

  • FSCx:位宽设置,置0,16位;置1,32位

  • FBMx:模式设置,置0,屏蔽模式;置1,列表模式

  • FFAx:关联设置,置0,FIFO 0;置1,FIFO 1

  • FACTx:激活设置,置0,禁用;置1,启用

4.测试模式:(CAN_MODE)

静默模式、环回模式、静默环回模式

	/* CAN_Mode_Normal             ((uint8_t)0x00)  正常模式CAN_Mode_LoopBack           ((uint8_t)0x01)  环回模式CAN_Mode_Silent             ((uint8_t)0x02)  静默模式CAN_Mode_Silent_LoopBack    ((uint8_t)0x03)  环回静默模式*/

5.工作模式:

初始化模式、正常模式、睡眠模式、AWUM(置1自动唤醒,置0手动唤醒)

6.位时间特性

  • 波特率 = APB1时钟频率 / 分频系数 / 一位的Tq数量

                   = 36MHz / (BRP[9:0]+1) / (1 + (TS1[3:0]+1) + (TS2[2:0]+1))

  • 7.四个中断

    • 发送中断:发送邮箱空时产生
    • FIFO 0中断:收到一个报文/FIFO 0满/FIFO 0溢出时产生
    • FIFO 1中断:收到一个报文/FIFO 1满/FIFO 1溢出时产生
    • 状态改变错误中断:出错/唤醒/进入睡眠时产生
    • 8.时间触发通信

    • TTCM:置1,开启时间触发通信功能;置0,关闭时间触发通信功能(CAN外设内部有一个16位计数器,用于记录时间戳)

    • 9.离线恢复

    • ABOM:置1,开启离线自动恢复,进入离线状态后,就自动开启恢复过程;置0,关闭离线自动恢复,软件必须先请求进入然后再退出初始化模式,随后恢复过程才被开启

    • 四、具体代码使用

  • 1.CAN初始化函数调用

  • void MyCAN_Init(void)
    {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//启用GPIOA时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//启用CAN1时钟//初始化GPIOA的12号引脚GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//模式为复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//12号引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//引脚速度GPIO_Init(GPIOA, &GPIO_InitStructure);//引脚初始化//初始化GPIO的11号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化CAN1控制器CAN_InitTypeDef CAN_InitStructure;//定义一个CAN的结构体/* CAN_Mode_Normal             ((uint8_t)0x00)  正常模式CAN_Mode_LoopBack           ((uint8_t)0x01)  环回模式CAN_Mode_Silent             ((uint8_t)0x02)  静默模式CAN_Mode_Silent_LoopBack    ((uint8_t)0x03)  环回静默模式*/CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;//CAN工作模式为环回模式,用于自收自发测试用//波特率 = 36M(时钟频率) / 48 (预分频器的值)/ (1 + 2(BS1)+ 3(BS2)) = 125KCAN_InitStructure.CAN_Prescaler = 48;	//预分频器的值为48	CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;//BS1的时间长度,1-16tqCAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;//BS2的时间长度CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;//SJW的时间长度,1-4tqCAN_InitStructure.CAN_NART = DISABLE;//DISABLE,表示寄存器置0,自动重传CAN_InitStructure.CAN_TXFP = DISABLE;//发送邮箱优先级,DISABLE置零,ID小的先发送,如果是ENABLE置一,先进先出CAN_InitStructure.CAN_RFLM = DISABLE;//禁用FIOF锁定,溢出后,新报文覆盖最后一个报文CAN_InitStructure.CAN_AWUM = DISABLE;//DISABLE 手动唤醒,ENABLE 自动唤醒CAN_InitStructure.CAN_TTCM = DISABLE;//关闭时间触发通信CAN_InitStructure.CAN_ABOM = DISABLE;//DISABLE 手动恢复,ENABLE 自动恢复CAN_Init(CAN1, &CAN_InitStructure);//初始化CAN//初始化CAN过滤器CAN_FilterInitTypeDef CAN_FilterInitStructure;//定义CAN过滤器结构体CAN_FilterInitStructure.CAN_FilterNumber = 0;//过滤器编号,0-13/*16位列表模式,四个参数分别存入一组ID即可,共四个16位ID列表屏蔽模式:IDHIGH存入第一组ID,MaskIDHIGH存入对应的屏蔽位,共两组16位ID和两组屏蔽位32位列表模式:IDHIGH和IDLOW组合成一个32位ID,MaskIDHIGH和MaskIDLOW组成第二组32位ID32位频闭模式:IDHIGH和IDLOW组合成一个32位ID,MaskIDHIGH和MaskIDLOW组成第二组32位屏蔽位*/CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;//高16位CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;//低16位CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;//选则相应模式为32位屏蔽模式CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;//过滤器位宽,32位或者16位,这里是32位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;//过滤器模式,CAN_FilterMode_IdMask 屏蔽模式 CAN_FilterMode_IdList  列表模式CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//配置过滤器关联,这里有两个CAN_Filter_FIFO0,CAN_Filter_FIFO1CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//激活过滤器CAN_FilterInit(&CAN_FilterInitStructure);//初始化过滤器
    }
    

2.发送报文

/*
Name:MyCAN_Transmit CAN 发送报文
Param: ID Length   数据长度*Data    数据指针
*/
void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{CanTxMsg TxMessage; //定义CanTxMsg结构体变量TxMessage.StdId = ID;//标准IDTxMessage.ExtId = ID;//扩展IDTxMessage.IDE = CAN_Id_Standard;		//扩展标志位,CAN_Id_Standard 标准ID ,CAN_Id_Extended扩展IDTxMessage.RTR = CAN_RTR_Data; //遥控标志位,CAN_RTR_Remote 遥控帧,	CAN_RTR_Data数据帧TxMessage.DLC = Length;//数据段长度,传入的参数//把形参DATA传过来的数组赋值给TxMessage.Datafor (uint8_t i = 0; i < Length; i ++){TxMessage.Data[i] = Data[i];//Data为8字节的数组}/*CAN_Transmit的原理:选择空发送邮箱——如果邮箱有空位,则将报文写入指定寄存器——TXRQ置1,请求发送*/uint8_t TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);//请求发送结构体指向的报文,返回值是邮箱编号//uint32_t Timeout = 0;//等待函数返回OK,当CAN1的邮箱状态为CAN_TxStatus_Ok表示发送成功,如果不成功则进入循环while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok){Timeout ++;//如果大于超时时间,则跳出循环if (Timeout > 100000){break;}}
}

3.接收报文 

/*
@brief: recieve CAN message
@param: *ID        the ID*Length    the length of DATA*DATA      the Data of CAN massage
*/
void MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{CanRxMsg RxMessage;//定义一个CanRxMsg结构体CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//判断接收的报文是标准ID还是扩展IDif (RxMessage.IDE == CAN_Id_Standard){*ID = RxMessage.StdId;//标准ID}else{*ID = RxMessage.ExtId; //扩展ID}if (RxMessage.RTR == CAN_RTR_Data)//是否为数据帧{*Length = RxMessage.DLC;//数据长度//数据内容for (uint8_t i = 0; i < *Length; i ++){Data[i] = RxMessage.Data[i];}}else{//遥控帧,暂时不做处理}
}

http://www.lryc.cn/news/439925.html

相关文章:

  • 【高等数学学习记录】函数
  • 【springboot过ingress后无法获取X-Forwarded-For头信息】
  • 表格标记<table>
  • Rust练手项目,写个有趣的小工具定时从一言网获取一段有趣的话并推送通知
  • 【隐私计算】Paillier半同态加密算法
  • 判断数字的奇偶[中秋快乐~]
  • 文件操作及重定向详解
  • 鸿蒙next json解析 ArkUI 带你玩转 arkts json解析
  • 东土科技加码芯片业务投资,携手神经元共建新型工业生态
  • 指纹与指甲检测系统源码分享
  • C++3D迷宫
  • 跨界融合,GIS如何赋能游戏商业——以《黑神话:悟空》为例
  • 【计网】从零开始使用TCP进行socket编程 --- 客户端与服务端的通信实现
  • Imagen:重塑图像生成领域的革命性突破
  • Golang | Leetcode Golang题解之第402题移掉K位数字
  • c++ gtsam/inference/Symbol.h 详细介绍
  • apache文件共享和访问控制
  • LeetCode 2398.预算内的最多机器人数目:滑动窗口+单调队列——思路清晰的一篇题解
  • vue 在线预览word和excel
  • 物联网智能项目
  • 828华为云征文|Flexus云服务器X:Python安装的极致便捷之旅
  • Midjourney中秋特典-12张图附魔咒
  • 编写程序,从键盘输入若干整数,将其保存入一个数组中。利用Arravs进行排序,然后查找出第3大的整数
  • VSCode 离线安装中文语言包
  • vue3补充
  • 关于Chrome浏览器没有网络,而其他浏览器正常这一问题的解决方法
  • 人工智能辅助汽车造型设计
  • 专家访谈:心脑血管名医蔡英丽医生的医学智慧
  • Ubuntu 22.04 源码下载的几种方法
  • floodfill+DFS(1)