Главная » Статьи » Проекты

Задействуем АЦП. Вольтметр на AVR.

 Аналогово-Цифровой Преобразователь служит для преобразования аналогового сигнала на входе в цифровую форму, понятную для МК.  Практически во всех современных микроконтроллерах от AVR имеется 10 битный АЦП, позволяющий оцифровывать аналоговый сигнал с дискретностью 1024 значений. Этого достаточно чтобы, например, делать замеры напряжений (в разумных пределах), снимать показания с различных датчиков, таких как фотодиод и термопара, делать анализаторы спектра и многое другое.



 Примерная работа преобразования приведена на рисунке ниже; через равные промежутки времени (ось X) происходит считывание значения напряжения на входе АЦП (ось Y). Так как АЦП имеет ограниченную разрешающую способность, появляется дискретность (дробление) значений.  



  Величина по оХ называется частотой дискретизации, чем больше частота тем точнее может быть полученная информация о сигнале. АЦП в мк AVR может работать на частотах дискретизации от 50 до 200 кГц

  В Bascom-AVR конфигурирование АЦП сводится к одной строчке:

Config Adc = SINGLE , Prescaler = 128 , Reference = Avcc

 здесь Adc - режим считывания значения: Single - единичное считывание, также может быть Free (режим постоянной работы преобразователя)
Prescaler = 128 - выбираем частоту дискретизации путем деления частоты кварца на определенное число (также может быть 2,4,8,16,32,64 или Auto). Если выбрать Auto, то компилятор сам выберет подходящую частоту работы АЦП
Reference – выбор источника опорного напряжения. Aref – внешний источник, Avcc – напряжение питания схемы, Internal – внутренний ИОН на 2,56 в.

  Преобразование аналога в цифру в Bascom-AVR происходит следующим образом, для примера считаем значение напряжения на первом канале АЦП и выведем результат преобразования на ЖКИ:

Start ADC         ' запускаем преобразование
M = GetADC(1)     ' приравниваем переменную М результату преобразования
Stop ADC          ' останавливаем работу АЦП
LCD  M           ' выводим значение на ЖКИ

  Считывание может быть произведено с любого пина АЦП микроконтроллера, от 0 до 7 (например для ATMEGA32). Для этого в строке GetADC(x) заместо х ставим интересующий нас канал. И все!

  В дополнение решил собрать вольтметр на ATMega8. Во-первых стало интересно как точно все это работает в железе, и во-вторых вольтметр пригодится в регулируемом блоке питания который хочу собрать.
Итак, первым делом нужно определиться с диапазоном измеряемых значений.  Я выбрал пределом измерения 30 Вольт, от этого зависит коэффициент на который нужно будет умножать результат преобразования, а также расчет резисторного делителя напряжения на входе АЦП. В качестве источника опорного напряжения выбран внутренний ИОН на 2,56 В. Поэтому резисторный делитель рассчитан таким образом, чтобы при максимально измеряемом напряжении 30 В на вход АЦП заходило не более 2,56 В. Схема вольтметра приведена ниже:


  Подстроечник RV1 - многооборотный, для точной настройки делителя.
  В схеме использованы семисегментные индикаторы с общим анодом, но без точки. Точку пришлось добавлять путем высверливания отверстия и вставки в него светодиода. Точку я сделал горящей постоянно, а сегменты управляются динамически.
  Индикация организована в главном цикле программы. Для считывания показаний АЦП задействован Timer1, который переполняется примерно 2 раза в секунду. При переполнении вызывается подпрограмма, в которой происходит считывание данных с АЦП и их преобразование.
 Так как переполнение таймера происходит каждые 0,5 сек, то показания вольтметра будут обновляться 2 раза в секунду. Это сделано для того чтобы четвертая цифра отображающая сотые доли не скакала слишком быстро, размазывая показания (так как АЦП имеет свойство иногда менять показания при неизменном напряжении на входе).


    $regfile = "m8def.dat"
    $crystal = 8000000
     
    ' * * * переменные * * *
     
    Dim W As Integer              ' переменная для хранения значения АЦП
     
    Dim N1 As Integer
    Dim N2 As Integer
    Dim N3 As Integer
    Dim N4 As Integer
     
    Dim M1 As Integer
    Dim M2 As Integer
    Dim M3 As Integer
    Dim M4 As Integer
     
    Dim C1 As Integer
    Dim C2 As Integer
    Dim C3 As Integer
    Dim C4 As Integer
     
     
    ' * * * настройка портов * * *
     
     
    Ddrb = &B11111111
    Ddrd = &B11111111
     
     
    ' * * * конфигурируем таймер1 и прерывание по его переполнению * * *
     
     
    Config Timer1 = Timer , Prescale = 64
     
    On Timer1 Acp:
     
     
    ' * * * конфигурация АЦП * * *
     
    Config Adc = Single , Prescaler = Auto , Reference = Internal
     
     
    ' * * * разрешаем прерывания * * *
     
    Enable Interrupts
    Enable Timer1
     
     
    ' * * * основная программа * * *
     
     
    Do
     
     
    Portb = &B11101111
     
    Select Case N1
    Case 0 : Portd = &B11111111
    Case 1 : Portd = &B11111001
    Case 2 : Portd = &B10100100
    Case 3 : Portd = &B10110000
    Case 4 : Portd = &B10011001
    Case 5 : Portd = &B10010010
    Case 6 : Portd = &B10000010
    Case 7 : Portd = &B11111000
    Case 8 : Portd = &B10000000
    Case 9 : Portd = &B10010000
    End Select
    Waitms 5
     
    Portb = &B11110111
     
    Select Case N2
    Case 0 : Portd = &B11000000
    Case 1 : Portd = &B11111001
    Case 2 : Portd = &B10100100
    Case 3 : Portd = &B10110000
    Case 4 : Portd = &B10011001
    Case 5 : Portd = &B10010010
    Case 6 : Portd = &B10000010
    Case 7 : Portd = &B11111000
    Case 8 : Portd = &B10000000
    Case 9 : Portd = &B10010000
    End Select
    Waitms 5
     
    Portb = &B11111011
     
    Select Case N3
    Case 0 : Portd = &B11000000
    Case 1 : Portd = &B11111001
    Case 2 : Portd = &B10100100
    Case 3 : Portd = &B10110000
    Case 4 : Portd = &B10011001
    Case 5 : Portd = &B10010010
    Case 6 : Portd = &B10000010
    Case 7 : Portd = &B11111000
    Case 8 : Portd = &B10000000
    Case 9 : Portd = &B10010000
    End Select
     
    Waitms 5
     
    Portb = &B11111101
     
    Select Case N4
    Case 0 : Portd = &B11000000
    Case 1 : Portd = &B11111001
    Case 2 : Portd = &B10100100
    Case 3 : Portd = &B10110000
    Case 4 : Portd = &B10011001
    Case 5 : Portd = &B10010010
    Case 6 : Portd = &B10000010
    Case 7 : Portd = &B11111000
    Case 8 : Portd = &B10000000
    Case 9 : Portd = &B10010000
    End Select
     
    Waitms 5
     
    Loop
     
    ' * * * подпрограмма считывания показания с АЦП * * *
     
    Acp:
     
    Start Adc          ' считываем показания АЦП
     
     
    W = Getadc(1)      ' <---- число которое получили с первого канала АЦП
     
     
    M1 = W * 3         ' переводим значение АЦП в вольты. Так как запятая у меня
                       ' фиксированная, то для удобства работать будем с целыми числами 3069/1023=3;
                       ' где 3069 максимальное значение напряжения отображаемое вольтметром,
                       ' или 30.69 В

    M2 = M1
    M3 = M1
    M4 = M1
     
    M1 = M1 / 1000     ' обработка тысяч
    N1 = Abs(M1)
     
    M2 = M2 Mod 1000   ' обработка сотен
    M2 = M2 / 100
    N2 = Abs(M2)
     
    M3 = M3 Mod 100    ' обработка десяток
    M3 = M3 / 10
    N3 = Abs(M3)
     
    M4 = M4 Mod 10     ' обработка единиц
    N4 = Abs(M4)
     
    Return
     
    End

  
  Заснял небольшое видео, показывающее работу вольтметра.


 Разрешение измерения вольтметра получилась 0,03 В (большего из 10 битного АЦП на таком диапазоне не выжать), точность не менее 0,05 В. Что весьма не плохо, учитывая что использовался внутренний ИОН и не использовались ВЧ-фильтры на входе преобразователя. Для блока питания самое то.

ЗЫ. Если говорить о точности измерения, то в качестве эталонного измерителя напряжения выступал мой Mastech MAS838, поэтому все относительно =) 



Скачать файлы к проекту
Категория: Проекты | Добавлено: 04.03.2011
Просмотров: 72177 | Комментарии: 32 | Теги: вольтметр, индикатор, ATmega8, исходники, измерения | Рейтинг: 4.9/7
Всего комментариев: 321 2 »
0  
32 exersizze   (05.01.2017 21:57) [Материал]
жулик, надо увеличить частоту работы мк перепрошив фьюзы на частоту 8МГц. Скорее всего просто надо снять фьюз CKDIV8, если камень новый.

31 жулик   (04.01.2017 00:09) [Материал]
ребята извените ! но просто не знаю с чего начать. поскажите пожалуйста . собрал схему 5 раз проверил залил через юзб асп запустил . а табло мигает с частотой 2 раза в секунду. я новичек. вот пытаюсь как то освоить это дело. спасибо!! а схемка зачетная. но чет я дето не до соображаю

30 abomin   (30.05.2016 22:28) [Материал]
Вариант с операционником пойдёт, т.к. хочу все шесть напряжений контролировать. Спасибо за идею!

0  
29 exersizze   (30.05.2016 14:15) [Материал]
Ну тогда просто меняем местами плюс с минусом. Отрицательное напряжение бп на землю контроллера, а землю бп на ацп, через делитель. Или вариант посложней, если земли должны быть общими, на операционнике делается схема инвертирующего усилителя. Конкретных схем не даю, так как по запросу в гугле вываливается куча всего.

28 abomin   (29.05.2016 22:38) [Материал]
Так компутерные БП - шесть напряжений: +5деж.; +3,3; +5; +12; -12; -5.

0  
27 exersizze   (29.05.2016 21:20) [Материал]
Можно. На выходе БП постоянное отрицательное напряжение или -/+ ?

26 abomin   (29.05.2016 10:54) [Материал]
А можно ли измерить отрицательное напряжение? Хочу тестер компутерных БП сделать.

0  
25 exersizze   (07.05.2015 09:29) [Материал]
tolp, причин может быть много. Это помехи по питанию, помехи на АЦП. Попробуйте влепить по керамическому конденсатору по 0,1мкФ на питание и на порт АЦП

24 tolp   (29.04.2015 23:56) [Материал]
собрал но работает по дурацки индикация постоянно прыгает то 12 то 9.99 вольта показывает .( измеряемое напряжение 12 вольт) подскажите в чем дело ?

23 Scorpushka   (06.07.2014 15:21) [Материал]
скажите пожалуйста, а как измерить переменное напряжение,например в домашней сети, применить диодный мост и увеличить номиналы сопротивлений?

22 sany2   (02.04.2013 22:45) [Материал]
Спасибо(догадывался,что нужно менять местами).И спрашиваю потому -что пытаюсь вникнуть(готовых прошивок полно).Никак макетка не доползёт с проводами(можно было бы методом проб и ошибок допетрить),а сразу в железе или навесу тяжело эксперименты проводить.Пытаюсь соорудить толщиномер из журнала радио с выводом на lcd.Код взят часть из этого проекта,а часть из Показометр уровня аудиосигнала-чтобы была линейка и цифровая шкала(в симуляторе вроде работает).Хотел на Nokia 3310 сделать-но циферки мелковаты получились(как я понял в Bascom эта проблема не решена).А пока закончились и lcd и нет макетки чтобы совместить две схемы озадачился подключением Led индикаторов.

21 exersizze   (02.04.2013 22:02) [Материал]
Простой заменой транзисторов не обойтись,  в коде надо менять все состояния портов.
Например
Код
Portb = &B11101111
       
     Select Case N1
     Case 0 : Portd = &B11111111
     Case 1 : Portd = &B11111001
     Case 2 : Portd = &B10100100
     Case 3 : Portd = &B10110000
     Case 4 : Portd = &B10011001
     Case 5 : Portd = &B10010010
     Case 6 : Portd = &B10000010
     Case 7 : Portd = &B11111000
     Case 8 : Portd = &B10000000
     Case 9 : Portd = &B10010000
     End Select

Нули нужно заменить единицами и наоборот:
Код
Portb = &B00010000
       
     Select Case N1
     Case 0 : Portd = &B00000000
     Case 1 : Portd = &B00000110
     Case 2 : Portd = &B01011011
     Case 3 : Portd = &B01001111
     Case 4 : Portd = &B01100110
     Case 5 : Portd = &B01101101
     Case 6 : Portd = &B01111101
     Case 7 : Portd = &B00000111
     Case 8 : Portd = &B01111111
     Case 9 : Portd = &B01101111
     End Select

Ну и немешало бы вникнуть, без этого никак:)

20 sany2   (02.04.2013 20:37) [Материал]
Добрался таки до семисигментников(точнее делать то делал.вернее тупо копировал-а в суть не вникал).Как в этой схеме подключить с общим катодом-достаточно ли в катодную цепь поставить npn транзисторы-или нужно ещё в коде что-то менять?

19 exersizze   (01.03.2013 08:30) [Материал]
Да, любое в пределах питающего напряжения.

18 skopik   (01.03.2013 00:29) [Материал]
глупий вопрос но, скажине на AREF можно подавать любое напр-ние.?

+1   Спам
17 top   (03.12.2012 01:00) [Материал]
Есть такое понятие в АЦП как время преобразования. Так вот, используя режим single придется ждать дольше чем в режиме free. Проще говоря при вызове getadc() в режиме single происходит запуск преобразования и потом возврат значения, тогда как вызов getadc() в режиме free возвращает уже готовое, ранее преобразованное, значение. Столкнулся с этим когда необходимо было сделать выборку из сигнала с определенной частотой. Что получил в итоге: в режиме single максимальная частота выборки около 4 кГц (не помню точное значение), когда дошло что можно использовать режим free получил необходимые мне 10 кГц. Из-за любопытства проверил, можно получить и 20 кГц. А в целом, если верить даташиту то время преобразования, у mega8 например, (я на ней делал эксперименты) лежит в диапазоне 13 - 260 мкс, что соответствует частотам выборки от 8 кГц (260мкс) до 77 кГц (13мкс).

16 exersizze   (02.12.2012 17:56) [Материал]
Спасибо, а в чем принципиальная разница с режимом Single? Если в обоих случаях значение с ацп считывается вручную по getadc()

+1   Спам
15 top   (01.12.2012 20:00) [Материал]
Насчет Config Adc = Free... Здесь все просто, в этом режиме АЦП оцифровывает значения сам, один за другим - т.е. как только завершилось преобразование одного отсчета начинается преобразование второго. Достаточно только прочитать эти значения. В режиме single производится однократное перобразование в момент вызова getadc().

14 pitato   (19.07.2012 11:11) [Материал]
Всё получилось. Спасибо, буду пробовать притулить второй вольтметр на индикатор.

+1   Спам
13 pchela5   (18.07.2012 12:29) [Материал]
> речь идет на знакогенерирующий HD44780 то замените все

и не забудьте его вначале отконфигурировать

>на простые семисегментные ЖК индикаторы?

с этим сложнее, но тоже решаемо. Но мое мнение - проще и дешевле применить готовый индикатор

12 exersizze   (18.07.2012 11:10) [Материал]
Если речь идет на знакогенерирующий HD44780 то замените все что между Do-Loop на
Code
cls
LCD N1; N2; "."; N3; N4
wait 1


или надо на простые семисегментные ЖК индикаторы?

11 pitato   (18.07.2012 10:43) [Материал]
Здравствуйте. Как перевести этот вольтметр на ЖК? Очень нужно.

10 sherman   (13.06.2012 11:48) [Материал]
Я бы на протеус не стал надеятся, посмотреть все ли работает как надо это да, можно. А вот уже когда нужно что-то посложней-с измерениями, тут только вживую.

9 amv2000   (12.06.2012 21:27) [Материал]
http://www.fayloobmennik.net/1969328 еще на стадии проекта, в том то и дело в Протеусе уже разница видна.

8 Aleks8383   (12.06.2012 20:14) [Материал]
Да чтото очень трудно верится что ацп нелинеен,а вот ваш источник напряжения или тем чем мериете вот то скорее нелинейно.

7 exersizze   (12.06.2012 19:23) [Материал]
Эт лучше у атмеловцев спросить, они лучше знают почему преобразователи в их микроконтроллерах такие)) Опорное напряжение чем задается? Попробуй выключать всю периферию во время считывания ацп.

6 amv2000   (12.06.2012 16:48) [Материал]
А почему не линейность такая в вольтметре? правильно показывает только на краях а в середине 14,09 на АЦП на индикаторе 14,43

5 amv2000   (28.09.2011 20:00) [Материал]
На форуме http://bascomavr.3bb.ru/ было предложено dmm:
Code
  $regfile = "m324pdef.dat" 'для чипа ATMega324p
    '$regfile = "m644def.dat" 'для чипа ATMega644

    Dim Dadc As Word 'данные АЦП (внутреннего)
    Dim Ua As Single 'временное значение
    Dim Ub As Single 'временное значение
    On Adcc Adc_int Nosave 'вектор прерывания от АЦП

    '...

    ' АЦП
    'Admux = &B00011111 'внешняя опора АЦП со входа AREF,  внутренняя выключена
    'измерение на закороченном входе

    '...

    ' измерение с помощью внутреннего АЦП. результат в Ua, выраженный в Вольтах шкалы

    Adcsr = &B10001110 'разрешить АЦП с частотой тактирования F / 64
    'в режиме с естественным положением битов, прерывание разрешено
    '-------
    For Tmpb = 1 To 16 'произвести 16 измерений
    Set Adcsr.6 'запустить АЦП
    Rdiadc1:
    Idle 'останов
    If B_adc = 0 Then 'есть данные внутреннего АЦП?
    Goto Rdiadc1 'нет - повторить
    End If
    Next Tmpb
    Adcsr = &B00000110 'запретить АЦП
    Dadc = Dadc - 25 'коррекция смещения нуля
    Ua = Dadc : Dadc = 0 'в формат с плавающей точкой, а исходный очистить
    Ua = Ua * 0.0003052 'привести к шкале 0...5 В
    Return

    'обработка прерывания от внутреннего АЦП
    Adc_int:
    $asm
    Push R31 'сохраним регистры
    In R31 , Sreg
    Push R31
    Push R30
    Push R29
    '-----
    'считать данные внутреннего АЦП
    lds R29 , {Dadc} 'считать сумму
    Lds R30 , {Dadc + 1}
    ' In R31 , Adcl 'считать показания (M32)
    lds R31 , Adcl 'считать показания (M644)
    Add R29 , R31 'добавить к сумме показания АПЦ
    ' In R31 , Adch ' (M32)
    LDS R31 , Adch ' (M644)
    Adc R30 , R31
    Sts {Dadc} , R29
    Sts {Dadc + 1} , R30
    '-----
    Ldi R31 , 255 'есть данные внутреннего АЦП
    Sts {B_adc} , R31
    '-----
    Adcinte:
    Pop R29 'восстановим регистры
    Pop R30
    Pop R31
    Out Sreg , R31
    Pop R31
    Reti
    $end Asm
    Return

4 exersizze   (28.09.2011 18:41) [Материал]
Да вообще лучше прерывания отрубать на время вычислений и преобразований, чтобы не словить в ненужный момент. Но здесь всего одно и случается достаточно редко, поэтому преобразование закончится задолго до того как произойдет следующее прерывание.
А чем тогда уровень сигнала снимать если не командой GetADC О_о
Преобразователь останавливается по команде Stop ADC или не то?
Опорное выбирается исходя из поставленных задач: где-то будет достаточно внутреннего иона с погрешностью ~12% (производитель гарантирует диапазон от от 2.3 до 2.9В), а где-то лучше использовать внешний источник, например тоже TL431 (у него не более 3мВ погрешность при правильной обвязке).

3 amv2000   (28.09.2011 11:10) [Материал]
Часто советуют отключать прерывания на время работы АЦП, да и функцию GETADC()не рекомендуют применять, так как нет останова и тем создаёт помехи. Имеет смысл обращать на это внимание и какое опорное предпочтительнее?

1-30 31-32
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]






авторизация
Логин:
Пароль: