wulin 发表于 2019-11-7 10:57 师傅,这个程序,如果再增加一路测试电压的话,程序应该怎么写呢 |
wulin 发表于 2019-11-7 10:57 非常感谢 我下载了 我需要好好去学习哈 |
不将就123 发表于 2019-11-6 22:07 给你把程序改好了,有详细注释。 ![]() ![]() |
感谢论坛的各位朋友 问题现在已经解决了 是开发板上面的 上拉电阻导致的误差 将I0口拿出来测试 问题得到完美的解决了 temp=(ADC_jiancedaozuidivoltage*AD_jie_guo); temp=(temp*6)/100; //这里为什么要/100 这个是我参考网上大师做的 |
大哥 我重新发了一次 您在给我看看 |
#include <STC12C5A60S2.H> #include "intrins.h" //typedef unsigned char BYTE; //typedef unsigned int WORD; #define uchar unsigned char #define uint unsigned int #define ulong unsigned long /*Define ADC operation const for ADC_CONTR*/ #define ADC_POWER 0x80 //ADC 电源控制位 10000000 0X80 / #define ADC_FLAG 0x10 //ADC 完成标志 00010000 0x10 #define ADC_START 0x08 //ADC 启动ADC开关 00001000 0x08 / #define ADC_SPEEDLL 0x00 //420 转换速度 00000000 0x00 / #define ADC_SPEEDL 0x20 //280 转换速度 00100000 0x20 #define ADC_SPEEDH 0x40 //140 转换速度 01000000 0x40 #define ADC_SPEEDHH 0x60 //70 转换速度 01100000 0x60 //float temp; //定义temp为浮点型参数 /*---------------------------------------------------------------------------------------------- meidangzuidivoltage:表示AD最高电压5V对应10bit_AD转换的最小电压是多少 5/1024= 0.00488V 也就是AD在 //10bit下测量的最小电压, //那么在计算AD电压值的时候将公式 “GetADCResult(ch)*5/1024”分两步进行 方便后面对数据分离显示到 //1602上面 先计算ADC_jiancedaozuidivoltage=5*10000000/1024 先将这个数放大100万倍 //最后在算AD_jie_guo=(ADC_jiancedaozuidivoltage*GetADCResult(ch); ----------------------------------------------------------------------------------------------*/ ulong ADC_jiancedaozuidivoltage,AD_jie_guo,temp; uint num,i,vlue;//ADC_mV,ADC_RESX,VCC_V=5.2; sbit lcden = P2^7; sbit rs = P2^6; sbit rw = P2^5; sbit LED = P3^0; void Delay(uint n); //延时函数 uint GetADCResult(uchar ch); // ad转换函数 void InitADC(); //ad初始化函数 //ulong count(uchar ch); //ad计算函数 uchar code table[20]= {" Shu Kong QuDong"}; uchar code table2[20]={" CQDZ Alan V1.01"}; float count(uchar ch); /* 延时函数*/ void delayms(uint xms) { uint i,j; for(i=xms;i>0;i--) for (j=960;j>0;j--); } /******写命令****/ void lcd_write_com(uchar com) { rw=0; rs=0; //写命令状态 P0=com; lcden=1; delayms(5); lcden=0; } /**写数据 ***/ void write_date_(uchar date) { rw=0; //写数据 rs=1; //写数据状态 P0=date; delayms(5); lcden=1; //使能 delayms(5); lcden=0; } /*初始化显示屏*/ void lcd_init(void) { lcden=0; lcd_write_com(0x38); //设置8位格式,2行,5*7 lcd_write_com(0x0c); //整体显示,关光标,不显示 lcd_write_com(0x06); // 设定输入方式,增量不移位 lcd_write_com(0x01);//清屏幕 delayms(5); //延? } /*----------------------- 函数名称 格式定义 函数的介绍 在某个屏幕位置上显示一个字符,X(0-16),y(1-2) X:表示字的格式 一共16个 Y:表示行 一共2行 -------------------------*/ //格式定义 void lcd_disp_char(uchar y,uchar x, uint dat) { uint address; if(y==1) //y为1 在第一行 address=0x80+0x10+4+x; //整屏左移动以后 从新定义新的起始位置 但是要加上之前移动后的地址 else address=0xc0+0x10+x; //y为2 在第二行 X显示字的位置 0XC0是 0x80+0x40的结果 lcd_write_com(address); //写入要写的位置 write_date_( dat); //写入你要写的数据 } /*显示函数2*********/ void disp() { temp=(ADC_jiancedaozuidivoltage*AD_jie_guo); temp=(temp*6)/100; //扩大电压 我的量程是0-30V 分压电阻是 10k 2k 电阻比的5 反推 当测试电压为5v //的时候 最高电压为25V 测量后调试 //0x30是显示数字 0-9 30表示第一个数0 int是将count转换为整数 lcd_disp_char(1,0, temp%10000000/1000000+0x30 ); //十位 lcd_disp_char(1,1, temp%1000000/100000+0x30); //个位 lcd_disp_char(1,2,'.' ); // 小数点 lcd_disp_char(1,3, temp%100000/10000+0x30 ); //个分位 lcd_disp_char(1,4, temp%10000/1000+0x30 ); //百分位 lcd_disp_char(1,5,'V' ); } //count(0) /*----------------------------------- 功能 开机的时候显示一下铭牌 for来完成 屏幕左移动 ----------------------------------*/ void init() { // lcd_write_com(0x80+0x10); //定义显示的位置 起始地址 lcd_write_com(0x80); //定义显示的位置 起始地址 for(num=0;num<20;num++) { write_date_(table[num]); //初始化屏幕的初始数字“0000” delayms(5); } // lcd_write_com(0x80+0x40+0x10); //定义第二排,显示的地址 0x80是显示屏寄存器第一排起始地址 lcd_write_com(0xc0); //定义显示的位置 起始地址 for(num=0;num<20;num++) //0x40是第二排起始地址 { write_date_(table2[num]); delayms(5); } for(num=0;num<20;num++) //整屏左移动 这里的21就是指可以移动多少格 可以是100可以是1000 相当于是电子屏幕一样 { lcd_write_com(0x18); //0x18是整屏左移 指令 delayms(50); } } void main() { lcd_init(); init(); InitADC(); //Init ADC sfr ADC_jiancedaozuidivoltage=49700000/1024; //5V参考电压,计算分度值的时候,数值过小会出现很大偏差 //所以放大1000万倍 这样精度会高一些 while (1) { /* if(temp > 3) { LED = 0; // 调试的时候用的 通过判断temp来判断是否过压 如果过压就启动保护 } if(temp < 3) { LED = 1; }*/ AD_jie_guo = count(0); //经过上面的计算求出来100次的平均值存放在AD_jie_guo里面 disp(); } } /************************* * 功能 ADC转换函数 * 带结果返回 带结果返回 **************************/ uint GetADCResult(uchar ch) //参数的定义“ch”初始化就是为0 所以这里的“ch”应该是用来设置用哪个端口 { //来设置AD的 这个“ADC_POWER|ADC_SPEEDLL|ch|ADC_START”;或出来的结果是 //10001000 后面三个0对应的是 CHS2 CHS1 CHS0 查表格 刚好是P1.0口 //作为AD模拟输入的。如果要改变端口就要给ch赋值 假如要P1.2 就需要赋值为ch=0x02; ADC_CONTR = ADC_POWER|ADC_SPEEDLL|ch|ADC_START; //配置相关项目打开 _nop_(); _nop_(); _nop_(); //4个机器时间的延时 以保障转换完成 _nop_(); while(!(ADC_CONTR&ADC_FLAG)); //判断是否转换完成 ADC_CONTR &= ~ADC_FLAG; //关闭ADC vlue = (ADC_RES*4+ADC_RESL); //返回结果10bit 也就是电压值 Delay(2); //延时一下不然电压的后2位变化的太快 return vlue; } /****************************** * 功能:计算1结果函数 将10bit的结果计算为对应电压值MV * 函数类型 float 型 浮点型 * 带结果返回 带结果返回 ******************************/ /* ulong count(uchar ch) { ADC_RESX=GetADCResult(ch); ADC_mV=(VCC_V*(long)ADC_RESX*10000/1024+5)/10;//强制转换长整型运算,得到结果mV(4舍5入) return ADC_mV; }*/ /****************************** * 功能:计算2结果函数 将10bit的结果计算为对应电压值MV * 函数类型 float 型 浮点型 * 带结果返回 带结果返回 ******************************/ float count(uchar ch) { float AD_val; //定义处理后的数值AD_val为浮点数 uchar n; for(n=0;n<100;n++) AD_val+=GetADCResult(ch); //转换100次求平均值(提高精度) AD_val/=100; //意思是GetADCResult(ch)经过了100次转换后的值保存到了AD_val里面 然后在 //除以100 就是求平均值 这样的精度高一点 // AD_val=(AD_val*5)/1024; //AD的参考电压是单片机上的5v,所以乘5即为实际电压值,应为分段计算这个就 //暂时不用 如果测量0-5v就可以这样计算 return AD_val; // temp = (float)GetADCResult(ch)*5/1024; //5表示单片机的供电 这个公式在手册上可以看到 //temp = (float) temp*5; // return temp; //这里的temp值就是最终的电压值MV 转换成V就在乘以1000 } //在显示屏函Dispy数里面已经做了倍率乘法 /*---------------------------- 功能 ADC初始化函数 ----------------------------*/ void InitADC() //adc初始化函数 { P1M1=0x01; //设置P1.0高阻模式 P1M0=0x00; //设置P1.0高阻模式 P1ASF = 0x01; //开放P1.0通道ADC功能 就是8个I/O口全部开放 ADC_RES = 0; //清除之前的结果 ADC_RESL = 0; ADC_CONTR = ADC_POWER | ADC_SPEEDLL; //打开AD电压 转换速度540 Delay(2); //ADC 延时一下 } /*---------------------------- Software delay function ----------------------------*/ void Delay(uint n) { uint x; while (n--) { x = 5000; while (x--); } } |
我发了 但是没有显示出来 我重新给您发一下 |
不将就123 发表于 2019-11-5 22:34 代码在哪? |
wulin 发表于 2019-11-5 12:45
但是现在出现的问题是 测量误差相当的大 我测量的是30V的电压 通过 10k 2k 分压后为5v 然后比例是6 结果当被测电压为3.0V的时候 单片机测量出来的就是6.78V 麻烦大哥看一下 是哪里出问题了 给我指导一下 非常感谢!!! 代码有些乱 费心了 |
不将就123 发表于 2019-11-4 20:52 STC12C5A60S2单片机的P1上电默认高电平为准双向口,用作ADC输入的端口必须设置为高阻模式,否则端口内部上拉电阻会影响测量精度,其它口保持准双向口模式可以另作它用。设置P1ASF寄存器的目的是开通MCU内部ADC模拟输入通道。其它口保持TTL模式可以另作它用。 ADC测量范围0~VCC,被测电压高于VCC需要通过分压电阻降到VCC以内,测量结果按降压比例放大还原。如果分压是电阻是 10K和2K,取样电压是实际电压的1/6,等测量结果换算成电压后*6就是实际电压值。 |
hhh402 发表于 2019-11-4 10:34 谢谢师傅 |
wulin 发表于 2019-11-4 16:49 可以说一下为什么吗? 谢谢你 大师 我现在要测量0-30V的电压 然后电阻分压是 10k 2k 5/30=0.167比率 然后我 用temp=temp/0.167 以后测量不出来啦 是不是我分离有问题 |
不将就123 发表于 2019-11-4 16:08 只一个P1.0口用作ADC P1ASF = 0x01;//设置P1.0做模拟输入 P1M1=0x01;//设置P1.0高阻模式 P1M0=0x00;//设置P1.0高阻模式 |
wulin 发表于 2019-11-4 11:34 这是为什么啊 我只用一个p1.0口 |
还要把P1端口设置为高阻模式。 P1M1=0xff; P1M0=0x00; |
wulin 发表于 2019-11-4 06:58 可以了 可以了 我没有给VCC_V赋值 |
wulin 发表于 2019-11-4 06:58 用了你这段还是不可以啊 |
主函数没有调用到:uchar GetADCResult(uchar ch), return (ADC_RES*4+ADC_RESL);//会发生数据溢出错误 改成:return ((uint)ADC_RES*4+ADC_RESL); |
//函数类型unsigned int uint GetADCResult(uchar ch) { ADC_CONTR=ADC_POWER|ADC_SPEEDLL|ch|ADC_START; _nop_(); _nop_(); _nop_(); _nop_(); while(!(ADC_CONTR&ADC_FLAG)); ADC_CONTR&=~ADC_FLAG; return (ADC_RES*4+ADC_RESL);//返回10位结果 } //函数类型unsigned int uint count(uchar ch) { ADC_RESX=GetADCResult(ch); ADC_mV=(VCC_V*(long)ADC_RESX*10000/1024+5)/10;//强制转换长整型运算,得到结果mV(4舍5入) return ADC_mV; } |