关于由多个不同的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 源文件在百度网盘

CAN编写完分帧发送, 分帧接收,J1939位域型结构体心得的更多相关文章

  1. I帧、P帧、B帧、GOP、IDR 和PTS, DTS之间的关系

    一.视频传输原理 视频是利用人眼视觉暂留的原理,通过播放一系列的图片,使人眼产生运动的感觉.单纯传输视频画面,视频量非常大,对现有的网络和存储来说是不可接受的.为了能够使视频便于传输和存储,人们发现视 ...

  2. java socket传送一个结构体给用C++编写的服务器解析的问题

    另一端是Java写客户端程序,两者之间需要通信.c++/c接收和发送的都是结构体,而Java是直接发送的字节流或者byte 数组.解决方法:c++/c socket 在发送结构体的时候其实发送的也是字 ...

  3. CAN分帧发送程序说明

    试验平台 仅仅 需要一台主机 一台 周立功 CAN 助手, 一个232 助手就OK ICAN 协议 资源节点地址 电脑 我认为是0x01 51单片机主机的地址 是 0x1f 建立连接的 功能码 是0x ...

  4. 构造并发送Beacon帧以伪造任意WiFi热点

    请想象一下这样的情景:你可以任意伪造很多个WiFi热点, 这个技术只能在linux上使用,而且对无线网卡也有一定的挑剔,具体的下面会讲- 阶段一:基本原理 首先需要搞清楚的是,手机.电脑等支持WiFi ...

  5. 程序设计入门——C语言 第6周编程练习 2 完数(5分)

    2 完数(5分) 题目内容: 一个正整数的因子是所有可以整除它的正整数.而一个数如果恰好等于除它本身外的因子之和,这个数就称为完数.例如6=1+2+3(6的因子是1,2,3). 现在,你要写一个程序, ...

  6. DIOCP数据包太大,请在业务层分拆发送

    DIOCP数据包太大,请在业务层分拆发送 DIOCP日志记录异常:数据包太大,请在业务层分拆发送...... 跟踪发现,原因在下图:

  7. stm32+lwip(五):以太网帧发送测试

    我是卓波,很高兴你来看我的博客. 系列文章: stm32+lwip(一):使用STM32CubeMX生成项目 stm32+lwip(二):UDP测试 stm32+lwip(三):TCP测试 stm32 ...

  8. CAN总线远程帧和错误帧

    远程帧 通常,数据传输是由数据源节点(例如,传感器发出数据帧)自主完成的.但也可能存在目标节点向源节点请求发送数据的情况.要做到这一点,目标节点需发送一个远程帧,其中的标识符应与所需数据帧的标识符相匹 ...

  9. 图解 I帧,B帧以及P帧

    I‑frame (Intra-coded picture): 即完整的一张图片 P‑frame (Predicted picture): 与前面一张图片的区别的区域 B‑frame (Bidirect ...

随机推荐

  1. 《SQL 进阶教程》 查找局部不一致的数据

    -- 从下面这张商品表里找出价格相等的商品的组合 select * from products p1LEFT JOIN products p2on p1.price = p2.price and p1 ...

  2. git 常用命令记录 -- 快捷&备忘

    1.安装 略2.git拉取远程分支 git config user.name git config user.email git config --global user.name xxxx git ...

  3. C#学习之time控件和timer_tick事件 -----转载

    Timer控件:Timer控件只有绑定了Tick事件,和设置Enabled=True后才会自动计时,停止计时可以用Stop()控制,通过Stop()停止之后,如果想重新计时,可以用Start()方法来 ...

  4. mybatis动态参数查询

    参考:https://blog.csdn.net/zbw18297786698/article/details/53727594

  5. 第2节 storm路由器项目开发:1 - 7、网络路由器项目

    网安需求: 1:IFTTT:随着物联网的兴起,if this then that .如果出现这种情况,那么及时反映做出对应的操作. 判断手机号黑白名单,mac地址黑白名单.如果是碰到手机号或者mac地 ...

  6. Ajax--概述

    1.Ajax(Asynchronous JavaScript and XML),允许浏览器与服务器通信而无需刷新当前页面的技术都被叫做Ajax; 2.XMLHttpRequest:该对象是对JavaS ...

  7. 45 孩子们的游戏(圆圈中最后剩下的数) + list操作总结+ for_each多记忆容易忘记

    题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指 ...

  8. 「NOIP2015」斗地主

    传送门 Luogu 解题思路 给你们一张搜索顺序图,然后就大力模拟就好. 细节注意事项 爆搜题,你们懂的... 参考代码 写的有点丑了,洛谷上只能过加强版的88分,会T六个点 #include < ...

  9. Redis散列表类型

    散列类型(hash)的键值也是一种字典结构,其存储了字段(field)和字段值的映射,但字段值只能是字符串,不支持其他的数据类型. 一个散列类型键可以包含至多2^32 -1个字段. 命令 赋值 HSE ...

  10. SSH框架系列:Spring配置多个数据源

    分类: [java]2013-12-09 16:59 1247人阅读 评论(0) 收藏 举报 1.问题的引入 对于普通的SSH框架而言,一般配置一个数据源,一个SessionFactory,一个事务管 ...