学习心得之调试篇

来源:互联网 发布:手机腾讯视频网络错误 编辑:程序博客网 时间:2024/06/01 09:57
此处主要讲基于KEIL的调试,其它的编译器可能也差不多,调试的时候掌握一些技巧,效率会更高。

调试错误可分为:编译错误和逻辑错误。

编译错误:

一般比较简单就那么几种:

1.undefined symble :某符号未定义,通常原因是,此符号在使用之前未进行定义/在一个.c文件中定义了,但是文件未编译/在编译的文件中定义了,同时被外部文件访问,但是此变量被你定义成了static类型/定义的符号与使用的符号有差异/定义并编译了,外部文件访问时未作申明(通常只会发出警告)

未完待续》》》》》》

逻辑错误:

这种错误一般不太好找,编译器不会报错,只有通过,跟踪相应变量或者寄存器的值,跟踪程序执行的路径,设置断点等措施,来判断出错的地方。

如运行时,无源无故进入到SVC_Handler/HardFault_Handler,这是些异常中断,即系统某处出错,如访问错误,栈溢出等,一量进入就会陷入无限的循环,这时你就要找出出错的位置,再相应进行处理,如何找出出错位置呢,这儿有个方法,因为这个中断里有个汇编语句B .,这才异致进入死循环,你只要将其改成,BX LR语句,再在此语句上设置个断点,程序一旦进入此中断,就会自动返回到出错的函数里去。

调试引发的思考:

1.编写代码

为了减少出错,尽量让程序的结构清晰,每当建立一个新文件时,要考虑两方面:1.这个文件中的函数和变量,哪一部分作为内部使用;2.这个文件中的函数和变量,哪一部分作为外部使用的接口;如果刚开始并不了解需求时,最好将所有的函数,和全局变量都定义成static类型,这样做是为了让整个文件,先与外界隔离,等到用的时候,再定义一个.h文件,引出需要用到的接口,解锁static。另外,为了方便系统的移植,将涉及到硬件和编译器的部分,设置为最底层,不同的硬件底层文件分别占用一个文件,此文件专用于底层与接口设备相关的功能,如果有需要用这些功能的地方,再另外编写一个文件,专门用于应用,这个应用文件不允许有对硬件接口的直接操作。这样做的好处是,当硬件结构改变时,我只要修改最底层的文件,应用文件丝毫不用做改变,这样才使得,上层文件真正脱离硬件而存在,比如freeRTos实时操作系统就是一个完全脱离硬件的文件。同样的,在进行开发时,也要弄明白哪些是底层文件(既与硬件相关的),哪些是应用文件(即完全脱离硬件而存在),不同的硬件,如果要实现同样的功能,那完全可以直接用已经提供的相同功能的应用文件,而只需修改底层文件,从而大大缩短开发时间。也就是说,为了追求效率,而且有时候会是高质量,尽量使用已有的代码,尽量减少自己编程,因此芯片厂商的编程水平通常要比我们这样的高,编程风格与系统也能保持一致。

2.理清逻辑

调试并不是一头扎进去瞎折腾,没有什么思路,应该先假设可能出现问题的地方,然后,逐个去验证。假设的情况必须有理可依,有足够强硬的理论依据才行,比如有一次SPI驱动的OLED就是显示不了,可以假设的情况:1.OLED没有收到数据(根源);2.OLED本身故障,即便收到正确的数据,却也不能显示(这点因为烧入别人的程序能正常显示,所以推翻了);3.OLED收到数据了,但是数据不正确,或者初始化OLED时,没有设置好,导致数据不能正确处理。现在没有其它有力证据时,不能断定是哪种情况,不过大致分为这两种,然后我们逐个分析,第一点,OLED没有收到数据原因可能是/*1.线路断了,数据没传过来(因为用别人的程序试过,所以可以推翻);2数据源有问题,没有发出数据,这个情况也分两种,一是数据源有硬件的故障,如IC坏了(这一点可以排除,因为有用别的程序试过);二是数据源设置不对,导致数据没发出来;三是,数据源发出数据了,但是OLED没有收到(OLED设置不对,或者数据源与OLED之间没有做好足够谐调)*/第三点:OLED收到了数据,但是数据本身不正确这也分为两种/*一是,收到的数据并非发送端想发送的数据;二是,收到的数据是发送端想发送的,但是,此数据本身不能让OLED正常显示*/从以上的分析来看,事件假设的情况,比较而言还算有理论依据的是:

1.数据源设置不对,导致数据没发出来;

2.数据源发出数据了,但是OLED没收到,由于发收间未设置好,如片选信号未拉低;

3.数据源发出数据,接收端也收到数据了,但是传输过程中有失真,导致接收不正确(发接的设置不对导致的);

4.数据源发出数据了,接收端也无失真的收到了,但是发送端本身的数据不对,不能驱动OLED;

5.数据源发出正确的数据,OLED也正确接收,但是OLED处理不当(设置不对如初始化设置,显示设置),导致无法正常显示。

可看出,数据有没有发送出去,这是第一个要验证的问题。可以用示波器去量测波形或者没有示波器的情况下,通过仿真跟踪SPI DR SR等寄存器的值来判定,因为数据发送的很快,很难跟踪到DR的值,但可看到SR值为第二位为1表明有数据发出了。如果,保证数据确实有往寄存器里写,但是寄存器的值都不对的话,那可能是时钟未设置。

如果已经验证数据确有发出来了,且片选也设置正确,接下来就来分析数据有没有失真,这一点暂时凭软件还不太好判断,因此可以先放下,作后面的验证,然后再用排除法分析,接下来验证发送端的数据本身对不对,从同事那儿得知,同样的数据被显示成功过,也就是说,发送端的数据没问题,问题是OLED有没有正确接收,正确处理,接下来,验证OLED本身的设置以及对数据的处理得当与否,将同事验证成功的初始化代码和数据处理函数都copy过来,但是还是不能显示。这样的话,基本就确定问题所在了,那就是传输过程中,数据有失真。从同事那儿得知,同样的程序在STM32F103的板子上正常跑起来过,同样的OLED同样的接线,那更加证明了,是数据传输过程中有失真了,线没问题,那只能是传输时的一些设置问题了,可是两个板子是同样的设置呀,那就只能从这两块板子的差异来分析了,经研究得,这两块板子的晶振频率有很大的差异,那这样看来,我们又找到入口点了,晶振频率会对同样程序的数据传输有什么影响呢:1.同样的程序的一条语句的执行时间,有些端口的时钟可能会有变化,那跟传输有关的时间设置,有什么呢,有端口的时钟设置,靠执行循环语句而实现的延迟程序。通过将端口的时钟设置成一样,还是不能显示,那基本确定是延迟时间的问题了。将本板上的延迟时间,通过计算实际时间与同事板上的设置成一致,终于可通了。

在处理IIC过程中也遇到了不少甚是苦恼的问题,如不知道什么原因老往硬件异常中断里跑,这个以经验分析总共有三种情况:1.项目的相关设置不对,如未勾选download to flash,使得程序并没有真正烧进flash中,而只作确认而已;2.使用数组或者指针错误,如数组越界,或者指针指向了非法区,因此要慎用指针,有时候编译器并不能帮你检查出关于指针的语法错误,如你将一个指向指针的值赋给一个指向数据的指针,而且由于指针操作异常而出错的概率相当大;3.栈空间过小,或者使用栈资源不合理,造成栈溢出。差不多处理玩硬件异常后,

 

接下来讲讲通过IIC通信来读写EEPROM出现异常并解决的过程,STM32固件库中提供了相应的读写函数,如果可以的话,尽量直接拿来用,而且,不要遗漏掉什么。刚开始调用了writeBffer的函数,但是不知道为什么,数据总是写不到EEPROM中去,于是乎,我开始进行分析,这个函数的结构有些复杂,需要理解了IIC的一些标志位,尤其是状态寄存器的各个标志位的值,很重要,它根据每一个阶段的处理情况作了相应的对策,如IIC在开始通信时产生一个开始条件,如果开始条件产生正常,则会产生一个中断(可选),并将状态寄存器中与之对应标志位置1,后续的程序再去判断此标志位的值,如果是1则继续下面的操作,否则直接bloked掉了,(进入死循环中),步骤2,开始条件正常后,就可以发送地址字节了,但是我的出现了一个问题,我的程序老跳入到一个死循环中,也就是说,相关标志位的值未被正常处理,正常逻辑,就要分析,这个标志位到底有什么含义,然后再分析,是什么异常引起的,弄清标志位的含义,如果程序没有正确说明的情况下,就要去看,芯片手册,STM32芯片手册中,都有对这些状态寄存器值的介绍,每个阶段的正常情况应该是一些什么值都有详细的解说,通过分析寄存器的值,得知是数据寄存器的值没有往外发,即我写的地址字节的数据没有往外发出去,另外没有得到收方返回的ACK,都会导致相关标志位的值不对,这时,第二点原因就能理解了,数据都没往外发当然收方不会有ACK返回,因此着重分析,为什么数据没有往外发呢,根据IIC的时序,当开始条件产生后,将SCL拉低后才允许数据往外发,那我的开始条件已经正常产生了,这一点没问题,问题是,是不是我的SCL没有拉低,或者拉低了,但拉低的时间不对,导致数据发不出去呢,可是,STM32并不能让用户去直接操作SCL和SDA,以及相关的时序控制,而是直接对一些寄存器进行赋值即可,在读写数据时,在传输阶段能够进行操作的寄存器只有DR数据寄存器,也就是说,我只要把数据放进去,相关的寄存器就能正常处理,那数据寄存器按照什么样的规定往外发数据呢,而且要符合各种不同用户的个性化需要,别忘了,我们在刚开始时,还对IIC各项功能进行了初始化呢,这才是数据发送的依据呀,那问题就显而易见了,肯定是我的初始化不正确,导致我的数据寄存器按照不正确的模式进行处理,从而出现异常。经过与范例对比,发现问题出现在,两个地方1.I/O口设制成AF模式时,Otype输出模式要设置成Open Drain类型(漏极开路),这样就实现了IIC多个IC pin接在一起形成一个逻辑与。由此可知,一定要了解通信时序,要了解函数如何实现通信,再就是调试比较重要的,特别是调试信号时,一定要会跟踪寄存器的值,特别是数据寄存器和状态寄存器,通信的情况都会智能地在寄存器上有显示,弄清楚正常的通信时,那些标志位会是什么样子,与你不正常的进行对比,有差异的地方再去STM32的芯片手册了解各标志位的含义及差异的原因,这样为你解bug提供了方向。

还有一次实例,我的功能是让它实现,每按一下键,进入中断程序中,从而实现相关的处理,但是不知道为什么,自从第一次按键跳入中断后,之后一直往中断里跳,根据触发中断的条件,是设置成上升沿触发,也就是说,按键按下并起来以后,才会跳入中断,我设想难道,按键有问题,一直处于上升沿,(其实这种情况不可能,因为如果按键本身有问题的话,应该在程序还没收到按键时就一直在中断里跳)二,就是中断的设置不对,通过对比完全正确,再说了,如果设置不对,那根本就进不了中断程序的,三我没考虑到的,就是你在第一次按键完成后,进入了中断子程序,做了一些不正确的处理,以致之后的每次都往中断里跳。再想想,用软件应该可以控制是否发生中断,那我在中断子程序第一次触发后,把中断关掉不就行了吗,可是关掉后,又担心,我按键真的来了,又不能触发中断。也就是说,需要一个功能实现,一次事件只能触发一次中断,那它这样不断的中断,是不是把这个事件放入某个等待处理的队列里了,但是处理完后,又没有把事件清掉,以致这个事件一直存在,每次看到,都要去执行,因此我们在处理完中断后,就要将已经处理完的事件在等待处理事件中进行清除。

上述事例中大部分出现在初始化阶段,因为STM32功能很强大,但是它功能的实现对于用户并不透明,大部分都是通过操作寄存器来实现的,因此这些寄存器的操作如果有误对整个功能的影响可以说是致命的,而且要去发现错误也比较麻烦,一般都只能根据状态寄存器的一些位来判别可能出现的原理。而且往往找出这样的错误会花费大量的时间,建议,对于一个新的平台,初始化部分的工作马虎不得,如果可能照搬已有的案例,因为有时候即使一条语句出现的位置不一样,都可能醇成大错。当然有时候如果时间充裕的话,允许自己写写,出出错然后再纠错,对于积累经验是必不可少的一部分,最近在进行TCP/IP移植的过程中,遇到了不少的问题,大部分出现在初始化阶段。

1.照搬人家的中断,却出现程序持续地往中断里跳。

(1)根据以往的经验,首先检查是否中断pending位在跳入中断后未进行清理,通过看代码把这一因素排除;

(2)接下来就分析是否一直有中断源在触发,而实现上我并没有往板子发送数据,可是奇怪的是,它似乎每次插上网线后就开始循环往中断里跳入,我初步判断,可能是插上网线后,PHY自主地往主IC发送了一些连通类的信息,有此猜想确没有去看芯片手册去验证,导致我在很长一段时间内都认为是这个原因导致跳入的中断的.耽误了不少时间,这点要吸取教训,千万不要理所当然,任何现象都是有理可依的。

(3)因为自认为上述两点均被排除,所以猜想是不是它的程序本身有问题,尤其是清除pending标志位的程序,于是,去细看了一下,此程序,发现与之前接触的清除程序不太一样,像之前的,在清除时,往往是将该寄存器的该标志位清0,而此处确在置1,由此我又断定,是因为人家程序写错了导致了,于是便理所当然的将人家的程序给改了,改后发现不再往中断中跳了。但是,当我真的发数据过去的时候,也跳不进中断,于是我就更加困惑了。前前后后无头无脑的折腾了不少时间,终于通过观察接收buffer里其实已经有值,才知道,原来一直有数据发送过来,但是不知道什么原因,触发不了中断,这样一想,当初为什么一直触发中断,就是因为有值进来,那我的修改岂不反倒错上加错了。后来改后,发现一直有数据传进来,中断也一直在触发,于是想,这些数据,我并没有发送,是从哪儿来的呢,于是,我打开我的抓包工具,发现在这一条点对点的网线上,居然有很多的其它的协议的数据,而且有些目标地址为全1的数据还被传到了我的buffer里头。我自己发的数据也被载入其中。

几番周折,最起码知道自己的数据被完整的载入到了目标机的buffer中,在此过程中,吸取了不少的教训:

1.千万不要理所当然的想问题,猜想可以,但必须要去验证;

2.不要随便怀疑别人的东西,先从自己身上找问题,直到你收集到的所有的证据足够充分说明自己没问题时,再去怀疑别人;

3.证据要足够充分,如我一直触发中断,如果中断没问题,自己也没有去触发中断源,但并不表示,别人也没在触发中断源呀;

接下来解决的问题是,我的数据明明有载入buffer,也触发了中断,但是为什么我这边确没有作出回应呢,

1.数据存在buffer中,也许没有被其所在的协议取到;为了验证,我在协议里设置了断点,果然没有跳进去,于是仔细去分析程序,发现,其所在的协议未进行初始化。

改后,程序似乎执行过来了,但是却跳入了异常中断去了,按照以往的经验在,并异常中断改成BX LR;找到了它返回的函数,接下来就要验证,到达这个函数的路径,于是,我在调用它的每个函数里加上了打印语句,再在调用它的函数的上层函,上上层函数,上上上层函数都加上了打印语句,最终找出了一条唯一的路径,找到了触发这条路径的罪魁祸首,某处的指针使用错误,由于我在刚开始移植时,根据需要改了这部分,而且指针使用确实有误,将一个二级指针作为实参传给了一个一级指针的形参,函数居然也未报错。改过后,通路正常,收到我发的ARP协议的数据,在抓包工具中,也发现目标机有回复,但是,当PC根据获取的MAC地址发送ICMP 的ping请求时,我这边确始终接收不到这包数据。

接下来问题便是:我这边接收不到ICMP包,连中断也未跳入,按照MAC层原理,只有目标地址正确,长度适宜,FSC值匹配的情况下,才会将数据转入FIFO进而触发中断,现在连中断都未能触发,一定是上述条件中有一项不能满足:

1.MAC地址不正确。MAC 地址目标机通过ARP协议传送给PC的,传的是,我在初始化时,随机写的地址,通过别人的案例发现这些地址是可以自己定义的,问题是,当MAC层要做过滤处理时,是与哪个值进行比较呢,因为MAC过滤机制是不透明处理的,这样的话,这个地址,一定是通过写入某个寄存器中去,然后目标机会根据这个值去作比对,会不会我写的值,和收到的不匹配,通过比对一致,那就是考虑,是不是这个值并没有被写进去呢,于是我调用了一个getMACaddr的函数,验证了我的观点 ,确实没写进去,因为写的明明是02读出来的却是FF,为什么会这样呢,分析上下文得知,写入这个地址是在MAC自身作softwareReset之前,因为softwareReset的作用是将所有的寄存器的值恢复成默认值,于是导致,我起初写的值等于白写了,于是我把将MAC地址的值写入寄存器的语句写在了softwareReset之后,问题解决了。抓包工具中看到我接收到了ICMP协议,并且给出了回应。

上述这两个允分验证了,不要随便改初始化程序,否则,出错都不好找原因,如果实在找不出来原因时,可以通过查找差异来找原因,即我的程序相对与标准版到底有什么差异。这个方法很有效,在实在找不出原因时,可以使用。

但是我的PC仍然显示,请求超时,我通过观察,发送buffer数据正确,通过观察抓包工具,发现目标机回复的数据,有个IP 首部的checksum值是错误的是全0,还提示可能使用了checksum off load功能,于是我去程序中找寻写入checksum值的地方,并于标准案例进行比对,发现完全一致,但是都在使用一个宏控制checksum值的写入情况,我猜想,会不会是两个平台,这个宏的定义情况不一致呢,于是我去找此宏定义之处,又发现完全一致,于是又猜想,定义了宏如果没有定义的文件没有被使用的文件包进来,也相当于没定义呀,会影响编绎,果然发现,有些地方使用了这个宏,却没有把定义的文件包含进来。改后,发现数据包不再报错,但是目标板明明有发出正确的数据来,PC这边仍提示请求超时。

接下来要解决的问题是:

明明有发出来数据,数据的MAC地址,长度,FSC均合适,可以通过MAC层的过滤,为什么还是提示请求超时呢,会不会是真的发出来了,但是发的太晚了,我的时间已经到了,可是你仍然没有回复我,直到我回应超时后,你才回复时,我已经不接收你了呢,如果是我的反应太慢,那是什么原因造成的呢,于是我看了下我的代码,发现所有的协议输入输出语句间都参杂有当初为了调试而加入的打印语句,于是,我把它们全mark掉,终于,我可以ping通了。

很可喜,但也收获了不少,原来打印语句是不能随便乱加的,会影响系统的时效,特别是像TCP/IP这种协议,其中有很多地方都设置了TTL即寿命,如果我发出的数据长期得不到应答,我就不再接收此数据相应的回应数据。教训是:以后调试语句用完后,要记得屏蔽掉。

接下来的日子,主要在TCP/IP协议栈启用一个web serve,即通过在PC端输入网址,就能将目标板网页的内容读过来,正常显示,这样就要求,目标板,能够有足够大容量的非易失性存储器件,用于存储网页文件信息,我们选用的是ATMEL的DataFlash,一个网页,往往存在多个不同类型的数据文件,为了便于管理这些文件,我们又决定将Fatfs移植过来,因此先要做的是,将flash驱动好,由于传输的数据量大,为了减轻CPU的负担,我们采用的SPI DMA模式传输数据,即不需要CPU的干涉,就能完成从外设到内在,或者内存到内存,内在到外设的数据传输,这个在DMA初始化时根据需求配置好。其中遇到了不少问题,首先为了验证简单的通信,我未启用DMA,还是像以往一样,刚接触新的外设,先获取其ID信息(因为在任何情况下,只要通信正常,读到的ID值都是确定,而且根据DataSheet其值也已知了,可以作为一个很好的依据),以验证通信正常,可是,我的值为全0,通过观看相应寄存器的值,数据确实是有发送出去即发空标志位已置1,也接收到了数据即接收非空寄存器置1,

我分析原因可能是:

1.接收设备没能收到数据,如果没有接收到的话为什么会发送方会接收非空呢(我刚开始将这一点排除了,因为我误码认为,接收数据寄存器非空是因为接收方有回应一个数据出来,这是对概念理解的错误,因为SPI的数据寄存器是一个双向寄存器,数据发出的同时就会有接收,无论对方有没有发出,这就是为什么,SPI虽然数据传输快,但是没有应答机制,从而增加了排错困难。

2.接收到了数据,但是数据错误,给出的答复为0,经验证数据正确;

3.接收方有接收到正确的数据,但是设备处于忙状态,或者故障,所心发出答复为0,根据数据手册,当设备处于忙状态时,会回应忙的信息,而不会是全0;

既然第一点是我的误排除,那我再推导一次,即接收端没有收到数据,如果没有收到数据的话,是不是时序不正确,导致数据失真,而不能完整接收,但是用户除了操作寄存器,和CS电平,已没有其它方法来控制时序了,除非初始化有错误,但是经验证,CS电平,初始化均正确,我能考虑到的均已排除。现在出示杀手锏,如同TCP/IP协议栈一样,看看是不是时间问题,即我发送出去,在我获取数据寄存器值的时候,接收设备还没来得及给我回数据呢,而我收到的只不过是数据线的电平值而已,均为0,因此我要再读一次数据,可是数据仍然为0,这是为什么呢,双向寄存器,有一个特点,就是只有在发的时候才能收,因此我再发出一个数据出去,这时接收到了正确值,这一次的推导麻烦,逻辑也不是很清晰,原因不在于,推理的思路,而是对概念的理解,这是最麻烦的,因此推导的根本是概念必须了解透彻(旁门左道的方法就是直接用别人的作法,然后通信成功后,再去分析原因)。

这个问题解决以后,我再使用DMA的方式,DMA刚使用时,又跳入了异常中断,根据上次的经验,加BX LR,然后顺藤摸瓜,找出了根源又是指针操作异常,接下来,向Flash写数据时,一页一页的写,当写到200页时,数据写不下去了,而在等待写结束的中断的持续循环,当时,先确认数据到底有没有写进去,于是,将写进去的数据,用平常的方法读了一遍,发现与写进去的一样(由于没有仔细比对,也误以为写进去了,之后一直在想为什么数据为什么只能写到200页,且在200附近,并不固定),后来,再用SPI的方法读了数据,发现数据根本就没写进去,再用DMA读,也是读不出正确的数据出来,看来,DMA是有问题的,而且一定是初始化的问题。于是去比对了别人写的SPI DMA的初始化,由于没有仔细去比对,导致,又误认为自己的正确,于是,还是找不出原因,后来,只能再去比对,终于发现,少了一个DMA request语句,于是,加上后,发现数据还是写不进去,于是思考是不是读的函数和写的函数有问题,因为特别是读的,当读数据时,必须有发数据,才能读到数据,于是我加了一个发的DMA CMD,再单纯的读,发现我的读函数通了,同样,我在写的函数里也加了读的dma cmd,读到了数据,但是,数据与写进去的有出入,数据有所减少,

我分析原因如下:

1.写的时候就写少了,

2.写的时候正常,读的时候读少了。

第一个原因,如果是的话,就是因为我还没有写完,就停止了,为什么我没有写完就停止了呢,

1.CS 被拉高了,2.写的时候将DMA关掉了

因为我只有在数据传送完成的时候才会关掉DMA所以排除第二种。

第二个原因,如果是的话,就是因为我没有读完就停止了,

1.CS被拉高了,2.没有数据发出来,导致不能读,3.DMA被关掉了

其中的3同样地被排除了,其中的2如果发生的话,原因是CS被拉高了,DMA被关掉了(排除了)。

因此所有的矛头都指向了CS被无意中拉高了,通过检查发现,在将数据写入BUFFER后还要将BUFFER的数据写入FLASH,但我在后个动作之前使用了读状态寄存器的指令,其实并不需要,因为BUFFER写入FLASH之前写BUFFER时,就已经确定FLASH不忙了,所以在这儿再判断一次纯属多余,另外还会有,DMA的数据未传输完就将CS接高的危险,因此,去除这条语句后,数据通信正常。

在DMA使用过程中,还遇到了一个问题,让我苦恼了许久。

使用文件系统时,为了验证文件系统能否工作,我先创建一个文件,并往文件中写入一堆字符,再从文件中将这些字符读出来,看是否与我写入的一致。

遇到的问题:

1.打开文件时,返回无文件系统。

我先追踪什么情况下会返回这样一个值,发现,在读第0个扇区时会如果里面的值与应该写入的引导扇区的内容不一致则返回无文件系统,考虑到,零扇区的内容是在格式化的时候写入的,于是,加了格式化的操作,顺利通过。

2.文件始终无法读出值。

于是我追踪什么情况下会读不出值,当读写指针-文件大小=0时,不会去读文件里的值,结果发现这两个值均为写入的数据大小的值,按理说,读指针在刚开始读的时候,应该指向零的,于是,我关闭了文件,再打开文件去读的时候,顺利通过。

3.文件始终无法读出值。

这次读写指针为0了,但是文件大小却也为0,按理说,文件的大小应该在关闭文件时存在了目录项里,再打开的时候,会去目录项对应的存储空间中去读取,为什么会是零呢,可能有三种情况:

1).文件大小本身为零,数据未写进去。通过检查,排除。

2).文件大小不为零,但是未被保存到目录项中,通过检查发现,数据在程序操作的时候,有写入到相应的buffer中,但是,buffer显示的却总是零,为什么不能对此buffer赋值呢,其它的buffer都能赋值,为什么唯独此个不行呢,打印信息也不可以。

当时没有立即找出来,而是若恼了很久,还是找差异

这个buffer与其它的buffer有什么差异呢

1.地址,在内存中的地址不一样;是不是用了什么禁用的地址,可是通过查找STM32的内在发现,此地址是正常的SRAM1的空间;

2.buffer,空间的大小,空间大小不一样,但是,其它的buffer改成同样的空间,也是可以正常动作的。

3.被用来放的数据不一样,操作不一样;终于发现了,此BUFFER被用作DMA数据传送的地址,那为什么,不能在此地址上赋值呢,唯一的原因就是DMA对此BUFFER进行操作,现在CPU又要去操作,才导致两相矛盾的。

终于解决了问题。

其实这种问题,解不出来的时候,就要会用杀手锏,即延迟事毕能找出原因的。

 

 

通过这些案例,我还有发现,以往的一些经验只是作为依据,千万不可确信无疑,否则经验反倒让我们走了弯路,因此下次在分析问题时,可以在纸上分析,用图笔的形式可以使得思路更清晰,把经验作为其中的一个依据,与其它的依据是平等的。

 

 

上面的例子充分证明了,调试要有理可依,才可以提高效率,有些人可能会讲我没办法像你那么轻松就能获取证据,如:你说你同事也用了同样的程序验证通过,而我没有这样的例子。就无理可依,毫无头绪,这里有两种方法:1.自己将同样的程序换在不同的平台试试,或者,将不同平台的程序换在你的平台试试,去网上找,