B50 - 基于51单片机的儿童成长管理系统

任务

针对传统儿童身高体重测量仪器测量费时、测量误差大、无法进行历史记录以方便查看儿童近期身高体重来直观的看到儿童的变化等缺点做了进一步的改进,主要介绍一个基于单片机的儿童成长管理系统。它由安卓手机APP和嵌入式硬件两部分组成,嵌入式硬件部分由电源电路、单片机最小系统电路、液晶屏显示电路、超声波模块测距电路、体重测量模块电路、蓝牙通讯模块电路构成,具有测距、测重、保存测量记录等功能,安卓APP部分由QT开发工具进行编写,具有儿童历史测量数据图表化显示等功能。本次设计融合了日益成熟的超声波、传感器等技术,主要实现了儿童身高、体重数据的采集、数字化显示以及保存,并通过蓝牙和安卓手机连接通过收便能方便的看到历史测量数据,达到监测儿童成长情况的目的。

本系统有身高检测模块、按键采集模块、体重检测模块进行信息的采集,身高检测模块采用了超声波传感器,能够避免光强度对身高检测的影响,体重检测模块采用了常用的电阻桥式压力传感器,采用HX711高精度数模转换芯片保证检测的精准度。输出采用LCD1602进行信息的可视化显示,采用了HC-05蓝牙模块来满足和手机的双向通信,通过蓝牙模块单片机可以接收手机发出的查询指令,也可以根据指令解析后根据预设程序向手机发出用户的历史测量数据,完成双向通信。本系统的整体结构如图2-1所示。
在这里插入图片描述
在这里插入图片描述

实物

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

原理图

在这里插入图片描述

硬件设计

体重检测模块

体重数据采集部分由称重传感器、信号放大和A/D转换部分组成,信号放大和A/D转换部分主要由专用型高精度24位AD转换芯片HX711实现。HX711是一款专为高精度称重传感器而设计的24位A/D 转换器芯片。与同类型其它芯片相比,该芯片集成了包括稳压电源、片内时钟振荡器等其它同类型芯片所需要的外围电路,具有集成度高、响应速度快、抗干扰性强等优点。
在这里插入图片描述

身高检测模块

本设计的身高检测模块是由超声波测距实现的。超声波发射电路由超声波发射探头,单片机STC89C52RC的P1.6引脚控制信号给超声波发生器,由超声波探头发射的超声波射向障碍物。超声波测距的原理是,通过不断检测超声波后遇到障碍物所反射的回波,从而测出发射超声波和接收到回波的时间差T,然后求出距离S=CXT/2,其中,C为超声波波速,常温下取为344m/s。声速确定后,只要测得超声波往返的时间,即可求得距离。采用STC89C52RC作为中央处理器,选用专用配对的超声波模块,进行超声波信号与电信号的相互转换,利用超声波传感器的选频特性,对接收到的超声波信号进行幅值判断,从而达到不同距离的选择与报警的目的。
在这里插入图片描述

蓝牙通信模块

本设计采用的是HC-05蓝牙串口通信模块进行和手机的无线连接。该模块的无线工作频段为2.4GHz ISM通过GFSK进行数据的调制解调。该模块的最大发射功率为4dBm,接收灵敏度-85dBm,板载了PCB天线,可以实现10米距离的无线通信需求。该模块可以通过AT指令来进行串口波特率、设备名称等的功能配置且能通过板载的LED灯来判断当前模块的工作状态。本系统使用HC-05蓝牙模块连接到了STC89C52RC单片机的串口对应的引脚P30和P31完成串口通信,当手机连接到该蓝牙模块后,完成本系统和手机的通信功能,手机端APP发送相应的控制指令经过手机蓝牙发送出去,HC-05模块能够接收到与之连接的手机蓝牙发出的信息并将接收到的信息通过串口传输给单片机供单片机解析接收的指令信息,解析完成后根据本系统设计的状态将相应反馈数据通过蓝牙返回给安卓手机,经过手机app处理和解析后显示出来供用户查阅。
在这里插入图片描述

断电存储模块

AT24C02是一个2K位串行CMOS E2PROM, 内部含有256个8位字节,CATALYST公司的先进CMOS技术实质上减少了器件的功耗。AT24C02有一个16字节页写缓冲器。该器件通过IIC总线接口进行操作,有一个专门的写保护功能。AT24C02支持I2C,总线数据传送协议I2C,总线协议规定任何将数据传送到总线的器件作为发送器。任何从总线接收数据的器件为接收器。数据传送是由产生串行时钟和所有起始停止信号的主器件控制的。主器件和从器件都可以作为发送器或接收器,但由主器件控制传送数据(发送或接收)的模式,由于A0、A1和A2可以组成000~111八种情况,即通过器件地址输入端A0、A1和A2可以实现将最多8个AT24C02器件连接到总线上,通过进行不同的配置进行选择器件。
在这里插入图片描述

源程序

主程序流程

本设计主程序开始首先是上电初始化阶段,对LCD1602外设进行配置为正常的显示模式,且显示对应的上电提示语,然后对串口外设进行了初始化操作,STC89C52RC仅有一个串口,本次采用了STC89C52RC相比C51型号新增加的定时器2来作为串口的波特率发生器,配置生成9600bps的串口波特率,8位数据位,无奇偶校验位模式。之后初始化AT24C02外设,该断电存储模块上电后会被检测是否在设定位置存储了设定的值,如果没有,则存储设定的值,且清空其他位置的数据,防止发生数据紊乱的情况。LCD的优势在于能够及时给出相应的显示提示,在此处会显示判定是否为新旧单片机的提示。之后HX711重量检测和HC-SR04身高检测外设初始化。各种外设初始化完成后进入循环,在循环中持续采集信息和显示。在正常的检测界面下,本设计会判定串口是否接收到了指令,该指令是通过手机蓝牙发送到该设计的数据,设计上的蓝牙模块接收到数据后通过串口传输给单片机,单片机接收到后进行对应的解析,判断手机APP上用户发送了那个模式的查看指令,要查看那个用户ID的数据,是查看体重还是身高数据,单片机做相应的读取后将数据通过蓝牙模块发出。在正常模式下,检测身高、体重数据并显示到LCD1602显示屏上。
在这里插入图片描述

数据存储流程

本设计存储程序是通过按键按下状态来实现的。当用户测量身高、体重后按下按键1,则单片机进入存储界面,在此界面下LCD显示用户ID和身高、体重数据,用户在此界面下可通过按键2进行更改用户ID,更改完成后按下按键3可执行数据存储。本设计的数据存储时首先读取AT24C02里对应的用户ID在其内存中存储的数据,每个用户ID分配了40个字节,身高体重各占20字节,每两个字节组成一个数据,因为内存是8位的,无法存储大于255的数值,故而采用两个字节来存储一个身高或者体重数据。当用户读取都对应ID的数据后判断是否存在为0的数据,如果存在,则代表历史测量记录没有达到满历史记录,故而将0数据对应的位置存储当前测量的身高、体重数据。如果达到了满历史记录,及内部没有为0的数据,则将该用户ID最先测到的数据丢弃一个,将最近的数据都向后移动一个位置,将最末尾位置腾出存放最新的测量数据。数据存储后要驱动AT24C02的写程序将该数据写入对应的用户ID的内存空间中。
在这里插入图片描述

蓝牙通信流程

蓝牙模块通信则是通过串口进行,当串口配置好后,就可以通过串口和蓝牙模块进行通信。本次设计了通信指令为自定义的,以 作为帧开头,以 @ 作为帧结尾,当收到 作为帧开头,以@作为帧结尾,当收到 作为帧开头,以@作为帧结尾,当收到IDX,1@时,其中X代表的用户ID,范围为1-5,单片机会读取对应ID在AT24C02存储的身高数据,并通过串口发送出去,蓝牙将该数据转成无线信号发送给手机。当收到$IDX,2@时,其中X代表的用户ID,范围为1-5,单片机会读取对应ID在AT24C02存储的体重数据,并通过串口发送出去,蓝牙将该数据转成无线信号发送给手机。
在这里插入图片描述

源代码

主程序(包含蓝牙通信的数据处理)

#include "config.h"
#include "lcd1602.h"
#include "24c02.h"
#include "Ultrasonic.h"
#include "HX711.h"
#include "Key_Duli.h"
#include "UART.H"  

void Send(uchar num);
extern bit is_uartRec;
extern uchar xdata Uart_Buff[20];

uchar xdata menu = 0;
uchar xdata userID = 1;
uint xdata userHeight[10];
uint xdata userWeight[10];

void main()
{
    
    
	uchar  i=0,j=0;
	int HC_SR04Value = 0;
	int HX711_Weight = 0;
	uchar KeyNum = 0xff;
	Init_1602();
  delay_ms(100);
	
	UART_Init();
	
	memset(userHeight,0,10);
	memset(userWeight,0,10);
	
	Inint_24c02();
	if(read24c02AddDat(ID_ADDR) == ID_VALUE)
	{
    
    
		write_string(1,0,"Welcome  To  Use");
		write_string(2,0,"OLD  EPROM      ");
//		for(i=1;i<245;i++) // 新内存,清空数据
//		{
    
    
//			write24c02AddDat(i,0);
//			delay_ms(10);
//		}
	}
	else
	{
    
    
		write24c02AddDat(ID_ADDR,ID_VALUE);
		write_string(1,0,"Welcome  To  Use");
		write_string(2,0,"NEW  EPROM      ");
		for(i=1;i<245;i++) // 新内存,清空数据
		{
    
    
			write24c02AddDat(i,0);
			delay_ms(10);
		}
	}
	delay_ms(1000);
	
	Init_Ultrasonic();
	write_string(1,0,"Height:    000CM");
	write_string(2,0,"Weight:    5000g");
	
	HX711_Init();
	
	printf("Welcome  To  Use,Made By Lei ZhenKun\r\n");
	while(1)
	{
    
    
		if(menu == 0)
		{
    
    
			if(is_uartRec)
			{
    
    
				is_uartRec = 0;
				if(Uart_Buff[1]=='I')
				{
    
    
					write_string(1,0,"Extracting  data");
					write_string(2,0,"Please wait.....");
					
					userID = Uart_Buff[3]-0x30;
								
					memset(userHeight,0,10);
					memset(userWeight,0,10);
					
					if(Uart_Buff[5] == '1')
					{
    
    
						j=0;
						for(i=0;i<20;i+=2)
						{
    
    
							userHeight[j] = read24c02AddDat(1+(userID-1)*40+i)*256 + read24c02AddDat(1+(userID-1)*40+i+1);//1,2; 3,4;5,6
							delay_ms(10);									
							j++;
						}		
						printf("$%01bu,9,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,@",
						userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000,userHeight[5]%1000,userHeight[6]%1000,userHeight[7]%1000,userHeight[8]%1000);
					}
					else if(Uart_Buff[5] == '2')
					{
    
    
						j=0;
						for(i=0;i<20;i+=2)
						{
    
    
							userWeight[j] = read24c02AddDat(21+(userID-1)*40+i)*256 + read24c02AddDat(21+(userID-1)*40+i+1);//21,22;3,4;5,6;
							delay_ms(10);									
							j++;
						}	
						printf("$%01bu,19,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,@",
							userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000,userWeight[5]%10000,userWeight[6]%10000,userWeight[7]%10000,userWeight[8]%10000);				
					}									
					write_string(1,0,"Extracting  data");
					write_string(2,0,"Send Data succes");
												
					delay_ms(2000);
					write_string(1,0,"Height:    000CM");
					write_string(2,0,"Weight:    5000g");
					write_sfm(1,11,3,HC_SR04Value);
					write_sfm(2,11,4,HX711_Weight);
				}
			}
			HC_SR04Value=Count();
			if(HC_SR04Value == -1)  //超出量程
			{
    
    
				write_string(1,11,"---");
			}
			else
			{
    
    
				write_sfm(1,11,3,HC_SR04Value);
			}
			HX711_Weight = Get_Weight();
			if(HX711_Weight == -1)  //超出量程
			{
    
    
//				write_string(2,11,"----");
			}
			else
			{
    
    
				write_sfm(2,11,4,HX711_Weight);
			}
			delay_ms(100);
		}
		KeyNum = KEY_Scan();
		if(KeyNum!=0xff)
		{
    
    
			switch(KeyNum)
			{
    
    
				case 1:
					if(menu == 0)
					{
    
    
						menu = 1;
						write_string(1,0,"Storage ID:   00");
						write_string(2,0,"H:000CM  W:5000g");
						write_sfm(1,14,2,userID);
						write_sfm(2,2,3,HC_SR04Value);
						write_sfm(2,11,4,HX711_Weight);
					}
					else
					{
    
    
						write_string(1,0,"Height:    000CM");
						write_string(2,0,"Weight:    5000g");
						write_sfm(1,11,3,HC_SR04Value);
						write_sfm(2,11,4,HX711_Weight);
						menu = 0;
					}
					break;
				case 2:
					if(menu == 1)
					{
    
    
						userID ++;
						if(userID > 5)
							userID = 1;
						write_sfm(1,14,2,userID);
					}
					break;
				case 3:
					if(menu == 1)  //存储
					{
    
    
						write_string(1,0,"Storage ID:     ");
						write_string(2,0,"Info saved  N:  ");
						
						memset(userHeight,0,10);
						memset(userWeight,0,10);
	
						j=0;
						for(i=0;i<20;i+=2)
						{
    
    
							userHeight[j] = read24c02AddDat(1+(userID-1)*40+i)*256 + read24c02AddDat(1+(userID-1)*40+i+1);//1,2; 3,4;5,6
							delay_ms(10);
							userWeight[j] = read24c02AddDat(21+(userID-1)*40+i)*256 + read24c02AddDat(21+(userID-1)*40+i+1);//21,22;3,4;5,6;
							delay_ms(10);						
							if(userHeight[j]==0 || userWeight[j] == 0)  //说明此位置没存到数据,那就存入当前要存的数据
							{
    
    
								break;
							}			
							j++;
						}						
						if(j >= 9) // 数据是满的,代表已经有了10次历史数据.清空最初的0位置数据,整体移动
						{
    
    
							for(i=0;i<8;i++)  // !!!!!!!!!!!!!! 没办法,10次不行导致程序输出错误,改动到9次
							{
    
    
								userHeight[i] = userHeight[i+1];
								userWeight[i] = userWeight[i+1];
							}
							userHeight[i] = HC_SR04Value; // 最后一位,数组8位置寸新数据
							userWeight[i] = HX711_Weight;
							
							write_sfm(1,14,2,userID);
							write_sfm(2,14,2,i+1);
						}
						else
						{
    
    
							userHeight[j] = HC_SR04Value;
							userWeight[j] = HX711_Weight;

							write_sfm(1,14,2,userID);
							write_sfm(2,14,2,j+1);
						}						
						j=0;
						for(i=0;i<20;i+=2)
						{
    
    
							write24c02AddDat(1+(userID-1)*40+i,userHeight[j]/256);
							write24c02AddDat(1+(userID-1)*40+i+1,userHeight[j]%256);
							delay_ms(10);
							write24c02AddDat(21+(userID-1)*40+i,userWeight[j]/256);
							write24c02AddDat(21+(userID-1)*40+i+1,userWeight[j]%256);
							delay_ms(10);
							j ++;
						}
						
//						printf("$%01bu,10,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,@",
//							userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000,userHeight[5]%1000,userHeight[6]%1000,userHeight[7]%1000,userHeight[8]%1000,userHeight[9]%1000);
//						printf("$%01bu,20,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,@",
//							userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000,userWeight[5]%10000,userWeight[6]%10000,userWeight[7]%10000,userWeight[8]%10000,userWeight[9]%10000);					
						
						delay_ms(2000);
						
						menu = 0;
						write_string(1,0,"Height:    000CM");
						write_string(2,0,"Weight:    5000g");
						write_sfm(1,11,3,HC_SR04Value);
						write_sfm(2,11,4,HX711_Weight);						
					}
					break;
				default:break;
			}
		}
	}
}
//void Send(uchar num)
//{
    
    
//	switch(num)
//	{
    
    
//		case 1:
//			printf("$%01bu,01,%03u,@",userID,userHeight[0]%1000);
//			printf("$%01bu,11,%04u,@",userID,userWeight[0]%10000);
//			break;
//		case 2:
//			printf("$%01bu,02,%03u,%03u,@",userID,userHeight[0]%1000,userHeight[1]%1000);
//			printf("$%01bu,12,%04u,%04u,@",userID,userWeight[0]%10000,userWeight[1]%10000);
//			break;
//		case 3:
//			printf("$%01bu,03,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000);
//			printf("$%01bu,13,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000);
//			break;
//		case 4:
//			printf("$%01bu,04,%03u,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000);
//			printf("$%01bu,14,%04u,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000);
//			break;
//		case 5:
//			printf("$%01bu,05,%03u,%03u,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000);
//			printf("$%01bu,15,%04u,%04u,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000);
//			break;
//		case 6:
//			printf("$%01bu,06,%03u,%03u,%03u,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000,userHeight[5]%1000);
//			printf("$%01bu,16,%04u,%04u,%04u,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000,userWeight[5]%10000);
//			break;
//		case 7:
//			printf("$%01bu,07,%03u,%03u,%03u,%03u,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000,userHeight[5]%1000,userHeight[6]%1000);
//			printf("$%01bu,17,%04u,%04u,%04u,%04u,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000,userWeight[5]%10000,userWeight[6]%10000);
//			break;
//		case 8:
//			printf("$%01bu,08,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000,userHeight[5]%1000,userHeight[6]%1000,userHeight[7]%1000);
//			printf("$%01bu,18,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000,userWeight[5]%10000,userWeight[6]%10000,userWeight[7]%10000);
//			break;
//		case 9:
//			printf("$%01bu,09,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000,userHeight[5]%1000,userHeight[6]%1000,userHeight[7]%1000,userHeight[8]%1000);
//			printf("$%01bu,19,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000,userWeight[5]%10000,userWeight[6]%10000,userWeight[7]%10000,userWeight[8]%10000);
//			break;
//		case 10:
//			printf("$%01bu,10,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,%03u,@",
//				userID,userHeight[0]%1000,userHeight[1]%1000,userHeight[2]%1000,userHeight[3]%1000,userHeight[4]%1000,userHeight[5]%1000,userHeight[6]%1000,userHeight[7]%1000,userHeight[8]%1000,userHeight[9]%1000);
//			printf("$%01bu,20,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,%04u,@",
//				userID,userWeight[0]%10000,userWeight[1]%10000,userWeight[2]%10000,userWeight[3]%10000,userWeight[4]%10000,userWeight[5]%10000,userWeight[6]%10000,userWeight[7]%10000,userWeight[8]%10000,userWeight[9]%10000);
//			break;
//		default:break;
//	}
//}

AT24C02断电存储模块

#ifndef __24C02_H__
#define __24C02_H__

#include "config.h"


#define add24c02 0									//定义24C02管脚输入地址比如:A2,A1,A0 = 0 0 0 = 0.(方便修改)

sbit SCL=P2^6;									//定义SCL时钟输出IO口.
sbit SDA=P2^5;									//定义SDA数据传输IO口.

#define ID_ADDR  250
#define ID_VALUE 250

void Inint_24c02(void);

unsigned char read24c02AddDat(unsigned char readAdd);//从指定的地址读取一个数据.
void write24c02AddDat(unsigned char writeAdd,unsigned char writeDat);//写入一个数据到指定地址.




#endif




#include "24c02.h"

void Delay5us()		//@12.000MHz
{
    
    
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}
//24C02部分
/*===========================================================================================
函数名	:	单片机上电,24C02初始化通迅IO.
调  用	:	无调用函数.
参  数	:	无参数.
返回值	:	无返回值.
备  注	:	24C02芯片在上电前,保持IO为空闲状态.
===========================================================================================*/
void Inint_24c02(void)					//上电保护空闲状态
{
    
    
	SCL = 1;
	SDA = 1;
}

/*===========================================================================================
函数名	:	24C02起始条件.
调  用	:	无调用函数.
参  数	:	无参数.
返回值	:	无返回值.
备  注	:	当SCL为高,SDA的下降沿叫做起始条件.(5V时:高大于0.6us,低大于0.6us)
===========================================================================================*/
void start24c02(void)				//24c02起始条件
{
    
    
	SDA = 1;						//SDA输出高电平(注意的是:SCL需在SDA后面拉高,如果外部已拉低SCL,SDA的拉高将被看着是停止条件)
	Delay5us();						//延时
	SCL = 1;						//SCL输出高电平
	Delay5us();						//延时
	SDA = 0;						//SDA下降沿.
	Delay5us();						//延时
	SCL = 0;						//SCL输出高电平
	Delay5us();						//延时
}

/*===========================================================================================
函数名	:	24C02停止条件.
调  用	:	无调用函数.
参  数	:	无参数.
返回值	:	无返回值.
备  注	:	当SCL为高,SDA的上升沿叫做停止条件.
			写此函数思想:1.要产生停止条件,必须要让SDA产生1个上升沿.在产生上升沿之前,SDA须为低.
						 2.正常停止信号在ACK应答后产生,考虑到应答和非应答,所以要先拉低SCL,允许
						 SDA确保为低,再拉高SCL来满足这1条件 
===========================================================================================*/
void stop24c02(void)				//24c02停止条件
{
    
    
	SCL = 0;						//允许SDA为低.
	Delay5us();						//延时
	SDA = 0;						//SDA输出低电平.
	Delay5us();						//延时
	SCL = 1;						//SCL输出高电平
	Delay5us();						//延时
	SDA = 1;						//SDA上升沿.
	Delay5us();						//延时
}


/*===========================================================================================
函数名	:	检测应答信号.
调  用	:	无调用函数.
参  数	:	无参数.
返回值	:	返回值应答,0表示应答,1表示非应答.
备  注	:	当发送完8个数据位后,再发送1个SCL用来检测24C02是否应答.
===========================================================================================*/
bit ACK24c02(void)					//检测应答信号
{
    
    
	SCL = 0;						//拉低SCL,允许SDA改变.
	Delay5us();						//延时
	SDA = 1;						//因为是要接收低电平,所以要先拉高SDA的电平.
	Delay5us();						//延时
	SCL = 1;						//发送1个SCL高.准备检测SDA数据.
	delay_ms(30);						//延时
	if(SDA == 1)					//检测SDA数据.
	{
    
    
		SCL = 0;					//拉低SCL,准备退出,允许外部改变SDA.
		Delay5us();					//延时
		return 1;					//如果读到应答对应位为1说明24c02没有应答.
	}
	else
	{
    
    
		SCL = 0;					//拉低SCL,准备退出,允许外部改变SDA.
		Delay5us();					//延时
		return 0;					//如果读到应答对应位为0说明24c02回复了应答.
	}
}

/*===========================================================================================
函数名	:	等待应答信号.
调  用	:	无调用函数.
参  数	:	无参数.
返回值	:	无返回值.
备  注	:
===========================================================================================*/
void ACKtime24c02(void)				//等待应答信号
{
    
    
	unsigned char i;				//定义1个变量用来循环延时.

	SCL = 0;						//允许数据改变
	Delay5us();						//延时.
	SDA = 1;						//因为是要接收低电平,所以要释放SDA数据线.
	Delay5us();						//延时.
	SCL = 1;						//发送一个时钟信号,等待SDA的应答信号.
	Delay5us();						//延时.
	while(SDA == 1 && i < 200)		//等待24c02应答信号
		i++;
	SCL = 0;						//接低SCL,允许外部改变SDA.
	Delay5us();						//延时.
}

/*===========================================================================================
函数名	:	回复应答或者非应答信号.
调  用	:	无调用函数.
参  数	:	ACKsw为:应答开关,为0时表示应答,1表示非应答.
返回值	:	无返回值.
备  注	:
===========================================================================================*/
void ACKSw24c02(bit ACKsw)	//回复应答或者非应答信号.为0时表示应答,1表示非应答.
{
    
    
	SCL = 0;						//拉抵SCL,允许SDA变化.
	Delay5us();						//延时
	SDA = ACKsw;					//把应答信号传输SDA.
	Delay5us();						//延时
	SCL = 1;						//拉高SCL,让24C来读取.
	Delay5us();						//延时
	SCL = 0;						//拉低SCL,允许外部改变SDA.
	Delay5us();						//延时
}

/*===========================================================================================
函数名	:	24C02位发送.
调  用	:	无调用函数.
参  数	:	dat为:要发送的8位数据.
返回值	:	无返回值.
备  注	:	传输1个字节数据的8个位.
===========================================================================================*/
void write8BitTO24c02(unsigned char dat)//24C02位发送
{
    
    
	unsigned char i;				//定义1个变量用来循环发送数据位用.
	
	SCL = 0;						//SCL输出低电平,允许数据改变
	Delay5us();						//延时
	for(i = 0;i < 8;i++)			//循环8次,发送1个数据的8个位
	{
    
    
		if(dat & 0x80)				//如果要发送的数据最高位为1
			SDA = 1;				//传输位1.
		else
			SDA = 0;				//否则传输0.
		dat <<= 1;					//把要传输的数据移到高位,方便下次发送.
		Delay5us();					//延时
		SCL = 1;					//SCL输出高电平24C02来读数
		Delay5us();					//延时
		SCL = 0;					//SCL输出低电平允许改变数据
		Delay5us();					//延时
	}
}

/*===========================================================================================
函数名	:	24C02读取8位.
调  用	:	无调用函数.
参  数	:	无参数.
返回值	:	返回读取到的8位数据.
备  注	:	读取1个字节数据的8个位.
===========================================================================================*/
unsigned char read8BitTO24c02(void)	//24C02读取8位
{
    
    
	unsigned char i;		//定义1个变量用来循环接收数据位用.
	unsigned char readDat;	//定义1个变量用来暂存接收到的数据
	
	readDat = 0;			//暂存初值为0.
	SCL = 0;				//SCL输出低电平,允许数据改变(任何时序函数结束时都应保持SCL低电平)
	SDA = 1;				//如果要接收数据,必须释放数据总线SDA.
	Delay5us();				//延时
	for(i = 0;i < 8;i++)	//循环8次,接收1个数据的8个位
	{
    
    
		SCL = 1;			//SCL拉高电平,24C输出数据位.
		delay_ms(30);			//延时
		readDat <<= 1;		//因为24C传输数据是高位在前,所以必须左移1位才有空位接收新的数据位.
		if(SDA == 1)		//如果读到的数据为1.
			readDat++;		//暂存最低位置1.
		SCL = 0;			//SCL输出低电平允许改变数据
		Delay5us();			//延时
	}
	return readDat;			//返回读取到的字节
}

/*===========================================================================================
函数名	:	写入一个数据到指定地址.
调  用	:	调用了起始条件函数,发送8位数据函数,检测应答函数,停止条件函数.
参  数	:	writeAdd为:指定数据存储的地址0-255
			writeDat为:要写入的数据.
返回值	:	无返回值.
备  注	:	使用此函数,只需指定数据存储的位置和要写入的数据.
			如果需要连续写,也可以用for循环指定开始位置和结束地址一次写入.
===========================================================================================*/
void write24c02AddDat(unsigned char writeAdd,unsigned char writeDat)//写入一个数据到指定地址.
{
    
    
	unsigned char addWrite;				//定义1个变量用来暂存器件地址.
	unsigned char i;					//定义1个变量i,用来循环检测24C是否应答.
	EA=0;
	addWrite = add24c02;				//把器件地址给暂存.
	addWrite <<= 1;						//器件地址向高位移1位.
	addWrite += 0;						//写入数据时,最低为应为0.
	addWrite |= 0xa0;					//把24C02高4位固定地址放在器件地址高4位.

	for(i = 0;i < 5;i++)				//最多检测5次应答信号,
	{
    
    
		start24c02();					//24c02起始条件.
		write8BitTO24c02(addWrite);		//写入7位器件地址,最低位为0.
		if(!ACK24c02())					//如果没有应答,继续发送起始条件和器件地址.
			break;						//如果应答了,就跳出for循环,准备写入地址.
	}
	write8BitTO24c02(writeAdd);			//写入数据要存储地址.
	ACKtime24c02();						//等待应答信号.
	write8BitTO24c02(writeDat);			//写数据.
	ACKtime24c02();						//等待应答信号.
	stop24c02();						//发送停止条件.
	EA=1;
}

/*===========================================================================================
函数名	:	从指定的地址读取一个数据.
调  用	:	调用了起始条件函数,发送8位数据函数,接收8位数据函数,等待应答函数,停止条件函数.
参  数	:	readAdd为:读出数据的地址0-255(要从哪个地址读出数据)
返回值	:	返回读出的数据.其类型为char.
备  注	:	此函数会返回该地址对应的数据.
===========================================================================================*/
unsigned char read24c02AddDat(unsigned char readAdd)//从指定的地址读取一个数据.
{
    
    
	unsigned char readDat;			//定义1个变量用来暂存读取数据.
	unsigned char addRead;			//定义1个变量用来暂存器件地址.
	unsigned char i;				//定义1个变量i,用来循环检测24C是否应答.
	EA=0;
	addRead = add24c02;				//把器件地址给暂存.
	addRead <<= 1;					//器件地址向高位移1位.
	addRead |= 0xa0;				//把24C02高4位固定地址放在器件地址高4位.
	readDat = 0;					//初始化暂存为0.

	for(i = 0;i < 5;i++)			//最多检测5次应答信号,
	{
    
    
		start24c02();				//24c02起始条件.
		write8BitTO24c02(addRead);	//写入7位器件地址,最低位为0.
		if(!ACK24c02())				//如果没有应答,继续发送起始条件和器件地址.
			break;					//如果应答了,就跳出for循环,准备写入地址.
	}
		write8BitTO24c02(readAdd);	//写入指定地址.
		ACKtime24c02();				//等待应答信号.
		start24c02();				//24c02起始条件.
		write8BitTO24c02(addRead+1);//写入7位器件地址,最低位为1(读).
		ACKtime24c02();				//等待应答信号
		readDat = read8BitTO24c02();//24C02位接收
		ACKSw24c02(1);				//非应答
		stop24c02();				//24c02停止条件
		EA=1;
		return readDat;				//返回读到的数据.
}
//24C02结束



/***************24C02的调用*****************

//记录数据:
if((flag_jishu)&&(m==0))
	{
		flag_jishu=0;
		password[0]=ch2o/10000%10;      //先将测的CH20值取位
		password[1]=ch2o/1000%10;
		password[2]=ch2o/100%10;
		password[3]=ch2o/10%10;
		password[4]=ch2o%10;
		for(i = 0;i < 5;i++)				//循环5次.,从24C02的0地址开始写入
			write24c02AddDat(i,password[i]);		//往24C02写入.
		m=1;
	}
//这样算来,每写入一个数据,就占了24C02的5个地址;
//即第二次数据的写入地址就从24C02的地址5开始了:
	if((m==1)&&(flag_jishu))
	{
		m=0;
		flag_jishu=0;
		password[5]=ch2o/10000%10;
		password[6]=ch2o/1000%10;
		password[7]=ch2o/100%10;
		password[8]=ch2o/10%10;
		password[9]=ch2o%10;
		for(i = 5;i < 10;i++)						//从地址5开始;循环5次.
			write24c02AddDat(i,password[i]);		//24C02写入.
	}
******************************************************/

HX711体重检测模块

#ifndef __HX711_H__
#define __HX711_H__


#include "config.h"

//IO设置
sbit HX711_DOUT=P2^3; 
sbit HX711_SCK=P2^4; 


//函数或者变量声明
extern uint GapValue;
void HX711_Init(void);
long Get_Weight(void);

#endif

#include "HX711.h"


//****************************************************
//延时函数
//****************************************************
void Delay__hx711_us(void)
{
    
    
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}

//****************************************************
//读取HX711
//****************************************************
unsigned long HX711_Read(void)	//增益128
{
    
    
	unsigned long count; 
	unsigned char i; 
  	HX711_DOUT=1; 
	Delay__hx711_us();
  	HX711_SCK=0; 
  	count=0; 
  	while(HX711_DOUT); 
  	for(i=0;i<24;i++)
	{
    
     
	  	HX711_SCK=1; 
	  	count=count<<1; 
		HX711_SCK=0; 
	  	if(HX711_DOUT)
			count++; 
	} 
 	HX711_SCK=1; 
    count=count^0x800000;//第25个脉冲下降沿来时,转换数据
	Delay__hx711_us();
	HX711_SCK=0;  
	return(count);
}

//****************************************************
//获取毛皮重量 -- 上称时上面的物体重量
//****************************************************
unsigned long idata Weight_Maopi = 0;
unsigned long idata Weight_Maopi_0 = 0;
uint GapValue  = 483;	//校准初值
void Get_Maopi()
{
    
    
	unsigned char clear;
mm:	Weight_Maopi_0 = HX711_Read();
	for(clear=0;clear<2;clear++)
	{
    
    
		delay_ms(500);	
	}
	Weight_Maopi = HX711_Read();
	if(Weight_Maopi/GapValue != Weight_Maopi_0/GapValue)
		goto mm;
} 
//****************************************************
//称重
//****************************************************
long Get_Weight(void)
{
    
    
	long Weight_Shiwu = 0;
	
	Weight_Shiwu = HX711_Read();
	Weight_Shiwu = Weight_Shiwu - Weight_Maopi;		//获取净重
	if(Weight_Shiwu < 0)
		Weight_Shiwu = 0;
	
	Weight_Shiwu = (unsigned int)((float)Weight_Shiwu/(float)GapValue + 0.5); 	//计算实物的实际重量																
	if(Weight_Shiwu > 5000)		//超重报警
	{
    
    
		return -1;
	}
	else
	{
    
    
		return 	(long)Weight_Shiwu;	
	}
}
void HX711_Init(void)
{
    
    
	Get_Maopi();
}






超声波测距模块

#ifndef __ULTRASONIC_H__
#define __ULTRASONIC_H__

#include "config.h"

sbit Trig_1=P1^6; //产生脉冲引脚
sbit Echo_1=P1^7; //回波引脚


void Init_Ultrasonic(void);		 //   T设置为0,E设置成1    10101010   

int Count(void);

#endif
#include "Ultrasonic.h"
bit Flag_Ultrasonic=0;
/*--------------------------------------------------------
函数名称:StartModule
函数功能://启动模块
注意事项:
提示说明:
输    入:
返    回:
---------------------------------------------------------*/
void  StartModule(void) 		         //启动模块
{
    
    
	TR0=0;
	TH0=0;
	TL0=0;
	Trig_1=1;		                     //启动一次模块
	_nop_();_nop_();_nop_();_nop_();_nop_(); //空操作5次
	_nop_();_nop_();_nop_();_nop_();_nop_(); //空操作5次
	_nop_();_nop_();_nop_();_nop_();_nop_(); //空操作5次
	_nop_();_nop_();_nop_();_nop_();_nop_(); //空操作5次
	Trig_1=0; 
}
/*--------------------------------------------------------
函数名称:Waiting_Ultrasonic
函数功能://等待超声波返回信号模块
注意事项:
提示说明:
输    入:
返    回:
---------------------------------------------------------*/
void Waiting_Ultrasonic(void)
{
    
    
	while(!Echo_1);TR0=1;		   
	while(Echo_1); TR0=0;
}

int Count(void)
{
    
    
	static int Time=0,S=0;
	
	StartModule();
	Waiting_Ultrasonic();
	
	Time=TH0*256+TL0;
	TH0=0;
	TL0=0;
  S=(int)((Time*1.7)/100.0);     //算出来是CM 	  //340
	if((S>=600)||Flag_Ultrasonic==1) //超出测量范围显示“-”
	{
    
    
		Flag_Ultrasonic = 0;
		return -1;
	}
	return S;
}
/*--------------------------------------------------------
函数名称:Timer0()
函数功能:定时器0中断子函数,计算距离溢出处理
注意事项:
提示说明:
输    入:
返    回:
---------------------------------------------------------*/
void Init_Timer0()
{
    
    
	TMOD&=0xf0;		   
	TMOD|=0x01;		//设T0为方式1
	TH0=0;		
	TL0=0;          //定时器0初始化装载0
	ET0=1;             //允许T0中断	
	EA=1;			   //开启总中断	
}
static void Timer0() interrupt 1 		 //T0中断用来计数器溢出,超过测距范围
{
    
    
	Flag_Ultrasonic=1;							 //中断溢出标志
}

void Init_Ultrasonic()		 //   T设置为0,E设置成1    10101010         
{
    
    
	Trig_1=0;
	Echo_1=1;
	
	Init_Timer0();
}

串口通信驱动

#ifndef _UART_H_  
#define _UART_H_  
 
#include "config.h"
  
#define FOSC 11059200L      //System frequency
#define BAUD 9600       //UART baudrate

/*Define UART parity mode*/
#define NONE_PARITY     0   //None parity
#define ODD_PARITY      1   //Odd parity
#define EVEN_PARITY     2   //Even parity
#define MARK_PARITY     3   //Mark parity
#define SPACE_PARITY    4   //Space parity

#define PARITYBIT NONE_PARITY   //Testing even parity

void UART_SendData(BYTE dat);
void UART_SendString(char *s);
void UART_Init(void);
	

#endif  
 
 
 
#include "UART.H"  
#include <stdio.h>

bit busy=0;

void UART_Init(void)
{
    
    
		#if (PARITYBIT == NONE_PARITY)
				SCON = 0x50;            //8-bit variable UART
		#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
				SCON = 0xda;            //9-bit variable UART, parity bit initial to 1
		#elif (PARITYBIT == SPACE_PARITY)
				SCON = 0xd2;            //9-bit variable UART, parity bit initial to 0
		#endif
	
		TL2 = RCAP2L = (65536-(FOSC/32/BAUD)); //Set auto-reload vaule
    TH2 = RCAP2H = (65536-(FOSC/32/BAUD)) >> 8;
    T2CON = 0x34;           //Timer2 start run
    ES = 1;                 //Enable UART interrupt
    EA = 1;                 //Open master interrupt switch
}

/*----------------------------
UART interrupt service routine
----------------------------*/
uchar xdata Uart_Buff[20];
uchar Uart_ResPos=0;
bit is_uartRec = 0;
void Uart_Isr() interrupt 4
{
    
    
    if (RI)
    {
    
    
        RI = 0;             //Clear receive interrupt flag
//        P0 = SBUF;          //P0 show UART data
//		GSMA6_Receive_Byte(SBUF);
			if(SBUF == '$')
			{
    
    
				Uart_ResPos = 0;
			}
			Uart_Buff[Uart_ResPos++] = SBUF;
			if(Uart_ResPos>10 || SBUF == '@')
			{
    
    
				is_uartRec = 1;
			}
    }
    if (TI)
    {
    
    
        TI = 0;             //Clear transmit interrupt flag
        busy = 0;           //Clear transmit busy flag
    }
}

/*----------------------------
Send a byte data to UART
Input: dat (data to be sent)
Output:None
----------------------------*/
void UART_SendData(BYTE dat)
{
    
    
    while (busy);           //Wait for the completion of the previous data is sent
    ACC = dat;              //Calculate the even parity bit P (PSW.0)
    if (P)                  //Set the parity bit according to P
    {
    
    
			#if (PARITYBIT == ODD_PARITY)
							TB8 = 0;            //Set parity bit to 0
			#elif (PARITYBIT == EVEN_PARITY)
							TB8 = 1;            //Set parity bit to 1
			#endif
					}
					else
					{
    
    
			#if (PARITYBIT == ODD_PARITY)
							TB8 = 1;            //Set parity bit to 1
			#elif (PARITYBIT == EVEN_PARITY)
							TB8 = 0;            //Set parity bit to 0
			#endif
    }
    busy = 1;
    SBUF = ACC;             //Send data to UART buffer
}

/*----------------------------
Send a string to UART
Input: s (address of string)
Output:None
----------------------------*/
//void UART_SendString(char *s)
//{
    
    
//    while (*s)              //Check the end of the string
//    {
    
    
//        UART_SendData(*s++);     //Send current char and increment string ptr
//    }
//}

// printf函数在格式化输出时,向下调用了char putchar(char c);这个函数,在“stdio.h”里可以发现有这个函数
char putchar(char c)//重定向
{
    
    
	UART_SendData(c);
	return c;
}



猜你喜欢

转载自blog.csdn.net/qq_20467929/article/details/126142494