DSP28377S_CAN通信
CAN通信
- CAN通信的由来
- CAN通信格式
- CAN通信配置
- 实验验证
- 结束语
- 参考资料目录
CAN通信的由来
为适应“减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”的需要,1986 年德国电气商博世公司开发出面向汽车的CAN 通信协议。CAN属于现场总线的范畴,它是一种有效支持分布式控制或实时控制的串行通信网络。
CAN通信格式
CAN通信共有5种,分别为数据帧、遥控帧、错误帧、过载帧、帧间隔。数据帧格式由下图所示,分为标准格式和拓展格式,笔者目前仅使用到标准数据帧,使用其中的64bits数据段进行CAN节点间的数据交互,以ID号区分数据类型。注意ACK!实际波形中彼己电平高度不一
CAN通信配置
①初始化CAN的映射GPIO,使用TI封装好的函数初始化CAN,选择CAN时钟源,设置波特率,使能CAN中断触发源,开启CAN;
void InitCana(void)
{InitCanaGpio();//// Initialize the CAN controller//CANInit(CANA_BASE);//// Setup CAN to be clocked off the M3/Master subsystem clock//CANClkSourceSelect(CANA_BASE, 0);//// Set up the bit rate for the CAN bus. This function sets up the CAN// bus timing for a nominal configuration. You can achieve more control// over the CAN bus timing by using the function CANBitTimingSet() instead// of this one, if needed.// In this example, the CAN bus is set to 500 kHz. In the function below,// the call to SysCtlClockGet() is used to determine the clock rate that// is used for clocking the CAN peripheral. This can be replaced with a// fixed value if you know the value of the system clock, saving the extra// function call. For some parts, the CAN peripheral is clocked by a fixed// 8 MHz regardless of the system clock in which case the call to// SysCtlClockGet() should be replaced with 8000000. Consult the data// sheet for more information about CAN peripheral clocking.//CANBitRateSet(CANA_BASE, 200000000, 200000); // 波特率200kbps//// Enable interrupts on the CAN peripheral. This example uses static// allocation of interrupt handlers which means the name of the handler// is in the vector table of startup code. If you want to use dynamic// allocation of the vector table, then you must also call CANIntRegister()// here.//CANIntEnable(CANA_BASE, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);//// Enable the CAN for operation.//CANEnable(CANA_BASE);//// Enable CAN Global Interrupt line0//CANGlobalIntEnable(CANA_BASE, CAN_GLB_INT_CANINT0);
}
②确定收发缓存数组、ID号;
// CAN通信
unsigned char ucRXMsgData1[8]; // CAN接受数据
unsigned char ucTXMsgData2[8]; // CAN发送数据
unsigned char ucTXMsgData3[8]; // CAN发送数据
unsigned char ucTXMsgData4[8]; // CAN发送数据
unsigned char ucTXMsgData5[8]; // CAN发送数据
unsigned char ucTXMsgData6[8]; // CAN发送数据tCANMsgObject sRXCANMessage1 = {RX_MSG_OBJ_ID1, 0, 2, 8, ucRXMsgData1}; // CAN接收结构体 MSG_OBJ_RX_INT_ENABLE = 2
tCANMsgObject sTXCANMessage2 = {TX_MSG_OBJ_ID2, 0, 1, 8, ucTXMsgData2}; // CAN发送结构体 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage3 = {TX_MSG_OBJ_ID3, 0, 1, 8, ucTXMsgData3}; // CAN发送结构体 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage4 = {TX_MSG_OBJ_ID4, 0, 1, 8, ucTXMsgData4}; // CAN发送结构体 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage5 = {TX_MSG_OBJ_ID5, 0, 1, 8, ucTXMsgData5}; // CAN发送结构体 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage6 = {TX_MSG_OBJ_ID6, 0, 1, 8, ucTXMsgData6}; // CAN发送结构体 MSG_OBJ_TX_INT_ENABLE = 1
③根据自己的通信机制,封装好CAN通信收发函数;
void CANA_TX(void)
{unsigned int T_switch = 0; // 选择数据发送CANA_TX_FRAME_CNT = CANA_TX_FRAME_CNT + 1; // 帧计数自增
// CANA_TX_FRAME_CNT = CANA_TX_FRAME_CNT & 0xFF; // 帧计数达到255后清零,循环计数T_switch = CANA_TX_FRAME_CNT % 5;switch(T_switch){case 0:ucTXMsgData2[0] = CAN_CNT_delta & 0xFF; // 帧计数ucTXMsgData2[1] = 0; // ...此处略去ucTXMsgData2[2] = 0; // ...此处略去ucTXMsgData2[3] = 0; // ...此处略去ucTXMsgData2[4] = 0; // ...此处略去ucTXMsgData2[5] = 0; // ...此处略去ucTXMsgData2[6] = 0; // ...此处略去ucTXMsgData2[7] = 0; // ...此处略去CanaMessageSet(TX_MSG_OBJ_ID2, &sTXCANMessage2, MSG_OBJ_TYPE_TX);break;case 1:ucTXMsgData3[0] = CAN_CNT_delta & 0xFF; // 帧计数ucTXMsgData3[1] = 0; // ...此处略去ucTXMsgData3[2] = 0; // ...此处略去ucTXMsgData3[3] = 0; // ...此处略去ucTXMsgData3[4] = 0; // ...此处略去ucTXMsgData3[5] = 0; // ...此处略去ucTXMsgData3[6] = // ...此处略去ucTXMsgData3[7] = // ...此处略去CanaMessageSet(TX_MSG_OBJ_ID3, &sTXCANMessage3, MSG_OBJ_TYPE_TX);break;case 2:ucTXMsgData4[0] = CAN_CNT_delta & 0xFF; // 帧计数ucTXMsgData4[1] = // ...此处略去ucTXMsgData4[2] = // ...此处略去ucTXMsgData4[3] = // ...此处略去ucTXMsgData4[4] = // ...此处略去ucTXMsgData4[5] = // ...此处略去ucTXMsgData4[6] = // ...此处略去ucTXMsgData4[7] = // ...此处略去CanaMessageSet(TX_MSG_OBJ_ID4, &sTXCANMessage4, MSG_OBJ_TYPE_TX);break;case 3:ucTXMsgData5[0] = CAN_CNT_delta & 0xFF; // 帧计数ucTXMsgData5[1] = // ...此处略去ucTXMsgData5[2] = // ...此处略去ucTXMsgData5[3] = // ...此处略去ucTXMsgData5[4] = // ...此处略去ucTXMsgData5[5] = // ...此处略去ucTXMsgData5[6] = // ...此处略去ucTXMsgData5[7] = // ...此处略去CanaMessageSet(TX_MSG_OBJ_ID5, &sTXCANMessage5, MSG_OBJ_TYPE_TX);break;case 4:ucTXMsgData6[0] = CAN_CNT_delta & 0xFF; // 帧计数ucTXMsgData6[1] = // ...此处略去ucTXMsgData6[2] = // ...此处略去ucTXMsgData6[3] = // ...此处略去ucTXMsgData6[4] = // ...此处略去ucTXMsgData6[5] = // ...此处略去ucTXMsgData6[6] = // ...此处略去ucTXMsgData6[7] = // ...此处略去CanaMessageSet(TX_MSG_OBJ_ID6, &sTXCANMessage6, MSG_OBJ_TYPE_TX);CANA_TX_Active_Flag = 0;break;default:break;}if(CANA_TX_FRAME_CNT >= 254){CANA_TX_FRAME_CNT = 0;}
}
void CANA_RX(void)
{if((CANA_errorFlag == 0) && (CANA_RX_Flag == 1)){RX_FRAME_CANA.CNT = (Uint16)ucRXMsgData1[0]; // 字节1RX_FRAME_CANA.x= (Uint16)(((ucRXMsgData1[1] & 0xF0) >> 4) & 0x0F); // 字节2HRX_FRAME_CANA.xx = (Uint16)ucRXMsgData1[1] & 0x0F; // 字节2LRX_FRAME_CANA.xxx= (Uint16)(((ucRXMsgData1[2] & 0xF0) >> 4) & 0x0F); // 字节3HRX_FRAME_CANA.xxxx= (Uint16)ucRXMsgData1[2] & 0x0F; // 字节3LRX_FRAME_CANA.xxxxx= ((Uint16)(ucRXMsgData1[3] & 0xFF)) * 0.2; // 字节4RX_FRAME_CANA.xxxxxx= ((Uint16)(ucRXMsgData1[4] & 0xFF)) * 196.08; // 字节5RX_FRAME_CANA.xxxxxxx= ((int)(((ucRXMsgData1[5] & 0xFF) << 8 ) | (ucRXMsgData1[6] & 0xFF))) * 0.02; // 字节6 字节7}
}
④使用中断服务函数,传输接收发送结构体;
interrupt void CANA0_ISR(void)
{/************************************************************Description:CANA0中断服务程序用于检测中断产生原因,发送接收上位机数据************************************************************/uint32_t status;//// Read the CAN-A interrupt status to find the cause of the interrupt//status = CANIntStatus(CANA_BASE, CAN_INT_STS_CAUSE);//// If the cause is a controller status interrupt, then get the status//if(status == CAN_INT_INT0ID_STATUS){//// Read the controller status. This will return a field of status// error bits that can indicate various errors. Error processing// is not done in this example for simplicity. Refer to the// API documentation for details about the error status bits.// The act of reading this status will clear the interrupt.//status = CANStatusGet(CANA_BASE, CAN_STS_CONTROL);//// Check to see if an error occurred.//if(((status & ~(CAN_ES_TXOK | CAN_ES_RXOK)) != 7) &&((status & ~(CAN_ES_TXOK | CAN_ES_RXOK)) != 0)){//// Set a flag to indicate some errors may have occurred.//CANA_errorFlag = 1;}}//// Check if the cause is the CAN-A receive message object 1//else if(status == RX_MSG_OBJ_ID1){//// Get the received message//CANMessageGet(CANA_BASE, RX_MSG_OBJ_ID1, &sRXCANMessage1, true);//// Getting to this point means that the RX interrupt occurred on// message object 1, and the message RX is complete. Clear the// message object interrupt.//CANIntClear(CANA_BASE, RX_MSG_OBJ_ID1);//// Since the message was received, clear any error flags.//CANA_errorFlag = 0;CANA_RX_Flag = 1;CANA_TX_Active_Flag = 1;Timer_CANA_TX_1ms = 0;}//// Check if the cause is the CAN-A send message object 1//else if(status == TX_MSG_OBJ_ID2){//// Getting to this point means that the TX interrupt occurred on// message object 1, and the message TX is complete. Clear the// message object interrupt.//CANIntClear(CANA_BASE, TX_MSG_OBJ_ID2);//// Since the message was sent, clear any error flags.//CANA_errorFlag = 0;}//// Check if the cause is the CAN-A send message object 1//else if(status == TX_MSG_OBJ_ID3){//// Getting to this point means that the TX interrupt occurred on// message object 1, and the message TX is complete. Clear the// message object interrupt.//CANIntClear(CANA_BASE, TX_MSG_OBJ_ID3);//// Since the message was sent, clear any error flags.//CANA_errorFlag = 0;}//// Check if the cause is the CAN-A send message object 1//else if(status == TX_MSG_OBJ_ID4){//// Getting to this point means that the TX interrupt occurred on// message object 1, and the message TX is complete. Clear the// message object interrupt.//CANIntClear(CANA_BASE, TX_MSG_OBJ_ID4);//// Since the message was sent, clear any error flags.//CANA_errorFlag = 0;}//// Check if the cause is the CAN-A send message object 1//else if(status == TX_MSG_OBJ_ID5){//// Getting to this point means that the TX interrupt occurred on// message object 1, and the message TX is complete. Clear the// message object interrupt.//CANIntClear(CANA_BASE, TX_MSG_OBJ_ID5);//// Since the message was sent, clear any error flags.//CANA_errorFlag = 0;}//// Check if the cause is the CAN-A send message object 1//else if(status == TX_MSG_OBJ_ID6){//// Getting to this point means that the TX interrupt occurred on// message object 1, and the message TX is complete. Clear the// message object interrupt.//CANIntClear(CANA_BASE, TX_MSG_OBJ_ID6);//// Since the message was sent, clear any error flags.//CANA_errorFlag = 0;}//// If something unexpected caused the interrupt, this would handle it.//else{//// Spurious interrupt handling can go here.//}//// Clear the global interrupt flag for the CAN interrupt line//CANGlobalIntClear(CANA_BASE, CAN_GLB_INT_CANINT0);//// Acknowledge this interrupt located in group 9//PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;}
实验验证
采用上位机(PC)发1帧,下位机(DSP)应答5帧的方式,实现遥控与遥测,要想实现更为精准的定时,则可以采取其他的方式,比如TT-CAN等。上位机发送间隔6ms,下位机应答间隔1ms。上位机数据帧ID(00000000001),下位机数据帧ID(00000000010、00000000011、00000000100、00000000101、00000000110)。使用ZLG的示波器,因为他自带CAN通信解码功能,使用起来非常方便。
上位机遥控帧:
下位机遥测帧①
下位机遥测帧⑤
结束语
笔者对于CAN的使用,仅停留在数据帧这一简单的帧种类上,以后若是有项目需要,则再补充学习其他的帧种类。TI对于CAN的支持比较到位,我们可以直接调用相应的函数,即可实现功能。当然规则越明细,开发人员对其标准化程度会越高,但使用灵活度、自由度变差。
参考资料目录
《TMS320F2837xS Delfino Microcontrollers Datasheet》Memory章节
《TMS320F2837xS Delfino Microcontrollers Technical Reference Manual》CAN章节
RENESAS应用手册《CAN入门书》
C2000Ware有关CAN的所有例程
DSP28377S_CAN通信相关推荐
- RPC 笔记(05)— socket 通信(单线程服务器)
1. Python 标准库 1.1 socket 提供 RPC 服务的网络通信功能,方便用户编写 tcp/udp 相关的代码.两个不同机器的进程需要通信时,可以通过 socket 来传输数据. 客 ...
- Python 多进程笔记 — 启动进程的方式、守护进程、进程间通信、进程池、进程池之间通信、多进程生产消费模型
1 面向过程启动多进程 Python 操作进程的类都定义在 multiprocessing 模块,该模块提供了一个 Process 类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另 ...
- HTTP 协议入门 — (TCP/IP协议族、通信传输流、URI 与 URL 的区别、Cookie 状态管理、HTTP 支持的方法、状态码类别、HTTP 首部字段)
TCP/IP协议族 在介绍 HTTP 协议之前,我们先对 TCP/IP 协议族有个大概的了解,TCP/IP 协议从上到下主要分为应用层.传输层.网络层和数据链路层,各层的主要功能如下表所示: 协议层 ...
- python第三方库之学习pyserial库--串口通信
pyserial串口通信库 1.安装pyserial库 2.填写串口参数的注意事项 3.简单封装一下 4.碰到的bug 1.安装pyserial库 pip install pyserial versi ...
- python 网络编程之Socket通信案例消息发送与接收
背景 网络编程是python编程中的一项基本技术.本文将实现一个简单的Socket通信案例消息发送与接收 正文 在python中的socket编程的大致流程图如上所示 我们来首先编写客户端的代码: # ...
- NVIDIA空中导航SDK改造5G通信
NVIDIA空中导航SDK改造5G通信 Transforming Next-Generation Wireless with 5T for 5G and the NVIDIA Aerial SDK N ...
- 十五天精通WCF——第六天 你必须要了解的3种通信模式
十五天精通WCF--第六天 你必须要了解的3种通信模式 原文:十五天精通WCF--第六天 你必须要了解的3种通信模式 wcf已经说到第六天了,居然还没有说到这玩意有几种通信模式,惭愧惭愧,不过很简单啦 ...
- C# Socket系列三 socket通信的封包和拆包
通过系列二 我们已经实现了socket的简单通信 接下来我们测试一下,在时间应用的场景下,我们会快速且大量的传输数据的情况! 1 class Program 2 { 3 static void Mai ...
- flex java socket通信
引用:http://developer.51cto.com/art/201003/189791.htm Java socket通信如何进行相关问题的解答呢?还是需要我们不断的学习,在学习的过程中会遇到 ...
- VC串口通信编程-2
VC串口通信编程 (2009-07-08 13:48:40) 转载▼ Win32串口编程(转:韩耀旭) 在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信.串口通信 ...
最新文章
- python 编程入门-Python编程入门电子书教程,看这几个就够了
- Gartner Magic Quadrant for Enterprise Network Firewall (2018,2017,2016,2015,2014,2013,2011,2010)
- struts:file 提交给action后获取文件信息
- 前端学习(3136):react-hello-react之不用柯里化的写法
- linux 内核宏container_of剖析
- 三星Galaxy A90翻转摄像头出变故:或仍将采用水滴屏设计
- python reduce函数怎么用的_我如何仅通过使用reduce函数在python中创建单...
- Quick-Cocos2d-x初学者游戏教程(五) --------------------- 辅助工具和跳转场景
- 安装svn + vs code配置svn
- linux修改域名命令是,Linux系统脚本命令修改动态域名解析记录
- 什么是平面设计,平面设计主要做什么?
- 基于IMDb数据集的情感分析(TF-IDF与机器学习实现)
- xpath prase string
- 【WSL2】ubuntu22.04 安装docker
- PMEdit一个富文本框可以编辑文本、图片并可以显示GIF动画
- 个性化智能推荐(协同过滤算法)技术研究
- snprintf() 函数
- SQL Assistant简介
- web前端开发需要学什么(包含前端学习路线)
- 小米与美的的“初吻”是为了什么
热门文章
- 离散数学4_第5章关系与函数__关系矩阵
- IEEE 2021年新增Fellow出炉,70余位华人入选
- 怎么用静图做gif动图?三步教你轻松做动图
- SiT3808:1 -80MHz 单端压控振荡器VCXO
- 22种免费网络推广方式有哪些?
- IE11 兼容 ES6
- python中单引号的作用_Python中单引号,双引号,3个引号的用法
- LiJian-kaldi搭建在线语音识别系统 资料汇总
- hdu 5064 Find Sequence(单调性优化DP)
- 【蓝牙sbc协议】sbc源码阅读笔记(二)——sbc_struct详解(下)