CAN编写完分帧发送, 分帧接收,J1939位域型结构体心得

关于由多个不同的C文件构成的工程,我采用以下方法

以为400Hz数字电源程序为例

假设工程由以下文件组成

DC_Comm.c 主要完成串口通讯部分

DC_Config.c 主要完成时钟,外设 中断初始化

DC_Control.c 主要完成电源数字化SPWM控制,以及串口接收中断的处理

DC_Memory.c 主要完成FM33256 的SPI时序的软件实现。故障记录与操作记录的写入与读取操作。

DC_Timing.h 主要完成与CPLD配合的一些时序。

 

响应的在include 中我还用到了一些头文件

DC_Comm.h 主要用来对DC_Comm.c中用到的数据类型进行声明,以及函数进行声明。这些函数都在DC_Comm.c中定义

DC_Control.h 主要用来对DC_Control.c中用到的数据类型进行声明,以及函数进行声明。这些函数在DC_Control.c 中定义

DC_Types.h 中宏定义了 一些Q格式常量 ,以及一些函数的声明。

 

总之:假设在DC_Comm.c中 定义了函数SCIRXProcess ()

则在DC_Comm.h中声明了 extern void SCIRXProcess ()

那么我在main.c 文件中调用 SCIRXProcess()的时候, 直接在main.c的前方将DC_Comm.h 包含进来就ok .

总结 就是 一个工程假设有A,B,C,main.c 4个文件组成, 假设在main.c 中定义了一些变量p,q,m 若A文件要使用p , 则需要在A文件的开头 用extern 关键字进行声明。

 

抛砖引玉:开始进入基于ican协议的CAN开发,该平台单片机采用STC89C52

该工程由两个文件组成SJA.C 和ican.c

    

SJA1000.h 中 定义了寄存器的硬件地址

基本地址 #define SJA_BaseAdr 0X7F00 由外部电路的硬件地址决定 单片机的那一个引脚连接在SJA1000的CS引脚上

内部控制寄存器 #define REG_CONTROL SJA_BaseAdr+0x00

命令寄存器 #define REG_COMMAND SJA_BaseAdr+0x01

状态此存器 #define REG_STATUS SJA_BaseAdr+0x02

…….

发送缓冲区寄存器

#define REG_TXBuffer1 SJA_BaseAdr+0x10 //发送缓冲区1

#define REG_TXBuffer2 SJA_BaseAdr+0x11 //

#define REG_TXBuffer3 SJA_BaseAdr+0x12 //

#define REG_TXBuffer4 SJA_BaseAdr+0x13 //

#define REG_TXBuffer5 SJA_BaseAdr+0x14 //

#define REG_TXBuffer6 SJA_BaseAdr+0x15 //

#define REG_TXBuffer7 SJA_BaseAdr+0x16 //

#define REG_TXBuffer8 SJA_BaseAdr+0x17 //

#define REG_TXBuffer9 SJA_BaseAdr+0x18 //

#define REG_TXBuffer10 SJA_BaseAdr+0x19 //

#define REG_TXBuffer11 SJA_BaseAdr+0x1A //

#define REG_TXBuffer12 SJA_BaseAdr+0x1B //

#define REG_TXBuffer13 SJA_BaseAdr+0x1C //发送缓冲区13

接收缓冲区寄存器

#define REG_RXBuffer1 SJA_BaseAdr+0x10 //接收缓冲区1

#define REG_RXBuffer2 SJA_BaseAdr+0x11 //

#define REG_RXBuffer3 SJA_BaseAdr+0x12 //

#define REG_RXBuffer4 SJA_BaseAdr+0x13 //

#define REG_RXBuffer5 SJA_BaseAdr+0x14 //

#define REG_RXBuffer6 SJA_BaseAdr+0x15 //

#define REG_RXBuffer7 SJA_BaseAdr+0x16 //

#define REG_RXBuffer8 SJA_BaseAdr+0x17 //

#define REG_RXBuffer9 SJA_BaseAdr+0x18 //

#define REG_RXBuffer10 SJA_BaseAdr+0x19 //

#define REG_RXBuffer11 SJA_BaseAdr+0x1A //

#define REG_RXBuffer12 SJA_BaseAdr+0x1B //

#define REG_RXBuffer13 SJA_BaseAdr+0x1C //接收缓冲区13

 

SJA1000.h中声明了若干函数 包括:

 

 

 

 

 

 

 

 

 

 

 

CAN总线发送数据的流程:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

发送数据还有一种写法:

    if ((ReadSJAReg(REG_CAN_SR) & (TBS_BIT|TCS_BIT)) != (TBS_BIT|TCS_BIT))

     { status = 0;}    

 

查看SJA1000资料 有以下要点:

  1. SJA1000 的peilican模式的发送是单次发送

    (2)与发送有关的状态寄存器的各位定义

符号

名称

功能

SR.5

Ts

发送状态

注3

1

发送 sja1000 在传送信息

0

空闲 没有要发送的信息

SR.3

Tcs

发送完毕状态 注4

1

完毕 最近一次发送请求被成功处理

0

未完毕 当前发送请求未处理完毕

SR.2

Tbs

发送缓冲区状态 注5

1

释放:CPU可以向发送缓存器写数据

0

锁定:CPU不能访问发送缓冲器,有信息正在等待

发送或者正在发送

 

注3:如果接收状态位和发送状态位 都是0 ,则CAN总线是空闲的。

注4:无论何时发送请求位被置为1,发送完毕位(Tcs)都会被置为0,发送完毕位会一直保持到消息被成功发送。

注5:如果CPU在发送缓冲器状态为是0时(锁定)试图写发送缓冲器,则写入的字节被拒绝接收且会在无任何提示的情况下丢失。

 

     与485通讯比较,485发送出去的数据 若接收方没有安装,主机依然显示发送成功,相比较CAN,CAN发送数据给另一个节点,则CAN节点在应答场会给主机CAN节点一个信号,表示主节点的CAN发送成功。

 

 

 

 

 

 

 

 

 

 

 

关于ican.c 中的应用

首先 我用结构体定义 iCANMSG 数据类型

在SJA.C 中定义了 icanmsg 数据类型的变量

 

iCANMSG message1 ;

iCANMSG * pcan ;

iCANMSG msg_readonly_s;

 

此外:对于ican协议我专门定义了指针 pcan 并用宏定义去进行处理,这样很方便的与29位ID号所对应的标识符号对应上。

 

 

然后在ican.c中因为 用到了这些变量 全部在前面加上 extern

 

在main.c 里 我用

至于为什么要在main () 文件 的开头 定义 msg_readonly_s

iCANMSG msg_readonly_s; //保存副本

 

是因为 如果出现如下情况 相当于是一个临时变量,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

关于使用位阈型结构体的总结:

ICAN协议:

遵循原则 第    条我认为不一定对,因为在ican下 我infoID定义8位

但是ican下 我的低3位 是没有被定义的 undef

Ican 协议的格式定义如下:

帧结构信息

BIT7

BIT6

BIT5

BIT4

BIT3

BIT2

BIT1

BIT0

说明

FF

RTR

X

X

DLC.3

DLC.2

DLC.1

DLC.0

 

帧标识符信息

 

ID28

ID27

ID26

ID25

ID24

ID23

ID22

ID21

00

SRCMACID(资源节点编号)

 

ID20

ID19

ID18

ID17

ID16

ID15

ID14

ID13

00

DestMACID(目标节点编号)

 

ID12

ID11

ID10

ID9

ID8

ID7

ID6

ID5

ACK

FUNCID(功能码)

SourceID(资源节点编号)

 

ID4

ID3

ID2

ID1

ID0

X

X

X

 

SourceID(资源节点编号)

未使用(忽略)

我定义的方法如下:

 

 

但是 我在应用j1939协议的时候

J1939协议 所定义的帧信息ID结构如下 (29位扩展)

ID28

ID27

ID26

ID25

ID24

ID23

ID22

ID21

优先级

保留位

数据页

PDU格式

ID20

ID19

ID18

ID17

ID16

ID15

ID14

ID13

PDU格式

特定PDU

ID12

ID11

ID10

ID9

ID8

ID7

ID6

ID5

特定PDU

源地址

ID4

ID3

ID2

ID1

ID0

X

X

X

源地址

     

 

 

我的定义方法如下: 从低位到高位定义

 

 

 

 

 

 

 

 

      

 

 

 

 

 

 

 

 

 

 

 

 

 

 

假设 节点2 和节点3正在通讯,某一时刻节点1要设置节点3 。给节点3发数据,

节点1不用等到节点2和节点3不通讯了。节点1直接发送数据,节点1 的CAN硬件会自动控制给节点1发数据。不用人为控制,由CAN控制器的硬件来完成。

 

 

基于51单片机的CAN通讯试验:

方法: 51单片机程序中不断的往上位机(CANtest)发送数据 ,然后在某一任意时刻, 我用周立功的(CANtest)发送建立连接命令,看单片机是否可以正常响应。 并且记录示波器的波形图。

 

在CANTEST 上 点击 发送消息帧 如下图 第2个较短的时间间隔内的帧

该消息的ID号是 0x0023e4fe 数据场是 00 ee 0a

 

 

 

51单片机接收到消息以后,往上传送消息 该消息的数据场 00 01 02 03 04

如下图所示: 左侧第一个较短的帧 就是 51单片机上传的响应帧。数据场为00 01 02 03 04

响应场 的数据 (该数据我先不解析)

 

为了验证我用kavaser 捕捉以下时间间隔 。

看两个时间间隔

第一 就是上位机 发送建立连接命令 到收到51单片机 返回的响应帧的时间间隔

第二 就是51 单片机 返回响应帧 到 51单片机继续往上位机传送计数值的时间间隔

第三 测试 看一下 默认情况下 上位机不发送连接命令,51单片机上传数据的时间间隔

 

第一个时间 我用示波器测试是: 约为200ms

第二个时间 我用示波器测试是: 约为1.5ms

第三个时间 我用示波器测试是: 约为12.4ms

 

我用kavaser 在 20190423 的 9点32 和 9点33 分左右的时候分别用cantest 发送建立连接命令 接收的时间间隔是 6041-4021=2021

2021*百分之一毫秒 约等于 200ms 与示波器测试一致

 

接下来 我用kavaser 的logging 功能测试

 

 

 

 

 

 

 

 

时刻 9点32 的数据

9点33时刻的数据

接下来 我的想法是 你新找一个51单片机 ,然后替换 周立功上位机的功能,进行连接命令的发送

试验平台大家如下:

 

 

 

 

试验平台照片

 

 

 

 

 

 

实际上 CAN 网络是不分主机和从机的,不像485网络。这里我设计的主机的功能就是:

按下:靠4个数码管一侧的按键, 按一下 数码管的显示增加1 然后并发送一帧

发送的消息帧 为 ID号 0x0023e4fe 数据场是00 ee 20 (16进制的20代表十进制32

计数 32次,认为握手时间是32秒,超过32秒可以认为连接断开)

做这个事情的目的是:消息帧的发送我在用嵌入式编程的时候,用can_send_anylength()函数就可以搞定。这种情况使用于网络中一直有数据通讯存在的情况。

 

 

 

 

 

 

 

试验现象:

在时间10点24

我用主机(51单片机)的按键 发送消息帧 ID号0x0023e4fe 数据 00 ee 2 0

从机51单片机 在1秒以后 反馈给我响应帧 ID号0X3E034EE 数据是 00 01 02 03 04

在这个1秒的时间间隔内,CAN数据线上 还有一帧消息在传递 如下图所示:

若在32秒内,主机再次发送连接命令,。从机将给主机反馈 已经在连接中的提示消息

该消息 的ID号是 0X3E02FFE 00 03

 

 

 

 

在时间:10:30:10:7323 我又用主机发送了 建立连接的消息

ID号是 0x023e4fe 00 ee 20

此时在10:30:10:8433 时刻 从机就给主机回复了消息帧 在这个时间间隔内,无其他帧在传递。 如下图:

重要:与上面的那个中间有一帧的情况的截图进行对比:可以知道:从机在接收到主机的连接,命令后,会判断CAN线上是否空闲,如果当前有数据发送或接收 从机就等该数据发送完毕以后,在发送响应帧, 如果CAN线上空闲,则从机便可以直接发送给主机器响应帧。发送程序的时候 从机程序仅仅检测 是不是上一帧数据是不是发送完成,并不检测总线上空闲,这一块是CAN控制器硬件自动完成的,我暂且先这么认为

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在时间 10时30分 13秒 在32秒的计时时间内, 我再次发送建立连接命令,此时 从机

便会给我回复响应的消息帧 帧ID号 0x 3e02ffe 00 03 如下图所示:

 

 

 

Word 源文件在百度网盘

 

posted @ 2020-01-20 17:12  STEVEN-SUN  阅读(1992)  评论(0编辑  收藏  举报