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


    一、固件库模板

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

    二、准备资料

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

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

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

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

    三、 SYSTick简介

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

    1. SysTick寄存器介绍

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

    寄存器名称寄存器描述
    CTRLSysTick控制及状态寄存器
    LOADSysTick重装载数值寄存器
    VAlSysTick当前数值寄存器
    CALIBSysTick校准数值寄存器

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    四、 SysTick定时实验

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

    1.硬件设计

    stm32f103c8t6自带PC13的LED

    2.软件设计

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

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

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

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

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

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

    在这里插入图片描述
    在这里插入图片描述

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

    在这里插入图片描述

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    用固件库编程的时候我们只需要调用库函数 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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    函数首先先判断形参 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 个可编程优先级,数值越小,优先级越高。如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。

    在这里插入图片描述

    如果我同时使用了 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;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这个就是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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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代码
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

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

    3. 编译

    1.c语法错误

    在这里插入图片描述在这里插入图片描述

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

    本文选择的是ST_Link烧录工具
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    5. 成品

    在这里插入图片描述

    工程链接

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

  • 相关阅读:
    mellanox在vmware中的切割
    贪心算法(2)--衍生问题
    上海亚商投顾:三大指数小幅上涨 HBM概念股全天强势
    实例分割Yolact边缘端部署 (四) 利用预训练模型快速标注
    【BSP开发学习5】GPIO子系统
    代码审计基础php_bugs
    恭喜马斯克、纳德拉当选美国工程院院士,张宏江、方岱宁入选外籍院士
    深度学习系列52:多目标跟踪
    二十二、【形状工具组】
    JNI入门
  • 原文地址:https://blog.csdn.net/weixin_55999942/article/details/126301697