ГлавнаяРегистрацияВход Сайт Сокола Сергея Вторник, 26.11.2024, 20:09
  Каталог статей Приветствую Вас Гость | RSS

 
 
Главная » Статьи » Мои статьи

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

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

В микроконтроллерах 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 следующим образом:

 MUX1MUX0
Вход АЦП
 00
 ADC0
 01
 ADC1
 10
 ADC2
 11
 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
Коэффициент деления
000
2
00
1
2
01
0
4
0
1
1
8
1
0
0
16
10
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 максимального, то должны гореть оба светодиода.

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

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

Желаю успехов!
Категория: Мои статьи | Добавил: mimino (10.01.2012)
Просмотров: 21105 | Комментарии: 38 | Рейтинг: 4.5/6
Всего комментариев: 281 2 »
28 Savage  
0
.include "tn13def.inc"
.equ Led1 = PB0
.equ Led2 = PB1 ; PB4 в оригинале

.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

cpi r16, 85 ; 1/3
brsh ledhalf

sbi PORTB, Led2 ; (выключить LED2)
sbi PORTB, Led1 ; (выключить LED1)
rjmp exit

ledfull:
cbi PORTB, Led2 ; (включить LED2)
cbi PORTB, Led1 ; (включить LED1)
rjmp exit
ledhalf:
sbi PORTB, Led2 ; (выключить LED2)
cbi PORTB, Led1 ; (включить LED1)
exit:
reti

27 kolibin  
0
Здравствуйте у меня тоже не выходило как 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

sb1:
cpi r16,84
brlo sb2
cbi PORTB,1
sbi PORTB,2
sbi PORTB,4
rjmp sd

sb2:
cpi r16,5            ;Сравниваем значение регистра r16 с 5
brlo sb3      
sbi PORTB,1 
sbi PORTB,2
sbi PORTB,4
rjmp sd

sb3:
sbi PORTB,2
sbi PORTB,1 
sbi PORTB,4
rcall delay     
cbi PORTB,1
rcall delay
sbi PORTB,1
rjmp sd
 
sd:

rjmp main            ;Конец основного цикла программы

delay:               ;начало задержки
ldi r17, 255      
ldi r18, 255
ldi r19, 25    
del:          
subi r17, 1       
sbci r18, 0       
sbci r19, 0
brcc del
ret
   

ADC_complete:         ;Начало обработчика прерывания от АЦП
in r16,ADCH           ;Копирование в регистр r16 результата преобразования
out OCR0A,r16         ;задания оборотов мотора
  
reti                  ;Возврат из прерывания

*********************************************************

Вопрос можно ли как то упростить 

cbi PORTB,1
sbi PORTB,2
sbi PORTB,4

**********

cbi PORTB,2
cbi PORTB,1 
sbi PORTB,4

и так далее .

24 igorit  
0
Добрый день!
Пытаюсь освоить... что-то получается, что-то нет.
Вот с этим уроком у меня возникли трудности. А точнее проблема с определением результата преобразований АЦП.
Исходя из вашего кода:
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"...

25 mimino  
0
Добрый день!
Если Вы хотите использовать несколько шагов, то одним битом, естественно, не обойтись. В таком случае надо использовать команды не битового сравнения, а побайтового, типа cpi (была рассмотрена в шестом шаге).

26 igorit  
0
Спасибо! Сделал вот такую конструкцию кода:

....
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

22 AVRик  
0
Своеобразный контроллер Mega, пока не запишешь оба регистра ADCL и ADCH в разные РОНы работать отказывается, а работаешь только младшим регистром. Пять регистров задействовал по домашнему заданию.

23 mimino  
0
Да, есть такое дело. Ну что поделать, таковы особенности архитектуры. Но в этом есть своя логика. Если бы использовались оба регистра, и оба были бы не равны нулю, то где гарантия, что Вы считали бы из обоих значения одного расчета,а не двух последующих? А так один блокируется, пока не будет считан другой, и все счастливы smile

21 AVRик  
0
Программатор AVR-910/AVR-Osp-II, классная штука

19 AVRик  
0
Авария случилась с программатором, банальный случай, замудрил разъем программатора mini USB. Не советую использовать, вначале неопознаное устройство в диспетчере, а потом драйвера не было. Пол дня потерял из-за этого.

20 mimino  
0
Это какой программатор? Microprog?

15 AVRик  
0
Ну что ж, АЦП заработал на Mege8. Смещение в лево не понадобилось, пришлось
задействовать младший и старший регистры OCR1, пока младший не записан в старший результат преобразования не записывается. Такие условия производителя.
prof

14 valik  
0
Уважаемый Сергей - спасибо за доступные уроки по программированию.
Пробую ваши проги - правда перевожу на atmega16 (имею отладочную плату c ней). Как справочник использую книгу Евстифеева. Возникают вопросы. Зацыклился на АЦП. В Протеусе работает, а на железе нет регулятор яркости. Может подскажете, где ошибка?
Привожу код:
.include "m16def.inc" ; Используем ATMega16 8000000МГц

; RAM ========================================================
.DSEG

; 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:

RJMP Main

; EEPROM =====================================================
.ESEG ; Сегмент EEPROM

16 mimino  
1
Доброго времени суток!

Рекомендую Вам обратиться к разделу 7.6.1 "Обращение к 16-битны регистрам" книги Евстифеева (стр. 372). Там указано, что при загрузке значений в 16-битный регистр первым нужно загружать старший байт,а затем младший. Поэтому Ваши сомнения касательно того, нужно ли загружать старший байт, если он равен нулю, думаю, вполне обоснованы smile

17 valik  
0
Заработало!!!
Спасибо за ответ. Проблема была в настройках входа 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

Извините, может написал что-то лишнее. Просто рад, что получилось.
biggrin

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

12 Владимир  
0
Сергей!
Успехи пока топчатся на месте, хочу на Mege8
начать с таймера, чтобы понять суть таймера А и В

13 mimino  
0
Владимир!
Не совсем понял, о чем Вы. В контроллерах AVR ведь таймеры нумеруются цифрами - таймер 0, таймер 1, таймер 2

11 mimino  
0
Владимир!
Как успехи? Удалось что-то сделать?
Сейчас я вроде немного разгрузился. Смогу побольше уделять времени сайту

1-10 11-16
Имя *:
Email *:
Код *:
 
 
Категории раздела
Мои статьи [20]

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Вход на сайт

Поиск

Посетители

Погода
GISMETEO: Погода по г.Мариуполь

 

Copyright MyCorp © 2024
Бесплатный конструктор сайтов - uCoz