搜索
bottom↓
回复: 19

C8051f120内置12位ADC采集不了交流信号

[复制链接]

出0入0汤圆

发表于 2012-5-18 20:32:19 | 显示全部楼层 |阅读模式
我用C8051f120内置12位ADC,直流信号可以采集,但采集的交流信号转换为模拟电压却总是等于Vref值,这个是为什么呀?
我是直接将函数发生器的输出(交流叠加直流所有信号为正极性)加到AIN0.0脚的。下面是我的源程序:
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <math.h>
#include "c8051f120.h"
#include "FFT_Code_Tables.h" // Code Tables for FFT routines
//-----------------------------------------------------------------------------
// 16-bit SFR Definitions for ‘F12x
//-----------------------------------------------------------------------------
sfr16 DP = 0x82; // data pointer
sfr16 ADC0 = 0xbe; // ADC0 data
sfr16 ADC0GT = 0xc4; // ADC0 greater than window
sfr16 ADC0LT = 0xc6; // ADC0 less than window
sfr16 RCAP2 = 0xca; // Timer2 capture/reload
sfr16 RCAP3 = 0xca; // Timer3 capture/reload
sfr16 RCAP4 = 0xca; // Timer4 capture/reload
sfr16 TMR2 = 0xcc; // Timer2
sfr16 TMR3 = 0xcc; // Timer3
sfr16 TMR4 = 0xcc; // Timer4
sfr16 DAC0 = 0xd2; // DAC0 data
sfr16 DAC1 = 0xd2; // DAC1 data
sfr16 PCA0CP5 = 0xe1; // PCA0 Module 5 capture
sfr16 PCA0CP2 = 0xe9; // PCA0 Module 2 capture
sfr16 PCA0CP3 = 0xeb; // PCA0 Module 3 capture
sfr16 PCA0CP4 = 0xed; // PCA0 Module 4 capture
sfr16 PCA0 = 0xf9; // PCA0 counter
sfr16 PCA0CP0 = 0xfb; // PCA0 Module 0 capture
sfr16 PCA0CP1 = 0xfd; // PCA0 Module 1 capture

//-----------------------------------------------------------------------------
// Global CONSTANTS and Variable Type Definitions
//-----------------------------------------------------------------------------
#define NUM_BITS 16         // Number of Bits in Data
#define DATA_BEGIN 0x0000         // Beginning of XRAM Data
#define SYSCLK 49000000         // Output of PLL derived from
                                        // (INTCLK*2/1)

#define VREF 3.3               //参考电压
#define SAMPLE_RATE 20480                // Sample frequency in Hz
#define RUN_ONCE 1     // Setting to a non-zero value will
                       // cause the program to stop after one
                       // data set.
unsigned int xdata Redata[NUM_FFT];
char xdata data_H[NUM_FFT],data_L[NUM_FFT];
int xdata i;
//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------

void SYSCLK_Init (void);
void PORT_Init (void);
void ADC0_Init (void);
void TIMER3_Init (int counts);
void ADC0_ISR (void);
void delay_us(unsigned int);


unsigned int BinNum;
bit Conversion_Set_Complete; // This indicates when the data has been
                                          // stored, and is ready to be processed
                                           // using the FFT routines



//-----------------------------------------------------------------------------
// MAIN Routine
//-----------------------------------------------------------------------------
void main()
{
   int xdata i;
   unsigned int xdata data1[NUM_FFT],data2[NUM_FFT],data3[NUM_FFT];
   float xdata Voltage[NUM_FFT];
   char xdata data_H1[NUM_FFT],data_L1[NUM_FFT];
//disable watchdog timer
WDTCN = 0xde;
WDTCN = 0xad;

SYSCLK_Init();                 // initialize external clock and PLL
PORT_Init ();                 // set up Port I/O
TIMER3_Init (SYSCLK/SAMPLE_RATE);                 // initialize Timer3 to overflow at
                                                // <SAMPLE_RATE>
ADC0_Init ();                 // init ADC0

EA = 1;                 // globally enable interrupts
while (1)
{
ADC_Index = 0;
Conversion_Set_Complete = 0;
EIE2 |= 0x02;                         // enable ADC interrupts

SFRPAGE = LEGACY_PAGE;

while(!Conversion_Set_Complete);

for(i=0;i<=NUM_FFT;i++)
{
data1[i]=Real[i];
data_H1[i] = data_H[i];
data_L1[i] = data_L[i];
data2[i] = data1[i] >> 4;
Voltage[i]=data2[i]/4096.0*VREF;    //计算实际电压
}
   
if (RUN_ONCE)
while(1);

}
} // END MAIN


//-----------------------------------------------------------------------------
// SYSCLK_Init
//-----------------------------------------------------------------------------
void SYSCLK_Init (void)                 //49MHz
{
        int i;
    char old_SFRPAGE = SFRPAGE;                 // Store current
    SFRPAGE   = CONFIG_PAGE;
    OSCICN    = 0x83;
    CCH0CN    &= ~0x20;
    SFRPAGE   = LEGACY_PAGE;
    FLSCL     = 0x90;
    SFRPAGE   = CONFIG_PAGE;
    CCH0CN    |= 0x20;
    PLL0CN    |= 0x01;
    PLL0DIV   = 0x01;
    PLL0FLT   = 0x21;
    PLL0MUL   = 0x02;
    for (i = 0; i < 15; i++);  // Wait 5us for initialization
    PLL0CN    |= 0x02;
    while ((PLL0CN & 0x10) == 0);
    CLKSEL    = 0x22;

    SFRPAGE = old_SFRPAGE;                 // restore SFRPAGE
}

void PORT_Init (void)       
{
SFRPAGE   = CONFIG_PAGE;
    XBR2      = 0x40;
}

//-----------------------------------------------------------------------------
// ADC0_Init
//-----------------------------------------------------------------------------
//
// Configure ADC0 to use Timer3 overflows as conversion source, to
// generate an interrupt on conversion complete, and to use left-justified
// output mode. Enables ADC0 end of conversion interrupt. Enables ADC0.
//
void ADC0_Init (void)
{
char old_SFRPAGE = SFRPAGE;         // Store current SFRPAGE
SFRPAGE = ADC0_PAGE;                 // Switch to ADC0 Setup Page
ADC0CN = 0x05;                 // ADC disabled; normal tracking
                        // mode; ADC conversions are initiated
                        // on overflow of Timer3, left-justify
REF0CN = 0x03;                 // enable on-chip VREF and output buffer
AMX0CF = 0x00;                 // Single-ended AIN0.0 input
AMX0SL = 0x00;
ADC0CF = (SYSCLK/(2*2500000)) << 3;         // ADC conversion clock <= 2.5MHz
ADC0CF |= 0x00;         // PGA gain = 1
AD0EN = 1;         // enable ADC0
SFRPAGE = old_SFRPAGE;         // restore SFRPAGE
}

//-----------------------------------------------------------------------------
// TIMER3_Init
//-----------------------------------------------------------------------------
//
// Configure Timer3 to auto-reload at interval specified by <counts> (no
// interrupt generated) using SYSCLK as its time base.
//
void TIMER3_Init (int counts)
{
char old_SFRPAGE = SFRPAGE;         // Save Current SFR page
SFRPAGE = TMR3_PAGE;         // Switch to Timer3 Setup Page
TMR3CN = 0x00;                 // Stop Timer3; Clear TF3
TMR3CF = 0x08;                 // use SYSCLK as timebase
RCAP3 = -counts;         // Init reload values
TMR3 = 0xffff;                 // set to reload immediately
EIE2 &= ~0x01;                 // disable Timer3 interrupts
TR3 = 0x01;                 // start Timer3
SFRPAGE = old_SFRPAGE;                 // restore SFRPAGE
}

//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ADC0_ISR
//-----------------------------------------------------------------------------
//// ADC end-of-conversion ISR
// The ADC sample is stored in memory, and an index variable is incremented.
// If enough samples have been taken to process the FFT, then a flag is set,
// and ADC interrupts are disabled until the next set is requested.
//
void ADC0_ISR (void) interrupt 15 using 3
{

AD0INT = 0;         // clear ADC conversion complete
                // flag

Real[ADC_Index] = ADC0;         // store ADC value
data_H[ADC_Index] = ADC0H;
data_L[ADC_Index] = ADC0L;
ADC_Index++;                         // Increment the index into memory
if (ADC_Index >= NUM_FFT)         // If enough samples have been collected
{
Conversion_Set_Complete = 1;                 // Tell the Main Routine and...
EIE2 &= ~0x02;                         // disable ADC interrupts
}
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2012-5-18 20:40:48 | 显示全部楼层
交流信号是电流的方向发生变化的信号。 你要测的信号应该还是直流信号。  是不是超出量程了?

出0入0汤圆

发表于 2012-5-18 21:43:13 | 显示全部楼层
单电源供电的AD都不能处理电压比GND还负的电压的,你的输入信号是不是有时候比GND还负?

出0入0汤圆

 楼主| 发表于 2012-5-19 10:05:15 | 显示全部楼层
tangxh 发表于 2012-5-18 20:40
交流信号是电流的方向发生变化的信号。 你要测的信号应该还是直流信号。  是不是超出量程了? ...

首先没有超出量程!其次我想问,我是想采集正弦信号并对其进行FFT变换,在频率域分析它的频率和功率的。AD用的是c8051f120内置12位ADC,由于ADC的输入必须是极性为正的信号,所以我在前级处理电路中给正弦信号叠加一个直流,让输入ADC的信号全部为正极性信号。可是加上前级处理电路之后,ADC采样的结果转换成电压依然全部为Vref,这个怎么解释呀?

出0入0汤圆

 楼主| 发表于 2012-5-19 10:07:27 | 显示全部楼层
locky_z 发表于 2012-5-18 21:43
单电源供电的AD都不能处理电压比GND还负的电压的,你的输入信号是不是有时候比GND还负? ...

没有输入负极性的电压信号。我在前几处理电路中加了一级极性变换电路,就是给正弦信号加一直流电压,让其变为正极性信号,再接入ADC的。

出0入0汤圆

发表于 2012-5-19 12:39:43 | 显示全部楼层
myduanning 发表于 2012-5-19 10:05
首先没有超出量程!其次我想问,我是想采集正弦信号并对其进行FFT变换,在频率域分析它的频率和功率的。 ...

你这个做法是对的,大部分交流采样都是这样做的。  你用示波器看看经过电平抬升电路之后的信号是什么样子的。   

出0入0汤圆

发表于 2012-5-19 17:58:35 | 显示全部楼层
很巧 我去年准备全国电赛的时候 用f020做的音频信号分析仪 你是做这个题吧  当时的做法 和你一样 也是交流+直流 后来用一个1602显示测量的电压值 类似于一个交流电压表吧 经过FFT后 还可以看直流分量等信息    当时我就是研究者一块的 你采数的时候 看看板子上是不是有跳线帽需要调整  看看是内基准还是外部基准 这些问题都注意一下 如果还不行 再给我回复  再一起交流

出0入442汤圆

发表于 2012-5-20 00:22:42 | 显示全部楼层
用两颗高精度电阻串接VCC和GND,取中点和ADC输入就OK,不要对输入信号处理,除了降压,要不然单片机可能烧坏。怀疑是你的前级电容太大了。

出0入0汤圆

发表于 2012-5-20 00:39:07 | 显示全部楼层
思路 很强悍

出0入4汤圆

发表于 2012-5-20 00:49:03 | 显示全部楼层
你先加一个固定电压测试一下是不是能转换嘛,基本的排错方法都没有么?

出0入127汤圆

发表于 2012-5-20 11:10:47 | 显示全部楼层
myduanning 发表于 2012-5-19 10:07
没有输入负极性的电压信号。我在前几处理电路中加了一级极性变换电路,就是给正弦信号加一直流电压,让其 ...

如果你抬高的电平接近Vref了,测出来不是最大值才怪,先一部分一部分找错误,就一个结果怎么知道是哪里错了?

出0入0汤圆

 楼主| 发表于 2012-5-23 18:50:12 | 显示全部楼层
tangxh 发表于 2012-5-19 12:39
你这个做法是对的,大部分交流采样都是这样做的。  你用示波器看看经过电平抬升电路之后的信号是什么样子 ...

用示波器看,电压抬高后的信号是全为正极性的、大小变化按正弦信号变化规律,并且信号最大值也未超过Vref。这样的信号输入单片机ADC,采样后的结果全是Vref值,很纳闷!

出0入0汤圆

 楼主| 发表于 2012-5-23 18:59:23 | 显示全部楼层
OyutianO 发表于 2012-5-19 17:58
很巧 我去年准备全国电赛的时候 用f020做的音频信号分析仪 你是做这个题吧  当时的做法 和你一样 也是交流+ ...

真巧!我就是做这个题目的,我的思路跟你的基本一样,只不过我用的是C8051F120而已。我用的是外部基准电压,基准电压端接的是VDD3.3V,这样可测量的信号的幅值范围能稍微大一点,内部基准在2.5V左右。我把前级电路处理后的正极性、大小变化按正弦规律、最大值未超过Vref值的信号送入单片机片内ADC,采样转换的结果存放在一个数组,程序下到板子运行结束后看采样转换后的值,全部为Vref,很纳闷!

出0入0汤圆

 楼主| 发表于 2012-5-23 19:02:34 | 显示全部楼层
wajlh 发表于 2012-5-20 00:49
你先加一个固定电压测试一下是不是能转换嘛,基本的排错方法都没有么?

测试了呀!纯直流可以,直流加交流就不对了呀!理论分析没问题,检查电路也没检查出问题,所以求助各路大侠,看问题有可能消除在哪儿?

出0入0汤圆

 楼主| 发表于 2012-5-23 19:18:08 | 显示全部楼层
wye11083 发表于 2012-5-20 00:22
用两颗高精度电阻串接VCC和GND,取中点和ADC输入就OK,不要对输入信号处理,除了降压,要不然单片机可能烧 ...

前级电容太大,为什么,它会影响按正弦规律变化的信号的采样吗?我在相关资料上看到说“由A/D转换芯片的模拟输入端口为容性负载,对输入信号会造成严重的波形失真采用两级运放可以消除误差”的啊!

出0入442汤圆

发表于 2012-5-23 20:32:42 | 显示全部楼层
myduanning 发表于 2012-5-23 19:18
前级电容太大,为什么,它会影响按正弦规律变化的信号的采样吗?我在相关资料上看到说“由A/D转换芯片的 ...

很简单了,你接了两级运放,问题就出在这两级运放上。有几种可能,
运放带宽不够。这个没办法,想办法调增益以改善。
运放干扰大。这个得想办法屏蔽了。
运放输入电阻太小。这个也得调增益。

出0入0汤圆

 楼主| 发表于 2012-5-23 22:07:42 | 显示全部楼层
wye11083 发表于 2012-5-23 20:32
很简单了,你接了两级运放,问题就出在这两级运放上。有几种可能,
运放带宽不够。这个没办法,想办法调 ...

谢谢!
经过检测以及初步判断,运放部分不是问题的原因,问题出在了单片机的内置ADC上,今天又测试,只要输入信号大于0,即使是0.5V直流,采样转换后的结果依然会是Vref,我怀疑是单片机出问题了,不过还有待进一步的确认。

出0入0汤圆

 楼主| 发表于 2012-5-24 11:46:26 | 显示全部楼层
myduanning 发表于 2012-5-23 18:59
真巧!我就是做这个题目的,我的思路跟你的基本一样,只不过我用的是C8051F120而已。我用的是外部基准电 ...

你好!
能留下你的邮箱吗?这样方便联系!

出70入0汤圆

发表于 2013-4-13 17:02:28 | 显示全部楼层
myduanning 发表于 2012-5-23 22:07
谢谢!
经过检测以及初步判断,运放部分不是问题的原因,问题出在了单片机的内置ADC上,今天又测试,只要 ...

我最近用到F120的ADC2也遇到了和你一样的问题,输入模拟电压只要大于0.5V转换结果就是255。还没找到原因,莫非如你所说单片机有问题?!

出70入0汤圆

发表于 2013-4-15 09:55:15 | 显示全部楼层
myduanning 发表于 2012-5-23 22:07
谢谢!
经过检测以及初步判断,运放部分不是问题的原因,问题出在了单片机的内置ADC上,今天又测试,只要 ...

我的F120的ADC2遇到和你一样的问题,现在已经解决了,用Configuration Wizard重新生成代码后发现和以前的很多初始化函数和一些寄存器(以前的代码是修改别人的)的调用前后顺序很多都变化了,个人觉得是这个原因,F120这片子用着真是蛋疼。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-18 10:45

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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