
#include "libCharLCD/lcdInterface.h"

/// \brief Initialise : call after ChibiOS is running .
void lcdInterface_t::init()
{
}

void lcdInterface_t::setLineMode(ioline_t line, unsigned mode)
{
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_InitStruct.Pin = line.pin;
        GPIO_InitStruct.Mode = mode;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        HAL_GPIO_Init(line.gpio, &GPIO_InitStruct);
}

void lcdInterface_t::polledDelay(unsigned microsec)
{

        uint16_t tval = __HAL_TIM_GET_COUNTER(LCD_DELAY_CNT);

        // need to determine timer is "after"

        while ((__HAL_TIM_GET_COUNTER(LCD_DELAY_CNT) - tval) < microsec)
        {
        };
};

void lcdInterface_t::writeData4(uint8_t data_)
{
        // set write bit to low = write
        HAL_GPIO_WritePin(m_wrBit.gpio, m_wrBit.pin, GPIO_PIN_RESET);

        for (int i = 0; i < 4; i++)
                setLineMode(m_dBits[i], GPIO_MODE_OUTPUT_PP);

        for (int i = 0; i < 4; i++)
                HAL_GPIO_WritePin(m_dBits[i].gpio, m_dBits[i].pin, (data_ & (1 << i)) ? GPIO_PIN_SET : GPIO_PIN_RESET);

        polledDelay(2);
        // pulse enable line high
        HAL_GPIO_WritePin(m_eBit.gpio, m_eBit.pin, GPIO_PIN_SET);
        polledDelay(2);
        HAL_GPIO_WritePin(m_eBit.gpio, m_eBit.pin, GPIO_PIN_RESET);
}
void lcdInterface_t::writeData8(uint8_t data_)
{
        writeData4((data_ & 0xF0) >> 4);
        writeData4(data_ & 0x0F);
}

uint8_t lcdInterface_t::readData4()
{
        // clear write bit to high = read
        HAL_GPIO_WritePin(m_wrBit.gpio, m_wrBit.pin, GPIO_PIN_SET);
        for (int i = 0; i < 4; i++)
                setLineMode(m_dBits[i], GPIO_MODE_INPUT);
        HAL_GPIO_WritePin(m_eBit.gpio, m_eBit.pin, GPIO_PIN_SET);
        polledDelay(2);
        uint8_t dat = 0;
        for (int i = 0; i < 4; i++)
        {
                if (HAL_GPIO_ReadPin(m_dBits[i].gpio, m_dBits[i].pin) == GPIO_PIN_SET)
                        dat |= 1 << i;
        }
        HAL_GPIO_WritePin(m_eBit.gpio, m_eBit.pin, GPIO_PIN_RESET);
        return dat;
}

uint8_t lcdInterface_t::readData8()
{
        uint8_t dat = readData4() << 4;
        dat |= readData4();
        return dat;
}

/// \brief Return the X size of this display
uint8_t lcdInterface_t::displayXsize() const
{
        switch (m_lcdType)
        {
        case LCD_8X1:
        case LCD_8X2:
                return 8;
        case LCD_10X1:
        case LCD_10X2:
                return 10;
        case LCD_16X1:
        case LCD_16X2:
                return 16;
        case LCD_20X1:
        case LCD_20X2:
                return 20;
        case LCD_40X1:
        case LCD_40X2:
                return 40;
        }
        return 0;
}

/// \brief Return the Y size (1 or 2) of this display
uint8_t lcdInterface_t::displayYsize() const
{
        switch (m_lcdType)
        {
        case LCD_8X1:
        case LCD_10X1:
        case LCD_16X1:
        case LCD_20X1:
        case LCD_40X1:
                return 1;
        case LCD_8X2:
        case LCD_10X2:
        case LCD_16X2:
        case LCD_20X2:
        case LCD_40X2:
                return 2;
        }
        return 0;
}

void lcdInterface_t::writeData(uint8_t data_)
{
        HAL_GPIO_WritePin(m_rsBit.gpio, m_rsBit.pin, GPIO_PIN_SET);
        writeData8(data_);
        pollBusy();
}

void lcdInterface_t::writeCtrl(uint8_t data_)
{
        HAL_GPIO_WritePin(m_rsBit.gpio, m_rsBit.pin, GPIO_PIN_RESET);
        writeData8(data_);
}

void lcdInterface_t::writeCtrl4(uint8_t data_)
{
        HAL_GPIO_WritePin(m_rsBit.gpio, m_rsBit.pin, GPIO_PIN_RESET);
        writeData4(data_ >> 4);
}

void lcdInterface_t::pollBusy()
{
        // read from the status bit
        HAL_GPIO_WritePin(m_rsBit.gpio, m_rsBit.pin, GPIO_PIN_RESET);
        // limit to a certain number of loops
        int tries = 1000;
        while (--tries)
        {
                if ((readData8() & 0x80) == 0)
                        return;
        }
        m_timeouts++;
        // fall out as it failed .
}

void lcdInterface_t::writeCtrlWait(uint8_t data_)
{
        writeCtrl(data_);
        pollBusy();
}

uint8_t lcdInterface_t::readData()
{
        HAL_GPIO_WritePin(m_rsBit.gpio, m_rsBit.pin, GPIO_PIN_SET);
        return readData8();
}

uint8_t lcdInterface_t::readCtrl()
{
        HAL_GPIO_WritePin(m_rsBit.gpio, m_rsBit.pin, GPIO_PIN_RESET);
        return readData8();
}

void lcdInterface_t::pulseE()
{
        // pulse enable line high
        polledDelay(2);
        HAL_GPIO_WritePin(m_eBit.gpio, m_eBit.pin, GPIO_PIN_SET);
        polledDelay(2);
        HAL_GPIO_WritePin(m_eBit.gpio, m_eBit.pin, GPIO_PIN_RESET);
}