I did a road test of the Ultra-Low Power Arm Cortex-M4 Darwin MCU EVM. In this post: a driver for the common Hitachy type LCD displays that you find everywhere. |
The Display and Driver
In many automates you find these blue or green character displays. Typically 16 characters and two rows, but they come in different sizes.
Most of them have a Hitachi 44780 compatible driver. The Arduino LCD library can talk to them.
I've ported that driver a few times before, to another microcontroller and for the Siemens IOT2000 platform (Intel based linux single board computer).
I've ported it to the MAX32660 today (I expect it to work for other controllers from that family that use the same hardware API).
Library and example project are attached to this blog.
It requires 6 GPIO pins and an external 5V supply for the display.
On the display that I tested, I had to set the GPIO voltage of the MAX32660 to 3.3 V.
When in 1.8 V mode, the logic high wasn't detected by the display driver.
Not surprisingly. The specs says it recognises 2.7 V as high.
I've ported the main API functions:
void lcdInit(const gpio_cfg_t *rs, const gpio_cfg_t *enable, const gpio_cfg_t *d0, const gpio_cfg_t *d1, const gpio_cfg_t *d2, const gpio_cfg_t *d3); void lcdBegin(uint32_t cols, uint32_t lines, uint32_t dotsize); void lcdSetCursor(uint32_t col, uint32_t row); void lcdPrint(const char *str);
The lcdInit() function takes 6 parameters, and those are the 6 pins needed to drive the screen.
The only thing happening in this function is binding the pins to their function, and initialising the control pins.
The data pins are bound when they are used.
I've tried to stay as true as possible to MAXIM's API. I used their gpio_cfg_t structure to set the pin attributes:
/** * Structure type for configuring a GPIO port. */ typedef struct { uint32_t port; /**< Index of GPIO port */ uint32_t mask; /**< Pin mask (multiple pins may be set) */ gpio_func_t func; /**< Function type */ gpio_pad_t pad; /**< Pad type */ } gpio_cfg_t;
I've used the following pin assignment:
This results into this setup code:
gpio_cfg_t rs = {PORT_0, PIN_6, GPIO_FUNC_OUT, GPIO_PAD_NONE}; gpio_cfg_t enable = {PORT_0, PIN_7, GPIO_FUNC_OUT, GPIO_PAD_NONE}; gpio_cfg_t d0 = {PORT_0, PIN_2, GPIO_FUNC_OUT, GPIO_PAD_NONE}; gpio_cfg_t d1 = {PORT_0, PIN_3, GPIO_FUNC_OUT, GPIO_PAD_NONE}; gpio_cfg_t d2 = {PORT_0, PIN_4, GPIO_FUNC_OUT, GPIO_PAD_NONE}; gpio_cfg_t d3 = {PORT_0, PIN_5, GPIO_FUNC_OUT, GPIO_PAD_NONE}; lcdInit(&rs, &enable, &d0, &d1, &d2, &d3);
The next step is to start up the display:
lcdBegin(16,2, 0);
From then on, you can start sending commands.
const char sLine0[] = "MAXIM MAX32660 \0"; const char sLine1[] = "Hello, world! \0"; lcdSetCursor(0, 0); // move to the beginning of the first line lcdPrint(sLine0); lcdSetCursor(0, 1); // move to the beginning of the second line lcdPrint(sLine1);
Improvements
The MAX32660 has a PWM enabled pin, P0_3. I'm using it here for the data line d1, but if you move that to one of the other free gpio pins, you can dim the backlight from firmware.
I've made a naive loop for the microsecond timer. It's depending on the clock frequency
void lcdDelayMicroSeconds(uint32_t us) { /* Demonstrates the TMR driver delay */ // TMR_Delay(MXC_TMR0, USEC(us), NULL); // todo check if this is µsec or msec uint32_t i = 0; for( i=0; i < us * 4; i++) { asm(" nop"); } }
If you uncomment line 4 and comment line 5 -> 8, the controller uses one of the internal timers.
(It's timer 0. If you want to use PWM for the dimmer, then use one of the two other timers).
The data pins are configured at each data exchange. Not needed. You can configure them in either IcdInit() or lcdBegin().
I've made a _GPIO_OutPut() function for debug purposes.
If you plan to use the library and don't like this, just replace any call from _GPIO_OutPut() to GPIO_OutPut().
That will call the default Maxim function.
You can also use the macros from michaelkellett.
I haven't ported the full Arduino API. But all low level functions are available.
If you need something I haven't ported, just copy from the Arduino lib. The rework should be low.
For completeness: the capture of the startup sequence:
Enjoy!
Top Comments