功能
本人移植平台为STM32F103C8+FreeRTOS实现按键短按按下 短按抬起 长按按下 长按抬起 长按连发 单键双击 组合按键 均为非阻塞方式,适合裸机和RTOS。
这篇笔记是为了在需要时方便个人拉取,因此只贴了相关代码,而没有太多的文字描述。移植参考的是安富莱的相关教程,相关链接已经贴在了帖子结尾处。
如果有朋友需要详细一点的笔记,或其它问题,可在评论区交流,有时间了在完善。
代码
- delay.c 中的 get_run_tim()、check_run_tim() 函数被 key_fifo.c里面的函数调用。
/**
****************************************************************************
* @file delay.c
* @author BJX
* @version V1.0
* @date 2023-10-xx
* @brief 延时函数,正点模板
****************************************************************************
* @attention
* 系统定时器(SysTick)是一个 24bit的向下递减的计数器,
* 计数器每计数一次的时间为 1/SYSCLK。
*
****************************************************************************
*/
#include "./delay/delay.h"
#include "./sys/sys.h"
static uint8_t fac_us = 0; /* us延时倍乘数 */
/*
全局运行时间,单位1ms,最长可以表示 24.85天
如果你的产品连续运行时间超过这个数,则必须考虑溢出问题
*/
__IO int32_t gil_run_tim = 0;
/**
* @brief systick中断服务获取CPU运行时间,单位1ms。
* 最长可以表示 24.85天,如果你的产品连续运行时间超过这个数,则必须考虑溢出问题
* @param 无
* @retval CPU运行时间,单位1ms
*/
int32_t get_run_tim(void)
{
int32_t run_time;
DISABLE_INT(); /* 关中断 */
run_time = gil_run_tim; /* 这个变量在Systick中断中被改写,因此需要关中断进行保护 */
ENABLE_INT(); /* 开中断 */
return run_time;
}
/**
* @brief 计算当前运行时间和给定时刻之间的差值。处理了计数器循环。
* @param _last_tim:上个时刻
* @retval 当前时间和过去时间的差值,单位1ms
*/
int32_t check_run_tim(int32_t _last_tim)
{
int32_t now_time;
int32_t time_diff;
DISABLE_INT(); /* 关中断 */
now_time = gil_run_tim; /* 这个变量在Systick中断中被改写,因此需要关中断进行保护 */
ENABLE_INT(); /* 开中断 */
if (now_time >= _last_tim)
{
time_diff = now_time - _last_tim;
}
else
{
time_diff = 0x7FFFFFFF - _last_tim + now_time;
}
return time_diff;
}
/* 判断是否使用OS */
#if SYSTEM_SUPPORT_OS
/* 添加公共头文件 FreeRTOS 使用 */
#include "FreeRTOS.h"
#include "task.h"
static uint16_t fac_ms = 0; /* ms延时倍乘数,在 os下,代表每个节拍的ms数 */
extern void xPortSysTickHandler(void);
/**
* @brief systick中断服务函数,使用OS时用到
* @param 无
* @retval 无
*/
void SysTick_Handler(void)
{
/* OS开始跑了,才执行正常的调度处理 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
/* 全局运行时间每1ms增1 */
gil_run_tim++;
/* 这个变量是 int32_t 类型,最大数为 0x7FFFFFFF */
if (gil_run_tim == 0x7FFFFFFF)
{
gil_run_tim = 0;
}
}
}
/**
* @brief 使用RTOS时的延迟函数初始化
* @param 无
* @retval 无
*/
void delay_init(void)
{
uint32_t reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); /* 选择外部时钟 HCLK */
fac_us = SystemCoreClock / 1000000; /* 不论是否使用 OS,fac_us 都需要使用 */
reload = SystemCoreClock / 1000000; /* 每秒钟的计数次数 单位为 M */
/* 根据 configTICK_RATE_HZ 设定溢出时间 reload 为 24 位寄存器, */
reload *= 1000000 / configTICK_RATE_HZ; /* 最大值:16777216,在 72M 下,约合 0.233s 左右 */
fac_ms = 1000 / configTICK_RATE_HZ; /* 代表 OS 可以延时的最少单位 */
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; /* 开启 SYSTICK 中断 */
SysTick->LOAD = reload; /* 每 1/configTICK_RATE_HZ 秒中断一次 */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 开启 SYSTICK */
}
/**
* @brief 微秒级延时
* @param nus:要延时的 us 数(0~204522252)
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD; /* LOAD 的值(LOAD:定时器重装值) */
ticks = nus * fac_us; /* 需要的节拍数 */
told = SysTick->VAL; /* 刚进入时的计数器值 */
while (1)
{
tnow = SysTick->VAL; /* VAL:当前计数值 */
if (tnow != told)
{
/* 这里注意一下 SYSTICK 是一个递减的计数器就可以了. */
if (tnow < told)
tcnt += told - tnow;
else
tcnt += reload - tnow + told;
told = tnow;
if (tcnt >= ticks)
break; /* 时间超过/等于要延迟的时间,则退出. */
}
}
}
/**
* @brief 毫秒级延时,会引起任务调度
* @param nms 延时时长,范围:0~65535
* @retval 无
*/
void delay_ms(uint32_t nms)
{
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) /* 系统已经运行 */
{
if (nms >= fac_ms) /* 延时的时间大于 OS 的最少时间周期 */
{
vTaskDelay(nms / fac_ms); /* FreeRTOS 延时 */
}
nms %= fac_ms; /* OS 已经无法提供这么小的延时了 采用普通方式延时*/
}
delay_us((uint32_t)(nms * 1000)); /* 普通方式延时 */
}
/**
* @brief 毫秒级延时,不会引起任务调度
* @param nms 延时时长,范围:0~204522252
* @retval 无
*/
void delay_xms(uint32_t nms)
{
uint32_t i;
for (i = 0; i < nms; i++)
{
delay_us(1000);
}
}
#else
/**
* @brief 初始化延迟函数
* @param sysclk: 系统时钟频率(即CPU频率(HCLK)),单位为 MHz
* @retval 无
*/
void delay_init(uint16_t sysclk)
{
/* 清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 */
SysTick->CTRL = 0;
fac_us = sysclk; /* 作为1us的基础时基 */
}
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD = nus * fac_us; /* 时间加载 */
SysTick->VAL = 0x00; /* 清空计数器 */
SysTick->CTRL |= 1 << 0 ; /* 开始倒数 */
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16))); /* CTRL.ENABLE位必须为1, 并等待时间到达 */
SysTick->CTRL &= ~(1 << 0) ; /* 关闭SYSTICK */
SysTick->VAL = 0X00; /* 清空计数器 */
}
/**
* @brief 延时nms
* @param nms: 要延时的ms数 (0< nms <= 65535)
* @retval 无
*/
void delay_ms(uint16_t nms)
{
/* 这里用1000,是考虑到可能有超频应用,比如128Mhz的时候,
delay_us最大只能延时1048576us左右了 */
uint32_t repeat = nms / 1000;
uint32_t remain = nms % 1000;
while (repeat)
{
delay_us(1000 * 1000); /* 利用delay_us 实现 1000ms 延时 */
repeat--;
}
if (remain)
{
delay_us(remain * 1000); /* 利用delay_us, 把尾数延时(remain ms)给做了 */
}
}
#endif
/****************************** END OF FILE ******************************/
delay.h
#ifndef<