Аналого-цифровое преобразование ATmega328p не отвечает

Следующий код не обновляет начальное значение adcValue. Светодиоды правильно работают с разными программами, и они также работают правильно при заданном начальном значении adcValue. На регулировку потенциометра не реагируют. Задержка нужна только для замедления, без нее тоже не работает.

#define F_CPU   1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

double adcValue = 700;

void startConversion()
{
    ADCSRA |= (1 << ADSC);
}        
void setupADC()
{
    // AVcc with external capacitor as reference, ADC5 as input
    ADMUX = (1 << REFS0) | (1 << MUX0) | (1 << MUX2);
    // Enabling ADC, acticating interrupts, setting prescaler to 8
    ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS0) | (1 << ADPS1) ;
    //disabling digital input buffer
    DIDR0 = (1 << ADC5D);       
    startConversion();
}            
ISR(ADC_vect)
{       
    adcValue = ADC;     
}    
int main(void)
{
    DDRB = 0b00111111;          
    setupADC();     
    sei();          
    while(1)
    {
        _delay_ms(500);
        if( adcValue < 400)
        {
            PORTB = 0b00000000;
        } 
        else if ( adcValue < 800)
        {
            PORTB = 0b00000011;
        } 
        else 
        {
            PORTB = 0b00000111;
        }

    startConversion();
}

}

РЕШЕНИЕ:

теперь это работает

volatile double adcValue = 700;

person hutar94    schedule 29.01.2021    source источник
comment
adcValue должен быть изменчивым. Он обновляется в ISR.   -  person yhyrcanus    schedule 29.01.2021
comment
Это также должно быть целым числом, так как AVR не имеет FPU. Это не ПК.   -  person Lundin    schedule 29.01.2021
comment
@yhyrcanus да, это работает   -  person hutar94    schedule 29.01.2021
comment
ADC имеет тип uint16_t, превращающий adcValue в double, в данном случае не нужен и не рекомендуется в любом случае на этой платформе.   -  person Clifford    schedule 30.01.2021
comment
Если вы хотите ответить на свой вопрос, сделайте это, добавив ответ, а не редактируя вопрос. Однако хороший ответ объяснил бы, почему решение работает. Исправление ошибок не входит в обязанности SO. embedded.com/introduction-to-the-volatile-keyword   -  person Clifford    schedule 30.01.2021
comment
Просто хотел указать, что предлагаемое решение по-прежнему подвержено ошибкам. Использование volatile НЕ гарантирует ATOMIC доступа к adcValue, так как AVR — это 8-битная платформа. Использование типа данных double (4 байта на AVR) еще больше усугубляет ситуацию. Использование uint16_t (как предлагалось ранее) было бы достаточным.   -  person Rev    schedule 01.02.2021
comment
пожалуйста, добавьте свои решения в качестве ответа и примите его, когда сможете   -  person Tarick Welling    schedule 01.02.2021


Ответы (1)


adcValue оптимизируется, потому что компилятор не видит изменения переменной в ISR (он не может сказать, будет ли когда-либо вызываться ISR). Поэтому он будет просто заменен константой. Чтобы исправить это, пометьте ее volatile, что говорит компилятору не делать никаких предположений о переменной и не позволяет компилятору ее оптимизировать.

Кроме того, adcValue является двойным типом. В общем, вы хотите избежать использования переменных с плавающей запятой (и особенно двойных) в устройствах без FPU. В частности, преобразование между целочисленными типами и типами с плавающей запятой занимает МНОГО циклов. Например, порядка сотен циклов. Вы, вероятно, не хотите этого в своем ISR.

Если вам действительно нужно, чтобы переменная была с плавающей запятой, я предлагаю выполнить преобразование вне ISR. У вас будет две переменные, static volatile uint16_t wAdcValue и локальная, где вам нужно число с плавающей запятой, вы назначите float fAdcValue = (float) wAdcValue; Обратите внимание, что любые манипуляции с плавающей запятой потребуют дополнительной обработки и использования флэш-памяти для их обработки.

person yhyrcanus    schedule 01.02.2021