GD32F30x 定时器实现高精度 Delay 延时函数

示例工程代码库地址如下:

源文件在 Project -> test -> bsp -> delay 目录下
参考文档在 Project -> test -> doc 目录下

1. 源代码

delay.h

/*
 * @Descripttion: delay function
 * @Author: Jerry
 * @Date: 2021-12-13 17:05:30
 * @LastEditTime: 2022-01-13 14:23:09
 * 
 * Copyright © 2021 Jerry, All Rights Reserved
 */
#ifndef _DELAY_H_
#define _DELAY_H_

#include "stdint.h"

void delay_init(void);

/**
 * @brief delay a time in microsecond
 * @param us [in] us
 */
void delay_us(uint32_t us);

/**
 * @brief delay a time in milliseconds
 * @param ms [in] ms
 */
void delay_ms(uint32_t ms);

#endif

delay.c

/*
 * @Descripttion: delay function
 * @Author: Jerry
 * @Date: 2021-12-13 17:05:25
 * @LastEditTime: 2022-01-13 14:37:01
 * 
 * Copyright © 2021 Jerry, All Rights Reserved
 */
#include "delay.h"
#include "gd32f30x_timer.h"

#define DELAY_TIMER_RCU_PERIPH  RCU_TIMER6
#define DELAY_TIMER_PERIPH      TIMER6
#define DELAY_TIMER_NVIC_IRQ    TIMER6_IRQn
#define DELAY_TIMER_IRQ_HANDLER TIMER6_IRQHandler

//int_freq = CLK / ((prescaler + 1) * period)
//int_freq = 1us
#define DELAY_TIMER_PRESCALER   1
#define DELAY_TIMER_PERIOD      60

volatile static uint32_t gs_count = 0;

/*******************************************************************/
/***       			    Local Function                           ***/
/*******************************************************************/
void delay_timer_init(void)
{
    timer_parameter_struct timer_parameter;

    rcu_periph_clock_enable(DELAY_TIMER_RCU_PERIPH);
    //预分频
    timer_parameter.prescaler = DELAY_TIMER_PRESCALER;
    //对齐模式
    timer_parameter.alignedmode = TIMER_COUNTER_EDGE;
    //定时器增长方向
    timer_parameter.counterdirection = TIMER_COUNTER_UP;
    //定时器自动加载值
    timer_parameter.period = DELAY_TIMER_PERIOD;
    //时钟分频值
    timer_parameter.clockdivision = TIMER_CKDIV_DIV1;
    
    timer_init(DELAY_TIMER_PERIPH, &timer_parameter);
    timer_interrupt_enable(DELAY_TIMER_PERIPH, TIMER_INT_UP);
    nvic_irq_enable(DELAY_TIMER_NVIC_IRQ, 0, 0);
    timer_enable(DELAY_TIMER_PERIPH);
}

void DELAY_TIMER_IRQ_HANDLER(void)
{
    timer_interrupt_flag_clear(DELAY_TIMER_PERIPH, TIMER_INT_UP);

    gs_count++;
}

/******************************************************************/
/***                    Exported Functions                      ***/
/******************************************************************/
void delay_init(void)
{
    delay_timer_init();
}

/**
 * @brief delay a time in microsecond
 * @param us [in] us
 */
void delay_us(uint32_t us)
{
    uint32_t start_count = gs_count;
    while((gs_count - start_count) < us);
}

/**
 * @brief delay a time in milliseconds
 * @param ms [in] ms
 */
void delay_ms(uint32_t ms)
{
    delay_us(ms * 1000);
}

2. 定时器使用说明

博主使用的是基本定时器 6,延时只需要用到基本定时功能

2.1 确定定时器主时钟

GD32F303xx 数据手册定时器种类描述如下:
在这里插入图片描述
GD32F30x_yoinghushouce.pdf 文档描述如下:
在这里插入图片描述
在这里插入图片描述
此处最难理解的是上图第二步,简化如下:

TIMER_CLK = if(APB1 prescale = 1) x 1 else x 2

计算如下:

CK_AHB = 120MHz
CK_APB1 = 60MHz
APB1 prescale = CK_AHB / CK_APB1 = 2

if(APB1 prescale == 1){
	TIMER_CLK = CK_APB1 x 1;
}else{
	TIMER_CLK = CK_APB1 x 2;
}

所以 TIMER_CLK = 120MHz

2.2 确定预分频和计数值

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

根据以上说明,得知定时器中断频率计算公式如下:

int_freq = TIMER_CLK / (prescaler + 1) / period

博主需要的定时频率为 1us 一次,即 1秒钟 1MHz 次定时中断,带入上式公式得

1MHz = 120MHz / (prescaler + 1) / period

得到很多种组合值,博主随意取其中一种如下:

prescaler = 1;
period = 60;

2.3 参数 clockdivision 的含义

首先查看 timer_init 函数的实现如下:

/*!
    \brief      initialize TIMER counter
    \param[in]  timer_periph: TIMERx(x=0..13)
    \param[in]  initpara: init parameter struct
                  prescaler: prescaler value of the counter clock, 0~65535
                  alignedmode: TIMER_COUNTER_EDGE, TIMER_COUNTER_CENTER_DOWN, TIMER_COUNTER_CENTER_UP, TIMER_COUNTER_CENTER_BOTH
                  counterdirection: TIMER_COUNTER_UP, TIMER_COUNTER_DOWN
                  period: counter auto reload value, 0~65535
                  clockdivision: TIMER_CKDIV_DIV1, TIMER_CKDIV_DIV2, TIMER_CKDIV_DIV4
                  repetitioncounter: counter repetition value, 0~255
    \param[out] none
    \retval     none
*/
void timer_init(uint32_t timer_periph, timer_parameter_struct* initpara)
{
    /* configure the counter prescaler value */
    TIMER_PSC(timer_periph) = (uint16_t)initpara->prescaler;

    /* configure the counter direction and aligned mode */
    if((TIMER0 == timer_periph) || (TIMER1 == timer_periph) || (TIMER2 == timer_periph)
        || (TIMER3 == timer_periph) || (TIMER4 == timer_periph) || (TIMER7 == timer_periph)){
        TIMER_CTL0(timer_periph) &= ~(uint32_t)(TIMER_CTL0_DIR|TIMER_CTL0_CAM);
        TIMER_CTL0(timer_periph) |= (uint32_t)initpara->alignedmode;
        TIMER_CTL0(timer_periph) |= (uint32_t)initpara->counterdirection;
    }

    /* configure the autoreload value */
    TIMER_CAR(timer_periph) = (uint32_t)initpara->period;

    if((TIMER5 != timer_periph) && (TIMER6 != timer_periph)){
        /* reset the CKDIV bit */
        TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_CKDIV;
        TIMER_CTL0(timer_periph) |= (uint32_t)initpara->clockdivision;
    }

    if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){
        /* configure the repetition counter value */
        TIMER_CREP(timer_periph) = (uint32_t)initpara->repetitioncounter;
    }

    /* generate an update event */
    TIMER_SWEVG(timer_periph) |= (uint32_t)TIMER_SWEVG_UPG;
}

由此可知,TIMER5 和 TIMER6 根本没有用到该参数,设置其值无意义。
而其他定时器对该配置参数的描述如下:
在这里插入图片描述
即是跟定时器采样时钟有关的一个参数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值