|
发表于 2018-4-23 23:47:57
|
显示全部楼层
改电路可能更麻烦了。而且100s时间也太长了,即使成了,也不好用啊。
建议按wash说的,改用等精度频率测量方法就可以了。如果不想买再买的这种频率计,可以自己做一个,按如下方案制作即可:
用STC12C5A60S2单片机搭一个最小系统,也就几个元件。晶振用18.432MHz
然后将P3.3与P3.5连接起来做为频率计的输入端。信号整形后,送入这个端子即可。
显示器用LCD1602,连接线按程序中定义的连线即可
sbit lcd_RS = P0^0; //数据命令控制位,0命令1数据
sbit lcd_RW = P0^1; //读写位,0写1读
sbit lcd_EN = P0^2; //使能位,下降沿触发
sbit lcd_D4 = P0^3; //数据端口D4
sbit lcd_D5 = P0^4; //数据端口D5
sbit lcd_D6 = P0^5; //数据端口D6
sbit lcd_D7 = P0^6; //数据端口D7
lcd1602的vo端子对地接2.2k电阻,供电用5V
程序如下(我以前写的一个程序的基础上修改的,没有优化,闸门0.7s,32768Hz可分辨到+-0.01Hz左右),设计这个程序还是有一点小难度,弄了1天,时间关系,不想优化了,如果兴趣自行优化一下。程序中有一半代码是无用的,可以删除,不会删的话,就留着也没事。
//==========================================================================
// ESR表驱动程序 V2.0
// 许剑伟 于莆田 2012.4
// 2013.7月最后修改
//==========================================================================
//==========================================================================
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
#include <reg52.h>
//==========================================================================
// 项目:LCD1602 四线驱动程序
// 设计要点:
// LCD1602 的运行速度慢,而单片机运行的速度快,因此容易因为速度不
// 匹配造成调试失败。因此,调试之前应准确测试lcd_delay() 延时函数
// 准确的延时量,如果不能满足注释中的要求,则应调整循次数。每步操
// 作所需的延时量,按照数据手册指标指行,同时留下足够的时间余量。
// 硬件连接:
// 至少需要9条线,电源线2条,7条信号线。信号线详见程序中的接口定义。
// 清注意对LCD1602比对的调节,否则无显示。
// 设计:许剑伟,于莆田,2010.12
//==========================================================================
sbit lcd_RS = P0^0; //数据命令控制位,0命令1数据
sbit lcd_RW = P0^1; //读写位,0写1读
sbit lcd_EN = P0^2; //使能位,下降沿触发
sbit lcd_D4 = P0^3; //数据端口D4
sbit lcd_D5 = P0^4; //数据端口D5
sbit lcd_D6 = P0^5; //数据端口D6
sbit lcd_D7 = P0^6; //数据端口D7
//==========================================================================
void lcd_delay(int n){ //LCD专用延时函数
//32MHz钟频下,约循环3000次延迟1毫秒
int i,j;
if(n<0) { for(i=0;i< 10;i++); return; } //10us
if(n== 0) { for(i=0;i<50;i++); return; } //50us
for(;n;n--){ for(j=0;j<1000;j++); } //n毫秒
}
//==========================================================================
void lcd_B(char f, uchar c, char t){ //控制四线式接口LCD的7个脚
//f=0写命令字, f=1写RAM数据, f=2读地址(或读忙), f=3读RAM数据
lcd_EN = 1;
lcd_RS = f%2;
lcd_RW = f/2%2;
//移入高四位
lcd_D4 = c & 16;
lcd_D5 = c & 32;
lcd_D6 = c & 64;
lcd_D7 = c & 128;
lcd_delay(-1); lcd_EN = 0; //使能脉冲
if(f==4) { lcd_delay(t); return; }
lcd_EN = 1;
//移入低四位
lcd_D4 = c & 1;
lcd_D5 = c & 2;
lcd_D6 = c & 4;
lcd_D7 = c & 8;
lcd_delay(-1); lcd_EN = 0; //使能脉冲
lcd_delay(t); //不同的命令,响应时间不同,清零命令需要2ms
lcd_EN = 1;
lcd_RS = 1;
lcd_RW = 1;
lcd_D4 = 1;
lcd_D5 = 1;
lcd_D6 = 1;
lcd_D7 = 1;
}
//==========================================================================
void lcd_init(){ //LCD1602 初始化
//启动四线模式须势行9个步骤,初始化所须耗时较长,约65ms,时限不可减
lcd_delay(20); //启动lcd之前须延时大于15ms,直到VDD大于4.5V
lcd_B(4, 0x30, 9); //置8线模式,须延时大于4.1ms
lcd_B(4, 0x30, 5); //置8线模式,须延时大于100us
lcd_B(4, 0x30, 5); //置8线模式,手册中未指定延时
lcd_B(4, 0x20, 5); //进入四线模式
lcd_B(0, 0x28, 5); //四线模式双行显示
lcd_B(0, 0x0C, 5); //打开显示器
lcd_B(0, 0x80, 5); //RAM指针定位
lcd_B(0, 0x01, 5); //启动清屏命初始化LCD
}
//==========================================================================
//=========================几个功能常用函数=================================
void lcd_cls() { lcd_B(0, 0x01+0, 2); } //清屏
void lcd_cur0() { lcd_B(0, 0x0C+0, 0); } //隐藏光标
void lcd_goto1(uchar x){ lcd_B(0, 0x80+x, 0); } //设置DDRAM地址,第1行x位
void lcd_goto2(uchar x){ lcd_B(0, 0xC0+x, 0); } //设置DDRAM地址,第2行x位
void lcd_putc(uchar d) { lcd_B(1, 0x00+d, 0); } //字符输出
void lcd_puts(uchar *s){ for(; *s; s++) lcd_B(1,*s,0); } //字串输出
//==============字符显示函数====================
#define digW 4 //数字显示位数宏
void lcd_puti(long a,char w){ //定宽显示正整数
char i=0, s[11] = " ";
if(a<0) { a=-a; lcd_puts("-"); }
else lcd_puts(" ");
do{
s[i++] = a%10+48;
a /= 10;
}while(a);
for(;w;w--) lcd_putc(s[w-1]);
}
void lcd_putf(float a,char n,char w){ //浮点输出,n是保留小数的位数,w是数字宽度
char i,g,fi=0;
long b,c=1;
if(a<0) { lcd_putc('-'); a = -a; }
else { lcd_putc(' '); }
for(i=0;i<n;i++) a *= 10;
for(i=1;i<w;i++) c *= 10;
b = a;
for(i=0;i<w;i++){
g = b/c;
b -= g*c;
c /= 10;
if(g>9||g<0) g='*'-48;
if(i == w-n ) lcd_putc('.');
if(!g && !fi && i<w-n-1) { lcd_putc(' '); continue; }
lcd_putc(g+48);
fi = 1;
}
if(!n) lcd_putc(' '); //无小数点的补足显示宽度
}
void lcd_putPic(char n,char *s){ //写入自字义字符
char i;
n = n<<3;
for(i=0;i<8;i++){
lcd_B(0,0x40+n+i,0);
lcd_B(1,s,0);
}
}
//==========================================================================
//===============================延时函数===================================
void delay(uint loop) { uint i; for(i=0;i<loop;i++); } //延时函数
void delay2(uint k) { for(;k>0;k--) delay(10000); } //长延时,k=100大约对应1秒
//==========================================================================
//=================================AD转换===================================
sfr P1ASF = 0x9D; //将P1置为模拟口寄存器(使能),各位中为1的有效
sfr ADC_CONTR = 0xBC; //A/D转换控制寄存器
sfr ADC_res = 0xBD; //A/D转换结果寄存器
sfr ADC_resl = 0xBE; //A/D转换结果寄存器
void set_channel(char channel){
P1ASF = 1<<channel;
ADC_CONTR = channel+128; //最高位是电源开关,低3位通道选择
delay(1); //首次打开电源应延迟,使输入稳定
}
uint getAD2(){
ADC_CONTR |= 0x08; //00001000,置ADC_START=1启动A/D 转换
while ( !(ADC_CONTR & 0x10) ); //等待A/D转换结束(ADC_FLAG==0)
ADC_CONTR &= 0xE7; //11100111,置ADC_FLAG=0清除结束标记, 置ADC_START=0关闭A/D 转换
return ADC_res*4 + ADC_resl;
}
/*
uchar get_AD(){
ADC_CONTR |= 0x08; //00001000,置ADC_START=1启动A/D 转换
while( !(ADC_CONTR & 0x10) ); //等待A/D转换结束(ADC_FLAG==0)
ADC_CONTR &= 0xE7; //11100111,置ADC_FLAG=0清除结束标记, 置ADC_START=0关闭A/D 转换
return ADC_res;
}
*/
//==========================================================================
//==================================EEPROW偏程==============================
sfr IAP_data = 0xC2;
sfr IAP_addrH = 0xC3;
sfr IAP_addrL = 0xC4;
sfr IAP_cmd = 0xC5;
sfr IAP_trig = 0xC6;
sfr IAP_contr = 0xC7;
/********************
写字节时,可以将原有数据中的1改为0,无法将0改为1,只能使用擦除命令将0改为1
应注意,擦除命令会将整个扇区擦除
*********************/
int eepEn = 0;
void saEEP(){ //触发并EEP保护
if(eepEn==12345) IAP_trig = 0x5A; //先送5A
if(eepEn==12345) IAP_trig = 0xA5; //先送5A再送A5立即触发
IAP_cmd = 0; //关闭令,保护
IAP_contr = 0; //关EEPROM,保护
IAP_trig = 0;
IAP_addrL = 255; //设置读取地址的低字节,地址改变才需要设置
IAP_addrH = 255; //设置读取地址的高字节,地址改变才需要设置
}
uchar readEEP(uint k){ //读取
IAP_addrL = k; //设置读取地址的低字节,地址改变才需要设置
IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
IAP_contr = 0x81; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
IAP_cmd = 1; //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
saEEP(); //触发并保护
return IAP_data;
}
void writeEEP(uint k, uchar da){ //写入
IAP_data = da; //传入数据
IAP_addrL = k; //设置读取地址的低字节,地址改变才需要设置
IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
IAP_contr = 0x81; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
IAP_cmd = 2; //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
saEEP(); //触发并保护
}
void eraseEEP(uint k){ //擦除
IAP_addrL = k; //设置读取地址的低字节,地址改变才需要设置
IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
IAP_contr = 0x81; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
IAP_cmd = 3; //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
saEEP(); //触发并保护
}
xdata struct Ida{
int c[3]; //起点校准,中点校准,末点校准
} cs;
void cs_RW(char rw){
uchar i,*p = &cs;
const int offs=512;
if(rw){
eraseEEP(offs);
for(i=0;i<sizeof(cs);i++) writeEEP(i+offs,p);
}else{
for(i=0;i<sizeof(cs);i++) p=readEEP(i+offs);
}
}
//==========================================================================
//==================================ESR主程序===============================
//==========================================================================
sfr P1M1=0x91; //P1端口设置寄存器
sfr P1M0=0x92; //P1端口设置寄存器
sfr P0M1=0x93; //P0端口设置寄存器
sfr P0M0=0x94; //P0端口设置寄存器
sfr P2M1=0x95; //P2端口设置寄存器
sfr P2M0=0x96; //P2端口设置寄存器
sfr P3M1=0xB1; //P3端口设置寄存器
sfr P3M0=0xB2; //P3端口设置寄存器
sfr WAKE_CLKO = 0x8F;
sfr AUXR = 0x8E;
sfr BRT = 0x9C;
xdata uchar menu=0,menu2=0; //菜单变量
ulong nn = 0; //时序计数器
long feq=0;
unsigned char Tk0=0,Tk1=0;
unsigned long f0=0,f1=0,Tm=200;
void timerInter0(void) interrupt 1 {//T0中断
static unsigned char Tu=0;
Tk0++;
Tu = (Tu+1)%Tm;
if(!Tu) { IE1 = 0; EX1 = 1; } //开外部中断
}
void timerInter1(void) interrupt 3 { Tk1++; } //T1中断
void int0(void) interrupt 0{ } //INT0中断
void int1(void) interrupt 2{ //INT1中断
static unsigned long m0=0,m1=0;
unsigned long n0,n1;
unsigned char v,a,b,c;
unsigned char V,A,B,C;
EX1 = 0; //关外部中断INT1
//如果c值小,跳变发生在取c与取v之间,b可信
//如果c值大,跳变发生在取c与取b之间,b不可信
v = TH0; c = TL0; b = TH0; a = Tk0;
V = TH1; C = TL1; B = TH1; A = Tk1;
if(v!=b) { //取值时定时器高位跳变
if(c<128){ if(b==0) a++; } //INT1优先级高定时计数中断此时无法触法,a无法跳变,所以这里修正a
else b--; //b不可信,需修正
}
if(V!=B) { //取值时计数器高位跳变
if(C<128){ if(B==0) A++; } //INT1优先级高定时计数中断此时无法触法,a无法跳变,所以这里修正a
else B--; //b不可信,需修正
}
n0 = a*256L*256L + b*256L + c;
n1 = A*256L*256L + B*256L + C;
if(n0<m0) f0 = (1L<<24)+n0-m0; else f0 = n0-m0;
if(n1<m1) f1 = (1L<<24)+n1-m1; else f1 = n1-m1;
m0 = n0;
m1 = n1;
}
main(){
uchar kn=0,key=0,keya=0,keyb=0; //键盘响应变量
//端口初始化,M1M0,00常规口,01推挽口,10高阻口,11开漏口
P1M0 = 0x01; //P1.0推挽口
P1M1 = 0x00; //
P3M0 = 0x00; //00110000 P3.3,P3.4置为高阻口
P3M1 = 0x28; //00100000
delay2(3); //等待升压电源电压上升
lcd_init(); //初始化LCD
lcd_cur0(); lcd_puts("Vref V1.0"); //隐藏光标并显示片本
lcd_goto2(0); lcd_puts("XJW Putian,2013"); //显示作者
lcd_putPic(1,"\x00\x00\xE\x11\x11\x0a\x1b\x00");
delay2(100); lcd_cls(); //启动延时
eepEn= 12345;
cs_RW(0); //读EEPROM
if( ((uint)cs.c[0]>>8)==0xff ) cs.c[0] = cs.c[1] = cs.c[2] = 0;
AUXR |= 0x80; //T0工作在1T
AUXR |= 0x40; //T1工作在1T
AUXR |= 0x04; //BRT工作在1T
WAKE_CLKO |= 0x04; //允许BRT输出时钟
AUXR |= 0x10; //启动BRT
BRT = 1;
TMOD = 0x51; //T1置为16位计数器,T0置为16位定时器
TR0 = TR1 = 1; //起动计数
ET0 = ET1 = 1; //开定时器中断
IT1 = 1; //INT1中断下降沿触发,置1后,只要有下降沿就会IE1就会置位,不受EX1控制
EX1 = 0; //INT1中断许可设置
PX1 = 1; //INT1中断优先级置1,即IP |= 0x04;
EA = 1; //开总中断
set_channel(1); //设置AD转换通道
while(1){
nn++;
//扫描键盘
key=0; keya=keyb; keyb=(~P3)&(128+64+32);
if(keya){
if(!keyb){ //键弹起
if(kn>2) key = keya; //短按
if(kn>40) key = keya+1; //长按,约1至2秒
kn = 0;
}
else kn += kn<255?1:0;
}
//菜单系统
if(key==65){ //菜单键
lcd_cls();
if(++menu>1) menu=0,menu2=0;
}
if(key==64){ //保存参数
//cs_RW(1);
//lcd_cls();
//lcd_puts("saved OK");
//delay2(50);
}
if(menu==0){ //显示输出电压
if(nn%20==0){
lcd_goto1(0); lcd_putf(18432000.0*f1/f0,2,10);
//lcd_goto1(0); lcd_puti(f0,7);
//lcd_goto2(0); lcd_puti(f1,7);
}
}
if(menu==1){ //校准
}
delay(10000);
}//while end
}
//==========================================================================
|
|