51单片机入门 - 详解定时器实现按键控制流水灯方向

硬件型号、软件版本、以及烧录流程

  • 操作系统:Windows 10 x84-64
  • 单片机:STC89C52RC
  • 编译器:SDCC
  • 烧录软件:stcgal 1.6
  • 开发板:普中51单片机开发板A2套件(2022)

在 VS Code 中新建项目到烧录的过程:

  1. 左侧EIDE图标 - 新建项目 - 空项目 - 8位MCU项目 - 保存文件夹。
  2. 更改构建配置:SDCC;更改烧录配置:stcgal
  3. 在项目文件夹下新建 src/main.c,右键 项目资源 - 添加源文件夹 - 普通文件夹,选择 src
  4. src 目录下开发,最后点击右上角 构建烧录,单片机上电,完成烧录。

定时器(Timer)、计数器(Counter)

我的单片机是89C52RC,属于STC89C52系列单片机,可以参考宏晶官网的文档:http://www.stcmcudata.com/datasheet/stc/STC-AD-PDF/STC89C51RC-RD.pdf 后面的内容都参考的是这篇文档,如有不同之处,请以官方文档为准。

该单片机内的两个16位定时器/计数器 T0T1 都具有定时方式计数方式两种工作方式。可以通过特殊功能寄存器TMOD来设定这两个定时器的工作方式。

定时器/计数器的核心部件是一个加法计数器,其本质是对脉冲进行计数:

  • 定时器:计数脉冲来自系统时钟,每12或每6个时钟得到一个计数脉冲,计数值加1;
  • 计数器:计数脉冲来自外部引脚T0P3.4T1P3.5,每得到一个脉冲,计数值加1.

这篇文章将只会用到T0的定时器模式,定时器模式默认每12个时钟自加1,也可以在STC-ISP烧录软件的使能6T(双倍速)模式勾选,来使其每6个时钟自加1.

STC89C52定时器的相关寄存器

51单片机入门 - 详解定时器实现按键控制流水灯方向_第1张图片

参考文档的相关部分,可以通过对定时器0的部分(即TMOD的低4位)置“0001”来选择16位定时器模式。TH0TL0 分别是定时器 T0 的高8位和低8位,需要对它们分别进行赋值,也就是说T0实际上的值为(TH0 <<8 ) + TL0

由文档中的示例程序得知,还需要设置:

    ET0   = 0x01;   // 打开定时器 0 中断允许
    EA    = 0x01;   // 打开总中断
    TR0   = 0x01;   // 打开定时器

来使用 T0 定时器。

定时器 T0 初始化

这块开发板的晶振是 12MHz,也就是 12 ⋅ 1 0 6 12 \cdot 10^6 12106 次每秒,定时器每12个时钟自增 1,也就是每秒会自增 10^6 次,即每 1us 自增 1,而计数器是16位的,可以表示 0-65535 ,当达到 65535 时,再加 1 就会出现溢出,引起中断,然后由 interrupt 1 接手处理这次中断。

为了方便计算,我们可以令 T0 的初始值为 65536 - 1000 = 64536,这样就会在第 1000us 即 第 1 ms 时触发一次中断。HEX(64536) = 0xFC18,即高 8 位 0xFC,低 8 位 0x18

void Timer0_Init(void) // initialize timer 0
{
    TMOD |= 0x01;   // 选择为定时器 0 模式,工作方式 1
    TH0   = 0xFC;   // T0 定时器高 8 位
    TL0   = 0x18;   // T0 定时器低 8 位
    ET0   = 0x01;   // 打开定时器 0 中断允许
    EA    = 0x01;   // 打开总中断
    TR0   = 0x01;   // 打开定时器
}

中断函数

函数类型 函数名 (形式参数) interrupt n [using n]
中断号    中断源         中断向量
  0     外部中断0           0003H
  1     定时器/计数器0      000BH
  2     外部中断1           0013H
  3     定时器/计数器1      001BH
  4     串行口              0023H

我们使用定时器 0 作为中断源,所以 n 为 1.

//void time0() interrupt 1   // Keil
void time0() __interrupt(1)  // SDCC
{
    static int i;   //定义静态变量i
    TH0 = 0xFC;
    TL0 = 0x18;
    i++;
    if (i == 500) { // i 被增加了 500 次, 也就是过去了 0.5s
        i = 0;
        LED_controller();
    }
}

完整程序

通过全局变量 forward 来决定流水灯的方向。定时器 T0 来控制流水灯每 0.5s 移动一位,main() 函数里实时监听按键情况,一旦 K1 按键按下,那么对 forward 取反来改变方向。

#include <8051.h>
#define K1 P3_1

int forward = 0;

void delay_10us(unsigned int n) {
    while(n--);	
}

void Timer0_Init() // initialize timer 0
{
    TMOD |= 0x01;   // 选择为定时器0模式,工作方式 1
    TH0   = 0xFC;   // T0 定时器高 8 位
    TL0   = 0x18;   // T0 定时器高 8 位
    ET0   = 0x01;   // 打开定时器0中断允许
    EA    = 0x01;   // 打开总中断
    TR0   = 0x01;   // 打开定时器
}

void LED_controller() {
    static int flag = 1;
    flag &= (1 << 8) - 1;
    if (forward) {
        flag = ((flag & 1) << 7) ^ (flag >> 1);
    } else {
        flag = ((flag >> 7) & 1) ^ (flag << 1);
    }
    P2 = 0xFF ^ flag;
}

void main() {
    Timer0_Init();
    while (1) {
        if (K1 == 0){
            delay_10us(1000);
            while (K1 == 0);
            delay_10us(1000);
            forward = !forward;
        }
    }
}

//void time0() interrupt 1   // Keil
void time0() __interrupt(1)  // SDCC
{
    static int i;   //定义静态变量 i
    TH0 = 0xFC;
    TL0 = 0x18;
    i++;
    if (i == 500) { // i 被增加了 500 次, 也就是过去了 0.5s
        i = 0;
        LED_controller();
    }
}

你可能感兴趣的:(51单片机,C/C++,单片机,51单片机)