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

AVR
Ну нету у меня 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.
Так, что старая истина — «если ничего не выходит, то прочитайте инструкцию» все еще действует несмотря на засилье Интернетов.

Комментарии (59)

RSS свернуть / развернуть
Мне для отладки пары светодиодов обычно хватает =)
0
Я и не спорю, настоящий доктор и без жопоскопа геморой определит, но если есть жопоскоп и его легко вставить, почему бы и не заглянуть внутрь пациента?
0
Сенсей Ди уже писАл о чудо отладке по USART: easyelectronics.ru/avr-uchebnyj-kurs-otladka-programm-chast-3.html
0
Нисколько не умаляя достоинств упомянутой статьи и сверхдостоинств сенсея (три раза ку!) все же замечу, что в статье сенсея идет речь о реализации на асемблере. AVR Libc же предлагает быстрый и легкий способ получить связь без лишних телодвижений, правда на C :)
0
Гораздо более легкий и быстрый способ и вот уж точно без лишних телодвижений это что то вроде:


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';


На все провсе уйдет несколько байтов, выполняется быстро, не тормозит. Правда для циклического вывода надо будет буферы делать. И вначале писать туда, а лишь потом выгружать результат работы.
0
Это если надо посмотреть однобайтовую переменную. А если кучу всяких переменных?
Например так: printf(«Stop j = %d, i = %x, val = %l»);
0
Бросаешь их всех в дамп. А выгружаешь потом. Иначе пока это все выводится форматированным выводом у нас прощелкает все прерывания, аппаратные события и прочее — смысл отладки резко теряется. Разве что внутренние алгоритмы отлаживать, но в этом случае рулит эмуляция в Авр студии.
0
Дамп — это опять лишние телодвижения вместо элементарного printf. Конечно основное ограничение это медленность вывода, тут как говорится надо «технически».
А эмулятор плох тем, что он показывает поведение сферического коня в вакууме, всякие наводки и перегрызенные кроликом провода в нем плохо эмулируются :)
0
Сразу вижу минусы такого подхода — не позвляет отлаживать быстрые процессы. Т.е. уарт не будет успевать отсылать данные. Плюс сильно раздувается размер кода (на пару килобайт точно). Делать так с буфферизированным выходом. Чтобы загнать лог в память, а потом спокойно выгрузить.
0
О великий DI! Предлагаемый способ не более чем быстрая диагностика, а тут уж как говорится: «дешево, качественно, быстро — вычеркните одно слово»
0
Просто диагностика должна как можно меньше влиять на исходный код. А тут втыкается огромная либа с жирными функциями. Из-за этого часть глюков может уйти на дно, часть наоборот внезапно вылезти. А при плотной упаковке в маленький камень так оно вообще не влезет :)
0
Всякий метод хорош под свои задачи, поэтому не надо впихивать невпихуемое — маленький камень вряд ли потребует большой программы, а значит и не потребует такого средства отладки. Достаточно будет действительно светодиода.
0
Не согласен. Например, камень может быть итак с избытком взят, а вот бутлоадер приходится трамбовать ногами, ибо бутсектор не резиновый.
0
Нехорошо, если наблюдение за процессом будет мешать ходу процесса. printf выполняется долго и может нарушить временные рамки.
Например, наблюдать за одной переменной можно, просто посылая ее значение в UDR (ну да, дамп); на компьютере по полученным данным можно и график построить
printf("Hello world!\n");
        for(i=10;i!=0;i--) printf("i = %d\n",i);

=>
for(i=10;i!=0;i--) UDR0=i;

посмотрите, насколько быстрее выполняется второй код (мне самому интересно)
0
кстати, замерил. 751us vs 3.3us
0
Ага. Только второй кусок нерабочий =)
+1
Есть такое, и замеры на скорую руку какие-то странные получились :)
Сам грешил такого рода «отладчиками», на принтф, чертовски удобно. И разговаривает на нашем, и все показывает четко, а раз в секунду-две его вызывать ничего не стоит. Вот слово «отладка»… больше подходит «отображение текущего статуса», ИМХО.
0
Как раз таки в большинстве случаев UDR='0'; больше чем достаточно. Только юзать прально надо)

А стдио это как бы жесть. Натыкиваеш такой «отладки» во все места, и сразу одни баги пропадут, а другие выскочат — тайминги работы прошивки кардинально меняются. Принтф и вывод в уарт на 9600 может и секунду запросто отожрать. Нафик такая отладка нужна)
0
Такая отладка нужна если надо по быстрому понять, что происходит в мозгах AVR. Для этого всего лишь надо написать три строки в основном коде программы и вы уже получите какую-то информацию.
0
Это банально некрасиво. stdio тупо бессмысленно сожрёт пол памяти в мк. Даж если у мя в проге нету никаких критичных к таймингам мест, я небуду юзать такую отладку — тупо в лом ждать пока этот монстр будет заливаться в мк)

Хм… ну хотя можно заюзать в крайнем случае, но тогда уж тихонько, никому не говоря что ты это делаеш))
0
Не могу сказать, что скорость заливки сильно изменилась в АTMega16 или ATMega32. А вот насчет «красиво — некрасиво» — это да. Решение далеко не идеальное, но зато оно даст вам время сделать красивую программу, а не сидеть и ждать когда придет отладочная плата с JTAG.
0
Есть куда более простые и удобные методы. Почитай статьи ди)
А JTAG (по крайней мере, первый) — страшная, тормозная фигня.
Переменные им не особо посмотриш, т.к. в прошивке всё перекорёжено оптимизатором и 90% переменных не показываются. Разве что проследить путь выполнения программы. Но задержки он, естественно, вносит дикие. Так что без обычной отладки всё равно никак.
0
В смысле, я про проги на Си:)
Чтобы эффективно юзать JTAG надо писать на Асме или хотя бы переключаться в дизассемблятину при отладке)
0
да вроде уже проще некуда — три строки в теле программы. Или можно обойтись одной или двумя?
0
Угу… только вот в результат такой отладки Вы можете вкуривать дольше, чем было бы если написать по-нормальному из-за того что где-то слетели тайминги, или какая-та переменная не в то время выгрузилась в память из регистров из-за нового вызова функции…
0
Переменная в регистры должна внезапно выгрузится в регистры например в прерывании, я вас правильно понял? Если да то тут уж надо будет соблюдать атомарность операций, и printf как бы не при чем.
0
Дык, отлаживаешь ты нерегулярный(!) баг, появившийся из-за несоблюдения атомарности, или отсутствия volatile (ну забыл, с кем не бывает), вставил printf, он тебе баг убил, выгрузив регистры в память. И сиди днями дожидайся, подлавливай его, что бы посмотреть какие же там значения. Нет уж нет уж, нафиг нафиг такое счастье.
0
UCSR0C=0x06;

printf("Hello word!\n");


Руки бы тебе оторвать =)
0
Думаете работать не будет?
0
Первое нечитабельно, а второе — «Привет Ворд» или «Привет слово».
0
Как говорил Чапаев в том анекдоте «Суть в песок Петька», работать будет, а для отладки это не критично. Насчет надпись согласен, надо было, что то оригинальнее придумать :)
0
Оно нечитабельно (не видно какие собственно настройки ставятся) и непортабельно (если на целевом камне будет другой порядок бит — сломается).
Насчет надписи — Hello World там должно быть, а не word.
0
Я и не претендую на универсальность. Потому так и назвал заметочку «Как заглянуть в мозги AVR AtMega без JTAG и гемороя». Ну а word вместо world — это моя оригинальная разработка. Она позволяет сэкономить одну букву при нажатии на клавиатуру, один байт в памяти мк и в то же время несет смысловую нагрузку, вот подумываю на патент подать. Вот только не могу решить в какой стране — англоязычной или в России.
0
Я себе для отладки вот такие макросы сделал


#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 — там это уместно. Ну или когда обязательно необходимо выводить значения в десятичном виде.
0
То же выход. Но опять же зачем городить для отладки такой кусок
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)
Проще для восприятия и быстрее написать. А сколько он там занимает это уже не так важно, в конечном продукте этого кода все равно не будет.
0
Ну… зависит конечно от того, какие у тебя тайминги. Интереса ради, посмотри листинг принтфа, да время его исполнения померь… Он, вообще-то написан на С, а не на асме и нифига не оптимизирован.
0
Да даже бог с ними, с таймингами, принтф — это вызов функции, а значит это серьёзные изменения в отлаживаемом коде. Сам факт добавления вызова функции потенциально может изменить поведение кода.
0
Безусловно изменяет, и конечно же время будет жрать больше. Но ведь никто не ждет, что такое простое и быстрое средство будет лучше работать чем полноценные средства отладки? Или и ждет? :)
0
лучше не лучше. но присоединяюсь к Alatar, вызываешь функцию — в регистрах уже могут быть отличные от первоначальных значения. + локальные переменные лезут в стек и изменяют его порядок. ну и скорость, сам понимаешь:) дольше функция дольше обрабатывается, чем макрос:)
0
Мы счас ведь про программу на С? Можно конечно рулить регистрами и стеком и в С, но по-моему это надо делать уже когда ты понимаешь что и почем. А раз ты такой большой то у тебя уже и JTAG есть и опыт. Тогда да, тебе это и не нужно.
0
Причем тут большой/небольшой. Я про компилятор, компилятор оперирует регистрами и стекам. Хз что там твориться если влезешь с функцией. А зачем тебе, если не секрет, залазить в мозги без jtag?
0
Раз компилятор оперирует регистрами и стеками, то он наверно и знает как правильно вызвать printf, и стеки нам сохранит и регистры. Jetag как раз мне теперь и не нужен :)
0
ага, сохранит он правильно, но значения РОН, RAM в, как ты говоришь, «мозгах» контроллера будут уже немного не то, что было до этого.
тем более через форматированный вывод. Дак зачем залазить без jtag?
0
>> А раз ты такой большой то у тебя уже и JTAG есть и опыт.

гыгы… не понял связи. У меня вот есть JTAG ICE mkII и что, это значит, что я уже большой? =)
Я им, кстати, пользуюсь только как программатором, ибо моргания ножками и выкидывание одного-двух байт в уарт на максимальной скорости гораздо меньше на код влияют.
0
Ну вот, уже и статьи с сайта начали в сообществе дублировать…
0
А какую статью я сдублировал? Ткните неразумного…
0
Да действительно, тема одна. Только там все же сделан упор на ассемблерный код, а тут наверно больше про С. Поэтому было бы не совсем корректно назвать это дублем. Ведь тогда все книги о любви можно назвать дублями — рассказываю то об одном и том же. :)
0
Согласен, возможно погорячился. Когда искал указанную статью, то увидел, что там на асме, а тут на С.
0
А кто нибудь мне обьяснит в чем разница писать на Си или на асме? Тем более когда надо то байт в периферию толкнуть :)
0
Как же! Разница в количестве нажатий клавиш на клавиатуре. Но тут как-то трудно вывести какое-то соотношение Си-Асм, ведь клавиатуры разные бывают :)
А если серъезно, то данном конкретном случае если программа на Си то выигрываешь в скорости подключения модуля вывода. Надо написать три строки: 1. include Serial.h 2. SerilaIni() 3. printf(«Val = %d», val)
Или вы про вообще использование инструмента?
0
Да про инструмент. По крайней мере когда дело касается работы с периферией (а не математика или всякие алгоритмы вроде работы со списками) то разницы между Си и Асмом не больше чем между Си и Паскалем. А иной раз на асме и покороче получается :)
0
Безусловно! На асме короче, быстрее, экономичнее. Но! рост функциональности тащит за собой усложнение алгоритмов обработки. Усложнение алгоритмов требует увеличение уровня абстракции. А с учетом прогресса в железе, роль языков высокого уровня будет как мне кажется только возрастать. Поэтому ограничивая себя применением Ассемблера мы загоняем себя в нишу устройств очень близко стоящих к исполнительным устройствам. А устройства прекрасно клепают китайцы и без нас, так что тут надо тщательнее. Как говорил Козма Прутков «Специалист подобен флюсу, полнота его односторонняя» Так, что Си то же нужен.
0
Речь не о том, вообще. Безусловно Си уже давно главный эмбеддерский язык. А асм он больше для понимания сути и оптимизации. Я про то, что когда я даю пример на асме то начинаются стоны оооо оно на ассемблере, а я пишу на си…

Тогда как все примеры у меня на аппаратуру и периферию, а значит в этой части разница что Си что асм мизерная. И пример написанный на асме понимается и переписывается на любой другой язык на уровне спинного мозга. Автоматом.
0
Ах вот вы про что! Извините не сразу понял. Естественно чем ближе к железу тем эффективнее будут работать инструменты более низкого уровня. И как всегда — каждый инструмент хорош для своего применения. Ведь если даже посмотреть на животных, то и у них нет универсальных видов, есть хищники и есть жертвы к примеру. А ведь прошло миллиарды лет эволюции! Куда уж нам со своими сотнями лет развития математики.
0
как всегда — в скорости
0
JTAG для AVR делал так — размяться, всегда хватало светодиодов. А вот в STM32 здорово помог.
0
  • avatar
  • psv
  • 21 апреля 2011, 18:00
Спасибо, помогло.
Правда на ATmega8 инициализация другая. Но разобрался. А в статье для какой меги вариант? 128 чтоли?
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.