Как заглянуть в мозги AVR AtMega без JTAG и гемороя

Что делать? (с) Чернышевский. «Кто виноват?», «Кому на Руси жить хорошо?» — тьфу ты не туда.
Вообщем берем и побыстрому подключаем стандартный поток вывода к USART (там где он, то есть USART, есть):
Для чего делаем два файлика — serial.h и serial.c
Файл заголовка serial.h
#include <stdio.h>
#include <avr\io.h>
#define BAUD 9600 //Желаемая скорость вывода
#define F_CPU 16000000L //Сами знаете что
extern int uart_putchar(char c, FILE *stream); //Функция побайтового вывода
extern void SerilalIni(void); //Функция инициализации
Файл тела программы — serial.c
#include "serial.h"
#include <util/setbaud.h> //С помощью этих макросов рассчитывается константы UBRRH_VALUE и UBRRL_VALUE
//Буфер для вывода
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
_FDEV_SETUP_WRITE);
//Функция выталкивающая буфер в порт USART
int uart_putchar(char c, FILE *stream){
if (c == '\n')
uart_putchar('\r', stream);
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
return 0;
}
//Инициализация USART
void SerilalIni(){
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 9600
UCSR0A=0x00;
UCSR0B=(1<<RXEN0) | (1<<TXEN0);
UCSR0C=0x06;
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
stdout = &mystdout;//Направляем поток вывода на наш буфер
}
Вуаля! Теперь подключив serial.h получаем полноценный вывод с помощью функций printf например.
Что-то типа этого:
#include "serial.h"
int main(void)
{
unsigned char i;
SerilalIni();//Инициализируем вывод
while(1)
{
printf("Hello word!\n");
for(i=10;i!=0;i--) printf("i = %d\n",i);
}
}
Подключив к порту USART нашего MK большого брата и запустив на нем любую терминальную программу, получим знаменитое «Hello word!» и обратный отсчет.
Все это можно прочитать в справке по AVR Libc — которая в свою очередь входит в AVR Studio 4.
Так, что старая истина — «если ничего не выходит, то прочитайте инструкцию» все еще действует несмотря на засилье Интернетов.
- -2
- 20 апреля 2011, 21:43
- KalyakaMalyaka
Я и не спорю, настоящий доктор и без жопоскопа геморой определит, но если есть жопоскоп и его легко вставить, почему бы и не заглянуть внутрь пациента?
- KalyakaMalyaka
- 21 апреля 2011, 06:50
- ↑
- ↓
Сенсей Ди уже писАл о чудо отладке по USART: easyelectronics.ru/avr-uchebnyj-kurs-otladka-programm-chast-3.html
Нисколько не умаляя достоинств упомянутой статьи и сверхдостоинств сенсея (три раза ку!) все же замечу, что в статье сенсея идет речь о реализации на асемблере. AVR Libc же предлагает быстрый и легкий способ получить связь без лишних телодвижений, правда на C :)
- KalyakaMalyaka
- 21 апреля 2011, 06:55
- ↑
- ↓
Гораздо более легкий и быстрый способ и вот уж точно без лишних телодвижений это что то вроде:
Ну и потом, где надо в коде:
На все провсе уйдет несколько байтов, выполняется быстро, не тормозит. Правда для циклического вывода надо будет буферы делать. И вначале писать туда, а лишь потом выгружать результат работы.
UBRRL = LO(bauddivider);
UBRRH = HI(bauddivider);
UCSRA = 0;
UCSRB = 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE|0<<UDRIE;
UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
Ну и потом, где надо в коде:
UDR = 'F';
На все провсе уйдет несколько байтов, выполняется быстро, не тормозит. Правда для циклического вывода надо будет буферы делать. И вначале писать туда, а лишь потом выгружать результат работы.
Это если надо посмотреть однобайтовую переменную. А если кучу всяких переменных?
Например так: printf(«Stop j = %d, i = %x, val = %l»);
Например так: printf(«Stop j = %d, i = %x, val = %l»);
- KalyakaMalyaka
- 21 апреля 2011, 07:20
- ↑
- ↓
Бросаешь их всех в дамп. А выгружаешь потом. Иначе пока это все выводится форматированным выводом у нас прощелкает все прерывания, аппаратные события и прочее — смысл отладки резко теряется. Разве что внутренние алгоритмы отлаживать, но в этом случае рулит эмуляция в Авр студии.
Дамп — это опять лишние телодвижения вместо элементарного printf. Конечно основное ограничение это медленность вывода, тут как говорится надо «технически».
А эмулятор плох тем, что он показывает поведение сферического коня в вакууме, всякие наводки и перегрызенные кроликом провода в нем плохо эмулируются :)
А эмулятор плох тем, что он показывает поведение сферического коня в вакууме, всякие наводки и перегрызенные кроликом провода в нем плохо эмулируются :)
- KalyakaMalyaka
- 21 апреля 2011, 08:37
- ↑
- ↓
Сразу вижу минусы такого подхода — не позвляет отлаживать быстрые процессы. Т.е. уарт не будет успевать отсылать данные. Плюс сильно раздувается размер кода (на пару килобайт точно). Делать так с буфферизированным выходом. Чтобы загнать лог в память, а потом спокойно выгрузить.
О великий DI! Предлагаемый способ не более чем быстрая диагностика, а тут уж как говорится: «дешево, качественно, быстро — вычеркните одно слово»
- KalyakaMalyaka
- 21 апреля 2011, 07:14
- ↑
- ↓
Просто диагностика должна как можно меньше влиять на исходный код. А тут втыкается огромная либа с жирными функциями. Из-за этого часть глюков может уйти на дно, часть наоборот внезапно вылезти. А при плотной упаковке в маленький камень так оно вообще не влезет :)
Всякий метод хорош под свои задачи, поэтому не надо впихивать невпихуемое — маленький камень вряд ли потребует большой программы, а значит и не потребует такого средства отладки. Достаточно будет действительно светодиода.
- KalyakaMalyaka
- 21 апреля 2011, 10:08
- ↑
- ↓
Не согласен. Например, камень может быть итак с избытком взят, а вот бутлоадер приходится трамбовать ногами, ибо бутсектор не резиновый.
Нехорошо, если наблюдение за процессом будет мешать ходу процесса. printf выполняется долго и может нарушить временные рамки.
Например, наблюдать за одной переменной можно, просто посылая ее значение в UDR (ну да, дамп); на компьютере по полученным данным можно и график построить
=>
посмотрите, насколько быстрее выполняется второй код (мне самому интересно)
Например, наблюдать за одной переменной можно, просто посылая ее значение в UDR (ну да, дамп); на компьютере по полученным данным можно и график построить
printf("Hello world!\n");
for(i=10;i!=0;i--) printf("i = %d\n",i);
=>
for(i=10;i!=0;i--) UDR0=i;
посмотрите, насколько быстрее выполняется второй код (мне самому интересно)
Есть такое, и замеры на скорую руку какие-то странные получились :)
Сам грешил такого рода «отладчиками», на принтф, чертовски удобно. И разговаривает на нашем, и все показывает четко, а раз в секунду-две его вызывать ничего не стоит. Вот слово «отладка»… больше подходит «отображение текущего статуса», ИМХО.
Сам грешил такого рода «отладчиками», на принтф, чертовски удобно. И разговаривает на нашем, и все показывает четко, а раз в секунду-две его вызывать ничего не стоит. Вот слово «отладка»… больше подходит «отображение текущего статуса», ИМХО.
Как раз таки в большинстве случаев UDR='0'; больше чем достаточно. Только юзать прально надо)
А стдио это как бы жесть. Натыкиваеш такой «отладки» во все места, и сразу одни баги пропадут, а другие выскочат — тайминги работы прошивки кардинально меняются. Принтф и вывод в уарт на 9600 может и секунду запросто отожрать. Нафик такая отладка нужна)
А стдио это как бы жесть. Натыкиваеш такой «отладки» во все места, и сразу одни баги пропадут, а другие выскочат — тайминги работы прошивки кардинально меняются. Принтф и вывод в уарт на 9600 может и секунду запросто отожрать. Нафик такая отладка нужна)
Такая отладка нужна если надо по быстрому понять, что происходит в мозгах AVR. Для этого всего лишь надо написать три строки в основном коде программы и вы уже получите какую-то информацию.
- KalyakaMalyaka
- 21 апреля 2011, 12:17
- ↑
- ↓
Это банально некрасиво. stdio тупо бессмысленно сожрёт пол памяти в мк. Даж если у мя в проге нету никаких критичных к таймингам мест, я небуду юзать такую отладку — тупо в лом ждать пока этот монстр будет заливаться в мк)
Хм… ну хотя можно заюзать в крайнем случае, но тогда уж тихонько, никому не говоря что ты это делаеш))
Хм… ну хотя можно заюзать в крайнем случае, но тогда уж тихонько, никому не говоря что ты это делаеш))
Не могу сказать, что скорость заливки сильно изменилась в АTMega16 или ATMega32. А вот насчет «красиво — некрасиво» — это да. Решение далеко не идеальное, но зато оно даст вам время сделать красивую программу, а не сидеть и ждать когда придет отладочная плата с JTAG.
- KalyakaMalyaka
- 21 апреля 2011, 13:37
- ↑
- ↓
Есть куда более простые и удобные методы. Почитай статьи ди)
А JTAG (по крайней мере, первый) — страшная, тормозная фигня.
Переменные им не особо посмотриш, т.к. в прошивке всё перекорёжено оптимизатором и 90% переменных не показываются. Разве что проследить путь выполнения программы. Но задержки он, естественно, вносит дикие. Так что без обычной отладки всё равно никак.
А JTAG (по крайней мере, первый) — страшная, тормозная фигня.
Переменные им не особо посмотриш, т.к. в прошивке всё перекорёжено оптимизатором и 90% переменных не показываются. Разве что проследить путь выполнения программы. Но задержки он, естественно, вносит дикие. Так что без обычной отладки всё равно никак.
да вроде уже проще некуда — три строки в теле программы. Или можно обойтись одной или двумя?
- KalyakaMalyaka
- 21 апреля 2011, 14:21
- ↑
- ↓
Переменная в регистры должна внезапно выгрузится в регистры например в прерывании, я вас правильно понял? Если да то тут уж надо будет соблюдать атомарность операций, и printf как бы не при чем.
- KalyakaMalyaka
- 21 апреля 2011, 13:41
- ↑
- ↓
Дык, отлаживаешь ты нерегулярный(!) баг, появившийся из-за несоблюдения атомарности, или отсутствия volatile (ну забыл, с кем не бывает), вставил printf, он тебе баг убил, выгрузив регистры в память. И сиди днями дожидайся, подлавливай его, что бы посмотреть какие же там значения. Нет уж нет уж, нафиг нафиг такое счастье.
Как говорил Чапаев в том анекдоте «Суть в песок Петька», работать будет, а для отладки это не критично. Насчет надпись согласен, надо было, что то оригинальнее придумать :)
- KalyakaMalyaka
- 21 апреля 2011, 10:59
- ↑
- ↓
Оно нечитабельно (не видно какие собственно настройки ставятся) и непортабельно (если на целевом камне будет другой порядок бит — сломается).
Насчет надписи — Hello World там должно быть, а не word.
Насчет надписи — Hello World там должно быть, а не word.
Я и не претендую на универсальность. Потому так и назвал заметочку «Как заглянуть в мозги AVR AtMega без JTAG и гемороя». Ну а word вместо world — это моя оригинальная разработка. Она позволяет сэкономить одну букву при нажатии на клавиатуру, один байт в памяти мк и в то же время несет смысловую нагрузку, вот подумываю на патент подать. Вот только не могу решить в какой стране — англоязычной или в России.
- KalyakaMalyaka
- 21 апреля 2011, 14:17
- ↑
- ↓
Я себе для отладки вот такие макросы сделал
Этого более чем достаточно для того, что бы понять чем сейчас занят контроллер — нужно только расставить в разных кусках программы метки (я обычно ставлю двухбайтные метки в стиле "~f", где первый символ указывает область программы, а второй — конкретное место — так читабельнее). А для вывода многобайтных переменных можно писать что-то в стиле
А обнуление UBRR позволяет получить максимальную скорость UART (например, при работе на 1MHz юарт выдаёт 62500), то есть минимальную задержку в коде (впрочем, при однобайтных посылках задержки почти нет на любых скоростях).
А printf я использовал только для работы с LCD — там это уместно. Ну или когда обязательно необходимо выводить значения в десятичном виде.
#ifdef DEBUG_MODE
#define UART_INIT \
{ \
UBRR1 = 0; \
UCSR1B = (_BV(TXEN1) | _BV(RXEN1) | _BV(RXCIE1)); \
UCSR1C = (_BV(UCSZ11) | _BV(UCSZ10)); \
for(uint8_t index = 0; index < 80; index++) \
USART1_SEND_BYTE_('+'); \
USART1_SEND_BYTE_('\r'); \
USART1_SEND_BYTE_('\n'); \
}
#define USART1_SEND_BYTE(a_data) \
{ \
loop_until_bit_is_set (UCSR1A, UDRE1); \
UDR1 = a_data; \
}
#define USART1_PRINT_BYTE(a_byte) \
do{ \
uint8_t tmp_char; \
USART1_SEND_BYTE_('x'); \
tmp_char = (a_byte>>4&0x0f)+0x30; \
USART1_SEND_BYTE_((tmp_char<0x3A)?tmp_char:(tmp_char+7)); \
tmp_char = ((a_byte)&0x0f)+0x30; \
USART1_SEND_BYTE_((tmp_char<0x3A)?tmp_char:(tmp_char+7)); \
}while(0);
#else
#define DEBUG_INIT
#define USART1_SEND_BYTE(a_data)
#define USART1_PRINT_BYTE(a_byte)
#endif // DEBUG_MODE
Этого более чем достаточно для того, что бы понять чем сейчас занят контроллер — нужно только расставить в разных кусках программы метки (я обычно ставлю двухбайтные метки в стиле "~f", где первый символ указывает область программы, а второй — конкретное место — так читабельнее). А для вывода многобайтных переменных можно писать что-то в стиле
USART1_SEND_BYTE('A');
USART1_PRINT_BYTE((uint8_t) a);
USART1_PRINT_BYTE((uint8_t)(a >> 8));
USART1_SEND_BYTE('\r');
USART1_SEND_BYTE('\n');
А обнуление UBRR позволяет получить максимальную скорость UART (например, при работе на 1MHz юарт выдаёт 62500), то есть минимальную задержку в коде (впрочем, при однобайтных посылках задержки почти нет на любых скоростях).
А printf я использовал только для работы с LCD — там это уместно. Ну или когда обязательно необходимо выводить значения в десятичном виде.
То же выход. Но опять же зачем городить для отладки такой кусок
если можно просто написать
USART1_SEND_BYTE('A');
USART1_PRINT_BYTE((uint8_t) a);
USART1_PRINT_BYTE((uint8_t)(a >> 8));
USART1_SEND_BYTE('\r');
USART1_SEND_BYTE('\n');
если можно просто написать
printf("A =%x",a)
Проще для восприятия и быстрее написать. А сколько он там занимает это уже не так важно, в конечном продукте этого кода все равно не будет.
- KalyakaMalyaka
- 21 апреля 2011, 11:05
- ↑
- ↓
Да даже бог с ними, с таймингами, принтф — это вызов функции, а значит это серьёзные изменения в отлаживаемом коде. Сам факт добавления вызова функции потенциально может изменить поведение кода.
Безусловно изменяет, и конечно же время будет жрать больше. Но ведь никто не ждет, что такое простое и быстрое средство будет лучше работать чем полноценные средства отладки? Или и ждет? :)
- KalyakaMalyaka
- 21 апреля 2011, 11:53
- ↑
- ↓
лучше не лучше. но присоединяюсь к Alatar, вызываешь функцию — в регистрах уже могут быть отличные от первоначальных значения. + локальные переменные лезут в стек и изменяют его порядок. ну и скорость, сам понимаешь:) дольше функция дольше обрабатывается, чем макрос:)
Мы счас ведь про программу на С? Можно конечно рулить регистрами и стеком и в С, но по-моему это надо делать уже когда ты понимаешь что и почем. А раз ты такой большой то у тебя уже и JTAG есть и опыт. Тогда да, тебе это и не нужно.
- KalyakaMalyaka
- 21 апреля 2011, 13:34
- ↑
- ↓
Причем тут большой/небольшой. Я про компилятор, компилятор оперирует регистрами и стекам. Хз что там твориться если влезешь с функцией. А зачем тебе, если не секрет, залазить в мозги без jtag?
Раз компилятор оперирует регистрами и стеками, то он наверно и знает как правильно вызвать printf, и стеки нам сохранит и регистры. Jetag как раз мне теперь и не нужен :)
- KalyakaMalyaka
- 21 апреля 2011, 14:14
- ↑
- ↓
>> А раз ты такой большой то у тебя уже и JTAG есть и опыт.
гыгы… не понял связи. У меня вот есть JTAG ICE mkII и что, это значит, что я уже большой? =)
Я им, кстати, пользуюсь только как программатором, ибо моргания ножками и выкидывание одного-двух байт в уарт на максимальной скорости гораздо меньше на код влияют.
гыгы… не понял связи. У меня вот есть JTAG ICE mkII и что, это значит, что я уже большой? =)
Я им, кстати, пользуюсь только как программатором, ибо моргания ножками и выкидывание одного-двух байт в уарт на максимальной скорости гораздо меньше на код влияют.
Да действительно, тема одна. Только там все же сделан упор на ассемблерный код, а тут наверно больше про С. Поэтому было бы не совсем корректно назвать это дублем. Ведь тогда все книги о любви можно назвать дублями — рассказываю то об одном и том же. :)
- KalyakaMalyaka
- 21 апреля 2011, 15:09
- ↑
- ↓
Согласен, возможно погорячился. Когда искал указанную статью, то увидел, что там на асме, а тут на С.
А кто нибудь мне обьяснит в чем разница писать на Си или на асме? Тем более когда надо то байт в периферию толкнуть :)
Как же! Разница в количестве нажатий клавиш на клавиатуре. Но тут как-то трудно вывести какое-то соотношение Си-Асм, ведь клавиатуры разные бывают :)
А если серъезно, то данном конкретном случае если программа на Си то выигрываешь в скорости подключения модуля вывода. Надо написать три строки: 1. include Serial.h 2. SerilaIni() 3. printf(«Val = %d», val)
Или вы про вообще использование инструмента?
А если серъезно, то данном конкретном случае если программа на Си то выигрываешь в скорости подключения модуля вывода. Надо написать три строки: 1. include Serial.h 2. SerilaIni() 3. printf(«Val = %d», val)
Или вы про вообще использование инструмента?
- KalyakaMalyaka
- 21 апреля 2011, 16:51
- ↑
- ↓
Безусловно! На асме короче, быстрее, экономичнее. Но! рост функциональности тащит за собой усложнение алгоритмов обработки. Усложнение алгоритмов требует увеличение уровня абстракции. А с учетом прогресса в железе, роль языков высокого уровня будет как мне кажется только возрастать. Поэтому ограничивая себя применением Ассемблера мы загоняем себя в нишу устройств очень близко стоящих к исполнительным устройствам. А устройства прекрасно клепают китайцы и без нас, так что тут надо тщательнее. Как говорил Козма Прутков «Специалист подобен флюсу, полнота его односторонняя» Так, что Си то же нужен.
- KalyakaMalyaka
- 21 апреля 2011, 17:06
- ↑
- ↓
Речь не о том, вообще. Безусловно Си уже давно главный эмбеддерский язык. А асм он больше для понимания сути и оптимизации. Я про то, что когда я даю пример на асме то начинаются стоны оооо оно на ассемблере, а я пишу на си…
Тогда как все примеры у меня на аппаратуру и периферию, а значит в этой части разница что Си что асм мизерная. И пример написанный на асме понимается и переписывается на любой другой язык на уровне спинного мозга. Автоматом.
Тогда как все примеры у меня на аппаратуру и периферию, а значит в этой части разница что Си что асм мизерная. И пример написанный на асме понимается и переписывается на любой другой язык на уровне спинного мозга. Автоматом.
Ах вот вы про что! Извините не сразу понял. Естественно чем ближе к железу тем эффективнее будут работать инструменты более низкого уровня. И как всегда — каждый инструмент хорош для своего применения. Ведь если даже посмотреть на животных, то и у них нет универсальных видов, есть хищники и есть жертвы к примеру. А ведь прошло миллиарды лет эволюции! Куда уж нам со своими сотнями лет развития математики.
- KalyakaMalyaka
- 21 апреля 2011, 17:27
- ↑
- ↓
Уже два раза была, теперь в третий раз:
AVR. Учебный Курс. Отладка программ. Часть 3
И ещё:
AVR. Учебный курс. Передача данных через UART
Форматный вывод на Си для микроконтроллеров.
AVR. Учебный Курс. Отладка программ. Часть 3
И ещё:
AVR. Учебный курс. Передача данных через UART
Форматный вывод на Си для микроконтроллеров.
Комментарии (59)
RSS свернуть / развернуть