登录后台

页面导航

本文编写于 277 天前,最后修改于 196 天前,其中某些信息可能已经过时。

0. 写在开头

代码风格与小蜜蜂老师是差不多的,后来我自己做了修改,代码风格在最后

本文代码使用 CCS 进行格式化(VSCode 不是很好用)

已更新完成(24.01.20 - 24.04.01)

1. LED 指示灯的基本控制

  • 引入基本头文件
#include<STC15F2K60S2.h>

蓝桥杯使用的单片机是 IAP15F2K61S2
在这里的头文件用的是 STC15F2K60S2
IAP 表示有调试功能
倒数三四位 61 和 60 表示的是程序空间大小,单位千字节
两者并无大的区别,在创建文件时也是用后者


#include<reg52.h>

这个头文件也可以,但是定义有部分缺失,需要后续添加定义,为避免麻烦直接选择上者


  • 为方便控制,在头文件下方定义引脚
sbit HC138_A = P2^5;
sbit HC138_B = P2 ^ 6;
sbit HC138_C = P2 ^ 7;

查询原理图可知,8 个 LED 由锁存器 74HC573 控制,LED 阳极接 VCC,阴极接锁存器输出,需要让锁存器输出低电平,才能点亮 LED 灯。锁存器使能端为 Y4C(高电平有效),Y4 和 WR 通过或非门输出为 Y4C,只要让 Y4 为低电平即可。

  • 创建一个延时函数
void Delay(unsigned int t)
{
    while(t--);
}


  • 点亮 LED
void LED()
{
    HC138_A = 0;
    HC138_B = 0;
    HC138_C = 1;

    P0 = 0x00;  //灯全亮
    Delay(60000);
    P0 = 0xff;//灯全灭
    Delay(60000);
}


  • 创建 main 函数
void main()
{
    while(1)
    {
        LED();
    }
}


  • 流水灯
void LED()
{
    unsigned char i;
    HC138_A = 0;
    HC138_B = 0;
    HC138_C = 1;  //使Y4为低电平

    for(i = 1; i <= 8; i++)
    {
        P0 =0xff << i;
        Delay(60000);
    }

    for(i = 1; i <= 8; i++)
    {
        P0 = ~(0xff << i);
        Delay(60000);
    }
}

~对操作数的每一位执行逻辑取反操作,即将每一位的 0 变为 1,1 变为 0。
<<为左移操作,将操作数的所有位向左移动指定的位数。
原来的数:1 1 1 1 1 1 1 1
左移一次:1 1 1 1 1 1 1 0
左移两次:1 1 1 1 1 1 0 0

2. 蜂鸣器与继电器的基本控制

蜂鸣器和继电器由另一片锁存器 74HC573 控制,使能端为 Y5C,让译码器 74HC138 输出 Y5 为低电平即可使用蜂鸣器与继电器

  • 控制继电器和蜂鸣器
// 头文件,引脚定义,主函数未写
void RelayBuzz()
{
    HC138_A = 1;
    HC138_B = 0;
    HC138_C = 1;  //使Y5为低电平

    P0 = 0x10;//继电器吸合
    Delay(60000);

    P0 = 0x00;
    Delay(60000);

    P0 = 0x40;//蜂鸣器响
    Delay(60000);

    P0 = 0x00;
    Delay(60000);
}


  • 优化(对 P0 的控制更加方便)
void Init74HC138(unsigned char n)
{
    switch(n)
    {
        case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
        case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
        case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
        case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void OutputP0(unsigned char channel, unsigned char dat)
{
    Init74HC138(channel);
    P0 = dat;
}

用这两个函数,就不用定义 74HC138 的引脚了。
使用 OutputP0(5, 0x10);这一语句就可以让继电器吸合。
使用 OutputP0(4, 0x00);这一语句就可以让 8 个 LED 亮。

  • 初始化

借助这个函数(调用引脚也行)进行初始化(让蜂鸣器不响)

void Init()
{
    OutputP0(5, 0x00);
}

3. 共阳数码管的静态显示

  • 共阳数码管的段码表
unsigned char code SMG[18] =
{   0xc0,0xf9,0xa4,0xb0,  //0 1 2 3
    0x99,0x92,0x82,0xf8,//4 5 6 7
    0x80,0x90,0x88,0x83,//8 9 A b
    0xc6,0xa1,0x86,0x8e,//C d E F
    0xbf,0x7f};  //- .
  • 原理

Y6C 位选
先用 74HC138 选择锁存器 Y6C,然后给想要点亮的数码管赋值锁存。
P0=0x11 将点亮第一个和第五个数码管(从左往右数)
Y7C 段选
先用 74HC138 选择锁存器 Y7C,直接给 P0 赋值锁存,P0=SMG[i];
P0=SMG[5]数码管就显示数字 5

  • 八位数码管全部显示 0-F

0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2
    ……

#include <STC15F2K60S2.h>

unsigned char code SMG[16] =
{   0xc0,0xf9,0xa4,0xb0,
    0x99,0x92,0x82,0xf8,
    0x80,0x90,0x88,0x83,
    0xc6,0xa1,0x86,0x8e};

void Delay(unsigned int t)
{
    while (t--)
        ;
    while (t--)
        ;
}

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void OutputP0(unsigned char channel, unsigned char dat)
{
    Init74HC138(channel);
    P0 = dat;
}

void showSMG(unsigned char dat, unsigned pos)
{
    OutputP0(6, pos);
    OutputP0(7, dat);
}

void Sta_Smg()
{
    unsigned char i;
    for (i = 0; i < 16; i++)
    {
        showSMG(SMG[i], 0xff);
        Delay(60000);
        Delay(60000);
        Delay(60000);
    }
}
void main()
{
    OutputP0(4, 0xff);
    OutputP0(5, 0x00);
    while (1)
    {
        Sta_Smg();
    }
}


  • 八位数码管轮流显示 0-F

0 X X X X X X X
X 1 X X X X X X
X X 2 X X X X X
    ……

#include <STC15F2K60S2.h>

unsigned char code SMG[16] =
{   0xc0,0xf9,0xa4,0xb0,
    0x99,0x92,0x82,0xf8,
    0x80,0x90,0x88,0x83,
    0xc6,0xa1,0x86,0x8e};

void Delay(unsigned int t)
{
    while (t--)
        ;
    while (t--)
        ;
}

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void OutputP0(unsigned char channel, unsigned char dat)
{
    Init74HC138(channel);
    P0 = dat;
}

void ShowSMG(unsigned char dat, unsigned pos)
{
    OutputP0(6, 0x01 << pos);
    OutputP0(7, dat);
}

void Sta_Smg()
{
    unsigned char i = 0, j = 0;
    while (1)
    {
        ShowSMG(SMG[i], j);
        Delay(60000);
        i++;
        j++;
        if (i > 15)
            i = 0;
        if (j > 7)
            j = 0;
    }
}
void main()
{
    OutputP0(4, 0xff);
    OutputP0(5, 0x00);
    while (1)
    {
        Sta_Smg();
    }
}

4. 共阳数码管的动态显示

  • 原理

利用快速循环扫描和人眼的视觉暂留效应实现

  • 显示 2024 0123
#include <STC15F2K60S2.h>

unsigned char code SMG[18] =
{   0xc0,0xf9,0xa4,0xb0,  //0 1 2 3
    0x99,0x92,0x82,0xf8,//4 5 6 7
    0x80,0x90,0x88,0x83,//8 9 A b
    0xc6,0xa1,0x86,0x8e,//C d E F
    0xbf,0x7f};  //- .

void Delay_SMG(unsigned char t)
{
    while (t--)
        ;  // 奇怪的CCS格式化,正常不用分两行写
}

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void OutputP0(unsigned char channel, unsigned char dat)
{
    Init74HC138(channel);
    P0 = dat;
}

void ShowSMG_Bit(unsigned char dat, unsigned char pos)
{
    Init74HC138(6);
    P0 = 0x01 << pos;
    Init74HC138(7);
    P0 = dat;
}

void Display()
{
    ShowSMG_Bit(SMG[2], 0);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[0], 1);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[2], 2);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[4], 3);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[0], 4);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[1], 5);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[2], 6);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[3], 7);
    Delay_SMG(500);
}

void main()
{
    OutputP0(4, 0xff);
    OutputP0(5, 0x00);
    while (1)
    {
        Display();
    }
}


  • 显示 2024 的日历
#include <STC15F2K60S2.h>

unsigned char code SMG[18] =
{   0xc0,0xf9,0xa4,0xb0,
    0x99,0x92,0x82,0xf8,
    0x80,0x90,0x88,0x83,
    0xc6,0xa1,0x86,0x8e,
    0xbf,0x7f};
unsigned char MON = 1;
unsigned char DAY = 1;

void Delay_SMG(unsigned char t)
{
    while (t--)
        ;
}

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void OutputP0(unsigned char channel, unsigned char dat)
{
    Init74HC138(channel);
    P0 = dat;
}

void ShowSMG_Bit(unsigned char dat, unsigned char pos)
{
    Init74HC138(6);
    P0 = 0x01 << pos;
    Init74HC138(7);
    P0 = dat;
}

void Display()
{
    ShowSMG_Bit(SMG[2], 0);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[0], 1);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[2], 2);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[4], 3);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[MON / 10], 4);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[MON % 10], 5);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[DAY / 10], 6);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[DAY % 10], 7);
    Delay_SMG(500);
}

void Delay(unsigned int t)
{
    while (t--)
    {
        Display();
    }
}
void main()
{
    OutputP0(4, 0xff);
    OutputP0(5, 0x00);
    while (1)
    {
        Display();
        DAY++;
        switch (MON)
        {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
            if (DAY == 32)
            {
                MON++;
                DAY = 1;
            }
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            if (DAY == 31)
            {
                MON++;
                DAY = 1;
            }
            break;
        case 2:
            if (DAY == 29)
            {
                MON++;
                DAY = 1;
            }
            break;
        case 12:
            if (DAY == 32)
            {
                DAY = 1;
                MON = 1;
            }
            break;
        }
        Delay(100);
    }
}

后注:事实上,在实际使用时,使用这样的代码显示数码管会造成很多不必要的麻烦。比如说,在按下按键时,数码管会部分不显示,需要写很多状态位……将数码管显示内容放入定时器中, 是一个比较好的方案

5. 独立按键的基本操作与扩展应用

先将 J5 跳线帽改到 2 3 两脚,选择 BTN 模式。
对应关系如下:
S7 ---> P30
S6 ---> P31
S5 ---> P32
S4 ---> P33

  • 独立按键的基本操作

S7 控制 L1,S6 控制 L2,S5 控制 L3,S4 控制 L4。按下点亮,松手熄灭。

#include <STC15F2K60S2.h>

sbit S7 = P3^0;
sbit S6 = P3 ^ 1;
sbit S5 = P3 ^ 2;
sbit S4 = P3 ^ 3;

sbit L1 = P0 ^ 0;
sbit L2 = P0 ^ 1;
sbit L3 = P0 ^ 2;
sbit L4 = P0 ^ 3;

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void Delay(unsigned char t)
{
    while (t--)
        ;
}

void ScanKey_Alone()
{
    if (S7 == 0)
    {
        Delay(100);
        if (S7 == 0)
        {
            L1 = 0;
            while (S7 == 0)
                ;
            L1 = 1;
        }
    }
    if (S6 == 0)
    {
        Delay(100);
        if (S6 == 0)
        {
            L2 = 0;
            while (S6 == 0)
                ;
            L2 = 1;
        }
    }
    if (S5 == 0)
    {
        Delay(100);
        if (S5 == 0)
        {
            L3 = 0;
            while (S5 == 0)
                ;
            L3 = 1;
        }
    }
    if (S4 == 0)
    {
        Delay(100);
        if (S4 == 0)
        {
            L4 = 0;
            while (S4 == 0)
                ;
            L4 = 1;
        }
    }
}

void main()
{
    Init74HC138(5);
    P0 = 0x00;  //关蜂鸣器
    Init74HC138(4);
    P0 = 0xff;  //先把灯全灭掉
    while (1)
    {
        ScanKey_Alone();
    }
}


  • 独立按键的扩展应用

(题目表述好复杂)
按键 S7 和 S6 为选择键,确定控制键控制那组 LED 指示灯。按键 S5 和 S4 为控制键,按键该键点亮指定的 LED 指示灯,松开后熄灭。
按下 S7 点亮 L1 指示灯,L1 点亮后,S6 不响应操作,S5 控制 L3,S4 控制 L4,再次按下 S7,L1 指示灯熄灭,S6 可响应操作。
按下 S6 点亮 L2 指示灯,L2 点亮后,S7 不响应操作,S5 控制 L5,S4 控制 L6,再次按下 S6,L2 指示灯熄灭,S7 可响应操作。
S7 和 S6 未按下时,即 L1 或 L2 未点亮时 S5 和 S4 不响应操作也就是未作 LED 灯区域选择时,控制键不能操作。

#include <STC15F2K60S2.h>

sbit S7 = P3^0;
sbit S6 = P3 ^ 1;
sbit S5 = P3 ^ 2;
sbit S4 = P3 ^ 3;

sbit L1 = P0 ^ 0;
sbit L2 = P0 ^ 1;
sbit L3 = P0 ^ 2;
sbit L4 = P0 ^ 3;
sbit L5 = P0 ^ 4;
sbit L6 = P0 ^ 5;

unsigned char flag = 0;

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void Delay(unsigned char t)
{
    while (t--)
        ;
}

void ScanKey_Alone()
{
    if (S7 == 0)
    {
        Delay(100);
        if (S7 == 0)
        {
            if (flag == 0)
            {
                L1 = 0;
                flag = 1;
            }
            else if (flag == 1)
            {
                L1 = 1;
                flag = 0;
            }
            while (S7 == 0)
                ;
        }
    }

    if (S6 == 0)
    {
        Delay(100);
        if (S6 == 0)
        {
            if (flag == 0)
            {
                L2 = 0;
                flag = 2;
            }
            else if (flag == 2)
            {
                L2 = 1;
                flag = 0;
            }
            while (S6 == 0)
                ;
        }
    }

    if (S5 == 0)
    {
        Delay(100);
        if (S5 == 0)
        {
            if (flag == 1)
            {
                L3 = 0;
                while (S5 == 0)
                    ;
                L3 = 1;
            }
            if (flag == 2)
            {
                L5 = 0;
                while (S5 == 0)
                    ;
                L5 = 1;
            }
        }
    }

    if (S4 == 0)
    {
        Delay(100);
        if (S4 == 0)
        {
            if (flag == 1)
            {
                L4 = 0;
                while (S4 == 0)
                    ;
                L4 = 1;
            }
            if (flag == 2)
            {
                L6 = 0;
                while (S4 == 0)
                    ;
                L6 = 1;
            }
        }
    }
}

void main()
{
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    while (1)
    {
        ScanKey_Alone();
    }
}

6. 矩阵键盘的扫描原理与基本应用

矩阵键盘原理: 逐行( Row)
扫描, 逐列( Column)
读取

先给所有列先加上高电平,再给每行依次加低电平(其余行高电平)。当按钮按下时,扫描到此行为低电平,会将对应的列由高电平拉至低电平, 读取列即可知道这一行是哪个按钮被按下

  • 示例:按下按钮,按顺序输出 0 ~ F
#include <STC15F2K60S2.h>

sbit R1 = P3^0;
sbit R2 = P3 ^ 1;
sbit R3 = P3 ^ 2;
sbit R4 = P3 ^ 3;

sbit C1 = P4 ^ 4;  //使用此头文件不需要额外定义P4
sbit C2 = P4 ^ 2;
sbit C3 = P3 ^ 5;
sbit C4 = P3 ^ 4;

unsigned char code SMG[16] =
{   0xc0,0xf9,0xa4,0xb0,
    0x99,0x92,0x82,0xf8,
    0x80,0x90,0x88,0x83,
    0xc6,0xa1,0x86,0x8e};

void Delay(unsigned char t)
{
    while (t--)
        ;
}

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void ShowSMG(unsigned char value)
{
    Init74HC138(6);
    P0 = 0x01;
    Init74HC138(7);
    P0 = value;
}

void ScanKeys()
{
    R1 = 0; //扫描第一行
    R2 = R3 = R4 = 1; //其余行为高电平
    C1 = C2 = C3 = C4 = 1;  //每列都设置为高电平
    if (C1 == 0)  //如果第一列高电平被拉低(说明按下了按钮)
    {
        while (C1 == 0)
            ;
        ShowSMG(SMG[0]);
    }
    else if (C2 == 0)
    {
        while (C2 == 0)
            ;
        ShowSMG(SMG[1]);
    }
    else if (C3 == 0)
    {
        while (C3 == 0)
            ;
        ShowSMG(SMG[2]);
    }
    else if (C4 == 0)
    {
        while (C4 == 0)
            ;
        ShowSMG(SMG[3]);
    }

    R2 = 0;  //扫描第二行
    R1 = R3 = R4 = 1;
    C1 = C2 = C3 = C4 = 1;
    if (C1 == 0)
    {
        while (C1 == 0)
            ;
        ShowSMG(SMG[4]);
    }
    else if (C2 == 0)
    {
        while (C2 == 0)
            ;
        ShowSMG(SMG[5]);
    }
    else if (C3 == 0)
    {
        while (C3 == 0)
            ;
        ShowSMG(SMG[6]);
    }
    else if (C4 == 0)
    {
        while (C4 == 0)
            ;
        ShowSMG(SMG[7]);
    }

    R3 = 0;  //扫描第三行
    R1 = R2 = R4 = 1;
    C1 = C2 = C3 = C4 = 1;
    if (C1 == 0)
    {
        while (C1 == 0)
            ;
        ShowSMG(SMG[8]);
    }
    else if (C2 == 0)
    {
        while (C2 == 0)
            ;
        ShowSMG(SMG[9]);
    }
    else if (C3 == 0)
    {
        while (C3 == 0)
            ;
        ShowSMG(SMG[10]);
    }
    else if (C4 == 0)
    {
        while (C4 == 0)
            ;
        ShowSMG(SMG[11]);
    }

    R4 = 0;  //扫描第四行
    R1 = R2 = R3 = 1;
    C1 = C2 = C3 = C4 = 1;
    if (C1 == 0)
    {
        while (C1 == 0)
            ;
        ShowSMG(SMG[12]);
    }
    else if (C2 == 0)
    {
        while (C2 == 0)
            ;
        ShowSMG(SMG[13]);
    }
    else if (C3 == 0)
    {
        while (C3 == 0)
            ;
        ShowSMG(SMG[14]);
    }
    else if (C4 == 0)
    {
        while (C4 == 0)
            ;
        ShowSMG(SMG[15]);
    }
}

void main()
{
    Init74HC138(4);
    P0 = 0xff;
    Init74HC138(5);
    P0 = 0x00;
    while (1)
    {
        ScanKeys();
    }
}

7. 中断系统与外部中断应用

中断次序和中断结构查 datasheet
以 INT0 为例(INT0 具有最高优先级)
INT0 对应 P32,即独立按键 S5

void Init_INT0()  //中断函数初始化
{
    IT0 = 1;  //0为低电平触发,1为下降沿触发
    EX0 = 1;  //EX0使能端,高电平有效
    EA = 1;  //EA使能端,高电平有效
}

void Service_INT0()
interrupt 0
{

}


再以 INT1 为例
INT1 对应 P33,即独立按键 S4
要改的就是两个使能端和中断次序

void Init_INT1()  //中断函数初始化
{
    IT1 = 1;  //0为低电平触发,1为下降沿触发
    EX1 = 1; //EX1使能端,高电平有效
    EA = 1; //EA使能端,高电平有效
}

void Service_INT1()
interrupt 1
{

}


例:L1 闪烁,当按下 S5 时,L8 闪烁

#include <STC15F2K60S2.h>

sbit L1 = P0^0;
sbit L8 = P0 ^ 7;

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    }
}
void Delay(unsigned int t)
{
    while (t--)
        ;
    while (t--)
        ;
}

/********************/
void Init_INT0()  //中断函数初始化
{
    IT0 = 1;
    EX0 = 1;
    EA = 1;
}

void Service_INT0()
interrupt 0
{
    Init74HC138(4);
    L8 = 0;
    Delay(60000);
    Delay(60000);
    L8 = 1;
    Delay(60000);
    Delay(60000);
}
/********************/

void Working()
{
    Init74HC138(4);
    L1 = 0;
    Delay(60000);
    L1 = 1;
    Delay(60000);
}

void main()
{
    Init_INT0();
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    while (1)
    {
        Working();
    }
}

这样子写,当按下 S5 时,中断函数立刻执行。(按下 S5 时 L8 闪烁,L1 可能亮也可能不亮)

#include <STC15F2K60S2.h>

sbit L1 = P0^0;
sbit L8 = P0 ^ 7;

unsigned char stat_int = 0;

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    }
}
void Delay(unsigned int t)
{
    while (t--)
        ;
    while (t--)
        ;
}

void LEDINT()
{
    if (stat_int == 1)
    {
        Init74HC138(4);
        L8 = 0;
        Delay(60000);
        Delay(60000);
        L8 = 1;
        Delay(60000);
        Delay(60000);
    }
    stat_int = 0;
}

/********************/
void Init_INT0()
{
    IT0 = 1;
    EX0 = 1;
    EA = 1;
}

void Service_INT0()
interrupt 0
{
    stat_int = 1;
}
/********************/

void Working()
{
    Init74HC138(4);
    L1 = 0;
    Delay(60000);
    L1 = 1;
    Delay(60000);
}

void main()
{
    Init_INT0();
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    while (1)
    {
        Working();
        LEDINT();
    }
}

这样子写,当按下 S5 时,中断函数改变标志状态,不会立刻执行。(按下 S5 时 L8 闪烁,L1 一定不亮)

8. 定时器的基本原理与应用

51 单片机有两个定时/计数器 T0 和 T1,为 16 位加法计数器,由低 8 位 TLx 和高 8 位 THx 两个寄存器组成,最大计数值为 65535 个计数脉冲。
单片机选用 12MHz 频率,经过十二分频后为脉冲为 1MHz,周期为 1μs,最大定时时间为 65.535ms,通过设定计数的起始值即可控制定时时长。
设定时时间为 t,单位 μs
TH0 = (65535 - t) / 256;
TL0 = (65535 - t) % 256;
若是不记得公式,打开 STC-ISP,找到定时器计算器,输入参数,复制代码
设定定时时长 50ms,定时器部分代码如下

void Init_Timer0()
{
    TMOD = 0x01;  //设定为16位计数模式
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;//设定定时时长50ms
    ET0 = 1; //定时器0使能开启
    EA = 1; //总中断使能开启
    TR0 = 1; //启动定时器
}

void Service_Timer0()
interrupt 1
{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;  //重新赋值
    /*要实现的功能*/
}


例:让 L1 以 0.5s 周期闪烁,L8 以 2s 周期闪烁

#include <STC15F2K60S2.h>

sbit L1 = P0^0;
sbit L8 = P0 ^ 7;

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    }
}

/********************/
void Init_Timer0()
{
    TMOD = 0x01;
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;  //50ms
    ET0 = 1;
    EA = 1;
    TR0 = 1;
}

unsigned int cnt_1 = 0;
unsigned int cnt_8 = 0;

void Service_Timer0()
interrupt 1
{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;  //50ms
    cnt_1++;
    cnt_8++;
    if(cnt_1 == 10)
    {
        L1 = ~L1;
        cnt_1 = 0;
    }
    if(cnt_8 == 40)
    {
        L8 = ~L8;
        cnt_8 = 0;
    }
}
/********************/

void main()
{
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    Init_Timer0();
    while (1)
    {

    }
}



例:使用数码管动态显示和定时器设计数字钟

#include <STC15F2K60S2.h>

unsigned int SEC = 0;
unsigned int MIN = 0;
unsigned int HOU = 0;
unsigned char code SMG[18] =
{   0xc0,0xf9,0xa4,0xb0,  //0 1 2 3
    0x99,0x90,0x82,0xf8,//4 5 6 7
    0x80,0x90,0x88,0x83,//8 9 A b
    0xc6,0xa1,0x86,0x8e,//C d E F
    0xbf,0x7f};  //- .

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

/********************/
void Init_Timer0()
{
    TMOD = 0x01;
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;  //50ms
    ET0 = 1;
    EA = 1;
    TR0 = 1;
}

unsigned int cnt = 0;

void Service_Timer0()
interrupt 1
{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;  //50ms
    cnt++;
    if(cnt == 20 )
    {
        SEC++;
        cnt = 0;
    }
}
/********************/

void Delay_SMG(unsigned int t)
{
    while (t--)
        ;
}

void ShowSMG_Bit(unsigned char dat, unsigned char pos)
{
    Init74HC138(6);
    P0 = 0x01 << pos;
    Init74HC138(7);
    P0 = dat;
}

void Display()
{
    ShowSMG_Bit(SMG[HOU / 10], 0);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[HOU % 10], 1);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[16], 2);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[MIN / 10], 3);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[MIN % 10], 4);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[16], 5);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[SEC / 10], 6);
    Delay_SMG(500);
    ShowSMG_Bit(SMG[SEC % 10], 7);
    Delay_SMG(500);
}

void main()
{
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    Init_Timer0();
    while (1)
    {
        Display();
        if (SEC == 60)
        {
            SEC = 0;
            MIN++;
        }
        if (MIN == 60)
        {
            MIN = 0;
            HOU++;
        }
        if (HOU == 24)
        {
            HOU = 0;
        }
    }
}

9.定时器的进阶综合案例解析

  • 定时器实现秒表功能

    1.显示格式为:分-秒-0.05 秒(即 50ms) 08-26-18 表示:8 分 26 秒 900 毫秒 2.独立按键 S4 为:暂停/启动 3.独立按键 S5 为:清零

#include <STC15F2K60S2.h>

sbit S5 = P3^2;
sbit S4 = P3 ^ 3;

unsigned int MS = 0;
unsigned int SEC = 0;
unsigned int MIN = 0;
unsigned char code SMG[18] =
{   0xc0,0xf9,0xa4,0xb0,  //0 1 2 3
    0x99,0x92,0x82,0xf8,//4 5 6 7
    0x80,0x90,0x88,0x83,//8 9 A b
    0xc6,0xa1,0x86,0x8e,//C d E F
    0xbf,0x7f};  //- .

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void ShowSMG(unsigned char dat, unsigned char pos)
{
    Init74HC138(6);
    P0 = 0x01 << pos;
    Init74HC138(7);
    P0 = dat;
}

void Display()
{
    ShowSMG(SMG[MIN / 10], 0);  //分钟的十位
    Delay(500);
    ShowSMG(SMG[MIN % 10], 1);  //分钟的个位
    Delay(500);
    ShowSMG(SMG[16], 2);
    Delay(500);
    ShowSMG(SMG[SEC / 10], 3);
    Delay(500);
    ShowSMG(SMG[SEC % 10], 4);
    Delay(500);
    ShowSMG(SMG[16], 5);
    Delay(500);
    ShowSMG(SMG[MS / 10], 6);
    Delay(500);
    ShowSMG(SMG[MS % 10], 7);
    Delay(500);
}

/********************/
void Init_Timer0()
{
    TMOD = 0x01;
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    ET0 = 1;
    EA = 1;
    TR0 = 1;
}

void Service_Timer0()
interrupt 1
{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    MS++;
    if(MS == 20)
    {
        SEC++;
        MS = 0;
    }
    if(SEC == 60)
    {
        SEC = 0;
        MIN++;
    }
    if(MIN == 60)
    {
        MIN = 0;
    }
}
/********************/

void ScanKey()
{
    if (S4 == 0)
    {
        Delay(100);
        if (S4 == 0)
        {
            while (S4 == 0)
            {
                Display();
            }
            TR0 = ~TR0;
        }
    }
    if (S5 == 0)
    {
        Delay(100);
        if (S5 == 0)
        {
            MS = SEC = MIN = 0;
        }
    }
}

void main()
{
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    Init_Timer0();
    while (1)
    {
        ScanKey();
        Display();
    }
}

10. PWM 脉宽调制信号的发生与控制

例:使用 PWM 波实现按键 S7 控制 L1 灯
具体要求如下:
1.PWM 脉宽信号的频率为 100Hz 2.系统上电后 L1 指示灯处在熄灭状态
3.L1 指示灯有 4 种亮度模式,分别是完全熄灭、10%的亮度、50%的亮度和 90%的亮度 4.按下 S7 按键,循环切换 L1 指示灯的四种亮度模式

100Hz ---> 10ms == 10000μs
将 10000μs 分成 100 份,前 50 份高电平,后 50 份低电平,即可实现 50%占空比方波

代码实现如下:

#include <STC15F2K60S2.h>

sbit S7 = P3^0;
sbit L1 = P0 ^ 0;

unsigned char MOD = 0;
unsigned char PWM = 0;
unsigned int cnt = 0;

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

/********************/
void Init_Timer0()
{
    TMOD = 0x01;
    TH0 = (65536 - 100) / 256;
    TL0 = (65536 - 100) % 256;
    ET0 = 1;
    EA = 1;
}

void Service_Timer0()
interrupt 1
{
    TH0 = (65536 - 100) / 256;
    TL0 = (65536 - 100) % 256;
    cnt++;
    if(cnt == PWM)
    {
        L1 = 1;
    }
    if(cnt == 100)
    {
        L1 = 0;
        cnt = 0;
    }
}
/********************/

void ScanKey()
{
    if (S7 == 0)
    {
        Delay(1000);
        if (S7 == 0)
        {
            if (MOD == 0) //此处用switch也行
            {
                TR0 = 1;
                PWM = 10;
                L1 = 0;
                MOD++;
            }
            else if (MOD == 1)
            {
                PWM = 50;
                L1 = 0;
                MOD++;
            }
            else if (MOD == 2)
            {
                PWM = 90;
                L1 = 0;
                MOD++;
            }
            else if (MOD == 3)
            {
                TR0 = 0; //关灯时也要关定时器
                PWM = 0;
                L1 = 1;
                MOD = 0;
            }
            while (S7 == 0)
                ;
        }
    }
}

void main()
{
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;  //关灯,满足题目要求(上电灯不亮)
    Init_Timer0();
    while (1)
    {
        ScanKey();
    }
}

11. 串口通信的基本原理与应用

  • 串行通信简介

    1.串行通信是指数据一位接一位地顺序发送或接收。 2.串行通信有 SPI、IIC、UART 等多种,最常见最通用的是指 UART。 3.串行通信的制式有:单工、半双工、全双工三种。单工只能收或发;半双工收发都可以,但不能同时进行;全双工收发都可以,也可以同时进行。 4.波特率:每秒钟传输的位数,9600 波特率就是指每秒钟传输 9600 位。

  • 关于波特率

在 51 单片机的串口通信中,模式 1 和模式 3 的波特率是可变的,取决于定时器 1 的溢出率,也就是说定时器 1 每溢出 1 次,串口就发送一次数据。
通常使用定时器 1 的工作模式 2 (8 位自动重装)来产生波特率,TL1 作为脉冲计数寄存器,TH1 作为自动重装寄存器,当计数到最大值溢出时,TH1 的值会自动装到 TL1 中。
12M 晶振或 11.0592M 晶振的情况下,要产生 9600BPS 的波特率
SMOD=0 时,参数为 0xfd,datasheet 第 8 章串行口通信第 3 部分波特率设置有,写的是 FDH,即十六进制 FD
SMOD=1 时,参数为 0xfa,同理

  • 数据收发

数据发送,把数据扔进 SBUF 后,内核会自动将数据发送出去,内容发生完成后,会将 TI 标志位置 1。
发送数据程序: SBUF = 数据/变量; 如: SBUF = 0x58
数据接收,内核从串口接收到一个完整的数据后,会将 RI 标志位置 1,用户用 SBUF 直接读取即可。
接收数据程序:变量 =SBUF;如: dat = SBUF

  • 串口控制寄存器 SCON

异步 8 位 UART 并且允许接收: SCON = 0x50,一般都是这个,具体情况见 datasheet 第 8 章串行口通信第一部分

  • 例题

利用 51 单片机串行接口与上位机建立传输信道进行数据的收发。采用 8 位的 UART 模式,即模式 1,波特率为 9600。数据发送采用查询方式,数据接收采用中断方式。系统上电初始化之后,单片机向上位机发送两个字节: 0x5a 和 0xa5,然后等待接收上位机的数据,每接收到一个字节后,在该字节的基础上加 1 然后返回给上位机。

STC-ISP 找到 USB-CDC/串口助手,收发都选择 HEX 模式,波特率选择 9600

#include <STC15F2K60S2.h>

unsigned char tmpRecv;

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

/********************/
void Init_Uart()
{
    TMOD = 0x20;
    TH1 = 0xfd;
    TL1 = 0xfd;
    AUXR = 0x00;
    TR1 = 1;
    SCON = 0x50;
    ES = 1;
    EA = 1;
}

void SendByte(unsigned char dat)
{
    SBUF = dat;
    while (TI == 0)
        ;
    TI = 0;
}

void Sevice_Uart()
interrupt 4  //中断号查datasheet 6.3 中断优先级
{
    if(RI == 1)
    {
        RI = 0;
        tmpRecv = SBUF;
        SendByte(tmpRecv + 1);
    }
}
/********************/

void main()
{
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    Init_Uart();
    SendByte(0x5a);
    SendByte(0xa5);
    while (1)
        ;
}

12.串口通信进阶应用案例解析

在 CT107D 单片机综合训练平台上,利用 51 单片机的串行接口与上位机建立传数据输信道。采用 8 位的 UART 模式,即模式 1,波特率为 9600BPS。数据发送采用查询方式,数据接收采用中断方式。

1.系统上电初始化之后,关闭蜂鸣器和继电器等无关设备,并向上位机发送字符串:“Hello World!”,回车换行。

2.上位机通过串口发送单字节命令可以控制下位机的 8 个 LED 灯开关。

3.上位机通过串口发送单字节命令可以读取下位机运行信息。

4.通信规约如下表:

      高四位  低四位 说 明
读取灯光 A L4 L3 L2 L1 低4位每位控制一个LED指示灯,0--关灯,1--开灯如: 0xA3,打开L1和L2,关闭L3和L4。
B L8 L7 L6 L5
读取信息 C 0 返回信息“The System is Running...”,回车,换行

代码如下:

#include <STC15F2K60S2.h>

unsigned char command = 0x00;

void Init74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

/********************/
void Init_Uart()
{
    TMOD = 0x20;
    TH1 = 0xfd;
    TL1 = 0xfd;
    AUXR = 0x00;
    TR1 = 1;
    SCON = 0x50;
    ES = 1;
    EA = 1;
}

void Sevice_Uart()
interrupt 4
{
    if(RI == 1)
    {
        command = SBUF;
        RI = 0;
    }
}

void SendByte(unsigned char dat)
{
    SBUF = dat;
    while (TI == 0)
        ;
    TI = 0;
}

void SendString(unsigned char *str)
{
    while (*str != '\0')
    {
        SendByte(*str++);
    }
}
/********************/

void Working()
{
    if (command != 0x00)
    {
        switch (command & 0xf0)
        {
        case 0xa0:
            P0 = (P0 | 0x0f) & (~command | 0xf0);
            command = 0x00;
            break;
        case 0xb0:
            P0 = (P0 | 0xf0) & ((~command << 4) | 0x0f);
            command = 0x00;
            break;
        case 0xc0:
            SendString("The System is Running...\r\n");
            command = 0x00;
            break;
        }
    }
}

void main()
{
    Init74HC138(5);
    P0 = 0x00;
    Init74HC138(4);
    P0 = 0xff;
    Init_Uart();
    SendString("Hello World!\r\n");
    while (1)
    {
        Working();
    }
}

13. 超声波

以后就用这种代码风格了,数码管显示写在在 Timer2(超声波要用 Timer1, NE555 要用 Timer0

#include "STC15F2K60S2.h"
#include "intrins.h"

sbit TX = P1 ^ 0;  //发送Transmit
sbit RX = P1 ^ 1;  //接受Receive

unsigned int Time = 0;
unsigned int dis = 0;

unsigned char code SMG[] =
{   0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xbf};  //数码管段码

unsigned SMG_dat[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; //数码管要显示的数据

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void Delay12us()  //12MHz,12μs
{
    unsigned char i;

    _nop_();
    _nop_();
    i = 33;
    while (--i)
        ;
}

void Init_74HC138(unsigned char n)
{
    switch (n)
    {
    case 4:
        P2 = P2 | 0x1f;
        P2 = 0x80;
        break;
    case 5:
        P2 = P2 | 0x1f;
        P2 = 0xa0;
        break;
    case 6:
        P2 = P2 | 0x1f;
        P2 = 0xc0;
        break;
    case 7:
        P2 = P2 | 0x1f;
        P2 = 0xe0;
        break;
    case 0:
        P2 = P2 | 0X1f;
        P2 = 0x00;
        break;
    }
}

void InitSystem(void)  //关灯、关蜂鸣器
{
    Init_74HC138(5);
    P0 = 0x00;
    Init_74HC138(4);
    P0 = 0xff;
}

void SMG_DisplayBit(unsigned char pos, unsigned char dat)  //显示一位的数码管
{
    Init_74HC138(6);
    P0 = 0x01 << pos;
    Init_74HC138(7);
    P0 = dat;
}

void SMG_CLOSEALL(unsigned char dat)
{
    Init_74HC138(6);
    P0 = 0xff;
    Init_74HC138(7);
    P0 = dat;
}

void Display()
{
    SMG_dat[0] = 0xff;
    SMG_dat[1] = 0xff;
    SMG_dat[2] = 0xff;
    SMG_dat[3] = 0xff;
    SMG_dat[4] = 0xff;
    SMG_dat[5] = SMG[dis / 100];
    SMG_dat[6] = SMG[dis / 10 % 10];
    SMG_dat[7] = SMG[dis % 10];
}

void Timer2_Init(void)  //2400us @12.000MHz Fosc/12
{
    //此处配置看datasheet 7.5.2.1
    T2L = 0xA0;
    T2H = 0xF6;
    AUXR |= 0x10;
    EA = 1;
    IE2 |= 0x04;
}

void Timer2()
interrupt 12
{
    static unsigned int pos = 0;  //此处换成全局变量应该也行,局部变量必出错
    //TL2 = (65536 - 1000) % 256;    自动重载,不需要手动
    //TH2 = (65536 - 1000) / 256;

    SMG_CLOSEALL(0xff);//消影

    switch (pos)
    {
        case 0: SMG_DisplayBit(0, SMG_dat[0]); pos++;break;
        case 1: SMG_DisplayBit(1, SMG_dat[1]); pos++;break;
        case 2: SMG_DisplayBit(2, SMG_dat[2]); pos++;break;
        case 3: SMG_DisplayBit(3, SMG_dat[3]); pos++;break;
        case 4: SMG_DisplayBit(4, SMG_dat[4]); pos++;break;
        case 5: SMG_DisplayBit(5, SMG_dat[5]); pos++;break;
        case 6: SMG_DisplayBit(6, SMG_dat[6]); pos++;break;
        case 7: SMG_DisplayBit(7, SMG_dat[7]); pos=0;break;
    }
}

void SendWave(void)  //发送8次
{
    unsigned char i;
    for (i = 0; i < 8; i++)
    {
        TX = 1;
        Delay12us();
        TX = 0;
        Delay12us();
    }
}
void Distance(void)
{
    TMOD &= 0x0f;
    TH1 = 0x00;
    TL1 = 0x00;
    TF1 = 0;  //溢出标志
    TR1 = 0;  //停止计时
    SendWave();  //发送超声波
    TR1 = 1;  //开始计时
    while (RX == 1 && TF1 == 0)
        ; //收到返回信号并且未溢出,就卡在循环里面
    TR1 = 0;

    if (TF1 == 1) //溢出了,距离999
    {
        TF1 = 0;
        dis = 999;
    }
    else
    {
        Time = (TH1 << 8) | TL1;
        dis = Time * 0.0170; //单位cm
    }
    Delay(10000);  //延时,不然数据跳得太快
}

void main(void)
{
    InitSystem();
    Timer2_Init();
    while (1)
    {
        Display();
        Distance();
    }
}

14. PCF8591

注:iic.c 和 iic.h 文件懒得写上来了

查 datasheet 可知,PCF8591 的控制位为 0x91(读)或 0x90(写),0x01 对应光敏电阻,0x03 对应可变电阻器

光敏电阻和可变电阻器只是改个参数而已

  • 光敏电阻 RD1
#include <STC15F2K60S2.h>
#include <iic.h>

unsigned char code SMG[] =
{   0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xbf};

unsigned SMG_dat[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned int Rd1 = 0;

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void Select573(unsigned int n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void SMG_DisplayBit(unsigned int pos, unsigned int dat)
{
    Select573(6);
    P0 = 0x01 << pos;
    Select573(7);
    P0 = dat;
}

void Display()
{
    SMG_dat[0] = 0xff;
    SMG_dat[1] = 0xff;
    SMG_dat[2] = 0xff;
    SMG_dat[3] = 0xff;
    SMG_dat[4] = 0xff;
    SMG_dat[5] = SMG[Rd1 / 100];
    SMG_dat[6] = SMG[Rd1 / 10 % 10];
    SMG_dat[7] = SMG[Rd1 % 10];
}

void Read_RD1()
{
    IIC_Start();
    IIC_SendByte(0x90);
    IIC_WaitAck();
    IIC_SendByte(0x01);
    IIC_WaitAck();
    IIC_Stop();

    Display();

    IIC_Start();
    IIC_SendByte(0x91);
    IIC_WaitAck();
    Rd1 = IIC_RecByte();
    IIC_SendAck(1);
    IIC_Stop();
    Display();
    //Delay(1000);
}

void SMG_CLOSEALL(unsigned char dat)
{
    Select573(6);
    P0 = 0xff;
    Select573(7);
    P0 = dat;
}

void Timer2_Init(void)  //2400us @12.000MHz Fosc/12
{
    T2L = 0xA0;
    T2H = 0xF6;
    AUXR |= 0x10;
    EA = 1;
    IE2 |= 0x04;
}

void Timer2()
interrupt 12
{
    static unsigned int pos = 0;
    //TL2 = (65536 - 1000) % 256;
    //TH2 = (65536 - 1000) / 256;

    SMG_CLOSEALL(0xff);
    switch (pos)
    {
        case 0: SMG_DisplayBit(0, SMG_dat[0]); pos++;break;
        case 1: SMG_DisplayBit(1, SMG_dat[1]); pos++;break;
        case 2: SMG_DisplayBit(2, SMG_dat[2]); pos++;break;
        case 3: SMG_DisplayBit(3, SMG_dat[3]); pos++;break;
        case 4: SMG_DisplayBit(4, SMG_dat[4]); pos++;break;
        case 5: SMG_DisplayBit(5, SMG_dat[5]); pos++;break;
        case 6: SMG_DisplayBit(6, SMG_dat[6]); pos++;break;
        case 7: SMG_DisplayBit(7, SMG_dat[7]); pos=0;break;
    }
}

void main()
{
    Select573(4);
    P0 = 0xff;
    Select573(5);
    P0 = 0x00;
    Timer2_Init();
    while (1)
    {
        Read_RD1();
        Display();
    }
}
  • 可变电阻器 Rb2
#include <STC15F2K60S2.h>
#include <iic.h>

unsigned char code SMG[] =
{   0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xbf};

unsigned SMG_dat[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned int Rb2 = 0;

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void Select573(unsigned int n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void SMG_DisplayBit(unsigned int pos, unsigned int dat)
{
    Select573(6);
    P0 = 0x01 << pos;
    Select573(7);
    P0 = dat;
}

void Display()
{
    SMG_dat[0] = 0xff;
    SMG_dat[1] = 0xff;
    SMG_dat[2] = 0xff;
    SMG_dat[3] = 0xff;
    SMG_dat[4] = 0xff;
    SMG_dat[5] = SMG[Rb2 / 100];
    SMG_dat[6] = SMG[Rb2 / 10 % 10];
    SMG_dat[7] = SMG[Rb2 % 10];
}

void Read_Rb2()
{
    IIC_Start();
    IIC_SendByte(0x90);
    IIC_WaitAck();
    IIC_SendByte(0x03);
    IIC_WaitAck();
    IIC_Stop();

    Display();

    IIC_Start();
    IIC_SendByte(0x91);
    IIC_WaitAck();
    Rb2 = IIC_RecByte();
    IIC_SendAck(1);
    IIC_Stop();
    Display();
    //Delay(1000);
}

void SMG_CLOSEALL(unsigned char dat)
{
    Select573(6);
    P0 = 0xff;
    Select573(7);
    P0 = dat;
}

void Timer2_Init(void)  //2400us @12.000MHz Fosc/12
{
    T2L = 0xA0;
    T2H = 0xF6;

    AUXR |= 0x10;
    EA = 1;
    IE2 |= 0x04;
}

void Timer2()
interrupt 12
{
    static unsigned int pos = 0;
    //TL2 = (65536 - 1000) % 256;
    //TH2 = (65536 - 1000) / 256;

    SMG_CLOSEALL(0xff);
    switch (pos)
    {
        case 0: SMG_DisplayBit(0, SMG_dat[0]); pos++;break;
        case 1: SMG_DisplayBit(1, SMG_dat[1]); pos++;break;
        case 2: SMG_DisplayBit(2, SMG_dat[2]); pos++;break;
        case 3: SMG_DisplayBit(3, SMG_dat[3]); pos++;break;
        case 4: SMG_DisplayBit(4, SMG_dat[4]); pos++;break;
        case 5: SMG_DisplayBit(5, SMG_dat[5]); pos++;break;
        case 6: SMG_DisplayBit(6, SMG_dat[6]); pos++;break;
        case 7: SMG_DisplayBit(7, SMG_dat[7]); pos=0;break;
    }
}

void main()
{
    Select573(4);
    P0 = 0xff;
    Select573(5);
    P0 = 0x00;
    Timer2_Init();
    while (1)
    {
        Read_Rb2();
        Display();
    }
}

15. NE555

这里使用了三个定时器 Timer012,其实两个就够, 用三个只是为了测试
原理:定时器 2 负责显示数码管;定时器 0 选择计数模式接收脉冲;定时器 1 选择定时模式,定 50ms,当定时 20 次(20 * 50ms = 1s)即一秒时,看定时器 0 接收到的脉冲次数,此为频率

#include <STC15F2K60S2.h>

unsigned long Fre = 0;
unsigned long cnt1 = 0;
unsigned long cnt2 = 0;

unsigned char code SMG[] =
{   0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xbf};

unsigned SMG_dat[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void Select573(unsigned int n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void DisplaySMG_Bit(unsigned int pos, unsigned int dat)
{
    Select573(6);
    P0 = 0x01 << pos;
    Select573(7);
    P0 = dat;
}

void Display()
{
    SMG_dat[0] = 0xff;
    SMG_dat[1] = 0xff;
    SMG_dat[2] = 0xff;

    if (Fre > 9999)
        SMG_dat[3] = SMG[Fre / 10000 % 10];
    else
        SMG_dat[3] = 0xff;

    if (Fre > 999)
        SMG_dat[4] = SMG[Fre / 1000 % 10];
    else
        SMG_dat[4] = 0xff;
    if (Fre > 99)
        SMG_dat[5] = SMG[Fre / 100 % 10];
    else
        SMG_dat[5] = 0xff;
    if (Fre > 9)
        SMG_dat[6] = SMG[Fre / 10 % 10];
    else
        SMG_dat[6] = 0xff;

    SMG_dat[7] = SMG[Fre % 10];
}

void Timer_Init(void)
{
    //TMOD和AUXR的设置看datasheet7.1
    TMOD = 0x16;
    AUXR = 0x10;

    //计时时间可以用ISP生成或者自己写
    TL0 = 0xFF;
    TH0 = 0xFF;
    TL1 = (65536 - 50000) % 256;
    TH1 = (65536 - 50000) / 256;
    T2L = 0xA0;
    T2H = 0xF6;

    TF0 = 0;
    TR0 = 1;
    TF1 = 0;
    TR1 = 1;

    EA = 1;
    ET0 = 1;
    ET1 = 1;
    IE2 |= 0x04;

}

void Timer0()
interrupt 1
{
    cnt1++;
}

void Timer1()
interrupt 3
{
    TL1 = (65536 - 50000) % 256;
    TH1 = (65536 - 50000) / 256;
    cnt2++;
    if(cnt2 == 20)
    {
        Fre = cnt1;
        cnt1 = 0;
        cnt2 = 0;
    }
}

void Timer2()
interrupt 12
{
    static unsigned int pos = 0;

    Select573(6);
    P0 = 0xff;
    Select573(7);
    P0 = 0xff;

    switch (pos)
    {
        case 0: DisplaySMG_Bit(0, SMG_dat[0]); pos++;break;
        case 1: DisplaySMG_Bit(1, SMG_dat[1]); pos++;break;
        case 2: DisplaySMG_Bit(2, SMG_dat[2]); pos++;break;
        case 3: DisplaySMG_Bit(3, SMG_dat[3]); pos++;break;
        case 4: DisplaySMG_Bit(4, SMG_dat[4]); pos++;break;
        case 5: DisplaySMG_Bit(5, SMG_dat[5]); pos++;break;
        case 6: DisplaySMG_Bit(6, SMG_dat[6]); pos++;break;
        case 7: DisplaySMG_Bit(7, SMG_dat[7]); pos=0;break;
    }
}

void main()
{
    Timer_Init();
    Select573(4);
    P0 = 0xff;
    Select573(5);
    P0 = 0x00;
    while (1)
    {
        Display();
    }

}

16. DS18B20

#include <STC15F2K60S2.h>
#include <onewire.h>
#include <intrins.h>

unsigned int Tem;

unsigned char SMG[18] = { 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80,
                          0x90, 0x88, 0x80, 0xc6, 0xc0, 0x86, 0x8e, 0xbf, 0x7f };

unsigned char DSMG[10] = { 0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00,
                           0x10 };

unsigned char SMG_dat[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
void Select573(unsigned int n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    case 0:
        P2 = (P2 & 0X1F) | 0X00;
        break;
    }
}

void Delay100us(void) //@12.000MHz
{
    unsigned char data
    i, j;

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

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void DisplaySMG_Bit(unsigned int pos, unsigned int dat)
{
    Select573(6);
    P0 = 0x01 << pos;
    Select573(7);
    P0 = dat;
}

void ReadTem()
{
    unsigned char LSB, MSB;
    init_ds18b20();
    Write_DS18B20(0xCC);
    Write_DS18B20(0x44);
    Delay100us();

    init_ds18b20();
    Write_DS18B20(0XCC);
    Write_DS18B20(0XBE);
    LSB = Read_DS18B20();
    MSB = Read_DS18B20();
    init_ds18b20();

    Tem = 0x0000;
    Tem = MSB;
    Tem <<= 8;
    Tem |= LSB;
    if ((Tem & 0xf800) == 0x0000)
    {
        Tem >>= 4;
        Tem = Tem * 10;
        Tem = Tem + (LSB & 0x0f) * 0.625;
    }

}

void Display_Tem()
{
    SMG_dat[0] = 0xff;
    SMG_dat[1] = 0xff;
    SMG_dat[2] = 0xff;
    SMG_dat[3] = 0xff;
    SMG_dat[4] = 0xff;
    SMG_dat[5] = SMG[(Tem / 100) % 10];
    SMG_dat[6] = DSMG[(Tem / 10) % 10];
    SMG_dat[7] = SMG[(Tem / 1) % 10];
}

void Timer2_Init(void)  //2400us @12.000MHz Fosc/12
{
    AUXR |= 0x10;
    T2L = 0xA0;
    T2H = 0xF6;
    IE2 |= 0x04;
    EA = 1;
}

void Timer2()
interrupt 12
{
    static unsigned char pos = 0;
    Select573(6);
    P0 = 0xff;
    Select573(7);
    P0 = 0xff;

    switch(pos)
    {
        case 0:DisplaySMG_Bit(0, SMG_dat[0]);pos++;break;
        case 1:DisplaySMG_Bit(1, SMG_dat[1]);pos++;break;
        case 2:DisplaySMG_Bit(2, SMG_dat[2]);pos++;break;
        case 3:DisplaySMG_Bit(3, SMG_dat[3]);pos++;break;
        case 4:DisplaySMG_Bit(4, SMG_dat[4]);pos++;break;
        case 5:DisplaySMG_Bit(5, SMG_dat[5]);pos++;break;
        case 6:DisplaySMG_Bit(6, SMG_dat[6]);pos++;break;
        case 7:DisplaySMG_Bit(7, SMG_dat[7]);pos=0;break;
    }
}

void main()
{

    Select573(5);
    P0 = 0x00;
    Select573(4);
    P0 = 0xff;
    Timer2_Init();
    while (1)
    {
        ReadTem();
        Display_Tem();
    }
}

17. DS1302

#include <STC15F2K60S2.h>
#include <ds1302.h>
#include <intrins.h>

void Display_Time();
void DS1302_ReadTime();

//下面三个数组的顺序看datasheet

//读地址
unsigned char code READ_RTC_ADDR[7] =
{   0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};

//写地址
unsigned char code WRITE_RTC_ADDR[7] =
{   0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};

//主要这是BCD码,0x23就是十进制23
//转换时,十位 /16   个位 &0x0f
unsigned char TIME[7] = { 0x23, 0x10, 0x19, 0x20, 0x03, 0x03, 0x24 };

unsigned char code SMG[16] =
{   0xc0,0xf9,0xa4,0xb0,  //0 1 2 3
    0x99,0x92,0x82,0xf8,//4 5 6 7
    0x80,0x90,0x88,0x83,//8 9 A b
    0xc6,0xa1,0x86,0x8e//C d E F
};

unsigned char SMG_dat[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

void Select573(unsigned int n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    }
}

void Delay(unsigned int t)
{
    while (t--)
        ;
}

void DisplaySMG_Bit(unsigned int pos, unsigned int dat)
{
    Select573(6);
    P0 = 0x01 << pos;
    Select573(7);
    P0 = dat;
}

//time
void DS1302_Cfg()
{
    unsigned char i;
    Write_Ds1302_Byte(0x8e, 0x00);
    for (i = 0; i < 7; i++)
    {
        Write_Ds1302_Byte(WRITE_RTC_ADDR[i], TIME[i]);
    }
    Write_Ds1302_Byte(0x8e, 0x80);
}

void DS1302_ReadTime()
{
    unsigned char n;
    for (n = 0; n < 7; n++)
    {
        TIME[n] = Read_Ds1302_Byte(READ_RTC_ADDR[n]);
    }
}

void Timer2_Init(void)  //2400us @12.000MHz Fosc/12
{
    AUXR |= 0x10;
    T2L = 0xA0;
    T2H = 0xF6;
    IE2 |= 0x04;
    EA = 1;
}

void Display_Time()
{
    SMG_dat[0] = SMG[TIME[2] / 16];
    SMG_dat[1] = SMG[TIME[2] & 0x0f];
    SMG_dat[2] = 0xbf;
    SMG_dat[3] = SMG[TIME[1] / 16];
    SMG_dat[4] = SMG[TIME[1] & 0x0f];
    SMG_dat[5] = 0xbf;
    SMG_dat[6] = SMG[TIME[0] / 16];
    SMG_dat[7] = SMG[TIME[0] & 0x0f];
}

void Timer2()
interrupt 12
{
    static unsigned char pos = 0;
    Select573(6);
    P0 = 0xff;
    Select573(7);
    P0 = 0xff;

    switch(pos)
    {
        case 0:DisplaySMG_Bit(0, SMG_dat[0]);pos++;break;
        case 1:DisplaySMG_Bit(1, SMG_dat[1]);pos++;break;
        case 2:DisplaySMG_Bit(2, SMG_dat[2]);pos++;break;
        case 3:DisplaySMG_Bit(3, SMG_dat[3]);pos++;break;
        case 4:DisplaySMG_Bit(4, SMG_dat[4]);pos++;break;
        case 5:DisplaySMG_Bit(5, SMG_dat[5]);pos++;break;
        case 6:DisplaySMG_Bit(6, SMG_dat[6]);pos++;break;
        case 7:DisplaySMG_Bit(7, SMG_dat[7]);pos=0;break;
    }
}

void main()
{
    Select573(5);
    P0 = 0x00;
    Select573(4);
    P0 = 0xff;
    DS1302_Cfg();
    Timer2_Init();
    while (1)
    {
        DS1302_ReadTime();
        Display_Time();
    }
}

18. 后记 :代码风格(确定)

#include <STC15F2K60S2.h>
#include <intrins.h>

//数码管最高位(第八位)置0,即可显示点
unsigned char SMG[18] = { 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80,
                          0x90, 0x88, 0x80, 0xc6, 0xc0, 0x86, 0x8e, 0xbf, 0x7f };
unsigned char DSMG[10] = { 0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00,
                           0x10 };

//此处存放数码管显示的数据,以0xff初始化
unsigned char SMG_dat[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

//选择锁存器,0x00可以不写
void Select573(unsigned int n)
{
    switch (n)
    {
    case 4:
        P2 = (P2 & 0x1f) | 0x80;
        break;
    case 5:
        P2 = (P2 & 0x1f) | 0xa0;
        break;
    case 6:
        P2 = (P2 & 0x1f) | 0xc0;
        break;
    case 7:
        P2 = (P2 & 0x1f) | 0xe0;
        break;
    case 0:
        P2 = (P2 & 0X1F) | 0X00;
        break;
    }
}

//延时函数(不精确),有的时候报L16警告是因为这个函数没用到
void Delay(unsigned int t)
{
    while (t--)
        ;
}

//显示数码管每个位
void DisplaySMG_Bit(unsigned int pos, unsigned int dat)
{
    Select573(6);
    P0 = 0x01 << pos;
    Select573(7);
    P0 = dat;
}

//保存数码管要显示的数据(可以把SMG_dat[]当作一个寄存器),方便Timer2()调用
void Display_xxx()
{
    SMG_dat[0] = 0xff;
    SMG_dat[1] = 0xff;
    SMG_dat[2] = 0xff;
    SMG_dat[3] = 0xff;
    SMG_dat[4] = 0xff;
    SMG_dat[5] = SMG[(xxx / 100) % 10];
    SMG_dat[6] = DSMG[(xxx / 10) % 10];
    SMG_dat[7] = SMG[(xxx / 1) % 10];
}

void Timer2_Init(void)  //2400us @12.000MHz Fosc/12
{
    AUXR |= 0x10;
    T2L = 0xA0;
    T2H = 0xF6;
    IE2 |= 0x04;
    EA = 1;
}

void Timer2()
interrupt 12
{
    static unsigned char pos = 0;

    //消影
    Select573(6);
    P0 = 0xff;
    Select573(7);
    P0 = 0xff;

    switch(pos)
    {
        case 0:DisplaySMG_Bit(0, SMG_dat[0]);pos++;break;
        case 1:DisplaySMG_Bit(1, SMG_dat[1]);pos++;break;
        case 2:DisplaySMG_Bit(2, SMG_dat[2]);pos++;break;
        case 3:DisplaySMG_Bit(3, SMG_dat[3]);pos++;break;
        case 4:DisplaySMG_Bit(4, SMG_dat[4]);pos++;break;
        case 5:DisplaySMG_Bit(5, SMG_dat[5]);pos++;break;
        case 6:DisplaySMG_Bit(6, SMG_dat[6]);pos++;break;
        case 7:DisplaySMG_Bit(7, SMG_dat[7]);pos=0;break;
    }
}

void main()
{

    Select573(5);
    P0 = 0x00;
    Select573(4);
    P0 = 0xff;
    Timer2_Init();
    while (1)
    {
        Display_xxx();
    }
}