diff --git a/source/shoh/src/peripherals/DigitalIoPin.cpp b/source/shoh/src/peripherals/DigitalIoPin.cpp new file mode 100644 index 0000000..13f7d87 --- /dev/null +++ b/source/shoh/src/peripherals/DigitalIoPin.cpp @@ -0,0 +1,64 @@ +/* + * DigitalIoPin.cpp + * + * Created on: Aug 29, 2022 + * Author: Vasily Davydov + */ + +#include "DigitalIoPin.h" + +DigitalIoPin::DigitalIoPin (int port, int pin, bool input, bool pullup, + bool invert) +{ + assert ((port <= UINT8_MAX_VALUE) && (pin <= UINT8_MAX_VALUE)); + _io._port = (uint8_t)port; + _io._pin = (uint8_t)pin; + _io._input = input; + _io._pullup = pullup; + _io._invert = invert; + _io.IOCON_mode = IOCON_MODE_INACT; + _io.IOCON_inv = IOCON_FUNC0; + setIoPin (); +} + +DigitalIoPin::~DigitalIoPin () +{ +} + +void +DigitalIoPin::setIoPin () +{ + bool direction = true; + if (_io._input) + { + direction = false; + _io.IOCON_mode = IOCON_MODE_PULLUP; + if (!_io._pullup) + { + _io.IOCON_mode = IOCON_MODE_PULLDOWN; + } + if (_io._invert) + { + _io.IOCON_inv = IOCON_INV_EN; + } + } + Chip_IOCON_PinMuxSet (LPC_IOCON, _io._port, _io._pin, + (_io.IOCON_mode | _io.DigitalEn | _io.IOCON_inv)); + /** False direction equals input */ + Chip_GPIO_SetPinDIR (LPC_GPIO, _io._port, _io._pin, direction); +} + +bool +DigitalIoPin::read () +{ + bool state = (Chip_GPIO_GetPinState (LPC_GPIO, _io._port, _io._pin)); + return (_io._invert && !_io._input) ? !state : state; +} + +void +DigitalIoPin::write (bool value) +{ + assert (!(_io._input)); + Chip_GPIO_SetPinState (LPC_GPIO, _io._port, _io._pin, ((_io._invert) ? !value : value)); +} + diff --git a/source/shoh/src/peripherals/DigitalIoPin.h b/source/shoh/src/peripherals/DigitalIoPin.h new file mode 100644 index 0000000..de004af --- /dev/null +++ b/source/shoh/src/peripherals/DigitalIoPin.h @@ -0,0 +1,43 @@ +/* + * DigitalIoPin.h + * + * Created on: Aug 29, 2022 + * Author: Vasily Davydov + */ + +#ifndef DIGITALIOPIN_H_ +#define DIGITALIOPIN_H_ + +#define UINT8_MAX_VALUE 255 + +#include +#include + +typedef struct DigitalIOConfigStruct +{ + uint8_t _port; + uint8_t _pin; + bool _input; + bool _pullup; + bool _invert; + uint32_t IOCON_mode; + uint32_t IOCON_inv; + uint32_t DigitalEn; +} DigitalIOConfigStruct; + +class DigitalIoPin +{ +public: + DigitalIoPin (int port, int pin, bool input = true, bool pullup = true, + bool invert = false); + DigitalIoPin (const DigitalIoPin &) = delete; + virtual ~DigitalIoPin (); + bool read (); + void write (bool value); + +private: + DigitalIOConfigStruct _io = { 0, 0, false, false, false, 0, 0, IOCON_DIGMODE_EN}; + void setIoPin (); +}; + +#endif /* DIGITALIOPIN_H_ */ diff --git a/source/shoh/src/peripherals/EEPROMWrapper.cpp b/source/shoh/src/peripherals/EEPROMWrapper.cpp new file mode 100644 index 0000000..4f9f160 --- /dev/null +++ b/source/shoh/src/peripherals/EEPROMWrapper.cpp @@ -0,0 +1,95 @@ +/* + * EEPROMWrapper.cpp + * + * Created on: Dec 4, 2022 + * Author: tylen + */ + +#include + +EEPROM_Wrapper::EEPROM_Wrapper () +{ + /* Enable EEPROM clock and reset EEPROM controller */ + Chip_Clock_EnablePeriphClock (SYSCTL_CLOCK_EEPROM); + Chip_SYSCTL_PeriphReset (RESET_EEPROM); + iap_exec = reinterpret_cast (IAP_ENTRY_LOCATION); +} + +EEPROM_Wrapper::~EEPROM_Wrapper () +{ + // TODO Auto-generated destructor stub +} + +static void +e_memcpy (void *from, void *to, unsigned int n) +{ + if (!from) + return; + char *source = (char *)from; + char *dest = (char *)to; + while (n) + { + (*dest++) = (*source++); + n--; + } +} + +void +EEPROM_Wrapper::eeprom_execute (EEPROM *rom) +{ + command[0] = rom->mode; + command[1] = rom->addr; + command[2] = rom->data; + command[3] = rom->size; + command[4] = rom->clock; + this->iap_exec (command, result); +} + +void +EEPROM_Wrapper::eeprom_use (uint8_t *data, uint32_t addr, uint32_t size, + bool mode) +{ + rom.addr = addr; + rom.data = (uint32_t)data; + rom.mode = (mode) ? IAP_EEPROM_READ : IAP_EEPROM_WRITE; + rom.size = size; +#if INCLUDE_vTaskSuspend + vTaskSuspendAll (); +#endif + /* Main execution of eeprom r/w */ + eeprom_execute (&rom); +#if INCLUDE_vTaskSuspend + xTaskResumeAll (); +#endif + assert (result[0] == IAP_CMD_SUCCESS); +} + +std::string +EEPROM_Wrapper::str_read_from (uint32_t addr, uint32_t amount) +{ + eeprom_use (buffer, addr, amount, READ); + std::string str = (char *)buffer; + return str; +} + +void +EEPROM_Wrapper::write_to (uint32_t addr, std::string str) +{ + std::copy (str.begin (), str.end (), std::begin (buffer)); + eeprom_use (buffer, addr, str.length (), WRITE); +} + +void * +EEPROM_Wrapper::read_from (uint32_t addr, uint32_t amount) +{ + eeprom_use (buffer, addr, amount, READ); + void *data = (void *)buffer; + return data; +} +void +EEPROM_Wrapper::write_to (uint32_t addr, void *data, uint32_t size_of_data) +{ + assert (size_of_data < EEPROM_MAX_BUFER_SIZE); + e_memcpy (data, buffer, size_of_data); + eeprom_use (buffer, addr, size_of_data, WRITE); +} diff --git a/source/shoh/src/peripherals/EEPROMWrapper.h b/source/shoh/src/peripherals/EEPROMWrapper.h new file mode 100644 index 0000000..b0aed9b --- /dev/null +++ b/source/shoh/src/peripherals/EEPROMWrapper.h @@ -0,0 +1,83 @@ +/* + * EEPROMWrapper.h + * + * Created on: Dec 4, 2022 + * Author: tylen + */ + +#ifndef EEPROMWRAPPER_H_ +#define EEPROMWRAPPER_H_ + +#include "FreeRTOS.h" +#include "task.h" +#include "chip.h" +#include +#include + + +typedef void (*IAP_call) (uint32_t[], uint32_t[]); + +typedef struct _eeprom +{ + uint32_t data; + uint32_t addr; + uint32_t size; + uint32_t mode; + uint32_t clock; +} EEPROM; + +#define READ true +#define WRITE false +#define EEPROM_MAX_BUFER_SIZE 1000 + +class EEPROM_Wrapper +{ +public: + /** + * @brief Construct a new eeprom wrapper object + * + */ + EEPROM_Wrapper (); + virtual ~EEPROM_Wrapper (); + /** + * @brief Read a string from EEPROM + * + * @param addr - address to read from + * @param amount - amount of bytes to read + * @return std::string - that was read + */ + std::string str_read_from (uint32_t addr, uint32_t amount); + /** + * @brief Write a string to EEPROM + * + * @param addr - address to write on + * @param str - string to write + */ + void write_to (uint32_t addr, std::string str); + /** + * @brief Read data from EEPROM + * + * @param addr - address to read from + * @param amount - amount of bytes to read + * @return void* - data that was read + */ + void *read_from (uint32_t addr, uint32_t amount); + /** + * @brief Write data to EEPROM + * + * @param addr - address to write on + * @param data - data to be written + * @param size_of_data - size of data to be written + */ + void write_to (uint32_t addr, void *data, uint32_t size_of_data); + +private: + IAP_call iap_exec; + uint32_t command[5], result[5]; + EEPROM rom = { 0, 0, 0, 0, 72000 }; + void eeprom_execute (EEPROM *rom); + void eeprom_use (uint8_t *data, uint32_t addr, uint32_t size, bool mode); + uint8_t buffer[EEPROM_MAX_BUFER_SIZE] = { 0 }; +}; + +#endif /* EEPROMWRAPPER_H_ */ diff --git a/source/shoh/src/peripherals/I2C.cpp b/source/shoh/src/peripherals/I2C.cpp new file mode 100644 index 0000000..19a0b6f --- /dev/null +++ b/source/shoh/src/peripherals/I2C.cpp @@ -0,0 +1,160 @@ +/* + * I2C.cpp + * + * Created on: 21.2.2016 + * Author: krl + * Based on example provided by NXP Semiconductors. See copyright notice + * below. + */ + +/* + * @brief I2CM bus master example using polling mode + * + * @note + * Copyright(C) NXP Semiconductors, 2014 + * All rights reserved. + * + * @par + * Software that is described herein is for illustrative purposes only + * which provides customers with programming information regarding the + * LPC products. This software is supplied "AS IS" without any warranties of + * any kind, and NXP Semiconductors and its licensor disclaim any and + * all warranties, express or implied, including all implied warranties of + * merchantability, fitness for a particular purpose and non-infringement of + * intellectual property rights. NXP Semiconductors assumes no responsibility + * or liability for the use of the software, conveys no license or rights under + * any patent, copyright, mask work right, or any other intellectual property + * rights in or to any products. NXP Semiconductors reserves the right to make + * changes in the software without notification. NXP Semiconductors also makes + * no representation or warranty that such application will be suitable for the + * specified use without further testing or modification. + * + * @par + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, under NXP Semiconductors' and its + * licensor's relevant copyrights in the software, without fee, provided that + * it is used in conjunction with NXP Semiconductors microcontrollers. This + * copyright, permission, and disclaimer notice must appear in all copies of + * this code. + */ + +#include "I2C.h" + +I2C::I2C (const I2C_config &cfg) : device (nullptr) +{ + // if(cfg.device_number == 0) { + device = LPC_I2C0; + // board init must have been called before the pins can be configured + Chip_IOCON_PinMuxSet (LPC_IOCON, 0, 22, IOCON_DIGMODE_EN | cfg.i2c_mode); + Chip_IOCON_PinMuxSet (LPC_IOCON, 0, 23, IOCON_DIGMODE_EN | cfg.i2c_mode); + Chip_SWM_EnableFixedPin (SWM_FIXED_I2C0_SCL); + Chip_SWM_EnableFixedPin (SWM_FIXED_I2C0_SDA); + //} + // else { + // currently we support only I2C number 0 + //} + + if (LPC_I2C0) + { + /* Enable I2C clock and reset I2C peripheral - the boot ROM does not + do this */ + Chip_I2C_Init (LPC_I2C0); + + /* Setup clock rate for I2C */ + Chip_I2C_SetClockDiv (LPC_I2C0, cfg.clock_divider); + + /* Setup I2CM transfer rate */ + Chip_I2CM_SetBusSpeed (LPC_I2C0, cfg.speed); + + /* Enable Master Mode */ + Chip_I2CM_Enable (LPC_I2C0); + } +} + +I2C::~I2C () +{ + // TODO Auto-generated destructor stub +} + +bool +I2C::write (uint8_t devAddr, uint8_t *txBuffPtr, uint16_t txSize) +{ + return transaction (devAddr, txBuffPtr, txSize, nullptr, 0); +} + +bool +I2C::read (uint8_t devAddr, uint8_t *rxBuffPtr, uint16_t rxSize) +{ + return transaction (devAddr, nullptr, 0, rxBuffPtr, rxSize); +} + +bool +I2C::transaction (uint8_t devAddr, uint8_t *txBuffPtr, uint16_t txSize, + uint8_t *rxBuffPtr, uint16_t rxSize) +{ + I2CM_XFER_T i2cmXferRec; + + // make sure that master is idle + while (!Chip_I2CM_IsMasterPending (LPC_I2C0)) + ; + + /* Setup I2C transfer record */ + i2cmXferRec.slaveAddr = devAddr; + i2cmXferRec.status = 0; + i2cmXferRec.txSz = txSize; + i2cmXferRec.rxSz = rxSize; + i2cmXferRec.txBuff = txBuffPtr; + i2cmXferRec.rxBuff = rxBuffPtr; + + I2CM_XferBlocking (LPC_I2C0, &i2cmXferRec); + // Chip_I2CM_XferBlocking returns before stop condition is fully completed + // therefore we need to wait for master to be idle when doing back-to-back + // transactions (see beginning of the function) + + /* Test for valid operation */ + if (i2cmXferRec.status == I2CM_STATUS_OK) + { + return true; + } + else + { + return false; + } +} + +/* Transmit and Receive data in master mode */ +/* This duplicates (and combines) the functionality of Chip_I2CM_Xfer and + * Chip_I2CM_XferBlocking with a modification that allows us to do a zero + * length write (needed to use honeywell humidity/temp sensor) + */ +uint32_t +I2C::I2CM_XferBlocking (LPC_I2C_T *pI2C, I2CM_XFER_T *xfer) +{ + uint32_t ret = 0; + /* start transfer */ + + /* set the transfer status as busy */ + xfer->status = I2CM_STATUS_BUSY; + /* Clear controller state. */ + Chip_I2CM_ClearStatus (pI2C, I2C_STAT_MSTRARBLOSS | I2C_STAT_MSTSTSTPERR); + /* Write Address and RW bit to data register */ + // Chip_I2CM_WriteByte(pI2C, (xfer->slaveAddr << 1) | (xfer->txSz == 0)); // + // original NXP version + // krl : both read and write lenght is 0 --> write (for honeywell temp + // sensor) + Chip_I2CM_WriteByte (pI2C, (xfer->slaveAddr << 1) + | (xfer->txSz == 0 && xfer->rxSz != 0)); + /* Enter to Master Transmitter mode */ + Chip_I2CM_SendStart (pI2C); + + while (ret == 0) + { + /* wait for status change interrupt */ + while (!Chip_I2CM_IsMasterPending (pI2C)) + { + } + /* call state change handler */ + ret = Chip_I2CM_XferHandler (pI2C, xfer); + } + return ret; +} diff --git a/source/shoh/src/peripherals/I2C.h b/source/shoh/src/peripherals/I2C.h new file mode 100644 index 0000000..910a596 --- /dev/null +++ b/source/shoh/src/peripherals/I2C.h @@ -0,0 +1,33 @@ +/* + * I2C.h + * + * Created on: 21.2.2016 + * Author: krl + */ + +#ifndef I2C_H_ +#define I2C_H_ + +#include "chip.h" + +struct I2C_config { + unsigned int device_number; + unsigned int speed; + unsigned int clock_divider; + unsigned int i2c_mode; + I2C_config(unsigned int dn, unsigned int sp, unsigned int cd): device_number(dn), speed(sp), clock_divider(cd), i2c_mode(IOCON_SFI2C_EN) {}; +}; + +class I2C { +public: + I2C(const I2C_config &cfg); + virtual ~I2C(); + bool transaction(uint8_t devAddr, uint8_t *txBuffPtr, uint16_t txSize, uint8_t *rxBuffPtr, uint16_t rxSize); + bool write(uint8_t devAddr, uint8_t *txBuffPtr, uint16_t txSize); + bool read(uint8_t devAddr, uint8_t *rxBuffPtr, uint16_t rxSize); +private: + LPC_I2C_T *device; + static uint32_t I2CM_XferBlocking(LPC_I2C_T *pI2C, I2CM_XFER_T *xfer); +}; + +#endif /* I2C_H_ */ diff --git a/source/shoh/src/peripherals/LiquidCrystal.cpp b/source/shoh/src/peripherals/LiquidCrystal.cpp new file mode 100644 index 0000000..234f925 --- /dev/null +++ b/source/shoh/src/peripherals/LiquidCrystal.cpp @@ -0,0 +1,289 @@ +#include "LiquidCrystal.h" + +#include +#include "chip.h" + +#define LOW 0 +#define HIGH 1 + + +#if 0 +void delayMicroseconds(unsigned int us) +{ + // implement with RIT +} +#else +void delayMicroseconds(uint32_t delay) +{ + static int init; + if(!init) { + // start core clock counter + CoreDebug->DEMCR |= 1 << 24; + DWT->CTRL |= 1; + init = 1; + } + + uint32_t start = DWT->CYCCNT; + delay = delay * 72; // assuming 72MHz clock + while(DWT->CYCCNT - start < delay); +} + +#endif + +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// LiquidCrystal constructor is called). + + + +LiquidCrystal::LiquidCrystal(DigitalIoPin *rs, DigitalIoPin *enable, + DigitalIoPin *d0, DigitalIoPin *d1, DigitalIoPin *d2, DigitalIoPin *d3) +{ + rs_pin = rs; + enable_pin = enable; + + data_pins[0] = d0; + data_pins[1] = d1; + data_pins[2] = d2; + data_pins[3] = d3; + + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + + begin(16, 2); // default to 16x2 display +} + +void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { + if (lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + _currline = 0; + + // for some 1 line displays you can select a 10 pixel high font + if ((dotsize != 0) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delayMicroseconds(50000); + // Now we pull both RS and R/W low to begin commands + rs_pin->write(false); //digitalWrite(_rs_pin, LOW); + enable_pin->write(false); //digitalWrite(_enable_pin, LOW); + + // note: this port supports only 4 bit mode + //put the LCD into 4 bit or 8 bit mode + if (! (_displayfunction & LCD_8BITMODE)) { + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03); + delayMicroseconds(150); + + // finally, set to 4-bit interface + write4bits(0x02); + } else { + // this is according to the hitachi HD44780 datasheet + // page 45 figure 23 + + // Send function set command sequence + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(4500); // wait more than 4.1ms + + // second try + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(150); + + // third go + command(LCD_FUNCTIONSET | _displayfunction); + } + + // finally, set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for romance languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + +} + +/********** high level commands, for the user! */ +void LiquidCrystal::clear() +{ + command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void LiquidCrystal::home() +{ + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + + +void LiquidCrystal::print(std::string const &s) +{ + print(s.c_str()); +} + +void LiquidCrystal::print(const char *s) +{ + while(*s) { + write(*s); + ++s; + } +} + +void LiquidCrystal::setCursor(uint8_t col, uint8_t row) +{ + int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; + if ( row >= _numlines ) { + row = _numlines-1; // we count rows starting w/0 + } + + command(LCD_SETDDRAMADDR | (col + row_offsets[row])); +} + +// Turn the display on/off (quickly) +void LiquidCrystal::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void LiquidCrystal::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turn on and off the blinking cursor +void LiquidCrystal::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void LiquidCrystal::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} +void LiquidCrystal::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void LiquidCrystal::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void LiquidCrystal::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor +void LiquidCrystal::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void LiquidCrystal::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters +void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + write(charmap[i]); + } +} + +/*********** mid level commands, for sending data/cmds */ + +inline void LiquidCrystal::command(uint8_t value) { + send(value, LOW); +} + +inline size_t LiquidCrystal::write(uint8_t value) { + send(value, HIGH); + return 1; // assume sucess +} + +/************ low level data pushing commands **********/ + +// write either command or data +void LiquidCrystal::send(uint8_t value, uint8_t mode) { + rs_pin->write(mode); //digitalWrite(_rs_pin, mode); + + write4bits(value>>4); + write4bits(value); +} + +void LiquidCrystal::pulseEnable(void) { + enable_pin->write(false); //digitalWrite(_enable_pin, LOW); + delayMicroseconds(1); + enable_pin->write(true); //digitalWrite(_enable_pin, HIGH); + delayMicroseconds(1); // enable pulse must be >450ns + enable_pin->write(false); //digitalWrite(_enable_pin, LOW); + delayMicroseconds(100); // commands need > 37us to settle +} + +void LiquidCrystal::write4bits(uint8_t value) { + for (int i = 0; i < 4; i++) { + data_pins[i]->write((value >> i) & 0x01); //digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + + pulseEnable(); +} + diff --git a/source/shoh/src/peripherals/LiquidCrystal.h b/source/shoh/src/peripherals/LiquidCrystal.h new file mode 100644 index 0000000..7c4dcee --- /dev/null +++ b/source/shoh/src/peripherals/LiquidCrystal.h @@ -0,0 +1,97 @@ +#ifndef LiquidCrystal_h +#define LiquidCrystal_h + + +#include +#include +#include "chip.h" +#include "DigitalIoPin.h" + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +class LiquidCrystal { +public: + + LiquidCrystal(DigitalIoPin *rs, DigitalIoPin *enable, + DigitalIoPin *d0, DigitalIoPin *d1, DigitalIoPin *d2, DigitalIoPin *d3); + + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + + void clear(); + void home(); + + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void leftToRight(); + void rightToLeft(); + void autoscroll(); + void noAutoscroll(); + + void createChar(uint8_t, uint8_t[]); + void setCursor(uint8_t, uint8_t); + virtual size_t write(uint8_t); + void command(uint8_t); + void print(std::string const &s); + void print(const char *s); + +private: + void send(uint8_t, uint8_t); + void write4bits(uint8_t); + void pulseEnable(); + + DigitalIoPin *rs_pin; // LOW(false): command. HIGH(true): character. + DigitalIoPin *enable_pin; // activated by a HIGH pulse. + DigitalIoPin *data_pins[4]; + + uint8_t _displayfunction; + uint8_t _displaycontrol; + uint8_t _displaymode; + + uint8_t _initialized; + + uint8_t _numlines,_currline; +}; + +#endif diff --git a/source/shoh/src/peripherals/LpcUart.cpp b/source/shoh/src/peripherals/LpcUart.cpp new file mode 100644 index 0000000..e90bcc0 --- /dev/null +++ b/source/shoh/src/peripherals/LpcUart.cpp @@ -0,0 +1,315 @@ +/* + * LpcUart.cpp + * + * Created on: 4.2.2019 + * Author: keijo + */ + +#include +#include +#include "LpcUart.h" + + +static LpcUart *u0; +static LpcUart *u1; +static LpcUart *u2; + +extern "C" { +/** + * @brief UART interrupt handler using ring buffers + * @return Nothing + */ +void UART0_IRQHandler(void) +{ + portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + + if(u0) { + u0->isr(&xHigherPriorityTaskWoken); + } + + portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); +} + +void UART1_IRQHandler(void) +{ + portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + + if(u1) { + u1->isr(&xHigherPriorityTaskWoken); + } + + portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); +} + +void UART2_IRQHandler(void) +{ + portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + + if(u2) { + u2->isr(&xHigherPriorityTaskWoken); + } + + portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); +} + +} + + +void LpcUart::isr(portBASE_TYPE *hpw) { + // get interrupt status for notifications + uint32_t istat = Chip_UART_GetIntStatus(uart); + + // chip library is used to handle receive and transmit + Chip_UART_IRQRBHandler(uart, &rxring, &txring); + + // notify of the events handled + if(notify_rx && (istat & UART_STAT_RXRDY) ) vTaskNotifyGiveFromISR(notify_rx, hpw); + if(notify_tx && (istat & UART_STAT_TXRDY) ) vTaskNotifyGiveFromISR(notify_tx, hpw); + if(on_receive && (istat & UART_STAT_RXRDY) ) on_receive(); +} + +bool LpcUart::init = false; + +LpcUart::LpcUart(const LpcUartConfig &cfg) { + CHIP_SWM_PIN_MOVABLE_T tx; + CHIP_SWM_PIN_MOVABLE_T rx; + CHIP_SWM_PIN_MOVABLE_T cts; + CHIP_SWM_PIN_MOVABLE_T rts; + bool use_rts = (cfg.rts.port >= 0); + bool use_cts = (cfg.cts.port >= 0); + + if(!init) { + init = true; + /* Before setting up the UART, the global UART clock for USARTS 1-4 + * must first be setup. This requires setting the UART divider and + * the UART base clock rate to 16x the maximum UART rate for all + * UARTs. + * */ + /* Use main clock rate as base for UART baud rate divider */ + Chip_Clock_SetUARTBaseClockRate(Chip_Clock_GetMainClockRate(), false); + } + + uart = nullptr; // set default value before checking which UART to configure + + if(cfg.pUART == LPC_USART0) { + if(u0) return; // already exists + else u0 = this; + tx = SWM_UART0_TXD_O; + rx = SWM_UART0_RXD_I; + rts = SWM_UART0_RTS_O; + cts = SWM_UART0_CTS_I; + irqn = UART0_IRQn; + } + else if(cfg.pUART == LPC_USART1) { + if(u1) return; // already exists + else u1 = this; + tx = SWM_UART1_TXD_O; + rx = SWM_UART1_RXD_I; + rts = SWM_UART1_RTS_O; + cts = SWM_UART1_CTS_I; + irqn = UART1_IRQn; + } + else if(cfg.pUART == LPC_USART2) { + if(u2) return; // already exists + else u2 = this; + tx = SWM_UART2_TXD_O; + rx = SWM_UART2_RXD_I; + use_rts = false; // UART2 does not support handshakes + use_cts = false; + irqn = UART2_IRQn; + } + else { + return; + } + + uart = cfg.pUART; // set the actual value after validity checking + + + if(cfg.tx.port >= 0) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.tx.port, cfg.tx.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(tx, cfg.tx.port, cfg.tx.pin); + } + + if(cfg.rx.port >= 0) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.rx.port, cfg.rx.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(rx, cfg.rx.port, cfg.rx.pin); + } + + if(use_cts) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.cts.port, cfg.cts.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(cts, cfg.cts.port, cfg.cts.pin); + } + + if(use_rts) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.rts.port, cfg.rts.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(rts, cfg.rts.port, cfg.rts.pin); + } + + notify_rx = nullptr; + notify_tx = nullptr; + on_receive = nullptr; + /* Setup UART */ + Chip_UART_Init(uart); + Chip_UART_ConfigData(uart, cfg.data); + Chip_UART_SetBaud(uart, cfg.speed); + + if(use_rts && cfg.rs485) { + uart->CFG |= (1 << 20); // enable rs485 mode + //uart->CFG |= (1 << 18); // OE turnaraound time + uart->CFG |= (1 << 21);// driver enable polarity (active high) + } + + Chip_UART_Enable(uart); + Chip_UART_TXEnable(uart); + + /* Before using the ring buffers, initialize them using the ring + buffer init function */ + RingBuffer_Init(&rxring, rxbuff, 1, UART_RB_SIZE); + RingBuffer_Init(&txring, txbuff, 1, UART_RB_SIZE); + + + /* Enable receive data and line status interrupt */ + Chip_UART_IntEnable(uart, UART_INTEN_RXRDY); + Chip_UART_IntDisable(uart, UART_INTEN_TXRDY); /* May not be needed */ + + NVIC_SetPriority(irqn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1); + /* Enable UART interrupt */ + NVIC_EnableIRQ(irqn); +} + +LpcUart::~LpcUart() { + if(uart != nullptr) { + NVIC_DisableIRQ(irqn); + Chip_UART_IntDisable(uart, UART_INTEN_RXRDY); + Chip_UART_IntDisable(uart, UART_INTEN_TXRDY); + + if(uart == LPC_USART0) { + u0 = nullptr; + } + else if(uart == LPC_USART1) { + u1 = nullptr; + } + else if(uart == LPC_USART2) { + u2 = nullptr; + } + } +} + +void LpcUart::set_on_receive(void(*cb)(void)) +{ + on_receive = cb; +} + + +int LpcUart::free() +{ + std::lock_guard lock(write_mutex); + + return UART_RB_SIZE - RingBuffer_GetCount(&txring); +} + +int LpcUart::peek() +{ + std::lock_guard lock(read_mutex); + + return RingBuffer_GetCount(&rxring); +} + +int LpcUart::read(char &c) +{ + return read(&c, 1); +} + +int LpcUart::read(char *buffer, int len) +{ + std::lock_guard lock(read_mutex); + + if(RingBuffer_GetCount(&rxring) <= 0) { + notify_rx = xTaskGetCurrentTaskHandle(); + while(RingBuffer_GetCount(&rxring) <= 0) { + ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); + } + notify_rx = nullptr; + } + + return Chip_UART_ReadRB(uart, &rxring, buffer, len); +} + + +int LpcUart::read(char *buffer, int len, TickType_t total_timeout, TickType_t ic_timeout) +{ + std::lock_guard lock(read_mutex); + + // we can't read more than ring buffer size at a time + if(len > UART_RB_SIZE) len = UART_RB_SIZE; + + TimeOut_t timeoutState; + vTaskSetTimeOutState(&timeoutState); + + notify_rx = xTaskGetCurrentTaskHandle(); + while(RingBuffer_GetCount(&rxring) < len && xTaskCheckForTimeOut(&timeoutState, &total_timeout) == pdFALSE) { + TickType_t timeout = total_timeout > ic_timeout ? ic_timeout : total_timeout; + if(ulTaskNotifyTake( pdTRUE, timeout ) == 0) break; + } + notify_rx = nullptr; + + return Chip_UART_ReadRB(uart, &rxring, buffer, len);; +} + +int LpcUart::write(char c) +{ + return write(&c, 1); +} + +int LpcUart::write(const char *s) +{ + return write(s, strlen(s)); +} + +int LpcUart::write(const char *buffer, int len) +{ + std::lock_guard lock(write_mutex); + + int pos = 0; + notify_tx = xTaskGetCurrentTaskHandle(); + + while(len > pos) { + // restrict single write to ring buffer size + int size = (len - pos) > UART_RB_SIZE ? UART_RB_SIZE : (len - pos); + + // wait until we have space in the ring buffer + while(UART_RB_SIZE - RingBuffer_GetCount(&txring) < size) { + ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); + } + pos += Chip_UART_SendRB(uart, &txring, buffer+pos, size); + } + notify_tx = nullptr; + + return pos; +} + +void LpcUart::txbreak(bool brk) +{ + // break handling not implemented yet +} + +bool LpcUart::rxbreak() +{ + // break handling not implemented yet + return false; +} + +void LpcUart::speed(int bps) +{ + std::lock_guard lockw(write_mutex); + std::lock_guard lockr(read_mutex); + + Chip_UART_SetBaud(uart, bps); +} + +bool LpcUart::txempty() +{ + std::lock_guard lock(write_mutex); + + return (RingBuffer_GetCount(&txring) == 0); +} diff --git a/source/shoh/src/peripherals/LpcUart.h b/source/shoh/src/peripherals/LpcUart.h new file mode 100644 index 0000000..2e7e799 --- /dev/null +++ b/source/shoh/src/peripherals/LpcUart.h @@ -0,0 +1,72 @@ +/* + * LpcUart.h + * + * Created on: 4.2.2019 + * Author: keijo + */ + +#ifndef LPCUART_H_ +#define LPCUART_H_ + +#include "chip.h" +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "Fmutex.h" + +struct LpcPinMap { + int port; /* set to -1 to indicate unused pin */ + int pin; /* set to -1 to indicate unused pin */ +}; + +struct LpcUartConfig { + LPC_USART_T *pUART; + uint32_t speed; + uint32_t data; + bool rs485; + LpcPinMap tx; + LpcPinMap rx; + LpcPinMap rts; /* used as output enable if RS-485 mode is enabled */ + LpcPinMap cts; +}; + + +class LpcUart { +public: + LpcUart(const LpcUartConfig &cfg); + LpcUart(const LpcUart &) = delete; + virtual ~LpcUart(); + int free(); /* get amount of free space in transmit buffer */ + int peek(); /* get number of received characters in receive buffer */ + int write(char c); + int write(const char *s); + int write(const char *buffer, int len); + int read(char &c); /* get a single character. Returns number of characters read --> returns 0 if no character is available */ + int read(char *buffer, int len); + int read(char *buffer, int len, TickType_t total_timeout, TickType_t ic_timeout = portMAX_DELAY); + void txbreak(bool brk); /* set break signal on */ + bool rxbreak(); /* check if break is received */ + void speed(int bps); /* change transmission speed */ + bool txempty(); + void set_on_receive(void(*cb)(void)); + + void isr(portBASE_TYPE *hpw); /* ISR handler. This will be called by the HW ISR handler. Do not call from application */ +private: + LPC_USART_T *uart; + IRQn_Type irqn; + /* currently we support only fixed size ring buffers */ + static const int UART_RB_SIZE = 128; + /* Transmit and receive ring buffers */ + RINGBUFF_T txring; + RINGBUFF_T rxring; + uint8_t rxbuff[UART_RB_SIZE]; + uint8_t txbuff[UART_RB_SIZE]; + static bool init; /* set when first UART is initialized. We have a global clock setting for all UARTSs */ + TaskHandle_t notify_rx; + TaskHandle_t notify_tx; + void (*on_receive)(void); // callback for received data notifications + Fmutex read_mutex; + Fmutex write_mutex; +}; + +#endif /* LPCUART_H_ */