前言

必读

51单片机入门教程(上篇)(代码+个人理解) – Echo (liveout.cn)

GitHub仓库链接:https://github.com/PGwind/51code

这篇文章是记录我粗略学习51单片机的一些代码,我会加些个人理解以及注释在里面。

因为是囫囵吞枣式学习,所以质量不是很好,后期我会慢慢优化 ?

如果你想要学习单片机,可以观看下面的B站教程并配合本文档学习

本文章使用的51单片机是 普中STC89C52RC

教程

推荐B站视频: 【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】 https://www.bilibili.com/video/BV1Mb411e7re/?share_source=copy_web&vd_source=55024add0415795a359bd7b29ca21142(应该都知道吧)。

资源

B站江科大资源 链接:https://pan.baidu.com/s/1dLED_1VqL66qYItLl5ic4A?pwd=1111 提取码:1111

普中 链接:https://pan.baidu.com/s/1dNCHm9lLMP8pe3rZu3ktZQ?pwd=1111 提取码:1111


9. LED点阵屏

原理

88LED

88LED引脚
88LED引脚

74HC595

74HC595是串行输入并行输出的移位寄存器,3根线输入串行数据,8根线输出比并行数据,多片级后,可输出16为、24为、32位等,常用于IO口扩展

9.1 LED点阵显示图形

#include <REGX52.H>
#include "Delay.h"
sbit RCK=P3^5;  //RCLK 上升沿锁存
sbit SCK=P3^6;  //SRCLK 上升沿移位
sbit SER=P3^4;  //SER

#define MATRIX_LED_PORT     P0

//Function Definition
/**
  * @brief 74HC595写入一个字节
  * @param  要写入的字节
  * @retval 无
  */

void _74HC595_WriteByte(unsigned char Byte)
{
    unsigned char i;
    for (i=0;i<8;i++)
    {
        SER=Byte&(0x80>>i);  //1000 0000
      SCK=1;
      SCK=0;
    }
    RCK=1;
    RCK=0;
}

//Function Definition
/**
  * @brief LED点阵屏显示一列数据
  * @param  Column 要选择的列,范围:0~7,0在最左边
  * @param  Data 选择列显示的数据,高位在上,1位亮,0位灭
  * @retval 无
  */

void MatrixLED_ShowColumn(unsigned char Column,Data)
{
    _74HC595_WriteByte(Data);  //Data==Byte
    MATRIX_LED_PORT=~(0x80>>Column); //0选中,1不选中
  Delay(1); //延迟
  MATRIX_LED_PORT=0xFF;  //全灭  消影
}

void main()
{
    SCK=0; //刚开始是高电平,所以要置0,使其为低电平
    RCK=0;

    while(1)
    {
        MatrixLED_ShowColumn(0,0x3C); //1亮0不亮
        MatrixLED_ShowColumn(1,0x42);
        MatrixLED_ShowColumn(2,0xA9);
        MatrixLED_ShowColumn(3,0x85);
        MatrixLED_ShowColumn(4,0x85);
        MatrixLED_ShowColumn(5,0xA9);
        MatrixLED_ShowColumn(6,0x42);
        MatrixLED_ShowColumn(7,0x3C);
    }
}

9.2 LED点阵显示动画

头文件

//MatrixLED.h
#ifndef __MATRIX_LED_H_
#define __MATRIX_LED_H_
void MatrixLED_Init();
void _74HC595_WriteByte(unsigned char Byte);
void MatrixLED_ShowColumn(unsigned char Column,Data);
#endif
//Delay.h
#ifndef __DELAY_H_
#define __DELAY_H_
void Delay(unsigned int xms);
#endif

函数

//MatrixLED.c
#include <REGX52.H>
#include "Delay.h"

sbit RCK=P3^5;  //RCLK 上升沿锁存
sbit SCK=P3^6;  //SRCLK 上升沿移位
sbit SER=P3^4;  //SER

#define MATRIX_LED_PORT     P0

//Function Definition
/**
  * @brief 74HC595写入一个字节
  * @param  要写入的字节
  * @retval 无
  */

void _74HC595_WriteByte(unsigned char Byte)
{
    unsigned char i;
    for (i=0;i<8;i++)
    {
        SER=Byte&(0x80>>i);  //1000 0000
      SCK=1;
      SCK=0;
    }
    RCK=1;
    RCK=0;
}

//Function Definition
/**
  * @brief 点阵屏初始化
  * @param  无
  * @retval 无
  */
void MatrixLED_Init()
{
    SCK=0; //刚开始是高电平,所以要置0,使其为低电平
    RCK=0;
}

//Function Definition
/**
  * @brief LED点阵屏显示一列数据
  * @param  Column 要选择的列,范围:0~7,0在最左边
  * @param  Data 选择列显示的数据,高位在上,1位亮,0位灭
  * @retval 无
  */

void MatrixLED_ShowColumn(unsigned char Column,Data)
{
    _74HC595_WriteByte(Data);  //Data==Byte
    MATRIX_LED_PORT=~(0x80>>Column); //0选中,1不选中
  Delay(1); //延迟
  MATRIX_LED_PORT=0xFF;  //全灭  消影
}
//Delay.c
//Function Definition
/**
  * @brief 延迟函数
  * @param  无
  * @retval 1ms
  */
void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while(xms)
    {
            i = 2;
            j = 199;
            do
            {
                while (--j);
            } while (--i);
            xms--;
    }
}

1. 通过流动实现动画

//main.c  通过流动实现动画
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"

unsigned char Animation[]={0x00,0x00,0x00,0x81,0xFF,0x81,0x00,0x00,
    0x7E,0x02,0x02,0x00,0x3C,0x42,0x42,0x3C,
    0x00,0x30,0x0C,0x02,0x0C,0x30,0x00,0x3E,
    0x2A,0x2A,0x00,0x00,0x60,0x18,0x06,0x18,0x60,
    0x00
};

void main()
{
    unsigned char i,Offset=1,Count=0;  //Offset偏移量,即数组第几列 Count控制移动速度
    MatrixLED_Init();
    while(1)
    {
        for (i=0; i<8; i++)
        {
            MatrixLED_ShowColumn(i,Animation[i+Offset]);
        }
        Count++;
        if (Count>12)
        {
            Count=0;
            Offset++;
            if(Offset>24) //防止数组溢出
            {
                Offset=0;
            }
        }
    }
}

2. 通过每次刷新(即每帧)实现动画

//main.c  通过每次刷新(即每帧)实现动画
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"

unsigned char code Animation[]={
    0x00,0x0C,0x1C,0x21,0xDE,0xE0,0x3F,0x10,
    0x00,0x06,0x16,0x21,0xDE,0xE0,0x3F,0x10,
    0x00,0x03,0x13,0x21,0xDE,0xE0,0x3F,0x10,
    0x0C,0x0C,0x10,0x21,0xDE,0xE0,0x3F,0x10,
    0x00,0x0C,0x1C,0x21,0xDE,0xE0,0x3F,0x10,
}; //加code这些数据会放在flash里面,不然在RAM里。flash内存较大
//但是后面数组无法更改

void main()
{
    unsigned char i,Offset=0,Count=0;  //Offset偏移量,即数组第几列 Count控制变换速度
    MatrixLED_Init();
    while(1)
    {
        for (i=0; i<8; i++)
        {
            MatrixLED_ShowColumn(i,Animation[i+Offset]);
        }
        Count++;
        if (Count>15)
        {
            Count=0;
            Offset+=8;
            if(Offset>32) //防止数组溢出
            {
                Offset=0;
            }
        }
    }
}

10. DS1302时钟

DS1302:低功耗实时时钟芯片,可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能

RTC:实时时钟,是一种集成电路,通常称为时钟芯片(51单片机不带)

BCD码:用4位二进制数来表示1位十进制数 如:0001 0011 表示13,而 0001 1010 则不合法

原理

阅读厂家提供的DS1302中文手册

DS1302时钟

10.1 DS1302时钟

头文件

//DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__

//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期
extern unsigned char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);

#endif

函数

//DS1302.c
#include <REGX52.H>

//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

//寄存器写入地址/指令定义
#define DS1302_SECOND        0x80
#define DS1302_MINUTE        0x82
#define DS1302_HOUR            0x84
#define DS1302_DATE            0x86
#define DS1302_MONTH        0x88
#define DS1302_DAY            0x8A
#define DS1302_YEAR            0x8C
#define DS1302_WP            0x8E

//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
unsigned char DS1302_Time[]={19,11,16,12,59,55,6};

/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */
void DS1302_Init(void)
{
    DS1302_CE=0;
    DS1302_SCLK=0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void DS1302_WriteByte(unsigned char Command,Data)
{
    unsigned char i;
    DS1302_CE=1;
    for(i=0;i<8;i++)
    {
        DS1302_IO=Command&(0x01<<i);
        DS1302_SCLK=1;
        DS1302_SCLK=0;
    }
    for(i=0;i<8;i++)
    {
        DS1302_IO=Data&(0x01<<i);
        DS1302_SCLK=1;
        DS1302_SCLK=0;
    }
    DS1302_CE=0;
}

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
    unsigned char i,Data=0x00;
    Command|=0x01;    //将指令转换为读指令
    DS1302_CE=1;
    for(i=0;i<8;i++)
    {
        DS1302_IO=Command&(0x01<<i);
        DS1302_SCLK=0;
        DS1302_SCLK=1;
    }
    for(i=0;i<8;i++)
    {
        DS1302_SCLK=1;
        DS1302_SCLK=0;
        if(DS1302_IO){Data|=(0x01<<i);}
    }
    DS1302_CE=0;
    DS1302_IO=0;    //读取后将IO设置为0,否则读出的数据会出错
    return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
    DS1302_WriteByte(DS1302_WP,0x00);
    DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
    DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
    DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
    DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
    DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
    DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
    DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
    DS1302_WriteByte(DS1302_WP,0x80);
}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
    unsigned char Temp;
    Temp=DS1302_ReadByte(DS1302_YEAR);
    DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
    Temp=DS1302_ReadByte(DS1302_MONTH);
    DS1302_Time[1]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_DATE);
    DS1302_Time[2]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_HOUR);
    DS1302_Time[3]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_MINUTE);
    DS1302_Time[4]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_SECOND);
    DS1302_Time[5]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_DAY);
    DS1302_Time[6]=Temp/16*10+Temp%16;
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"

void main()
{
    LCD_Init();
    DS1302_Init();
    LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
    LCD_ShowString(2,1,"  :  :  ");

    DS1302_SetTime();//设置时间

    while(1)
    {
        DS1302_ReadTime();//读取时间
        LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
        LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
        LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
        LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
        LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
        LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
    }
}

10.2 DS1302可调时钟

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"

unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;

void TimeShow(void)//时间显示功能
{
    DS1302_ReadTime();//读取时间
    LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
    LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
    LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
    LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
    LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
    LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
}

void TimeSet(void)//时间设置功能
{
    if(KeyNum==2)//按键2按下
    {
        TimeSetSelect++;//设置选择位加1
        TimeSetSelect%=6;//越界清零
    }
    if(KeyNum==3)//按键3按下
    {
        DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
        if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判断
        if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判断
        if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
            DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
        {
            if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
        }
        else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
        {
            if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
        }
        else if(DS1302_Time[1]==2)
        {
            if(DS1302_Time[0]%4==0)
            {
                if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
            }
            else
            {
                if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
            }
        }
        if(DS1302_Time[3]>23){DS1302_Time[3]=0;}//时越界判断
        if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判断
        if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判断
    }
    if(KeyNum==4)//按键3按下
    {
        DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
        if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
        if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判断
        if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
            DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
        {
            if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
            if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
        }
        else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
        {
            if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
            if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
        }
        else if(DS1302_Time[1]==2)
        {
            if(DS1302_Time[0]%4==0)
            {
                if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
                if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
            }
            else
            {
                if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
                if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
            }
        }
        if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判断
        if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判断
        if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判断
    }
    //更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
    if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
    else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
    if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}
    else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
    if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}
    else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
    if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
    else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
    if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
    else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
    if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
    else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
}

void main()
{
    LCD_Init();
    DS1302_Init();
    Timer0Init();
    LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
    LCD_ShowString(2,1,"  :  :  ");

    DS1302_SetTime();//设置时间

    while(1)
    {
        KeyNum=Key();//读取键码
        if(KeyNum==1)//按键1按下
        {
            if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
            else if(MODE==1){MODE=0;DS1302_SetTime();}
        }
        switch(MODE)//根据不同的功能执行不同的函数
        {
            case 0:TimeShow();break;
            case 1:TimeSet();break;
        }
    }
}

void Timer0_Routine() interrupt 1
{
    static unsigned int T0Count;
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    T0Count++;
    if(T0Count>=500)//每500ms进入一次
    {
        T0Count=0;
        TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
    }
}

11. 蜂鸣器

原理

蜂鸣器

 C调音符与频率对照表

11.1 蜂鸣器播放提示音

头文件

//Buzzer.h
#ifndef __BUZZER_H__
#define __BUZZER_H__

void Buzzer_Time(unsigned int ms);

#endif

函数

//Buzzer.c
#include <REGX52.H>
#include <INTRINS.H>

//蜂鸣器端口:
sbit Buzzer=P2^5;

/**
  * @brief  蜂鸣器私有延时函数,延时500us
  * @param  无
  * @retval 无
  */
void Buzzer_Delay500us()        //@12.000MHz
{
    unsigned char i;

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

/**
  * @brief  蜂鸣器发声
  * @param  ms 发声的时长,范围:0~32767
  * @retval 无
  */
void Buzzer_Time(unsigned int ms)
{
    unsigned int i;
    for(i=0;i<ms*2;i++)
    {
        Buzzer=!Buzzer;
        Buzzer_Delay500us();
    }
}
//mian.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Buzzer.h"

sbit Buzzer=P2^5;

unsigned char KeyNum;
unsigned int i;

void main()
{
     Nixie(1,0);
    while(1)
    {
        KeyNum=Key();
        if(KeyNum)
        {
            for(i=0; i<100; i++)
            {
                Buzzer=!Buzzer;
                Delay(1);
            }
            Buzzer_Time(100);
            Nixie(1,KeyNum);
        }
    }
}

11.2 蜂鸣器播放音乐

#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"

//蜂鸣器端口定义
sbit Buzzer=P2 ^5;

//播放速度,值为四分音符的时长(ms)
#define SPEED    500

//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P    0
#define L1    1
#define L1_    2
#define L2    3
#define L2_    4
#define L3    5
#define L4    6
#define L4_    7
#define L5    8
#define L5_    9
#define L6    10
#define L6_    11
#define L7    12
#define M1    13
#define M1_    14
#define M2    15
#define M2_    16
#define M3    17
#define M4    18
#define M4_    19
#define M5    20
#define M5_    21
#define M6    22
#define M6_    23
#define M7    24
#define H1    25
#define H1_    26
#define H2    27
#define H2_    28
#define H3    29
#define H4    30
#define H4_    31
#define H5    32
#define H5_    33
#define H6    34
#define H6_    35
#define H7    36

//索引与频率对照表
unsigned int FreqTable[]={
    0,
    63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
    64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
    65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};

//乐谱
unsigned char code Music[]={
    //音符,时值,

    //1
    P,    4,
    P,    4,
    P,    4,
    M6,    2,
    M7,    2,

    H1,    4+2,
    M7,    2,
    H1,    4,
    H3,    4,

    M7,    4+4+4,
    M3,    2,
    M3,    2,

    //2
    M6,    4+2,
    M5,    2,
    M6, 4,
    H1,    4,

    M5,    4+4+4,
    M3,    4,

    M4,    4+2,
    M3,    2,
    M4,    4,
    H1,    4,

    //3
    M3,    4+4,
    P,    2,
    H1,    2,
    H1,    2,
    H1,    2,

    M7,    4+2,
    M4_,2,
    M4_,4,
    M7,    4,

    M7,    8,
    P,    4,
    M6,    2,
    M7,    2,

    //4
    H1,    4+2,
    M7,    2,
    H1,    4,
    H3,    4,

    M7,    4+4+4,
    M3,    2,
    M3,    2,

    M6,    4+2,
    M5,    2,
    M6, 4,
    H1,    4,

    //5
    M5,    4+4+4,
    M2,    2,
    M3,    2,

    M4,    4,
    H1,    2,
    M7,    2+2,
    H1,    2+4,

    H2,    2,
    H2,    2,
    H3,    2,
    H1,    2+4+4,

    //6
    H1,    2,
    M7,    2,
    M6,    2,
    M6,    2,
    M7,    4,
    M5_,4,

    M6,    4+4+4,
    H1,    2,
    H2,    2,

    H3,    4+2,
    H2,    2,
    H3,    4,
    H5,    4,

    //7
    H2,    4+4+4,
    M5,    2,
    M5,    2,

    H1,    4+2,
    M7,    2,
    H1,    4,
    H3,    4,

    H3,    4+4+4+4,

    //8
    M6,    2,
    M7,    2,
    H1,    4,
    M7,    4,
    H2,    2,
    H2,    2,

    H1,    4+2,
    M5,    2+4+4,

    H4,    4,
    H3,    4,
    H3,    4,
    H1,    4,

    //9
    H3,    4+4+4,
    H3,    4,

    H6,    4+4,
    H5,    4,
    H5,    4,

    H3,    2,
    H2,    2,
    H1,    4+4,
    P,    2,
    H1,    2,

    //10
    H2,    4,
    H1,    2,
    H2,    2,
    H2,    4,
    H5,    4,

    H3,    4+4+4,
    H3,    4,

    H6,    4+4,
    H5,    4+4,

    //11
    H3,    2,
    H2,    2,
    H1,    4+4,
    P,    2,
    H1,    2,

    H2,    4,
    H1,    2,
    H2,    2+4,
    M7,    4,

    M6,    4+4+4,
    P,    4,

    0xFF    //终止标志
};

unsigned char FreqSelect,MusicSelect;

void main()
{
    Timer0Init(); //定时器0初始化
    while(1)
    {
        if(Music[MusicSelect]!=0xFF)    //如果不是停止标志位
        {
            FreqSelect=Music[MusicSelect];    //选择音符对应的频率
            MusicSelect++;
            Delay(SPEED/4*Music[MusicSelect]);    //选择音符对应的时值
            MusicSelect++;
            TR0=0;
            Delay(5);    //音符间短暂停顿
            TR0=1;
        }
        else    //如果是停止标志位
        {
            TR0=0;
            while(1);
        }
    }
}

void Timer0_Routine() interrupt 1
{
    if(FreqTable[FreqSelect])    //如果不是休止符
    {
        /*取对应频率值的重装载值到定时器*/
        TL0 = FreqTable[FreqSelect]%256;        //设置定时初值
        TH0 = FreqTable[FreqSelect]/256;        //设置定时初值
        Buzzer=!Buzzer;    //翻转蜂鸣器IO口
    }
}

12. AT24C02(存储器)

原理

EEPROM

12.1 AT24C02数据存储

头文件

//12C.h
#ifndef __I2C_H_
#define __I2C_H_

void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_ReceiveByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);

#endif
//AT24C02.h
#ifndef __AT24C02_H_
#define __AT24C02_H_

void AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);

#endif

函数

//I2C.c
#include <REGX52.H>

sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;

//Function Definition
/**
  * @brief I2C开始
  * @param  无
  * @retval 无
  */
void I2C_Start(void)
{
    I2C_SDA=1; //高
    I2C_SCL=1;
     I2C_SDA=0; //低
    I2C_SCL=0;
}

//Function Definition
/**
  * @brief I2C停止
  * @param  无
  * @retval 无
  */
void I2C_Stop(void)
{
    I2C_SDA=0; //低
    I2C_SCL=1; //高
    I2C_SDA=1; //高
}

//Function Definition
/**
  * @brief I2C发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void I2C_SendByte(unsigned char Byte)
{
    unsigned char i;
    for (i=0;i<8;i++)
    {
        I2C_SDA=Byte&(0x80>>i);
        I2C_SCL=1;
        I2C_SCL=0;
    }

}

//Function Definition
/**
  * @brief I2C接收一个字节
  * @param  无
  * @retval 接收到的一个字节数据
  */
unsigned char I2C_ReceiveByte(void)
{
    unsigned char Byte,i;

    I2C_SDA=1;

    for (i=0;i<8;i++)
    {
        I2C_SCL=1;
        if(I2C_SDA) {Byte|=(0x80>>i);}
        I2C_SCL=0;
    }
    return Byte;
}

//Function Definition
/**
  * @brief  I2C发送应答位
  * @param  AckBit 应答位,0为应答,1为非应答
  * @retval 无
  */
void I2C_SendAck(unsigned char AckBit)
{
    I2C_SDA=AckBit;
    I2C_SCL=1;
    I2C_SCL=0;
}

//Function Definition
/**
  * @brief  I2C接收应答位
  * @param  无
  * @retval 接收到的应答位,  0为应答,1为非应答
  */
unsigned char I2C_ReceiveAck(void)
{
    unsigned char AckBit;
    I2C_SDA=1;
    I2C_SCL=1;
    AckBit=I2C_SDA;
    I2C_SCL=0;
    return AckBit;
}
//AT24C02.c
#include <REGX52.H>
#include "I2C.h"

#define AT24C02_ADDRESS  0xA0

//Function Definition
/**
  * @brief AT24C02写入一个字节
  * @param  WordAddress  要写入字节的地址   Data  要写入的数据
  * @retval 无
  */
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
    I2C_Start();
    I2C_SendByte(AT24C02_ADDRESS);
    I2C_ReceiveAck();
    I2C_SendByte(WordAddress);
    I2C_ReceiveAck();
    I2C_SendByte(Data);
    I2C_ReceiveAck();
    I2C_Stop();
}

//Function Definition
/**
  * @brief  AT24C02读取一个字节
  * @param   WordAddress  要读出字节的地址
  * @retval  Data  要读出的数据
  */
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
    unsigned char Data;
    I2C_Start();
    I2C_SendByte(AT24C02_ADDRESS);
    I2C_ReceiveAck();
    I2C_SendByte(WordAddress);
    I2C_ReceiveAck();
    I2C_Start();
    I2C_SendByte(AT24C02_ADDRESS|0x01);
    I2C_ReceiveAck();
    Data=I2C_ReceiveByte();
    I2C_SendAck(1);
    I2C_Stop();
    return Data;
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Key.h"
#include "AT24C02.h"
#include "Delay.h"

unsigned char KeyNum;
unsigned int Num;

void main()
{
    LCD_Init();
    LCD_ShowNum(1,1,Num,5);
    while(1)
    {
        KeyNum=Key();
        if(KeyNum==1)
        {
            Num++;
            LCD_ShowNum(1,1,Num,5);
        }

        if(KeyNum==2)
        {
            Num--;
            LCD_ShowNum(1,1,Num,5);
        }

        if(KeyNum==3)
        {
            AT24C02_WriteByte(0,Num%256); //低8位
            Delay(5);
            AT24C02_WriteByte(1,Num/256); //高8位
            Delay(5);
            LCD_ShowString(2,1,"Write Ok");
            Delay(1000);
            LCD_ShowString(2,1,"         ");
        }

        if(KeyNum==4)
        {
            Num=AT24C02_ReadByte(0); //o地址
            Num|=AT24C02_ReadByte(1)<<8; //1地址
            LCD_ShowNum(1,1,Num,5);
            LCD_ShowString(2,1,"Read OK");
            Delay(1000);
            LCD_ShowString(2,1,"         ");
        }
    }
}

12.2 秒表(定时器扫描按键数码管)

头文件

//Nixie.h
#ifndef __NIXIE_H__
#define __NIXIE_H__

void Nixie_SetBuf(unsigned char Location,Number);
void Nixie_Scan(unsigned char Location,Number);
void Nixie_Loop(void);

#endif
//Key.h
#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key(void);
void Key_Loop(void);

#endif

函数

//Nixie.c
#include <REGX52.H>
#include "Delay.h"

//数码管显示缓存区
unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};

//数码管段码表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};

/**
  * @brief  设置显示缓存区
  * @param  Location 要设置的位置,范围:1~8
  * @param  Number 要设置的数字,范围:段码表索引范围
  * @retval 无
  */
void Nixie_SetBuf(unsigned char Location,Number)
{
    Nixie_Buf[Location]=Number;
}

/**
  * @brief  数码管扫描显示
  * @param  Location 要显示的位置,范围:1~8
  * @param  Number 要显示的数字,范围:段码表索引范围
  * @retval 无
  */
void Nixie_Scan(unsigned char Location,Number)
{
    P0=0x00;                //段码清0,消影
    switch(Location)        //位码输出
    {
        case 1:P2_4=1;P2_3=1;P2_2=1;break;
        case 2:P2_4=1;P2_3=1;P2_2=0;break;
        case 3:P2_4=1;P2_3=0;P2_2=1;break;
        case 4:P2_4=1;P2_3=0;P2_2=0;break;
        case 5:P2_4=0;P2_3=1;P2_2=1;break;
        case 6:P2_4=0;P2_3=1;P2_2=0;break;
        case 7:P2_4=0;P2_3=0;P2_2=1;break;
        case 8:P2_4=0;P2_3=0;P2_2=0;break;
    }
    P0=NixieTable[Number];    //段码输出
}

/**
  * @brief  数码管驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Nixie_Loop(void)
{
    static unsigned char i=1;
    Nixie_Scan(i,Nixie_Buf[i]);
    i++;
    if(i>=9){i=1;}
}
//Key.c
#include <REGX52.H>
#include "Delay.h"

unsigned char Key_KeyNumber;

/**
  * @brief  获取按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key(void)
{
    unsigned char Temp=0;
    Temp=Key_KeyNumber;
    Key_KeyNumber=0;
    return Temp;
}

/**
  * @brief  获取当前按键的状态,无消抖及松手检测
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key_GetState()
{
    unsigned char KeyNumber=0;

    if(P3_1==0){KeyNumber=1;}
    if(P3_0==0){KeyNumber=2;}
    if(P3_2==0){KeyNumber=3;}
    if(P3_3==0){KeyNumber=4;}

    return KeyNumber;
}

/**
  * @brief  按键驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Key_Loop(void)
{
    static unsigned char NowState,LastState;
    LastState=NowState;                //按键状态更新
    NowState=Key_GetState();        //获取当前按键状态
    //如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
    if(LastState==1 && NowState==0)
    {
        Key_KeyNumber=1;
    }
    if(LastState==2 && NowState==0)
    {
        Key_KeyNumber=2;
    }
    if(LastState==3 && NowState==0)
    {
        Key_KeyNumber=3;
    }
    if(LastState==4 && NowState==0)
    {
        Key_KeyNumber=4;
    }
}
//main.c
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include "Nixie.h"
#include "Delay.h"
#include "AT24C02.h"

unsigned char KeyNum;
unsigned char Min,Sec,MiniSec;
unsigned char RunFlag;

void main()
{
    Timer0_Init();
    while(1)
    {
        KeyNum=Key();
        if(KeyNum==1)            //K1按键按下
        {
            RunFlag=!RunFlag;    //启动标志位翻转
        }
        if(KeyNum==2)            //K2按键按下
        {
            Min=0;                //分秒清0
            Sec=0;
            MiniSec=0;
        }
        if(KeyNum==3)            //K3按键按下
        {
            AT24C02_WriteByte(0,Min);    //将分秒写入AT24C02
            Delay(5);
            AT24C02_WriteByte(1,Sec);
            Delay(5);
            AT24C02_WriteByte(2,MiniSec);
            Delay(5);
        }
        if(KeyNum==4)            //K4按键按下
        {
            Min=AT24C02_ReadByte(0);    //读出AT24C02数据
            Sec=AT24C02_ReadByte(1);
            MiniSec=AT24C02_ReadByte(2);
        }
        Nixie_SetBuf(1,Min/10);    //设置显示缓存,显示数据
        Nixie_SetBuf(2,Min%10);
        Nixie_SetBuf(3,11);
        Nixie_SetBuf(4,Sec/10);
        Nixie_SetBuf(5,Sec%10);
        Nixie_SetBuf(6,11);
        Nixie_SetBuf(7,MiniSec/10);
        Nixie_SetBuf(8,MiniSec%10);
    }
}

/**
  * @brief  秒表驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Sec_Loop(void)
{
    if(RunFlag)
    {
        MiniSec++;
        if(MiniSec>=100)
        {
            MiniSec=0;
            Sec++;
            if(Sec>=60)
            {
                Sec=0;
                Min++;
                if(Min>=60)
                {
                    Min=0;
                }
            }
        }
    }
}

void Timer0_Routine() interrupt 1
{
    static unsigned int T0Count1,T0Count2,T0Count3;
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    T0Count1++;
    if(T0Count1>=20)
    {
        T0Count1=0;
        Key_Loop();    //20ms调用一次按键驱动函数
    }
    T0Count2++;
    if(T0Count2>=2)
    {
        T0Count2=0;
        Nixie_Loop();//2ms调用一次数码管驱动函数
    }
    T0Count3++;
    if(T0Count3>=10)
    {
        T0Count3=0;
        Sec_Loop();    //10ms调用一次数秒表驱动函数
    }
}

13. DS18B20温度传感器

数字温度传感器

原理

DS18B02

13.1 温度读取

头文件

//OneWire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__

unsigned char OneWire_Init(void);
void OneWire_SendBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit(void);
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte(void);

#endif
//DS18B02.h
#ifndef __DS18B20_H__
#define __DS18B20_H__

void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);

#endif

函数

//OneWire.c
#include <REGX52.H>

//引脚定义
sbit OneWire_DQ=P3^7;

/**
  * @brief  单总线初始化
  * @param  无
  * @retval 从机响应位,0为响应,1为未响应
  */
unsigned char OneWire_Init(void)
{
    unsigned char i;
    unsigned char AckBit;
    OneWire_DQ=1;
    OneWire_DQ=0;
    i = 247;while (--i);        //Delay 500us
    OneWire_DQ=1;
    i = 32;while (--i);            //Delay 70us
    AckBit=OneWire_DQ;
    i = 247;while (--i);        //Delay 500us
    return AckBit;
}

/**
  * @brief  单总线发送一位
  * @param  Bit 要发送的位
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit)
{
    unsigned char i;
    OneWire_DQ=0;
    i = 4;while (--i);            //Delay 10us
    OneWire_DQ=Bit;
    i = 24;while (--i);            //Delay 50us
    OneWire_DQ=1;
}

/**
  * @brief  单总线接收一位
  * @param  无
  * @retval 读取的位
  */
unsigned char OneWire_ReceiveBit(void)
{
    unsigned char i;
    unsigned char Bit;
    OneWire_DQ=0;
    i = 2;while (--i);            //Delay 5us
    OneWire_DQ=1;
    i = 2;while (--i);            //Delay 5us
    Bit=OneWire_DQ;
    i = 24;while (--i);            //Delay 50us
    return Bit;
}

/**
  * @brief  单总线发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void OneWire_SendByte(unsigned char Byte)
{
    unsigned char i;
    for(i=0;i<8;i++)
    {
        OneWire_SendBit(Byte&(0x01<<i));
    }
}

/**
  * @brief  单总线接收一个字节
  * @param  无
  * @retval 接收的一个字节
  */
unsigned char OneWire_ReceiveByte(void)
{
    unsigned char i;
    unsigned char Byte=0x00;
    for(i=0;i<8;i++)
    {
        if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
    }
    return Byte;
}
//DS18B02.c
#include <REGX52.H>
#include "OneWire.h"

//DS18B20指令
#define DS18B20_SKIP_ROM          0xCC
#define DS18B20_CONVERT_T         0x44
#define DS18B20_READ_SCRATCHPAD     0xBE

/**
  * @brief  DS18B20开始温度变换
  * @param  无
  * @retval 无
  */
void DS18B20_ConvertT(void)
{
    OneWire_Init();
    OneWire_SendByte(DS18B20_SKIP_ROM);
    OneWire_SendByte(DS18B20_CONVERT_T);
}

/**
  * @brief  DS18B20读取温度
  * @param  无
  * @retval 温度数值
  */
float DS18B20_ReadT(void)
{
    unsigned char TLSB,TMSB;
    int Temp;
    float T;
    OneWire_Init();
    OneWire_SendByte(DS18B20_SKIP_ROM);
    OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
    TLSB=OneWire_ReceiveByte();
    TMSB=OneWire_ReceiveByte();
    Temp=(TMSB<<8)|TLSB;
    T=Temp/16.0;
    return T;
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"

float T;

void main()
{
    DS18B20_ConvertT();        //上电先转换一次温度,防止第一次读数据错误
    Delay(1000);            //等待转换完成
    LCD_Init();
    LCD_ShowString(1,1,"Temperature:");
    while(1)
    {
        DS18B20_ConvertT();    //转换温度
        T=DS18B20_ReadT();    //读取温度
        if(T<0)                //如果温度小于0
        {
            LCD_ShowChar(2,1,'-');    //显示负号
            T=-T;            //将温度变为正数
        }
        else                //如果温度大于等于0
        {
            LCD_ShowChar(2,1,'+');    //显示正号
        }
        LCD_ShowNum(2,2,T,3);        //显示温度整数部分
        LCD_ShowChar(2,5,'.');        //显示小数点
        LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示温度小数部分
    }
}

13.2 温度报警器

头文件

//Key.h
#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key(void);
void Key_Loop(void);

#endif
//OneWire.h
#ifndef __DS18B20_H__
#define __DS18B20_H__

void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);

#endif

函数

//Key.c
#include <REGX52.H>
#include "Delay.h"

unsigned char Key_KeyNumber;

/**
  * @brief  获取按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key(void)
{
    unsigned char Temp=0;
    Temp=Key_KeyNumber;
    Key_KeyNumber=0;
    return Temp;
}

/**
  * @brief  获取当前按键的状态,无消抖及松手检测
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key_GetState()
{
    unsigned char KeyNumber=0;

    if(P3_1==0){KeyNumber=1;}
    if(P3_0==0){KeyNumber=2;}
    if(P3_2==0){KeyNumber=3;}
    if(P3_3==0){KeyNumber=4;}

    return KeyNumber;
}

/**
  * @brief  按键驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Key_Loop(void)
{
    static unsigned char NowState,LastState;
    LastState=NowState;                //按键状态更新
    NowState=Key_GetState();        //获取当前按键状态
    //如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
    if(LastState==1 && NowState==0)
    {
        Key_KeyNumber=1;
    }
    if(LastState==2 && NowState==0)
    {
        Key_KeyNumber=2;
    }
    if(LastState==3 && NowState==0)
    {
        Key_KeyNumber=3;
    }
    if(LastState==4 && NowState==0)
    {
        Key_KeyNumber=4;
    }
}
//OneWire.c  函数改动部分(跳出中断)单总线不能中断
#include <REGX52.H>

//引脚定义
sbit OneWire_DQ=P3^7;

/**
  * @brief  单总线初始化
  * @param  无
  * @retval 从机响应位,0为响应,1为未响应
  */
unsigned char OneWire_Init(void)
{
    unsigned char i;
    unsigned char AckBit;
    EA=0;                     //关闭中断
    OneWire_DQ=1;
    OneWire_DQ=0;
    i = 247;while (--i);        //Delay 500us
    OneWire_DQ=1;
    i = 32;while (--i);            //Delay 70us
    AckBit=OneWire_DQ;
    i = 247;while (--i);        //Delay 500us
    EA=1;                     //打开中断
    return AckBit;
}
.
.
.
//main
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"
#include "Buzzer.h"

float T,TShow;
char TLow,THigh;
unsigned char KeyNum;

void main()
{
    DS18B20_ConvertT();        //上电先转换一次温度,防止第一次读数据错误
    Delay(1000);            //等待转换完成
    THigh=AT24C02_ReadByte(0);    //读取温度阈值数据
    TLow=AT24C02_ReadByte(1);
    if(THigh>125 || TLow<-55 || THigh<=TLow)
    {
        THigh=20;            //如果阈值非法,则设为默认值
        TLow=15;
    }
    LCD_Init();
    LCD_ShowString(1,1,"T:");
    LCD_ShowString(2,1,"TH:");
    LCD_ShowString(2,9,"TL:");
    LCD_ShowSignedNum(2,4,THigh,3);
    LCD_ShowSignedNum(2,12,TLow,3);
    Timer0_Init();

    while(1)
    {
        KeyNum=Key();

        /*温度读取及显示*/
        DS18B20_ConvertT();    //转换温度
        T=DS18B20_ReadT();    //读取温度
        if(T<0)                //如果温度小于0
        {
            LCD_ShowChar(1,3,'-');    //显示负号
            TShow=-T;        //将温度变为正数
        }
        else                //如果温度大于等于0
        {
            LCD_ShowChar(1,3,'+');    //显示正号
            TShow=T;
        }
        LCD_ShowNum(1,4,TShow,3);        //显示温度整数部分
        LCD_ShowChar(1,7,'.');        //显示小数点
        LCD_ShowNum(1,8,(unsigned long)(TShow*100)%100,2);//显示温度小数部分

        /*阈值判断及显示*/
        if(KeyNum)
        {
            if(KeyNum==1)    //K1按键,THigh自增
            {
                THigh++;
                if(THigh>125){THigh=125;}
            }
            if(KeyNum==2)    //K2按键,THigh自减
            {
                THigh--;
                if(THigh<=TLow){THigh++;}
            }
            if(KeyNum==3)    //K3按键,TLow自增
            {
                TLow++;
                if(TLow>=THigh){TLow--;}
            }
            if(KeyNum==4)    //K4按键,TLow自减
            {
                TLow--;
                if(TLow<-55){TLow=-55;}
            }
            LCD_ShowSignedNum(2,4,THigh,3);    //显示阈值数据
            LCD_ShowSignedNum(2,12,TLow,3);
            AT24C02_WriteByte(0,THigh);        //写入到At24C02中保存
            Delay(5);
            AT24C02_WriteByte(1,TLow);
            Delay(5);
        }
        if(T>THigh)            //越界判断
        {
            LCD_ShowString(1,13,"OV:H");
            Buzzer_Time(100); //报警器函数
        }
        else if(T<TLow)
        {
            LCD_ShowString(1,13,"OV:L");
            Buzzer_Time(100);
        }
        else
        {
            LCD_ShowString(1,13,"    ");
        }
    }
}

void Timer0_Routine() interrupt 1
{
    static unsigned int T0Count;
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    T0Count++;
    if(T0Count>=20)
    {
        T0Count=0;
        Key_Loop();    //每20ms调用一次按键驱动函数
    }
}

14. LCD1602

LCD与数码管引脚冲突

原理(图片)

代码

头文件

//LCD_1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__

void LCD_Init(void);
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_WriteCommand(unsigned char Command);

#endif

函数

//LCD_1602.c
#include <REGX52.H>

//引脚定义
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_E=P2^7;
#define LCD_DataPort P0

/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()        //@12.000MHz 1ms
{
    unsigned char i, j;

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

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
    LCD_RS=0;
    LCD_RW=0;
    LCD_DataPort=Command;
    LCD_E=1;
    LCD_Delay();
    LCD_E=0;
    LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
    LCD_RS=1;
    LCD_RW=0;
    LCD_DataPort=Data;
    LCD_E=1;
    LCD_Delay();
    LCD_E=0;
    LCD_Delay();
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init(void)
{
    LCD_WriteCommand(0x38);
    LCD_WriteCommand(0x0C);
    LCD_WriteCommand(0x06);
    LCD_WriteCommand(0x01);
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
    if(Line==1)
    {
        LCD_WriteCommand(0x80|(Column-1));
    }
    else
    {
        LCD_WriteCommand(0x80|(Column-1)+0x40);
    }
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
    LCD_SetCursor(Line,Column);
    LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String)
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=0;String[i]!='\0';i++)
    {
        LCD_WriteData(String[i]);
    }
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
    unsigned char i;
    int Result=1;
    for(i=0;i<Y;i++)
    {
        Result*=X;
    }
    return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD_WriteData('0'+Number/LCD_Pow(10,i-1)%10);
    }
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
    unsigned char i;
    unsigned int Number1;
    LCD_SetCursor(Line,Column);
    if(Number>=0)
    {
        LCD_WriteData('+');
        Number1=Number;
    }
    else
    {
        LCD_WriteData('-');
        Number1=-Number;
    }
    for(i=Length;i>0;i--)
    {
        LCD_WriteData('0'+Number1/LCD_Pow(10,i-1)%10);
    }
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    unsigned char SingleNumber;
    LCD_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        SingleNumber=Number/LCD_Pow(16,i-1)%16;
        if(SingleNumber<10)
        {
            LCD_WriteData('0'+SingleNumber);
        }
        else
        {
            LCD_WriteData('A'+SingleNumber-10);
        }
    }
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD_WriteData('0'+Number/LCD_Pow(2,i-1)%2);
    }
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"

void main()
{
    LCD_Init();                        //LCD初始化
    LCD_ShowChar(1,1,'A');            //在1行1列显示字符A
    LCD_ShowString(1,3,"Hello");    //在1行3列显示字符串Hello
    LCD_ShowNum(1,9,66,2);            //在1行9列显示数字66,长度为2
    LCD_ShowSignedNum(1,12,-88,2);    //在1行12列显示有符号数字-88,长度为2
    LCD_ShowHexNum(2,1,0xA5,2);        //在2行1列显示十六进制数字0xA5,长度为2
    LCD_ShowBinNum(2,4,0xA5,8);        //在2行4列显示二进制数字0xA5,长度为8
    LCD_ShowChar(2,13,0xDF);        //在2行13列显示编码为0xDF的字符
    LCD_ShowChar(2,14,'C');            //在2行14列显示字符C
    LCD_ShowString(1,16,"Welcome to China!"); //字符往左移动
    while(1)
    {
        LCD_WriteCommand(0x18);
        Delay(500);
    }
}

15. 直流电机驱动(PWM)

原理

五线四项步进电机

15.1 LED呼吸灯

#include <REGX52.H>

sbit LED=P2^0;

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

void main()
{
    unsigned char Time,i;

    while(1)
    {
        for(Time=0;Time<100;Time++) //暗->亮
        {
            for(i=0;i<20;i++)
            {
                LED=0;
                Delay(Time);
                LED=1;
                Delay(100-Time);
            }
        }

        for(Time=100;Time>0;Time--) //亮->暗
        {
            for(i=0;i<20;i++)
            {
                LED=0;
                Delay(Time);
                LED=1;
                Delay(100-Time);
            }
        }
    }
}

15.2 直流电机调速

//Timer0.c
#include <REGX52.H>

/**
  * @brief  定时器0初始化,100us@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0x9C;        //设置定时初值
    TH0 = 0xFF;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0=1;
    EA=1;
    PT0=0;
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Timer0.h"

sbit Motor=P1^0;  //给1电机转

unsigned char Counter,Compare;
unsigned char KeyNum,Speed;

void main()
{
    Timer0_Init();
    Compare=90;
    while(1)
    {
        KeyNum=Key();
        if(KeyNum==1)
        {
            Speed++;
            Speed%=4;
            if(Speed==0) {Compare=0;}
            if(Speed==1) {Compare=50;}
            if(Speed==2) {Compare=75;}
            if(Speed==3) {Compare=100;}
        }
        Nixie(1,Speed); //Speed越大,Compare越大,所占比也就越大
    }
}

void Timer0_Routine() interrupt 1
{
    TL0 = 0x9C;        //设置定时初值
    TH0 = 0xFF;        //设置定时初值
    Counter++;
    Counter%=100;
    if(Counter<Compare)
    {
        Motor=1;
    }
    else
    {
        Motor=0;
    }
}

16. AD/DA

原理

AD:模拟--数字转换,模拟信号变为计算机可操作的数字信号

DA:数字--模拟转换

ADC

DAC

AD模数转换

头文件

//XPT2046.h
#ifndef __XPT2046_H_
#define __XPT2046_H_
//8位
#define XPT2046_XP_8    0x9C
#define XPT2046_YP_8    0xDC
#define XPT2046_VBAT_8  0xAC
#define XPT2046_AUX_8   0xEC
//12位
#define XPT2046_XP_12    0x94
#define XPT2046_YP_12    0xD4
#define XPT2046_VBAT_12  0xA4
#define XPT2046_AUX_12   0xE4

unsigned int XPT2046_ReadAD(unsigned char Command);

#endif

函数

//XPT2046.c
#include <REGX52.H>
//引脚定义
sbit XPT2046_CS=P3^5;
sbit XPT2046_DCLK=P3^6;
sbit XPT2046_DIN=P3^4;
sbit XPT2046_DOUT=P3^7;

/**
  * @brief  ZPT2046读取AD值
  * @param  Command 命令字,范围:头文件内定义的宏,结尾的数字表示转换的位数
  * @retval AD转换后的数字量,范围:8位为0~255,12位为0~4095
  */
unsigned int XPT2046_ReadAD(unsigned char Command)
{
    unsigned  char i;
    unsigned int ADValue=0;
    XPT2046_DCLK=0;
    XPT2046_CS=0;

    for(i=0;i<8;i++)
    {
        XPT2046_DIN=Command&(0x80>>i);
        XPT2046_DCLK=1;
        XPT2046_DCLK=0;
    }

    for(i=0;i<16;i++)
    {

      XPT2046_DCLK=1;
        XPT2046_DCLK=0;
        if(XPT2046_DOUT) {ADValue|=(0x8000>>i);}
    }
    XPT2046_CS=1;
    if(Command&0x08)
    {
        return ADValue>>8; //8位显示
    }
    else return ADValue>>4; //12位显示
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "XPT2046.h"

unsigned int ADValue;

void main()
{
    LCD_Init();
    LCD_ShowString(1,1,"ADJ NTC RG");
    while(1)
    {
        ADValue= XPT2046_ReadAD(XPT2046_XP_8); //读取AIN0,可调电阻
        LCD_ShowNum(2,1,ADValue,3);
        ADValue= XPT2046_ReadAD(XPT2046_YP_8); //读取AIN1,热敏电阻
        LCD_ShowNum(2,5,ADValue,3);
        ADValue= XPT2046_ReadAD(XPT2046_VBAT_8);  //读取AIN2,光敏电阻
        LCD_ShowNum(2,9,ADValue,3);
        Delay(10);

    }
}

DA数模转换

//main.c 直流电机微改
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"

sbit DA=P2^1;

unsigned char Counter,Compare;    //计数值和比较值,用于输出PWM
unsigned char i;

void main()
{
    Timer0_Init();
    while(1)
    {
        for(i=0;i<100;i++)
        {
            Compare=i;            //设置比较值,改变PWM占空比
            Delay(10);
        }
        for(i=100;i>0;i--)
        {
            Compare=i;            //设置比较值,改变PWM占空比
            Delay(10);
        }
    }
}

void Timer0_Routine() interrupt 1
{
    TL0 = 0x9C;        //设置定时初值
    TH0 = 0xFF;        //设置定时初值
    Counter++;
    Counter%=100;    //计数值变化范围限制在0~99
    if(Counter<Compare)    //计数值小于比较值
    {
        DA=1;        //输出1
    }
    else                //计数值大于比较值
    {
        DA=0;        //输出0
    }
}

17. 红外遥控

原理

红外接收模块

遥控器键码

遥控器

17.1 红外遥控

头文件

//Int0.h
#ifndef __INT0_H__
#define __INT0_H__

void Int0_Init(void);

#endif
//Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter(void);
void Timer0_Run(unsigned char Flag);

#endif
//IR.h
#ifndef __IR_H__
#define __IR_H__

#define IR_POWER        0x45
#define IR_MODE            0x46
#define IR_MUTE            0x47
#define IR_START_STOP    0x44
#define IR_PREVIOUS        0x40
#define IR_NEXT            0x43
#define IR_EQ            0x07
#define IR_VOL_MINUS    0x15
#define IR_VOL_ADD        0x09
#define IR_0            0x16
#define IR_RPT            0x19
#define IR_USD            0x0D
#define IR_1            0x0C
#define IR_2            0x18
#define IR_3            0x5E
#define IR_4            0x08
#define IR_5            0x1C
#define IR_6            0x5A
#define IR_7            0x42
#define IR_8            0x52
#define IR_9            0x4A

void IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned char IR_GetCommand(void);

#endif

函数

//Int0.c
#include <REGX52.H>

/**
  * @brief  外部中断0初始化
  * @param  无
  * @retval 无
  */
void Int0_Init(void)
{
    IT0=1;
    IE0=0;
    EX0=1;
    EA=1;
    PX0=1;
}

/*外部中断0中断函数模板
void Int0_Routine(void) interrupt 0
{

}
*/
//Timer0.c
#include <REGX52.H>

/**
  * @brief  定时器0初始化
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0;        //设置定时初值
    TH0 = 0;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 0;        //定时器0不计时
}

/**
  * @brief  定时器0设置计数器值
  * @param  Value,要设置的计数器值,范围:0~65535
  * @retval 无
  */
void Timer0_SetCounter(unsigned int Value)
{
    TH0=Value/256;
    TL0=Value%256;
}

/**
  * @brief  定时器0获取计数器值
  * @param  无
  * @retval 计数器值,范围:0~65535
  */
unsigned int Timer0_GetCounter(void)
{
    return (TH0<<8)|TL0;
}

/**
  * @brief  定时器0启动停止控制
  * @param  Flag 启动停止标志,1为启动,0为停止
  * @retval 无
  */
void Timer0_Run(unsigned char Flag)
{
    TR0=Flag;
}
//IR.c
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"

unsigned int IR_Time;
unsigned char IR_State;

unsigned char IR_Data[4];
unsigned char IR_pData;

unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;

/**
  * @brief  红外遥控初始化
  * @param  无
  * @retval 无
  */
void IR_Init(void)
{
    Timer0_Init();
    Int0_Init();
}

/**
  * @brief  红外遥控获取收到数据帧标志位
  * @param  无
  * @retval 是否收到数据帧,1为收到,0为未收到
  */
unsigned char IR_GetDataFlag(void)
{
    if(IR_DataFlag)
    {
        IR_DataFlag=0;
        return 1;
    }
    return 0;
}

/**
  * @brief  红外遥控获取收到连发帧标志位
  * @param  无
  * @retval 是否收到连发帧,1为收到,0为未收到
  */
unsigned char IR_GetRepeatFlag(void)
{
    if(IR_RepeatFlag)
    {
        IR_RepeatFlag=0;
        return 1;
    }
    return 0;
}

/**
  * @brief  红外遥控获取收到的地址数据
  * @param  无
  * @retval 收到的地址数据
  */
unsigned char IR_GetAddress(void)
{
    return IR_Address;
}

/**
  * @brief  红外遥控获取收到的命令数据
  * @param  无
  * @retval 收到的命令数据
  */
unsigned char IR_GetCommand(void)
{
    return IR_Command;
}

//外部中断0中断函数,下降沿触发执行
void Int0_Routine(void) interrupt 0
{
    if(IR_State==0)                //状态0,空闲状态
    {
        Timer0_SetCounter(0);    //定时计数器清0
        Timer0_Run(1);            //定时器启动
        IR_State=1;                //置状态为1
    }
    else if(IR_State==1)        //状态1,等待Start信号或Repeat信号
    {
        IR_Time=Timer0_GetCounter();    //获取上一次中断到此次中断的时间
        Timer0_SetCounter(0);    //定时计数器清0
        //如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
        if(IR_Time>12442-500 && IR_Time<12442+500)
        {
            IR_State=2;            //置状态为2
        }
        //如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
        else if(IR_Time>10368-500 && IR_Time<10368+500)
        {
            IR_RepeatFlag=1;    //置收到连发帧标志位为1
            Timer0_Run(0);        //定时器停止
            IR_State=0;            //置状态为0
        }
        else                    //接收出错
        {
            IR_State=1;            //置状态为1
        }
    }
    else if(IR_State==2)        //状态2,接收数据
    {
        IR_Time=Timer0_GetCounter();    //获取上一次中断到此次中断的时间
        Timer0_SetCounter(0);    //定时计数器清0
        //如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
        if(IR_Time>1032-500 && IR_Time<1032+500)
        {
            IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));    //数据对应位清0
            IR_pData++;            //数据位置指针自增
        }
        //如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
        else if(IR_Time>2074-500 && IR_Time<2074+500)
        {
            IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));    //数据对应位置1
            IR_pData++;            //数据位置指针自增
        }
        else                    //接收出错
        {
            IR_pData=0;            //数据位置指针清0
            IR_State=1;            //置状态为1
        }
        if(IR_pData>=32)        //如果接收到了32位数据
        {
            IR_pData=0;            //数据位置指针清0
            if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3]))    //数据验证
            {
                IR_Address=IR_Data[0];    //转存数据
                IR_Command=IR_Data[2];
                IR_DataFlag=1;    //置收到连发帧标志位为1
            }
            Timer0_Run(0);        //定时器停止
            IR_State=0;            //置状态为0
        }
    }
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"

unsigned char Num;
unsigned char Address;
unsigned char Command;

void main()
{
    LCD_Init();
    LCD_ShowString(1,1,"ADDR  CMD  NUM");
    LCD_ShowString(2,1,"00    00   000");

    IR_Init();

    while(1)
    {
        if(IR_GetDataFlag() || IR_GetRepeatFlag())    //如果收到数据帧或者收到连发帧
        {
            Address=IR_GetAddress();        //获取遥控器地址码
            Command=IR_GetCommand();        //获取遥控器命令码

            LCD_ShowHexNum(2,1,Address,2);    //显示遥控器地址码
            LCD_ShowHexNum(2,7,Command,2);    //显示遥控器命令码

            if(Command==IR_VOL_MINUS)        //如果遥控器VOL-按键按下
            {
                Num--;                        //Num自减
            }
            if(Command==IR_VOL_ADD)            //如果遥控器VOL+按键按下
            {
                Num++;                        //Num自增
            }

            LCD_ShowNum(2,12,Num,3);        //显示Num
        }
    }
}

17.2 红外遥控电机调速

头文件

//Timer1.h
#ifndef __TIMER1_H__
#define __TIMER1___

void Timer1_Init(void);

#endif
//Motor.h
#ifndef __MOTOR_H__
#define __MOTOR_H__

void Motor_Init(void);
void Motor_SetSpeed(unsigned char Speed);

#endif
//Int0.h
#ifndef __INT0_H__
#define __INT0_H__

void Int0_Init(void);

#endif
//Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter(void);
void Timer0_Run(unsigned char Flag);

#endif

函数

//Timer1.c
#include <REGX52.H>

/**
  * @brief  定时器1初始化,100us@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer1_Init(void)
{
    TMOD &= 0x0F;        //设置定时器模式
    TMOD |= 0x10;        //设置定时器模式
    TL1 = 0x9C;        //设置定时初值
    TH1 = 0xFF;        //设置定时初值
    TF1 = 0;        //清除TF1标志
    TR1 = 1;        //定时器1开始计时
    ET1=1;
    EA=1;
    PT1=0;
}

/*定时器中断函数模板
void Timer1_Routine() interrupt 3
{
    static unsigned int T1Count;
    TL1 = 0x9C;        //设置定时初值
    TH1 = 0xFF;        //设置定时初值
    T1Count++;
    if(T1Count>=1000)
    {
        T1Count=0;

    }
}
*/
//Motor.c
#include <REGX52.H>
#include "Timer1.h"

//引脚定义
sbit Motor=P1^0;

unsigned char Counter,Compare;

/**
  * @brief  电机初始化
  * @param  无
  * @retval 无
  */
void Motor_Init(void)
{
    Timer1_Init();
}

/**
  * @brief  电机设置速度
  * @param  Speed 要设置的速度,范围0~100
  * @retval 无
  */
void Motor_SetSpeed(unsigned char Speed)
{
    Compare=Speed;
}

//定时器1中断函数
void Timer1_Routine() interrupt 3
{
    TL1 = 0x9C;        //设置定时初值
    TH1 = 0xFF;        //设置定时初值
    Counter++;
    Counter%=100;    //计数值变化范围限制在0~99
    if(Counter<Compare)    //计数值小于比较值
    {
        Motor=1;        //输出1
    }
    else                //计数值大于比较值
    {
        Motor=0;        //输出0
    }
}
//Int0.c
#include <REGX52.H>

/**
  * @brief  外部中断0初始化
  * @param  无
  * @retval 无
  */
void Int0_Init(void)
{
    IT0=1;
    IE0=0;
    EX0=1;
    EA=1;
    PX0=1;
}

/*外部中断0中断函数模板
void Int0_Routine(void) interrupt 0
{

}
*/
#include <REGX52.H>

/**
  * @brief  定时器0初始化
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0;        //设置定时初值
    TH0 = 0;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 0;        //定时器0不计时
}

/**
  * @brief  定时器0设置计数器值
  * @param  Value,要设置的计数器值,范围:0~65535
  * @retval 无
  */
void Timer0_SetCounter(unsigned int Value)
{
    TH0=Value/256;
    TL0=Value%256;
}

/**
  * @brief  定时器0获取计数器值
  * @param  无
  * @retval 计数器值,范围:0~65535
  */
unsigned int Timer0_GetCounter(void)
{
    return (TH0<<8)|TL0;

/**
  * @brief  定时器0启动停止控制
  * @param  Flag 启动停止标志,1为启动,0为停止
  * @retval 无
  */
void Timer0_Run(unsigned char Flag)
{
    TR0=Flag;
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Motor.h"
#include "IR.h"

unsigned char Command,Speed;

void main()
{
    Motor_Init();
    IR_Init();
    while(1)
    {
        if(IR_GetDataFlag())    //如果收到数据帧
        {
            Command=IR_GetCommand();        //获取遥控器命令码

            if(Command==IR_0){Speed=0;}        //根据遥控器命令码设置速度
            if(Command==IR_1){Speed=1;}
            if(Command==IR_2){Speed=2;}
            if(Command==IR_3){Speed=3;}

            if(Speed==0){Motor_SetSpeed(0);}    //速度输出
            if(Speed==1){Motor_SetSpeed(50);}
            if(Speed==2){Motor_SetSpeed(75);}
            if(Speed==3){Motor_SetSpeed(100);}
        }
        Nixie(1,Speed);                        //数码管显示速度
    }
}

芜湖,本文章于 2022-12-14 完结散花~~~ 后续会持续优化本文

下面开启STM32的旅途?

标签: 单片机

仅有一条评论

  1. […] 51单片机入门教程(下篇)(代码+个人理解)2022年12月9日 […]

添加新评论