示例工程代码库地址如下:
源文件在 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 根本没有用到该参数,设置其值无意义。
而其他定时器对该配置参数的描述如下:
即是跟定时器采样时钟有关的一个参数。