This PIC MCU project shows how to measure AC voltage up to 500V using PIC18F46K22 8-bit microcontroller. The RMS (effective) value of the voltage under measure is sent to PC which can be printed on any serial monitor software and also displayed on 16×2 LCD screen.
CCS C Compiler is used in this project.
Hints:
No warranty is provided with this project, so do it at your own risk!
A part of project circuit may be subjected to a high voltage level which is very harmful to human body, so please be-careful!
Abbreviations:
AC: Alternating Current.
DC: Direct Current.
TRMS: True Root Mean Square.
ADC: Analog-to-Digital Converter
AC Voltage measurement using PIC18F46K22 microcontroller circuit:
Project circuit diagram is shown below (click on the image for better view).
Hardware required:
This is a summary of circuit required parts (circuit schematic diagram may contain some component parameters not mentioned below).
- PIC18F46K22 microcontroller (U2) —> datasheet
- DL-PT202D 2mA/2mA voltage sensor transformer (T1)
- OPA2191 op amp (U3) —> details
- MCP1501-20 2.048 voltage reference (U4) —> details
- TPS60403 charge pump voltage inverter (U1) —> details
- 1602 LCD screen
- 2 x 2.2uF capacitor (C9, C10)
- 3 x 1uF capacitor (C1, C2, C3)
- 0.22uF capacitor (C6)
- 3 x 0.1uF capacitor (C4, C5, C7)
- 100pF capacitor (C8)
- 5 x 100k ohm resistor (R1, R2, R3, R4, R11)
- 2 x 20k ohm resistor (R5, R10)
- 3 x 10k ohm resistor (R7, R9, R13)
- 330 ohm resistor (R15)
- 120 ohm resistor (R8)
- 100 ohm resistor (R6)
- 10 ohm resistor (R12)
- 10k ohm variable resistor (R14)
- FTDI FT232RL USB to serial UART converter module (optional)
- 5V source
- Breadboard & jumper wires
Circuit description:
The voltage to measure terminals is indicated in the circuit diagram as ‘AC Source’, we can apply an AC voltage up to 500V, this means we can measure home phase-to-natural (230V) and phase-to-phase (400V) voltages.
The two AC voltage source terminals are connected to DL-PT202D transformer (T1) through 3 current limiting resistors each of 100k ohm (R2, R3 & R4), the tolerance of these resistors should be 1% or lower in order to get better results, voltage rating of each resistor should be greater than or equal to 200V with rated power of 0.5W or higher. This transformer is just a current-type voltage transformer with ratio of 1000:1000 which converts an AC current on the primary to another equal one on the secondary. The maximum working current that may be applied to the primary is 2 mA RMS.
There are many alternatives of the voltage transformer that I’m using such as ZMPT112, any other replacement should have the same voltage rating (or higher) and ratio (2mA/2mA).
This type of transformers gives a significant safety factor to the circuit as it provide a galvanic isolation between the high voltage mains circuit and the low voltage control circuit which is in a direct contact with human body and other equipments. They also have a small size, low cost and require few components.
Since the voltage transformer used in this project is a current type then its secondary shouldn’t be kept floating, a resistor (R8) with resistance of 120 Ohm is connected in parallel with the secondary of the voltage transformer and hence the current is converted to voltage.
The ratio of the voltage transformer circuit including the resistors R2, R3, R4 & R8 is:
120/300k = 1/2500.
That means a voltage of 400V RMS will be scaled down to 0.16 V = 160 mV RMS.
The voltage across the resistor R8 is amplified using OPA2191 differential amplifier from Texas Instruments, with a gain of 5.
The OPA2191 IC contains 2 independent op amps in one package which indicated in the circuit diagram by U3:A and U3:B. A bipolar supply of ±5 V is used to power the IC.
To get a negative voltage of -5 V I used the TPS60403 60mA charge pump voltage inverter from Texas Instruments. The TPS60403 device automatically generates -5 V from the +5 V supply.
The second op amp of the OPA2191 with resistors R7 & R9 are used to apply a gain of 0.5 and add an offset voltage of 1.024 V to the input signal. The OPA2191 also provides a low impedance source to the PIC18F46K22 microcontroller ADC converter.
With the two op amp circuits gains (5 and 0.5) the overall gain of the circuit becomes:
5 x 0.5 x 1/2500 = 1/1000,
so with an applied sine wave AC voltage of 500 V the output will be 500 mV (707 mV peak).
The MCP1501-20 high-precision buffered voltage reference from Microchip is used to get a precise voltage of 2.048V which is then used as positive voltage reference for the PIC18F46K22 microcontroller ADC module. Its filtered output is connected the MCU RA3 pin (#5).
The 1602 LCD screen (2 rows and 16 columns) is used to display the RMS value of the applied voltage, it is connected to the PIC18F46K22 MCU as follows:
RS —> pin RD0
RW —> pin RD1
E —> pin RD2
D4 —> pin RD3
D5 —> pin RD4
D6 —> pin RD5
D7 —> pin RD6
VSS, D0, D1, D2, D3 and K are connected to circuit ground
VEE to the variable resistor (or potentiometer) output
VDD to +5V and A to +5V through 330 ohm resistor.
VEE pin is used to control the contrast of the LCD. A (anode) and K (cathode) are the back light LED pins.
The FTDI FT232RL USB to UART module is used as an interface between the PIC18F46K22 MCU and the laptop, the MCU sends voltage values to the laptop through this module, any software may be used to display the data such as CCS C IDE or Arduino serial monitor tools.
The internal oscillator of the PIC18F46K22 is used @ 16MHz.
AC Voltage measurement using PIC18F46K22 microcontroller C code:
The C code below is for CCS C compiler, it was tested with version 5.088.
This C code calculates average and RMS values of AC signals, the average value is just used in the RMS calculations. The MCU only shows the RMS value of the applied voltage on the LCD and serial monitor.
Simply the average (mean or DC offset) value in discrete-time is the sum of all sample values divided by number of samples:
and the RMS value in discrete-time can be calculated using the following equation:
The PIC18F46K22 microcontroller contains a 10-bit ADC module, with a positive voltage reference of 2.048 V, a 0 V is digitally represented by 0 and a 2.048 V is represented by 1023.
Programming hints:
As we have a 10-bit ADC module working in the range [0, 2.048V], the MCU makes 64 samples every 1 cycle and save the sampled data in an array named _array.
For a 50 Hz signal the period is 20 ms, the MCU takes one sample every 20000/64 = 312.5 us. I used Timer 2 interrupt every 312.5 us to initiate a new analog to digital conversion and once the conversion is complete ADC interrupt saves the digital value on the the array variable _array. The array element is incremented every new reading until the filling of all elements and the completion of the cycle.
The setting of Timer 2 for 50 Hz waveform is:
1 2 | // Timer 2 setup: prescaler = 1, period = 249, postscaler = 5 (for 50 Hz waveform @ 16 MHz MCU clock) setup_timer_2 ( T2_DIV_BY_1, 249, 5 ); |
For a 60 Hz signal we can use the following setting for Timer 2:
setup_timer_2 ( T2_DIV_BY_1, 208, 5 );
Full CCS C code:
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 | /************************************************************************** AC Voltage measurement using PIC18F46K22 microcontroller. Voltage values are printed on 1602 LCD screen and serial monitor. C Code for CCS C compiler. This is a free software with NO WARRANTY - Use it at your own risk!. http://simple-circuit.com/ /**************************************************************************/ // LCD module connections #define LCD_RS_PIN PIN_D0 #define LCD_RW_PIN PIN_D1 #define LCD_ENABLE_PIN PIN_D2 #define LCD_DATA4 PIN_D3 #define LCD_DATA5 PIN_D4 #define LCD_DATA6 PIN_D5 #define LCD_DATA7 PIN_D6 // end LCD module connections #include <18F46K22.h> #device ADC = 10 #fuses NOMCLR,NOLVP,NOBROWNOUT,PUT,NOXINST #use delay(internal = 16MHz) #use rs232(xmit = PIN_C3, rcv = PIN_C2, baud = 9600) #include <stdint.h> #include <lcd.c> // include LCD driver source file #INCLUDE <math.h> const uint8_t n = 64; // number of samples int16_t _array[n]; uint8_t m = 0; uint16_t _offset = 0; // Timer2 ISR #INT_TIMER2 void Timer2_ISR(void) { read_adc(ADC_START_ONLY); } // ADC ISR #INT_AD void ADC_ISR(void) { _array[m] = read_adc(ADC_READ_ONLY); _offset += _array[m]; m++; } // main function void main(void) { setup_adc(ADC_CLOCK_DIV_16); // set ADC module clock source // configure AN0 pin as analog & set voltage references to: VSS - VREF (external voltage reference) setup_adc_ports(sAN0 | VSS_VREF); set_adc_channel(0); // select AN0 channel enable_interrupts(GLOBAL); // enable global interrupts // Timer 2 setup: prescaler = 1, period = 249, postscaler = 5 (for 50 Hz waveform @ 16 MHz MCU clock) setup_timer_2 ( T2_DIV_BY_1, 249, 5 ); delay_ms(500); lcd_init(); // initialize LCD module lcd_gotoxy(1, 1); // go to column 1 row 1 lcd_putc ("RMS Voltage ="); while (TRUE) { _offset = 0; uint32_t ch_voltage = 0; set_timer2(0); clear_interrupt(INT_TIMER2); // clear Timer2 interrupt flag bit enable_interrupts(INT_TIMER2); // enable Timer2 interrupt clear_interrupt(INT_AD); // clear ADC interrupt flag bit enable_interrupts(INT_AD); // enable ADC interrupt while (m < n) ; // wait for 1 cycle to complete (duration of 20 ms @ 50 Hz) m = 0; disable_interrupts(INT_AD); // disable ADC interrupt disable_interrupts(INT_TIMER2); // disable Timer2 interrupt _offset = _offset / n; // caculate signal average value (dc offset) // calculate voltage RMS value for ( uint8_t i = 0; i < n; i++ ) { uint16_t j = abs( _array[i] - _offset ); ch_voltage += ( (uint32_t)j * j); } ch_voltage = ch_voltage / n; ch_voltage = sqrt(ch_voltage) * 10.0; // 10.0 to get a more precise value // Now calculate real voltage RMS value applied to MCU analog channel // 2 = 2048/1024 where 2048 is ADC +ive VREF = 2048 mV and 1024 is 10-bit ADC max digital value // Note that this voltage is the same as the AC voltage under measure (in Volts) as the hardware circuit ratio is 1000:1 ch_voltage = ch_voltage * 2 ; // print data on serial monitor printf( "Voltage = %Lu V AC\n\r", ch_voltage / 10 ); // print data on LCD lcd_gotoxy(1, 2); // go to column 1 row 2 printf(lcd_putc, "%Lu V AC ", ch_voltage / 10); delay_ms(500); } } // end of code. |
The following small video shows my DIY circuit for this project, a Multimeter is used to test the accuracy of the circuit:
Related Project:
PIC18F46K22 MCU Based AC Current Measurement Project
Discover more from Simple Circuit
Subscribe to get the latest posts sent to your email.