51单片机定时器使用

Posted 丁丁的笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51单片机定时器使用相关的知识,希望对你有一定的参考价值。

51单片机学习

一直想给女儿做一个平衡小车玩具,想用PLC做,感觉难度,用单片机吧,都快20年没用了。
最近考试考完了,时间和资源都有,正好可以捣鼓一下。看了郭天翔的视频,讲的很不错,
边学边写。

定时器使用

控制任务:

P1.0 控制一个LED灯,亮0.5s,灭0.5s。

设计思路:这里我们只用定时器,不用软延时。51的定时器最多定时60ms,所以我们设置定时器每
50ms中断一次,通过在中断程序设置一个变量来统计中断次数,从而实现较长时间的定时。这里我们
是每500ms执行一次灯亮灯灭的动作,所以每10个中断等于500ms(50ms x 10)。第6行,全局变量
timer50msCount 就是中断次数。第19-23行,当timer50msCount 为10时,代表500ms时间到,把
P1.0 取反,动作一次。

这里有个繁琐的地方,定时器的初值需要手工计算。不过前人开发了一下小程序,直接拿过来用就可以了。
见下图。把自动生成代码中的第一行删掉就可以了。

程序如下:

#include <reg52.h>
typedef unsigned int uint;
typedef unsigned char uchar;

sbit P10 = P1 ^ 0;
// 第 6 行
uchar timer50msCount = 0;   

void Timer0Init(void);

void main()
{
    EA = 1;         // 开总中断
    ET0 = 1;        // 开定时器 0 中断
    Timer0Init();

    while (1)
    {
        // 每500ms允许if语句块中的程序
        // 19 - 23 行
        if (timer50msCount == 10)
        {
            timer50msCount = 0;
            P10 = ~P10;
        }
    }
}

void Timer0Init(void) //50ms@11.0592MHz
{
    TMOD &= 0xF0; // 设置定时器模式,这里为T0
    TMOD |= 0x01; // 设置定时器工作方式1,为16为定时器
    TL0 = 0x00;   // 设置定时器低位初值
    TH0 = 0x4C;   // 设置定时器高位初值
    TF0 = 0;      // 清楚TF0溢出标志位
    TR0 = 1;      // 启动定时器0开始计时
}


// 定时器0中断子程序
void timer0Interrupt() interrupt 1
{
    timer50msCount++;
    // 每次中断时,定时器初值为0,需重新设置定时器初值,保持50ms
    // 时间不变
    TL0 = 0x00;
    TH0 = 0x4C;
}

独立键盘

控制任务:

按下 s2 键,led 灯亮,松手灯灭。

见面的程序用到一个10ms的延时函数,可以用软件自动生成,见下图。
图片选了1ms,和10ms的延时函数是不一致。1ms的程序里包含了_nop()_指令,
使用这条指令,需要在在代码中使用头文件intrins.h。

#include <intrins.h>

程序如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;
sbit s2 = P3 ^ 4;       // s2 按键
sbit d1 = P1 ^ 0;      //  led 灯

uchar code table[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71};

uchar code welaTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};

void Delay10ms();

void main()
{
    uchar num = 0;
    wela = 1;
    P0 = 0xfe;
    wela = 0;

    // 把P3口置高电平
    P3 = 0xff;
    while (1)
    {
        // s2 == 0, 表示 s2 变为低电平,此时按键按下
        if (s2 == 0)
        {
            Delay10ms();  // 去按下时的抖动
            if (s2 == 0)
            {
                d1 = 0;  // 点亮led
                num++;   // 按键次数计数
                if (num > 9)  // led 数码管计数最大为9, 超出置为0
                {
                    num = 0;
                }
            }
            
            // 按键从按下到释放,时间有100-200ms,单片机的
            // 扫描时间很快,此处防止键盘按的过程中,num值反复加
            while (!s2)
                ;
            // 键盘松手是去毛刺延时
            Delay10ms();
        }
        else
        {
            d1 = 1;
        }

        dula = 1;
        P0 = table[num];
        dula = 0;
    }
}

void Delay10ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 18;
    j = 235;
    do
    {
        while (--j)
            ;
    } while (--i);
}

矩阵键盘

控制任务:

4 x 4 的矩阵键盘,按下第0个键,6个数码管都显示0,按下第一个键,6个数码管显示1,一次类推。

程序如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;

// 数字码表,数字最后个0x00为不显示.
uchar code numberTable[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71,
    0x00};

// 行扫描编码表, 每个元素代表一行
uchar code keyTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7};

void delay10ms();
uchar keyScan();
void show(uchar num);

// 51的c编译器比较原始,函数中返回的值只能用
// 全局变量
uchar num = 16;

void main()
{
    // 此处传入16,数字码表选择0x00,
    // 上电时关闭数码管,防止显示乱码, 
    show(16);

    wela = 1;
    P0 = 0xc0;
    wela = 0;

    while (1)
    {
        // keyScan函数返回 0 - 15 数字
        uchar keyNum = keyScan();
        show(keyNum);
    }
}

uchar keyScan()
{

    // 键盘为4X4的键盘矩阵,每个for循环扫描一行,while循环中扫描列,
    // num变量返回这个键盘矩阵的键位,
    // 比如第0个键按下,返回0,第一个返回1,以此类推
    uchar i = 0, temp;
    for (; i < 4; i++)
    {
        P3 = keyTable[i];
        temp = P3 & 0xf0;
        while (temp != 0xf0)
        {
            delay10ms();
            temp = P3 & 0xf0;
            switch (temp)
            {
            case 0xe0:
                num = 4 * i + 0;
                break;
            case 0xd0:
                num = 4 * i + 1;
                break;
            case 0xb0:
                num = 4 * i + 2;
                break;
            case 0x70:
                num = 4 * i + 3;
                break;
            }
        }
    }
    return num;
}

void show(uchar num2)
{
    dula = 1;
    P0 = numberTable[num2];
    dula = 0;
}

void delay10ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 18;
    j = 235;
    do
    {
        while (--j)
            ;
    } while (--i);
}

动态显示

控制任务:

在六个数码管上分别显示1,2,3,4,5,6

程序如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;

// 段码表
uchar code table[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71};

// 位码表
uchar code welaTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};

void Delay1ms();

void main()
{
    uchar i;

    while (1)
    {
        // 依次只显示一个数码管,但因速度很快,
        // 从而造成眼睛的错觉
        for (i = 0; i < 6; i++)
        {
            P0 = 0xFF;  // 消影
            wela = 1;
            P0 = welaTable[i];
            wela = 0;
            dula = 1;
            P0 = table[i + 1];
            dula = 0;
            Delay1ms();  // 务必要加延时
        }
    }
}

void Delay1ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 2;
    j = 199;
    do
    {
        while (--j)
            ;
    } while (--i);
}

ADC0804 模数转换

/************************
 *  通过AD芯片把模拟量电压值转换为数字量 *
 *  转换后的数字量显示在LED数码管上   *
 ************************/


#include <reg52.h>
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit dula = P2 ^ 6;     // 段选信号
sbit wela = P2 ^ 7;     // 位选信号
sbit adWr = P3 ^ 6;     // AD 开始转换信号,低电平有效
sbit adRd = P3 ^ 7;     // AD 开始读取信号,低电平有效
sbit adCs = P0 ^ 7;     // AD 片选信号,低电平有效

// LED 数码管段选码表
uchar code numberTable[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71,
    0x00};

// LED 数码管位选码表
uchar code welaTable[] = {
    0x5f, 0x6f, 0x77, 0x7b, 0x7d, 0x7e};

void delay5ms();

void showNumOnSelectedLED(uchar number, uchar welaNum);

void main()
{
    uchar i, onesPlace, tensPlace, hundredsPlace, adValue;

    // AD 芯片片选使能
    adCs = 0;

    while (1)
    {
        // 转换开始
        _nop_();
        adWr = 0;
        _nop_();
        adWr = 1;

        // 显示数值,放在adWr后,是为了增加AD芯片的转换时间
        for (i = 0; i < 10; i++)
        {
            showNumOnSelectedLED(onesPlace, 0);
            showNumOnSelectedLED(tensPlace, 1);
            showNumOnSelectedLED(hundredsPlace, 2);
        }

        // adRd读取转换后的数字量
        _nop_();
        adRd = 0;
        _nop_();
        adValue = P1;
        adRd = 1;

        // adValue 是读取到的数字量,然后把个、十、百分离开来
        hundredsPlace = adValue / 100;
        tensPlace = adValue % 100 / 10;
        onesPlace = adValue % 10;
    }
}

/** 
 * @brief  在指定的LED数码上显示指定的数字
 * @note   例如,number = 3, placeOfNum = 0, 那么为最右边的第0个数码管显示数字3 
 * @param  number: 显示的数字 0 - F 
 * @param  placeOfNum:  显示的位置,从右到左,分别是 0 - 6 位
 * @retval None
 */
void showNumOnSelectedLED(uchar number, uchar placeOfNum)
{
    // 送位选数据前关闭所有显示,防止打开位选锁存时,
    // 原来段选数据通过位选锁存器造成混乱
    P0 = 0xff;


    wela = 1;
    P0 = welaTable[placeOfNum];
    wela = 0;
    dula = 1;
    P0 = numberTable[number];
    dula = 0;
    delay5ms();
}

/** 
 * @brief  延时函数
 * @note   
 * @retval None
 */
void delay5ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 9;
    j = 244;
    do
    {
        while (--j)
            ;
    } while (--i);
}

DAC0832 数模转换


/*
单片机控制DAC0832芯片输出电流,让发光二极管D12由灭均匀变到
最亮,再有最亮均匀熄灭。最亮和最暗时蜂鸣器发声
*/

#include <intrins.h>
#include <reg52.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
sbit daWr = P3 ^ 6; 
sbit daCs = P3 ^ 2; 
sbit beep = P2 ^ 3; // 蜂鸣器

void delay100ms();
void delay50ms();

void main() {
  uchar flag, value;

  // 关闭数码管
  dula = 0;
  wela = 0;

  // daCs 和 daWr 低电平时,开始DA开始转换
  daCs = 0;
  daWr = 0;

  // P0 口清零
  P0 = 0x00;

  while (1) {
    if (flag == 0) {
      value += 5;
      P0 = value;
      if (value == 255) {
        flag = 1;
        beep = 0;
        delay100ms();
        beep = 1;
      }
      delay50ms();
    } else {
      value -= 5;
      P0 = value;
      if (value == 0) {
        flag = 0;
        beep = 0;
        delay50ms();
        beep = 1;
      }
      delay50ms();
    }
  }
}

void delay100ms()  //@11.0592MHz
{
  unsigned char i, j;

  i = 180;
  j = 73;
  do {
    while (--j)
      ;
  } while (--i);
}

void delay50ms()  //@11.0592MHz
{
  unsigned char i, j;

  i = 90;
  j = 163;
  do {
    while (--j)
      ;
  } while (--i);
}

以上是关于51单片机定时器使用的主要内容,如果未能解决你的问题,请参考以下文章

51单片机ec11编码器中断法

Proteus仿真51单片机利用定时器倒计数(99-0)

用STC单片机的PCA做定时器,输出PWM

51单片机 定时器流水灯+Proteus仿真

51单片机 通过定时器实现PWM输出

Proteus仿真51单片机+555可调定时器电路