diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index a7382cee..d8392271 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -126,3 +126,5 @@ add_example (bmi160) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) +add_custom_example (lcm1602-i2c-example-c lcm1602-i2c.c lcm1602) +add_custom_example (lcm1602-parallel-example-c lcm1602-parallel.c lcm1602) diff --git a/examples/c/lcm1602-i2c.c b/examples/c/lcm1602-i2c.c new file mode 100644 index 00000000..7951fe6b --- /dev/null +++ b/examples/c/lcm1602-i2c.c @@ -0,0 +1,52 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "lcm1602.h" + +int main(int argc, char **argv) +{ +//! [Interesting] + lcm1602_context lcd = lcm1602_i2c_init(0, 0x20, true, 16, 2); + + if (!lcd) + { + printf("lcm1602_i2c_init() failed\n"); + return 1; + } + + lcm1602_set_cursor(lcd, 0, 0); + lcm1602_write(lcd, "Hello World 1", 13); + + upm_delay(3); + + lcm1602_set_cursor(lcd, 1, 0); + lcm1602_write(lcd, "Hello World 2", 13); + + upm_delay(3); + + lcm1602_close(lcd); +//! [Interesting] + + return 0; +} diff --git a/examples/c/lcm1602-parallel.c b/examples/c/lcm1602-parallel.c new file mode 100644 index 00000000..290ac3dc --- /dev/null +++ b/examples/c/lcm1602-parallel.c @@ -0,0 +1,61 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "lcm1602.h" + +int main(int argc, char **argv) +{ +//! [Interesting] + // LCD connection: + // LCD RS pin to digital pin 8 + // LCD Enable pin to digital pin 13 + // LCD D4 pin to digital pin 2 + // LCD D5 pin to digital pin 3 + // LCD D6 pin to digital pin 4 + // LCD D7 pin to digital pin 5 + // (LCD R/W pin to ground - write only) + // columns 16, rows 2 + lcm1602_context lcd = lcm1602_gpio_init(8, 13, 2, 3, 4, 5, 16, 2); + + if (!lcd) + { + printf("lcm1602_gpio_init() failed\n"); + return 1; + } + + lcm1602_set_cursor(lcd, 0, 0); + lcm1602_write(lcd, "Hello World 1", 13); + + upm_delay(3); + + lcm1602_set_cursor(lcd, 1, 0); + lcm1602_write(lcd, "Hello World 2", 13); + + upm_delay(3); + + lcm1602_close(lcd); +//! [Interesting] + + return 0; +} diff --git a/src/lcm1602/CMakeLists.txt b/src/lcm1602/CMakeLists.txt new file mode 100644 index 00000000..f8c3c925 --- /dev/null +++ b/src/lcm1602/CMakeLists.txt @@ -0,0 +1,9 @@ +upm_mixed_module_init (NAME lcm1602 + DESCRIPTION "upm LCM1602 and compatibles LCD display driver" + C_HDR lcm1602.h + C_SRC lcm1602.c +# CPP_HDR lcm1602.hpp +# CPP_SRC lcm1602.cxx +# FTI_SRC lcm1602_fti.c +# CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/lcm1602/hd44780_bits.h b/src/lcm1602/hd44780_bits.h new file mode 100644 index 00000000..846c4015 --- /dev/null +++ b/src/lcm1602/hd44780_bits.h @@ -0,0 +1,82 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +// This file contains bit definitions for the HD44780 and compatible +// LCD controllers. It is used primarily my the lcm1602 driver and +// it's derivatives. +// +// Those values (DATA, CMD) are specific to the implementation of the +// i2C expander in use, so may not be appropriate for inclusion into +// this file. But for now, we will leave them here. + +#include + +// commands +const uint8_t HD44780_CLEARDISPLAY = 0x01; +const uint8_t HD44780_RETURNHOME = 0x02; +const uint8_t HD44780_ENTRYMODESET = 0x04; +const uint8_t HD44780_DISPLAYCONTROL = 0x08; +const uint8_t HD44780_CURSORSHIFT = 0x10; +const uint8_t HD44780_FUNCTIONSET = 0x20; + +// flags for display entry mode +const uint8_t HD44780_ENTRYRIGHT = 0x00; +const uint8_t HD44780_ENTRYLEFT = 0x02; +const uint8_t HD44780_ENTRYSHIFTINCREMENT = 0x01; +const uint8_t HD44780_ENTRYSHIFTDECREMENT = 0x00; + +// flags for display on/off control +const uint8_t HD44780_DISPLAYON = 0x04; +const uint8_t HD44780_DISPLAYOFF = 0x00; +const uint8_t HD44780_CURSORON = 0x02; +const uint8_t HD44780_CURSOROFF = 0x00; +const uint8_t HD44780_BLINKON = 0x01; +const uint8_t HD44780_BLINKOFF = 0x00; + +// flags for display/cursor shift +const uint8_t HD44780_DISPLAYMOVE = 0x08; +const uint8_t HD44780_MOVERIGHT = 0x04; +const uint8_t HD44780_MOVELEFT = 0x00; + +// flags for function set +const uint8_t HD44780_8BITMODE = 0x10; +const uint8_t HD44780_4BITMODE = 0x00; +const uint8_t HD44780_2LINE = 0x08; +const uint8_t HD44780_1LINE = 0x00; +const uint8_t HD44780_5x10DOTS = 0x04; +const uint8_t HD44780_5x8DOTS = 0x00; + +// flags for CGRAM +const uint8_t HD44780_SETCGRAMADDR = 0x40; + +// may be implementation specific +const uint8_t HD44780_EN = 0x04; // Enable bit +const uint8_t HD44780_RW = 0x02; // Read/Write bit +const uint8_t HD44780_RS = 0x01; // Register select bit +const uint8_t HD44780_DATA = 0x40; +const uint8_t HD44780_CMD = 0x80; + +const uint8_t HD44780_BACKLIGHT = 0x08; +const uint8_t HD44780_NOBACKLIGHT = 0x00; diff --git a/src/lcm1602/javaupm_lcm1602.i b/src/lcm1602/javaupm_lcm1602.i new file mode 100644 index 00000000..34d96755 --- /dev/null +++ b/src/lcm1602/javaupm_lcm1602.i @@ -0,0 +1,40 @@ +%module javaupm_i2clcd +%include "../upm.i" +%include "stdint.i" +%include "typemaps.i" +%include "arrays_java.i"; + +%apply signed char[] {uint8_t []}; +%ignore BasicFont; + +%typemap(jni) (uint8_t *data, int bytes) "jbyteArray"; +%typemap(jtype) (uint8_t *data, int bytes) "byte[]"; +%typemap(jstype) (uint8_t *data, int bytes) "byte[]"; + +%typemap(javain) (uint8_t *data, int bytes) "$javainput"; + +%typemap(in) (uint8_t *data, int bytes) { + $1 = (uint8_t *) JCALL2(GetByteArrayElements, jenv, $input, NULL); + $2 = JCALL1(GetArrayLength, jenv, $input); +} + +%typemap(freearg) (uint8_t *data, int bytes) { + JCALL3(ReleaseByteArrayElements, jenv, $input, (jbyte *)$1, 0); +} + +%{ + #include "lcm1602.hpp" +%} + +%include "lcm1602.hpp" + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_i2clcd"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/lcm1602/jsupm_lcm1602.i b/src/lcm1602/jsupm_lcm1602.i new file mode 100644 index 00000000..db0e97d5 --- /dev/null +++ b/src/lcm1602/jsupm_lcm1602.i @@ -0,0 +1,8 @@ +%module jsupm_i2clcd +%include "../upm.i" +%include "../carrays_uint8_t.i" + +%include "lcm1602.hpp" +%{ + #include "lcm1602.hpp" +%} diff --git a/src/lcm1602/lcm1602.c b/src/lcm1602/lcm1602.c new file mode 100644 index 00000000..6bb1ceef --- /dev/null +++ b/src/lcm1602/lcm1602.c @@ -0,0 +1,640 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Based on UPM C++ drivers originally developed by: + * Author: Daniel Mosquera + * Copyright (c) 2013 Daniel Mosquera + * + * Author: Thomas Ingleby + * Copyright (c) 2014 Intel Corporation. + * + * Contributions: Sergey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "lcm1602.h" +#include "hd44780_bits.h" + +// forward declarations +static upm_result_t send(const lcm1602_context dev, uint8_t value, int mode); +static upm_result_t write4bits(const lcm1602_context dev, uint8_t value); +static upm_result_t expandWrite(const lcm1602_context dev, uint8_t value); +static upm_result_t pulseEnable(const lcm1602_context dev, uint8_t value); + +lcm1602_context lcm1602_i2c_init(int bus, int address, bool is_expander, + uint8_t num_columns, uint8_t num_rows) +{ + lcm1602_context dev = + (lcm1602_context)malloc(sizeof(struct _lcm1602_context)); + + if (!dev) + return NULL; + + // make sure MRAA is initialized + int mraa_rv; + if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); + lcm1602_close(dev); + return NULL; + } + + memset((void *)dev, 0, sizeof(struct _lcm1602_context)); + + // initialize the MRAA context + + if (!(dev->i2c = mraa_i2c_init(bus))) + { + printf("%s: mraa_i2c_init failed.\n", __FUNCTION__); + lcm1602_close(dev); + + return NULL; + } + + // now check the address... + if (mraa_i2c_address(dev->i2c, address) != MRAA_SUCCESS) + { + printf("%s: mraa_i2c_address failed.\n", __FUNCTION__); + + lcm1602_close(dev); + + return NULL; + } + + dev->isI2C = true; + dev->backlight = HD44780_BACKLIGHT; + dev->columns = num_columns; + dev->rows = num_rows; + + // if we are not dealing with an expander we will only initialize + // the I2C context and bail, leaving it up to the caller to handle + // further communications (like JHD1313M1) + + if (!is_expander) + return dev; + + upm_delay_us(50000); + lcm1602_backlight_on(dev, true); + upm_delay_us(100000); + + // try to put us into 4 bit mode + write4bits(dev, 0x03 << 4); + upm_delay_us(4500); + + write4bits(dev, 0x30); + upm_delay_us(4500); + + write4bits(dev,0x30); + upm_delay_us(150); + + // Put us into 4 bit mode, for realz yo. + write4bits(dev, 0x20); + + // Set number of lines + lcm1602_command(dev, HD44780_FUNCTIONSET | 0x0f); + + // default display control + dev->displayControl = HD44780_DISPLAYON | HD44780_CURSOROFF + | HD44780_BLINKOFF; + + lcm1602_command(dev, HD44780_DISPLAYCONTROL | dev->displayControl); + upm_delay_us(2000); + lcm1602_clear(dev); + + // Set entry mode. + dev->entryDisplayMode = HD44780_ENTRYLEFT | HD44780_ENTRYSHIFTDECREMENT; + lcm1602_command(dev, HD44780_ENTRYMODESET | dev->entryDisplayMode); + + lcm1602_home(dev); + + return dev; +} + +lcm1602_context lcm1602_gpio_init(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t num_columns, + uint8_t num_rows) +{ + lcm1602_context dev = + (lcm1602_context)malloc(sizeof(struct _lcm1602_context)); + + if (!dev) + return NULL; + + // make sure MRAA is initialized + int mraa_rv; + if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); + lcm1602_close(dev); + return NULL; + } + + memset((void *)dev, 0, sizeof(struct _lcm1602_context)); + + // initialize the MRAA contexts + + if (!(dev->gpioRS = mraa_gpio_init(rs))) + { + printf("%s: mraa_gpio_init(rs) failed.\n", __FUNCTION__); + lcm1602_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpioRS, MRAA_GPIO_OUT); + + if (!(dev->gpioEN = mraa_gpio_init(enable))) + { + printf("%s: mraa_gpio_init(enable) failed.\n", __FUNCTION__); + lcm1602_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpioEN, MRAA_GPIO_OUT); + + if (!(dev->gpioD0 = mraa_gpio_init(d0))) + { + printf("%s: mraa_gpio_init(d0) failed.\n", __FUNCTION__); + lcm1602_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpioD0, MRAA_GPIO_OUT); + + if (!(dev->gpioD1 = mraa_gpio_init(d1))) + { + printf("%s: mraa_gpio_init(d1) failed.\n", __FUNCTION__); + lcm1602_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpioD1, MRAA_GPIO_OUT); + + if (!(dev->gpioD2 = mraa_gpio_init(d2))) + { + printf("%s: mraa_gpio_init(d2) failed.\n", __FUNCTION__); + lcm1602_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpioD2, MRAA_GPIO_OUT); + + if (!(dev->gpioD3 = mraa_gpio_init(d3))) + { + printf("%s: mraa_gpio_init(d3) failed.\n", __FUNCTION__); + lcm1602_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpioD3, MRAA_GPIO_OUT); + + dev->isI2C = false; + // no backlight for GPIO + dev->backlight = 0; + dev->columns = num_columns; + dev->rows = num_rows; + + // set RS and Enable low to begin issuing commands + mraa_gpio_write(dev->gpioRS, 0); + mraa_gpio_write(dev->gpioEN, 0); + + // wait to stabilize + upm_delay_us(100000); + + // set 4bit mode + + // These steps are adapted from the HD44780 datasheet, figure 24 + + // try 1 + write4bits(dev, 0x03); + upm_delay_us(4500); + + // try 2 + write4bits(dev, 0x03); + upm_delay_us(4500); + + // try 3 + write4bits(dev, 0x03); + upm_delay_us(150); + + // Finally, put into 4 bit mode + write4bits(dev, 0x02); + + // Set number of lines + lcm1602_command(dev, HD44780_FUNCTIONSET | HD44780_2LINE | HD44780_4BITMODE + | HD44780_5x8DOTS); + + + dev->displayControl = HD44780_DISPLAYON | HD44780_CURSOROFF + | HD44780_BLINKOFF; + lcm1602_command(dev, HD44780_DISPLAYCONTROL | dev->displayControl); + upm_delay_us(2000); + lcm1602_clear(dev); + + // Set entry mode. + dev->entryDisplayMode = HD44780_ENTRYLEFT | HD44780_ENTRYSHIFTDECREMENT; + lcm1602_command(dev, HD44780_ENTRYMODESET | dev->entryDisplayMode); + + lcm1602_home(dev); + + return dev; +} + +void lcm1602_close(lcm1602_context dev) +{ + assert(dev != NULL); + + if (dev->i2c) + mraa_i2c_stop(dev->i2c); + + if (dev->gpioRS) + mraa_gpio_close(dev->gpioRS); + if (dev->gpioEN) + mraa_gpio_close(dev->gpioEN); + if (dev->gpioD0) + mraa_gpio_close(dev->gpioD0); + if (dev->gpioD1) + mraa_gpio_close(dev->gpioD1); + if (dev->gpioD2) + mraa_gpio_close(dev->gpioD2); + if (dev->gpioD3) + mraa_gpio_close(dev->gpioD3); + + free(dev); +} + +upm_result_t lcm1602_write(const lcm1602_context dev, char *buffer, + int len) +{ + assert(dev != NULL); + + upm_result_t error = UPM_SUCCESS; + + int i; + for (i=0; icolumns; + uint8_t offset = column; + + switch (dev->rows) + { + case 1: + // Single row displays with more than 8 columns usually have their + // DDRAM split in two halves. The first half starts at address 00. + // The second half starts at address 40. E.g. 16x2 DDRAM mapping: + // 00 01 02 03 04 05 06 07 40 41 42 43 44 45 46 47 + if (dev->columns > 8) + { + offset = (column % (dev->columns / 2)) + + (column / (dev->columns / 2)) * 0x40; + } + break; + + case 2: + // this should work for any display with two rows + // DDRAM mapping: + // 00 .. 27 + // 40 .. 67 + offset += row * 0x40; + break; + + case 4: + if (dev->columns == 16) + { + // 16x4 display + // DDRAM mapping: + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + // 40 41 42 43 43 45 46 47 48 49 4A 4B 4C 4D 4E 4F + // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + // 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F + int row_addr[] = { 0x00, 0x40, 0x10, 0x50 }; + offset += row_addr[row]; + } + else + { + // 20x4 display + // DDRAM mapping: + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 + // 40 41 42 43 43 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 + // 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 + // 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 + int row_addr[] = { 0x00, 0x40, 0x14, 0x54 }; + offset += row_addr[row]; + } + break; + } + + return lcm1602_command(dev, HD44780_CMD | offset); +} + +upm_result_t lcm1602_clear(const lcm1602_context dev) +{ + assert(dev != NULL); + + upm_result_t ret; + ret = lcm1602_command(dev, HD44780_CLEARDISPLAY); + upm_delay_us(2000); // this command takes awhile + return ret; +} + +upm_result_t lcm1602_home(const lcm1602_context dev) +{ + assert(dev != NULL); + + upm_result_t ret; + ret = lcm1602_command(dev, HD44780_RETURNHOME); + upm_delay_us(2000); // this command takes awhile + return ret; +} + +upm_result_t lcm1602_create_char(const lcm1602_context dev, + unsigned int slot, + lcm1602_custom_char_t data) +{ + assert(dev != NULL); + + upm_result_t error = UPM_SUCCESS; + + slot &= 0x07; // only have 8 positions we can set + + error = lcm1602_command(dev, HD44780_SETCGRAMADDR | (slot << 3)); + + if (error == UPM_SUCCESS) + { + int i; + for (i = 0; i < 8; i++) { + error = lcm1602_data(dev, data[i]); + } + } + + return error; +} + +upm_result_t lcm1602_display_on(const lcm1602_context dev, bool on) +{ + assert(dev != NULL); + + if (on) + dev->displayControl |= HD44780_DISPLAYON; + else + dev->displayControl &= ~HD44780_DISPLAYON; + + return lcm1602_command(dev, HD44780_DISPLAYCONTROL | dev->displayControl); +} + +upm_result_t lcm1602_cursor_on(const lcm1602_context dev, bool on) +{ + assert(dev != NULL); + + if (on) + dev->displayControl |= HD44780_CURSORON; + else + dev->displayControl &= ~HD44780_CURSORON; + + return lcm1602_command(dev, HD44780_DISPLAYCONTROL | dev->displayControl); +} + +upm_result_t lcm1602_cursor_blink_on(const lcm1602_context dev, bool on) +{ + assert(dev != NULL); + + if (on) + dev->displayControl |= HD44780_BLINKON; + else + dev->displayControl &= ~HD44780_BLINKON; + + return lcm1602_command(dev, HD44780_DISPLAYCONTROL | dev->displayControl); +} + +upm_result_t lcm1602_backlight_on(const lcm1602_context dev, bool on) +{ + assert(dev != NULL); + + if (on) + dev->backlight = HD44780_BACKLIGHT; + else + dev->backlight = HD44780_NOBACKLIGHT; + + return expandWrite(dev, dev->backlight); +} + +upm_result_t lcm1602_scroll_display_left(const lcm1602_context dev) +{ + assert(dev != NULL); + + return lcm1602_command(dev, HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE + | HD44780_MOVELEFT); +} + +upm_result_t lcm1602_scroll_display_right(const lcm1602_context dev) +{ + assert(dev != NULL); + + return lcm1602_command(dev, HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE + | HD44780_MOVERIGHT); +} + +upm_result_t lcm1602_entry_left_to_right(const lcm1602_context dev, bool on) +{ + assert(dev != NULL); + + if (on) + dev->entryDisplayMode |= HD44780_ENTRYLEFT; + else + dev->entryDisplayMode &= ~HD44780_ENTRYLEFT; + + return lcm1602_command(dev, HD44780_ENTRYMODESET | dev->entryDisplayMode); +} + +upm_result_t lcm1602_autoscroll_on(const lcm1602_context dev, bool on) +{ + assert(dev != NULL); + + if (on) + dev->entryDisplayMode |= HD44780_ENTRYSHIFTINCREMENT; + else + dev->entryDisplayMode &= ~HD44780_ENTRYSHIFTINCREMENT; + + return lcm1602_command(dev, HD44780_ENTRYMODESET | dev->entryDisplayMode); +} + +upm_result_t lcm1602_command(const lcm1602_context dev, uint8_t cmd) +{ + assert(dev != NULL); + + return send(dev, cmd, 0); +} + +upm_result_t lcm1602_data(const lcm1602_context dev, uint8_t cmd) +{ + assert(dev != NULL); + return send(dev, cmd, HD44780_RS); // 1 +} + + +// static declarations +static upm_result_t send(const lcm1602_context dev, uint8_t value, + int mode) +{ + assert(dev != NULL); + + uint8_t h; + uint8_t l; + + upm_result_t rv = UPM_SUCCESS; + + if (dev->isI2C) + { + h = value & 0xf0; + l = (value << 4) & 0xf0; + if (write4bits(dev, h | mode)) + rv = UPM_ERROR_OPERATION_FAILED; + if (write4bits(dev, l | mode)) + rv = UPM_ERROR_OPERATION_FAILED; + + return rv; + } + + // else, gpio (4 bit) + + // register select + if (mraa_gpio_write(dev->gpioRS, mode)) + { + printf("%s: mraa_gpio_write() failed\n", __FUNCTION__); + rv = UPM_ERROR_OPERATION_FAILED; + } + + h = value >> 4; + l = value & 0x0f; + + if (write4bits(dev, h)) + rv = UPM_ERROR_OPERATION_FAILED; + if (write4bits(dev, l)) + rv = UPM_ERROR_OPERATION_FAILED; + + return rv; +} + +static upm_result_t write4bits(const lcm1602_context dev, + uint8_t value) +{ + assert(dev != NULL); + + upm_result_t rv = UPM_SUCCESS; + + if (dev->isI2C) + { + if (expandWrite(dev, value)) + rv = UPM_ERROR_OPERATION_FAILED; + if (pulseEnable(dev, value)) + rv = UPM_ERROR_OPERATION_FAILED; + + return rv; + } + + // else gpio + mraa_result_t mrv = MRAA_SUCCESS; + mrv = mraa_gpio_write(dev->gpioD0, ((value >> 0) & 0x01) ); + mrv = mraa_gpio_write(dev->gpioD1, ((value >> 1) & 0x01) ); + mrv = mraa_gpio_write(dev->gpioD2, ((value >> 2) & 0x01) ); + mrv = mraa_gpio_write(dev->gpioD3, ((value >> 3) & 0x01) ); + + if (mrv) + { + printf("%s: mraa_gpio_write() failed\n", __FUNCTION__); + rv = UPM_ERROR_OPERATION_FAILED; + } + + if (pulseEnable(dev, value)) // value is ignored here for gpio + { + printf("%s: pulseEnable() failed\n", __FUNCTION__); + rv = UPM_ERROR_OPERATION_FAILED; + } + + return rv; +} + +static upm_result_t expandWrite(const lcm1602_context dev, + uint8_t value) +{ + assert(dev != NULL); + + // invalid for gpio + if (!dev->isI2C) + return UPM_ERROR_NO_RESOURCES; + + uint8_t buffer = value | dev->backlight; + + if (mraa_i2c_write_byte(dev->i2c, buffer)) + { + printf("%s: mraa_i2c_write_byte() failed\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +static upm_result_t pulseEnable(const lcm1602_context dev, + uint8_t value) +{ + assert(dev != NULL); + + upm_result_t rv = UPM_SUCCESS; + + if (dev->isI2C) + { + if (expandWrite(dev, value | HD44780_EN)) + rv = UPM_ERROR_OPERATION_FAILED; + upm_delay_us(1); + + if (expandWrite(dev, value & ~HD44780_EN)) + rv = UPM_ERROR_OPERATION_FAILED; + + upm_delay_us(50); + return rv; + } + + // else gpio + + mraa_result_t mrv = MRAA_SUCCESS; + mrv = mraa_gpio_write(dev->gpioEN, 1); + upm_delay_us(1); // must be > 450ns + mrv = mraa_gpio_write(dev->gpioEN, 0); + upm_delay_us(100); // must be >37us + + if (mrv) + { + printf("%s: mraa_gpio_write() failed\n", __FUNCTION__); + rv = UPM_ERROR_OPERATION_FAILED; + } + + return rv; +} diff --git a/src/lcm1602/lcm1602.h b/src/lcm1602/lcm1602.h new file mode 100644 index 00000000..000a6ae4 --- /dev/null +++ b/src/lcm1602/lcm1602.h @@ -0,0 +1,269 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Based on UPM C++ drivers originally developed by: + * Author: Daniel Mosquera + * Copyright (c) 2013 Daniel Mosquera + * + * Author: Thomas Ingleby + * Copyright (c) 2014 Intel Corporation. + * + * Contributions: Sergey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + /** + * @file lcm1602.h + * @library lcm1602 + * @brief C API for the LCM1602 family of LCD displays + * + * @include lcm1602.c + */ + + /** + * Device context + */ + typedef struct _lcm1602_context { + // A large number of GPIOs + mraa_gpio_context gpioRS; + mraa_gpio_context gpioEN; + mraa_gpio_context gpioD0; + mraa_gpio_context gpioD1; + mraa_gpio_context gpioD2; + mraa_gpio_context gpioD3; + + // I2C command control + mraa_i2c_context i2c; + + // what interface are we using? + bool isI2C; + + // configuration + unsigned int columns; + unsigned int rows; + + // display command + uint8_t displayControl; + uint8_t entryDisplayMode; + uint8_t backlight; + } *lcm1602_context; + + /** + * Custom character. + */ + typedef char lcm1602_custom_char_t[8]; + + /** + * LCM1602 I2C initialization. This is used for those devices + * using an I2C expander, or other I2C interface. If you are + * using an expander, you must specify true for the is_expander + * argument. If not, then the caller of this function is + * responsible for any transfers to the I2C device. Internally, + * only direct access to an I2C expander is supported. + * + * @param bus I2C bus to use. + * @param address I2C address the LCD is configured for. + * @param is_expander True if we are dealing with an I2C expander, + * false otherwise. + * @param num_columns Number of columns the display has. + * @param num_rows Number of rows the display has. + * @return Device Ccontext, or NULL on error. + */ + lcm1602_context lcm1602_i2c_init(int bus, int address, bool is_expander, + uint8_t num_columns, uint8_t num_rows); + + /** + * LCM1602 GPIO (parallel) initialization, used for GPIO based + * HD44780 controllers supporting RS, Enable, and 4 data pins in + * 4-bit mode. + * + * @param rs Register select pin + * @param enable Enable pin + * @param d0 Data 0 pin + * @param d1 Data 1 pin + * @param d2 Data 2 pin + * @param d3 Data 3 pin + * @param num_columns Number of columns the display has. Default 16. + * @param num_rows Number of rows the display has. Default 2. + * @return Device Ccontext, or NULL on error. + */ + lcm1602_context lcm1602_gpio_init(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t num_columns, + uint8_t num_rows); + + /** + * LCM1602 close. + * + * @param dev The device context. + */ + void lcm1602_close(lcm1602_context dev); + + /** + * Writes a string to the LCD. + * + * @param dev The device context. + * @param buffer Character buffer containing characters to write to + * the display; note: only ASCII characters are supported + * @param len The number of characters to write. + * @return UPM result. + */ + upm_result_t lcm1602_write(const lcm1602_context dev, char *buffer, + int len); + + /** + * Sets the cursor to specified coordinates + * + * @param dev The device context. + * @param row Row to set the cursor to. + * @param column Column to set the cursor to. + * @return UPM result. + */ + upm_result_t lcm1602_set_cursor(const lcm1602_context dev, + unsigned int row, unsigned int column); + + /** + * Clears the display of all characters. + * + * @param dev The device context. + * @return UPM result. + */ + upm_result_t lcm1602_clear(const lcm1602_context dev); + + /** + * Returns to the home coordinates (0,0). + * + * @param dev The device context. + * @return UPM result. + */ + upm_result_t lcm1602_home(const lcm1602_context dev); + + /** + * Create a custom character. + * + * @param dev The device context. + * @param slot The character slot to write, only 8 are available. + * @param data The character data (8 bytes) making up the character. + * @return UPM result. + */ + upm_result_t lcm1602_create_char(const lcm1602_context dev, + unsigned int slot, + lcm1602_custom_char_t data); + + /** + * Turn the display on. + * + * @param dev The device context. + * @param on true to turn display on, false otherwise. + * @return UPM result. + */ + upm_result_t lcm1602_display_on(const lcm1602_context dev, bool on); + + /** + * Turn the cursor on. + * + * @param dev The device context. + * @param on true to turn cursor on, false otherwise. + * @return UPM result. + */ + upm_result_t lcm1602_cursor_on(const lcm1602_context dev, bool on); + + /** + * Turn cursor blink on. + * + * @param dev The device context. + * @param on true to turn cursor blink on, false otherwise. + * @return UPM result. + */ + upm_result_t lcm1602_cursor_blink_on(const lcm1602_context dev, bool on); + + /** + * Turn backlight on. + * + * @param dev The device context. + * @param on true to turn backlight on, false otherwise. + * @return UPM result. + */ + upm_result_t lcm1602_backlight_on(const lcm1602_context dev, bool on); + + /** + * Scroll the display left, without changing the character RAM. + * + * @param dev The device context. + * @return UPM result. + */ + upm_result_t lcm1602_scroll_display_left(const lcm1602_context dev); + + /** + * Scroll the display right, without changing the character RAM. + * + * @param dev The device context. + * @return UPM result. + */ + upm_result_t lcm1602_scroll_display_right(const lcm1602_context dev); + + /** + * Set the entry mode so that characters are added left to right. + * + * @param dev The device context. + * @param on true to add characters left to right, false for right + * to left. + * @return UPM result. + */ + upm_result_t lcm1602_entry_left_to_right(const lcm1602_context dev, + bool on); + + /** + * Right justify text entered from the cursor. + * + * @param dev The device context. + * @param on true right justify text, false to left justify text. + * @return UPM result. + */ + upm_result_t lcm1602_autoscroll_on(const lcm1602_context dev, bool on); + + + // Add a command() and data() virtual member functions, with a + // default implementation in lcm1602. This is expected to be + // implemented by derived classes with different needs (Jhd1313m1, + // for example). + upm_result_t lcm1602_command(const lcm1602_context dev, uint8_t cmd); + upm_result_t lcm1602_data(const lcm1602_context dev, uint8_t data); + + +#ifdef __cplusplus +} +#endif diff --git a/src/lcm1602/pyupm_lcm1602.i b/src/lcm1602/pyupm_lcm1602.i new file mode 100644 index 00000000..38e7272b --- /dev/null +++ b/src/lcm1602/pyupm_lcm1602.i @@ -0,0 +1,12 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_i2clcd +%include "../upm.i" +%include "../carrays_uint8_t.i" + +%feature("autodoc", "3"); + +%include "lcm1602.hpp" +%{ + #include "lcm1602.hpp" +%}