【.Net Micro Framework PortingKit – 12】SysTick驱动开发
SysTick驅(qū)動對TinyCLR來說非常重要,.Net Micro Framework系統(tǒng)的多線程和多任務(wù)(對托管代碼來說是單任務(wù)多線程,但是還存在和托管代碼同時運行的任務(wù),如我們用MFDeploy程序Ping TinyCLR或擦寫Flash 的時候,就是另外的任務(wù)在執(zhí)行)就是靠它來實現(xiàn)的。
SysTick驅(qū)動有三個功用,一是我們上面所說的多任務(wù)和多線程支持;二是獲得系統(tǒng)當(dāng)前Tick,以此實現(xiàn)延時等待,比如我們常見的Events_WaitForEvents函數(shù)就靠它來實現(xiàn)延時功能的;三是為Native代碼提供兩個版本的Sleep函數(shù)。
和ARM7或ARM9相比,Cortex-M3系列的CPU提供了SysTick這個feature,所以我們就不需要用Timer來模擬Tick的功能了,直接用系統(tǒng)提供的SysTick就可以了。Cortex-M3的SysTick其定時器計數(shù)是遞減的,遞減到0就會觸發(fā)中斷(當(dāng)然要使TICKINT使能),然后自動會加載LOAD寄存器的值,啟動下一次計數(shù)循環(huán)。
LOAD寄存器可填入的最大值為0x00FFFFFF,對72M主頻的CPU來說,大概會有250毫秒左右的延時。由于VAL寄存器的值是遞減的,所以在移植相關(guān)代碼的時候要特別注意,我們概念中的Tick的值應(yīng)該是(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.h、SysTick.cpp、SysTick_Functions.cpp、dotNetMF.proj。
在SysTick.h中創(chuàng)建SYSTICK_Driver結(jié)構(gòu)體,SysTick.cpp存放該結(jié)構(gòu)體的具體實現(xiàn)代碼。
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內(nèi)核下的UINT64 CounterValue()函數(shù)的具體實現(xiàn)不同于.Net Micro Framework自帶的各平臺上的源碼,所以有必要介紹一下它的實現(xiàn):
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),這是和其它平臺的驅(qū)動一個區(qū)別。此外m_Tick也是我新添加的,它是不斷累加計數(shù)的,但是它不能真實反映系統(tǒng)開機(jī)以來的Tick數(shù),因為SysTick有可能會被禁止中斷,也就是說ISR函數(shù)不能保證整個系統(tǒng)運行期內(nèi)都被正常觸發(fā)。
ISR是一個重點,它是系統(tǒng)實現(xiàn)多任務(wù)和多線程的“動力源”。
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()代碼是關(guān)鍵,每間隔一個指定的時間就會執(zhí)行一次任務(wù),常見間隔時間為20ms。
Sleep_uSec函數(shù)是通過Tick計數(shù)計算延時間隔的。
void __section(SectionForFlashOperations) SYSTICK_Driver::Sleep_uSec( UINT32 uSec )
{
??? GLOBAL_LOCK(irq);
??? CortexM3_SysTick &SysTick= CortexM3::SysTick(); ?
??? UINT32 maxDiff?= CPU_MicrosecondsToTicks( uSec );?? //每微秒的滴答數(shù)
?? ???? SysTick.LOAD = ?maxDiff & 0xFFFFFF; ?
??? while(!(SysTick.CTRL & CortexM3_SysTick::CTRL_COUNTFLAG));
}
__section(SectionForFlashOperations)標(biāo)識該函數(shù)會被拷貝到RAM中去運行(保證執(zhí)行時間)。
而Sleep_uSec_Loop函數(shù)則是通過匯編代碼的循環(huán)實現(xiàn)的,延時相對比較精確。
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的實現(xiàn)代碼是匯編,我把它放在FirstEntry.s文件里了,具體代碼如下:
IDelayLoop2
??? EXPORT?IDelayLoop2
??? subs??? r0,r0, #2????????? ;; 1 cycle
??? bgt???? IDelayLoop2??????? ;; 1 cycle
??? ?mov???? pc, lr???????????? ?;; 5 cycles?
Sleep_uSec_Loop函數(shù)實現(xiàn)代碼中的uSec – 14是從其它CPU代碼中拷貝來的,針對Cortex-M3應(yīng)該是多少,我還沒有細(xì)算過,以后有時間再補上這一課。
在NativeSample.proj中添加如下條目,就可以測試SysTick驅(qū)動了:
?<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驅(qū)動的一部分功能,全部的功能要在TinyCLR項目去測試了。
小插曲:在實現(xiàn)LCD驅(qū)動的時候,初始化LCD寄存器需要延時,在采用CYCLE_DELAY_LOOP2時,debug版本和release版本有很大的區(qū)別,debug可正常運行,但是release會有問題,在Sleep_uSec_Loop函數(shù)開始添加GLOBAL_LOCK(irq)代碼就可以了,但是這樣一改在TinyCLR代碼中能正常運行的debug版本就會出問題了。可見嵌入式開發(fā)的難點不在于代碼的編寫,而在于調(diào)試。
總結(jié)
以上是生活随笔為你收集整理的【.Net Micro Framework PortingKit – 12】SysTick驱动开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SharePoint Explorer
- 下一篇: ***某知名网络安全公司