关于由多个不同的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. iOS Common Design Patterns:常用设计模式

    原文:http://www.jianshu.com/p/bf431fff235e 我们经常在编程中使用各种设计模式,在iOS中比较常见的设计模式有:单例模式.委托模式.观察者模式,当然实际上在Coco ...

  2. 为Docker Desktop安装kubernet-dashboard

    在上一篇,在windows上,用最简方法(比其他的脚本法,提前拉取镜像简便太多了)安装好了docker desktop,并启用了内置的kubernetes. 这种安装方法实际上是在Hyper-v虚拟机 ...

  3. python对文件中光标的操作迭代器

    seek()    默认从文件开头开始.seek(10) seek(10,1)   需要以b的模式读取文件,从相对位置进行移动光标 seek(-3,2)  倒着移动光标的模式 例如: f= open( ...

  4. 题解 CF165D 【Beard Graph】

    思路:将黑边标记为1,白边标记为100000,树链剖分 如果查询时ans超过100000,那就有白边,输出-1,不然直接输出ans #include<bits/stdc++.h> #def ...

  5. 为什么阿里Java规约要求谨慎使用SimpleDateFormat

    前言 在阿里Java开发规约中,有强制性的提到SimpleDateFormat 是线程不安全的类 ,在使用的时候应当注意线程安全问题,如下: 其实之前已经介绍过使用JDK1.8的DateTimeFor ...

  6. nginx反向代理(2)

    目录 nginx缓存 基本概念 常用模块 proxy_cache 超时相关 常见架构 ========================================================= ...

  7. 【剑指Offer面试编程题】题目1504:把数组排成最小的数--九度OJ

    题目描述: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 输入: 输 ...

  8. 移动互联网APP测试流程及测试点

    1.2测试周期 测试周期可按项目的开发周期来确定测试时间,一般测试时间为两三周(即15个工作日),根据项目情况以及版本质量可适当缩短或延长测试时间.正式测试前先向主管确认项目排期. 1.3测试资源 测 ...

  9. 【Python数组及其基础操作】【numpy ndarray】

    一.创建数组 在python中创建数组最简单的办法就是使用array函数.它接受一切序列型的对象,然后产生一个含有传入数据的numpy数组.其中,嵌套序列(比如由一组等长列表组成的列表)会被转换为一个 ...

  10. 【深入】 - AST抽象语法树

    参考: https://segmentfault.com/a/1190000016231512