51单片机基础05 定时器

目录

一、为什么要定时器

二、定时器中断

1、定时器中断参数

2、定时器中断程序

3、定时器计数


一、为什么要定时器

前文提到,比如进行流水灯等操作,都是直接写了delay_ms这类操作。

但是在51单片机中,其一般就是靠双for进行的循环时延,单片机会浪费这部分时间而不能做其他的事

其次如果程序很多的情况下,就得根据其他代码执行的时间来调整时延,这非常麻烦。

那有没有既可以定时、又不影响其他程序的进行呢?

比如,通过两个按键控制,按下按键后两个LED延时1s切换状态,如果通过以下代码进行,那么在延时的时候,按键按下另一个KEY就会完全没有反应。

cpp 复制代码
if(KEY0==0)
{
    delay_ms(10);
    if(KEY0==0)
    {
        delay_ms(1000);
        LED0=~LED0;
    }
}
if(KEY1==0)
{
    delay_ms(10);
    if(KEY1==0)
    {
        delay_ms(1000);
        LED1=~LED1;
    }
}

二、定时器中断

1、定时器中断参数

|------|----------|-----|
| 中断编号 | 中断名 | 中断源 |
| 1 | 定时器0溢出中断 | TF0 |
| 3 | 定时器1溢出中断 | TF1 |

可以看到定时器0和定时器1的中断编号分别为1、3,因此T1的中断优先级没有T0高。

|-------|---------------------|-------------------|
| 中断寄存器 | 功能 | 实现 |
| ET0 | 定时器/计数器T0的溢出中断允许控制位 | 1:允许T0中断 0:禁止T0中断 |
| ET1 | 定时器/计数器T1的溢出中断允许控制位 | 1:允许T1中断 0:禁止T1中断 |

该寄存器控制了是否允许中断。

|---------|-------------------|--------------------|
| 中断触发控制位 | 功能 | 实现 |
| TF0 | 定时/计数器T0溢出中断请求标志位 | 1:进入T0中断 0:未进入T0中断 |
| TF1 | 定时/计数器T1溢出中断请求标志位 | 1:进入T1中断 0:未进入T1中断 |

该寄存器在计数溢出后自动置位为1并进入中断服务函数,需要用户手动清零。

|---------|------------|-----------|
| 定时器开关控制 | 功能 | 实现 |
| TR0 | 打开或关闭定时器T0 | 1:打开 0:关闭 |
| TR1 | 打开或关闭定时器T1 | 1:打开 0:关闭 |

配套初始化。

如下为TMOD的控制,其中D0~D3为定时器0配置,D4~D7为定时器1配置。

|----------------------------------------|-----------|----|----|
| D3 | D2 | D1 | D0 |
| GATE | C/T | M1 | M0 |
| 0:仅要求TR0=1开启定时器 1:同时要求T0引脚为高电平才可以开启定时器 | 0:定时 1:计数 | 00:13位计数器8192 01:16位计数器65536 10:8位计数器256,可自动重新装载 11:两组独立8位计时器256,不可同时用 ||

TMOD寄存器,必须直接对整个地址操作,而不像TCON,其可以单位操作。

定时时间TH0、TL0控制,以定时器01模式为例

TH0=(65536-XX)/256

TL0=(65536-XX)%256

其中XX表示多少us,例如XX=50000,就是50ms,由此可以看出,单次定时时间最大为65536ms,最小为1us。

2、定时器中断程序

举例,还以上面按键控制LED闪烁为例

cpp 复制代码
unsigned int count0=0;
unsigned int count1=0;
char flag0 = 0;
char flag1 = 0;
void main()
{
   EA=1;            //开启总中断
   TMOD =0x01;
   TH0=(65536-50000)/256;    //50ms
   TL0=(65536-50000)%256;
   TR0=1;         //打开定时器T0
   ET0=1;         //允许中断

   while(1)
   {
       if(key0==0)
        {
            delay_ms(10)
            if(key0==0) 
                flag0=1;
        }
       if(key1==0)
        {
            delay_ms(10)
            if(key1==0) 
                flag1=1;
        }
   }
}
void time0() interrupt 1
{
   TF0=0;          //清除触发位,实现再次计数
   TH0=(65536-50000)/256;    //50ms 装初值
   TL0=(65536-50000)%256;
   if(flag0==1)
       count0+=1;
   if(count0>=20)
   {
      led0=~led0;
      count0=0;
   }
   if(flag1==1)
       count1+=1;
   if(count1>=20)
   {
      led1=~led1;
      count1=0;
   }
 }

可以看到定时器50ms的中断,判断到按键按下后,在中断额外进行计数,达到20,说明达到了1秒,从而进行LED反转,不影响main内的有关代码执行。

3、定时器计数

前文提到,T0引脚是定时器的引脚,但是前面的定时代码怎么都和T0无关呢。

当51单片机的定时器工作在计数模式 时,它会通过外部引脚(如T0或T1引脚)接收外部输入的脉冲信号。这时,I/O口就有直接关系了。外部设备产生的脉冲通过这些引脚输入到单片机,定时器/计数器累加这些脉冲来实现计数功能。举例来说,假如你需要用定时器0来测量输入信号的频率,你可以将信号输入到T0引脚,然后配置定时器0为计数模式,它会根据输入脉冲增加计数值。

计数模式C/T=1,16位M1M0=01,即TMOD=0101=0X05;

cpp 复制代码
#include <reg51.h>

// 定义定时器的控制位
sbit T0_PIN = P3^4; // T0口作为输入信号(定时器0外部输入)

// 定义全局变量
unsigned int count = 0; // 计数器计数值
unsigned long frequency = 0; // 测得的频率值

void Timer1_Init(void) {
    TMOD |= 0x10; // 设置定时器1为模式1(16位定时模式)
    TH1 = 0x3C; // 高字节初值(1秒定时器)
    TL1 = 0xB0; // 低字节初值
    ET1 = 1;   // 使能定时器1中断
    EA = 1;    // 总中断使能
    TR1 = 1;   // 启动定时器1
}

void Timer0_Init(void) {
    TMOD |= 0x05; // 设置定时器0为模式5(16位计数模式)
    TR0 = 1;      // 启动计数器0
}

void Timer1_ISR(void) interrupt 3 {
    TR1 = 0; // 关闭定时器1

    // 计算频率
    count = (TH0 << 8) | TL0; // 读取定时器0的计数值
    TH0 = 0; // 清零高字节
    TL0 = 0; // 清零低字节
    frequency = count;
    count = 0; // 计数值清零

    // 重装载定时器1初值
    TH1 = 0x3C;
    TL1 = 0xB0;
    TR1 = 1; // 重启定时器1
}

void main(void) {
    Timer0_Init(); // 初始化定时器0(计数器模式)
    Timer1_Init(); // 初始化定时器1(定时器模式)

    while (1) {
        
        // 这里可以添加代码来显示频率值frequency,如通过串口输出
    }
}

注意:中断服务函数理应不要放太多的代码,尤其是延时是要完全避免的,否则会影响相关的精度。

相关推荐
我想学LINUX1 小时前
基于Zynq FPGA对雷龙SD NAND的测试
嵌入式硬件·学习·fpga开发·sd nand·雷龙开发
程序员JerrySUN1 小时前
安全机制解析:深入SELinux与权限管理
linux·嵌入式硬件·物联网·安全
最后一个bug1 小时前
lua脚本语言基本原理
linux·c语言·开发语言·单片机·嵌入式硬件
相醉为友3 小时前
STM32电源管理—实现低功耗
stm32·单片机·嵌入式硬件
最后一个bug5 小时前
分享一些关于 C 函数与 lua 交互的实际项目案例
linux·c语言·嵌入式硬件·lua
芋头莎莎5 小时前
单片机智能家居火灾环境安全检测
c语言·stm32·单片机·嵌入式硬件·51单片机
最后一个bug5 小时前
C函数如何返回参数lua使用
linux·c语言·开发语言·嵌入式硬件·lua
芋头莎莎5 小时前
STM32设计井下瓦斯检测联网WIFI加Zigbee多路节点协调器传输
c语言·数据库·stm32·单片机·嵌入式硬件·物联网
非概念14 小时前
STM32 学习笔记-----STM32 的启动过程
笔记·stm32·嵌入式硬件·学习