寫在前面
承接前文的模擬部分,這次開始寫下單片機部分的仿真程序設計,本文介紹C52單片機的設置,後面將會介紹MSP430F249的具體配置。
題目
基礎部分
- 搭建 DC-AC 電路以及檢測電路。
- 調整系統的參數,使得輸出的交流電的頻率爲 20Hz。
- 測量輸出交流電的頻率並顯示。
發揮部分
- 在基礎部分 3 的基礎上,將測量到的頻率數據通過串口發送給另外一個單片
機 2 系統,並且顯示出來。 - 將此電源系統擴展爲三相交流電源逆變電路,並在示波器上顯示輸出波形。
單片機部分
C52 MSP430整體程序思路介紹
本次方案採取使用 C52 單片機作爲程序部分實現的主控,一共使用了兩塊單片機 C52,一塊進行測頻然後進行串口發送,另一塊作爲串口接受機,兩塊 89C52 單片機均採用LCD12864 顯示,顯示出測量的頻率,精確到 2 位小數。
MSP430道理相同。一塊進行測頻然後進行串口發送,另一塊作爲串口接受機,採用的顯示方式不同這是oled屏幕。
程序框圖
結果展示
直接貼代碼吧這裏我進行了頭文件的劃分,分成了小模塊方便移植調用,這裏只給出完整的C文件,關於頭文件自行定義吧,後面附上下載連接
C52-無字庫12864仿真頻率串口發送接收.zip
C52代碼測頻+串口發送機
這裏需要使用三個定時器,也就是C51滿足不了性能要求,我們只能進行C52進行操作。這裏的晶體震盪頻率爲11.0592MHz,12M的滿足不了波特率時鐘,誤差較大,大家操作請注意。
lcd.c
#include <reg52.h>
#include "DataType.h"
#include "lcd12864.h"
//延時
void delay(uint n)
{
uint i;
for(;n>0;n--)
for(i=200;i>0;i--);
}
//判斷是否忙
void check()
{
rs=0;
rw=1; //讀 e=1;
port=0x00;
e=1;
while(busy);
e=0;
}
//寫指令
void sendcommand(uchar command)
{
check();
rs=0; //指令
rw=0; //寫 e=0;
port=command;
e=1;
e=0; //寫入指令
}
//寫數據
void writedata(uchar dat)
{
check();
rs=1; //數據
rw=0;
port=dat;
e=1;
e=0;
}
//選屏幕 0--全屏,1--左屏,2--右屏;
void select(uint n)
{
switch(n)
{
case 0:cs1=0;cs2=0;break; //低電平選中
case 1:cs1=0;cs2=1;break; //cs1左屏
case 2:cs1=1;cs2=0;break; //cs2右屏
}
}
//頁
void setpage(uchar page)
{
page=page&0x07;
page=page|0xb8;
sendcommand(page);
}
//列
void setcolumn(uchar column)
{
column=column&0x3f;
column=column|0x40;
sendcommand(column);
}
//起始行
void setline(uchar line)
{
line=line&0x3f;
line=line|0xc0;
sendcommand(line);
}
//屏幕開關顯示 0--關,1--開;
void seton(uint n)
{
n=n|0x3e;
sendcommand(n);
}
//清屏 0--全屏,1--左屏,2--右屏;
void clear(uint n)
{
uchar i,j;
select(n);
for(i=0;i<8;i++)
{
setpage(i);
setcolumn(0);
for(j=0;j<64;j++)
writedata(0); //置0清空
}
}
//初始化
void init(uchar i)
{
check();
seton(1);
select(0);
//clear(0);
setline(i);
}
//顯示漢字 16*16顯示
void show16(uchar page,uchar column,uchar screen,uchar method,uchar *str) //頁,列,
{
uchar i,j;
select(screen);
j=0;
setpage(page);
setcolumn(column);
for(i=0;i<16;i++)
{ if(method==1) writedata(~str[j++]); //method爲顯示方式。當等於1時,反白。
else writedata(str[j++]);
}
setpage(page+1);
setcolumn(column);
for(i=0;i<16;i++)
{ if(method==1) writedata(~str[j++]);
else writedata(str[j++]);
}
}
//顯示數字 8*16顯示
void show8(uchar page,uchar column,uchar screen,uchar method,uchar *str)
{
uchar i,j;
select(screen);j=0;
setpage(page);
setcolumn(column);
for(i=0;i<8;i++)
{ if(method==1) writedata(~str[j++]);
else writedata(str[j++]);
}
setpage(page+1);
setcolumn(column);
for(i=0;i<8;i++)
{ if(method==1) writedata(~str[j++]);
else writedata(str[j++]);
}
}
timer.c
#include <reg52.h>
#include "DataType.h"
#include "timer.h"
sfr T2MOD = 0xc9;
void init_timer()
{
TMOD |= 0X01; //設置爲定時器計數器模式,定時器計數器0爲定時模式
//配置定時器0
TL0 = 0x00; //設置定時初值
TH0 = 0xB8; //設置定時初值
TR0 = 1 ;
ET0 = 1 ;
//設置爲定時器計數器2爲脈衝計數模式
T2CON=0x06; //0000,0110: TR2=1,C/T2=1
T2MOD=0x00; //0000,0000: 加計數,
TH2=0x00; //給定時器T2賦初值
TL2=0x00;
ET2 = 1 ;
EA = 1;//開總中斷
}
main.c
#include <reg52.h>
#include "DataType.h"
#include "lcd12864.h"
#include "timer.h"
#include "zk.h"
#include "uart.h"
#include "stdio.h"
int i = 0 , t = 0 ,j=0; //計數器溢出的存儲變量
ulint frency; //頻率值
ulint frency1; //頻率值
uint d;
uchar tmp1[4];
void calculate() ; //計算頻率模塊
/**************************
顯示頻率函數
**************************/
void dispaly_f1()
{
int show1[6] ;
int six_number1 = frency1/100000 ;
int five_number1 = frency1/10000%10;
int four_number1 = frency1/1000%10 ;
int three_number1= frency1/100%10 ;
int two_number1 = frency1/10%10 ;
int one_number1 = frency1%10 ;
show1[5] = one_number1;
show1[4] = two_number1;
show1[3] = three_number1 ;
show1[2] = four_number1 ;
show1[1] = five_number1 ;
show1[0] = six_number1 ;
show16(4,0,1,0,hz[2]); //寫數據
show16(4,16,1,0,hz[3]); //寫數據
show8(4,32,1,0,sign[0]); //寫數據
show8(4,40,1,0,num[show1[0]]); //寫數據
show8(4,48,1,0,num[show1[1]]); //寫數據
show8(4,56,1,0,num[show1[2]]); //寫數據
show8(4,0,2,0,num[show1[3]]); //寫數據
show8(4,8,2,0,num[show1[4]]); //寫數據
show8(4,16,2,0,num[show1[5]]); //寫數據
show8(4,24,2,0,sign[1]); //寫數據
show8(4,32,2,0,sign[2]); //寫數據
/**/
}
void delayms(uint n)
{
uchar i;
while(n--)
{
for(i = 0;i < 120;i++);
}
}
void main()
{
init_timer();
init_uart();
clear(0);
show16(0,0,1,0,hz[5]); //寫數據
show16(0,16,1,0,hz[6]); //寫數據
show16(0,32,1,0,hz[7]); //寫數據
show16(0,48,1,0,hz[8]); //寫數據
show16(0,0,2,0,hz[9]); //寫數據
delayms(200);
//send("Receiving from ...\r\n"); 測試
//delayms(200);
while(1)
{
dispaly_f1();
sprintf((char *)tmp1,"%4.0lu",frency1);//僅僅發送數據
send(tmp1);//
delayms(50);
}
}
void time0() interrupt 1
{
TL0 = 0x00; //設置定時初值
TH0 = 0xB8; //設置定時初值
if( ++t >= 50 ) { //使用多次中斷實現1s的延時
TR2 = 0 ; //爲了計數的準確性,在計算的時候將計數器1關閉
calculate() ;
t = 0 ;
TH2 = 0 ; //清空計數器中的值,防止對下一個週期計數產生影響
TL2 = 0 ;
TR2 = 1 ; //最後打開中斷
}
}
void time2() interrupt 5
{
j++ ;
}
void calculate()
{
frency1 = j*65535 + TH2*256 + TL2 ;
//i = 0 ;
j = 0 ; //將溢出的次數清零,爲下一次計數做準備
}
uart.c
#include <reg52.h> //這裏的晶體震盪頻率爲11.0592MHz
#include "DataType.h"
#include "uart.h"
void init_uart()
{
//配置定時器1
TMOD |= 0x20; //模式2 8位自動重載模式 溢出時,將TH1裝入TL1
TH1 = 0xfd; //波特率:9600
TL1 = TH1;
PCON = 0x00;
SCON = 0x50; //方式1(定時器1溢出率)允許接收
TR1=1;//開定時器1中斷
}
void send(uchar *c)
{
while(*c != '\0')
{
SBUF=*c;
c++;
while(TI==0);
TI=0;
//delay(5);
}
}
字庫頭文件
對於proteus的顯示仿真,字庫需要自己構建,這裏給出我用的這個
#ifndef __ZK_H
#define __ZK_H
#include "DataType.h"
extern uchar code hz[][32]={
{0x00,0x00,0xF8,0x88,0x88,0x88,0x88,0xFF,0x88,0x88,0x88,0x88,0xF8,0x00,0x00,0x00,
0x00,0x00,0x1F,0x08,0x08,0x08,0x08,0x7F,0x88,0x88,0x88,0x88,0x9F,0x80,0xF0,0x00},/*"電",0*/
{0x00,0x00,0xFE,0x02,0x82,0x82,0x82,0x82,0xFA,0x82,0x82,0x82,0x82,0x82,0x02,0x00,
0x80,0x60,0x1F,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x44,0x58,0x40,0x40,0x00},/*"壓",1*/
{0x40,0x7C,0x40,0x7F,0x48,0x48,0x40,0xF2,0x12,0x1A,0xD6,0x12,0x12,0xF2,0x02,0x00,
0x90,0x8E,0x40,0x4F,0x20,0x1E,0x80,0x4F,0x20,0x18,0x07,0x10,0x20,0x4F,0x80,0x00},/*"頻",2*/
{0x00,0x14,0xA4,0x44,0x24,0x34,0xAD,0x66,0x24,0x94,0x04,0x44,0xA4,0x14,0x00,0x00,
0x08,0x09,0x08,0x08,0x09,0x09,0x09,0xFD,0x09,0x09,0x0B,0x08,0x08,0x09,0x08,0x00},/*"率",3*/
{0x10,0x60,0x02,0x8C,0x00,0x44,0x64,0x54,0x4D,0x46,0x44,0x54,0x64,0xC4,0x04,0x00,
0x04,0x04,0x7E,0x01,0x80,0x40,0x3E,0x00,0x00,0xFE,0x00,0x00,0x7E,0x80,0xE0,0x00},/*"流",4*/
{0x00,0x00,0x3C,0x24,0x24,0x24,0x24,0xFF,0x24,0x24,0x24,0x24,0x3C,0x00,0x00,0x00,
0x00,0x1F,0x09,0x09,0x09,0x09,0x09,0xFF,0x09,0x09,0x09,0x09,0x09,0x1F,0x00,0x00},/*"串",2*/
{0x00,0x00,0xFC,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0xFC,0x00,0x00,0x00,
0x00,0x00,0x7F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7F,0x00,0x00,0x00},/*"口",3*/
{0x00,0x00,0x18,0x16,0x10,0xD0,0xB8,0x97,0x90,0x90,0x90,0x92,0x94,0x10,0x00,0x00,
0x00,0x20,0x10,0x8C,0x83,0x80,0x41,0x46,0x28,0x10,0x28,0x44,0x43,0x80,0x80,0x00},/*"發",4*/
{0x40,0x40,0x42,0xCC,0x00,0x88,0x89,0x8E,0x88,0xF8,0x88,0x8C,0x8B,0x88,0x80,0x00,
0x00,0x40,0x20,0x1F,0x20,0x40,0x50,0x48,0x46,0x41,0x42,0x44,0x58,0x40,0x40,0x00},/*"送",5*/
{0x10,0x10,0xD0,0xFF,0x90,0x10,0x00,0xFE,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,
0x04,0x03,0x00,0xFF,0x00,0x83,0x60,0x1F,0x00,0x00,0x00,0x3F,0x40,0x40,0x78,0x00},/*"機",6*/
};
extern uchar code num[][16]={
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},
//"0",0
{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},
//"1",1
{0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},
//"2",2
{0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},
//"3",3
{0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},
//"4",4
{0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},
//"5",5
{0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},
//"6",6
{0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},
//"7",7
{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},
//"8",8
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00} //"9",9
};
extern uchar code sign[][16]={
{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},/*":",0*/
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},/*"H",1*/
{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},/*"Z",2*/
{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x01,0x3E,0x01,0x3F,0x20,0x00},/*"M",3*/
{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},/*"V",4*/
{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},/*"m",5*/
{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x03,0x0C,0x30,0x0C,0x03,0x00,0x00},/*"v",6*/
};
#endif
DATATYPE_H
#ifndef __DATATYPE_H
#define __DATATYPE_H
#define uint unsigned int
#define uchar unsigned char
#define ushort unsigned short
#define ulint unsigned long int
#define ldouble long double
#endif
C52代碼串口接收機
lcd.c
同剛剛
uart.c
#include <reg52.h> //這裏的晶體震盪頻率爲11.0592MHz
#include "DataType.h"
#include "uart.h"
void init_uart()
{
//配置定時器1
TMOD |= 0x20; //模式2 8位自動重載模式 溢出時,將TH1裝入TL1
TH1 = 0xfd; //波特率:9600
TL1 = TH1;
PCON = 0x00;
SCON = 0x50; //方式1(定時器1溢出率)允許接收
TR1=1;//開定時器1中斷
EA = 1;//開總中斷
ES = 1; //開串口接收中斷
}
//發送
void send(uchar *c)
{
while(*c != '\0')
{
SBUF=*c;
c++;
while(TI==0);
TI=0;
}
}
main.c
#include <reg52.h>
#include "DataType.h"
#include "lcd12864.h"
#include "zk.h"
#include "uart.h"
#include "stdio.h"
#include "string.h"
uint k=0;
uchar Read_buff[4]; //設置接收字符串緩存
uint buff_size = 5; //設置緩存大小
uint dat_count; //單次接收數據總數
uint buff_lenght; //計算接收數據長
void delayms(uint n)
{
uchar i;
while(n--)
{
for(i = 0;i < 120;i++);
}
}
//字符發送函數
void putchar1(unsigned char data1)
{
SBUF = data1; //將待發送的字符送入發送緩衝器
while(TI == 0); //等待發送完成
TI = 0; //發送中斷標誌請0
}
//字符串發送函數
void putstring(unsigned char *dat)
{
while(*dat != '\0') //判斷字符串是否發送完畢
{
putchar1(*dat); //發送單個字符
dat++; //字符地址加1,指向先下一個字符
//delay(5);
}
}
void main()
{
//init_uart();
SCON = 0x50; //串口方式1 ,允許接收
TMOD = 0x20; //T1工作於方式2
PCON = 0x00; //波特率不倍增
TL1 = 0xfd; //波特率設置
TH1 = 0xfd; //
EA = 1; //開總中斷
ES = 1; //開串口接收中斷
//TI = 0;
TR1 = 1; //定時器開啓
delay(200);
putstring("Receiving from 8051...\r\n"); //串口向終端發送字符串,結尾處回車換行
putstring("----------------------\r\n");
delay(50);
clear(0);
//界面
show16(0,0,1,0,hz[5]); //寫數據
show16(0,16,1,0,hz[6]); //寫數據
show16(0,32,1,0,hz[10]); //寫數據
show16(0,48,1,0,hz[11]); //寫數據
show16(0,0,2,0,hz[9]); //寫數據
show16(4,0,1,0,hz[2]); //寫數據
show16(4,16,1,0,hz[3]); //寫數據
show8(4,32,1,0,sign[0]); //寫數據
delayms(100);
//send("Receiving from ...\r\n"); 測試
//delayms(200);
while(1)
{
show16(4,0,1,0,hz[2]); //寫數據
show16(4,16,1,0,hz[3]); //寫數據
show8(4,32,1,0,sign[0]); //寫數據
show8(4,24,2,0,sign[1]); //寫數據
show8(4,32,2,0,sign[2]); //寫數據
if(dat_count==4){
dat_count=0;
show8(4,40,1,0,num[Read_buff[0]-'0']); //寫數據
show8(4,48,1,0,num[Read_buff[1]-'0']); //寫數據
show8(4,56,1,0,num[Read_buff[2]-'0']); //寫數據
show8(4,0,2,0,num[Read_buff[3]-'0']); //寫數據
ES= 0;
putstring(Read_buff);
ES=1;
}
}
}
/*
void Usart() interrupt 4
{
uchar receiveData;
receiveData=SBUF;//出去接收到的數據
Receive(receiveData);
RI = 0;//清除接收中斷標誌位
SBUF=receiveData;//將接收到的數據放入到發送寄存器
while(!TI);//等待發送數據完成
TI=0;//清除發送完成標誌位
}
*/
void Receive(char x)
{
Read_buff[k]=x;
k++;
if(k==4){
k=0;
show8(4,40,1,0,num[Read_buff[3]-'0']); //寫數據
show8(4,48,1,0,num[Read_buff[2]-'0']); //寫數據
show8(4,56,1,0,num[Read_buff[1]-'0']); //寫數據
show8(4,0,2,0,num[Read_buff[0]-'0']); //寫數據
ES= 0;
putstring(Read_buff);
ES=1;
}
}
/*
void revdata(void) interrupt 4
{
unsigned char temp;
if(RI == 0) return; //如果沒有接收中斷標誌,返回
ES = 0; //關閉串口中斷
RI = 0; //清串行中斷標誌位
temp = SBUF; //接收緩衝器中的字符
Receive(temp);
ES = 1; //開啓串口中斷
}
*/
/*中斷函數,中斷函數完全用作了接收函數*/
void serial()interrupt 4
{
uchar temp;uint i;
if(RI == 1)
{
temp = SBUF;
RI = 0;
if(dat_count < buff_size && temp != '\0') //判斷接收結束
{
if(dat_count == 0)
for(i = 0; i < buff_size;i ++) //清空接收緩存
Read_buff[i] = ' ';
if(temp==' ')
Read_buff[dat_count] = '0';
else
Read_buff[dat_count] = temp; //將數據存入存儲
dat_count ++;
buff_lenght = dat_count; //獲取接收字符串長度
}
else
{
dat_count = 0;
}
}
}
工程鏈接
包括C52測頻串口發送機、串口接收機以及proteus8.6仿真,上文會操作的建議自己來試試,不會的可以下載工程,修改參考,時間比較緊,製作可能有些部分不太合理,歡迎大家指教
工程鏈接