/*
* Author: Pawel Jablonski
* E-mail: pj@xirx.net
* WWW: xirx.net
* GIT: git.xirx.net
*
* License: You can use this code however you like
* but leave information about the original author.
* Code is free for non-commercial and commercial use.
*/
#include "io.h"
/**
* Constructor for the IO class. It connects all communication classes for IO operations.
*
* @param parent Parent object
*/
IO::IO(QObject *parent) : QObject(parent)
{
this->reset();
QObject::connect(&this->led, SIGNAL(updateRunSignal(bool)), this, SIGNAL(updateLEDRunSignal(bool)));
QObject::connect(&this->led, SIGNAL(updateErrorSignal(bool)), this, SIGNAL(updateLEDErrorSignal(bool)));
QObject::connect(&this->lcd, SIGNAL(updateBufferSignal(LCD::Buffer)), this, SIGNAL(updateLCDBufferSignal(LCD::Buffer)));
QObject::connect(&this->rs232, SIGNAL(updateTxSignal(unsigned char)), this, SIGNAL(updateRS232TxSignal(unsigned char)));
QObject::connect(&this->rs232, SIGNAL(updateRxSignal(unsigned char)), this, SIGNAL(updateRS232RxSignal(unsigned char)));
QObject::connect(&this->rtc, SIGNAL(updateDateTimeSignal(QDateTime)), this, SIGNAL(updateRTCDateTimeSignal(QDateTime)));
QObject::connect(&this->speaker, SIGNAL(updateStatusSignal(int)), this, SIGNAL(updateSpeakerStatusSignal(int)));
QObject::connect(&this->keyboard, SIGNAL(dataReadySignal(bool)), this, SLOT(keyboardDataReadySlot(bool)));
QObject::connect(&this->rs232, SIGNAL(dataReadySignal(bool)), this, SLOT(rs232DataReadySlot(bool)));
}
//! Destructor for the IO class
IO::~IO()
{
QObject::disconnect(this);
}
//! Reset the IO buffers and trigger reset for all connected communication classes
void IO::reset()
{
this->regSelected = 0;
this->reg = {};
this->in = 0;
this->keyboard.reset();
this->led.reset();
this->lcd.reset();
this->rs232.reset();
this->rtc.reset();
this->speaker.reset();
this->fs.reset();
}
/**
* Process a key press event
*
* @param key Pressed key code
* @param modifiers Optional modifiers to pressed key
*/
void IO::keyboardKeyPress(int key, Qt::KeyboardModifiers modifiers)
{
this->keyboard.keyPress(key, modifiers);
}
/**
* Set enable status of the run LED
*
* @param enable Enable status
*/
void IO::ledSetRun(bool enable)
{
this->led.setRun(enable);
}
/**
* Set enable status of the error LED
*
* @param enable Enable status
*/
void IO::ledSetError(bool enable)
{
this->led.setError(enable);
}
/**
* Process a received text event
*
* @param text Received text
*/
void IO::rs232Receive(const QString &text)
{
this->rs232.rx(text);
}
/**
* Set date and time
*
* @param dateTime Date and time to set in own RTC format
*/
void IO::rtcSetDateTime(const QDateTime &dateTime)
{
this->rtc.setDateTime(dateTime);
}
/**
* Set volume
*
* @param volume Volume level in range 0-100
*/
void IO::speakerSetVolume(unsigned int volume)
{
this->speaker.setVolume(volume);
}
/**
* Set path to the emulated file system on the local disk
*
* @param path Path to emulated file system
*/
void IO::fsSetPath(const QString &path)
{
this->fs.setPath(path);
}
/**
* Process "keyboard data is ready to get" event
*
* @param ready Status of ready data to get
*/
void IO::keyboardDataReadySlot(bool ready)
{
if(ready)
{
this->in |= IN_KEYBOARD_READY_BIT;
}
else
{
this->in &= ~IN_KEYBOARD_READY_BIT;
}
emit inSignal(this->in);
}
/**
* Process "RS232 receive data is ready to get" event
*
* @param ready Status of ready data to get
*/
void IO::rs232DataReadySlot(bool ready)
{
if(ready)
{
this->in |= IN_RS232_READY_BIT;
}
else
{
this->in &= ~IN_RS232_READY_BIT;
}
emit inSignal(this->in);
}
/**
* Process "update the output register value" event.
* Fill up the output register data half with corresponding data from selected virtual register.
*
* @param out Output register value with set up data of the flags half
*/
void IO::outSlot(unsigned char out)
{
if((out & OUT_DATA_READY_BIT) != 0)
{
unsigned char value = out;
bool half = ((value & OUT_HALF_BIT) != 0);
bool mode = ((value & OUT_MODE_BIT) != 0);
bool rw = ((value & OUT_RW_BIT) != 0);
if(!mode)
{
// Select register to work
regSelected = (value & REG_SELECT_MASK);
}
else
{
if(!rw)
{
unsigned char dataOut = 0x00;
// Read register value
switch(regSelected)
{
case REG_KEYBOARD :
{
if(!half)
{
this->reg.keyboard = this->keyboard.getKey();
}
dataOut = this->reg.keyboard;
}
break;
case REG_RS232_RX :
{
if(!half)
{
this->reg.rs232Rx = this->rs232.receive();
}
dataOut = this->reg.rs232Rx;
}
break;
case REG_RTC_FIELD :
switch(this->reg.address.low)
{
case RTC_YEAR :
dataOut = this->reg.rtcField.year;
break;
case RTC_MONTH :
dataOut = this->reg.rtcField.month;
break;
case RTC_DAY :
dataOut = this->reg.rtcField.day;
break;
case RTC_HOUR :
dataOut = this->reg.rtcField.hour;
break;
case RTC_MINUTE :
dataOut = this->reg.rtcField.minute;
break;
case RTC_SECOND :
dataOut = this->reg.rtcField.second;
break;
default :
dataOut = 0;
break;
}
break;
case REG_SPEAKER_FIELD :
{
if(!half)
{
this->reg.speakerBufferFree = this->speaker.getBufferFree();
}
dataOut = this->reg.speakerBufferFree;
}
break;
case REG_FS_DATA :
{
dataOut = this->reg.fsBlock.data[this->reg.address.low];
if(half)
{
this->reg.address.low++;
}
}
break;
case REG_FS_COMMAND :
switch(this->reg.address.low)
{
case FS_OPEN_APP :
{
this->reg.fsName.data[FS::NAME_MAX_LENGTH] = 0;
if(this->fs.open("app", this->reg.fsName, this->reg.fsSize))
{
dataOut = 1;
}
else
{
dataOut = 0;
}
}
break;
case FS_OPEN_FILE :
{
this->reg.fsName.data[FS::NAME_MAX_LENGTH] = 0;
if(this->fs.open("file", this->reg.fsName, this->reg.fsSize))
{
dataOut = 1;
}
else
{
dataOut = 0;
}
}
break;
case FS_LIST_APP :
{
if(this->fs.list("app", this->reg.fsSize))
{
dataOut = 1;
}
else
{
dataOut = 0;
}
}
break;
case FS_LIST_FILE :
{
if(this->fs.list("file", this->reg.fsSize))
{
dataOut = 1;
}
else
{
dataOut = 0;
}
}
break;
case FS_SIZE :
switch(this->reg.address.high)
{
case 0 :
dataOut = this->reg.fsSize.low;
break;
case 1 :
dataOut = this->reg.fsSize.high;
break;
default:
dataOut = 0;
break;
}
break;
default :
dataOut = 0;
break;
}
break;
default :
dataOut = 0;
break;
}
if(half)
{
dataOut >>= 4;
}
// Register value to out
this->in = ((this->in & IN_CONTROL_MASK) | (dataOut & IN_DATA_MASK));
}
else
{
unsigned char dataInRaw = (value & IN_DATA_MASK);
unsigned char dataIn = dataInRaw;
unsigned char mask = HALF_UP_DATA_MASK;
if(half)
{
dataIn <<= HALF_DATA_OFFSET;
mask >>= HALF_DATA_OFFSET;
}
// Write register value
switch(this->regSelected)
{
case REG_LED_COMMAND :
switch(dataInRaw)
{
case LED_RUN_OFF :
this->led.setRun(false);
break;
case LED_RUN_ON :
this->led.setRun(true);
break;
case LED_ERROR_OFF :
this->led.setError(false);
break;
case LED_ERROR_ON :
this->led.setError(true);
break;
}
break;
case REG_ADDRESS_LOW :
this->reg.address.low = ((this->reg.address.low & mask) | dataIn);
break;
case REG_ADDRESS_HIGH :
this->reg.address.high = ((this->reg.address.high & mask) | dataIn);
break;
case REG_LCD_COLOR :
if(!half)
{
this->reg.lcdForegroundColor = dataInRaw;
}
else
{
this->reg.lcdBackgroundColor = dataInRaw;
}
break;
case REG_LCD_CHAR :
this->reg.lcdChar = ((this->reg.lcdChar & mask) | dataIn);
if(half)
{
this->lcd.setChar(this->reg.lcdChar, this->reg.address.low, this->reg.address.high, this->reg.lcdForegroundColor, this->reg.lcdBackgroundColor);
}
break;
case REG_LCD_COMMAND :
switch(dataIn)
{
case LCD_CLEAR :
this->lcd.clear();
this->lcd.refresh();
this->lcd.update();
break;
case LCD_REFRESH :
this->lcd.refresh();
this->lcd.update();
break;
case LCD_SCROLL :
this->lcd.scroll();
break;
}
break;
case REG_RS232_TX :
this->reg.rs232Tx = ((this->reg.rs232Tx & mask) | dataIn);
if(half)
{
this->rs232.send(this->reg.rs232Tx);
}
break;
case REG_RTC_FIELD :
switch(this->reg.address.low)
{
case RTC_YEAR :
this->reg.rtcField.year = ((this->reg.rtcField.year & mask) | dataIn);
break;
case RTC_MONTH :
this->reg.rtcField.month = ((this->reg.rtcField.month & mask) | dataIn);
break;
case RTC_DAY :
this->reg.rtcField.day = ((this->reg.rtcField.day & mask) | dataIn);
break;
case RTC_HOUR :
this->reg.rtcField.hour = ((this->reg.rtcField.hour & mask) | dataIn);
break;
case RTC_MINUTE :
this->reg.rtcField.minute = ((this->reg.rtcField.minute & mask) | dataIn);
break;
case RTC_SECOND :
this->reg.rtcField.second = ((this->reg.rtcField.second & mask) | dataIn);
break;
}
break;
case REG_RTC_COMMAND :
switch(dataInRaw)
{
case RTC_READ :
this->reg.rtcField = this->rtc.getDateTime();
break;
case RTC_WRITE :
this->rtc.setDateTime(this->reg.rtcField);
break;
}
break;
case REG_SPEAKER_FIELD :
switch(this->reg.address.low)
{
case SPEAKER_NOTE :
this->reg.speakerField.note = ((this->reg.speakerField.note & mask) | dataIn);
break;
case SPEAKER_TIME :
this->reg.speakerField.time = ((this->reg.speakerField.time & mask) | dataIn);
break;
case SPEAKER_FILL :
this->reg.speakerField.fill = dataInRaw;
break;
case SPEAKER_VOLUME :
this->reg.speakerField.volume = dataInRaw;
break;
}
break;
case REG_SPEAKER_COMMAND :
switch(dataInRaw)
{
case SPEAKER_CLEAR :
this->speaker.clear();
break;
case SPEAKER_ADD_NOTE :
this->speaker.addNote(this->reg.speakerField);
break;
}
break;
case REG_FS_NAME :
if(this->reg.address.low < FS::NAME_MAX_LENGTH)
{
this->reg.fsName.data[this->reg.address.low] = ((this->reg.fsName.data[this->reg.address.low] & mask) | dataIn);
}
break;
case REG_FS_COMMAND :
switch(dataInRaw)
{
case FS_READ_BLOCK :
this->fs.readBlock(this->reg.fsBlock, this->reg.address.low);
break;
}
break;
}
}
}
// Operation is completed
this->in ^= IN_OPERATION_COMPLETE_BIT;
emit inSignal(this->in);
}
}