3 STM32单片机-delay延时驱动
系列文章目录
文章目录
- 系列文章目录
- 前言
- 1 工程目录
- 2 软件编写
- 2.1 main.c
- 2.2 board_config
- 2.2.1 board_config.c
- 2.2.2 board_config.h
- 2.3 delay
- 2.3.1 delay.c
- 2.3.2 delay.h
- 2.4 led_driver
- 2.4.1 led_driver.c
- 2.4.2 led_driver.h
- 3 延时验证
- 总结
前言
在做单片机项目时,延时是经常需要使用到的,在单片机中,有两种定时器,一种是systick定时器,一种是TIM定时器。在这里推荐使用TIM定时器作为系统延时,一方面是systick在RTOS操作系统中默认作为时基了。还有一方面是TIM更加稳定。
在大项目中,TIM2(或其他通用外设定时器)比 SysTick 更适合实现稳定的系统延时,核心原因是:
资源隔离:独立于内核和 RTOS,避免与系统核心功能(如任务调度)冲突;
灵活适配:支持长延时、多场景定时,时钟源选择多样,抗干扰能力强;
可靠兼容:中断机制完善,优先级可控,适配复杂系统的中断协同需求。
基于以上原因,本文使用TIM作为延时函数。
为了将来不更新延时驱动文件,我这里还是封装一下,将来只需要更改板子文件就行了,不需要再次修改延时驱动文件。
1 工程目录
2 软件编写
2.1 main.c
#include "board_config.h"/************************************************systick 定时器驱动实验现象:LED1闪烁。淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click作者:胜磊电子
************************************************//************************************* 宏定义 *******************************************************//*********************************** 局部函数 *******************************************************//*
************************************************************
* 函数名称: main
*
* 函数功能:
*
* 入口参数: 无
*
* 返回参数: 0
*
* 说明:
************************************************************
*/
int main(void)
{// 初始化所有外设BOARD_InitAll();while (1) {// 闪烁LED1LED_Toggle(&BOARD_LED3);DelayMs(500);}
}
2.2 board_config
2.2.1 board_config.c
#include "board_config.h"/************************************************淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click作者:胜磊电子
************************************************/// 定义LED对象
LED_TypeDef BOARD_LED1;
LED_TypeDef BOARD_LED2;
LED_TypeDef BOARD_LED3;// 定义蜂鸣器对象
Beep_TypeDef BOARD_BEEP;void BOARD_InitLEDs(void) {// 初始化LED1 (PB0)LED_Init(&BOARD_LED1, GPIOC, GPIO_Pin_0);// 初始化LED2 (PB1)LED_Init(&BOARD_LED2, GPIOB, GPIO_Pin_1);
}void BOARD_InitBeep(void) {// 初始化蜂鸣器 (PB8)Beep_Init(&BOARD_BEEP, GPIOB, GPIO_Pin_8);
}void BOARD_InitDelay(uint8_t timer) {// 初始化延时模块,可选择TIM2/TIM3等Delay_Init(timer);
}void BOARD_InitAll(void) {// 初始化所有外设,使用TIM2作为延时定时器BOARD_InitLEDs();BOARD_InitBeep();BOARD_InitDelay(1);
}
2.2.2 board_config.h
#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H#include "led_driver.h"
#include "beep_driver.h"
#include "delay.h"/************************************************淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click作者:胜磊电子
************************************************/// 导出LED对象供外部使用
extern LED_TypeDef BOARD_LED1;
extern LED_TypeDef BOARD_LED2;
extern LED_TypeDef BOARD_LED3;// 导出蜂鸣器对象供外部使用
extern Beep_TypeDef BOARD_BEEP;// 板子初始化函数
void BOARD_InitAll(void);
void BOARD_InitLEDs(void);
void BOARD_InitBeep(void);
void BOARD_InitDelay(uint8_t timer);#endif /* BOARD_CONFIG_H */
2.3 delay
2.3.1 delay.c
#include "delay.h"/************************************************淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click作者:胜磊电子
************************************************//** 系统支持的所有定时器配置表 */
const Delay_TimerConfigTypeDef TimerConfigTable[] = {{TIM1, RCC_APB2Periph_TIM1, RCC_APB2Periph_TIM1, RCC_APB2PeriphClockCmd},{TIM2, RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM2, RCC_APB1PeriphClockCmd},{TIM3, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM3, RCC_APB1PeriphClockCmd},{TIM4, RCC_APB1Periph_TIM4, RCC_APB1Periph_TIM4, RCC_APB1PeriphClockCmd},{TIM5, RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM5, RCC_APB1PeriphClockCmd},{TIM6, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM6, RCC_APB1PeriphClockCmd},{TIM7, RCC_APB1Periph_TIM7, RCC_APB1Periph_TIM7, RCC_APB1PeriphClockCmd},{TIM8, RCC_APB2Periph_TIM8, RCC_APB2Periph_TIM8, RCC_APB2PeriphClockCmd},{TIM9, RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM9, RCC_APB2PeriphClockCmd},{TIM10, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM10, RCC_APB2PeriphClockCmd},{TIM11, RCC_APB2Periph_TIM11, RCC_APB2Periph_TIM11, RCC_APB2PeriphClockCmd},{TIM12, RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM12, RCC_APB1PeriphClockCmd},{TIM13, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM13, RCC_APB1PeriphClockCmd},{TIM14, RCC_APB1Periph_TIM14, RCC_APB1Periph_TIM14, RCC_APB1PeriphClockCmd},
};/** 定时器配置表大小 */
const uint8_t TimerConfigTableSize = sizeof(TimerConfigTable) / sizeof(TimerConfigTable[0]);/** 当前使用的定时器索引 */
static uint8_t currentTimerIndex = 0;/*** @brief 初始化定时器,用于延时功能* @param timerIndex: 定时器在配置表中的索引* @retval 无*/
void Delay_Init(uint8_t timerIndex)
{if (timerIndex >= TimerConfigTableSize) {// 索引超出范围,使用默认定时器timerIndex = 0;}currentTimerIndex = timerIndex;const Delay_TimerConfigTypeDef* config = &TimerConfigTable[timerIndex];// 使能定时器时钟config->CLKCmd(config->TIMx_CLK, ENABLE);// 停止定时器TIM_Cmd(config->TIMx, DISABLE);// 配置定时器为向上计数模式TIM_InternalClockConfig(config->TIMx);TIM_CounterModeConfig(config->TIMx, TIM_CounterMode_Up);// 设置 PSC 为 71,使计数器时钟为 1MHz (72MHz / 72 = 1MHz)TIM_PrescalerConfig(config->TIMx, 71, TIM_PSCReloadMode_Immediate);// 清除更新标志位TIM_ClearFlag(config->TIMx, TIM_FLAG_Update);
}/*** @brief 微秒级延时函数* @param us: 延时的微秒数,范围 0 - 65535* @retval 无*/
void DelayUs(uint16_t us)
{if (us == 0) return;const Delay_TimerConfigTypeDef* config = &TimerConfigTable[currentTimerIndex];// 设置自动重载值TIM_SetAutoreload(config->TIMx, us);// 清零计数器TIM_SetCounter(config->TIMx, 0);// 清除更新标志位TIM_ClearFlag(config->TIMx, TIM_FLAG_Update);// 启动定时器TIM_Cmd(config->TIMx, ENABLE);// 等待更新标志位被置位while (!TIM_GetFlagStatus(config->TIMx, TIM_FLAG_Update));// 停止定时器TIM_Cmd(config->TIMx, DISABLE);// 清除更新标志位TIM_ClearFlag(config->TIMx, TIM_FLAG_Update);
}/*** @brief 毫秒级延时函数* @param ms: 延时的毫秒数* @retval 无*/
void DelayMs(uint16_t ms)
{for (uint16_t i = 0; i < ms; i++){DelayUs(1000); // 循环调用 1000 微秒延时实现 1 毫秒延时}
}/*** @brief 获取当前使用的定时器索引* @retval 当前使用的定时器在配置表中的索引*/
uint8_t Delay_GetCurrentTimerIndex(void)
{return currentTimerIndex;
}
2.3.2 delay.h
#ifndef DELAY_H
#define DELAY_H#include "system_config.h"/************************************************淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click作者:胜磊电子
************************************************//** 定时器配置结构体 */
typedef struct {TIM_TypeDef* TIMx; // 定时器指针uint32_t TIMx_CLK; // 定时器时钟uint32_t APBxPeriph_CLKCmd; // 时钟使能函数参数void (*CLKCmd)(uint32_t, FunctionalState); // 时钟使能函数指针
} Delay_TimerConfigTypeDef;/** 系统支持的所有定时器配置表 */
extern const Delay_TimerConfigTypeDef TimerConfigTable[];
extern const uint8_t TimerConfigTableSize;/** 延时函数初始化和操作 */
void Delay_Init(uint8_t timerIndex);
void DelayUs(uint16_t us);
void DelayMs(uint16_t ms);/** 获取当前使用的定时器索引 */
uint8_t Delay_GetCurrentTimerIndex(void);#endif /* DELAY_H */
2.4 led_driver
2.4.1 led_driver.c
#include "led_driver.h"/************************************************LED灯驱动淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click作者:胜磊电子
************************************************//*** @brief 初始化LED* @param led: LED结构体指针* @param GPIOx: GPIO端口* @param GPIO_Pin: GPIO引脚* @retval 无*/
void LED_Init(LED_TypeDef* led, GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) {GPIO_InitTypeDef GPIO_InitStructure;uint32_t RCC_APB2Periph_GPIOx = 0;// 根据GPIO端口确定RCC时钟if (GPIOx == GPIOA) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOA;} else if (GPIOx == GPIOB) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOB;} else if (GPIOx == GPIOC) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOC;} else if (GPIOx == GPIOD) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOD;} else if (GPIOx == GPIOE) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOE;}// 使能GPIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);// 配置GPIO为推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOx, &GPIO_InitStructure);// 初始化LED结构体led->GPIOx = GPIOx;led->GPIO_Pin = GPIO_Pin;led->state = LED_OFF;// 默认关闭LEDGPIO_SetBits(GPIOx, GPIO_Pin);
}/*** @brief 打开LED* @param led: LED结构体指针* @retval 无*/
void LED_On(LED_TypeDef* led) {GPIO_ResetBits(led->GPIOx, led->GPIO_Pin);led->state = LED_ON;
}/*** @brief 关闭LED* @param led: LED结构体指针* @retval 无*/
void LED_Off(LED_TypeDef* led) {GPIO_SetBits(led->GPIOx, led->GPIO_Pin);led->state = LED_OFF;
}/*** @brief 切换LED状态* @param led: LED结构体指针* @retval 无*/
void LED_Toggle(LED_TypeDef* led) {if (led->state == LED_ON) {LED_Off(led);} else {LED_On(led);}
}/*** @brief 获取LED状态* @param led: LED结构体指针* @retval LED状态*/
LED_State LED_GetState(LED_TypeDef* led) {return led->state;
}
2.4.2 led_driver.h
#ifndef LED_DRIVER_H
#define LED_DRIVER_H#include "system_config.h"/************************************************LED灯驱动淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click作者:胜磊电子
************************************************/// LED状态枚举
typedef enum {LED_OFF = 0,LED_ON = 1
} LED_State;// LED结构体定义
typedef struct {GPIO_TypeDef* GPIOx; // GPIO端口uint16_t GPIO_Pin; // GPIO引脚LED_State state; // LED当前状态
} LED_TypeDef;// 函数声明
void LED_Init(LED_TypeDef* led, GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void LED_On(LED_TypeDef* led);
void LED_Off(LED_TypeDef* led);
void LED_Toggle(LED_TypeDef* led);
LED_State LED_GetState(LED_TypeDef* led);#endif /* LED_DRIVER_H */
3 延时验证
延时500ms,使用逻辑分析仪采集到的:
虽然赶不上systick的精度,但是稳定性是极大的提高了。
总结
通过上面这样设置,就可以使用延时函数进行系统延时了,可以随意使用延时所用的定时器,不会和其他资源冲突。