• [技术干货] 11月嵌入式项目合集
    基于STM32的油井数据采集与处理cid:link_2本文介绍了油井数据采集与传输的流程,并给出了相应的Python代码示例。通过实时采集油井数据,并将其传输到服务器,从而实现对油井状态的监测和维护。可作为平时训练的例子。 开天平台+鸿蒙OS小熊派打造智能大棚温控报警cid:link_0本文把华为的众多生态结合在一起运用,对于想要了解华为云iot生态的用户具有非常好的借鉴意义IoTDA设备接入服务和边缘计算的结合:构建高效的边缘物联网方案cid:link_1本文介绍了边缘计算的概念,并且提供了一些python版本的demo,可以为入门的iot的新手提供一些边缘计算方面的的帮助。STC89C52+HX711完成电子秤设计cid:link_3此文介绍了C52制作一个电子秤,可以作为学习C52单片机的入门项目,丰富嵌入式开发的经验和了解电路的整体设计。IoTDA平台OTA升级与设备远程控制:华为云物联网平台的能力介绍cid:link_3
  • [技术干货] 基于单片机设计的水平仪(STC589C52+MPU6050)
    一、前言【1】项目背景水平仪是一种常见的测量工具,用于检测物体或设备的水平姿态。在许多应用中,如建筑、制造和航空等领域,保持设备的水平姿态是非常重要的。为了实现实时的水平检测和显示,基于单片机设计的水平仪是一个常见的解决方案。数字水平仪是一种用于测量物体相对于水平面的角度的仪器。它基于单片机设计,主控芯片为STC89C52,姿态检测采用MPU6050六轴传感器,显示屏用于显示水平姿态数据,锂电池供电。该仪器具有高精度、低功耗、易操作等特点,广泛应用于建筑、工程、测绘等领域。整个系统的设计思路是通过MPU6050获取设备的姿态数据,然后利用STC89C52进行数据处理和计算,最后将计算得到的水平偏移值通过SPI接口传输到0.96寸的OLED显示屏上进行实时显示。基于单片机设计的数字水平仪具有以下功能特点:主控芯片:本设计采用STC89C52单片机作为主控芯片,具有强大的处理能力和丰富的外设接口,能够满足数字水平仪的功能需求。姿态检测:通过MPU6050六轴传感器实现对物体姿态的实时检测,包括加速度计、陀螺仪和磁力计等,能够精确测量物体在三维空间中的倾斜角度。显示屏显示:采用液晶显示屏实时显示水平姿态数据,用户可以通过显示屏直观地了解物体的倾斜情况。锂电池供电:采用锂电池作为电源,具有高能量密度、长寿命和环保等优点,能够满足数字水平仪长时间工作的需求。低功耗设计:通过合理的硬件设计和软件优化,实现低功耗运行,降低能耗,延长电池使用寿命。数据存储与传输:内置存储器可存储大量姿态数据,支持USB接口进行数据传输,方便用户进行数据分析和处理。易于操作:数字水平仪具有简洁明了的操作界面,用户只需简单设置即可开始测量,无需复杂的操作步骤。稳定性高:通过高精度的姿态检测和数据处理算法,实现对物体倾斜角度的准确测量,保证测量结果的稳定性和可靠性。下面是手机上的水平仪软件显示效果: 原理是一样的【2】项目的关键点包括(1)硬件设计:包括将STC89C52和MPU6050连接在一起,确保它们之间的通信正常。同时,需要将OLED显示屏与STC89C52通过SPI接口连接起来,以便将姿态数据显示在屏幕上。(2)软件设计:需要编写嵌入式软件,包括驱动程序和算法,以实现数据的采集、处理和显示。主控芯片STC89C52上的程序需要读取MPU6050传感器的数据,并进行姿态计算,然后将结果发送到OLED显示屏上进行显示。(3)界面设计:在OLED显示屏上实时显示水平偏移值,需要设计一个简洁直观的用户界面,使用户能够清楚地了解设备的姿态状态。通过该项目,能够实现一个基于单片机设计的水平仪,可以实时检测设备的水平姿态,并将结果显示在OLED屏幕上。这对于许多需要保持设备水平的应用场景非常有用,提高了工作效率和准确性。二、项目软硬件设计思路【1】硬件设计思路(1)主控芯片选择:选择了STC89C52作为主控芯片。STC89C52是一款常用的单片机,具有丰富的外设接口和强大的处理能力,适合用于嵌入式应用。它具有8位的数据总线和12MHz的主频,能够满足的需求。(2)姿态检测传感器选择:选择了MPU6050作为姿态检测传感器。MPU6050是一种集成了三轴陀螺仪和三轴加速度计的传感器模块,能够准确地检测设备的姿态变化。它通过I2C接口与主控芯片进行通信,传输姿态数据。(3)OLED显示屏选择:选择了一款采用SPI接口的0.96寸OLED显示屏。SPI接口可以提供高速的数据传输,适合实时显示姿态数据。OLED显示屏具有高对比度、低功耗和快速响应的特点,非常适合作为水平偏移值的显示设备。(4)硬件接线:在硬件设计中,需要将STC89C52、MPU6050和OLED显示屏进行合适的接线连接。具体接线方式如下:将STC89C52的引脚与MPU6050的I2C接口连接,实现主控芯片与姿态传感器之间的通信。将STC89C52的引脚与OLED显示屏的SPI接口连接,以便将姿态数据传输到显示屏上。【2】软件设计思路(1)初始化:在软件设计中,首先需要进行硬件的初始化设置。包括初始化STC89C52的引脚和外设配置,以及初始化MPU6050和OLED显示屏的通信设置。(2)数据采集:通过主控芯片的I2C接口,读取MPU6050传感器的原始数据。MPU6050提供了陀螺仪和加速度计的数据,可以通过读取寄存器获取这些数据。(3)姿态计算:利用获取的陀螺仪和加速度计数据,进行姿态计算。常见的姿态计算算法包括互补滤波算法和卡尔曼滤波算法。(4)水平偏移值计算:根据姿态计算的结果,计算出水平偏移值。水平偏移值可以通过比较设备的当前姿态与水平状态的差异来确定。(5)数据显示:将计算得到的水平偏移值通过SPI接口发送到OLED显示屏。需要设计一个简洁的用户界面,在屏幕上实时显示水平偏移值。(6)循环执行:以上步骤需要在一个循环中不断执行,以实现实时的姿态检测和显示。循环的周期可以根据实际需求进行设置,通常需要考虑到实时性和性能的平衡。【3】硬件连线说明在此项目中,硬件模块需要连接到STC89C52单片机的不同引脚。下面是硬件模块与单片机引脚的连接描述:(1)MPU6050连接:MPU6050的SCL引脚(时钟线)连接到STC89C52的P1.0引脚,作为I2C总线的时钟线。MPU6050的SDA引脚(数据线)连接到STC89C52的P1.1引脚,作为I2C总线的数据线。MPU6050的VCC引脚连接到电源正极(3.3V或5V)。MPU6050的GND引脚连接到电源地线。(2)OLED显示屏连接:OLED显示屏的SCL引脚(时钟线)连接到STC89C52的P1.2引脚,作为SPI总线的时钟线。OLED显示屏的SDA引脚(数据线)连接到STC89C52的P1.3引脚,作为SPI总线的数据线。OLED显示屏的RST引脚(复位线)连接到STC89C52的P1.4引脚,用于复位显示屏。OLED显示屏的DC引脚(数据/命令选择线)连接到STC89C52的P1.5引脚,用于选择发送数据或命令。OLED显示屏的CS引脚(片选线)连接到STC89C52的P1.6引脚,用于选中显示屏。OLED显示屏的VCC引脚连接到电源正极(3.3V或5V)。OLED显示屏的GND引脚连接到电源地线。三、项目代码设计#include <reg52.h>#include <intrins.h>​// 定义OLED显示屏引脚sbit OLED_RST = P1^0; // RST引脚sbit OLED_DC = P1^1; // DC引脚sbit OLED_DIN = P1^2; // DIN引脚sbit OLED_CLK = P1^3; // CLK引脚sbit OLED_CS = P1^4; // CS引脚​// 姿态检测传感器相关定义sbit MPU_SCL = P2^6; // I2C时钟引脚sbit MPU_SDA = P2^7; // I2C数据引脚​// 定义全局变量float pitch = 0.0; // 当前设备的俯仰角​// OLED显示屏相关函数void OLED_WrCmd(unsigned char cmd);void OLED_WrDat(unsigned char dat);void OLED_Init();void OLED_SetPos(unsigned char x, unsigned char y);void OLED_Fill(unsigned char bmp_data);void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str);​// I2C总线相关函数void I2C_Start();void I2C_Stop();unsigned char I2C_WaitAck();void I2C_Ack();void I2C_NAck();void I2C_SendByte(unsigned char dat);unsigned char I2C_ReadByte();​// MPU6050相关函数void MPU_Init();void MPU_WriteReg(unsigned char reg, unsigned char dat);unsigned char MPU_ReadReg(unsigned char reg);void MPU_ReadData(short *data);​// 延时函数void Delay(unsigned int n);​// 主函数void main() { unsigned char str[16]; MPU_Init(); // 初始化MPU6050 OLED_Init(); // 初始化OLED显示屏 while (1) { short data[3]; MPU_ReadData(data); // 读取姿态传感器数据 pitch = -atan2(data[1], data[2]) * (180.0 / 3.14159); // 计算俯仰角度 sprintf(str, "Pitch:%.2f", pitch); // 格式化俯仰角数据 OLED_ShowString(0, 0, str); // 在OLED显示屏上显示俯仰角度 Delay(100); }}​// OLED显示屏写命令void OLED_WrCmd(unsigned char cmd) { unsigned char i; OLED_DC = 0; OLED_CS = 0; for (i = 0; i < 8; i++) { OLED_CLK = 0; if (cmd & 0x80) { OLED_DIN = 1; } else { OLED_DIN = 0; } OLED_CLK = 1; cmd <<= 1; } OLED_CS = 1;}​// OLED显示屏写数据void OLED_WrDat(unsigned char dat) { unsigned char i; OLED_DC = 1; OLED_CS = 0; for (i = 0; i < 8; i++) { OLED_CLK = 0; if (dat & 0x80) { OLED_DIN = 1; } else { OLED_DIN = 0; } OLED_CLK = 1; dat <<= 1; } OLED_CS = 1;}​// OLED显示屏初始化void OLED_Init() { OLED_RST = 0; Delay(100); OLED_RST = 1; Delay(100); OLED_WrCmd(0xae); // 关闭显示 OLED_WrCmd(0x00); // 设置低列地址 OLED_WrCmd(0x10); // 设置高列地址 OLED_WrCmd(0x40); // 设置起始行地址 OLED_WrCmd(0x81); // 对比度设置 OLED_WrCmd(0xcf); // 设置对比度 OLED_WrCmd(0xa1); // 设置段重映射 OLED_WrCmd(0xc8); // 设置列重映射 OLED_WrCmd(0xa6); // 正常显示 OLED_WrCmd(0xa8); // 多路复用设置 OLED_WrCmd(0x3f); // 设置多路复用 OLED_WrCmd(0xd3); // 设置显示偏移 OLED_WrCmd(0x00); // 设置显示偏移 OLED_WrCmd(0xd5); // 设置显示时钟分频 OLED_WrCmd(0x80); // 设置显示时钟分频 OLED_WrCmd(0xd9); // 设置预充电周期 OLED_WrCmd(0xf1); // 设置预充电周期 OLED_WrCmd(0xda); // 设置COM硬件引脚配置 OLED_WrCmd(0x12); // 设置COM硬件引脚配置 OLED_WrCmd(0xdb); // 设置VCOMH电压倍率 OLED_WrCmd(0x40); // 设置VCOMH电压倍率 OLED_WrCmd(0x8d); // 设置DC-DC电压输出开关 OLED_WrCmd(0x14); // 设置DC-DC电压输出开关 OLED_WrCmd(0xaf); // 打开显示 OLED_Fill(0x00); // 清屏}​// OLED显示屏设置位置void OLED_SetPos(unsigned char x, unsigned char y) { OLED_WrCmd(0xb0 + y); OLED_WrCmd(((x & 0xf0) >> 4) | 0x10); OLED_WrCmd((x & 0x0f) | 0x01);}​// OLED显示屏填充void OLED_Fill(unsigned char bmp_data) { unsigned char y, x; for (y = 0; y < 8; y++) { OLED_WrCmd(0xb0 + y); OLED_WrCmd(0x00); OLED_WrCmd(0x10); for (x = 0; x < 128; x++) { OLED_WrDat(bmp_data); } }}​// OLED显示屏显示字符串void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) { unsigned char c = 0, i = 0; while (str[i] != '\0') { c = str[i] - 32; if (x > 120) { x = 0; y++; } OLED_SetPos(x, y); for (i = 0; i < 6; i++) { OLED_WrDat(F6x8[c][i]); } i++; x += 6; }}​// I2C总线开始信号void I2C_Start() { MPU_SDA = 1; MPU_SCL = 1; Delay(1); MPU_SDA = 0; Delay(1); MPU_SCL = 0;}​// I2C总线停止信号void I2C_Stop() { MPU_SDA = 0; MPU_SCL = 1; Delay(1); MPU_SDA = 1; Delay(1);}​// I2C总线等待应答信号unsigned char I2C_WaitAck() { unsigned char ack; MPU_SDA = 1; Delay(1); MPU_SCL = 1; Delay(1); ack = MPU_SDA; MPU_SCL = 0; return ack;}​// I2C总线发送应答信号void I2C_Ack() { MPU_SCL = 0; MPU_SDA = 0; Delay(1); MPU_SCL = 1; Delay(1); MPU_SCL = 0; MPU_SDA = 1; Delay(1);}​// I2C总线发送非应答信号void I2C_NAck() { MPU_SCL = 0; MPU_SDA = 1; Delay(1); MPU_SCL = 1; Delay(1); MPU_SCL = 0;}​// I2C总线发送一个字节数据void I2C_SendByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { MPU_SDA = (dat & 0x80) >> 7; dat <<= 1; Delay(1); MPU_SCL = 1; Delay(1); MPU_SCL = 0; Delay(1); } MPU_SDA = 1; Delay(1); MPU_SCL = 1; Delay(1); MPU_SCL = 0;}​// I2C总线读取一个字节数据unsigned char I2C_ReadByte() { unsigned char i, dat; for (i = 0; i < 8; i++) { dat <<= 1; MPU_SCL = 1; Delay(1); dat |= MPU_SDA; MPU_SCL = 0; Delay(1); } return dat;}​// MPU6050初始化void MPU_Init() { I2C_Start(); I2C_SendByte(0xd0); // 输入器件地址 I2C_WaitAck(); I2C_SendByte(0x6b); // PWR_MGMT_1寄存器地址 I2C_WaitAck(); I2C_SendByte(0x00); // 写0,唤醒设备 I2C_WaitAck(); I2C_Stop();}​// MPU6050写寄存器void MPU_WriteReg(unsigned char reg, unsigned char dat) { I2C_Start(); I2C_SendByte(0xd0); // 输入器件地址 I2C_WaitAck(); I2C_SendByte(reg); // 寄存器地址 I2C_WaitAck(); I2C_SendByte(dat); // 数据 I2C_WaitAck(); I2C_Stop();}​// MPU6050读寄存器unsigned char MPU_ReadReg(unsigned char reg) { unsigned char dat; I2C_Start(); I2C_SendByte(0xd0); // 输入器件地址 I2C_WaitAck(); I2C_SendByte(reg); // 寄存器地址 I2C_WaitAck(); I2C_Start(); I2C_SendByte(0xd1); // 输出器件地址 I2C_WaitAck(); dat = I2C_ReadByte(); // 读取数据 I2C_NAck(); I2C_Stop(); return dat;}​// MPU6050读取数据void MPU_ReadData(short *data) { unsigned char i; unsigned char buf[14]; I2C_Start(); I2C_SendByte(0xd0); // 输入器件地址 I2C_WaitAck(); I2C_SendByte(0x3b); // 寄存器地址 I2C_WaitAck(); I2C_Start(); I2C_SendByte(0xd1); // 输出器件地址 I2C_WaitAck(); for (i = 0; i < 13; i++) { buf[i] = I2C_ReadByte(); // 读取数据 I2C_Ack(); } buf[13] = I2C_ReadByte(); // 读取数据 I2C_NAck(); I2C_Stop(); // 数据转换 data[0] = ((short)buf[0] << 8) | buf[1]; data[1] = ((short)buf[2] << 8) | buf[3]; data[2] = ((short)buf[4] << 8) | buf[5];}​四、总结这个项目是基于单片机设计的水平仪,使用了STC89C52作为主控芯片和MPU6050作为姿态检测传感器。其主要功能是检测当前设备的姿态,并计算出水平偏移值,最后通过OLED显示屏实时展示。整个项目涉及到硬件和软件两个方面。硬件方面,使用STC89C52作为主控芯片,负责控制整个系统的运行和数据处理。MPU6050姿态检测传感器用于获取设备的姿态信息,包括加速度和角速度。OLED显示屏采用SPI接口的0.96寸显示屏,用于将计算得到的水平偏移值实时显示出来。软件方面,编写嵌入式C程序来实现系统的功能。通过STC89C52与MPU6050进行通信,获取姿态传感器的原始数据。根据这些原始数据进行姿态计算,得到水平偏移值。再将计算得到的水平偏移值通过SPI接口发送给OLED显示屏,实时显示在屏幕上。项目利用STC89C52和MPU6050实现了一个水平仪,能够检测设备的姿态并计算出水平偏移值,并通过OLED显示屏实时展示。这个水平仪可以在许多应用场景中使用,如建筑工地、航空航天等需要测量水平的领域。
  • [技术干货] STC89C52+HX711完成电子秤设计
    一、项目背景电子秤是一种通过传感器测量物体质量的设备,被广泛应用于商业和工业领域。传统的机械秤已经逐渐被电子秤取代,因为电子秤具有更高的精度、更方便的使用和更多的功能。本项目利用STC89C52单片机和HX711模块完成电子秤设计,具有去皮功能、累加功能以及LCD1602显示屏显示实时称重数据。通过HX711模块实现对物品重量的精确测量;支持去皮功能,可以在测量前将容器的重量减去,得到净重;具备累加功能,可以记录多次测量结果并进行累加;使用LCD1602显示屏实时显示当前称重数据,方便用户查看。该电子秤实现了基本称重功能和去皮功能。基本称重功能指的是通过传感器测量物体质量并显示结果。HX711模块连接到电子秤上的传感器,通过读取HX711模块输出的数字信号来获取物体的重量数据。STC89C52单片机通过串行通信协议与HX711模块进行通信,并从中获取到称重数据。然后,通过控制LCD1602显示器,将称重结果以可读的方式显示在LCD屏幕上。用户可以清晰地看到物体的重量。除了基本称重功能,该电子秤还具有去皮功能。当用户按下独立按键时,系统将自动记录当前的称重值为皮重。之后,无论在物体放置前还是放置后按下独立按键,系统都会自动去除皮重,并显示净重。这样可以方便地进行物体重量的测量,而无需手动计算。整个系统的核心是STC89C52单片机,负责与HX711模块和LCD1602显示器进行通信,并控制显示内容和去皮功能。该设计不仅简单实用,而且具有良好的可扩展性和稳定性,适用于各种实际应用场景,如商业称重、食品加工等。二、项目设计思路【1】硬件设计思路:本项目采用STC89C52单片机作为核心控制器,HX711模块作为传感器,以及LCD1602显示屏和独立按键作为用户接口。整个系统的连接方式如下:将PD_SCK引脚连接到P1.5引脚(中断0),DOUT引脚连接到P1.6引脚(中断1),这两个引脚用于与HX711模块进行SPI通信。LCD1602显示屏数据线DB0-DB7分别连接到P0.0-P0.7引脚,RS(寄存器/数据选择)引脚连接至P2.0引脚,R/W(读写模式选择)引脚连接至P2.1引脚,E(使能)引脚连接至P2.2引脚,这些引脚用于控制LCD1602显示器上的文本显示。独立按键引脚连接至P3.2引脚,用于触发去皮功能。【2】软件设计思路:(1)初始化 LCD1602 显示器。设置显示模式、清除显示、光标复位等。(2)初始化 HX711 模块。通过 SPI 通信协议与模块建立连接,设置增益和偏移量等参数。(3)建立中断服务程序。P1.5和P1.6引脚在正常工作时,都是高电平状态,但当需要进行SPI通信时,需要向这两个引脚分别输出规定的低电平与高电平。可以通过设置中断服务程序,在需要进行SPI通信的时候向P1.5和P1.6引脚分别输出指定的电平。当SPI通信完成后,恢复高电平状态。(4)定义数据结构体。在程序中定义一个数据结构体,包含去皮函数的标志位、去皮值以及重量值等变量。当需要对秤进行去皮操作时,可以通过修改该数据结构体中的参数来实现。(5)测量重量并显示结果。通过调用HX711模块的函数读取传感器的模拟信号,并将其转换为数字信号。然后再通过数学运算计算出物体的重量,并将其转换成字符串的形式依次显示在LCD1602显示屏上的第一行和第二行。(6)实现去皮功能。当用户按下独立按键时,首先判断是否已经进行过去皮操作,如果没有就记录当前称重值为皮重值。之后,每次测量重量时都从当前重量中减去去皮值,得到净重值。(7)主程序循环执行。主程序通过延时函数实现循环执行的功能。在这个过程中,不断测量重量并显示结果,同时根据用户输入实现去皮功能。本项目的硬件设计采用STC89C52单片机、HX711模块、LCD1602显示屏和独立按键。软件设计主要包括初始化程序、中断服务程序、重量测量与显示程序、去皮函数和主程序等部分。通过完美地结合硬件和软件的功能,可以实现电子秤的基本称重和去皮功能,并且具有较高的精度和稳定性。三、项目代码编写#include <reg52.h>#include <intrins.h>​// 定义LCD1602相关引脚sbit RS = P2^0; // RS引脚sbit RW = P2^1; // RW引脚sbit E = P2^2; // E引脚sbit DATA = P0; // 数据线引脚​// 定义HX711相关引脚sbit PD_SCK = P1^5; // 时钟引脚sbit DOUT = P1^6; // 数据引脚​// 定义独立按键引脚sbit KEY = P3^2;​// 定义全局变量bit tareFlag = 0; // 去皮操作标志位long tareValue = 0; // 去皮值​// LCD1602写命令void LcdWriteCmd(unsigned char cmd) { RS = 0; RW = 0; DATA = cmd; _nop_(); E = 1; _nop_(); E = 0; _nop_();}​// LCD1602写数据void LcdWriteData(unsigned char dat) { RS = 1; RW = 0; DATA = dat; _nop_(); E = 1; _nop_(); E = 0; _nop_();}​// LCD1602初始化void LcdInit() { LcdWriteCmd(0x38); // 设置16x2显示模式 LcdWriteCmd(0x0c); // 开启光标并关闭闪烁 LcdWriteCmd(0x06); // 光标右移 LcdWriteCmd(0x01); // 清除显示 LcdWriteCmd(0x80); // 设置显示位置为第一行第一个字符}​// HX711读取数据long Hx711Read() { unsigned char i; long value = 0; while(DOUT); for (i = 0; i < 24; i++) { PD_SCK = 1; value = value << 1; _nop_(); if (DOUT) { value++; } PD_SCK = 0; _nop_(); } for (i = 0; i < 1; i++) { PD_SCK = 1; _nop_(); PD_SCK = 0; _nop_(); } return value;}​// 延时函数void Delay(unsigned int n) { unsigned int i, j; for (i = n; i > 0; i--) { for (j = 110; j > 0; j--); }}​// 显示重量和去皮值void DisplayWeight(long weight) { unsigned char i; unsigned long temp; unsigned char str[] = "Weight:"; temp = weight; for (i = 7; i >= 0; i--) { str[7 - i + 7] = temp % 10 + '0'; temp /= 10; } for (i = 0; i < 8; i++) { LcdWriteData(str[i]); } if (tareFlag) { unsigned char strTare[] = " Tare:"; temp = tareValue; for (i = 7; i >= 0; i--) { strTare[7 - i + 6] = temp % 10 + '0'; temp /= 10; } for (i = 0; i < 7; i++) { LcdWriteData(strTare[i]); } }}​// 去皮操作void Tare() { tareFlag = 1; tareValue = Hx711Read();}​// 主函数void main() { long weight; LcdInit(); // 初始化LCD1602 Delay(5); while (1) { weight = Hx711Read(); // 读取重量 if (tareFlag) { weight -= tareValue; // 计算净重 } DisplayWeight(weight); // 显示重量 if (KEY == 0) { Delay(50); if (KEY == 0) { Tare(); // 执行去皮操作 while (!KEY); Delay(10); } } }}项目代码里实现了HX711传感器读取重量数据,并通过LCD1602模块显示重量信息。在引入头文件和定义相关引脚后,通过全局变量来设置去皮操作的标志位和去皮值。定义了几个辅助函数,包括LCD1602的写命令和写数据函数、LCD1602的初始化函数、HX711传感器读取数据函数以及延时函数。在主函数中,进行LCD1602的初始化。进入一个无限循环,不断读取HX711传感器的重量数据并显示在LCD上。如果去皮操作的标志位被设置了,会计算净重并显示在LCD上。当检测到一个按键按下时,执行去皮操作,并等待按键释放。四、总结本项目是一个基于8051单片机的电子秤程序,利用HX711传感器读取重量数据,并通过LCD1602模块显示重量信息。在项目实现过程中,进行了硬件的连接,包括将HX711传感器与单片机相连以及将LCD1602模块与单片机相连。然后,通过编写相应的代码来实现功能。在代码实现中,主要包括了对LCD1602的初始化和写命令、写数据操作的函数定义,以及对HX711传感器的数据读取和显示重量的函数定义。利用全局变量设置了去皮操作的标志位和去皮值,并在主函数中通过循环不断读取HX711传感器的重量数据并进行相应的处理和显示。
  • [技术干货] 51单片机+DS1302设计一个电子钟(LCD1602显示时间)
    一、前言电子钟是一种能够准确显示时间的设备,广泛应用于家庭、办公场所和公共场所,为人们提供了方便和准确的时间信息。本项目设计一个基于51单片机的电子钟,使用DS1302作为RTC时钟芯片,LCD1602作为显示屏,并通过串口方式连接上位机进行时间设置和闹钟设置。STC89C52作为主控芯片,具有较高的性能和稳定性,可完成对外设的控制和数据处理。DS1302是一款低功耗的实时时钟芯片,能够提供准确的时间计数和日期功能。LCD1602是一款常用的字符型液晶显示屏,具有两行16列的显示区域,能够清晰显示时间和其他相关信息。通过串口连接上位机,用户可以方便地设置电子钟的时间和闹钟时间,实现个性化需求。此外,电子钟还带有一个蜂鸣器,可以根据设置的闹钟时间进行响铃,提醒用户。电子钟具有以下功能:(1)显示当前时间和日期:LCD1602显示屏将实时更新并显示当前的时间和日期信息。(2)时间设置:通过串口连接上位机,用户可以进行时间的设置,包括小时、分钟和秒。(3)日期设置:用户可以通过上位机设置当前的年、月和日。(4)闹钟设置:用户可以设置闹钟的时间,包括小时和分钟。到达设定时间时,蜂鸣器将响铃提醒用户。(5)整点报时:每到整点,蜂鸣器将发出短促的提示音,提醒用户当前时间。(6)闹钟响铃:当闹钟时间到达时,蜂鸣器将持续响铃,直到用户停止。(7)该项目将借助STC89C52单片机的控制能力和串口通信功能,结合DS1302时钟芯片和LCD1602显示屏,实现一个简单而实用的电子钟。用户可以根据自己的(8)需求进行时间设置和闹钟设置,方便实用,并且具有较高的准确性和稳定性。二、项目的设计思路项目的设计思路分为硬件设计和软件设计两部分。2.1 硬件设计思路(1)主控芯片选择:选择STC89C52作为主控芯片,由于其较高的性能和稳定性,适合用于控制和数据处理。(2)RTC时钟芯片选择:选择DS1302作为RTC时钟芯片,具有低功耗、精确计时和日期功能。(3)显示屏选择:选择LCD1602作为显示屏,它具有两行16列的字符显示区域,能够清晰显示时间和其他相关信息。(4)串口连接:设计串口连接电路,实现与上位机的通信,用于时间设置和闹钟设置。(5)蜂鸣器:添加蜂鸣器模块,用于整点报时和闹钟响铃功能。(6)按键输入:添加按键输入模块,用于用户操作,如切换设置模式、调整时间和设置闹钟。2.2 软件设计思路(1)初始化设置:在程序启动时,进行硬件初始化,包括配置主控芯片的引脚、初始化DS1302时钟芯片和LCD1602显示屏。(2)时间获取与显示:通过DS1302时钟芯片获取当前的时间和日期,并将其显示在LCD1602显示屏上。(3)串口通信:通过串口与上位机进行通信,接收上位机发送的时间设置和闹钟设置指令,并进行相应的处理(4)时间设置:根据上位机发送的时间设置指令,更新DS1302时钟芯片的时间计数器。(5)日期设置:根据上位机发送的日期设置指令,更新DS1302时钟芯片的日期计数器。(6)闹钟设置:根据上位机发送的闹钟设置指令,设置闹钟时间,并将其保存在主控芯片的内部存储器中。(7)整点报时:通过检测DS1302时钟芯片的小时计数器,当小时值变化时,触发蜂鸣器发出短促的提示音。(8)闹钟响铃:通过比较当前时间和保存的闹钟时间,当达到闹钟时间时,触发蜂鸣器持续响铃,直到用户停止或设定的时间段结束。三、项目硬件接线(1)STC89C52与DS1302:STC89C52的P2.0口连接到DS1302的SCLK(时钟)引脚,用于提供时钟信号。STC89C52的P2.1口连接到DS1302的IO(数据)引脚,用于数据传输。STC89C52的P2.2口连接到DS1302的RST(复位)引脚,用于对DS1302进行复位操作。(2)STC89C52与LCD1602:STC89C52的P0口连接到LCD1602的D0-D7引脚,用于传输字符数据和控制信号。STC89C52的P2.3口连接到LCD1602的RS(寄存器选择)引脚,用于选择数据或命令寄存器。STC89C52的P2.4口连接到LCD1602的RW(读写选择)引脚,用于选择读或写操作。STC89C52的P2.5口连接到LCD1602的E(使能)引脚,用于启动传输。(3)STC89C52与蜂鸣器模块:STC89C52的P3.7口连接到蜂鸣器模块的信号引脚,用于触发蜂鸣器响铃。(4)串口通信接口。在STC89C52单片机上,串口引脚如下:UART接收线(RXD):连接至外部设备的发送线。STC89C52的P3.0口(RXD)用于接收串口数据。UART发送线(TXD):连接至外部设备的接收线。STC89C52的P3.1口(TXD)用于发送串口数据。四、项目代码4.1 DS1302时钟读取、设置下面代码实现了,STC89C52读取DS1302时钟信息打印到串口,以及设置闹钟、时间读取、打印到串口的功能。其中,采用了UART通信进行与上位机交互,可以接收上位机发送过来的时间字符串,并据此设置闹钟和时间。#include <reg52.h>#include <stdio.h>​#define uchar unsigned char#define uint unsigned int​// 定义DS1302时钟寄存器地址#define DS1302_SEC_REG 0x80#define DS1302_MIN_REG 0x82#define DS1302_HR_REG 0x84#define DS1302_DAY_REG 0x86#define DS1302_MONTH_REG 0x88#define DS1302_YEAR_REG 0x8C​// 定义DS1302控制寄存器命令#define DS1302_CMD_WRITE 0x80#define DS1302_CMD_READ 0x81​// 定义串口波特率为9600#define BAUDRATE 9600#define FOSC 11059200L#define TIMER_INTERVAL (65536 - FOSC / 12 / BAUDRATE)​// 声明全局变量uchar time_buffer[20]; // 存放时间字符串uchar alarm_buffer[20]; // 存放闹钟时间字符串uint i;bit flag; // 标记是否接收到上位机的时间字符串​// 初始化UART模块void InitUart() { TMOD &= 0x0F; TMOD |= 0x20; TH1 = TIMER_INTERVAL / 256; TL1 = TIMER_INTERVAL % 256; PCON |= 0x80; SCON = 0x50; ES = 1; TR1 = 1; EA = 1;}​// 将单个字节发送到串口void SendData(uchar dat) { SBUF = dat; while (!TI); TI = 0;}​// 将字符串发送到串口void SendString(uchar *s) { while (*s != '\0') { SendData(*s++); }}​// 初始化DS1302时钟芯片void InitDS1302() { uchar i;​ // 使能DS1302写保护功能 DS1302_CE = 0; DS1302_SCL = 0; DS1302_CE = 1; Write_DS1302(DS1302_CMD_WRITE | 0x8e, 0x80);​ // 关闭时钟允许,准备写入数据 Write_DS1302(DS1302_CMD_WRITE | 0x90, 0x00);​ // 写入年月日时分秒周 Write_DS1302(DS1302_SEC_REG, 0x00); Write_DS1302(DS1302_MIN_REG, 0x30); Write_DS1302(DS1302_HR_REG, 0x11); Write_DS1302(DS1302_DAY_REG, 0x08); Write_DS1302(DS1302_MONTH_REG, 0x09); Write_DS1302(DS1302_YEAR_REG, 0x21); Write_DS1302(0x8e, 0x00);​ // 初始化闹钟时间 for (i = 0; i < 20; i++) { alarm_buffer[i] = 0; }}​// 向DS1302写入数据void Write_DS1302(uchar addr, uchar dat) { uchar i;​ DS1302_CE = 0; DS1302_SCL = 0;​ // 发送起始信号 DS1302_CE = 1; DS1302_SCL = 1; DS1302_CE = 0;​ // 发送命令字节地址 DS1302_WriteByte(addr); // 发送数据字节 DS1302_WriteByte(dat);​ // 停止信号 DS1302_SCL = 0; DS1302_CE = 1;​ // 延时至少1us for (i = 0; i < 10; i++);}​// 向DS1302读取数据uchar Read_DS1302(uchar addr) { uchar dat; uchar i;​ DS1302_CE = 0; DS1302_SCL = 0;​ // 发送起始信号 DS1302_CE = 1; DS1302_SCL = 1; DS1302_CE = 0;​ // 发送命令字节地址 DS1302_WriteByte(addr | 0x01); // 读取数据字节 dat = DS1302_ReadByte();​ // 停止信号 DS1302_SCL = 0; DS1302_CE = 1;​ // 延时至少1us for (i = 0; i < 10; i++); return dat;}​// 读取DS1302时间并打印到串口void ReadTime() { uchar sec, min, hour, day, month, year; sprintf(time_buffer, "Time: "); sec = Read_DS1302(DS1302_SEC_REG); min = Read_DS1302(DS1302_MIN_REG); hour = Read_DS1302(DS1302_HR_REG); day = Read_DS1302(DS1302_DAY_REG); month = Read_DS1302(DS1302_MONTH_REG); year = Read_DS1302(DS1302_YEAR_REG); sprintf(time_buffer + 6, "%02d:%02d:%02d %02d/%02d/%02d\r\n", hour, min, sec, day, month, year); SendString(time_buffer);}​// 向DS1302写入闹钟时间void SetAlarm(uchar *str) { uint i = 0;​ // 将字符串转换为数字 while (str[i] != '\0') { alarm_buffer[i] = str[i] - '0'; i++; if (i > 19) // 防止溢出 break; }​ // 写入闹钟时间 Write_DS1302(DS1302_CMD_WRITE | 0x81, alarm_buffer[10] << 4 | alarm_buffer[11]); Write_DS1302(DS1302_CMD_WRITE | 0x83, alarm_buffer[8] << 4 | alarm_buffer[9]); Write_DS1302(DS1302_CMD_WRITE | 0x85, alarm_buffer[6] << 4 | alarm_buffer[7]);}​// 从串口接收数据中解析出时间信息void ParseTime() { uchar i, j; uchar temp; for (i = 0; i < 20; i++) { time_buffer[i] = 0; }​ // 接收字符串格式为:hh:mm:ss dd/mm/yy for (i = 0; i < 8; i++) { temp = 0; for (j = 0; j < 2; j++) { temp *= 10; temp += (SBUF - '0'); while (!RI); // 等待接收完成 RI = 0; } time_buffer[i] = temp; if (i == 2 || i == 4) { while (SBUF != ' '); // 跳过空格字符 while (!RI); // 等待接收完成 RI = 0; } } flag = 1; // 标记已经接收到字符串}​// 主函数void main() { InitUart(); InitDS1302(); flag = 0; while (1) { if (flag) { // 接收到时间字符串,设置闹钟和时间 SetAlarm(time_buffer); Write_DS1302(DS1302_CMD_WRITE | 0x80, time_buffer[6] << 4 | time_buffer[7]); Write_DS1302(DS1302_CMD_WRITE | 0x82, time_buffer[3] << 4 | time_buffer[4]); Write_DS1302(DS1302_CMD_WRITE | 0x84, time_buffer[0] << 4 | time_buffer[1]); flag = 0; } ReadTime(); // 读取当前时间并发送到串口 }}​// UART接收中断函数void UartIsr() interrupt 4 { if (RI) { // 接收到数据 ParseTime(); // 解析时间字符串 } RI = 0;}​4.2 LCD1602显示时钟基于STC89C52控制LCD1602显示时间字符串的实现代码。#include <reg52.h>#include <stdio.h>​// 定义Data和Command寄存器选择端口sbit LCD_RS = P2^0; // RS引脚(寄存器选择)sbit LCD_RW = P2^1; // RW引脚(读写选择)sbit LCD_EN = P2^2; // EN引脚(使能)​// 定义数据总线端口#define LCD_DATA P0 ​void DelayMs(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 120; j++);}​void WriteCommand(unsigned char cmd) { LCD_RS = 0; // 选择指令寄存器 LCD_RW = 0; // 写模式 LCD_EN = 0; // 低电平使能 LCD_DATA = cmd; // 发送指令 DelayMs(1); // 延时等待指令写入 LCD_EN = 1; // 高电平使能 DelayMs(1); // 持续一段时间 LCD_EN = 0; // 结束使能}​void WriteData(unsigned char dat) { LCD_RS = 1; // 选择数据寄存器 LCD_RW = 0; // 写模式 LCD_EN = 0; // 低电平使能 LCD_DATA = dat; // 发送数据 DelayMs(1); // 延时等待数据写入 LCD_EN = 1; // 高电平使能 DelayMs(1); // 持续一段时间 LCD_EN = 0; // 结束使能}​void LCDInit() { WriteCommand(0x38); // 设置显示模式为2行、5x8点阵字符 WriteCommand(0x0C); // 显示器开,光标关闭 WriteCommand(0x06); // 光标右移,整屏不移动 WriteCommand(0x01); // 清除显示并设置光标回到初始位置}​void LCDDisplayTime(char* time) { int i; WriteCommand(0x80); // 设置光标位置为第一行的起始位置​ for (i = 0; i < 16; i++) { WriteData(time[i]); // 在第一行显示时间字符串 }​ WriteCommand(0xC0); // 设置光标位置为第二行的起始位置​ for (i = 0; i < 16; i++) { WriteData(time[16 + i]); // 在第二行显示时间字符串 }}​void main() { char time_buffer[32] = "Current Time: 00:00:00"; // 时间字符串 unsigned char sec = 0, min = 0, hour = 0; // 当前时间变量​ LCDInit(); // 初始化LCD显示器​ while (1) { // 更新时间变量 sec++; if (sec >= 60) { sec = 0; min++; if (min >= 60) { min = 0; hour++; if (hour >= 24) { hour = 0; } } }​ // 格式化时间字符串 sprintf(time_buffer + 14, "%02d:%02d:%02d", hour, min, sec);​ // 显示时间字符串 LCDDisplayTime(time_buffer);​ DelayMs(1000); // 延时1秒 }}​代码使用LCD_RS、LCD_RW和LCD_EN分别表示LCD1602的RS、RW和EN引脚。数据总线通过LCD_DATA定义,连接到P0端口。先初始化LCD显示器,在一个无限循环中更新时间变量并格式化时间字符串,最后在LCD上显示时间字符串。
  • 智能数据洞察(DataArts Insight)产品功能
    数据接入支持多种数据源接入能力,包括DWS、ClickHouse、API、本地文件作为现代商业智能分析的数据源。支持公网连接、支持数据源的连通性测试。数据加工支持在工作空间新建数据集,通过数据源导入、图形化和SQL形式创建数据集。数据集支持度量和维度的设置,支持新建分组维度,层次维度和计算字段,支持数据集字段隐藏。仪表板提供仪表板的搭建能力,支持通过拖拽的方式快速布局可视化组件,通过可视化组件的数据配置、属性配置、样式配置,生成具备交互式分析能力的数据报表。数据大屏通过场景模板来创建数据大屏,同时对已有数据大屏实现编辑、复制、删除、预览、发布等应用操作。支持2D可视化大屏制作,支持可交互仿真推演的三维数据可视化大屏,包括静态世界基座,可视仿真推演以及风格化可视计算。交互式分析根据业务需求选择指标、维度和维度值,可以对任意数据行列进行合并与拆分,支持在表格区域内直接进行多种计算、排序,快速进行报表制作与交互分析。嵌入式分析提供报表页面、卡片的嵌入式分析能力。支持报表或独立卡片嵌入目标系统,一键式生成嵌入式代码。智能分析助手提供零门槛自助分析,实现自然语言交互的BI租住分析和自助数据获取,轻松获取和分析数据,降低专业分析师的工作负荷。智能洞察支持内置或集成行业算法(自动异常检测、关键驱动因素发现、根因分析、what-if对照分析、趋势预测、决策行动),深入挖掘数据价值,自动分析洞察见解和专题数据故事生成。BI内存引擎提供内存计算和多维分析加上能力,10亿数据秒级响应。企业级数据管理和安全DataArts Insight与华为云库、仓、湖、治理等数据平台产品原生集成,降低平台集成和运维难度,提供端到端全链路数据安全保护能力。
  • [技术干货] 基于单片机设计的智能风扇(红外线无线控制开关调速定时)
    一、项目介绍在炎热的夏季,风扇成为人们室内生活中必不可少的电器产品。然而,传统的风扇控制方式存在一些不便之处,比如需要手动操作开关、无法远程控制和调速,以及缺乏定时功能等。为了解决这些问题,设计了一款基于单片机的智能风扇,利用红外线无线控制开关、调速和定时功能,使用户能够更加便捷和舒适地使用风扇。主控芯片采用STC89C52,这是一款功能强大且性能稳定的单片机,具备足够的计算和控制能力。通过支持红外线NEC协议,该单片机能够接收遥控器发送的控制指令,并根据指令完成对风扇的开关、调速和定时切换。为了实现风扇的控制,采用了L298N驱动模块来驱动小型的直流电机,模拟真实风扇的工作原理。L298N驱动模块具有高电流和高电压的特点,可以有效地控制电机的转速和方向。通过单片机的IO口与L298N驱动模块进行连接,可以精确控制电机的转速,并且支持正转、反转和停止等操作。这个智能风扇项目具备多项实用的功能。利用红外线遥控器,用户可以随时随地对风扇进行开关操作,无需手动接触开关,提高了使用的便捷性。通过调速功能,用户可以根据需要调整风扇的转速,以获得理想的风速效果,增加了舒适感。还有定时功能,用户可以设置定时关闭风扇,避免长时间运行造成不必要的能耗,同时也提供了更多的节能选项。此外,基于单片机设计的智能风扇还具备一定的智能化潜力。通过进一步的开发和改进,可以引入温湿度传感器,实现自动调节风速的功能,根据环境温湿度情况自动调整风扇转速,为用户带来更加智能、个性化的使用体验。智能风扇的设计背景源于对人们日常生活的需求和对智能化家居的追求。通过采用单片机控制和红外线无线控制技术,结合驱动模块的应用,成功地打造了一款功能强大、操作便捷的智能风扇,让人们在炎热夏季享受到更为舒适和智能化的生活体验。二、整体项目设计思路2.1 硬件设计思路(1)主控芯片选择:选择了STC89C52作为主控芯片。STC89C52是一款功能强大且性能稳定的单片机,具备足够的计算和控制能力,适合用于风扇控制。(2)红外接收模块选择:为了支持红外线无线控制功能,选用了适用于NEC协议的红外接收模块。该模块能够接收遥控器发送的红外信号,并将其转换成电信号供主控芯片进行处理。(3)驱动模块选择:为了驱动小型直流电机,采用了L298N驱动模块。L298N驱动模块具有高电流和高电压的特点,可以有效地控制电机的转速和方向。(4)其他器件选择:除了上述关键器件外,还需要选择适当的传感器、按键开关、显示屏等组件,根据需求进行搭配和连接。2.2 软件设计思路(1)红外协议解码:首先,需要编写程序对红外接收模块接收到的红外信号进行解码,识别出NEC协议中的控制指令。这些指令包括开关、调速和定时控制等功能。(2)控制逻辑设计:根据接收到的控制指令,编写程序实现相应的风扇控制逻辑。例如,根据接收到的开关指令控制电机的启停,根据调速指令控制电机转速的变化,根据定时指令设置风扇的定时关闭等。(3)与L298N驱动模块的通信:将主控芯片的IO口与L298N驱动模块进行连接,并编写程序实现与其的通信。通过设置相应的引脚状态,控制电机的正转、反转和停止等操作。(4)用户界面设计:如果有显示屏的需求,可以设计一个简单的用户界面,显示当前风扇状态、转速、定时设置等信息,提供用户操作的反馈和展示。(5)其他功能增强:根据需求可以进一步增加其他功能,比如温湿度传感器的接入,实现智能调节风速的功能。三、硬件连线说明下表是智能风扇模块和单片机的连接关系:模块引脚连接到单片机的IO口红外接收模块P1.0L298N驱动模块ENAP2.0ENBP2.1IN1P2.2IN2P2.3小型直流电机正转P2.4反转P2.5停止P2.6四、项目代码设计#include <reg51.h>​sbit IN1 = P2^2;sbit IN2 = P2^3;sbit ENA = P2^0;sbit ENB = P2^1;sbit IR_IN = P1^0;​void delay(unsigned int t) { unsigned int i, j; for (i = 0; i < t; i++) for (j = 0; j < 1000; j++);}​void motorControl(int speed, int direction) { switch (direction) { case 1: // 正转 IN1 = 1; IN2 = 0; break; case -1: // 反转 IN1 = 0; IN2 = 1; break; case 0: // 停止 IN1 = 0; IN2 = 0; break; } // 调整PWM占空比控制速度 if (speed < 0) speed = 0; if (speed > 255) speed = 255; ENA = 1; ENB = 1; delay(speed); ENA = 0; ENB = 0;}​sbit IR_IN = P1^0;​void delay(unsigned int t) { unsigned int i, j; for (i = 0; i < t; i++) for (j = 0; j < 1000; j++);}​void irInit() { IT0 = 1; // 设置外部中断0下降沿触发 EX0 = 1; // 使能外部中断0 EA = 1; // 允许中断}​void irINT0() interrupt 0 { unsigned int i; unsigned char repeatFlag = 0; unsigned long codeValue = 0; delay(16); // 等待16ms,进入起始位 if (IR_IN == 0) { delay(8); // 等待8ms,确认起始位 if (IR_IN == 1) { for (i = 0; i < 32; i++) { while (IR_IN == 1); // 等待低电平的结束位 delay(3); // 等待3ms,读取数据位 if (IR_IN == 0) { codeValue <<= 1; } else { codeValue = (codeValue << 1) | 0x0001; } while (IR_IN == 0); // 等待高电平的开始位或重复码标志 } repeatFlag = codeValue & 0xFF; codeValue >>= 8; // 在这里根据codeValue的值进行控制操作 // 比如判断codeValue的值对应的指令是开启风扇,则执行相应代码 } } EX0 = 1; // 再次使能外部中断0}​void main() { int speed = 0; // 初始速度为0 int direction = 0; // 初始方向为停止 irInit(); while (1) { if (IR_IN == 0) { // 接收到红外信号 // 解析红外信号,根据NEC协议得到控制指令 if (控制指令为开启风扇) { direction = 1; // 设置为正转 } else if (控制指令为关闭风扇) { direction = 0; // 设置为停止 } else if (控制指令为调整风速) { speed = 风速值; // 设置风速值 } else if (控制指令为定时切换) { // 执行定时操作,你可以使用定时器/计数器来实现 } // 执行风扇控制 motorControl(speed, direction); } }}​五、总结通过使用STC89C52主控芯片和L298N驱动模块,成功地设计了一款智能风扇系统。该系统支持红外线NEC协议接收遥控器发送的控制指令,实现了风扇的开关、调速和定时切换功能。在项目实施过程中,编写了红外信号解码函数,将接收到的指令转换为对应的控制操作。通过控制L298N驱动模块的输入引脚,实现风扇电机的正转、反转和停止等控制操作。利用PWM技术调整占空比,实现了风扇的调速功能。此外,通过定时器/计数器实现了风扇的定时切换功能,可以根据用户需求自动开启或关闭风扇。
  • [技术干货] 51单片机+SIM800C(GSM模块)实现短信发送功能
    一、前言本项目利用51单片机和SIM800C GSM模块实现短信发送功能。短信作为一种广泛应用的通信方式,在许多领域具有重要的作用,如物联网、安防系统、远程监控等。通过将51单片机与SIM800C GSM模块相结合,可以实现在各种应用场景下的短信通信功能。本项目的核心组件是51单片机,是一种低成本、低功耗的单片机,广泛应用于嵌入式系统开发。利用51单片机的串口功能来控制SIM800C GSM模块的通信。SIM800C是一款功能强大的GSM模块,支持GSM/GPRS通信,具有发送和接收短信的能力。在本项目中,搭建51单片机和SIM800C GSM模块的硬件连接。使用C语言编写程序,在51单片机上实现与SIM800C的通信控制。通过串口通信向SIM800C发送AT指令,实现短信的发送功能。为了实现短信发送功能,需要熟悉SIM800C的AT指令集,了解如何设置短信参数、编写短信内容并发送。还需要处理SIM800C返回的响应,以确保短信发送的成功与否。二、SIM800C硬件介绍SIM800C是一款功能强大、灵活可靠的GSM/GPRS模块,广泛应用于各种通信和控制场景,尤其在物联网应用中能发挥重要作用。通过合理使用SIM800C的AT指令,可以轻松实现短信发送和收取等功能。2.1 SIM800C的特点【1】支持多种通信方式:SIM800C支持GSM、GPRS、SMS、MMS、TCP/IP等通信方式,可以实现语音通话、短信收发、数据传输等功能。【2】大量接口:SIM800C提供了UART、SPI和I2C等接口,方便与其他设备进行通信和控制。【3】低功耗设计:SIM800C具有低功耗模式,在待机时能够极大地减少电力消耗。【4】小巧的尺寸:SIM800C模块体积小巧,便于嵌入各种设备中。【5】丰富的工作温度范围:SIM800C适用于广泛的工作温度范围,可在恶劣的环境条件下正常工作。2.2 使用场景SIM800C的使用场景广泛,主要包括以下几个方面:【1】物联网应用:SIM800C可以通过GPRS进行数据传输,用于物联网设备的远程监控、远程控制、数据采集和传输等。【2】安防系统:SIM800C可以用于报警系统,通过短信或语音通知用户有关安全事件的信息。【3】远程控制应用:通过SIM800C模块,可以实现远程控制设备,比如远程开关、门禁系统等。【4】移动支付终端:SIM800C可以与移动支付系统集成,实现移动支付终端的功能。2.3 AT指令介绍SIM800C使用AT指令进行通信和控制。下面是一些常用的与短信相关的AT指令:【1】AT+CMGF:设置短信模式,用于选择短信的格式。例如,AT+CMGF=1表示以文本模式发送和接收短信。【2】AT+CMGS:发送短信。需要指定接收方的电话号码,并在输入结束后按Ctrl+Z(ASCII码为0x1A)表示短信内容输入完成。例如,AT+CMGS="+123456789"表示发送短信给号码+123456789。【3】AT+CMGR:读取短信。可以读取已存储在模块中的已接收短信,返回包括发送方号码和短信内容在内的信息。【4】AT+CMGD:删除短信。用于删除指定索引处的短信。例如,AT+CMGD=1表示删除索引为1的短信。【5】AT+CNMI:设置新短消息指示。可以配置模块在接收到新短信时给出通知,以便及时处理。三、代码实现3.1 STC89C52硬件配置【1】串口:STM89C52共有两个串口,分别是UART0和UART1。可以用于与其他设备进行异步串行通信。【2】定时器:STM89C52共有三个定时器,分别是Timer0、Timer1和Timer2。可以用于产生定时中断、计时等功能。【3】GPIO:STM89C52具有32个I/O口,每个I/O口可以配置为输入或输出。其中,P0口(Port 0)和P2口(Port 2)上的引脚可以作为UART0的GPIO引脚使用,而P3口(Port 3)上的引脚可以作为UART1的GPIO引脚使用。串口对应的GPIO口编号如下:【A】UART0:TXD:对应P0.0口RXD:对应P0.1口【B】UART1:TXD:对应P3.1口RXD:对应P3.0口在STM89C52中,UART0的TXD引脚对应P0.0口,RXD引脚对应P0.1口;UART1的TXD引脚对应P3.1口,RXD引脚对应P3.0口。3.2 短信发送代码实现#include <reg52.h>​// 定义SIM800C的串口引脚sbit SIM_RX = P3^0; // SIM800C的串口接收引脚sbit SIM_TX = P3^1; // SIM800C的串口发送引脚​// 定义波特率常量#define BAUDRATE 9600​// 定义发送函数void sendATCommand(char* command) { // 发送AT指令 for (int i = 0; command[i] != '\0'; i++) { SBUF = command[i]; while (TI == 0); // 等待发送完成 TI = 0; // 清除发送完成标志 }}​// 主函数void main() { // 初始化串口 TMOD = 0x20; // 设置定时器1为模式2 TH1 = 256 - BAUDRATE / 9600; // 设置波特率 TL1 = TH1; TR1 = 1; // 启动定时器1 SCON = 0x50; // 设置串口为模式1,允许接收​ // 发送AT指令初始化SIM800C模块 sendATCommand("AT\r\n"); // 发送AT指令,检测模块是否正常 sendATCommand("AT+CMGF=1\r\n"); // 设置短信模式为文本模式 sendATCommand("AT+CNMI=1,2,0,0,0\r\n"); // 设置接收新短信时的提示方式​ // 发送短信 sendATCommand("AT+CMGS=\"+1234567890\"\r\n"); // 设置短信接收号码 sendATCommand("Hello, this is a test message.\x1A"); // 发送短信内容,以Ctrl+Z作为结束符​ while (1);}3.3 短信发送、电话拨打功能-封装子函数#include <reg51.h>​// 定义串口1的引脚连接sbit UART1_TX = P3^1;sbit UART1_RX = P3^0;​// 初始化串口1void UART1_Init() { TMOD |= 0x20; // 设置定时器1为模式2(8位自动重载) SCON = 0x50; // 设置串口1为工作方式1,并允许接收 TH1 = 0xFD; // 设置波特率9600,对应12MHz晶振 TL1 = 0xFD; TR1 = 1; // 启动定时器1}​// 发送一个字符到串口1void UART1_SendChar(unsigned char c) { SBUF = c; while(!TI); // 等待发送完成 TI = 0; // 清除发送标志}​// 发送字符串到串口1void UART1_SendString(const unsigned char *str) { while (*str) { UART1_SendChar(*str++); }}​// 发送AT指令到SIM800C模块void SIM800C_SendATCommand(const unsigned char *atCmd) { UART1_SendString(atCmd); UART1_SendChar('\r'); UART1_SendChar('\n');}​// 发送短信void SIM800C_SendSMS(const unsigned char *phoneNumber, const unsigned char *message) { SIM800C_SendATCommand("AT+CMGF=1"); // 设置为文本模式 // 等待回复 // ... SIM800C_SendATCommand("AT+CMGS=\""); UART1_SendString(phoneNumber); // 接收方手机号 UART1_SendChar('"'); UART1_SendChar('\r'); UART1_SendString(message); // 短信内容 UART1_SendChar(0x1A); // 发送Ctrl+Z结束短信}​// 拨打电话void SIM800C_MakeCall(const unsigned char *phoneNumber) { SIM800C_SendATCommand("ATD"); // 拨号命令 UART1_SendString(phoneNumber); // 目标手机号 UART1_SendChar(';'); // 发送分号以拨号}​void main() { UART1_Init(); // 初始化串口1​ // 等待SIM800C模块初始化完成 // ...​ // 发送短信 SIM800C_SendSMS("手机号", "短信内容");​ // 拨打电话 // SIM800C_MakeCall("目标手机号");​ while(1);}代码中需要将"手机号"和"目标手机号"填充为实际的电话号码。
  • [技术干货] 通过51单片机控制SG90舵机按角度正反转转动
    一、前言本文介绍如何通过51单片机控制SG90舵机实现角度的正反转转动。SG90舵机是一种常用的微型舵机,具有体积小、重量轻、结构简单等特点,被广泛应用于机器人、遥控模型和各种自动控制系统中。使用51单片机(STC89C52)作为控制器,利用其强大的IO口和定时器功能来实现对SG90舵机的控制。通过编程控制,可以精确地控制舵机按指定的角度进行正转或反转运动。舵机的控制是通过脉冲宽度调制(PWM)来实现的。在控制舵机时,需要向舵机发送一系列的脉冲信号,脉冲的宽度决定了舵机的角度位置。通常情况下,SG90舵机的控制脉冲周期为20毫秒,脉冲宽度在0.5毫秒到2.5毫秒之间,对应的角度范围为0度到180度。为了实现舵机的正反转转动,需要控制脉冲的宽度在不同的范围内,以达到不同的角度位置。通过调整脉冲的宽度和周期,我们可以控制舵机按照我们的要求进行旋转。下面将介绍如何通过51单片机的IO口和定时器来生成适用于SG90舵机的PWM信号。编写相应的程序,通过调整脉冲宽度来实现舵机的正反转转动,并提供示例代码。二、SG90电机介绍SG90电机是一种微型舵机,常用于模型、机器人和其他小型机械装置中。1. 工作原理: SG90电机基于直流电机的原理,通过PWM(脉宽调制)信号控制舵机转动角度。它由一个电机、减速齿轮组和一个位置反馈电路组成。该反馈电路使用了一个电位器来检测舵机的当前位置并将其反馈给控制电路。2. 特点:尺寸小巧:SG90电机非常小巧,体积轻盈,适合于空间有限的应用。转动角度范围广:通常情况下,SG90电机可以转动约180度左右,但具体转动范围可以通过控制信号调整。高精度:SG90电机具有较高的转动精度和稳定性,适用于需要精确控制的应用。低功耗:SG90电机功耗较低,可以在低电压下工作。相对经济:相比大型舵机或步进电机,SG90电机价格相对较低,适合在预算有限的项目中使用。3. 控制方式: 控制SG90电机需要提供PWM信号。以下是控制SG90电机的基本步骤:将SG90电机的VCC引脚连接到正电源(通常为5V),将GND引脚连接到地。将信号线(例如,控制舵机角度的引脚)连接到微控制器或其他控制设备的数字输出引脚。在控制设备上设置指定的PWM输出引脚,并使用相应的编程语言或库发送PWM信号。PWM的工作周期通常为20ms,并且脉宽的范围可以在0.5ms到2.5ms之间调整。根据所发送的PWM信号,SG90电机会转动到相应的角度位置。一般来说,0.5ms的脉宽对应最左端角度,2.5ms的脉宽对应最右端角度,1.5ms的脉宽对应中间位置。具体的脉宽范围和对应的角度可以根据电机型号和要求进行调整。SG90电机的额定工作电压为4.8V-6V,超过这个范围可能会损坏电机。舵机在运行时会产生一定的电流峰值,在使用时应确保电源能够提供足够的电流。三、实现代码3.1 正反转实现-模拟延时以下是通过51单片机控制SG90舵机按角度正反转转动的实现代码,封装子函数调用:#include <reg51.h>​// 定义IO口连接舵机的引脚sbit servoPin = P1^0;​// 延时函数void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) { for(j = 0; j < 1000; j++); }}​// 控制舵机按指定角度进行正转void rotateClockwise(unsigned int angle) { unsigned int pulseWidth = 500 + angle * 11.11; unsigned int i; for(i = 0; i < 50; i++) { servoPin = 1; // 输出高电平 delay(pulseWidth); servoPin = 0; // 输出低电平 delay(20000 - pulseWidth); }}​// 控制舵机按指定角度进行反转void rotateCounterclockwise(unsigned int angle) { unsigned int pulseWidth = 2500 - angle * 11.11; unsigned int i; for(i = 0; i < 50; i++) { servoPin = 1; // 输出高电平 delay(pulseWidth); servoPin = 0; // 输出低电平 delay(20000 - pulseWidth); }}​void main() { while(1) { // 正转90度 rotateClockwise(90); delay(2000); // 停留2秒​ // 反转90度 rotateCounterclockwise(90); delay(2000); // 停留2秒 }}3.2 正反转角度控制-PWM控制下面是使用STC89C52的定时器0和GPIO口来模拟产生PWM信号的实现代码:#include <reg52.h>​#define FREQ_OSC 11059200UL // 单片机工作频率#define PWM_FREQ 50 // PWM信号频率#define PWM_RESOLUTION 100 // PWM信号分辨率​sbit Servo = P1^0; // SG90舵机控制引脚​unsigned int pwmWidth = 0; // PWM脉宽​// 定时器0初始化函数void Timer0Init() { EA = 0; // 关闭总中断 TMOD &= 0xF0; // 清除T0控制位 TMOD |= 0x01; // 设置T0为工作方式1(16位定时器) TH0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) / 256; // 计算并设置初始计数值高8位 TL0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) % 256; // 计算并设置初始计数值低8位 TR0 = 1; // 启动定时器0 ET0 = 1; // 允许定时器0中断 EA = 1; // 开启总中断}​// 定时器0中断服务函数void Timer0Interrupt() interrupt 1 { if (pwmWidth > PWM_RESOLUTION) { Servo = 0; // 舵机复位 } else { Servo = 1; // 舵机置位 } TH0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) / 256; // 重新设置计数值高8位 TL0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) % 256; // 重新设置计数值低8位 pwmWidth++; // 每次中断增加PWM脉宽}​// 主函数void main() { Timer0Init(); // 初始化定时器0​ while (1) { if (pwmWidth > PWM_RESOLUTION) { pwmWidth = 0; } }}代码中,使用P1^0引脚作为SG90舵机的控制引脚,并通过定时器0来产生PWM信号。在Timer0Init函数中,设置定时器0为16位定时器工作方式1,计算并设置初始计数值,启动定时器0,并允许定时器0中断。在Timer0Interrupt函数中,每次定时器0中断时调整舵机控制引脚的电平状态,并更新定时器0的计数值。在主函数中,循环检测PWM脉宽是否达到设定的分辨率,如果超过则重新从0开始计数。
  • [技术干货] 通过51单片机控制28byj48步进电机按角度正反转旋转
    一、前言本项目基于STC89C52单片机,通过控制28BYJ-48步进电机实现按角度正反转旋转的功能。28BYJ-48步进电机是一种常用的电机,精准定位和高扭矩输出,适用于许多小型的自动化系统和机械装置。在这个项目中,使用STC89C52单片机作为控制器,这是一款强大而常用的8位单片机芯片,具有丰富的外设和强大的计算能力。通过编写适当的程序,可以通过单片机的IO口来控制步进电机的运动。28BYJ-48步进电机是一种低成本、低功耗的步进电机,拥有精确的定位能力和较高的转矩输出。将使用单片机与步进电机之间的接口信号来驱动电机旋转,并通过控制电流脉冲的频率和顺序来控制电机前进或后退以及旋转的角度。本项目的目标是实现根据用户输入的角度值,控制28BYJ-48步进电机按指定角度进行正反转旋转。通过灵活调整步进电机的控制信号,可以实现不同角度范围内的精确旋转。在接下来的内容将介绍所需的硬件和软件资源,包括STC89C52单片机的基本特性、28BYJ-48步进电机的工作原理,以及编写控制程序的关键步骤。二、设计流程【1】硬件准备:51单片机开发板:选择STC89C52单片机开发板。28BYJ-48步进电机:一个28BYJ-48步进电机+ULN2003驱动板。驱动电路:使用ULN2003芯片来驱动步进电机。连接线和电源:准备连接线和电源供电。【2】连接电路:将51单片机与驱动电路和步进电机连接起来。【3】编写程序:使用keil集成开发环境(IDE)编写51单片机的控制程序。初始化引脚和端口设置,配置控制步进电机所需的引脚。编写函数来控制步进电机的正反转旋转。编写函数来控制步进电机按照指定的角度进行旋转。【4】控制步进电机旋转:在主程序中,调用适当的函数来控制步进电机的旋转。使用按键输入设备来触发步进电机的旋转。控制旋转的角度、速度和方向。【5】调试和测试:通过编译程序,并将生成的可执行文件下载到51单片机开发板中。三、代码实现3.1 电机正反转控制下面是通过STC89C52单片机控制28BYJ-48步进电机实现正转和反转的实现代码:#include <reg52.h>#include <intrins.h>​#define motorPort P1 // 步进电机的控制引脚连接到P1口#define clockwise 0 // 顺时针方向#define counterclockwise 1 // 逆时针方向​// 函数声明void delay(unsigned int time);void motorRotate(unsigned char direction, unsigned int steps);​void main(){ while (1) { // 正转,执行一定的步数 (这里为512步,可根据需要修改) motorRotate(clockwise, 512); delay(1000); // 延时1秒​ // 反转,执行一定的步数 motorRotate(counterclockwise, 256); delay(1000); // 延时1秒 }}​// 延时函数void delay(unsigned int time){ unsigned int i, j; for (i = time; i > 0; i--) { for (j = 110; j > 0; j--); // 指令周期延时 }}​// 控制步进电机旋转void motorRotate(unsigned char direction, unsigned int steps){ unsigned int i; unsigned char motorSequence[8] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09}; // 步进电机的控制序列​ for (i = 0; i < steps; i++) { if (direction == clockwise) { motorPort = motorSequence[i % 8]; } else if (direction == counterclockwise) { motorPort = motorSequence[7 - (i % 8)]; }​ delay(2); // 每步之间的延时,可根据需要调整 }​ motorPort = 0x00; // 停止电机}代码里使用 STC89C52 单片机的 P1 口连接到28BYJ-48步进电机的控制引脚。在 main 函数中,通过循环实现了正转和反转的功能。motorRotate 函数用于控制步进电机的旋转方向和步数,其中 clockwise 和 counterclockwise 分别代表顺时针和逆时针方向。3.2 角度旋转下面代码使用STC89C52单片机控制28BYJ-48步进电机按指定的角度进行正转和反转,封装子函数进行调用。#include <reg52.h>​// 定义28BYJ-48步进电机的相序unsigned char stepSequence[8] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09};​// 定义步进电机当前位置和角度unsigned char currentPosition = 0;unsigned int currentAngle = 0;​// 延时函数void delay(unsigned int time) { unsigned int i, j; for (i = 0; i < time; i++) { for (j = 0; j < 120; j++); }}​// 步进电机正转函数void stepForward(unsigned int angle) { unsigned int steps = angle / 5; // 每步转动角度为5度 unsigned int i; for (i = 0; i < steps; i++) { currentPosition++; if (currentPosition >= 8) { currentPosition = 0; } P1 = stepSequence[currentPosition]; delay(10); // 控制步进电机转速,可调整延时时间 } currentAngle += angle;}​// 步进电机反转函数void stepBackward(unsigned int angle) { unsigned int steps = angle / 5; // 每步转动角度为5度 unsigned int i; for (i = 0; i < steps; i++) { if (currentPosition == 0) { currentPosition = 8; } currentPosition--; P1 = stepSequence[currentPosition]; delay(10); // 控制步进电机转速,可调整延时时间 } currentAngle -= angle;}​// 主函数void main() { while (1) { // 正转180度 stepForward(180); delay(1000); // 停顿1秒钟 // 反转90度 stepBackward(90); delay(1000); // 停顿1秒钟 }}代码使用STC89C52单片机的P1口作为输出口,通过控制P1口输出的电平来控制步进电机的旋转。步进电机的相序存储在stepSequence数组中,每个元素对应一个相位。stepForward函数用于实现步进电机的正转,stepBackward函数用于实现步进电机的反转。delay函数用于控制步进电机的转速,可以根据需要调整延时时间。在主函数中,演示了步进电机的正转180度和反转90度的操作。3.3 按键控制电机有2个按键,接在P2口3上面的,按下是低电平。下面代码加入2个按键,实现了2个按键的功能。#include <reg52.h>​#define motorPort P1 // 步进电机的控制引脚连接到P1口#define clockwise 0 // 顺时针方向#define counterclockwise 1 // 逆时针方向​sbit startBtn = P2^0; // 启动按钮连接到P2.0口sbit stopBtn = P2^1; // 停止按钮连接到P2.1口sbit cwBtn = P2^2; // 顺时针按钮连接到P2.2口sbit ccwBtn = P2^3; // 逆时针按钮连接到P2.3口​unsigned char motorSequence[8] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09}; // 步进电机的控制序列bit motorRunning = 0; // 步进电机是否正在运行unsigned int targetAngle = 0; // 目标转动角度,初始为0bit clockwiseDirection = 1; // 电机默认启动方向为顺时针​// 函数声明void delay(unsigned int time);void motorRotate(unsigned char direction, unsigned int steps);​void main(){ while (1) { if (startBtn == 0) // 如果按下了启动按钮 { while (startBtn == 0); // 等待按钮释放​ if (!motorRunning) { motorRunning = 1; motorRotate(clockwiseDirection, targetAngle); // 启动电机 } }​ if (stopBtn == 0) // 如果按下了停止按钮 { while (stopBtn == 0); // 等待按钮释放​ if (motorRunning) { motorRunning = 0; motorPort = 0x00; // 停止电机 } }​ if (cwBtn == 0) // 如果按下了顺时针按钮 { while (cwBtn == 0); // 等待按钮释放 clockwiseDirection = 1; // 设置电机启动方向为顺时针 }​ if (ccwBtn == 0) // 如果按下了逆时针按钮 { while (ccwBtn == 0); // 等待按钮释放 clockwiseDirection = 0; // 设置电机启动方向为逆时针 } }}​// 延时函数void delay(unsigned int time){ unsigned int i, j; for (i = time; i > 0; i--) { for (j = 110; j > 0; j--); // 指令周期延时 }}​// 控制步进电机旋转void motorRotate(unsigned char direction, unsigned int steps){ unsigned int i;​ for (i = 0; i < steps; i++) { if (!motorRunning) break;​ if (direction == clockwise) { motorPort = motorSequence[i % 8]; } else if (direction == counterclockwise) { motorPort = motorSequence[7 - (i % 8)]; }​ delay(2); // 每步之间的延时,可根据需要调整 }​ motorPort = 0x00; // 停止电机}在以上代码中,增加了 cwBtn 和 ccwBtn 两个按键引脚,并定义为 P2^2 和 P2^3。按下顺时针按钮时,将 clockwiseDirection 设置为 1,表示启动方向为顺时针;按下逆时针按钮时,将 clockwiseDirection 设置为 0,表示启动方向为逆时针。
  • [技术干货] 基于单片机设计的自动门控制系统
    一、项目介绍随着科技的不断发展,自动门成为公共场所、商业建筑和住宅社区等地的常见设施。自动门的出现使得进出门的操作更加便捷,提高了人们的生活质量和工作效率。为了实现自动门的开关控制,本项目基于单片机设计了一套自动门控制系统。本项目的主控芯片选择了STC89C52,这是一款性能稳定且广泛应用于嵌入式系统的单片机。具有较高的计算能力和丰富的外设接口,非常适合用于本项目中的自动门控制。自动门的开关控制通过红外热释电传感器实现。红外热释电传感器是一种能够检测人体红外辐射的传感器,当有人靠近时,传感器会感知到人体的存在。本项目中,红外热释电传感器被安装在自动门的控制区域,用于检测人体的接近。为了实现自动门的开关动作,本项目采用了SG90舵机进行控制。SG90舵机是一种小型直流电机,具有较高的转动精度和响应速度。通过模拟控制方式,根据控制信号的脉冲宽度来控制门的开关状态。在系统运行时,红外热释电传感器不断检测周围的人体活动。当传感器检测到人体接近时,会向主控芯片发送信号。主控芯片接收到信号后,会控制SG90舵机执行开门动作,使门自动打开。当人体离开控制区域时,传感器再次发送信号,主控芯片控制舵机执行关门动作,实现自动门的关闭。自动门控制系统具有以下优点:(1)通过红外热释电传感器实现人体接近检测,无需人工干预,使门的开关更加智能化。(2)采用SG90舵机进行控制,具有较高的转动精度和响应速度,门的开关动作更加准确和迅速。(3)通过使用STC89C52主控芯片,系统具有良好的扩展性和可靠性,可以方便地进行功能扩展和故障排除。自动门控制系统可以广泛应用于各种场所,如商场、酒店、医院、办公楼、住宅小区等,为人们提供便捷、安全的出入门体验,提高生活和工作的效率。二、设计思路硬件选型:(1)主控芯片:STC89C52是一款常用的8位单片机,具有丰富的外设资源和较大的存储空间,适合用作自动门控制系统的主控芯片。(2)红外热释电传感器:红外热释电传感器可以检测到人体的红外辐射,用于感知人体接近门的情况。常用的红外热释电传感器模块包括HC-SR501等。(3)舵机:SG90舵机是一种小型伺服舵机,适合用于控制门的开关动作。可以按照指定的角度精确控制转动。软件设计思路:(1)引脚连接:将红外热释电传感器的输出引脚连接到STC89C52的一个GPIO口,将舵机的控制引脚连接到另一个GPIO口。(2)初始化设置:在程序开始时,初始化GPIO口的方向和状态设置。(3)检测人体接近:通过读取红外热释电传感器的输出状态,判断是否有人体接近门。如果有人体接近,则执行下一步开门操作;否则执行关闭门操作。(4)开门动作:控制舵机旋转至开门角度,使门打开。(5)关闭门动作:控制舵机旋转至关闭门角度,使门关闭。(6)延时处理:为了避免舵机转动过快或过慢,可以增加适当的延时操作。(7)循环检测:通过循环结构,不断检测人体接近状态,实现自动门的开关控制。三、核心代码3.1 基础框架#include <reg52.h>​sbit infraredSensor = P1^0; // 红外热释电传感器连接的引脚sbit servoMotor = P2^0; // SG90舵机连接的引脚​void delay(unsigned int ms) { unsigned int i, j; for (i = ms; i > 0; i--) for (j = 112; j > 0; j--);}​void servoRotate(unsigned int angle) { unsigned int i; for (i = 0; i < angle; i++) { servoMotor = 1; // 产生脉冲信号 delay(1); // 控制脉冲宽度,控制舵机转动角度 servoMotor = 0; delay(19); }}​void main() { while (1) { if (infraredSensor == 1) { // 检测到人体接近 servoRotate(90); // 打开门,舵机转动90度 delay(2000); // 延时2秒,保持门开启状态 servoRotate(0); // 关闭门,舵机转动至初始位置 } }}代码框架中,使用了reg52.h头文件来定义了单片机的寄存器和引脚。红外热释电传感器连接到P1口的第0位引脚,SG90舵机连接到P2口的第0位引脚。主函数中使用了一个无限循环,不断检测红外热释电传感器的状态。当检测到有人接近时,调用servoRotate函数控制舵机打开门(转动角度为90度),然后延时2秒,保持门开启状态。最后,再次调用servoRotate函数将舵机转动至初始位置,关闭门。3.2 优化版增加防夹功能,预防小孩子、小动物 误开门设计。要增加防夹功能以防止小孩子、小动物误开门,可以通过阻挡传感器来实现。当前的改进方案用于检测门是否被阻挡,如果有阻挡则停止或反向门的运动。#include <reg52.h>​sbit infraredSensor = P1^0; // 红外热释电传感器连接的引脚sbit obstructionSensor = P1^1; // 阻挡传感器连接的引脚sbit servoMotor = P2^0; // SG90舵机连接的引脚​void delay(unsigned int ms) { unsigned int i, j; for (i = ms; i > 0; i--) for (j = 112; j > 0; j--);}​void servoRotate(unsigned int angle) { unsigned int i; for (i = 0; i < angle; i++) { servoMotor = 1; // 产生脉冲信号 delay(1); // 控制脉冲宽度,控制舵机转动角度 servoMotor = 0; delay(19); }}​void main() { while (1) { if (infraredSensor == 1) { // 检测到人体接近 if (obstructionSensor == 0) { // 检测到门被阻挡 // 停止或反向门的运动 // 可以在此处添加相应的代码来停止或反向门的运动 // 例如,可以调用servoRotate(0)来立即关闭门 } else { servoRotate(90); // 打开门,舵机转动90度 delay(2000); // 延时2秒,保持门开启状态 servoRotate(0); // 关闭门,舵机转动至初始位置 } } }}在代码改进中,添加了一个名为obstructionSensor的阻挡传感器,连接到P1口的第1位引脚。该传感器用于检测门是否被阻挡。在检测到人体接近的同时,检测阻挡传感器的状态。如果阻挡传感器检测到门被阻挡,可以根据需求添加相应的代码来停止或反向门的运动,例如调用servoRotate(0)来立即关闭门。
  • [专题汇总] 10月嵌入式项目开发专题总汇
    一、前言随着科技的飞速发展,单片机在各个领域的应用日益广泛。本文将介绍一系列基于单片机的项目开发过程,涵盖了智能家居、环境监测、安防等多个领域。这些项目为了提高人们的生活品质,实现智能化、自动化的目标。在智能家居领域,基于STM32设计的格力空调遥控器项目是一个值得关注的案例。该项目的目标是通过单片机控制空调的开关、温度调节等功能,实现对空调的远程控制。接下来,我们将详细介绍NV12数据格式转H265编码格式的实现过程,这是一个涉及到视频编解码技术的项目。在物联网技术的应用方面,基于STM32+华为云IOT设计的智能衣柜和智能垃圾桶项目具有很高的实用价值。这些项目利用物联网技术,实现了对家居环境的智能化管理。环境监测是另一个重要的应用领域。基于STM32的蔬菜大棚温湿度智能控制系统设计和花卉温室控温系统设计旨在通过实时监测温湿度数据,为农作物提供最佳的生长环境。同时,基于单片机的煤气泄漏检测报警装置设计对于家庭安全具有重要意义。在系统监控方面,掌握Windows下读取系统的内存、CPU、GPU等使用信息的方法以及VisualStudio(VS)设置程序的版本信息(C-C++)的技巧对于软件开发人员来说非常实用。最后,基于单片机设计的家用自来水水质监测装置、智能窗帘控制系统、防煤气泄漏装置和电子柜锁项目充分利用了单片机的功能,为人们的生活带来了便利和安全。二、项目总汇【1】基于STM32设计的格力空调遥控器cid:link_7格力空调的红外控制协议被称为格力红外通讯协议或者格力红外遥控协议。这个协议定义了一系列红外信号,可以用来控制格力空调的各种操作,例如开关、温度控制、模式选择、风速控制等等。格力空调的红外控制协议是一种自定义协议,它并没有像NEC、RC5、RC6等协议一样被广泛应用。因此,不同型号的格力空调可能会有不同的红外控制协议。如果想要使用红外发送器控制格力空调,需要先了解当前空调使用的是哪种红外控制协议。一般来说,格力空调的红外控制协议包含一个头部和一系列数据位。头部通常由一个起始位和一个引导位组成。数据位通常包括操作码、温度、模式、风速等信息。【2】NV12数据格式转H265编码格式实现过程cid:link_8在视频处理和传输应用中,将视频数据编码为高效的格式是非常重要的。H.265(也称为HEVC)是一种先进的视频编码标准,具有更好的压缩性能和图像质量,相比于传统的编码标准(如H.264),可以显著减少视频的带宽和存储需求。NV12是一种常见的视频格式,用于表示YUV图像数据,尤其在实时视频处理中广泛使用。它将亮度(Y)和色度(UV)分量分开存储,其中Y分量占据连续的内存块,而UV分量交错存储在另一个连续的内存块中。本需求实现将NV12格式的视频数据转换为H.265格式的数据,并将其保存在内存中。这样可以方便地对视频数据进行后续处理,如网络传输、存储或实时流媒体传输等。为了实现这一需求,使用了C语言和FFmpeg库。FFmpeg是一个强大的开源多媒体处理库,提供了丰富的功能和编解码器,包括H.265编码器。下面代码实现了如何使用FFmpeg库将NV12格式的视频数据编码为H.265格式的数据,并将其保存在内存中。函数接受NV12数据、宽度和高度作为输入,并返回编码后的H.265数据和数据大小。这样,用户可以方便地将NV12格式的视频数据转换为H.265格式,并在内存中进行进一步处理或传输。同时也提供了文件的方式。这个功能可以在各种视频处理应用中使用,如视频编辑软件、实时视频流处理系统、视频通信应用等。通过使用H.265编码,可以提高视频传输的效率和质量,减少带宽和存储需求,同时保持良好的视觉体验。【3】Qt加载本地图片转为YUV420P格式数据cid:link_9在流媒体应用中,视频编码是必不可少的一环。视频编码的作用是将高带宽、高码率的原始视频流压缩成低带宽、低码率的码流,以便于传输和存储。H264是一种高效的视频编码标准,具有良好的压缩性能和广泛的应用范围,在实时流媒体应用中得到了广泛的应用。在将本地图片编码成H264并通过RTMP推流到流媒体服务器时,需要经过以下步骤:(1)使用图像处理库(如Qt)加载本地图片,并将其转换为YUV420P格式。转换后的YUV420P数据可以作为H264编码器的输入。(2)使用H264编码器对YUV420P数据进行编码。H264编码器将YUV420P数据压缩成H264码流,并将码流输出。(3)用RTMP协议将H264码流推送到流媒体服务器。RTMP协议是一种实时流媒体传输协议,可以将音视频数据推送到流媒体服务器,并提供流媒体回放和点播功能。在实现上述功能时,使用第三方库(FFmpeg)来完成H264编码和RTMP推流的功能。FFmpeg是一种跨平台的开源多媒体框架,它提供了丰富的音视频处理功能,包括视频编码、解码、转换、推流、拉流等功能。使用FFmpeg,可以方便地将本地图片编码成H264,并通过RTMP协议推流到流媒体服务器。【4】基于STM32+华为云IOT设计的智能衣柜cid:link_0随着智能家居的发展,人们对于家居设备的智能化和远程控制需求越来越高。智能衣柜作为智能家居的一部分,可以提供衣物存储和保护的功能,并通过传感器和互联网技术实现对衣柜内部环境的监测和控制,为用户提供更好的使用体验。本项目基于STM32F103ZET6主控芯片设计了一个智能衣柜系统,主要功能包括温度和湿度的监测以及烘干控制。为了实现温湿度的监测,采用了DHT11传感器,可以准确地测量环境的温度和湿度。通过将传感器连接到STM32F103ZET6,可以实时获取衣柜内部的温湿度数据。为了实现烘干功能,系统使用加热丝和小风扇来制造热气并循环衣柜内部的空气,以去除湿气并防止衣物发霉。加热丝的控制采用继电器来控制加热丝的通断,从而控制烘干的开关。通过与STM32F103ZET6的连接,可以实现对加热丝和小风扇的控制。为了实现远程监控和控制,系统采用了ESP8266-WIFI模块将采集到的温湿度数据上传到华为云物联网平台。用户可以通过在Android手机上开发的Qt应用程序远程查看衣柜的实时温度和湿度,并设置湿度阀值。如果湿度超出阀值,系统会通过本地蜂鸣器报警和手机APP提示用户,以防止衣物发霉。此外,用户还可以通过手机APP远程控制衣柜的烘干系统,去除湿气,防止衣物发霉或出现霉味。整个系统通过将传感器、主控芯片、继电器、ESP8266-WIFI模块和手机APP进行集成,实现了智能衣柜的温湿度监测和远程控制功能,为用户提供了便捷、智能的衣物存储和保护解决方案。【5】基于STM32+华为云IOT设计的智能垃圾桶cid:link_10 在商业街、小吃街和景区等人流密集的场所,垃圾桶的及时清理对于提供良好的游客体验至关重要。然而,传统的垃圾桶清理方式通常是定时或定期进行,无法根据实际情况进行及时响应,导致垃圾桶溢满,影响环境卫生,给游客带来不便和不满。为了解决这一问题,本项目基于STM32F103ZET6主控芯片和华为云物联网平台,设计了一套智能垃圾桶管理系统。该系统通过NBIOT-BC26模块连接到华为云物联网平台,实现了垃圾桶数据的实时采集和上传。在本地,垃圾桶通过多种传感器进行数据采集。使用DHT11模块实时监测环境温度和湿度,以了解垃圾桶所处环境的状态。采用中科微电子出品的GPS模块,通过串口输出GPS数据,实现垃圾桶的定位功能。垃圾桶口还配备了红外传感器,用于检测垃圾桶是否已满。通过NBIOT-BC26模块,采集到的数据被实时上传到华为云物联网平台。在保洁人员管理中心,开发了一个数据大屏,采用Qt开发,运行在Windows系统下。数据大屏展示了该区域内垃圾桶的详细情况,包括环境温度、湿度、GPS定位和垃圾桶的满溢状态。当垃圾桶满了时,上位机会实时发送短信通知保洁人员进行清理,并提供垃圾桶的位置信息,以便保洁人员快速响应并进行清理操作。通过这套智能垃圾桶管理系统,垃圾桶的清理可以根据实际情况进行及时调度,提高了垃圾桶的使用效率,改善了环境卫生状况,提升了游客的体验感。同时,保洁人员能够更加高效地管理垃圾桶,提升工作效率,减少资源浪费。整个系统的设计旨在提供一个智能、高效的垃圾桶管理解决方案,为公共场所的环境卫生管理带来便利和改进。【6】基于STM32的蔬菜大棚温湿度智能控制系统设计cid:link_11 随着人们对健康和可持续生活方式的关注不断增加,蔬菜大棚成为了现代农业中的重要组成部分。蔬菜大棚提供了一个受控的环境,使得农民能够在任何季节种植蔬菜,并根据需要进行调节。为了实现最佳的蔬菜生长和产量,对温度和湿度等环境条件的精确控制至关重要。传统的蔬菜大棚管理通常依赖于人工监测和调节。这种方法存在一些问题,例如人工监测容易出现误差和延迟,而且对于大规模的蔬菜大棚来说,人工调节工作量巨大。所以开发一种基于智能控制系统的蔬菜大棚温湿度管理方案变得非常重要。基于STM32微控制器的蔬菜大棚温湿度智能控制系统用于解决传统管理方法的问题,并提供一种自动化的解决方案。该系统利用STM32微控制器的强大计算和控制能力,结合温湿度传感器和执行器,实现对蔬菜大棚环境的精确监测和控制。通过该系统,农民可以实时监测蔬菜大棚内的温度和湿度,并根据预设的目标范围自动调节。系统可以自动控制温室内的加热器、通风设备和加湿器等设备,以维持最适宜的生长环境条件。项目的目标是提高蔬菜大棚的生产效率和质量,降低能源消耗,并减少人力投入。通过智能控制系统的应用,农民能够实现更加可持续和高效的农业生产,为社会提供更多健康的蔬菜产品。【7】基于STM32的花卉温室控温系统设计cid:link_1 随着人们对花卉养殖的需求不断增长,花卉温室的建设和管理成为了一个重要的课题。在花卉温室中,温度是一个至关重要的环境参数,对花卉的生长和发展有着直接的影响。为了提供一个稳定的生长环境,控制温室的温度变得非常重要。本项目设计一个基于STM32微控制器的花卉温室控温系统。该系统利用STM32F103C8T6作为主控芯片,通过与DS18B20温度传感器和0.96寸OLED显示屏等硬件模块的连接,实现对温室内温度的监测和控制。同时,系统还配备了两个独立按键,用于设置温度阀值。温度传感器采用DS18B20,能够准确地监测温室内的温度。通过与STM32微控制器的通信,可以实时获取温度数据。显示屏采用SPI协议的0.96寸OLED显示屏,用于显示当前环境的温度以及温度阀值。用户可以通过按键设置温度阀值,以便系统能够根据设定的阀值进行温度控制。当温度低于设定的温度阀值时,系统将通过继电器控制热风机进行加热,吹出热风来控制室温。通过实时监测温度并根据设定的阀值进行控制,系统能够保持温室内的温度在一个适宜的范围,为花卉提供一个稳定的生长环境。项目的设计用于提高花卉温室的自动化程度,减轻人工管理的负担,同时提供一个稳定的温度控制方案,以促进花卉的生长和发展。通过使用STM32微控制器和相关硬件模块,该系统能够实现温度的实时监测和自动控制,为花卉温室管理者提供了一种方便、高效的解决方案。【8】基于单片机的煤气泄漏检测报警装置设计cid:link_12煤气泄漏是一种常见的危险情况,可能导致火灾、爆炸和人员伤亡。为了及时发现煤气泄漏并采取相应的安全措施,设计了一种基于单片机的煤气泄漏检测报警装置。主控芯片采用STM32F103C8T6作为主控芯片,具有强大的计算和控制能力。煤气检测传感器选择了MQ-5,它能够检测到环境中的煤气浓度,并将其转换为电信号输出。装置通过读取传感器输出的模拟信号,并经过ADC转换获得相应的数字值,实时监测煤气浓度。当检测到煤气浓度超过设定的安全阈值时,装置会触发报警机制。通过控制蜂鸣器发出高频报警声,吸引人的注意并提醒危险情况。同时,装置会控制LED灯光闪烁,以视觉方式提醒用户。这样的多重报警方式可以在不同环境中有效地引起人们的警觉。为了进一步提高报警的及时性和可靠性,还集成了SIM800C模块,用于发送报警短信给指定的联系人。当煤气浓度超标时,装置会通过SIM800C模块发送预先设定的报警短信,通知相关人员及时采取措施。通过以上设计,基于单片机的煤气泄漏检测报警装置能够实时监测环境中的煤气浓度,并在检测到异常情况时通过声光报警和短信通知提醒用户。这样的装置可以广泛应用于家庭、工业和商业环境中,为人们的生命财产安全提供有效的保障。【9】Windows下读取系统的内存、CPU、GPU等使用信息cid:link_2在当今计算机应用广泛的领域中,了解系统的内存、CPU和GPU使用情况是非常重要的。对于开发人员和系统管理员来说,准确获取这些信息可以帮助他们优化软件性能、诊断问题并做出相应的调整。在Windows平台上实现这一目标会涉及到调用Windows系统API,使用合适的工具和库来获取所需的信息。本文将介绍如何使用Qt和Windows API来读取系统的内存、CPU和GPU使用详细信息。将提供一个完整的示例代码,展示了如何使用这些技术来获取系统的关键性能指标。通过阅读本文,将学习如何使用Qt框架和Windows API来实现这些功能,以及如何根据需求进行扩展和定制。【10】VisualStudio(VS)设置程序的版本信息(C-C++)cid:link_3在软件开发过程中,通常需要为生成的程序添加一些重要的元数据,如版本号、公司名称和版权信息。这些信息不仅可以提供对程序的更详细描述,还可以帮助用户了解程序的来源和使用限制。在 Visual Studio (以2017为例)中,可以轻松地设置这些信息,使应用程序具有更专业、规范的要求。本文将介绍如何在 Visual Studio 2017 中设置生成程序的版本信息、公司信息和版权信息逐步指导大家完成这个过程,无论是开发新项目,还是为现有项目添加这些重要的元数据,都能从本文中获得帮助。下面将详细说明每个步骤,并提供示例。【11】基于单片机设计的家用自来水水质监测装置cid:link_4本文介绍基于单片机设计的家用自来水水质监测装置。利用STM32F103ZET6作为主控芯片,结合水质传感器和ADC模块,实现对自来水水质的检测和监测功能。通过0.96寸OLED显示屏,将采集到的水质数据以直观的方式展示给用户。随着人们对健康意识的提高和环境保护的重视,水质安全已经成为人们生活中一个重要的议题。自来水作为我们日常生活中最主要的饮用水来源之一,其水质的安全与否直接关系到我们的健康。本设计采用了先进的STM32F103ZET6主控芯片,具备强大的处理能力和丰富的外设接口。通过水质传感器,可以实时采集与水质相关的模拟信号。然后,通过ADC模块将模拟数据转换为数字信号,再经过算法处理得到相应的水质参数。最后,将结果通过0.96寸OLED显示屏进行展示,用户可以清晰地了解自来水的水质状况。该装置特点:易于携带、操作简单、实时性好、精度高。用户只需将传感器浸入自来水中,即可获取到水质参数,并通过显示屏直观地了解水质状况,为家庭提供了一个简单方便的水质监测解决方案。【12】基于单片机设计的智能窗帘控制系统cid:link_5 智能家居技术在近年来取得了巨大的发展,并逐渐成为人们日常生活中的一部分。智能家居系统带来了便利、舒适和高效的生活体验,拥有广泛的应用领域,其中之一就是智能窗帘控制系统。传统窗帘需要手动操作,打开或关闭窗帘需要人工干预,而且无法根据环境光照强度进行自动调节。这种方式不仅耗费时间和精力,还无法满足人们对舒适、智能化生活的需求。为了解决这一问题,智能窗帘采用先进的智能技术,包括语音识别、定时控制和光强度检测等功能,使窗帘的开启和关闭更加便捷和智能化。语音识别技术是智能窗帘控制系统的核心功能之一。通过语音识别模块,用户可以使用简单的语音指令来控制窗帘的开关,实现真正的智能化操作。用户只需说出"打开窗帘"或"关闭窗帘"等简单指令,系统就能自动识别并执行相应的操作,大大提高了用户的使用便捷性。时间段控制功能也是智能窗帘控制系统的重要特点之一。用户可以根据自己的需求,在系统中设置窗帘的打开和关闭时间段。在设定的时间段内,系统会自动控制窗帘的开关,无需人工干预。这样,用户可以根据自己的作息时间和需求,享受到更加智能化的窗帘控制体验。光强度检测是智能窗帘控制系统的另一个关键功能。系统配备了光强度检测模块,能够实时检测环境光照强度。当光照强度超过预定阈值时,系统会自动关闭窗帘,避免阳光直射进入室内,降低室内温度,保护家具和电器设备,提高室内舒适度。【13】基于单片机设计的防煤气泄漏装置cid:link_13 煤气泄漏是一个严重的安全隐患,可能导致火灾、爆炸以及对人体健康的威胁。为了提高家庭和工业环境中煤气泄漏的检测和预防能力,设计了一种基于单片机的防煤气泄漏装置。选择了STC89C52作为主控芯片,这是一款功能强大且广泛应用的单片机,具有高性能和稳定性。为了检测煤气泄漏,采用了MQ4传感器,能够快速、准确地检测煤气浓度。通过采集MQ4传感器的模拟信号,使用PCF8591模数转换芯片将模拟信号转换为数字信号,以便进行处理和分析。为了方便用户获取检测结果,采用了IIC接口的OLED显示屏,将采集到的数据显示出来。用户可以通过两个独立按键设置煤气泄漏的报警阈值,以适应不同环境的需求。当检测到煤气泄漏超过设定的阈值时,装置会触发蜂鸣器进行报警,并同时打开换气扇进行通风换气,以迅速排除煤气并降低安全风险。这种基于单片机设计的防煤气泄漏装置具有以下优点:高效可靠的煤气泄漏检测能力、灵活的报警阈值设置、直观清晰的数据显示以及及时的安全响应措施。可以广泛应用于家庭、工业和商业场所,提供有效的煤气泄漏监测和安全保护。【14】基于单片机设计的电子柜锁cid:link_6随着现代社会的不断发展,电子柜锁的应用越来越广泛。传统的机械柜锁存在一些不便之处,例如钥匙容易丢失、密码容易泄露等问题。设计一款基于单片机的电子柜锁系统成为了一个有趣而有意义的项目。该电子柜锁系统采用STC89C52作为主控芯片,具有较强的处理能力和丰富的外设接口。系统通过电磁锁作为柜锁的开关,通过继电器控制电磁锁的开关状态。用户可以通过矩阵键盘输入密码进行开锁,并且密码数据会通过LCD1602液晶显示屏进行显示。同时,系统还支持输入密码验证开锁和修改密码的功能。当用户成功输入正确的密码并开锁时,系统会通过蜂鸣器发出提示音。这款电子柜锁系统的设计为了提高柜锁的安全性和便利性。相比传统的机械柜锁,电子柜锁具有以下优势:(1)密码安全性:电子柜锁采用密码作为开锁方式,相比传统钥匙更加安全可靠,用户可以根据需要设置较复杂的密码,有效防止密码泄露和非法开锁。(2)方便易用:用户只需要通过矩阵键盘输入密码即可开锁,无需携带钥匙或记忆复杂的机械操作步骤,操作简单方便。(3)修改密码功能:用户可以根据需要随时修改密码,提高了柜锁的灵活性和可维护性。(4)提示音提示:系统通过蜂鸣器发出提示音,让用户在输入密码和开锁成功时得到明确的反馈,提升了用户体验。电子柜锁系统的设计不仅具有实用性,而且可以为学习嵌入式系统设计和单片机编程的初学者提供一个非常好的实践项目。通过这个项目,可以学习和掌握单片机的输入输出控制、按键扫描、LCD显示、蜂鸣器控制等相关知识和技术。还涉及到密码输入和验证的算法设计和实现,锻炼了逻辑思维和程序设计能力。通过这个电子柜锁系统项目,可以体验到现代电子技术的魅力,提高柜锁的安全性和便利性,为用户提供更好的使用体验。
  • [技术干货] 基于单片机设计的电子柜锁
    一、前言随着现代社会的不断发展,电子柜锁的应用越来越广泛。传统的机械柜锁存在一些不便之处,例如钥匙容易丢失、密码容易泄露等问题。设计一款基于单片机的电子柜锁系统成为了一个有趣而有意义的项目。该电子柜锁系统采用STC89C52作为主控芯片,具有较强的处理能力和丰富的外设接口。系统通过电磁锁作为柜锁的开关,通过继电器控制电磁锁的开关状态。用户可以通过矩阵键盘输入密码进行开锁,并且密码数据会通过LCD1602液晶显示屏进行显示。同时,系统还支持输入密码验证开锁和修改密码的功能。当用户成功输入正确的密码并开锁时,系统会通过蜂鸣器发出提示音。这款电子柜锁系统的设计为了提高柜锁的安全性和便利性。相比传统的机械柜锁,电子柜锁具有以下优势:【1】密码安全性:电子柜锁采用密码作为开锁方式,相比传统钥匙更加安全可靠,用户可以根据需要设置较复杂的密码,有效防止密码泄露和非法开锁。【2】方便易用:用户只需要通过矩阵键盘输入密码即可开锁,无需携带钥匙或记忆复杂的机械操作步骤,操作简单方便。【3】修改密码功能:用户可以根据需要随时修改密码,提高了柜锁的灵活性和可维护性。【4】提示音提示:系统通过蜂鸣器发出提示音,让用户在输入密码和开锁成功时得到明确的反馈,提升了用户体验。电子柜锁系统的设计不仅具有实用性,而且可以为学习嵌入式系统设计和单片机编程的初学者提供一个非常好的实践项目。通过这个项目,可以学习和掌握单片机的输入输出控制、按键扫描、LCD显示、蜂鸣器控制等相关知识和技术。还涉及到密码输入和验证的算法设计和实现,锻炼了逻辑思维和程序设计能力。通过这个电子柜锁系统项目,可以体验到现代电子技术的魅力,提高柜锁的安全性和便利性,为用户提供更好的使用体验。二、硬件选型介绍硬件选型方面,根据需求,下面是电子柜锁的最终硬件选型:【1】主控芯片:STC89C52单片机是一款常用的8位单片机,具有丰富的外设资源和较大的存储容量,适合作为电子柜锁的主控芯片。【2】电磁锁:选择适合的电磁锁作为柜锁的开关,确保其能提供足够的安全性和可靠性。考虑使用12V电磁锁,满足电源和控制信号要求。【3】继电器:使用继电器来控制电磁锁的通断,确保信号隔离和电流放大。【4】矩阵键盘:选择适用的矩阵键盘用于输入密码。选择4x4矩阵键盘,具有16个按键,支持数字和功能键。【5】LCD1602液晶显示屏:作为密码输入和状态显示的界面,LCD1602具有两行16列的字符显示,能够清晰显示输入的密码和相关提示信息。【6】蜂鸣器:用于发出开锁成功、密码输入错误等提示音。【7】电源模块:有稳定可靠的电源供应非常重要,选择使用AC/DC 5/12V适配器供电。三、整体设计思路软件设计逻辑和思路如下:【1】初始化:在程序开始时,进行系统初始化设置,包括配置IO口、定时器和外设等。同时,需要初始化密码存储区、LCD1602显示屏和蜂鸣器等。【2】密码输入和验证:通过矩阵键盘读取用户输入的密码。可以采用一个固定长度的密码,例如4位。用户每按下一个数字键,将其添加到密码缓冲区中,并在LCD1602上显示相应的“*”字符表示已输入。当输入的密码长度达到预设长度时,即可触发密码验证操作。【3】密码验证:将密码缓冲区中的数字转换为字符串形式,与预先设置好的正确密码进行比较。如果密码输入正确,则进行开锁操作;否则,进行密码错误提示处理。【4】开锁操作:当密码验证成功后,控制继电器通断,打开或关闭电磁锁。同时,通过蜂鸣器发出开锁成功的提示音,并在LCD1602上显示开锁成功信息。【5】修改密码:提供修改密码的功能。在成功验证密码后,用户可以输入新密码进行修改。修改完成后,将新密码存储起来,供下次验证使用。【6】状态显示:将相关的状态信息实时显示在LCD1602上,例如输入密码错误提示、修改密码成功提示等。【7】系统保护:为了保护系统安全,可以设置安全策略,例如密码输入错误次数限制、锁定时间等。当达到错误次数上限或锁定时间到达时,系统会自动进行相应的保护处理。【8】中断服务:使用定时器中断等方式进行按键检测和LCD1602刷新等操作,提高系统的实时性。【9】循环检测:设计一个主循环函数,不断检测矩阵键盘的按键输入、执行开锁、密码验证、密码修改以及状态显示等功能。四、项目代码#include <reg51.h>#include <intrins.h>​#define PASSWORD_LENGTH 4 // 密码长度#define MAX_ATTEMPTS 3 // 最大尝试次数​sbit Buzzer = P1^0; // 蜂鸣器控制引脚sbit ElectromagneticLock = P1^1; // 电磁锁控制引脚​unsigned char password[PASSWORD_LENGTH] = {1, 2, 3, 4}; // 初始密码unsigned char enteredPassword[PASSWORD_LENGTH]; // 输入的密码unsigned char attempts = 0; // 尝试次数​// 延时函数void delay(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 110; j++) ;}​// 初始化LCD1602void LCD_Init() { LCD_WriteCommand(0x38); // 设置8位数据总线,2行显示,5x8点阵字符 LCD_WriteCommand(0x0c); // 显示器开,光标关闭 LCD_WriteCommand(0x06); // 光标右移,字符不移动 LCD_WriteCommand(0x01); // 清屏}​// 写入命令到LCD1602void LCD_WriteCommand(unsigned char cmd) { LCD_RS = 0; LCD_RW = 0; LCD_EN = 1; P0 = cmd; _nop_(); _nop_(); LCD_EN = 0;}​// 写入数据到LCD1602void LCD_WriteData(unsigned char dat) { LCD_RS = 1; LCD_RW = 0; LCD_EN = 1; P0 = dat; _nop_(); _nop_(); LCD_EN = 0;}​// 在LCD1602上显示字符串void LCD_ShowString(unsigned char x, unsigned char y, unsigned char *str) { unsigned char i = 0; if (x < 16) { if (y == 0) LCD_WriteCommand(0x80 + x); else if (y == 1) LCD_WriteCommand(0xc0 + x); while (str[i] != '\0') { LCD_WriteData(str[i]); i++; } }}​// 初始化矩阵键盘void Keypad_Init() { Keypad_Row1 = 1; Keypad_Row2 = 1; Keypad_Row3 = 1; Keypad_Row4 = 1;}​// 读取矩阵键盘按键值unsigned char Keypad_Read() { unsigned char row, col; unsigned char keyVal;​ for (col = 0; col < 4; col++) { Keypad_Col1 = 1; Keypad_Col2 = 1; Keypad_Col3 = 1; Keypad_Col4 = 1; switch (col) { case 0: Keypad_Col1 = 0; break; case 1: Keypad_Col2 = 0; break; case 2: Keypad_Col3 = 0; break; case 3: Keypad_Col4 = 0; break; } for (row = 0; row < 4; row++) { if (Keypad_Row1 == 0) { delay(5); if (Keypad_Row1 == 0) { while (Keypad_Row1 == 0) ; keyVal = row * 4 + col + 1; return keyVal; } } if (Keypad_Row2 == 0) { delay(5); if (Keypad_Row2 == 0) { while (Keypad_Row2 == 0) ; keyVal = row * 4 + col + 5; return keyVal; } } if (Keypad_Row3 == 0) { delay(5); if (Keypad_Row3 == 0) { while (Keypad_Row3 == 0) ; keyVal= row * 4 + col + 9; return keyVal; } } if (Keypad_Row4 == 0) { delay(5); if (Keypad_Row4 == 0) { while (Keypad_Row4 == 0) ; keyVal = row * 4 + col + 13; return keyVal; } } } }​ return 0xFF; // 返回0xFF表示没有按键按下}​// 检查输入的密码是否与设定密码一致bit CheckPassword() { unsigned char i; for (i = 0; i < PASSWORD_LENGTH; i++) { if (enteredPassword[i] != password[i]) return 0; // 密码不一致 } return 1; // 密码一致}​// 输入密码bit EnterPassword() { unsigned char i; unsigned char key; for (i = 0; i < PASSWORD_LENGTH; i++) { while ((key = Keypad_Read()) == 0xFF) ; enteredPassword[i] = key; LCD_WriteData('*'); delay(300); } return CheckPassword();}​// 修改密码void ChangePassword() { unsigned char i; LCD_ShowString(0, 1, "Enter New Password"); for (i = 0; i < PASSWORD_LENGTH; i++) { while ((enteredPassword[i] = Keypad_Read()) == 0xFF) ; LCD_WriteData('*'); delay(300); } for (i = 0; i < PASSWORD_LENGTH; i++) password[i] = enteredPassword[i]; LCD_ShowString(0, 1, "Password Changed "); delay(1000); LCD_ShowString(0, 1, "Enter Password: ");}​// 开锁void Unlock() { LCD_ShowString(0, 1, "Unlocking..."); Buzzer = 1; // 发出提示音 ElectromagneticLock = 0; // 解锁状态 delay(2000); Buzzer = 0; // 关闭提示音 ElectromagneticLock = 1; // 上锁状态 LCD_ShowString(0, 1, "Enter Password: ");}​// 主函数void main() { LCD_Init(); // 初始化LCD1602 Keypad_Init(); // 初始化矩阵键盘​ LCD_ShowString(0, 0, "Electronic Lock"); LCD_ShowString(0, 1, "Enter Password: ");​ while (1) { if (EnterPassword()) { Unlock(); // 密码正确,开锁 attempts = 0; // 尝试次数清零 } else { attempts++; // 尝试次数加一 if (attempts >= MAX_ATTEMPTS) { LCD_ShowString(0, 1, "Max Attempts Exceeded"); Buzzer = 1; // 发出警报音 delay(2000); Buzzer = 0; // 关闭警报音 attempts = 0; // 尝试次数清零 } else { LCD_ShowString(0, 1, "Wrong Password "); delay(1000); LCD_ShowString(0, 1, "Enter Password: "); } }​ while ((Keypad_Read()) != 0xFF) ; // 等待按键释放​ if (Keypad_Read() == '#') { ChangePassword(); // 输入'#'进入修改密码模式 } }}
  • [技术干货] 基于单片机设计的防煤气泄漏装置
    一、前言煤气泄漏是一个严重的安全隐患,可能导致火灾、爆炸以及对人体健康的威胁。为了提高家庭和工业环境中煤气泄漏的检测和预防能力,设计了一种基于单片机的防煤气泄漏装置。选择了STC89C52作为主控芯片,这是一款功能强大且广泛应用的单片机,具有高性能和稳定性。为了检测煤气泄漏,采用了MQ4传感器,能够快速、准确地检测煤气浓度。通过采集MQ4传感器的模拟信号,使用PCF8591模数转换芯片将模拟信号转换为数字信号,以便进行处理和分析。为了方便用户获取检测结果,采用了IIC接口的OLED显示屏,将采集到的数据显示出来。用户可以通过两个独立按键设置煤气泄漏的报警阈值,以适应不同环境的需求。当检测到煤气泄漏超过设定的阈值时,装置会触发蜂鸣器进行报警,并同时打开换气扇进行通风换气,以迅速排除煤气并降低安全风险。这种基于单片机设计的防煤气泄漏装置具有以下优点:高效可靠的煤气泄漏检测能力、灵活的报警阈值设置、直观清晰的数据显示以及及时的安全响应措施。可以广泛应用于家庭、工业和商业场所,提供有效的煤气泄漏监测和安全保护。二、硬件选型在设计基于单片机的防煤气泄漏装置时,硬件选型是非常关键的。以下是详细介绍硬件选型的相关内容:【1】主控芯片选择:STC89C52 STC89C52是一款8051架构的单片机,具有丰富的接口资源、较高的性能和稳定可靠的工作特性,广泛应用于各种嵌入式系统中。具有8位数据总线、16位地址总线和4KB的内部存储器。STC89C52具备多个通用I/O口、定时器/计数器、串口等功能,非常适合本项目需求。【2】煤气传感器选择:MQ4 MQ4传感器是一种能够检测多种可燃气体,如天然气、甲烷等的传感器。具有高灵敏度和快速响应的特点,能够准确地检测煤气泄漏情况。MQ4传感器的输出为模拟信号,需要通过模数转换器将其转换为数字信号供主控芯片处理。【3】模数转换器选择:PCF8591 PCF8591是一款集成了8位模数/数模转换和4个模拟输入通道的模数转换器。采用IIC总线通讯接口,能够将模拟信号转换为数字信号,并通过IIC协议发送给主控芯片。本项目中,PCF8591用于采集MQ4传感器输出的模拟信号,并将其转换为数字信号供STC89C52处理。【4】显示屏选择:0.96寸OLED显示屏(IIC接口) 本设计采用基于IIC接口的OLED显示屏,具有高亮度、对比度和快速响应的特点。通过简单的通讯方式,可以将煤气浓度信息实时显示在屏幕上。OLED显示屏使用面积小、功耗低,在嵌入式系统中应用广泛。【5】按键选择:独立按键 本设计采用两个独立按键来设置报警的阀值。一个按键用于递增阀值,另一个按键用于递减阀值。独立按键具有简单可靠、使用方便等特点,适合本项目需求。【6】报警装置选择:蜂鸣器和换气扇 当检测到煤气泄漏超过设定的报警阀值时,蜂鸣器将发出警报,用于提醒周围人员。同时,为了降低煤气浓度,需要启动换气扇进行通风换气。具体的报警和换气扇电路可以根据实际需求设计。三、设计思路软件设计思路如下:【1】初始化:在程序开始时,进行主控芯片STC89C52的初始化设置,包括引脚配置、定时器设置等。同时,初始化PCF8591和OLED显示屏,确保它们可以正常工作。【2】传感器检测:通过MQ4传感器检测煤气是否泄漏。将MQ4传感器与STC89C52的模拟输入引脚连接,通过读取该引脚的模拟电压值,获取煤气浓度数据。【3】数据采集与处理:使用PCF8591模数转换芯片,将MQ4传感器的模拟输出信号转换为数字信号,并通过STC89C52的IIC接口与PCF8591进行通信,获取转换后的数字数据。【4】数据显示:将采集到的煤气浓度数据通过IIC接口的OLED显示屏进行显示。使用STC89C52的IIC通信功能,将数据发送给OLED显示屏,通过显示屏将数据以可读的方式展示给用户。【5】阈值设置:通过两个独立按键实现报警阈值的设置。将按键与STC89C52的GPIO引脚连接,通过读取按键状态来判断用户是否进行阈值设置操作。当按键按下时,进入设置模式,用户可以通过按键的不同组合来调整报警阈值,并将设置的值保存在相应的变量中。【6】报警与通风控制:根据当前采集到的煤气浓度数据和用户设置的报警阈值进行比较。如果煤气浓度超过设定的阈值,触发蜂鸣器进行报警,并控制换气扇打开进行通风换气。反之,当煤气浓度低于或等于设定的阈值时,停止报警并关闭换气扇。【7】循环监测:使用主循环结构,不断进行煤气泄漏检测、数据采集、数据显示和阈值比较等操作,以实现持续的监测和反馈。四、项目模块代码4.1 PCF8591采集代码下面是使用STC89C52单片机通过PCF8591读取MQ4传感器的ADC数据的代码。使用IIC总线进行PCF8591之间的通信,使用了自定义的IIC总线函数。通过readADC()函数实现了读取MQ4传感器模拟量的ADC转换结果。#include <reg52.h>​#define uchar unsigned char#define uint unsigned int​sbit SDA = P2^0; // IIC总线数据线sbit SCL = P2^1; // IIC总线时钟线​sbit MQ4_DOUT = P3^0; // MQ4传感器数字输出引脚sbit MQ4_AIN = P3^1; // MQ4传感器模拟输入引脚​sfr IAP_DATA = 0xe2; // 定义IAP_DATA寄存器sfr IAP_ADDRH = 0xe3; // 定义IAP_ADDRH寄存器sfr IAP_ADDRL = 0xe4; // 定义IAP_ADDRL寄存器sfr IAP_CMD = 0xe5; // 定义IAP_CMD寄存器sfr IAP_TRIG = 0xe6; // 定义IAP_TRIG寄存器sfr IAP_CONTR = 0xe7; // 定义IAP_CONTR寄存器​// 函数声明void delay(uint ms);void startIIC();void stopIIC();void sendByte(uchar dat);uchar receiveByte();void writeDAC(uchar dat);uchar readADC();​void main() { uchar mq4Value; while (1) { mq4Value = readADC(); // 读取ADC转换结果 // 处理mq4Value值,进行相应操作 delay(100); // 延时一段时间 }}​// 延时函数void delay(uint ms) { uint i, j; for(i = ms; i > 0; i--) { for(j = 110; j > 0; j--); }}​// IIC总线起始信号void startIIC() { SDA = 1; _nop_(); SCL = 1; _nop_(); SDA = 0; _nop_(); SCL = 0; _nop_();}​// IIC总线停止信号void stopIIC() { SDA = 0; _nop_(); SCL = 1; _nop_(); SDA = 1; _nop_();}​// 发送一个字节的数据void sendByte(uchar dat) { uchar i; for (i = 0; i < 8; i++) { SDA = dat >> 7; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_(); dat <<= 1; } SDA = 1; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_();}​// 接收一个字节的数据uchar receiveByte() { uchar i, dat = 0; SDA = 1; for (i = 0; i < 8; i++) { dat <<= 1; SCL = 1; _nop_(); dat |= SDA; SCL = 0; _nop_(); } return dat;}​// 写入DAC数值void writeDAC(uchar dat) { startIIC(); sendByte(0x90); // 地址和写命令 receiveByte(); // 接收应答 sendByte(0x40); // DAC通道A,并写入数据 receiveByte(); // 接收应答 sendByte(dat); // DAC数据 receiveByte(); // 接收应答 stopIIC();}​// 读取ADC转换结果uchar readADC() { uchar adcValue; startIIC(); sendByte(0x91); // 地址和读命令 receiveByte(); // 接收应答 adcValue = receiveByte(); // 读取ADC数据 stopIIC(); return adcValue;}​4.2 OLED显示屏代码以下是通过STC89C52控制IIC接口的OLED显示屏显示ADC数据实现代码。#include <reg51.h>​#define SCL P1_0 // IIC时钟线#define SDA P1_1 // IIC数据线​#define OLED_ADDR 0x78 // OLED显示屏的IIC地址​// OLED显示屏初始化函数void OLED_Init() { // 初始化OLED显示屏 // ...​ // 发送初始化命令到OLED显示屏 // ...}​// IIC总线开始信号void IIC_Start() { SDA = 1; SCL = 1; SDA = 0; SCL = 0;}​// IIC总线停止信号void IIC_Stop() { SDA = 0; SCL = 1; SDA = 1;}​// IIC总线发送一个字节的数据void IIC_WriteByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { SCL = 0; if (dat & 0x80) SDA = 1; else SDA = 0; SCL = 1; dat <<= 1; } SCL = 0; SDA = 1; SCL = 1;}​// 设置OLED显示屏光标位置void OLED_SetPos(unsigned char x, unsigned char y) { IIC_Start(); IIC_WriteByte(OLED_ADDR); IIC_WriteByte(0xb0 + y); IIC_WriteByte(((x & 0xf0) >> 4) | 0x10); IIC_WriteByte((x & 0x0f) | 0x01); IIC_Stop();}​// 在OLED显示屏上显示一个字符void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch) { unsigned char c = 0, i = 0; c = ch - ' '; // 获取字符在字库中的偏移量 if (x > 128 - 8 || y > 64 - 16) return; // 超出屏幕范围,退出函数 OLED_SetPos(x, y); for (i = 0; i < 8; i++) { IIC_Start(); IIC_WriteByte(OLED_ADDR); IIC_WriteByte(0x40); IIC_WriteByte(*(OLED_CharSet + c * 16 + i)); IIC_Stop(); x++; }}​// 在OLED显示屏上显示字符串void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) { while (*str) { OLED_ShowChar(x, y, *str); x += 8; str++; }}​// ADC模拟数值转换为字符串void ADC_ToString(unsigned int adcValue, unsigned char *str) { unsigned char i, j; unsigned int temp;​ temp = adcValue; for (i = 0; i < 4; i++) { str[i] = temp % 10 + '0'; temp /= 10; }​ // 反转字符串 i = 0; j = 3; while (i < j) { temp = str[i]; str[i] = str[j]; str[j] = temp; i++; j--; } str[4] = '\0'; // 字符串结束符}​// 主函数void main() { unsigned int adcValue = 0; unsigned char str[5];​ // 初始化OLED显示屏 OLED_Init();​ while (1) { // 读取ADC数据 adcValue = ADC_Read(); // 假设使用的函数为ADC_Read(),用于读取ADC数据​ // 将ADC数据转换为字符串 ADC_ToString(adcValue, str);​ // 在OLED显示屏上显示ADC数据 OLED_ShowString(0, 0, "ADC Value:"); OLED_ShowString(0, 2, str); }}4.3 主代码逻辑#include <reg51.h>​#define SCL P1_0 // IIC时钟线#define SDA P1_1 // IIC数据线​#define OLED_ADDR 0x78 // OLED显示屏的IIC地址#define MQ4_PIN P2 // MQ4传感器连接的引脚​sbit Buzzer = P3^0; // 蜂鸣器控制引脚sbit VentilationFan = P3^1; // 换气扇控制引脚​// 全局变量unsigned int alarmThreshold = 100; // 报警阈值,默认为100unsigned int adcValue = 0; // 保存ADC采集到的数值​// IIC总线开始信号void IIC_Start() { SDA = 1; SCL = 1; SDA = 0; SCL = 0;}​// IIC总线停止信号void IIC_Stop() { SDA = 0; SCL = 1; SDA = 1;}​// IIC总线发送一个字节的数据void IIC_WriteByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { SCL = 0; if (dat & 0x80) SDA = 1; else SDA = 0; SCL = 1; dat <<= 1; } SCL = 0; SDA = 1; SCL = 1;}​// 设置OLED显示屏光标位置void OLED_SetPos(unsigned char x, unsigned char y) { IIC_Start(); IIC_WriteByte(OLED_ADDR); IIC_WriteByte(0xb0 + y); IIC_WriteByte(((x & 0xf0) >> 4) | 0x10); IIC_WriteByte((x & 0x0f) | 0x01); IIC_Stop();}​// 在OLED显示屏上显示一个字符void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch) { unsigned char c = 0, i = 0; c = ch - ' '; // 获取字符在字库中的偏移量 if (x > 128 - 8 || y > 64 - 16) return; // 超出屏幕范围,退出函数 OLED_SetPos(x, y); for (i = 0; i < 8; i++) { IIC_Start(); IIC_WriteByte(OLED_ADDR); IIC_WriteByte(0x40); IIC_WriteByte(*(OLED_CharSet + c * 16 + i)); IIC_Stop(); x++; }}​// 在OLED显示屏上显示字符串void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) { while (*str) { OLED_ShowChar(x, y, *str); x += 8; str++; }}​// ADC转换函数unsigned int ADC_Convert(unsigned char channel) { ADC_CONTR = ADC_POWER | ADC_START | channel | ADC_SPEED; _nop_(); _nop_(); _nop_(); _nop_(); while (!(ADC_CONTR & ADC_FLAG)) ; ADC_CONTR &= ~ADC_FLAG; return (ADC_RES * 256 + ADC_RESL);}​// ADC模拟数值转换为字符串void ADC_ToString(unsigned int adcValue, unsigned char *str) { unsigned char i, j; unsigned int temp;​ temp = adcValue; for (i = 0; i < 4; i++) { str[i] = temp % 10 + '0'; temp /= 10; }​ // 反转字符串 i = 0; j = 3; while (i < j) { temp = str[i]; str[i] = str[j]; str[j] = temp; i++; j--; } str[4] = '\0'; // 字符串结束符}​//// 主函数void main() { unsigned char str[5];​ // 初始化OLED显示屏 OLED_Init();​ // 设置初始报警阈值 SetAlarmThreshold();​ while (1) { // 读取MQ4传感器的ADC数值 adcValue = ADC_Convert(0); // 假设MQ4传感器连接到ADC的通道0​ // 将ADC数值转换为字符串 ADC_ToString(adcValue, str);​ // 在OLED显示屏上显示ADC数值 OLED_ShowString(0, 0, "Gas Level:"); OLED_ShowString(0, 2, str);​ // 判断是否触发报警 if (adcValue > alarmThreshold) { // 触发报警 ActivateAlarm(); } else { // 取消报警 DeactivateAlarm(); }​ // 检测是否按下设置阈值的按键 if (IsThresholdButtonPressed()) { // 设置报警阈值 SetAlarmThreshold(); }​ // 检测是否按下通风换气的按键 if (IsVentilationButtonPressed()) { // 控制通风换气 ControlVentilation(); } }}
  • [技术干货] 基于单片机设计的智能窗帘控制系统
    一、前言智能家居技术在近年来取得了巨大的发展,并逐渐成为人们日常生活中的一部分。智能家居系统带来了便利、舒适和高效的生活体验,拥有广泛的应用领域,其中之一就是智能窗帘控制系统。传统窗帘需要手动操作,打开或关闭窗帘需要人工干预,而且无法根据环境光照强度进行自动调节。这种方式不仅耗费时间和精力,还无法满足人们对舒适、智能化生活的需求。为了解决这一问题,智能窗帘采用先进的智能技术,包括语音识别、定时控制和光强度检测等功能,使窗帘的开启和关闭更加便捷和智能化。语音识别技术是智能窗帘控制系统的核心功能之一。通过语音识别模块,用户可以使用简单的语音指令来控制窗帘的开关,实现真正的智能化操作。用户只需说出"打开窗帘"或"关闭窗帘"等简单指令,系统就能自动识别并执行相应的操作,大大提高了用户的使用便捷性。时间段控制功能也是智能窗帘控制系统的重要特点之一。用户可以根据自己的需求,在系统中设置窗帘的打开和关闭时间段。在设定的时间段内,系统会自动控制窗帘的开关,无需人工干预。这样,用户可以根据自己的作息时间和需求,享受到更加智能化的窗帘控制体验。光强度检测是智能窗帘控制系统的另一个关键功能。系统配备了光强度检测模块,能够实时检测环境光照强度。当光照强度超过预定阈值时,系统会自动关闭窗帘,避免阳光直射进入室内,降低室内温度,保护家具和电器设备,提高室内舒适度。二、系统架构 +-------------------------------------+ | | | 智能窗帘控制系统 | | | +--------------+----------------------+ | +-------------------|-------------------+ | | |+-------v-------+ +-------v------+ +------v-------+| STC89C52 | | LD3320 | | BH1750 || 主控芯片 | | 语音识别模块 | | 光强度检测模块 |+-------+-------+ +-------------+ +-------------+ | | | +--------------------|--------------------+ | +------v------+ | 电机驱动器 | +--------------+​三、系统功能设计3.1 语音控制语音指令通过麦克风输入到LD3320语音识别模块中进行处理。LD3320识别到特定的语音指令后,将指令发送到STC89C52主控芯片。STC89C52根据接收到的语音指令,控制电机驱动器开启/关闭窗帘。3.2 时间段控制用户可以预先设置打开和关闭窗帘的时间段。STC89C52通过定时器功能,在设定的时间段内控制电机驱动器实现窗帘的自动打开和关闭操作。3.3 光强度检测BH1750光强度检测模块通过I2C总线连接到STC89C52主控芯片。STC89C52主控芯片通过BH1750模块测量当前的光强度。如果光强度超过预定阈值,STC89C52会自动控制电机驱动器关闭窗帘。3.4 电机驱动器电机驱动器通过接口与STC89C52主控芯片连接,控制窗帘的开启和关闭操作。STC89C52通过控制电机驱动器的引脚,实现窗帘的自动控制。四、代码实现4.1 BH1750光照强度采集#include <reg52.h>#include <stdio.h>#include <intrins.h>​#define BH1750_ADDR 0x46 // BH1750的默认I2C地址​sbit SDA = P2^0; // I2C数据线sbit SCL = P2^1; // I2C时钟线​// 延时函数void Delay(unsigned int t){ while (t--);}​// I2C总线起始信号void I2C_Start(){ SDA = 1; SCL = 1; _nop_(); _nop_(); SDA = 0; _nop_(); _nop_(); SCL = 0;}​// I2C总线停止信号void I2C_Stop(){ SDA = 0; SCL = 1; _nop_(); _nop_(); SDA = 1;}​// I2C总线发送应答信号bit I2C_SendACK(){ bit ackBit;​ SDA = 0; _nop_(); _nop_(); SCL = 1; _nop_(); _nop_(); ackBit = SDA; SCL = 0;​ return ackBit;}​// I2C总线发送非应答信号void I2C_SendNAK(){ SDA = 1; _nop_(); _nop_(); SCL = 1; _nop_(); _nop_(); SCL = 0;}​// I2C总线发送一个字节的数据void I2C_SendByte(unsigned char dat){ unsigned char i;​ for (i = 0; i < 8; i++) { SDA = (bit)(dat & 0x80); _nop_(); _nop_(); SCL = 1; _nop_(); _nop_(); SCL = 0; dat <<= 1; }​ SDA = 1; _nop_(); _nop_(); SCL = 1; _nop_(); _nop_(); SCL = 0;}​// I2C总线接收一个字节的数据unsigned char I2C_ReceiveByte(){ unsigned char i, dat = 0;​ SDA = 1; _nop_(); _nop_();​ for (i = 0; i < 8; i++) { dat <<= 1; SCL = 1; _nop_(); _nop_(); dat |= SDA; SCL = 0; }​ return dat;}​// 初始化BH1750光强度传感器void BH1750_Init(){ I2C_Start(); I2C_SendByte(BH1750_ADDR); I2C_SendByte(0x10); // 采用连续高分辨率模式 I2C_Stop();}​// 读取光强度数值unsigned int BH1750_ReadValue(){ unsigned int value;​ I2C_Start(); I2C_SendByte(BH1750_ADDR + 1); // I2C读模式 value = I2C_ReceiveByte() << 8; I2C_SendACK(); value |= I2C_ReceiveByte(); I2C_SendNAK(); I2C_Stop();​ return value;}​// 主函数void main(){ unsigned int lightValue; char str[16];​ BH1750_Init(); // 初始化BH1750​ while (1) { lightValue = BH1750_ReadValue(); // 读取光强度数值​ sprintf(str, "Light: %d lx", lightValue); // 打印光照强度值 // 在这里你可以将字符串通过串口或者LCD显示出来​ Delay(500); // 延时一段时间再读取 }}​4.2 主项目逻辑代码#include <reg52.h> // 单片机头文件#include <stdio.h> // 标准输入输出库​// 定义IO口连接的引脚sbit Voice_SCK = P1^0; // 语音模块时钟引脚sbit Voice_SI = P1^1; // 语音模块数据输入引脚sbit Voice_SO = P1^2; // 语音模块数据输出引脚sbit Voice_CS = P1^3; // 语音模块片选引脚​sbit Light_SCL = P2^0; // 光强度传感器SCL引脚sbit Light_SDA = P2^1; // 光强度传感器SDA引脚​sbit Curtain_Open = P3^0; // 窗帘开启控制引脚sbit Curtain_Close = P3^1; // 窗帘关闭控制引脚​// 初始化语音识别模块void Voice_Init(){ // 在此处编写语音识别模块的初始化代码}​// 语音识别处理函数void Voice_Process(){ // 在此处编写语音识别的处理代码}​// 初始化光强度传感器void Light_Init(){ // 在此处编写光强度传感器的初始化代码}​// 读取光强度传感器数值int Light_ReadValue(){ // 在此处编写读取光强度传感器数值的代码 // 并返回光强度数值}​// 控制窗帘打开void Curtain_OpenControl(){ // 在此处编写控制窗帘打开的代码}​// 控制窗帘关闭void Curtain_CloseControl(){ // 在此处编写控制窗帘关闭的代码}​// 主函数void main(){ // 初始化语音识别模块 Voice_Init();​ // 初始化光强度传感器 Light_Init();​ while (1) { // 处理语音识别 Voice_Process();​ // 读取光强度数值 int lightValue = Light_ReadValue();​ // 检测光强度,根据阈值决定窗帘是否关闭 if (lightValue > 阈值) { Curtain_CloseControl(); }​ // 在指定时间段内,打开或关闭窗帘 if (在时间段内) { Curtain_OpenControl(); } else { Curtain_CloseControl(); } }}​
  • [技术干货] 基于单片机设计的家用自来水水质监测装置
    一、前言本文介绍基于单片机设计的家用自来水水质监测装置。利用STM32F103ZET6作为主控芯片,结合水质传感器和ADC模块,实现对自来水水质的检测和监测功能。通过0.96寸OLED显示屏,将采集到的水质数据以直观的方式展示给用户。随着人们对健康意识的提高和环境保护的重视,水质安全已经成为人们生活中一个重要的议题。自来水作为我们日常生活中最主要的饮用水来源之一,其水质的安全与否直接关系到我们的健康。本设计采用了先进的STM32F103ZET6主控芯片,具备强大的处理能力和丰富的外设接口。通过水质传感器,可以实时采集与水质相关的模拟信号。然后,通过ADC模块将模拟数据转换为数字信号,再经过算法处理得到相应的水质参数。最后,将结果通过0.96寸OLED显示屏进行展示,用户可以清晰地了解自来水的水质状况。该装置特点:易于携带、操作简单、实时性好、精度高。用户只需将传感器浸入自来水中,即可获取到水质参数,并通过显示屏直观地了解水质状况,为家庭提供了一个简单方便的水质监测解决方案。二、硬件选型【1】主控芯片:STM32F103ZET6,这是一款基于ARM Cortex-M3内核的高性能微控制器。具有丰富的外设接口和较大的存储容量,适合用于处理水质传感器的数据采集和处理。【2】水质传感器:自来水水质监测的传感器。【3】显示屏:选择0.96寸OLED显示屏,可以在小尺寸的装置上显示采集到的水质数据。OLED显示屏具有高对比度、低功耗和快速刷新的特点,适合嵌入式应用。三、常见的水质传感器以下是一些常见的水质传感器类型,可用于家用自来水水质监测装置:【1】pH传感器:用于测量水的酸碱度,即pH值。pH传感器通常基于玻璃电极原理,可以提供准确的pH值。【2】溶解氧传感器:用于测量水中的溶解氧含量。溶解氧传感器可以采用膜式传感器或电极式传感器,根据测量原理的不同,提供溶解氧浓度的准确值。【3】浊度传感器:用于测量水中的悬浮颗粒物的浓度或水的浊度。浊度传感器可以采用光散射原理或光吸收原理进行测量。【4】电导率传感器:用于测量水中的电导率,即水的导电性。电导率传感器可以提供水中的总溶解固体(TDS)值或盐度值。四、家用自来水的水质标准以下是常见水质指标和标准参考:【1】pH值:pH值表示水的酸碱度,一般应在6.5-8.5之间。【2】浑浊度:浑浊度表示水中悬浮颗粒物的浓度,常用浊度单位为NTU(涡轮比色法浊度单位)。根据国际标准,家用自来水的浑浊度应小于1 NTU。【4】溶解氧:溶解氧表示水中溶解的氧气含量,通常以毫克/升(mg/L)为单位。对于家用自来水,溶解氧的标准范围可以在5-8 mg/L之间。【5】铁和锰:铁和锰是常见的金属元素,高浓度的铁和锰会给水带来颜色和异味。根据标准,家用自来水中的铁含量应小于0.3 mg/L,锰含量应小于0.1 mg/L。【6】氟化物:氟化物是一种有益的微量元素,但高浓度的氟化物对人体有害。一般而言,家用自来水中的氟化物含量应小于1.5 mg/L。【7】各种有害物质:家用自来水应符合国家或地区的相关法规和标准,以确保其不含有害物质,如重金属、有机污染物、农药残留等。五、硬件代码5.1 采集数据显示#include "stm32f10x.h"#include "oled.h"​// 定义ADC采集通道和引脚#define ADC_CHANNEL 0 // 假设使用ADC1的通道0#define ADC_PIN GPIO_Pin_0#define ADC_PORT GPIOA​// 定义OLED屏幕相关参数#define OLED_WIDTH 128#define OLED_HEIGHT 64​// 全局变量uint16_t adc_value = 0; // ADC采集到的数值​// 函数声明void ADC_Configuration(void);void OLED_Init(void);​int main(void){ // 初始化ADC和OLED ADC_Configuration(); OLED_Init();​ while (1) { // 启动ADC转换 ADC_SoftwareStartConvCmd(ADC1, ENABLE);​ // 等待ADC转换完成 while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);​ // 读取ADC转换结果 adc_value = ADC_GetConversionValue(ADC1);​ // 在OLED上显示ADC采集的数值 char str[10]; sprintf(str, "%4d", adc_value); OLED_ShowString(0, 0, str); // 在坐标(0, 0)位置显示字符串​ // 延时一段时间 for (uint32_t i = 0; i < 100000; i++); }}​// ADC配置函数void ADC_Configuration(void){ ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能ADC1和GPIOA的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置ADC引脚为模拟输入 GPIO_InitStructure.GPIO_Pin = ADC_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(ADC_PORT, &GPIO_InitStructure); // ADC配置 ADC_DeInit(ADC1); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure);​ // 配置ADC通道 ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SampleTime_13Cycles5);​ // 使能ADC ADC_Cmd(ADC1, ENABLE);​ // 开启ADC校准 ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)) ; ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)) ;​ // 设置ADC转换序列 ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SampleTime_13Cycles5);}​// OLED屏幕初始化函数void OLED_Init(void){ // 初始化OLED屏幕 OLED_Init(); OLED_Clear();}​5.2 oled.h#ifndef __OLED_H__#define __OLED_H__​#include "stm32f10x.h"​#define OLED_WIDTH 128#define OLED_HEIGHT 64​void OLED_Init(void);void OLED_Clear(void);void OLED_WriteByte(uint8_t data, uint8_t cmd);void OLED_SetPos(uint8_t x, uint8_t y);void OLED_ShowChar(uint8_t x, uint8_t y, char ch);void OLED_ShowString(uint8_t x, uint8_t y, const char* str);​#endif /* __OLED_H__ */​5.3 oled.c#include "OLED.h"​const uint8_t OLED_GRAM[128][8] = {0}; // OLED显示缓存​void OLED_Init(void){ // ... 初始化OLED屏幕的相关操作 ...}​void OLED_Clear(void){ // 清空OLED显示缓存(全黑) memset((void*)OLED_GRAM, 0x00, sizeof(OLED_GRAM));​ // 更新OLED屏幕显示 OLED_SetPos(0, 0); for (uint8_t i = 0; i < 8; i++) { OLED_WriteByte(0xb0 + i, 0x00); // 设置页地址(0-7) OLED_WriteByte(0x00, 0x00); // 设置列地址低4位(0-3) OLED_WriteByte(0x10, 0x00); // 设置列地址高4位(4-7)​ for (uint8_t j = 0; j < 128; j++) { OLED_WriteByte(0x00, 0x40); // 写入数据,全黑 } }}​void OLED_WriteByte(uint8_t data, uint8_t cmd){ // ... 将数据写入OLED屏幕的具体操作 ...}​void OLED_SetPos(uint8_t x, uint8_t y){ // ... 设置OLED屏幕显示位置的具体操作 ...}​void OLED_ShowChar(uint8_t x, uint8_t y, char ch){ uint8_t c = ch - ' '; // 获取字库中的字模​ if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return;​ for (uint8_t i = 0; i < 6; i++) { uint8_t line = cFont6x8[c][i];​ for (uint8_t j = 0; j < 8; j++) { if (line & 0x01) { OLED_GRAM[y + j][x + i] = 1; } else { OLED_GRAM[y + j][x + i] = 0; }​ line >>= 1; } }​ // 更新OLED屏幕显示 OLED_SetPos(x, y / 8); for (uint8_t i = 0; i < 8; i++) { OLED_WriteByte(0xb0 + (y / 8) + i, 0x00); // 设置页地址 OLED_WriteByte((x & 0x0f), 0x00); // 设置列地址低4位 OLED_WriteByte((x >> 4) | 0x10, 0x00); // 设置列地址高4位​ for (uint8_t j = 0; j < 128; j++) { OLED_WriteByte(OLED_GRAM[y + i][j], 0x40); // 写入数据 } }}​void OLED_ShowString(uint8_t x, uint8_t y, const char* str){ while (*str != '\0') { OLED_ShowChar(x, y, *str++); x += 6;​ if (x >= OLED_WIDTH) { x = 0; y += 8; } }}​