123
[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
Страница 1 из 11
Модератор форума: nightmare, Huntswarrior, Aleks_Crow 
Форум » Программирование микроконтроллеров AVR, PIC » Учимся программированию микроконтроллеров » Подпрограммы AVR-Assembler (*icon-0*)
Подпрограммы AVR-Assembler
Отправлено 09.01.2012 - 02:431
Модераторы
174 сообщений
Мужчина
Сразу хочу отметить, все мои материалы в этой теме предназначены исключительно для публикации на ресурсах сайта www.servodroid.ru. Любое цитирование, копирование и т.п. на сторонних ресурсах рассматривается как нарушение интеллектуальной собственности. Ссылки же на данную тему допускаются.


Arduino - это зло!
Профиль Личное сообщение Дом. страница icq Skype
7
Отправлено 09.01.2012 - 14:372
Модераторы
174 сообщений
Мужчина
Фрагмент кода, позволяющий реализовать программпую генерацию ШИМ-сигналов для управления сервоприводами (писался для AtTiny2313, но подвергается переделке под другие микроконтроллеры):

Описание: Это уже вторая версия данного кода, которая реализует программную генерацию ШИМ-сигналов с частотой 50 Гц. Длительностью импульсов составляет от 500 мкс до 2500 мкс с шагом 10 мкс, что соответствует 200 возможным положениям качалки сервопривода. Задание необходимого положения происходит через регистры r0, r1 и так далее - по числу сервоприводов, каждый регистр отвечает за положение качалки одного из сервоприводов.

Как это работает: В генерации задействован таймер 0, работающий в CTC-режиме, это значит, что обработчик таймера будет вызываться через равные промежутки времени, указанные при инициализации и равные 10 мкс. С каждым вызовом обработчика происходит увеличение переменной-счетчика, принимающей значения от 0 до 2000 по кругу (естественно, реализовать эту задачу в одном регистре не представляется возможным, поэтому отвечают за счет регистры r17 и r18). Передние фронта импульсов формируется при переполнении переменной-счетчика, длинна импульсов задается в регистрах r0, r1, ... и равна значению регистра умноженному на 10 мкс. Исходя из этого получаем, что минимальное значение в любом регистре - 50 (500 мкс), максимальное - 250 (2500 мкс). Очевидно, что удобнее использовать диапазон от 0 до 200, для чего введено понятие смещения, в командах для микроконтроллера передаются числа от 0 до 200, микроконтроллер же сам к ним добавляет смещение 50.

Особенности:
Исправил:
1) Данный вариант генерации ШИМ-сигналов, несмотря на некоторую неоптимальность, пожалуй, будет более понятен тем, кто знаком с микроконтроллерами недавно именно благодаря своему прямому решению задачи "в лоб". Возможно, через какое то время я попробую оптимизировать данный код.
Задействованны регистры r0, r1 и т.д. для хранения положения качалки каждого сервопривода, r16 для хранения величины смещения, r17 и r18 для переменной-счетчика, r19, r20 и т.д. для вывода сигналов в выбранные порты, по одному на порт.
2) Важно, микроконтроллер должен запускаться с кварцевым резонатором 8 МГц и НЕзапрограммированном флаге CKDIV8 (деление тактовой частоты на 8). В противном случае, код инициализации таймера придется изменить.
Добавил:
3) Эмпирически выяснилось, что для каждого отдельно взятого сервопривода соответствия между длинной импульсов и границами положения качалки различаются, для одного это может быть длительность 500-2500 мкс, для другого 400-2400 мкс, для третьего 600-2400 мкс. Поэтому рекомендую не передавать микроконтроллеру команду выведения сервоприводов в крайние положения, а использовать диапазон чисел от 15 до 185. После обработки такой команды длительность импульса составит 650-2350 мкс.

Листинг: Листинг программы состоит из нескольких частей.
Code

; инициализация таймера 0
ldi r16, 10     ; загружаем константу для сравнения
out OCR0A, r16
ldi r16, 0b00000010    ; выбираем Normal Output A Mode, Normal Output B Mode, CTC Mode
out TCCR0A, r16
ldi r16, 0b00000010    ; выбираем CTC Mode, деление тактовой частоты равным восьми
out TCCR0B, r16
in r16, TIMSK     ; разрешаем Compare Match A прерывание таймера 0, для этого меняем 3-0 биты регистра MCUCR не затрагивая остальные биты
cbr r16, 0b00000111    ; обнуляем 2-0 биты
sbr r16, 0b00000001    ; устанавливаем 0 бит
out TIMSK, r16

; инициализация портов
; в этом месте необходимо сконфигурировать выбранные ножки микроконтроллера на вывод


Загружаем начальные значения регистров. Если этого не сделать, при старте микроконтроллера все сервоприводы могут попытаться уйти в недопустимые конструкцией положения, а это чревато выводом сервоприводов из строя.
Code

; загрузка начальных констант в регистры
ldi r17, 0     ; обнуление регистров переменной-счетчика на случай, если они были использованны ранее
ldi r18, 0
ldi r16, 50     ; загружаем смещение
mov r0, r16     ; начальная длительность импульса равна смещению
; mov r1, r16     ; и так далее по числу сервоприводов


И, собственно, обработчик прерывания таймера 0:
Code

tim0_compA:
        inc r17     ; это событие происходит каждые 10 мкс

        ; для первого сервопривода
        cp r17, r0    ; если значения регистров равны - формируем задний фронт импульса        
        brne tim0_compA_control_mark1        
        cbr r19, 0b00000001   ; очищаем бит 0 (сервопривод подключен к ножке PB0)
        tim0_compA_control_mark1:

        ; и так далее по числу сервоприводов
        ; cp r17, r1    ; если значения регистров равны - формируем задний фронт импульса        
        ; brne tim0_compA_control_mark2        
        ; cbr r20, 0b01000000  ; очищаем бит 6  (сервопривод подключен к ножке PD6)
        ; tim0_compA_control_mark2:

        cpi r17, 250    ; возможно, регистр r17 близок к переполнению
        brne tim0_compA_control_mark
        inc r18     ; тогда задействуем вспомогательный регистр, это событие происходит каждые 2.5 мс
        ldi r17, 0    ; а регистр r17 обнуляем

        cpi r18, 8    ; появление этого числа в регистре означает, что переменная-счетчик достигла значения 250*8=2000
        brne tim0_compA_control_mark        
        ldi r18, 0    ; тогда ее надо обнулить (r17 уже обнулен к этому моменту), это событие происходить каждые 20 мс
        sbr r19, 0b00000001   ; в этом случае устанавливаем бит в единицу (формируется передний фронт импульсов)
        tim0_compA_control_mark:

        out PORTB, r19   ; выводим в порт
        ; out PORTD, r20   ; и так далее по числу использованных портов
reti


Вопросы в ЛС или онлайн чат http://www.servodroid.ru/index/online_chat_po_robototekhnike/0-77


Arduino - это зло!
Профиль Личное сообщение Дом. страница icq Skype
7
Форум » Программирование микроконтроллеров AVR, PIC » Учимся программированию микроконтроллеров » Подпрограммы AVR-Assembler (*icon-0*)
Страница 1 из 11
Поиск: