Это старая версия документа!
Отладка программ на Обероне при использовании компилятора O7 осуществляется главным образом через обработку аварийных остановок (TRAP) с помощью модуля MicroARMv{6,7}MTraps. При срабатывании аварийной остановки, микроконтроллер перезагружается, переменная этого модуля trapFlag становится TRUE, а информация о причине перезагрузки доступна в переменной trap.
Так во время разработки и отладки программы удобным является следующий прием:
MODULE MyProgram;
IMPORT Traps := MicroARMv7MTraps;
PROCEDURE Init;
BEGIN
...
IF Traps.trapFlag THEN Traps.ClearTrapFlag; ... handle Traps.trap ... END
...
END Init;
BEGIN
Init;
MainLoop
END MyProgram.
Аварийные остановки происходят по ряду причин.
Программные причины (Oberon):
Аппаратные причины (ARMv{6,7}-M):
Аварийные остановки сообщают program counter pc и положение в исходном тексте модуля pos, поэтому, чтобы узнать место в программе, в котором произошло исключение, надо:
Для микроконтроллеров NXP достаточно pc поделить на 2 (т. к. команды Thumb 2-байтные). Потом сравнить полученное число со списком вывода команды O7ARMv{6,7}MLinker.Link. Ошибка произошла в том модуле, где самое близкое из чисел меньших pc.
Для микроконтроллеров STM32 надо предварительно из pc вычесть 8000000H, так как программа в ROM начинается с этого положения.
ASSERT в готовых модулях служит для проверки предусловия или постусловия в рамках контрактного программирования.
SYSTEM.GET(SYSTEM.REG(SP), stkPos); (* SP = 13 *) ASSERT(stkPos > MicroKernel0.heapLim);
Этот код проверит, не начал ли стек «заползать» на кучу. И если такая ошибка происходит, произойдет аварийная остановка.
Чтобы узнать больше про особенности работы с памятью читайте эту заметку.
Чтобы осуществлять отладку микроконтроллера необходимо обмениваться сообщениями. Более подробно организация обмена описана в заметке Обмен сообщениями с контроллером.
MODULE MobxAssert; IMPORT SYSTEM, ARMv7M := MicroARMv7M, Traps := MicroARMv7MTraps, Sys := MicroSTM32F103x8System, Pins := MicroSTM32F10xPinCfg, TPs := MicroSTM32F1TPorts; CONST idInit = 0CBX; idTrap = 0DEX; VAR p0: TPs.Port; PROCEDURE Receive (id: CHAR; a: ARRAY OF CHAR; len: INTEGER); BEGIN ASSERT(FALSE) END Receive; PROCEDURE MainLoop; BEGIN REPEAT TPs.Receive(p0); ARMv7M.WFI UNTIL FALSE END MainLoop; PROCEDURE Init; VAR ok: BOOLEAN; par: TPs.InitPar; BEGIN par.n := TPs.USART1; par.RXPinPort := Pins.A; par.RXPinN := 10; par.TXPinPort := Pins.A; par.TXPinN := 9; par.UCLK := Sys.PCLK2; par.baud := 19200; par.parity := TPs.parityNone; par.receive := Receive; par.version2 := 2; TPs.Init(p0, par); TPs.Send(p0, idInit, 0, 0, ok); (* required for "heating" UART *) IF Traps.trapFlag THEN Traps.ClearTrapFlag; TPs.Send(p0, idTrap, SYSTEM.ADR(Traps.trap), 9, ok) END END Init; BEGIN Init; MainLoop END MobxAssert.
Собирать командой:
^Q O7ARMv7MLinker.Link STM32F103C8 MobxAssert
В журнал будет выведена информация о модулях.
linking MobxAssert Ok MicroARMv7M 256 MicroKernel0 301 MicroARMv7MTraps 987 MicroSTM32F10xxD 1283 MicroSTM32F103x8System 1286 MicroSTM32F10xPinCfg 1535 MicroSTM32F1TPorts 1662 MobxAssert 3146 ROM: 6816 B; RAM: 748 B
Программа для Блэкбокса, которая покажет отладочную информацию
MODULE MobxDebug;
IMPORT SYSTEM, TP := MicroTPorts, Log := StdLog;
TYPE
MessagesHandler = POINTER TO RECORD (TP.MessagesHandler) END;
CONST
id = 20X;
idTrap = 0DEX;
VAR
p: TP.Port;
mh: MessagesHandler;
PROCEDURE Send*(t: INTEGER);
VAR a: ARRAY 4 OF SHORTCHAR;
BEGIN
SYSTEM.PUT(SYSTEM.ADR(a), t);
TP.SendMessage(p, id, a, 4)
END Send;
PROCEDURE (h: MessagesHandler) MessageReceived (id: SHORTCHAR; IN a: ARRAY OF SHORTCHAR; len: INTEGER);
VAR pc, pos, code: INTEGER;
BEGIN
IF id = idTrap THEN
SYSTEM.GET(SYSTEM.ADR (a), pc);
SYSTEM.GET(SYSTEM.ADR (a)+4, pos);
code := ORD(a[8]);
IF pc >= 8000000H THEN pc := pc - 8000000H END;
Log.String("pc: "); Log.Int(pc DIV 2); Log.Ln;
Log.String("pos: "); Log.Int(pos); Log.Ln;
Log.String("code: "); Log.Int(code); Log.Ln;
END
END MessageReceived;
PROCEDURE Open*(port: ARRAY OF CHAR);
BEGIN
IF p = NIL THEN
p := TP.NewPort(port, 19200, {}, mh);
Log.Bool(p # NIL); Log.Ln;
END
END Open;
PROCEDURE Close*;
BEGIN
TP.Close(p)
END Close;
BEGIN
NEW(mh)
CLOSE
Close
END MobxDebug.
В Windows запускается командой:
^Q "MobxDebug.Open('COM1')"
Для Linux будет следующая команда:
^Q "MobxDebug.Open('/dev/ttyUSB0')"
Любая отправка МК команды, вызовет аварийную остановку из-за ASSERT(FALSE) в модуле прошивки.
^Q "MobxDebug.Send(0)"
В журнал Блэкбокса будет выведена информация:
pc: 3155 pos: 337 code: 7
Program Counter (pc) равен 3155, что больше 3146 (см. информацию при сборке), а значит аварийная остановка произошла в модуле MobxAssert в положении 337 с кодом 7.
Чтобы перейти к положению ошибки в модуле, удобно воспользоваться инструментом O7→Position.
MODULE MobxSTM32F4WWDG;
IMPORT SYSTEM,
ARMv7M := MicroARMv7M,
Traps := MicroARMv7MTraps,
MCU := MicroSTM32F405;
CONST
SP = 13;
SR0 = 0; SR1 = 1; SR2 = 2; SR3 = 3; SR12 = 4; SLR = 5; SPC = 6;
Int = MCU.WWDGInt;
int = Int MOD 32;
ISER = ARMv7M.NVICISER0 + (Int DIV 32) * 4;
ICER = ARMv7M.NVICICER0 + (Int DIV 32) * 4;
IPR = ARMv7M.NVICIPR0 + Int;
PROCEDURE* InterruptsHandler;
CONST nLVars = 1;
VAR pc: INTEGER;
BEGIN
(* compiler-dependent *)
SYSTEM.GET(SYSTEM.REG(SP) + (SPC + nLVars + 1 + 8) * 4, pc);
Traps.trapHandler(0EX (* 14 *), pc, 0)
END InterruptsHandler;
PROCEDURE Update*;
BEGIN
SYSTEM.PUT(MCU.WWDGCR, {0..7})
END Update;
PROCEDURE Init*;
CONST
(* RCCAPB1ENR bits: *)
WWDGEN = 11;
(* WWDGCR bits: *)
WDGA = 7;
(* WWDGCFR bits: *)
EWI = 9;
(* WWDGSR bits: *)
EWIF = 0;
VAR x: SET;
i: INTEGER;
BEGIN
SYSTEM.PUT(ICER, {int}); ARMv7M.ISB;
(* timeout = 4096 * 8 / PCLK1 * 64 = 52.4288 ms @ PCLK1 = 40 MHz *)
SYSTEM.GET(MCU.RCCAPB1ENR, x);
SYSTEM.PUT(MCU.RCCAPB1ENR, x + {WWDGEN}); ARMv7M.DSB;
SYSTEM.PUT(MCU.WWDGCFR, {0..8} + {EWI}); (* W := 127; WDGTB := 3; !EWI *)
SYSTEM.PUT(Traps.vectorsOrg + (ARMv7M.ExternalInterrupt0 + Int) * 4, InterruptsHandler);
(* decrease priority of all interrupts *)
i := 0;
WHILE i < 60 DO
SYSTEM.PUT(ARMv7M.NVICIPR0 + i, 80808080H); (* set priorities to 8 *)
INC(i)
END;
(* increase priority of WWDG interrupts *)
SYSTEM.PUT(IPR, 0X); (* set priority to 0 *)
SYSTEM.GET(MCU.WWDGSR, x);
SYSTEM.PUT(MCU.WWDGSR, x - {EWIF});
SYSTEM.PUT(MCU.WWDGCR, {0..6} + {WDGA}); (* enable *)
SYSTEM.PUT(ISER, {int})
END Init;
END MobxSTM32F4WWDG.
Нужно периодически вызывать процедуру Update.
При «зависании» микроконтроллера сработает аварийная остановка с кодом 14. И можно определить место в программе, где это произошло (Trap.pc)
«Независимый» сторожевой таймер (но без возможности определения места остановки): см. MicroSTM32FxIWDG
Авторы заметки: И. А. Денисов, А. В. Ширяев