При написании прошивки надо очень внимательно подходить к процессу организации архитектуры будущей программы. Программа должна быть быстрой, не допускать задержек главного цикла и легко расширяться. Оптимально использовать аппаратные ресурсы и стараться выжать максимум возможного из имеющихся ресурсов.
Вообще, архитектура программ это отдельная тема и ближе к концу курса, в его Сишной части я подробней рассказываю о разных типах организации прошивки. Можешь забежать вперед и поглядеть, что да как.
В ассемблерной же части, я расскажу о одном из самых простых вариантов — флаговом автомате, а позже, когда ты уже будешь вовсю ориентироваться в моем коде, дам пример на основе конвейерного диспетчера, с подробным описанием его работы.
Суперцикл
Все программы на микроконтроллерах обычно зацикленные. Т.е. у нас есть какой то главный цикл, который вращается непрерывно.
Структура же программы при этом следующая:
- Макросы и макроопредения
- Сегмент ОЗУ
- Точка входа — ORG 0000
- Таблица векторов — и вектора, ведущие в секцию обработчиков прерываний
- Обработчики прерываний — тела обработчиков, возврат отсюда только по RETI
- Инициализация памяти — а вот уже отсюда начинается активная часть программы
- Инициализация стека
- Инициализация внутренней периферии — программирование и запуск в работу всяких таймеров, интерфейсов, выставление портов ввода-вывода в нужные уровни. Разрешение прерываний.
- Инициализация внешней периферии — инициализация дисплеев, внешней памяти, разных аппаратных примочек, что подключены к микроконтроллеру извне.
- Запуск фоновых процессов — процессы работающие непрерывно, вне зависимости от условий. Такие как сканирование клавиатуры, обновление экрана и так далее.
- Главный цикл — тут уже идет вся управляющая логика программы.
- Сегмент ЕЕПРОМ
Начинается все с макросов, их пока не много, если что по ходу добавим.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | .include "m16def.inc" ; Используем ATMega16 ;= Start macro.inc ======================================== .macro OUTI LDI R16,@1 .if @0 < 0x40 OUT @0,R16 .else STS @0,R16 .endif .endm .macro UOUT .if @0 < 0x40 OUT @0,@1 .else STS @0,@1 .endif .endm ;= End macro.inc ======================================= |
В оперативке пока ничего не размечаем. Нечего.
1 2 3 | ; RAM =================================================== .DSEG ; END RAM =============================================== |
С точкой входа и таблицей векторов все понятно, следуя нашему давнему шаблону, берем его оттуда:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ; 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 RETI ; (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 ; Конец таблицы прерываний |
Обработчики пока тоже пусты, но потом добавим
1 2 | ; Interrupts ============================================== ; End Interrupts ========================================== |
Инициализация ядра. Память, стек, регистры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | Reset: LDI R16,Low(RAMEND) ; Инициализация стека OUT SPL,R16 ; Обязательно!!! LDI R16,High(RAMEND) OUT SPH,R16 ; Start coreinit.inc RAM_Flush: LDI ZL,Low(SRAM_START) ; Адрес начала ОЗУ в индекс LDI ZH,High(SRAM_START) CLR R16 ; Очищаем R16 Flush: ST Z+,R16 ; Сохраняем 0 в ячейку памяти CPI ZH,High(RAMEND) ; Достигли конца оперативки? BRNE Flush ; Нет? Крутимся дальше! CPI ZL,Low(RAMEND) ; А младший байт достиг конца? BRNE Flush CLR ZL ; Очищаем индекс CLR ZH CLR R0 CLR R1 CLR R2 CLR R3 CLR R4 CLR R5 CLR R6 CLR R7 CLR R8 CLR R9 CLR R10 CLR R11 CLR R12 CLR R13 CLR R14 CLR R15 CLR R16 CLR R17 CLR R18 CLR R19 CLR R20 CLR R21 CLR R22 CLR R23 CLR R24 CLR R25 CLR R26 CLR R27 CLR R28 CLR R29 ; End coreinit.inc |
Всю эту портянку можно и нужно спрятать в inc файл и больше не трогать.
Секции внешней и внутренней инициализации переферии пока пусты, но ненадолго. Равно как и запуск фоновых программ. Потом я просто буду говорить, что мол добавьте эту ботву в секцию Internal Hardware Init и все :)
1 2 3 4 5 6 7 8 9 10 11 | ; Internal Hardware Init ====================================== ; End Internal Hardware Init =================================== ; External Hardware Init ====================================== ; End Internal Hardware Init =================================== ; Run ========================================================== ; End Run ====================================================== |
А теперь, собственно, сам главный цикл.
1 2 3 4 5 | ; Main ========================================================= Main: JMP Main ; End Main ===================================================== |
Все процедуры располагаются в отдельной секции, не смешиваясь с главным циклом. Так удобней, потом можно их по частям вынести в библиотечные файлы и разделить исходник на несколько файлов. Но мы пока это делать не будем. Разделим их просто логически.
1 2 3 | ; Procedure ==================================================== ; End Procedure ================================================ |
«Суть в том, чтобы в автоматическом режиме, каждые несколько миллисекунд вызывать прерывание, в котором происходит подсчет импульсов от одометров»
Очень сложный, и неудачный подход.
Для подсчета импульсов с энкодеров в разы проще и эффективнее использовать прерывание по изменению состояния входа.
Пришел импульс — сгенерировалось прерывание, посчитали его быстренько и работаем спокойно дальше. Итого: 1 импульс на энкодере — одно прерывние, а не постоянная беготня с проверкой порта и сравнением с предыдущим состоянием :)
Да можно и так. Я подумал об этом в последний момент, но у меня на инты повешан драйвер движка, когда я его туда вешал я даже не думал про энкодеры на редуктор, Точнее у меня на них был собрана макетка, и я решил оставить обратную совместимость и ничего не менять. Так что чистейшая ошибка планирования, когда итоговая конструкция допридумывается походу дела такое сплошь и рядом :))))) Но зато появился кусок извратского кода для изучения. Тоже полезно, заодно показал как логика работает. :)
«на инты повешан драйвер движка»
Пора на Атмегу88 переходить :) У нее все порты (B, C, D) умеют внешние прерывания обслуживать, 24 входа для прерываний — с ума можно сойти.
Не продают у нас такое. А заказывать больно накладно выходит.
У нас тоже не продают. Приходится заказывать из Москвы, хорошо еще что удается «прицепляться» к заказам для фирмы и не платить за доставку.
Не разобрался: прерывание по изменению состояния входа на ATMega8 можно только на 4 и 5 (INT0 и INT1) ноге делать?
Да. На мега88 есть PCINT которая на дофига ногах есть.
Я что-то недопонял, или у вас тут
ANDI R16,11<<LGEAR ; Побитовое И регистра R16 и числа “00010000″
и тут
ANDI R17,11<<ODOL ; Побитовое И регистра R17 и числа “00010000″
опечатка?
11 вместо 1.
Да опечатка. Щас поправлю.
Очепятка:
«Каждый его биты заведуют режимами работы.»
Народ, проясните пожалуйста следующий момент про одометры:
У DI HALT’а на схеме фотодиоды непосредственно к ногам МК подходят. На фотодиоды меня жаба задушила, взял фототранзисторы подешевле =) А они, злыдни, нужного эффекта не дают. Светишь, не светишь, все равно сигнал на МК не приходит. Кроме того, на разных ресурсах все норовят ОУ использовать для подобных датчиков. (Уточнение, буду использовать именно как датчик отражения.)
Собственно вопрос: Можно ли фотодатчик подключать напрямую к ногам МК? Если «да» то какие характеристики у него должны быть?
P.S. Тихо радуюсь за себя =) За месяц с нуля при отсутствии радиолюбительского опыта удалось из радиоуправляемой машинки сделать робота с контактными датчиками, управляемого с установленного на нем КПК и удаленно по WiFi с компа. В целом все живет, сейчас занимаюсь отладкой. Платформа (р/у машинка)оказалась неудачной: моторчик фиговый и без датчика оборотов предсказать, сколько он проезжает за одно и то же время — нереально. А без этого нормального управления не сделать.
Кстати, попутный вопрос: Какие бы максимально просто определять положение передних (рулевых) колес? Сложность: конкретного положения им не задать — поворачиваться они могут только постепенно в процессе движения вперед или назад. Т.о. хочется знать, а в каком они положении в каждый момент времени. Пока пришла только одна мысль: переменный рез. и к АЦП но это как-то… не просто. К тому же движения рулевой рейки там минимальные (меньше 1 см в каждую сторону). В общем буду рад идеям.
Ты бы замерил его характеристики (хотя бы сопротивление) на свету и не на свету. Возможно ты пытаешься светить ему не тем спектром. Мои, например, совершенно не реагируют на зеленый и синий светодиоды, но вот зато на красный сразу же.
Спасибо за ответ.
Не поверишь — замерял =)
Транзистор ИК, свечу я на него тоже ИК-диодом. Сопротивление меняется от 15-11 кОм до 7-4 кОм. Пробовал мерять напряжение — не меняется 8-[ ] Стабильно показывает 5 с копейками, которые через него бегут.
Доп. инф.: Стоит он между ногой МК и землей. Если ему ноги закоротить — МК сигнал принимает. Видимо, все-таки сопротивление у него дюже большое. М.б. постоянный резистор параллельно воткнуть? Вот только не соображу, как посчитать. Или как-то по-другому надо?
Изменения очень невелики. ПОэтому у тебя и не хватает. Варианты тут такие:
1. Подобрать делитель так, чтобы малейшее изменение вызывало переход через логический уровень.
2. Смасштабировать через ОУ.
3. Можно попробовать измерительный мост приспособить, но не уверен.
Спасибо огромное.
Извини, еще попристаю.
Можно для идиотов чуть поподробнее:
1) Какие изменения будут достаточными для Atmega8?
2) Если взять не транзистор, а диод, станет ли лучше?
3) Мне казалось, что транзистор и диод — бинарные элементы. Т.е. ток либо идет и идет весь либо не идет. С чего оно ведет себя, как фоторезистор?
4) Делитель, это параллельный резистор? А можно формулу расчета или конкретный номинал для меня?
5) Что есть «измерительный мост»?
6) М.б. порекомендуешь конкретные ИК-диоды/транзисторы, которые можно ставить между МК и землей без танцев? Если прямо из каталога Ч&Д — вообще будет супер.
7) Твой-то робот как? С момента последней статьи год прошел.
P.S. Понимаю, что Гугл рулит, но во-первых от живого человека ответы приятнее. Во-вторых, большей частью в результатах валится промышленная реклама. В третьих, стиль у тебя понятный.
В даташите есть раздел:
Pin Thresholds and Hysteresis: там график зависимости лог уровня от напряжения питания. Так вот на 5 вольтах все что ниже 1.4 вольт — это ноль. А все что выше 1.8 вольт единица. Между ними граничное значение где может считываться и так и так.
Так вот, твой транзистор и резистор должны образовать делитель напряжения. Читай основы на пальцах в 1…4 части про делитель было расписано. В общем, сопроитивление должно быть подобрано так, чтобы изменение верхнего плеча делителя (твой транз) таскало напругу через эту границу в 1.8-1.4 вольта.
5) таки в гугл
6) модели не помню, я их обычно выпаиваю из старых дисководов.
7) стоит :)
И еще раз спасибо.
Делитель помог — заработало. Завтречка вместо трех последовательных прикуплю один и запихаю наконец в робота.
Похвастаюсь немного =)
Одометр довел до рабочего состояния и вставил в платформу. Работает на отражении от метки на колесе.
Сегодня добил софтовые задачи:
1) Поддержки заданной скорости (задаю желаемую скорость, а прошивка рулит ШИМОМ)
2) Проезда заданного расстояния.
Все стало ездить гораздо предсказуемее =)
Надо еще китайский магнит на руле на серву поменять и будет мне счастье.
Все это хорошо, хоть и весьма усложнено. Но это теория. На практике будет «дребезг контактов» , датчик может оказаться в пограничном положении. Надо писать функцию проверки истинности импульса. Вообще-то не пойму, зачем вам для простого счета импульсов нужны именно прерывания?? В каждом цикле можно опрашивать порты, как обычные входы. Есть сигнал > процедура проверки > подтвердился факт импульса > установился флаг события, уложился в РАМ (либо сразу вызвался счетчик), далее счетчик проверил флаг импульса.Если флаг установлен > добавил себе единичку или вычел, смотря в какую сторону ехал.. В следующем цикле этот флаг сбросился там же, где и устанавливался или в самом счетчике после счета.. Счетчики тоже проще организовать РАМ. Опрос порта займет несколько микросекунд, остальное время можно МК нагружать чем хочешь. А по вашему методу у вас очень скоро просто закончатся и регистры и счетчики.))
А если мы делаем что то медленное и опрос пропустит пару импульсов? Это же критично!
Дребезга тут нет, т.к. датчик работает быстро и весь остальной цикл это дело сглаживает
Что можно делать настолько медленное? Может и неправильно с «академической» точки зрения (я не программист), но у меня большинство внешних событий вызывает установку соответствующего флага в РАМ, а в основной программе в каждом цикле проверяется его состояние. Так очень удобно — если у меня по , скажем, импульсу_Х происходит и счет в счетчике, и проверка выхода счетчика за пределы переполнения, и зашиты всевозможные. Так же в дальнейшем к этому флажку можно легко подвязаться любой подпрограммой. Получается очень удобно добавлять новые возможности. Есть флаги, которые устанавливаются и сбрасываются основной логикой программы, а уж опираясь на них можно ыешать кучу , так сказать «сервисных» и контрольных функций. Если все эти программы обработок и контроля вызывать сразу после прихода импулься, то получится достаточно громоздкая и неуклюжая конструкция. Конечно, если нужна очень высокая скорость, то импульсы надо считать по прерываниям. Но вот где взять в Меге8 8 внешних прерываний, да так чтобы и осталось еще что полезное из периферии свободным.
В моем случае, импульс успевает 50 раз проверится на истинность, посчитаться, еще 50 раз провериться на заднем фронте (спаде). А
В данном случае это трудно представить. Но если поставить оптический энкодер с частотой выдачи импульсов в килогерцы? Тогда проблема вылазит в полный рост.
Можно просто чаще опрашивать входа, скажем за цикл не один раз, а десять.
Опрос-то всего-ничего:
sbis pinX, x
rjmp a1
—
a1: sbis pinX, x
rjmp a2
—
a2: …..
Это практически не занимает ни время МК, ни его ресурсы. Понятно, что это до определенного потолка быстродействия, ну так и по прерывания такая же картина. Вроде бы примитивно, но зато удобно, и регистры все свободны. У меня на программу в 18кБ из регистров занято штук шесть. Да и вся периферия практически свободна, можно еще функций навешивать и навешивать.
Но есть риск прозевать событие. Неважно по какой причине. Не попало в опрос и все тут. С прерываниями такого не выйдет.
А чего жалеть регистры? Вот когда будет поджимать тогда и разберемся :)
Di, кстати, а как проверить истинность импульса (в смысле, что не короткая помеха проскочила) если считать их по прерываниям? Как я понимаю, после перехода в обработчик прерывания, флаг прерывания сбрасывается, и что дальше? Как проверить, что это именно импульс? Обычно перепроверяют либо через некоторый промежуток времени, либо N-ное кол-во раз. А с прерыванием что-то не могу придумать. Сдается мне, что никак. Ну для машинки такой метод годится, а как быть, если до датчика 20 м кабеля? Там же помехи, наводки могут такого «насчитать», что за голову схватишься! Прошу прощения за въедливость, просто
всегда интересно мнение более опытного товарища, а то у меня ведь — так, самодеятельность.
Если есть риск того, что это помеха то только повторной проверкой.
Из прерывания можно по таймеру сделать через некоторое время проверку. Т.е. суть прерывания поиметь событие в любом случае. А уж как оно будет обрабатываться потом это дело другое.
DI HALT, я заметил, что в твоем коде ты (вроде) не делаешь задержек после того, как поймал нажатие кнопки. Она у тебя не дребезжит? Или ты как-то борешся с этим аппаратно? Триггер? Вообщем, мне интересно)
Дребезг мешает только тогда, когда надо подсчитывать число нажатий или что то подобное. Если важен только лишь факт нажатия кнопки (единоразовый), то его можно не учитывать.
А сколько времени (в среднем) примерно длится дребезг при «нормальном» нажатии у «нормальной» кнопки?
Нормальной это какой? У китайской SWT я дребезг не смог увидеть даже не осциллографе 40мгц. А у советской ТМ-2 успокивать приходилось чуть ли не в 3мс.
Нормальной — это значит средьненькой такой по цене кнопочки в магазине. Твоей советской ТМ-2 наверняка уже больше 20ти лет и использовалась она не раз. Но то что у новых зарубежных кнопок дребезга практически нет, радует. Не так все плохо оказывается, как пугают в книжках.
ТМ-2 тоже новая. Просто у ней конструкция более расхлябаная.
Ну дак значит российская, а не советская. Советский союз был давно )
Ну валяться на складе с 89 года можно было без особых проблем. По крайней мере она была не паяная, не окисленная, а на корпусе клеймо 89 год. =))))
DIHALT привет. Почему например файл с макросами располагается вначале? Например можно ли его расположить после прерываний? На что это повлияет? И вообще если поменять структуру расположения вложенный файлов, это допускается? Или только так как указано выше?
Компилятор парсит исходник сверху вниз и если он, не найдя макросов, нарвется на их использование будет эррор. Плюс я не знаю как он отреагирует на макросы в исходном коде. Может вкомпилит их все. Поэтому я распологаю их за пределами .CSEG
Секции внешней и внутренней инициализации переферии пока пусты, но ненадолго.
Далее идет разметка кода:
; Internal Hardware Init ======================================
; End Internal Hardware Init ===================================
; External Hardware Init ======================================
; End Internal Hardware Init ===================================
; Run ==========================================================
; End Run ======================================================
в 4-ой строке должно быть не End Internal Hardware Init, а End External Hardware Init.
в файле проекта также
у Белова перед циклом программы идет инициализация портов. у Ревича тоже. инициализацию пихать в секцию
; Internal Hardware Init ======================================
; End Internal Hardware Init ===================================
?
и еще совсем уж глупый вопрос — что произойдет если порты вообще не инициализировать?
Что такое инициализация? Это настройка периферии в нужный режим работы. Только и всего. Ну и избавление от умолчаний. Т.е. если после сброса порт находится уже в нужном нам режиме, то это совсем не значит, что его не нужно заново в этот режим выставить. Кто знает как отработает схема сброса и будет ли при следующем сбросе или сбое порт в нужном режиме? Поэтому то все каждый раз заново и выставляют при старте. Чтобы точно знать, что у нас все настроено правильно
DI HALT, подскажите, пожалуйста, где можно почитать про инициализацию USB в AVR (1286, 1287, …) на ассемблере. На Вашем сайте (за сайт — огромное спасибо) я этого что-то не нашел. Так, чтобы было достаточно подробно описано:
1. Включение внутреннего стабилизатора напряжения
2. Настройка интерфейса блока PLL
3. Разрешение работы блока PLL и ожидание входа его в состояние захвата (lock)
4. Разрешение работы USB-интерфейса
5. Настройка USB-интерфейса (скорость, настройка конечных точек и др.)
6. Ожидание информационного подключения USB VBUS
7. Присоединение к шине
На сайте Atmel примеры есть на Си (не всегда работающие, несмотря что фирменные), а вот на Asm — нет.
Не знаю, никогда не интересовался USB плотно.
Добрый день!
У меня снова повился вопрос: Делаю схему на ATtiny13A (ATtiny13V) питание +2 В. В даташите для питания 1,8-2,7В рекомендованно использовать тактовую частоту <=4МГц. Внутренний генератор можно настроить только на 4,8 МГц. Как сделать 4 МГц? С помощью OSCCAL? Ток я не понял, что это за зверь и как он работает. Объясните пожалуйста… если можно с примером..
Заранее спасибо. :)
Можешь попробовать через oscal загнать в минус. Просто вписав туда минимальное значение.
либо через CLKPRE поделить частоту. Тебе надо точно 4мгц или 2х хватит?
Добрый день продолжается!
Спасибо за подсказку. Я пропустил описание CLKPR когда смотрел описание. В запаре был. Мне достаточно м 2 МГц. Но тут дилема. Два варианта реализации:
1. При программировании установить FUSE биты CKSEL 01 получив тем самым 4,8 МГц, а потом при запуске МК в системе программно задать CLKPR — 0001 поделить 4,8/2 = 2,4 МГц.
2. Не париться с Фьюзами (ещё ни разу с ними дела не имел) и сразу в программе задать CLKPR — 0010 поделив 9,6/4 = 2,4 МГц.
А дилема в чём? Прокатит ли вариант 2 при питании 2 вольта? Я, в общем, склоняюсь к варианту 1.
Оба варианта одинаковые. 2 проще и безопасней.
Имеются опечатки, возможно, это метки для защиты копирайта, например, на
http://shop.easyelectronics.ru/index.php?productID=147
адптера
мы можешь
Имхо,логическая опечатка:
Хотя в разделе
http://easyelectronics.ru/category/avr-uchebnyj-kurs Стартовая инициализация
«убиваем все регистры от первого до последнего» в цикле,
в разделе
http://easyelectronics.ru/avr-uchebnyj-kurs-vtoraya-programma.html#more-30
это не используется, идет тупой перебор регистров.
Вопрос по существу, можете сделать сравнительный анализ программаторов отладчиков
(расхвалить свой товар)
Отладочная плата PinBoard http://shop.easyelectronics.ru/index.php?productID=147
http://www.electronshik.ru/card/otladochniy-komplekt-stk500-dlya-avr-59500
http://www.new-technik.ru/product/stk500/
у STK500 очень хороший программатор с возможностью HVPROG. И возможность втыкать разные МК.
Больше там ничего особо нет. JTAG отладчик придется покупать отдельно, дисплей отдельно. На самой плате из органов управления только кнопочки и светодиоды. Был бы он за 1500 еще можно было купить, но не за 5000.
Тот что по второй ссылке — это просто программатор, но весьма неплохой.
RunningWolf 25 Ноя 2009 16:16
Народ, проясните пожалуйста следующий момент про одометры:
У DI HALT’а на схеме фотодиоды непосредственно к ногам МК подходят. На фотодиоды меня жаба задушила, взял фототранзисторы подешевле =)=
==================
извините за дурацкий вопрос.. но разве к этой статье схема приложена??
Когда то давно статья была существенно другой. Потом была почти полностью переписана под другую платформу
Чтобы обнулялась последняя ячейка RAM, в текст программы и в файл шаблона надо внести исправления, на которые указывал Andriy в предыдущей статье, а именно заменить:
CPI ZL,Low(RAMEND) на CPI ZL,Low(RAMEND+1)
У меня вопрос. Скорее всего он тупой)) просто я изучаю весь курс поп порядку. В макросах ты используешь конструкцию if-then-else. Но на скока я понял в ассемблере нет таких команд. ведь у тебя целая статья про типовые конструкции и про то как создать if then else через БРАНЧИ. я посмотрел файл m16def.inc но там ничего не написано про это. это случайно не ммакросы (.if и .else)? И вообще что озночает точка перед командой?
эти иф елзе относятся к компилятору, а не командам процессора. Благодаря им он на этап компиляции определяет какие куски кода компилить, а какие выбросить, что куда подставить и все подобное. На выходе же получается чистый ассемблерный листинг.
ОК. этот момент я понял. т.е в случае сооружения структуры из бранчей в код будет прописано и ИФ и ЭЛСЕ. а здесь в нашем случае пропишется либо ИФ, либо ЭЛСЕ, в зависимости от начальных условий. В данном случае от переменных, которые мы подставляем в макрос. ок. ТОГДА ОПЯТЬ ТУПОЙ ВОПРОС. нам же главное результат, так? почему я не могу соорудить такой макрос, который бы мне , к примеру сравнивал величины. и делал бы либо то, либо другое. Я понимаю, что получается не совсем программа, а, по большому счету код будет прописан «в лоб», только пишем не мы а компилятор. т.е не будет логики. прокатит только для отдельно взятого случая. ток вот ни пофигу ли нам??? в частных случаях прокатило бы и это. или чего то изначально не понимаю?)) заранее спасибо
Макросы оперируют только тем, что можно вычислить на этапе компиляции. Если у тебя масса логики решается уже тут — то ее надо тут и сделать. Т.к. появляется читаемость программы, а все что надо посчитать будет посчано компилером. Но это же все только константы. Ты можешь не пользуя макросами все сам посчитать и подставить.
А когда прога будет уже зашита, то например ты не сможешь сделать переход на основании пришедшей в порт циферки.
DI HALT, доброго времени суток.
Я тут пытаюсь соорудить одно устройство на ATtiny2313, использую этот шаблон, но авр студия ругается, пишет вот такие замечания:
error: Overlap in.cseg: addr=0x14 conflicts with 0x14:0x15 (это на строку: «OUT SPL,R16»)
error: Overlap in.cseg: addr=0x16 conflicts with 0x16:0x17 (это на строку: «OUT SPH,R16»)
и так далее. всего только на сам шаблон без самой программы 11 ошибок.
Подскажи, в чем причина? пока самому не получается разобраться. спасибо.
1. Убедись, что адрес который пихается в SPL указан как RAMEND и инклюдник от тини2313 взят, а не от чего то другого.
2. У тини2313 нет SPH регистра. Ее озу укладывается в 255 байт.
В коде «Инициализация ядра. Память, стек, регистры» есть ошибка. Когда чистим SRAM от начала (SRAM_START) до конца (RAMEND), то последняя ячейка (RAMEND) не очищается. Почему? Да потому, что выполнилось сравнение что Z=RAMEND и заканчиваем цикл без обнуления этой ячейки памяти RAMEND. Поэтому надо вставить после цикла еще одну строку для обнуления.
=====================================
; Start coreinit.inc
RAM_Flush: LDI ZL,Low(SRAM_START) ; Адрес начала ОЗУ в индекс
LDI ZH,High(SRAM_START)
CLR R16 ; Очищаем R16
Flush: ST Z+,R16 ; Сохраняем 0 в ячейку памяти
CPI ZH,High(RAMEND) ; Достигли конца оперативки?
BRNE Flush ; Нет? Крутимся дальше!
CPI ZL,Low(RAMEND) ; А младший байт достиг конца?
BRNE Flush
========еще одна строка для обнуления ячейки памяти RAMEND
ST Z,R16 ; Сохраняем 0 в последнюю ячейку памяти (RAMEND)
;========еще одна строка для обнуления ячейки памяти RAMEND
Предлагаю автору проверить и поправить в коде источника.
Что-то большинство коментов от какой-то другой статьи. Ты бы их посносил, всё равно бессмысленны без контекста. И название «Скелет программы», а ссылка /avr-uchebnyj-kurs-vtoraya-programma.html — не соответствует :)
Что-то у меня разрыв шаблона.
Комментарии и статья как-то связаны???
Может уже и нет. Статьи иногда переписываются практически полностью.
Решил писать програмку по данному шаблону, все куски кода из статьи вписывал в тойже последовательности, инклюдка для меги8, но на кусок с таблицей прерываний авр-студия выдает при компиляции матюки
…..\Kom2x2_14.asm(144): error: Overlap in .cseg: addr=0x14 conflicts with 0x14:0x15
и так далее до ….\Kom2x2_14.asm(171): error: Overlap in .cseg: addr=0x28 conflicts with 0x28:0x29
При убивании строчки .ORG INT_VECTORS_SIZE компилит без ошибок.
Собственно немогу понять чего он на нее ругается?
У тебя где то ORGи наложились друг на друга. Проверь это внимательно. Они должны быть строго по порядку возрастания, а у тебя где то перепутались. В результате возникла ошибка перекрытия Overlap.
Странно — кроме этой таблички там пока нет никаких ORGов, перепроверил инклюдки — там вообще нет ORGов
Собственно строка .ORG INT_VECTORS_SIZE мне не совсем понятна — «INT_VECTORS_SIZE» это должен быть адрес в памяти? и этим именем он должон гдето быть назван с помощью .def ? или я ошибаюсь?
Либо ORG возник после физического кода, т.е. ты навтыкал кода (тех же векторов) скажем на 10 байт, а ORG предписывает на 9 и возникло перекрытие.
Да это адрес в памяти, он для каждого контроллера свой и прописан где то в def файлах. Если не найдешь, то проверь ,что используешь assembler2 и файлы из него, т.к. в ассемблер1 таких дефайнов много меньше (да и сам ассемблер1 намного слабей чем версия 2)
Таблицу векторов правильно нарисовал? А то если ты ее копипастнул откуда-то, то там может быть больше векторов чем есть на самом деле, т.к. она может быть с другого контроллера. Вот тебе и оверлап вылез.
Спасибо за терпение Di, разобрался — копипастнул табличку из статьи — а она под мегу16 — у меня-же мега8 — вот и матюкалось — исправил — работает, учим дальше RTOS. ))
Не очень понимаю зачем так самозабвенно обнуляются регистры? Есть там мусор, ну и хер с ним. А берётся регистр в оборот так всё равно туда либо что то предварительно грузится. Или это чисто академический пример?
Di доброго времени. Могли бы помочь перенсти RTOS на mega328? Вектора поправил, uart поправил, а вот с таймерами разобраться не могу.