Этот шаг можно считать релаксирующим по сравнению с предыдущим. И программа попроще, и новых команд поменьше, да и сам модуль АЦП легче для понимания.
Итак, начнем с рассмотрения самого модуля АЦП. Для тех, кто совершенно не в курсе (хотя, думаю, таких на седьмом шаге уже нет, но все же...), расшифровываю: АЦП - аналого-цифровое преобразование. Тут ситуация, обратная ЦАП. Модуль АЦП преобразует напряжение, поступающее на вход, в пропорциональное ему число, записываемое в определенный регистр.
В микроконтроллерах 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 непрерывно: яркость свечения светодиода должна быть пропорциональна входному напряжению.
Задание хоть и выглядит громоздким, но реализация его не так уж сложна. Текст программы, выполняющей поставленную задачу, представлен ниже.
.include "F:\Prog\AVR\asm\Appnotes\tn13def.inc" .org 0 ;Задание нулевого адреса старта программы rjmp reset ;Безусловный переход к метке reset .org 9 ;Задание адреса прерывания по окончанию преобразования АЦП rjmp ADC_complete;Безусловный переход к метке ADC_complete
reset: ;Начало раздела инициализации контроллера ldi r16,RAMEND ;Загрузка в регистр r16 адреса верхней границы ОЗУ out SPL, r16 ;Копирование значения из r16 в регистр указателя стека SPL ldi r16, 1|(1<<4);Загрузка в r16 единиц в нулевой и четвертый биты out DDRB,r16 ;Переключение выводов PB0 и PB4 на выход ldi r16,(1<<ADLAR)|(1<<MUX0)|(1<<MUX1);См. описание программы out ADMUX,r16 ;Копирование из r16 в ADMUX ldi r16,(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2);См.опис. out ADCSRA,r16 ;Копирование из r16 в ADCSRA ldi r16,(1<<WGM00)|(1<<WGM01)|(1<<COM0A1)|(1<<COM0A0);Fast PWM out TCCR0A,r16 ;с включением OC0A при совпадении с регистром OCR0A ldi r16,(1<<CS01);Загрузка в регистра r16 единицы в бит CS01 out TCCR0B,r16 ;Установка делителя тактовой частоты таймера 0 равным 8 sei ;Глобальное разрешение прерываний
main: ;Основной цикл программы rjmp main ;Конец основного цикла программы
ADC_complete:;Начало обработчика прерывания от АЦП in r16,ADCH ;Копирование в регистр r16 результата преобразования out OCR0A,r16 ;Копирование из r16 в регистр OCR0A sbrs r16,7 ;Если 7-й бит в регистре r16 равен 1, пропустить след. строку sbi PORTB,4 ;Установить 4-й бит PORTB (выключить LED2) sbrc r16,7 ;Если 7-й бит в регистре r16 равен 0, пропустить след. строку cbi PORTB,4 ;Сбросить 4-й бит в регистре PORTB (включить LED2) 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 максимального, то должны гореть оба светодиода.
Засим разрешите откланяться до следующего шага. Он обещает быть не в пример труднее предыдущих, так что собирайтесь с силами и с духом.
Если у вас возникнут вопросы, задавайте их на форуме или здесь в виде комментариев к статье.
.org 0 ; Задание нулевого адреса старта программы rjmp reset ; Безусловный переход к метке reset .org 9 ; Задание адреса прерывания по окончанию преобразования АЦП rjmp ADC_complete ; Безусловный переход к метке ADC_complete
reset: ; Начало раздела инициализации контроллера ldi r16, RAMEND ; Загрузка в регистр r16 адреса верхней границы ОЗУ out SPL, r16 ; Копирование значения из r16 в регистр указателя стека SPL
ldi r16, (1<<Led1)|(1<<Led2) ; Загрузка в r16 единиц в нулевой и четвертый биты out DDRB, r16 ; Переключение выводов PB0 и PB4 на выход
ldi r16, (1<<ADLAR)|(0<<MUX0)|(1<<MUX1) ; См. описание программы PB4/ADC2 MUX(1,0) на моей плате out ADMUX, r16 ; Копирование из r16 в ADMUX
ldi r16, (1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2) ; См.опис. out ADCSRA, r16 ; Копирование из r16 в ADCSRA
sei ; Глобальное разрешение прерываний
main: ; Основной цикл программы rjmp main ; Конец основного цикла программы
ADC_complete: ; Начало обработчика прерывания от АЦП in r16, ADCH ; Копирование в регистр r16 результата преобразования cpi r16, 170 ; 2/3 brsh ledfull
Здравствуйте у меня тоже не выходило как igorit .Коменти я не читал а мудрил сам вот что у меня вышло ,но задачу автора я изменил под свои потребности но смисл тот самой у меня 3 светодиода, в крайнем положении один светодиодов моргает, по мере вращения потонцеометра он засвечивается и включаются другие, в другом крайнем положении два светятся один моргает.Вот как я реализовал .
.include "tn13def.inc" .org 0 ;Задание нулевого адреса старта программы rjmp reset ;Безусловный переход к метке reset .org 9 ;Задание адреса прерывания по окончанию преобразования АЦП rjmp ADC_complete ;Безусловный переход к метке ADC_complete
reset: ;Начало раздела инициализации контроллера ldi r16,RAMEND ;Загрузка в регистр r16 адреса верхней границы ОЗУ out SPL, r16 ;Копирование значения из r16 в регистр указателя стека SPL ldi r16, 7|(1<<4) ;Загрузка в r16 единиц в нулевой и четвертый биты out DDRB,r16 ;Переключение выводов PB0 и PB4 на выход ldi r16,(1<<ADLAR)|(1<<MUX0)|(1<<MUX1);См. описание программы out ADMUX,r16 ;Копирование из r16 в ADMUX ldi r16,(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2);См.опис. out ADCSRA,r16 ;Копирование из r16 в ADCSRA ldi r16,(1<<WGM00)|(1<<WGM01)|(1<<COM0A1)|(1<<COM0A0);Fast PWM out TCCR0A,r16 ;с включением OC0A при совпадении с регистром OCR0A ldi r16,(1<<CS01) ;Загрузка в регистра r16 единицы в бит CS01 out TCCR0B,r16 ;Установка делителя тактовой частоты таймера 0 равным 8 ldi r16,0 ;загрузка в регистр r16 значения 0 (мотор виключен) out OCR0A,r16 ;задание начаьной скорости мотора sei ;Глобальное разрешение прерываний
main: ;Основной цикл программы cpi r16,250 ;сравниваем значение регистра r16 с 250 brlo sb0 ;если менше, то переходим к следущей подпрограмми sb0 cbi PORTB,2 cbi PORTB,1 sbi PORTB,4 rcall delay ;визов подпрограмми задержки cbi PORTB,4 rcall delay ;визов подпрограмми задержки sbi PORTB,4 rjmp sd ;переход к метке sd
sb0: cpi r16,168 ;сравниваем значение регистра r16 с 168 brlo sb1 ;если менше, то переходим к следующей подпрограмми sb1 cbi PORTB,2 cbi PORTB,1 sbi PORTB,4 rjmp sd
ADC_complete: ;Начало обработчика прерывания от АЦП in r16,ADCH ;Копирование в регистр r16 результата преобразования out OCR0A,r16 ;задания оборотов мотора
Добрый день! Пытаюсь освоить... что-то получается, что-то нет. Вот с этим уроком у меня возникли трудности. А точнее проблема с определением результата преобразований АЦП. Исходя из вашего кода: out OCR0A,r16 ;Копирование из r16 в регистр OCR0A sbrs r16,7 ;Если 7-й бит в регистре r16 равен 1, пропустить след. строку получается что мы даже не число определяем, а всего лишь 1 бит из всего числа. А как быть если я хочу подключить 4 светодиода, так что бы 1-й загорался когда в ADCH будет записано число 5-10, 2-й загорался когда будет 64, 3-й загорался когда будет 128, 4-й загорался когда будет 192.. Мучился весь день так и не понял как это сделать. Ведь привязка к конкретному байту не поможет в ситуации когда нужно отслеживать, на пример, 64. Так как при значении большем чем 127 байт отвечающий за "64" может быть равен как "0" так и "1"...
Добрый день! Если Вы хотите использовать несколько шагов, то одним битом, естественно, не обойтись. В таком случае надо использовать команды не битового сравнения, а побайтового, типа cpi (была рассмотрена в шестом шаге).
.... cpi r16,128 ; вместо 128 можно использовать любое значение 0-225 BRSH Displa sbi PORTB,4 ;Установить 4-й бит PORTB (выключить LED2) rjmp Return Displa: cbi PORTB,4 ;Сбросить 4-й бит в регистре PORTB (включить LED2) Return: reti
Своеобразный контроллер Mega, пока не запишешь оба регистра ADCL и ADCH в разные РОНы работать отказывается, а работаешь только младшим регистром. Пять регистров задействовал по домашнему заданию.
Да, есть такое дело. Ну что поделать, таковы особенности архитектуры. Но в этом есть своя логика. Если бы использовались оба регистра, и оба были бы не равны нулю, то где гарантия, что Вы считали бы из обоих значения одного расчета,а не двух последующих? А так один блокируется, пока не будет считан другой, и все счастливы
Авария случилась с программатором, банальный случай, замудрил разъем программатора mini USB. Не советую использовать, вначале неопознаное устройство в диспетчере, а потом драйвера не было. Пол дня потерял из-за этого.
Ну что ж, АЦП заработал на Mege8. Смещение в лево не понадобилось, пришлось задействовать младший и старший регистры OCR1, пока младший не записан в старший результат преобразования не записывается. Такие условия производителя.
Уважаемый Сергей - спасибо за доступные уроки по программированию. Пробую ваши проги - правда перевожу на atmega16 (имею отладочную плату c ней). Как справочник использую книгу Евстифеева. Возникают вопросы. Зацыклился на АЦП. В Протеусе работает, а на железе нет регулятор яркости. Может подскажете, где ошибка? Привожу код: .include "m16def.inc" ; Используем ATMega16 8000000МГц
; FLASH ====================================================== .CSEG .ORG $000 ; (RESET) RJMP Reset .ORG $002 Reti ; ; (INT0) External Interrupt Request 0 .ORG $004 Reti ; (INT1) External Interrupt Request 1 .ORG $006 RETI ; (TIMER2 COMP) Timer/Counter2 Compare Match .ORG $008 RETI ; (TIMER2 OVF) Timer/Counter2 Overflow .ORG $00A RETI ; (TIMER1 CAPT) Timer/Counter1 Capture Event .ORG $00C Reti ; (TIMER1 COMPA) Timer/Counter1 Compare Match A .ORG $00E RETI ; (TIMER1 COMPB) Timer/Counter1 Compare Match B .ORG $010 RETI ; (TIMER1 OVF) Timer/Counter1 Overflow .ORG $012 reti ; (TIMER0 OVF) Timer/Counter0 Overflow .ORG $014 RETI ; (SPI,STC) Serial Transfer Complete .ORG $016 RETI ; (USART,RXC) USART, Rx Complete .ORG $018 RETI ; (USART,UDRE) USART Data Register Empty .ORG $01A RETI ; (USART,TXC) USART, Tx Complete .ORG $01C Rjmp adc_comp ; (ADC) ADC Conversion Complete .ORG $01E RETI ; (EE_RDY) EEPROM Ready .ORG $020 RETI ; (ANA_COMP) Analog Comparator .ORG $022 RETI ; (TWI) 2-wire Serial Interface .ORG $024 RETI ; (INT2) External Interrupt Request 2 .ORG $026 reti ; (TIMER0 COMP) Timer/Counter0 Compare Match .ORG $028 RETI ; (SPM_RDY) Store Program Memory Ready
.ORG INT_VECTORS_SIZE ; Конец таблицы прерываний
; Interrupts ============================================== adc_comp: in r16,adch ;считываем значение преобразования out ocr1al,r16 ;записываем в регистр сравнения t1a в младний байт ;может нужно обнулить старший байт? но ведь он и так равен 0 reti
; End Interrupts ==========================================
Reset: LDI R16,Low(RAMEND) ; Инициализация стека OUT SPL,R16 ; Обязательно!!!
LDI R16,High(RAMEND) OUT SPH,R16
;Hardware Init ====================================== ;Настраиваем работу АЦП ;(1<<refs0) опорное напряжение +5В ;(1<<adlar) выравнивание по левому краю ;(1<<mux4) вход pa0/adc0 без усиления ;(1<<aden) разрешаем АЦП ;(1<<adsc) запускаем ;(1<<adate) непрерывное преобразование ;(1<<adie) разрешаем прерывание после завершения преобразования ;(1<<adps2)|(1<<adps1)|(1<<adps0) коэф. деления 1:128 (на железе 8000000Гц) ; ldi r16,(1<<refs0)|(1<<adlar)|(1<<mux4) out admux,r16 ldi r16,(1<<aden)|(1<<adsc)|(1<<adate)|(1<<adie)|(1<<adps2)|(1<<adps1)|(1<<adps0) out adcsra,r16
;;Настраиваем работу Т1 режим Быстрый ШИМ ;(1<<com1a1) 1 при совпадении ;(0<<wgm13)|(1<<wgm12)|(0<<wgm1)|(1<<wgm10) fast pwm 8 разрядов ;(0<<cs12)|(0<<cs11)|(1<<cs10) деление 1:1 ;
sbi DDRd, 5 ;Установка 5-го бита в регистре DDRd в "1" (Рd5 - выход) ldi r16,(1<<com1a1)|(0<<wgm12)|(1<<wgm10) out tccr1a,r16 ; задействуем вывод pd5 ldi r16,(0<<cs12)|(0<<cs11)|(1<<cs10)|(0<<wgm13)|(1<<wgm12) out tccr1b,r16 ; задаем частоту и режим
ldi r16,0 out ocr1al,r16 ;начальное значение регистра сравнения
SEI ; End Hardware Init ===================================
; Main ========================================================= Main:
Рекомендую Вам обратиться к разделу 7.6.1 "Обращение к 16-битны регистрам" книги Евстифеева (стр. 372). Там указано, что при загрузке значений в 16-битный регистр первым нужно загружать старший байт,а затем младший. Поэтому Ваши сомнения касательно того, нужно ли загружать старший байт, если он равен нулю, думаю, вполне обоснованы
Заработало!!! Спасибо за ответ. Проблема была в настройках входа adc0/pa0. Я не разобравшись задал его как дифференциальный (1<<mux4), а надо было все биты mux4…mux0 задать по нулям(не трогать вообще). По книге так и не понял что такое дифференциальный. Может это используются два входа сразу? Но как подключать переменник? В книге Ефстифеева прочитал раздел 13.6.1 про обращение к 16-разрядным регистрам (у меня наверное другого года издательства – не совпадают страницы). Не менял ничего при считывании-записи: ; Interrupts ============================================== adc_comp: in r16,adch out ocr1al,r16 reti
Правда для надежности добавил запрет прерываний на входе и разрешение на выходе. Получилось так: ; Interrupts ============================================== adc_comp: cli in r16,adch out ocr1al,r16 sei reti
Извините, может написал что-то лишнее. Просто рад, что получилось.
Рад, что Вы смогли разобраться у меня как-то была такая же почти проблема, поэтому и написал совет из личного опыта. Насчет дифференциального входа - Вы совершенно правы, в этом режиме используется два входа. Он бывает полезен при изменении тока или переменного напряжения. В первом случае в цепь включается резистор, на котором измеряется падение напряжения. Понятно, что не всегда нужно измерять ток, протекающий откуда-то к земле, поэтому однополярным режимом не обойтись. Насчет второго случая, думаю, тоже ясно. При обычном (однополярном) подключении измеряется потенциал на входе АЦП относительно земли, и если он отрицательный, контроллер все равно будет его воспринимать как 0. А в дифференциальном режиме появляется возможность измерять разность потенциалов, поступающих на два входа АЦП, что позволяет измерять и положительные, и отрицательные напряжения.