Proteus仿真—51單片機實現AC信號測頻、顯示、雙機通信

在Proteus仿真軟件裏面使用STC89C52實現指定頻率的AC信號的測頻、顯示、雙機通信。

一.原理圖部分

整體的電路圖如示:
在這裏插入圖片描述
DC-AC電路部分的輸出就使用信號源直接模擬了。
原理圖如下:
在這裏插入圖片描述

運行結果如下:
在這裏插入圖片描述

可以準確測量出結果,並在LCD上顯示,單片機1測量到頻率後通過串口發送給單片機2,然後在單片機2的LCD上再次顯示。

二.源碼部分

單片機1


/*單片機1功能:
 *1.測量AC的頻率20Hz,T0檢測上升沿、T1進行計時
 *2.LED顯示實時測量頻率
 *3.與單片機2串口通信,T2提供波特率
 *晶振頻率:11.0592MHz
 */
 
#include <reg52.h>
#include <stdio.h>
#include <intrins.h>
#include <string.h>

#define uint unsigned int
#define uchar unsigned char


sfr T2MOD=0XC9;        //寄存器T2MOD定義





/********************* LCD驅動 ************************************/
/* LCD引腳說明 */
sbit RS = P2^0;
sbit RW = P2^1;
sbit EN = P2^2;


/*說明:延時函數
 *參數:延時的ms數
 */
void DelayMS(uint ms)
{
 	uchar i;
	while(ms--)
	{
	 	for(i=0;i<120;i++);
	}
}


/*說明:LCD讀
 *參數:返回讀取的值
 */
uchar Read_LCD_State()
{
 	uchar state;
	RS=0;
	RW=1;
	EN=1;
	DelayMS(1);
	state=P0;
	EN = 0;
	DelayMS(1);
	return state;
}


/*說明:LCD忙判斷
 *參數:無
 */
void LCD_Busy_Wait()
{
 	while((Read_LCD_State()&0x80)==0x80);
	DelayMS(5);
}


/*說明:向LCD寫1Byte數據
 *參數:寫入的數據
 */
void Write_LCD_Data(uchar dat)
{
 	LCD_Busy_Wait();
	RS=1;
	RW=0;
	EN=0;
	P0=dat;
	EN=1;
	DelayMS(1);
	EN=0;	
}


/*說明:LCD寫命令
 *參數:cmd:寫入的命令
 */
void Write_LCD_Command(uchar cmd)
{
 	LCD_Busy_Wait();
	RS=0;
	RW=0;
	EN=0;
	P0=cmd;
	EN=1;
	DelayMS(1);
	EN=0;	
}


/*說明:LCD初始化
 *參數:無
 */
void Init_LCD()
{
 	Write_LCD_Command(0x38);
	DelayMS(1);	
	Write_LCD_Command(0x01);
	DelayMS(1);	
	Write_LCD_Command(0x06);
	DelayMS(1);	
	Write_LCD_Command(0x0c);
	DelayMS(1);	
}


/*說明:LCD定位顯示
 *參數:0X00第一行  0X40第二行
 */
void Set_LCD_POS(uchar p)
{
 	Write_LCD_Command(p|0x80);	
}
 
 
/*說明:LCD顯示函數
 *參數:p:位置信息
 *     *s:顯示的字符串(一行只能顯示16個字符)
 */
void Display_LCD_String(uchar p,uchar *s)
{
 	uchar i;
	Set_LCD_POS(p);
	for(i=0;i<16;i++)
	{
		/* 寫數據 */
		Write_LCD_Data(s[i]);
		DelayMS(1); 	
	}
}







/******************************* 測頻驅動 *************************************************/
long uint pulse=0;   //T0計數的方波的個數;
long uint freq=0;    //輸入信號的頻率
uchar temp=0;        //臨時變量


/*說明:定時器/計數器初始化函數
 *T0的工作方式爲16位計數器模式,用來記錄上升沿 
 *T1的工作方式爲16位定時器模式,用來計時
 *
 *參數:無
 */
void Time_Init()
{
	EA=1;         //打開全局中斷
	TMOD |= 0x15; //T0:16位計數器模式  T1:16位定時器模式
		
	TH0 = 0xff;   //計數器0的初值
	TL0 = 0xff;   //計數器0的初值
	ET0=1;        //允許計數器0中斷
	TR0=1;        //打開計數器0,開始計數

	TH1 = 0x4b;   //定時器1的初值
	TL1 = 0xff;   //定時器1的初值
	ET1=1;	      //允許定時器1中斷
	TR1 = 1;      //打開定時器1,開始計數		
}


/*說明:T0的中斷服務函數,用來計數脈衝數,每一個方波脈衝,計數器進入中斷,脈衝數+1
 *參數:無
 */
void ISQ_timer0(void) interrupt 1
{
	TR0 = 0;
	pulse++;
	TH0  = 0xff;
	TL0  = 0xff;
	TR0 = 1;
}


/*說明:T1的中斷服務函數,每次中斷代表50ms,測量20次中斷(1s)的脈衝數就是頻率
 *參數:無
 */
void ISQ_time1() interrupt 3  
{
	TH1  = 0x4b;    //11.0592MHz,50ms定時的重裝值
	TL1  = 0xff;
	temp++;
	if(temp==20)
	{
		TR0 = 0;    //進入處理時,關閉
		TR1 = 0;
		temp=0;
		freq=pulse;
		pulse=0;    //將脈衝數清零,重新計數
		TR0 = 1;    //處理完畢,打開
		TR1 = 1;    			
	}	
}



/************************************ T2uart *************************************************/

/*說明:uart配置,T2作爲波特率發生定時器
 *參數:無
 */
void uart_config(void)
{	

	T2MOD = 0x01; 	//自動重載
	T2CON = 0x30; 	//T2用做發送接收時鐘
	TH2 = 0xFF;  	//9600波特率,11.0592Mhz晶振
	TL2 = 0xDC; 
	RCAP2H = 0xFF; 
	RCAP2L = 0xDC; 
	SCON = 0x50; 	//串口方式1,1個起始位,1個停止位,8位數據,可變波特率
	PCON = 0X00;	//波特率不加倍
 	TR2 = 1;  		//啓動T2 
	ES = 1;         //開串口中斷
  	EA = 1;         //開總中斷
}


/*說明:uart發送一個字符
 *參數:byte:發送的字符
 */
void uart_send_byte( uchar byte )
{
	ES=0;
	SBUF=byte;
	while(!TI);
	TI=0;
	ES=1;
}


/*說明:uart發送字符串,固定長度爲16*uchar
 *參數:*str:發送的字符串首地址
 */
void uart_send_str( uchar *str )
{
	uint i=0;
	for( i=0;i<16;i++ )
	{
		uart_send_byte(str[i]);

	}
}




void main()
{
 	uchar temp[16];      //定義字符顯示緩存數組
	
    Time_Init();       //T0、T1初始化
	uart_config();     //uart初始化,主要是T2初始化波特率
 	Init_LCD();        //LCD初始化
    
 	while(1)
	{ 

		sprintf(temp,"FREQ:%08.0fHz",(float)freq);
	    Display_LCD_String(0X40,temp);
		
		uart_send_str(temp);
	}
}

單片機2

本來單片機2還負責給DC-AC電路產生控制信號的,在這裏我忽略了DC-AC電路部分,所以控制信號的代碼可有可無。

/*單片機2功能:
 *1.與單片機1進行串口通訊,接收測量的AC頻率
 *2.LED顯示實時單片機1傳回的測量頻率
 *3.爲DC-AC逆置電路提供信號
 *晶振頻率:11.0592MHz
 */
#include <reg52.h>
#include <stdio.h>
#include <intrins.h>
#include <string.h>


#define uint unsigned int
#define uchar unsigned char
#define  uint8_t uint


/********************* 控制信號發生 ************************************/
//爲DC-AC電路提供穩定的20Hz脈衝信號
sbit O1_4 = P2^3 ;
sbit O2_3 = P2^4;

sbit G1=P1^2;
sbit G2=P1^3;
sbit G3=P1^4;
sbit G4=P1^5;
sbit G5=P1^6;
sbit G6=P1^7;
unsigned int time1;
unsigned int time2;


/*說明:T0初始化,利用T0輸出PWM脈衝信號
 *參數:無
 */
void Timer0_Init(void)		//1毫秒@11.0592
{
	TMOD |= 0x01;
	TH0 = 0xfc;
	TL0 = 0x66;
	TF0 = 0;
	EA = 1;
	TR0 = 1;
	ET0 = 1;	

}


/*說明:T0中斷服務函數,產生PWM脈衝信號
 *參數:無
 */
void time0() interrupt 1
{
TH0=0xfc;
TL0=0x66;
 time1++;
 time2++;
 /* 單相 */
if((time1>=0)&&(time1<=25)){O1_4=1;O2_3=0;}
if((time1>25)&&(time1<=50)){O1_4=0;O2_3=1;}
if(time1>50)time1=0;

/* 三相 */
if(time2>0&&time2<=8){G1=1;G2=0;G3=0;G4=0;G5=1;G6=1;}
if(time2>8&&time2<=16){G1=1;G2=1;G3=0;G4=0;G5=0;G6=1;}
if(time2>16&&time2<=24){G1=1;G2=1;G3=1;G4=0;G5=0;G6=0;}
if(time2>24&&time2<=32){G1=0;G2=1;G3=1;G4=1;G5=0;G6=0;}
if(time2>32&&time2<=40){G1=0;G2=0;G3=1;G4=1;G5=1;G6=0;}
if(time2>40&&time2<=48){G1=0;G2=0;G3=0;G4=1;G5=1;G6=1;}
if(time2>48)time2=0;


}//50ms的週期


/********************* LCD驅動 ************************************/
/* LCD引腳 */
sbit RS = P2^0;
sbit RW = P2^1;
sbit EN = P2^2;

/*說明:延時函數
 *參數:延時的ms數
 */
void DelayMS(uint ms)
{
 	uchar i;
	while(ms--)
	{
	 	for(i=0;i<120;i++);
	}
}

/*說明:LCD讀
 *參數:返回讀取的值
 */
uchar Read_LCD_State()
{
 	uchar state;
	RS=0;
	RW=1;
	EN=1;
	DelayMS(1);
	state=P0;
	EN = 0;
	DelayMS(1);
	return state;
}

/*說明:LCD忙判斷
 *參數:無
 */
void LCD_Busy_Wait()
{
 	while((Read_LCD_State()&0x80)==0x80);
	DelayMS(5);
}

/*說明:向LCD寫1Byte數據
 *參數:寫入的數據
 */
void Write_LCD_Data(uchar dat)
{
 	LCD_Busy_Wait();
	RS=1;
	RW=0;
	EN=0;
	P0=dat;
	EN=1;
	DelayMS(1);
	EN=0;	
}

/*說明:LCD寫命令
 *參數:cmd:寫入的命令
 */
void Write_LCD_Command(uchar cmd)
{
 	LCD_Busy_Wait();
	RS=0;
	RW=0;
	EN=0;
	P0=cmd;
	EN=1;
	DelayMS(1);
	EN=0;	
}

/*說明:LCD初始化
 *參數:無
 */
void Init_LCD()
{
 	Write_LCD_Command(0x38);
	DelayMS(1);	
	Write_LCD_Command(0x01);
	DelayMS(1);	
	Write_LCD_Command(0x06);
	DelayMS(1);	
	Write_LCD_Command(0x0c);
	DelayMS(1);	
}

/*說明:LCD定位顯示
 *參數:0X00第一行  0X40第二行
 */
void Set_LCD_POS(uchar p)
{
 	Write_LCD_Command(p|0x80);	
}

/*說明:LCD顯示函數
 *參數:p:位置信息
 *     *s:顯示的字符串(一行只能顯示16個字符)
 */
void Display_LCD_String(uchar p,uchar *s)
{
 	uchar i;
	Set_LCD_POS(p);
	for(i=0;i<16;i++)
	{
		/* 寫數據 */
		Write_LCD_Data(s[i]);
		DelayMS(1); 	
	}
}


/************************************ T1uart *************************************************/

/*說明:uart初始化函數,T1爲uart提供9600波特率
 *參數:無
 */
void uart_config(void)
{
    TMOD |= 0x20;  //設置定時器1的工作方式2---8位自動裝填
    SCON = 0x50;
    TH1 = 0xfd;   //設置初始值:使比特率爲9600bps
    TL1 = 0xfd;
    PCON = 0x00;  //SMOD=0,不加倍
    EA = 1;       //打開總中斷
    ES = 1;       //打開串口通訊中斷
    TR1 = 1;      //打開定時器中斷開關
}


uchar Rx_Str[16]={0};  //接收緩存
uint state;            //接收狀態:1:正在接收   0:接收完畢
volatile uint i=0;
/*說明:uart中斷處理函數,進行解析數據幀
 *參數:無
 */
void uart_receive() interrupt 4 
{
	uchar num;
	if(1==RI)
	{
		RI=0;      //清除標誌位
		num=SBUF;  //從計算機接收數據,賦給num
		if( num == 'F' )  //解析幀頭
		{
			state = 1;
		}
		if( Rx_Str[i-1] == 'z' )  //解析幀尾
		{
			state = 0;
			i=0;
		}
	
		if( 1 == state )        //緩存數據
		{
			Rx_Str[i++] = num;
		}
	
	}
}



void main()
{
	Timer0_Init();  //T0初始化,產生脈衝信號
	uart_config();  //uart初始化,接收串口數據
 	Init_LCD();     //LCD初始化,顯示接收的數據幀
		
 	while(1)
	{ 
		Display_LCD_String(0X40,Rx_Str);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章