首先贴出工程
工程上传到了我的github上了,点此进入
我用的LED引脚是PC13,芯片是stm32c8t6。
首先理解systick
我们一般称它为系统滴答定时器,一个24位的系统节拍定时器,它只有基本的计数功能,所有cortex m3核心的单片机都有这样一个定时器,因为它是属于cortex m3内核的,不属于外设一类。
它可以干什么
首先它是一个定时器,肯定是来定时的,学过51的都了解,定时器就是计数来的,计数达到定时要求。它的寄存器非常简单
在这里可以看到它的解释,如果想了解更多,可以看一下《Cortex-M3权威指南》
所有基于ARM Cortex_M3内核的控制器都带有SysTick定时器,这样就方便了程序在不同的器件之间的移植。而使用RTOS的第一项工作往往就是将其移植到开发人员的硬件平台上,由于SYSTICK的存在无疑降低了移植的难度。
这也是为什么我们使用滴答定时器的原因,因为它属于内核,每个arm单片机都有,简单。
关于滴答定时器中断
32的滴答定时器的中断受到NVIC的管理,所以它的中断是可以设置优先级的
其中断函数是void SysTick_Handler(void)这个我就不说了。中断优先级,在调用SysTick_Config(uint32_t ticks)之后,调用 void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)。这个函数在core_cm3.h头文件中。
关于延时
目前接触的有三种常用的延时方式
第一种是最普通的延时,也就是根据系统运行频率去加减一个数来达到延时效果,这个的特点是实现简单但是不能保证时间的完全正确,这个用来粗略延时场所是最合适的。
第二种通过系统滴答定时器计数,获取寄存器状态来获得延时效果,本质上与第一个延时方法一致,但是更加精确。个人不喜欢这种定时方式,arm的芯片确实用起来可以,一旦换了其它内核的芯片就不好移植了。
第三种通过定时器中断,不限于滴答定时器,但是平常我们都用滴答定时器来做。中断计数,获取计数值来延时。这个的好处就是相对精确,ms以上延迟用着不错,但是涉及中断的嵌套,这个是要注意中断优先级的,将这个中断优先级设置为最高就好,反正计数不过US级别的时间,不会对其它任务有影响。
很明显第二种和第三种不可能兼容
所以我推荐第一种和第三种,第二种是正点原子做的方式吧,我不是很喜欢,毕竟我使用任务调度搞一个timebase,照它这个搞法timebase是没得咯。
第一种延时代码
//毫秒级的延时
void delay_ms(u16 time)
{
u16 i=0;
while(time--)
{
i=12000; //自己定义
while(i--) ;
}
}
这个可以自己调嘛,肯定是的。。。。
第二种延时代码
void delay_init()
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8
fac_us=SystemCoreClock/8000000; //为系统时钟的1/8,实际上也就是在计算1usSysTick的VAL减的数目
fac_ms=(u16)fac_us*1000; //代表每个ms需要的systick时钟数,即每毫秒SysTick的VAL减的数目
}
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达,看CTRL的第16位(COUNTFLAG)是否为1,看STRL的第0位(ENABLE)是否为1
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
第三种延时代码
官方sdk的代码是这样子的
void systick_Init(void){
SysTick_Config(SystemCoreClock / 1000);
}
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
TimingDelay_Decrement();
}
但是由于老毛病,我日常需要一个向上计数的timebase,所以我改了改。
下面是我的第三种延迟代码
void systick_Init(void){
SysTick_Config(SystemCoreClock / 1000);
}
void sysdelay_ms(u16 ms)
{
u32 end_time = systime_ms+ms;
while( systime_ms < end_time)
{
;
}
}
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
systime_ms++;
}
OK,收工。。。。