Hi,
Well, I finally assembled the AXE133 OLED board that I couldn't resist in the Black Friday sale . But now there are several hardware enhancements available, such as the "backlight" dimming from nick12ab, and adding a "DS3231 for PI" Clock and Temperature module by marks (post #7). So I want to add a command (control code) to adjust the brightness, and in due course probably other features such as selectable baud rate/pin/polarity and directly updating the message text, etc..
However, there are many threads on the forum which indicate that the timing of the code is critical, to avoid characters being lost or corrupted. I don't want to write lots of new program code and then discover that it doesn't work reliably, so I analysed the timings of all the individual instructions in the critical part of the main loop in the AXE133 program. The original code is quite "fast", but I think it can be done faster.
My preference is to do all PICaxe timing estimations at the default 4 MHz clock frequency (of M2s), because then each PIC instruction cycle (and the period of "Timer 1") is exactly one microsecond. However, the standard AXE133 code runs at 16 MHz (to receive data at 2400 baud), but I will consider the equivalent baud rate at 4 MHz (600 baud) and convert up to 8 MHz/1200 baud and 32 MHz/4800 baud, etc. when required. 600 baud has a bit period of ~ 1600 us.
Although PICaxe Basic has several "high level" coding structures (IF..THEN..ELSE , DO..WHILE/UNTIL , CASE..SELECT, etc.) the base PIC (and I suspect the PICaxe interpreter) has only an elementary "Jump" (GOTO) instruction, with the other structures being converted by the Program Editor / Compiler. Also, the two paths from a "branch" instruction have different execution delays; the "fall through" is faster than the "jump" (to another part of the program), principally because the Program Counter needs to be reloaded. So my first task was to identify how the IF..THEN..ELSEIF.. commands in the basic program are converted to GOTOs (tokens).
This is not trivial because the different IF... structures behave differently! IF <true condition> THEN {GOTO} obviously invokes a jump and the <false condition> is a (faster) fall-through into the next line of the program. However, the IF <true condition> THEN <newline> structure falls through to the next line, whilst the <false condition> (to ELSE / ENDIF) uses a (slower) jump to elsewhere in the program. Of course the true/false conditions are complementary, so either path structure is possible, but it appears that (at least in the AXE133 code) the PE inverts the true/false test so that the false result causes a jump and true falls through.
So now, here is the critical part of the code in its original form and then converted to use only GOTOs, complete with some approximate execution times at 4 MHz :
The goto main represents a significant delay (before the next character can be received) but this can be reduced by rearranging the program loop :
This is a "win-win" situation because both the main loop and the notchar path are reduced by around 400 us each. Of course at the beginning of the program we need to jump into the loop at main: (or initialise b1 as a non-printing character) but this seems a small price to pay.
Note that the second (and each subsequent) ELSE in the original program adds another delay of ~ 1250 us, which pushes the path delays for codes 253 and 255 well past 3000 us. Generally it would be better for a "true" to jump out of the list, so that each test adds a rather smaller ~ 850 us to the longest path. These particular paths are not quite so time-critical as the "character" loop because the (present) commands all receive an additional parameter, so there is no need to jump back into the main loop immediately. Actually, that might be a useful strategy when adding any additional commands, even if the reception of an additional byte is not strictly needed.
A structure which is considerably faster than a series of IF...ELSEs is the ON var GOTO label0 , etc.... . The first jump (to label0 for var = 0) takes a time similar to a normal GOTO, but then each subsequent label adds only around 250 us (i.e. about 5 times faster than the original ELSEs). Furthermore, the last label can be omitted from the list and instead executed as a (faster) fall-through. But a problem is that the var list must start from zero, which is where the User Definable Characters are located. Calculating an "offset" adds a significant delay: (b1 = b1 - offset is ~ 650 us), particularly if we need to retain the original value (i.e. b2 = b1 - offset takes ~ 1000 us).
But now to the main point of this thread: What additional commands do others think might be useful in the AXE133 controller and how (or if) the command set should be restructured ? The present commands (253, 254, 255) overlay existing characters in the ROM, which thus cannot be used. They're not "essential" characters, but 255 is a solid block which at least might be useful for testing. AFAIK the character codes between 16 and 31 are not used at all, so are an ideal place to locate the command codes. But that requires a (slower) double-test (greater than and less than conditions) before a value can be definitely identified as a character (if the User-Definable Characters are included).
However, the AXE133 character map has the 8 User-Definable Characters (0 - 7) duplicated between 8 and 15, so why not locate all the command codes between 0 and 7 and use the higher group of UDCs ? Does anybody know of any character sets that (e.g.) have 16 UDCs, or located only from zero upwards? Alternatively, would it be useful to decode more character codes "on the fly" (e.g. to allow codes such as 13 <Carriage Return> and 10 <Line Feed>) to add functions such as "Newline" and "Scroll")? But that may require much more effort in checking all the worst-case timing delays.
Cheers, Alan.
Well, I finally assembled the AXE133 OLED board that I couldn't resist in the Black Friday sale . But now there are several hardware enhancements available, such as the "backlight" dimming from nick12ab, and adding a "DS3231 for PI" Clock and Temperature module by marks (post #7). So I want to add a command (control code) to adjust the brightness, and in due course probably other features such as selectable baud rate/pin/polarity and directly updating the message text, etc..
However, there are many threads on the forum which indicate that the timing of the code is critical, to avoid characters being lost or corrupted. I don't want to write lots of new program code and then discover that it doesn't work reliably, so I analysed the timings of all the individual instructions in the critical part of the main loop in the AXE133 program. The original code is quite "fast", but I think it can be done faster.
My preference is to do all PICaxe timing estimations at the default 4 MHz clock frequency (of M2s), because then each PIC instruction cycle (and the period of "Timer 1") is exactly one microsecond. However, the standard AXE133 code runs at 16 MHz (to receive data at 2400 baud), but I will consider the equivalent baud rate at 4 MHz (600 baud) and convert up to 8 MHz/1200 baud and 32 MHz/4800 baud, etc. when required. 600 baud has a bit period of ~ 1600 us.
Although PICaxe Basic has several "high level" coding structures (IF..THEN..ELSE , DO..WHILE/UNTIL , CASE..SELECT, etc.) the base PIC (and I suspect the PICaxe interpreter) has only an elementary "Jump" (GOTO) instruction, with the other structures being converted by the Program Editor / Compiler. Also, the two paths from a "branch" instruction have different execution delays; the "fall through" is faster than the "jump" (to another part of the program), principally because the Program Counter needs to be reloaded. So my first task was to identify how the IF..THEN..ELSEIF.. commands in the basic program are converted to GOTOs (tokens).
This is not trivial because the different IF... structures behave differently! IF <true condition> THEN {GOTO} obviously invokes a jump and the <false condition> is a (faster) fall-through into the next line of the program. However, the IF <true condition> THEN <newline> structure falls through to the next line, whilst the <false condition> (to ELSE / ENDIF) uses a (slower) jump to elsewhere in the program. Of course the true/false conditions are complementary, so either path structure is possible, but it appears that (at least in the AXE133 code) the PE inverts the true/false test so that the false result causes a jump and true falls through.
So now, here is the critical part of the code in its original form and then converted to use only GOTOs, complete with some approximate execution times at 4 MHz :
Code:
main:
serin RX,baud,b1 ; wait for the next byte
; NB keep character mode test as first item in this list to optimise speed
if b1 < 253 then
let pinsB = b1 ; output the data
pulsout enable , 1 ; pulse the enable pin to send data.
goto main ; quickly loop back to top
else .......
else .......
; IS (probably) EQUIVALENT TO :
main:
serin RX,baud,b1 ; Wait for the next byte
if b1 => 253 then goto notchar ; Fall through = ~ 850 us (= PIC cycles)
let pinsB = b1 ; Output the data = ~ 500 us
pulsout enable , 1 ; Pulse the enable pin to send data = ~ 500 us
goto main ; Quickly loop back to top = ~ 850 us = Total = ~ 2700 us
notchar: ; Jump to here = ~ 1250us
; Continue decoding
;
Code:
character:
let pinsB = b1 ; Output the data = ~ 500 us
pulsout enable , 1 ; Pulse the enable pin to send data = ~ 500 us
main:
serin RX,baud,b1 ; Wait for the next byte
if b1 < 253 then goto character ; Jump back = ~ 1250 us , Total = ~ 2250 us
notchar: ; Fall through = ~ 850 us
Note that the second (and each subsequent) ELSE in the original program adds another delay of ~ 1250 us, which pushes the path delays for codes 253 and 255 well past 3000 us. Generally it would be better for a "true" to jump out of the list, so that each test adds a rather smaller ~ 850 us to the longest path. These particular paths are not quite so time-critical as the "character" loop because the (present) commands all receive an additional parameter, so there is no need to jump back into the main loop immediately. Actually, that might be a useful strategy when adding any additional commands, even if the reception of an additional byte is not strictly needed.
A structure which is considerably faster than a series of IF...ELSEs is the ON var GOTO label0 , etc.... . The first jump (to label0 for var = 0) takes a time similar to a normal GOTO, but then each subsequent label adds only around 250 us (i.e. about 5 times faster than the original ELSEs). Furthermore, the last label can be omitted from the list and instead executed as a (faster) fall-through. But a problem is that the var list must start from zero, which is where the User Definable Characters are located. Calculating an "offset" adds a significant delay: (b1 = b1 - offset is ~ 650 us), particularly if we need to retain the original value (i.e. b2 = b1 - offset takes ~ 1000 us).
But now to the main point of this thread: What additional commands do others think might be useful in the AXE133 controller and how (or if) the command set should be restructured ? The present commands (253, 254, 255) overlay existing characters in the ROM, which thus cannot be used. They're not "essential" characters, but 255 is a solid block which at least might be useful for testing. AFAIK the character codes between 16 and 31 are not used at all, so are an ideal place to locate the command codes. But that requires a (slower) double-test (greater than and less than conditions) before a value can be definitely identified as a character (if the User-Definable Characters are included).
However, the AXE133 character map has the 8 User-Definable Characters (0 - 7) duplicated between 8 and 15, so why not locate all the command codes between 0 and 7 and use the higher group of UDCs ? Does anybody know of any character sets that (e.g.) have 16 UDCs, or located only from zero upwards? Alternatively, would it be useful to decode more character codes "on the fly" (e.g. to allow codes such as 13 <Carriage Return> and 10 <Line Feed>) to add functions such as "Newline" and "Scroll")? But that may require much more effort in checking all the worst-case timing delays.
Cheers, Alan.
Last edited: