写在前面
承接前文的模拟部分,这次开始写下单片机部分的仿真程序设计,本文介绍C52单片机的设置,后面将会介绍MSP430F249的具体配置。
题目
基础部分
- 搭建 DC-AC 电路以及检测电路。
- 调整系统的参数,使得输出的交流电的频率为 20Hz。
- 测量输出交流电的频率并显示。
发挥部分
- 在基础部分 3 的基础上,将测量到的频率数据通过串口发送给另外一个单片
机 2 系统,并且显示出来。 - 将此电源系统扩展为三相交流电源逆变电路,并在示波器上显示输出波形。
单片机部分
C52 MSP430整体程序思路介绍
本次方案采取使用 C52 单片机作为程序部分实现的主控,一共使用了两块单片机 C52,一块进行测频然后进行串口发送,另一块作为串口接受机,两块 89C52 单片机均采用LCD12864 显示,显示出测量的频率,精确到 2 位小数。
MSP430道理相同。一块进行测频然后进行串口发送,另一块作为串口接受机,采用的显示方式不同这是oled屏幕。
程序框图
结果展示
直接贴代码吧这里我进行了头文件的划分,分成了小模块方便移植调用,这里只给出完整的C文件,关于头文件自行定义吧,后面附上下载连接
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仿真,上文会操作的建议自己来试试,不会的可以下载工程,修改参考,时间比较紧,制作可能有些部分不太合理,欢迎大家指教