ADC – analog-to-digital converter (ADC-Analog-to-Digital Converter). Converts a certain analog signal to digital. Bitrate ADC determines the accuracy of the signal conversion. Conversion time – respectively, the speed of the ADC. The ADC is embedded in many microcontrollers of the AVR family and simplifies the use of the microcontroller in any regulation schemes where it is necessary to digitize a certain analog signal.
Consider the principle of operation of the ADC . To convert, you need a reference voltage source and the actual voltage that we want to digitize (the voltage that is converted must be less than the reference voltage). You also need a register where the converted value will be stored, let’s call it Z. Input voltage = Reference voltage * Z / 2 ^ N, where N is the ADC bit . We agree that this register, like ATmega8 , is 10-bit. The transformation in our case takes place in 10 stages. Z9 high bit is set to one.
Next, a voltage is generated (Reference voltage * Z / 1024) , this voltage is compared with an input comparator using an analog comparator, if it is greater than the input voltage, Z9 bit becomes zero, and if it is less, it remains one. Next, go to bit Z8 and the above method, we obtain its values. After the calculation of the Z register is completed, a flag is set, which signals that the conversion is completed and the resulting value can be read. The conversion accuracy and interference, as well as the conversion rate, can greatly influence the conversion accuracy. The slower the conversion occurs, the more accurate it is. Guidance and interference should be fought with inductance and capacitance, as advised by the manufacturer in the datasheet:
In AVR microcontrollers, the AREF pin , or internal sources of 2.56V or 1.23V, can be used as a reference voltage source. Also the source of the reference voltage can be the supply voltage. In some cases and models of microcontrollers there are separate outputs for powering the ADC:AVCC and AGND . Conclusions ADCn – ADC channels.
From which channel the signal will be digitized, you can choose using a multiplexer.
Now we will demonstrate by example what was said above. We will build a model that will work as a voltmeter with a digital scale. We agree that the maximum measured voltage will be 10V. Also let our layout display the contents of the ADC register on the LCD.
Connection diagram:
The binding of the microcontroller and LCD WH1602A is standard. X1 – a quartz resonator at 4 MHz, capacitors C1, C2 – 18-20 pF. The R1-C7chain at the reset pin is 10 kΩ and 0.1 μF, respectively. Signal LED D1 and limiting resistor R2 200 ohms and R3 – 20 ohms. LCD contrast adjustment – VR1 at 10 kΩ. The reference voltage source we will use is built in to 2.56V. With the help of the divider R4-R5 we will achieve the maximum voltage of 2.5V at the input of PC0 , with a voltage on the probe 10V. R4 – 3 kOhm, R5 – 1 kOhm, in their denomination should be treated carefully, but if it is not possible to choose exactly such, you can make any resistive 1: 4 divider and programmatically correct the readings, if necessary. A 10 μH choke and a 0.1 μF capacitor to eliminate noise and pickups on the ADC are not shown in the diagram. Their presence is implied if the ADC is used. Now it’s up to the program:
C program
#include #define RS 2 // RS = PD2 #define E 3 // E = PD3 #define TIME 10 // Time delay constant for LCD // Frequency clocking MK - 4 MHz #define R_division 3.837524 // = R4 / R5 constant unsigned int u = 0 ; // Global variable with transform content void pause ( unsigned int a ) { unsigned int i ; for ( i = a ; i > 0 ; i - ) ; } void lcd_com ( unsigned char lcd ) // Transmit LCD Command { unsigned char temp ; temp = ( lcd & ~ ( 1 << RS ) ) | ( 1 << E ) ; // RS = 0 is a command PORTD = temp ; // Output to the portD the top tetrad of the command, signals RS, E asm ( "nop" ) ; // A small delay in 1 tact MK, to stabilize PORTD = temp & ~ ( 1 << E ) ; // command recording signal temp = ( ( lcd * 16 ) & ~ ( 1 << RS ) ) | ( 1 << E ) ; // RS = 0 is a command PORTD = temp ; // Output the command, signals RS, E to portD asm ( "nop" ) ; // A small delay in 1 tact MK, to stabilize PORTD = temp & ~ ( 1 << E ) ; // command recording signal pause ( 10 * TIME ) ; // Pause for command execution } void lcd_dat ( unsigned char lcd ) // Write data to LCD { unsigned char temp ; temp = ( lcd | ( 1 << RS ) ) | ( 1 << E ) ; // RS = 1 is data PORTD = temp ; // Output to the portD the highest data tetrad, RS, E signals asm ( "nop" ) ; // A small delay in 1 tact MK, to stabilize PORTD = temp & ~ ( 1 << E ) ; // Data Write Signal temp = ( ( lcd * 16 ) | ( 1 << RS ) ) | ( 1 << E ) ; // RS = 1 is data PORTD = temp ; // Display on the portD the lowest tetrad of data, signals RS, E asm ( "nop" ) ; // A small delay in 1 tact MK, to stabilize PORTD = temp & ~ ( 1 << E ) ; // Data Write Signal pause ( TIME ) ; // Pause for data output } void lcd_init ( void ) // Initialize the LCD { lcd_com ( 0x2c ) ; // 4-wire interface, 5x8 character size pause ( 100 * TIME ) ; lcd_com ( 0x0c ) ; // Show image, do not show cursor pause ( 100 * TIME ) ; lcd_com ( 0x01 ) ; // Clear DDRAM and set the cursor to 0x00 pause ( 100 * TIME ) ; } unsigned int getADC ( void ) // Read ADC { unsigned int v ; ADCSRA | = ( 1 << ADSC ) ; // Start conversion while ( ( ADCSRA & _BV ( ADIF ) ) == 0x00 ) // Wait for the conversion to complete ; v = ( ADCL | ADCH << 8 ) ; return v ; } void write_data ( unsigned int u ) { unsigned char i ; double voltage = 0 ; lcd_com ( 0x84 ) ; // Display the ADC register on the LCD for ( i = 0 ; i < 10 ; i ++ ) if ( ( u & _BV ( 9 - i ) ) == 0x00 ) lcd_dat ( 0x30 ) ; else lcd_dat ( 0x31 ) ; lcd_com ( 0xc2 ) ; voltage = R_division * 2.56 * u * 1.024 ; // Calculate Stress i = voltage / 10,000 ; // Output Voltage On LCD voltage = voltage - i * 10000 ; if ( i ! = 0 ) lcd_dat ( 0x30 + i ) ; i = voltage / 1000 ; voltage = voltage - i * 1000 ; lcd_dat ( 0x30 + i ) ; lcd_dat ( ',' ) ; i = voltage / 100 ; voltage = voltage - i * 100 ; lcd_dat ( 0x30 + i ) ; i = voltage / 10 ; voltage = voltage - i * 10 ; lcd_dat ( 0x30 + i ) ; lcd_dat ( 'v' ) ; } int main ( void ) { DDRD = 0xfc ; pause ( 3000 ) ; // Delay to turn on the LCD lcd_init ( ) ; // Initialize LCD lcd_dat ( 'A' ) ; // We write "ADC =" and "U =" on the LCD lcd_dat ( 'D' ) ; lcd_dat ( 'C' ) ; lcd_dat ( '=' ) ; lcd_com ( 0xc0 ) ; lcd_dat ( 'U' ) ; lcd_dat ( '=' ) ; ADCSRA = ( 1 << ADEN ) | ( 1 << ADPS1 ) | ( 1 << ADPS0 ) ; // Turn on the ADC, the clock frequency of the inverter = / 8 from the clock microcontroller ADMUX = ( 1 << REFS1 ) | ( 1 << REFS0 ) | ( 0 << MUX0 ) | ( 0 << MUX1 ) | ( 0 << MUX2 ) | ( 0 << MUX3 ) ; // Internal reference voltage source Vref = 2.56, the input of the ADC is PC0 while ( 1 ) { u = getADC ( ) ; // Read data write_data ( u ) ; // Display them on LCD pause ( 30,000 ) ; } return 1 ; } The program is simple. At the beginning, we initialize the I / O ports. In order to serve as an input to the ADC , the pin PC0 must work as an input. Next, we carry out the initialization of the LCD and ADC . Initializing the ADC is to turn it on with the ADEN bit in the ADCSRA register.And the choice of frequency conversion bits ADPS2, ADPS1, ADPS0 in the same register. We also choose the reference voltage source, bitsREFS1 REFS0 in the ADMUX register and input of the ADC : bits MUX0, MUX1, MUX2, MUX3 (in our case, the input of the ADC is PC0 , therefore MUX0.3 = 0 ). Next, in the perpetual cycle, we start the conversion by setting the ADSC bit in the ADCSRA register. We are waiting for the conversion to finish (the ADIF to ADCSRA bit becomes 1). Next, remove the data from the register ADC and display them on the LCD . It is necessary to remove data from the ADC in the following sequence: v = (ADCL + ADCH * 256); if you use v = (ADCH * 256 + ADCL); - point blank does not work.
So there is a trick to not work with fractional numbers. When to calculate the input voltage in volts. We will simply store our voltages in millivolts.For example, the value of the voltage variable 4234 means that we have 4.234 volts. In general, operations with fractional numbers consume a lot of microcontroller memory (our voltmeter firmware weighs slightly more than 4 kilobytes, this is half the memory of ATmega8 programs!), They are recommended to be used only when absolutely necessary. Calculating the input voltage in millivolts is simple: voltage = R_division * 2.56 * u * 1.024;
Here R_division is the coefficient of the resistive divider R4-R5 . So, as the real divider coefficient can differ from the calculated one, our voltmeter will lie. But it’s easy to correct. Using a tester, we measure a certain voltage, we get X volts, and let our voltmeter show Y volts. Then R_division = 4 * X / Y , if Y is greater than X and 4 * Y / X if X is greater than Y. This completes the setting of the voltmeter, and it can be used.
You can also modify your power supply. By inserting a digital voltmeter ammeter on the LCD and overload protection into it (to measure the current, we need a powerful shunt of about 1 Ohm resistance).
In my power supply, I also integrated the overload protection when the current exceeds 2A, then the piezo squeaker begins to squeak earnestly, signaling overload:
Source: avrlab.comz AVR Ammeter Volt Meter Project files Alternative link: adc-example-atmega8-digital-volt-meter-ammeter-project.rar