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

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

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

Давайте в этот раз поступим так же, как и в прошлый. Сразу поставим себе задачу, а потом будем ее решать. Однако, в отличие от прошлого раза, сегодня задание сформулируем максимально конкретно.

Необходимо по прерыванию от переполнения таймера 0 непрерывно и автоматически изменять содержимое регистра r16 от 0 до 255, а затем в обратном порядке с шагом 1. Кроме того, необходимо изменять яркость свечения светодиода LED1 при помощи ШИМ таким образом, чтобы эта яркость была пропорциональна квадрату содержимого регистра r16.

Итак, как мы видим, можно поставленную задачу разбить на две подзадачи: управление содержимым регистра r16 и математическая обработка этого самого содержимого. И если с первой подзадачей все более-менее ясно, то над второй нужно немного подумать.

Нам нужно изменять яркость светодиода пропорционально квадрату r16. Но значение r16 изменяется в пределах от 0 до 255, а, следовательно, его квадрат - от 0 до 65025. Это значение выходит за пределы однобайтового числа, которое может быть записано в регистр OCR0A. То есть мы должны записать в OCR0A 255, а не 65025, при r16=255. Таким образом, перед нами встает задача масштабирования, решая которую мы должны получить коэффициент пропорциональности между r162 и OCR0A.

По заданию минимальная яркость должна соответствовать нулевому значению в r16, поэтому если r16=0, то и OCR0A=0. А чтобы получить коэффициент масштабирования, нам, как большинство читателей уже догадалось, нужно разделить 65025 на 255. При этом получается число 255 (что, собственно, неудивительно).  Таким образом, чтобы вложиться в однобайтовое число, нам нужно вычислять значение OCR0A по следующей формуле:

OCR0A =
r162 / 255

То есть для выполнения поставленной задачи нам потребуется умножить содержимое r16 само на себя и разделить полученное значение на 255. Именно в такой последовательности, иначе для всех значений r16, кроме 255, OCR0A будет равно 0 (поскольку деление целочисленное, и дробная часть просто отсекается).

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

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

.include "F:\Prog\AVR\asm\Appnotes\tn13def.inc"
.org 0        ;Задание нулевого адреса старта программы
rjmp reset  ;Безусловный переход к метке reset
.org 3         ;Задание адреса прерывания по переполнению таймера 0
rjmp tmr0   ;Безусловный переход к метке tmr0

reset:                    ;Начало раздела инициализации контроллера
ldi r16,RAMEND      ;Загрузка в регистр r16 адреса верхней границы ОЗУ
out SPL, r16           ;Копирование значения из r16 в регистр указателя стека SPL
sbi DDRB, 0           ;Установка 0-го бита в регистре DDRB в единицу (РВ0 - выход)
ldi r16, (1<<TOIE0);Загрузка в регистра r16 единицы, смещенной на TOIE0
out TIMSK0,r16      ;Копирование значения из регистра r16 в регистр TIMSK0
ldi r16,(1<<WGM00)|(1<<WGM01)|(1<<COM0A1)|(1<<COM0A0); Fast PWM
out TCCR0A,r16     ;с включением OC0A при совпадении с регистром OCR0A
ldi r16,(1<<CS01)|(1<<CS00);Загрузка в регистра r16 единицы в бит CS01
out TCCR0B,r16    ;Установка делителя тактовой частоты таймера 0 равным 8
ser r17                 ;Регистр направления изменения яркости
clr r18                  ;Регистр старшего байта для множителя и делителя
ser r22                 ;Регистр младшего байта делителя
sei                       ;Глобальное разрешение прерываний

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

tmr0:                   ;Обработка прерывания по переполнению таймера 0
cpi r16,0              ;Сравнение регистра r16 с 0
breq step_up        ;Если он равен 0,то перейти к метке step_up
cpi r16,255           ;Сравнение регистра r16 с 255
brne set               ;Если не равен 255, то перейти к метке set
ser r17                 ;Иначе установить регистр r17
rjmp set               ;И перейти к метке set
step_up:              ;Метка step_up - признак приращения r16
clr r17                  ;Очистка r17
set:                     ;Метка set - изменение яркости светодиода
sbrs r17,0            ;Если нулевой бит r17 установлен, пропустить след. строку
inc r16                 ;Увеличить r16 на 1
sbrc r17,0            ;Если нулевой бит r17 сброшен, пропустить след. строку
dec r16                ;Уменьшить r16 на 1
rcall multiply         ;Вызов подпрограммы умножения
rcall divide            ;Вызов подпрограммы деления
out OCR0A,r23      ;Копирование в OCR0A значения r23 - результата операций
reti                      ;Возврат из подпрограммы обработки прерывания

multiply:              ;Подпрограмма умножения
clr r19                 ;Очистка младшего байта результата
clr r20                 ;Очистка старшего байта результата
mov r21,r16         ;Копирование в r21 значения из r16
sum:                   ;Цикл суммирования
add r19,r16         ;Прибавление к младшему биту результата содержимого r16
adc r20,r18         ;Прибавление к старшему биту результата 0 с учетом переноса
dec r21               ;Вычитание единицы из регистра r21
brne sum            ;Если результат не равен 0, то вернуться к метке sum
ret                     ;Возврат из подпрограммы

divide:               ;Подпрограмма деления
clr r23                ;Очищаем регистр r23 - частное
minus:               ;Цикл вычитания делителя из делимого
cp r19,r22          ;Сравниваем младшие байты делимого и делителя
cpc r20,r18        ;Сравниваем старшие байты делимого и делителя
brlo exit            ;Если делимое уже меньше, то выходим из цикла
sub r19,r22       ;Иначе вычитаем из делимого делитель (младшие байты)
sbc r20,r18        ;и старшие байты
inc r23              ;Прибавляем к частному единицу
rjmp minus       ;Возвращаемся к началу цикла
exit:                 ;Метка выхода из цикла
ret                   ;Возврат из подпрограммы


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

Команда inc имеет один операнд - РОН. В результате ее выполнения содержимое  указанного РОН увеличивается на единицу.

Команда dec также имеет один операнд - РОН. По назначению она противоположна предыдущей: уменьшает содержимое регистра на единицу.

Команда mov имеет два операнда, оба - регистры общего назначения. В результате ее выполнения в регистр, записанный первым, копируется значение, содержащееся в регистре, записанном вторым. Команда предназначена для работы только с РОН, но никак не с РВВ. Напомню, что для копирования их содержимого в РОН и обратно используются команды in и out соответственно.

Все рассмотренные далее команды будут также иметь два операнда, и оба также только РОН. При этом следует запомнить, что результат выполнения команды всегда записывается в регистр, указанный первым (кроме команд cp  и cpc). Я написал эту общую часть потому, что иначе пришлось бы ее дублировать для всех нижеследующих команд, а это как-то скучно. Далее опишу только результаты выполнения команд.

Команда add выполняет простое суммирование указанных РОН. Если в результате операции происходит перенос в старший разряд, он записывается в бит С статусного регистра SREG.

Команда adc выполняет арифметическое суммирование с учетом переноса из младшего разряда. То есть он суммирует указанные регистры и прибавляет к результату значение бита С. Если в результате снова происходит перенос в старший разряд, он снова сохраняется в бите С.

Команда sub выполняет
простое вычитание второго регистра из первого. Если для выполнения операции требуется заем из старшего разряда, то снова устанавливается в единицу бит С регистра SREG.

Команда sbc выполняет
арифметическое вычитание второго регистра из первого с учетом переноса. То есть из первого РОН вычитается второй, а также вычитается значение бита С. При выполнении этой команды также возможен заем из старшего разряда с автоматической установкой бита С.

Команда cp выполняет сравнение содержимого указанных РОН. По своему смыслу она похожа на команду cpi, только сравнивает она не РОН и константу (как cpi), а два РОН. Команда cp вычитает из первого регистра второй, но не осуществляет сохранения результата в первом регистре. Она только устанавливает флаги выполнения операций в статусном регистре SREG. При выполнении этой команды так же, как и в командах вычитания, может происходить заем из старшего разряда через бит С.

Команда cpc выполняет сравнение содержимого указанных РОН с учетом переноса. Из первого регистра вычитается второй, а также бит переноса С. И так же, как и для предыдущих операций, в результате ее выполнения может устанавливаться бит С.

Ну вот, как-то так. Разберем теперь применение этих команд на практике, в написанной нами программе.

2-5 строки. Таблица векторов прерываний. Тут все уже известно из прошлых шагов.

6-20 строки. Раздел инициализации. В нем тоже все команды и используемые регистры контроллера были описаны ранее. Остановлюсь лишь на назначении регистров, указанных в строках 16,17,18.

Регистр r17 является флагом, определяющим направление изменения яркости. Если он равен 0, то происходит уменьшение яркости, если 255, то увеличение. Вначале мы командой ser записываем в него 255, тем самым задавая, что при старте программы яркость будет увеличиваться.

Регистр r18 командой clr обнуляется. О его назначении расскажу при описании алгоритмов умножения и деления.

В регистр r22 командой ser записывается число 255. Этот регистр будет играть роль делителя в формуле масштабирования, записанной выше.

25 строка. Метка tmr0 - начало подпрограммы обработки прерывания по переполнению таймера 0.

26-33 строки. В них происходит установка и сброс флага r17 при достижении регистром r16 крайних значений (0 или 255). Рассмотрим подробнее, как это работает.

В строках 26-27 происходит сравнение регистра r16 с нулем, и если это равенство выполняется, то осуществляется переход к метке step_up (строка 32), за которой следует команда ser, записывающая в r17 число 255 (строка 33). Таким образом получается, что при достижении регистром r16 значения "0" мы устанавливаем в r17 число 255, определяя, что теперь содержимое регистра r16 должно увеличиваться.

В строках 28 и 29 происходит сравнение регистра r16 с 255, и если равенство не выполняется (команда brcc), происходит переход к метке set (строка 34), а иначе происходит переход к следующей строке (30), в которой осуществляется сброс флага r17 (командой clr). Таким образом, при достижении регистром r16 значения "255" в r17 устанавливается значение "0", определяющее, что далее следует уменьшать содержимое регистра r16.

Если в r16 будет записано любое другое число, то после выполнения строки 29 сразу произойдет переход к метке set без изменения содержимого регистра r17 (почему именно так, подумайте сами, алгоритм довольно прост).

35-38 строки. Конструкция знакомая по прошлым шагам. Мы проверяем младший бит регистра r17 (строки 35, 37), и в зависимости от его значения выполняем увеличение  содержимого регистра r16 на 1, если этот бит равен 1 (строка 36), либо уменьшение на 1, если он равен 0 (строка 38). По большому счету можно было бы проверять абсолютно любой бит регистра r17, так как мы устанавливаем и сбрасываем все восемь бит сразу.

39 строка. Вызов подпрограммы умножения, выполняющей возведение содержимого регистра r16 в квадрат

40 строка. Вызов подпрограммы деления, выполняющей деление полученного результата на 255.

41 строка. Копирование результата двух предыдущих операций (регистр r23) в регистр OCR0A для непосредственного изменения яркости светодиода.

44 строка. Метка multiply - начало подпрограммы умножения. Для начала опишу алгоритм работы этой подпрограммы, а затем уже рассмотрим его реализацию.

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

45, 46 строки. Очистка регистров r19, r20. В этих регистрах мы и будем накапливать квадрат содержимого r16. Я уже говорил в самом начале, что для хранения квадрата однобайтового числа потребуется два байта. Будем считать, что r19 содержит в себе младший байт результата, а r20 - старший.

47 строка. Копирование в РОН r21 содержимого r16. Регистр r21 будет выполнять роль второго множителя. Поскольку нам нужно возвести r16 во вторую степень, то логично, что оба множителя одинаковые.

48 строка. Метка sum обозначает начало цикла суммирования.

49,50 строки. Таким образом осуществляется сложение двух двухбайтовых чисел. Вначале производится суммирование младших байт командой add (строка 49), а затем - старших байт с учетом переноса командой adc (строка 50). Сложение должно осуществляться именно этими командами и именно в такой последовательности. Собственно, это должно быть понятно из обычной математики - мы всегда суммируем, начиная с младшего разряда, и если осуществляется перенос из младшего разряда, мы его учитываем и добавляем к старшему. Тут как раз пришло время объяснить назначение регистра r18. Несмотря на то, что мы прибавляем только младшие разряды, а в старшем накапливаем только переносы, все равно из-за синтаксиса команды adc нужно использовать два регистра. И если мы хотим прибавлять только бит переноса, то второй регистр (r18) нужно сделать равным нулю, что мы и совершили в 17 строке.

51 строка. Вычитание единицы из регистра r21. Тут, думаю, тоже должно быть понятно. Мы сначала загрузили в него нужный нам множитель, а затем на каждом проходе цикла вычитаем из него по "1". Когда в r21 останется 0, значит мы выполнили все итерации, и цикл можно завершить.

52 строка. Собственно, здесь мы и проверяем, не получилось ли в результате предыдущей операции нуля, и если не получилось (команда brne), то переходим к метке sum - началу цикла.

53 строка. Возврат из подпрограммы умножения.

Хочу еще заметить, что при сложении чисел большей разрядности (3, 4 и т.д. байт) алгоритм не меняется. Нужно прибавлять младшие байты командой add, а все последующие - в порядке возрастания командой adc.

55 строка. Метка divide - начало подпрограммы деления. Как и в случае с предыдущей подпрограммой сначала рассмотрим алгоритм ее работы.

В качестве лирического отступления поведаю историю создания этого алгоритма. При изучении ассемблера я для себя решил не пользоваться чужими программами, а каждый раз изобретать велосипед заново. Такую же задачу я поставил и перед своими студентами, которые начали учить этот язык почти одновременно со мной. Когда возникла необходимость написать подпрограмму деления, я сотворил замечательный, как мне тогда казалось, алгоритм. В чем-то он и есть замечательный. Его достоинством является то, что количество итераций  не зависит от величины делителя и делимого, а только от разрядности чисел. Но он получился достаточно громоздким и сложным для понимания и описания, поэтому я его здесь не привожу. Гордый собой, я задал своим студентам написать собственные алгоритмы деления. На следующий вечер мне был явлен алгоритм, поразивший меня до глубины души своей простотой и логичностью. Я не мог понять, как не додумался до такого простого и очевидного решения. Мне он настолько понравился, что я стал его использовать в своих программах. И здесь я привожу именно его с любезного разрешения автора - Марии Забильской. Понимаю, что скорее всего этот алгоритм уже много раз описан в разных учебниках, но в рамках нашего небольшого коллектива я соблюдаю авторские права.

Но это так, к слову. Теперь наконец-то рассмотрим сам алгоритм. Смысл его заключается в многократном вычитании делителя из делимого внутри цикла до тех пор, пока делимое не станет меньше делителя. Одновременно с этим на каждом проходе цикла к частному прибавляется единица. Таким образом после выхода из цикла мы получаем частное, а в регистре, в котором было делимое - остаток от деления. Рассмотрим теперь реализацию алгоритма в программе.

56 строка. Очистка регистра r23. В нем мы будем накапливать частное.

57 строка. Метка minus - начало цикла вычитания делителя из делимого и наращивания частного.

58-59 строки. Сравнение двух двубайтовых чисел: делимого (r19,r20) и делителя (r18,r22). Тут логика та же, что и при суммировании: сначала сравниваются младшие байты при помощи обычной команды равнения (ср), а затем старшие с учетом переноса (срс). Тут тоже стоит вспомнить математику - вычитание так же, как и сложение, начинается с младшего разряда с возможностью заема единицы из старшего. Обратите внимание, что для сравнения старших разрядов мы используем в делителе все тот же регистр r18, равный 0, поскольку наш делитель однобайтный (r22=255), а для синтаксиса команды срс нужно сравнивать два регистра.

60 строка. Если делимое меньше делителя (команда brlo), то выходим из цикла (метка exit), иначе переходим к следующей строке.

61-62 строки. Вычитаем из делимого делитель. Логика тут абсолютно такая же, как и для строк 58-59, единственное отличие в том, что после выполнения команд sub и sbc происходит изменение регистров r19,r20

63 строка. Прибавление к частному (r23) единицы.

64 строка. Безусловный переход к началу цикла (метка minus). Таким образом из цикла мы можем выйти только через строку 60.

65 строка. Метка exit служит для указания места выхода из цикла для строки 60.

66 строка. Возврат из подпрограммы деления.

После этой строки мы попадаем в 41 строку, где частное (r23) копируется в регистр OCR0A.

Ну вот как бы и все, что касается работы программы. Что тут можно еще добавить... При загрузке программы в контроллер светодиод начнет плавно менять свою яркость от минимальной до максимальной и обратно. Возможно, внимательный читатель спросит: "Ну и где тут видно, что оно пропорционально квадрату r16?". Тут должен признаться честно, это не очень отличимо невооруженным глазом. Но для сравнения попробуйте в строке 41 в OCR0A копировать не r23, а r16, и вы увидите, что характер изменения яркости стал несколько иным.

Напоследок традиционное задание для самостоятельного решения.

Написать программу индикации величины напряжения, снимаемого с движка переменного резистора при помощи светодиода LED1. При этом яркость светодиода должна быть пропорциональна кубу считываемого напряжения.

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

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

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

Желаю успехов!
Категория: Мои статьи | Добавил: mimino (15.01.2012)
Просмотров: 16790 | Комментарии: 20 | Рейтинг: 5.0/1
Всего комментариев: 16
15 AVRик  
0
Сергей, я в интернет магазине заказал ATtiny 13, хочу на ней реализовать свои идеи. Да, только нет даташитов в моем справочнике.

16 mimino  
0
Владимир!
Как успехи в освоении Tiny13? На него есть даташит в книге Евстифеева "Микроконтроллеры AVR семейства Tiny. Руководство пользователя"

13 AVRик  
0
Сергей, а как перевести в вольты программно или сосчитать это напряжение с порта ДИФ АЦП ? Какой нибудь бы кусок кода для примерчика ?

14 mimino  
0
Владимир!
Посмотрел в документации. У ATmega8 нет дифференциального входа АЦП. Поэтому можно попробовать реализовать псевдо-дифференциальный вход, если, конечно, ток не меняется с высокой частотой. То есть падение напряжения измерять следующим образом: подключить шунтирующий резистор между двумя входами АЦП. Сначала измерить напряжение с одного входа, потом с другого, а затем вычесть их. И уже разность разделить на сопротивление, чтобы получить ток

11 AVRик  
0
Сергей !
Задачку эту я решил !!! немного код преобразовал и все пошло.
Прошу наставить меня на путь истинный. Задача предомной вот какая: при подаче напряжение на порт, нужно измерить ток порта и выставить единицу на другом порту. Начиная от 7-8 мА и более, но естественно все это через токоограничивающий делитель на 20мА. Я думаю измерение должно происходить относительно земли. Как решить пока не знаю ?

12 mimino  
0
Владимир!
Вот тут как раз и применяются дифференциальные входы, о которых я писал ранее. Задача схемотехнически решается так: последовательно с портом включается токоограничивающий резистор, измеряется падение напряжение на нем (как раз при помощи дифференциального входа АЦП), а затем полученное значение переводится в Вольты и делится на величину сопротивления, получая Амперы

9 AVRик  
0
Спасибо Сергей!
Уроки заканчиваются, но грабли остаются !
Насчет времени: наверно не все студенты здали с первого раза, понимаю им тоже надо время уделять.

10 mimino  
0
Владимир!
Да нет, студенты как раз отнимают мало времени. Просто приходится крутиться, искать подработки, а сейчас подошел срок сдачи работы. Надеюсь все за эту неделю завершить. Сильно надеюсь...

6 Владимир  
0
Сергей !
Мое сообщение не видно ?

7 mimino  
0
Владимир!
Видно. Просто сейчас занят, а там надо сесть и разобраться. Чуть позже гляну

8 mimino  
0
По-порядку.
1. Насчет ошибки про Invalid opcode 0xffff at address 0x000004.
Вы разрешили прерывание по переполнению таймера 0:
ldi r16, (1<<TOIE2);Разрешение флага прерываний Т2
out TIMSK,r16 ;Копирование значения из регистра r16 в регистр TIMSK0
Но в начале программы не сделали ссылки на обработчик прерывания.

2. Насчет правильного вычитания. Мне непонятно, зачем Вы используете 10-битную точность АЦП, когда для поставленной задачи вполне достаточно 8-битной. Для 10-битной точности нужно постоянно оперировать с двумя регистрами - старший байт и младший байт. При этом процедура вычитания, конечно, будет несколько сложнее. Попробуйте сначала сделать как в моем примере, используя 8-битную точность АЦП, а потом, когда заработает, усложнить

5 AVRик  
0
Сергей !
По заданию вопросик. Что-то компилятор ошибку (AVR Simulator: Invalid opcode 0xffff at address 0x000004) выдает ссылаяс на адрес 4, при изменении на входе ADC4 на выходе OCR2 постоянно единица висит. Наверно где-то не правильно разделил ? Вот код программы, прошу помощи в делении. wacko

;####################################
;# Пример АЦП с матоперацией #
;# на ATmega-8 #
;# #
;####################################

.include "m8def.inc"; Присоединение файла описантй
.org 0 ; Задание нулевого адреса старта программы
rjmp reset ; Безусловный переход к метке reset
.org 14 ; Задание адреса прерываний по окончанию преобразованию АЦП
rjmp FADC ; Безусловный переход к метке FADC

reset: ; Начало раздела инициализации контроллера
ldi r16,high(ramend) ; Загрузка в r16 верхней границы ОЗУ
out SPH,r16 ; Копипрование значений в r16 верхней границы ОЗУ
ldi r16,low(ramend)
out SPL,r16
sbi DDRB,3 ;Установка 3-го бита в регистре DDRB в единицу (РВ3-выход)
ldi r16, (1<<TOIE2);Разрешение флага прерываний Т2
out TIMSK,r16 ;Копирование значения из регистра r16 в регистр TIMSK0
ldi r16,(1<<WGM20)|(1<<WGM21)|(1<<COM20)|(1<<COM21)|(1<<CS20)|(1<<CS20);Fast PWM, OC2 и дел.на8
out TCCR2,r16 ;с включением OC02 при совпадении с регистром OCR2
ldi r16,(0<<ADLAR)|(0<<MUX1)|(1<<MUX2) ; Подключегние ADC4 для считывания
out ADMUX,r16 ; Копирование из в r16 ADMUX
ldi r16,(1<<ADEN)|(1<<ADSC)|(1<<ADFR)|(1<<ADIE)|(1<<ADPS2)
out ADCSR,r16 ; Копирование из r16 в ADCRA
sei ; Глобальное разрешение прерываний

main: ; Основной цикл программы
nop
rjmp main ; Конец основного цикла программы
FADC: ; Начало обработчика прервыаний АЦП
in r16,ADCL ; Копирование результата преобразования в ADCL
in r17,ADCH ; Копирование в r16 результат преобразований
mov r18,r16
adc r16,r18
adc r16,r18
clr r19
minus:
cp r16,r18
brlo exit
sub r16,r18
inc r19
rjmp minus
exit:
out OCR2,r16 ; Копирование из r16 в регитр OCR1AL в ADCH
reti ; Возрат из прерывания

3 AVRик  
0
Сергей, по поводу задания никаких идей на ум не приходит ...

4 mimino  
0
Владимир!

Тут же все просто. Как считывать данные с АЦП, Вы знаете. А теперь просто возьмите полученное число, умножьте его само на себя два раза, чтоб возвести в куб. А затем разделите на (256*256), чтоб привести к диапазону от 0 до 256. И полученное значение занесите в регистр управления ШИМ.

1 Roma  
0
Предлагаю ещё написать статью по AVR и сдвиговым регистрам типа 595, на asm. Интересует как ростет код с увеличением числа сдвиговых регистров. Заранее спасибо!

2 mimino  
0
К сожалению, я не имел дела с такими регистрами, поэтому с ходу сказать ничего сказать не могу. Поскольку таких регистров у меня нет в наличии, могу попробовать написать программу чисто умозрительно, что, как вы понимаете, никак не гарантирует ее правильности, тем более при написании на ассемблере.

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

Статистика

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

Вход на сайт

Поиск

Посетители

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

 

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