Go up
Вы используете устаревший браузер. Подробнее »
Чтобы использовать все возможности сайта, загрузите и установите один из этих браузеров: Используется тема VK-Style © http://Sergey.Pro
Posts The private archive of professor Preobrazhensky
Картинка профиля Профессор Преображенский

Этот шаг можно считать релаксирующим по сравнению с предыдущим. И программа попроще, и новых команд поменьше, да и сам модуль АЦП легче для понимания.

Итак, начнем с рассмотрения самого модуля АЦП. Для тех, кто совершенно не в курсе (хотя, думаю, таких на седьмом шаге уже нет, но все же…), расшифровываю: АЦП — аналого-цифровое преобразование. Тут ситуация, обратная ЦАП. Модуль АЦП преобразует напряжение, поступающее на вход, в пропорциональное ему число, записываемое в определенный регистр.

В микроконтроллерах AVR применяются 10-битные АЦП. Это значит, что максимально возможное число в регистре АЦП равно 1023. Как же вместить это значение в один регистр? Вот тут и кроется военная хитрость. Регистр АЦП — двухбайтный, и значение записывается одновременно в два байта, и должно считываться и обрабатываться также как двухбайтное. Однако, если нет необходимости в использовании 10-битной точности, а достаточно лишь 8-битной, то есть возможность считывать результат всего из одного регистра. В нашей сегодняшней программе мы именно так и поступим.

Далее возникает еще один вопрос. А какому же напряжению будет соответствовать максимальное число в регистре АЦП? В контроллере ATtiny13 на этот вопрос есть два варианта ответа. Максимальное напряжение, подаваемое на вход АЦП, определяется величиной так называемого опорного напряжения. Для ATtiny13 в качестве источника опорного напряжения может выступать либо источник питания контроллера (в нашем случае это 5 В), либо встроенный источник опорного напряжения величиной 1,1 В. Откуда именно будет поступать опорное напряжение для модуля АЦП, определяется при помощи специального бита. Об этом чуть позднее.

В качестве входов АЦП можно использовать один из четырех выводов, в названии которых есть обозначение ADCх (где х = 0, 1, 2, 3). Если посмотреть на схему нашей платы, а потом на обозначение выводов контроллера, можно видеть, что мы подключили движок переменного резистора к входу ADC3. Вывод, с которого в данный момент будет считываться напряжение, определяется опять же при помощи специальных битов.

Следующий нюанс. Один цикл преобразования АЦП занимает 13 тактов. Рекомендуется для повышения точности преобразования использовать в качестве тактового сигнала АЦП источник с частотой 50…200 кГц. Для этого, как и в рассмотренных ранее таймерах используется делитель частоты. Коэффициент деления также задается при помощи соответствующих битов.

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

Собственно, на этом теоретическое введение можно считать оконченным. Перейдем к практике.

Напишем программу, которая бы в непрерывно считывала напряжение с движка переменного резистора и преобразовывала его в 8-битное число. В качестве источника опорного напряжения АЦП использовать источник питания 5 В. Величину считанного напряжения индицировать при помощи светодиодов LED1 и LED2 таким образом:
— при помощи LED2 дискретно: если напряжение на входе АЦП меньше 2,5 В светодиод LED2 погашен, если больше, то зажжен;
— при помощи LED1 непрерывно: яркость свечения светодиода должна быть пропорциональна входному напряжению.

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

  1. .include "F:\Prog\AVR\asm\Appnotes\tn13def.inc"
  2. .org 0            ;Задание нулевого адреса старта программы
  3. rjmp reset        ;Безусловный переход к метке reset
  4. .org 9            ;Задание адреса прерывания по окончанию преобразования АЦП
  5. rjmp ADC_complete ;Безусловный переход к метке ADC_complete
  6.  
  7. reset:            ;Начало раздела инициализации контроллера
  8. ldi r16,RAMEND    ;Загрузка в регистр r16 адреса верхней границы ОЗУ
  9. out SPL, r16      ;Копирование значения из r16 в регистр указателя стека SPL
  10. ldi r16, 1|(1<<4) ;Загрузка в r16 единиц в нулевой и четвертый биты
  11. out DDRB,r16      ;Переключение выводов PB0 и PB4 на выход
  12. ldi r16,(1<<ADLAR)|(1<<MUX0)|(1<<MUX1);См. описание программы
  13. out ADMUX,r16     ;Копирование из r16 в ADMUX
  14. ldi r16,(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2);См.опис.
  15. out ADCSRA,r16    ;Копирование из r16 в ADCSRA
  16. ldi r16,(1<<WGM00)|(1<<WGM01)|(1<<COM0A1)|(1<<COM0A0);Fast PWM
  17. out TCCR0A,r16    ;с включением OC0A при совпадении с регистром OCR0A
  18. ldi r16,(1<<CS01) ;Загрузка в регистра r16 единицы в бит CS01
  19. out TCCR0B,r16    ;Установка делителя тактовой частоты таймера 0 равным 8
  20. sei               ;Глобальное разрешение прерываний
  21.  
  22. main:             ;Основной цикл программы
  23. rjmp main         ;Конец основного цикла программы
  24.  
  25. ADC_complete:     ;Начало обработчика прерывания от АЦП
  26. in r16,ADCH       ;Копирование в регистр r16 результата преобразования
  27. out OCR0A,r16     ;Копирование из r16 в регистр OCR0A
  28. sbrs r16,7        ;Если 7-й бит в регистре r16 равен 1, пропустить след. строку
  29. sbi PORTB,4       ;Установить 4-й бит PORTB (выключить LED2)
  30. sbrc r16,7        ;Если 7-й бит в регистре r16 равен 0, пропустить след. строку
  31. cbi PORTB,4       ;Сбросить 4-й бит в регистре PORTB (включить LED2)
  32. reti              ;Возврат из прерывания

Как видите, тут гораздо больше места занимает блок инициализации, нежели собственно рабочий блок программы.

Итак, очередные новые команды, встречающиеся в данной программе.

Команда in имеет два операнда: РОН и РВВ. В результате ее выполнения содержимое РВВ копируется в РОН. Таким образом, эта команда противоположна по назначению команде out (собственно, это видно даже из их названия).

Команда sbrs имеет два операнда: РОН и номер бита в этом РОН (от 0 до 7). Если бит с указанным номером в данном регистре равен «1», то следующая строка пропускается. Эта команда аналогична команде sbis, только предназначена для проверки не РВВ, а РОН.

Команда sbrc является противоположностью предыдущей. Если указанный бит в указанном регистре равен «0», то следующая строка пропускается. Опять же по своему назначению она аналогична sbic, и опять же, в отличие от sbic, проверяющей только РОН, применяется только для РВВ.

Остальные команды уже встречались ранее. Кстати, замечу в скобках, что в нашем распоряжении уже 23 ассемблерные команды.

Теперь перейдем к рассмотрению программы. Как и на прошлом шаге, я буду это делать с пропуском уже известных конструкций.

Начинается программа как всегда с таблицы векторов прерываний. На этот раз нам необходимо задействовать прерывание по окончанию цикла преобразования АЦП, находящееся по адресу 9, что и указано в 4-й строке.

Далее блок инициализации модулей контроллера. На нем придется остановиться поподробней, поскольку многое в нем непонятно.

10 строка. Командой ldi в регистр r16 загружается конструкция 1|(1<<4). Возможно она покажется непонятной с первого взгляда, хотя в ней ничего сложного нет. Загружается несмещенная «1» и «1», смещенная на 4 бита влево. Команда «Побитовое ИЛИ» (|) указывает, что нужно суммировать эти единицы.

11 строка. Командой out содержимое r16 копируется в регистр DDRB тем самым переводя на вход выводы РВ0 (светодиод LED1) и РВ4 (светодиод LED2).

12, 13 строки. В регистре ADMUX устанавливаются едиицы в битах ADLAR, MUX0 и MUX1. Регистр ADMUX предназначен для управления мультиплексором модуля АЦП, то есть для выбора того входа, с которого в данный момент нужно считывать напряжение. Этот выбор как раз задается битами MUXx следующим образом:

 MUX1 MUX0 Вход АЦП
 0 0  ADC0
 0 1  ADC1
 1 0  ADC2
 1 1  ADC3


Поскольку мы уже выше договорились, что движок переменного резистора подключен ко входу ADC3, то мы и установили в единицу оба бита мультиплексора.

Назначение бита ADLAR не столь очевидно. Попытаюсь описать как можно понятней. Итак, я уже говорил ранее, что по умолчанию точность АЦП составляет 10 бит, и результат преобразования хранится в двух байтах. Но полная вместимость двух байт составляет 16 бит. Получается, что шесть бит остаются незадействованными. По умолчанию эти 6 бит — это шесть старших бит старшего байта, то есть результат представляется в следующем виде: 0b000000хххххххххх, где «х» — это любое значение (0 или 1). При таком хранении результата нам обязательно нужно считывать и обрабатывать оба байта. Если же нам достаточно 8-битной точности, то можно выравнять результат по левой границе слова, оставив незадействованными младшие 6 бит младшего байта: 0bхххххххххх000000. В этом случае нам достаточно считать только старший байт, а младший вовсе не трогать. Потерянные младшие два бита результата, конечно, несколько сократят нам точность, но для нашего задания как раз и нужна именно 8-битная точность. Собственно, о чем я… Так вот, бит ADLAR, как уже многие читатели догадались, как раз и предназначен для задания выравнивания результата. Если он равен нулю, то результат равняется по правой границе, а если единице, то по левой. Так как нам нужно равнение именно по левой границе, мы и устанавливаем данный бит.

Еще в регистре ADMUX есть бит REFS0. Мы его оставили равным нулю. Однако, я считаю, что о нем стоит упомянуть. Если он равен «0», то в качестве источника опорного напряжения для модуля АЦП используется источник питания, а если равен «1», то внутренний источник величиной 1,1 В.

14, 15 строки. Тут все еще более весело. В регистре ADCSRA устанавливаются единицы в битах ADEN, ADSC, ADATE, ADIE, ADPS2. Рассмотрим их по порядку.

Бит ADEN разрешает функционирование модуля АЦП. Если он установлен, то модуль активен, если сброшен, то, соответственно, неактивен.

Бит ADSC запускает преобразование. В режиме одиночного преобразования именно установка этого бита в «1» стартует преобразование, и далее модуль АЦП ожидает очередной установки его в «1». В режиме непрерывного преобразования установка этого бита определяет старт первого преобразования, а все последующие уже не зависят от состояния бита ADSC.

Бит ADATE Как раз и определяет, в каком режиме будет работать модуль АЦП. Если он равен «0», то устанавливается режим одиночного преобразования, а если «1», то режим непрерывного преобразования.

Бит ADIE разрешает генерацию прерывания по завершению цикла преобразования АЦП. Поскольку мы собираемся использовать именно это прерывание, то в программе мы и устанавливаем данный бит.

Биты ADPSx (х = 0, 1, 2) Определяют коэффициент деления тактовой частоты контроллера для тактирования модуля АЦП. Этот коэффициент зависит от указанных битов следующим образом:

 ADPS2  ADPS1  ADPS0 Коэффициент деления
0 0 0 2
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 128


Мы установили в «1» только бит ADPS2, тем самым задав коэффициент деления равным 16. При этом тактовая частота модуля АЦП составит 1000000/16=62500Гц, что вполне укладывается в рекомендуемые границы 50-200 кГц.

Как я уже говорил, эти биты относятся к регистру ADCSRA. Но раз есть А, значит есть и B. И таки да, есть регистр ADCSRB. Его биты определяют источник запуска нового преобразования АЦП в режиме непрерывного преобразования. У нас преобразования должны следовать непосредственно одно за другим, а этот режим устанавливается, если все биты ADCSRB равны 0, поэтому мы данный регистр и не задействовали в программе.

16-20 строки. Полностью аналогичны таковым в программе шестого шага, поэтому на них я не останавливаюсь.

25 строка. Метка ADC_complete определяет начало обработчика прерывания по завершению цикла преобразования АЦП.

26-27 строки. Копирование содержимого РВВ ADCH в РВВ OCR0A через промежуточный РОН r16. Как я уже говорил ранее, нельзя непосредственно скопировать содержимое одного РВВ в другой РВВ. Для этого нужно использовать промежуточный РОН и команды in и out. Регистр ADCH — это старший регистр результата преобразования АЦП, младший называется, как нетрудно догадаться, ADCL, но мы его не используем по причинам, описанным выше. Итак, что же происходит в результате выполнения этих строк? Значение, записанное в ADCH, пропорционально напряжению на входе ADC3, а яркость свечения светодиода пропорциональна значению, записанному в OCR0A. Таким образом, путем копирования одного регистра в другой мы получим, что яркость светодиода будет пропорциональна входному напряжению, что и требовалось по заданию.

28-31 строки. Они должны что-то напоминать внимательному читателю, что-то давно забытое и простое… Если не вспомнили, поведаю, что похожая конструкция использовалась нами в самой первой нашей программе (во втором шаге), только там мы проверяли нажатие кнопки. Что же происходит тут? В строках 28 и 30 мы проверяем 7-й бит регистра r16 (в 28 строке на «1», а в 30 — на «0»). Почему так? По заданию нам нужно включать светодиод LED2 если напряжение на входе больше 2,5 В. 2,5 В — это половина напряжения питания. При этом в регистре ADCH будет записано число 256/2=128. В двоичной форме это число выглядит как 0b10000000. А число 127 имеет представление 0b01111111. Таким образом, если в ADCH значение больше или равно 128, то в старшем (седьмом) бите будет «1», а если меньше, то «0». Именно эту проверку мы и совершаем в строках 28 и 30. Для особо невнимательных читателей, недоумевающих, почему я рассказываю о регистре ADCH, а проверяем мы регистр r16, поясню: в 26 строке мы копируем значение из ADCH в r16, так что в дальнейшем нет никакой разницы, какой из них проверять. В строках 29 и 31 происходит установка (строка 29) или сброс (строка 31) четвертого бита в регистре PORTB, при этом происходит соответственно выключение либо включение светодиода LED2. Логику работы смотрите во втором шаге. Она нисколько не изменилась.

Да вот, как бы и все, что я имел сказать по поводу АЦП. По большому счету я, сам того не желая, практически полностью расписал работу модуля АЦП, хотя поначалу планировал описывать только работу с ассемблером. Ну да ладно. С меня не убудет, а вам меньше лазить по даташитам.

Ну и напоследок задание для самостоятельного выполнения.

Написать программу графической индикации величины напряжения, поступающего на вход АЦП при помощи светодиодов LED1 и LED2. Если напряжение меньше 1/3 от максимального, оба светодиода должны быть погашены, если напряжение находится в пределах от 1/3 до 2/3 от максимального, должен гореть светодиод LED1, а если напряжение больше 2/3 максимального, то должны гореть оба светодиода.

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

Если у вас возникнут вопросы, задавайте их на форуме или здесь в виде комментариев к статье.

Желаю успехов!

Автор: Сергей Сокол, материал взят с его сайта https://sokolsp.at.ua/


No comments yet.

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

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">

Перейти к верхней панели