STM32:systick
1 SysTick定时器
定义:systick为24位倒数计数器,产生的中断由由NVIC控制,使用的时候需要配置nvic优先级,默认优先级还挺高;
systick时钟频率为AHB或AHB/8;时钟源直接来自AHB总线时钟;
systick只需要使能自身中断就可以进入中断函数;普通外设既需要使能自身中断,还需要使能NVIC_ISER才能进入中断函数;
作用:可以为多任务系统的任务设置一个执行周期,如果在时钟周期内任务未完成,就产生systick中断,保证了系统的稳定性;
可以为操作系统提供“心跳”,提供周期性的定时;
当处理器处于低功耗模式下的时候,可能会终止SYSCLK时钟源,于是systick也随之终止;
1.1 SysTick的4个寄存器
还有一个校验值寄存器未用上,也不想看怎么使用,就没有列出来了;
1.2 SysTick的标准库封装
/***core_cm3.h
***通常这些地址映射是定义在stm32f10x.h中,systick是core外设,所以才定义在core_cm3.h中把;
***stm322f10x.h中 #include了"core_cm3.h",好像也可以算是定义在了stm32f10x.h中;***/
#define SysTick ((SysTick_Type *) SysTick_BASE) /*!< SysTick configuration struct */
#define SysTick_BASE (SCS_BASE + 0x0010) /*!< SysTick Base Address */
#define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */
/**************core_cm3.h *************************/
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */
} SysTick_Type;
/* SysTick Control / Status Register Definitions */
#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */
#define SysTick_CTRL_COUNTFLAG_Msk (1ul << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */
#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */
#define SysTick_CTRL_CLKSOURCE_Msk (1ul << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */
#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */
#define SysTick_CTRL_TICKINT_Msk (1ul << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */
#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */
#define SysTick_CTRL_ENABLE_Msk (1ul << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */
/* SysTick Reload Register Definitions */
#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */
/* SysTick Current Register Definitions */
#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */
#define SysTick_VAL_CURRENT_Msk (0xFFFFFFul << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */
/* SysTick Calibration Register Definitions */
#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */
#define SysTick_CALIB_NOREF_Msk (1ul << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */
#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */
#define SysTick_CALIB_SKEW_Msk (1ul << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */
#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */
#define SysTick_CALIB_TENMS_Msk (0xFFFFFFul << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */
/*@}*/ /* end of group CMSIS_CM3_SysTick */
1.2.1 systick的cm3内核使用函数
/**core_cm3.h,神奇吧,这个函数写在.h文件中;
*下面那个__Vendor_SysTickConfig也不知道判断啥的,在stm32f10x.h中定义的,不过这个函数可以直接用就是了;
*/
#if (!defined (__Vendor_SysTickConfig)) || (__Vendor_SysTickConfig == 0)
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
#endif
1.2.2 systick作为nvic控制外设的使用函数
/***misc.c line199***
*misc.c和misc.h是NVIC的代码,systick也属于内核代码,和NVIC放一块了;
*/
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
/* Check the parameters */
assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
{
SysTick->CTRL |= SysTick_CLKSource_HCLK;
}
else
{
SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
}
}
/***misc.h line172**********
***注意到了么,这里的8分频是0xFFFF FFFB,所以8分频配置时是通过“&”,这样“&1”的位都不变;
*************这里的不分频是0x0000 0004,所以不分频配置时是通过“|”,这样“|0”的位都不变;
*/
#define SysTick_CLKSource_HCLK_Div8 ((uint32_t)0xFFFFFFFB)
#define SysTick_CLKSource_HCLK ((uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
((SOURCE) == SysTick_CLKSource_HCLK_Div8))
1.2.3 systick的中断函数
/****stm32f10x_it.c*************************************
*中断标志置1后的处理函数,如果需要处理重新编写即可,由xx.s启动文件映射到这里的;*/
void SysTick_Handler(void)
{
}
1.3 systick的延时应用
1.3.1 直接配置寄存器
/*
*0xFFFFFF=16777215次;AHB为72M,systick为8分频,每次振动为1/9000000s,则systick定时范围约1.86秒;
*1 设置systick的时钟为AHB/8;
*2 load_ms_count 每毫秒内systick时钟源震动的次数;
*3 定时范围超过计时范围return1;定时范围约1.86秒;
*/
int delay_ms(int ms)
{
u32 load_ms_count = 0;
u32 load_total = 0;
u32 temp_systick = 0;
load_ms_count = SystemCoreClock/(8*1000);
load_total = ms*load_ms_count;
if(load_total > 0xFFFFFF) return 1;
SysTick->LOAD = load_total;
SysTick->VAL = 0;
SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk |
SysTick_CTRL_TICKINT_Msk ;
do{
temp_systick = SysTick->CTRL;
}while(!(temp_systick&SysTick_CTRL_COUNTFLAG_Msk));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->LOAD = 0;
SysTick->VAL = 0;
return 0;
}
1.3.2 使用标准库函数配置
/***AHB_72MHz,systick_8分频,时钟周期为1/9us,函数计数范围:(1/9)*0xFF FFFF=1.864s;*************/
int delay_ms(int ms)
{
unsigned int load_count =0;
unsigned int load_done_flag = 0;
load_count = SystemCoreClock/(1000*8)*ms;
if (load_count > SysTick_LOAD_RELOAD_Msk)
return 1;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
SysTick->LOAD = (load_count & SysTick_LOAD_RELOAD_Msk) - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
do{
load_done_flag = SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk;
}while(!load_done_flag);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
return 0;
}
/***AHB_72MHz,时钟周期为1/72us,函数计数范围:(1/72)*0xFF FFFF=0.233s;**********/
int delay_us(int us)
{
unsigned int load_count =0;
unsigned int load_done_flag = 0;
load_count = SystemCoreClock/(1000*1000)*us;
SysTick_Config(load_count);
do{
load_done_flag = SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk;
}while(!load_done_flag);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
return 0;
}
2 小结
代码是之前就写好的,之前也能跑但是有逻辑错误,好久没学习了,所以没看出来,后来才发现是位运算的逻辑问题;
隔了两天才重新把代码整理了一下,以为是内核里的应该内容挺多的挺杂的吧,没想到就是一个寄存器直接操作给我整迷惑了;
标准库只提供了两个函数使用,两个函数的配置还互相冲突不能同时使用,最后还是要直接操作寄存器那还不如不用库函数;
整理完发现这也太简单了吧,那我干嘛老是畏手畏脚踟蹰不行呢;
一方面遇到逻辑问题的时候总是会想是否还有其他最优写法,但是自身经验不足没法想出来加上单片机要我啥逻辑呀,而我又偏偏还爱卡着想下去;
一方面是出于对未知的胆怯,只要还有后路就总是磨磨蹭蹭不想直接面对问题,就想逃避问题,即使问题本身面对起来绰绰有余;
一方面是自己的行为模式习惯于尽可能多的收集各种信息进行信息整合,而在收集的过程总是担心收集的不够全面然后消耗好多时间;
一方面还是要体谅自己学会给自己容错的空间来修复情绪,错误和浪费都是不可避免的,不妨大胆地去走自己的夜路;
天哪,我真是情绪管理小天才;