• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#程序导致MFDeploy无法ping通的问题

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

//=====================================================================
//TITLE:
// C#程序导致MFDeploy无法ping通的问题
//AUTHOR:
// norains
//DATE:
// Thursday 05-May-2011
//Environment:
// .Net Micro Framework Porting V4.1
// Keil MDK 4.02
// STM32F103ZE Redcow Board
// Visual Studio 2010
//=====================================================================

为了测试编写USB驱动是否正常,编写了一个C#程序,其完整的代码如下:
namespace HelloWorld { public class Program { public static void Main() { Debug.Print("Hello, World!/n"); Debug.Print("It's the first C# application in .Net Micro Framework!/n"); } } }

这段代码看起来没什么问题,用VS2010也能正常下载,并且从串口中也能看到打印出来的字符串。但奇怪的事情就此发生了,自从成功下载该程序,并且正常运行之后,我的板子插到电脑上虽然可以枚举,但通过MFDeploy的Ping指令,却经常无法正常通讯。如果用Bus Hound软件来查看,发现PC端发送一个OUT包之后就卡死了,如图所示:


如果将NAND上的C#程序给擦除,那么一切又恢复了正常。这究竟是怎么一回事呢?

通过MDK的断点调试,会发现代码一直在执行Systick中断函数,而查看Systick的寄存器,发现reload的数值非常小,如图所示:


造成频繁执行Systick中断函数的原因是否源于此呢?通过MDK来强制更改NVIC_ST_RELOAD寄存器的数值为0x00FFFFFF来试试。结果不出所料,Bus Bound监控到USB数据又开始交互,MFDeploy也不再假死,而检测到板子为TinyCLR了!

转回来想想,为什么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()函数。我们进到该函数看看。因为该函数比较大,有一些东西并不需要我们了解,所以下面罗列的是缩减后的主要部分,如下所示:
void HAL_COMPLETION::DequeueAndExec() { ... HAL_COMPLETION* ptr = (HAL_COMPLETION*)g_HAL_Completion_List.FirstNode(); HAL_COMPLETION* ptrNext = (HAL_COMPLETION*)ptr->Next(); if(ptrNext) { ... HAL_Time_SetCompare( ptrNext->Next() ? ptrNext->EventTimeTicks : HAL_Completion_IdleValue ); ... } }

从中可以看到,该函数其实会调用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函数中执行一个死循环,如下所示:
namespace HelloWorld { public class Program { public static void Main() { Debug.Print("Hello, World!/n"); Debug.Print("It's the first C# application in .Net Micro Framework!/n"); while(true); } } }

结果没错,这样更改之后,的确一切都正常了。似乎一切都很美好,不是么?但是,这样的健壮性是不够的,因为我们无法确保做应用的程序员能够遵守这个约定。而万一他们忘记的话,那么结果将是灾难性的:因为USB已经无法PING通,更谈不上下载程序,所以做应用的程序员无法凭一己之力来挽回自己的错误。除了你,.NMF的移植者,通过MDK来设置Systick的Reload寄存器,让CPU跑到正常的轨道上,除此以外,再无它法。
  
为了避免这情况,我们可以将中断函数修正一下。我们自己来检测ptrNext,如果它为NULL的话,那么我们手动调用HAL_Time_SetCompare函数即可,如下所示:

  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无法响应的问题不再存在,我们也不用再担心应用程序员忘记最后写死循环啦!


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#学习总结(1)---认识C#发布时间:2022-07-14
下一篇:
C#发布时间:2022-07-14
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap