《单片机课程设计说明书》-占空比可调信号发生器
占空比可调信号发生器
1 软件介绍
1.1proteus软件
Proteus ISIS是英国Labcenter公司开发的电路分析与实物仿真软件。它运行于Windows操作系统上,可以仿真、分析(SPICE)各种模拟器件和集成电路,该软件的特点是:(1)实现了单片机仿真和SPICE电路仿真相结合。具有模拟电路仿真、数字电路仿真、单片机及其外围电路组成的系统的仿真、RS232动态仿真、I2C调试器、SPI调试器、键盘和LCD系统仿真的功能;有各种虚拟仪器,如示波器、逻辑分析仪、信号发生器等。
(2)支持主流单片机系统的仿真。目前支持的单片机类型有:68000系列、8051系列、AVR系列、PIC12系列、PIC16系列、PIC18系列、Z80系列、HC11系列以及各种外围芯片。
(3) 提供软件调试功能。在硬件仿真系统中具有全速、单步、设置断点等调试功能,同时可以观察各个变量、寄存器等的当前状态,因此在该软件仿真系统中,也必须具有这些功能;同时支持第三方的软件编译和调试环境,如Keil C51 uVision2等软件。
(4) 具有强大的原理图绘制功能。总之,该软件是一款集单片机和SPICE分析于一身的仿真软件,功能极其强大。本章介绍Proteus ISIS软件的工作环境和一些基本操作。
特点:支持ARM7,PIC ,AVR,HC11以及8051系列的微处理器CPU模型,更多模型正在开发中:
交互外设模型有LCD显示、RS232终端、通用键盘、开关、按钮、LED等;
强大的调试功能,如访问寄存器与内存,设置断点和单步运行模式;
支持如IAR、Keil和Hitech等开发工具的源码C和汇编的调试;
一键“make”特性:一个键完成编译与仿真操作;
内置超过6000标准SPICE模型,完全兼容制造商提供的SPICE模型;
DLL界面为应用提供特定的模式;
基于工业标准的SPICE3F5混合模型电路仿真器
14种虚拟仪器:示波器、逻辑分析仪、信号发生器、规程分析仪等;
高级仿真包含强大的基于图形的分析功能:模拟、数字和混合瞬时图形;频率;转换;
噪声;失真;付立叶;交流、直流和音频曲线;
模拟信号发生器包括直流、正旋、脉冲、分段线性、音频、指数、单频FM;数字信号
发生器包括尖脉冲、脉冲、时钟和码流;
集成PROTEUS PCB设计形成完整的电子设计系统。
1.2Keil软件
Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。Keil提供了包括C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境(uVision)将这些部分组合在一起。运行Keil软件需要WIN98、NT、WIN2000、WINXP等操作系统。如果你使用C语言编程,那么Keil几乎就是你的不二之选,即使不使用C语言而仅用汇编语言编程,其方便易用的集成环境、强大的软件仿真调试工具也会令你事半功倍。
Keil C51开发系统基本知识Keil C51开发系统基本知识
(1)系统概述
Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。下面详细介绍Keil C51开发系统各部分功能和使用。
(2)Keil C51单片机软件开发系统的整体结构
C51工具包的整体结构,uVision与Ishell分别是C51 for Windows和for Dos的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。然后分别由C51及C51编译器编译生成目标文件(.OBJ)。目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.ABS)。ABS文件由OH51转换成标准的Hex文件,以供调试器dScope51或tScope51使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。
Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。
2设计原理及方法
2.1 定时器、的工作原理
定时/计数器实质上是一个加1计数器,它可以工作于定时方式,也可以工作于计数方式,两种工作方式实际是对脉冲计数,只不过是所计脉冲来源不同。定时/计数器的内部结构和控制信号如下图2.1.1所示。
图2.1.1定时器/计数器内部结构和控制信号
当其工作于定时方式时, =0,开关S打向上,计数器,的计数脉冲来自振荡器的12分频后的脉冲(即/12),即对系统的机器周期计数。档开关K受控合上时,每过一个周期,计数器,加1;当记满了预设的个数,,回零,置位定时/计数器溢出中断标志位,产生溢出中断。定时/计数器亦是如此。
工作方式寄存器TMOD(地址:89H)为8位寄存器,用于设置定时/计数器的工作方式,低四位用于,高四位用于。其格式如下表2.1.1所示。
表2.1.1 定时/计数器方式控制寄存器TMOD
GATE C/T M1 M0 GATE C/T M1 M0 GATE:门控位。GATE=0时,只要用软件使TCON中的为1,就可以启动定时/计数器工作;GATA=1时,要用软件使为1,同时外部中断引脚也为高电平时,才能启动定时/计数器工作。即此时定时器的启动多了一条件。
:定时/计数模式选择位。=0为定时模式; =1为计数模式。
:工作方式设置位。定时/计数器有四种工作方式,由进行设置。
C/T
C/T C/T C/T
定时/计数器控制寄存器TCON(地址88H)设置如下表2.1.3所示。
表2.1.3 定时/计数器控制寄存器TCON
在TCON寄存器中,定时/计数器的控制仅用了其中的高四位。
:溢出中断请求标志位。,计数溢出时由硬件自动置为1。CPU响应中断后由硬件自动清0。工作时,CPU可随时查询的状态。所以,可用作查询测试的标志。也可以用软件置1或清0,同硬件置1或清0的效果一样。
:运行控制位。置1时,开始工作;置0时,停止工作。由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
:溢出中断请求标志位,其功能与类同。
:运行控制位,其功能与类同。
定时/计数器工作方式设置如下表2.1.2所示。
表2.1.2 定时/计数器工作方式设置表
M1M0 工作方式说明
00 方式0 13位定时/计数器
01 方式1 16位定时/计数器
10 方式2 8自动重装定时/计数器
11 方式3 分成两个独立的8位定时/计数器;此方式停止计数
方式0为13位计数,由的低5位(高3位未用)和的8位组成,最大计数值为(8192个脉冲)。启动后计数器加1计数。的低5位计数满回零后向进位,当13位计数满回零时,中断溢出位置1,产生中断请求。
定时器模式时有:N=t/ Tcy
计数初值计算的公式为:
定时器的初值还可以采用计数个数直接取补法获得。
方式1和方式0基本相同,唯一的区别是方式1的计数位数是16位,由作为低8位、作为高8位,组成了16位加1计数器。其最大计数值为(65536个脉冲),是几种方式中计数值最大的方式。
计数个数与计数初值的关系为:
13
X=2-N
16
X=2-N
方式2为自动重装初值的8位计数方式。在这种方式下,在和两个寄存器中,专用于寄存8位计数初值并保持不变,进行8位加1计数,当
计数溢出时,
除产生溢出中断请求外,还自动将
中不变的初值重新装载到
。
计数个数与计数初值的关系为:
方式3只适用于定时/计数器,定时器处于方式3时相当于=0,停止计数。
工作方式3将
分成为两个独立的8位计数器
和
。
2.2 定时器的工作原理
定时器/计数器
也是一个16 位定时器/计数器。除了具备定时/计数器
、
的定
时计数功能外,还具有16位自动重装载、捕获方式和加、减计数方式。所谓捕获方式,就是把16位瞬时计数值同时记录在特殊功能寄存器的RCAP2H 和RCAP2L 中,这样CPU 在读数值的时候,就避免了在读高字节时低字节在变化,从而引起误差。
定时/计数器
的内部结构和捕捉方式原理如图2.2.1所示。由图可见,除了具有
相同的定时计数结构外,增加了特殊功能寄存器的RCAP2H/RCAP2L
和控制位,在不同的工作方式下有不同的作用。
图2.2.1 定时/计数器
的内部结构和捕捉方式原理
引用了两个外部引脚和
作用如下:
(
):定时/计数器2的外部计数脉冲输入,定时脉冲输出。
(T2EX):定时/计数器2的捕捉/重装方式的触发和检测控制。
定时器
/计数器
控制寄存器T2CON (地址C8H )的设置如下表2.2.1所示。
8X=2-N
表2.2.1 定时/计数器控制寄存器T2CON
(MSB) (LSB) EXF2 RLCLK TCLK EXEN2 C/CP/RL2 :计数溢出标志位,当允许中断,将引起中断,必须软件清零。=1,有溢出;=0,无溢出。如果RCLK=1或TCLK=1,此位无效。
EXF2:的外部标志位。当外部使能位EXEN2=1,且T2EX(引脚)有一个下降沿产生,EXF2被置1,如果允许中断将引起中断,EXF2必须软件清零。
RCLK、TCLK:发送时钟、接收时钟允许。如果RCLK=1或TCLK=1,则8XX51的串行使用作为波特率发生器,分别产生发送时钟或接收时钟,两个可以分别控制。如果RCLK=0或TCLK=0,则定时器作为串行口波特率发生器。
:的启动控制标志;=0:停止;=1:启动
C/:定时计数器选择。只能通过软件的置位或清除;C/=0,工作于定时器方式,对/12的脉冲(机器周期)计数;C/=1,工作于计数器方式,对(引脚)外部输入脉冲的下降沿计数。
CP/RT2:捕获/重装方式选择,只能通过软件的置位或清除。CP/RT2=0时,工作于重装方式,RCAP2H,RCAP2L;CP/RT2=1时,工作于捕获方式,RCAP2H,RCAP2L。
定时器/计数器方式控制寄存器T2MOD(地址C9H)的设置如下表2.2.2所示。
表 2.2.2定时器/计数器方式控制寄存器T2MOD
(MSB) (LSB)
T2OE DCEN T2OE:输出允许位,T2OE=1,允许定时时钟输出到。T2OE=1,禁止定时时钟输出到。
DCEN:计数方式选择。DCEN=1,的计数方式由引脚状态分配:=1,减计数;=0,加计数。DCEN=0,计数方式与无关,同和一样,采用加计数方式。
的数据寄存器、和、的用法一样,而捕获寄存器RCAP2H、RCAP2L只是在捕获方式下,产生捕获操作时自动保存、的值。
定时器/计数器的工作方式见下表2.2.3所示。
表2.2.3定时器/计数器的工作方式
RCLK+TCLK CP/RL2 方式
0 0 1 X 0
1
X
X
1
1
1
16位自动重装
16位捕捉方式
波特率发生器
停止工作
自动捕捉方式下,如果从检测到一个下降沿,和的当前值就会被捕捉到RCAP2H 和RCAP2L中,同时使EXF2=1.如果允许中断,将产生中断。
自动重装方式1(DCEN=0)下,如果从检测到一个下降沿,RCAP2H和RCAP2L中的值就会被重装到和中,同时使EXF2=1。
自动重装方式2(DCEN=1)下,=1,减计数,当计数溢出时,和中自动重装为0FFH;=0,加计数,当计数溢出时,和中自动重装为RCAP2H和RCAP2L中的值。无论是加计数还是减计数,溢出时=1。
波特率发生器方式下,的计数脉冲可以由/2或输入。此时,RCAP2H和RCAP2L 中的值用做计数初值,溢出后此值自动装到和中。如果RCLK或TCLK中某值为1时,表示收发时钟一个用,一个用。在这种方式下,如果在检测到一个下降沿,则EXF2变为1,可引起中断。
(波特率)==
时钟输出方式
的溢出脉冲从输出。输出脉冲频率由下式决定:
=
2.3 设计方法
(1)方波的产生
在本设计方案中,采用的定时器产生频率和占空比可调的方波。对于工作方式0,工作方式1具有16位的寄存器,定时时间更长,产生的频率范围更宽。对比于工作方式2,虽然方式2的自动重载功能使定时更加准确,但对于产生低频来说,方式2的只有256us 的定时时间,产生的中断多,误差更大,因此选择设置在工作方式1下,GATE=0。
设定定时器设置初始值,即给高低电平分别分配定时时间。当程序启动,由输入给定高电平,定时器开始加1计数,当高电平16位计数满回零时,置1,产生中断,高电平转为低电平,当低电平16位计数满回零时,置1,产生中断,低电平再转为高电平,得出方波。
定时器初始值计算如下所示:
高电平初始值=-高电平时间
低电平初始值=-低电平时间
(2)高电平时间的测量
本设计方案中,采用定时器测量高电平的时间。将定时器设置在工作方式1下,并设置GATE=1,此时要用软件使为1,同时外部中断1(INT1)引脚也为高电平时,才能启动定时/计数器工作。所以将控制输出的方波接在外部中断1的输入引脚上,当输入高电平,定时器开始启动,在下降沿的时候,读、的值,同时将、
清零,即可算出高电平时间T1_high。
(3)频率的测量
在本设计方案中,采用定时器测量输出方波一周期的时间。将定时器设置在工作方式16位捕捉方式下,为了更加准确,在前两个下降沿的时候,先清、、、,当第三个下降沿来临的时候,读取RCAP2(即、)的值(相当于三个周期中只测量了一个周期)即为输出方波的周期T2。
综合(2)(3)上述,根据以下公式
f=1/T2
占空比=高电平时间/周期=T1_high/T2
由此测出所输出方波的频率和占空比。
3系统硬件线路设计图3.1仿真电路连线图
图3.1.1 仿真线路连接图3.2 实物连线图
图3.2.1 实物线路连接图
4 程序框图
图4.1 主程序框图
图4.2 定时器0中断程序框图
图4.3 定时器2中断程序框图
5 资源分配表
I/O口分配表如下表5.1所示。
表5.1 I/O口分配表
-、
液晶数据线捕捉方式
下方波信号
输入端液晶控制器键盘扫描方波输出测高电平
时方波输入
端
内部资源分配表如下表5.2所示。
表5.2 内部资源分配表
定时器定时器定时器
产生频率、占空比可调方波测量高电平的时间测量输出方波的周期6 源程序
函数声明:文件public.h
#include
#include
#define uchar unsigned char
#define uint unsigned int
void delay1ms(void);
void write_data(uchar date);
void write_com(uchar com);
void lcd_init();
void display_init();
void store_num();
void ok();
void refresh_time_0();
void refresh_duty();
void keycode_return();
uchar keyscan();
void init();
void dis_freq_futy();
主程序:文件main.c
#include"public.h"
sbit out=P3^0;//输出
sbit out2=P3^1;
sfr16 RCAP2 = 0xCA; //T2捕获寄存器
sfr T2MOD = 0xC9;
char th0_low,tl0_low,th0_high,tl0_high,th1_out,tl1_out;
float time0,freq_test,duty_test;
uchar freq_out_num[4],duty_test_num[3],vaule=1;
uint
freq=100,duty_factor=50,flag=1,time0_high,time0_low,high_count=0,low_count=0, count1,count2;
uint Last,Now,freq_test1=500,duty_test1=0;
void time_2() interrupt 5 //T2中断程序
{
if(EXF2)
{
if(vaule==2)
{
th1_out=TH1;
tl1_out=TL1;
EXF2=0;//外部标志位需要软件清零;
Now=RCAP2;
vaule=0;
}
else
{
EXF2=0;
vaule++;
TH2=TH1=0;
TL2=TL1=0;
}
}
else
{
TF2=0;
}
}
void time_0() interrupt 1 //T0中断程序{
if(flag==0)
{
TH0=th0_low;
TL0=tl0_low;
flag=1;
out=0;
out2=0;
}
else
{
TH0=th0_high;
TL0=tl0_high;
flag=0;
out=1;
out2=1;
}
}
void main()
{
init();
while(1)
{
keycode_return();
time0=1000000/freq; //不求time0直接求高低电平,可能精度高些
time0_high=time0/100*duty_factor; //高电平时间
time0_low=time0-time0_high; //低电平
high_count=low_count=0;
th0_high=(65536-time0_high)/256; // 高低电平的定时器初值
tl0_high=(65536-time0_high)%256;
th0_low=(65536-time0_low)/256;
tl0_low=(65536-time0_low)%256;
//************T2频率测量处理************************
// ET2=0;
freq_test=1000000/Now; //计算出频率值
//************T1占空比测量处理*********************
duty_test=th1_out*256+tl1_out;//计算T1计数值
duty_test=duty_test*1000/Now;//求百分比
// if(abs(duty_test-duty_factor)>1)
// duty_test+=600;
dis_freq_futy();
// ET2=1;
}
}
void dis_freq_futy()
{
uchar j;
//*************频率显示**************************** freq_test1=(uint)freq_test;
for(j=0;j<4;j++)
{
freq_out_num[j]=freq_test1%10;
freq_test1/=10;
}
write_com(0x95);
for(j=0;j<4;j++)
{
write_data(freq_out_num[3-j]+0x30);
}
//*************占空比显示************************** duty_test1=(uint)duty_test;
write_com(0x9d);
for(j=0;j<3;j++)
{
duty_test_num[j]=duty_test1%10;
duty_test1/=10;
}
for(j=0;j<2;j++)
{
write_data(duty_test_num[2-j]+0x30);
}
write_data('.');
write_data(duty_test_num[0]+0x30);
write_data('%');
}
void init()
{
display_init();
//***************************************************
//**************定时器0初始化************************
time0=1000000/freq; //不求time0直接求高低电平,可能精度高些
time0_high=time0/100*duty_factor; //高电平时间
time0_low=time0-time0_high; //低电平
TMOD=0x91;//设置工作方式GATE1 C/T M1 M0=1001;工作方式GATE0 C/T M1 M0=0001 th0_high=(65536-time0_high)/256; // 高低电平的定时器初值
tl0_high=(65536-time0_high)%256;
th0_low=(65536-time0_low)/256;//
tl0_low=(65536-time0_low)%256;
EA=1;
ET0=1;
TR0=1;
//****************************************************
//***************定时器2初始化************************
ET2=1;//开T2中断
T2MOD=0X00;
T2CON=0x0d;//16位捕获模式,定时,外部使能
TH2=0;
TL2=0;
//****************************************************
//****************定时器1初始化***********************
// ET1=1;//不开T1中断
TR1=1;
TH1=0;
TL1=0;
}
矩阵键盘程序:文件keycode.c
#include
uchar key,keycode;
extern freq,duty_factor;
uchar num[4],freq_num[4],duty_factor_num[2],n; uint freq1;
void delay(uint z)//4.476ms
{
uint x,y;
for(y=z;y>0;y--)
for(x=110;x>0;x--);
}
unsigned char code KeyTable[4][4]={
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'0','F','E','D'}};
//***键盘扫描*********//
uchar keyscan()
{
unsigned int row=0,column=0;
uchar key=0;
// EA=0;
P2=0xf0;
key=P2;
if(key!=0xf0){
delay(10);
key=P2;
if(key!=0xf0){
P2=key|0x0f;
switch(key)
{
case 0xe0:column=0;break;//1110
case 0xd0:column=1;break;//1101
case 0xb0:column=2;break;//1011
case 0x70:column=3;break;//0111 }
key=P2&0x0f;
switch(key)
{
case 0x0e:row=0;break;//1110
case 0x0d:row=1;break;//1101
case 0x0b:row=2;break;//1011
case 0x07:row=3;break;//0111
}
}
P2=0x0f;
key=P2;
while((key&0x0f)!=0x0f){key=P2;}
EA=0;