STM32手册材料下载:STM32材料Github链接;STM32材料Gitee链接;

留意:Github是国外的,要翻墙,Gitee是国内的,无需翻墙。

目录

滴答定时器的功用

模块化思维

什么叫做模块化

怎么运用keil完结模块化

第一步,预备工程文件

第二步,树立delay.c和delay.h文件

第三步,将sys参加工程

第四步,参加途径

代码

.c文件

.h文件

HAL库

规范库

往后怎么将delay模块参加其他工程

main.c调用

初始化

完结软件PWM

Delay_us()试验

Delay_ms()试验

代码讲解

Delay_Init()

代码

滴答定时器寄存器介绍

Delay_Init()函数介绍

Delay_us()函数介绍

Delay_ms()函数介绍

终究再次强调!!!


野火和正点原子的滴答定时器部分的延时函数我都看了,感觉对新手都及其不友好。所以我运用海创电子(教的是规范库的内容,可是真的真的讲得棒!)的滴答定时器部分代码作为讲解。

本次试验运用SysTick精准延时,完结软件PWM。需求预备一个LED灯(这个或许不太直观),或许一个示波器(这个精准一些)。

滴答定时器的功用

(1)滴答定时器可用于操作系统产生时基,维持操作系统的心跳。一般操作系统都需求一个时基,进行使命的调度、同步等功用完结。(这个了解即可,不知道没关系)

(2)滴答定时器常用于计数。比方进行奇妙、毫秒延时。(这个才是要点)

留意:

(1)这一篇不需求运用到STM32CubeMX,咱们是直接对寄存器进行操作!

(2)想了解底层完结的能够看具体阐明,只想要延时函数的能够直接仿制代码就走。

(3)由于咱们都是裸机开发,所以关于操作系统部分的内容没有。需求操作系统部分的延时程序,可自行去野火或许正点原子的官方例程中仿制。

(4)规范库和HAL库代码根本相同,就只需那个需求分频的函数部分需求更改,以及头文件需求更改。

(5)在裸机开发中,一般都运用滴答定时器作为精准延时函数。所以我就只讲精准延时部分。

模块化思维

什么叫做模块化

由于或许有人是依据我的博客来学习的,没有模块化的思维,甚至不知道什么叫做模块化。

(1)什么是模块化?

比方咱们下载一个工程文件,里边会有许多.c文件(这些.c放在对应的文件夹下面了),只要一个main.c文件,如下。像key文件夹,lcd文件夹下面存储的这些.c文件便是模块。

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(2)这样模块化了有什么用呢?

咱们都是从学习C语言开端的。像咱们运用printf打印字符,scanf获取键盘上的字符。printf和scanf函数便是存放在stdio.h这个头文件下。

当咱们有了stdio这个模块之后,咱们就不需求从头写printf和scanf函数的完结了。直接引用文件,就能够运用了。

现在咱们所说的模块化亦是如此。假如咱们将延时函数进行模块化了,之后咱们需求在其他工程运用延时函数,咱们只需求仿制delay这个模块化到对应工程下面即可。

怎么运用keil完结模块化

许多人或许没怎么运用过keil这个编译器,不知道怎么运用keil创建一个模块,现在我教学一下。

第一步,预备工程文件

假如是运用HAL库首要运用STM32CubeMX生成一个工程文件。

假如是规范库,你自己预备好一个点灯工程。第一步就不必看了

(1)装备RCC

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(2)主频装备为72MHZ

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(3)装备SYS

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(4)装备GPIO

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(5) 生成文件

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

第二步,树立delay.c和delay.h文件

(1) 在工程目录下树立一个文件夹名为User,然后再在这个文件夹里边树立一个delay文件夹。

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(2)在delay文件夹下面树立两个txt文件

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(3)更改两个文件名为delay.c和delay.h。

留意:记得要勾选文件扩展名!!!不会的百度!!!

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

第三步,将sys参加工程

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

第四步,参加途径

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(1)再点击两个OK,现在delay这个模块就参加工程了。

(2)然后双击delay.c就能够打开delay.c这个文件了。

(3)在delay.c中参加#include “delay.h”,编译之后,delay.h也参加了工程。

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

代码

.c文件

由于咱们是直接对寄存器进行操作,所以无论你是运用的HAL库仍是规范库,都不影响!!!

直接将下面代码仿制到工程里边即可。

#include  "delay.h"
uint8_t fac_us=0;
uint16_t fac_ms=0;
void Delay_Init()
{
	//只能够挑选不分频或许8分频,这儿挑选系统时钟8分频,终究频率为9MHZ
	SysTick->CTRL &= ~(1<<2);
	//SystemCoreClock为72000000,终究fac_us为9,也便是记载轰动9次。由于频率为9MHZ所认为1us
	fac_us  = SystemCoreClock  / 8000000;  
	fac_ms  = fac_us*1000;  //1000us=1ms
}
/*
	CTRL     SysTick操控及状况寄存器
	LOAD     SysTick重装载数值寄存器
	VAL      SysTick当时数值寄存器
*/
void Delay_us(uint32_t nus)
{
	uint32_t temp;
	SysTick->LOAD  =nus*fac_us;   //设置加载的值,比方1us就要计数9次。nus传入1,CALIB=1*9=9,终究便是1us
	SysTick->VAL   =0x00;         //清空计数器中的值,LOAD里的值不是写入后就会加载,而是在systick使能且VAL值为0时才加载
	SysTick->CTRL  |=SysTick_CTRL_ENABLE_Msk;  //使能时钟,开端计时
	do
	{
		temp=SysTick->CTRL;   //查询是否计数完结
	}while((temp&0x01)&&!(temp&(1<<16)));   //先判别定时器是否在运行,再判别是否计数完结
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//封闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}
void Delay_ms(uint32_t nms)
{
	uint32_t temp;
	SysTick->LOAD  =nms*fac_ms;   //设置加载的值,比方1us就要计数9次。nus传入1,CALIB=1*9=9,终究便是1us
	SysTick->VAL   =0x00;         //清空计数器中的值,LOAD里的值不是写入后就会加载,而是在systick使能且VAL值为0时才加载
	SysTick->CTRL  |=SysTick_CTRL_ENABLE_Msk;  //使能时钟,开端计时
	do
	{
		temp=SysTick->CTRL;   //查询是否计数完结
	}while((temp&0x01)&&!(temp&(1<<16)));   //先判别定时器是否在运行,再判别是否计数完结
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//封闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}

.h文件

HAL库

由于HAL库和规范库的头文件姓名不相同,所以这儿仍是有区分的

#ifndef     __delay_H
#define     __delay_H
#include "stm32f1xx.h"  // 相当于51单片机中的  #include <reg51.h>
void Delay_Init(void);
void Delay_us(uint32_t nus);
void Delay_ms(uint32_t nms);
#endif

规范库

#ifndef     __delay_H
#define     __delay_H
#include "stm32f10x.h"   // 相当于51单片机中的  #include <reg51.h>
void Delay_Init(void);
void Delay_us(uint32_t nus);
void Delay_ms(uint32_t nms);
#endif

往后怎么将delay模块参加其他工程

咱们只需求仿制delay这个文件夹,到其他工程中,然后按照第三步,将sys参加工程和第四步,参加途径即可。

main.c调用

初始化

(1)首要咱们需求在main.c最上面一行写上#include “delay.h”

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(2)然后在main函数里边需求调用Delay_Init();

留意:STM32Cube MX主动生成的 SystemClock_Config();需求放在Delay_Init();之前

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

完结软件PWM

只讲死循环部分,非死循环部分需求调整的如上。

Delay_us()试验

  while (1)
  {
    /* USER CODE END WHILE */
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
		Delay_us(1000);
    /* USER CODE BEGIN 3 */
  }

终究生成了一个周期为2ms,占空比为50%的PWM

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

Delay_ms()试验

  while (1)
  {
    /* USER CODE END WHILE */
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
		Delay_ms(10);
    /* USER CODE BEGIN 3 */
  }

终究生成了一个周期为20ms,占空比为50%的PWM.

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

代码讲解

这一部分给想跟深刻了解底层的人学习的,假如仅仅想用延时函数的人不需求看,对后续操作不影响。

Delay_Init()

代码

这个函数便是对滴答定时器进行一个8分频。然后设置两个变量。

uint8_t fac_us=0;
uint16_t fac_ms=0;
void Delay_Init()
{
	//只能够挑选不分频或许8分频,这儿挑选系统时钟8分频,终究频率为9MHZ
	SysTick->CTRL &= ~(1<<2);
	//SystemCoreClock为72000000,终究fac_us为9,也便是记载轰动9次。由于频率为9MHZ所认为1us
	fac_us  = SystemCoreClock  / 8000000;  
	fac_ms  = fac_us*1000;  //1000us=1ms
}

滴答定时器寄存器介绍

由于滴答定时器归于CM3内核相关的内容,所以咱们需求检查CM3的手册。在我上面的链接里边,有许多STM32F103的材料,能够看野火,正点原子的SysTick部分的材料,或许直接看CM3威望攻略。

首要解说Sys Tick定时器相关的寄存器

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(1)CTRL操控及状况寄存器

CTRL只要0,1,2,16这四个位有用,其他的都没有运用。

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(2)LOAD重装载数值寄存器

1,咱们知道,Sys Tick滴答定时器是一个24位定时器,所以他的重装载数值寄存器是一个24bit的寄存器。

2,重装载或许有些人无法了解。由于Sys Tick滴答定时器是一个向下计数的寄存器。比方滴答定时器现在的值为100,那么他从100一直自减到0的时分,LOAD会主动将100存入滴答定时器。

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(3)VAL当时数值寄存器

这个负责记载当时滴答定时器的值。参加这个值为0了,那么重装载寄存器里边的值将会主动存入VAL。(留意,重装载寄存器中的值并没有减少!相当于将重装载寄存器中的值仿制,然后张贴给VAL)

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

(4)CALIB 校准数值寄存器

这个不知道什么用,正点原子手册里边没讲解,野火的手册里边说他们也是懵逼的。所以我也不明白,可是我猜测是进行校准滴答定时器的值,假如里边的值出问题了会又一些操作进行校正。用不到,不必纠结

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

Delay_Init()函数介绍

下面为Delay_Init()这个函数的全部部分。uint8_t fac_us=0;和uint16_t fac_ms=0;也要包含!

uint8_t fac_us=0;
uint16_t fac_ms=0;
void Delay_Init()
{
	//只能够挑选不分频或许8分频,这儿挑选系统时钟8分频,终究频率为9MHZ
	SysTick->CTRL &= ~(1<<2);
	//SystemCoreClock为72000000,终究fac_us为9,也便是记载轰动9次。由于频率为9MHZ所认为1us
	fac_us  = SystemCoreClock  / 8000000;  
	fac_ms  = fac_us*1000;  //1000us=1ms
}

(1)咱们看第一行代码,很简单,便是对滴答定时器进行一次八分频。CTRL是操控状况寄存器,当咱们对他的bit2进行操作的时分,便是在挑选SysTick的时钟源。这也是为什么我上面强调SystemClock_Config();需求放在Delay_Init();之前的原因了。

(2)由于咱们CubeMX生成的滴答定时器是72MHZ,没有进行8分频。(不过你能够在CubeMX设置让他8分频)假如SystemClock_Config() 放在Delay_Init() 前面,那么滴答定时器本来在Delay_Init() 进行了八分频,现在你又用SystemClock_Config() 把滴答定时器变成没有分频。会导致延时出现问题。

(3)这个时分有人会问了,为什么滴答定时器要进行八分频呢?能够不八分频吗?

(4)答案显然是能够的,可是假如看过我之前的博客就知道,假如频率越高,功耗越大,响应速度也快。上面说了,滴答定时器就延时给操作系统供给时基的,咱们不必操作系统,那么不需求考虑响应速度的问题。

(5)那么现在就考虑延时的问题,先说结论,假如频率越高,滴答定时器最大延时越短。什么意思呢?

(6)咱们先又一个概念,1MHZ表明1S跳变1,000,000次。跳变一次,滴答定时器的VAL( 当时数值寄存器)中的数据就会减1。滴答定时器是24位定时器,2^24=16,777,215。能够跳变16,777,215次

(7)假设不分频,72MHZ,那么现在能够计数,也便是最大延时0.233S

(8)可是假如是进行了八分频,,最大延时1.864S所以,为了更长的延时时刻,咱们挑选了八分频

(9)fac_us 与fac_ms 作用又是什么呢?

现在咱们的滴答定时器是9MHZ(72MHZ/8=9MHZ),所以说,当VAL( 当时数值寄存器)每减一,那么就表明过了1/9,000,000S。那么1us(1us=1/1,000,000S)便是VAL的值减9次。1ms便是VAL减9,000次。

(10)假如咱们硬是要72MHZ的滴答定时器怎么办呢?

fac_us = SystemCoreClock / 8000000; ——>fac_us = SystemCoreClock / 1000000;即可。

STM32滴答定时器SysTick精准延时,兼容HAL库和标准库

Delay_us()函数介绍

void Delay_us(uint32_t nus)
{
	uint32_t temp;
	SysTick->LOAD  =nus*fac_us;   //设置加载的值,比方1us就要计数9次。nus传入1,CALIB=1*9=9,终究便是1us
	SysTick->VAL   =0x00;         //清空计数器中的值,LOAD里的值不是写入后就会加载,而是在systick使能且VAL值为0时才加载
	SysTick->CTRL  |=SysTick_CTRL_ENABLE_Msk;  //使能时钟,开端计时
	do
	{
		temp=SysTick->CTRL;   //查询是否计数完结
	}while((temp&0x01)&&!(temp&(1<<16)));   //先判别定时器是否在运行,再判别是否计数完结
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//封闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}

(1)咱们知道了fac_us能够表明1us,那么咱们传入参数nus*fac_us就能够表明为延时了多少us。现在将要延时的时刻存入LOAD(主动重装载寄存器),然后清空VAL( 当时数值寄存器)的值,在咱们敞开滴答定时器的瞬间,LOAD会将数据存入VAL。由于当VAL寄存器为0的时分,LOAD会将值传给VAL。

(2)现在咱们进行轮询法,不断查询CTRL(操控及状况寄存器)的bit16,由于CTRL的bit16在VAL为0的时分会为1。

(3)这个时分有人会问了呀,咱们一开端就置零了VAL,那么现在CTRL的bit16不就现已是1了吗?所以咱们要完结清空CTRL的bit16位。

(4)这个时分咱们就需求自行看手册阐明晰。假如在上次读取本寄存器(也便是CTRL)后,SysTick 现已计到了 0,则该位为 1。

所以流程是,读取CTRL寄存器——>LOAD为0——>CTRL的bit16置1。咱们在上面清零LOAD的时分,并没有读取CTRL,当咱们在进行轮询的时分,LOAD的值现已被重装载了。

(5)一直轮询,直到VAL为0,那么现在延时结束。封闭滴答定时器,清空VAL的值即可。

(6)能够看到这儿,阐明你有必定根底。可是或许仍是有一些新手像了解底层,坚持到了这儿,看到SysTick_CTRL_ENABLE_Msk这个东西很奇怪,不知道这个是啥。这个时分咱们需求将鼠标点击到SysTick_CTRL_ENABLE_Msk,按F12能够跳转到他的定义。

咱们看发现SysTick_CTRL_ENABLE_Msk其实便是无符号的数字1。开关Sys Tick依托CTRL的bit0。

#define SysTick_CTRL_ENABLE_Msk            (1UL /*<< SysTick_CTRL_ENABLE_Pos*/)           /*!< SysTick CTRL: ENABLE Mask */

Delay_ms()函数介绍

void Delay_ms(uint32_t nms)
{
	uint32_t temp;
	SysTick->LOAD  =nms*fac_ms;   //设置加载的值,比方1us就要计数9次。nus传入1,CALIB=1*9=9,终究便是1us
	SysTick->VAL   =0x00;         //清空计数器中的值,LOAD里的值不是写入后就会加载,而是在systick使能且VAL值为0时才加载
	SysTick->CTRL  |=SysTick_CTRL_ENABLE_Msk;  //使能时钟,开端计时
	do
	{
		temp=SysTick->CTRL;   //查询是否计数完结
	}while((temp&0x01)&&!(temp&(1<<16)));   //先判别定时器是否在运行,再判别是否计数完结
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//封闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}

Delay_ms()与Delay_us()操作过程相同的,就仅仅把 SysTick->LOAD =nus*fac_us; 改为 SysTick->LOAD =nms*fac_ms; 。

终究再次强调!!!

为了更长的延时时刻,咱们挑选了八分频。可是八分频之后,依旧只能最大延时1.864S!