【.Net Micro Framework PortingKit – 12】SysTick驱动开发

简介: SysTick驱动对TinyCLR来说非常重要,.Net Micro Framework系统的多线程和多任务(对托管代码来说是单任务多线程,但是还存在和托管代码同时运行的任务,如我们用MFDeploy程序Ping TinyCLR或擦写Flash 的时候,就是另外的任务在执行)就是靠它来实现的。

SysTick驱动对TinyCLR来说非常重要,.Net Micro Framework系统的多线程和多任务(对托管代码来说是单任务多线程,但是还存在和托管代码同时运行的任务,如我们用MFDeploy程序Ping TinyCLR或擦写Flash 的时候,就是另外的任务在执行)就是靠它来实现的。

SysTick驱动有三个功用,一是我们上面所说的多任务和多线程支持;二是获得系统当前Tick,以此实现延时等待,比如我们常见的Events_WaitForEvents函数就靠它来实现延时功能的;三是为Native代码提供两个版本的Sleep函数。

ARM7ARM9相比,Cortex-M3系列的CPU提供了SysTick这个feature,所以我们就不需要用Timer来模拟Tick的功能了,直接用系统提供的SysTick就可以了。Cortex-M3SysTick其定时器计数是递减的,递减到0就会触发中断(当然要使TICKINT使能),然后自动会加载LOAD寄存器的值,启动下一次计数循环。

LOAD寄存器可填入的最大值为0x00FFFFFF,对72M主频的CPU来说,大概会有250毫秒左右的延时。由于VAL寄存器的值是递减的,所以在移植相关代码的时候要特别注意,我们概念中的Tick的值应该是(LOAD-VAL)。

CortexM3.h文件中添加如下代码,以便于配置SysTick寄存器。

struct CortexM3_SysTick

{

    static const UINT32 c_Base = 0xE000E010;

    /****/ volatile UINT32 CTRL;  //0xE000E010

    static const    UINT32 CTRL_COUNTFLAG= ((UINT32)0x00010000); 

    static const    UINT32 CTRL_CLKSOURCE= ((UINT32)0x00000004);

    static const    UINT32 CTRL_TICKINT= ((UINT32)0x00000002);

    static const    UINT32 CTRL_ENABLE= ((UINT32)0x00000001);

    /****/ volatile UINT32 LOAD;  //0xE000E014

    static const    UINT32 LOAD_RELOAD= ((UINT32)0x00FFFFFF); 

    /****/ volatile UINT32 VAL;  //0xE000E018

    static const    UINT32 VAL_CURRENT= ((UINT32)0x00FFFFFF); 

    /****/ volatile UINT32 CALIB;  //0xE000E01C

    static const    UINT32 CALIB_NOREF= ((UINT32)0x80000000); 

    static const    UINT32 CALIB_SKEW= ((UINT32)0x40000000); 

    static const    UINT32 CALIB_TENMS= ((UINT32)0x00FFFFFF);     

};

然后在/DeviceCode/Targets/Native/CortexM3/DeviceCode/SysTick新建四个文件SysTick.hSysTick.cppSysTick_Functions.cppdotNetMF.proj

SysTick.h中创建SYSTICK_Driver结构体,SysTick.cpp存放该结构体的具体实现代码。

struct SYSTICK_Driver

{

    static const UINT32 c_MaxTimerValue = 0xFFFFFF; //16777215  最大 250ms左右的定时

 

    volatile UINT64 m_Tick;

    volatile UINT64 m_nextCompare;

   

    static BOOL Initialize  ();

    static BOOL Uninitialize();

    static UINT64 CounterValue();

    static void SetCompareValue( UINT64 CompareValue );

    static INT64 TicksToTime( UINT64 Ticks );

    static INT64 CurrentTime();

    static void Sleep_uSec( UINT32 uSec );

    static void Sleep_uSec_Loop( UINT32 uSec );

    static void ISR( void* Param );

};

Cortex-M3内核下的UINT64 CounterValue()函数的具体实现不同于.Net Micro Framework自带的各平台上的源码,所以有必要介绍一下它的实现:

UINT64 SYSTICK_Driver::CounterValue()

{

    GLOBAL_LOCK(irq);

    CortexM3_SysTick &SysTick= CortexM3::SysTick();

    UINT32 value = (SysTick.LOAD - SysTick.VAL);

  

    if(SysTick.CTRL & CortexM3_SysTick::CTRL_COUNTFLAG)

    {

        g_SYSTICK_Driver.m_Tick+=SysTick.LOAD;          

    }

    

    return  g_SYSTICK_Driver.m_Tick + value;

}

Value的值为(SysTick.LOAD - SysTick.VAL),这是和其它平台的驱动一个区别。此外m_Tick也是我新添加的,它是不断累加计数的,但是它不能真实反映系统开机以来的Tick数,因为SysTick有可能会被禁止中断,也就是说ISR函数不能保证整个系统运行期内都被正常触发。

ISR是一个重点,它是系统实现多任务和多线程的“动力源”。

void SYSTICK_Driver::ISR( void* Param )

{   

    if(CounterValue() >= g_SYSTICK_Driver.m_nextCompare)

    {

       HAL_COMPLETION::DequeueAndExec();

    }

    else

    {

        SetCompareValue( g_SYSTICK_Driver.m_nextCompare );

    }

}

HAL_COMPLETION::DequeueAndExec()代码是关键,每间隔一个指定的时间就会执行一次任务,常见间隔时间为20ms

Sleep_uSec函数是通过Tick计数计算延时间隔的。

void __section(SectionForFlashOperations) SYSTICK_Driver::Sleep_uSec( UINT32 uSec )

{

    GLOBAL_LOCK(irq);

    CortexM3_SysTick &SysTick= CortexM3::SysTick();  

    UINT32 maxDiff  = CPU_MicrosecondsToTicks( uSec );   //每微秒的滴答数

        SysTick.LOAD =   maxDiff & 0xFFFFFF;  

    while(!(SysTick.CTRL & CortexM3_SysTick::CTRL_COUNTFLAG));

}

__section(SectionForFlashOperations)标识该函数会被拷贝到RAM中去运行(保证执行时间)。

Sleep_uSec_Loop函数则是通过汇编代码的循环实现的,延时相对比较精确。

void __section(SectionForFlashOperations) SYSTICK_Driver::Sleep_uSec_Loop( UINT32 uSec )

{

    // iterations must be signed so that negative iterations will result in the minimum delay

    uSec *= (SYSTEM_CYCLE_CLOCK_HZ / CLOCK_COMMON_FACTOR);

    uSec /= (ONE_MHZ               / CLOCK_COMMON_FACTOR);

 

    // iterations is equal to the number of CPU instruction cycles in the required time minus

    // overhead cycles required to call this subroutine.

    int iterations = (int)uSec - 14;      // Subtract off call & calculation overhead

    CYCLE_DELAY_LOOP2(iterations);

}

CYCLE_DELAY_LOOP2的实现代码是汇编,我把它放在FirstEntry.s文件里了,具体代码如下:

IDelayLoop2

    EXPORT  IDelayLoop2

    subs    r0,r0, #2          ;; 1 cycle

    bgt     IDelayLoop2        ;; 1 cycle

      mov     pc, lr              ;; 5 cycles 

Sleep_uSec_Loop函数实现代码中的uSec – 14是从其它CPU代码中拷贝来的,针对Cortex-M3应该是多少,我还没有细算过,以后有时间再补上这一课。

NativeSample.proj中添加如下条目,就可以测试SysTick驱动了:

  <ItemGroup>

    <RequiredProjects Include="$(SPOCLIENT)/DeviceCode/Targets/Native/CortexM3/DeviceCode/SysTick/dotNetMF.proj" />

    <DriverLibs Include="SysTick.$(LIB_EXT)" />

  </ItemGroup>

NativeSample.cpp中我们只能通过Events_WaitForEvents( 0, 1000 )代码测试SysTick驱动的一部分功能,全部的功能要在TinyCLR项目去测试了。

小插曲:在实现LCD驱动的时候,初始化LCD寄存器需要延时,在采用CYCLE_DELAY_LOOP2时,debug版本和release版本有很大的区别,debug可正常运行,但是release会有问题,在Sleep_uSec_Loop函数开始添加GLOBAL_LOCK(irq)代码就可以了,但是这样一改在TinyCLR代码中能正常运行的debug版本就会出问题了。可见嵌入式开发的难点不在于代码的编写,而在于调试。

相关文章
|
存储 对象存储 UED
CDN适用哪些场景?
CDN是将源站内容分发至最接近用户的节点,使用户可就近取得所需内容,提高用户访问的响应速度和成功率。今天为大家分享几个CDN的典型适用场景。
16720 0
|
数据采集 存储 JavaScript
如何使用Puppeteer和Node.js爬取大学招生数据:入门指南
本文介绍了如何使用Puppeteer和Node.js爬取大学招生数据,并通过代理IP提升爬取的稳定性和效率。Puppeteer作为一个强大的Node.js库,能够模拟真实浏览器访问,支持JavaScript渲染,适合复杂的爬取任务。文章详细讲解了安装Puppeteer、配置代理IP、实现爬虫代码的步骤,并提供了代码示例。此外,还给出了注意事项和优化建议,帮助读者高效地抓取和分析招生数据。
485 0
如何使用Puppeteer和Node.js爬取大学招生数据:入门指南
|
人工智能 中间件
呼叫中心中间件-网关配置
支持给网关指定变量,设置网关的语音编码编码和主叫号码。网关配置编辑后,不能实时生效 ,需要执行sofia命令才可以生效,具体看SIP设置。 配置 cti_gateway@domain [哈希表] key 网关名字 value 网关配置 | ``` { "param": { "register": "true", "caller-id-in-from": "true", "realm": "180.76.224.191:35560", "from-user": "", "destination-prefi
|
安全 网络协议 应用服务中间件
Linux OS||不响应SYN总结
背景 对外提供TCP服务的进程,在压测时发现,TCP连接SYN响应慢,甚至不响应。导致无法正常接收新的请求,影响业务。 抓包分析:  如上有大量的重传,有时能够正常的响应请求,有时就无法响应请求。
6262 0
|
Unix Linux Apache
【Linux必知必会】五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)
作者:gnuhpc 出处:http://www.cnblogs.com/gnuhpc/ 最初来自:http://www.sinoprise.com/read.php?tid-662-page-e-fpage-1.html (遗憾的是这个链接已经打不开了),我基本未改动,只是进行了一些排版和整理。
2028 0
|
Web App开发 弹性计算 前端开发
超详细图文教程·阿里云免费学生ECS云服务器领取并使用全过程(部署Python多人聊天室程序,包含源码)
超级详细的教程,包括从领取到ECS云服务器,到最终部署成功Python多人聊天室程序的全部过程!花了一个晚上,在老师和同学的帮助下终于摸索出来了,想把我的整个过程都记录下来,也希望能为后来者提供一些便利,少踩点俺踩过的坑QwQ,最后由衷感谢我的老师和同学!
3170 2
超详细图文教程·阿里云免费学生ECS云服务器领取并使用全过程(部署Python多人聊天室程序,包含源码)
|
开发框架 网络协议 安全
r0capture安卓应用层通杀脚本-使用文档
r0capture安卓应用层通杀脚本-使用文档
r0capture安卓应用层通杀脚本-使用文档
|
存储 数据采集 算法
Paper Time|开放式时空大数据助力智能公交路线规划
Paper Time|开放式时空大数据助力智能公交路线规划
1302 0
Paper Time|开放式时空大数据助力智能公交路线规划
|
缓存 JavaScript 前端开发
从零开始搭建Vue2.0项目(一)之项目快速开始
该样板适用于大型,严肃的项目,并假定您对Webpack和有所了解`vue-loader`。确保还阅读[`vue-loader`的文档,](https://vue-loader.vuejs.org/)了解常见的工作流程配方。 如果您只想尝试`vue-loader`或快速制作出原型,请改用[webpack-simple](https://github.com/vuejs-templates/webpack-simple)模板。
521 0
从零开始搭建Vue2.0项目(一)之项目快速开始