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

STM32-CAN

CAN通信:异步通信;半双工;差分信号

闭环总线网络:高速短距离;40m;1MPBS 

开环总线网络:匹配电阻区别;低速远距离;1KM;125KBPS 

 在理想情况下,节点不受限制;差分信号两根线的电压差来代表0,1

显性优先(0);共用总线时,同一时间只有一个节点发送其余节点接收 

 

 SS段:同步段,节点与总线时序同步1Tq

RTS段:传播时间段(1-8Tq)

PBS1段:1-8Tq

PBS2段:用于检测边沿阶位误差2-8Tq

重新同步PBS1+;PBS2-;的长度为重新同步补偿的宽度:SJW (软件可限制位数)

相位超前:PBS1+ 

相位滞后:PBS2- 

 

谁先出现1谁接收

 

 SOF:帧起始0

ID:

RTR:0数据帧;1远程请求帧

IDE:标准数据0;扩展数据1

r0:保留位0

DLC:数据长度4位(0~8)

数据段:原始数据;最高字节在前0-8个字节

CRC段:校验位15位;发送接收CRC码不同向发送方返回错误信息重新发送

CRC界定符:1;与ACK间隔

ACK槽:1

ACK段:0

ACK界定符:隔开

帧结束:7个1

 

  • STM32 CAN1
  • 发送邮箱:3个,有4个寄存器

  • 接受邮箱:缓存6个,锁定模式下;FIFO溢出丢弃新数据保留原数据;非锁定模式下,新数据覆盖原数据

  • 验收筛选器:两个寄存器,用于检查使用的ID

 ID和掩码

 

掩码为1时,筛选的掩码必须要跟ID码一致 

  • CAN2:要使能CAN1 

4种工作模式:

波特率计算:

 

 软件流程:

  • 使能CAN时钟,将对应的引脚复用映射为CAN功能APB1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); //打开CAN1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //PA端口时钟打开
  • 设置CAN工作模式;波特率等
typedef struct
{uint16_t CAN_Prescaler;   /*!< 指定时间量子长度,取值范围为1到1024 */uint8_t CAN_Mode;         /*!< 指定CAN工作模式,此参数可以是@ref CAN_operating_mode中的值 */uint8_t CAN_SJW;          /*!< 指定CAN硬件在重新同步时允许延长或缩短位的最大时间量子数,此参数可以是@ref CAN_synchronisation_jump_width中的值 */uint8_t CAN_BS1;          /*!< 指定位段1的时间量子数,此参数可以是@ref         CAN_time_quantum_in_bit_segment_1中的值 */uint8_t CAN_BS2;          /*!< 指定位段2的时间量子数,此参数可以是@ref CAN_time_quantum_in_bit_segment_2中的值 */FunctionalState CAN_TTCM; /*!< 启用或禁用时间触发通信模式,此参数可以设置为ENABLE或DISABLE */FunctionalState CAN_ABOM; /*!< 启用或禁用自动离线管理,此参数可以设置为ENABLE或DISABLE */FunctionalState CAN_AWUM; /*!< 启用或禁用自动唤醒模式,此参数可以设置为ENABLE或DISABLE */FunctionalState CAN_NART; /*!< 启用或禁用无自动重传模式,此参数可以设置为ENABLE或DISABLE */FunctionalState CAN_RFLM; /*!< 启用或禁用接收FIFO锁定模式,此参数可以设置为ENABLE或DISABLE */FunctionalState CAN_TXFP; /*!< 启用或禁用发送FIFO优先级,此参数可以设置为ENABLE或DISABLE */
} CAN_InitTypeDef;
  • 设置CAN筛选器
typedef struct
{uint16_t CAN_FilterIdHigh;         /*!< 指定过滤器识别号(32位配置时为高16位,16位配置时为第一个ID)此参数取值范围为0x0000至0xFFFF */uint16_t CAN_FilterIdLow;          /*!< 指定过滤器识别号(32位配置时为低16位,16位配置时为第二个ID)此参数取值范围为0x0000至0xFFFF */uint16_t CAN_FilterMaskIdHigh;     /*!< 根据工作模式指定过滤器掩码号或识别号(32位配置时为高16位,16位配置时为第一个掩码/ID)此参数取值范围为0x0000至0xFFFF */uint16_t CAN_FilterMaskIdLow;      /*!< 根据工作模式指定过滤器掩码号或识别号(32位配置时为低16位,16位配置时为第二个掩码/ID)此参数取值范围为0x0000至0xFFFF */uint16_t CAN_FilterFIFOAssignment; /*!< 指定将分配给该过滤器的FIFO(0或1)此参数可以是@ref CAN_filter_FIFO中的值 */uint8_t CAN_FilterNumber;          /*!< 指定要初始化的过滤器编号,范围从0到13 */uint8_t CAN_FilterMode;            /*!< 指定要初始化的过滤器模式此参数可以是@ref CAN_filter_mode中的值 */uint8_t CAN_FilterScale;           /*!< 指定过滤器尺度此参数可以是@ref CAN_filter_scale中的值 */FunctionalState CAN_FilterActivation; /*!< 启用或禁用过滤器此参数可以设置为ENABLE或DISABLE */
} CAN_FilterInitTypeDef;
  • 选择CAN中断类型,开启中断
/*** @brief  配置CAN中断使能* @param  CANx: 指向CAN外设结构体的指针(CAN1或CAN2)* @param  CAN_IT: 指定要配置的CAN中断源*         可使用以下参数的组合:*           - CAN_IT_TME: 发送邮箱空中断*           - CAN_IT_FMP0: FIFO0消息挂起中断*           - CAN_IT_FMP1: FIFO1消息挂起中断*           - CAN_IT_FF0: FIFO0溢出中断*           - CAN_IT_FF1: FIFO1溢出中断*           - CAN_IT_FOV0: FIFO0满中断*           - CAN_IT_FOV1: FIFO1满中断*           - CAN_IT_EWG: 错误警告中断*           - CAN_IT_EPV: 错误被动中断*           - CAN_IT_BOF: 总线关闭中断*           - CAN_IT_LEC: 错误码变化中断*           - CAN_IT_WKU: 唤醒中断*           - CAN_IT_SLK: 睡眠中断* @param  NewState: 新状态(ENABLE或DISABLE)* @retval 无*/
void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState)
  • CAN发送和接收消息

发送:

/*** @brief  向CAN总线发送消息* @param  CANx: 指向CAN外设结构体的指针(CAN1或CAN2)* @param  TxMessage: 指向包含待发送消息参数的CanTxMsg结构体指针*         结构体成员需预先配置:*           - StdId/ExtId: 标准/扩展标识符*           - IDE: 标识符类型(标准/扩展)*           - RTR: 帧类型(数据帧/远程帧)*           - DLC: 数据长度(0-8字节)*           - Data[]: 待发送数据(若为远程帧则忽略)* @retval uint8_t: 发送邮箱状态*           - 0x00: 无空闲邮箱,发送失败*           - 0x01: 使用邮箱0,发送请求已提交*           - 0x02: 使用邮箱1,发送请求已提交*           - 0x03: 使用邮箱2,发送请求已提交*/
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage)
typedef struct
{uint32_t StdId;  /*!< 指定标准标识符,取值范围为0到0x7FF */uint32_t ExtId;  /*!< 指定扩展标识符,取值范围为0到0x1FFFFFFF */uint8_t IDE;     /*!< 指定待发送消息的标识符类型,此参数可以是@ref CAN_identifier_type中的值(注:IDE=0表示使用标准标识符StdId,IDE=1表示使用扩展标识符ExtId) */uint8_t RTR;     /*!< 指定待发送消息的帧类型,此参数可以是@ref CAN_remote_transmission_request中的值(注:RTR=0表示数据帧,用于发送数据;RTR=1表示远程帧,用于请求数据) */uint8_t DLC;     /*!< 指定待发送帧的数据长度,取值范围为0到8(单位:字节) */uint8_t Data[8]; /*!< 包含待发送的数据,数组中每个元素的取值范围为0到0xFF */
} CanTxMsg;

接收:

/*** @brief  从指定CAN FIFO接收消息并存储到接收结构体中* @param  CANx: 指向CAN外设结构体的指针(CAN1或CAN2)* @param  FIFONumber: 指定要读取的FIFO编号*         此参数可以是以下值之一:*           - CAN_FIFO0: 读取FIFO 0中的消息*           - CAN_FIFO1: 读取FIFO 1中的消息* @param  RxMessage: 指向接收消息结构体的指针,用于存储接收到的CAN消息*         函数会填充以下成员:*           - StdId/ExtId: 接收到的标准/扩展标识符*           - IDE: 标识符类型(标准/扩展)*           - RTR: 帧类型(数据帧/远程帧)*           - DLC: 数据长度(0-8字节)*           - Data[]: 接收到的数据(若为远程帧则Data[]内容未定义)*           - Timestamp: 接收时间戳(需启用时间戳功能)*           - FMI: 过滤器匹配索引(标识哪个过滤器匹配了该消息)* @retval 无*/
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage)
/*** @brief  获取指定CAN FIFO中待处理的消息数量* @param  CANx: 指向CAN外设结构体的指针(CAN1或CAN2)* @param  FIFONumber: 指定FIFO编号*         此参数可以是以下值之一:*           - CAN_FIFO0: FIFO 0*           - CAN_FIFO1: FIFO 1* @retval uint8_t: 待处理的消息数量*         返回值范围:0-3(FIFO深度为3级)*/
uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber)
  • CAN状态获取(检测标志位)

代码如下:

#include "can.h"
#include "usart.h"//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:时间段2的时间单元.   范围:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:时间段1的时间单元.   范围:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024;  tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+tbs2+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
//则波特率为:36M/((8+9+1)*4)=500Kbps
//返回值:0,初始化OK;
//    其他,初始化失败;
void CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{GPIO_InitTypeDef GPIO_InitStructure;CAN_InitTypeDef        CAN_InitStructure;CAN_FilterInitTypeDef  CAN_FilterInitStructure;#if CAN_RX0_INT_ENABLE NVIC_InitTypeDef  		NVIC_InitStructure;
#endifRCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); //打开CAN1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //PA端口时钟打开GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;		//PA11	   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 	 //上拉输入模式GPIO_Init(GPIOA, &GPIO_InitStructure);	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;		//PA12	   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 	 //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //IO口速度为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);//CAN单元设置CAN_InitStructure.CAN_TTCM=DISABLE;	//非时间触发通信模式   CAN_InitStructure.CAN_ABOM=DISABLE;	//软件自动离线管理	  CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)CAN_InitStructure.CAN_NART=ENABLE;	//使用报文自动传送 CAN_InitStructure.CAN_RFLM=DISABLE;	//报文不锁定,新的覆盖旧的  CAN_InitStructure.CAN_TXFP=DISABLE;	//优先级由报文标识符决定 CAN_InitStructure.CAN_Mode= mode;	 //模式设置 CAN_InitStructure.CAN_SJW=tsjw;	//重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tqCAN_InitStructure.CAN_BS1=tbs1; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tqCAN_InitStructure.CAN_BS2=tbs2;//Tbs2范围CAN_BS2_1tq ~	CAN_BS2_8tqCAN_InitStructure.CAN_Prescaler=brp;  //分频系数(Fdiv)为brp+1	CAN_Init(CAN1, &CAN_InitStructure);   // 初始化CAN1//配置过滤器CAN_FilterInitStructure.CAN_FilterNumber=0;	  //过滤器0CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位 CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;////32位IDCAN_FilterInitStructure.CAN_FilterIdLow=0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASKCAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器0CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化#if CAN_RX0_INT_ENABLE CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);				//FIFO0消息挂号中断允许.		    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     // 主优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            // 次优先级为0NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
#endif
}#if CAN_RX0_INT_ENABLE	//使能RX0中断
//中断服务函数			    
void USB_LP_CAN1_RX0_IRQHandler(void)
{CanRxMsg RxMessage;int i=0;CAN_Receive(CAN1, 0, &RxMessage);for(i=0;i<8;i++)printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)	
//len:数据长度(最大为8)				     
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
//		 其他,失败;
u8 CAN_Send_Msg(u8* msg,u8 len)
{	u8 mbox;u16 i=0;CanTxMsg TxMessage;TxMessage.StdId=0x12;	 // 标准标识符为0TxMessage.ExtId=0x12;	 // 设置扩展标示符(29位)TxMessage.IDE=0;		  // 使用扩展标识符TxMessage.RTR=0;		  // 消息类型为数据帧,一帧8位TxMessage.DLC=len;							 // 发送两帧信息for(i=0;i<len;i++)TxMessage.Data[i]=msg[i];				 // 第一帧信息          mbox= CAN_Transmit(CAN1, &TxMessage);   i=0;while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;	//等待发送结束if(i>=0XFFF)return 1;return 0;		
}//can口接收数据查询
//buf:数据缓存区;	 
//返回值:0,无数据被收到;
//		 其他,接收的数据长度;
u8 CAN_Receive_Msg(u8 *buf)
{		   		   u32 i;CanRxMsg RxMessage;if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;		//没有接收到数据,直接退出 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据	for(i=0;i<RxMessage.DLC;i++)buf[i]=RxMessage.Data[i];  return RxMessage.DLC;	
}

主函数代码(KEY_UP_PRESS按下切换模式正常模式和回环模式;KEY1_PRESS按下,发送数据和接收数据)

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "key.h"
#include "can.h"int main()
{u8 i=0,j=0;u8 key;u8 mode=0;u8 res;u8 tbuf[8];u8 rbuf[8];SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组LED_Init();USART1_Init(115200);KEY_Init();CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal);//500Kbps波特率while(1){key=KEY_Scan(0);if(key==KEY_UP_PRESS)  //模式切换{mode=!mode;CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,mode);if(mode==0){printf("Normal Mode\r\n");}else{printf("LoopBack Mode\r\n");}}if(key==KEY1_PRESS)  //发送数据{for(j=0;j<8;j++){tbuf[j]=j;}res=CAN_Send_Msg(tbuf,8);if(res){printf("Send Failed!\r\n");}else{printf("发送数据:");for(j=0;j<8;j++){printf("%X  ",tbuf[j]);}printf("\r\n");}}res=CAN_Receive_Msg(rbuf);if(res){	printf("接收数据:");for(j=0;j<8;j++){printf("%X  ",rbuf[j]);}printf("\r\n");}i++;if(i%20==0){LED1=!LED1;}delay_ms(10);}
}

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

相关文章:

  • 开发避坑短篇(2):uni-app微信小程序开发‘createIndependentPlugin‘模块缺失问题分析与解决方案
  • 初探:C语言FILE结构之文件描述符与缓冲区的实现原理
  • iOS OC 图片压缩
  • CityEngine自动化建模
  • Java面试宝典:Maven
  • 片上网络(NoC)拓扑结构比较
  • 现代R语言机器学习:Tidymodel/Tidyverse语法+回归/树模型/集成学习/SVM/深度学习/降维/聚类分类与科研绘图可视化
  • PHP:经典与现代交融的Web开发利器
  • 生成式引擎优化(GEO)核心解析:下一代搜索技术的演进与落地策略
  • 超简单linux上部署Apache
  • UDP协议介绍
  • 深入理解Linux文件操作:stdin/stdout/stderr与C语言文件函数全解析
  • IDEA 2020.1版本起下载JDK
  • 基于 Docker 及 Kubernetes 部署 vLLM:开启机器学习模型服务的新篇章
  • Docker --privileged 命令详解
  • Jenkins+Docker+Git实现自动化CI/CD
  • [2025CVPR-目标检测方向]FSHNet:一种用于3D物体检测的全稀疏混合网络。
  • vue2 面试题及详细答案150道(41 - 60)
  • Linux系统安装Docker及部署Node.js 20.15.0(含pnpm、pm2)完整指南
  • 武汉江滩某码头变形及应力自动化监测
  • 由于热爱,我选PGCE专家学习
  • 小红书采集工具:无水印图片一键获取,同步采集笔记与评论
  • 接口测试时如何上传文件(图片、安装包等)
  • MyBatis缓存实战指南:一级与二级缓存的深度解析与性能优化
  • Tomcat及Nginx部署使用
  • 淘宝高级详情接口接入指南与Python代码实战
  • 如何搭建systemverilog/UVM验证环境开发vip(腾讯元宝)
  • C专题5:函数进阶和递归
  • InnoDB 多版本控制 慢sql排查(基于MySQL 5.7)
  • CentOS7 内网服务器yum修改