Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

adc_logАЦП — Аналого-цифровой преобразователь. Из названия можно догадаться, что на вход подается аналоговый сигнал, который преобразуется в число.

Первое о чем нужно сказать — АЦП микроконтроллера умеет измерять только напряжение. Чтобы произвести измерение других физических величин, их нужно вначале преобразовать в напряжение. Сигнал всегда измеряется относительно точки называемой опорное напряжение, эта же точка является максимумом который можно измерить. В качестве источника опорного напряжения (ИОН), рекомендуется выбирать высокостабильный источник напряжения, иначе все измерения будут плясать вместе с опорным.

Одной из важнейших характеристик является разрешающая способность, которая влияет на точность измерения. Весь диапазон измерения разбивается на части. Минимум ноль, максимум напряжение ИОН. Для 8 битного АЦП это 2^8=256 значений, для 10 битного 2^10=1024 значения. Таким образом, чем выше разрядность тем точнее можно измерять сигнал.

Допустим вы измеряете сигнал от 0 до 10В. Микроконтроллер используем Atmega8, с 10 битным АЦП. Это значит что диапазон 10В будет разделен на 1024 значений.  10В/1024=0,0097В — с таким шагом мы сможем измерять напряжение. Но учтите, что микроконтроллер будет считать, величину 0.0097, 0.0098, 0.0099… одинаковыми.

Тем не менее шаг в 0,01 это достаточно неплохо. Однако, есть несколько рекомендаций, без которых эта точность не будет соблюдена, например для измерения с точностью 10бит, частота на которой работает АЦП должна быть 50-200 кГц. Первое преобразование занимает 25 циклов и 13 циклов далее. Таким образом, при частоте 200кГц мы сможем максимум выжать
200 000/13 = 15 384 измерений.

В качестве источника опорного напряжения можно использовать внутренний источник и внешний. Напряжение внутреннего источника (2,3-2,7В) не рекомендуется использовать, по причине низкой стабильности. Внешний источник подключается к ножке AVCC или Aref, в зависимости от настроек программы.

При использовании АЦП ножка AVCC должна быть подключена. Напряжение AVCC не должно отличаться от напряжения питания микроконтроллера более чем на 0,3В. Как было сказано, максимальное измеряемое напряжение равно опорному напряжению(Vref), находится оно в диапазоне 2В-AVCC. Таким образом, микроконтроллер не может измерить более 5В.

Чтобы расширить диапазон измерения, нужно измерять сигнал через делитель напряжения. Например, максимальное измеряемое напряжение 10В, опорное напряжение 5В. Чтобы расширить диапазон измерения, нужно уменьшить измеряемый сигнал в 2 раза.

Формула для расчета делителя выглядит так:

Uвых =  UвхR2/(R1 + R2)

Подставим наши значения в формулу:

5 = 10*R2/(R1+R2)

(R1+R2)=2*R2

R1=R2

т.е. можно взять любые два одинаковых резистора и подключить их по схеме
adc6

Следовательно, когда мы измеряем напряжение через делитель, нужно полученное значение АЦП умножить на коэффициент=Uвых/Uвх.

Полная формула вычисления измеряемого напряжения будет выглядеть так:
U=(опорное напряжение*значение АЦП*коэффициент делителя)/число разрядов АЦП

Пример: опорное 5В, измеренное значение АЦП = 512, коэффициент делителя =2, АЦП 10разрядный.

(5*512*2)/1024=5В — реальное измеренное значение напряжения.

Некоторые программисты пишут программу так, чтобы микроконтроллер автоматически вычислял коэффициент делителя, для этого выходной сигнал измеряют образцовым прибором и заносят это значение в программу. Микроконтроллер сам соотносит истинное напряжение каждому значению АЦП, сам процесс однократный и носит название калибровки.

Перейдем к программной реализации. Создаем проект с указанными параметрами. Также подключим дисплей на порт D для отображения информации.

adc3

Измерение будет производиться в автоматическом режиме, обработка кода в прерывании, опорное напряжение подключаем к ножке AVCC. По сути нам нужно только обрабатывать получаемые данные. Измеренные данные хранятся в переменной adc_data[0]. Если нужно опрашивать несколько каналов, то выбираем какие каналы сканировать, а данные будут для ножки 0 в adc_data[0], для ножки 1 в adc_data[1] и т.д.

В основном цикле добавим строки:

result=((5.00*adc_data[0])/1024.00); //пересчитываем значение АЦП в вольты
sprintf(lcd_buffer,»U=%.2fV»,result);   //помещаем во временную переменную результат
lcd_puts(lcd_buffer);                          //выводим на экран

Небольшое замечание, чтобы использовать числа с плавающей точкой, нужно в настройках проекта изменить (s)printf Features: int, width на float, width, precision. Если этого не сделать десятые и сотые мы не увидим.
adc_feature

Таким образом, мы всего лишь перевели значение АЦП в вольты и вывели на дисплей. Результат в протеусе выглядит так:
adc1

Резистором можно менять напряжение, измеряемое напряжение выведено на дисплей. При сборке на реальном железе к ножке Aref нужно подключить конденсатор на 0,1мкФ. Урок получился немного сложным, но думаю он вам понравится.

Файл протеуса и прошивка:

Update:
Измерение тока:
adc_current

227 комментариев: Урок 10. АЦП в AVR микроконтроллерах. Простой вольтметр на AVR.

  • Извините, все вроде записал…

  • Как привязать ШИМ к АЦП? Например насколько уменьшилось значение АЦП, на столько и уменьшилось значение ШИМ и наоборот.

  • читаете регистр ацп, записываете его в регистр таймера

  • Как при помощи АЦП считывать переменное напряжение?

  • выпрямить в постоянное и уже считывать

  • CodeVisionAVR в исходном коде нашел 4 ошибки: https://pp.userapi.com/c639425/v639425629/1f099/I7UitJ4lW6s.jpg

  • вы видимо в новом cavr пытаетесь скомпилить 1 пример, там надо либу поменять с lcd на alcd, со всеми вытекающими.register static unsigned char input_index=0; надо поменять на unsigned char input_index=0;

  • Спасибо. Дальше пошло веселее. Я взялся компиллировать второй пример, а там вообще: https://pp.userapi.com/c837620/v837620629/3ab36/ZWwvd-5X4_g.jpg

  • все правильно, компилятор говорит включить библиотеку в настройках проекта.

  • Добрый день! Ищу уже давно примеры реализации дифференциального входа АЦП для вольтметра. Почти нигде нет. А там, где есть — сам чёрт ногу сломит. Не могли бы Вы поподробнее рассмотреть пример реализации такого входа?

  • возьмите схему дифференциального усилителя на одном операционнике, выход заведите на обычный вход АЦП.

  • Здоровья всем! Делаю амперметр, ток от 0 до 15А, с шунта выходит от 0 до 150 мВольт, вывожу так
    // Текущий ток
    lcd_gotoxy(9,0);
    result=((5.00*adc_data[2])*100.00/1023.00); //пересчитываем значение АЦП в вольты
    sprintf(lcd_buffer,»%.1f A «,result); //помещаем во временную переменную результат
    lcd_puts(lcd_buffer); //выводим на экран
    В протеусе все нормально, а в железе заработает? Смущает коэффициент 100.00
    Спасибо заранее!

  • По идее должно, только учитывайте мощность, которая будет выделяться на резисторе, чтобы он не перегрелся

  • Во как быстро! Это первое полезное изделие, после светодиодов. Сомневался по поводу измерения милливольт. Если Вы подразумеваете под резистором шунт, то все нормально — константановый толщиной 2 мм.
    Разрешите еще пару вопросов! В уроке при создании проекта в параметрах ADC частота 125,000kHz. Почему именно такая? И какая частота процессора лучше? В общем, для создания ампервольтметра какие параметры желательнее?
    Спасибо, что Вы есть!!!

  • Можете почитать апнот AVR126, там детальная теория с формулами. Если в кратце:
    The ADC can prescale the system clock to provide an ADC clock that is between 50kHz and 200kHz to
    get maximum resolution. If an ADC resolution of less than 10-bits is required, then the ADC clock
    frequency can be higher than 200kHz. At 1MHz it is possible to achieve up to eight bits of resolution

  • Уважаемый админ! Можно ли в строке result=((5.00*adc_data[2])*100.00/1023.00); для корректировки погрешности вместо 5.00 написать например 4.91?

  • Вместо 5 у вас должно стоять ваше напряжение опоры, если оно 4.91, значит так и есть.

  • То есть можно писать дробное значение по фактическому значению опорного?
    Может еще подскажете — почему измерения прыгают по 0,4 как бы объяснить — 1,0 дрожит когда плавно добавляешь, потом сразу 1,4 начинаешь постепенно добавлять 1,8 и т.д.

  • Научите корректировать (обманывать) контроллер! Измеряем, например, 15 вольт, рассчитываем делитель, ставим резисторы ( а они не идеальные), на дисплей выводится 14,7 а образцовый вольтметр показывает 15,0 или наоборот.

  • Не нужно никого обманывать :) То что вы хотите называется калибровкой. Вам нужно померить реальным вольтметром напряжение и придумать как ввести данные в контроллер, может уарт, может при помощи дисплея. Далее пересчитать коэффициент, занести его в еепром и юзать в дальнейшем.

  • Круто! Пока не умеем, но будем учиться! Спасибо!

  • Откалибровал шунт… напильником! теперь все точно! Одна проблема осталась: показания на дисплей выводятся скачками по 0,5. Точнее до 2,0 по 0,5, а дальше 2,4 2,9 3,4 и т.д. до максимума. Всемогущий Админ! Помогите!

  • если у вас используется 8бит для измерений, то 5В/255 = 0,019В на отсчет, далее надо смотреть какой шунт. Даже проще, если вам надо мерить 15А, то один отсчет будет 15/255 = 0,059А. Дальше смотрите у себя где косяк

  • АЦП 10 бит, шунт — 0,01 Ом
    //Текущее напряжение
    lcd_gotoxy(0,0);
    result=((5.00*adc_data[0])*4/1023.00); //пересчитываем значение АЦП в вольты
    sprintf(lcd_buffer,»%.2f V «,result); //помещаем во временную переменную результат
    lcd_puts(lcd_buffer); //выводим на экран

    // Ток стабилизации
    lcd_gotoxy(9,1);
    result=((5.00*adc_data[1])*3/1023.00); //пересчитываем значение АЦП в вольты
    sprintf(lcd_buffer,»%.1f As «,result); //помещаем во временную переменную результат
    lcd_puts(lcd_buffer); //выводим на экран

    // Текущий ток
    lcd_gotoxy(9,0);
    result=((5.00*adc_data[2])*100.00/1023.00); //пересчитываем значение АЦП в вольты
    sprintf(lcd_buffer,»%.1f A «,result); //помещаем во временную переменную результат
    lcd_puts(lcd_buffer); //выводим на экран

    // Ваттметр
    lcd_gotoxy(0,1);
    result=((5.00*adc_data[0])*4/1023.00)*((5.00*adc_data[2])*100.00/1023.00); //Умножаем В*А
    sprintf(lcd_buffer,»%.1f W «,result); //помещаем во временную переменную результат
    lcd_puts(lcd_buffer); //выводим на экран
    Все показания, кроме текущего тока, выводятся равномерно. Косяк в прошивке или в железе?

  • При 15А на таком шунте у вас упадет всего 0.15В. Если предположить что АЦП мерит честно 10бит, то шаг измерений будет ~0,005В, т.е. из 1024 будут задействованы всего 30 отсчетов. Чтобы было нормально нужно усилитель ставить. Хотя бы на 10.

  • Спасибо огромное! Я уже так и сделал, поставил усилитель (при 15А — 5В), все четко, механизма не понял, а сейчас Вы и это объяснили!
    Уважаемый Админ! У Вас в планах нет сделать урок по настройке неиспользуемых портов?

  • Собираю устройство, которое на основании измеренного напряжения на резисторе своим АЦП на одном из своих портов выдает импульсы широтой, зависящей от измеренного напряжения. Широтой этого импульса задается ток, который, протекая через вышеупомянуты резистор, формирует вновь измеряемое напряжение.. Пока результат такой. Зеленым цветом — сигнал на выходе контроллера, красным — напряжение на резисторе в цепи истока полевого транзистора, оно пропорционально току, протекающему через этот транзистор.Результат такой:
    https://pp.userapi.com/c836536/v836536629/3a2dd/SdIiT_Ejk9Q.jpg
    Вопрос знатокам: почему это измеренное напряжение всегда застывает на одной и той же величине (0,8В)? Код:
    #include
    #include
    #include

    long result;
    long n=1;
    // Voltage Reference: AVCC pin
    #define ADC_VREF_TYPE ((0<<REFS1) | (1<<REFS0) | (1<<ADLAR))

    unsigned char read_adc(unsigned char adc_input)
    {
    ADMUX=adc_input | ADC_VREF_TYPE;
    // Delay needed for the stabilization of the ADC input voltage
    delay_us(10);
    // Start the AD conversion
    ADCSRA|=(1<<ADSC);
    // Wait for the AD conversion to complete
    while ((ADCSRA & (1<<ADIF))==0);
    ADCSRA|=(1<<ADIF);
    return ADCH;
    }

    void main(void)
    {

    // Input/Output Ports initialization
    // Port B initialization
    // Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
    DDRB=(0<<DDB7) | (0<<DDB6) | (0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
    // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
    PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

    // Port C initialization
    // Function: Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
    DDRC=(0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
    // State: Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
    PORTC=(0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

    // Port D initialization
    // Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
    DDRD=(0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
    // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
    PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);

    // ADC initialization
    // ADC Clock frequency: 125,000 kHz
    // ADC Voltage Reference: AVCC pin
    // Only the 8 most significant bits of
    // the AD conversion result are used
    ADMUX=ADC_VREF_TYPE;
    ADCSRA=(1<<ADEN) | (0<<ADSC) | (0<<ADFR) | (0<<ADIF) | (0<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (0<<ADPS0);
    SFIOR=(0<2) // и сравниваем, например, с 2В. В случае отклонения
    // измеренной величины от 2В меняем длительнось импульса n.
    {
    n=n-1;
    PORTB.0=1;
    delay_ms(n);
    PORTB.0=0;
    delay_ms(20);
    }
    if (result<2)
    {
    n=n+1;
    PORTB.0=1;
    delay_ms(n);
    PORTB.0=0;
    delay_ms(20);
    }
    };
    }

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Свежие записи
Последние комментарии
  • Загрузка...
Архивы
Счетчик
Яндекс.Метрика