以前做项目,不注意记录调试过程中遇到的问题,以后应该注意这一点。今天抽空总结一下PCI-CAN卡驱动与数据通信调试过程中遇到的问题,方便以后回忆和思考。

1. 中断服务之字节流报文组包状态机

这是一个典型的适合采用状态机来思考和处理数据的场合。报文一般分为这几个字段:报文头,长度,命令,数据,校验和。在报文接收端,能看到的只是一连串的字节,这需要状态机的控制。

状态机分这几个状态:(1)接收报文头;(2)接收报文长度字段;(3)接收剩余部分,以报文长度作为跳出判断状态条件;(4)校验报文;(5)数据加工存储,组包完成。

  
                            图1 状态机之报文组包

在最初的代码中,没有用状态机的思想,比较混乱。在使用状态机的过程中,注意状态转移条件和计数器。

2. 双端口RAM数据读写过程分析

为了保证数据的完备性,双端口RAM要求PCI和MCU不能在同一时刻读写同一个memory uint,为此双端口RAM提供了独占使用的Sepahores,特殊的电路设计保证了Sempahores被一端写入0后,另一端就会写入0失败。通过Sepahores协调MCU和PCI对RAM的同时Read/Write操作。

将双端口RAM作为循环队列,在RAM的某个指定地址放置读写指针。以PCI向MCU下行数据为例说明:循环队列缓冲区从0~0x0FFF,0x3FE0放置MCU读指针,0x3FE4放置PCI写指针,指针占用4个字节。PCI写指针缩写pWR, MCU读指针缩写pRD.

如图2所示,当pRD、pWR递增到0x1000时自动复位到0x0000(队列缓冲区不包含0x1000),pRD和pWR在队列的位置分图2所示的两种情况。循环队列的深度是0x1000个字节,当写入的字节总数 - 读出的字节总数 > =0x1000时,则发生队列溢出,造成数据丢失。

在情况1时,pWR >= pRD,pRD~pWR之间灰色RAM区域,是等待被读出的有效数据。读取数据的字节数 = pWR-pRD;

在情况2时,pWR < pRD,pRD~0x1000, 0~pWR之间灰色RAM区域,是等待被读出的有效数据。读取数据的字节数 = pWR+0x1000-pRD。

  
                         图2 循环队列读写过程分析

如果将循环队列的头尾假想的连续起来,可以把队列想象成无限长度,那么永远满足:pRD < pWR,换句话说,读操作发生在小于pWR的区域,写操作发生在大于等于pWR的区域,所以我们可以不用担心读写数据区域的重叠(?),只需关心pWR指针本身读写的完备性。所以,PCI写数据和MCU读数据的过程是这样的:

PCI写数据过程:
     (1) 从RAM中读pWR;
     (2) 向 >= pWR区域写入数据,此时pWR保持不变,维护1个局部的字节计数WRCnt;
     (3) 获取锁/Sepahore;
     (4) 在RAM中更新pWR, pWR = pWR+WRCnt;
     (5) 释放锁/Sepahore;
     (6) 向MCU发中断。

MCU读数据过程:
     (1) 获取锁/Sepahore;
     (2) 从RAM读出pWR;
     (3) 释放锁/Sepahore;
     (4) 从RAM读出pRD,计算应该读出数据的字节数;
     (5) 循环读数据,此时pRD保持不变,维护1个局部的字节计数RDCnt;
     (6) 在RAM中更新pRD, pRD = pRD+RDCnt (或 pRD = pWR)。

细心的你可能会发现,我在上文中标注了1个红色的问号。这里存在了一种假设,FIFO深度足够大,读写数据的速度足够匹配。只有在满足该假设的情况下,我们才不用担心数据区域的重叠。当此假设不满足时,很可能存在这样一种情况,pWR马上就要追上pRD(追上!!不是pRD<pWR么?不过这里说的没错,如果你不赞同,尝试理解一下我的意思),此时读写数据就会重叠。这种情况下,怎么办?这需要进行写操作是清楚的知道pRD在哪里,如果必要的话,增加pRD(快点快点,我都快追上你了,踢你一脚,别挡我的路!哈哈)。在写入数据前,判断pWR==pRD?满足该条件时,递增pRD。这样会降低效率!!!

很幸运,我们的系统满足这个假设,所以就不用进行这种复杂的操作啦。很多时候,一个系统设计的并不完美,存在逻辑上的漏洞,可是如果简单简洁本身就是一种美妙的事情,那么我们用这个漏洞去换取这份美妙,也很棒。

3. PCI本地总线写RAM失败现象

像你说话的时候,牙齿竟然咬到自己的舌头。ou, my god!不过这确实发生了。

引用PCI local bus spec. “This bridge provides a low latency path through which the processor may directly access PCI devices mapped anywhere in the memory or I/O address spaces”.是的,我们的双端口RAM映射在CPU的memory空间,理论上说,CPU读写双端口RAM与CPU读写内存之间不存在任何区别,我以前从来没有怀疑过CPU竟然会读写内存失败,所以这个问题一直隐藏的很深,让我很费解很费解,为什么竟然丢数据丢的这么莫名其妙!

让我们分析一下,双端口RAM的读写时序,如图3所示。

写数据:EN#是RAM使能信号,此后RAM在不需要任何驱动的情况下,其内部逻辑自动定位到ADDR指示的memory uint;WR#是写RAM选通信号,触发RAM内部电路对DATA进行锁存,并将数据写入ADDR指示的memory unit。在WR#选通时,需要保证DATA和ADDR的信号时稳定的,也就是需要在ADDR/DATA有效与WR#选通之间有latency,可能是1或几个clk.

读数据:与写数据逻辑相反,DATA是输出,EN#选通后,RAM将数据放置到DATA线上,确保DATA和ADDR是稳定时,选通RD#。


                       图3 双端口RAM的写时序和读时序

我们在看看9030本地总线的读写时序,为了准确,直接截图9030 datasheet.


                      图4 9030本地写时序


                       图5 9030本地读时序

已经很清楚了, 注意Write Strobe Delay和Read Strobe Delay,这两个值是9030的EEPROM设置的。


                   图6 9030 EEPROM space descriptor典型值

参考9030 data sheet的典型值,设置EEPROM,复位9030芯片。从此之后,再也没有碰到CPU读写双端口RAM失败。难道牙齿再也不会咬到舌头了么?我不是很确定,但很久没有咬到了。

4. 谨慎对待计数器的位宽

问题:函数RecvFunc从缓冲区读取数据,返回读取数据的个数。函数内部设置1个计数器,返回此计数器的值。很不小心,这个内部计数器被设计为8位宽,而我读取数据个数有可能超过0xFF,导致经常发现数据丢失?

这个问题很简单,也很隐藏。当一个系统存在多个原因导致数据通信丢失时,这个不起眼的问题最初会掩盖掉重要的问题所在,致使问题复杂化。单独列出来,是为了警告自己,不要犯类似的错误。

5. 谨慎对待数组下标

问题:从报文中获取某个字段,并将该字段作为数组下标使用。起初,我考虑到数据通信失败时,数组下标越界的可能性,但是想到经过报文校验,应该不会发生吧。不过确实发生了,导致非法的内存访问,程序崩溃退出。

问题虽然很简单,很幼稚,缺经常发生。

6. 使用try-catch块避免程序自动崩溃结束

在遇到问题5这样的情况,程序会直接被OS结束掉。这给会造成极其恶劣的用户体验,一定要避免。设计的程序要具备一项性能,不关用户怎么操作,可以向用户输出失败,但决不允许莫名其妙的跑飞,崩溃。

可是貌似不管你怎么细心的设计,程序还是可能在不起眼的地方出现意外。C++中有一项重要的机制,try-catch块,windows也实现了结构化异常机制。请恰当的使用这些机制!

7. 接收数据缓冲区/FIFO留足够空间

数据通信丢失报文的一个重要原因,可能是FIFO深度不够。测试FIFO深度够不够,可以设置一个FIFO深度使用的最大计数。

8. windows系统偶发性的时延,数据挤压而在某个小的时间段喷涌数据

数据通信丢失报文的另一个重要原因,可能是FIFO深度已经很深的情况下,数据突发性的向总线喷涌,而总线带宽有限,这时就需要更大的FIFO深度。我们的测试程序运行在xp环境下,VC编写。windows应用程序基于消息框架,每个进程和线程都有自己的消息队列。我们的测试程序使用多媒体timer,每隔1ms产生一次TimerHandler调用。这一过程可以这么理解,windows每隔1ms向TimerHandler消息队列发1个timer消息,如果消息没有及时处理(windows很复杂,干着很多让你摸不着头脑的事情,所以不要奢望每个消息发出后,繁忙的windows都会把宝贵的CPU及时交给TimerHandler),那么消息队列就会变得很长。例如某个100ms时间段,TimerHandler一直没有得到调用,其消息队列堆积了100个timer消息,然后CPU使用权终于交给TimerHandler了,TimerHandler迫不及待的的把堆积的100个消息,恨不得在1ms都给处理了。所以数据从windows向洪水泄闸一样涌向RAM,又从RAM涌向MCU,MCU不停的往内部FIFO灌报文数据,而CAN总线最高只有1Mbps的数据带宽,即使CAN控制器以最快的速度发,MCU内部FIFO也会很可能堆满数据,造成FIFO溢出,报文丢失啦,就像长江洪水一样。

这只是我们的假想,能不能实际看看呢?通过逻辑分析仪采集中断信号,验证了我们的结果。

PCI-CAN卡驱动与数据通信调试小记的更多相关文章

  1. dialogic d300语音卡驱动重装后启动报错问题解决方法

    dialogic d300 驱动重装后 dlstart  报错解决 问题描述:dlstart  后如下报错 [root@BJAPQ091 data]#dlstop Stopping Dialogic ...

  2. S3C2416裸机开发系列十六_sd卡驱动实现

    S3C2416裸机开发系列十六 sd卡驱动实现 象棋小子    1048272975 SD卡(Secure Digital Memory Card)具有体积小.容量大.传输数据快.可插拔.安全性好等长 ...

  3. tiny4412 --Uboot移植(6) SD卡驱动,启动内核

    开发环境:win10 64位 + VMware12 + Ubuntu14.04 32位 工具链:linaro提供的gcc-linaro-6.1.1-2016.08-x86_64_arm-linux-g ...

  4. vmware esxi 查看网卡、Raid卡驱动

    vmware esxi 查看网卡.Raid卡驱动 http://blog.51cto.com/adamcrab/1942763 查看网卡 [root@localhost:~] esxcfg-nics  ...

  5. 全网最新Kali Linux系统如何安装N卡驱动

    转载请注明来源:全网最新Kali Linux系统如何安装N卡驱动[亲测-暗影精灵3-1050TI有效] - 大家好,我系渣渣辉 https://www.zzhsec.com/255.html 1.更换 ...

  6. sd 卡驱动--基于高通平台

    点击打开链接 内容来自以下博客: http://blog.csdn.net/qianjin0703/article/details/5918041 Linux设备驱动子系统第二弹 - SD卡 (有介绍 ...

  7. SD卡驱动分析(二)

    三.下面分析一下高通的android2.3的代码中SD卡驱动的流程. 在kernel中,SD卡是作为平台设备加入到内核中去的,在/kernel/arch/arm/mach-msm/devices-ms ...

  8. SD卡驱动分析(一)

    Android下的SD卡驱动与标准LINUX下的SD卡驱动好像没有太大的区别,这里就以高通的ANDROID 2.3以代表,来简要分析一下LINUX下SD卡驱动的写法.由于小弟的技术有限,分析的有错的地 ...

  9. windows server 2008 集成raid卡驱动

    给服务器安装2008系统,一般都需要通过引导盘和操作系统盘来进行安装,安装过程比较繁琐时间也比较长,于是就想做一个集成了服务器驱动的2008系统盘,这样就可以直接用光盘安装,简单方便,第一步需要解决的 ...

随机推荐

  1. Dreamoon and Strings CodeForces - 477C (字符串dp)

    大意: 给定字符串$s$, $p$, 对于$0\le x\le |s|$, 求$s$删除$x$个字符后, $p$在$s$中的最大出现次数. 显然答案是先递增后递减的, 那么问题就转化求最大出现次数为$ ...

  2. Digester库

    在之前所学习关于启动简单的Tomcat部分实现的代码中,我们使用一个启动类Bootstrap类 来实例化连接器.servlet容器.wrapper实例.和其他组件,然后调用各个对象的set方法将他们关 ...

  3. c# 多线程使用队列顺序写日志的类 (需要再优化)

    using System; using System.Collections.Generic; using System.Threading; public class LogManager { // ...

  4. JS基础_while的练习2

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  5. BZOJ5017题解SNOI2017炸弹--玄学递推

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=5017 分析 老师讲课谈到了这道题,课上想出了个连边建图然后乱搞的操作,被老师钦定的递推方 ...

  6. 百度URL链接中文转码

    百度搜索链接规则为: http://www.baidu.com/s?wd=[搜索词目]&cl=3 有多个搜索词通过加号进行链接: http://www.baidu.com/s?wd=keywo ...

  7. 解决IDEA报错Could not autowire. There is more than one bean of 'xxx' type

    更新项目之后IDEA突然出现了这样的报错信息.显示Could not autowire. There is more than one bean of 'xxx' type.这个错误的意思是xxx类型 ...

  8. ado.net 断开 非断开

    非断开 SqlConnection SqlCommand / SqlDataReader 接 断开 SqlConnection SqlDataAdapter / DataSet 接

  9. 【转】vm ubuntu14.04 建立共享文件夹

    第一步:启动Ubuntu 14.04(其他版本的Ubuntu操作差不多)       1. 在VMware虚拟机界面,点VM->Install VMware Tools.之后,显示如下图:   ...

  10. Java SE 核心 I

    Java SE 核心 I 1.Object类 在 Java 继承体系中,java.lang.Object 类位于顶端(是所有对象的直接或间接父类).如果一个类没有写 extends 关键字声明其父类, ...