如何使用4G模块通过MQTT协议传输温湿度数据到onenet
本次实验是采用SIM7600CE 4G cat4 模块进行操作的,本模块支持GNSS定位功能。也可以采用别的4G模块,只要支持TCP传输就行。本模块支持的AT命令相当强大,拥有TCP&UDP命令集,HTTP命令集,MQTT命令集。本模块虽然已经支持了MQTT功能,但是我们在实际研发项目中,为了保持项目的可移植性和兼容性,通常不会使用模块自带的MQTT命令集,而是用代码实现MQTT上层功能,底层再调用模块的TCP AT命令集。
一、MQTT简介
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。它是IBM 1999年发布的一个用于物联网的协议.MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。
在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
MQTT设计思想是开放、简单、轻量、易于实现。这些特点使它适用于受限环境。
例如,但不仅限于此: 网络代价昂贵,带宽低、不可靠。 在嵌入设备中运行,处理器和内存资源有限。
该协议的特点有:
使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;对负载内容屏蔽的消息传输;使用 TCP/IP 提供网络连接;有三种消息发布服务质量:小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。二、ONENET平台
OneNET是由中国移动打造的物联网开放平台。平台能够帮助开发者轻松实现设备接入与设备连接,快速完成产品开发部署,为智能硬件、智能家居产品提供完善的物联网解决方案。
OneNET具备如下功能:
专网专号:中国移动基于物联网特点打造的专业化网络通道,提供“云-管-端”一体化的智能管道和支撑系统,支持工业级、车规级的专网卡和通信模组;海量连接:基于多类型标准协议和API开发满足海量设备的高并发快速接入;在线监控:实现终端设备的监控管理、在线调试、实时控制功能;数据存储:基于分布式云存储、消息对象结构、丰富的数据调用接口实现数据高并发读、写库操作,有效保障数据的安全;消息分发:将采集的各类数据通过消息转发、短彩信推送、APP信息推送方式快速告知业务平台、用户手机、APP客户端,建立双向通信的有效通道;能力输出:汇聚中国移动短彩信、位置服务、视频服务、公有云等核心能力,提供标准API接口,缩短终端与应用的开发周期;事件告警:打造事件触发引擎,用户可以基于引擎快速实现应用逻辑编排;数据分析:基于Hadoop等提供统一的数据管理与分析能力。三、如何接入OneNet
用户要接入 OneNet,
首先要在 OneNet 平台上进行注册用户、创建设备等一系列准备工作,然后要通过网络和 OneNet 服务器建立TCP 连接,最后将用户数据按照一定的协议(比如:EDP、RestFul API,MQTT)打包上传至 OneNet 平台,从而实现设备终端接入。 用户注册:在接入 OneNet 之前,需在平台网站注册用户账户,登陆网址:http://open.iot.10086.cn/,显示如下界面:如上图:点击右上角进行注册.
注册完成后,进行登录,如下:
如上图所示,点击MQTT物联网套件:
如上图所示,选择多协议接入:
如上图,点击添加产品,onenet平台MQTT协议框架下分为产品ID和设备ID,可以建立多个不同的产品,每个产品拥有不同的设备,设备以ID号进行区分。
如上图所示进行选择,接下来还有操作系统选择linux,网络运营商选择移动,因为模块使用的移动的usim卡,可以根据自己模块卡类型进行选择。
如上图所示,再点击添加设备:
点击右上角添加设备,
点击添加,
如上图所示,产品添加成功后的截图,显示为离线状态,当4G模块有数据上传的时候,就会显示为在线。
四、单片机&4G模块与ONENET服务器通信
下面讲解一下如何用单片机通过串口AT命令驱动4G模块与ONENET服务器进行连接,单片机采用STM32F103RBT6 ,开发环境采用MDK5.
如上图所示,606923821表示设备ID ,PZqqtD0O=5lNOex4NshJn0RLflY= 表示API KEY
如上图,点击查看,输入手机号发送验证码即可获得,357092 表示产品ID,上图的产品ID是424250 ,可根据自己的情况进行填写。
CAT11表示鉴权信息,鉴权信息可以在设备列表-设备详情进行获取。183.230.40.3, 6002 表示onenet平台的服务器和端口号 ,有了这些信息就可以用4G模块和自己创建的产品进行通信了。
如下图所示:既可观察设备的上下线情况,以及设备的数据
五、资料获取及硬件
1) 后台回复 mqtt 即可获得对应的源代码功能。
2) 文中单片机 STM32F103RBT6 可以自行到某宝搜索, 推荐如下:
3) 用的SIM卡可以用手机卡也可以用物联网卡,
4) 4g模块通用的模块即可。
51单片机超声波水位控制器设计 (C源码+PCB+原理图与实物制作)
最近几天总是下雨,搞的人心情也很差,昨天看到有粉丝留言说,导师让他们组做一个关于超声波控制液体水位的项目,但是无从下手,希望我帮帮他,可以有偿。他是我的铁粉,下面我就把我前段时间帮人家设计的一个超声波水位自动控制系统 分享出来,希望可以帮到他们。
51单片机超声波水位控制器设计
设计说明:
本设计中液晶显示有4个字母,分别为
H------容器的最高水位设定值(不能高于实际高度) L------容器的最低水位设定值
D-----容器实际高度(可以设置)
C-----容器内液体的高度(在实际演示中,障碍物离探头越近,液晶C显示越大,因为障碍物好比液面,离探头近了说明水位高了)
特别提醒:如果容器实际高度D你设置为1米,那么C液体的高度最高能测到98cm,因为探头的盲区在2cm左右。如果D设为2米,那么最高能测到1.98m.
按键功能分别为:设置键 增加键 减小键 复位键
三个指示灯的分别功能为:红色----超过设定的最高水位H 黄色-----低于设定的最低水位L
绿色----最高H和最低L中间
本文采用AT89C52单片机系统实现了水塔水位的自动控制,设计出一种低成本、高实用价值的水塔水位控制器。该系统具有水位检测、水位高度LCD显示、低水位高水位报警以及自动加水等功能。
本设计过程中主要采用了传感技术、单片机技术、光报警技术以及弱电控制强电的技术。本设计传感器使用了超声波模块,并且详细阐述了超声波测距测的原理,给出了系统构成框图。此系统具有易控制、工作可靠、测量精度高的优点,可实时监控液位。并采用52单片机系统控制整个电路的信号处理以及采用光电耦合和继电器来实现弱电控制强电来实现加水系统的自动控制。它能自动完成水位检测、光报警、上水停水的全部工作循环,保证液面高度始终处于较理想的范围内,它结构简单,制造成本低,灵敏度高,节约能源显著,是用于各种高层液体储存的理想设备。
为了大家更好地理解,请如下看示意图
制作出来的实物图如下:
AD的设计图如下:
超声波水位控制器元件清单
1) 9*15万用板 1
2) AT89C51单片机 1
3) 超声波探头 0
4) 40脚IC座 1
5) 4脚排针 0
6) 杜邦线4根 0
7) 继电器*2 0
8) LCD1602液晶 1
9) 103电位器 0
10) 16脚IC座 0
11) 16脚排针 1
12) 蜂鸣器 0
13) 8550三极管*3 0
14) 1k电阻*8 0
15) 10k电阻 0
16) 10uf电容 0
17) 30pf电容*2 0
18) 12M晶振 1
19) 3mmLED(红、绿各2个,黄1个) 0
20) 轻触按键*4 1
21) 自锁开关 1
22) DC电源插口 1
23) USB电源线(电池盒)
24) 直流水泵*2(根据客户自选)
单片机程序源码如下:
/***************************************************************
名称:基于51单片机的超声波水位监测报警系统
单片机型号:AT89C51
单片机设置:时钟12T,晶体12MHZ
作者:从零开始学单片机
注:修改增加水泵控制和排水控制,即双继电器
***************************************************************/
#include <reg51.h>
#include <intrins.h> // 包含循环移位:_cror_
#include "main.h"
//----------------------------------------------------------------------
uchar code TabNumASCII[10] = {'0','1','2','3','4','5','6','7','8','9'};
bool g_flag = isNo; //用于标记超时(65.536ms)
bool g_flag05s = isNo; //用于标记0.52秒
uchar ucCount = 0; //用于计数0.52秒
uint uiH = 80; //设定的最高报警水位 H
uint uiL = 30; //设定的最低报警水位 L
uint uiD = 100; //检测探头到水库底部的距离 D
bool g_flagSwitch = isNo; //控制阀门连续开启间隔延时(保护)标志
bool g_flagBeepTimer = isNo; //定时提醒标志
//-----------------------------------------------------------------------
// 延时10us
void delay10us(void) //@12MHz
{
unsigned char i;
_nop_();
i = 2;
while (--i);
}
// 延时100us
void delay100us(void) //@12MHz
{
uchar i;
_nop_();
i = 47;
while (--i);
}
// 延时125us
void delay125us(void) //@12MHz
{
unsigned char i;
i = 60;
while (--i);
}
// 延时5ms
void delay5ms(void) //@12.000MHz
{
unsigned char i, j;
i = 10;
j = 183;
do
{
while (--j);
} while (--i);
}
// 延时500ms
void delay500ms(void) //@12MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//-----------------------------------------------------------------------
//初始化IO端口
void initIO(void)
{
P0 = 0xff;
P1 = 0xff;
P2 = 0xff;
P3 = 0xff;
}
// 初始化定时器0,定时器时钟12T模式 模式1,16位 @12.000MHz
void initTimer0(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0; //定时器初值清零
TH0 = 0; //定时器初值清零
//TR0 = 1; //开定时器0
ET0 = 1; //开定时器0中断
EA = 1; //开总中断
}
// 初始化定时器1,定时器时钟12T模式 模式1,16位 @12.000MHz
void initTimer1(void) //50毫秒@12.000MHz
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0xB0; //设置定时初值
TH1 = 0x3C; //设置定时初值
TR1 = 1; //定时器1开始计时
ET1 = 1; //开定时器0中断
}
//-----------------------------------------------------------------------
//定时器0中断
void zd0(void) interrupt 1
{
g_flag = isYes; //中断溢出标志,g_flag = isYes超过测距范围
if(++ucCount >= 8)
{
ucCount = 0;
g_flag05s = isYes; //g_flag05s = isYes定时0.52秒到,用于测量周期延时
}
TL0 = 0; //设置定时初值
TH0 = 0; //设置定时初值
}
//定时器1中断 定时50ms
void tm1_isr() interrupt 3 using 1
{
static uchar count = DATA_switchTime; //50ms的200倍 = 10S
static uchar uiCount = 1200; // = 1分钟
static uint uiCount_BeepTimer = DATA_BeepTimer;
TL1 = 0xB0; //设置定时初值
TH1 = 0x3C; //设置定时初值
if (g_flagSwitch == isNo)
{
if (count-- == 0) //50ms * 200 -> 10s
{
count = DATA_switchTime;
g_flagSwitch = isYes;
// TR1 = 0;
}
}
if(g_flagBeepTimer == isNo)
{
if (uiCount-- == 0) //= 1分钟
{
uiCount = 1200;
if(uiCount_BeepTimer-- == 0)
{
uiCount_BeepTimer = DATA_BeepTimer;
g_flagBeepTimer = isYes;
// TR1 = 0;
}
}
}
}
//-----------------------------------------------
//外部中断1
void exint1() interrupt 2
{
EX1 = 0; //关闭当前中断
TR0 = 0; //关闭时器0
}
//-----------------------------------------------------------------------
//读LCD忙状态并等待忙状态结束
void LCD_waitNotBusy(void)
{
IO_LCD_Data = 0xFF;
io_LCD_RS = 0;
io_LCD_RW = 1;
io_LCD_E = 0;
_nop_();
_nop_();
io_LCD_E = 1;
while(IO_LCD_Data & 0x80); //检测如果是忙信号,一直等到不忙
}
//给LCD写指令
void LCDWriteCommand(uchar command,bool ifReadBusy) //ifReadBusy = 1 时先进行忙检测
{
if (ifReadBusy == isReadBusy) LCD_waitNotBusy(); //根据需要检测忙
IO_LCD_Data = command;
io_LCD_RS = 0;
io_LCD_RW = 0;
io_LCD_E = 0;
_nop_();
_nop_();
io_LCD_E = 1;
}
//给LCD写数据
void LCDWriteData(uchar dat)
{
LCD_waitNotBusy(); //等到不忙
IO_LCD_Data = dat;
io_LCD_RS = 1;
io_LCD_RW = 0;
io_LCD_E = 0;
_nop_();
_nop_();
io_LCD_E = 1;
}
// 初始化LCD1602液晶显示屏
void initLCD1602(void)
{
uchar i;
IO_LCD_Data = 0; // 数据端口清零
for(i = 0; i < 3; i++) // 设置三次显示模式
{
LCDWriteCommand(0x38,isNotReadBusy); // 不检测忙信号
delay5ms();
}
LCDWriteCommand(0x38,isReadBusy); // 设置显示模式,检测忙信号
LCDWriteCommand(0x08,isReadBusy); // 关闭显示
LCDWriteCommand(0x01,isReadBusy); // 显示清屏
LCDWriteCommand(0x06,isReadBusy); // 显示光标移动设置
LCDWriteCommand(0x0F,isReadBusy); // 显示开及光标设置
}
//按指定位置显示一个字符
void putOneCharToLCD1602(uchar line, uchar position, uchar ucData)
{
line &= DATA_LineMax;
position &= DATA_PositionMax;
if (line == DATA_LineTow) position |= 0x40; //当要显示第二行时地址码+0x40;
position |= 0x80; //设置两行显示格式 D7 = 1;
LCDWriteCommand(position, isReadBusy); //发送命令 设置字符地址
LCDWriteData(ucData); //写入字符的数据
}
//按指定位置显示一串字符
void putLineCharsToLCD1602(uchar line, uchar position, uchar count, uchar code *ucData)
{
uchar i;
for(i = 0; i < count; i++) //连续显示单个字符
{
putOneCharToLCD1602(line, position + i, ucData[i]);
}
}
//按指定位置连续显示三个字符(三位数字)
void putThreeCharToLCD1602(uchar line, uchar position, uint uiNumber)
{
uiNumber %= 1000;
putOneCharToLCD1602(line, position, TabNumASCII[uiNumber / 100]);
putOneCharToLCD1602(line, ++position, TabNumASCII[uiNumber % 100 / 10]);
putOneCharToLCD1602(line, ++position, TabNumASCII[uiNumber % 100 % 10]);
}
// 按键检测子程序,有键按下返回键端口数据,无键返回0
uchar GetKey(void)
{
uchar KeyTemp = (IO_KEY | DATA_KEY_ORL); //获取按键端口数据
if( KeyTemp != DATA_KEY_Null ) // 如果不为空
{
uchar CountTemp = 0;
do
{
delay125us();
if(KeyTemp != (IO_KEY | DATA_KEY_ORL)) return 0; //在延时期间检测键,如果不稳定保持则退出
} while(++CountTemp > Data_Key20msCountMax); // 延时20ms去抖动
while((IO_KEY | DATA_KEY_ORL) != DATA_KEY_Null); //等键释放
return KeyTemp; // 有键按下返回键端口数据
}
return 0; // 无有效键返回0
}
//加一
uchar INC_Number(uchar Number, uchar Min, uchar Max)
{
if(Number >= Max) return Min; else return (++ Number);
}
//减一
uchar DEC_Number(uchar Number, uchar Min, uchar Max)
{
if(Number <= Min) return Max; else return (-- Number);
}
// 检测到有按键后 这里执行按键任务
void execute_key_task(uchar ucKeyValue)
{
uchar state = 0; //定义调整数据的状态变量
uchar keyValue = 0; //定义键值得临时变量
if(ucKeyValue != DATA_KEY_Set) return; //不是设置键退出
//是设置键继续-----------------------------------------------------
putLineCharsToLCD1602(lineTow, 8, 8, "C:000cm "); //清零显示当前距离CURRENT
putThreeCharToLCD1602(lineOne, 8 + 2, uiD); //光标调整到调整总距离(检测探头到水库底部的距离“D:000cm”)
while(1)
{
keyValue = GetKey();
if(keyValue == 0) continue;
switch(keyValue)
{
case DATA_KEY_Set:
{
// 如果按的是设置键,顺序设置总距离D——高水位H——低水位L——退出
switch(state)
{
case 0: // 如果是设置总距离状态,改变为设置高水位状态,并显示高水位,实现移动光标到高水位后面
{
state = 1;
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
break;
case 1:
{
uchar tempMax = uiD - DATA_uiD_Min;
if(tempMax < 2 + 2) tempMax = 2 + 2;
if(uiH > tempMax)
{
uiH = tempMax;
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
else if(uiH < 2 + 2)
{
uiH = 2 + 2;
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
state = 2;
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
break;
case 2:
{
if(uiL > uiH - 2)
{
uiL = uiH - 2;
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
return;
}
break;
}
}
break;
// 如果按的是增加键,改变相应数据并显示
case DATA_KEY_INC:
{
switch(state)
{
case 0:
{
uiD = INC_Number(uiD, DATA_uiD_Min, DATA_uiD_Max);
putThreeCharToLCD1602(lineOne, 8 + 2, uiD);
}
break;
case 1:
{
uchar tempMax = uiD - DATA_uiD_Min;
if(tempMax < 2 + 2) tempMax = 2 + 2;
uiH = INC_Number(uiH, 2, tempMax);
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
break;
case 2:
{
uiL = INC_Number(uiL, 0, uiH - 2);
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
break;
}
}
break;
// 如果按的是减少键,改变相应数据并显示
case DATA_KEY_DEC:
{
switch(state)
{
case 0:
{
uiD = DEC_Number(uiD, DATA_uiD_Min, DATA_uiD_Max);
putThreeCharToLCD1602(lineOne, 8 + 2, uiD);
}
break;
case 1:
{
uchar tempMax = uiD - DATA_uiD_Min;
if(tempMax < 2 + 2) tempMax = 2 + 2;
uiH = DEC_Number(uiH, 2, tempMax);
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
break;
case 2:
{
uiL = DEC_Number(uiL, 0, uiH - 2);
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
break;
}
}
break;
}
}
}
// 蜂鸣器
void buzzerCall(void)
{
uchar i;
for(i = 0; i < 90; i++)
{
io_Buzzer = 0;
delay100us();
io_Buzzer = 1;
delay100us();
delay100us();
}
delay100us();
delay100us();
}
//计算水位
bool CalculatedWaterLevel(void)
{
uchar i = 8 + 2; //当前水位的数字在LCD屏显示的起点位置
uint uiTime; //声波传播时间
ulong ulDis; //实时测量到距离
uiTime = TH0 << 8 | TL0;
ulDis = (uiTime * 3.40) / 200; //计算当前测量的距离,单位cm
TH0 = 0;
TL0 = 0;
if((ulDis > uiD) || (g_flag == isYes )) // ulDis > uiD 超出测量范围;g_flag == isYes超时;
{
g_flag = isNo;
TR0 = 0;
putLineCharsToLCD1602(lineTow, i, 3, "Err"); // 显示Err
//阀门动作:
// if(g_flagSwitch == isYes)
// {
// io_Control_Inlet = isio_Control_Inlet_OFF;
// io_Control_Outlet = isio_Control_Outlet_ON;
// g_flagSwitch = isNo;
// }
//指示灯:
ioLed_Red = ! ioLed_Red; // 三个灯同时快速闪亮
ioLed_Green = ! ioLed_Green;
ioLed_Yellow = ! ioLed_Yellow;
// 蜂鸣器叫:
if(buzzerCallFlag == isCall)
{
buzzerCall(); // 蜂鸣器叫
}
return isNo; // 返回错误信息
}
else
{
ulDis = uiD - ulDis; // 当前水位C = 总距离 - 当前检测到的距离
if(ulDis > uiH) // 如果水位超高
{
//阀门动作:
io_Control_Inlet = isio_Control_Inlet_OFF;
io_Control_Outlet = isio_Control_Outlet_ON;
g_flagSwitch = isNo;
//指示灯:
ioLed_Red = ! ioLed_Red; // 红灯闪
ioLed_Green = isLedOFF;
ioLed_Yellow = isLedOFF;
// 蜂鸣器叫:
if(ulDis - uiH > (uiD - uiH) / DATA_alarmCoefficient) //当“当前水位”超出最高水位“ ((“总高度减高水位)除以2的值”)时报警
{
buzzerCall(); // 蜂鸣器叫
}
}
else if(ulDis < uiL) // 如果水位超低
{
//阀门动作:
if(g_flagSwitch == isYes)
{
io_Control_Outlet = isio_Control_Outlet_OFF;
io_Control_Inlet = isio_Control_Inlet_ON;
g_flagSwitch = isNo;
}
//指示灯:
ioLed_Red = isLedOFF;
ioLed_Green = isLedOFF;
ioLed_Yellow = ! ioLed_Yellow; //黄灯闪
// 蜂鸣器叫:
if( uiL - ulDis > uiL / DATA_alarmCoefficient)//uiL / 2 当“当前水位”低于“低水位” “低水位除以2的值”时报警
{
buzzerCall(); // 蜂鸣器叫
}
}
else // 水位在正常范围
{
ioLed_Red = isLedOFF;
ioLed_Green = ! ioLed_Green;
ioLed_Yellow = isLedOFF;
}
putThreeCharToLCD1602(lineTow, i, ulDis);
return isYes;
}
return isYes;
}
void main(void)
{
initIO(); //初始化IO端口
delay500ms(); //启动延时,给器件进入正常工作状态留够时间
initLCD1602(); //LCD初始化
putLineCharsToLCD1602(lineOne, 8, 8, "D:000cm "); //显示distance (总)距离(检测探头到水库底部的距离)D
putThreeCharToLCD1602(lineOne, 8 + 2, uiD); //显示三位数值
putLineCharsToLCD1602(lineOne, 0, 8, "H:000cm "); //显示设定的最高报警水位H
putThreeCharToLCD1602(lineOne, 0 + 2, uiH); //显示三位数值
putLineCharsToLCD1602(lineTow, 0, 8, "L:000cm "); //显示设定的最低报警水位L
putThreeCharToLCD1602(lineTow, 0 + 2, uiL); //显示三位数值
putLineCharsToLCD1602(lineTow, 8, 8, "C:000cm "); //显示当前CURRENT水位C
initTimer0(); //初始化定时器0
initTimer1();
//阀门动作:初始先排水
io_Control_Inlet = isio_Control_Inlet_OFF;
io_Control_Outlet = isio_Control_Outlet_ON;
g_flagSwitch = isNo;
while(1)
{
io_US_TX = 1; //启动超声波模块信号
delay10us();
io_US_TX = 0;
while(io_US_RX == 0); //等待计时开始
TR0 = 1; //开启定时器0,计时开始
IT1 = 1; //设置外中断INT1输入信号模式(1:Falling only仅下降沿有效 0:Low level低电平有效)
EX1 = 1; //使能外中断INT1
while(EX1 == 1 && g_flag == isNo)//等待中断或超时退出
{
uchar ucKeyValue = GetKey(); //在等待中检测按键
if(ucKeyValue) execute_key_task(ucKeyValue); //如果有键按下则执行按键任务
}
if(CalculatedWaterLevel() == isNo) continue; //计算水位,如果超出范围返回isNo并重新循环
TR0 = 0; //暂时关闭定时器0
//清零定时器和计数变量以及标志
TL0 = 0;
TH0 = 0;
g_flag = isNo;
ucCount = 0;
g_flag05s = isNo;
TR0 = 1; //打开定时器0
鉴于篇幅限制,只能写部分代码
最后,如果有什么意见或者建议欢迎您留言给我,让我们共同学习一起进步,
如果需要 完整代码或设计文件,请在下方留言或者私信我,看到后会第一时间回复。
谢谢!
感谢你的阅读,希望您有所收获,喜欢请点赞评论加关注!
相关问答
如何把hex 程序 转化成源代码?不能因为hex程序已经是经过机器代码翻译后的二进制文件,不再具有可读性,无法直接转化成源代码(即高级语言)。如果需要获取源代码,只能通过重新编写程序或使...
单片机程序 用什么软件可以拷贝出来修改?单片机程序你即使能复制出来也没用啊!二进制文件,又不是源代码,你直接修改二进制文件的话有可能导致运行出错,无法运行等问题,当然除非你是反编译高手,既然...
proteus如何查看源代码?适合你的集成开发环境(IDE)打开Proteus的源代码文件,并浏览、编辑和调试代码。3、请注意,要深入理解和修改Proteus的源代码,需要具...在Proteus中查看源代...
家用电器上的 单片机 芯片,如果仅仅 程序 出错了,怎么写入?我认为芯片程序出错的可能性不大,机率太小了。不知道你是怎么确定是程序出错了。检查单片机是否正常工作,按如下方法进行。1.确定单片机供电正常,不同的单片...
如何从 单片机 转嵌入式linux编程?Linux嵌入式编程分内核编程和和应用层编程两种。内核编程是写外设驱动或做网络报文处理。应用层编程就是写各种应用程序。这两种编程截然不同,即使是一个做了...
单片机 串行 程序 不工作?单片机不工作,稍微有点基础的人都会去查电源、晶振等硬件及源代码,关键是具体到一个特定项目(系统)怎么去查,就此做一些个人经验介绍。1.单片机本身就是集...
Stm8 单片机 怎样让 程序 运行在内存里?这个的话一般程序的运行都是在内存里的这个的话一般程序的运行都是在内存里的
求 单片机 指令DJNZ的用法?[回答]DJNZ指令有两种格式:DJNZRn,rel和DJNZdirect,rel,前者为双字节指令,其中第二个人字节存储rel的相对地址,范围是-128~+127,所以转移范围很窄,所以...
初学 单片机 ,keil这个软件界面能设置成中文吗?加了源代码、功能导航器、模板编辑以及改进的搜索功能外,uVision3还提供了一个配置向导功能,加速了启动代码和配置文件的生成。此外其内置的仿真器可模拟...
习 单片机 开发,正在考虑入手一个单片机开发板。C51单片机开...[回答]本资源是在RT1052开发板上调通验证通过的TOUCHGFX工程源代码,希望能帮到有需要的朋友。恩智浦单片机手册,技术开发、学习均可以使用!nxp厂家自己的b...