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 v0.9-94-g02705ec

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)

Building

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

Flashing

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 \ elua_lua_stm32f4discovery.bin

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 http://openocd.sourceforge.net/doc/doxygen/bugs.html 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

Testing

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()) STM32F4DISCOVERY/STM32F4/STM32F407VG > 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) ) 0 > print( pio.pin.getval(pb) ) 1

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

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