eLua and STM32F4

By | 2013-09-11

I am sufficiently taken with Lua that I went looking for interesting Lua projects. I came across eLua. eLua is a project to build a version of Lua that can be used in embedded projects. There is certainly potential there for making life easier. Often in embedded projects, the vast majority of the work is getting the hardware to play nicely. You gather inputs, make a decision and set outputs (broadly). The “decision” part of that sequence could easily be done by Lua scripts; with all the hardware work done by low-level C code.

It’s an interesting enough idea that it made me want to try. My interest was particularly piqued by the presence of support for the STM32F4DISCOVERY board; which is the big brother to the STM32F0DISCOVERY board I wrote about earlier this year.

Grab a copy of eLua :

$ git clone https://github.com/elua/elua.git
$ git describe

Then we’ll be following the build instructions, with the STM32F4 as our platform and STM32F4DISCOVERY as our board. The documentation on the website, unfortunately, describes how to build using the scons system. The eLua developers seem to be in the process of removing scons; so the build instructions on the website are wrong. Instead see the build instructions in the git repository itself.

Build System

Some of the required Lua libraries are already in Debian.

$ apt-get install lua5.1 lua-filesystem lua-md5

One of them isn’t, lpack. We’ll have to use lua’s own package manager for that (note that Debian’s luarocks, at time of writing, uses lua 5.1 not 5.2).

$ apt-get install luarocks
$ luarocks install lpack
Installing http://luarocks.org/repositories/rocks/lpack-20070629-1.src.rock...
Using http://luarocks.org/repositories/rocks/lpack-20070629-1.src.rock... switching to 'build' mode
Archive:  /tmp/luarocks_luarocks-rock-lpack-20070629-1-3149/lpack-20070629-1.src.rock
  inflating: lpack-20070629-1.rockspec  
 extracting: lpack.tar.gz            
Do not use 'module' as a build type. Use 'builtin' instead.
gcc -O2 -fPIC -I/usr/include/lua5.1 -c lpack.c -o lpack.o
gcc -shared -o pack.so -L/usr/local/lib lpack.o
Updating manifest for /usr/local/lib/luarocks/rocks

lpack 20070629-1 is now built and installed in /usr/local/ (license: Public domain)


Here’s my first build attempt

$ ./build_elua.lua board=stm32f4discovery
[CONFIG] Found board description file at boards/known/stm32f4discovery.lua
[CONFIG] Generated board header file at boards/headers/board_stm32f4discovery.h
Unable to find an usable toolchain in your path.
List of accepted toolchains (for STM32F407VG): arm-gcc,codesourcery,devkitarm,arm-eabi-gcc

This is happening to me because I don’t keep my arm compiler in the PATH, I prefer to specify a toolchain prefix that includes the path (since I have to do that anyway, I figure why slow down normal command line PATH, I might as well include the path in the prefix). Unfortunately for me, eLua doesn’t offer a prefix option (and that’s going to come back and bite them, I’ll bet). In fact, it doesn’t even include the correct prefix as an option for the ARM-maintained gcc from launchpad.net. No matter; I just edited build_data.lua and added this to the toolchain_list:

diff --git a/build_data.lua b/build_data.lua
index e0ad79e..ad1f577 100644
--- a/build_data.lua
+++ b/build_data.lua
@@ -23,6 +23,17 @@ local toolchain_list =
     cross_lualong = 'int 32',
     version = '--version'
+  [ 'arm-none-eabi-gcc' ] = {
+    compile = '/opt/gcc-arm-none-eabi-4_7-2013q2/bin/arm-none-eabi-gcc',
+    link = '/opt/gcc-arm-none-eabi-4_7-2013q2/bin/arm-none-eabi-ld',
+    asm = '/opt/gcc-arm-none-eabi-4_7-2013q2/bin/arm-none-eabi-as',
+    bin = '/opt/gcc-arm-none-eabi-4_7-2013q2/bin/arm-none-eabi-objcopy',
+    size = '/opt/gcc-arm-none-eabi-4_7-2013q2/bin/arm-none-eabi-size',
+    cross_cpumode = 'little',
+    cross_lua = 'float 64',
+    cross_lualong = 'int 32',
+    version = '--version'
+  },
   [ 'arm-eabi-gcc' ] = {
     compile = 'arm-eabi-gcc',
     link = 'arm-eabi-ld',
@@ -96,7 +107,7 @@ local arch_data = {
 local toolchain_map = {
   arm = { 'arm-gcc', 'codesourcery', 'devkitarm', 'arm-eabi-gcc' },
   cortexm3 = { 'arm-gcc', 'codesourcery', 'devkitarm', 'arm-eabi-gcc' },
-  cortexm4 = { 'arm-gcc', 'codesourcery', 'devkitarm', 'arm-eabi-gcc' },
+  cortexm4 = { 'arm-gcc', 'codesourcery', 'devkitarm', 'arm-eabi-gcc', 'arm-none-eabi-gcc' },
   avr32 = { 'avr32-gcc', 'avr32-unknown-none-gcc' },
   i386 =  { 'i686-gcc' }

The build script will then happily find a toolchain that’s acceptable to it:

$ ./build_elua.lua board=stm32f4discovery

This went much better.

-rwxrwxr-x  1 andyp andyp 417682 Sep 11 13:51 elua_lua_stm32f4discovery.elf
-rw-rw-r--  1 andyp andyp 718346 Sep 11 13:51 elua_lua_stm32f4discovery.map


I used objcopy to make a binary from the elf,

$ /opt/gcc-arm-none-eabi-4_7-2013q2/bin/arm-none-eabi-objcopy \
    -O binary \
    elua_lua_stm32f4discovery.elf \

then openocd to flash the binary to the board.

$ openocd \
   -f /usr/share/openocd/scripts/board/stm32f4discovery.cfg \
   -c "init" \
   -c "reset halt" \
   -c "sleep 100" \
   -c "wait_halt 2" \
   -c "echo \"--- Writing elua_lua_stm32f4discovery.bin\"" \
   -c "flash write_image erase elua_lua_stm32f4discovery.bin 0x08000000" \
   -c "sleep 100" \
   -c "echo \"--- Verifying\"" \
   -c "verify_image elua_lua_stm32f4discovery.bin 0x08000000" \
   -c "sleep 100" \
   -c "echo \"--- Done\"" \
   -c "resume" \
   -c "shutdown"
Open On-Chip Debugger 0.7.0 (2013-05-15-12:03)
Licensed under GNU GPL v2
For bug reports, read
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : This adapter doesn't support configurable speed
Info : STLINK v2 JTAG v14 API v2 SWIM v0 VID 0x0483 PID 0x3748
Info : Target voltage: 2.868772
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x080002c0 msp: 0x20000400
--- Writing elua_lua_stm32f4discovery.bin
auto erase enabled
Info : device id = 0x10016413
Info : flash size = 1024kbytes
wrote 262144 bytes from file elua_lua_stm32f4discovery.bin in 14.261276s (17.951 KiB/s)
--- Verifying
verified 232464 bytes in 2.129896s (106.585 KiB/s)
--- Done
shutdown command invoked


We should now have an STM32F4DISCOVERY board with eLua running on it. The debug UART on this board is not made available via the programming USB (Mini USB socket at the top of the board). The Lua terminal is supplied as a CDC USB serial device on the application port on the Micro USB port at the bottom of the board.

usb 2-3: new full-speed USB device number 10 using ohci_hcd
usb 2-3: New USB device found, idVendor=0483, idProduct=5740
usb 2-3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 2-3: Product: STM32 Virtual ComPort in FS Mode
usb 2-3: Manufacturer: STMicroelectronics
usb 2-3: SerialNumber: 00000000050C
cdc_acm 2-3:1.0: This device cannot do calls on its own. It is not a modem.
cdc_acm 2-3:1.0: ttyACM0: USB ACM device

Run a terminal on it at 115200 8n1 and you’ll get the eLua shell prompt.

eLua# help
Shell commands:
  help   - shell help
  lua    - start a Lua session
  ls     - lists files and directories
  dir    - lists files and directories
  cat    - list the contents of a file
  type   - list the contents of a file
  recv   - receive files via XMODEM
  cp     - copy files
  mv     - move/rename files
  rm     - remove files
  ver    - show version information
  mkdir  - create directories
  exit   - exit the shell
For more information use 'help <command>'.

Magical. This is not yet Lua; this is the embedded equivalent of a bash shell. We can run an interactive Lua session of course…

eLua# lua
Press CTRL+Z to exit Lua
Lua 5.1.4  Copyright (C) 1994-2011 Lua.org, PUC-Rio
> print "Hello, World!"
Hello, World!

eLua comes with a standard library appropriate for controlling the on-chip peripherals (ADC, CAN, PIO, PWM, SPI, timers, UART). You can experiment with these if you wish. Here’s a few snippets as an example:

> print(pd.board() .. "/" .. pd.platform() .. "/" .. pd.cpu())

> ledpin = pio.PD_13
> pio.pin.setdir(pio.OUTPUT, ledpin)
> pio.pin.sethigh(ledpin)

> pb = pio.PA_0
> pio.pin.setdir(pio.INPUT, pb)
> print( pio.pin.getval(pb) )
> print( pio.pin.getval(pb) )

I pushed the “user” button between those last two.

Next time we’ll look at customising the build and including our own Lua source.

Leave a Reply