AVR. Учебный Курс. Отладка программ. Часть 3 |
|
| Рубрика: Электроника для всех | Раздел: AVR. Учебный курс |
| дата:30-05-2010 | |
|
Метод 3. USART (Работа с последовательными интерфейсами) Пользоваться им проще простого — в любой момент, когда нам это захочется, мы берем и отправляем нужный байт по этому интерфейсу. При этом достаточно заранее его настроить — стандартная инициализация UART: 1 2 3 4 5 6 7 8 9 10 11 12 ; Usart INIT .equ XTAL = 8000000 .equ baudrate = 9600 .equ bauddivider = XTAL/(16*baudrate)-1 uart_init: LDI R16, low(bauddivider) OUT UBRRL,R16 LDI R16, high(bauddivider) OUT UBRRH,R16 LDI R16,0 OUT UCSRA, R16 1 2 3 ; Прерывания запрещены, прием-передача разрешен. LDI R16, (1<Маркеры Я даже проверку на занятость UDR не делаю. Для одиночного байта, если у нас отправки по UART нет и участок не зациклен, этого вполне достаточно — регистр будет свободен. Разумеется в реальной программе такого делать не следует, но для отладочной затычки вполне сойдет. Можно нафаршировать весь код такими вот затычками, но с разными буквами и смотреть в терминалке в каком порядке программа выполняется по выдаваемому слову. Правда учитывайте тот момент, что если данные будут сыпаться в терминалку быстрей чем она ее может прожевать, то регистр UDR захлебнется и у нас будет полная лажа. В этом случае можно сделать затычку чуть сложней, с ожиданием освобождения UDR 1 2 3 4 LDI R16,'A' SBIS UCSRA,UDRE ; Пропуск если нет флага готовности RJMP PC-1 ; ждем готовности - флага UDRE OUT UDR, R16 ; шлем байтМожно в макрос это запихать: 1 2 3 4 5 6 7 8 .MACRO DEB_SEND PUSH R16 LDI R16,@0 SBIS UCSRA,UDRE RJMP PC-1 OUT UDR,R16 POP R16 .ENDMЗаодно R16 в стеке спрячем, так что макрос можно юзать где угодно, словно команду: 1 DEB_SEND 'A'Но это затормозит выполнение программы, могут полететь тайминги. Так что если у вас есть какие то участки жестко зависимые от времени (скажем тайминги протокола 1-wire), то данная затычка даст сбой. Выводить можно любую инфу. Например содержание нужных регистров, содержание переменных, ячеек памяти, состояния битов регистров периферии. Что хотим поглядеть — то и тащим. Надо только загрузить это в UDR. А поскольку там могут быть произвольные значения, то глядеть их лучше через терминалку способную показывать в хексах. Например, моя любимая Terminal v1.9b. Также полезно выбрасывать в терминал маркеры прохождения кода. Чтобы оценить правильность переходов и логики работы. Еще настоятельно рекомендую заиметь спец литеру отправляемую в терминал сразу же после инициализации USART, еще до перехода к основной программе. У меня это обычно буква “R”. От “Reset”. Это позволяет поймать момент перезагрузки контроллера. Например, глючила у меня программа. Все работало нормально, но при попытке принять байт все начинало странно глючить. Все облазил, код весь до дыр проглядел. По коду все должно работать… Наконец воткнул в код OUTI UDR,’R’ еще до вхождения в main. Опаньки — а контроллер то сбрасывается! А почему контроллер может сбрасываться? Сбой по питанию я исключил сразу — не было там ничего такого, что бы могло дергаться. RESET был подтянут через 10кОм и особо тоже не вихлялся. Что еще? Срыв стека, если произошел переход за конец кода, тоже похож на сброс — программа уходит за конец памяти и возвращается к нулевому адресу. Прошарил в отладчике и этот момент — нет срыва. Но может срыв не сразу, а спустя несколько итераций? Поставил .ORG на предпоследний адрес флеша и прописал туда маркер — не вылазит, значит срыва стека нет. Заглянул под панельку… Вот в чем засада — медная ворсинка от провода МГТФ попала между RXD и RST (На меге8 они совсем рядом) и при приходе байта в порт дрыганья на RXD сбрасывали контроллер. Соблюдайте чистоту на рабочем месте! Не допетри я тогда поставить на перезагрузку маркер, так я бы до утра ковырялся с кодом. А так я резко сузил круг возможных косяков и ушло не более десяти минут на отлов этого бага. Управление кодом, добыча произвольных данных Мы же можем не только слать, но и принимать! А принятое обрабатывать! Так что нам мешает делать полноценные брейкпоинты? Скажем, такие: 1 2 3 4 5 PUSH R16 SBIS UCSRA,RXC RJMP PC-1 IN R16,UDR ; Читаем UDR, чтобы сбросить RXC флаг POP R16Данный код является банальным бряком, т.е. программа на ней встанет как вкопанная до тех пор, пока мы не зашлем ей байт в терминалку. Удобно, особенно когда осциллографа нет под рукой, можно, например, спокойно мультиметром уровни замерить на выводах, а потом пустить прогу до следующего чекпоинта. В сочетании с маркерами дает еще и информацию о том, где мы встали. Но можно же и большее заиметь! Что нам мешает сделать небольшой обработчик команд? Скажем, если мы послали R - выдаст значение нужного регистра, если М - ячейки памяти, I - байта из IO, а G - пойдет дальше: Пример макроса может быть таким: 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 .MACRO DEB_CMD PUSH R16 ; Сохраняем регистр и флаги в стек IN R16,SREG PUSH R16 SBIS UCSRA,RXC RJMP PC-1 IN R16,UDR ; Читаем UDR, чтобы сбросить RXC флаг ; Определяем что там пришло CPI R16,'R' BREQ PC+0x07 ; BREQ REGISTER CPI R16,'M' BREQ PC+0x07 ; BREQ MEMORY CPI R16,'I' BREQ PC+0x09 ; BREQ IO CPI R16,'G' BREQ PC+0x0A ; BREQ GONEXT OUT UDR,@0 ; REGISTER - отправляем в UDR значение регистра RJMP PC+0x0008 ; Проверка на пустоту UDR тут скорей всего не нужна. ; Т.к. пока впечатаешь ручкам в терминалку байт, наверняка ; все что хотело уйти уже уйдет и UDR опустошится. LDS R16,@1 ; MEMORY - шлем значение из памяти OUT UDR,R16 RJMP PC+0x0004 IN R16,@2 ; IO - шлем значение из порта ввода-вывода. OUT UDR,R16 POP R16 ; GONEXT - достаем все сохраненное из стека и идем дальше OUT SREG,R16 POP R16 .ENDMКак видишь, макрос массово использует относительные переходы (иначе при вставке двух макросов метки будут дублироваться). Так что модифицировать его нужно очень осторожно. Высчитывая команды. Но есть спрособ проще - написать его вначале с обычными метками, скомпилить, запустить отладку, потом открыть дизассемблер (view->disassembler) и поглядеть там смещения. Используется просто — вставляешь в код такую штуку: 1 DEB_CMD R15,0x0000,PORTDИ при попадании в это место МК остановится и будет ждать приказа. По команде G - пойдет дальше. По команде R - даст содержание регистра R15, по команде M - содержание ячейки ОЗУ с адресом 0х0000, а по команде I - значение из порта PORTD. Порты, регистры, ячейки памяти вписываешь те которые хочешь посмотреть. Если нужно что то одно, то пишешь в ненужные поля любые подходящие значения. Ну или делаешь узкоспециализированные макросы под память, регистр и порт ВВ. Можно еще прерывания запретить, чтобы стопор был полный, но не забывай, что всякие таймеры продолжают тикать самостоятельно вне зависимость от того работает МК или зациклен в ожидании команды. А еще, если включен WATCHDOG, то он может и за задницу укусить. Поэтому в макрос можно и команду WDR добавить, в цикл ожидания байта. Более того, никто не запрещает написать простейший (или сложный) командный обработчик с шахматами и поэтессами, позволяющий указывать непосредственно в передаваемой команде какой ресурс надо забрать. Или что и в какой регистр записать, куда перейти… В общем, этакий микро SoftICE (если кто еще помнит этот легендарный отладчик). А если дружишь с какими-нибудь Дельфями, то можешь снаружи и отладчик написать, который через этот супервизор будет выковыривать все нужные данные по USART и раскладывать их красивыми рядами на панельках, дабы было любо-дорого глядеть. Например Николев в своем отладочном модуле так и сделал, правда он через SPI работал, но не суть важно. Оставляю это на самостоятельное изучение :) На Си все пишется примерно в том же ключе. Можешь функции забабахать под это дело. Можешь макросы, тут по желанию. |
|
| <<< Предыдущая статья | Следующая статья >>> |