day15:系统定时器Systick
SysTick:系统定时器,24位,只能递减,存在于内核,嵌套在NVIC中,所有的Cortex-M内核的单片机都具有这个定时器。

counter在时钟的驱动下,从reload初值开始往下递减计数到0,产生中断和置位COUNTFLAG标志。然后又从reload值开始重新递减计数,如此循环。







代码:
1、拷贝固件库编程模板目录:FWLIB-Template,复制day11的模板:https://www.cnblogs.com/josephcnblog/articles/8955631.html
2、工程结构

3、代码
(1)bsp_led.h
/* 和LED功能模块相关的程序 */
#ifndef __BSP_LED_H__
#define __BSP_LED_H__
#include "stm32f10x.h"
/*宏定义*/
#define GPIO_CLK_D4 RCC_APB2Periph_GPIOC // 时钟
#define GPIO_PORT_D4 GPIOC // C端口
#define GPIO_PIN_D4 GPIO_Pin_2 // PC2引脚
/*参数宏定义*/
/*
digitalTOGGLE(p,i)是参数宏定义,p表示LED的端口号,ODR是数据输出寄存器,
查stm32f10x的官方中文手册的第8.2章的ODR寄存器,要点亮LED,根据原理图,要输出低电平0,
C语言中,^表示异或,即a^b表示a和b不同时输出为1,相同时输出为0,比如0^1=1,1^1=0,0^0=0,
这里为什么操作ODR,p是什么?查看stm32f10x.h文件,搜索GPIO_TypeDef就会明白,
i是LED的引脚对应的位电平,经过digitalTOGGLE(p,i) {p->ODR ^= i;}之后,
第一次p为0,i一直为1,第一次异或结果输出1,第二次输出0,第三次输出1,这样间断输出010101,灯不断亮灭
*/
#define digitalTOGGLE(p,i) {p->ODR ^= i;}
#define LED1_TOGGLE digitalTOGGLE(GPIO_PORT_D4,GPIO_PIN_D4)
/*配置GPIO*/
void LED_GPIO_Config(void);
#endif /*__BSP_LED_H__*/
(2)bsp_led.c
/* 和LED功能模块相关的程序头文件 */
#include "./led/bsp_led.h" /* 绝对路径,也可在Options for target中设置头文件 */
/* GPIO初始化 */
void LED_GPIO_Config(void)
{
/*外设结构体*/
GPIO_InitTypeDef GPIO_InitStruct_D4;
/*第一步:打开外设的时钟,看stm32f10x_rcc.c这个文件的RCC_APB2PeriphClockCmd函数介绍 */
RCC_APB2PeriphClockCmd(GPIO_CLK_D4, ENABLE);
/* 第二步:配置外设的初始化结构体 */
GPIO_InitStruct_D4.GPIO_Pin = GPIO_PIN_D4; // PC2的那盏LED灯(D4)的引脚
GPIO_InitStruct_D4.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式
GPIO_InitStruct_D4.GPIO_Speed = GPIO_Speed_10MHz; // 引脚速率
/* 第三步:调用外设初始化函数,把配置好的结构体成员写到寄存器里面 */
GPIO_Init(GPIO_PORT_D4, &GPIO_InitStruct_D4);
}
(3)bsp_systick.h
#ifndef __BSP_SYSTICK_H__ #define __BSP_SYSTICK_H__ #include "stm32f10x.h" /* 精确延迟ms毫秒(这里不是软件延迟,软件延迟不精确,这里是硬件延迟,是系统中断Systick延迟) */ void SysTick_Delay_ms(uint32_t ms); #endif /* __BSP_SYSTICK_H__ */
(4)bsp_systick.c
#include "./systick/bsp_systick.h"
#if 0 /* 以下代码只是用于解读,不算真正代码,这里不编译! */
/* 这个函数在core_cm3.h文件中第1694行处定义 */
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
/* reload 寄存器为24位,最大值为2^24 */
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); // 如果传入的值大于2^24,则结束函数
/*
初始化reload寄存器的值:ticks & SysTick_LOAD_RELOAD_Msk = ticks
SysTick->LOAD:是SysTick系统定时器的重装载值,-1是因为从0开始计数
*/
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
/* 配置systick的中断优先级 */
NVIC_SetPriority(SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
/* 计数器值清0,SysTick->VAL:是系统定时器的当前值 */
SysTick->VAL = 0;
/*
(1)配置systick的时钟为AHB的时钟,查底层代码,SysTick_CTRL_CLKSOURCE_Msk是1<<2,即100
(2)使能systick的中断,SysTick_CTRL_TICKINT_Msk是1<<1,即10
(3)使能systick,SysTick_CTRL_ENABLE_Msk是1<<0,即1
故:0100|0010|0001=0111,SysTick->CTRL是32位的,前面都是0,后面四位是0111
*/
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
return (0);
}
#endif
/* 毫秒延迟函数 */
void SysTick_Delay_ms(uint32_t ms)
{
uint32_t i;
/*
机器周期是1/72M,72000即(1/72M)*72000=1000us=1ms,即一个Systick中断将产生1ms延迟的时钟
*/
SysTick_Config(72000);
/*
循环ms次,每次循环产生1毫秒的延迟时间,即ms毫秒的延迟时间
1、SysTick->CTRL:获取到系统时钟的CRTL寄存器(控制与状态寄存器),这个寄存器的特点是
当Systick计数到0时,位16的COUNTFLAG置1;
2、1<<16:即1000 0000 0000 0000;
3、(SysTick->CTRL)&(1<<16):表示如果CTRL的位16(即最高位)为0时,结果的最高位为0,
当CTRL的位16为1时,结果的最高位为1,所以结果取决于CTRL的最高位为0还是为1,因为
SysTick_Config(72000);已经配置了每次中断延迟为1ms,下面的while循环会不断地读取系统的
CTRL值,当其值为0时,也即1ms的计数到了,
则SysTick->CTRL的位16返回1(查STM32F10xxx Cortex-M3编程手册-英文版.pdf的4.5.1小结,在本篇结尾有该文件的截图),
所以while(条件)的条件为假,退出循环,进行下一次循环,这样每次循环都延迟1ms(通过系统Systick
的中断延迟来产生的延迟时间),循环500次就产生了500ms的时间。
*/
for(i=0; i<ms; i++)
{
while(!((SysTick->CTRL)&(1<<16)));
}
/*
让SysTick->CTRL失能,查STM32F10xxx Cortex-M3编程手册-英文版.pdf的4.5.1小结可知,
SysTick->CTRL寄存器的位0为0时,寄存器为disabled,为1时寄存器为enabled,这里让其
失能,让所有位均置0,所以是:与或等于取反,用符号表示为:&=~1
SysTick_CTRL_ENABLE_Msk:默认为1,~1即为0,经过&=之后,所有位均被置0
*/
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
(5)main.c
/* 编写一个毫秒和微秒延迟函数,让LED灯不断闪烁。微秒几乎用不上 */
#include "stm32f10x.h"
#include "./systick/bsp_systick.h"
#include "./led/bsp_led.h"
int main(void)
{
// LED初始化
LED_GPIO_Config();
/* D4的LED会相隔500ms不断闪烁 */
while(1)
{
/* 配置ODR寄存器 */
GPIO_SetBits(GPIO_PORT_D4, GPIO_PIN_D4); // D4的LED
SysTick_Delay_ms(500); // 延迟500ms
GPIO_ResetBits(GPIO_PORT_D4, GPIO_PIN_D4); // D4的LED
SysTick_Delay_ms(500); // 延迟500ms
}
}
实验现象:D4的那盏LED灯隔500ms不断闪烁
================================== 以上是使用SysTick->CTRL的位段16(COUNTFLAG)来进行延迟
================================== 下面使用中断服务函数来产生中断
(1)在bsp_systick.h中添加下面的函数
/* 使用中断服务函数来延迟 */ void SysTick_Delay_ms_INT(uint32_t ms);
(2)在bsp_systick.c中添加
/* ---------- 使用中断服务函数来产生中断 ---------- */
/*
全局变量,表示1ms的计数值
volatile:CPU在读取变量的时候,第一次是从内存中读取的,之后就不再从内存读取了,它会将
此变量存放到缓存中,直接从缓存中读取,这里使用volatile,是避免CPU从缓存中读取变量,这样
可能会产生计数值isr_ms的脏读(计数来不及更新,读的是未更新前的数据),所以加上volatile,
表示让CPU每次都从内存中读取isr_ms的变量值,这样每次读到的都是更新后的最新的计数值。
*/
volatile uint32_t isr_ms;
void SysTick_Delay_ms_INT(uint32_t ms)
{
/* 变量isr_ms的初始值 */
isr_ms = ms;
/* 配置中断延迟时间为1ms */
SysTick_Config(72000);
/*
在stm32f10x_it.c文件中的中断服务函数SysTick_Handler()中
每次产生中断,isr_ms变量就自减,直到isr_ms为0时跳出循环,这时完成了ms次中断,
产生了ms毫秒的中断延迟时间。
*/
while(isr_ms);
/* 失能systick */
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
(3) 在stm32f10x_it.c的SysTick_Handler(void)函数中添加,第138行
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
/* extern修饰变量表示此变量已经在其他文件中定义了 */
extern volatile uint32_t isr_ms; /* 此变量在bsp_systick.c中声明(第79行) */
void SysTick_Handler(void)
{
/* 每产生一次中断,isr_ms值就减1 */
isr_ms--;
}
(4)修改main.c
/* 编写一个毫秒和微秒延迟函数,让LED灯不断闪烁。微秒几乎用不上 */
#include "stm32f10x.h"
#include "./systick/bsp_systick.h"
#include "./led/bsp_led.h"
int main(void)
{
// LED初始化
LED_GPIO_Config();
/* D4的LED会相隔500ms不断闪烁 */
while(1)
{
/* 配置ODR寄存器 */
GPIO_SetBits(GPIO_PORT_D4, GPIO_PIN_D4); // D4的LED
//SysTick_Delay_ms(500); // 延迟500ms
SysTick_Delay_ms_INT(500);
GPIO_ResetBits(GPIO_PORT_D4, GPIO_PIN_D4); // D4的LED
//SysTick_Delay_ms(500); // 延迟500ms
SysTick_Delay_ms_INT(500);
}
}
实验现象:将程序烧录到单片机,实验现象和上面的使用Systick->CTRL的标志位16的COUNTFLAG判断0或1来产生中断的结果一样
每次隔500ms产生亮或灭
【 附件 】
STM32F10xxx Cortex-M3编程手册-英文版.pdf




浙公网安备 33010602011771号