基于89c52单片机的远程抄表
1.前言
随着数字技术的快速发展,数字技术被广泛应用于智能控制的领域中。单片机以其集成度高、运算速度快、体积小、运行可靠、价格低廉等特点,在过程控制、数据采集、机电一体化、智能化仪表、家用电器及网络技术等方面得到了广泛的应用。
电能计量是供电企业中的一项重要工作,传统方式的电能计量与结算全部靠人工定期到现场抄读数据,显然在实时性、准确性和应用性等方面都存在许多不足之处。对于用电客户而言,不仅要求始终具有高质量的电能用,而且还要享受到更好的服务,加上供电部门对防窃电技术提出的更高要求,所以提高电力部门电费实时性结算水平, 建立一种新型的抄表方式很有必要,它已成为所有电力部门的共识。基于51单片机的远程自动抄表系统有价钱便宜,质量好的优点,有很大发展潜力。
2.设计任务
2.1设计的主要要求
2.2设计的主要功能
3.硬件方案设计
硬件方案的主电路框图如下图3.1
AT89C52RC |
数码管显示电路 |
命令控制显示 |
与PC机通讯 |
电能表脉冲产生电路 |
图3.1主电路框图
3.1 AT89C52简介
设计的核心是AT89C52单片机,其主要芯片资料即功能介绍如下:
AT89C52单片机是ATMEL公司在Intel公司生产的MCS-52系列基础上生产的8位Flash单片机系列产品之一,是一种20引脚双列直插式芯片,其原理图如下图4.1。它内含2KB可反复烧录及擦除内存和128字节的RAM,有15条可编程控制的I/O接口线,5个中断触发源,指令与MCS-52系列完全兼容,还包含2个16位定时器/计数器;1个全双工串行口;1个精密模拟比较器以及片内振荡器和时钟电路。此外,AT89C52是用可降到0频率的静态逻辑操作设计的,并支持两种可选的软件节电工作方式,空闲方式停止CPU工作,但允许RAM、定时器/计数器、串行口和中断系统继续工作;掉电方式保存RAM内容,但振荡器停止工作,并禁止所有其他不见的工作直到下一个硬件复位[3]。
AT89C52的单片机的引脚及说明:
图3.2 AT89C52引脚图
(1)Vcc:电源正极端。
(2)GND:接地端。
(3)P1口:P1口是一8位双向I/O口。引脚P1.2~P1.7提供内部上拉电阻,P1.0和P1.1要求外部上拉电阻,P1.0和P1.1还分别作为片内精密模拟比较器的同相输入(AIN0)和反相输入(AIN1)。P1口输出缓冲器可吸收20mA电流,并能直接驱动LED显示。当P1口引脚写入“1”时,可用作输入端,当引脚P1.2~P1.7用作输入端并被外部拉低时,将因内部的上拉电阻而输出电流()。
P1口还在Flash编程和程序校验期间接收代码数据。
(4)P3口:P3口的P3.0~P3.5和P3.7是带有内部上拉电阻的7个双向I/O引脚,P3.6用于固定输入片内比较器的输出信号,并且作为一通用I/O引脚而不可访问。P3口缓冲器可吸收20mA电流,当P3引脚写入“1”时,它们被内部上拉电阻高并可用作输入端,用作输入端时,被外部拉低的P3口引脚将用上拉电阻而输出电流()。P3口还具有第二功能,如下表3.3所示:
P3口还接收一些用于Flash存储器编程和程序校验的控制信号。
(5)RES:复位输入.RES一旦变成高电平,所有的I/O引脚就复位到“1”,当振荡器正在运行时,持续给出RES引脚两个机器周期需12个振荡器周期或时钟周期。
(6)XTAL1:作为振荡器反相放大器的输入和内部时钟发生器的输入。
(7)XTAL2:作为振荡器反相放大器的输出[5]。
口引脚 |
功能 |
P3.0 |
RXD(串行输入端口) |
P3.1 |
TXD(串行输出端口) |
P3.2 |
|
P3.3 |
|
P3.4 |
T0(定时器0外部输入) |
P3.5 |
T0(定时器0外部输入) |
表3.3 P3口的第二功能
AT89C52中断系统:
中断系统是单片机(AT89C52)的重要功能模块之一。所谓中断是指CPU对系统中或系统外发生的某个事件的一种响应过程,即CPU暂时停止现行程序的执行,而自动转去执行预先安排好的处理该事件的服务子程序,当处理结束后,再返回到被暂停程序的断点处,继续执行原来被中断的程序。
中断系统具有中断优先权排队、实现中断嵌套、自动响应中断、实现中断返回功能。中断技术使设计系统提高了CPU的效率和实时数据的处理时效,且使系统能进行故障自动诊断处理操作。
AT89C52单片机有5个中断源(两外部中断源 INT0、INT1,两内部中断源T0、T1和一个串行口中断),相关的特殊功能寄存器(SFR)有:中断屏蔽寄存器IE、中断优先级寄存器IP、定时器/计数器控制寄存器TCON和串行口控制寄存器SCON中的有关位(注:均可位寻址),这些有关的寄存器位在中断系统初始化时须被正确的置位。
CPU响应中断后,由硬件自动执行如下的功能操作:
(1)根据中断请求源的优先级高低,对相应的优先级状态触发器置1。
(2)保护断点,即把程序计数器PC的内容压入堆栈保存。
(3)清内部硬件可清除的中断请求标志位(IE0、IE1、TF0、TF1)。
(4)把被响应的中断服务程序入口地址送入PC,从而转入响应的中断服务程序执行(各个中断源的中断入口地址均已由系统规定,不能改动。INT0、T0、INT1、T1、串行口中断源对应的入口地址分别是0003H、000BH、0013H、001BH、0023H)。
中断服务程序的最后必须使中断返回,即CPU将先相应的优先级状态触发器清零,然后从堆栈中弹出栈顶的两个字节PC,从而返回到断点处。
AT89C52定时器/计数器:
定时器/计数器也是单片机(AT89C52)的重要功能模块之一。在检测、控制及智能仪器等应用中,常用定时器时钟来实现定时检测、定时控制。还可用定时器产生毫秒宽的脉冲,驱动步进电机一类的电器机械。计数器主要用于外部事件的计数。
定时器/计数器具有多种工作方式,可以是计数方式也可以是定时方式,其计数值是可变的,当然计数的最大值是有限的,这取决于计数器的位数,计数的最大值也就限定了定时的最大值,在到达设定的定时或计数值时发出中断申请,以便实现定时控制。设计时其方式具体选择由定时器/计数器方式控制寄存器TMOD的位设置控制,其启动运行由定时器/计数器控制寄存器TCON的位设置来控制。
定时器/计数器T0和T1分别由两个8位的专用寄存器组成,即定时器/计数器T0由TH0和TL0组成,T1由TH1和TL1组成。
3.1整体电路原理图
图3.4 电路原理图
通过信号发生器来模拟脉冲电能表产生脉冲,通过计数器来对电能表进行脉冲计数,从而达到计数,因为是模拟远程抄表系统,所以,为了更形象地显示电能表读数,我用数码管显示了电能表的实际读数。为了和实际更相像,我在仿真中没有使用虚拟串口来模拟,而是实物中做实验,通过max232进行通讯。
4.软件方案设计
软件流程图 如图4.1所示
Initial软件流程图
|
Start |
Initial |
Send to PC |
Open ES=1 |
Display |
串口初始化 |
定时初始化 |
开中断 |
优先级设置 |
图4.1 软件流程图
5.调试
5.1硬件调试
首先通以小幅的电压,检测AT89C52芯片,稳压管电路部分其电路是否有短路或者电压不准确的情况,检查它们的功能实现是否良好。而后把硬件电路如硬件电路原理图部分所划分的块电路先后把各部分电路连接好了,就先用诸如万用表这样的仪器检查其各部分工作是否良好,待各部分都没有接线不良等问题后就逐部分把它们连接起来,最后再用同样的方法检查整个硬件电路的连接状况。全都连接好后,就把已经在Keil编译环境下调试好的程序下载到单片机里面去。固化后出现一些小问题都一一解决了。
5.2软件调试
在调试过程中出现了几个问题,比如,计数器不能计数,最后发现中断优先级没有设定,最后设置后就好了,还有个问题,如数码管显示出现不稳定情况,很容易看出来是因为延时时间出问题了,都一一解决了。
6.结束语
通过本次的课程设计,使我更进一步的熟悉了单片机的应用,同时对数字电路和模拟电路的知识得以巩固,逻辑思维、分析能力、综合运用所学知识的能力等也都得以培养和提高。
程序附录
/*-----------------------------------------------
名称:远程抄表系统
编写:邹腾
日期:2013.6.2
修改:无
内容:连接好串口或者usb转串口至电脑,下载该程序,打开电源
打开串口调试程序,将波特率设置为19200,无奇偶校验
晶振22.1184MHz,发送和接收使用的格式相同,如都使用
字符型格式,按复位重启程序,可以看到接收到请在发送区输入任意信息:
然后在发送区发送任意信息,接收区返回同样信息,表明串口收发无误
------------------------------------------------*/
#include<reg52.h> //包含头文件
/*------------------------------------------------
全局变量
------------------------------------------------*/
table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //0……9
unsigned char count0=0;
unsigned char flag0=0;
/*------------------------------------------------
函数声明
------------------------------------------------*/
void InitUART(void);
void Init_Timer0(void);
void SendByte(unsigned char dat);
void SendStr(unsigned char *s);
void delay(unsigned int ms,unsigned int us);
void display(unsigned int date);
/*------------------------------------------------
主函数
------------------------------------------------*/
void main (void)
{
P1=0x00;
InitUART(); //串口中断初始化
Init_Timer0(); //定时中断初始化
EA=1; //全局开中断
IP=0x06; //将定时器0设为高优先级
SendStr("控制命令提示:0为读取当前电能信息;1为发送通知给当事人;");
SendStr("请在发送区输入任意信息:");
ES = 1; //打开串口中断
while (1)
{
display(count0);
}
}
/*------------------------------------------------
发送一个字节
------------------------------------------------*/
void SendByte(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
/*------------------------------------------------
发送一个字符串
------------------------------------------------*/
void SendStr(unsigned char *s)
{
while(*s!='\0')// \0 表示字符串结束标志,通过检测是否字符串末尾
{
SendByte(*s);
s++;
}
}
/*---------------------------------------------
数码管显示函数
----------------------------------------------*/
void display(unsigned int date)
{
unsigned char qian,bai,shi,ge; //局部变量
qian=date/1000; // 千位
bai=date/100%10; // 百位
shi=date/10%10; //十位
ge=date%10; //个位
P2=0x0e; // 最高位显示,即千位
P0=table[qian];
delay(10,100);
P2=0x0d; // 百位
P0=table[bai];
delay(10,100);
P2=0x0b; // 十位
P0=table[shi];
delay(10,100);
P2=0x07; // 个位
P0=table[ge];
delay(10,100);
}
/*---------------------------------------------
延时函数
----------------------------------------------*/
void delay(unsigned int ms,unsigned int us)
{
for(;ms>0;ms--)
for(;us>0;us--);
}
/*------------------------------------------------
串口初始化
------------------------------------------------*/
void InitUART (void)
{
SCON = 0x50; // SCON: 模式 1, 8-bit UART, 使能接收
TMOD |= 0x20; // TMOD: timer 1, mode 2, 8-bit 重装
TH1 = 0xFd; // TH1: 重装值 19200 波特率 晶振 22.0184MHz
TR1 = 1; // TR1: timer 1 打开
EA = 1; //打开总中断
// ES = 1; //打开串口中断
}
/*------------------------------------------------
定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
TMOD |= 0x01 | 0x04; //使用模式1,16位计数器,使用"|"符号可以在使用多个定时器时不受影响
TH0=0xFF; //给定初值
TL0=55; //从55计数到255
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
/*------------------------------------------------
定时器中断子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1 using 1
{
TH0=0xFF; //重新给定初值
TL0=55; //从55计数到255
count0++; //电能表读数加1
//命令1,0:读当前电能表数据
if(P1==0x30)
{
SendByte(count0);
}
//命令2,1:
/*if(P1==0xce)
{
SendStr("请注意查看");
}
*/
//命令3,2:
/*if(P1==0xcd)
{
SendStr("请注意查看");
}
*/
//命令4,3:
/*if(P1==0xcb)
{
SendStr("请注意查看");
}
*/
}
/*------------------------------------------------
串口中断程序
------------------------------------------------*/
void UART_SER (void) interrupt 4 //串行中断服务程序
{
unsigned char Temp; //定义临时变量
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
Temp=SBUF; //读入缓冲区的值
P1=Temp; //把值输出到P1口,用于观察
SBUF=Temp; //把接收到的值再发回电脑端
}
if(TI) //如果是发送标志位,清零
TI=0;
}
/*将T0、外中断1设为高优先级,其它为低优先级,求IP的值。
IP的首3位没用,可任意取值,设为000,后面根据要求写就可
以了XX,因此,最终,IP的值就是06H。例:在上例中,如果5
个中断请求同时发生,求中断响应的次序。响应次序为:定时
器0->外中断1->外中断0->定时器1->串行中断。
*/