.. index:: delay,inlining,machine code:inline,assembly code,gdb, swd,openocd,arm-none-eabi-as .. _delay: Delay ===== Accurate Blocking Delay ----------------------- This blocking delay is created using arm-none-eabi-as to produce the machine code which is then INLINED into a Forth Word. .. note:: While the delay is being used, the CPU is not available to do anything else. To see the same thing done using Interactive Assembly see :ref:`Interactive Assembly Blocking Delay` Why do it this way ? -------------------- \The reason that the millisecond blocking delay is done this way rather than in pure Forth is because it can be done in about half the binary code size compared to a pure Forth Word. Another reason is that sometimes maximum speed is required and this means using minimal instructions in the Forth Word. The delay example is a great way to demonstrate how to achieve this. .. note:: The :ref:`THUMB Instruction Set` is used here Using Pure Forth Instead ------------------------ The pure Forth way is certainly much faster to write and debug compared to the alternate methods. The "delay" Word below uses the common "do-loop". :: : delay 0 do loop ; Disassembly ^^^^^^^^^^^ :: see delay 2000068A: B500 push { lr } 2000068C: B430 push { r4 r5 } 2000068E: 2400 movs r4 #0 20000690: 0035 lsls r5 r6 #0 20000692: CF40 ldmia r7 { r6 } 20000694: 3401 adds r4 #1 20000696: 42AC cmp r4 r5 20000698: D1FC bne 20000694 2000069A: BC30 pop { r4 r5 } 2000069C: BD00 pop { pc } Bytes: 20 ok. Delay Measurments ----------------- Timing measurements were performed using the on chip Systick counter set to a count of 0.1ms using a Interrupt. =========== ======== ======== =================================================== Input Delay Period Comments =========== ======== ======== =================================================== 10000000 6.5359 seconds uncalibrated =========== ======== ======== =================================================== The Method below produces the following delays: ----------------------------------------------- 1. 105.2nS @ 48Mhz 2. 631.2nS @ 8Mhz .. note:: Although very accurate due to the small loop, the accuracy will be affected by many factors such as the cache, interrupts and the stability of the clock. All blocking loop delays are affected by these things, but they are fine for general purpose delays of a known time period such a providing a minimum pulse width for a LCD display, blinking a LED and so on. If greater accuracy is required, consider using a Timer Peripheral instead. In this example the delay Word is named "105ns" for use on a 48MHz system. .. _delay.code: Delay Code ---------- :: : 105ns ( u -- ) \ 105.2ns delay @ 48mhz clock [ $3e01 h, \ subs r6, #1 $46c0 h, \ nop $d1fc h, \ bne.n 8 ] drop ; Disassembly ^^^^^^^^^^^ :: see 105ns 20000688: B500 push { lr } 2000068A: 3E01 subs r6 #1 2000068C: 46C0 mov r8 r8 2000068E: D1FC bne 2000068A 20000690: CF40 ldmia r7 { r6 } 20000692: BD00 pop { pc } Bytes: 12 ok. Delay Measurments ----------------- Timing measurements were performed using the on chip Systick counter set to a count of 0.1ms using a Interrupt. This delay should work for a minium delay of 105.2 nS to a maximum delay of 451.86 seconds (7.53 Minutes) =========== ======== ======== =================================================== Input Delay Period Comments =========== ======== ======== =================================================== 1 105.2 ns CALCULATED (too small to measure with systick) 1000 0.1052 ms CALCULATED (too small to measure with systick) 1000000 0.1052 seconds 10000000 1.0521 seconds $00FFFFFF 1.7651 seconds $0FFFFFFF 28.2415 seconds $FFFFFFFF 451.8640 seconds (7.531067 minutes) =========== ======== ======== =================================================== Millisecond Delay ----------------- The ms Word calls the 105ns Word and is scaled for 1 ms. :: : ms ( u -- ) \ for a 48 Mhz clock 9505 * 105ns ; Example usage ------------- This lights the green LED on a Discovery board (clocked at 48MHz) for exactly 1 second. :: : green-led-on-1-second ( -- ) green-on 1000 ms green-off ; How to Inline code ------------------ .. seealso:: :ref:`Machine Code` and :ref:`Interactive Assembly` Write the code in Assembler ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. image:: pics/assembly-delay-gvim.jpg Compile the code with arm-none-eabi-as ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. image:: pics/assembly-delay-makefile.jpg Test the Code in GDB ^^^^^^^^^^^^^^^^^^^^ Note: the code shown here is slightly different to the actual :ref:`"Delay Code"` above because I have inserted a fake "Top Of Stack" line to supply R6 with a delay value of 0x2 for the debugging session. When this code is used with Forth, R6 will already contain a valid number from the user program so the fake TOS isn't required. GDB talks to the actual MCU via SWD and single steps thru the program *on the actual chip*, displaying register contents and following the program flow *on the actual chip*. It's incredible to think that this facility which once used to cost tens of *thousands* of dollars is available in a MCU costing $0.56 USD and that the GNU software is Free. .. image:: pics/assembly-delay-gdb.jpg Cut and Paste the Machine Code into a Forth Word ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The paste must be edited to suit Forth as seen in the :ref:`"Delay Code"` above. .. image:: pics/assembly-delay.list.jpg