• [问题求助] 如何实现 除法器设计实例-设计目标
    设计一个32位无符号整数除法器,输入为两个32位无符号整数A和B,输出为32位商D和32位的余数R速度要求尽可能快(吞吐量尽可能大)电路面积、功耗不限可以要求外部给出合适的时钟和复位信号start信号为高电平时启动运算除数为0时应该给出错误信号(置err信号为高),运算结束时应该给出完成信号(置ok信号为高)
  • [分享交流] 20240922上海海思-基于华为海思星闪前沿技术的单片机与嵌入式课程教学改革探索实践
    为了较好地分享海思最新的技术进展与高校合作规划,共同探讨电子工业高质量应用型人才培养之路,深入推进校企合作和产教融合,切实提高专业建设质量、课程教学质量和人才培养质量,把华为海思MCU技术与星闪前沿技术与课程教学深度结合,真正的实现校企协同育人,扎实做好产教融合工作,为产业输送更多的优秀人才,9月22日,单片机原理与应用虚拟教研室多位教师应邀参加于上海海思创新与生态实验室举办的上海海思-高校人才培养交流会,沈阳理工大学国家一流课程负责人、辽宁省本科教学名师、华为开发者布道师、星闪科技创新团队指导教师张东阳教授作基于海思MCU/星闪前沿技术的单片机与嵌入式课程教学改革主题报告,来自全国各地的二十多位单片机与嵌入式国家一流课程负责人和优秀教师参加了会议。会议首先由海思高校生态合作总监谢晶带领与会教师参观上海海思展厅,海思生态与伙伴发展部部长赵秋静致欢迎词并简要说明了本次会议的目的和意义,随后谢晶总监作海思MCU/星闪前沿技术分享,并发布了新的基于海思MCU/星闪技术的全国大学生嵌入式芯片与系统设计竞赛-海思赛道的竞赛计划。海思致力于使能万物互联的智能终端,成为千行百业数字化、网联化、智能化、低碳化的产业基石,并着眼未来,把高校开发者培养作为自己的重要战略与责任,把高校作为自己实现产业创新最为重要的推动力量,本次会议以高校人才培养为主题,以灵感碰撞为目标,以校企合作惠及高校广大师生,以产教融合惠及更多产业。张东阳教授作为首批华为开发者布道师为大家讲述了华为海思前沿技术融入课程教学实施计划,并作了基于海思MCU/星闪前沿技术的单片机与嵌入式课程教学改革主题报告,报告主要包括三个方面的内容:一是单片机与嵌入式课程教学改革所取得的良好教学成效及其目前在高质量应用型人才方面所面临的主要问题;二是基于星闪前沿技术+海思MCU的单片机和嵌入式课程教学改革、实践教学改革和课外创新团队建设;三是通过深入开展华为海思嵌入式芯片-星闪应用领域“课-训-赛-用”综合人才培养合作,可以探索一套高效的基于前沿技术的课程教学模式和人才培养模式,帮助高校师生拓展行业视野,提升技术知识,丰富实践经验,并应用前沿技术,围绕真实的应用环境,开发真实应用项目,解决真实问题,为自主可控的产业生态快速培养大批高质量嵌入式开发工程师,为师生的未来发展拓展出无限的发展空间。与会教师和华为海思生态专家与技术专家就如何应用星闪前沿技术+海思MCU深入开展单片机与嵌入式课程教学改革、实践教学改革、创新团队建设和高质量应用型人才培养,把海思前沿技术与高校教学深度结合,较好地实现校企合作产教融合协同育人,以校企合作惠及高校广大师生,以产教融合惠及更多产业,为产业输送优秀人才等进行了深入的交流,并达成了广泛的共识。
  • [专题汇总] 2024年6月嵌入式项目开发专题总汇
    一、前言在当前的数字化和智能化浪潮中,技术的跨界融合与创新应用为各行各业带来了前所未有的发展机遇。从音视频处理到物联网通信,从嵌入式系统开发到云计算服务,技术的不断迭代和进步正推动着社会向更高效、更智能的方向前进。本文汇总一系列技术项目文章,涵盖了从视频转码、嵌入式系统设计到物联网通信等多个领域,为读者提供一个全面的技术视野和实践指南。文章开篇,深入探讨了利用FFmpeg这一强大工具,将视频转码成GIF图片并调整其大小的技巧,为多媒体内容创作者提供了高效解决方案。紧接着,通过结合STC90C51/52单片机与多种传感器模块,如HC-SR04超声波测距、DS1302实时时钟、EEPROM-AT24C02存储器、PCF8591 AD转换器、DS18B20温度传感器等,一系列实用的硬件控制系统应运而生,不仅涵盖了测距仪设计、环境监测、数据存储等基础应用,还实现了智能温控、红外线解码等进阶功能,全面展示了嵌入式开发的魅力。在软件与算法层面,介绍了如何在Android平台上利用QT与OpenCV实现人脸识别与目标检测,为移动应用开发者打开了计算机视觉的大门。文章还触及了物联网通讯协议的核心,对比了MQTT 3.1.1与5.0版本,揭示了其在物联网领域的关键作用,并通过Python实例展示了如何与华为云物联网服务器进行数据交互,为云端融合应用提供了实战演练。最后,是介绍单片机开发的基础技能,如STC51系列单片机的串口通讯、LCD1602字符屏显示控制、外部中断与定时器的综合应用,以及对NE555脉冲频率的检测,这些都为初学者构建了坚实的知识框架。二、技术文章汇总【1】利用ffmpeg转码视频为gif图片,调整gif图片的大小cid:link_0在今天的数字化时代,视频内容无处不在,但有时候我们需要将视频片段转化为更加轻便、易于分享的格式,如GIF图片。GIF图片因其循环播放的特性和较小的文件大小,在社交媒体和在线聊天中备受欢迎。本篇文章将介绍如何使用ffmpeg这一强大的音视频处理工具,将视频转码为GIF图片,并教您如何调整GIF图片的大小,以便适应不同的分享场景。【2】STC90C51+HC-SR04超声波测距模块完成测距仪设计(测量距离实现壁障)(STC90C52)cid:link_5超声波测距技术凭借其高精度、非接触式测量的特点,在工业、医疗和机器人导航等领域得到了广泛应用。本文将探讨如何使用STC90C51单片机和HC-SR04超声波测距模块设计一个简易的测距仪,实现距离的测量和壁障功能。这不仅是一个有趣的项目,还能帮助读者深入理解超声波测距的原理和单片机编程的应用。【3】QT+OpenCV在Android上实现人脸实时检测与目标检测cid:link_6在移动应用开发中,人脸实时检测和目标检测功能正变得越来越重要。QT和OpenCV是两个强大的工具,分别用于开发用户界面和进行图像处理。本文将指导读者如何在Android平台上使用QT和OpenCV实现人脸实时检测与目标检测功能。这不仅对于移动应用开发者来说是一个实用的教程,也为图像处理爱好者提供了学习和实践的机会。【4】DS1302实时时钟芯片_读写时间实现电子钟功能(STC90C52)cid:link_7DS1302是一款流行的实时时钟芯片,广泛应用于电子钟、定时器和其他需要准确时间显示的设备中。本篇文章将详细介绍如何使用STC90C52单片机与DS1302实时时钟芯片进行时间读写操作,实现电子钟功能。通过本教程,读者可以学习到DS1302芯片的工作原理、与单片机的连接方式以及时间读写的编程方法。【5】EEPROM-AT24C02存储器芯片数据读写(STC90C52)cid:link_1EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种非易失性存储器,常用于存储需要长期保存的数据。AT24C02是EEPROM存储器芯片中的一种,具有容量适中、操作简便的特点。本文将探讨如何在STC90C52单片机上实现对AT24C02存储器芯片的数据读写操作。通过本教程,读者可以学习到EEPROM存储器的基本原理、与单片机的接口设计以及数据读写的编程方法。【6】PCF8591_AD转换芯片完成光敏、温度模拟量采集(STC90C52)cid:link_8在嵌入式系统设计中,模拟信号的采集和处理是不可或缺的一部分。PCF8591是一款常用的AD转换芯片,能够将模拟信号转换为数字信号,方便单片机等数字系统进行处理。本文将介绍如何使用STC90C52单片机与PCF8591 AD转换芯片进行光敏和温度模拟量的采集。通过学习本教程,读者将掌握AD转换芯片的基本原理、与单片机的连接方式以及如何通过编程实现模拟信号的采集和处理。【7】STC51 配置串口1实现数据发送和接收(STC90C52)cid:link_2串口通信是单片机与外部设备之间常用的一种通信方式。STC90C52单片机提供了多个串口接口,可以实现与其他设备的通信。本文将详细介绍如何使用STC51单片机配置串口1,并实现数据的发送和接收功能。通过本教程,读者将学习到串口通信的基本原理、配置方法以及如何通过编程实现数据的发送和接收,为后续的嵌入式系统设计和开发打下基础。【8】STC51控制LCD1602字符屏显示字符数据(STC90C52)cid:link_9LCD1602字符屏是一种常见的显示设备,广泛应用于各种嵌入式系统中。STC90C52单片机可以通过编程控制LCD1602字符屏显示指定的字符数据。本文将介绍如何使用STC51单片机控制LCD1602字符屏,并展示如何显示字符数据。通过学习本教程,读者将掌握LCD1602字符屏的工作原理、与单片机的连接方式以及如何通过编程控制显示内容,为嵌入式系统中的界面设计提供有力的支持。【9】定时器+外部中断实现NEC红外线协议解码(STC90C52)cid:link_10红外线协议解码是智能家居、遥控系统等领域中常见的需求。NEC红外线协议是一种常用的红外线通信协议,广泛应用于各种遥控器中。本文将介绍如何使用定时器和外部中断实现NEC红外线协议的解码功能,以便在嵌入式系统中实现对遥控信号的识别和处理。通过学习本教程,读者将了解NEC红外线协议的基本原理、解码方法以及如何通过编程实现解码功能,为智能家居和遥控系统的开发提供技术支持。【10】读取DS18B20温度、测量环境温度信息(单只DS18B20写法)(STC90C52)cid:link_11DS18B20是一款常用的数字温度传感器,具有高精度、高可靠性等特点,广泛应用于各种温度测量场合。本文将详细介绍如何使用STC90C52单片机读取DS18B20温度传感器的数据,并测量环境温度信息。通过学习本教程,读者将掌握DS18B20温度传感器的工作原理、与单片机的连接方式以及如何通过编程读取温度数据,为嵌入式系统中的温度测量和控制提供有力的支持。同时,本教程还将介绍单只DS18B20的写法,方便读者在实际应用中灵活使用。【11】基于STC51实现的智能温控系统(STC90C52)cid:link_12随着现代科技的快速发展,智能温控系统已成为工业自动化、家庭智能化等领域的重要组成部分。本文将介绍如何使用STC90C52单片机设计一个基于STC51的智能温控系统。该系统能够实时检测环境温度,并根据预设的温度阈值自动调节加热或制冷设备,实现温度的精准控制。通过本教程的学习,读者将掌握智能温控系统的基本原理、硬件设计和软件编程,为后续的工业自动化和智能家居项目开发提供宝贵的经验。【12】检测NE555脉冲发生器产生的频率(STC90C52)cid:link_3NE555是一款常用的脉冲发生器芯片,能够产生稳定的脉冲信号,广泛应用于各种电子设备中。本文将介绍如何使用STC90C52单片机检测NE555脉冲发生器产生的频率。通过学习本教程,读者将了解NE555脉冲发生器的工作原理、输出信号的特性以及如何通过单片机编程实现对脉冲频率的检测和测量。这对于电子爱好者和工程师们来说,是一个既有趣又实用的项目。【13】目前主流的MQTT 3.1.1和MQTT 5.0协议介绍cid:link_13MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅消息传输协议,广泛应用于物联网(IoT)领域。随着物联网技术的不断发展,MQTT协议也在不断更新和完善。本文将介绍目前主流的MQTT 3.1.1和MQTT 5.0协议的特点和优势,帮助读者了解这两个版本的差异以及它们在实际应用中的适用性。通过学习本教程,读者将更好地掌握MQTT协议的原理和应用,为物联网项目的开发提供有力支持。【14】使用Python连接华为云物联网服务器与服务器完成数据交互cid:link_4华为云物联网服务器提供了丰富的功能和服务,支持各种物联网设备的接入和数据交互。Python作为一种流行的编程语言,具有简洁、易读、易学的特点,广泛应用于各个领域。本文将介绍如何使用Python连接华为云物联网服务器,实现与服务器之间的数据交互。通过学习本教程,读者将了解华为云物联网服务器的基本概念和架构,掌握使用Python连接服务器的步骤和方法,为物联网项目的开发提供实践指导。
  • [技术干货] STC90C51+HC-SR04超声波测距模块完成测距仪设计(测量距离实现壁障)
    一、前言1.1 功能介绍本项目的开发背景源于对机器人或自动化设备中安全壁障功能的需求。在诸如无人驾驶车辆、自动搬运机器人、智能家居系统以及工业自动化等应用场景中,为了避免设备与环境中的障碍物发生碰撞,确保设备和人员安全,实时、准确地检测并测量与障碍物之间的距离变得至关重要。HC-SR04超声波测距模块以其高精度、低成本和易于集成的特点,成为了实现这一功能的理想选择。该模块通过发射超声波并接收其反射信号来测量距离,具有测量范围广、响应速度快、抗干扰能力强等优点。结合ST90C51微控制器的强大处理能力和丰富的I/O接口,可以实现对HC-SR04模块的精确控制,并快速处理测距数据,实现实时的壁障功能。本项目结合ST90C51微控制器和HC-SR04超声波测距模块,设计并实现一个能够实时测量与障碍物之间距离,并根据距离信息执行相应壁障动作的系统。通过编写程序算法,可以实现对测量数据的分析、判断和控制,当检测到设备接近障碍物时,自动触发壁障机制,如减速、转向或停止,以避免发生碰撞事故。这一项目的成功实施,不仅可以提升设备的安全性能,降低维护成本,还可以为相关领域的自动化和智能化发展提供有力的技术支持。1.2 HC-SR04超声波测距模块介绍HC-SR04超声波测距模块是一种基于超声波原理进行测距的传感器模块,被广泛应用于机器人导航、智能家居、安防系统等领域。该模块主要由两个压电陶瓷超声传感器构成,一个用于发射超声波信号,另一个用于接收被目标物体反射回来的超声波信号。由于超声波信号在空气中的传播速度已知(约为340米/秒),模块通过测量超声波从发射到接收的时间差,可以精确计算出目标与传感器之间的距离。HC-SR04模块的主要技术参数包括:使用电压为DC 5V,静态电流小于2mA,感应角度不大于15度,探测距离范围从2cm到450cm(部分资料提到400cm),并具有高精度特性,可达0.2cm或0.3cm。模块的工作原理是通过IO口触发测距,给至少10us的高电平信号后,模块会自动发送8个40kHz的方波,并自动检测是否有信号返回。当有信号返回时,模块会通过IO口输出一个高电平信号,高电平持续的时间就是超声波从发射到返回的时间。根据这个时间差和超声波在空气中的传播速度,可以计算出目标与传感器之间的距离。HC-SR04超声波测距模块的性能稳定,测距精确,能够与国外的SRF05、SRF02等超声波测距模块相媲美。其接线方式简单,通常包括VCC(电源正极)、Trig(控制端)、Echo(接收端)和GND(电源负极)四个引脚。此外,HC-SR04模块还提供了丰富的编程接口和参考程序,支持多种微控制器和编程语言,如Arduino、C51、PIC等,方便用户进行二次开发和集成。HC-SR04超声波测距模块以其高精度、高可靠性和易用性,成为了许多项目中不可或缺的一部分,为机器人导航、智能家居、安防系统等领域的发展提供了有力的支持。二、代码实现2.1 main.c#include <reg51.h>#include <INTRINS.H>#include "delay.h"#include "type.h"#include "uart.h"#include "ds1302.h"#include "pcf8591.h"//#include "key.h"//#include "led.h"#include "timer.h"//#include "exti.h"//#include "infrared.h"//#include "ds18b20.h"//#include "at24c02.h"​sbit ECHO=P1^0; //超声波的回响信号输出脚sbit TRIG=P1^1; //触发超声波测距的引脚u32 timt0_cnt=0; //记录定时器0溢出的次数u16 time_val=0;float distance=0.0; //保存测量的距离int main(){ ECHO=0; TRIG=0; UART_Init(); //初始化串口波特率为4800 while(1) { TRIG=1;//触发测距 delay20us(); //延时20us TRIG=0; //停止触发 while(ECHO==0){} //等待回响信号返回 Timer0_16bit_Init(65535); //初始化定时器0,并开始计数 while(ECHO==1){} //等待回响信号结束 TR0=0; //关闭定时器0 time_val=(TH0<<8|TL0)+timt0_cnt*65535; //计算时间 distance=time_val/58.0; //得到距离。单位是厘米 printf("distance=%f CM\r\n",distance); timt0_cnt=0; //溢出次数清零 DelayMs(1000); //延时1秒 }}​​2.2 exit.c#include "exti.h"/*配置外部中断0下降沿触发中断IO口: P3.2*/sfr IPH=0xB7; //定义特殊功能寄存器void EXTI0_Init(void){ IPH|=1<<0; //配置PX0H=1 PX0=1; //当PX0H=1且PX0=1时,外部中断0为最高优先级中断(优先级3) EA=1; //开启总中断 IT0=1; //外部中断0下降沿触发 EX0=1; //允许外部中断0产生中断}​///*//外部中断0中断服务函数//*///void EXTI0_IRQHandler(void) interrupt 0//{// LED=~LED; //改变LED灯的状态// IE0=0; //清除外部中断0的标志位//}​/*配置外部中断1下降沿触发中断IO口: P3.2*/void EXTI1_Init(void){ EA=1; //开启总中断 IT1=1; //外部中断1下降沿触发 EX1=1; //允许外部中断1产生中断}/*外部中断1中断服务函数IO口: P3.3*/void EXTI1_IRQHandler(void) interrupt 2{ while(1){} //死循环 LED=~LED; //改变LED灯的状态 IE1=0; //清除外部中断1的标志位}​2.3 exit.h#ifndef _EXTI_H#define _EXTI_H#include <reg51.h>#include "led.h"void EXTI0_Init(void);void EXTI1_Init(void);#endif
  • [技术干货] DS18B20温度传感器完整使用介绍(配合51单片机)
    DS18B20是一款由Maxim Integrated(原Dallas Semiconductor)生产的数字温度传感器,以其高精度、低功耗、灵活的接口方式和易于使用的特性,在各种温度监测应用中被广泛采用。以下是DS18B20的详细介绍:基本特性数字输出:DS18B20直接输出数字信号,与传统的模拟温度传感器相比,它简化了与微控制器的接口设计,减少了对模数转换器(ADC)的需求。单总线接口:采用独特的单线(1-Wire)通信协议,仅需一根数据线即可完成与微控制器的双向通信,降低了硬件成本和复杂度。此外,多个DS18B20可以通过这条单线总线连接在一起,每个传感器都有一个唯一的64位序列号,使得系统能够识别并单独寻址每个传感器。温度测量范围:DS18B20的测量范围广泛,从-55°C到+125°C(-67°F至+257°F),满足了大多数常规温度测量需求。高精度与分辨率:在-10°C到+85°C范围内,其精度通常可达到±0.5°C;分辨率可在9位至12位之间调节,默认为12位,对应分辨率最高达0.0625°C。低功耗:工作电压范围为3V至5.5V,静态电流极低,非常适合电池供电的应用。多功能性:DS18B20支持多种工作模式,包括温度转换模式、读取温度模式、配置模式等,用户可以根据需要设置不同的工作参数。封装形式多样:DS18B20提供了多种封装形式,如TO-92、SOP8、DIP8等,适应不同的安装需求,包括管道式、螺纹式、磁铁吸附式、不锈钢封装式等,适用于各种恶劣或狭小环境的温度测量。应用场景DS18B20因其灵活性和可靠性,被广泛应用于多种领域,包括但不限于:家用电器(如冰箱、空调温度监控)工业自动化(如设备温度监控、环境温度控制)农业(如温室大棚温度管理)数据中心和服务器机房的温度监测汽车电子(发动机温度监控)医疗设备温度监控水族箱和养鱼场的水温控制使用方法使用DS18B20通常需要遵循一定的通信协议,包括初始化、搜索传感器、读取或设置寄存器等步骤。在编程时,开发者需编写相应的代码来控制单总线的时序,或者利用已有的库函数(如Arduino、Raspberry Pi等平台上的库)来简化操作。通过简单的函数调用,即可读取到当前的温度值。原理图实验板上的DS18B20模块接在单片机的P3.7 IO口上,在插入DS18B20芯片时,圆弧朝上插入,具体效果可以看上面图片。DS18B20的工作原理介绍DS18B20的温度检测与数字数据输出全集成于一个芯片之上,从而抗干扰力更强。它的一个工作周期可分为两个部分,温度检测和数据处理。DS18B20内部有三种形态的存储器:(1) ROM只读存储器:用于存放 DS18B20ID 编码,其前 8 位是单线系列编码(DS18B20 的编码是19H),后面 48 位是芯片唯一的序列号,最后8位是以上56的位的 CRC 码(冗余校验),数据在芯片出厂时设置不可由用户更改。DS18B20 共 64 位 ROM(8+48+8)。(2) RAM数据暂存器:用于内部计算和数据存取,数据在掉电后丢失,DS18B20 共 9 个字节 RAM,每个字节为 8 位。第 1、 2 个字节是温度转换后的数据值信息,第 3、 4 个字节是用户 EEPROM(常用于温度报警值储存)的镜像。在上电复位时其值将被刷新。第 5 个字节则是用户第 3 个 EEPROM的镜像。第 6、 7、 8 个字节为计数寄存器,是为了让用户得到更高的温度分辨率而设计的,同样也是内部温度转换、计算的暂存单元。第 9 个字节为前 8 个字节的 CRC 码。(3) EEPROM非易失性记忆体:用于存放长期需要保存的数据。比如: 上下限温度报警值和校验数据,DS18B20共有3个字节的EEPROM,并在 RAM 都存在镜像,以方便用户操作。DS18B20默认工作在12位分辨率模式,转换后得到的12位数据,存储在DS18B20的两个8比特的RAM中(最前面的两个字节),二进制中的前面5位是符号位,如果测得的温度大于0,这5位为0,只要将测到的数值乘于0.0625即可得到实际温度;如果温度小于0,这5位为1,测到的数值需要取反加1再乘于0.0625即可得到实际温度。数据提取也可以使用位运算,读取出来的数据是2个字节一共16位(H和L),最低4位是小数位,剩下的是整数位。如果读取的数据是负数,需要-1再取反即可得到真实数据。例如:int temp=0;temp=DS18B20_ReadTemp(); //读取一次DS18B20采集的温度(返回H+L位)if(temp<0) //如果温度是负数{ temp=temp-1; temp=~temp; printf("DS18b20=-%d.%d\r\n",temp>>4,temp&0xF);}else{printf("DS18b20=%d.%d\r\n",temp>>4,temp&0xF);}​读取DS18B20温度示例代码(单只DS18B20情景)下面代码演示了循环读取DS18B20温度的过程,在主函数里1秒的间隔读取一次温度。在编写DS18B20时序代码时,要注意时间的把控。当前实验板的环境:采用STC90C516RD单片机,晶振是12MHZ,工作在12T模式下,代码中执行一条i++语句大概消耗的时间是12us。 程序中的延时时间,都是通过该时间推算的,如果程序要移植到其他单片机上,要注意时间的问题。(硬件平台说明:CPU是STC90C516RD、晶振频率12MHZ 、工作在12T模式下、一个机器周期为1us时间)示例代码:#include <reg51.h>/*DS18B20硬件接口: P3.7*/sbit DS18B20_GPIO=P3^7;int DS18B20_ReadTemp(void);/*说明: 在12MHZ晶振下,12T模式下,i++消耗的时间差不多是12us*//*函数名称:u8 DS18B20_Init(void)函数功能:向DS18B20发送复位脉冲,并检测应答信号返 回 值:1表示失败,0表示成功说明: 51单片机IO口默认输出高电平*/u8 DS18B20_ResetSignal(void){ u8 i=0; //1. 发送复位信号 DS18B20_GPIO=0;//将总线拉低480us i=50; while(i--){} //延时600us ,最少480us i=0; DS18B20_GPIO=1;//然后释放(拉高)总线,如果DS18B20做出反应会将在15us~60us后总线拉低 //2. 等待DS18B20拉低总线 while(DS18B20_GPIO) { i++; if(i>10)return 1;//失败 ,大概120us } //3. 等待DS18B20释放总线 i=0; while(DS18B20_GPIO==0) //60us~240us { i++; if(i>20)return 1;//失败,大概240us } return 0;//初始化成功}/*函数名称:u8 DS18B20_WriteByte(void)函数功能:向DS18B20写入一个字节的数据函数形参:写入的字节数据*/void DS18B20_WriteByte(u8 byte){ u16 i=0,j=0; for(j=0;j<8;j++) { DS18B20_GPIO=0;//每写入一位数据之前先把总线拉低1us i++; //+1消耗的时间是12us DS18B20_GPIO=byte&0x01;//然后写入一个数据,从最低位开始 i=6; while(i--){}//持续时间最少60us,这里大概72us DS18B20_GPIO=1;//然后释放总线 byte>>=1;//继续发送 }}/*函数名称:u8 DS18B20_ReadByte(void)函数功能:从DS18B20读取一个字节的数据返 回 值:读到的数据*/u8 DS18B20_ReadByte(void){ u8 byte=0; u16 i=0,j=0; for(j=0;j<8;j++) { DS18B20_GPIO=0;//先将总线拉低1us i++;//+1消耗的时间是12us DS18B20_GPIO=1;//然后释放总线 i++; i++;//至少等待15us的时间,在读取数据 byte>>=1; //先从低位开始接收数据 if(DS18B20_GPIO)byte|=0x80; i=4; //读取完之后等待48us再接着读取下一个数据 while(i--){} } return byte;}/*函数名称:u16 DS18B20_ReadTemp(void)函数功能:读取一次DS18B20的温度数据返 回 值:读取的温度值注意: 返回值要使用有符号的数据类型,因为温度可以返回负数。*/int DS18B20_ReadTemp(void){ int temp=0;//存放温度数据 u8 TH,TL; //第一步: 启动温度转换 DS18B20_ResetSignal(); //发送复位脉冲并检测应答信号 DS18B20_WriteByte(0xcc);//跳过ROM操作命令 DS18B20_WriteByte(0x44);//温度转换命令 //第二步: 读取温度 DS18B20_ResetSignal();//发送复位脉冲并检测应答信号 DS18B20_WriteByte(0xcc);//跳过ROM操作命令 DS18B20_WriteByte(0xbe);//发送读取温度命令 TL=DS18B20_ReadByte();//读取温度值共16位,先读低字节 TH=DS18B20_ReadByte();//再读高字节 temp=TH<<8|TL; //合并成16位 return temp;}int main(){ int temp=0; UART_Init(); //初始化串口波特率为4800 while(1) { temp=DS18B20_ReadTemp(); if(temp<0) //如果温度是负数 { temp=temp-1; temp=~temp; printf("DS18b20=-%d.%d\r\n",temp>>4,temp&0xF); } else { printf("DS18b20=%d.%d\r\n",temp>>4,temp&0xF); } DelayMs(1000);}}
  • [技术干货] DS1302实时时钟芯片完整使用介绍(配合51单片机)
    DS1302是一款由美国DALLAS Semiconductor公司(现已被Maxim Integrated公司收购)设计的高性能、低功耗的实时时钟集成电路。这款芯片因其简单易用的接口和丰富的功能,在嵌入式系统、消费电子、工业控制等多个领域得到广泛应用。原理图寄存器寄存器的最低位是读写控制位,0 是写,1是读。寄存器里的数据是BCD* 码格式,得到十进制可以进行分离:data>>4*分离出十位,data&0x0F得到个位。秒寄存器说明:秒寄存器的位7定义为时钟暂停位。当此位设置为逻辑1时, 时钟振荡器停止,DS1302被置入低功率的备份方式, 其电源消耗小于 100 纳安(nanoamp)。 当把此位写成逻辑 0 时, 时钟将启动。控制寄存器说明:写保护寄存器的位7是写保护位。 开始 7 位(位 0-6) 置为零, 在读操作时总是读出零。 在对时钟或RAM 进行写操作之前, 位 7 必须为零。 当它为高电平时, 写保护位禁止对任何其它寄存器进行写操作。小时寄存器说明:小时寄存器的位 7 定义为 12 或 24 小时方式选择位。 当它为高电平时, 选择 12 小时方式, 在 12 小时方式下, 位 5 是 AM/PM 位, 此位为逻辑高电平表示 PM。在 24 小时方式下, 位 5 是第 2 个 10 小时位(20-23时)。上面寄存器地址,转换成16进制的地址如下:控制寄存器(写保护): (写)0x8E年寄存器地址: (写)0x8c (读)0x8c|0x01月寄存器地址: (写)0x88 (读)0x88|0x01日寄存器地址: (写)0x86 (读)0x86|0x01时寄存器地址: (写)0x84 (读)0x84|0x01分寄存器地址: (写)0x82 (读)0x82|0x01秒寄存器地址: (写)0x80 (读)0x80|0x01星期寄存器地址:(写)0x8a (读)0x8a|0x01时序图(1). 读数据时序上面的时序图是从DS1302寄存器读取数据的时序图,读取数据之前,需要先设置读取数据的寄存器地址,再接收DS1302返回的数据。从时序图里得知,开始传输数据之前,RST保持低电平,时钟线保持低电平,开始传输数据时,RST保持高电平。数据是先从低位开始传输,在上升沿改变数据,在下降沿保持数据稳定,数据传输完毕之后RST保持低电平。/*函数功能: 从DS1302指定寄存器里读取一个字节数据*/u8 DS1302_ReadByte(u8 addr){ u8 n=0,dat=0; DS1302_RST=1; //然后将DS1302_RST(CE)置高电平。 /*1. 设置读取的地址*/ for(n=0;n<8;n++) { DS1302_IO=addr&0x01;//数据从低位开始传送 addr>>=1; DS1302_SCLK=1;//数据在上升沿时,DS1302读取数据 DS1302_SCLK=0;//DS1302下降沿时,放置数据 } /*2. 读取数据*/ for(n=0;n<8;n++) { dat>>=1; if(DS1302_IO)dat|=0x80; DS1302_SCLK=1; DS1302_SCLK=0;//DS1302下降沿时,放置数据 } DS1302_RST=0; //必须的操作,复位时间 DS1302_IO=0; DS1302_IO=1;return dat; }(2). 写数据时序上面的时序图是向DS1302寄存器写入数据的时序图,写入数据之前,需要先设置写入数据的寄存器地址,再写入实际的数据。从时序图里得知,开始传输数据之前,RST保持低电平,时钟线保持低电平,开始传输数据时,RST保持高电平。数据是先从低位开始传输,在上升沿改变数据,在下降沿保持数据稳定,数据传输完毕之后RST保持低电平。/*函数功能: 向DS1302指定寄存器里写一个字节数据*/void DS1302_WriteByte(u8 addr,u8 dat){ u8 n; DS1302_RST=1; //然后将DS1302_RST(CE)置高电平。 /*1. 设置写入的地址*/ for(n=0;n<8;n++) { DS1302_IO=addr&0x01;//数据从低位开始传送 addr>>=1; DS1302_SCLK=1;//数据在上升沿时,DS1302读取数据 DS1302_SCLK=0; } /*2. 写入数据*/ for(n=0;n<8;n++) { DS1302_IO=dat&0x01; dat>>=1; DS1302_SCLK=1;//数据在上升沿时,DS1302读取数据 DS1302_SCLK=0; } DS1302_RST=0;//传送数据结束}以下是DS1302的主要特性和工作原理的详细介绍:主要特性时间与日期功能:DS1302能够提供精确的时钟/日历功能,包括年、月、日、周、时、分、秒的计时,并具有闰年补偿功能,有效至2100年。低功耗设计:适合电池供电的系统,工作电压范围宽泛,为2.5V至5.5V,且在待机模式下功耗极低。三线串行接口:通过简单的三线接口(SCLK、I/O、RST)与微控制器通信,支持同步串行数据传输,可进行读取和写入操作。内置RAM:包含31字节的静态随机存取存储器(SRAM),可用于存储与时间无关的用户数据。涓流充电能力:具有为备用电源(如电池)提供涓流充电的能力,并且涓流充电功能可以根据需求开启或关闭。自动调整功能:能够自动处理不同月份的天数差异,包括闰年的2月29日调整。时钟模式选择:支持12小时和24小时两种时间显示模式,具备AM/PM指示。工作温度范围:一般适用于-40°C至+85°C的工作温度范围,适合多种环境应用。工作原理初始化与通信:在与DS1302进行通信前,需要通过复位引脚(RST)发送一个高电平脉冲以初始化时钟芯片,之后通过串行时钟线(SCLK)和双向数据线(I/O)进行数据的读写操作。数据传输采用先写地址后读写数据的方式。读取时间数据:微控制器通过发送适当的命令序列,可以读取DS1302内部的时钟数据和RAM数据。时间数据以BCD码(二进制编码的十进制数)形式存储。设置时间与日期:同样,通过特定的命令序列,可以向DS1302写入新的时间或日期数据,以调整时钟。电源管理:DS1302设计有主电源(Vcc)和备用电源(Vbat)引脚,当主电源掉电时,芯片会自动切换到备用电源,保证时钟继续运行。BCD码转十进制BCD码是用4位二进制数来表示1位十进制数中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。BCD码这种编码形式利用了四个位元来储存一个十进制的数码,使二进制和十进制之间的转换变得方便。示例代码:/*函数功能: 将十进制数据转为BCD码*/u8 DEC_TO_BCD(u8 val){ return ((val/10)<<4)+val%10;}/*函数功能: 将BCD码数据转为十进制格式*/u8 BCD_TO_DEC(u8 val){return (val&0x0f)+(val>>4)*10;}DS1302示例代码下面代码里实现DS1302的寄存器读写,时间的设置与读取,在主函数里判断了之前DS1302是否正常工作,如果DS1302处于停止计时状态,就重新设置时间,在循环代码里,每1秒钟,向串口打印读取的时间。(硬件平台说明:CPU 是STC90C516RD 、晶振频率12MHZ、工作在12T模式下、一个机器周期为1us时间)示例代码:#include <reg51.h>//定义ds1302使用的IO口sbit DS1302_IO=P3^4;sbit DS1302_RST=P3^5;sbit DS1302_SCLK=P3^6;​u8 DS1302_TIME[7]; //存放读取的时间​/*函数功能: 将十进制数据转为BCD码*/u8 DEC_TO_BCD(u8 val){ return ((val/10)<<4)+val%10;}​/*函数功能: 将BCD码数据转为十进制格式*/u8 BCD_TO_DEC(u8 val){ return (val&0x0f)+(val>>4)*10;}​void DS1302_Init(void){ DS1302_RST=0; DS1302_SCLK=0;//先将DS1302_SCLK置低电平。}​/*函数功能: 向DS1302指定寄存器里写一个字节数据*/void DS1302_WriteByte(u8 addr,u8 dat){ u8 n; DS1302_RST=1; //然后将DS1302_RST(CE)置高电平。 /*1. 设置写入的地址*/ for(n=0;n<8;n++) { DS1302_IO=addr&0x01;//数据从低位开始传送 addr>>=1; DS1302_SCLK=1;//数据在上升沿时,DS1302读取数据 DS1302_SCLK=0; } /*2. 写入数据*/ for(n=0;n<8;n++) { DS1302_IO=dat&0x01; dat>>=1; DS1302_SCLK=1;//数据在上升沿时,DS1302读取数据 DS1302_SCLK=0; } DS1302_RST=0;//传送数据结束}​/*函数功能: 从DS1302指定寄存器里读取一个字节数据*/u8 DS1302_ReadByte(u8 addr){ u8 n=0,dat=0; DS1302_RST=1; //然后将DS1302_RST(CE)置高电平。 /*1. 设置读取的地址*/ for(n=0;n<8;n++) { DS1302_IO=addr&0x01;//数据从低位开始传送 addr>>=1; DS1302_SCLK=1;//数据在上升沿时,DS1302读取数据 DS1302_SCLK=0;//DS1302下降沿时,放置数据 } /*2. 读取数据*/ for(n=0;n<8;n++) { dat>>=1; if(DS1302_IO)dat|=0x80; DS1302_SCLK=1; DS1302_SCLK=0;//DS1302下降沿时,放置数据 } DS1302_RST=0; //必须的操作,复位时间 DS1302_IO=0; DS1302_IO=1; return dat; }​/*函数功能: 设置DS1302芯片的时间DS1302的时间基准是从2000年开始的,设置年份时要减去2000再传入设置例如:DS1302_WriteTime(20,1,18,14,46,20,6);*/void DS1302_WriteTime(u8 year,u8 mon,u8 mday,u8 hour,u8 min,u8 sec,u8 week){ DS1302_WriteByte(0x8E,0x00); //禁止写保护,就是关闭写保护功能 DS1302_WriteByte(0x8c,DEC_TO_BCD(year)); //设置年 DS1302_WriteByte(0x88,DEC_TO_BCD(mon)); //设置月 DS1302_WriteByte(0x86,DEC_TO_BCD(mday)); //设置日 DS1302_WriteByte(0x84,DEC_TO_BCD(hour)); //设置时 DS1302_WriteByte(0x82,DEC_TO_BCD(min)); //设置分 DS1302_WriteByte(0x80,DEC_TO_BCD(sec)); //设置秒 DS1302_WriteByte(0x8a,DEC_TO_BCD(week)); //设置星期 DS1302_WriteByte(0x8E,0x80); //打开写保护功能}​/*函数功能: 读取DS1302时钟的时间DS1302寄存器的最低位是读写位,0是写,1是读*/void DS1302_ReadTime(void){ DS1302_TIME[0]=BCD_TO_DEC(DS1302_ReadByte(0x8c|0x01));//读取年 DS1302_TIME[1]=BCD_TO_DEC(DS1302_ReadByte(0x88|0x01));//读取月 DS1302_TIME[2]=BCD_TO_DEC(DS1302_ReadByte(0x86|0x01));//读取日 DS1302_TIME[3]=BCD_TO_DEC(DS1302_ReadByte(0x84|0x01));//读取时 DS1302_TIME[4]=BCD_TO_DEC(DS1302_ReadByte(0x82|0x01));//读取分 DS1302_TIME[5]=BCD_TO_DEC(DS1302_ReadByte(0x80|0x01));//读取秒 DS1302_TIME[6]=BCD_TO_DEC(DS1302_ReadByte(0x8a|0x01));//读取星期}​int main(){ u8 stat; UART_Init(); //初始化串口波特率为4800 DS1302_Init(); stat=DS1302_ReadByte(0x80|0x01);//读取秒 if(stat&0x80) { DS1302_WriteTime(2020-2000,1,18,16,33,33,6); } else { printf("DS1302 OK\r\n"); } while(1) { DS1302_ReadTime(); printf("DS1302:%d-%d-%d %d:%d:%d %d\r\n", (int)DS1302_TIME[0]+2000, (int)DS1302_TIME[1], (int)DS1302_TIME[2], (int)DS1302_TIME[3], (int)DS1302_TIME[4], (int)DS1302_TIME[5], (int)DS1302_TIME[6] ); DelayMs(1000); }}​
  • [技术干货] 基于STM32的轻量级Web服务器设计
    一、前言1.1 开发背景本项目的目的是构建一个基于STM32F103ZET6微控制器的嵌入式Web服务器,以满足远程监控和控制嵌入式设备的需求。随着物联网技术的快速发展,远程监控和控制嵌入式设备变得越来越重要。本项目通过选择STM32F103ZET6作为主控芯片,结合ENC28J60网卡实现网络通信,并移植UIP协议栈来构建轻量级的Web服务器。项目还集成了DS18B20温度传感器、LED灯模块和高电平触发的有源蜂鸣器,以实现远程监控和控制STM32设备端的功能,如LED灯和蜂鸣器的控制,以及设备端温度和RTC时间的显示。这种设计使得用户能够通过浏览器访问服务器,实时查看和控制嵌入式设备,为物联网应用提供了一种灵活、高效的解决方案。1.2 实现的功能(1)网络通信搭建:通过STM32F103ZET6微控制器与ENC28J60以太网控制器的集成,利用SPI接口实现数据传输,成功移植UIP轻量级TCP/IP协议栈,从而在嵌入式平台上搭建起一个功能完备的Web服务器。(2)网页服务:在STM32内部存储一个简易的网页文件,该网页设计用于用户界面展示及交互。当用户使用任何标准的Web浏览器访问此服务器的IP地址时,即可加载并显示该网页内容。(3)远程控制功能:LED灯控制:网页上设有控制按钮或界面元素,允许用户远程开关连接到STM32的LED灯模块,实现远程照明控制演示。蜂鸣器控制:同样通过网页界面,用户能激活或关闭STM32连接的高电平触发的有源蜂鸣器,完成远程报警或信号提示功能的测试。(4)环境监测与显示:温度监控:集成DS18B20数字温度传感器,周期性采集环境温度数据,并通过Web界面实时显示给用户,提供基本的环境监测能力。实时时钟显示:利用STM32内置的RTC(实时时钟)模块,获取并准确显示当前的时间信息,增强系统的实用性和用户交互体验。项目不仅实现了从硬件选型、网络配置到软件开发的全过程,还展示了物联网技术在实际应用中的一个小而完整的案例,即通过简单的Web界面远程监控和控制物理设备,体现了STM32平台在物联网领域的灵活性与强大功能。1.3 硬件模块组成(1)主控制器模块:STM32F103ZET6微控制器:作为系统的核心处理器,负责运行控制程序、管理外设通信、处理网络数据包及执行用户指令。它拥有丰富的外设资源,如SPI、USART、I2C等,支持高速运算和低功耗操作,是实现项目功能的基础。(2)网络通信模块:ENC28J60以太网控制器:通过SPI接口与STM32连接,提供物理层和数据链路层的网络功能,实现与外部网络的连接。它是项目中实现Web服务器功能的关键组件,支持以太网数据包的收发,使嵌入式设备能够接入互联网。(3)温度监测模块:DS18B20数字温度传感器:通过单总线(One-Wire)接口与STM32通信,用于精确测量环境温度。该传感器具有体积小、精度高、直接数字输出等特点,非常适合嵌入式系统中的温度监控应用。(4)输出控制模块:LED灯模块:作为基本的输出设备,通过GPIO(通用输入输出端口)直接由STM32控制,用于响应用户的控制命令,如点亮或熄灭,以直观展示控制效果。有源蜂鸣器:通过高电平触发的方式连接至STM32的一个GPIO引脚,根据控制信号产生声音,实现报警或状态反馈功能。(5)电源模块:为确保所有硬件组件稳定工作,需要一个合适的电源供应模块,为STM32微控制器、ENC28J60网卡、传感器及外围电路提供稳定的电压和电流。1.4 ENC28J60网卡介绍ENC28J60是一款集成MAC(Media Access Control,媒体访问控制)和10BASE-T PHY(物理层)的以太网控制器,特别适合于嵌入式系统和微控制器应用。以下是ENC28J60网卡的一些关键特性与介绍:(1)接口类型:它使用SPI(Serial Peripheral Interface,串行外设接口)作为与外部微控制器通信的主要方式,这使得它能够以较少的引脚数(通常为4或5条线)与诸如STM32、Arduino等微控制器连接,降低了硬件设计的复杂度。(2)兼容性:ENC28J60兼容IEEE 802.3标准,这意味着它可以无缝地融入标准以太网网络环境中。它支持10Mbps的传输速率,适用于不需要高速网络连接的应用场景。(3)集成功能:除了MAC和PHY层之外,ENC28J60还集成了其他一些功能,如缓冲区管理、DMA(Direct Memory Access,直接内存访问)支持、以及对多种网络帧类型的支持,包括广播、多播和单播。(4)网络功能:能够实现完整的以太网数据包的发送和接收,支持IP、TCP、UDP等网络协议栈。开发者通常会结合LwIP(Lightweight IP)这样的轻量级TCP/IP协议栈来实现网络通信。(5)物理层特性:支持10BASE-T标准,可以通过RJ45接口连接双绞线(UTP),支持自动极性和交叉检测,可工作在全双工或半双工模式下。(6)功耗与封装:ENC28J60设计考虑到了低功耗应用的需求,适合电池供电设备。它通常采用SSOP(Shrink Small Outline Package)或QFN(Quad Flat No-Leads)等小型封装形式,便于在空间受限的设计中使用。(7)灵活性与应用:由于其SPI接口和相对较低的成本,ENC28J60被广泛应用于各种嵌入式项目中,如物联网设备、智能家居、工业控制、远程监控系统等。ENC28J60以其集成度高、接口灵活、成本效益好等特点,成为了许多嵌入式系统设计中实现网络连接的优选解决方案。1.5 UIP协议栈UIP(Micro IP)协议栈是一种专门为资源受限的嵌入式系统设计的轻量级TCP/IP协议栈。它最初由Adam Dunkels在SICS(瑞典计算机科学研究所)开发,目的是使即便是8位微控制器也能轻松实现网络通信,而不必负担全尺寸TCP/IP协议栈的开销。下面是UIP协议栈的详细介绍:【1】目标与特点轻量化设计:UIP的核心目标是在保持TCP/IP协议核心功能的同时,尽可能减小代码大小和内存占用。其代码量通常仅为几千字节,RAM消耗最低可达几百字节,这使得它非常适合于微控制器等资源有限的环境。事件驱动编程:UIP采用了事件驱动的编程模型,而不是基于多线程或中断驱动的方法。这种设计减少了对内存的需求,并且简化了程序逻辑,使得协议栈更加易于理解和维护。协议支持:尽管精简,UIP仍实现了网络层(IP)、网络互联层(ARP)、传输层(TCP)和部分应用层(如ICMP ping)的基本功能。它还支持UDP,尽管可能不如TCP那样全面。对于不常用的TCP/IP特性,如窗口缩放、时间戳等,则被省略以减少资源消耗。无操作系统依赖:UIP设计为可以在“裸机”环境下运行,即不需要操作系统的支持,这使得它非常灵活,可以部署在各种嵌入式平台上。【2】核心组件IP层:负责数据包的路由和分片重组,实现基本的网络层功能。ARP层:地址解析协议,用于将IP地址转换为MAC地址,确保数据包能够正确送达物理网络层。ICMP层:因特网控制消息协议,主要用于网络诊断,如ping命令的实现。TCP层:传输控制协议,用于建立可靠的、面向连接的数据传输服务,尽管在UIP中进行了大幅简化以适应嵌入式环境。内存管理:由于嵌入式系统内存资源有限,UIP实现了高效的内存块管理和缓冲区分配机制。【3】应用与优势应用广泛:UIP被广泛应用于物联网设备、智能家居、传感器网络、嵌入式Web服务器等场景。易于移植:由于其代码结构清晰、依赖少,UIP很容易被移植到不同的微控制器平台上。资源效率:在保证基本网络功能的同时,极大地节省了系统资源,使得低成本、低功耗的设备也能实现网络连接。二、硬件设计2.1 接线说明【1】ENC28J60接线【2】LED灯接线【3】蜂鸣器接线【4】DS18B20接线2.2 代码与服务器交互的控制这是判断网页下发的请求。2.3 修改网页的页面这个是写好的网页模版:代码如下:html<!DOCTYPE html><html><head><meta charset="UTF-8"><title>WEB服务器设计</title><style> .LED{ margin-left: 10px; } .font{ font-size:30px; width:130px; height:60px; } .LED:hover{ background: red; } .LED value{ background: blue; }</style></head><body> <div style="border:1px solid black;text-align: center;width:600px;height:500px;margin:auto;"> <h1>基于STM32的WEB服务器设计</h1> <h1>微信公众号:DS小龙哥嵌入式技术资讯</h1> <h1>这是基于ENC28J60+UIP协议栈设计的WEB服务器</h1> <div class="paren_LED "> <button class="LED font" v="on1" num = "1">LED1</button> <button class="LED font" v="on2" num = "2">LED2</button> <button class="LED font" v="on3" num = "3">LED3</button> <button class="LED font" v="on4" num = "4">LED4</button> <button class="LED font" v="on5" num = "5" style="display: block;margin-left:23px;margin-top:5px;">蜂鸣器</button> </div> <div style="text-align: left;"> <span class="font" style="margin-left:22px">温度:</span><input style="font-size: 30px" value="23℃" id="wendu"/> </div> <div style="text-align: left;"> <span class="font" style="margin-left:22px">时间:</span><input style="font-size: 30px" value="1s" id="time"/> </div> </div> <script> var LED = document.getElementsByClassName("paren_LED")[0]; var wendu = document.getElementById("wendu"); var time = document.getElementById("time"); var xhr = new XMLHttpRequest(); var xhr2 = new XMLHttpRequest(); //控制灯的按钮 LED.onclick = function(e){ var status = e.target.getAttribute("v"); var paren_LED = LED.children; xhr.open("GET","test?data="+status,true); //xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(); xhr.onreadystatechange = function () { if(xhr.readyState==4&&xhr.status==200){ //获取后台的数据 var data = xhr.responseText; var sta = e.target.getAttribute("num"); e.target.setAttribute("v",data+sta); for(var i=0;i<paren_LED.length;i++){ var str = paren_LED[i].getAttribute("v"); var st = str.substring(0,str.length-1); if("off"==st){ paren_LED[i].style.background="blue"; }else if("on"==st){ paren_LED[i].style.background=""; }else if("Beep_on"==st){ paren_LED[i].style.background=""; }else if("Beep_off"==st){ paren_LED[i].style.background="blue"; } } } } } //定时器 var wd = function(){ xhr2.open("GET","test?data=temp",true); xhr2.send(); xhr2.onreadystatechange = function () { var data = xhr2.responseText.split("&"); wendu.value= data[0]+"℃"; time.value = data[1]+"s"; } }; setInterval("wd()",1000); </script></body></html>将这个文件转为C语言数组放到单片机代码工程里就行了。如何转换? 用winhex 这个工具。​替换这个数组就行了:2.4 设置网卡地址因为板子是静态IP,为了方便通信,ENC28J60通过网线直接与电脑网口连接。 设置固定的IP地址。2.5 程序运行效果【1】下载程序【2】串口看效果【3】ping板子IP地址【4】打开网页看效果2.6 ENC28J60驱动代码#include "delay.h"#include <stdio.h>#include "enc28j60.h" /*以下是ENC28J60驱动移植接口:MISO--->PA6----主机输入MOSI--->PA7----主机输出SCLK--->PA5----时钟信号CS----->PA4----片选RESET-->PG15---复位*/#define ENC28J60_CS PAout(4) //ENC28J60片选信号#define ENC28J60_RST PGout(15) //ENC28J60复位信号#define ENC28J60_MOSI PAout(7) //输出#define ENC28J60_MISO PAin(6) //输入#define ENC28J60_SCLK PAout(5) //时钟线static u8 ENC28J60BANK;static u32 NextPacketPtr;/*函数功能:底层SPI接口收发一个字节说 明:模拟SPI时序,ENC28J60时钟线空闲电平为低电平,在第一个下降沿采集数据*/u8 ENC28J60_SPI_ReadWriteOneByte(u8 tx_data){ u16 cnt=0; while((SPI1->SR&1<<1)==0) //等待发送区空--等待发送缓冲为空 { cnt++; if(cnt>=65530)return 0; //超时退出 u16=2个字节 } SPI1->DR=tx_data; //发送一个byte cnt=0; while((SPI1->SR&1<<0)==0) //等待接收完一个byte { cnt++; if(cnt>=65530)return 0; //超时退出 } return SPI1->DR; //返回收到的数据 }/*函数功能:复位ENC28J60,包括SPI初始化/IO初始化等MISO--->PA6----主机输入MOSI--->PA7----主机输出SCLK--->PA5----时钟信号CS----->PA4----片选RESET-->PG15---复位*/void ENC28J60_Reset(void){/*开启时钟*/ RCC->APB2ENR|=1<<12; //开启SPI1时钟 RCC->APB2ENR|=1<<2; //PA GPIOA->CRL&=0X0000FFFF; //清除寄存器 GPIOA->CRL|=0XB8B30000; GPIOA->ODR|=0XF<<4; // 上拉--输出高电平 GPIOA->ODR&=~(1<<5); RCC->APB2ENR|=1<<8; //2 3 4 5 6 7 8 GPIOG->CRH&=0x0FFFFFFF; GPIOG->CRH|=0x30000000; /*SPI2基本配置*/ SPI1->CR1=0X0; //清空寄存器 SPI1->CR1|=0<<15; //选择“双线双向”模式 SPI1->CR1|=0<<11; //使用8位数据帧格式进行发送/接收; SPI1->CR1|=0<<10; //全双工(发送和接收); SPI1->CR1|=1<<9; //启用软件从设备管理 SPI1->CR1|=1<<8; //NSS SPI1->CR1|=0<<7; //帧格式,先发送高位 SPI1->CR1|=0x1<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。 SPI1->CR1|=1<<2; //配置为主设备 SPI1->CR1|=1<<1; //空闲状态时, SCK保持高电平。 SPI1->CR1|=1<<0; //数据采样从第二个时钟边沿开始。 SPI1->CR1|=1<<6; //开启SPI设备。 //针对ENC28J60的特点(SCK空闲为低电平)修改SPI的设置 SPI1->CR1&=~(1<<6); //SPI设备失能 SPI1->CR1&=~(1<<1); //空闲模式下SCK为0 CPOL=0 SPI1->CR1&=~(1<<0); //数据采样从第1个时间边沿开始,CPHA=0 SPI1->CR1|=1<<6; //SPI设备使能 ENC28J60_RST=0; //复位ENC28J60 DelayMs(10); ENC28J60_RST=1; //复位结束 DelayMs(10); }/*函数功能:读取ENC28J60寄存器(带操作码) 参 数:op:操作码 addr:寄存器地址/参数返 回 值:读到的数据*/u8 ENC28J60_Read_Op(u8 op,u8 addr){ u8 dat=0; ENC28J60_CS=0; dat=op|(addr&ADDR_MASK); ENC28J60_SPI_ReadWriteOneByte(dat); dat=ENC28J60_SPI_ReadWriteOneByte(0xFF); //如果是读取MAC/MII寄存器,则第二次读到的数据才是正确的,见手册29页 if(addr&0x80)dat=ENC28J60_SPI_ReadWriteOneByte(0xFF); ENC28J60_CS=1; return dat;}/*函数功能:读取ENC28J60寄存器(带操作码) 参 数: op:操作码 addr:寄存器地址 data:参数*/void ENC28J60_Write_Op(u8 op,u8 addr,u8 data){ u8 dat = 0; ENC28J60_CS=0; dat=op|(addr&ADDR_MASK); ENC28J60_SPI_ReadWriteOneByte(dat); ENC28J60_SPI_ReadWriteOneByte(data); ENC28J60_CS=1;}/*函数功能:读取ENC28J60接收缓存数据参 数: len:要读取的数据长度 data:输出数据缓存区(末尾自动添加结束符)*/void ENC28J60_Read_Buf(u32 len,u8* data){ ENC28J60_CS=0; ENC28J60_SPI_ReadWriteOneByte(ENC28J60_READ_BUF_MEM); while(len) { len--; *data=(u8)ENC28J60_SPI_ReadWriteOneByte(0); data++; } *data='\0'; ENC28J60_CS=1;}/*函数功能:向ENC28J60写发送缓存数据参 数: len:要写入的数据长度 data:数据缓存区*/void ENC28J60_Write_Buf(u32 len,u8* data){ ENC28J60_CS=0; ENC28J60_SPI_ReadWriteOneByte(ENC28J60_WRITE_BUF_MEM); while(len) { len--; ENC28J60_SPI_ReadWriteOneByte(*data); data++; } ENC28J60_CS=1;}/*函数功能:设置ENC28J60寄存器Bank参 数: ban:要设置的bank*/void ENC28J60_Set_Bank(u8 bank){ if((bank&BANK_MASK)!=ENC28J60BANK)//和当前bank不一致的时候,才设置 { ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,ECON1,(ECON1_BSEL1|ECON1_BSEL0)); ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,(bank&BANK_MASK)>>5); ENC28J60BANK=(bank&BANK_MASK); }}/*函数功能:读取ENC28J60指定寄存器参 数:addr:寄存器地址返 回 值:读到的数据*/u8 ENC28J60_Read(u8 addr){ ENC28J60_Set_Bank(addr);//设置BANK return ENC28J60_Read_Op(ENC28J60_READ_CTRL_REG,addr);}/*函数功能:向ENC28J60指定寄存器写数据参 数: addr:寄存器地址 data:要写入的数据 */void ENC28J60_Write(u8 addr,u8 data){ ENC28J60_Set_Bank(addr); ENC28J60_Write_Op(ENC28J60_WRITE_CTRL_REG,addr,data);}/*函数功能:向ENC28J60的PHY寄存器写入数据参 数: addr:寄存器地址 data:要写入的数据 */void ENC28J60_PHY_Write(u8 addr,u32 data){ u16 retry=0; ENC28J60_Write(MIREGADR,addr); //设置PHY寄存器地址 ENC28J60_Write(MIWRL,data); //写入数据 ENC28J60_Write(MIWRH,data>>8); while((ENC28J60_Read(MISTAT)&MISTAT_BUSY)&&retry<0XFFF)retry++;//等待写入PHY结束 }/*函数功能:初始化ENC28J60参 数:macaddr:MAC地址返 回 值: 0,初始化成功; 1,初始化失败;*/u8 ENC28J60_Init(u8* macaddr){ u16 retry=0; ENC28J60_Reset(); //复位底层引脚接口 ENC28J60_Write_Op(ENC28J60_SOFT_RESET,0,ENC28J60_SOFT_RESET);//软件复位 while(!(ENC28J60_Read(ESTAT)&ESTAT_CLKRDY)&&retry<500)//等待时钟稳定 { retry++; DelayMs(1); }; if(retry>=500)return 1;//ENC28J60初始化失败 // do bank 0 stuff // initialize receive buffer // 16-bit transfers,must write low byte first // set receive buffer start address 设置接收缓冲区地址 8K字节容量 NextPacketPtr=RXSTART_INIT; // Rx start //接收缓冲器由一个硬件管理的循环FIFO 缓冲器构成。 //寄存器对ERXSTH:ERXSTL 和ERXNDH:ERXNDL 作 //为指针,定义缓冲器的容量和其在存储器中的位置。 //ERXST和ERXND指向的字节均包含在FIFO缓冲器内。 //当从以太网接口接收数据字节时,这些字节被顺序写入 //接收缓冲器。 但是当写入由ERXND 指向的存储单元 //后,硬件会自动将接收的下一字节写入由ERXST 指向 //的存储单元。 因此接收硬件将不会写入FIFO 以外的单 //元。 //设置接收起始字节 ENC28J60_Write(ERXSTL,RXSTART_INIT&0xFF); ENC28J60_Write(ERXSTH,RXSTART_INIT>>8); //ERXWRPTH:ERXWRPTL 寄存器定义硬件向FIFO 中 //的哪个位置写入其接收到的字节。 指针是只读的,在成 //功接收到一个数据包后,硬件会自动更新指针。 指针可 //用于判断FIFO 内剩余空间的大小 8K-1500。 //设置接收读指针字节 ENC28J60_Write(ERXRDPTL,RXSTART_INIT&0xFF); ENC28J60_Write(ERXRDPTH,RXSTART_INIT>>8); //设置接收结束字节 ENC28J60_Write(ERXNDL,RXSTOP_INIT&0xFF); ENC28J60_Write(ERXNDH,RXSTOP_INIT>>8); //设置发送起始字节 ENC28J60_Write(ETXSTL,TXSTART_INIT&0xFF); ENC28J60_Write(ETXSTH,TXSTART_INIT>>8); //设置发送结束字节 ENC28J60_Write(ETXNDL,TXSTOP_INIT&0xFF); ENC28J60_Write(ETXNDH,TXSTOP_INIT>>8); // do bank 1 stuff,packet filter: // For broadcast packets we allow only ARP packtets // All other packets should be unicast only for our mac (MAADR) // // The pattern to match on is therefore // Type ETH.DST // ARP BROADCAST // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 // in binary these poitions are:11 0000 0011 1111 // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 //接收过滤器 //UCEN:单播过滤器使能位 //当ANDOR = 1 时: //1 = 目标地址与本地MAC 地址不匹配的数据包将被丢弃 //0 = 禁止过滤器 //当ANDOR = 0 时: //1 = 目标地址与本地MAC 地址匹配的数据包会被接受 //0 = 禁止过滤器 //CRCEN:后过滤器CRC 校验使能位 //1 = 所有CRC 无效的数据包都将被丢弃 //0 = 不考虑CRC 是否有效 //PMEN:格式匹配过滤器使能位 //当ANDOR = 1 时: //1 = 数据包必须符合格式匹配条件,否则将被丢弃 //0 = 禁止过滤器 //当ANDOR = 0 时: //1 = 符合格式匹配条件的数据包将被接受 //0 = 禁止过滤器 ENC28J60_Write(ERXFCON,ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); ENC28J60_Write(EPMM0,0x3f); ENC28J60_Write(EPMM1,0x30); ENC28J60_Write(EPMCSL,0xf9); ENC28J60_Write(EPMCSH,0xf7); // do bank 2 stuff // enable MAC receive //bit 0 MARXEN:MAC 接收使能位 //1 = 允许MAC 接收数据包 //0 = 禁止数据包接收 //bit 3 TXPAUS:暂停控制帧发送使能位 //1 = 允许MAC 发送暂停控制帧(用于全双工模式下的流量控制) //0 = 禁止暂停帧发送 //bit 2 RXPAUS:暂停控制帧接收使能位 //1 = 当接收到暂停控制帧时,禁止发送(正常操作) //0 = 忽略接收到的暂停控制帧 ENC28J60_Write(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); // bring MAC out of reset //将MACON2 中的MARST 位清零,使MAC 退出复位状态。 ENC28J60_Write(MACON2,0x00); // enable automatic padding to 60bytes and CRC operations //bit 7-5 PADCFG2:PACDFG0:自动填充和CRC 配置位 //111 = 用0 填充所有短帧至64 字节长,并追加一个有效的CRC //110 = 不自动填充短帧 //101 = MAC 自动检测具有8100h 类型字段的VLAN 协议帧,并自动填充到64 字节长。如果不 //是VLAN 帧,则填充至60 字节长。填充后还要追加一个有效的CRC //100 = 不自动填充短帧 //011 = 用0 填充所有短帧至64 字节长,并追加一个有效的CRC //010 = 不自动填充短帧 //001 = 用0 填充所有短帧至60 字节长,并追加一个有效的CRC //000 = 不自动填充短帧 //bit 4 TXCRCEN:发送CRC 使能位 //1 = 不管PADCFG如何,MAC都会在发送帧的末尾追加一个有效的CRC。 如果PADCFG规定要 //追加有效的CRC,则必须将TXCRCEN 置1。 //0 = MAC不会追加CRC。 检查最后4 个字节,如果不是有效的CRC 则报告给发送状态向量。 //bit 0 FULDPX:MAC 全双工使能位 //1 = MAC工作在全双工模式下。 PHCON1.PDPXMD 位必须置1。 //0 = MAC工作在半双工模式下。 PHCON1.PDPXMD 位必须清零。 ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX); // set inter-frame gap (non-back-to-back) //配置非背对背包间间隔寄存器的低字节 //MAIPGL。 大多数应用使用12h 编程该寄存器。 //如果使用半双工模式,应编程非背对背包间间隔 //寄存器的高字节MAIPGH。 大多数应用使用0Ch //编程该寄存器。 ENC28J60_Write(MAIPGL,0x12); ENC28J60_Write(MAIPGH,0x0C); // set inter-frame gap (back-to-back) //配置背对背包间间隔寄存器MABBIPG。当使用 //全双工模式时,大多数应用使用15h 编程该寄存 //器,而使用半双工模式时则使用12h 进行编程。 ENC28J60_Write(MABBIPG,0x15); // Set the maximum packet size which the controller will accept // Do not send packets longer than MAX_FRAMELEN: // 最大帧长度 1500 ENC28J60_Write(MAMXFLL,MAX_FRAMELEN&0xFF); ENC28J60_Write(MAMXFLH,MAX_FRAMELEN>>8); // do bank 3 stuff // write MAC address // NOTE: MAC address in ENC28J60 is byte-backward //设置MAC地址 ENC28J60_Write(MAADR5,macaddr[0]); ENC28J60_Write(MAADR4,macaddr[1]); ENC28J60_Write(MAADR3,macaddr[2]); ENC28J60_Write(MAADR2,macaddr[3]); ENC28J60_Write(MAADR1,macaddr[4]); ENC28J60_Write(MAADR0,macaddr[5]); //配置PHY为全双工 LEDB为拉电流 ENC28J60_PHY_Write(PHCON1,PHCON1_PDPXMD); // no loopback of transmitted frames 禁止环回 //HDLDIS:PHY 半双工环回禁止位 //当PHCON1.PDPXMD = 1 或PHCON1.PLOOPBK = 1 时: //此位可被忽略。 //当PHCON1.PDPXMD = 0 且PHCON1.PLOOPBK = 0 时: //1 = 要发送的数据仅通过双绞线接口发出 //0 = 要发送的数据会环回到MAC 并通过双绞线接口发出 ENC28J60_PHY_Write(PHCON2,PHCON2_HDLDIS); // switch to bank 0 //ECON1 寄存器 //寄存器3-1 所示为ECON1 寄存器,它用于控制 //ENC28J60 的主要功能。 ECON1 中包含接收使能、发 //送请求、DMA 控制和存储区选择位。 ENC28J60_Set_Bank(ECON1); // enable interrutps //EIE: 以太网中断允许寄存器 //bit 7 INTIE: 全局INT 中断允许位 //1 = 允许中断事件驱动INT 引脚 //0 = 禁止所有INT 引脚的活动(引脚始终被驱动为高电平) //bit 6 PKTIE: 接收数据包待处理中断允许位 //1 = 允许接收数据包待处理中断 //0 = 禁止接收数据包待处理中断 ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE); // enable packet reception //bit 2 RXEN:接收使能位 //1 = 通过当前过滤器的数据包将被写入接收缓冲器 //0 = 忽略所有接收的数据包 ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN); if(ENC28J60_Read(MAADR5)== macaddr[0])return 0;//初始化成功 else return 1; }/*函数功能:读取EREVID参 数:*/u8 ENC28J60_Get_EREVID(void){ //在EREVID 内也存储了版本信息。 EREVID 是一个只读控 //制寄存器,包含一个5 位标识符,用来标识器件特定硅片 //的版本号 return ENC28J60_Read(EREVID);}/*函数功能:通过ENC28J60发送数据包到网络参 数: len :数据包大小 packet:数据包*/void ENC28J60_Packet_Send(u32 len,u8* packet){ //设置发送缓冲区地址写指针入口 ENC28J60_Write(EWRPTL,TXSTART_INIT&0xFF); ENC28J60_Write(EWRPTH,TXSTART_INIT>>8); //设置TXND指针,以对应给定的数据包大小 ENC28J60_Write(ETXNDL,(TXSTART_INIT+len)&0xFF); ENC28J60_Write(ETXNDH,(TXSTART_INIT+len)>>8); //写每包控制字节(0x00表示使用macon3的设置) ENC28J60_Write_Op(ENC28J60_WRITE_BUF_MEM,0,0x00); //复制数据包到发送缓冲区 //printf("len:%d\r\n",len); //监视发送数据长度 ENC28J60_Write_Buf(len,packet); //发送数据到网络 ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS); //复位发送逻辑的问题。参见Rev. B4 Silicon Errata point 12. if((ENC28J60_Read(EIR)&EIR_TXERIF))ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,ECON1,ECON1_TXRTS);}/*函数功能:从网络获取一个数据包内容函数参数: maxlen:数据包最大允许接收长度 packet:数据包缓存区返 回 值:收到的数据包长度(字节) */u32 ENC28J60_Packet_Receive(u32 maxlen,u8* packet){ u32 rxstat; u32 len; if(ENC28J60_Read(EPKTCNT)==0)return 0; //是否收到数据包? //设置接收缓冲器读指针 ENC28J60_Write(ERDPTL,(NextPacketPtr)); ENC28J60_Write(ERDPTH,(NextPacketPtr)>>8); // 读下一个包的指针 NextPacketPtr=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0); NextPacketPtr|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8; //读包的长度 len=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0); len|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8; len-=4; //去掉CRC计数 //读取接收状态 rxstat=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0); rxstat|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8; //限制接收长度 if (len>maxlen-1)len=maxlen-1; //检查CRC和符号错误 // ERXFCON.CRCEN为默认设置,一般我们不需要检查. if((rxstat&0x80)==0)len=0;//无效 else ENC28J60_Read_Buf(len,packet);//从接收缓冲器中复制数据包 //RX读指针移动到下一个接收到的数据包的开始位置 //并释放我们刚才读出过的内存 ENC28J60_Write(ERXRDPTL,(NextPacketPtr)); ENC28J60_Write(ERXRDPTH,(NextPacketPtr)>>8); //递减数据包计数器标志我们已经得到了这个包 ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON2,ECON2_PKTDEC); return(len);}
  • [技术干货] 单片机与阿里云的mqtt简单通信(塔石nb-iot篇)-转载
     这篇文章主要记录了一次单片机通过塔石的模块,实现与阿里云的mqtt通信(仅能简单传递信息,不涉及更深入操作)的过程。  其中会包括如,阿里云物联网平台中“产品”与“设备”的创建,塔石nb-iot模块的使用等。  前期准备: 1.塔石nb-iot模块 (E33V-DTU带天线)  2.stm32g431(stm32F1系列等均可)  3.阿里云账号  4.一张流量卡(主要为了塔石模块能连网)  阿里云部分 1.登录阿里云后,在产品中查找“物联网平台”并进入。    2.进入“管理控制台”   3.进入“公共实例”   4.创建一个新“产品”   5.给你的产品取一个名字(例如我的“HDUGEEK”), 同时因为我们只打算实现简单的通信,所以所属品类自定义即可。    6.为了让之后我们的通信过程更“简单”(自认为),点击产品右边的“查看”,再找到“Topic类列表”“自定义Topic”,添加两个具有“发布和订阅”权限的topic,“read”,“say”分别用于接受单片机的信息,向单片机传递信息。  如果想了解更多有关Topic内容,可以看看推荐视频的 P1 ~ P3 ,虽然很长但都是干货,看完能帮你迅速理解mqtt原理。  推荐视频:【物联网MQTT协议解析、报文构造、程序设计、项目实战】 https://www.bilibili.com/video/BV1Jz4y1X7aH/?share_source=copy_web&vd_source=e1c657085d91a6a8e457baf141d1a49a    7.接下来我们要创建属于该产品下的设备。(我的设备取名“geek”)  当设备刚创建时会显示“未激活”,不过等我们第一次连上设备后就不会再显示这个了。    8.同样查看我们的设备信息,找到“MQTT连接参数”,这是后面会用到的重要数据。    塔石部分 (有关塔石模块的资料可以找商家要)  1.为我们的塔石模块连好天线,插上流量卡。根据“产品手册”中的引脚描述,将塔石通过usb转ttl模块与电脑相连。      2.打开塔石的配置工具,选择产品型号后进行参数配置。    3.把通道1的工作模式改为MQTT透传,将之前设备的MQTT参数填入对应位置。   4.将之前产品中我们自定义的Topic“say”复制到订阅参数中,“read”复制到推送参数中。记得把${deviceName}改为自己的设备名(如"geek")       5.打开串口,点击"进入配置状态""一键配置参数"来保存配置,再“退出配置状态”,等待塔石模块自动连上设备。   6. 回到我们阿里云设备的界面,找到Topic列表,发布一个消息(如“123”)。  可以看到在塔石的配置工具中我们成功收到了这个消息。     7.直接在配置工具中发送4567,接着就能在日志服务中看到塔石发送到阿里云的消息了    单片机部分         我们已经成功在电脑上利用配置工具实现了塔石与阿里云的简单通讯,也完成了对塔石模块的参数配置,接下来就可以尝试直接把塔石连接到单片机上。          单片机与塔石可以采用串口通信,这样之后,单片机传给塔石模块的信息就会“原封不动”的传给阿里云,而塔石模块收到的,来自阿里云的信息也会通过串口传给单片机。          实现这一切只需要你熟悉单片机的串口发送与串口接受,因为比较简单,所以不再赘述。  虽然我们只是简单实现了“对话”,但大家想必也能感受到其中巨大的可能性。希望未来你实现了更加复杂,更加实用的项目时,这篇文章曾为你提供过一点点帮助。 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/HDUGEEK/article/details/130304819 
  • [技术干货] 传感器技术:STC89C52单片机读取MPU6050姿态数据
    MPU6050是一个6轴姿态传感器,能够测量芯片自身的X、Y、Z轴的加速度以及角速度参数。通过数据融合,它可以进一步得到姿态角。这种传感器常应用于平衡车、飞行器等需要检测自身姿态的场景。MPU6050由两部分组成:3轴加速度计和3轴陀螺仪传感器。3轴加速度计用于测量X、Y、Z轴的加速度,而3轴陀螺仪传感器则用于测量X、Y、Z轴的角速度。这使得MPU6050能够全面感知设备的运动状态。在MPU6050中,陀螺仪的工作原理基于一个物理原理:一个旋转物体的旋转轴所指的方向在不受外力影响时是不会改变的。这个原理使得陀螺仪能够保持方向,进而读取轴所指示的方向,并自动将数据信号传给控制系统。而加速度传感器则是一种能够测量加速度的传感器,通常由质量块、阻尼器、弹性元件、敏感元件和适调电路等部分组成。在加速过程中,通过对质量块所受惯性力的测量,利用牛顿第二定律,传感器能够获得加速度值。此外,MPU6050还具有一些重要参数。它采用16位ADC采集传感器的模拟信号,其量化范围为-32768到32767。加速度计和陀螺仪都有多种满量程选择,可以根据实际应用场景进行调整。MPU6050还具有可配置的数字低通滤波器、时钟源和采样分频等功能,这些功能使得MPU6050能够适应不同的应用需求。由于其高性能、小型化、低功耗等特点,MPU6050被广泛应用于各种需要运动跟踪和姿态解算的智能设备中,如无人机、机器人、智能手机、平板电脑和可穿戴设备等。在无人机中,MPU6050可以提供角速度和加速度数据,帮助无人机实现精确的运动控制;在机器人中,MPU6050可以实现姿态感知和运动控制。以下是使用STC89C52单片机读取MPU6050姿态数据的简单示例代码:#include <reg52.h>#include <intrins.h>#define MPU6050_ADDRESS 0xD0 // MPU6050 I2C地址(写入模式)#define MPU6050_RA_ACCEL_XOUT_H 0x3B // 加速度传感器X轴高位寄存器地址#define MPU6050_RA_PWR_MGMT_1 0x6B // 电源管理寄存器地址#define MPU6050_PWR1_CLKSEL_BIT 2 // CLKSEL位在电源管理寄存器中的位置#define MPU6050_PWR1_CLKSEL_LENGTH 3 // CLKSEL位的位长#define MPU6050_CLOCK_PLL_XGYRO 0x01 // PLL以X轴陀螺仪为时钟源sbit SCL = P2^1; // I2C时钟线sbit SDA = P2^0; // I2C数据线void delay_ms(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 110; j++);}void I2C_start() { SDA = 1; _nop_(); SCL = 1; _nop_(); SDA = 0; _nop_(); SCL = 0; _nop_();}void I2C_stop() { SDA = 0; _nop_(); SCL = 1; _nop_(); SDA = 1; _nop_(); SCL = 0; _nop_();}void I2C_write(uint8_t data) { uint8_t i; for (i = 0; i < 8; i++) { SDA = (data & 0x80) >> 7; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_(); data <<= 1; } SDA = 1; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_();}uint8_t I2C_read() { uint8_t i, data = 0; SDA = 1; for (i = 0; i < 8; i++) { _nop_(); SCL = 1; _nop_(); data = (data << 1) | SDA; _nop_(); SCL = 0; } return data;}void MPU6050_init() { I2C_start(); I2C_write(MPU6050_ADDRESS); I2C_write(MPU6050_RA_PWR_MGMT_1); I2C_write(MPU6050_CLOCK_PLL_XGYRO); I2C_stop();}int read_accel_x() { int accel_x; I2C_start(); I2C_write(MPU6050_ADDRESS); I2C_write(MPU6050_RA_ACCEL_XOUT_H); I2C_start(); I2C_write(MPU6050_ADDRESS | 1); accel_x = ((int)I2C_read() << 8) | I2C_read(); I2C_stop(); return accel_x;}void main() { int accel_x; MPU6050_init(); while (1) { accel_x = read_accel_x(); // 处理加速度X轴数据 // 在这里可以添加你的代码来处理加速度数据,比如计算姿态等 // 注意:这里仅仅读取了加速度X轴数据,如果需要完整的姿态数据,需要读取加速度Y轴和Z轴数据以及陀螺仪数据,并进行相应的处理 delay_ms(100); // 延时等待,可以根据需要调整 }}
  • [技术干货] 物联网里常用的协议介绍
    物联网中常用的协议包括多种,每种协议都有其特定的应用场景和优势。以下是一些主要的物联网协议及其详细介绍:MQTT协议:MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅模式的消息传输协议,专为低带宽、高延迟或不稳定的网络环境设计。它工作在TCP/IP协议族上,以极少的代码和带宽为远程设备提供实时可靠的消息服务。MQTT协议广泛应用于物联网、移动应用、小型设备、智能家电、传感器等领域。CoAP协议:CoAP(Constrained Application Protocol)是一种针对受限设备的应用层协议,特别适用于低功耗、低带宽的物联网环境。它类似于Web的HTTP协议,但为物联网设备做了优化,支持请求/响应模型,并提供了可靠传输、数据重传和块传输等功能。CoAP适用于资源受限的物联网设备,如传感器、智能电表等。LWM2M协议:LWM2M(Lightweight Machine to Machine)是一种物联网设备管理协议,用于远程管理和监控物联网设备。它定义了设备管理的基本功能和接口,使得物联网设备可以被远程配置、监控和更新。ZigBee协议:ZigBee是一种低功耗、近距离的无线通信协议,适用于家庭自动化和工业控制领域。它采用RF频段进行通信,能够实现设备之间的互联和远程控制,具有低复杂度、自组织、高可靠性和低成本等特点。Bluetooth协议:Bluetooth是一种短距离无线通信协议,广泛应用于手机、电脑、耳机等个人消费电子设备之间的通信。蓝牙技术提供了设备间的无线连接和数据传输功能,使得设备可以方便地进行数据交换和协同工作。物联网中还有其他一些常用的协议,如LoRaWAN(用于低功耗广域网通信)、Wi-Fi(用于无线局域网通信)、RFID(无线射频识别)等。实现这些物联网协议时,具体的传感器选择会根据应用场景、设备特性以及协议的要求而有所不同。以下是一些常见的传感器,以及它们如何与上述协议结合使用:MQTT协议MQTT协议由于其轻量级和发布/订阅的特性,适用于多种传感器类型。例如:温度传感器:用于监测环境温度,通过MQTT协议发布温度数据。湿度传感器:用于监测环境湿度,同样可以通过MQTT协议发布数据。运动传感器:用于检测物体的移动或人体的活动,通过MQTT发送触发信号。CoAP协议由于CoAP是为受限设备设计的,它特别适用于低功耗、低带宽的传感器。例如:光照传感器:用于检测环境光照强度,并通过CoAP协议与其他设备通信。土壤湿度传感器:在农业物联网中,用于监测土壤湿度,通过CoAP协议发送数据。LWM2M协议LWM2M协议通常用于需要远程管理和监控的设备。以下传感器可以与其结合使用:压力传感器:用于监测管道压力、气体压力等,数据通过LWM2M协议上传至远程服务器。电量传感器:用于监测电池电量或设备的能耗,通过LWM2M进行远程监控。ZigBee协议ZigBee协议因其低功耗和近距离通信的特性,常与以下传感器结合使用:烟雾传感器:用于检测烟雾,通常在火灾报警系统中使用,通过ZigBee与其他报警设备联动。气体传感器:用于检测空气中的有害气体,通过ZigBee协议发送警报信号。Bluetooth协议Bluetooth协议常用于个人消费电子设备,因此与以下传感器结合较多:心率传感器:在健身设备或健康监测设备中使用,通过Bluetooth将数据传输至手机或电脑。位置传感器:如GPS模块,通过Bluetooth与其他设备共享位置信息。
  • [技术干货] 基于单片机设计的指纹锁(读取、录入、验证指纹)
    一、前言指纹识别技术是一种常见的生物识别技术,利用每个人指纹的唯一性进行身份认证。相比于传统的密码锁或者钥匙锁,指纹锁具有更高的安全性和便利性,以及防止钥匙丢失或密码泄露的优势。基于单片机设计的指纹锁项目是利用STC89C52作为主控芯片,结合AS608光学指纹识别模块和LCD1602显示屏,实现了指纹的读取、录入和验证功能。用户可以通过按键来进行指纹的录入和删除操作,并通过LCD显示屏来查看指纹识别的状态。在该项目中,AS608光学指纹识别模块是核心部件。它使用光学传感器采集指纹图像,然后通过算法进行特征提取和比对,最终判断指纹是否匹配。AS608模块具有高精度和高速的指纹识别能力,并且支持多种功能指令,如添加指纹、删除指纹和验证指纹等。另外,LCD1602显示屏提供了可视化的界面,能够直观地显示指纹识别的状态信息。用户可以通过观察LCD显示屏上的提示信息,了解指纹录入、删除和验证的结果。步进电机是用于模拟开锁操作的部件,通过正反转来实现门锁的解锁和上锁。当指纹验证成功时,步进电机会进行适当的旋转,使门锁打开,允许用户进入。该项目的背景是为了满足人们对安全性和便利性的需求,提供一种高效且可靠的门禁系统。通过指纹识别技术,可以确保只有授权的人员才能够进入特定区域,避免了传统钥匙的遗失和密码的泄露问题。同时,单片机作为主控芯片,具有低功耗、稳定性强等特点,非常适合用于嵌入式系统的设计与开发。这个项目的实施背景可以是家庭门禁系统、办公场所门禁系统、学校宿舍门禁系统等各种需要实现安全控制的场景。二、项目整体设计思路2.1 硬件设计思路(1)主控芯片:选择STC89C52作为主控芯片,它是一款功能强大且广泛使用的单片机。它具有丰富的GPIO口、中断和定时器等功能,能够满足指纹锁项目的需求。(2)光学指纹识别模块:采用AS608光学指纹识别模块作为指纹识别设备,该模块具有高精度的指纹识别能力。它通过光学传感器采集指纹图像,并通过算法进行特征提取和比对,最终实现指纹的识别和验证。(3)显示屏:选用LCD1602液晶显示屏作为人机交互的界面,该显示屏可以直观地显示指纹识别的状态信息,提供用户友好的操作界面。(4)按键:通过按键实现指纹的录入、删除和管理等操作。按键可以设置为添加指纹、删除指纹和确认等功能,方便用户进行指纹的管理。(5)步进电机:使用28BYJ-48步进电机来模拟开锁操作,根据指纹识别结果控制步进电机的正反转。当指纹验证成功时,步进电机会旋转,使门锁打开。2.2 软件设计思路:(1)初始化:在系统启动时,进行相关硬件的初始化操作,包括主控芯片、指纹识别模块、显示屏和步进电机等。(2)指纹录入功能:当用户选择指纹录入操作时,系统会提示用户按下指纹,然后通过光学传感器采集指纹图像,并提取特征信息。将提取的指纹特征存储在芯片的存储器中,以备后续的指纹验证使用。(3)指纹删除功能:用户可以选择删除已录入的指纹,系统会提示用户选择要删除的指纹,并进行相应的删除操作。(4)指纹验证功能:当用户选择指纹验证操作时,系统会提示用户按下指纹,然后通过光学传感器采集指纹图像,并提取特征信息。将提取的特征信息与存储在芯片存储器中的指纹特征进行比对,判断指纹是否匹配。如果匹配成功,则触发步进电机旋转,打开门锁;否则,提示验证失败。(5)显示功能:通过LCD1602显示屏展示指纹验证的状态信息,包括录入、删除和验证等操作的结果。例如,显示指纹录入成功、删除成功或验证成功等信息。(6)错误处理:在系统运行过程中,需要对各种可能出现的错误进行处理,如指纹录入失败、删除失败或验证失败等情况。系统需要及时给出相应的提示信息,以便用户了解具体的错误原因。硬件设计上,主要选用适合的单片机、指纹识别模块、显示屏和步进电机等组件,搭建起指纹锁的硬件平台;软件设计上,利用主控芯片进行指纹录入、删除和验证的功能实现,并通过显示屏展示相关信息,实现一个完整的指纹锁系统。三、硬件连线说明以下是指纹识别模块、显示屏、按键和步进电机的引脚连接:模块引脚连接到单片机的IO口指纹识别模块 AS608VCC5VGNDGNDTXDP1.0 (串口发送)RXDP1.1 (串口接收)液晶显示屏 LCD1602VCC5VGNDGNDSDAP2.0 (I2C总线数据)SCLP2.1 (I2C总线时钟)RSP3.0RWP3.1EP3.2D4-D7P4.0-P4.3按键添加指纹P0.0删除指纹P0.1确认P0.2步进电机 28BYJ-48IN1P5.0IN2P5.1IN3P5.2IN4P5.3四、项目代码设计#include <reg52.h>#include <stdio.h>#include <intrins.h>#include "lcd1602.h"​#define uchar unsigned char#define uint unsigned int​// 指纹识别相关定义#define FINGERPRINT_ADD 0x01 // 添加指纹命令#define FINGERPRINT_DEL 0x02 // 删除指纹命令#define FINGERPRINT_VERIFY 0x03 // 验证指纹命令#define FINGERPRINT_SUCCESS 0x00 // 指纹验证成功返回值#define FINGERPRINT_FAIL 0x01 // 指纹验证失败返回值​// 步进电机相关定义sbit IN1 = P2^0;sbit IN2 = P2^1;sbit IN3 = P2^2;sbit IN4 = P2^3;​// 按键相关定义sbit Key1 = P1^0; // 录入指纹按键sbit Key2 = P1^1; // 删除指纹按键​uchar comdata[19] = {0}; // 串口接收数据缓冲区uchar fingerStatus = 0; // 指纹识别状态,0:未识别,1:已识别uchar fingerprintIndex = 1; // 指纹索引​void delay(uint ms) { while(ms--) { uint xms = 120; // 延时大概1ms while(xms--); }}​void uartInit() { TMOD = 0x20; // 设置T1工作在方式2,8位定时/计数器 SCON = 0x50; // 设置串口工作在模式1,波特率9600 TH1 = 0xFD; // 波特率为9600时的T1重装值 TL1 = 0xFD; TR1 = 1; // 启动T1}​void uartSendByte(uchar dat) { SBUF = dat; // 将数据送入UART发送缓冲区 while(!TI); // 等待UART发送完毕 TI = 0; // 清除发送完成标志位}​void uartSendString(uchar *str) { while(*str) { uartSendByte(*str); str++; }}​void uartInterrupt() interrupt 4 using 1 { uchar c; if (RI) { // 接收到了数据 RI = 0; // 清除接收中断标志位 c = SBUF; // 读取接收到的数据​ if (c == 0xEF && comdata[0] != 0xEF) { // 接收到帧头 comdata[0] = 0xEF; comdata[1] = c; } else if (comdata[1] == 0x01 && comdata[2] == 0xF5) { // 接收到返回数据长度 comdata[2] = c; } else if (comdata[2] != 0) { // 接收到数据 comdata[comdata[2]] = c; if (comdata[2] == 18) { // 数据接收完毕 fingerStatus = comdata[9]; } } }}​void fingerPrintAdd(uchar index) { uchar temp[] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x07, 0x09, index, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1E, 0x00, 0x2C}; uchar checkSum = 0; for (uint i = 2; i < 19; i++) { checkSum += temp[i]; uartSendByte(temp[i]); } temp[19] = (~checkSum) + 1; uartSendByte(temp[19]);}​void fingerPrintDelete(uchar index) { uchar temp[] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x03, 0x0C, index, 0x00, 0x10}; uchar checkSum = 0; for (uint i = 2; i < 13; i++) { checkSum += temp[i]; uartSendByte(temp[i]); } temp[13] = (~checkSum) + 1; uartSendByte(temp[13]);}​void fingerPrintVerify() { uchar temp[] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x03, 0x03, 0x00, 0x07}; uchar checkSum = 0; for (uint i = 2; i < 12; i++) { checkSum += temp[i]; uartSendByte(temp[i]); } temp[12] = (~checkSum) + 1; uartSendByte(temp[12]);}​void stepperMotorRotate(uint steps, uchar direction) { uint i;​ for (i = 0; i < steps; i++) { IN1 = 1; delay(1); IN1 = 0; IN2 = 1; delay(1); IN2 = 0; IN3 = 1; delay(1); IN3 = 0; IN4 = 1; delay(1); IN4 = 0; }​ delay(1000);​ if (direction == 1) { // 正转 for (i = 0; i < 512; i++) { IN4 = 1; delay(1); IN4 = 0; IN3 = 1; delay(1); IN3 = 0; IN2 = 1; delay(1); IN2 = 0; IN1 = 1; delay(1); IN1 = 0; } } else { // 反转 for (i = 0; i < 512; i++) { IN1 = 1; delay(1); IN1 = 0; IN2 = 1; delay(1); IN2 = 0; IN3 = 1; delay(1); IN3 = 0; IN4 = 1; delay(1); IN4 = 0; } }}​void main() { lcdInit(); uartInit();​ EA = 1; // 开启总中断 ES = 1; // 开启串口中断​ while (1) { if (Key1 == 0) { // 录入指纹按键按下 fingerPrintAdd(fingerprintIndex); lcdClear(); lcdSetCursor(0, 0); lcdPrint("Add Finger"); delay(1000); if (fingerStatus == FINGERPRINT_SUCCESS) { // 指纹录入成功 lcdClear(); lcdSetCursor(0, 0); lcdPrint("Add Success"); delay(1000); fingerprintIndex++; } else { // 指纹录入失败 lcdClear(); lcdSetCursor(0, 0); lcdPrint("Add Fail"); delay(1000); } while (Key1 == 0); // 等待按键释放 }​ if (Key2 == 0) { // 删除指纹按键按下 fingerPrintDelete(fingerprintIndex - 1); lcdClear(); lcdSetCursor(0, 0); lcdPrint("Del Finger"); delay(1000); if (fingerStatus == FINGERPRINT_SUCCESS) { // 指纹删除成功 lcdClear(); lcdSetCursor(0, 0); lcdPrint("Del Success"); delay(1000); if (fingerprintIndex > 1) { fingerprintIndex--; } } else { // 指纹删除失败 lcdClear(); lcdSetCursor(0, 0); lcdPrint("Del Fail"); delay(1000); } while (Key2 == 0); // 等待按键释放 }​ fingerPrintVerify(); if (fingerStatus == FINGERPRINT_SUCCESS) { // 指纹验证成功 lcdClear(); lcdSetCursor(0, 0); lcdPrint("Success"); delay(500); stepperMotorRotate(64, 1); // 正转开锁 } else if (fingerStatus == FINGERPRINT_FAIL) { // 指纹验证失败 lcdClear(); lcdSetCursor(0, 0); lcdPrint("Fail"); delay(500); } }}​五、总结项目基于STC89C52单片机设计了一款指纹锁,能够实现指纹的读取、录入和验证等功能。AS608光学指纹识别模块提供了高精度的指纹识别能力,而LCD1602显示屏和按键配合完成了人机交互的功能设计。门锁则通过28BYJ-48步进电机实现正反转模拟开锁的操作。该项目完全自主设计制造,具有较高的实用性和创新性。
  • [技术干货] 基于单片机设计的激光测距仪(采用XKC-Kl200模块)
    一、前言随着科技的不断进步和应用需求的增加,测距仪成为了许多领域必备的工具之一。传统的测距仪价格昂贵、体积庞大,使用起来不够方便。本项目采用STC89C52单片机作为主控芯片,结合XKC-KL200激光测距模块和LCD1602显示器,实现了一个简易且高效的激光测距仪。这个测距仪可以帮助用户快速准确地测量目标与测距仪之间的距离,并将结果通过LCD1602显示器直观地展示出来。目前很多测距仪主要采用超声波或红外线等技术进行测量,但这些方法存在一定的局限性,比如受到环境干扰、测量距离有限等问题。而激光测距技术在测量精度和稳定性方面具有显著优势。XKC-KL200模块是一款基于激光测距原理的模块,具有高精度、快速测量等特点。通过与STC89C52单片机相结合,能够利用模块提供的数据和功能,快速实现一个功能完善的激光测距仪。LCD1602显示器作为输出设备,能够直观地显示测得的距离信息,具有体积小巧、低功耗、易于集成等特点,非常适合作为测距仪的显示屏。通过将测量结果转换为字符串,并利用LCD1602的命令和数据写入函数,可以在显示器上清晰地展示出测得的距离值。基于STC89C52单片机和XKC-KL200激光测距模块的激光测距仪项目,结合了激光测距技术和单片机控制技术,通过LCD1602显示器直观地展示出测得的距离信息。这个项目不仅满足了测距需求,而且具有成本低、体积小、使用方便等优势,无论是在建筑、工程、地理测量还是运动、航空等领域,这个激光测距仪都可以正常使用。二、硬件连线说明【1】LCD1602模块(1)VSS 引脚连接到单片机的GND引脚(地线)(2)VDD 引脚连接到单片机的5V引脚(正电源)(3)VO 引脚可以通过一个10K电位器连接到单片机的GND引脚,用于调节背光亮度(4)RS 引脚连接到单片机的P0口(作为命令/数据选择引脚)(5)RW 引脚连接到单片机的GND引脚(将LCD设为写模式)(6)E 引脚连接到单片机的P1口(作为使能引脚)(7)D0 ~ D7 引脚分别连接到单片机的P2 ~ P7口(作为数据引脚)【2】XKC-KL200激光测距模块(1)VCC 引脚连接到单片机的5V引脚(正电源)(2)GND 引脚连接到单片机的GND引脚(地线)(3)TX 引脚连接到单片机的RXD引脚(串口接收引脚)(4)RX 引脚连接到单片机的TXD引脚(串口发送引脚)三、XKC-KL200激光测距模块XKC-KL200 是一款智能非接触式开关、带有 UART 串口,高低电平或 NPN 驱动输出的激光测距传感器。该传感器利用激光对物体漫反射原理:当人或物体进入传感器设定的感应区域,传感器输出信号,同时能精确输出 距离;人或物体离开感应区后传感器则关闭输出。通信协议硬件采用 uart。 棕色(VCC)、黄色(信号输出)蓝色(GND)、黑色(RXD)供电 5~24V在客户 MCU 电源与 OUTPUT(黄线)之间跨接一个 1K 左右的上拉电阻。串口默认配置: 波特率:9600 、数据位:8 、校验位:无 、停止位:1应用范围(1)智能感应洁具。(2)家居安防。(3)智能检测,智能控制。(4)机器人障碍识别。(5)实时显示距离。(6)水龙头感应、大小便斗自动冲水、自动烘手机、防盗器、自动门铃、楼梯过道感应、电视近距离收看电视提醒器、自动门、广告灯箱、自动垃圾箱。四、项目代码设计#include <reg52.h>#include <stdio.h>​// LCD1602引脚连接sbit LCD_RS = P0^0; // RS引脚接口定义sbit LCD_E = P1^0; // E引脚接口定义sbit LCD_D4 = P2^4; // D4引脚接口定义sbit LCD_D5 = P2^5; // D5引脚接口定义sbit LCD_D6 = P2^6; // D6引脚接口定义sbit LCD_D7 = P2^7; // D7引脚接口定义​// 激光测距模块引脚连接sbit laser_TX = P3^0; // TX引脚接口定义sbit laser_RX = P3^1; // RX引脚接口定义​// LCD1602初始化void LCD_Init() { LCD_WriteCommand(0x02); // 回到Home位置 LCD_WriteCommand(0x28); // 设置4位数据总线、2行显示、5x8点阵 LCD_WriteCommand(0x0C); // 显示开,关游标 LCD_WriteCommand(0x06); // 光标右移 LCD_WriteCommand(0x01); // 清屏}​// 向LCD写入命令void LCD_WriteCommand(unsigned char command) { LCD_RS = 0; // 将RS置低,指定为写入命令 LCD_E = 0; // 拉低E线,准备写入 LCD_D4 = command >> 4 & 0x01; // 写入高4位数据 LCD_D5 = command >> 5 & 0x01; LCD_D6 = command >> 6 & 0x01; LCD_D7 = command >> 7 & 0x01; LCD_E = 1; // 拉高E线,写入命令 DelayMs(1); // 延时等待 LCD_E = 0; // 拉低E线,结束写入 LCD_D4 = command >> 0 & 0x01; // 写入低4位数据 LCD_D5 = command >> 1 & 0x01; LCD_D6 = command >> 2 & 0x01; LCD_D7 = command >> 3 & 0x01; LCD_E = 1; // 拉高E线,写入命令 DelayMs(1); // 延时等待 LCD_E = 0; // 拉低E线,结束写入}​// 向LCD写入数据void LCD_WriteData(unsigned char dat) { LCD_RS = 1; // 将RS置高,指定为写入数据 LCD_E = 0; // 拉低E线,准备写入 LCD_D4 = dat >> 4 & 0x01; // 写入高4位数据 LCD_D5 = dat >> 5 & 0x01; LCD_D6 = dat >> 6 & 0x01; LCD_D7 = dat >> 7 & 0x01; LCD_E = 1; // 拉高E线,写入数据 DelayMs(1); // 延时等待 LCD_E = 0; // 拉低E线,结束写入 LCD_D4 = dat >> 0 & 0x01; // 写入低4位数据 LCD_D5 = dat >> 1 & 0x01; LCD_D6 = dat >> 2 & 0x01; LCD_D7 = dat >> 3 & 0x01; LCD_E = 1; // 拉高E线,写入数据 DelayMs(1); // 延时等待 LCD_E = 0; // 拉低E线,结束写入}​// 清空LCD显示void LCD_Clear() { LCD_WriteCommand(0x01); // 清屏指令 DelayMs(2); // 延时等待}​// 在指定位置显示字符串void LCD_DisplayString(unsigned char x, unsigned char y, unsigned char *str) { unsigned char addr; if (y == 0) { addr = 0x80 + x; // 第一行地址计算 } else { addr = 0xC0 + x; // 第二行地址计算 } LCD_WriteCommand(addr); // 设置显示位置 while (*str != '\0') { LCD_WriteData(*str); // 逐个显示字符 str++; }}​// 毫秒级延时函数void DelayMs(unsigned int ms) { unsigned int i, j; for (i = ms; i > 0; i--) { for (j = 110; j > 0; j--); }}​// 串口初始化void UART_Init() { TMOD = 0x20; // 设置定时器1为模式2 TH1 = 0xFD; // 设置波特率为9600 TL1 = 0xFD; TR1 = 1; // 启动定时器1开始工作 SCON = 0x50; // 设置UART为模式1,允许接收}​// 串口数据接收unsigned char UART_Receive() { while (!RI); // 等待接收完成 RI = 0; return SBUF; // 返回接收到的数据}​五、总结在本项目中,成功设计了一个激光测距仪,使用STC89C52作为主控芯片,搭配XKC-Kl200激光测距模块。通过串口通信的方式,能够获取到被测物体与激光测距模块之间的距离,并将其实时显示在LCD1602液晶显示屏上。项目的实现过程中,先进行硬件连接,将STC89C52与XKC-Kl200模块通过串口相连,连接了LCD1602显示屏。编写程序代码,LCD1602和串口通信的初始化函数,以及数据的接收和显示函数。通过激光测距仪,可以方便地获得不同物体与测距模块之间的距离信息,并通过LCD1602显示出来。这为测量工作提供了便利,无论是在科研实验、工程测量还是日常生活中,都具有广泛的应用前景。
  • [技术干货] 基于STM32设计的格力空调遥控器
    一、格力空调协议介绍格力空调的红外控制协议被称为格力红外通讯协议或者格力红外遥控协议。这个协议定义了一系列红外信号,可以用来控制格力空调的各种操作,例如开关、温度控制、模式选择、风速控制等等。格力空调的红外控制协议是一种自定义协议,它并没有像NEC、RC5、RC6等协议一样被广泛应用。因此,不同型号的格力空调可能会有不同的红外控制协议。如果想要使用红外发送器控制格力空调,需要先了解当前空调使用的是哪种红外控制协议。一般来说,格力空调的红外控制协议包含一个头部和一系列数据位。头部通常由一个起始位和一个引导位组成。数据位通常包括操作码、温度、模式、风速等信息。下面是一个格力空调红外控制信号:Start Bit: 9000usLead-in: 4500us, 4500usData: 0x88, 0x20, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00在这个示例中,红外控制信号的起始位持续时间为9000微秒。接下来是一个引导位,持续时间为4500微秒和4500微秒。引导位后面是一系列数据位,包括操作码、温度、模式、风速等信息。在这个示例中,操作码为0x88,表示将空调设置为制冷模式,并把温度设置为32度。要控制格力空调,需要使用一个红外发送器,将这个信号通过红外光线发送到空调控制器。可以使用STM32F103ZET6的GPIO引脚控制红外线发送管来发送这个信号。二、代码实现2.1 代码实现#include "stm32f10x.h"​// 定义红外发送器引脚#define IR_SENDER_GPIO_PORT GPIOA#define IR_SENDER_GPIO_PIN GPIO_Pin_1​// 定义红外发送器协议参数#define IR_PROTOCOL_FREQ 38000 // 红外协议频率#define IR_PROTOCOL_START_DURATION 9000 // 红外协议起始位持续时间#define IR_PROTOCOL_LEADIN_DURATION 4500 // 红外协议引导位持续时间#define IR_PROTOCOL_DATA_BIT_0_DURATION 560 // 红外协议数据位0持续时间#define IR_PROTOCOL_DATA_BIT_1_DURATION 1690 // 红外协议数据位1持续时间​// 发送一个红外协议信号void IR_SendProtocol(uint32_t protocol){ uint8_t i, j; uint32_t data_bit;​ // 发送起始位 GPIO_SetBits(IR_SENDER_GPIO_PORT, IR_SENDER_GPIO_PIN); delay_us(IR_PROTOCOL_START_DURATION);​ // 发送引导位 GPIO_ResetBits(IR_SENDER_GPIO_PORT, IR_SENDER_GPIO_PIN); delay_us(IR_PROTOCOL_LEADIN_DURATION); GPIO_SetBits(IR_SENDER_GPIO_PORT, IR_SENDER_GPIO_PIN); delay_us(IR_PROTOCOL_LEADIN_DURATION);​ // 逐位发送协议数据 for (i = 0; i < 14; i++) { data_bit = (protocol >> i) & 0x01; // 获取当前位的值​ // 发送数据位 GPIO_ResetBits(IR_SENDER_GPIO_PORT, IR_SENDER_GPIO_PIN); if (data_bit) { delay_us(IR_PROTOCOL_DATA_BIT_1_DURATION); } else { delay_us(IR_PROTOCOL_DATA_BIT_0_DURATION); } GPIO_SetBits(IR_SENDER_GPIO_PORT, IR_SENDER_GPIO_PIN); delay_us(IR_PROTOCOL_DATA_BIT_0_DURATION); }}​// 控制格力空调开关void IR_ControlPower(int on_off){ uint32_t protocol;​ if (on_off) { // 开机 protocol = 0x8820000200200000; } else { // 关机 protocol = 0x8820000000200000; }​ IR_SendProtocol(protocol);}​// 控制格力空调温度void IR_ControlTemperature(int temperature){ uint32_t protocol;​ if (temperature < 16 || temperature > 31) { return; // 温度范围无效 }​ // 温度码为0x20加上实际温度值 protocol = 0x8820002020000000 | (temperature - 16);​ IR_SendProtocol(protocol);}​// 控制格力空调模式void IR_ControlMode(int mode){ uint32_t protocol;​ switch (mode) { case 0: // 制冷 protocol = 0x8820000200200000; break; case 1: // 制热 protocol = 0x8820000400200000; break; case 2: // 自动 protocol = 0x8820000800200000; break; case 3: // 送风 protocol = 0x8820001000200000; break; default: return; // 模式无效 }​ IR_SendProtocol(protocol);}​// 控制格力空调风速void IR_ControlFanSpeed(int fan_speed){ uint32_t protocol;​ switch (fan_speed) { case 0: // 自动风速 protocol = 0x8820002000200000; break; case 1: // 低风速 protocol = 0x8820004000200000; break; case 2: // 中风速 protocol = 0x8820008000200000; break; case 3: // 高风速 protocol = 0x8820010000200000; break; default: return; // 风速无效 }​ IR_SendProtocol(protocol);}在代码中,定义了一些常量来表示格力空调红外控制协议的参数,例如红外协议频率、起始位持续时间、引导位持续时间、数据位0和数据位1的持续时间。也定义了一些函数来控制格力空调的各种操作,例如控制开关、温度、模式和风速。这些函数调用了IR_SendProtocol()函数来发送适当的红外信号。在IR_SendProtocol()函数中,先发送起始位和引导位。然后,逐位发送协议数据,根据数据位的值发送适当的红外信号。在代码中,使用delay_us()函数来延迟一定的时间来模拟红外信号的持续时间。2.2 常见的控制码以下是一些常见的格力空调红外控制协议:【1】控制开关// 开机0x8820000200200000​// 关机0x8820000000200000【2】控制温度// 温度16度0x8820002020000000​// 温度17度0x8820002120000000​// 温度18度0x8820002220000000​// ...​// 温度31度0x8820003F20000000【3】控制模式// 制冷0x8820000200200000​// 制热0x8820000400200000​// 自动0x8820000800200000​// 送风0x8820001000200000【4】控制风速// 自动风速0x8820002000200000​// 低风速0x8820004000200000​// 中风速0x8820008000200000​// 高风速0x8820010000200000【5】控制扫风// 扫风开0x8820080000200000​// 扫风关0x8820100000200000【6】控制节能模式// 节能开0x8820200000200000​// 节能关0x8820400000200000【7】控制睡眠模式// 睡眠开0x8821000000200000// 睡眠关0x8822000000200000【8】控制定时开关机// 定时开机,时间为1小时0x8824000000200000// 定时开机,时间为2小时0x8828000000200000// 定时开机,时间为3小时0x8830000000200000// ...// 定时开机,时间为24小时0x8878000000200000// 取消定时开关机0x8820000000400000【9】控制清新功能// 清新开0x8820000002200000// 清新关0x8820000004200000【10】控制快速冷热// 快速冷0x8820000080200000// 快速热0x8820000040200000【11】控制干燥功能// 干燥开0x8820000001200000// 干燥关0x8820000002200000【12】控制自清洁功能// 自清洁开0x8820000008200000// 自清洁关0x8820000010200000【13】控制室内外循环功能// 室内外循环开0x8820020000200000// 室内外循环关0x8820040000200000
  • Huawei LiteOS编译时出错
    在执行编译时出现了以下错误make[1]: Entering directory 'e:/HuaweiEmbed/pj2' python  e:/HuaweiEmbed/pj2/tools/menuconfig/usr_config.py savemenuconfig process_begin: CreateProcess(NULL, python e:/HuaweiEmbed/pj2/tools/menuconfig/usr_config.py savemenuconfig, ...) failed. make (e=2): 系统找不到指定的文件。 make[1]: *** [tools/menuconfig/Makefile.kconfig:12: savemenuconfig] Error 2 make[1]: Leaving directory 'e:/HuaweiEmbed/pj2' make: *** [Makefile:53: e:/HuaweiEmbed/pj2/targets/menuconfig.h] Error 2求大佬解答
  • [问题求助] 构建环境时在MobaXterm中一直卡在获取源码上无法installed
    如图,在输入指令hpm i @bearpi/bearpi_hm_nano后,一直都卡在这里动不了,无法弹出installed,也无法输入下一步指令网络连接:正常ip地址:用Ubuntu获取的及时ip在虚拟盘里确实已经建立了code文件夹了,如图想问下应该如何继续呢
总条数:235 到第
上滑加载中