LCDモジュール AQM0802 を制御するクラス

LcdAqm0802.cpp

Committer:
orivant
Date:
2016-12-18
Revision:
0:ea6cbd4e50ce

File content as of revision 0:ea6cbd4e50ce:

/**
* @file    LCDモジュール AQM0802 を制御するクラスの定義
*/
#include "LcdAqm0802.h"

const uint8_t  LcdAqm0802::LINE_HEAD_ADDR_TBL[LINE_COUNT] = {0x00, 0x40};   //!< 行先頭アドレス

/** コンストラクタ
*/
LcdAqm0802::LcdAqm0802(PinName pinReset, PinName pinSDA, PinName pinSCL)
    :m_pinReset(pinReset)
    ,m_pinSDA(pinSDA)
    ,m_pinSCL(pinSCL)
    ,m_resetOut(pinReset)
    ,m_i2c(pinSDA, pinSCL)
    ,m_displayVisible(true)
    ,m_cursorVisible(false)
    ,m_blinkEnable(false)
    ,m_contrast(DEFAULT_CONTRAST)
    ,m_currentLineNo(0)
    ,m_currentCharaNo(0)
{
    m_resetOut = 1;
    m_i2c.frequency(I2C_MAX_FREQUENCY);
}
/** コンストラクタ
*/
LcdAqm0802::LcdAqm0802(PinName pinReset, I2C& i2c)
    :m_pinReset(pinReset)
    ,m_pinSDA(NC)
    ,m_pinSCL(NC)
    ,m_resetOut(pinReset)
    ,m_i2c(i2c)
    ,m_displayVisible(true)
    ,m_cursorVisible(false)
    ,m_blinkEnable(false)
    ,m_contrast(DEFAULT_CONTRAST)
    ,m_currentLineNo(0)
    ,m_currentCharaNo(0)
{
    m_resetOut = 1;
}
/** デストラクタ
*/
LcdAqm0802::~LcdAqm0802()
{
}


/** 初期化
*/
void LcdAqm0802::initialize()
{
    static const int32_t COMMAND_MARGINE_WAIT_US = 27;
    uint8_t bufByte;
    
    // Function set
    writeCommand(FUNC_SET_BYTE_NRM);
    wait_us(COMMAND_MARGINE_WAIT_US);
    // Function set
    writeCommand(FUNC_SET_BYTE_EX);
    wait_us(COMMAND_MARGINE_WAIT_US);
    // Internal OSC frequency
    writeCommand(0x14);
    wait_us(COMMAND_MARGINE_WAIT_US);
    // Contrast set
    bufByte = calcContrastSettingByteLowwer();
    writeCommand(bufByte);
    wait_us(COMMAND_MARGINE_WAIT_US);
    // Power/ICON/Contrast control
    bufByte = calcContrastSettingByteUpper();
    writeCommand(bufByte);
    wait_us(COMMAND_MARGINE_WAIT_US);
    // Follower control
    writeCommand(0x6c);
    wait_ms(200);
    // Function set
    writeCommand(FUNC_SET_BYTE_NRM);
    wait_us(COMMAND_MARGINE_WAIT_US);
    // Display ON/OFF control
    bufByte = calcDisplayControlByteData();
    writeCommand(bufByte);
    wait_us(COMMAND_MARGINE_WAIT_US);
    // Clear Display
    writeCommand(0x01);
    wait_us(1080);
}
/** リセット
*/
void LcdAqm0802::reset()
{
    // リセット信号を送信
    m_resetOut = 0;
    // ちょっと待つ
    wait_us(5);
    // リセット信号を止める
    m_resetOut = 1;
}


/** 1文字描画
*/
void LcdAqm0802::putc(char c)
{
    writeData(c);
    bool ret = incCursorPos();
    if (ret) {
        // 改行が必要なら、カーソル位置を変更
        uint8_t bufByte = calcCursorAddr(m_currentLineNo, m_currentCharaNo);
        writeCommand(0x80 | bufByte);
    }
}
/** 文字列描画
*/
void LcdAqm0802::puts(const char* pStr)
{
    // 文字を判定しながら入力する処理
    uint8_t addr;
    bool stayLocate = false;
    bool chainPut = false;
    const char* pt = pStr;
    bool ret;
    // LCD操作開始
    m_i2c.start();
    m_i2c.write(ADDR_BYTE_WRITE);
    while (*pt) {
        const char &c = *pt;
        if (c == '\r') {
            // 復帰コードの場合、行の先頭にカーソルを移動する
            m_currentCharaNo = 0;
            // 実際の移動処理は後で行う
            stayLocate = true;
        }
        else if (c == '\n') {
            // 改行コードの場合、次の行にカーソルを移動する
            m_currentLineNo = (m_currentLineNo + 1) % LINE_COUNT;
            // 実際の移動処理は後で行う
            stayLocate = true;
        }
        else {
            // 文字入力処理
            if (stayLocate) {
                // 復帰・改行の予約がある場合は先に処理
                if (chainPut) {
                    // 文字入力中の場合は一旦通信を止めないとコマンドを受け付けない
                    m_i2c.stop();
                    m_i2c.start();
                    m_i2c.write(ADDR_BYTE_WRITE);
                }
                addr = calcCursorAddr(m_currentLineNo, m_currentCharaNo);
                m_i2c.write(CTRL_BYTE_SINGLE_COMMAND);
                m_i2c.write(0x80 | addr);
                stayLocate = false;
                chainPut = false;
            }
            if (!chainPut) {
                // 文字入力が途切れた後はデータ入力コントロールを行う
                m_i2c.write(CTRL_BYTE_CHAIN_DATA);
                chainPut = true;
            }
            // 文字入力
            m_i2c.write(c);
            // カーソル位置管理処理
            ret = incCursorPos();
            if (ret) {
                // 改行が発生したのでカーソル位置変更
                // 実際の移動処理は後で行う
                stayLocate = true;
            }
        }
        pt++;
    }
    if (stayLocate) {
        // 復帰・改行の予約が残っている場合は処理する
        if (chainPut) {
            // 文字入力中の場合は一旦通信を止めないとコマンドを受け付けない
            m_i2c.stop();
            m_i2c.start();
            m_i2c.write(ADDR_BYTE_WRITE);
        }
        addr = calcCursorAddr(m_currentLineNo, m_currentCharaNo);
        m_i2c.write(CTRL_BYTE_SINGLE_COMMAND);
        m_i2c.write(0x80 | addr);
    }
    // LCD操作終了
    m_i2c.stop();
}
/** フォーマット指定描画
*/
void LcdAqm0802::printf(const char* pFormat, ...)
{
    va_list ap;
    va_start(ap, pFormat);
    char* allocatedBuffer;
    int size = vasprintf(&allocatedBuffer, pFormat, ap);
    va_end(ap);
    puts(allocatedBuffer);
    free(allocatedBuffer);
}


/** カーソル位置変更
*/
void LcdAqm0802::locate(uint8_t lineNo, uint8_t charaNo)
{
    // カーソル位置クランプ
    if (lineNo >= LINE_COUNT) lineNo = LINE_COUNT - 1;
    if (charaNo >= LINE_CHARA_COUNT) charaNo = LINE_CHARA_COUNT - 1;
    m_currentLineNo = lineNo;
    m_currentCharaNo = charaNo;
    uint8_t addr = calcCursorAddr(m_currentLineNo, m_currentCharaNo);
    writeCommand(0x80 | addr);
}
/** カーソル位置取得:行番号
*/
uint8_t LcdAqm0802::getCurrentLineNo() const
{
    return m_currentLineNo;
}
/** カーソル位置取得:文字番号
*/
uint8_t LcdAqm0802::getCurrentCharaNo() const
{
    return m_currentCharaNo;
}


/** 画面クリア
*/
void LcdAqm0802::clearDisplay()
{
    writeCommand(0x01);
    m_currentLineNo = 0;
    m_currentCharaNo = 0;
    wait_ms(1);
}
/** 行クリア
*/
void LcdAqm0802::clearLine(uint8_t lineNo)
{
    if (lineNo >= LINE_COUNT) return;
    // クリアする行の先頭に移動
    uint8_t addr = calcCursorAddr(lineNo, 0);
    writeCommand(0x80 | addr);
    // 1行全体に' '書き込み
    uint8_t data[LINE_CHARA_COUNT];
    for (uint8_t i = 0; i < LINE_CHARA_COUNT; i++) data[i] = ' ';
    writeData(data, LINE_CHARA_COUNT);
    // カーソル位置をクリアした行の先頭に移動
    m_currentLineNo = lineNo;
    m_currentCharaNo = 0;
    addr = calcCursorAddr(m_currentLineNo, m_currentCharaNo);
    writeCommand(0x80 | addr);
}
/** 範囲クリア
*/
void LcdAqm0802::clearRange(uint8_t startLineNo, uint8_t startCharaNo, uint8_t endLineNo, uint8_t endCharaNo)
{
    if (startLineNo >= LINE_COUNT) return;
    if (startCharaNo >= LINE_CHARA_COUNT) return;
    if (startLineNo > endLineNo) return;
    if (startCharaNo > endCharaNo) return;
    if (endLineNo >= LINE_COUNT) endLineNo = LINE_COUNT - 1;
    if (endCharaNo >= LINE_CHARA_COUNT) endCharaNo = LINE_CHARA_COUNT - 1;
    uint8_t addr;
    const uint8_t eraseCount = endCharaNo - startCharaNo + 1;
    uint8_t eraseData[eraseCount];
    for (uint8_t i = 0; i < eraseCount; i++) eraseData[i] = ' ';
    for (uint8_t lineNo = startLineNo; lineNo <= endLineNo; lineNo++) {
        // 対象行の削除開始位置にカーソル移動
        addr = calcCursorAddr(lineNo, startCharaNo);
        writeCommand(0x80 | addr);
        // 消す範囲に' 'を書き込み
        writeData(eraseData, eraseCount);
    }
    // クリアした範囲の開始位置にカーソルを移動
    m_currentLineNo = startLineNo;
    m_currentCharaNo = startCharaNo;
    addr = calcCursorAddr(m_currentLineNo, m_currentCharaNo);
    writeCommand(0x80 | addr);
}


/** 画面の表示設定
*/
void LcdAqm0802::setDisplayVisible(bool visible)
{
    if (m_displayVisible == visible) return;
    m_displayVisible = visible;
    uint8_t command = calcDisplayControlByteData();
    writeCommand(command);
}
/** 画面の表示設定取得
*/
bool LcdAqm0802::getDisplayVisible() const
{
    return m_displayVisible;
}
/** カーソルの表示設定
*/
void LcdAqm0802::setCursorVisible(bool visible)
{
    if (m_cursorVisible == visible) return;
    m_cursorVisible = visible;
    uint8_t command = calcDisplayControlByteData();
    writeCommand(command);
}
/** カーソルの表示設定取得
*/
bool LcdAqm0802::getCursorVisible() const
{
    return m_cursorVisible;
}
/** カーソルの点滅設定
*/
void LcdAqm0802::setBlinkEnable(bool enable)
{
    if (m_blinkEnable == enable) return;
    m_blinkEnable = enable;
    uint8_t command = calcDisplayControlByteData();
    writeCommand(command);
}
/** カーソルの点滅設定取得
*/
bool LcdAqm0802::getBlinkEnable() const
{
    return m_blinkEnable;
}


/** コントラスト設定
*/
void LcdAqm0802::setContrast(uint8_t val)
{
    if (val > MAX_CONTRAST) val = MAX_CONTRAST;
    if (m_contrast == val) return;
    
    m_contrast = val;
    
    // LCD操作開始
    m_i2c.start();
    m_i2c.write(ADDR_BYTE_WRITE);
    // 拡張コマンド使用設定
    m_i2c.write(CTRL_BYTE_SINGLE_COMMAND);
    m_i2c.write(FUNC_SET_BYTE_EX);
    // コントラスト設定
    unsigned char bufByte = calcContrastSettingByteLowwer();
    m_i2c.write(CTRL_BYTE_SINGLE_COMMAND);
    m_i2c.write(bufByte);
    bufByte = calcContrastSettingByteUpper();
    m_i2c.write(CTRL_BYTE_SINGLE_COMMAND);
    m_i2c.write(bufByte);
    // 拡張コマンド使用解除
    m_i2c.write(CTRL_BYTE_SINGLE_COMMAND);
    m_i2c.write(FUNC_SET_BYTE_NRM);
    // LCD操作終了
    m_i2c.stop();
}
/** コントラスト取得
*/
unsigned char LcdAqm0802::getContrast() const
{
    return m_contrast;
}


/** LCDコマンド単発書き込み
*/
void LcdAqm0802::writeCommand(uint8_t commandByte)
{
    m_i2c.start();                          // I2C通信開始
    m_i2c.write(ADDR_BYTE_WRITE);           // LCDへ書き込み指定
    m_i2c.write(CTRL_BYTE_SINGLE_COMMAND);  // 単発コマンド指定
    m_i2c.write(commandByte);               // コマンド書き込み
    m_i2c.stop();                           // I2C通信終了
}
/** LCDコマンド連続書き込み
*/
void LcdAqm0802::writeCommand(uint8_t commandBytes[], uint8_t commandCount)
{
    m_i2c.start();                          // I2C通信開始
    m_i2c.write(ADDR_BYTE_WRITE);           // LCDへ書き込み指定
    m_i2c.write(CTRL_BYTE_CHAIN_COMMAND);   // 連続コマンド指定
    for (uint8_t i = 0; i < commandCount; i++) {
        m_i2c.write(commandBytes[i]);       // コマンド書き込み
    }
    m_i2c.stop();                           // I2C通信終了
}
/** LCDデータ単発書き込み
*/
void LcdAqm0802::writeData(uint8_t dataByte)
{
    m_i2c.start();                          // I2C通信開始
    m_i2c.write(ADDR_BYTE_WRITE);           // LCDへ書き込み指定
    m_i2c.write(CTRL_BYTE_SINGLE_DATA);     // 単発データ指定
    m_i2c.write(dataByte);                  // データ書き込み
    m_i2c.stop();                           // I2C通信終了
}
/** LCDデータ連続書き込み
*/
void LcdAqm0802::writeData(uint8_t dataBytes[], uint8_t dataCount)
{
    m_i2c.start();                          // I2C通信開始
    m_i2c.write(ADDR_BYTE_WRITE);           // LCDへ書き込み指定
    m_i2c.write(CTRL_BYTE_CHAIN_DATA);      // 連続データ指定
    for (uint8_t i = 0; i < dataCount; i++) {
        m_i2c.write(dataBytes[i]);          // データ書き込み
    }
    m_i2c.stop();                           // I2C通信終了
}


/** コントラスト設定用バイトデータ生成(上位ビット)
*/
uint8_t LcdAqm0802::calcContrastSettingByteUpper() const
{
    uint8_t ret = 0x54;
    uint8_t contrast = (m_contrast >> 4) & 0x03;
    ret = ret | contrast;
    return ret;
}
/** コントラスト設定用バイトデータ生成(下位ビット)
*/
uint8_t LcdAqm0802::calcContrastSettingByteLowwer() const
{
    uint8_t ret = 0x70;
    uint8_t contrast = m_contrast & 0x0f;
    ret = ret | contrast;
    return ret;
}


/** ディスプレイ制御コマンド指定バイトデータの生成
*/
uint8_t LcdAqm0802::calcDisplayControlByteData(bool displayVisible, bool cursorVisible, bool blinkEnable) const
{
    uint8_t ret = 0x08;
    if (displayVisible) {
        ret = ret | 0x04;
    }
    if (cursorVisible) {
        ret = ret | 0x02;
    }
    if (blinkEnable) {
        ret = ret | 0x01;
    }
    return ret;
}


/** カーソル位置移動管理
*/
bool LcdAqm0802::incCursorPos()
{
    m_currentCharaNo++;
    if (m_currentCharaNo >= LINE_CHARA_COUNT) {
        m_currentCharaNo = 0;
        m_currentLineNo = (m_currentLineNo + 1) % LINE_COUNT;
        return true;
    }
    return false;
}


/** カーソル位置のアドレス計算
*/
uint8_t LcdAqm0802::calcCursorAddr(uint8_t lineNo, uint8_t charaNo)
{
    if (lineNo >= LINE_COUNT) lineNo = LINE_COUNT - 1;
    if (charaNo >= LINE_CHARA_COUNT) charaNo = LINE_CHARA_COUNT - 1;
    unsigned char addr = LINE_HEAD_ADDR_TBL[lineNo] + charaNo;
    return addr;
}