How to add Energia support to UTFT library
For some reason I like MSP430 microcontrollers and I always put them into my new designs. This time I decided to use MSP430FR5739 as painting chamber thermostat controller. This board has 2.2" TFT screen with touch controller, 2 NTC thermistors and 4 power MOSFET outputs for controlling actuators like relays and pneumatic solenoids.
Libraries #
Eagle schematic and layout can be found here. I have not tested yet if this fancy Linear LT1161 high-side MOSFET driver works properly. I am more interested in getting TFT to work. The display I am using uses HX8340 display driver.
At glance there seems to be 2 ways to get it to display something rather quickly:
There are pros and cons with both solutions. UTFT is meant for Arduino which does not know nothing about MSP430, however recently Energia project has matured quite a bit, so it should be possible to use Arduino libraries within Energia. 43oh graphics library is designed for MSP430, especially for Launchpad which is very resource limited MCU, so I expect it to take minimum resources, although I have not tested it yet. Considering Energia’s mature enough ecosystem I decided to give UTFT library a go.
UTFT #
Fortunately UTFT library already has nice hardware abstraction layer, therefore it was relatively easy to port it to the MSP430. If you look at UTFT.h we see that there are some hardware dependent defines. Notice that I have already added MSP430 stuff to the end.
#if defined(__AVR__)
#include "Arduino.h"
#include "HW_AVR_defines.h"
#elif defined(__PIC32MX__)
#include "WProgram.h"
#include "HW_PIC32_defines.h"
#elif defined(__arm__)
#include "Arduino.h"
#include "HW_ARM_defines.h"
#elif defined(__MSP430__)
#include "Energia.h"
#include "HW_MSP430_defines.h"
#endif
First line is straightforward: Energia.h is basically the same as Arduino.h. HW_MSP430_defines.h declares some family specific variables that UTFT uses. I created this by merging HW_ARM_defines.h and HW_AVR_defines.h. It was kind of trial and error to see what is actually needed for MSP430. ARM and AVR are so different processors, therefore I compared those side by side to see how this library uses different architectures. I came up with this:
HW_MSP430_defines.h #
// Hardwarespecific defines
#define cbi(reg, bitmask) *reg &= ~bitmask
#define sbi(reg, bitmask) *reg |= bitmask
#define pulse_high(reg, bitmask) sbi(reg, bitmask); cbi(reg, bitmask);
#define pulse_low(reg, bitmask) cbi(reg, bitmask); sbi(reg, bitmask);
#define cport(port, data) port &= data
#define sport(port, data) port |= data
#define swap(type, i, j) {type t = i; i = j; j = t;}
#define fontbyte(x) cfont.font[x]
#define pgm_read_word(data) *data
#define pgm_read_byte(data) *data
#define PROGMEM
#define regtype volatile uint8_t
#define regsize uint8_t
#define bitmapdatatype unsigned short*
Another thing that must be changed is device hardware abstraction layer. We could call HW_MSP430_defines.h as familiy specific layer and HW_MSP430FR5739.h as device or platform specific layer. I am using MSP430FR5739 on my custom board, therefore its pin mapping is quite different from for example Fraunchpad’s pin mapping that actually uses the same MCU. If MSP430 with different pin mapping is going to be added, similar file to HW_MSP430FR5739.h should be created. It could be called HW_MSP430Fraunchpad.h. Lets see what changes must be made in this file.
HW_MSP430FR5739.h snippets #
void UTFT::LCD_Writ_Bus(char VH,char VL, byte mode) {
...
case 8:
// Set 8-bit parallel interface pins
// D0 J.1
// D1 J.0
// D2 1.3
// D3 3.3
// D4 3.2
// D5 3.1
// D6 3.0
// D7 1.2
PJOUT &= ~0x03;
PJOUT |= ((VH & 0x01) << 1) | (VH & 0x02)>>1; // set D0, D1
P1OUT &= ~0x08;
P1OUT |= (VH & 0x04) << 1; // set D2
P3OUT &= ~0x0F;
P3OUT |= (VH & 0x08) | ((VH & 0x10) >> 2) | ((VH & 0x20) >> 4) | ((VH & 0x40) >> 6); // set D3, D4, D5, D6
P1OUT &= ~0x04;
P1OUT |= (VH & 0x80) >> 5; // set D7
pulse_low(P_WR, B_WR);
PJOUT &= ~0x03;
PJOUT |= ((VL & 0x01) << 1) | (VL & 0x02)>>1; // set D0, D1
P1OUT &= ~0x08;
P1OUT |= (VL & 0x04) << 1; // set D2
P3OUT &= ~0x0F;
P3OUT |= (VL & 0x08) | ((VL & 0x10) >> 2) | ((VL & 0x20) >> 4) | ((VL & 0x40) >> 6); // set D3, D4, D5, D6
P1OUT &= ~0x04;
P1OUT |= (VL & 0x80) >> 5; // set D7
pulse_low(P_WR, B_WR);
break;
case 16:
#pragma message("LCD_Writ_Bus mode 16 pins not defined!")
break;
case LATCHED_16:
asm("nop"); // Mode is unsupported
break;
...
void UTFT::_set_direction_registers(byte mode)
{
if (mode!=LATCHED_16)
{
PJDIR |= 0x03;
P1DIR |= 0x0C;
P3DIR |= 0x0F;
if (mode==16) {
#pragma message("_set_direction_registers mode 16 pins not defined!")
}
}
else
{
asm("nop"); // Mode is unsupported
}
}
void UTFT::_fast_fill_16(int ch, int cl, long pix)
{
long blocks;
#pragma message("_fast_fill_16 pins not defined!")
...
}
void UTFT::_fast_fill_8(int ch, long pix)
{
long blocks;
// Set 8-bit parallel interface pins
// D0 J.1
// D1 J.0
// D2 1.3
// D3 3.3
// D4 3.2
// D5 3.1
// D6 3.0
// D7 1.2
PJOUT &= ~0x03;
PJOUT |= ((ch & 0x01) << 1) | (ch & 0x02)>>1; // set D0, D1
P1OUT &= ~0x08;
P1OUT |= (ch & 0x04) << 1; // set D2
P3OUT &= ~0x0F;
P3OUT |= (ch & 0x08) | ((ch & 0x10) >> 2) | ((ch & 0x20) >> 4) | ((ch & 0x40) >> 6); // set D3, D4, D5, D6
P1OUT &= ~0x04;
P1OUT |= (ch & 0x80) >> 5; // set D7
...
}
The main thing in HW_MSP430FR5739.h is to set 8 or 16 bit parallel interface pins correctly. Library did not have a nice comment here before, so I added it by myself.
// Set 8-bit parallel interface pins
// D0 J.1
// D1 J.0
// D2 1.3
// D3 3.3
// D4 3.2
// D5 3.1
// D6 3.0
// D7 1.2
Basically it just says which pin in parallel interface is mapped with which MCU pin. At first all pins are cleared and then only those pins are set whose bits are set in int ch
parameter, if we are talking about _fast_fill_8
method. I think seeing this comment and looking how it is implemented in code should explain how to modify it for different MSP430.
HW_MSP430FR5739.h is used in UTFT.cpp. Here it is added to the end. Notice that #elif defined(__MSP430__)
is used here, so actually all MSP430 implementations are using currently HW_MSP430FR5739.h pinmapping. It should be changed to some other define to make difference for example between Launchpad and Fraunchpad. Look how it is done with ARM, they have separate define for SAM3X8E. For convenience I did not solve this problem because currently I have only one MSP430 family MCU supported.
...
#elif defined(__arm__)
#if defined(__SAM3X8E__)
#pragma message("Compiling for Arduino Due (AT91SAM3X8E)...")
#include "HW_SAM3X8E.h"
#else
#error "Unsupported ARM MCU!"
#endif
#elif defined(__MSP430__)
#pragma message("Compiling for MSP430FR5739 Garage board...")
#include "HW_MSP430FR5739.h"
#endif
Memory hungry #
MSP430FR5739 has only 16 KB ROM, so code must be as small as possible. UTFT is quite large library supporting multiple different displays. By default support to all displays is compiled in, although probably we want to use just one display driver. Fortunately there is possibility to discard drivers that you do not use at the moment. In memorysaver.h comment out only this driver that you are going to use. I am using HX8340B_8, so only this line is commented out.
memorysaver.h #
// UTFT Memory Saver
// -----------------
//
// Since most people have only one or possibly two different display modules a lot
// of memory has been wasted to keep support for many unneeded controller chips.
// You now have the option to remove this unneeded code from the library with
// this file.
// By disabling the controllers you don't need you can reduce the memory footprint
// of the library by several Kb.
//
// Uncomment the lines for the displaycontrollers that you don't use to save
// some flash memory by not including the init code for that particular
// controller.
#define DISABLE_HX8347A 1 // ITDB32
#define DISABLE_ILI9327 1 // ITDB32WC / TFT01_32W
#define DISABLE_SSD1289 1 // ITDB32S / TFT_32 / GEEE32 / ELEE32_REVA / ELEE32_REVB - This single define will disable both 8bit, 16bit and latched mode for this controller
#define DISABLE_ILI9325C 1 // ITDB24
#define DISABLE_ILI9325D 1 // ITDB24D / ITDB24DWOT / ITDB28 / TFT01_24_8 / TFT01_24_16 - This single define will disable both 8bit and 16bit mode for this controller
//#define DISABLE_HX8340B_8 1 // ITDB22 8bit mode / GEEE22
#define DISABLE_HX8340B_S 1 // ITDB22 Serial mode
#define DISABLE_HX8352A 1 // ITDB32WD / TFT01_32WD
#define DISABLE_ST7735 1 // ITDB18SP
#define DISABLE_PCF8833 1 // LPH9135
#define DISABLE_S1D19122 1 // ITDB25H
#define DISABLE_SSD1963_480 1 // ITDB43
#define DISABLE_SSD1963_800 1 // ITDB50
#define DISABLE_S6D1121 1 // ITDB24E - This single define will disable both 8bit and 16bit mode for this controller
#define DISABLE_ILI9320 1 // GEEE24 / GEEE28 - This single define will disable both 8bit and 16bit mode for this controller
To put it all together – here is commit to my github repository that adds MSP430 support to UTFT v2.1 library: https://github.com/andresv/industrial-switcher/commit/9ca1816f5c4ab81bea9e3fc3d24c0e07481e152a
However there is a big problem to tackle – how to make this library even smaller. Drawing just a small rectangle takes about 7 KB, but if print is called it does not link together. Linker says it takes about 3 KB more than we have. So currently simple print takes about 12 KB, which is a lot.
garage.ino #
include #
#define G_GREEN_LED 3
#define G_RED_LED 4
#define OUTPUT_1 11
#define OUTPUT_2 12
#define RS_PIN 28
#define WR_PIN 27
#define CS_PIN 14
#define RST_PIN 13
// select font
extern uint8_t SmallFont[];
UTFT myGLCD(HX8340B_8, RS_PIN, WR_PIN, CS_PIN, RST_PIN);
void setup() {
pinMode(G_GREEN_LED, OUTPUT);
pinMode(G_RED_LED, OUTPUT);
pinMode(OUTPUT_1, OUTPUT);
pinMode(OUTPUT_2, OUTPUT);
myGLCD.InitLCD();
myGLCD.setFont(SmallFont);
}
bool fill = true;
void loop() {
if (fill) {
// Clear the screen and draw the frame
myGLCD.clrScr();
myGLCD.setColor(255, 0, 0);
myGLCD.fillRect(0, 0, 219, 13);
// without this line 7343 byes
// with this line 19726 bytes, which is 3342 bytes too much
//myGLCD.print("TFT", CENTER, 1);
fill = false;
}
digitalWrite(G_GREEN_LED, HIGH);
digitalWrite(OUTPUT_1, HIGH);
delay(250);
digitalWrite(G_GREEN_LED, LOW);
digitalWrite(OUTPUT_1, LOW);
delay(250);
digitalWrite(G_RED_LED, HIGH);
delay(250);
digitalWrite(G_RED_LED, LOW);
delay(250);
}
In conclusion I would say that UTFT library works under Energia. Basic functionality (draw rectangles) is ported to MSP430FR5739, but printing characters to screen does not work yet because it takes too much memory. Have to find out how to make it smaller. Maybe some hints from 43oh library can be found.
EDIT 07.04.13: #
I found out what caused huge memory usage if print was called. Print supports character rotating and this rotating function uses a lot of memory. Therefore I added DISABLE_CHAR_ROTATING flag that can be used to turn rotating feature completely off. Here is the commit that adds this functionality: https://github.com/andresv/industrial-switcher/commit/36f59887b5189857f0bf6ae72ef4211c834fbf2a
Now I would say that UTFT library is ported to Energia. However Launchpad/Fraunchpad specific IO defines for parallel interface must be made.
EDIT 05.05.13: #
Now UTFT-Energia has its own Github repository. Feel free to fork it.