【.Net Micro Framework PortingKit – 12】SysTick驅動開發

SysTick驅動對TinyCLR來講很是重要,.Net Micro Framework系統的多線程和多任務(對託管代碼來講是單任務多線程,可是還存在和託管代碼同時運行的任務,如咱們用MFDeploy程序Ping TinyCLR或擦寫Flash 的時候,就是另外的任務在執行)就是靠它來實現的。多線程

SysTick驅動有三個功用,一是咱們上面所說的多任務和多線程支持;二是得到系統當前Tick,以此實現延時等待,好比咱們常見的Events_WaitForEvents函數就靠它來實現延時功能的;三是爲Native代碼提供兩個版本的Sleep函數。ide

和ARM7或ARM9相比,Cortex-M3系列的CPU提供了SysTick這個feature,因此咱們就不須要用Timer來模擬Tick的功能了,直接用系統提供的SysTick就能夠了。Cortex-M3的SysTick其定時器計數是遞減的,遞減到0就會觸發中斷(固然要使TICKINT使能),而後自動會加載LOAD寄存器的值,啓動下一次計數循環。函數

LOAD寄存器可填入的最大值爲0x00FFFFFF,對72M主頻的CPU來講,大概會有250毫秒左右的延時。因爲VAL寄存器的值是遞減的,因此在移植相關代碼的時候要特別注意,咱們概念中的Tick的值應該是(LOAD-VAL)。oop

在CortexM3.h文件中添加以下代碼,以便於配置SysTick寄存器。測試

  
  
  
  
  1. struct CortexM3_SysTick  
  2.  
  3. {  
  4.  
  5.     static const UINT32 c_Base = 0xE000E010;  
  6.  
  7.     /****/ volatile UINT32 CTRL;  //0xE000E010  
  8.  
  9.     static const    UINT32 CTRL_COUNTFLAG= ((UINT32)0x00010000);    
  10.  
  11.     static const    UINT32 CTRL_CLKSOURCE= ((UINT32)0x00000004);   
  12.  
  13.     static const    UINT32 CTRL_TICKINT= ((UINT32)0x00000002);   
  14.  
  15.     static const    UINT32 CTRL_ENABLE= ((UINT32)0x00000001);   
  16.  
  17.     /****/ volatile UINT32 LOAD;  //0xE000E014  
  18.  
  19.     static const    UINT32 LOAD_RELOAD= ((UINT32)0x00FFFFFF);    
  20.  
  21.     /****/ volatile UINT32 VAL;  //0xE000E018  
  22.  
  23.     static const    UINT32 VAL_CURRENT= ((UINT32)0x00FFFFFF);    
  24.  
  25.     /****/ volatile UINT32 CALIB;  //0xE000E01C  
  26.  
  27.     static const    UINT32 CALIB_NOREF= ((UINT32)0x80000000);    
  28.  
  29.     static const    UINT32 CALIB_SKEW= ((UINT32)0x40000000);    
  30.  
  31.     static const    UINT32 CALIB_TENMS= ((UINT32)0x00FFFFFF);        
  32.  
  33. };  
  34.  

而後在\DeviceCode\Targets\Native\CortexM3\DeviceCode\SysTick新建四個文件SysTick.h、SysTick.cpp、SysTick_Functions.cpp、dotNetMF.proj。ui

在SysTick.h中建立SYSTICK_Driver結構體,SysTick.cpp存放該結構體的具體實現代碼。this

  
  
  
  
  1. struct SYSTICK_Driver  
  2.  
  3. {  
  4.  
  5.     static const UINT32 c_MaxTimerValue = 0xFFFFFF; //16777215  最大 250ms左右的定時  
  6.  
  7.    
  8.  
  9.     volatile UINT64 m_Tick;  
  10.  
  11.     volatile UINT64 m_nextCompare;  
  12.  
  13.       
  14.  
  15.     static BOOL Initialize  ();  
  16.  
  17.     static BOOL Uninitialize();  
  18.  
  19.     static UINT64 CounterValue();  
  20.  
  21.     static void SetCompareValue( UINT64 CompareValue );  
  22.  
  23.     static INT64 TicksToTime( UINT64 Ticks );  
  24.  
  25.     static INT64 CurrentTime();  
  26.  
  27.     static void Sleep_uSec( UINT32 uSec );  
  28.  
  29.     static void Sleep_uSec_Loop( UINT32 uSec );  
  30.  
  31.     static void ISR( void* Param );  
  32.  
  33. };  
  34.  

Cortex-M3內核下的UINT64 CounterValue()函數的具體實現不一樣於.Net Micro Framework自帶的各平臺上的源碼,因此有必要介紹一下它的實現:spa

  
  
  
  
  1. UINT64 SYSTICK_Driver::CounterValue()  
  2.  
  3. {  
  4.  
  5.     GLOBAL_LOCK(irq);  
  6.  
  7.     CortexM3_SysTick &SysTick= CortexM3::SysTick();  
  8.  
  9.     UINT32 value = (SysTick.LOAD - SysTick.VAL);  
  10.  
  11.      
  12.  
  13.     if(SysTick.CTRL & CortexM3_SysTick::CTRL_COUNTFLAG)  
  14.  
  15.     {  
  16.  
  17.         g_SYSTICK_Driver.m_Tick+=SysTick.LOAD;             
  18.  
  19.     }  
  20.  
  21.        
  22.  
  23.     return  g_SYSTICK_Driver.m_Tick + value;   
  24.  
  25. }  
  26.  

Value的值爲(SysTick.LOAD - SysTick.VAL),這是和其它平臺的驅動一個區別。此外m_Tick也是我新添加的,它是不斷累加計數的,可是它不能真實反映系統開機以來的Tick數,由於SysTick有可能會被禁止中斷,也就是說ISR函數不能保證整個系統運行期內都被正常觸發。線程

ISR是一個重點,它是系統實現多任務和多線程的「動力源」。debug

  
  
  
  
  1. void SYSTICK_Driver::ISR( void* Param )  
  2.  
  3. {      
  4.  
  5.     if(CounterValue() >= g_SYSTICK_Driver.m_nextCompare)  
  6.  
  7.     {  
  8.  
  9.        HAL_COMPLETION::DequeueAndExec();  
  10.  
  11.     }  
  12.  
  13.     else 
  14.  
  15.     {  
  16.  
  17.         SetCompareValue( g_SYSTICK_Driver.m_nextCompare );  
  18.  
  19.     }  
  20.  
  21. }  
  22.  

HAL_COMPLETION::DequeueAndExec()代碼是關鍵,每間隔一個指定的時間就會執行一次任務,常見間隔時間爲20ms。

Sleep_uSec函數是經過Tick計數計算延時間隔的。

  
  
  
  
  1. void __section(SectionForFlashOperations) SYSTICK_Driver::Sleep_uSec( UINT32 uSec )  
  2.  
  3. {  
  4.  
  5.     GLOBAL_LOCK(irq);  
  6.  
  7.     CortexM3_SysTick &SysTick= CortexM3::SysTick();     
  8.  
  9.     UINT32 maxDiff  = CPU_MicrosecondsToTicks( uSec );   //每微秒的滴答數  
  10.  
  11.         SysTick.LOAD =   maxDiff & 0xFFFFFF;     
  12.  
  13.     while(!(SysTick.CTRL & CortexM3_SysTick::CTRL_COUNTFLAG));  
  14.  
  15. }  
  16.  

__section(SectionForFlashOperations)標識該函數會被拷貝到RAM中去運行(保證執行時間)。

而Sleep_uSec_Loop函數則是經過彙編代碼的循環實現的,延時相對比較精確。

  
  
  
  
  1. void __section(SectionForFlashOperations) SYSTICK_Driver::Sleep_uSec_Loop( UINT32 uSec )  
  2.  
  3. {  
  4.  
  5.     // iterations must be signed so that negative iterations will result in the minimum delay  
  6.  
  7.     uSec *= (SYSTEM_CYCLE_CLOCK_HZ / CLOCK_COMMON_FACTOR);  
  8.  
  9.     uSec /= (ONE_MHZ               / CLOCK_COMMON_FACTOR);  
  10.  
  11.    
  12.  
  13.     // iterations is equal to the number of CPU instruction cycles in the required time minus  
  14.  
  15.     // overhead cycles required to call this subroutine.  
  16.  
  17.     int iterations = (int)uSec - 14;      // Subtract off call & calculation overhead  
  18.  
  19.     CYCLE_DELAY_LOOP2(iterations);  
  20.  
  21. }  
  22.  

CYCLE_DELAY_LOOP2的實現代碼是彙編,我把它放在FirstEntry.s文件裏了,具體代碼以下:

  
  
  
  
  1. IDelayLoop2  
  2.  
  3.     EXPORT  IDelayLoop2  
  4.  
  5.     subs    r0,r0, #2          ;; 1 cycle  
  6.  
  7.     bgt     IDelayLoop2        ;; 1 cycle  
  8.  
  9.       mov     pc, lr              ;; 5 cycles    
  10.  

Sleep_uSec_Loop函數實現代碼中的uSec – 14是從其它CPU代碼中拷貝來的,針對Cortex-M3應該是多少,我尚未細算過,之後有時間再補上這一課。

在NativeSample.proj中添加以下條目,就能夠測試SysTick驅動了:

 

  
  
  
  
  1. <ItemGroup> 
  2.  
  3.     <RequiredProjects Include="$(SPOCLIENT)\DeviceCode\Targets\Native\CortexM3\DeviceCode\SysTick\dotNetMF.proj" /> 
  4.  
  5.     <DriverLibs Include="SysTick.$(LIB_EXT)" /> 
  6.  
  7.   </ItemGroup> 
  8.  

在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版本就會出問題了。可見嵌入式開發的難點不在於代碼的編寫,而在於調試。

相關文章
相關標籤/搜索