定时器与数码管基础

逻辑电路与逻辑运算

在数字电路中经常会遇到逻辑电路,而在C语言中则经常用到逻辑运算。

逻辑上的“假”就是数字电路或C语言中的“0”这个值,而逻辑“真”就是其他一切“非0值”。

  • &&逻辑与。F = A && B,当A、B的值都为真(即非0值,下同)时,其运算结果F为真(具体数值为1,下同);当A、B值任意一个为假(即0,下同)时,结果F为假(具体数值为0,下同)。
  • || 逻辑或。F = A || B,当A、B值任意一个为真时,其运算结果F为真;当A、B值都为假时,结果F为假。
  • ! 逻辑非,F = !A,当A值为假时,其运算结果F为真;当A值为真时,结果F为假。

以下逻辑运算符都是按照变量内的每一个位来进行运算的,通常就叫作位运算符:

  • & 按位与,F = A & B,将A、B两个字节中的每一位都进行与运算,再将得到的每一位结果组合为总结果F,例如A = 0b11001100,B = 0b11110000,则结果F就等于0b11000000。
  • | 按位或,F = A | B,将A、B两个字节中的每一位都进行或运算,再将得到的每一位结果组合为总结果F,例如A = 0b11001100,B = 0b11110000,则结果F就等于0b11111100。
  • ~按位取反,F = ~A,将A字节内的每一位进行非运算(就是取反),再将得到的每一位结果组合为总结果F,例如A = 0b11001100,则结果F就等于0b00110011;这个运算符在前面的流水灯实验里已经用过了,现在再回头看一眼,是不是清楚多了。
    • ^ 按位异或,异或的意思是,如果运算双方的值不同(即相异)则结果为真,双方值相同则结果为假。在C语言里没有按变量整体值进行的异或运算,所以仅以按位异或为例,F=A^B,A=0b11001100,B=0b11110000,则结果F就等于0b00111100。

电路符号

 

 定时器学习

定时器初步认识

1、时钟周期

时钟周期=\frac{1}{时钟源频率}

 2、机器周期

51单片机系列,在其标准架构下一个机器周期是12个时钟周期。

定时器和计数器是单片机内部的同一个模块,通过配置SFR(特殊功能寄存器)可以实现两种不同的功能

定时器的寄存器

标准的51单片机内部有T0和T1这两个定时器,T就是Timer的缩写。

表5-1的寄存器是存储定时器的计数值的。TH0/TL0用于T0,TH1/TL1用于T1。

 表5-2是定时器控制寄存器TCON的位分配,表5-3则是对每一位的具体含义的描述。

说明: 只要写到硬件置1或者清0的,就是指一旦符合条件,单片机将自动完成动作,只要写软件置1或者清0的,是指必须用程序去完成这个动作

定时器有多种工作模式,工作模式的选择就由TMOD来控制

 

 表5-2的TCON最后标注了“可位寻址”,而表5-4的TMOD标注的是“不可位寻址”。意思就是说:比如TCON有一个位叫TR1,可以在程序中直接进行TR1 = 1这样的操作。但对TMOD里的位比如(T1)M1 = 1这样的操作就是错误的。要操作就必须一次操作这整个字节,也就是必须一次性对TMOD所有位操作,不能对其中某一位单独进行操作,那么能不能只修改其中的一位而不影响其他位的值呢?当然可以,在后续内容中读者就会学到方法的,现在就先不关心它了。

 模式1,是THn和TLn组成了一个16位的定时器,计数范围是0~65535,溢出后,只要不对THn和TLn重新赋值,则从0开始计数。模式2,是8位自动重装载模式,只有TLn做加1计数,计数范围0~255,THn的值不会变化,而会保持原来的值;TLn溢出后,TFn就直接置1了,并且THn原先的值直接赋给TLn,然后TLn从新赋值的这个数字开始计数。这个功能可以用来产生串口的通信波特率,讲串口的时候要用到,本节我们重点来学习模式1。

(1)TR0和下边或门电路的结果要进行与运算,TR0如果是0的话,与运算完了肯定是0,所以如果要让定时器工作,那么TR0就必须置1。

(2)这里的与门结果要想得到1,那么前面的或门出来的结果必须也得是1才行。在GATE位为1的情况下,经过一个非门变成0,或门电路结果要想是1的话,那INT0即P3.2引脚必须是1的情况下,这个时候定时器才会工作,而INT0引脚是0的情况下,定时器不工作,这就是GATE位的作用。

(3)当GATE位为0的时候,经过一个非门会变成1,那么不管INT0引脚是什么电平,经过或门电路后都肯定是1,定时器就会工作。

(4)要想让定时器工作,就是自动加1,从图上看有两种方式,第一种方式是那个开关打到上边的箭头,就是C/T=0的时候,一个机器周期TL就会加1一次,当开关打到下边的箭头,即C/T =1的时候,T0引脚即P3.4引脚来一个脉冲,TL就加1一次,这也就是计数器功能。

定时器的应用

  1. 设置特殊功能寄存器TMOD,配置好工作模式。
  2. 设置计数寄存器TH0和TL0的初值。
  3. 设置TCON,通过TR0置1来让定时器开始计数。
  4. 判断TCON寄存器的TF0位,监测定时器溢出情况。

计算如何用定时器定时时间

晶振是11.0592M,时钟周期就是1/11059200,机器周期是12/11059200,假如要定时20ms,就是0.02秒,要经过x个机器周期得到0.02秒,下面来算一下x*12/11059200=0.02,得到x= 18432。16位定时器的溢出值是65536(因65535再加1才是溢出),于是就可以这样操作,先给TH0和TL0一个初始值,让它们经过18432个机器周期后刚好达到65536,也就是溢出,溢出后可以通过检测TF0的值得知,就刚好是0.02秒。那么初值y=65536-18432=47104,转成十六进制就是0xB800,也就是TH0=0xB8,TL0=0x00。

这样0.02秒的定时就做出来了,细心的读者会发现,如果初值直接给一个0x0000,一直到65536溢出,定时器定时值最大也就是71ms左右,那么想定时更长时间怎么办呢?用小学学过的逻辑,倍数关系就可以解决此问题。

 #include <reg52.h>
    
    sbit LED = P0^0;
    sbit ADDR0 = P1^0;
    sbit ADDR1 = P1^1;
    sbit ADDR2 = P1^2;
    sbit ADDR3 = P1^3;
    sbit ENLED = P1^4;
    
    void main()
    {
      unsigned char cnt = 0;  //定义一个计数变量,记录T0溢出次数
    
      ENLED = 0;              //使能U3,选择独立LED
      ADDR3 = 1;
      ADDR2 = 1;
      ADDR1 = 1;
      ADDR0 = 0;
      TMOD = 0x01;             //设置T0为模式1
      TH0 = 0xB8;             //为T0赋初值0xB800
      TL0 = 0x00;
      TR0 = 1;              //启动T0
    
      while (1)
      {
        if (TF0 == 1)          //判断T0是否溢出
        {
          TF0 = 0;          //T0溢出后,清零中断标志
          TH0 = 0xB8;         //并重新赋初值
          TL0 = 0x00;
          cnt++;           //计数值自加1
          if (cnt >= 50)       //判断T0溢出是否达到50次
          {                   
            cnt = 0;        //达到50次后计数值清零
            LED = ~LED;       //LED取反:0-->1、1-->0
          }
        }
      }
    }

本程序实现的结果是开发板上最右边的小灯点亮一秒,熄灭一秒,也就是以0.5Hz的频率进行闪烁。

数码管学习

图5-3的数码管上边有2个com,这就是数码管的公共端,用2个com可以把公共电流平均到2个引脚上去,降低单条线路承受的电流。

从开发板的电路图上能看出来,所用的数码管都是共阳数码管,一共有6个,如图5-5所示。

 6个数码管的com都是接到了正极上,当然了,和LED小灯电路一样,也是由74HC138控制三极管的导通来控制整个数码管的使能。先来看最右边的DS1这个数码管,原理图上可以看出,控制DS1的三极管是Q17,控制Q17的引脚是LEDS0,对应到74HC138上边就是U3的Y0输出,如图5-6所示。

 数码管的真值表

数码管的8个段直接当成8个LED小灯来控制,那就是a、b、c、d、e、f、g、dp一共8个LED小灯。通过图5-3可以看出,如果点亮b和c这两个LED小灯,也就是数码管的b段和c段,其他的所有的段都熄灭的话,就可以让数码管显示出一个数字1,那么这个时候实际上P0的值就是0b11111001,十六进制就是0xF9。那么写一个程序进去,来看一看数码管显示的效果,代码如下:

#include <reg52.h>
    
    sbit ADDR0 = P1^0;
    sbit ADDR1 = P1^1;
    sbit ADDR2 = P1^2;
    sbit ADDR3 = P1^3;
    sbit ENLED = P1^4;
    
    void main()
    {
      ENLED = 0;      //使能U3,选择数码管DS1
      ADDR3 = 1;
      ADDR2 = 0;
      ADDR1 = 0;
      ADDR0 = 0;
      P0 = 0xF9;      //点亮数码管段b和c
      while (1);
    }
共阳数码管真值表
字符01234567
数值0xc00xf90xa40xb00x990x920x820xf8
字符89ABCDEF
数值0x800x900x880x830xc60xa10x860x8e

数码管共阴极
    unsigned char gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

 数码管的静态显示

先来介绍一个51单片机的关键字code。一般用到unsigned char或者unsigned int这两个关键字,这样定义的变量都是放在单片机的RAM中在程序中可以随意去改变这些变量的值。在程序中要使用,但是却不会改变它的值,定义这种数据时可以加一个code关键字修饰一下,这个数据就会存储到程序空间Flash中,这样可以大大节省单片机的RAM的使用量。可以用code关键字把它放入Flash中。

#include <REG52.H>

typedef unsigned char u8;
typedef unsigned int u16;

sbit ADDR0 = P1 ^ 0;
sbit ADDR1 = P1 ^ 1;
sbit ADDR2 = P1 ^ 2;
sbit ADDR3 = P1 ^ 3;
sbit ENLED = P1 ^ 4;

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

void main()
{
    u8 cnt = 0,sec=0;
    ENLED = 0;
    ADDR3=1;
    ADDR2=0;
    ADDR1=0;
    ADDR0=0;
    TMOD = 0x01;
    TH0=0xB8;
    TL0=0x00;
    TR0=1;

    while(1){
        if(TF0 ==1){
            TF0=0;
            TH0=0xB8;
            TL0=0x00;
            cnt++;
            if(cnt >= 50){
                cnt =0;
                P0=LedChar[sec];
                sec++;
                if(sec >=16){
                    sec=0;
                }
            }
        }
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值