大家好,又见面了,我是你们的朋友全栈君。
承接前文的模拟部分,这次开始写下单片机部分的仿真程序设计,本文介绍C52单片机的设置,后面将会介绍MSP430F249的具体配置。
本次方案采取使用 C52 单片机作为程序部分实现的主控,一共使用了两块单片机 C52,一块进行测频然后进行串口发送,另一块作为串口接受机,两块 89C52 单片机均采用LCD12864 显示,显示出测量的频率,精确到 2 位小数。 MSP430道理相同。一块进行测频然后进行串口发送,另一块作为串口接受机,采用的显示方式不同这是oled屏幕。
直接贴代码吧这里我进行了头文件的划分,分成了小模块方便移植调用,这里只给出完整的C文件,关于头文件自行定义吧,后面附上下载连接 C52-无字库12864仿真频率串口发送接收.zip
这里需要使用三个定时器,也就是C51满足不了性能要求,我们只能进行C52进行操作。这里的晶体震荡频率为11.0592MHz,12M的满足不了波特率时钟,误差较大,大家操作请注意。
#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++]);
}
}
#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;//开总中断
}
#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 ; //将溢出的次数清零,为下一次计数做准备
}
#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
#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
同刚刚
#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;
}
}
#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仿真,上文会操作的建议自己来试试,不会的可以下载工程,修改参考,时间比较紧,制作可能有些部分不太合理,欢迎大家指教 工程链接
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/132585.html原文链接:https://javaforall.cn