Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

How to read signal similar to half-wave rectifier output using PIC16F's ADC?

Status
Not open for further replies.

baileychic

Advanced Member level 3
Advanced Member level 3
Joined
Aug 2, 2017
Messages
728
Helped
56
Reputation
112
Reaction score
57
Trophy points
28
Activity points
7,033
How to read signal similar to half-wave rectifier output using PIC16F's ADC?

I am using PIC16F1779 and need to read a signal similar to half wave rectifier output whose peak value is 2.5V. The frequency of the signal is 100 Hz.
 

It doesn't matter that it looks like half a sine wave or anything else. What's important is what information you're trying to extract. If you only need to determine the peak value, you could just sample at something greater than 200Hz. (That's not completely true, but you get the idea). Can you explain a little more about your application?
 

I am just trying to read a signal generated from Signal generator. I have a 1N4148 diode connected in series (FB) with the signal line. The signal is a sine wave of 100 Hz and I want to read the peak value but with my code the values are fluctuating.

The sine wave signal is passed through 1N4148 before applying to ADC input pin. The sine wave signal value is 4Vpp.

This is my code.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
oldTemp = 0;
         do {
            temp = (unsigned int)ADC_Read(0);
            Delay_us(20);
 
            if(temp > oldTemp) {
               oldTemp = temp;
            }
         }while(oldTemp < temp);
         
         adc.rawAdcValue = oldTemp;
         //display raw adc value



PIC16F1779 with HX XTAL 8 MHz.
EasyPIC v7 development board
mikroC PRO PIC Compiler.

All variables used in ADC code are "unsigned int" type.
 

while(oldTemp < temp) makes no sense. If you start the measurement during falling input signal, it will immediately return with the instantaneous voltage instead of detecting the peak. You better wait for a specific amount of time.

The other problem is achieved sample rate, don't know the applied ADC configuration. If it's too slow, you'll miss the peak as well.
 
Hi,

You talk about a diode only, but you need to connect a "load", too.
Usually a simple resistor to GND.

Then you need to "reset" the peak value somehow.
* this can be done after a dedicated number of samples. Let's say after 1000 samples. I recommend to transmit the peak value just before resetting it to zero.
* or you do this my continously multiplying with a value close below "1". Maybe 0.999. But this needs high data width to operate correctly.
(It is similar to a high ohmic resistor connected to a capacitor as peak detector)
* or by subtracting 1 LSB every let's say 20 samples ... as long as the value is >1.
(This is similar to a constant current connected to a peak detecting capacitor)

Some hints:
* The diode causes a voltage drop. Consider to compensate for this.
* This solution is prone to noise. Every noise (peak!) may cause 1:1 error.
* If you want to calculate back to some RMS input value, then take the average of a known number of samples. Do the diode drop compensation and multiply the value. This is way more precise, because it (almost) cancels out noise.

Klaus
 

Why the delays in the measurement loop? If the "ADCRead()" function returns a valid result, the ADC is immediately ready to take the next measurement. 20uS isn't long but enough to make the result less accurate.

I would also be concerned about accuracy at low voltages, the diode doesn't drop a constant voltage across itself, the amount is not linear, particularly at the lowest voltages. You should either offset the voltage with a small DC 'pedestal' to keep the diode in it's conductive region or better still use a precision rectifier circuit.

Note that if you want the RMS value, there is no need to cater for polarity reversal so you can just use the 'Mean' or average of all the measurement but you need to take enough of them to cover the whole half cycle, no more and no less, and the total will be bigger than an unsigned int can hold so you need to think of using longer variables.

Brian.
 
I had even tried the timer method but that didn't read anything. Here is that code.

Okay I will add a resistor as load at RA0 pin (4.7k).


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// LCD module connections
sbit LCD_RS at LATB4_bit;
sbit LCD_EN at LATB5_bit;
sbit LCD_D4 at LATB0_bit;
sbit LCD_D5 at LATB1_bit;
sbit LCD_D6 at LATB2_bit;
sbit LCD_D7 at LATB3_bit;
 
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;
// End LCD module connections
 
#define Max_Output_Voltage  5.0
#define ADC_Resolution 1023.0
 
typedef struct {
  unsigned int rawAdcValue;
  unsigned int oldRawAdcValue;
  double adc.OutputVoltage;
}ADC_TYPE;
 
ADC_TYPE adc;
 
char txt[23];
 
unsigned int temp = 0;
unsigned int oldTemp = 0;
unsigned int i = 0;
 
const code char txt1[] = "ADC: ";
const code char txt2[] = "SV: ";
 
//Timer2
//Prescaler 1:16; Postscaler 1:5; TMR2 Preload = 250; Actual Interrupt Time : 10.0025 ms
void InitTimer2() {
    T2CON = 0x26;
    PR2 = 250;
    TMR2IE_bit = 1;
    TMR2ON_bit = 0;
    INTCON = 0xC0;
}
 
void interrupt() {        
    if((TMR2IE_bit) && (TMR2IF_bit)) {
        TMR2IF_bit = 0;
        
        if(++i >= 200) {
            TMR2ON_bit = 0;
            i = 0;
        }
    }
}
 
// copy const to ram string
char *CopyConst2Ram(char *dest, const code char *src) {
    char *d;
 
    d = dest;
 
    for(;*dest++ = *src++;);
 
    return d;
}
 
void initailizePorts() {
    TRISA = 0x31;
    TRISB = 0x00;
    TRISC = 0x01;
    TRISD = 0x00;
    TRISE = 0x00;
 
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
 
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
 
    ODCONA = 0x00;
    ODCONB = 0x00;
    ODCONC = 0x00;
    ODCOND = 0x00;
    ODCONE = 0x00;
 
    HIDRVB = 0x00;
 
    SLRCONA = 0x00;
    SLRCONB = 0x00;
    SLRCONC = 0x00;
    SLRCOND = 0x00;
    SLRCONE = 0x00;
}
 
void initailizeADC() {
    ANSELA = 0x37;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x00;
 
    ADCON0 = 0x00;
    ADCON1 = 0b11010000;
    ADCON2 = 0x00;
}
 
void initializeVariables() {
    adc.rawAdcValue = 0;
    adc.oldRawAdcValue = 0;    
}
 
void main() {
    initailizePorts();
    initailizeADC();
    initializeVariables();
 
    Delay_ms(200);
 
    Lcd_Init();
    Lcd_Cmd(_LCD_CURSOR_OFF);
    Lcd_Cmd(_LCD_CLEAR);
 
    Lcd_Out(1,1,CopyConst2Ram(txt,txt1));   
    Lcd_Out(1,10,CopyConst2Ram(txt,txt2));
 
    InitTimer2();
 
    while(1) {
         TMR2ON_bit = 1;
         oldTemp = 0;
         while(TMR2ON_bit) {
            temp = (unsigned int)ADC_Read(0);
            Delay_us(20);
            
            if(temp > oldTemp) {
               oldTemp = temp;
            }
         }
         
         adc.rawAdcValue = oldTemp;
         
         if(adc.oldRawAdcValue != adc.rawAdcValue) {
            IntToStr(adc.rawAdcValue,txt);
            Ltrim(txt);
            Rtrim(txt);
            Lcd_Out(1,5,txt);
            adc.OutputVoltage = (double)adc.rawAdcValue * Max_OpAmp_Output_Voltage / ADC_Resolution;
            sprintf(txt, "%4.2f", adc.OutputVoltage);
            Ltrim(txt);
            Rtrim(txt);
            Lcd_Out(1,13,txt);
            
            adc.oldRawAdcValue = adc.rawAdcValue;
         }         
    }
}

 

I don't use MikroC but that code doesn't look right to me at all.
I can't see why you are using TMR2 at all, if it is pace the ADC readings, the better method is to slow the ADC clock until you fit enough readings in one half cycle then divide by the number of readings to get the mean and find the highest to get the peak. Using the ADC clock to pace the readings gives better accuracy and less code is needed.

pseudo code:
1. read ADC until reading is zero (or your baseline voltage)
2. read ADC until voltage is not zero (start of half cycle you are measuring)
3. take ADC reading
4. add it to a running total
5. if > stored maximum, make reading the new maximum
6. increment count of number of measurements taken
7. go back to step 3 until measurement goes to zero (end of half cycle you are measuring)
8. stored maximum is the peak voltage
9. running total/number of measurements is the mean voltage

Brian.
 
I don't wany mean or average. I want only peak value that is if peak is 2.5V I need to display it as 2.5V.
 

Remove steps 2, 6 and 9. You need to factor a time constant into it or else treat each half cycle as a new check using steps 1, 2 and 7 so you define the time by the presence of any voltage.

Brian.
 
Hi,

I just did some calculation: If your mains frequency is 50Hz, pure sine and you want to find the peak with max 1 LSB error on a 10 bit ADC,
--> then you need a sampling period of not larger than 140us = about 7100Hz

Klaus
 

Alternative approach:
let the voltage charge a capacitor for a period of at least the half cycle, it should then hold peak voltage. Measure it, then discharge the capacitor through a resistor into another PIC pin. This method removes the need to keep sampling and by driving the 'discharge' pin low and using it's TRIS bit you can turn the discharge path on and off.

Brian.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top