找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2902|回复: 20
打印 上一主题 下一主题
收起左侧

stc12c5a60s2单片机内部AD功能为什么电压无法读取?

[复制链接]
跳转到指定楼层
楼主
stc12c5a60s2 为什么我这个电压无法读取 请大师看看呢    我是直接用的单片机5V供电的
#include <STC12C5A60S2.H>
#include "intrins.h"

//typedef unsigned char BYTE;
//typedef unsigned int WORD;

#define uchar unsigned char
#define uint  unsigned int
/*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为浮点型参数

uint num,i;

sbit    lcden = P2^7;
sbit    rs = P2^6;
sbit    rw = P2^5;


void Delay(uint n);              //延时函数
uchar GetADCResult(uchar ch);                 // ad转换函数
void InitADC();                                         //ad初始化函数
float  count(uchar ch);                     //ad计算函数
uchar code table[3]={"000"};
uchar code table2[10]={"zheng chao"};

/*  延时函数*/
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);  //延时
  
   
}

/*显示函数*********/
void  disp(uchar date3)
{        
     uchar bai,shi,ge;
     bai=date3/100;
         shi=date3/10%10;
         ge=date3%10;  
            lcd_write_com(0x80+0x10);
//         lcd_disp_char(1,0,(0x30+bai) );
         write_date_(0x30+bai);
     write_date_(0x30+shi);
         write_date_(0x30+ge);
//   lcd_disp_char(1,1,(0x30+shi) );
//   lcd_disp_char(1,2,(0x30+ge) );


}

void init()

{

        lcd_write_com(0x80+0x10);           //定义显示的位置 起始地址     
        for(num=0;num<3;num++)
                {        
               
                        write_date_(table[num]);               //初始化屏幕的初始数字“000”
                        delayms(5);                          
                }

        lcd_write_com(0x80+0x40+0x10+3);   //定义第二排,显示的地址 0x80是显示屏寄存器第一排起始地址
    for(num=0;num<10;num++)                         //0x40是第二排起始地址
              {
                           write_date_(table2[num]);
                           delayms(5);                  
                  }
                  for(num=0;num<16;num++)        //整屏左移动
                  {
                      lcd_write_com(0x18);
                          delayms(500);
                     }        
                  

}



void main()

{
    lcd_init();
        init();
    InitADC();                      //Init ADC sfr
    while (1)
    {

            i=0;
      while(i<1000)
           {
              
        disp((int)count(0)*100);
                delayms(500);        
            i++;
           }
    }
}

/*************************
*  功能        ADC转换函数
*  带结果返回  带结果返回
**************************/

uchar 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
    return (ADC_RES*4+ADC_RESL);            //返回结果  也就是电压值
}
/******************************
*  功能        计算结果函数
*  函数类型    float 型  浮点型
*  带结果返回  带结果返回
******************************/

float  count(uchar ch)
{
        
   temp=(float)GetADCResult(ch)*4.85/1024;

   return temp;
}


/*----------------------------
功能   ADC初始化函数
----------------------------*/
void InitADC()                                //adc初始化函数
{
    P1ASF = 0xff;                   //开放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--);
    }
}



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:213173 发表于 2019-11-4 06:58 | 只看该作者
//函数类型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;
}
回复

使用道具 举报

板凳
ID:491577 发表于 2019-11-4 10:34 | 只看该作者
主函数没有调用到:uchar GetADCResult(uchar ch),
return (ADC_RES*4+ADC_RESL);//会发生数据溢出错误
改成:return ((uint)ADC_RES*4+ADC_RESL);
回复

使用道具 举报

地板
ID:254747 发表于 2019-11-4 10:48 | 只看该作者
wulin 发表于 2019-11-4 06:58
//函数类型unsigned int
uint GetADCResult(uchar ch)
{

用了你这段还是不可以啊
回复

使用道具 举报

5#
ID:254747 发表于 2019-11-4 11:06 | 只看该作者
wulin 发表于 2019-11-4 06:58
//函数类型unsigned int
uint GetADCResult(uchar ch)
{

可以了 可以了  我没有给VCC_V赋值
回复

使用道具 举报

6#
ID:213173 发表于 2019-11-4 11:34 | 只看该作者
还要把P1端口设置为高阻模式。
P1M1=0xff;
P1M0=0x00;
回复

使用道具 举报

7#
ID:254747 发表于 2019-11-4 16:08 | 只看该作者
wulin 发表于 2019-11-4 11:34
还要把P1端口设置为高阻模式。
P1M1=0xff;
P1M0=0x00;

这是为什么啊  我只用一个p1.0口
回复

使用道具 举报

8#
ID:213173 发表于 2019-11-4 16:49 | 只看该作者
不将就123 发表于 2019-11-4 16:08
这是为什么啊  我只用一个p1.0口

只一个P1.0口用作ADC
P1ASF = 0x01;//设置P1.0做模拟输入
P1M1=0x01;//设置P1.0高阻模式
P1M0=0x00;//设置P1.0高阻模式
回复

使用道具 举报

9#
ID:254747 发表于 2019-11-4 20:52 | 只看该作者
wulin 发表于 2019-11-4 16:49
只一个P1.0口用作ADC
P1ASF = 0x01;//设置P1.0做模拟输入
P1M1=0x01;//设置P1.0高阻模式

可以说一下为什么吗?   谢谢你 大师    我现在要测量0-30V的电压    然后电阻分压是 10k  2k     5/30=0.167比率   然后我 用temp=temp/0.167   以后测量不出来啦    是不是我分离有问题
回复

使用道具 举报

10#
ID:254747 发表于 2019-11-4 20:53 | 只看该作者
hhh402 发表于 2019-11-4 10:34
主函数没有调用到:uchar GetADCResult(uchar ch),
return (ADC_RES*4+ADC_RESL);//会发生数据溢出错误
...

谢谢师傅
回复

使用道具 举报

11#
ID:213173 发表于 2019-11-5 12:45 | 只看该作者
不将就123 发表于 2019-11-4 20:52
可以说一下为什么吗?   谢谢你 大师    我现在要测量0-30V的电压    然后电阻分压是 10k  2k     5/30=0 ...

    STC12C5A60S2单片机的P1上电默认高电平为准双向口,用作ADC输入的端口必须设置为高阻模式,否则端口内部上拉电阻会影响测量精度,其它口保持准双向口模式可以另作它用。设置P1ASF寄存器的目的是开通MCU内部ADC模拟输入通道。其它口保持TTL模式可以另作它用。
    ADC测量范围0~VCC,被测电压高于VCC需要通过分压电阻降到VCC以内,测量结果按降压比例放大还原。如果分压是电阻是 10K和2K,取样电压是实际电压的1/6,等测量结果换算成电压后*6就是实际电压值。
回复

使用道具 举报

12#
ID:254747 发表于 2019-11-5 22:34 | 只看该作者
wulin 发表于 2019-11-5 12:45
STC12C5A60S2单片机的P1上电默认高电平为准双向口,用作ADC输入的端口必须设置为高阻模式,否则端口 ...
  1. <blockquote>#include <STC12C5A60S2.H>
复制代码
大哥呀  我被这个东西折磨坏了    我设置了高阻态模式  同时 也设置了 P1ASF = 0x01;   
但是现在出现的问题是   测量误差相当的大  
我测量的是30V的电压   通过  10k 2k 分压后为5v  然后比例是6   结果当被测电压为3.0V的时候   单片机测量出来的就是6.78V  麻烦大哥看一下  是哪里出问题了  给我指导一下  非常感谢!!!

代码有些乱   费心了
回复

使用道具 举报

13#
ID:213173 发表于 2019-11-6 06:57 | 只看该作者
不将就123 发表于 2019-11-5 22:34
大哥呀  我被这个东西折磨坏了    我设置了高阻态模式  同时 也设置了 P1ASF = 0x01;   
但是现在出现的 ...

代码在哪?
回复

使用道具 举报

14#
ID:254747 发表于 2019-11-6 11:10 | 只看该作者
我发了  但是没有显示出来 我重新给您发一下
回复

使用道具 举报

15#
ID:254747 发表于 2019-11-6 11:10 | 只看该作者
#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--);
    }
}




回复

使用道具 举报

16#
ID:254747 发表于 2019-11-6 11:11 | 只看该作者

大哥  我重新发了一次   您在给我看看
回复

使用道具 举报

17#
ID:254747 发表于 2019-11-6 22:07 | 只看该作者

可以了 大哥   哎呀 我知道为什么我的高阻模式不管用了  是应为我在开发板上面弄的  这个上面有上拉电阻 导致p1^0始终都有电压存在 这才导致电压检测误差特别大  我现在把这个叫 拿出来 就对了 程序没有问题   但是我是参考网上的  这里有一句我不是特别懂 为什么要/100       temp=(ADC_jiancedaozuidivoltage*AD_jie_guo);
         temp=(temp*6)/100;    //这里为什么要除以100   麻烦讲一下  谢谢      

IMG20191106215829.jpg (3.2 MB, 下载次数: 52)

IMG20191106215829.jpg

IMG20191106215832.jpg (2.7 MB, 下载次数: 38)

IMG20191106215832.jpg
回复

使用道具 举报

18#
ID:254747 发表于 2019-11-6 22:09 | 只看该作者
感谢论坛的各位朋友  问题现在已经解决了  是开发板上面的 上拉电阻导致的误差  将I0口拿出来测试 问题得到完美的解决了      
temp=(ADC_jiancedaozuidivoltage*AD_jie_guo);
temp=(temp*6)/100;   //这里为什么要/100     这个是我参考网上大师做的     
                        
回复

使用道具 举报

19#
ID:213173 发表于 2019-11-7 10:57 | 只看该作者
不将就123 发表于 2019-11-6 22:07
可以了 大哥   哎呀 我知道为什么我的高阻模式不管用了  是应为我在开发板上面弄的  这个上面有上拉电阻  ...

给你把程序改好了,有详细注释。
电压表.zip (18.9 KB, 下载次数: 32)



回复

使用道具 举报

20#
ID:254747 发表于 2019-11-9 13:05 | 只看该作者
wulin 发表于 2019-11-7 10:57
给你把程序改好了,有详细注释。

非常感谢 我下载了 我需要好好去学习哈
回复

使用道具 举报

21#
ID:600631 发表于 2020-1-29 09:47 来自手机 | 只看该作者
wulin 发表于 2019-11-7 10:57
给你把程序改好了,有详细注释。

师傅,这个程序,如果再增加一路测试电压的话,程序应该怎么写呢
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表