当前位置: 首页 > 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.dtcms.com/a/286896.html

相关文章:

  • vs openssl编译提示无法打开文件“libssl.lib”或“libcrypto.lib”
  • 理解 VMA 与 LMA
  • 【实战】Dify从0到100进阶--文档解读(8)文档列表节点
  • 深入剖析 Delta Live Tables (DLT):声明式数据管道的核心原理与底层实现
  • git:tag标签远程管理
  • 公贝固定资产管理系统对接HR、财务及采购系统的方案与效益
  • 【实用工具】HDCleaner:高效、安全、免费的系统清洁工具,免费的电脑清理垃圾神器,20秒扫出20G垃圾!
  • LP-MSPM0G3507学习--05中断及管脚中断
  • 习题4.1 输出3个人的顺序
  • APIs案例及知识点串讲(下)
  • NFS读写性能评估与优化指南(上)
  • Android性能优化之电量优化
  • C 语言字符大小写互转:tolower / toupper 详解与实战
  • MySQL使用any_value()函数解决only_full_group_by报错
  • IT 和OT指的什么?
  • 短视频矩阵的时代结束了吗?
  • 智能点餐推荐网站,解决选择困难
  • Linux基础IO通关秘籍:从文件描述符到重定向
  • 使用wrk对api接口进行性能测试
  • 机器视觉基础(直播回放)
  • git从本地仓库添加到远程仓库
  • 人工智能day9——模块化编程概念(模块、包、导入)及常见系统模块总结和第三方模块管理
  • MinIO 分布式文件系统
  • 阿里云ubuntu建一个简单网页+公网访问+域名访问
  • android14截屏
  • 短视频矩阵系统:从源头到开发的全面解析
  • 电源PCB设计的热管理攻坚战:从散热瓶颈到高功率密度突破
  • 3.0 - 指针-序列化
  • 傅里叶积分法求解偏微分方程
  • 第七章 愿景09 海波龙的坑