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

这篇具有很好参考价值的文章主要介绍了单片机编程-CAN通讯-理解与实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

声明

最近刚刚做完一个较复杂项目,为强化巩固,准备写几篇笔记方便后续回溯,本篇重点是个人对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举例,要写的太多我直接写纸上拍下来:

单片机can通信,单片机编程,单片机,网络,服务器通讯帧结构里标注了各个帧的区间位置,结合手稿看下:
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,如无特殊要求请确保使用此波特率,使用其他波特率请按照相对应的规范标准与设计需求;文章来源地址https://www.toymoban.com/news/detail-570870.html

到了这里,关于单片机编程-CAN通讯-理解与实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【单片机】UART、I2C、SPI、TTL、RS232、RS422、RS485、CAN、USB、SD卡、1-WIRE、Ethernet等常见通信方式

    在单片机开发中,UART、I2C、RS485等普遍在用,这里做一个简单的介绍 UART口指的是一种物理接口形式(硬件)。 UART是异步(指不使用时钟同步,依靠帧长进行判断),全双工(收发可以同时进行)串口总线。它比同步串口复杂很多。有两根线,一根TXD用于发送,一根RXD用于接收

    2024年02月11日
    浏览(31)
  • 利用51单片机实现与RS485通讯,接收数据

    目录 一、研究背景 二、硬件准备 三、软件准备  四、实验原理 五、单片机代码  六、Python读取串口处理数据 七、开发总结          学院给了14天的时间,一枚专业课紧的大三狗匆忙做出来。探索出一种研究阳极氧化工艺电解液中金属离子浓度在线测量和监控的设备和方式

    2024年02月07日
    浏览(31)
  • 关于Keil开发C51单片机的头文件“REGX52.H“问题。其他类型的warning C318:can‘t open fine头文件也可以按照这个步骤来

    我用的是宏晶STC的入门板子,最近在学习写代码的时候也是遇到了这个问题,这个\\\"REGX52.H\\\"是只能用大写的,我们也必须用大写。 其他类型的板子也可以按照最下面的步骤试试;    第一步,我们先点开魔法棒   第二步,我们点开魔法棒后按箭头步骤点击 第三步 :打开后新建,选择你

    2024年02月12日
    浏览(26)
  • 应广单片机跑马灯实现--阻塞式编程模式

           我这边再写了一个跑马灯程序,使用阻塞式编程模式,看起来会更简单直观。对于初学者来说,阻塞式编程比较直观好理解。在一些任务单一或是任务不多的程序来说,还是不错的选择。但是建议还是采用任务式/查询式编程,这样使程序会有更好的扩展性能,减少推

    2024年02月09日
    浏览(37)
  • STM32G0+EMW3080+阿里云实现单片机WiFi智能联网功能(一)EMW3080实现和PC之间的串口通讯

    项目描述:该系列记录了STM32G0+EMW3080实现单片机智能联网功能项目的从零开始一步步的实现过程; 硬件环境:单片机为STM32G030C8T6;物联网模块为EMW3080V2-P;网联网模块的开发板为MXKit开发套件,具体型号为XCHIP MXKit-Base V2.2; 软件环境:STM32需要的软件有STM32CubeMX和STM32CubeIDE;

    2024年02月10日
    浏览(41)
  • 基于51单片机的通讯聊天系统

    通过编写51单片机通讯聊天程序,掌握51单片机的各个模块的知识点和运用C语言编写51单片机的能力。 用51单片机与口袋开发板设计实现一个单片机与电脑串口助手实现通讯功能的程序。程序的主要功能如下: 1 OLED初始化显示Welcome to Bluetooth communication! 字符串。 2 程序开始时

    2023年04月08日
    浏览(26)
  • C51单片机实现流水灯的三种编程方法

        初步学习51单片机,总是离不开流水灯。     下面介绍实现流水灯的三种方法。 目录 一、调用数组实现流水灯 二、 移位符实现流水灯 三、采用左右移函数实现流水灯 思路:for循环调用数组当中的进制数后赋值给LED小灯的负极。 移位符:         移位运算符在程序设

    2024年02月01日
    浏览(34)
  • OpenMV图像处理之后给单片机通讯

    详细介绍:OpenMV扫码识别;OpenMV串口通讯详解;OpenMV的单颜色识别讲解;MSP430F5529库函数学习——串口 目录 接线 OpenMV代码 代码 for...in...部分简单介绍 stm32代码 msp430f5529代码 个人一开始以为我写了的这几个博客已经够清楚了,让各大网友正常使用OpenMV图像处理,然后与单片机

    2024年02月16日
    浏览(29)
  • STC15系列单片机学习4:串口通讯

    在使用单片机的串口前,得先知道所使用的单片机有几个串口,再结合你的硬件电路图来使用哪个串口。 以下是STC15各系列单片机的串口数量,STC15W4K32S4系列有4个串口 工作模式0:同步移位寄存器(官方建议初学者不学) 工作模式1:8位串口,波特率可变 工作模式2:9位串口

    2024年04月13日
    浏览(29)
  • 电脑通过串口助手和51单片机串口通讯

    今天有时间把电脑和51单片机之间的串口通讯搞定了,电脑发送的串口数据,单片机能够正常接收并显示到oled屏幕上,特此记录一下,防止后面自己忘记了怎么搞得了。 先来两个图片看看结果吧! 下面是串口3.c的文件全部内容: 下面是oled.h的全部内容: 下面是oled.c的全部内

    2024年02月07日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包