目录
笔记:
首先是对应的头文件delay.h中的函数:
1、delay_init(u8 SYSCLK);
此处将把关于UCOS相关代码忽略,后面学习:
注:以下为SysTick结构体详解,与主体函数只是有一定联系,可略过。
SysTick结构体中的CTRL就是系统控制和状态寄存器(SysTick Control and Status Register):
SysTick结构体中的LOAD就是重转载数据寄存器(24位):
SysTick结构体中的VAL就是当前值寄存器(24位):
SysTick结构体中的CALIB就是校准数值寄存器(24位)(不常用):
SysTick_Config函数让SysTick定时器初始化过程:
2、void delay_ms(u16 nms);和delay_us(u32 nus);
笔记:
首先是对应的头文件delay.h中的函数:
#ifndef __DELAY_H#define __DELAY_H #include void delay_init(u8 SYSCLK);void delay_ms(u16 nms);void delay_us(u32 nus);#endif
可以看到只有三个函数,delay_init(u8 SYSCLK)是SysTick定时器初始化的函数,delay_ms(u16 nms)是计毫秒的,delay_us(u32 nus)是计微秒的。
在调用相关的函数之前,一定要先初始化!
关于fac_ms和fac_us,在文件一开始有这样的定义(一开始是0,后来根据实际情况改变):
static u8 fac_us=0;//us延时倍乘数 static u16 fac_ms=0;//ms延时倍乘数,在os下,代表每个节拍的ms数
这两个全局变量的作用会在第二节讲到。
1、delay_init(u8 SYSCLK);
//初始化延迟函数//当使用OS的时候,此函数会初始化OS的时钟节拍//SYSTICK的时钟固定为AHB时钟的1/8//SYSCLK:系统时钟频率void delay_init(u8 SYSCLK){#if SYSTEM_SUPPORT_OS //如果需要支持OS.u32 reload;#endif SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us=SYSCLK/8;//不论是否使用OS,fac_us都需要使用#if SYSTEM_SUPPORT_OS //如果需要支持OS.reload=SYSCLK/8;//每秒钟的计数次数 单位为M reload*=1000000/delay_ostickspersec;//根据delay_ostickspersec设定溢出时间//reload为24位寄存器,最大值:16777216,在168M下,约合0.7989s左右fac_ms=1000/delay_ostickspersec;//代表OS可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断SysTick->LOAD=reload; //每1/delay_ostickspersec秒中断一次SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK #elsefac_ms=(u16)fac_us*1000;//非OS下,代表每个ms需要的systick时钟数 #endif}
此处将把关于UCOS相关代码忽略,后面学习:
这里出现了SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 这个函数作用就是配置外部时钟源(下文紧接着会介绍),具体定义是:
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; }}
加上宏定义:
#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))#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */typedef struct{ __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */} SysTick_Type; //注意SysTick_Type在这里!!!
注:以下为SysTick结构体详解,与主体函数只是有一定联系,可略过。
SysTick结构体中的CTRL就是系统控制和状态寄存器(SysTick Control and Status Register):
第零位的ENABLE就是开关。
第一位的TICKINT中的 “1=SysTick倒数到0时产生Sys Tick异常请求” 就是产生中断信号。
第二位的CLKSOURCE选择0,外部时钟源,意思是选择AHB始终总线提供的SYSCLK,这在上个笔记SystemInit函数的初始化中记过。
而SysTick_CLKSource_HCLK_Div8就是8分频的意思,因此fac_us=SYSCLK/8的意思是将SYSCLK的频率给fac_us时应该➗8,默认SYSCLK是最大值,168MHz,所以此时fac_us=21,之后会用到。
SysTick结构体中的LOAD就是重转载数据寄存器(24位):
SysTick结构体中的VAL就是当前值寄存器(24位):
一般是先写入LOAD,然后LOAD传值给VAL。
SysTick结构体中的CALIB就是校准数值寄存器(24位)(不常用):
这些都可服务于core_cm4.h中的一个叫SysTick_Config(uint32_t ticks);的函数,该函数作用就是初始化SysTick定时器并且定时,且可以直接用于main()函数:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks){ if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = ticks - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<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 */}
ticks就是两个中断之间的时钟周期个数
SysTick_Config函数让SysTick定时器初始化过程:
先判断ticks有效性,之后把ticks-1赋给LOAD,然后设置优先级(此处后面学习),再之后令VAL=0(让VAL重新加载),最后设定TRL,使能之。
使用示例:SysTick_Config(168000000/1000);代表:168000000/1000*(1/168MHz)=1/1000=1(ms)。即定时为1ms。并且产生中断,由SysTick_Handler函数检测到并作出反应。由于官方的delay.h文件采用的不是中断的方式计时,在此不过多赘述。
2、void delay_ms(u16 nms);和delay_us(u32 nus);
//延时nus//nus为要延时的us数.//注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)void delay_us(u32 nus){u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器 }
SysTick->LOAD=nus*fac_us的nus是想要计数的微妙数,而fac_us含义是1微秒所要计数的次数,根据第一节计算,值为168/8=21,单位是M。
!(temp&(1<<16))的含义就是1左移16位,再与上temp,最后取反。CRTL的第16位数到0则为1,如果读了就变成0,所以当:
1、temp&0x01=0(恒成立,因为CRTL第0位是使能位,为0打开);
2、temp&(1<<16)=0xFFFF(即CRTL的16位为1)时,整个结果取反,!temp&(1<<16)=0:
时间到达,跳出循环。(temp=SysTick->CTRL)
//延时nms //nms:0~65535void delay_ms(u16 nms){ u8 repeat=nms/540;//这里用540,是考虑到某些客户可能超频使用,//比如超频到248M的时候,delay_xms最大只能延时541ms左右了u16 remain=nms%540;while(repeat){delay_xms(540);repeat--;}if(remain)delay_xms(remain);}
这里用到了切片处理,每540ms算一片,比如延迟2000ms,则是延迟三个540ms+一个380ms。
540这个数值可以调整,好处是有效范围变长,比如原本最多延迟798ms(nms<=0xffffff*8*1000/SYSCLK),但是这样操作后,最多延迟为65535,即0xFFFF,u16的最大长度。
这里面用到了delay_xms函数:
//延时nms//注意nms的范围//SysTick->LOAD为24位寄存器,所以,最大延时为://nms<=0xffffff*8*1000/SYSCLK//SYSCLK单位为Hz,nms单位为ms//对168M条件下,nmsLOAD=(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<CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器 }
此函数和delay_us基本上一致,不再赘述。