五、stm32-SysTick(系统定时器)

目录

  • 一、固件库模板
  • 二、准备资料
  • 三、 SYSTick简介
    • 1. SysTick寄存器介绍
  • 四、 SysTick定时实验
    • 1.硬件设计
    • 2.软件设计
      • 2.1 复制工程 (复制点亮LED工程)
      • 2.2 新建SysTick文件(打开工程)
      • 2.3 导入.c文件,绑定路径
      • sysTick.c
      • SysTick 中断时间的计算
      • stm32f10x_it.c
      • main.c
    • 3. 编译
      • 1.c语法错误
    • 4.选择烧录工具并配置MDK
    • 5. 成品
  • 工程链接

一、固件库模板

见博客:stm32f103c8t6新建固件库模板(可自取)

二、准备资料

  1. 固件库模板
  2. MDK5开发环境
  3. 定时器协议
  4. 利用固件库模板点灯工程

实验程序已经发布到百度网盘,本文末有链接可以自取

中断查看这篇博客STM32中断应用概括

一、利用固件库模板点灯(附模板及案例程序

三、 SYSTick简介

SysTick-系统定时器是属于CM3的一个外设,内嵌在NVIC中,系统定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK,我们一般设置SYSCLK的值为72M。当重装载数值寄存器的值递减到0,系统定时器就产生一次中断,

1. SysTick寄存器介绍

SysTick-系统定时器有4个寄存器,只需要配置前三个寄存器,最后一个是校准寄存器不需要使用。

寄存器名称 寄存器描述
CTRL SysTick控制及状态寄存器
LOAD SysTick重装载数值寄存器
VAl SysTick当前数值寄存器
CALIB SysTick校准数值寄存器

五、stm32-SysTick(系统定时器)_第1张图片
在这里插入图片描述
五、stm32-SysTick(系统定时器)_第2张图片
五、stm32-SysTick(系统定时器)_第3张图片

四、 SysTick定时实验

利用SysTick产生一秒的延时,LED以1s的频率闪烁

1.硬件设计

stm32f103c8t6自带PC13的LED

2.软件设计

  1. 设置重装载寄存器的值

  2. 清除当前数值寄存器的值

  3. 配置控制及状态寄存器

2.1 复制工程 (复制点亮LED工程)

一、利用固件库模板点灯(附模板及案例程序)

2.2 新建SysTick文件(打开工程)

五、stm32-SysTick(系统定时器)_第4张图片
五、stm32-SysTick(系统定时器)_第5张图片

2.3 导入.c文件,绑定路径

五、stm32-SysTick(系统定时器)_第6张图片

SysTick 配置库函数

// 这个 固件库函数 在 core_cm3.h 中
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
// reload 寄存器为 24bit,最大值为 2^24
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);

// 配置 reload 寄存器的初始值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;

 // 配置中断优先级为 1<<4 -1 = 15,优先级为最低
 NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);

// 配置 counter 计数器的值
SysTick->VAL = 0;

// 配置 systick 的时钟为 72M
// 使能中断
// 使能 systick
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
		sysTick_CTRL_TICKINT_Msk |
		SysTick_CTRL_ENABLE_Msk;
return (0);
}

用固件库编程的时候我们只需要调用库函数 SysTick_Config() 即可,形参 ticks 用来设置重装载寄存器的值,最大不能超过重装载寄存器的值 224,当重装载寄存器的值递减到 0 的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。紧随其后设置好中断优先级,最后配置系统定时器的时钟等于 AHBCLK=72M,使能定时器和定时器中断,这样系统定时器就配置好了,一个库函数搞定。SysTick_Config() 库函数主要配置了 SysTick 中的三个寄存器: LOAD、 VAL 和 CTRL,有关具体的部分看代码注释即可。

配置SysTick 中断优先级

__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
	if ((int32_t)IRQn < 0) {
	SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] =(uint8_t)		((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
	} else {
    NVIC->IP[((uint32_t)(int32_t)IRQn)] =(uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
	}
}

函数首先先判断形参 IRQn 的大小,如果是小于 0,则表示这个是系统异常,系统异常的优先级由内核外设 SCB 的寄存器 SHPRx 控制,如果大于 0 则是外部中断,外部中断的优先级由内核外设 NVIC 中的 IPx 寄存器控制。因为 SysTick 属于内核外设,跟普通外设的中断优先级有些区别,并没有抢占优先级和子优先级的说法。在 STM32F103 中,内核外设的中断优先级由内核 SCB 这个外设的寄存器: SHPRx(x=1.2.3)来配置。有关 SHPRx 寄存器的详细描述可参考《Cortex-M3 内核编程手册》 4.4.8 章节。

下面我们简单介绍下这个寄存器。
SPRH1-SPRH3 是一个 32 位的寄存器,但是只能通过字节访问,每 8 个字段控制着一个内核外设的中断优先级的配置。在 STM32F103 中,只有位 7:4 这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为: 0~15,只有 16 个可编程优先级,数值越小,优先级越高。如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。

五、stm32-SysTick(系统定时器)_第7张图片

如果我同时使用了 systick 和片上外设呢?而且片上外设也刚好需要使用中断,那 systick 的中断优先级跟外设的中断优先级怎么设置?会不会因为 systick 是内核里面的外设,所以它的中断优先级就一定比内核之外的外设的优先级高?

外设在设置中断优先级的时候,首先要分组,然后设置抢占优先级和子优先级。而 systick 这类内核的外设在配置的时候,只需要配置一个寄存器即可,取值范围为 0~15。既然配置方法不同,那如何区分两者的优先级?下面举例说明。
比如配置一个外设的中断优先级分组为 2,抢占优先级为 1,子优先级也为 1, systick 的优先级为固件库默认配置的 15。当我们比较内核外设和片上外设的中断优先级的时候,我们只需要抓住NVIC 的中断优先级分组不仅对片上外设有效,同样对内核的外设也有效。我们把 systick 的优先级 15 转换成二进制值就是 1111(0b),又因为 NVIC 的优先级分组 2,那么前两位的 11(0b) 就是3,后两位的 11(0b) 也是 3。无论从抢占还是子优先级都比我们设定的外设的优先级低。如果当两个的软件优先级都配置成一样,那么就比较他们在中断向量表中的硬件编号,编号越小,优先级越高。

中断在之前STM32中断应用概括已经讲过,不清楚的可以继续查看。

sysTick.c

#include "sysTick.h"

/**
  * @brief  启动系统滴答定时器 SysTick
  * @param  无
  * @retval 无
  */
void SysTick_Init( void )
{
    /* SystemFrequency / 1000    1ms中断一次
     * SystemFrequency / 100000	 10us中断一次
     * SystemFrequency / 1000000 1us中断一次
     */
    if ( SysTick_Config(SystemCoreClock / 100) )//10ms
    {
        /* Capture error */
        while (1);
    }

    // 关闭滴答定时器
    //SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;

    // 使能滴答定时器  10ms中断一次
    SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;

}

SysTick 中断时间的计算

SysTick 定时器的计数器是向下递减计数的,计数一次的时间 TDEC=1/CLKAHB,当重装载寄存器中的值 VALUELOAD 减到 0 的时候,产生中断,可知中断一次的时间 TINT=VALUELOAD*TDEC=VALUELOAD/CLKAHB,其中 CLKAHB =72MHZ。如果设置 VALUELOAD 为 72,那中断一次的时间TINT=72/72M=1us。不过 1us 的中断没啥意义,整个程序的重心都花在进出中断上了,根本没有时间处理其他的任务 。

SysTick_Config()的形我们配置为 SystemCoreClock / 1000000=72M/100000=72,从刚刚分析我们知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在把 SysTick 定时器中断一次的时间 TINT=72/72M=1us

微秒延时

void CPU_TS_Tmr_Delay_US( __IO uint32_t us)
{
	uint32_t i;
	SysTick_Config(SystemCoreClock/1000000);///1000 h毫秒

	for (i=0; i<us; i++) {
		// 当计数器的值减小到 0 的时候, CRTL 寄存器的位 16 会置 1
		while ( !((SysTick->CTRL)&(1<<16)) );
	}
	 // 关闭 SysTick 定时器
	 SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
 }

这个就是delay的延时函数,使用SysTick延时

#define Delay_ms(ms)  	CPU_TS_Tmr_Delay_MS(ms)
#define Delay_us(us)  	CPU_TS_Tmr_Delay_US(us)
#define Delay_s(s)  	CPU_TS_Tmr_Delay_S(s)

void	CPU_TS_Tmr_Delay_US(uint32_t us);
#define CPU_TS_Tmr_Delay_MS(ms)     CPU_TS_Tmr_Delay_US(ms*1000)
#define CPU_TS_Tmr_Delay_S(s)       CPU_TS_Tmr_Delay_MS(s*1000)

stm32f10x_it.c

#define TASK_DELAY_NUM  2       //总任务个数,可以自己根据实际情况修改
#define TASK_DELAY_0    100			//任务0延时 100*10毫秒
#define TASK_DELAY_1    10     		//任务1延时 10*10毫秒后执行:

uint32_t Task_Delay_Group[TASK_DELAY_NUM];  //任务数组,用来计时、并判断是否执行对应任务


// - 标志置 1表示完成读取,在主循环处理数据
// - 标志置 0表示未完成读取
// - 标志置-1表示读取错误
int read_LED_finish;


void SysTick_Handler(void)
{
    //让任务计数,中断一次任务组分别加一,等到判断与设定的任务时间一致时进入任务
    int i;
    for(i=0; i<TASK_DELAY_NUM; i++)
    {
        Task_Delay_Group[i] ++;                   //任务计时,时间到后执行
    }

    /* 处理任务0,判断 */
    if(Task_Delay_Group[0] >= TASK_DELAY_0)     //判断是否执行任务0
    {
        Task_Delay_Group[0] = 0;                  //置0重新计时
			
				read_LED_finish=1;

    }
    /* 处理任务1 */
    if(Task_Delay_Group[1] >= TASK_DELAY_1)     //判断是否执行任务1
    {
        //任务1代码
    }
}

main.c

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "stm32f10x.h"
#include "led.h"
#include "sysTick.h"

extern int read_LED_finish;

int main()
{
		/********************************************************************************
    *    Delay_init();				  //本实验使用的是SysTick时钟
    *    CPU_TS_TmrInit();      //已经使能宏,不需要初始化
    *    uart1_init(115200);	 	//串口初始化为115200,需要在usart.h中使能
    *    uart3_init(115200);	 	//串口初始化为115200
		********************************************************************************/
    
    /* 初始化 */
    LED_GPIO_Config();
		SysTick_Init();
    while(1)
    {
				//1.如果中断时间到达,设置中断
        if(read_LED_finish)
        {
            GPIOC->ODR^=GPIO_Pin_13;		//反转电平
						read_LED_finish=0;		//清除标志
        }
//		//2.通过SysTick的延时函数
//		PCout(13)=0;
//      Delay_s(1);		//已经在delay.h中初始化
//		PCout(13)=1;
//      Delay_s(1);
    }
}

主函数中初始化了 LED 和 SysTick,然后在一个 while 循环中以判断SysTick的中断函数标志read_LED_finish让 LED 闪烁。
另外一种更简洁的定时编程
使用SysTick的延时函数。我敢肯定这样的写法,初学者肯定会更喜欢,因为它直接,套路浅。

3. 编译

1.c语法错误

五、stm32-SysTick(系统定时器)_第8张图片五、stm32-SysTick(系统定时器)_第9张图片

4.选择烧录工具并配置MDK

本文选择的是ST_Link烧录工具
在这里插入图片描述
在这里插入图片描述
如果没有ID号看博客:ST-Link V2烧录问题(已解决)
在这里插入图片描述
在这里插入图片描述

5. 成品

工程链接

链接:https://pan.baidu.com/s/1pRNl-S8fGg0nK88D238yAg 提取码:0000

你可能感兴趣的:(#,stm32,单片机,arm)