单片机编程-CAN通讯-理解与实现

日期作者版本说明
2023.02.03Mr.ZhengV1.1CAN通讯个人笔记,初版


声明

最近刚刚做完一个较复杂项目,为强化巩固,准备写几篇笔记方便后续回溯,本篇重点是个人对CAN通讯的理解与实现,第一部分为CAN的模型架构,CAN的功能实现是第二部分,需要一定的编程基础,希望能够给初学者带来启发。


一、CAN的模型架构

1.CAN总线的通讯模型

CAN协议有不同的国际标准,设计之初需要确认标准,这里用ISO11898CAN 高速通信标准,它的通信速度为 5kbps至1Mbps。下述为此标准下的CAN总线通信模型

第一层:物理层:规定信号传输媒介、电平、硬件收发等
第二层:数据层:物理层接收有用数据进行报错、响应、通知等职能
第三层:网络层:数据传输地址管理
第四层:传输层:数据传输缓冲、排序与错误回溯
第五层:会话层:通讯环中的收发响应、数据收发
第六层:表示层:传输数据的格式转换
第七层:应用层:协议应用,功能表达,需求实现

2.CAN总线的通讯帧结构

以项目用到的某某协议进行CAN的扩展帧结构说明,提前声明,不同公司的CAN协议可能会有不同,这里用一种举例,后续应用换汤不换药基本用法完全一致

起始帧:1 bit 硬件自动生成
仲裁帧:29 bit 就是常说的CAN的ID!仲裁帧扩展ID结构看下一个表
控制帧:6 bit 这里是控制帧存放位置
数据帧:0-64 bit 这就是协议中数据具体传输位置了,收发协议确定后在这传
校验帧:15+1 bit 硬件自动生成
确认帧:2 bit 硬件自动生成
结束帧:7 bit 硬件自动生成

这里选用的扩展帧的方式通讯,仲裁帧的29个bit将会如下图拆分成五项段码

ID28-ID24 源ID:代表载体终端,比如说这个是电池程序就是电池协议ID
ID23-ID19 目标ID:代表联系终端,比如说电池想给bms发数据就填bmsID
ID18-ID16 控制命令:读/写/应答/错误应答/长起始/长传输/长结束/状态码
ID15-ID8 索引:字典型命令集,靠索引与子索引来定向调动命令集中的数据
ID7-ID0 子索引:同上,可以看成目录集的一级标题和二级标题关系

二、CAN协议通讯实现

这里开始附代码并讲解如何实现,不同的协议有差别但是思路完全一致,比如说我想写一个功能:控制器调用电池的当前实时数据,那么你首先就要去根据你的协议设定的各种功能或者数据的存放集合(后面我叫他字典集),写个对应ID的结构体以便于后面使用。

1.数据结构体

根据协议字典集合将你需要用到的数据先打包成结构体,里面根据需求存放一些数据,这也将会是你后面CAN数据传输的必经中转站,我这里直接敲个小demo:

typedef union 
{
	struct
	{
		uint8_t S0    :  8;                  //数据这里可以拆的更细
		uint8_t S1    :  8;                  //占一位
		uint16_t S2   :  16;                 //占两位
		uint8_t S4    :  8;			
	} Sig;
	uint8_t Msg[5];
} CANMsg082A6021Union;                    //082A6021是协议ID下小节面讲
extern CANMsg082A6021Union  CANMsg082A6021; 
#define ZMX_mode  CANMsg082A6021.Sig.S0   //随便举的例子别在乎命名规范
#define ZMX_cur   CANMsg082A6021.Sig.S1   //可以定义下各节点数据
#define ZMX_vol   CANMsg082A6021.Sig.S2   //后面用变量名拿数据
#define ZMX_Temp  CANMsg082A6021.Sig.S4   //也可以直接调用结构体

2.CAN的ID解析

上述结构体例子,里面数据拆成多少,全看需求,自行更改,现在讲解下较为重点的CAN ID,用082A6021举例,要写的太多我直接写纸上拍下来:

在这里插入图片描述通讯帧结构里标注了各个帧的区间位置,结合手稿看下:
082A6021拆成二进制就是0000/1000/0010/1010/0110/0000/0010/0001
源节点ID 在28-24就是08,要看你协议的定义,假设他是控制器
目标节点ID 在23-19就是05,假设他是电池
控制命令码 在18-16就是02,对应表格就是正常应答
索引 在15-8就是60,假设在协议字典集里面的记录是电池数据
子索引 在7-0就是21,假设在协议字典集里面的记录是当前实时电池数据

那么连起来,这个ID的意思就是控制器正常调用了电池的当前实时数据;
同理,根据字典集,可将所有协议功能打包成结构体,用来实现对应协议功能

这些数据在被软件触发执行之后你的结构体里面就存储了调用的CAN总线上你想调用的某个终端的数据,之后就可以使用这些数据进行需求编写了,直接调用结构体元素或者调用变量都行,举个例子

--在屏上显示电池的当前温度
LCD_ShowNum(27,20,CANMsg082A6021.Sig.S4);
--在屏上显示电池的当前电流
LCD_ShowNum(50,20,ZMX_cur);
--在屏上显示电池的当前电压
LCD_ShowNum(50,20,CANMsg082A6021.Sig.S2);

3.CAN配置

拿兆易创新的举个例子,实际应用根据自己的mcu型号移植对应程序

--CAN模式初始化
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{ 
	can_parameter_struct can_parameter;
	can_filter_parameter_struct can_filter;
	rcu_periph_clock_enable(RCU_CAN0);
	rcu_periph_clock_enable(RCU_GPIOB);
	gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); 
	gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);	
	gpio_pin_remap_config(GPIO_CAN_PARTIAL_REMAP, ENABLE);
	/* initialize CAN register */
	can_deinit(CAN0);
	/* initialize CAN */
	can_parameter.time_triggered = DISABLE;
	can_parameter.auto_bus_off_recovery = DISABLE;
	can_parameter.auto_wake_up = DISABLE;
	can_parameter.auto_retrans = DISABLE;
	can_parameter.rec_fifo_overwrite = DISABLE;
	can_parameter.trans_fifo_order = DISABLE;
	can_parameter.working_mode = mode;
	can_parameter.resync_jump_width = tsjw;
	can_parameter.time_segment_1 = tbs1;
	can_parameter.time_segment_2 = tbs2;
	can_parameter.prescaler = brp;
	can_init(CAN0, &can_parameter);
	/* initialize filter */
	#ifdef  CAN0_USED
	/* CAN0 filter number */
	can_filter.filter_number = 0;
	#else
	can_filter.filter_number = 0;
	#endif
	/* initialize filter */
	can_filter.filter_mode = CAN_FILTERMODE_MASK;
	can_filter.filter_bits = CAN_FILTERBITS_32BIT;
	can_filter.filter_list_high = 0x0000;
	can_filter.filter_list_low = 0x0000;
	can_filter.filter_mask_high = 0x0000;
	can_filter.filter_mask_low = 0x0000;
	can_filter.filter_fifo_number = CAN_FIFO0;
	can_filter.filter_enable = ENABLE;
	can_filter_init(&can_filter);
	nvic_irq_enable(USBD_LP_CAN0_RX0_IRQn,1,0);
	can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE0);	
	return 0;
}  

--CAN发送
u8 Can_Send_Msg(uint32_t CAN_Id ,u8* msg,u8 len)
{	
	u8 mbox;
	u16 i=0;
	can_trasnmit_message_struct TxMessage;
	TxMessage.tx_sfid=0x00;		
	TxMessage.tx_efid=CAN_Id;		
	TxMessage.tx_ff=CAN_FF_EXTENDED; 	
	TxMessage.tx_ft=CAN_FT_DATA;		
	TxMessage.tx_dlen=len;			
	for(i=0;i<len;i++)
		TxMessage.tx_data[i]=msg[i];			          
	mbox= can_message_transmit(CAN0, &TxMessage);   
	i=0; 
	while((can_transmit_states(CAN0, mbox)== CAN_TRANSMIT_FAILED )&&(i<0XFFF))i++;	
	if(i>=0XFFF)return 1;
	return 0;
}

--CAN接收
void vTaskCanRec(void *pvParameters)
{
	static int i = 0;
	BaseType_t xResult;
	const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 100 );
	while(1)
	{
		xResult = xQueueReceive( xQ_CanRec, ( void* )&CAN_Rec, xMaxBlockTime );
		if( pdPASS == xResult )
		{
			Can_Rec_Val(CAN_Rec);
			Can_Send_Val(CAN_Rec);
		}
	}
}

--将MCU接收到的CAN信息保存进结构体里,软件触发条件根据需求自己写
memcpy( &CANMsg082A6021.Msg[0],&CAN_Rec.Data[0], 5);

三、CAN通讯易错点补充(实时更新)

1.CAN通讯失效,纠错分析-硬件层

1.CAN的匹配电阻为120Ω,硬件设计之初就请确认总线通讯中哪个终端加此匹配电阻(后面我会出一个硬件CAN电路设计范例与原理解析,在硬件栏详解此点)。

2.切记,没有焊接can通讯芯片或者can芯片没有正常工作的时候,mcu与can芯片是无有效can回路的,这个时候示波器测mcu的can引脚是无正常can波形的,别认为mcu故障,焊好can芯片以及外围器件再测试。

2.CAN通讯失效,纠错分析-软件层

1.ISO11898国际规范下的高速CAN通讯波特率一般使用250kbit/s,市面常见CAN盒子调试默认值也是250kbit/s,如无特殊要求请确保使用此波特率,使用其他波特率请按照相对应的规范标准与设计需求;

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
单片机与NB-IoT模组通讯的代码主要包括以下几个步骤: 1. 硬件连接:首先,需要将单片机与NB-IoT模组进行硬件连接。通常情况下,单片机的串口(UART)与NB-IoT模组的串口进行连接,并且确保单片机和模组之间的电源和地线连接正确。 2. 初始化模组:在代码中,需要对NB-IoT模组进行初始化操作。这包括设置串口通信的波特率、数据位、停止位等参数,还可以设置模组的工作模式等。 3. 建立网络连接:通过发送相应的AT指令,可以在代码中实现与NB-IoT网络的连接。这通常涉及到设置APN等网络参数,并发送SIM卡中的鉴权信息。 4. 发送数据:一旦成功连接到NB-IoT网络,就可以使用单片机发送数据到服务器。在代码中,可以设置需要发送的数据,然后通过AT指令将数据发送给NB-IoT模组。同时,需要设置模组的数据传输协议和传输方式等。 5. 接收数据:除了发送数据,单片机还可以接收来自服务器的数据。通过监听串口,当模组接收到服务器发送的数据时,单片机可以读取数据并进行相应的处理。 6. 断开网络连接:在通信完成后,可以通过发送相应的AT指令来断开与服务器的连接,释放资源。 以上是一个简单的单片机与NB-IoT模组通讯的代码框架。具体实现时,需要根据单片机和NB-IoT模组的型号和厂家提供的通信协议进行代码编写。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值