当前位置: 首页 > news >正文

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定时时间为10msTH0 = 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引脚从低电平跳到高电平时开启定时器1TR1 = 1;while(Echo == 1);  //当Echo引脚从高电平跳到低电平时关闭定时器1TR1 = 0;time = (TH1*256 + TL1) * 1.085;  //微秒/*定时器16位全用时:高八位TH寄存器每加次1,计数存储器的值就加256;低八位TL寄存器每次加1,计数存储器的值就加1;计数存储器的值每次加1时,就经过了一个机械周期(经过时间1.085微秒)*/distance = time * 0.017;  //CMif(distance < 10){buzzer = 0;}else{buzzer = 1;}//定时器1清0TH1 = 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 P0sbit 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();  //延时 15mswriteCmd(0x38);  //写指令 38H(不检测忙信号) Delay5ms();  //延时 5mscheckBusySignal();//以后每次写指令,读/写数据操作均需要检测忙信号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 P0sbit 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();  //延时 15mswriteCmd(0x38);  //写指令 38H(不检测忙信号) Delay5ms();  //延时 5mscheckBusySignal();//以后每次写指令,读/写数据操作均需要检测忙信号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 P0sbit 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清0TH1 = 0x00;TL1 = 0x00;ultrasonicStart();while (Echo == 0); // 当Echo引脚从低电平跳到高电平时开启定时器1TR1 = 1;while (Echo == 1); // 当Echo引脚从高电平跳到低电平时关闭定时器1TR1 = 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();       // 延时 15mswriteCmd(0x38);       // 写指令 38H(不检测忙信号)Delay5ms();           // 延时 5mscheckBusySignal(); // 以后每次写指令,读/写数据操作均需要检测忙信号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 P0sbit 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();       // 延时 15mswriteCmd(0x38);       // 写指令 38H(不检测忙信号)Delay5ms();           // 延时 5mscheckBusySignal(); // 以后每次写指令,读/写数据操作均需要检测忙信号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清0TH1 = 0x00;TL1 = 0x00;ultrasonicStart();while (Echo == 0); // 当Echo引脚从低电平跳到高电平时开启定时器1TR1 = 1;while (Echo == 1); // 当Echo引脚从高电平跳到低电平时关闭定时器1TR1 = 0;time = (TH1 * 256 + TL1) * 1.085; // 微秒return (time * 0.017);
}

ultrasonic.h文件编写

void timer1Init();void ultrasonicStart();double getDistance();

相关文章:

51单片机之喝水提醒器

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

扒一扒抖音是如何做线程优化的

背景 最近在对一些大厂App进行研究学习&#xff0c;在对某音App进行研究时&#xff0c;发现其在线程方面做了一些优化工作&#xff0c;并且其解决的问题也是之前我在做线上卡顿优化时遇到的&#xff0c;因此对其具体实现方案做了深入分析。本文是对其相关源码的研究加上个人理…...

149.网络安全渗透测试—[Cobalt Strike系列]—[重定器/代理服务器/流量走向分析]

我认为&#xff0c;无论是学习安全还是从事安全的人多多少少都会有些许的情怀和使命感&#xff01;&#xff01;&#xff01; 文章目录一、Cobalt Strike 重定器1、Cobalt Strike 重定器简介2、重定器用到的端口转发工具二、cobalt strike重定器实验1、实验背景2、实验过程3、流…...

Qt调用Chrome浏览器

一、前言 最近有个小项目需要跳转网页&#xff0c;之前有了解过&#xff0c;但是没有在项目中使用过Qt网页嵌入&#xff1b; 结合自己之前的博客&#xff0c;有如下两种技术可以实现我的需求&#xff1a; 1、Qt–网页嵌入 2、Qt使用QAxWidget调用Windows组件 但是在实际开…...

JVM虚拟机垃圾回收机制

JVM虚拟机垃圾回收机制垃圾回收机制判断是否存活算法引用计数法可达性分析法最终判定垃圾回收算法分代收集机制空间分配担保垃圾回收机制 判断是否存活算法 java语言和我们之前学的c/c不同&#xff0c;c/c可以手动进行内存释放&#xff0c;那样随时随地就可以释放不必要的内存…...

菜鸟刷题Day3

⭐作者&#xff1a;别动我的饭 ⭐专栏&#xff1a;菜鸟刷题 ⭐标语&#xff1a;悟已往之不谏&#xff0c;知来者之可追 一.字符串压缩&#xff1a;面试题 01.06. 字符串压缩 - 力扣&#xff08;LeetCode&#xff09; 描述 字符串压缩。利用字符重复出现的次数&#xff0c;编…...

南京邮电大学数据库第三次课后作业

1.单选(2分) 下列关于模式的术语中,(C)不是指数据库三级模式结构中的外模式 &#xff08;A&#xff09;子模式 &#xff08;B&#xff09;用户模式 &#xff08;C&#xff09;存储模式 &#xff08;D&#xff09;用户视图 2单选题(2分) 数据库的三级模式结构中,描述数据全局逻辑…...

【vue2】使用vue常见的业务流程与实现思路

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;vue的业务处理思路。前台数据渲染与后台的增删改查操作 【前言】当大家会点开这一篇文章…...

Linux操作系统ARM体系结构处理器机制原理与实现

ARM 的概念ARM(Advanced RISC Machine)&#xff0c;既可以认为是一个公司的名字&#xff0c;也可以认为是对一类微处理器的通称&#xff0c;还可以认为是一种技术的名字。ARM 公司并不生产芯片也不销售芯片&#xff0c;它只出售芯片技术授权。其合作公司针对不同需求搭配各类硬…...

Mongodb 常用基本语法与操作

常用操作 1、 Help查看命令提示 db.help(); 2、 切换/创建数据库 use test 如果数据库不存在&#xff0c;则创建数据库&#xff0c;否则切换到指定数据库 3、 查询所有数据库 show dbs; 4、 删除当前使用数据库 db.dropDatabase(); 5、 查看当前使用的数据库 db.getName(); 6、…...

MySQL注入秘籍【绕过篇】

MySQL注入秘籍【绕过篇】1.通用方法2.绕过空格3.绕过引号4.绕过逗号,5.绕过等号6.绕过and/or7.绕过注释符8.绕过函数检测1.通用方法 编码 编码无非就是hex、url等等编码&#xff0c;让传到数据库的数据能够解析的即可&#xff0c;比如URL编码一般在传给业务的时候就会自动解码…...

TCP三次握手/四次挥手

TCP三次握手 任何基于TCP的应用&#xff0c;在发送数据之前&#xff0c;都需要由TCP进行“三次握手”建立连接示意图 第一次握手&#xff1a;客户端PC发送一个SYN位置1&#xff08;SYN1代表请求服务端建立连接&#xff09;的TCP报文发送给要建立TCP连接的Server&#xff0c;此…...

Python程序员看见一个好看的手机壁纸网站,开撸!

人生苦短&#xff0c;我用python 最近好像没什么大事&#xff0c; .那就采集一下小——姐——姐————看下吧~ python 安装包资料:点击此处跳转文末名片获取 最近有同学的爬虫代码出了bug&#xff0c;给问我怎么改 于是就发现了这个好看的手机壁纸网站。 这个图片应该是违规…...

浏览器工作原理

一、JavaScript 的历史 JavaScript&#xff08;简称JS&#xff09;Web前端开发的脚本语言。 它诞生1995年&#xff0c;由网景公司的 Brendan Eich 开发。最初&#xff0c;JavaScript 被设计用于在网页上嵌入动态内容和交互式功能。 1996年&#xff0c;JavaScript 1.1 成为国…...

对在使用容器HashSet存放自定义对象时重写其类的hashcode和equals方法的几点认识

判断是否是相同对象时&#xff0c;hashcode和equals方法的调用顺序 先调用hashcode()方法&#xff0c;再调用equals()方法如果hashcode()方法得到的哈希值不同&#xff0c;那么两个对象一定不相同&#xff0c;不作后续判断如果hashcode()方法得到的哈希值相同&#xff0c;那么…...

Java集群:单体架构升级到集群架构(二)实现session共享

默认情况下&#xff0c;session是保存在TOMCAT服务器内存中的&#xff0c;如果我们有两个TOMCAT&#xff0c;它们的session是没有共享的。我们这回要做的就是把session保存在redis中&#xff0c;这样两个TOMCAT就可以共享session了。其实这货的详细原理还是很复杂的&#xff0c…...

MySQL索引及索引失效的分析(MySQL8.0.19)

目录索引数据结构主键索引非主键索引索引在什么时候是有效的&#xff1f;字符串比较大小btween and索引数据结构 主键索引 我们先来看看索引的数据结构&#xff0c;以及我们是如何利用索引来搜索数据的。MySQL的数据存储结构是B树&#xff0c;在叶子节点存储了数据行&#xff…...

第一个 Django 应用

1. 创建项目 1.1 新建项目 首先新建一个项目&#xff0c;名为 mysite&#xff0c;命令如下&#xff1a; django-admin startproject mysite # 或用 django-admin.py运行成功&#xff0c;生成一些目录&#xff1a; mysite/manage.py # 管理 Django 项目的命令行工具mysit…...

001-ksum 求符合条件的 k 个数 1. Two Sum/15. 3Sum/18. 4Sum/

推荐阅读 000-从零开始的数据结构与算法 001-01-ksum 求符合条件的 k 个数 1. Two Sum/15. 3Sum/18. 4Sum/ 002-两数相加 add two numbers 003-无重复字符的最长子串 Longest Substring Without Repeating Characters 004-寻找两个正序数组的中位数 005-最长回文子串 Lon…...

Nginx学习笔记(三)Linux环境下Nginx的安装和部署

目录一、官网下载二、配置基本信息1.上传 Linux2.解压3.安装编译环境4.配置基本信息4.1 配置失败原因(1)&#xff1a;没有安装C编译环境4.2 配置失败原因(2)&#xff1a;没有安装 PCRE 依赖4.3 配置失败原因(3)&#xff1a;没有安装 zlib 依赖5.查看文件列表三、编译安装四、配…...

【十二天学java】day05--数组和循环高级

**# 1.数组 概念&#xff1a; 指的是一种容器&#xff0c;可以同来存储同种数据类型的多个值。 但是数组容器在存储数据的时候&#xff0c;需要结合隐式转换考虑。 比如&#xff1a; 定义了一个int类型的数组。那么boolean。double类型的数据是不能存到这个数组中的&#…...

用队列实现栈和用栈实现队列(C 语言)

目录 一、用队列实现栈 二、 用栈实现队列 一、用队列实现栈 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int…...

albedo开源框架配置多数据源

前言&#xff1a;公司框架项目一直都没认真阅读过&#xff0c;最近项目需要连接oracle数据&#xff0c;所以尝试使用框架连接多数据库。添加多数据源插件&#xff1a;我们在项目的插件模块内添加多数据源插件&#xff1a;albedo-dynamic-datasource<?xml version"1.0&…...

22张图带你了解IP地址有什么作用

了解IP地址 1、IP地址的格式 在IP协议的报文中&#xff0c;可以得知IP地址是有32个比特&#xff0c;IP地址在计算机中是以二进制的方式处理的&#xff0c;如果全部以二进制的形式来表示&#xff0c;使用跟表达都非常的困难&#xff0c;所以为了人类方便记忆&#xff0c;采用了…...

121.Android 简单的人工智能聊天项目,chatAi,AI聊天项目,GPTAi

//首页xml布局代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"mat…...

C++ this指针详解

this 是 C 中的一个关键字&#xff0c;也是一个 const 指针&#xff0c;它指向当前对象&#xff0c;通过它可以访问当前对象的所有成员。所谓当前对象&#xff0c;是指正在使用的对象。例如对于stu.show();&#xff0c;stu 就是当前对象&#xff0c;this 就指向 stu。下面是使用…...

CSS 实现六边形柱状图

前言 &#x1f44f;CSS 实现六边形柱状图 速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现步骤 定义全局css变量&#xff0c;柱状宽度为–w&#xff0c;最大高度为–h&#xff0c;柱形整体为渐变色&#xff0c;定义上部分颜色为…...

什么是推挽输出,开漏输出?

这篇文章是看B站“工科男孙老师”这个视频的笔记推挽 开漏 高阻 这都是谁想出来的词&#xff1f;&#xff1f; 我觉得讲的很好&#xff0c;做一下笔记 1.什么是IO输出三态 一共有&#xff1a;高电平, 低电平&#xff0c;浮空/高阻态 三种IO态 2.推挽输出 推挽输出能够表示高、…...

【图像分割】Unet系列深度讲解(FCN、UNET、UNET++)

【图像分割】Unet 深度讲解 文章目录【图像分割】Unet 深度讲解1. 介绍1.1 背景介绍&#xff1a;1.2 医学图像特点1.3 图像分割是什么2. Unet发展历程&#xff08;FCN、Unet、Unet&#xff09;2.1 全卷积网络-FCN2.1.1 FCN介绍&#xff1a;2.1.2 FCN框架2.1.3 反卷积层2.1.4 输…...

list底层的简单实现(万字长文详解!)

list底层的简单实现 文章目录list底层的简单实现list_node的实现&#xff01;list_node的构造函数list的迭代器&#xff01;——重点&#xff01;list迭代器的成员变量迭代器的构造函数* 重载前置 重载后置 重载前置-- 重载后置-- 重载! 重载 重载-- 重载list的const迭代器——…...