1 需求
使用TIM2定时器实现毫秒级延迟,实现原理:定义全局计数器,定时器中断触发中断服务函数,在中断里对计数器加一操作,直到累加的数值和期望延时的数字相同
2 实现流程
需要用到的外设有TIM2定时器,通过0.96寸OLED屏幕,IIC通信,通过延时显示不同字符串来验证延时功能是否正常
3 代码实现流程
先准备好测试用字符串,hello world1 hello world2 12345678987654321 三个字符串 然后在显示函数之间插入要使用的延时函数,
主函数主要进行调用写好的功能函数和初始化等函数,具体的实现和声明等操作在功能文件中编写,本次实验重点部分在Tim文件的实现,Tim文件的具体实现流程是初始化tim外设,比如开启始终,配置分频数 计数值等配置优先级分组,优先级配置,然后是能定时器中断,编写中断服务函数,然后中断函数里进行计数器加一操作
易错点:delay_ms延时函数的实现:
最开始我直接使用全局计数器和需要进行延时的数值进行比较,导致功能无法正常工作,后来查资料才知道,上电之后计数器就在运行了,所以如果直接使用这个值是错误的,要在进入延时函数的时候,获取到的计数值,然后进行当前计数值和最开始的这个计数值比较,如果达到了延时需要的数值就结束循环,这种实现属于阻塞CPU的操作,延时期间CPU不能作其他操作,非阻塞方式就是采用轮询的方式,看当前的计数值与开始值的差值比如这段伪代码
// 使用示例,不要直接调用Delay_ms,用标记判断 uint32_t tick_start; tick_start = tim_ms; if(tim_ms - tick_start >= 500) { // 500ms到了,执行oled刷新 }
4 实验代码展示
//main函数
#include "stm32f10x.h" // Device header
//#include "Delay.h"
#include "OLED.h"
#include "Tim2.h"
int main(void)
{
OLED_Init();
Tim2_Init();//实现延迟函数 如果是在操作系统中 就可以使用systick来进行延时操作
OLED_ShowChar(1,1,'A');
OLED_ShowString(2,1, "hello world1");
delay_ms(1000);
OLED_ShowString(2,1, "hello world2");
delay_ms(1000);
OLED_ShowString(2,1, "1234567898765432");// 最长16位
//OLED_Clear();
while(1)
{
}
}
//定时器函数
#include "stm32f10x.h"
#include "Tim2.h"volatile uint32_t tim_ms = 0; // 全局毫秒计数器
void Tim2_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 72M时钟,PSC=71, ARR=999 → 1ms溢出中断
TIM_TimeBaseInitStruct.TIM_Period = 999; // ARR
TIM_TimeBaseInitStruct.TIM_Prescaler = 71; // PSC
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}// 定时器中断服务函数:每1ms进来一次
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
tim_ms++; // 毫秒计数+1
}
}
//毫秒延迟函数
void delay_ms(uint32_t num)
{
uint32_t start = tim_ms; // 保存进入函数时的时间点
while( (tim_ms - start) < num )
{
;
}
}