STM32F103 HAL库 TIM通用定时器-内部定时功能实现 以及准确延时示例 ing...
定时器分类介绍(来自野火指南者开放板资料)
STM32CubeMX 配置文件
- 找到对应STM32型号,配置SYS和RCC串口烧录和外部晶振的选择
- 找到对应TIMx定时器,选择 internal clock (内部时钟源)一般均选择这个选项。其他都不需要选。
3.时钟配置
一般均设为最大 72Mhz
4.parameter Settings
PSC 预分频器值(PSC):0~65535 (分频值)
Counter Mode :UP (定时器计数模式)
Counter Period(ARR):1~65536 (自动重载值)
auto-reload preload:Enable (自动转入重载值?)
定时时间计算:
HCLK=72Mhz
定时时间(秒)=1/(HCLK)(PSC+1)(ARR+1)
最终计算出来的是以秒为单位* 注意!!!
NVIC Setting 配置 (使能)
例程代码
HAL库TIM中断开启函数
__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);//清除TIM定时器标志位
HAL_TIM_Base_Start_IT(&htim3);//开启内部定时器tim3
TIM初始化函数
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_ON_GPIO_Port, LED_ON_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = LED_ON_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_ON_GPIO_Port, &GPIO_InitStruct);
}
HAL库TIM中断回调函数配置
//TIM定时返回函数
void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef *htim)//回调函数
{
if(htim->Instance == TIM3)//判断进入回调函数方式?
{
__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);//清除标志位
HAL_TIM_Base_Stop_IT(&htim3);//停止定时器
printf("1");//测试USART打印函数
HAL_GPIO_TogglePin(LED_ON_GPIO_Port, LED_ON_Pin);//LED端口反转
HAL_TIM_Base_Start_IT(&htim3);//开启定时器
}
}
USART 打印函数配置
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 1000);
return (ch);
}
HAL库———延时函数与计数函数以及实现准确延时功能的函数
__weak void HAL_Delay(uint32_t Delay);//延时函数
__weak uint32_t HAL_GetTick(void); //计时函数
HAL_Delay() 函数用于是程序主循环暂停一定时间,单位毫秒(mm)。
HAL_GetTick() 函数用于获取MCU自复位以来运行的时间,单位同样是毫秒(mm)。
其实HAL_Delay()也是靠HAL_GetTick()函数实现功能的。
代码如下:
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
这两个函数使用起来非常方便,但是也有一定的不足。
HAL_Delay()不能满足精确延时需要,如微秒级延时。
这很好理解,上边我们说过HAL_Delay()是毫秒级延时。
HAL_Delay()函数在中断中使用,可能会卡死。
HAL_GetTick的替代函数getCurrentMicros
uint32_t getCurrentMicros(void)
{
/* Ensure COUNTFLAG is reset by reading SysTick control and status register */
LL_SYSTICK_IsActiveCounterFlag(); //清除计数器"溢出"标志位
uint32_t m = HAL_GetTick();
const uint32_t tms = SysTick->LOAD + 1;
__IO uint32_t u = tms - SysTick->VAL;
if (LL_SYSTICK_IsActiveCounterFlag()) {
m = HAL_GetTick();
u = tms - SysTick->VAL;
}
return (m * 1000 + (u * 1000) / tms);
}
__STATIC_INLINE uint32_t LL_SYSTICK_IsActiveCounterFlag(void)
{
//判断COUNTFLAG位是否为1,1则计数器已经递减到0了至少一次。读取该位后该位自动清零。
return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));
}
getCurrentMicros()函数用于获取自MCU复位以来的运行时间,单位微秒,这个函数的实质还是使用了系统滴答定时器,只是在HAL_GetTick()函数的基础上使用了SysTick的当前计数值SysTick->VAL,根据这个数据和SysTick->LOAD重装载值的关系即可计算出不足1毫秒的那一部分时间,从而得到准确的以微秒为单位的时间。
LL_SYSTICK_IsActiveCounterFlag()函数时LL库的一个函数。
HAL_Delay的替代函数delayMicroseconds
//删减版delayMicroseconds函数原型
static inline void delayMicroseconds(uint32_t) __attribute__((always_inline, unused));
static inline void delayMicroseconds(uint32_t us)
{
//方法一
__IO uint32_t currentTicks = SysTick->VAL;
/* Number of ticks per millisecond */
const uint32_t tickPerMs = SysTick->LOAD + 1;
/* Number of ticks to count */
const uint32_t nbTicks = ((us - ((us > 0) ? 1 : 0)) * tickPerMs) / 1000;
/* Number of elapsed ticks */
uint32_t elapsedTicks = 0;
__IO uint32_t oldTicks = currentTicks;
do {
currentTicks = SysTick->VAL;
elapsedTicks += (oldTicks < currentTicks) ? tickPerMs + oldTicks - currentTicks :
oldTicks - currentTicks;
oldTicks = currentTicks;
} while (nbTicks > elapsedTicks);
}