单片机的课程设计NO.4
程序功能
1、ADC0808采集一路0~5V的直流信号
2、生成频率100Hz的PWM信号,占空比与采集到的直流信号电压成正比
3、LCD1602显示当前采集到的电压值与输出PWM的占空比
4、DAC0832输出一路0~5V正弦信号,频率值(1~9)/10Hz
5、向上位机传输当前采集到的电压值与PWM占空比
6、由上位机控制输出正弦信号的频率
项目代码
/*上位机操作指令*/
/*
指令 作用
0 单片机停止发送
1
2 显示实时测量值
3 改变DAC0832产生正弦信号的频率(在此之后输入频率)
*/
/* ----------------------------
程序功能
1、ADC0808采集一路0~5V的直流信号
2、生成频率100Hz的PWM信号,占空比与采集到的直流信号电压成正比
3、LCD1602显示当前采集到的电压值与输出PWM的占空比
4、DAC0832输出一路0~5V正弦信号,频率值(1~9)/10Hz
5、向上位机传输当前采集到的电压值与PWM占空比
6、由上位机控制输出正弦信号的频率
----------------------------*/
#include<reg52.h>
#include<intrins.h> //包含_nop_()空函数指令的头文件
#include<string.h>
#define uchar unsigned char
#define uint unsigned int
#define out P2
sbit CS=P1^1; //DAC0832片选端
sbit WR1=P1^2; //DAC0832WR1端
sbit PWM=P1^4; //PWM信号输出端
sbit RS = P1^5; //LCD1602寄存器选择
sbit RW = P1^6; //LCD1602读写操作选择
sbit E = P1^7; //LCD1602使能信号
sbit START = P3^3; //ADC0808启动信号
sbit Clock = P3^4; //74HC595移位信号
sbit Data = P3^5; //74HC595数据输出
sbit Lock = P3^6; //74HC595锁存信号
sbit OE=P3^7; ///ADC0808转换结果输出允许端
bit flag_timer=0; //当flag_timer=0时,中断程序T0调用timer[0]~timer[3],change_timer函数重装timer[4]~timer[7],反之调换
bit flag_volt=0; //ADC测量结束标志
bit flag_fre=0; //DAC频率输入标志
uchar code seg[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90};
uchar const code await_f[]="Please enter frequency(n/10Hz):(1~9)\n";
uchar Sin[]="sin:0.00V ",Pwm[]="PWM:00%",measure[20];
uchar send_mode=0;
uchar ac_data=0; //数字量
uint ac_volt=0; //电压值
uchar Fre = 1; //DA正弦频率
uchar timer[8]; //timer[0]=HighTH0,timer[1]=HighTL0
//timer[2]=LowTH0,timer[3]=LowTL0
//timer[4]=HighTH1,timer[5]=HighTL1
//timer[6]=LowTH1,timer[7]=LowTL1
//上述TH0/TL0和TH1/TL1均为计时器T0的预装载值,两组预装载值交替使用,一组使用时另一组进行计算和重装
uchar const code sin_tab[256]={
0x80,0x83,0x86,0x89,0x8C,0x8F,0x92,0x95,0x98,0x9B,
0x9E,0xA1,0xA4,0xA7,0xAA,0xAD,0xB0,0xB3,0xB6,0xB9,
0xBC,0xBE,0xC1,0xC4,0xC6,0xC9,0xCB,0xCE,0xD0,0xD3,
0xD5,0xD7,0xDA,0xDC,0xDE,0xE0,0xE2,0xE4,0xE6,0xE8,
0xE9,0xEB,0xED,0xEE,0xF0,0xF1,0xF3,0xF4,0xF5,0xF6,
0xF8,0xF9,0xF9,0xFA,0xFB,0xFC,0xFD,0xFD,0xFE,0xFE,
0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,
0xFE,0xFD,0xFD,0xFC,0xFB,0xFA,0xFA,0xF9,0xF8,0xF7,
0xF5,0xF4,0xF3,0xF1,0xF0,0xEF,0xED,0xEB,0xEA,0xE8,
0xE6,0xE4,0xE2,0xE0,0xDE,0xDC,0xDA,0xD8,0xD5,0xD3,
0xD1,0xCE,0xCC,0xC9,0xC6,0xC4,0xC1,0xBE,0xBC,0xB9,
0xB6,0xB3,0xB0,0xAE,0xAB,0xA8,0xA5,0xA2,0x9F,0x9C,
0x99,0x95,0x92,0x8F,0x8C,0x89,0x86,0x83,0x80,0x7D,
0x79,0x76,0x73,0x70,0x6D,0x6A,0x67,0x64,0x61,0x5E,
0x5B,0x58,0x55,0x52,0x4F,0x4C,0x49,0x46,0x44,0x41,
0x3E,0x3B,0x39,0x36,0x34,0x31,0x2F,0x2C,0x2A,0x28,
0x26,0x23,0x21,0x1F,0x1D,0x1B,0x19,0x17,0x16,0x14,
0x12,0x11,0x0F,0x0E,0x0C,0x0B,0x0A,0x09,0x08,0x07,
0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x02,
0x02,0x03,0x04,0x05,0x05,0x06,0x07,0x08,0x0A,0x0B,
0x0C,0x0D,0x0F,0x10,0x12,0x14,0x15,0x17,0x19,0x1B,
0x1D,0x1F,0x21,0x23,0x25,0x27,0x2A,0x2C,0x2E,0x31,
0x33,0x36,0x38,0x3B,0x3E,0x40,0x43,0x46,0x49,0x4B,
0x4E,0x51,0x54,0x57,0x5A,0x5D,0x60,0x63,0x66,0x69,
0x6C,0x6F,0x73,0x76,0x79,0x7C};
void change_timer(uchar dc); //刷新计时器预装值函数
void lcd_initial(void); //LCD初始化函数
void check_busy(void); //检查忙标志函数
void write_command(uchar com); //写命令函数
void write_data(uchar dat); //写数据函数
void string(uchar ad,uchar *s); //显示字符串函数
void delay(uint j);
void S_to_P(uchar message); //串转并函数
void send(uchar *s); //串口通信发送函数
void dacSIN(); //正弦波函数
void init_DAC0832(); //DAC0832初始化
void main_initial(); //主函数初始化
void show_LCD();
void main(void)
{
uchar dc,i=0;
main_initial();
while(1)
{
if(flag_volt)
{
dc = ac_data*0.39; //将采集到的二进制数转换成百分比
ac_volt=ac_data*1.96; //将采得的二进制数转换成可读的电压值
change_timer(dc); //依据占空比改变PWM占空比
Sin[4]=ac_volt/100+'0';
Sin[6]=ac_volt/10%10+'0';
Sin[7]=ac_volt%10+'0';
Pwm[4]= dc/10+'0';
Pwm[5]= dc%10+'0';
string(0x84,&Sin[4]); //显示到LCD管上
string(0xc4,&Pwm[4]);
i++;
if(i==5)
{
strcpy(measure,Sin); //发送给上位机
strcat(measure,Pwm);
strcat(measure,"\n");
TI = 1;
i=0;
}
flag_volt = 0;
}
}
}
/* ---------------------------
主函数初始化函数
----------------------------*/
void main_initial()
{
init_DAC0832();
lcd_initial();
string(0x80,Sin); //显示到LCD管上
string(0xc0,Pwm);
TMOD = 0X21; //设置T0、T1的工作方式
CP_RL2=0;EXEN2=0; //设置T2为16位自动重装载定时器工作方式
change_timer(50); //为timer赋初值(占空比50)
flag_timer=!flag_timer;
TH0 = timer[0+flag_timer*4];
TL0 = timer[1+flag_timer*4];
TH1 = 0XFD; //波特率9600
TL1 = 0XFD;
TH2 = RCAP2H = (65536-3906)/256;
TL2 = RCAP2L = (65536-3906)%256;
SCON = 0X50;
PCON = 0X00;
EA = 1; //中断总允许
ES = 1; //收发中断源允许
ET0 = 1; //T0中断允许
ET2 = 1; //T2中断允许
EX0 = 1; //INT0中断允许
IT0 = 1; //INT0中断下跳沿触发
TR0 = 1; //T0计时器开启
TR1 = 1; //T1计时器开启
TR2 = 1; //T2计时器开启
START = 0;
START = 1; //启动模数转换
START = 0;
send_mode=1;
}
/* ---------------------------
PWM占空比预装载函数
----------------------------*/
void change_timer(uchar dc) //对timer[]进行重装
{
uint temp;
static uint freq = 100; //PWM频率为100Hz
temp=65535 - 12000000/12/freq*dc/100;
timer[0+!flag_timer*4]=(uchar)(temp>>8);
timer[1+!flag_timer*4]=(uchar)temp;
temp=65535 - 12000000/12/freq*(100-dc)/100;
timer[2+!flag_timer*4]=(uchar)(temp>>8);
timer[3+!flag_timer*4]=(uchar)temp;
}
/* ---------------------------
PWM生成函数
----------------------------*/
void TO_Interrupt() interrupt 1 //生成PWM波
{
if(PWM==0)
{
PWM = 1;
flag_timer=!flag_timer; //交换timer[]读写区域
TH0 = timer[0+flag_timer*4];
TL0 = timer[1+flag_timer*4];
START = 1;
START = 0; //PWM上升沿启动模数转换
}
else
{
PWM = 0;
TH0 = timer[2+flag_timer*4];
TL0 = timer[3+flag_timer*4];
}
}
/* ---------------------------
ADC0808电压采集函数
----------------------------*/
void INTO_Interrupt() interrupt 0 //采集电压
{
OE = 1;
ac_data = P0;
flag_volt = 1;
OE = 0;
}
/* ---------------------------
DAC0832正弦信号生成函数
----------------------------*/
void T2_INT(void) interrupt 5
{
TF2 = 0; //复位中断标志位TF2
dacSIN();
}
void dacSIN() //正弦波函数
{
static uchar i=0;
S_to_P(sin_tab[i]);
i++; //自动溢出
}
void S_to_P(uchar message) //串转并
{
uchar i,temp = 0x80;
for(i = 0;i<8;i++)
{
if( message&(temp>>i) ) //按位分解
Data = 1;
else
Data = 0;
Clock = 0;
Clock = 1; //移位
}
Lock = 0;
Lock = 1; //锁存
}
void init_DAC0832() //DAC0832初始化
{
CS=0;
WR1=0;
}
/* ---------------------------
上下位机通信函数
----------------------------*/
void TRX_Interrupt() interrupt 4 //串口中断
{
static uchar temp=0;
if(RI) //接收程序
{
RI = 0;
temp = SBUF;
if((temp>='0')&&(temp<='9'))
{
if(!flag_fre)
{
send_mode=temp-'0';
if(send_mode==3)
flag_fre=1;
}
else
{
Fre=temp-'0';
RCAP2H=(65536-1000000/256/Fre*10)/256;
RCAP2L=(65536-1000000/256/Fre*10)%256;
flag_fre=0;
send_mode=2;
}
}
}
if(TI) //发送程序
{
TI = 0;
if(send_mode==2)
send(measure);
else if(send_mode==3)
send(await_f);
}
}
void send(uchar *s) //串口发送函数
{
static uchar i=0;
if(*(s+i))
{
SBUF=*(s+i);
i++;
}
else //当一个字符串发送完毕后会进入这里
{
i=0;
if(send_mode==2);
else
send_mode=0;
}
}
/* ---------------------------
LCD1602显示函数
----------------------------*/
void delay(uint j) //延时函数
{
uchar i = 250;
for(;j>0;j--)
{
while(--i);
i=249;
while(--i);
i=250;
}
}
void check_busy(void) //检查忙标志函数
{
uchar dt;
do
{
dt = 0xff;
E = 0;
RS = 0;
RW = 1;
E = 1;
dt = out;
}while(dt&0x80);
E = 0;
}
void write_command(uchar com) //写命令函数
{
check_busy();
E = 0;
RS = 0;
RW = 0;
out = com;
E = 1;
_nop_();
E = 0;
delay(1);
}
void write_data(uchar dat) //写显示数据函数
{
check_busy();
E = 0;
RS = 1;
RW = 0;
out = dat;
E = 1;
_nop_();
E = 0;
}
void lcd_initial(void) //LCD初始化函数
{
write_command(0x38); //写入命令0x38:8位两行显示,5*7点阵字符
write_command(0x0c); //写入命令0xc:开整体显示,光标关,无闪烁
write_command(0x06); //写入命令0x06:光标右移
write_command(0x01); //写入命令0x01:清屏
delay(1);
}
void string (uchar ad,uchar *s) //输出显示字符串的函数
{
write_command(ad);
while(*s>0)
{
write_data(*s++);
}
}