51单片机之喝水提醒器

定时器

定时器介绍

晶振

晶体震荡器,又称数字电路的“心脏”,是各种电子产品里面必不可少的频率元器件。数字电路的所有工作都离不开时钟,晶振的好坏、晶振电路设计的好坏,会影响到整个系统的稳定性。

时钟周期

时钟周期也称为振荡周期,定义为时钟频率的倒数。时钟周期是计算机中最基本的、最小的时间单 位。在一个时钟周期内,CPU仅完成一个最基本的动作。时钟周期是一个时间的量。更小的时钟周 期就意味着更高的工作频率。

机械周期

机器周期也称为CPU周期。在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶 段(如取指、译码、执行等),每一阶段完成一个基本操作。完成一个基本操作所需要的时间称为 机器周期。一般情况下,一个机器周期由若干个时钟周期组成。

每经过一个机械周期经过多长时间

以晶振频率11.0592MHZ为例,时钟周期为晶振的倒数,即1/1105920000秒。

  • 如果是12T模式,机械周期 = 12 X 时钟周期,即12/11059200000秒 = 1.085微秒

  • 如果是6T模式,机械周期 = 6 X 时钟周期,即6/11059200000秒 = 0.5425微秒

定时器和计数器的区别

51单片机中的定时器和计数器使用同一个硬件电路,通过修改寄存器的配置来将该硬件电路变成定时器或者计数器。

  • 当配置成定时器时,每经过一个机械周期,计数存储器的值加1,C51有两个定时器T0和T1。

  • 当配置成计数器时,每来一个负跳变信号(高电平跳到低电平),计数存储器的值加1。

定时器相关寄存器

定时器计时寄存器(TH和TL寄存器)

当定时器的TH寄存器和TL寄存器都用起来,即一共有16位,那么该定时器最多数2^16 = 65536下,即大概65536*1.085微秒 = 71毫秒,也就是说定时器T0或者定时器T1最多定时71毫秒。

例如现在需要使用定时器0定时10毫秒,怎么配置寄存器TH0和TL0,只需要配置TH0 = 0xDC , TL0 = 0x00。

定时器控制寄存器(TCON寄存器)

以定时器0和外部中断0为例

  • 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();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2944.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据库备份

数据库备份&#xff0c;恢复实操 策略一&#xff1a;&#xff08;文件系统备份工具 cp&#xff09;&#xff08;适合小型数据库&#xff0c;是最可靠的&#xff09; 1、停止MySQL服务器。 2、直接复制整个数据库目录。注意&#xff1a;使用这种方法最好还原到相同版本服务器中&…

银河麒麟v10sp2安装nginx

nginx官网下载&#xff1a;http://nginx.org/download/ 银河麒麟系统请先检查yum源是否配置&#xff0c;若没有配置请参考&#xff1a;https://qdhhkj.blog.csdn.net/article/details/129680789 一、安装 1、yum安装依赖 yum install gcc gcc-c make unzip pcre pcre-devel …

用嘴写代码?继ChatGPT和NewBing之后,微软又开始整活了,Github Copilot X!

用嘴写代码&#xff1f;继ChatGPT和NewBing之后&#xff0c;微软又开始整活了&#xff0c;Github Copilot X&#xff01; AI盛行的时代来临了&#xff0c;在这段时间&#xff0c;除了爆火的GPT3.5后&#xff0c;OpenAI发布了GPT4版本&#xff0c;同时微软也在Bing上开始加入了A…

新版logcat最全使用指南

前言&#xff1a; 俗话说&#xff0c;工欲善其事&#xff0c;必先利其器。logcat是我们通过日志排查bug的重要武器之一。从某个版本开始&#xff0c;logcat改版了&#xff0c;改版之后&#xff0c;也许某些人觉得不太习惯&#xff0c;但是如果稍微学习下之后&#xff0c;就发现…

从 X 入门Pytorch——BN、LN、IN、GN 四种归一化层的代码使用和原理

Pytorch中四种归一化层的原理和代码使用前言1 Batch Normalization&#xff08;2015年提出&#xff09;Pytorch官网解释原理Pytorch代码示例2 Layer Normalization&#xff08;2016年提出&#xff09;Pytorch官网解释原理Pytorch代码示例3 Instance Normalization&#xff08;2…

AJAX,Axios,JSON简单了解

一. AJAX简介概念: AJAX(Asynchronous JavaScript And XML): 异步的JavaScript 和XMLAJAX作用:1.与服务器进行数据交换: 通过AJAX可以给服务器发送请求&#xff0c;并获取服务器响应的数据使用了AJAX和服务器进行通信&#xff0c;就可以使用 HTMLAJAX来替换JSP页面了2.异步交互…

ChatGPT文心一言逻辑大比拼(一)

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

静态通讯录,适合初学者的手把手一条龙讲解

数据结构的顺序表和链表是一个比较困难的点&#xff0c;初见会让我们觉得有点困难&#xff0c;正巧C语言中有一个类似于顺序表和链表的小程序——通讯录。我们今天就来讲一讲通讯录的实现&#xff0c;也有利于之后顺序表和链表的学习。 目录 0.通讯录的初始化 1.菜单的创建…

python例程:五子棋(控制台版)程序

目录《五子棋&#xff08;控制台版&#xff09;》程序使用说明程序示例代码可执行程序及源码下载路径《五子棋&#xff08;控制台版&#xff09;》程序使用说明 在PyCharm中运行《五子棋&#xff08;控制台版&#xff09;》即可进入如图1所示的系统主界面。 图1 游戏主界面 具…

一个比较全面的C#公共帮助类

上次跟大家推荐过2个C#开发工具箱&#xff1a;《推荐一个不到2MB的C#开发工具箱&#xff0c;集成了上千个常用操作类》、《推荐一个.Net常用代码集合&#xff0c;助你高效完成业务》。 今天再给大家推荐一个&#xff0c;这几个部分代码功能有重合的部分&#xff0c;大家可以根…

静态版通讯录——“C”

各位CSDN的uu你们好呀&#xff0c;之前小雅兰学过了一些结构体、枚举、联合的知识&#xff0c;现在&#xff0c;小雅兰把这些知识实践一下&#xff0c;那么&#xff0c;就让我们进入通讯录的世界吧 实现一个通讯录&#xff1a; 可以存放100个人的信息每个人的信息&#xff1a;名…

FPGA打砖块游戏设计(有上板照片)VHDL

这是一款经典打砖块游戏,我们的努力让它更精致更好玩,我们将它取名为打砖块游戏(Flyball),以下是该系统的一些基本功能:  画面简约而经典,色彩绚丽而活泼,动画流畅  玩家顺序挑战3个不同难度的级别,趣味十足  计分功能,卡通字母数字  4条生命值,由生命条显示…

智能扑克牌识别软件(Python+YOLOv5深度学习模型+清新界面)

摘要&#xff1a;智能扑克牌识别软件利用视觉方法检测和识别日常扑克牌具体花色与数字&#xff0c;快速识别牌型并标注结果&#xff0c;帮助计算机完成扑克牌对战的前期识别步骤。本文详细介绍基于深度学习的智能扑克牌识别软件&#xff0c;在介绍算法原理的同时&#xff0c;给…

[数据结构]排序算法

目录 常用排序算法的实现&#xff1a;&#xff1a; 1.排序的概念及其运用 2.插入排序 3.希尔排序 4.选择排序 5.冒泡排序 6.堆排序 7.快速排序 8.归并排序 9.排序算法复杂度及稳定性分析 10.排序选择题练习 常用排序算法的实现&#xff1a;&#xff1a; 1.排序的概念及其运用…

【C语言蓝桥杯每日一题】——排序

【C语言蓝桥杯每日一题】—— 排序&#x1f60e;前言&#x1f64c;排序&#x1f64c;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &#x1f60a;作者简介&am…

【千题案例】TypeScript获取两点之间的距离 | 中点 | 补点 | 向量 | 角度

我们在编写一些瞄准、绘制、擦除等功能函数时&#xff0c;经常会遇到计算两点之间的一些参数&#xff0c;那本篇文章就来讲一下两点之间的一系列参数计算。 目录 1️⃣ 两点之间的距离 ①实现原理 ②代码实现及结果 2️⃣两点之间的中点 ①实现原理 ②代码实现及结果 3…

当下的网络安全行业前景到底怎么样?还能否入行?

前言网络安全现在是朝阳行业&#xff0c;缺口是很大。不过网络安全行业就是需要技术很多的人达不到企业要求才导致人才缺口大常听到很多人不知道学习网络安全能做什么&#xff0c;发展前景好吗&#xff1f;今天我就在这里给大家介绍一下。网络安全作为目前比较火的朝阳行业&…

给程序加个进度条吧,1行Python代码,快速添加~

大家好&#xff0c;这里是程序员晚枫。 你在写代码的过程中&#xff0c;有没有遇到过以下问题&#xff1f; 已经写好的程序&#xff0c;想看看程序执行的进度&#xff1f; 在写代码批量处理文件的时候&#xff0c;如何显示现在处理到第几个文件了&#xff1f; &#x1f446…

真要被00后职场整顿了?老员工纷纷表示真的干不过.......

最近聊到软件测试的行业内卷&#xff0c;越来越多的转行和大学生进入测试行业。想要获得更好的待遇和机会&#xff0c;不断提升自己的技能栈成了测试老人迫在眉睫的问题。 不论是面试哪个级别的测试工程师&#xff0c;面试官都会问一句“会编程吗&#xff1f;有没有自动化测试…

dfs和bfs能解决的问题

一.理解暴力穷举之dfs和bfs暴力穷举暴力穷举是在解决问题中最常用的手段&#xff0c;而dfs和bfs算法则是这个手段的两个非常重要的工具。其实&#xff0c;最简单的穷举法是直接遍历&#xff0c;如数列求和&#xff0c;遍历一个数组即可求得所问答案&#xff0c;这与我在前两篇博…