//========================================================================== // LCR表驱动程序 V1.0 // xjw01 于莆田 2011.10 //========================================================================== //========================================================================== #define uchar unsigned char #define uint unsigned int #define ulong unsigned long #include #include //========================================================================== // 项目:LCD1602 四线驱动程序 // 设计要点: // LCD1602 的运行速度慢,而单片机运行的速度快,因此容易因为速度不 // 匹配造成调试失败。因此,调试之前应准确测试lcd_delay() 延时函数 // 准确的延时量,如果不能满足注释中的要求,则应调整循次数。每步操 // 作所需的延时量,按照数据手册指标指行,同时留下足够的时间余量。 // 硬件连接: // 至少需要9条线,电源线2条,7条信号线。信号线详见程序中的接口定义。 // 清注意对LCD1602比对的调节,否则无显示。 // 设计:许剑伟,于莆田,2010.12 //========================================================================== sbit lcd_RS = P0^6; //数据命令控制位,0命令1数据 sbit lcd_RW = P0^5; //读写位,0写1读 sbit lcd_EN = P0^4; //使能位,下降沿触发 sbit lcd_D4 = P0^3; //数据端口D4 sbit lcd_D5 = P0^2; //数据端口D5 sbit lcd_D6 = P0^1; //数据端口D6 sbit lcd_D7 = P0^0; //数据端口D7 //========================================================================== void lcd_delay(int n){ //LCD专用延时函数 //32MHz钟频下,约循环3000次延迟1毫秒 int i,j; if(n<0) { for(i=0;i< 30;i++); return; } //10us if(n== 0) { for(i=0;i<150;i++); return; } //50us for(;n;n--){ for (j=0;j<3000;j++); } //n毫秒 } //========================================================================== void lcd_B(char f, uchar c, char t){ //控制四线式接口LCD的7个脚 //f=0写命令字, f=1写RAM数据, f=2读RAM数据, f=3读RAM数据 lcd_EN = 0; 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_EN = 1; lcd_delay(-1); lcd_EN = 0; //使能脉冲 if(f==4) { lcd_delay(t); return; } //移入低四位 lcd_D4 = c & 1; lcd_D5 = c & 2; lcd_D6 = c & 4; lcd_D7 = c & 8; lcd_EN = 1; lcd_delay(-1); lcd_EN = 0; //使能脉冲 lcd_delay(t); //不同的命令,响应时间不同,清零命令需要2ms } //========================================================================== 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); } //字串输出 //========================================================================== //===============================延时函数=================================== void delay(uint loop) { uint i; for(i=0;i0;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<>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,擦除时按所在字节整个扇区撺除 IAP_trig = 0x5A; //先送5A IAP_trig = 0xA5; //先送5A再送A5立即触发 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,擦除时按所在字节整个扇区撺除 IAP_trig = 0x5A; //先送5A IAP_trig = 0xA5; //先送5A再送A5立即触发 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,擦除时按所在字节整个扇区撺除 IAP_trig = 0x5A; //先送5A IAP_trig = 0xA5; //先送5A再送A5立即触发 saEEP(); //保护 } xdata struct Ida{ char zo[3];//三个频率下的零点改正值 char j1; //相位补偿(3倍档) char j2; //相位补偿(10倍档) char J[4]; //相位补偿(V/I变换器) char R[4]; //下臂电阻修正(20,1k,10k,100k) char g1; //增益修正(3倍档) char g2; //增益修正(10倍档) } cs; void cs_RW(char rw){ uchar i,*p = &cs; if(rw){ eraseEEP(0); for(i=0;ib) f = a; else f = b; if(qmin) { a += qmin/2, a -= fmod(a,qmin)-qmin/1000; b += qmin/2; b -= fmod(b,qmin)-qmin/1000; } for(i=1;i= 1000) g/=1000, c++; } //以3位为单位移动小数点,右移 for(i=0;i> 8; //截断正弦相位累加器,取高8位 y = x + chuX; //方波相位 CCAP0H = sinB[x];//正弦DDS输出 DDS2 = fbB[y]; //方波DDS输出 ph += phM; //相位累加 } void setDDS(uint f){ //参考时钟是c=(fosc/2)/256=32000000/2/256=62500,频率f=c*phM/2^16 feq = f; phM=f*65536.0/62500; //phM=f*2^16/62500 feqX = 62500.0*phM/65536; //实际输出频率 ph = 0; //高频时,使波形对称 if(!f) CR=0; else CR=1; } //相位控制函数 xdata char xw=0; //相位 void set90(char k){ //设置方波的相位差 k %= 4; if(k<0) k += 4; if(k==0) chuX=0; //移相0度 if(k==1) chuX=64; //移相90度 if(k==2) chuX=128; //移相180度 if(k==3) chuX=192; //移相270度 xw = k; } void set902() { set90(xw+1); } //相位步进 //==============量程控制函数==================== xdata char rng=1; //量程 void setRng(char k){//切换量程 if(k==0) Ka=0,Kb=0; //100欧 if(k==1) Ka=0,Kb=1; //1k欧 if(k==2) Ka=1,Kb=0; //10k欧 if(k==3) Ka=1,Kb=1; //100k欧 rng = k; } void setRng2(){ setRng( (rng+1)%4); } //量程步进 //==============增益控制函数==================== char curGain=1; //当前增益索引号 void setGain(char k){ //设置电路增益 if(k>3) k=3; if(k<0) k=0; if(k==0) K4=0,K6=0; //1倍 if(k==1) K4=0,K6=1; //3倍 if(k==2) K4=1,K6=0; //10倍 if(k==3) K4=1,K6=1; //30倍 curGain = k; } void setGain2(){ setGain((curGain+1)%4); } //==============LCR测量==================== xdata int Vxy[4]={0,0,0,0}; //Vxy[Vx1,Vy1,Vx2,Vy2] xdata char Sxw[4]={0,1,0,1}; //保存正确相位 xdata char Vga[4]={1,1,1,1}; //上下臂增益记录表 xdata uchar tim=0,tims=0; xdata char pau=0; //暂停坐标自动旋转 #define Vfull 9600 #define gad (9600/30) uchar mT = 6; //测量速度,mT取值为6或12或24时,可以消除数字噪声,尾数不动,但不利于于取平均 //==============设置频率==================== xdata char feqK=1; //频率索引号 void setF(char k){ if(k==-1){ //步进 k = 0; if(feq==100) k=1; if(feq==1000) k=2; if(feq==7813) k=0; } feqK = k; if(k==0) { setDDS(100); K5=0; K8=1; mT=12; } //置为100Hz if(k==1) { setDDS(1000); K5=0; K8=0; mT=6; } //置为1kHz if(k==2) { setDDS(7813); K5=1; K8=0; mT=6; } //置为7.8125kHz TH1 = 47, TL1 = 171; //置为20ms tims = 0; tim = 0; ph = 0; } int absMax(int a,int b){ //取两个数绝对值最大者 if(a<0) a = -a; if(b<0) b = -b; if(b>a) a = b; return a; } #define avn 4 //求平无个数 xdata float vq[3][avn]; //数据缓存,用于求平均 void LCRcalc(int *v,char *g){ //LCR计算 code float ga[4] = { 1, 3, 9, 27 }; //增益表 code float dwR[4] = { 20, 1e3, 1e4, 1e5 }; //各档电阻表 xdata int g12 = (int)cs.g1+cs.g2; //增益最大补偿 xdata int j12 = (int)cs.j1+cs.j2; //相位最大补偿 xdata float JD = 0,cJD, G = 0; //补偿变量 xdata float a,b,c,e; char i; a = +( 1.0*v[2]*v[2] + 1.0*v[3]*v[3] ); b = -( 1.0*v[0]*v[2] + 1.0*v[1]*v[3] ); c = -( 1.0*v[2]*v[1] - 1.0*v[0]*v[3] ); a *= ga[g[0]] / ga[g[2]]; a /= dwR[rng]*(1+cs.R[rng]/10000.0); //除以下臂电阻阻值 //可控增益单元的增益修正、相位补偿量 if(g[0] == 1) JD += cs.j1, G += cs.g1; if(g[0] == 2) JD += cs.j2, G += cs.g2; if(g[0] == 3) JD += j12, G += g12; if(g[2] == 1) JD -= cs.j1, G -= cs.g1; if(g[2] == 2) JD -= cs.j2, G -= cs.g2; if(g[2] == 3) JD -= j12, G -= g12; JD -= cs.J[rng]; JD *= feqX/7813/1000; cJD = 1 - JD*JD/2; a *= 1+G/10000; //增益补偿 e = b*cJD - c*JD; //相位补偿 c = b*JD + c*cJD; //相位补偿 b = e; //入队 for(i=1;i=mT) tims = 0, tim++, c = 1; if(tim>=4) tim=0; if(pau) return; if(c){ //tim进位触发 c = getAD10(); //读取电压值 c -= cs.zo[feqK]; Vxy[tim] = xw<2 ? c : -c; //保存当前电压 Vga[tim] = curGain; //保存当前增益 Sxw[tim] += c<0 ? 2 : 0; //相位翻转(预测下次的相位采用值) Sxw[tim] %= 4; if(tim==1||tim==3){ //上下臂切换 //电压模值才能反应运放的输出幅度,所以增益切换判断得用模值 if(tim==1) K3=1, c = absMax(Vxy[2],Vxy[3]), g=Vga[2]; //切换到下臂 if(tim==3) K3=0, c = absMax(Vxy[0],Vxy[1]), g=Vga[0]; //切换到上臂 if(c>Vfull) g--; else if(c999) c=999; } //计算Q else c = 999; lcd_putf(c,0,0); //显示Q } //void timerInter(void) interrupt 1 {}//T0中断 main(){ uchar i=0,kn=0,key=0; uchar dispN=0; //显示扫描索引 uchar spkN=0; //蜂鸣器发声时长 uint nn=0; uchar binLian=0; lcd_init(); //初始化LCD lcd_cur0(); //隐藏光标 lcd_puts("LCR 2.0"); lcd_goto2(0); lcd_puts("XJW Putian,2011"); delay2(80); //启动延时 cs_RW(0); //读EEPROM TCON=0, TMOD=0x12; //将T0置为自动重装定时器,T1置为定时器 TH1 = 0, TL1 = 0; TR1=1; //T1开始计数 TR0=0; //T0暂停计数 ET1=1; //T1开中断 ET0=1; //T1开中断 EA=1; //开总中断 PT0=1; //设置优先级 set_channel(0); //设置AD转换通道 P2M0 = 0xFF; //P2.01234567置为推勉输出 P1M0 = 0xFC; //P1.234567置为推换口 P1M1 = 0x03; //P1.0置为高阻抗 P2 = 0x0F; PWM_init();//DDS初始化 set90(2); //初始设置相位 setRng(1); //初始设置量程 setGain(1); //初始设置增益 setF(1); //DDS初始设置为1kHz while(1){ //显示disp nn++; //扫描键盘 key = ~P3; if(key&&kn<255) kn++; else kn=0; for(i=0;key;i++) key/=2; key=i; if(kn==4) spkN=10; else key=0; //当按下一定时间后key才有效。spkN发声时长设置 if(spkN) spkN--, spk=0; else spk=1; //键盘发声 //菜单系统 if(key==8){//菜单键 lcd_cls(); lcd_puts("Menu: 1 LCR"); lcd_goto2(0); lcd_puts("3 Test,7 Set"); menu=0; key=0; } if(menu>=1 && menu<=4){ if(key==7) setRng2(); //量程步进 if(key==6) setF(-1); //设置频率 } if(menu==0){ //显示量程和菜单 if(key) lcd_cls(); if(key>=1 && key<=7) menu = key, menu2 = 0; key = 0; } if(menu==1){ //LCR测量(串联) pau = 0; if(key==1) binLian = (binLian+1)%2; //串并联切换 showR(binLian); } if(menu==2){ lcd_goto1(0); lcd_puts("up:"); lcd_putc(Vga[0]+48); lcd_puts(" dw:"); lcd_putc(Vga[2]+48); } if(menu==3){ //手动调试 pau = 1; if(key==1) setGain2();//增益控制 if(key==2) { }; if(key==3) K3=~K3; //切换上下臂 if(key==4) set902(); //相位旋转 lcd_goto1(0); lcd_puts(" xw="); lcd_putc(xw+48); //相位索引号 lcd_puts(" K3="); lcd_putc(K3?49:48); //K3状态 lcd_puts(" Ga="); lcd_putc(curGain+48); //增益索引号 lcd_goto2(0); if(nn%32==0) lcd_int(getAD10(),5); } if(menu==7){ //设置零点偏移数 code char *csR[15] = { "Z0 : 20", "Z1 : 20","Z2 : 20", "R1X: 20", "R2X: 1k", "R3X:10k", "R4X:.1M", "G1X:3k3", "G2X:10k", "R1 : 20", "R2 : 1k", "R3 :10k", "R4 :.1M", "G1 :3k3", "G2 :10k"}; char *p,bc=1, feqD=1,rngD=1; static char kc=0, mo=0; if(menu2==0) p = cs.zo+0, feqD=0, rngD=2; //100Hz零点校准,接入10欧电阻 if(menu2==1) p = cs.zo+1, feqD=1, rngD=2; //1kHz零点校准,接入10欧电阻 if(menu2==2) p = cs.zo+2, feqD=2, rngD=2; //7.8kHz零点校准,接入10欧电阻 if(menu2==3) p = cs.J+0, feqD=2, rngD=0; //VI变换器相位校准,接入20电阻 if(menu2==4) p = cs.J+1, feqD=2, rngD=1; //VI变换器相位校准,接入1k电阻 if(menu2==5) p = cs.J+2, feqD=2, rngD=2; //VI变换器相位校准,接入10k电阻 if(menu2==6) p = cs.J+3, feqD=2, rngD=3; //VI变换器相位校准,接入100k电阻 if(menu2==7) p = &cs.j1, feqD=2, rngD=1; //运放3倍档相位校准,接入3.3k欧电阻 if(menu2==8) p = &cs.j2, feqD=2, rngD=1; //运放10倍档相位校准,接入10k欧电阻 if(menu2==9) p = cs.R+0, bc=2, feqD=1, rngD=0; //VI变换器模值校准,接入20欧 if(menu2==10) p = cs.R+1, bc=2, feqD=1, rngD=1; //VI变换器模值校准,接入1k欧 if(menu2==11) p = cs.R+2, bc=2, feqD=1, rngD=2; //VI变换器模值校准,接入10k欧 if(menu2==12) p = cs.R+3, bc=2, feqD=1, rngD=3; //VI变换器模值校准,接入100k欧 if(menu2==13) p = &cs.g1, bc=2, feqD=1, rngD=1; //运放3倍增益校准,接入2k欧电阻 if(menu2==14) p = &cs.g2, bc=2, feqD=1, rngD=1; //运放10倍增益校准,接入10k欧电阻 if(key==1) *p += bc; //X键增 if(key==2) *p -= bc; //R键减 if(key==3) { cs_RW(1); setF(feqK); } //L键保存 if(key==4) *p = 0; //C键清除 if(key==5) { if(menu2==0) menu2=14; else menu2--; mo=0; } if(key==6) { if(menu2==14)menu2=0; else menu2++; mo=0; } if(key==7) mo = (mo+1)%2; if(key==4){ //恢复到默认值 if(++kc==5){ kc = 0; cs.j1 = 16, cs.j2 = 18; cs.g1 = 0, cs.g2 = 0; cs.zo[0] = 20; cs.zo[1] = 19; cs.zo[2] = 14; cs.J[0] = cs.J[1] = cs.J[2] = 0, cs.J[3] = 20; cs.R[0] = cs.R[1] = cs.R[2] = cs.R[3] = 0; } } else { if(key) kc=0; } //显示 if(mo){ if(feqD!=feqK) setF(feqD); if(rngD!=rng) setRng(rngD); showR(1); }else{ lcd_cls(); lcd_goto1(0); lcd_puts(csR[menu2]); //输出参数名称 lcd_goto2(0); lcd_putf(*p,0,0); lcd_puts("X:+ R:-"); } } delay(20000); }//while end } //==========================================================================