Как сделать analogRead() на ассемблере AVR?
Если мне нужно быть конкретным: я спрашиваю про чип ATmega328P. Аналоговые контакты находятся под PortC
на этом чипе.
Я узнал, что digitalWrite
может быть сделано с помощью out
, а также digitalRead
с помощью in
,
Но как я могу сделать analogRead
?? Пожалуйста, объясни. Я новичок в этом.
ДОПОЛНИТЕЛЬНО: Было бы полезно, если бы вы показали analogWrite
тоже (в смысле ШИМ).
2 ответа
Вы можете прочитать исходный код analogRead
из среды Arduino:
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_analog.c
Важно найти все места, где он читает или пишет, из специального регистра функций (SFR), такого как ADMUX, а затем убедитесь, что вы делаете то же самое в коде сборки.
Вам также следует взглянуть на таблицу данных ATmega328P, в которой определены все эти SFR, как способ перепроверить, что вы делаете правильно.
Если у вас возникнут дальнейшие проблемы, я рекомендую задать новый вопрос, где вы покажете некоторый код и уточните, какая именно часть analogRead
смущает вас.
Это для будущих посетителей, которые наткнулись здесь...
Как упоминалось в Rev1.0, Arduino C делает вещи слишком простыми для вас. Когда вы пишете простое заявление, под капотом происходит много сложных вещей analogRead()
, Но это не так сложно, когда ты это понимаешь. Вы должны обязательно прочитать на АЦП.
Как упомянул Дэвид Грейсон, вы обязательно должны взглянуть на исходный код analogRead()
, Вот таблица данных ATmega328P и руководство по набору инструкций для ATmega328P, чтобы помочь вам понять, что происходит.
Вы можете прочитать это и это, чтобы получить представление о том, как точно написать код.
Теперь вот что я придумал для моего варианта использования в моем проекте.
Жирные слова говорят вам, что этот код НЕ был написан для общего случая использования. Copy-Pasting это, скорее всего, не будет работать.
Вы видите множество ссылок в этом посте? Прочитайте их все. Ниже только для справки, если вы застряли, и это может помочь.
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
Используется так:
call adcInit
mainLoop:
call adcRead
call adcWait
lds r18, ADCL ; Must read ADCL first, and ADCH after that
lds r19, ADCH
После долгой борьбы со мной, я просматриваю таблицу данных ATmega 328P и многие статьи о серфинге в Google, простой и работоспособный код завершен, как показано ниже.
; UNO_asmADCapp.asm
; revised by bsliao: 2020/5/12 下午 03:39:20, TEST OK 2020/05/13, 11:33
; Reference:
; https://stackru.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