GD32F450(梁山派)的精确延迟delay函数

手上有一个嘉立创的梁山派开发板,最近打算用做一个小东西,需要用到iic协议,而如果用软件i2c的话,精确延迟就免不了。下面记录一下我的开发过程。

下面的代码参考了正点原子的精确延迟的函数

首先这里用的是systick的库函数,systick是芯片自带的时钟,与timer定时器不一样,更容易移植,而且不产生中断,不会导致程序跑飞,缺点是属于堵塞延迟,长时间使用会占用系统资源,不过用于us级别的延迟正合适,使用完关闭就可以。

下面是systick的官方说明:
/*SysTick 定时器
1、CTRL该寄存器中有四个位有效,分别是:

第16位: COUNTFLAG,Systick 计数结束标志,如果在上次读取本寄存器后, SysTick 已经计到
了 0,则该位置 1,如果读取该位,该位将自动清零。用在查询方式上,确定计时结束,并开始新的计数。

第2位: 时钟源选择位,=0 时 AHB/8,=1 时 处理器时钟AHB

第1位: =1时,SysTick 倒数计数到 0 时产生 SysTick 异常请求;=0时,数到 0 时无动作,即中断使能位。(也可以通过读取 COUNTFLAG 标志位来确定计数器是否递减到0。)

第0位: SysTick 定时器的使能位,使用SysTick开始计时时需要将它置1

2、LOAD SysTick 重装载数值寄存器
Systick是一个递减的定时器,当定时器递减至0时,重装载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重装载寄存器是个24位的寄存器,最大计数 0xFFFFFF。

3、VAL SysTick 当前数值寄存器
当前数值寄存器也是个24位的寄存器,读取时返回当前倒计数的值。写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志。

4、CALIB SysTick 校准数值寄存器*/

使用systick前需要先引入必要的头文件

#include "gd32f4xx_misc.h"

然后进行必要的初始化。

void delay_init(unsigned char SYSCLK){  //SYSCLK为systick的时钟
    systick_clksource_set(SYSTICK_CLKSOURCE_HCLK_DIV8);  //设置systick的时钟源为AHB/8,梁山派的AHB = __SYSTEM_CLOCK_ = 200mhz,也就是systick为200/8=25hmz
    SysTick->CTRL&=0xfffffffb;  // 系统时钟控制及状态寄存器,这个fffffffb换算成二进制代表bit2被置零,时钟源=AHB( HCLK)/8
    fac_us = SYSCLK;
    //fac_ms =(unsigned short int)fac_us*1000;
}


上面的SYSCLK的值要与AHB的频率的八分频结果一致才能精确到1us,AHB的频率我查了很久,发现它与系统时钟是一致的,而系统时钟的值__SYSTEM_CLOCK_可以在system_gd32f4xx.c里面找到。我这边是200mhz,所以使用的时候就是200/8=25   :delay_init(25)。

也就是说,systick在200mhz的主频下,systick的频率为25mhz,所以相对的,它每计数25次,就刚好是一微秒。

接下来进行us延迟的编写。

void delay_us(unsigned int nus)
{        
    unsigned int temp;             
    SysTick->LOAD=nus*fac_us; //时间加载               
    SysTick->VAL=0x00;        //清空计数器
    SysTick->CTRL=0x01 ;      //开始倒数      
    do
    {
        temp=SysTick->CTRL;
    }
    while(temp&0x01&&!(temp&(1<<16)));//等待时间到达   
    SysTick->CTRL=0x00;       //关闭计数器
    SysTick->VAL =0X00;       //清空计数器     

}

这段代码比较难理解的是while(temp&0x01&&!(temp&(1<<16)))这一段,其实就是将CTRL的值传给temp,然后检查第一位和第十六位,第一位表示计数正在进行,第十六位就是计数完成。如果计数没有完成,就反复检查CTRL的值,直到计数完成为止。

然后就是ms延迟的编写。

void delay_ms(unsigned int nms)
{                     
    unsigned int i;
    for(i=0;i<nms;i++) delay_us(1000);
}   

之所以ms延迟要用for而不像us使用更精确的systick,是因为systick计数器是有上限的,在200m的速率下,671ms就已经满了,这个时候再装入更高的值是没有意义的,为了避免使用毫秒函数的时候装超过671的值而导致计数不准确,就直接用for循环来完成。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值