在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
//===================================================================== 为了测试编写USB驱动是否正常,编写了一个C#程序,其完整的代码如下: 这段代码看起来没什么问题,用VS2010也能正常下载,并且从串口中也能看到打印出来的字符串。但奇怪的事情就此发生了,自从成功下载该程序,并且正常运行之后,我的板子插到电脑上虽然可以枚举,但通过MFDeploy的Ping指令,却经常无法正常通讯。如果用Bus Hound软件来查看,发现PC端发送一个OUT包之后就卡死了,如图所示:
通过MDK的断点调试,会发现代码一直在执行Systick中断函数,而查看Systick的寄存器,发现reload的数值非常小,如图所示:
转回来想想,为什么Systick的Reload的数值太小,导致MFDeploy假死呢?原因很简单,Reload是每次systick中断的间隔,而这个时间太小,很可能在刚执行完systick中断函数的时候,下一次中断又发生了,从而导致主线程的代码没有获得CPU时间执行! 原因找到了,那么我们该如何解决这个问题呢?首先我们来看看Systick的中断函数。一般来说,根据.NMF的示例代码,我们的中断函数无非如此: void ISR(void *pParam) { g_TicksLastRead = CounterValue(); if(g_TicksLastRead >= g_TicksCompare) { // this also schedules the next one, if there is one HAL_COMPLETION::DequeueAndExec(); } else { // // Because we are limited in the resolution of timer, // resetting the compare will properly configure the next interrupt. // // You mustn't comment the source code, or it would not boot normally. SetCompareValue( g_TicksCompare ); } } 这个中断函数逻辑很简单,如果当前的ticks大于要比较的ticks值,就执行HAL_COMPLETION::DequeueAndExec()函数。我们进到该函数看看。因为该函数比较大,有一些东西并不需要我们了解,所以下面罗列的是缩减后的主要部分,如下所示: 从中可以看到,该函数其实会调用HAL_Time_SetCompare来设置下一次的中断时间。但如果我们使用MDK单步调试,却会惊讶发现,该函数一直没有被调用,因为ptrNext一直为NULL!如果再仔细点,在还没执行C#程序之前,ptrNext不是为NULL的! 根据经验推测,问题就出在于C#程序。具体点,问题在于C#的Main函数返回了!因为C#的Main函数返回,导致Node的结点被释放,于是ptrNext就为NULL。如果此时又刚好Systick的间隔很小,代码又因为ptrNext为NULL而不执行HAL_Time_SetCompare函数,那么最终结果就是:不停地发生Systick中断,而无瑕执行主线程代码! 原因找到之后,可能大家想到的最简单的解决方式就是在C#的Main函数中执行一个死循环,如下所示: 结果没错,这样更改之后,的确一切都正常了。似乎一切都很美好,不是么?但是,这样的健壮性是不够的,因为我们无法确保做应用的程序员能够遵守这个约定。而万一他们忘记的话,那么结果将是灾难性的:因为USB已经无法PING通,更谈不上下载程序,所以做应用的程序员无法凭一己之力来挽回自己的错误。除了你,.NMF的移植者,通过MDK来设置Systick的Reload寄存器,让CPU跑到正常的轨道上,除此以外,再无它法。 void ISR(void *pParam) { g_TicksLastRead = CounterValue(); if(g_TicksLastRead >= g_TicksCompare) { // this also schedules the next one, if there is one HAL_COMPLETION::DequeueAndExec(); //If the ptrNext is NULL,it wouldn't call HAL_Time_SetCompare() in DequeueAndExec() function //which means the main thread may not get the time to run when the systick reload value is very small and //systick interrupt occurs frequently. So I reset the systick reload value here by calling HAL_Time_SetCompare function HAL_COMPLETION* ptr = (HAL_COMPLETION*)g_HAL_Completion_List.FirstNode(); HAL_COMPLETION* ptrNext = (HAL_COMPLETION*)ptr->Next(); if(ptrNext == NULL) { //0x0000FFFFFFFFFFFFull is used by the HAL_Completion_IdleValue variable which defined in the completions.cpp file. HAL_Time_SetCompare(0x0000FFFFFFFFFFFFull); } } else { // // Because we are limited in the resolution of timer, // resetting the compare will properly configure the next interrupt. // // You mustn't comment the source code, or it would not boot normally. SetCompareValue( g_TicksCompare ); } } 经过这么一更改,那么引发MFDeploy无法响应的问题不再存在,我们也不用再担心应用程序员忘记最后写死循环啦! |
请发表评论