Reputation: 2695
If I need to be specific: I'm asking about ATmega328P chip. The analog pins are under PortC
on this chip.
I have learnt that digitalWrite
can be done using out
, and digitalRead
using in
.
But how can I do analogRead
?? Please explain. I'm new to this.
EXTRA: It would be helpful if you show analogWrite
too (In the sense of PWM).
Upvotes: 1
Views: 8556
Reputation: 1
After a long time struggle with me, I survey the datasheet of ATmega 328P and many google surfing articles, the simple and workable code is completed as below.
; UNO_asmADCapp.asm
; revised by bsliao: 2020/5/12 下午 03:39:20, TEST OK 2020/05/13, 11:33
; Reference:
; https://stackoverflow.com/questions/38972805/
; [1] how-to-code-an-adc-for-an-atmega328p-in-assembly
; Author : Dario, Created: 8/14/2016 7:34:43 AM
; [2] https://robotics.ee.uwa.edu.au/courses/des/labprep/
; LabPrep%205%20-%20Timers%20and%20ADC%20in%20ATMEL.pdf
; [3] https://www.avrfreaks.net/forum/adc-converter-assembly-using-atmega328p-mcu
; AD0 --- uno A0
; value ADCH (b9 b8) ADCL (b7- b0) <Internal> --- PB1(uno d9) PB0 (d8), PD7-PD0 (uno D7 -D0)
#define F_CPU 16000000UL
.def temp =r16
; Replace with your application code
.include "./m328Pdef.inc"
.org 0x000
rjmp start
; .org 0x002A
; rjmp ADC_conversion_complete_Handler
start:
eor r1, r1
out SREG, r1
ldi temp, HIGH(RAMEND)
out SPH, r16
ldi temp, LOW(RAMEND)
out SPL, r16
setup:
ldi temp, 0xFF ; set r16 = 1111 1111
out ddrb, temp ; set all d pins as output
out ddrd, temp ; set all b pins as output
configADC0:
;------initialize ADC0 ------- Set ADMUX and ADCSRA:
;REF1 REFS0 ALLAR - (MUX3 MUX2 MUX1 MUX0 )=(0000)
;Aref=5.0 V is used, default right-adjust result, analog in at AIN0 (ADC0)
LDI temp, 0x00
STS ADMUX, temp
;ADcENable, (ADPS2 ADPS1 ADPS0 )=(000) : division factor=128 16Mhz/128: ADC0 is applied.
LDI temp, (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
STS ADCSRA, temp
andi temp, 0b11011111
STS ADCSRA, temp
; the first conversion
LDS temp,ADCSRA
ori temp, (1<<ADSC);
STS ADCSRA, temp
LOOP:
; start the next single conversion on ADCn, here n=0
LDS temp,ADCSRA
ori temp, (1<<ADSC);
STS ADCSRA, temp
adc_read_loop:
// while (bit_is_set(ADCSRA, ADSC));
lds temp,ADCSRA
sbrc temp,ADSC ;after ADC0 conversion over, the bit ADSC in the ADCSRA is set to zero and the bit ADIF is set to one.
rjmp adc_read_loop
read_ADC_value:
lds r24,ADCL
lds r25,ADCH
display_ADC_value:
andi r25, 0x03
out PORTB, r25 ; LEDs active high, PORTB most significant byte
com r24 ; LEDs active low
out PORTD, r24 ; PORTD less significant byte
call one_sec_delay
rjmp LOOP
one_sec_delay:
ldi r20, 20
ldi r21, 255
ldi r22, 255
delay: dec r22
brne delay
dec r21
brne delay
dec r20
brne delay
ret
Upvotes: 0
Reputation: 2695
This is for the future visitors who stumble upon here...
As mentioned by Rev1.0, Arduino C does make things too easy for you. A lot of complicated things are going on under the hood when you write a simple statement analogRead()
. But it's not that complicated once you understand it. You should definitely read up on ADCs.
As mentioned by David Grayson, you should definitely take a look at the source code of analogRead()
. Here is the datasheet of ATmega328P and the instruction set manual for ATmega328P to help you understand what is going on.
You can read this and this to get some idea on how to exactly write the code.
Now, here is what I came up with for my use-case in my project.
The bold-face words are there to tell you that this code was NOT written for a general use-case. Copy-Pasting this will most probably not work.
You see the amuont of links in this post? Read all of them. Below is only for using as a reference in case you get stuck and it might help.
adcInit:
ldi r16, 0b01100000 ; Voltage Reference: AVcc with external capacitor at AREF pin
sts ADMUX, r16 ; Enable ADC Left Adjust Result
; Analog Channel: ADC0
ldi r16, 0b10000101 ; Enable ADC
sts ADCSRA, r16 ; ADC Prescaling Factor: 32
ret
adcRead:
ldi r16, 0b01000000 ; Set ADSC flag to Trigger ADC Conversion process
lds r17, ADCSRA ;
or r17, r16 ;
sts ADCSRA, r17 ;
ret
adcWait:
lds r17, ADCSRA ; Observe the ADIF flag, it gets set by hardware when ADC conversion completes
sbrs r17, 4 ;
jmp adcWait ; Keep checking until the flag is set by hardware
ldi r16, 0b00010000 ; Set the flag again to signal 'ready-to-be-cleared' by hardware
lds r17, ADCSRA ;
or r17, r16 ;
sts ADCSRA, r17 ;
ret
It is used like this:
call adcInit
mainLoop:
call adcRead
call adcWait
lds r18, ADCL ; Must read ADCL first, and ADCH after that
lds r19, ADCH
Upvotes: 3
Reputation: 87416
You can read the source code of analogRead
from the Arduino environment:
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_analog.c
The important thing is to find all the places where it reads or writes from a special function register (SFR) like ADMUX, and then make sure you do the same thing in your assembly code.
You should also look at the ATmega328P datasheet, which defines all of those SFRs, as a way to double check that you are doing the correct thing.
If you have further trouble, I recommend asking a new question where you show some code and get specific about exactly what part of analogRead
is confusing to you.
Upvotes: 5