一文读懂单片机的定时器
目录
引言
单片机定时器的基本原理
定时器的组成
定时器的工作原理
单片机定时器的配置方法
选择时钟源
设置预分频器
选择工作模式
配置中断
单片机定时器的应用实例
实例1:简单的延时程序
实例2:频率测量
实例3:PWM信号生成
代码解释
引言
单片机(Microcontroller Unit,MCU)是一种集成了处理器、存储器、输入输出接口等组件的微型计算机系统。在嵌入式系统设计中,单片机被广泛应用于各种控制和处理任务中。定时器是单片机中一个非常重要的组件,它能够提供精确的时间控制和事件触发功能,广泛应用于计时、频率测量、PWM(脉宽调制)生成等场合。本文将详细介绍单片机定时器的工作原理、配置方法以及应用实例,帮助读者快速掌握单片机定时器的使用技巧。
单片机定时器的基本原理
定时器的组成
单片机的定时器通常由以下几个部分组成:
- 计数器(Counter):用于记录时钟脉冲的个数。计数器可以是8位、16位或32位的寄存器,其大小决定了定时器的计数范围。
- 预分频器(Prescaler):用于对输入时钟进行分频,以降低计数器的计数频率。预分频器的分频比可以是固定的,也可以是可编程的。
- 时钟源(Clock Source):为定时器提供时钟信号。常见的时钟源包括内部时钟、外部时钟和外部事件触发等。
- 控制寄存器(Control Registers):用于配置定时器的工作模式、中断使能、预分频器设置等。
- 中断服务程序(Interrupt Service Routine, ISR):当定时器溢出或达到设定值时,会触发中断,中断服务程序用于处理定时器事件。
定时器的工作原理
定时器的工作过程可以简单概括为以下步骤:
- 初始化配置:通过控制寄存器设置定时器的工作模式、时钟源、预分频器等参数。
- 启动定时器:使能定时器开始计数。计数器开始接收时钟信号,并根据预分频器的设置对时钟信号进行分频。
- 计数过程:计数器在每个时钟周期内增加1,直到达到其最大值(例如,8位计数器的最大值为255)。
- 溢出处理:当计数器溢出时,会触发中断,中断服务程序会被调用。中断服务程序可以执行一些特定的任务,如更新计数器值、发送信号等。
- 循环计数:计数器溢出后,通常会自动重置为初始值,继续计数。
单片机定时器的配置方法
选择时钟源
定时器的时钟源决定了计数器的计数频率。常见的时钟源包括:
内部时钟:来自单片机内部振荡器的时钟信号。通常用于简单的定时任务。 外部时钟:来自外部振荡器或时钟源的信号。适用于需要精确时钟的应用。 外部事件触发:由外部事件(如按钮按下、传感器信号等)触发定时器计数。
设置预分频器
预分频器用于降低计数器的计数频率,以延长定时器的计时范围。例如,如果时钟源频率为1MHz,预分频器设置为128,则计数器的计数频率为1MHz / 128 = 7.8125kHz。预分频器的设置通常通过控制寄存器中的相应位来实现。
选择工作模式
定时器的工作模式决定了其计数方式和应用场合。常见的工作模式包括:
模式0(13位定时/计数器):适用于8051单片机等。将8位计数器和5位预分频器组合成13位计数器。 模式1(16位定时/计数器):适用于需要较长计时范围的应用。 模式2(8位自动重载定时/计数器):适用于需要周期性定时任务的场合。 模式3(两个独立的8位定时/计数器):适用于需要同时进行两个独立定时任务的应用。
配置中断
定时器溢出或达到设定值时,可以触发中断。中断的配置包括:
中断使能:通过控制寄存器中的中断使能位来启用定时器中断。 中断优先级:设置中断的优先级,以确定中断的响应顺序。 中断服务程序:编写中断服务程序来处理定时器事件。
单片机定时器的应用实例
实例1:简单的延时程序
下面是一个使用定时器实现简单延时的示例代码(以8051单片机为例):
#include <reg51.h>
#define DELAY_TIME 1000 // 延时时间(单位:ms)
void delay_ms(unsigned int ms) {
unsigned int i;
TMOD = 0x01; // 设置定时器0为模式1(16位定时/计数器)
TH0 = 0xFC; // 设置定时器初值,定时1ms
TL0 = 0x18;
for (i = 0; i < ms; i++) {
TF0 = 0; // 清除定时器溢出标志
TR0 = 1; // 启动定时器
while (!TF0); // 等待定时器溢出
TR0 = 0; // 停止定时器
}
}
void main() {
while (1) {
P1 = 0xFF; // 点亮所有LED
delay_ms(DELAY_TIME); // 延时1秒
P1 = 0x00; // 熄灭所有LED
delay_ms(DELAY_TIME); // 延时1秒
}
}
实例2:频率测量
定时器可以用于测量外部信号的频率。下面是一个使用定时器测量频率的示例代码(以STM32为例):
#include "stm32f10x.h"
#define TIM_CLOCK_FREQ 72000000 // 定时器时钟频率
#define TIM_PRESCALER 7199 // 预分频器值,定时器频率为10kHz
void TIM_Config(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; // 自动重载寄存器的值
TIM_TimeBaseStructure.TIM_Prescaler = TIM_PRESCALER; // 预分频器值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // TIM2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能TIM2中断通道
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能TIM2更新中断
TIM_Cmd(TIM2, ENABLE); // 启动定时器
}
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
static unsigned int count = 0;
static unsigned int freq = 0;
static unsigned int last_count = 0;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除TIM2更新中断待处理位
count++;
if (count >= 100) {
freq = (count - last_count) * 10000; // 计算频率
last_count = count;
count = 0;
}
}
}
int main(void) {
TIM_Config(); // 配置定时器
while (1) {
// 可以在此处处理频率测量结果
}
}
实例3:PWM信号生成
定时器可以用于生成PWM信号。下面是一个使用定时器生成PWM信号的示例代码(以AVR单片机为例):
#include <avr/io.h>
#include <avr/interrupt.h>
#define PWM_FREQ 1000 // PWM频率(单位:Hz)
#define PWM_DUTY_CYCLE 75 // PWM占空比(单位:百分比)
void PWM_Init(void) {
// 设置定时器1为快速PWM模式,8位分辨率
TCCR1A = (1 << WGM11) | (1 << COM1A1);
TCCR1B = (1 << WGM13) | (1 << CS11); // 预分频器设置为8
// 计算定时器的自动重载值,以达到所需的PWM频率
// 例如,假设系统时钟为16MHz,预分频为8,则定时器时钟为2MHz
// 8位定时器的最大值为255,因此频率为2MHz / 256 = 7.8125kHz
// 为了达到1kHz,需要调整自动重载值
ICR1 = (F_CPU / (PWM_FREQ * 8 * 256)) - 1;
// 设置占空比
OCR1A = (ICR1 * PWM_DUTY_CYCLE) / 100;
// 设置PB1为输出
DDRB |= (1 << PB1);
}
int main(void) {
PWM_Init(); // 初始化PWM
while (1) {
// 可以在此处添加其他代码
}
}
代码解释
-
定时器配置:
TCCR1A
和TCCR1B
寄存器用于配置定时器1的工作模式和时钟源。WGM11
和WGM13
位设置定时器为快速PWM模式,8位分辨率。COM1A1
位设置为非反转模式,即当计数器值小于OCR1A
时,输出为高电平.CS11
位设置预分频器为8,假设系统时钟为16MHz,则定时器时钟为2MHz.-
自动重载值计算:
ICR1
寄存器用于设置定时器的最大计数值,以达到所需的PWM频率.- 计算公式为
ICR1 = (F_CPU / (PWM_FREQ * 预分频 * 256)) - 1
,其中F_CPU
是系统时钟频率. -
占空比设置:
OCR1A
寄存器用于设置PWM的占空比,计算公式为OCR1A = (ICR1 * PWM_DUTY_CYCLE) / 100
.-
端口配置:
DDRB |= (1 << PB1);
将PB1设置为输出模式,以便将PWM信号输出到该引脚.
通过以上配置,定时器1将生成一个频率为1kHz、占空比为75%的PWM信号,并将其输出到PB1引脚.
作者:厉昱辰