STM32官方固件库开发:延时函数的讲解

        最近没啥事,就研究了一下在使用ST官方固件库的延时实现,然后在上网搜寻代码研究时,发现了一些坑,所以写一篇博文来和大家分享延时函数的编写。

程序放在Q群里:659512171

一,新建工程:

        与之前使用STM32CubeMX不一样,这里不使用工具生成基础配置文件,而是使用工程模板的方式创建,工程模板的创建教程很多,大家根据自己习惯使用就好,我的模板放在Q群和网盘里,下面是网盘链接                提取码:ao0d链接icon-default.png?t=N7T8https://pan.baidu.com/s/1o1m3jqlX7BSICDTJbAmDFQ?pwd=ao0d        工程模板的使用也有教程,以后有时间会专门写一篇,这里就简单说说

        将文件下载后解压出来,找到Sample文件夹,打开

 打开USER文件夹,把Keil工程文件改名为需要的名字(为了方便区分,Sample的文件夹名字也可以改一下)

然后双击打开工程,下图是各文件夹的基本功能

另外,如果想要记录一些说明,可以新建一个名为Doc(或其他,看个人习惯)的文件夹,里面放入TXT文档用于说明

我这里使用的是 F103C8T6 的MCU,属于中容量产品,所以只保留了 “startup_stm32f10x_md.s”这个文件, 具体保留哪个根据自己情况确定。

二,添加文件:

新建一个 gpio.c 文件,保存在工程中Core文件夹下的Src中,再新建一个 gpio.h 文件,保存在Core中的Inc中(方便管理,根据个人习惯就行了,但要求文件夹路径包含在编译器中)

三,编写程序:

        在gpio.h中添加如下代码:

#ifndef __GPIO_H_
#define __GPIO_H_

#include "stm32f10x.h"

void MY_GPIO_Init(void);

#endif

接着在gpio.c中添加代码用于初始化GPIO口:

#include  "gpio.h"

void MY_GPIO_Init(void){
  GPIO_InitTypeDef GPIO_InitStructure;//定义结构体

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能GPIOC时钟

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOC,&GPIO_InitStructure);//初始化GPIOC13为输出

  GPIO_SetBits(GPIOC,GPIO_Pin_13);//操作IO口,输出高电平(低电平:GPIO_ResetBits();)
}

然后在 main.c 中编写延时函数(也可以新建一个delay.c和delay.h文件)

void delay_us(u16 time)
{    
   u16 i=0;  
   while(time--)
   {
      i=10;  //根据单片机速度定义
      while(i--) ;    
   }
}

void delay_ms(u16 time)
{    
   u16 i=0;  
   while(time--)
   {
      i=12000;  //根据单片机速度定义
      while(i--) ;    
   }
}

这种延时属于最简单的,但同时也是误差最大的,所以一般不会使用

void delay_us(u32 nus)
{
  u32 temp;
  SysTick->LOAD = SystemCoreClock/8000000*nus;
  SysTick->VAL=0X00;//清空计数器
  SysTick->CTRL=0X01;//开启计数器
  do {
    temp=SysTick->CTRL;//读取当前控制位
  } while((temp&0x01)&&(!(temp&(1<<16))));//确定控制位bit0为1(即有效)同时等待控制位bit16为1
  SysTick->CTRL=0x00; //关闭计数器
  SysTick->VAL =0X00; //清空计数器
}

void delay_ms(u16 nms)
{
  u32 temp;
  SysTick->LOAD = SystemCoreClock/8000*nms;
  SysTick->VAL=0X00;//清空计数器
  SysTick->CTRL=0X01;//开启计数器
  do {
    temp=SysTick->CTRL;//读取当前控制位
  } while((temp&0x01)&&(!(temp&(1<<16))));//确定控制位bit0为1(即有效)同时等待控制位bit16为1
  SysTick->CTRL=0x00; //关闭计数器
  SysTick->VAL =0X00; //清空计数器
}

以上程序是我根据原子哥的程序写的,直接复制即可。下面来讲解一下这个查询法延时

        在这个程序中,用到了 SystemCoreClock 和 SysTick 这两个东西,其中SystemCoreClock是固件库中的一个宏定义,与单片机主频相等,所以不管是什么单片机都可以直接使用此程序,SysTick就是STM32中的滴答定时器,是一个倒计时的定时器,当数值减到0时重装为自动重装值寄存器(LOAD)中的数值,在用HAL库或者RTOS时一般作为时基信号,SysTick的频率为主频的 1/8,所以这里用主频除以8(上面程序是将算式化简了)即为SysTick的频率,然后根据延时的单位长度,确定再次除以多少,比如我要延时 1ms,SysTick的频率为 9M(即9000000),那么 9000000 * 1/1000 就是每ms内的计数次数,然后再以ms为单位乘以要延时的时间,最后将上述式子整合就得到了 SystemCoreClock/8000*nms 这么一个式子(us类同)。

         然后在主程序中添加如下程序:

/*Main Code*/
int main(void){
  /*Init*/
  MY_GPIO_Init();
  while(1){
    /*Code*/
    GPIO_ResetBits(GPIOC,GPIO_Pin_13);
    delay_ms(1000);
    GPIO_SetBits(GPIOC,GPIO_Pin_13);
    delay_ms(1000);
  }
}

最后完整的main.c: 

/*
************************************************
*工程名称:延时示例
*作者:岁心
*CSDN主页:https://blog.csdn.net/m0_73677866
************************************************
*/

/*Include*/
#include "main.h"
#include "gpio.h"
/*Function Code*/
void delay_us(u32 nus)
{
  u32 temp;
  SysTick->LOAD = SystemCoreClock/8000000*nus;
  SysTick->VAL=0X00;//清空计数器
  SysTick->CTRL=0X01;//开启计数器
  do {
    temp=SysTick->CTRL;//读取当前控制位
  } while((temp&0x01)&&(!(temp&(1<<16))));//确定控制位bit0为1(即有效)同时等待控制位bit16为1
  SysTick->CTRL=0x00; //关闭计数器
  SysTick->VAL =0X00; //清空计数器
}

void delay_ms(u16 nms)
{
  u32 temp;
  SysTick->LOAD = SystemCoreClock/8000*nms;
  SysTick->VAL=0X00;//清空计数器
  SysTick->CTRL=0X01;//开启计数器
  do {
    temp=SysTick->CTRL;//读取当前控制位
  } while((temp&0x01)&&(!(temp&(1<<16))));//确定控制位bit0为1(即有效)同时等待控制位bit16为1
  SysTick->CTRL=0x00; //关闭计数器
  SysTick->VAL =0X00; //清空计数器
}

/*Main Code*/
int main(void){
  /*Init*/
  MY_GPIO_Init();
  while(1){
    /*Code*/
    GPIO_ResetBits(GPIOC,GPIO_Pin_13);
    delay_ms(1000);
    GPIO_SetBits(GPIOC,GPIO_Pin_13);
    delay_ms(1000);
  }
}

 编译、下载、运行,可以看到,连接在PC13接口上的LED按照1s的频率一亮一灭,本篇实验结束

总结: 

        对于固件库开发,由于没有第三方库的延时,所以自己拥有一个好用精准的延时函数还是很重要的,我一般习惯于使用查询法的延时函数,大家根据实际情况选择即可。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值