Embedded systems often come with LCDs. Sometimes they’re little tiny ones of 2×8 chars, sometimes they’re great full colour monsters, but the one thing they have in common is that they’re very inconvenient for getting information off of and back into a host computer when you’re developing with them.
You might ask why you’d ever want to do that. Running test cases with LCD output is one good example, or perhaps you don’t actually have the LCD panel the target expects to be using in your development setup. In short, it’s often useful to be able to get that LCD back onto your development host. orblcd can help.
The function of orblcd is very simple; It uses ARM ITM signalling to ‘Create’ a virtual LCD on the host according to the specification provided by the target, and then updates it as the data on that panel changes. There are no particular constraints on update rates or exactly how and when the information is changed, which makes orblcd pretty versatile. There’s no intrinsic reason you couldn’t build orblcd for use over a regular serial port, but the ARM ITM infrastructure means you can still do text debugging and run utilities like orbtop while you’ve got that lcd active.
Let’s illustrate with a couple of examples;
One bit output
In the orbmule repository you’ll find the vidout example. That uses an STM32F103 to generate realtime video to a VGA monitor by ‘racing the beam’…no framebuffer is ever held by the target device, the pixels to be output are created as they’re needed on the fly. The video generation part of vidout runs totally under interrupt, so normal programs don’t ever need to be aware that it’s active. Adding orblcd to vidout is trivial, and only a couple of functions need to be added; one to establish the video mode, and one to output the data, once the orblcd_protocol.h header file has been included.
Setting the video mode is done by sending a message on the orblcd control channel specifying the size of the lcd and its bit depth (vidout.c::235);
This command also serves to instruct the host to refresh the screen, so it’s re-sent at the end of each video frame. This is done so even if the host arrives to the party late, it will soon get into sync with the target and a sensible display will be established. It also means that if the screen resolution changes dynamically (yes, I’ve heard of LCDs that do that when going from a low to high power mode) then the host display will update accordingly.
The second function sends the data, and that is done at rasterline.c::83. We could wait until the complete line of data are available and send them all in vidout.c, but by sending each word as soon as it is ready spreads out the transmission and so improves the utilisation of the ITM channel.
Building and testing vidout is trivial, and since the lcd is now virtual you don’t even have to do the hardware-bits to try it out;
$ make Compiling thirdparty/CMSIS/src/core_cm3.c Compiling thirdparty/CMSIS/src/system_stm32f10x.c Compiling vidout/displayFile.c Compiling vidout/rasterLine.c Compiling vidout/itm_messages.c Compiling vidout/vidout.c Compiling app/main.c Assembling thirdparty/CMSIS/src/startup_stm32f10x_md.s text data bss dec hex filename 13164 40 3144 16348 3fdc ofiles/firmware.elf Built Release version
We’ll assume you’re using an ORBTrace device which is decoding UART framed SWO for the purposes of setting up the rest of the demonstration, and that you’ve got the program onto the target using instructions in that repository, or from one of the tutorials previous to this. That’s not difficult, there’s a suitable gdbinit in the repository and you can use blackmagic probe as your interface driver.
Of course, by changing these incantations you can use anything from a USB-UART interface to a Jlink just as easily, so let’s see how it looks;
Start orbuculum, tell it to start orbtrace and to power the target, then to establish a SWO/UART channel at 36Mbaud;
orbuculum -m 1000 -v 2 --orbtrace "-Tu -a 36000000 -e vtref,on -p vtref,3.3
Now that session is running we can examine what the target is doing using orbtop,we’ve covered that in the past;
$ orbtop -e ofiles/firmware.elf 57.27% 2518 main 23.54% 1035 rasterLine 11.03% 485 ITM_Send32 5.18% 228 TIM1_CC_IRQHandler 1.20% 53 DMA1_Channel3_IRQHandler 0.38% 17 DF_getG 0.29% 13 DF_getLine 0.18% 8 __DF_getLine_veneer 0.15% 7 DF_getXres 0.15% 7 memset 0.13% 6 __DF_getG_veneer 0.13% 6 DF_writeString 0.11% 5 __DF_getXres_veneer ----------------- 99.74% 4388 of 4396 Samples [-S-H] Interval = 1000mS
You can see that the target is pretty busy generating rasterlines (video out on the fly is not trivial to create) but there’s still nearly 60% of the CPU dedicated to the user application and sending the lcd image over the ITM channel is only taking around 11%.
Finally, we can start orblcd to collect the lcd output and present it on a doulble-zoomed panel on screen;
$ ./build/orblcd -z 2 New window 400x288, depth 1
The combination of the orbtop and orblcd output over the channel in this configuration consumes about 8.6Mbits/sec…well within the capabilities of the SWO, which can run at up to 48Mbits/sec using Manchester encoding, or 62.5Mbits/sec using UART encoding. On the host side, orblcd consumes around 5% of one of my CPUs, and because it’s using SDL accelerated rendering, that doesn’t really change too much no matter how much I zoom the screen.
High Colour Example
For a second example, let’s try something a little more taxing; a true colour 24 bit 480×320 panel from the lcd_demo repository. You can look at the source code (and specifically the file display_orblcd.c) to see how lrblcd has been integrated into this, and build it with;
$ make GRAPHIC_LIBRARY=ORBLCD <SNIP> arm-none-eabi-objcopy -O binary orbdemo.elf orbdemo.bin arm-none-eabi-objdump -h -S orbdemo.elf > orbdemo.lss arm-none-eabi-nm -n orbdemo.elf > orbdemo.sym text data bss dec hex filename 19864 136 133296 153296 256d0 orbdemo.elf 19864 136 133296 153296 256d0 (TOTALS)
There’s a .gdbinit file already associated with this project, which programs the device via openocd and uses an ORBTrace to set up parallel trace. This important lines are;
source /usr/local/share/orbcode/gdbtrace.init !orbtrace -T 4 -e vtref,on -p vtref,3.3 !gnome-terminal --hide-menubar -- /usr/bin/openocd -f /usr/local/share/orbcode/muleboard.ocd target remote localhost:3333 file orbdemo.elf monitor reset halt load set mem inaccessible-by-default off enableSTM32TRACE 4
If you prefer to use SWO as previously, then replace the TRACE line above with something like;
prepareSWO 160000000 40000000 1 1 enableSTM32SWO 4
You’ll also want to set up orbtrace to collect SWO/Manchester ITM formatted data in that case with the -Tm option. So now, let’s go ahead and load the demo;
$ arm-none-eabi-gdb -q warning: No executable has been specified and target does not support determining executable automatically. Try using the "file" command. 0x080021b8 in ?? () target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08000734 msp: 0x20020ab0 Loading section .text, size 0x4d98 lma 0x8000000 Loading section .data, size 0x88 lma 0x8004d98 Start address 0x08000734, load size 20000 Transfer rate: 18 KB/sec, 6666 bytes/write. $1 = 1 (gdb) continue Continuing.
As previously, we can use orbtop to see what the application is doing (nothing very interesting, we had to slow it down a lot because orblcd output is very fast) and then start orblcd;
$ ./build/orblcd New window 480x320, depth 24
In this case it takes around 6-8% of one of the CPUs on my machine and since orbuculum will serve the same data to any number of clients, you can really go a bit crazy;
I don’t know why you’d ever do that, but that’s 24 instances of the LCD at varying sizes and a load average of 1.07.
Lcd without an lcd. Orbuculum is the gift that keeps on giving.