51单片机

大家可能都知道破解51单片机是很容易的,但为什么容易,又是如何来破解的,可能很多人就不大清楚了,致芯解密专家结合网上一些前辈整理的资料和自己的经验,对MCU破解技术做个简单分析。

大家不要把解密想的很复杂,他不像研发一款产品那样,先确定客户需求或者新产品主要功能,然后立项确定技术指标,分配软硬件开发任务,基于硬件调试程序,然后验证功能,测试bug,还要做环境试验。行业里解密的方法有很多,每个人破解的思路也不一样。但是大致分为几种。

一、软件破解

利用软件破解目标单片机的方法,利用这种方法,不会对目标MCU元器件造成物理损伤。主要是对WINBONGD,SYNCMOS单片机和GAL门阵列,这种利用软件解密设备,按照一定的步骤操作,执行片内的程序送到片外的指令,然后用解密的设备进行截获,这样芯片内部的程序就被解密完成了(GAL采用逻辑猜测),就可以得到加密单片机中的程序。

二、硬件破解

流程如下:

1、测试

使用高档编程器等设备测试芯片是否正常,并把配置字保存。

2、开盖

采用手工或专用开盖设备进行开盖处理,这里说的开盖并不是说单片机或者其他MCU真有一个盖。简单解释一下,MCU其实是一个大规模集成电路,它是由N个电路组合而成的,而晶圆就是搭载集成电路的载体。将晶圆进行封装后,就形成了我们日常所用的IC芯片,封装形式可以有多种,比如TSSOP28、QFN28等。


3、做电路修改

对不同芯片,提供对应的图纸,让厂家做电路修改,目的是让MCU的存储区变得可读。有些MCU默认不允许读出Flash或者E2PROM中的数据,因为有硬件电路做保护,而一旦切断加密连线,程序就暴露可读了。如图2所示


4、读程序

取回修改过的MCU,直接用编程器读出程序,可以是HEX文件,或者BIN文件。

5、烧写样片给客户

按照读出的程序和配置,烧写到目标MCU中,这样就完成了MCU的破解。 至此,硬件破解法成功完成。

三、软硬兼施

采用软件和硬件结合的方法,需要对芯片的内部结构非常的熟悉。

另外还有其他一些破解技术,例如电子探测攻击、过错产生技术等等,但是最终目的只有一个,就是能够模仿出目标MCU的功能就可以了。

看到这里大家应该明白一个道理,破解MCU并不能做到把MCU中的程序原封不动的还原出来。目前的技术也做不到,至少国内应该做不到。针对以上情况,加密芯片应运而生,初期确实能很好的保护MCU的安全,但很快就被找到了漏洞。

来源:芯片解密

围观 564

直流电动机的PWM调压调速原理

直流电动机转速N的表达式为:N=U-IR/Kφ

由上式可得,直流电动机的转速控制方法可分为两类:调节励磁磁通的励磁控制方法和调节电枢电压的电枢控制方法。其中励磁控制方法在低速时受磁极饱和的限制,在高速时受换向火花和换向器结构强度的限制,并且励磁线圈电感较大,动态响应较差,所以这种控制方法用得很少。现在,大多数应用场合都使用电枢控制方法。

对电动机的驱动离不开半导体功率器件。在对直流电动机电枢电压的控制和驱动中,对半导体器件的使用上又可分为两种方式:线性放大驱动方式和开关驱动方式。

线性放大驱动方式是使半导体功率器件工作在线性区。这种方式的优点是:控制原理简单,输出波动小,线性好,对邻近电路干扰小;但是功率器件在线性区工作时由于产生热量会消耗大部分电功率,效率和散热问题严重,因此这种方式只用于微小功率直流电动机的驱动。绝大多数直流电动机采用开关驱动方式。开关驱动方式是使半导体器件工作在开关状态,通过脉宽调制PWM来控制电动机电枢电压,实现调速。

在PWM调速时,占空比α是一个重要参数。以下3种方法都可以改变占空比的值。

(1)定宽调频法

这种方法是保持t1不变,只改变t2,这样使周期T(或频率)也随之改变。

(2)调频调宽法

这种方法是保持t2不变,只改变t1,这样使周期T(或频率)也随之改变。

(3)定频调宽法

这种方法是使周期T(或频率)保持不变,而同时改变t1和t2。

前两种方法由于在调速时改变了控制脉冲的周期(或频率),当控制脉冲的频率与系统的固有频率接近时,将会引起振荡,因此这两种方法用得很少。目前,在直流电动机的控制中,主要使用定频调宽法。

直流电动机双极性驱动可逆PWM控制系统

双极性驱动则是指在一个PWM周期里,作为在电枢两端的脉冲电压是正负交替的。

双极性驱动电路有两种,一种称为T型,它由两个开关管组成,采用正负电源,相当于两个不可逆控制系统的组合。但由于T型双极性驱动中的开关管要承受较高的反向电压,因此只用在低压小功率直流电动机驱动。

另一种称为H型。

H型双极性驱动

1、电动机控制电路模块

H桥电动机驱动电路的工作原理:

A:当单片机的P0.0脚输出高电平,而P0.1脚输出低电平时,通过光电耦合器后仍然输出为高电平,使Q4管导通,此时Q1也处于导通状态,但Q2管的基极的电位被强行拉低,Q2管处于截止状态。由于单片机的P0.1脚输出低电平,

Q8处于截止状态,而此时Q7因为Q5的截止而处于导通状态,从而使电动机形成回路,电机正常工作。

B:同理可得,当P0.0脚输出低电平,而P0.1脚输出高电平时,三极管的状态与上述相反,电机同样处于正常工作状态。

C:当P0.0脚和P0.1同时为高电平或低电平,由于Q4与Q8和Q3与Q7的工作状态相同,同时处于导通或截止,使电机两断电位相同,无法使电机形成闭和回路,电机不工作,着就是所谓本设计所提及的刹车状态。

由于电路中在驱动功率管的发射极各添加了一个小电感,目的是为了使电机驱动电压更加稳定,得到较为平滑的驱动电压,从而增加了刹车时动作的准确性,减少电机的在起动和停止的瞬间产生过大的电压对功率管的冲击,导致功率管的损坏。同时也提高了电机的刹车控制可靠性和准确性,不至于因惯性而导致控制上产生较大的误差。

该桥的优点是电路的原理简单、易控制、功耗低带负载能力强、刹车的精度很高而且价格低廉。在驱动电路的控制信号输入断采用了光电隔离技术,减小H桥电机驱动电路对单片机的干扰,实现模拟电路与数字电路的隔离。在单片机的配合下,通过PWM调节脉宽的方法,实现了对驱动电机的轻松调速,通过键盘的配置可以对体的参数进行修改,可以使电机适应各种不同的工作状态,而实现智能控制的目的。正因为采取了PWM该技术,使我们完成基本要求的过程变得简单易行。

在电路中所采取的功率管为中功率管,其中将驱动功率管设计为灵活替换方式,可以根据实际驱动电路的需要,从而调整功率管的型号而不用另行更改电路,就可以满足电路控制的要求

2、软件模块部分

在速度控制方面,一般是能通过改变加在电机两端的电压来实现的,可以是连续改变(加直流电压),也可以是断续改变(加脉冲电压)。为了简单用,我们采用了脉宽调速,脉宽的变化可以通过硬件或软件来实现。

方案一 硬件实现是通过改变振荡电路中RC参数来调整充放电时间。若用硬件电路来实现,在稳定性方面得不到保证。

方案三 用软件的作法是通过设置高电平及低电平的保持时间来达到PWM的脉宽调制目的。

就比较而言,软件调整量化指标更高、调整更可靠、更方便、更准确。因此在设计时,常考虑方案二。

脉冲频率对电机转机也有影响,脉冲频率高连续性好,但带负载能力差,频率低则反之。经实验发现,脉冲频率在15━20HZ效果最佳。在本设计中采用了20HZ进行设计。

脉冲调速实质上是调节加在电机两端的平均功率,通过计算可发现电机的速度与脉宽成正比。

软件编程的考虑是设置脉宽这个变量。在P0.0,P0.1的输出控制信号来产生20HZ可调脉宽方波。

下面是51单片机的实验程序

#include < reg51.h >
#include < intrins.h >
sbit  K1 =P1^4 ;                    //增加键
sbit  K2 =P1^5 ;                    //减少键
sbit  P00 =P0^1;
sbit  BEEP =P3^7 ;                //蜂鸣器
unsigned char PWM=0xe7;   //赋初值
void Beep();
void delayms(unsigned char ms);
void delay(unsigned char t);
/*********************************************************/
void main()
{
     P1=0xff;
    TMOD=0x21 ;
    TH0=0xff ;             //50us延时常数
    TL0=0xce ;            //频率调节
    TH1=PWM ;            //脉宽调节
    TL1=0 ;
     EA=1;
     ET0=1;
     ET1=1;
     TR0=1 ;
   while(1)
   {
     do{
            if(PWM!=0xff)
           {PWM++ ;delayms(10);}
           else Beep() ;
         }
     while(K1==0);
     do{
           if(PWM!=0xce)
          {PWM-- ;delayms(10);}
           else Beep() ;
          }
     while(K2==0);
   }
}
void timer0() interrupt 1
{
    TR1=0 ;
    TH0=0xff ;
    TL0=0xce ;
    TH1=PWM ;
    TR1=1 ;
    P00=0 ;      //启动输出
}
void timer1() interrupt 3
{
    TR1=0 ;
    P00=1 ;     //结束输出
}
/*********************************************************/
//蜂鸣器子程序
/*********************************************************/
void Beep()   
  {
      unsigned char i  ;
      for (i=0  ;i<100  ;i++)
        {
          delay(100)  ;
          BEEP=!BEEP  ;                //Beep取反
        }
     BEEP=1  ;                            //关闭蜂鸣器
     delayms(100);
  } 
/*********************************************************/
// 延时子程序
/*********************************************************/
void delay(unsigned char t)
 {
    while(t--)   ;
 }
/*********************************************************/
// 延时子程序
/*********************************************************/
void delayms(unsigned char ms)
{
    unsigned char i ;
    while(ms--)
     {
        for(i = 0 ; i < 120 ; i++) ;
     }
}
/*********************************************************/

转自:博客园 - Aaronfay

围观 465

本文提出了基于51单片机两路温度控制器的设计方案,该设计方案采用两个DS18B20温度传感器,采集两个不同地方的温度,通过AT89C51处理进行,由四位LED数码管显示所测量温度,前两位为第一个温度传感器的温度,后两位为第二个温度传感器的温度。采用3个按键实现温度最高和最低的设定,采用蜂鸣器和电动机实现温度过高或过低报警。

1. 引言

目前,温度控制器存在的问题是如何缩减成本,减少功耗,温度测量的准确性和多路温度的同时显示。本方案设计的实现基于C51单片机的两路温度控制器,做到成本最低化,精确度高,两路温度的显示和控制,能在温度超出设定的最高温度时启动电风扇进行降温,在温度低于设定的最低温度时启动蜂鸣器报警,能够用户设定最高最低温。

2. 系统结构

温度控制器系统包括以下几个主要部分:温度传感器,报警电路,LED显示电路,键盘控制,89C51控制部分。如图所示:

基于51单片机对两路DS18B20温度传感器的设计

本系统设计实现:启动温度控制器后,绿灯亮起,四位LED数码显示器上前两位为温度传感器1所测的环境温度,后两位为温度传感器2所测的环境温度。

3. 硬件结构

3.1 温度传感器

本设计采用的是DS18B20作为温度传感器,DS18B20与传统的热敏电阻相比具有精确度高,测量误差小,方便实现多点测温等优点,因此用DS18B20作温度传感器。

3.2 报警电路

本设计采用蜂鸣器和电风扇报警电路。蜂鸣器报警电路由三极管和蜂鸣器组成。当温度低于设定的最低温度时,则蜂鸣器报警。电风扇报警电路由三极管和电风扇组成。当温度高于设定的最高温度时,则电风扇报警。

3.3 显示电路

本系统采用L E D数码显示管显示,LED亮度高,可视角度高。LCD的可视角度低,亮度较低,价格高。考虑到此温度传感器主要用于温室大棚等亮度不太高的环境,从经济与实用的角度来看选LED作为显示器。

3.4 键盘控制

本系统采用3个独立的按键作为键盘控制电路。键盘一般分为独立式和矩阵键盘两种。独立式键盘结构简单,但占用的资源较多;矩阵键盘结构比较复杂,但占用的口线少。考虑到本设计所需按键数不多,采用三个独立键盘完成两个温度传感器温度的设定。

3.5 89C51控制部分

本系统采用的是AT89C51,小电子产品用51,硬件设计电路如图1所示。

基于51单片机对两路DS18B20温度传感器的设计

4. 软件设计

本系统使用汇编语言编码实现的,比C语言编码的程序处理时间更快。

主程序中包含系统初始化,键盘扫描选择子程序,温度比较子程序,温度测量子程序,温度计算子程序,显示子程序。

4.1 主程序模块

主程序中先对数据进行初始化,然后调用键盘扫描子程序KEY_TEST,温度比较子程序C O M P A R E,温度采集子程序G E T _ T E M P,温度显示子程序D I S _ S E T和DISPLAY,再判断采集,显示第二个温度传感器的温度值。编写程序如下:

基于51单片机对两路DS18B20温度传感器的设计

4.2 LED显示模块

LED显示可以分为动态显示和静态显示两种,静态显示占用更多口线,为了减少硬件成本,本设计采用动态扫描显示的方法显示两个温度传感器的温度值。

DISPLAY和DISPLAY1函数分别读取第一个和第二个温度传感器的温度并根据暂存单元的数据显示两个温度传感器的温度。编程思路:根据SIGN标志来判断转入不同的显示,将查表所得的数据存入不同的单元并显示在LED上。

4.3 键盘控制模块

键盘通过设定SIGN标志来判断设定第一个或者第二个温度传感器的最高温或者最低温,编程思路为:将SIGN初始设定为0,当第一个按键按下时将其赋为1,再次按下时加一,直到按到第5次重新赋值为0,根据SIGN的值确定进行不同的设置。

4.4 温度传感器模块

根据温度传感器DS18B20完成温度转换所必须经过的3个步骤,程序:MOV A,#0CCH//跳过ROM MOV A,#44H / / 进行温度变换 MOV A,#0BEH//读暂存存储器内容。

4.5 报警模块

当实时温度高于设定的最高温度时或者实时温度低于设定的最低温度时,单片机会控制蜂鸣器或者电风扇工作,判断当前温度是否在正常范围的函数为COMPARE,高温部分程序如下:

基于51单片机对两路DS18B20温度传感器的设计

5. 仿真测试

我们对DS18B20写入程序之前,必须调试自己的程序。但我们不能看到程序是怎样运行的。因此我们可以用仿真机来仿真,通过仿真机我们可以看到DS18B20发送过来的数据,读出来的温度值,所利用寄存的值的变化。系统连接示意图如下图所示:

基于51单片机对两路DS18B20温度传感器的设计

我们现在能把DS18B20所采集到的温度在PC机上显示出来并且每一个温度值显示后换一行。试验证明了系统实用性强,达到了预定的功能。

6. 总结

本方案中所设计的温度控制器,采用AT89C51单片机作为内核,采用DS18B20作为温度传感器,通过四位LED显示,通过循环扫描实现了两路温度的采集与显示。然后又经过过仿真测试证实了改设计方案经济适用,实用性强,能够测量两个地方的温度,满足温室大棚,室内家居,工业控制等不同环境下的使用。

来源:电子发烧友

围观 577

本51项目基于STC89C52MCU,温度传感器为DS18B20,显示模块用的是LCD1602,无线模块用的是Nodemcu。

项目用到的编程语言:C,C++,Lua。

实现思路是这样,DS18B20测温,然后数据串行传送给51单片机,然后51通过串口将数据传送给Nodemcu,Nodemcu通过其WIFI模块将数据发送给上位机,上位机上的程序是用Qt编写的GUI。(这里无线传输采用的是无连接的UDP协议)

1. DS18B20温度测量模块

DS18B20是单总线器件,所以时序要求非常严格,程序编写时注意时序,否则读不出温度。DS18B20有寄生供电和单独供电,这里采用单独供电方式。

它的驱动程序如下:

#include"Temp.h"
void delay1ms(unsigned int y)
{
unsigned int x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}

void Init18b20(){
int i;
DQ=0;
i=60;
while(i--);   //10us左右,一共延时60*10=600us左右。
DQ=1;

while(DQ)
{
i=1;
while(i--); 
}
}

void Writeonebyte(unsigned char ins){
int i=0;
int j=0;
for(i=0;i<8;i++){
DQ=0;
j++;                   //延时1us
DQ=0x01&ins;   //写0或者1,写完以后保持,15us内DS18B20会来采集数据。
j++;
j=6;
while(j--);           //一个写周期至少60us。
DQ=1;                //释放数据线
  ins=ins>>1;      //写下一位数据
}
}

unsigned char Readonebyte(){
int i=0;
int j=0;
unsigned char bi, byte;
for(i=0;i<8;i++){
DQ=0; 
j++;
DQ=1;              //拉低1us后释放,给一个准备读的信号
j++;
j++;
bi=DQ;             
byte=(byte>>1)|(bi<<7);    //读取8位数据
j=5;
while(j--);
}
return byte;
}

void Tempconvert(){
Init18b20();
delay1ms(1); 
Writeonebyte(0xcc);
Writeonebyte(0x44);
}

void Readrom(){
Init18b20();
delay1ms(1);
Writeonebyte(0xcc);
Writeonebyte(0xbe);
}

int Readtemp(){
int temp;
unsigned char tml,tmh;
Tempconvert();
Readrom();
tml=Readonebyte();
tmh=Readonebyte();
temp=tmh;
temp=temp<<8;
temp=temp|tml;
return temp;
}

头文件

#include"reg52.h"
sbit DQ=P1^0;
void Init18b20();
void Writeonebyte(char ins);
unsigned char Readonebyte();
void Tempconvert();
void Readrom();
int Readtemp();
void delay1ms();

2.LCD1602显示模块

这个模块比较简单,注意时序,然后初始化函数里面不要写清屏指令,以免显示的数据一直闪烁。

#include"lcd1602.h"
char init[]="Temp ";
void delay1ms()   //误差 -0.651041666667us
{
    unsigned char a,b;
    for(b=102;b>0;b--)
        for(a=3;a>0;a--);
}

void delay5ms()   //误差 -0.000000000001us
{
    unsigned char a,b;
    for(b=15;b>0;b--)
        for(a=152;a>0;a--);
}
void writecom(int com){
E=0;
RS=0;
RW=0;
P0=com;     //并行送数据
delay1ms(); //送完后保持一下 使其稳定
E=1;           //拉高,让数据发送
delay5ms();
E=0;
}
void writedat(char dat){
E=0;
RS=1;
RW=0;
P0=dat;
delay1ms();
E=1;
delay5ms();
E=0;
}
void lcdinit(){
int i=0;
delay1ms();
writecom(0x06);
writecom(0x0c);
delay1ms();
writecom(0x38);
writecom(0xc3);
for(i=0;i<5;i++)
writedat(init[i]);
}

头文件

#include<reg52.h>
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay1ms();
void delay5ms();
void lcdinit();
void writecom(int com);
void writedat(char dat);

3.Nodemcu模块

手里只有这个MCU,没有ESP8266。其实差不多,Nodemcu就是一个ESP8266的小系统板,然后将一些资源进行二次封装,然后用Lua语言开发。如果是单独的ESP8266模块,就用串口 AT指令调试。 相同的是一开始都要烧固件进去,固件可以自己选,模块都在github上。这个Nodemcu三种工作方式 STA ,AP,STA+AP。 STA模式就是可以像手机一样接到你的路由器上,然后你可以用你的手机电脑通过网络来操纵。AP就是这个模块成为一个热点,你手机电脑可以连接上这个热点,这种方式适用于局域网。STA+AP是都可以的。

本次项目使用STA模式。

代码如下

print('Setting up WIFI...')
wifi.setmode(wifi.STATION)
wifi.sta.config('AP', 'SSID')
wifi.sta.connect()

tmr.alarm(1, 1000, tmr.ALARM_AUTO, function()
    if wifi.sta.getip() == nil then
        print('Waiting for IP ...')
    else
        print('IP is ' .. wifi.sta.getip())
    tmr.stop(1)
    end
end)

cu=net.createConnection(net.UDP, 0)
cu:connect(9999,"ip")
cu:on("receive",function(cuk,c) print(c) end )

uart.setup(0,9600,8,0,1,0)
uart.on("data",
function(data)
print("receive from uart:", data) 
cu:send(data)
end, 0)

这里要注意的是你烧进去的第一个程序,名字必须是init.lua 它上电是从这个文件开始执行的。

最后一点

各个模块的组合,即C语言主函数如下

#include"Temp.h"
#include"lcd1602.h"
#include<reg52.h>
void dlay1ms(int y)
{
int x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}
void Serialinit(){
TMOD=0x20;
TH1=0XFD;
TL1=0XFD;
TR1=1;
SCON=0x50;    //配置好串口,这里波特率为9600. 只用定时器和串口,不用中断。
}

char list[11]={'0','1','2','3','4','5','6','7','8','9','.'};
char value[4];
int i=0;

void Datapros(int temp){
float fp;
fp=temp;
temp=fp*0.0625*100;     //注意 0.0625*temp是温度,但是乘出来的可能是带小数部分的,你要先把temp变成带小数的,再乘0.0625,否则乘出来的小数部分就被类型转换截掉了。
value[0]=list[temp/1000];
value[1]=list[temp%1000/100];
value[2]=list[10];
value[3]=list[temp%100/10];      //显示一位小数,这种显示算法对数码管也通用。
}

void Lcddisplay(){
lcdinit();
writedat(value[0]);
dlay1ms(20);
writedat(value[1]);
dlay1ms(20);
writedat(value[2]);
dlay1ms(20);
writedat(value[3]);
dlay1ms(20);
}

int main(){
Serialinit();
while(1){
Datapros(Readtemp());
Lcddisplay();
for(i=0;i<4;i++)
{
SBUF=value[i];    //启动串口传输,51单片机的TXD RXD接到Nodemcu上的TX RX上。
while(!TI);
TI=0;
}
}
}

上位机界面用Qt编写。

代码就不贴出来了。效果图已经上传。

整个项目做完以后的总结与不足。

总结:DS18B20时序一定要特别注意。Lcd如果添加了清屏指令,显示温度会闪烁。Nodemcu第一个程序名一定是init.lua。烧写工作有好几种,自己选择。Lua语言需要了解一下,Nodemcu的API需要了解一下。然后就是Qt写的GUI,里面用到的UDP协议,端口和IP要对应好,不然接收不到数据。

不足: DS18B20可以用寄生方式供电,这个我不是很了解,下次可以尝试寄生供电方式工作,这个适合远距离测温。无线数据传说部分可以用更可靠的TCP协议来传输,这个必须要会,下次会尝试使用这个协议来编写上位机程序。采集到的温度没有进行处理,只用来显示了,希望在学习数据库以后加入数据的存储筛选功能。

来源:博客园 - Crenx ,转载此文目的在于传递更多信息,版权归原作者所有。

围观 388

本51项目基于STC89C52MCU,温度传感器为DS18B20,显示模块用的是LCD1602,无线模块用的是Nodemcu。

项目用到的编程语言:C,C++,Lua。

实现思路是这样,DS18B20测温,然后数据串行传送给51单片机,然后51通过串口将数据传送给Nodemcu,Nodemcu通过其WIFI模块将数据发送给上位机,上位机上的程序是用Qt编写的GUI。(这里无线传输采用的是无连接的UDP协议)

1. DS18B20温度测量模块

DS18B20是单总线器件,所以时序要求非常严格,程序编写时注意时序,否则读不出温度。DS18B20有寄生供电和单独供电,这里采用单独供电方式。它的驱动程序如下:

#include"Temp.h"
void delay1ms(unsigned int y)
{
unsigned int x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}

void Init18b20(){
int i;
DQ=0;
i=60;
while(i--); //10us左右,一共延时60*10=600us左右。
DQ=1;

while(DQ)
{
i=1;
while(i--);
}
}

void Writeonebyte(unsigned char ins){
int i=0;
int j=0;
for(i=0;i<8;i++){
DQ=0;
j++; //延时1us
DQ=0x01&ins; //写0或者1,写完以后保持,15us内DS18B20会来采集数据。
j++;
j=6;
while(j--); //一个写周期至少60us。
DQ=1; //释放数据线
ins=ins>>1; //写下一位数据
}
}

unsigned char Readonebyte(){
int i=0;
int j=0;
unsigned char bi, byte;
for(i=0;i<8;i++){
DQ=0;
j++;
DQ=1; //拉低1us后释放,给一个准备读的信号
j++;
j++;
bi=DQ;
byte=(byte>>1)|(bi<<7); //读取8位数据
j=5;
while(j--);
}
return byte;
}

void Tempconvert(){
Init18b20();
delay1ms(1);
Writeonebyte(0xcc);
Writeonebyte(0x44);
}

void Readrom(){
Init18b20();
delay1ms(1);
Writeonebyte(0xcc);
Writeonebyte(0xbe);
}

int Readtemp(){
int temp;
unsigned char tml,tmh;
Tempconvert();
Readrom();
tml=Readonebyte();
tmh=Readonebyte();
temp=tmh;
temp=temp<<8;
temp=temp|tml;
return temp;
}

头文件
#include"reg52.h"
sbit DQ=P1^0;
void Init18b20();
void Writeonebyte(char ins);
unsigned char Readonebyte();
void Tempconvert();
void Readrom();
int Readtemp();
void delay1ms();

2. LCD1602显示模块

这个模块比较简单,注意时序,然后初始化函数里面不要写清屏指令,以免显示的数据一直闪烁。

#include"lcd1602.h"
char init[]="Temp ";
void delay1ms() //误差 -0.651041666667us
{
unsigned char a,b;
for(b=102;b>0;b--)
for(a=3;a>0;a--);
}

void delay5ms() //误差 -0.000000000001us
{
unsigned char a,b;
for(b=15;b>0;b--)
for(a=152;a>0;a--);
}
void writecom(int com){
E=0;
RS=0;
RW=0;
P0=com; //并行送数据
delay1ms(); //送完后保持一下 使其稳定
E=1; //拉高,让数据发送
delay5ms();
E=0;
}
void writedat(char dat){
E=0;
RS=1;
RW=0;
P0=dat;
delay1ms();
E=1;
delay5ms();
E=0;
}
void lcdinit(){
int i=0;
delay1ms();
writecom(0x06);
writecom(0x0c);
delay1ms();
writecom(0x38);
writecom(0xc3);
for(i=0;i<5;i++)
writedat(init[i]);
}

头文件

#include<reg52.h>
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay1ms();
void delay5ms();
void lcdinit();
void writecom(int com);
void writedat(char dat);

3. Nodemcu模块

手里只有这个MCU,没有ESP8266。其实差不多,Nodemcu就是一个ESP8266的小系统板,然后将一些资源进行二次封装,然后用Lua语言开发。如果是单独的ESP8266模块,就用串口 AT指令调试。 相同的是一开始都要烧固件进去,固件可以自己选,模块都在github上。这个Nodemcu三种工作方式 STA ,AP,STA+AP。 STA模式就是可以像手机一样接到你的路由器上,然后你可以用你的手机电脑通过网络来操纵。AP就是这个模块成为一个热点,你手机电脑可以连接上这个热点,这种方式适用于局域网。STA+AP是都可以的。本次项目使用STA模式。

代码如下

print('Setting up WIFI...')
wifi.setmode(wifi.STATION)
wifi.sta.config('AP', 'SSID')
wifi.sta.connect()

tmr.alarm(1, 1000, tmr.ALARM_AUTO, function()
if wifi.sta.getip() == nil then
print('Waiting for IP ...')
else
print('IP is ' .. wifi.sta.getip())
tmr.stop(1)
end
end)

cu=net.createConnection(net.UDP, 0)
cu:connect(9999,"ip")
cu:on("receive",function(cuk,c) print(c) end )

uart.setup(0,9600,8,0,1,0)
uart.on("data",
function(data)
print("receive from uart:", data)
cu:send(data)
end, 0)

这里要注意的是你烧进去的第一个程序,名字必须是init.lua 它上电是从这个文件开始执行的。

最后一点

各个模块的组合,即C语言主函数如下

#include"Temp.h"
#include"lcd1602.h"
#include<reg52.h>
void dlay1ms(int y)
{
int x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}
void Serialinit(){
TMOD=0x20;
TH1=0XFD;
TL1=0XFD;
TR1=1;
SCON=0x50; //配置好串口,这里波特率为9600. 只用定时器和串口,不用中断。
}

char list[11]={'0','1','2','3','4','5','6','7','8','9','.'};
char value[4];
int i=0;

void Datapros(int temp){
float fp;
fp=temp;
temp=fp*0.0625*100; //注意 0.0625*temp是温度,但是乘出来的可能是带小数部分的,你要先把temp变成带小数的,再乘0.0625,否则乘出来的小数部分就被类型转换截掉了。
value[0]=list[temp/1000];
value[1]=list[temp%1000/100];
value[2]=list[10];
value[3]=list[temp%100/10]; //显示一位小数,这种显示算法对数码管也通用。
}

void Lcddisplay(){
lcdinit();
writedat(value[0]);
dlay1ms(20);
writedat(value[1]);
dlay1ms(20);
writedat(value[2]);
dlay1ms(20);
writedat(value[3]);
dlay1ms(20);
}

int main(){
Serialinit();
while(1){
Datapros(Readtemp());
Lcddisplay();
for(i=0;i<4;i++)
{
SBUF=value[i]; //启动串口传输,51单片机的TXD RXD接到Nodemcu上的TX RX上。
while(!TI);
TI=0;
}
}
}

上位机界面用Qt编写。

代码就不贴出来了。

整个项目做完以后的总结与不足。

总结:DS18B20时序一定要特别注意。Lcd如果添加了清屏指令,显示温度会闪烁。Nodemcu第一个程序名一定是init.lua。烧写工作有好几种,自己选择。Lua语言需要了解一下,Nodemcu的API需要了解一下。然后就是Qt写的GUI,里面用到的UDP协议,端口和IP要对应好,不然接收不到数据。

不足: DS18B20可以用寄生方式供电,这个我不是很了解,下次可以尝试寄生供电方式工作,这个适合远距离测温。无线数据传说部分可以用更可靠的TCP协议来传输,这个必须要会,下次会尝试使用这个协议来编写上位机程序。采集到的温度没有进行处理,只用来显示了,希望在学习数据库以后加入数据的存储筛选功能。

来源:博客园 - Crenx

围观 274

一、总线概述

计算机系统是以微处理器为核心的,各器件要与微处理器相连,且必须协调工作,所以在微处理机中引入了总线的概念,各器件共同享用总线,任何时候只能有一个器件发送数据(可以有多个器件同时接收数据) 。

计算机的总线分为控制总线、地址总线和数据总线等三种。而数据总线用于传送数据,控制总线用于传送控制信号, 地址总线则用于选择存储单元或外设。

二、单片机的三总线结构

51系列单片机具有完善的总线接口时序,可以扩展控制对象,其直接寻址能力达到64k( 2的16次方) 。在总线模式下,不同的对象共享总线,独立编址、分时复用总线,CPU 通过地址选择访问的对象,完成与各对象之间的信息传递。

单片机三总线扩展示意如图1 所示。

51单片机总线时序介绍

1、数据总线

51 单片机的数据总线为P0 口,P0 口为双向数据通道,CPU 从P0 口送出和读回数据。

2、地址总线

51 系列单片机的地址总线为16 位。

为了节约芯片引脚,采用P0 口复用方式,除了作为数据总线外,在ALE 信号时序匹配下,通过外置的数据锁存器,在总线访问前半周期从P0口送出低8位地址,后半周期从P0 口送出8 位数据。

高8位地址则通过P2 口送出。

3、控制总线

51 系列单片机的控制总线包括读控制信号P3.7 和写控制信号P3.6 等,二者分别作为总线模式下数据读和数据写的使能信号。

三、单片机总线时序分析

51 单片机总线时序如图2 所示。

51单片机总线时序介绍

从图2 中可以看出,完成一次总线( 读写) 操作周期为T,P0 口分时复用,在T0 期间,P0 口送出低8 位地址,在ALE 的下降沿完成数据锁存,送出低8位地址信号。在T1 期间,P0 口作为数据总线使用,送出或读入数据,数据的读写操作在读、写控制信号的低电平期间完成。

需要注意的是,在控制信号( 读、写信号) 有效期间,P2 口送出高8位地址,配合数据锁存器输出的低8 位地址,实现16 位地址总线,即64kB 范围的内的寻址。

由于CPU不可能同时执行读和写操作,所以读、写信号不可能同时有效。

四、常见单片机编址电路

1、简单地址扩展

51 单片机的P2 口可以直接作为高8位地址总线使用,在一些简单系统电路中,常使用P2口直接编址驱动。

下面以使用数据缓冲器74LS273 驱动数码显示为例,分析P2 口编址驱动的静态数码显示电路的设计。

一位LED 数码显示单元电路如图3 所示。

51单片机总线时序介绍

WR 与A8( P2.0) 相或提供74LS273的时钟信号,当执行“MOVX @DPTR,A”指令时,地址信息由DPTR 寄存器确定,会出现有效的写信号WR,只有当地址A8 为满足“0”时,写信号才可以作为74LS273 的时钟信号输入,完成数据锁存。

P2 口为A8~A15 的8 位地址线,很容易扩展到8 只LED 数码管,WR 信号分别与A8~A15 按或关系连接,每位地址线均为低电平有效,即可实现8 个有效地址。

该方案电路简单,但有效地址数太少,不适用于复杂系统设计。

2、低8 位地址锁存

通常的设计电路是使用8D 锁存器74LS373 实现地址锁存,74HC573 与之逻辑功能相同,只是引脚布局不一样,使用74HC573 布线更容易。

74LS373 真值表如图4所示。

51单片机总线时序介绍

在输出允许OE 为L、控制使能LE 为H 时,输出为跟随状态;

OE 为L、LE 为L 时,输出为保持状态。

地址锁存电路如图5 所示。OE 接地,LE 接单片机的ALE脚将产生满足时序的低8 位地址信号。

执行以下三条指令会得到如图6所示的时序图。

MOV DPTR,# 0FF55H; 低8 位地址为55H

MOV A,# 0AAH; 待发送数据0AAH→A( 55H 取反)

MOVX,@DPTR,A; A 中的0AAH送地址为0FF55H 的对象中会。

51单片机总线时序介绍

从图6 中可以看出,P0 口先送55H,在ALE 下降沿实现地址锁存,随后送出数据0AAH,在WR 有效( 低电平) 期间锁存器输出低8 位地址55H,P0 口送出数据0AAH。

3、带译码器的复杂地址接口电路

理论上高8 位地址线可以产生256 个有效地址,如何实现地址“扩展”呢? 地址扩展准确描述是地址译码,例如3 根地址线可以译码成8 个地址,4根译码成16 个有效地址。这里选择3-8 译码器实现地址译码,电路图以及对应的编址如表1 所示。

51单片机总线时序介绍

五、单片机总线编址电路实例

带总线扩展接口的单片机系统,包括外部32k RAM 扩展、LCD1602 接口、输入输出口。

带编址扩展的单片机最小系统电路如图7 所示。

51单片机总线时序介绍

使用74HC573 锁存低8 位地址;74138 实现8 个地址扩展,74138 的A、B、C 接A8 ~A10,E1 接A15, E2、E3 接地常有效,得到0F8FFH 到0FFFFH8 个地址( 无关位用1 表示) 或者8000H 到8700H( 无关位用0 表示) 。

32k RAM 接口如图8 所示。

51单片机总线时序介绍

D0~D7 接数据总线P0 口,地址线A0~A14接单片机地址总线低15 位,单片机地址线A15 接RAM 片选信号,低电平有效,这样RAM 地址分配从0000H 到7FFFH,与74138 译码地址不冲突。

LCD1602 接口电路如图9 所示。

51单片机总线时序介绍

RS、RW 分别接A12、A13,使能信号编址为Y7,这样LCD 的四个驱动地址( 数据读写和命令读写) 为0CFFFH 到0FFFFH ( 无关位为1) 或者8700H 到0B700H( 无关位为0)。

有些时候单片机引脚不够用,还要进行扩展,输入口扩展电路如图10 所示。

51单片机总线时序介绍

利用74HC573( 74LS373) 的高阻态功能,将其输出Q0~Q7 接P0 口,在满足总线地址读操作中,可以把输入InPORT的数据读入单片机的累加器,地址为0F8FFH 或8000H。

输出口扩展电路如图11 所示。

51单片机总线时序介绍

利用74LS273 数据锁存功能,在满足总线地址写操作中,可以把单片机累加器里的数据写入273 锁存输出,地址为0F8FFH 或8000H。由于所用控制总线不同,可以和输入共用地址。

六、结束语

总线扩展是设计单片机控制电路必须掌握的技术,大量的特殊功能IC都支持总线接口, 如ADC0809,TLC7528,DDS 器件AD9851 等。

总线接口的要点就是在严格的控制时序下,总线被分时复用,以实现复杂系统设计。

转自 51单片机总线时序 - 电子技术基础知识 - 21IC中国电子网 http://www.21ic.com/jichuzhishi/mcu/shixu/2013-02-27/159327.html

围观 338

data:固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。

bit:是指0x20-0x2f的可位寻址区

idata:固定指前面0x00-0xff的256个RAM,其中前128和dATa的128完全相同,只是因为访问的方式不同。

idata是用类似C中的指针方式访问的。

汇编中的语句为:mox ACC,@Rx.(不重要的补充:c中idATa做指针式的访问效果很好)

xdATa:外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。

pdATa:外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movx ACC,@Rx读写。这个比较特殊,而且C51好象有对此BUG,建议少用。但也有他的优点,具体用法属于中级问题,这里不提。

startup.a51的作用和汇编一样,在C中定义的那些变量和数组的初始化就在startup.a51中进行,如果你在定义全局变量时带有数值,如unsigned char dATa xxx="100";,那startup.a51中就会有相关的赋值。如果没有=100,startup.a51就会把他清0。
(startup.a51==变量的初始化)。

这些初始化完毕后,还会设置SP指针。对非变量区域,如堆栈区,将不会有赋值或清零动作。

有人喜欢改startup.a51,为了满足自己一些想当然的爱好,这是不必要的,有可能错误的。比如掉电保护的时候想保存一些变量,但改startup.a51来实现是很笨的方法,实际只要利用非变量区域的特性,定义一个指针变量指向堆栈低部:0xff处就可实现。为什么还要去改? 可以这么说:任何时候都可以不需要改startup.a51,如果你明白它的特性。

bit 是在内部数据存储空间中 20H .. 2FH 区域中一个位的地址,这在DATA的20H以后以字节形式出现,可互相参照。另外加上8051 可寻址 的SFR,但刚刚试过,只是00H--7FH起作用,也就是说当数据有变化时颜色变红,以后的从80H到--FFH就不是位寻址区了,是位寻址的特殊寄存器,如涉及到了可位寻址的那11个当然会有反应。

复位后,程序计数器PC的内容为0000H,内部RAM各单元的值不确定。

各功能寄存器的复位值如下:

堆栈指针SP的复位值为07H,累加器ACC、寄存器B的复位值为00H,数据指针DPTR的复位值为0000H,而p0、p1、p2、p3四个口的复位值为0FFH。其他SFR如PSW、TCON、TMOD、TL0、TH0、TL1、TH1的复位值也为00H。

wave中是低128字节和高128字节(0-7FH),低128字节是片内RAM区,高128字节(80-FFH)是SFR(特殊功能寄存器)bit则是位于低128字节的20H .. 2FH 区域,即data的20H .. 2FH 区域
code 是在 0000H .. 0FFFFH 之间的一个代码地址。

我用
ORG 5000H
TAB: DB 22H,3BH,43H,66H,5H,6DH,88H后,
CODE从5000H开始以后变成DB各位

data 是在 0 到 127 之间的一个数据存储器地址,或者加 128 .. 255 范围内的一个特殊功能寄存器(SFR)地址。两者访问的方式不同。实际上由于PSW的复位设置PSW.3=RS0和PSW.4=RS1皆为0,所以通用工作寄存器区就是第0区,所以data的00--07H部分是与REG栏中的R0--R7对应的。以后的则仅代表低128字节的内部RAM。

idata 是 0 to 255 范围内的一个 idata 存储器地址

idata与data重合低128字节,有的地方只有DATA表示256字节的片内RAM

xdata 是 0- 65535 范围内的一个 xdata 存储器地址。

指针类型和存储区的关系详解

一、存储类型与存储区关系

data --->   可寻址片内ram
bdata --->   可位寻址的片内ram
idata --->  可寻址片内ram,允许访问全部内部ram
pdata ---> 分页寻址片外ram (MOVX @R0) (256 BYTE/页)
xdata ---> 可寻址片外ram (64k 地址范围FFFFH)
code ---> 程序存储区 (64k 地址范围),对应MOVC @DPTR

二、指针类型和存储区的关系

对变量进行声明时可以指定变量的存储类型如:

uchar data x和data uchar x相等价都是在内ram区分配一个字节的变量。

同样对于指针变量的声明,因涉及到指针变量本身的存储位置和指针所指向的存储区位置不同而进行相应的存储区类型关键字的使用如:

uchar xdata * data pstr
是指在内ram区分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向xdata区("*"前xdata关键字的作用),

可能初学C51时有点不好懂也不好记。没关系,我们马上就可以看到对应“*”前后不同的关键字的使用在编译时出现什么情况。
......
uchar xdata tmp[10]; //在外ram区开辟10个字节的内存空间,地址是外ram的0x0000-0x0009
......

第1种情况:

uchar data * data pstr;
pstr="tmp";
首先要提醒大家这样的代码是有bug的, 他不能通过这种方式正确的访问到tmp空间。 为什么?我们把编译后看到下面的汇编

代码:
MOV 0x08,#tmp(0x00) ;

0x08是指针pstr的存储地址看到了吗!本来访问外ram需要2 byte来寻址64k空间,但因为使用data关键字(在"*"号前的那个),所以按KeilC编译环境来说就把他编译成指向内ram的指针变量了,这也是初学C51的朋友们不理解各个存储类型的关键字定义而造成的bug。

特别是当工程中的默认的存储区类为large时,又把tmp[10] 声明为uchar tmp[10] 时,这样的bug是很隐秘的不容易被发现。

第2种情况:

uchar xdata * data pstr;
pstr = tmp;
这种情况是没问题的,这样的使用方法是指在内ram分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向 xdata区("*"前xdata关键字的作用)。编译后的汇编代码如下。

MOV 0x08,#tmp(0x00) ;0x08和0x09是在内ram区分配的pstr指针变量地址空间
MOV 0x09,#tmp(0x00)

这种情况应该是在这里所有介绍各种情况中效率最高的访问外ram的方法了,请大家记住他。

第3种情况:

uchar xdata * xdata pstr;
pstr="tmp";
这中情况也是对的,但效率不如第2种情况。编译后的汇编代码如下。
MOV DPTR, #0x000A ;0x000A,0x000B是在外ram区分配的pstr指针变量地址空间
MOV A, #tmp(0x00)
MOV @DPTR, A
INC DPTR
MOV A, #tmp(0x00)
MOVX @DPTR, A
这种方式一般用在内ram资源相对紧张而且对效率要求不高的项目中。

第4种情况:

uchar data * xdata pstr;
pstr="tmp";
如果详细看了第1种情况的读者发现这种写法和第1种很相似,是的,同第1 种情况一样这样也是有bug的,但是这次是把pstr分配到了外ram区了。编译后的汇编代码如下。

MOV DPTR, #0x000A ;0x000A是在外ram区分配的pstr指针变量的地址空间
MOV A, #tmp(0x00)
MOVX @DPTR, A

第5种情况:

uchar * data pstr;
pstr="tmp";
大家注意到"*"前的关键字声明没有了,是的这样会发生什么事呢?下面这么写呢!对了用齐豫的一首老歌名来说就是 “请跟我 来”,请跟我来看看编译后的汇编代码,有人问这不是在讲C51吗? 为什么还要给我们看汇编代码。C51要想用好就要尽可能提升C51 编译后的效率,看看编译后的汇编会帮助大家尽快成为生产高效C51代码的高手的。还是看代码吧!

MOV 0x08, #0X01 ;0x08-0x0A是在内ram区分配的pstr指针变量的地址空间
MOV 0x09, #tmp(0x00)
MOV 0x0A, #tmp(0x00)

注意:这是新介绍给大家的,大家会疑问为什么在前面的几种情况的pstr指针变量都用2 byte空间而到这里就用3 byte空间了呢?这是KeilC的一个系统内部处理,在KeilC中一个指针变量最多占用 3 byte空间,对于没有声明指针指向存储空间类型的指针, 系统编译代码时都强制加载一个字节的指针类型分辩值。具体的对应关系可以参考KeilC的help中C51 User’s Guide。

第6种情况:

uchar * pstr;
pstr="tmp";

这是最直接最简单的指针变量声明,但他的效率也最低。还是那句话,大家一起说好吗!编译后的汇编代码如下。

MOV DPTR, #0x000A ;0x000A-0x000C是在外ram区分配的pstr指针变量地址空间
MOV A, #0x01
MOV @DPTR, A
INC DPTR
MOV DPTR, #0x000A
MOV A, #tmp(0x00)
MOV @DPTR, A
INC DPTR
MOV A, #tmp(0x00)
MOVX @DPTR, A
这种情况很类似第5种和第3种情况的组合,既把pstr分配在外ram空间了又增加了指针类型的分辨值。

转自:张凌001

围观 360

一、作用

定时器/计数器以定时器的作用最为突出。一般来说涉及到定时器,都可以算作定时器算法。简单的如秒表等。复杂的如果系统内核的定时算法等。在学习单片机的路上,必学会定时器。

二、工作原理

定时器/计数器的原理其实没那么难。

定时器也就是一个计数器。而每计一个数的时间是一定的,于是就等价了定时器。51的定时器是从你设置的初值开始计数,直到他所能达到的最高数值为止。即:

定时时间 =(最大值 – 初值)X 每计一个数的时间

最大值因不同模式而定,初值需要你设计。

当达到定时器最大值的时候,就会触发定时器标志置位。当开启全局中断和定时器中断的时候,定时器标记置位会触发定时器中断函数,执行玩中断后,标记会自动复位。备注:如果没有触发中断函数,标记不会自动复位。

三、定时器术语理解

1、溢出:当定时器达到定时或者计数标准的时候,定时器就处在溢出停止状态。

2、溢出标记:当定时器溢出的时候,定时器标志就会置位。

3、初值:设计者自行设计的定时器计数的起步值。定时器是从这个值开始定时或者计数到最大值。

4、初值的计算:定时器有装载定时计数的寄存器,只要对这个寄存器进行操作即可。

四、工作模式寄存器TMOD,地址89H(不可按位寻址)

51单片机的定时计数器

GATE:定时操作开关控制位。当GATE=1时,INT0或INT1引脚为高电平,同时TCON中的TR0或TR1控制位为1时,计时/计数器0或1才开始工作。若GATE=0,则只要将TR0或TR1控制位设为1,计时/计数器0或1就开始工作。

C/T:定时器/计数器功能的选择位。C/T=1为计数器,通过外部引脚T0或T1输入计数脉冲。C/T=0时为定时器,由内部系统时钟提供计时工作脉冲。

M1和M0为定时器工作模式选择位。

定时器的工作模式如图2所示:

51单片机的定时计数器

五、控制寄存器TCON,地址位88H(可按位寻址)

51单片机的定时计数器

TF1:定时器T1溢出标志,可由程序查询和清零,TF1也是中断请求源,当CPU响应T1中断时由硬件清零。

TF0:定时器T0溢出标志,可由程序查询和清零,TF0也是中断请求源,当CPU响应T0中断时由硬件清零。

TR1:T1充许计数控制位,为1时充许T1计数。

TR0:T0充许计数控制位,为1时充许T0计数。

备注:其他位供其他功能使用

六、控制寄存器T2CON

51单片机的定时计数器

TF2:T2溢出中断标志。TF2必须由用户程序清“0”。当T2作为串口波特率发生器时,TF2不会被置“1”。

EXF2:定时器T2外部中断标志。EXEN2为1时,当T2EX(P1.1)发生负跳变时置1中断标志DXF2,EXF2必须由用户程序清“0”。

TCLK:串行接口的发送时钟选择标志。TCLK=1时,T2工作于波特率发生器方式。

RCLK:串行接口的接收时钟选择标志位。RCLK=1时,T2工作于波特率发生器方式。

EXEN2:T2的外部中断充许标志。

C/T2:外部计数器/定时器选择位。C/T2=1时,T2为外部事件计数器,计数脉冲来自T2(P1.0);C/T2=0时,T2为定时器,振荡脉冲的十二分频信号作为计数信号。

TR2:T2计数/定时控制位。TR1为1时充许计数,为0时禁止计数。

CP/RL2:捕捉和常数自动再装入方式选择位。为1时工作于捕捉方式,为0时T2工作于常数自动再装入方式。当TCLK或RCLK为1时,CP/RL2被忽略,T2总是工作于常数自动再装入方式。

定时器2工作模式如图5所示:

51单片机的定时计数器

七、定时器应用技巧

1、定时器定时长度扩展。可以人为的加入变量,每当定时器溢出,变量进行自加。这样就可以无限的扩展定时时间长度。

2、定时器误差问题。因为在执行定时器中断的时候,执行里面指令肯定要耗时。所以减少误差的就两种办法:简化中断服务程序;将定时器中断的程序外迁。

转自:武力戡乱的博客

围观 456

51系列单片机内部主要有四大功能模块,分别是I/O口模块、中断模块、定时器模块和串口通信模块(串行I/O口)。51开发的重点其实就是对这四个部分进行具体的开发,而其对这四个模块的开发实质则又是能否对每个模块所对应寄存器的正确操纵。

单片机的内部结构可以大概归纳如下图:四大功能模块相关的寄存器又可分为四大部分:

I/O口相关:P1 P2 P3 P4
中断相关:IP IE
定时器相关:TMOD TCON TL0、TH0、TL1、TH1
串口通信相关:PCON SBUF

51的特殊功能寄存器sfr笔记

51单片机内部共有21个特殊功能寄存器SFR,从下图中可以看出,每个SFR占1个字节,多数字节单元中的每一位又有专用的“位名称”。这21个SFR又按是否可以位寻址分为两大部分,ACC、IE、P1等11个可以位寻址,SP、TMOD等不可以位寻址。

51的特殊功能寄存器sfr笔记

51的特殊功能寄存器sfr笔记

能位寻址是指能够对它的每一位都可以进行位操作,比如我们常用的用十六进制的数据0x01为p1口赋值使得p1^0输出高电平,这种叫做能位寻址;不可寻址,则是指不能单独进行每一位的操作,如TMOD定时器工作模式及工作方式寄存器,在进行操作时,只能写TMOD=0xXX。再以IE寄存器为例进行位操作的解释。IE寄存器为中断允许寄存器,如各位的作用如图.其中第7位EA是51单片机5个中断的总开关,如要进入任何一个中断时,需先把EA打开,因为可以进行位操作,此时程序有两种写法:1)IE=0x80(假如其它位为0,即1000 0000),也可以直接写EA=1,后者EA=1即属于位操作。

51的特殊功能寄存器sfr笔记

关于能否进行位操作,可以通过查相关资料知道,当然还有个技巧就是其字节地址换成10进制后能否被“8”整除,能被“8”整除的就能进行位操作,不能被“8”整除就不能,如P1地址为90H,10进制为144 144/8=18,能被整除,所以可以位操作。再如TMOD地址为89H, 10进制为137 137/8=17.125,不能被整除,所以不可以位操作。

转自:劳斯机要开车了

围观 451

数据传送指令共有29条,数据传送指令一般的操作是把源操作数传送到目的操作数,指令执行完成后,源操作数不变,目的操作数等于源操作数。

如果要求在进行数据传送时,目的操作数不丢失,则不能用直接传送指令,而采用交换型的数据传送指令,数据传送指令不影响标志C,AC和OV,但可能会对奇偶标志P有影响。

以累加器A为目的操作数类指令(4条)

这4条指令的作用是把源操作数指向的内容送到累加器A。有直接、立即数、寄存器和寄存器间接寻址方式:
MOV A,data;   (data)→(A) 直接单元地址中的内容送到累加器A
MOV A,#data;   #data →(A) 立即数送到累加器A中
MOV A,Rn;    (Rn)→(A) Rn中的内容送到累加器A中
MOV A,@Ri;   ((Ri))→(A) Ri内容指向的地址单元中的内容送到累加器A

以寄存器Rn为目的操作数的指令(3条)

这3条指令的功能是把源操作数指定的内容送到所选定的工作寄存器Rn中。有直接、立即和寄存器寻址方式:
MOV Rn,data;  (data)→(Rn) 直接寻址单元中的内容送到寄存器Rn中
MOV Rn,#data;  #data →(Rn) 立即数直接送到寄存器Rn中
MOV Rn,A;    (A)→(Rn)    累加器A中的内容送到寄存器Rn中

以直接地址为目的操作数的指令(5条)

这组指令的功能是把源操作数指定的内容送到由直接地址data所选定的片内RAM中。有直接、立即、寄存器和寄存器间接4种寻址方式:
MOV data,data; (data)→(data) 直接地址单元中的内容送到直接地址单元
MOV data,#data; #data→(data)   立即数送到直接地址单元
MOV data,A; (A)→(data)    累加器A中的内容送到直接地址单元
MOV data,Rn; (Rn)→(data)    寄存器Rn中的内容送到直接地址单元
MOV data,@Ri; ((Ri))→(data) 寄存器Ri中的内容指定的地址单元中数据送到直接地址单元

以间接地址为目的操作数的指令(3条)

这组指令的功能是把源操作数指定的内容送到以Ri中的内容为地址的片内RAM中。有直接、立即和寄存器3种寻址方式:
MOV @Ri,data; (data)→((Ri)) 直接地址单元中的内容送到以Ri中的内容为地址的RAM单元
MOV @Ri,#data; #data→((Ri)) 立即数送到以Ri中的内容为地址的RAM单元
MOV @Ri,A; (A)→((Ri)) 累加器A中的内容送到以Ri中的内容为地址的RAM单元

查表指令(2条)

这组指令的功能是对存放于程序存储器中的数据表格进行查找传送,使用变址寻址方式:
MOVC A,@A+DPTR;((A))+(DPTR)→(A) 表格地址单元中的内容送到累加器A中
MOVC A,@A+PC; ((PC))+1→(A),((A))+(PC)→(A) 表格地址单元中的内容送到累加器A中

累加器A与片外数据存储器RAM传送指令(4条)

这4条指令的作用是累加器A与片外RAM间的数据传送。使用寄存器寻址方式:
MOVX @DPTR,A; (A)→((DPTR)) 累加器中的内容送到数据指针指向片外RAM地址中
MOVX A, @DPTR; ((DPTR))→(A) 数据指针指向片外RAM地址中的内容送到累加器A中
MOVX A, @Ri; ((Ri))→(A) 寄存器Ri指向片外RAM地址中的内容送到累加器A中
MOVX @Ri,A; (A)→((Ri)) 累加器中的内容送到寄存器Ri指向片外RAM地址中

堆栈操作类指令(2条)

这4类指令的作用是把直接寻址单元的内容传送到堆栈指针SP所指的单元中,以及把SP所指单元的内容送到直接寻址单元中。

这类指令只有两条,下述的第一条常 称为入栈操作指令,第二条称为出栈操作指令。

需要指出的是,单片机开机复位后,(SP)默认为07H,但一般都需要重新赋值,设置新的SP首址。入栈的第 一个数据必须存放于SP+1所指存储单元,故实际的堆栈底为SP+1所指的存储单元。
PUSH data; (SP)+1 →(SP),(data)→(SP) 堆栈指针首先加1,直接寻址单元中的数据送到堆栈指针SP所指的单元中
POP data; (SP)→(data),(SP)-1→(SP) 堆栈指针SP所指的单元数据送到直接寻址单元中,堆栈指针SP再进行减1操作

交换指令(5条)

这5条指令的功能是把累加器A中的内容与源操作数所指的数据相互交换。
XCH A,Rn;  (A)←→(Rn)         累加器与工作寄存器Rn中的内容互换
XCH A,@Ri;  (A)←→((Ri))       累加器与工作寄存器Ri所指的存储单元中的内容互换
XCH A,data; (A)←→(data)       累加器与直接地址单元中的内容互换
XCHD A,@Ri; (A3-0)←→((Ri)3-0) 累加器与工作寄存器Ri所指的存储单元中的内容低半字节互换
SWAP A;    (A3-0)←→(A7-4)     累加器中的内容高低半字节互换

16位数据传送指令(1条)

这条指令的功能是把16位常数送入数据指针寄存器。
MOV DPTR,#data16;#dataH→(DPH)   #dataL→(DPL)  16位常数的高8位送到DPH,低8位送到DPL

来源:Bruce Chen

围观 305

页面

订阅 RSS - 51单片机