Муфты электромонтажные от производителя Fucon
РадиоЛоцман - Все об электронике

Отладка программ МК AVR и осциллограф. Часть 2

Atmel ATmega48 ATmega1284P

Журнал РАДИОЛОЦМАН, ноябрь 2014

В. И. Иволгин, г. Тамбов

Часть 1.

Выбираем схему BMS для заряда литий-железофосфатных (LiFePO4) аккумуляторов

Как известно [2], микроконтроллеры семейства AVR используют линейную организацию статической памяти МК. В ее начальной части располагается регистровая память, а в оставшейся – внутренняя SRAM или, далее – SRAM. В свою очередь, регистровая память делится на область регистров общего назначения (РОН) и регистров ввода/вывода (РВВ), причем пространство РВВ может делиться на основную часть и дополнительную. Эти разделы регистровой памяти сравнительно невелики. Так РОН включает в себя всего 32 регистра, довольно сложным образом связанных друг с другом. Основная часть РВВ – 64 байта, представлена служебными регистрами и регистрами, определяющими настройку периферии. В «продвинутых» МК AVR, где возможно существование дополнительной части РВВ, предназначенной для настройки периферии, ее объем не может превышать 160 байт. Таким образом, объем этого вида памяти ограничен 256 регистрами, для адресации которых достаточно однобайтового адреса. Оставшаяся же часть статической памяти предназначена для хранения произвольной двоичной информации, в том числе и переменных, и адресуется через двухбайтовые адреса.

Таким образом, каждый выделенный раздел в силу его специфики и для достижения максимального быстродействия требует своего метода доступа к информации, что проявляется в различии команд обращения к байту в МК AVR. По этой причине команда обращения к переменной в конкретной программе должна соответствовать месту ее размещения в статической памяти. В рассмотренных выше примерах, критическим пунктом в этом смысле является строка 4 программ Contr1, Contr2, в которой выбранная переменная передается в подпрограмму для ее вывода на экран.

Рассмотрим пример одновременного вывода четырех переменных из различных разделов статической памяти, который иллюстрирует это требование. Пусть первая из них, переменная Flag, как и ранее, будет принадлежать к РОН, в качестве второй выберем служебный регистр PORTB основного раздела РВВ, хранящего информацию о логических уровнях на выводах порта В. Следующая переменная – TWDR, относящаяся к дополнительному разделу РВВ микроконтроллера ATmega 48, является регистром данных двухпроводного интерфейса TWI этого МК, а последняя переменная MaxF будет из области SRAM. Следует отметить, что если символические имена для РОН и SRAM присваиваются непосредственно в программе, то имена регистров РВВ жестко фиксированы в спецификациях соответствующих МК и не допускают их малейших изменений.

Выберем в качестве основы для новой подпрограммы уже проверенную Contr2, которую надо будет дополнить так, чтобы считывание переменных в r28 выполнялось командами, предназначенными для работы в соответствующих разделах статической памяти. После них, как и ранее, надо расположить задержку для стабилизации изображения. Пусть она будет той же длительности, хотя при необходимости ее можно и изменить. Тогда программа для вывода четырех байт будет выглядеть так:

                   Contr3:			;=== вывод 4 байтов на осциллограф
cli ; запрет прерываний
wdr ; сброс таймера
mov r28, Flag ; сохранение в r28 перем. Flag из РОН
all OutByte ; вывести байт
in r28, PORTB ; сохранение в r28 перем. POPRB из РВВ
call OutByte ; вывести байт
lds r28, TWDR ; сохранение в r28 перем. TWDR из доп. РВВ
call OutByte ; вывести байт
ldi ZH,High(MaxF) ; определение адреса
ldi ZL,Low (MaxF) ; переменной MaxF
ld r28, Z ; сохранение в r28 перем. MaxF из SRAM
call OutByte ; вывести байт
call Delay_Contr ; задержка для стабилизации изображения
jmp Contr3 ; вернуться на начало Contr3 и повторить

При анализе текста программы следует обратить особое внимание на строки 10-12, где представлен вариант обращения к переменной MaxF на основе ее двухбайтового указателя Z. Здесь в строках 10, 11 по имени устанавливается двухбайтовый адрес переменной, а в 12 осуществляется доступ к ней. Нужно отметить, что этот метод обладает бóльшей общностью по сравнению с представленными здесь же локальными методами работы с регистровой памятью (строки 4, 6 и 8). Это позволяет применять его без ограничений и для работы с регистровой памятью, хотя здесь он демонстрируется только применительно к SRAM. Один из вариантов работы с регистровой памятью через двухбайтовые указатели будет рассмотрен позднее.

Нужно также учесть, что если вывод, используемый для связи с осциллографом, принадлежит порту В, то к информации об уровне на нем доверять не следует, поскольку его состояние уже изменено в соответствии с логикой работы программы Contr3 при выводе (строки 5, 6). Но к остальным выводам порта В это замечание не относится.

По изображению, полученному в результате работы программы Contr3 и приведенному на Рисунке 2, можно сделать вывод также о вполне удовлетворительной информативности предлагаемого способа вывода байтовой информации в виде пакета из 4 байт. При необходимости это число, в зависимости от возможностей экрана осциллографа, конечно, можно увеличить (или уменьшить), доработав соответствующим образом программу. Это изображение было получено на осциллографе С1-73 с размером экрана всего лишь 40 × 60 мм, что, тем не менее, позволяет вполне уверенно считывать до 4 байтов одновременно, хотя при трех байтах будет все же удобнее.

Отладка программ МК AVR и осциллограф
Рисунок. 2. Изображение байтов 10110101, 11101101, 11111111, 00101110.

И несколько слов о вызове этой программы. На первый взгляд, ее возможности не так уж и велики – вызов в одной точке довольно ограниченного числа переменных с прекращением работы тестируемой программы. Но важно, что в этом режиме МК способен поддерживать вывод указанных переменных вплоть до его перезагрузки. И эта способность сохранения переменных при переходе программы в аварийный режим может оказаться полезной в тех случаях, когда сбои в программе являются нерегулярными, и нет возможности длительное время вести наблюдение за ее нормальной работой. Правда, здесь очень важно вовремя, в момент возникновения аварийной ситуации, вызвать эту программу. До сих пор рассматривался лишь один способ инициализации вывода – достижение тестируемой программой контрольной точки, в которой, как подозревалось, программа возможно перейдет в аварийный режим. Но такое место в сложных случаях назвать практически невозможно. И здесь более уместно использование ее вызова по какому-либо условию. Обычно для этой цели может служить какая-либо переменная, принимающая в этот момент критическое значение. Пусть, например, это будет Alarm1, а ее критическое значение в указанном смысле равно 3. Тогда целесообразно в подозрительных местах тестируемой программы расставить, например, такие строчки кода:

                      	ldi	Flag, 6		; установить номер данной контрольной точки
cpi Alarm1, 3 ; сравнить значение переменной и константы
brlo PC+1 ; если авария,
call Contr3 ; то перейти на вывод

В этом случае срабатывание вывода произойдет в одной из известных контрольных точек (строка 1) и почти по известному событию (строки 2, 3). Разумеется, использованное условие может быть изменено в соответствии с обстоятельствами. Следует отметить, что эти рассуждения, хотя и в упрощенном виде, применимы и к программе Contr2.

Подводя итог обсуждения Contr3, можно сказать, что рассмотренная программа содержит набор необходимых сведений, позволяющих получить доступ к любой части статической памяти МК и вывода необходимой информации в виде отдельных байт либо их пакетов на экран осциллографа для последующего анализа. И таким образом, с этого момента уже можно приступать к отладке основной программы МК, используя полученные результаты и расставляя в нужных местах строчки кода вида Contr3. Но… Смущает то, что для получения очередных двух-трех байт надо останавливать программу, редактировать и запускать ее снова! Хорошо, если проблема будет решена после нескольких таких попыток, а если потребуется выводить и анализировать большие объемы информации и из разных точек тестируемой программы? Как увеличить «производительность» этого процесса?

Как уже ранее отмечалось, в случае использования в качестве регистрирующего устройства аналогового осциллографа, одной из проблем формирования изображения является его регенерация. По этой причине ресурсы МК в точке контроля приходится отвлекать на решение этой задачи. И самый простой, и доступный вариант, не вносящий непредсказуемых изменений в переменные тестируемой программы – ее остановка и поддержка только процедуры вывода. Собственно, из-за его простоты он и был использован на начальном этапе. Но, разумеется, корректные возможности запуска тестируемой программы после остановки, вызванной считыванием результатов, можно создать. Важно только организовать этот процесс так, чтобы было обеспечено время для считывания контрольной информации и были восстановлены все переменные, которые при выводе были изменены.

Программа, приведенная ниже, как раз и соответствует этому сценарию. Для этого в теле программы размещен цикл, который позволяет ей выполнять вывод изображения с регенерацией только в соответствии с установленным временем, например, 5 секунд. После его окончания производится восстановление значений переменных, использованных подпрограммой, и далее управление передается тестируемой программе для продолжения ее работы. Ее текст приводится ниже.

                  Contr4:				;=== вывод 3 байтов с продолжением теста
cli ; запрет прерываний
push r28 ; сохранение
push r29 ; переменных
push r30 ; в стеке
push r31 ; ...
mov r31, SREG ; сохранение состояния
push r31 ; SREG
ldi r31, $40 ; установка времени удержания
ser r30 ; пакета байтов
L1_Contr4: ;= удержание пакета на 5 с
wdr ; сброс таймера
mov r28, Flag ; сохранение в r28 переменной Flag
call OutByte ; вывести байт
mov r28, Page ; сохранение в r28 переменной Page
call OutByte ; вывести байт
mov r28, NFile ; сохранение в r28 переменной NFile
call OutByte ; вывести байт
call Delay_Contr ; формирование «хвоста» пакета
sbiw r30, 1 ; посчитать: 5 секунд уже закончились?
brcc L1_Contr4 ; если 5 с истекли, то цикл завершить
pop r31 ; восстановление состояния
mov SREG, r31 ; SREG
pop r31 ; восстановление
pop r30 ; переменных
pop r29 ; ...
pop r28 ; ...
sei ; разрешить прерывания
; jmp Contr4 ; возврат на начало
ret ; выход из п/п

Некоторые пояснения относительно содержания программы и ее возможностей. Как следует из текста, в ней в основной части используются 4 старших регистра общего назначения, которые при работе неизбежно изменят свои значения. Это замечание относится также и к регистру SREG, используемому при организации ветвления тестируемой программы. Для того, чтобы после завершения вывода РОН и последующего продолжения основной программы МК ее работа не была нарушена, в этой программе приняты меры по защите их значений, полученных до остановки – перед использованием они предварительно сохраняются в стеке, а в конце восстанавливаются. Нужно также иметь в виду, что вывод этой программой регистров r28-r31 не имеет особого смысла, поскольку они будут содержать значения, приобретенные уже в процессе выполнения Contr4, а не те, которые они имели на момент остановки в заданной точке основной программы МК. Тем не менее, в продолжение работы основной тестируемой программы МК микроконтроллер войдет с нужным набором значений и в этих регистрах.

Теперь о деталях. По сравнению с предыдущей программой ее объем заметно вырос. Причина – увеличения числа используемых регистров и процедур восстановления их значений. Эти дополнительные регистры r30 и r31 использованы для формирования пятисекундной задержки, ее величина регулируется значением размещенной в них константы. В тексте она выделена красным цветом и располагается в его средней части. Непосредственно значение задержки формируется путем подсчета числа циклов вывода байта. Предпоследняя строка, которая предполагает, как и ранее, возврат на начало и остановку работы тестируемой программы, сохранена как комментарий.

Следует отметить, что в изложенной редакции характер действия программы радикально изменился, что зафиксировано в ее заключительной команде – ret. Теперь по истечении 5…20 секунд после ее запуска и кратковременного вывода байтов на осциллограф она устраняет свое действие на тестируемую программу! Это важная ее особенность, поскольку теперь за счет ссылок на Contr4 в различных критических точках тестируемой программы можно отслеживать состояние трех, как в данном случае, переменных, на протяжении всего рабочего цикла программы. Важно подчеркнуть – рабочего, хотя и заторможенного кратковременными остановками для вывода информации. Но проблема ускорения отладки остается по-прежнему актуальной. Ведь и в этом случае разговор идет лишь пока о нескольких переменных, преимущественно расположенных в регистровой области. Хотя в реальности их подавляющая часть размещается в SRAM.

В связи с этим рассмотрим теперь процедуру обращения к SRAM и причины, по которым возникает необходимость ее использования для хранения переменных.

Как уже отмечалось, регистровая память довольно жестко структурирована и подавляющую ее часть составляют различные служебные регистры и регистры, с помощью которых устанавливаются параметры периферийных устройств. Поэтому в целях организации работы программы могут быть использованы только 32 первых регистра. На первый взгляд, это большой ресурс, но если разобраться, то на самом деле и не очень. А если исходить из практики программирования, то этих регистров всегда катастрофически не хватает – очень они уж удобны. И единственная возможность покрыть их дефицит – создавать и хранить переменные в SRAM.

Но работа со SRAM, по сравнению с регистровой памятью, отягощена тем, что обращение к ней ведется через двухбайтовые указатели, которые делают программы более громоздкими. И главная задача при байтовом выводе – сократить необходимость их применения в таком стандартном виде, как это было сделано, например, в программе Contr3 (строки 10-12).

Принципиально отказаться от двухбайтовых указателей нельзя, поскольку этого требует размер их адреса, но сократить объем манипуляций с ним можно при соблюдении простых условий. Это будет тем более целесообразно в связи с тем, что в ассемблере для МК семейства AVR уже предусмотрен механизм автоинкрементации указателей при обращении к последовательно расположенным байтам памяти.

Обычно переменные в области SRAM размещаются последовательно без пропусков одна за другой. Причем, в зависимости от сложности тестируемой программы, их число может быть довольно значительным. И лучше предусмотреть их вывод в виде отдельных блоков путем задания его начальной точки и общего числа байтов. Адрес начального байта такого блока надо будет задать через имя соответствующей переменной. Все последующие байты будут выводиться по числовым значения адресов, вычисляемых при автоинкрементировании во время выполнения процедуры переноса значения байта из памяти в регистр r28.

Порядок размещения переменных в программе формируется обычно по мере возникновения в них надобности при написании программы. Естественно, это приводит к некоторой хаотичности их размещения в памяти, хотя при отладке чаще всего требуется анализ логически связанных групп переменных. Поэтому для повышения эффективности процесса отладки целесообразно проводить структуризацию их размещения. Допустим, в целях наглядности, что все N последовательно расположенных байтовых переменных имеют имена var1, var2, var3, …varN, и требуется выполнить вывод на осциллограф блока последовательно расположенных переменных, начиная с var7 в количестве 12 штук пакетами по 3 байта. Предусмотрим также размещение в начале пакета байта, который определяет номер следующей за ним переменной. Это приведет к необходимости увеличения пакета до 4 байт, но позволит различать их друг от друга при последовательном выводе. Как и ранее, нужно будет соблюсти все предосторожности для сохранения возможности запуска тестируемой программы после ее остановки для вывода информации. Текст такой программы приведен ниже.

              Contr5:				;= вывод переменных из SRAM
push r25 ; номер первого байта в пакете
push r26 ; время удержания пакета
push r27 ; ....
push r28 ; регистры для OutByte
push r29 ; ...
in r29, SREG ; сохранение состояния
push r29 ; SREG
ldi r25, 1 ; номер первого байта в пакете
Contin: ; вывод блока байтов
ldi r27, 20 ; время удержания очередного пакета
Pack: ; формирование пакета
wdr ; сброс таймера
mov r28, r25 ; номер первого байта в пакете
rcall OutByte ; вывести на экран
ld r28, Z+ ; считать первый байт пакета
rcall OutByte ; и вывести его на экран
ld r28, Z+ ; считать второй байт пакета
rcall OutByte ; и вывести его на экран
ld r28, Z+ ; считать третий байт пакета
rcall OutByte ; и вывести его на экран
rcall Delay_Contr ; задержка для стабилизации изображения
sbiw Z, 3 ; вернуть Z к началу пакета
sbiw r26,1 ; посчитать - удержание пакета закончились?
brcc Pack ; если "да", то
adiw Z, 3 ; перейти к следующему пакету и
subi r25, 3 ; задать новый номер начала пакета и
cp r25, r24 ; сравнить с числом байт в блоке:
brlo Contin ; если >, то завершить программу
pop r29 ; восстановить
out SREG, r29 ; SREG и все
pop r29 ; использованные
pop r28 ; регистры
pop r27 ; ...
pop r26 ; ...
pop r25 ; ...
; rjmp Contr5 ; к началу для повторения
ret ; завершить программу

В связи с тем, что для удобства пользования Contr5 предусмотрено внешнее задание адреса начала блока и его размера, вызов этой программы производится следующим образом:

             Contr5A:
cli ; запрет прерываний
push ZH ; сохранение указателя Z
push ZL ; ...
push r24 ; сохранение r24
ldi ZH, High(Var7) ; адрес начального байта блока
ldi ZL, Low (Var7) ; (r30,r31)
; adiw Z, $20 ; адрес регистра РВВ по его имени
ldi r24, 9 ; размер блока в байтах
rcall Contr5 ; вызов программы
pop r24 ; восстановление регистра r24
pop ZL ; восстановление указателя Z
pop ZH ; ...
sei ; разрешение прерываний
ret ; передача управления тестирующей программе

Как следует из приведенных текстов Contr5 и Contr5A, здесь, в отличие от большинства предыдущих программ, обращение к переменным производится через двухбайтовый указатель Z, который включает в себя r30 и r31 – младший и старший байты адреса, соответственно. Причем его начальное значение, которое является адресом переменной начала блока, задается в Contr5A, что позволяет не подвергать изменениям более громоздкую Contr5. По тем же соображениям этим способом производится и задание размера блока байтов. Изменяемые величины в последней программе выделены красным цветом. Для повышения удобства программу Contr5A лучше использовать в виде макроса.

Программу Contr5 можно адаптировать к решению конкретных задач путем изменения рабочих параметров подпрограммы, так же выделенных в тексте Contr5 красным цветом (размещены в средней части текста). Так первая цифра задает время между последующими сменами наборов байтов (пакетов), а последние три определяют число одновременно выводимых однобайтовых переменных. Для изменения первого параметра – длительности задержки – достаточно в месте его размещения провести лишь необходимую цифровую замену. А вот для увеличения числа байтов в пакете, например, с 3 до 4, необходимо не только заменить три цифры 3 на 4, но и в теле подпрограммы добавить две строки, отвечающих за считывание и вывод очередного байта. Кроме того, для выбора режима использования этой программы, нужно выбрать одну из последних строк. Так при ее окончании, предложенном в программе, произойдет передача управления основной (тестируемой) программе, а при переносе символа комментирования с предпоследней строки на последнюю будет осуществляться бесконечный вывод полученной информации.

Хотя Contr5 заявлена для работы с переменными в области SRAM, она может использоваться для этих же целей и в регистровой памяти, поскольку адресное пространство статической памяти МК является общим для всех ее разделов и доступно через двухбайтовые указатели. Но есть и некоторые нюансы. Сначала о разделе РОН. Здесь адрес начала блока может задаваться через имя переменной, например, Flag, которая соответствует, допустим, регистру r18. Использовать здесь имя r18 нельзя, хотя во многих других ситуациях это допускалось. Но зато можно осуществить ввод, задав числовое значение адреса этого регистра. Это несложно, поскольку имена регистров общего назначения фактически содержит его в самом названии, и для упомянутого r18 эта величина равна 18. Таким образом, начало блока переменных в программе Contr5A можно задать двумя способами: либо вместо переменной Var7 в строках 6, 7 записать имя переменной Flag, либо число 18. Оба эти способа применимы и для основного и для дополнительного разделов РВВ. Но эти значения, как имена, так и адреса, придется искать в спецификации (или в AvrStudio). И здесь есть такая тонкость. Для каждого из регистров, наряду с именем, даются два адреса, причем один из них в скобках. Нужно пользоваться только значением в скобках, которое задано для всех регистров. Что касается использования имени, то адрес, полученный с его применением через функции в строках 6, 7, оказывается равным адресу без скобок и меньше реального адреса на $20, что является следствием наличия двух систем отсчета адресов регистров. С другой стороны, поскольку в программах при обращении к РВВ используются только имена, то было бы правильно при контроле тоже опираться на них, а не на адреса. Для получения такой возможности нужно будет снять знак комментария со строки 8 программы Contr5A. Но следует иметь в виду, что тогда программа будет корректно работать только для этой ситуации, т. е. только при использовании имен для регистров ввода/вывода! Для всех других случаев – и для SRAM, и для РОН, программу применять будет нельзя.

Нужно также иметь в виду, что, как и ранее, вывод используемых регистров r24r31 не имеет смысла, так как их значения уже будут изменены при работе программы Contr5. Но возможность полноценного продолжения работы тестируемой программы после завершения вывода, тем не менее, будет обеспечена восстановлением их значений в конце этой операции. При крайней необходимости контроля названных регистров можно попробовать произвести дополнительный запуск одной из программ, предложенных ранее, которые не используют искомый регистр. Если речь пойдет о регистрах r28, r29, то в этом случае проблему можно будет решить только использованием в OutByte и Delay_Contr двух каких-либо других регистров из r16r31.

Литература

  1. «Отладка систем на небольших микроконтроллерах с помощью осциллографа». РадиоЛоцман, 2014, июль, стр. 61.
  2. Евстифеев А.В. Микроконтроллеры AVR семейства Tiny и Mega фирмы Atmel М., Додэка-ХХ1. 2004
64 предложений от 34 поставщиков
Микроконтроллер AVR 4K-Флэш-память/512-ОЗУ/256-ЭППЗУ+8x10 АЦП, электропитание 2,7...5,5В
ЗУМ-СМД
Россия
ATMEGA48PA-AU
Atmel
29 ₽
ATMEGA48PB-AU
Atmel
29 ₽
ChipWorker
Весь мир
ATMEGA48-20MU
Microchip
67 ₽
Элитан
Россия
ATMEGA48PA-AU
Microchip
148 ₽
Электронные компоненты. Бесплатная доставка по России
Для комментирования материалов с сайта и получения полного доступа к нашему форуму Вам необходимо зарегистрироваться.
Имя
Фрагменты обсуждения:Полный вариант обсуждения »
  • Статья из серии "Когда обезьяне нечем заняться...". Придумывать такой геморрой, да еще с зависанием программы на время вывода отладочного "сообщения" - это надо быть мазохистом. Особенно, учитывая что USART есть почти в каждом МК. А где нет - там настолько мало ресурсов, что можно обойтись довольно неплохим симулятором в AVR Studio, тем более автор так вольно запрещает прерывания, что видно на Real time он крепко забил. И никакого взаимодействия с другими устройствами в его схеме нет. Стоит ли так сильно напрягаться?
  • Можно еще второй МК прицепить с выводом на ПК или ЛСД...
  • МК AVR вообще за что-то путное не считаю. Школота какая-то. Все остальное катит.
  • Несколько лет назад МК AVR были очень привлекательны по возможностям и цене. Добавляла привлекательности удобная среда разработки. Сейчас, с появлением МК STM32 и разнообразных средств разработки под него, согласен, МК AVR сильно устарел.