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

 

posted @ 2018-05-31 23:27  半生戎马,共话桑麻、  阅读(383)  评论(0)    收藏  举报
levels of contents