51 SCM drinking water reminder

timer

Timer introduction

crystal oscillator

Crystal oscillators, also known as the "heart" of digital circuits, are essential frequency components in various electronic products. All the work of digital circuits is inseparable from the clock. The quality of the crystal oscillator and the design of the crystal oscillator circuit will affect the stability of the entire system.

clock cycle

The clock period, also known as the oscillation period, is defined as the reciprocal of the clock frequency. A clock cycle is the most basic and smallest unit of time in a computer. In one clock cycle, the CPU only completes one of the most basic actions. A clock cycle is a quantity of time. A smaller clock period means a higher operating frequency.

mechanical cycle

Machine cycles are also known as CPU cycles. In a computer, in order to facilitate management, the execution process of an instruction is often divided into several stages (such as fetching, decoding, executing, etc.), and each stage completes a basic operation. The time required to complete a basic operation is called a machine cycle. In general, a machine cycle consists of several clock cycles.

How long does it take for each mechanical cycle to pass

Take the crystal oscillator frequency of 11.0592MHZ as an example, the clock period is the reciprocal of the crystal oscillator, that is, 1/1105920000 seconds.

  • If it is 12T mode, the mechanical cycle = 12 X clock cycle, that is, 12/11059200000 seconds = 1.085 microseconds

  • If it is 6T mode, the mechanical cycle = 6 X clock cycle, that is, 6/11059200000 seconds = 0.5425 microseconds

The difference between timer and counter

The timer and counter in the 51 single-chip microcomputer use the same hardware circuit, and the hardware circuit can be changed into a timer or a counter by modifying the configuration of the register.

  • When configured as a timer, every time a mechanical cycle passes, the value of the counting memory is increased by 1. C51 has two timers T0 and T1.

  • When configured as a counter, every time a negative transition signal (high level jumps to low level), the value of the counting memory is increased by 1.

Timer Related Registers

Timer Timing Registers (TH and TL Registers)

When both the TH register and TL register of the timer are used, that is, there are 16 bits in total, then the timer counts at most 2^16 = 65536 times, that is, about 65536*1.085 microseconds = 71 milliseconds, that is to say, the timer T0 or The timer T1 can time up to 71 milliseconds.

For example, now you need to use timer 0 to time 10 milliseconds, how to configure registers TH0 and TL0, you only need to configure TH0 = 0xDC, TL0 = 0x00.

TIMER CONTROL REGISTER (TCON REGISTER)

Take timer 0 and external interrupt 0 as an example

  • TF标志位:当定时器0爆表后(即定时结束后),TF标志位,TF0会置1(TF0 = 1),此时会向CPU请求中断,如果中断条件允许的话就执行外部中断0,执行完中断后,TF0会硬件置0(TF = 0),当我们不想它执行中断就可以软件置0,即手动将TF0置0(TF =0)。

  • TR标志位:当TR0 =1 时,定时器0才1会允许计数,即开始计时,当TR0 = 0时,不允许定时器0进行计数。

  • IE标志位:当IE0 = 1时,会向CPU请求外部中断0,当CPU响应外部中断0后会将IE0硬件置零(IE0 = 0)。

  • IT标志位:当IT0 = 1时,低电平触发外部中断0;当IT0 = 0时,下降沿触发外部中断0。

定时器模式寄存器(TMOD寄存器)

以定时器0为例

  • GATE标志位:一般为0,GATE = 0时,当TR0 = 0时,定时器0开始计数。

  • C/T标志位:一般为0,C/T为0时,让定时器0作为定时器

  • M1、M0标志位:一般为0、1,16位定时器,TL0和TH0两个寄存器都使用。

通过定时器0,让蜂鸣器叫一秒,不叫一秒,定时器0爆表后,不执行中断

#include"reg52.h"

sbit buzzer = P1^2;

voidmain(){
	int cnt = 0;
	buzzer = 1;
	
	TMOD = 0x01; //设置定时器0为16为计时模式
	
	//设置定时器0定时时间为10ms
	TH0 = 0xDC;
	TL0 = 0x00;

	TR0 = 1; //定时器0开始计时
	TF0 = 0; //外部中断0标志位置0,不进行定时器0产生的中断

	while(1){
		//当定时器0爆表时
		if(TF0 == 1){ 
			TF0 = 0; //外部中断0标志位置0,不进行定时器0产生的中断
			cnt++;
			TH0 = 0xDC;
			TL0 = 0x00;
			if(cnt == 100){
				cnt = 0;
				buzzer = !buzzer;
			}
		}
	}
}

超声波(HC-SR04)测距

超声波时序图

  • 发送超声波:当Trig引脚接收到一个10微秒以上的高电平后开始发送超声波,当开始发送超声波后,Echo引脚会从低电平跳转到高电平。

  • 接收超声波:当发出去的超声波返回来并被接收后,Echo引脚会从高电平跳转到低电平。

  • 超声波从发出到被接收的时间:Echo持续高电平的时间,当超声波发出去的瞬间启动定时器,超声波被接收的瞬间停止定时器,查看中间经过的时间。

  • 测距:距离 = 声音速度(340m/s)* 时间 / 2,除以2是因为超声波经过了两倍距离。

超声波测距,距离小于10cm时,蜂鸣器叫,距离大于10cm时,蜂鸣器不叫

#include "reg52.h"
#include <intrins.h>
 
sbit buzzer = P1^4;
sbit Trig = P1^2;
sbit Echo = P1^3;
 
void Delay15us()        //@11.0592MHz
{
    unsigned char i;
 
    i = 4;
    while (--i);
}
 
void timer1Init()
{    
    TMOD = 0x10;
    TH1 = 0x00;
    TL1 = 0x00;
    TF1 = 0;
}
 
void ultrasonicStart()
{
    Trig = 0;
    Trig = 1;
    Delay15us();
    Trig = 0; 
}
 
void main()
{
    double time = 0;
    double distance = 0;
 
    timer1Init();
 
    while(1){
        
        ultrasonicStart();
        
        while(Echo == 0);  //当Echo引脚从低电平跳到高电平时开启定时器1
        TR1 = 1;
        
        while(Echo == 1);  //当Echo引脚从高电平跳到低电平时关闭定时器1
        TR1 = 0;
 
        time = (TH1*256 + TL1) * 1.085;  //微秒
        /*
            定时器16位全用时:
            高八位TH寄存器每加次1,计数存储器的值就加256;
            低八位TL寄存器每次加1,计数存储器的值就加1;
            计数存储器的值每次加1时,就经过了一个机械周期(经过时间1.085微秒)
        */
 
        distance = time * 0.017;  //CM
 
        if(distance < 10){
            buzzer = 0;
        }else{
            buzzer = 1;
        }
        //定时器1清0
        TH1 = 0x00;
        TL1 = 0x00;
    }
 
}

LCD1602

LCD1602介绍

LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。字符型液晶显示模块是一种专门用于显示字母、数字和符号等的点阵式LCD,常用16×1,16×2,20×2和40×2等的模块。一般的LCD1602字符型液晶显示器的内部控制器大部分为HD44780,能够显示英文字母、阿拉伯数字、日文片假名和一般性符号。

LCD1602引脚

  • 第 1 脚: VSS 为电源地

  • 第 2 脚: VDD 接 5V 正电源

  • 第 3 脚: VL 为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度

  • 过高时会产生“鬼影”,使用时可以通过一个 10K 的电位器调整对比度。

  • 第 4 脚:RS 为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。

  • 第 5 脚:R/W 为读写信号线,高电平时进行读操作,低电平时进行写操作。当 RS 和 R/W 共 同为低电平时可以写入指令或者显示地址,当 RS 为低电平 R/W 为高电平时可以读忙信号, 当 RS 为高电平 R/W 为低电平时可以写入数据。

  • 第 6 脚:E 端为使能端,当 E 端由高电平跳变成低电平时,液晶模块执行命令。

  • 第 7~14 脚:D0~D7 为 8 位双向数据线。

  • 第 15 脚:背光源正极。

  • 第 16 脚:背光源负极。

LCD1602显示字符

写时序图和读时序图

读时序图

写时序图

时序图的参数

根据读时序图,封装读取LCD1602的数据函数,检测忙信号

检测忙标志是否忙

  • 液晶显示模块是一个慢显示器件,所以在写入每条指令或数据之前一定要确认模块的忙标志为低电平,表示不忙,否则此指令失效。

  • 由于BF标志位为数据线的D7引脚,我们只关心D7引脚,而且BF标志位为高电平时,LCD1602表示忙,可以利用一个while(BF)循环卡住,然后不断读取数据线的D7引脚,等待LCD1602不忙时,硬件将BF标志位置0。

根据写时序图,封装LCD1602写入指令和写入数据的两个函数

写入指令(地址)

写入数据(地址)

LCD1602初始化

8bit的LCD1602初始化

  1. 延时 15ms

  1. 写指令 38H(不检测忙信号)

  1. 延时 5ms

  1. 检测忙信号

  1. 写指令 38H:显示模式设置

  1. 写指令 08H:显示关闭

  1. 写指令 01H:显示清屏

  1. 写指令 06H:显示光标移动设置

  1. 写指令 0CH:显示开及光标设置

4bit的LCD1602初始化

  1. 延时 50ms

  1. 发送 0x03(4bit)(rs=0,rw=0)

  1. 延时 4.5ms

  1. 发送 0x03(4bit)(rs=0,rw=0)

  1. 延时 4.5ms

  1. 发送 0x03(4bit)(rs=0,rw=0)

  1. 延时 150μs

  1. 发送 0x02(4bit)(rs=0,rw=0)

  1. 写指令 28H(8bit)

  1. 写指令 0CH(8bit)

  1. 写指令 01H(8bit)

  1. 延时 2ms(8bit)

  1. 写指令 06H(8bit)

在哪里显示,写入指令(地址)

LCD1602 内部显示地址

由于写入指令或数据的时候,数据线D7恒定为高电平,即如果想要在0x40显示字符,则实际写入的地址为0x40 + 0x80。

显示什么字符(写入数据)

LCD1602 模块字库表

由于字符A的ASCII码为65,即0100 0001,与LCD1602 模块字库表一致,因此在写入字符的时候,直接传入一个'A'即可。

LCD1602显示一个字符

#include"reg52.h"#include<intrins.h>#define dataBuf P0

sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;

voidDelay5ms()		//@11.0592MHz{
	unsignedchar i, j;

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

voidDelay15ms()		//@11.0592MHz{
	unsignedchar i, j;

	i = 27;
	j = 226;
	do
	{
		while (--j);
	} while (--i);
}

//检测忙信号,读数据线的D7位voidcheckBusySignal(){
	char tmp = 0x80;
	dataBuf = 0x80;
	while(tmp & 0x80){  //当忙标志位(数据位D7)为高电平时,此时模块不能接收命令或者数据
		//根据读时序图,读LCD1602的数据
        RS = 0;
		RW = 1;
		EN = 0;
		_nop_();
		EN = 1;
		_nop_();
		_nop_();
		tmp = dataBuf;  //读八位数据线的数据,当不忙时,硬件会自动将该位置0,当检测到D7为低电平时将退出死循环
		EN = 0;
		_nop_();
	} 
}

//写指令voidwriteCmd(char cmd){
	checkBusySignal();
	RS = 0;  //选择写指令寄存器
	RW = 0;
	EN = 0;
	_nop_();  //该函数为延时函数,延时一微秒
	dataBuf = cmd;  //将指令存入八位数据线中
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}

//写数据voidwriteData(char myData){
	checkBusySignal();
	RS = 1;  //选择写数据寄存器
	RW = 0;
	EN = 0;
	_nop_();  //该函数为延时函数,延时一微秒
	dataBuf = myData;  //将数据存入八位数据线中
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}

//LCD1602初始化voidlcd1602Init(){
	Delay15ms();  //延时 15ms
	writeCmd(0x38);  //写指令 38H(不检测忙信号) 
	Delay5ms();  //延时 5ms
	checkBusySignal();//以后每次写指令,读/写数据操作均需要检测忙信号
	writeCmd(0x38);  //写指令 38H:显示模式设置
	writeCmd(0x08);  //写指令 08H:显示关闭
	writeCmd(0x01);  //写指令 01H:显示清屏
	writeCmd(0x06);  //写指令 06H:显示光标移动设置
	writeCmd(0x0C);  //写指令 0CH:显示开及光标设置
}

voidmain(){
	char displayAddress = 0x80 + 0x05;  
	char dsiplayData = 'J';  

	lcd1602Init();

	writeCmd(displayAddress);  //字符显示的地址
	writeData(dsiplayData);  //显示的字符
}

LCD1602显示一行字符

#include"reg52.h"#include<intrins.h>#define dataBuf P0

sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;

voidDelay5ms()		//@11.0592MHz{
	unsignedchar i, j;

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

voidDelay15ms()		//@11.0592MHz{
	unsignedchar i, j;

	i = 27;
	j = 226;
	do
	{
		while (--j);
	} while (--i);
}

//检测忙信号,读数据线的D7位voidcheckBusySignal(){
	char tmp = 0x80;
	dataBuf = 0x80;
	while(tmp & 0x80){  //当忙标志位(数据位D7)为高电平时,此时模块不能接收命令或者数据
		RS = 0;
		RW = 1;
		EN = 0;
		_nop_();
		EN = 1;
		_nop_();
		_nop_();
		tmp = dataBuf;  //读八位数据线的数据,当不忙时,硬件会自动将该位置0,当检测到D7为低电平时将退出死循环
		EN = 0;
		_nop_();
	} 
}

//写指令voidwriteCmd(char cmd){
	checkBusySignal();
	RS = 0;  //选择写指令寄存器
	RW = 0;
	EN = 0;
	_nop_();  //该函数为延时函数,延时一微秒
	dataBuf = cmd;  //将指令存入八位数据线中
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}

//写数据voidwriteData(char myData){
	checkBusySignal();
	RS = 1;  //选择写数据寄存器
	RW = 0;
	EN = 0;
	_nop_();  //该函数为延时函数,延时一微秒
	dataBuf = myData;  //将数据存入八位数据线中
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}

//LCD1602初始化voidlcd1602Init(){
	Delay15ms();  //延时 15ms
	writeCmd(0x38);  //写指令 38H(不检测忙信号) 
	Delay5ms();  //延时 5ms
	checkBusySignal();//以后每次写指令,读/写数据操作均需要检测忙信号
	writeCmd(0x38);  //写指令 38H:显示模式设置
	writeCmd(0x08);  //写指令 08H:显示关闭
	writeCmd(0x01);  //写指令 01H:显示清屏
	writeCmd(0x06);  //写指令 06H:显示光标移动设置
	writeCmd(0x0C);  //写指令 0CH:显示开及光标设置
}

//LCD1602显示一行字符voidlcd1602ShowData(char rows,char columns,char *str){
	switch (rows){  //选择行
	case1:
		writeCmd(0x80 + columns-1);  //选择列
		while(*str != '\0'){
			writeData(*str);  //显示字符
			str++;
		}
		break;

	case2:
		writeCmd(0x80 + 0x40 +columns-1);  //选择列
		while(*str != '\0'){
			writeData(*str);  //显示字符
			str++;
		}
		break;

	default:
		break;
	}
}

voidmain(){
	lcd1602Init();
	lcd1602ShowData(1,1,"jiangxiaoya");
	lcd1602ShowData(2,1,"hao  zi  ge");
}

喝水提醒器

思路

利用HC-08超声波模块测距,获取水杯是否被放下或者拿起,当水杯被放下时间超过设定喝水时间就会利用蜂鸣器狗叫发出响声提醒喝水,LCD1602会显示距离上一次喝水过去的时间。

main.c单文件开发

项目工程

main.c文件编写

#include "reg52.h"
#include <intrins.h>
#include <stdio.h>
#include <string.h>

/* LCD1602 */
#define dataBuf P0

sbit EN = P3 ^ 4;
sbit RS = P3 ^ 5;
sbit RW = P3 ^ 6;

/* 蜂鸣器 */
sbit buzzer = P2 ^ 3;

/* 超声波 */
sbit Trig = P1 ^ 1;
sbit Echo = P1 ^ 2;

/* 延时相关函数 */
void Delay10us() //@11.0592MHz
{
    unsigned char i;

    i = 2;
    while (--i)
        ;
}

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

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

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

    i = 27;
    j = 226;
    do
    {
        while (--j)
            ;
    } while (--i);
}

/* 定时器相关函数 */
/* 定时器1初始化 */
void timer1Init()
{
    // 设置定时器1为16位计时模式
    TMOD &= 0x0F;
    TMOD |= 0x10;

    TH1 = 0x00;
    TL1 = 0x00;
    TF1 = 0; // 定时器1计数溢出不产生中断
}

/* 定时器0初始化 */
void timer0Init()
{
    // 设置定时器0为16位计时模式
    TMOD &= 0xF0;
    TMOD |= 0x01;

    /* 定时10ms    */
    TH0 = 0xDC;
    TL0 = 0x00;

    TF0 = 0; // 定时器0计数溢出不产生中断
}

/* 超声波相关函数 */
void ultrasonicStart()
{
    Trig = 0;
    Trig = 1;
    Delay10us();
    Trig = 0;
}

/* 获取超声波测距距离 */
double getDistance()
{
    double time = 0;

    // 定时器1清0
    TH1 = 0x00;
    TL1 = 0x00;

    ultrasonicStart();

    while (Echo == 0)
        ; // 当Echo引脚从低电平跳到高电平时开启定时器1
    TR1 = 1;

    while (Echo == 1)
        ; // 当Echo引脚从高电平跳到低电平时关闭定时器1
    TR1 = 0;

    time = (TH1 * 256 + TL1) * 1.085; // 微秒

    return (time * 0.017);
}

/* LCD1602相关函数 */
// 检测忙信号,读数据线的D7位
void checkBusySignal()
{
    char tmp = 0x80;
    dataBuf = 0x80;
    while (tmp & 0x80)
    { // 当忙标志位(数据位D7)为高电平时,此时模块不能接收命令或者数据
        RS = 0;
        RW = 1;
        EN = 0;
        _nop_();
        EN = 1;
        _nop_();
        _nop_();
        tmp = dataBuf; // 读八位数据线的数据,当不忙时,硬件会自动将该位置0,当检测到D7为低电平时将退出死循环
        EN = 0;
        _nop_();
    }
}

// 写指令
void writeCmd(char cmd)
{
    checkBusySignal();
    RS = 0; // 选择写指令寄存器
    RW = 0;
    EN = 0;
    _nop_();       // 该函数为延时函数,延时一微秒
    dataBuf = cmd; // 将指令存入八位数据线中
    EN = 1;
    _nop_();
    _nop_();
    EN = 0;
    _nop_();
}

// 写数据
void writeData(char myData)
{
    checkBusySignal();
    RS = 1; // 选择写数据寄存器
    RW = 0;
    EN = 0;
    _nop_();          // 该函数为延时函数,延时一微秒
    dataBuf = myData; // 将数据存入八位数据线中
    EN = 1;
    _nop_();
    _nop_();
    EN = 0;
    _nop_();
}

// LCD1602初始化
void lcd1602Init()
{
    Delay15ms();       // 延时 15ms
    writeCmd(0x38);       // 写指令 38H(不检测忙信号)
    Delay5ms();           // 延时 5ms
    checkBusySignal(); // 以后每次写指令,读/写数据操作均需要检测忙信号
    writeCmd(0x38);       // 写指令 38H:显示模式设置
    writeCmd(0x08);       // 写指令 08H:显示关闭
    writeCmd(0x01);       // 写指令 01H:显示清屏
    writeCmd(0x06);       // 写指令 06H:显示光标移动设置
    writeCmd(0x0C);       // 写指令 0CH:显示开及光标设置
}

// LCD1602显示一行字符
void lcd1602ShowData(char rows, char columns, char *str)
{
    switch (rows)
    { // 选择行
    case 1:
        writeCmd(0x80 + columns - 1); // 选择列
        while (*str != '\0')
        {
            writeData(*str); // 显示字符
            str++;
        }
        break;

    case 2:
        writeCmd(0x80 + 0x40 + columns - 1); // 选择列
        while (*str != '\0')
        {
            writeData(*str); // 显示字符
            str++;
        }
        break;

    default:
        break;
    }
}

void main()
{
    unsigned int time = 100 * 3; /* 喝水提醒时间:100*3为3秒 */
    unsigned int cnt = 0;
    char strbuf[16] = {0};

    lcd1602Init();
    timer0Init();
    timer1Init();

    sprintf(strbuf, "Drink Time: %d  S", cnt / 100);

    lcd1602ShowData(1, 1, "   zhao yi hao");
    lcd1602ShowData(2, 1, strbuf);

    while (1)
    {
        /* 放下水杯 */
        if (getDistance() < 10)  
        {

            TR0 = 1;  /* 定时器0开始计时 */
            
            /* 定时器0溢出 */
            if (TF0 == 1)  
            {
                TF0 = 0;  /* 定时器0中断标志位置0,不产生中断 */
                cnt++;
                TH0 = 0xDC;
                TL0 = 0x00;

                /* 显示距离上一次喝水的时间 */
                if (cnt / 100 > 9)  
                {
                    sprintf(strbuf, "Drink Time: %d S", cnt / 100);
                    lcd1602ShowData(2, 1, strbuf);
                }
                else
                {
                    sprintf(strbuf, "Drink Time: %d  S", cnt / 100);
                    lcd1602ShowData(2, 1, strbuf);
                }

                /* 如果超过设定的喝水时间,提醒喝水 */
                if (cnt > time)
                {
                    buzzer = 0;
                }
                else
                {
                    buzzer = 1;
                }
            }
        }

        /* 拿起水杯 */
        else
        {
            cnt = 0;  /* 喝水时间清0 */
        }
    }
}

模块化开发

项目工程

main.c文件编写

void main()
{
    unsigned int time = 100 * 3; /* 喝水提醒时间:100*3为3秒 */
    unsigned int cnt = 0;
    char strbuf[16] = {0};

    lcd1602Init();
    timer0Init();
    timer1Init();

    sprintf(strbuf, "Drink Time: %d  S", cnt / 100);

    lcd1602ShowData(1, 1, "   zhao yi hao");
    lcd1602ShowData(2, 1, strbuf);

    while (1)
    {
        /* 放下水杯 */
        if (getDistance() < 10)  
        {

            TR0 = 1;  /* 定时器0开始计时 */
            
            /* 定时器0溢出 */
            if (TF0 == 1)  
            {
                TF0 = 0;  /* 定时器0中断标志位置0,不产生中断 */
                cnt++;
                TH0 = 0xDC;
                TL0 = 0x00;

                /* 显示距离上一次喝水的时间 */
                if (cnt / 100 > 9)  
                {
                    sprintf(strbuf, "Drink Time: %d S", cnt / 100);
                    lcd1602ShowData(2, 1, strbuf);
                }
                else
                {
                    sprintf(strbuf, "Drink Time: %d  S", cnt / 100);
                    lcd1602ShowData(2, 1, strbuf);
                }

                /* 如果超过设定的喝水时间,提醒喝水 */
                if (cnt > time)
                {
                    buzzer = 0;
                }
                else
                {
                    buzzer = 1;
                }
            }
        }

        /* 拿起水杯 */
        else
        {
            cnt = 0;  /* 喝水时间清0 */
        }
    }
}

delay.c文件编写

void Delay10us() //@11.0592MHz
{
    unsigned char i;

    i = 2;
    while (--i)
        ;
}

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

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

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

    i = 27;
    j = 226;
    do
    {
        while (--j)
            ;
    } while (--i);
}

delay.h文件编写

void Delay10us();

void Delay5ms();
 
void Delay15ms();

timer.c文件编写

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

/* 定时器1初始化 */
void timer1Init()
{
    // 设置定时器1为16位计时模式
    TMOD &= 0x0F;
    TMOD |= 0x10;

    TH1 = 0x00;
    TL1 = 0x00;
    TF1 = 0; // 定时器1计数溢出不产生中断
}

/* 定时器0初始化 */
void timer0Init()
{
    // 设置定时器0为16位计时模式
    TMOD &= 0xF0;
    TMOD |= 0x01;

    /* 定时10ms    */
    TH0 = 0xDC;
    TL0 = 0x00;

    TF0 = 0; // 定时器0计数溢出不产生中断
}

timer.h文件编写

void timer0Init();

void timer1Init();

lcd1602.c文件编写

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

#define dataBuf P0
 
sbit EN = P3^4; 
sbit RS = P3^5;
sbit RW = P3^6;

// 检测忙信号,读数据线的D7位
void checkBusySignal()
{
    char tmp = 0x80;
    dataBuf = 0x80;
    while (tmp & 0x80)
    { // 当忙标志位(数据位D7)为高电平时,此时模块不能接收命令或者数据
        RS = 0;
        RW = 1;
        EN = 0;
        _nop_();
        EN = 1;
        _nop_();
        _nop_();
        tmp = dataBuf; // 读八位数据线的数据,当不忙时,硬件会自动将该位置0,当检测到D7为低电平时将退出死循环
        EN = 0;
        _nop_();
    }
}

// 写指令
void writeCmd(char cmd)
{
    checkBusySignal();
    RS = 0; // 选择写指令寄存器
    RW = 0;
    EN = 0;
    _nop_();       // 该函数为延时函数,延时一微秒
    dataBuf = cmd; // 将指令存入八位数据线中
    EN = 1;
    _nop_();
    _nop_();
    EN = 0;
    _nop_();
}

// 写数据
void writeData(char myData)
{
    checkBusySignal();
    RS = 1; // 选择写数据寄存器
    RW = 0;
    EN = 0;
    _nop_();          // 该函数为延时函数,延时一微秒
    dataBuf = myData; // 将数据存入八位数据线中
    EN = 1;
    _nop_();
    _nop_();
    EN = 0;
    _nop_();
}

// LCD1602初始化
void lcd1602Init()
{
    Delay15ms();       // 延时 15ms
    writeCmd(0x38);       // 写指令 38H(不检测忙信号)
    Delay5ms();           // 延时 5ms
    checkBusySignal(); // 以后每次写指令,读/写数据操作均需要检测忙信号
    writeCmd(0x38);       // 写指令 38H:显示模式设置
    writeCmd(0x08);       // 写指令 08H:显示关闭
    writeCmd(0x01);       // 写指令 01H:显示清屏
    writeCmd(0x06);       // 写指令 06H:显示光标移动设置
    writeCmd(0x0C);       // 写指令 0CH:显示开及光标设置
}

// LCD1602显示一行字符
void lcd1602ShowData(char rows, char columns, char *str)
{
    switch (rows)
    { // 选择行
    case 1:
        writeCmd(0x80 + columns - 1); // 选择列
        while (*str != '\0')
        {
            writeData(*str); // 显示字符
            str++;
        }
        break;

    case 2:
        writeCmd(0x80 + 0x40 + columns - 1); // 选择列
        while (*str != '\0')
        {
            writeData(*str); // 显示字符
            str++;
        }
        break;

    default:
        break;
    }
}

lcd1602.h文件编写

void checkBusySignal();

void writeCmd(char cmd);

void writeData(char myData);

void lcd1602Init();

void lcd1602ShowData(char rows,char columns,char *str);

ultrasonic.c文件编写

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

 
sbit Trig = P1^1;
sbit Echo = P1^2;

void ultrasonicStart()
{
    Trig = 0;
    Trig = 1;
    Delay10us();
    Trig = 0; 
}
 
/* 获取超声波测距距离 */
double getDistance()
{
    double time = 0;

    // 定时器1清0
    TH1 = 0x00;
    TL1 = 0x00;

    ultrasonicStart();

    while (Echo == 0)
        ; // 当Echo引脚从低电平跳到高电平时开启定时器1
    TR1 = 1;

    while (Echo == 1)
        ; // 当Echo引脚从高电平跳到低电平时关闭定时器1
    TR1 = 0;

    time = (TH1 * 256 + TL1) * 1.085; // 微秒

    return (time * 0.017);
}

ultrasonic.h文件编写

void timer1Init();

void ultrasonicStart();

double getDistance();

Guess you like

Origin blog.csdn.net/weixin_54076783/article/details/129696447