Как сделать 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
Другие вопросы по тегам