AVR Embedded Tutorial - Analog Read
│ Deutsch (de) │ English (en) │
Analog Read
This code is for an Atmega328/Arduino runnning at 16MHz. How to control the UART can be found here:
UART UARTInit and UARTSendString (...
The ATmega328 can measure with 10-bit accuracy, so a value between 0..1023 is output.
Polling method
Initialize ADC
Configure analog reading. AVcc is used as the reference voltage, this is the easiest way. This must be configured with ADMUX.
The following reference voltages can be configured with ADMUX:
Bit 7 | Bit 6 | Description |
---|---|---|
0 | 0 | Pin AREF |
0 | 1 | Use AVcc |
1 | 0 | - |
1 | 1 | 2.56V |
The converter is started with ADCSRA.
procedure ADC_Init;
begin
ADMUX := (1 shl REFS); // Use AVcc for reference
ADCSRA := (1 shl ADEN) or %111; // Switch on converter, divider 128
end;
Measuring
The procedure for measuring is as follows.
- Specify the port.
- Wait until the measurement is complete.
- Read out the measured value.
With ADMUX you have to enter the reference voltage again because it shares the register with the reference. You could also do an OR operation with ADMUX .
function ADC_Read(Port : byte) : integer;
begin
ADMUX := (1 shl REFS) or (Port and $0F); // Specify port
ADCSRA := ADCSRA or ( 1 shl ADSC ); // Start measuring
while (ADCSRA and (1 shl ADSC)) <> 0 do // Wait until measured
begin
end;
Result := ADC; // Read out the measured value
end;
If you always read in from the same port, you only have to call up the ADMUX line once. You can also start measuring directly after the while loop which saves a few clock cycles.
Output via UART
Buffer variables for the measurement data.
var
Data : integer;
s : string[10];
The measurement data is then output in an endless loop.
begin
// Initialize UART
UARTInit;
// Initialize ADC
ADC_Init;
// Allow interrupt
asm Sei end;
// output measured values.
repeat
Data := ADC_Read(0);
str(Data : 6, s);
UARTSendString(s);
until 1 = 2;
end.
Interrupt method
The advantage of the interrupt-controlled method is that you do not have to wait until the measurement is finished, so you have time for other things.
Initialisation
The difference:
- With ADMUX you enter the input port at the beginning.
- At ADCSRA you have to start the first measurement with ADSC.
- With ADIE you communicate that the conversion interrupt is controlled.
procedure ADC_Init;
const
Port = 0;
begin
ADMUX := (1 shl REFS) or (Port and $0F);
ADCSRA := %111 or (1 shl ADEN) or (1 shl ADSC) or (1 shl ADIE);
end;
Output measured value in the interrupt
procedure ADC_Ready public name 'ADC_ISR'; interrupt;
var
Data : integer;
s : string[10];
begin
Data := ADC;
ADCSRA := ADCSRA or (1 shl ADSC);
str(Data : 6, s);
UARTSendString(s);
end;
Tip: Interrupt routines should be kept short. This does not mean that you cannot do any processing in an interrupt routine, but you do need to ensure that when the next interrupt occurs, the last one is not still running.
Main program
Since the measurement is interrupt-controlled, the main loop remains empty. It is important to switch on the interrupts.
begin
// Initialize UART
UARTInit;
// ADC
ADC_Init;
// Allow interrupt
asm Sei end;
// main loop
repeat
// Do something
until 1 = 2;
end.
See also
- AVR Embedded Tutorials - Overview