upm/src/lcd/lcm1602.cxx
Jon Trulson 0589f445f0 Wreorder: fix a variety of re-ordering warnings
Signed-off-by: Jon Trulson <jtrulson@ics.com>
2016-11-03 12:19:21 -06:00

475 lines
12 KiB
C++

/*
* The MIT License (MIT)
*
* Author: Daniel Mosquera
* Copyright (c) 2013 Daniel Mosquera
*
* Author: Thomas Ingleby <thomas.c.ingleby@intel.com>
* Copyright (c) 2014 Intel Corporation.
*
* Contributions: Jon Trulson <jtrulson@ics.com>
* Sergey Kiselev <sergey.kiselev@intel.com>
*
* 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 <string>
#include <stdexcept>
#include <unistd.h>
#include "hd44780_bits.hpp"
#include "lcm1602.hpp"
using namespace upm;
Lcm1602::Lcm1602(int bus_in, int addr_in, bool isExpander,
uint8_t numColumns, uint8_t numRows) :
m_numColumns(numColumns), m_numRows(numRows),
m_i2c_lcd_control(new mraa::I2c(bus_in)),
m_gpioRS(0), m_gpioEnable(0), m_gpioD0(0),
m_gpioD1(0), m_gpioD2(0), m_gpioD3(0)
{
mraa::Result error = mraa::SUCCESS;
m_name = "Lcm1602 (I2C)";
m_isI2C = true;
m_backlight = LCD_BACKLIGHT;
m_lcd_control_address = addr_in;
error = m_i2c_lcd_control->address(m_lcd_control_address);
if (error != mraa::SUCCESS) {
throw std::invalid_argument(std::string(__FUNCTION__) +
": I2c.address() failed");
return;
}
// default display control
m_displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
// if we are not dealing with an expander (say via a derived class
// like Jhd1313m1), then we do not want to execute the rest of the
// code below. Rather, the derived class's constructor should
// follow up with any setup required -- we will only initialize
// the I2C context and bail.
if (!isExpander)
return;
usleep(50000);
backlightOn();
usleep(100000);
write4bits(0x03 << 4);
usleep(4500);
write4bits(0x30);
usleep(4500);
write4bits(0x30);
usleep(150);
// Put into 4 bit mode
write4bits(0x20);
m_displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
// Set numeber of lines
command(LCD_FUNCTIONSET | 0x0f);
command(LCD_DISPLAYCONTROL | m_displayControl);
clear();
// Set entry mode.
m_entryDisplayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
command(LCD_ENTRYMODESET | m_entryDisplayMode);
home();
}
Lcm1602::Lcm1602(uint8_t rs, uint8_t enable, uint8_t d0,
uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t numColumns, uint8_t numRows) :
m_numColumns(numColumns), m_numRows(numRows),
m_i2c_lcd_control(0),
m_gpioRS(new mraa::Gpio(rs)), m_gpioEnable(new mraa::Gpio(enable)),
m_gpioD0(new mraa::Gpio(d0)), m_gpioD1(new mraa::Gpio(d1)),
m_gpioD2(new mraa::Gpio(d2)), m_gpioD3(new mraa::Gpio(d3))
{
m_name = "Lcm1602 (4-bit GPIO)";
m_isI2C = false;
m_backlight = LCD_BACKLIGHT;
// setup our gpios
m_gpioRS->dir(mraa::DIR_OUT);
m_gpioEnable->dir(mraa::DIR_OUT);
m_gpioD0->dir(mraa::DIR_OUT);
m_gpioD1->dir(mraa::DIR_OUT);
m_gpioD2->dir(mraa::DIR_OUT);
m_gpioD3->dir(mraa::DIR_OUT);
// set RS and Enable low to begin issuing commands
m_gpioRS->write(0);
m_gpioEnable->write(0);
// wait to stabilize
usleep(100000);
// set 4bit mode
// These steps are adapted from the HD44780 datasheet, figure 24
// try 1
write4bits(0x03);
usleep(4500);
// try 2
write4bits(0x03);
usleep(4500);
// try 3
write4bits(0x03);
usleep(150);
// Finally, put into 4 bit mode
write4bits(0x02);
// Set number of lines
command(LCD_FUNCTIONSET | LCD_2LINE | LCD_4BITMODE | LCD_5x8DOTS);
m_displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
command(LCD_DISPLAYCONTROL | m_displayControl);
usleep(2000);
clear();
// Set entry mode.
m_entryDisplayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
command(LCD_ENTRYMODESET | m_entryDisplayMode);
home();
}
Lcm1602::~Lcm1602()
{
// clean up after ourselves
if (m_isI2C)
{
delete m_i2c_lcd_control;
}
else
{
delete m_gpioRS;
delete m_gpioEnable;
delete m_gpioD0;
delete m_gpioD1;
delete m_gpioD2;
delete m_gpioD3;
}
}
/*
* **************
* virtual area
* **************
*/
mraa::Result
Lcm1602::write(std::string msg)
{
mraa::Result error = mraa::SUCCESS;
for (std::string::size_type i = 0; i < msg.size(); ++i) {
error = data(msg[i]);
}
return error;
}
mraa::Result
Lcm1602::setCursor(int row, int column)
{
column = column % m_numColumns;
uint8_t offset = column;
switch (m_numRows)
{
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 (m_numColumns > 8)
{
offset = (column % (m_numColumns / 2)) +
(column / (m_numColumns / 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 (m_numColumns == 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 command(LCD_CMD | offset);
}
mraa::Result
Lcm1602::clear()
{
mraa::Result ret;
ret = command(LCD_CLEARDISPLAY);
usleep(2000); // this command takes awhile
return ret;
}
mraa::Result
Lcm1602::home()
{
mraa::Result ret;
ret = command(LCD_RETURNHOME);
usleep(2000); // this command takes awhile
return ret;
}
mraa::Result
Lcm1602::createChar(uint8_t charSlot, uint8_t charData[])
{
mraa::Result error = mraa::SUCCESS;
charSlot &= 0x07; // only have 8 positions we can set
error = command(LCD_SETCGRAMADDR | (charSlot << 3));
if (error == mraa::SUCCESS) {
for (int i = 0; i < 8; i++) {
error = data(charData[i]);
}
}
return error;
}
mraa::Result Lcm1602::displayOn()
{
m_displayControl |= LCD_DISPLAYON;
return command(LCD_DISPLAYCONTROL | m_displayControl);
}
mraa::Result Lcm1602::displayOff()
{
m_displayControl &= ~LCD_DISPLAYON;
return command(LCD_DISPLAYCONTROL | m_displayControl);
}
mraa::Result Lcm1602::cursorOn()
{
m_displayControl |= LCD_CURSORON;
return command(LCD_DISPLAYCONTROL | m_displayControl);
}
mraa::Result Lcm1602::cursorOff()
{
m_displayControl &= ~LCD_CURSORON;
return command(LCD_DISPLAYCONTROL | m_displayControl);
}
mraa::Result Lcm1602::cursorBlinkOn()
{
m_displayControl |= LCD_BLINKON;
return command(LCD_DISPLAYCONTROL | m_displayControl);
}
mraa::Result Lcm1602::cursorBlinkOff()
{
m_displayControl &= ~LCD_BLINKON;
return command(LCD_DISPLAYCONTROL | m_displayControl);
}
mraa::Result Lcm1602::backlightOn()
{
m_backlight = LCD_BACKLIGHT;
return expandWrite(m_backlight);
}
mraa::Result Lcm1602::backlightOff()
{
m_backlight = LCD_NOBACKLIGHT;
return expandWrite(m_backlight);
}
mraa::Result Lcm1602::scrollDisplayLeft()
{
return command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
mraa::Result Lcm1602::scrollDisplayRight()
{
return command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}
mraa::Result Lcm1602::entryLeftToRight()
{
m_entryDisplayMode |= LCD_ENTRYLEFT;
return command(LCD_ENTRYMODESET | m_entryDisplayMode);
}
mraa::Result Lcm1602::entryRightToLeft()
{
m_entryDisplayMode &= ~LCD_ENTRYLEFT;
return command(LCD_ENTRYMODESET | m_entryDisplayMode);
}
mraa::Result Lcm1602::autoscrollOn()
{
m_entryDisplayMode |= LCD_ENTRYSHIFTINCREMENT;
return command(LCD_ENTRYMODESET | m_entryDisplayMode);
}
mraa::Result Lcm1602::autoscrollOff()
{
m_entryDisplayMode &= ~LCD_ENTRYSHIFTINCREMENT;
return command(LCD_ENTRYMODESET | m_entryDisplayMode);
}
mraa::Result Lcm1602::command(uint8_t cmd)
{
return send(cmd, 0);
}
mraa::Result Lcm1602::data(uint8_t cmd)
{
return send(cmd, LCD_RS); // 1
}
/*
* **************
* private area
* **************
*/
mraa::Result
Lcm1602::send(uint8_t value, int mode)
{
mraa::Result ret = mraa::SUCCESS;
uint8_t h;
uint8_t l;
if (m_isI2C)
{
h = value & 0xf0;
l = (value << 4) & 0xf0;
ret = write4bits(h | mode);
ret = write4bits(l | mode);
return ret;
}
// else, gpio (4 bit)
// register select
m_gpioRS->write(mode);
h = value >> 4;
l = value & 0x0f;
ret = write4bits(h);
ret = write4bits(l);
return ret;
}
mraa::Result
Lcm1602::write4bits(uint8_t value)
{
mraa::Result ret = mraa::SUCCESS;
if (m_isI2C)
{
ret = expandWrite(value);
ret = pulseEnable(value);
return ret;
}
// else gpio
ret = m_gpioD0->write( ((value >> 0) & 0x01) );
ret = m_gpioD1->write( ((value >> 1) & 0x01) );
ret = m_gpioD2->write( ((value >> 2) & 0x01) );
ret = m_gpioD3->write( ((value >> 3) & 0x01) );
ret = pulseEnable(value); // value is ignored here for gpio
return ret;
}
mraa::Result
Lcm1602::expandWrite(uint8_t value)
{
// invalid for gpio
if (!m_isI2C)
return mraa::ERROR_INVALID_RESOURCE;
uint8_t buffer = value | m_backlight;
return m_i2c_lcd_control->writeByte(buffer);
}
mraa::Result
Lcm1602::pulseEnable(uint8_t value)
{
mraa::Result ret = mraa::SUCCESS;
if (m_isI2C)
{
ret = expandWrite(value | LCD_EN);
usleep(1);
ret = expandWrite(value & ~LCD_EN);
usleep(50);
return ret;
}
// else gpio
ret = m_gpioEnable->write(0);
usleep(1);
ret = m_gpioEnable->write(1);
usleep(1); // must be > 450ns
ret = m_gpioEnable->write(0);
usleep(100); // must be >37us
return ret;
}