Программирование и JTAG-отладка микроконтроллера Atmega16 на языке C в среде IAR, часть 1
Введение
По моему мнению, чтобы быстро научится программировать практически любой микроконтроллер, существующий в мире, нужно освоить язык C и пользоваться JTAG-отладкой, конечно, помимо изучения технической документации. Поясню свою мысль. Компиляторы языка C существуют практически для всех существующих микроконтроллеров. Поэтому язык С давно зарекомендовал себя, как кроссплатформенный ассемблер. Его знание освобождает от необходимости изучения ассемблерных команд для каждого нового семейства микроконтроллеров. JTAG-отладка, в свою очередь, обеспечивает не только возможность внутрисхемного поиска ошибок, но и помогает изучать микроконтроллер изнутри. Я думаю, что для всех очевиден тот факт, что при просто программировании без отладки даже простых микроконтроллеров мы подходим к изучению системы, как к черному ящику с входами и выходами. Такой подход, особенно на начальном этапе, затрудняет обучение. C другой стороны JTAG-отлдака позволяет забраться во внутрь, посмотреть как выполняется программа по шагам, посмотреть, что происходит в памяти и регистрах, запустить волнение до точек останова, выполнять дизассемблированный вариант программы. Эта возможность позволяет значительно ускорить обучение.
Такие микроконтроллеры как AVR, STM8, MSP430, AVR32, STM32, EFM32, Renesas RX имеют компиляторы языка С и возможность отладки при помощи интерфейса JTAG. Единой кроссплатформенной средой для этих многих других микроконтроллеров является Embedded Workbench. Хоть среда и платная, но есть возможность использовать 30-дневную бесплатную версию или версию с ограничением по размеру кода. Для начального изучения нового семейства месяца может быть вполне достаточно. После чего можно составить свое мнение о семействе и решить продолжить с ним работу на бесплатных инструментах, либо для небольших проектов пользоваться версией ограниченной по размеру кода. Для больших коммерческих продуктов можно и приобрести данную среду.
Также я считаю, что начинать изучение лучше с семейств AVR, STM8, MSP430. Работу этих микроконтроллеров проще понять, потому что они имеют сравнительно простую систем команд, небольшое количество регистров процессора и периферии. Начинать лучше с написания простых примеров, постепенно переходя к написанию своих библиотек. Все это хорошо охватывается головой. Для первого знакомства это очень удачно.
Для примеров, рассмотренных далее, я выбрал AVR, потому что это наиболее популярное в микроконтроллерном мире семейство. Я думаю, что с него проще начать, но не стоит им ограничиваться.
Выбор JTAG-отладчика
Для внутрисхемной отладки написанных на языке C программ нам потребуется JTAG-отладчик для микроконтроллеров AVR. Самыми доступными по цене и простыми в изготовления являются клоны фирменного отладчика AVR JTAGICE. Такой клон можно заказать по небольшой цене с eBay. Качество здесь, как лотерея.
Также клоны производит, например, фирма Olimex. Есть классический вариант, подключаемый к компьютеру через COM-порт и более новый вариант, где подключение осуществляется по интерфейсу USB.
При личном изготовлении такого клона, если нет возможности сделать свою печатную плату, можно использовать только DIP-компоненты, что упростит монтаж. Ранняя версия Evertool, который содержит в себе клон JTAGICE, была сделана именно так.
]
В основе большинства клонов JTAGICE лежит схема, изображенная на рисунке снизу. Точнее часть, которая называется JTAGICE section. Ядром отладчика является микроконтроллер Atmega16L. Если хотим получить подключение клона JTAGICE по USB, то меняем микросхему преобразования уровней MAX3232 на микросхему FTDI FT232. Однако FT232 в DIP-корпусе не выпускают, а цены на DIP-модули с данной микросхемой довольно высоки. Поэтому тут уже одной монтажной платой и DIP-компонентами не отделаешься. Придется либо, как вариант, припаять FT232 тонкими проводами к макетке, либо вытравить хлорным железом однослойную печатную плату с нормальным посадочным местом.
Прошивку можно найти в папке установки AVR Studio 4 или скачать здесь.
Недостатком клонов JTAGICE, описанных выше, является то, что в AVR Studio 6 они не поддерживаются. Однако в старой версии AVR Studio 4 и старых и новых версиях IAR для AVR данные клоны прекрасно поддерживаются.
Для отладки примеров кода на C, написанных далее, я пользовался клоном, сделанным по данной схеме. Микросхема MAX3232 в нем замена на ADM3202, что сути не меняет.
Данный клон JTAGICE достался мне по наследству. Я нашел в коробке с радиотехническим мусором на работе. Он не работал, и я решил его перепрошить. Для чего потребовалось сделать специальный переходник, потому что, как видно на фото, разъем AVR ISP для программирования Atmega16L не выведен для уменьшения габаритов. После прошивки JTAGICE заработал и обрел «загробную» жизнь.
Сейчас, когда COM-порт стал немодным, я не стал бросать «старого друга» и купил кабель-преобразователь USB в RS-232.
Отладочная плата
В качестве отладочной платы я буду использовать цифровой термометр, собранный мной на макетной плате. Питается плата по USB, ядром является микроконтроллер Atmega16 в DIP-корпусе. Микроконтроллер тактируется от кварцевого резонатора частотой 16 МГц и обвязан минимально необходимым набором пассивных компонентов (конденсаторы по 22 пФ для кварца, резистор 10 кОм на подтяжку линии Reset к питанию, конденсаторы 0,1 мкФ по питанию). Для программирования и отладки микроконтроллера выведены два стандартных разъема шестиконтактный AVR ISP и десятиконтактный AVR JTAG. На плате есть аналоговый температурный датчик, статический семисегментный индикатор на три цифры, дополнительный светодиод. Индикационный светодиод и сегменты индикатора подключены к микроконтроллеру через токоограничивающие резисторы по 500 Ом. Каждый из трех семисегментных индикаторов подключен к выводам с нулевого по шестой одного из портов A, B и D. Индикационный светодиод подключен к 7-му выводу порта D. Выход аналогового датчика подключен к 7-му выводу порта A (7-й канал встроенного АЦП).
Все элементы были спаяны проводом МГТФ.
Среда разработки IAR
Для написания и отладки программ будем использовать среду IAR Embedded Workbench for Atmel AVR, а именно бесплатную ее версию с ограничением по размеру кода в 4 Кб. Для небольших проектов на языке С для микроконтроллеров семейства AVR этого вполне достаточно.
Зайдем сюда и почитаем, что обеспечивает последняя версия IAR для AVR на текущий момент времени. Отладчик JTAGICE, о котором говорилось выше, как и раньше, поддерживается.
В списке поддерживаемых семейств есть megaAVR.
Убедившись, что наш JTAG-отладчик и отладочная плата пойдут, скачаем IAR.
Установщик позволяет зарегистрироваться с лицензией Kickstart.
После установки, чтобы зарегистрировать IAR, придется заполнить анкету регистрации.
После заполнения анкеты на вашу почту придет ключ, введя который, мы и получим бесплатную зарегистрированную Kickstart-версию не ограниченную по времени.
Создадим пустой проект для наших задач. Мы выбираем язык C и вариант AVR Studio 4 compatible output, на случай если придется зашивать выходной hex-файл средствами AVR Studio 4.
После выбора языка перед нами появится совсем пустой почти пустой source-файл.
Перейдем к настройкам нашего проекта. Т.к. мы будем писать программы для Atmega16, выберем данный микроконтроллер в графе Processor configuration.
В пункте меню Debugger выберем JTAGICE.
В пункте меню Debugger-> JTAGICE выберем номер COM-порта, к которому подключен отладчик.
Для корректной работы определений значащих битов регистров периферии микроконтроллера в библиотеках среды также необходимо поставить
галочку Enable bit definitions in I/O-Include files в пункте меню General Options->System.
Программирование и отладка
Прежде чем приступить к описанию примеров, нужно объяснить некоторые особенности среды IAR. В заголовочном файле iom16.h, который содержит определения адресов регистров для Atmega16, есть макросы, которые позволяют обращаться к конкретным битам регистров внутренней периферии, следующим образом:
По сути можно использовать любой из этих вариантов. И выбор здесь, по сути, состоит в удобстве использования конкретного варианта и простоте последующего понимания написанного.
В среде IAR есть возможность использования встроенной функции задержки __delay_cycles(x), где x — это время задержки в тактах. Для нашего случая задержка на один такт это 1/16000000 = 62,5 нс.
Теперь, когда мы знаем эти два момента, можно двинуться к первому примеру, где происходит просто мигание светодиодом, при помощи исключающего или.
Для упрощения формирования задержки требуемой длительности созданы макрофункции задержки DELAY_US(us) и DELAY_MS(ms), которые позволяют задавать задержку не в тактах, а сразу в микросекундах и миллисекундах. Далее перейдем в основную функцию программы main(), в которую мы попадаем после включения микроконтроллера или после сброса. Логично, что первое, что мы должны сделать после включения это настроить необходимые для работы периферийные блоки в нашем случае это только один вывод порта D. Чтобы настроить его на вывод достаточно установить бит DDD7 регистра направления данных DDRD в единицу. Для того, чтобы выставить на ноге «1» (5 В) необходимо установить бит PORTD7 в регистре данных PORTD, для того чтобы выставить на ноге «0» (земля) необходимо сбросить бит PORTD7. В даташите можно почитать об этом подробнее.
Для установки и записи битов управляющих регистров DDRD и PORTD нежелательно использовать просто запись (DDRD = 0x01, DDRD = 0x00) без побитовой дизъюнкции и конъюнкции (DDRD |= 0x01, DDRD &=
0x01, DDRD |= (1 Download and Debug или нажмем сочетание клавиш Ctrl+D или на иконку с красным треугольником, после этого, если нет синтаксических ошибок в коде, мы перейдем в режим отладки, в котором нам доступны команды пошагового выполнения Step Over, Step Into, Step Out, Next Statement, Go, Reset. Помимо этого есть возможность использования точек останова. Это позволяет пройти программу по шагам и посмотреть, что происходит на каждом шаге в регистрах процессора и регистрах DDRD и PORTD порта D, запустить на выполнение, затем остановить, запустить до точки останова. Все это дает не только инструмент поиска ошибок, но также помогает лучше понять работу микроконтроллера. Научится быстрей с ним работать.
Второй пример это тоже мигание светодиодом, но уже с использованием дополнительных макрофункций.
Подход с использованием макрофункций обеспечивает удобство в двух отношениях. Первое это обеспечение улучшения читаемости кода. Второе это упрощение возможных исправлений. Теперь, если светодиод нужно перенастроить на другой вывод другого порта, достаточно поменять три определения LED_DDR, LED_PORT, LED_PIN, а не делать исправления по всему тексту программы. При написании библиотек это сильно упрощает жизнь.
Третий пример это реализация мигания светодиодом, при помощи машины состояний. Машина состояний – это один из вариантов реализации модели встраиваемого ПО микроконтроллера, в котором бесконечный цикл делится на ветки в зависимости от текущего сотояния.
В нашем примере, есть три состояния STATE_LOW_FREQ_BLINK, STATE_AVR_FREQ_BLINK, STATE_HIGH_FREQ_BLINK, которые соответствуют миганию светодиода с низкой частой, средней частотой и высокой частотой соответственно. Каждому состоянию соответствует своя величина задержки DELAY_TIME_LF, DELAY_TIME_AF, DELAY_TIME_HF, выполняемая в каждом проходе состояния. Одинаковый период нахождения в каждом состоянии обеспечивают определения LIMIT_CNT_LF, LIMIT_CNT_AF, LIMIT_CNT_HF, определяющие количество проходов-переключений для каждого состояния. Оператор switch обеспечивает переключение между состояниями в соответствии с текущим значением переменной curr_state. Переменная state_cnt инкрементируется в каждом проходе состояния до достижения предела переключений в состоянии. Оператор if определяет, достигнут ли предел переключений. Если предел достигнут, происходит переход в следующее состояние, счетчик проходов state_cnt обнуляется.
Под отладкой теперь, кроме уже описанного выше, можно в View->Watch посмотреть значение переменных curr_state и state_cnt.
Четвертый пример посвящен работе с восьмиразрядным таймером (TIMER0) микрокотроллера. В данном примере в качестве второго светодиода используется один из сегментов семисегментного индикатора, подключенный к 6-му выводу порта D.
В рассматриваемом примере в функции main() после настройки светодиодов происходит настройка нулевого таймера, которая состоит в установке битов в управляющем регистре TCCRO. Т.к. мы будем использовать режим работы таймера Normal, то биты WGM00 и WGM01 должны равняться нулю. Начальное значение и так равно нулю, поэтому просто их не трогаем. Биты CS00, СS01, СS02 устанавливаем так чтобы получить максимальный делитель (1024) частоты тактирования 16 МГц.
После деления получим частоту работы таймера 15625 Гц, что соответствует тику в 64 мкс. Наиболее близкий к 10 мс мы получим, если умножим тик на 156 (156 * 0,000064 c = 0,009984 c = 10 мс). Поэтому, чтобы получить генерацию прерывания по обработке события переполнения каждые 10 мс, необходимо загрузить в счетный TCNT0 регистр 99 (255-156 = 99). Т.к. таймер начнет считать с 99, а прерывание сгенерируется при достижении 255, то мы и получим путь в 156 тиков.
Далее в примере, чтобы генерация прерываний по переполнению нулевого таймера происходила, происходит установка бита TOIE0 в регистре маскирования прерываний TIMSK. После чего делаем глобальное разрешение всех маскируемых прерываний макрофункцией _SEI().
Определения всех векторов прерываний есть в заголовочной файле iom16.h:
Логика работы программы представляет уже описанную в первом примере систему с суперциклом, где обеспечивается квазипараллельная работа основного цикла и обработчика прерывания. В основном цикле проиходит переключение шестого вывода порта D с периодом 500 мс. Основной цикл прерывается каждые 10 мс на обработку прерывания по переполнения нулевого таймера. Обработка осуществляет при помощи функции-обработчика ISR_TickTimer(), которая вызывается по этому событию. В данной функции путем инкрементрования переменной T0_tick_cnt происходит подсчет 10-ти милисекундных тиков. Когда переменная T0_tick_cnt достигает 100 (т.е. прошла одна секунда), при помощи оператора if в обработчике определяется это событие. После чего переменная T0_tick_cnt и происходит переключение вывода 7 порта D, что обеспечивает мигание второго светодиода с периодом 1000 мс.
При отладке данного примера можо поставить точку останова, как в основном цикле, так и в обработчике прерываний.
Пятый пример представляет собой по логике работы предыдущий пример, но с использованием макрофункций.
Выставки битов в управляющем регистре нулевого таймера TCCRO, при настройке, осуществляется при помощи макрофункции TIMER0_SET_CLK_DIV(x), аргумент которой x определяет насколько будет делиться частота тактирования и выбирается из набора определений F_CPU_DIV_1, F_CPU_DIV_8, F_CPU_DIV_64, F_CPU_DIV_256, F_CPU_DIV_1024. Запись начального значения в регистре счета TCNT0 осуществляется при помощи макрофункции TIMER0_SET_CNT(x), где аргумент x – это собственно начальное значение (в нашем случае TCNT0_VALUE = 99). Установка бита TOIE0 в регистре маскирования прерываний TIMSK проиходит при помощи макрофункции TIMER0_OVF_INT_ON().
Все примеры в виде проекта IAR можно скачать здесь.
Заключение
Если этот пост вызовет интерес, то в следующей части рассмотрим примеры работы со встроенным АЦП, семисегментниками. После чего соберем все рассмотренные примеры в программный проект цифрового термометра.
Дополнение
Если есть потребность получить hex-файл программы написанной в IAR, то сделать это совсем не сложно. Достаточно в настройках проекта на вкладке Linker->Output и на вкладке Linker->Extra Output произвести настройки, как на скриншотах ниже. После этого, если произвести пересборку проекта нажав Project->Rebuild All в подпапке [название проекта]\Debug\Exe можно будет найти hex-файл. Который можно будет зашить программатором AVR910, клоном STK500 или любым другим доступным программатором.
Источник