Lipstick ======== "Lipstick" is software that runs on an NXP LPC ARM7TDMI microcontroller board to provide remote analogue and digital GPIO over MODBUS. See: http://chezphil.org/lipstick/ Much of the software is general-purpose low-level code for this board. Licence ======= This software is distrubuted under the terms of the Boost software license; see LICENCE.txt. Hardware ======== Futurelec "ET-ARM Stamp". NXP LPC2119 processor. Connect the board to 3.3 V power, e.g. the Futurelec regulaor board. Connect its serial cable (it is true serial voltages, there is a MAX232 on the bottom of the board), to e.g. a USB-serial dongle. I believe the current board uses the "no suffix", "not enhanced" version of the chip. This lacks various features, including fractional baud rates and ADC per-channel data and interrupt enable registers. The board doesn't have much supply decoupling; I'm unsure how good the analogue inputs will be. Docs ==== https://www.futurlec.com/ET-ARM_Stamp.shtml https://www.nxp.com/docs/en/data-sheet/LPC2109_2119_2129.pdf https://www.nxp.com/docs/en/user-guide/UM10114.pdf http://skpang.co.uk/catalog/images/ett/ET-ARM7_stamp_LPC2119.pdf Tools ===== arm-none-eabi-* from Debian package gcc-arm-none-eabi. newlib from Debian package libnewlib-arm-none-eabi. asmflash ======== Make an output flash with some assembler, to prove the board works. The nops skip over the vectors. There are a couple of issues; the unused vector is repurposed as a checksum and lpc21isp modifies that value during programming, and the chip changes the mapping for the first 64 bytes at various times. So skip to after the first 64 bytes. E0028000 is the base address for GPIO0. +8 sets the direction, 1 = output. +4 sets bits, +12 clears bits. $ arm-none-eabi-as -o asmflash.o asmflash.s $ arm-none-eabi-ld --script=lipstick.ld -o asmflash.elf asmflash.o $ objcopy asmflash.elf -O ihex asmflash.hex $ lpc21isp asmflash.hex /dev/ttyUSB0 9600 19660 9600 is the serial port speed; faster speeds e.g. 38400 work (the board autodetects the speed), 19660 is the board's crystal frequency. Press both reset and load buttons, then release reset first. Code will be downloaded. Observe that at least many of the IOs are changing at about 200 kHz. (Could obviously be much faster). C/C++ startup ============= vectors.s has a jump from the reset vector to _start, a PC load from the interrupt controller for the IRQ vector, placeholders for the other vectors, and padding nops as above. start.cc privdes _start(), which sets the stack pointer(s), zeros the BSS, copies mdata to data, calls global constructors, and calls main(). This relies on various symbols and sections that are defined by the linker script. Note that with some optimisation settings, the loops will be replaced with calls to memset and memcpy. C++ === Note -fno-exceptions. I don't know what's needed to make exceptions work. Note gcc 7.3.1 has incomplete C++-17 support. It does have string_view, but doesn't have to_chars. Thumb ===== We can build Thumb code for better code density with -mthumb. The reset vector is always executed in ARM mode, so _start needs to be ARM mode. An attribute on the function ensures this, even if we compile with -mthumb. There are other other things that must be in ARM mode, notably enabling/disabling interrupts. We end up with relatively little thumb code in Lipstick. Newlib ====== Newlib relies on various stubs to provide underlying features. stubs.c defines _sbrk, which is needed by malloc(). Note that it is currently disabled; see comments in the file. libgcc ====== To support e.g. integer division, link with -lgcc. Makefile ======== Invokes as, gcc, g++, ld, objcopy and lpc21isp with suitable options. make %.prog to program the device, and %.progterm to program and then connect a terminal (at the same baud rate). Hello World =========== Sends Hello World to UART0; trivial UART driver with polling. $ make -B hello_world.elf arm-none-eabi-g++ -mcpu=arm7tdmi-s -mthumb -Os --std=c++17 -fno-exceptions -c hello_world.cc arm-none-eabi-as -o vectors.o vectors.s arm-none-eabi-g++ -mcpu=arm7tdmi-s -mthumb -Os --std=c++17 -fno-exceptions -c start.cc arm-none-eabi-gcc -mcpu=arm7tdmi-s -mthumb -Os -c stubs.c arm-none-eabi-ld --script=lipstick.ld -o hello_world.elf -L/usr/lib/arm-none-eabi/newlib/thumb -L/usr/lib/gcc/arm-none-eabi/7.3.1/thumb vectors.o start.o hello_world.o --start-group -lc stubs.o -lgcc --end-group $ make hello_world.progterm arm-none-eabi-objcopy hello_world.elf -O ihex hello_world.hex lpc21isp -term hello_world.hex /dev/ttyUSB0 38400 19660 lpc21isp version 1.97 File hello_world.hex: loaded... Start Address = 0x00000058 converted to binary format... image size : 368 Image size : 368 Synchronizing (ESC to abort)....... OK Read bootcode version: 65 1 Read part ID: LPC2119, 128 kiB FLASH / 16 kiB SRAM (0x0201FF12) Will start programming at Sector 1 if possible, and conclude with Sector 0 to ensure that checksum is written last. Erasing sector 0 first, to invalidate checksum. OK Sector 0: .............. Download Finished... taking 1 seconds Now launching the brand new code Terminal started (press Escape to abort) Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Peripheral Drivers ================== Headers with simple drivers for the LPC2119's on-chip peripherals and related things: adc.hh - Analogue to digital converter arm.hh - ARM ASM features buffer.hh - Volatile small container clocks.hh - Frequencies of clocks cpu.hh - Vector memory map, idle mode, flash memory controller registers.hh - Addresses of registers rtc.hh - Real Time Clock timer.hh - Counter/timer uart.hh - UARTs wdt.hh - Watchdog timer modbus.cc ========= The main program that provides the MODBUS functionality. Talks MODBUS on UART1, which is expected to be connected to an RS-485 driver with RTS connected to the driver enable. Slave address etc. can be configured.