Adding a fake LCD to your embedded system

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);

ITM_Send32(LCD_COMMAND_CHANNEL,ORBLCD_OPEN_SCREEN(XSIZE*8,YSIZE*16,ORBLCD_DEPTH_1));

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.