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通信相关推荐

  1. RPC 笔记(05)— socket 通信(单线程服务器)

    1. Python 标准库 1.1 socket 提供 RPC 服务的网络通信功能,方便用户编写 tcp/udp 相关的代码.两个不同机器的进程需要通信时,可以通过 socket 来传输数据. ​ 客 ...

  2. Python 多进程笔记 — 启动进程的方式、守护进程、进程间通信、进程池、进程池之间通信、多进程生产消费模型

    1 面向过程启动多进程 Python 操作进程的类都定义在 multiprocessing 模块,该模块提供了一个 Process 类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另 ...

  3. HTTP 协议入门 — (TCP/IP协议族、通信传输流、URI 与 URL 的区别、Cookie 状态管理、HTTP 支持的方法、状态码类别、HTTP 首部字段)

    TCP/IP协议族 在介绍 HTTP 协议之前,我们先对 TCP/IP 协议族有个大概的了解,TCP/IP 协议从上到下主要分为应用层.传输层.网络层和数据链路层,各层的主要功能如下表所示: 协议层 ...

  4. python第三方库之学习pyserial库--串口通信

    pyserial串口通信库 1.安装pyserial库 2.填写串口参数的注意事项 3.简单封装一下 4.碰到的bug 1.安装pyserial库 pip install pyserial versi ...

  5. python 网络编程之Socket通信案例消息发送与接收

    背景 网络编程是python编程中的一项基本技术.本文将实现一个简单的Socket通信案例消息发送与接收 正文 在python中的socket编程的大致流程图如上所示 我们来首先编写客户端的代码: # ...

  6. NVIDIA空中导航SDK改造5G通信

    NVIDIA空中导航SDK改造5G通信 Transforming Next-Generation Wireless with 5T for 5G and the NVIDIA Aerial SDK N ...

  7. 十五天精通WCF——第六天 你必须要了解的3种通信模式

    十五天精通WCF--第六天 你必须要了解的3种通信模式 原文:十五天精通WCF--第六天 你必须要了解的3种通信模式 wcf已经说到第六天了,居然还没有说到这玩意有几种通信模式,惭愧惭愧,不过很简单啦 ...

  8. C# Socket系列三 socket通信的封包和拆包

    通过系列二 我们已经实现了socket的简单通信 接下来我们测试一下,在时间应用的场景下,我们会快速且大量的传输数据的情况! 1 class Program 2 { 3 static void Mai ...

  9. flex java socket通信

    引用:http://developer.51cto.com/art/201003/189791.htm Java socket通信如何进行相关问题的解答呢?还是需要我们不断的学习,在学习的过程中会遇到 ...

  10. VC串口通信编程-2

    VC串口通信编程 (2009-07-08 13:48:40) 转载▼ Win32串口编程(转:韩耀旭) 在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信.串口通信 ...

最新文章

  1. python 编程入门-Python编程入门电子书教程,看这几个就够了
  2. Gartner Magic Quadrant for Enterprise Network Firewall (2018,2017,2016,2015,2014,2013,2011,2010)
  3. struts:file 提交给action后获取文件信息
  4. 前端学习(3136):react-hello-react之不用柯里化的写法
  5. linux 内核宏container_of剖析
  6. 三星Galaxy A90翻转摄像头出变故:或仍将采用水滴屏设计
  7. python reduce函数怎么用的_我如何仅通过使用reduce函数在python中创建单...
  8. Quick-Cocos2d-x初学者游戏教程(五) --------------------- 辅助工具和跳转场景
  9. 安装svn + vs code配置svn
  10. linux修改域名命令是,Linux系统脚本命令修改动态域名解析记录
  11. 什么是平面设计,平面设计主要做什么?
  12. 基于IMDb数据集的情感分析(TF-IDF与机器学习实现)
  13. xpath prase string
  14. 【WSL2】ubuntu22.04 安装docker
  15. PMEdit一个富文本框可以编辑文本、图片并可以显示GIF动画
  16. 个性化智能推荐(协同过滤算法)技术研究
  17. snprintf() 函数
  18. SQL Assistant简介
  19. web前端开发需要学什么(包含前端学习路线)
  20. 小米与美的的“初吻”是为了什么

热门文章

  1. 离散数学4_第5章关系与函数__关系矩阵
  2. IEEE 2021年新增Fellow出炉,70余位华人入选
  3. 怎么用静图做gif动图?三步教你轻松做动图
  4. SiT3808:1 -80MHz 单端压控振荡器VCXO
  5. 22种免费网络推广方式有哪些?
  6. IE11 兼容 ES6
  7. python中单引号的作用_Python中单引号,双引号,3个引号的用法
  8. LiJian-kaldi搭建在线语音识别系统 资料汇总
  9. hdu 5064 Find Sequence(单调性优化DP)
  10. 【蓝牙sbc协议】sbc源码阅读笔记(二)——sbc_struct详解(下)