mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-23 04:41:24 +01:00
Update version.h
4.2.18
This commit is contained in:
parent
f281938606
commit
ef85d5eaba
@ -24,6 +24,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "I2CManager.h"
|
||||
#include "I2CManager_NonBlocking.h" // to satisfy intellisense
|
||||
|
||||
//#include <avr/io.h>
|
||||
//#include <avr/interrupt.h>
|
||||
|
141
IO_ExternalEEPROM.h
Normal file
141
IO_ExternalEEPROM.h
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* © 2023, Neil McKechnie. All rights reserved.
|
||||
*
|
||||
* This file is part of DCC++EX API
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This device driver monitors the state of turnout objects and writes updates,
|
||||
* on change of state, to an external 24C128 (16kByte) or 24C256 (32kByte)
|
||||
* EEPROM device connected via I2C.
|
||||
*
|
||||
* When the device is restarted, it repositions the turnouts in accordance
|
||||
* with the last saved position.
|
||||
*
|
||||
* To create a device instance,
|
||||
* IO_ExternalEEPROM::create(0, 0, i2cAddress);
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IO_EXTERNALEEPROM_H
|
||||
#define IO_EXTERNALEEPROM_H
|
||||
|
||||
#include "IODevice.h"
|
||||
#include "I2CManager.h"
|
||||
#include "Turnouts.h"
|
||||
|
||||
class ExternalEEPROM : public IODevice {
|
||||
private:
|
||||
// Here we define the device-specific variables.
|
||||
int _sizeInKBytes = 128;
|
||||
Turnout *_turnout = 0;
|
||||
int _lastTurnoutHash = 0;
|
||||
I2CRB _rb;
|
||||
uint8_t _buffer[32]; // 32 is max for Wire write
|
||||
|
||||
public:
|
||||
// Static function to handle "IO_ExampleSerial::create(...)" calls.
|
||||
static void create(I2CAddress i2cAddress, int sizeInKBytes) {
|
||||
if (checkNoOverlap(0, 0, i2cAddress)) new ExternalEEPROM(i2cAddress, sizeInKBytes);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Constructor.
|
||||
ExternalEEPROM(I2CAddress i2cAddress, int sizeInKBytes) {
|
||||
_I2CAddress = i2cAddress;
|
||||
_sizeInKBytes = sizeInKBytes;
|
||||
|
||||
// Set up I2C structures.
|
||||
_rb.setWriteParams(_I2CAddress, _buffer, 32);
|
||||
|
||||
addDevice(this);
|
||||
}
|
||||
|
||||
// Device-specific initialisation
|
||||
void _begin() override {
|
||||
I2CManager.begin();
|
||||
I2CManager.setClock(1000000); // Max supported speed
|
||||
|
||||
if (I2CManager.exists(_I2CAddress)) {
|
||||
// Initialise or read contents of EEPROM
|
||||
// and set turnout states accordingly.
|
||||
// Read 32 bytes from address 0x0000.
|
||||
I2CManager.read(_I2CAddress, _buffer, 32, 2, 0, 0);
|
||||
// Dump data
|
||||
DIAG(F("EEPROM First 32 bytes:"));
|
||||
for (int i=0; i<32; i+=8)
|
||||
DIAG(F("%d: %x %x %x %x %x %x %x %x"),
|
||||
i, _buffer[i], _buffer[i+1], _buffer[i+2], _buffer[i+3],
|
||||
_buffer[i+4], _buffer[i+5], _buffer[i+6], _buffer[i+7]);
|
||||
|
||||
#if defined(DIAG_IO)
|
||||
_display();
|
||||
#endif
|
||||
} else {
|
||||
DIAG(F("ExternalEEPROM not found, I2C:%s"), _I2CAddress.toString());
|
||||
_deviceState = DEVSTATE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop function to do background scanning of the turnouts
|
||||
void _loop(unsigned long currentMicros) {
|
||||
(void)currentMicros; // Suppress compiler warnings
|
||||
|
||||
if (_rb.isBusy()) return; // Can't do anything until previous request has completed.
|
||||
if (_rb.status == I2C_STATUS_NEGATIVE_ACKNOWLEDGE) {
|
||||
// Device not responding, probably still writing data, so requeue request
|
||||
I2CManager.queueRequest(&_rb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lastTurnoutHash != Turnout::turnoutlistHash) {
|
||||
_lastTurnoutHash = Turnout::turnoutlistHash;
|
||||
// Turnout list has changed, so pointer held from last run may be invalid
|
||||
_turnout = 0; // Start at the beginning of the list again.
|
||||
//#if defined(DIAG_IO)
|
||||
DIAG(F("Turnout Hash Changed!"));
|
||||
//#endif
|
||||
}
|
||||
|
||||
// Locate next turnout, or first one if there is no current one.
|
||||
if (_turnout)
|
||||
_turnout = _turnout->next();
|
||||
else
|
||||
_turnout = Turnout::first();
|
||||
|
||||
// Retrieve turnout state
|
||||
int turnoutID = _turnout->getId();
|
||||
int turnoutState = _turnout->isThrown();
|
||||
(void)turnoutID; // Suppress compiler warning
|
||||
(void)turnoutState; // Suppress compiler warning
|
||||
|
||||
// TODO: Locate turnoutID in EEPROM (or EEPROM copy) and check if state has changed.
|
||||
// TODO: If it has, then initiate a write of the updated state to EEPROM
|
||||
|
||||
delayUntil(currentMicros+5000); // Write cycle time is 5ms max for FT24C256
|
||||
}
|
||||
|
||||
// Display information about the device.
|
||||
void _display() {
|
||||
DIAG(F("ExternalEEPROM %dkBytes I2C:%s %S"), _sizeInKBytes, _I2CAddress.toString(),
|
||||
_deviceState== DEVSTATE_FAILED ? F("OFFLINE") : F(""));
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // IO_EXTERNALEEPROM_H
|
@ -86,6 +86,11 @@ GPIOBase<T>::GPIOBase(FSH *deviceName, VPIN firstVpin, uint8_t nPins, I2CAddress
|
||||
_hasCallback = true;
|
||||
// Add device to list of devices.
|
||||
addDevice(this);
|
||||
|
||||
_portMode = 0; // default to input mode
|
||||
_portPullup = -1; // default to pullup enabled
|
||||
_portInputState = -1; // default to all inputs high (inactive)
|
||||
_portInUse = 0; // No ports in use initially.
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@ -100,10 +105,6 @@ void GPIOBase<T>::_begin() {
|
||||
#if defined(DIAG_IO)
|
||||
_display();
|
||||
#endif
|
||||
_portMode = 0; // default to input mode
|
||||
_portPullup = -1; // default to pullup enabled
|
||||
_portInputState = -1;
|
||||
_portInUse = 0;
|
||||
_setupDevice();
|
||||
_deviceState = DEVSTATE_NORMAL;
|
||||
} else {
|
||||
|
256
IO_TFTDisplay.h
Normal file
256
IO_TFTDisplay.h
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* © 2023, Neil McKechnie. All rights reserved.
|
||||
*
|
||||
* This file is part of DCC++EX API
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver provides a way of driving a ST7735 TFT display through SCREEN(disp,line,"text").
|
||||
* If the line specified is off the screen then the text in the bottom line will be
|
||||
* overwritten. There is however a special case that if line 255 is specified,
|
||||
* the existing text will scroll up and the new line added to the bottom
|
||||
* line of the screen.
|
||||
*
|
||||
* To install, use the following command in myHal.cpp:
|
||||
|
||||
* TFTDisplay::create(address, width, height);
|
||||
*
|
||||
* where address is the I2C address (0x3c or 0x3d),
|
||||
* width is the width in pixels of the display, and
|
||||
* height is the height in pixels of the display.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef IO_TFTDISPLAY_H
|
||||
#define IO_TFTDDISPLAY_H
|
||||
|
||||
#include "IODevice.h"
|
||||
#include "DisplayInterface.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
template <class T>
|
||||
class TFTDisplay : public IODevice, public DisplayInterface {
|
||||
private:
|
||||
uint8_t _displayNo = 0;
|
||||
// Here we define the device-specific variables.
|
||||
uint8_t _height; // in pixels
|
||||
uint8_t _width; // in pixels
|
||||
T *_displayDriver;
|
||||
uint8_t _rowNo = 0; // Row number being written by caller
|
||||
uint8_t _colNo = 0; // Position in line being written by caller
|
||||
uint8_t _numRows;
|
||||
uint8_t _numCols;
|
||||
char *_buffer = NULL;
|
||||
uint8_t *_rowGeneration = NULL;
|
||||
uint8_t *_lastRowGeneration = NULL;
|
||||
uint8_t _rowNoToScreen = 0;
|
||||
uint8_t _charPosToScreen = 0;
|
||||
DisplayInterface *_nextDisplay = NULL;
|
||||
uint8_t _selectedDisplayNo = 0;
|
||||
|
||||
public:
|
||||
// Static function to handle "TFTDisplay::create(...)" calls.
|
||||
static void create(I2CAddress i2cAddress, int width = 128, int height=64) {
|
||||
/* if (checkNoOverlap(i2cAddress)) */ new TFTDisplay(0, i2cAddress, width, height);
|
||||
}
|
||||
static void create(uint8_t displayNo, I2CAddress i2cAddress, int width = 128, int height=64) {
|
||||
/* if (checkNoOverlap(i2cAddress)) */ new TFTDisplay(displayNo, i2cAddress, width, height);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Constructor
|
||||
TFTDisplay(uint8_t displayNo, I2CAddress i2cAddress, int width, int height) {
|
||||
_displayDriver = new T(i2cAddress, width, height);
|
||||
_displayNo = displayNo;
|
||||
_I2CAddress = i2cAddress;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_numCols = (_width+5) / 6; // character block 6 x 8, round up
|
||||
_numRows = _height / 8; // Round down
|
||||
|
||||
_charPosToScreen = _numCols;
|
||||
|
||||
// Allocate arrays
|
||||
_buffer = (char *)calloc(_numRows*_numCols, sizeof(char));
|
||||
_rowGeneration = (uint8_t *)calloc(_numRows, sizeof(uint8_t));
|
||||
_lastRowGeneration = (uint8_t *)calloc(_numRows, sizeof(uint8_t));
|
||||
// Fill buffer with spaces
|
||||
memset(_buffer, ' ', _numCols*_numRows);
|
||||
|
||||
_displayDriver->clearNative();
|
||||
|
||||
// Is this the main display?
|
||||
if (_displayNo == 0) {
|
||||
// Set first two lines on screen
|
||||
this->setRow(0, 0);
|
||||
print(F("DCC-EX v"));
|
||||
print(F(VERSION));
|
||||
setRow(0, 1);
|
||||
print(F("Lic GPLv3"));
|
||||
}
|
||||
|
||||
// Store pointer to this object into CS display hook, so that we
|
||||
// will intercept any subsequent calls to displayHandler methods.
|
||||
// Make a note of the existing display reference, to that we can
|
||||
// pass on anything we're not interested in.
|
||||
_nextDisplay = DisplayInterface::displayHandler;
|
||||
DisplayInterface::displayHandler = this;
|
||||
|
||||
addDevice(this);
|
||||
}
|
||||
|
||||
|
||||
void screenUpdate() {
|
||||
// Loop through the buffer and if a row has changed
|
||||
// (rowGeneration[row] is changed) then start writing the
|
||||
// characters from the buffer, one character per entry,
|
||||
// to the screen until that row has been refreshed.
|
||||
|
||||
// First check if the OLED driver is still busy from a previous
|
||||
// call. If so, don't to anything until the next entry.
|
||||
if (!_displayDriver->isBusy()) {
|
||||
// Check if we've just done the end of a row or just started
|
||||
if (_charPosToScreen >= _numCols) {
|
||||
// Move to next line
|
||||
if (++_rowNoToScreen >= _numRows)
|
||||
_rowNoToScreen = 0; // Wrap to first row
|
||||
|
||||
if (_rowGeneration[_rowNoToScreen] != _lastRowGeneration[_rowNoToScreen]) {
|
||||
// Row content has changed, so start outputting it
|
||||
_lastRowGeneration[_rowNoToScreen] = _rowGeneration[_rowNoToScreen];
|
||||
_displayDriver->setRowNative(_rowNoToScreen);
|
||||
_charPosToScreen = 0; // Prepare to output first character on next entry
|
||||
} else {
|
||||
// Row not changed, don't bother writing it.
|
||||
}
|
||||
} else {
|
||||
// output character at current position
|
||||
_displayDriver->writeNative(_buffer[_rowNoToScreen*_numCols+_charPosToScreen++]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// IODevice Class Member Overrides
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
// Device-specific initialisation
|
||||
void _begin() override {
|
||||
// Initialise device
|
||||
if (_displayDriver->begin()) {
|
||||
|
||||
DIAG(F("TFTDisplay installed on address %s as screen %d"),
|
||||
_I2CAddress.toString(), _displayNo);
|
||||
|
||||
// Force all rows to be redrawn
|
||||
for (uint8_t row=0; row<_numRows; row++)
|
||||
_rowGeneration[row]++;
|
||||
|
||||
// Start with top line (looks better)
|
||||
_rowNoToScreen = _numRows;
|
||||
_charPosToScreen = _numCols;
|
||||
}
|
||||
}
|
||||
|
||||
void _loop(unsigned long) override {
|
||||
screenUpdate();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// DisplayInterface functions
|
||||
//
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
public:
|
||||
void loop() override {
|
||||
screenUpdate();
|
||||
if (_nextDisplay)
|
||||
_nextDisplay->loop(); // continue to next display
|
||||
return;
|
||||
}
|
||||
|
||||
// Position on nominated line number (0 to number of lines -1)
|
||||
// Clear the line in the buffer ready for updating
|
||||
// The displayNo referenced here is remembered and any following
|
||||
// calls to write() will be directed to that display.
|
||||
void setRow(uint8_t displayNo, byte line) override {
|
||||
_selectedDisplayNo = displayNo;
|
||||
if (displayNo == _displayNo) {
|
||||
if (line == 255) {
|
||||
// LCD(255,"xxx") or SCREEN(displayNo,255, "xxx") -
|
||||
// scroll the contents of the buffer and put the new line
|
||||
// at the bottom of the screen
|
||||
for (int row=1; row<_numRows; row++) {
|
||||
strncpy(&_buffer[(row-1)*_numCols], &_buffer[row*_numCols], _numCols);
|
||||
_rowGeneration[row-1]++;
|
||||
}
|
||||
line = _numRows-1;
|
||||
} else if (line >= _numRows)
|
||||
line = _numRows - 1; // Overwrite bottom line.
|
||||
|
||||
_rowNo = line;
|
||||
// Fill line with blanks
|
||||
for (_colNo = 0; _colNo < _numCols; _colNo++)
|
||||
_buffer[_rowNo*_numCols+_colNo] = ' ';
|
||||
_colNo = 0;
|
||||
// Mark that the buffer has been touched. It will be
|
||||
// sent to the screen on the next loop entry, by which time
|
||||
// the line should have been written to the buffer.
|
||||
_rowGeneration[_rowNo]++;
|
||||
|
||||
}
|
||||
if (_nextDisplay)
|
||||
_nextDisplay->setRow(displayNo, line); // Pass to next display
|
||||
|
||||
}
|
||||
|
||||
// Write one character to the screen referenced in the last setRow() call.
|
||||
size_t write(uint8_t c) override {
|
||||
if (_selectedDisplayNo == _displayNo) {
|
||||
// Write character to buffer (if there's space)
|
||||
if (_colNo < _numCols) {
|
||||
_buffer[_rowNo*_numCols+_colNo++] = c;
|
||||
}
|
||||
}
|
||||
if (_nextDisplay)
|
||||
_nextDisplay->write(c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Write blanks to all of the screen (blocks until complete)
|
||||
void clear (uint8_t displayNo) override {
|
||||
if (displayNo == _displayNo) {
|
||||
// Clear buffer
|
||||
for (_rowNo = 0; _rowNo < _numRows; _rowNo++) {
|
||||
setRow(displayNo, _rowNo);
|
||||
}
|
||||
_rowNo = 0;
|
||||
}
|
||||
if (_nextDisplay)
|
||||
_nextDisplay->clear(displayNo); // Pass to next display
|
||||
}
|
||||
|
||||
// Display information about the device.
|
||||
void _display() {
|
||||
DIAG(F("TFTDisplay %d Configured addr %s"), _displayNo, _I2CAddress.toString());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // IO_TFTDDISPLAY_H
|
136
IO_Wire.h
Normal file
136
IO_Wire.h
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* © 2023, Neil McKechnie. All rights reserved.
|
||||
*
|
||||
* This file is part of DCC++EX API
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The purpose of this module is to provide an interface to the DCC
|
||||
* I2CManager that is compatible with code written for the Arduino
|
||||
* 'Wire' interface.
|
||||
*
|
||||
* To use it, just replace
|
||||
* #include "Wire.h" or #include <Wire.h>
|
||||
* with
|
||||
* #include "IO_Wire.h"
|
||||
*
|
||||
* Note that the CS only supports I2C master mode, so the calls related to
|
||||
* slave mode are not implemented here.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IO_WIRE
|
||||
#define IO_WIRE
|
||||
|
||||
#include "IODevice.h"
|
||||
|
||||
#ifndef I2C_USE_WIRE
|
||||
|
||||
class IO_Wire : public IODevice, public Stream {
|
||||
public:
|
||||
IO_Wire() {
|
||||
addDevice(this);
|
||||
};
|
||||
void begin() {
|
||||
I2CManager.begin();
|
||||
}
|
||||
void setClock(uint32_t speed) {
|
||||
I2CManager.setClock(speed);
|
||||
}
|
||||
void beginTransmission(uint8_t address) {
|
||||
i2cAddress = address;
|
||||
outputLength = 0;
|
||||
}
|
||||
size_t write(byte value) override {
|
||||
if (outputLength < sizeof(outputBuffer)) {
|
||||
outputBuffer[outputLength++] = value;
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
size_t write(const uint8_t *buffer, size_t size) override {
|
||||
for (size_t i=0; i<size; i++) {
|
||||
if (!write(buffer[i])) return i;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
uint8_t endTransmission(bool) {
|
||||
// As this software doesn't run in a multi-master environment, there
|
||||
// is no advantage to holding the bus between transactions. Therefore,
|
||||
// for simplicity, a stop condition is always sent.
|
||||
return I2CManager.write(i2cAddress, outputBuffer, outputLength);
|
||||
}
|
||||
uint8_t requestFrom(uint8_t address, uint8_t readSize, uint8_t sendStop) {
|
||||
(void)sendStop; // suppress compiler warning
|
||||
uint8_t status = I2CManager.read(address, inputBuffer, readSize);
|
||||
inputPos = 0;
|
||||
inputLength = readSize;
|
||||
return status;
|
||||
}
|
||||
uint8_t requestFrom(uint8_t address, uint8_t quantity)
|
||||
{
|
||||
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
|
||||
}
|
||||
uint8_t requestFrom(int address, int quantity)
|
||||
{
|
||||
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
|
||||
}
|
||||
uint8_t requestFrom(int address, int quantity, int sendStop)
|
||||
{
|
||||
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop);
|
||||
}
|
||||
int read() override {
|
||||
if (inputPos < inputLength)
|
||||
return inputBuffer[inputPos++];
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
int available() override {
|
||||
return (inputPos < inputLength);
|
||||
}
|
||||
int peek() override {
|
||||
if (inputPos < inputLength)
|
||||
return inputBuffer[inputPos];
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
uint8_t endTransmission() {
|
||||
return endTransmission(true);
|
||||
}
|
||||
|
||||
static IO_Wire Wire();
|
||||
|
||||
protected:
|
||||
void _begin() { }
|
||||
void _display() {
|
||||
DIAG(F("I2CManager Wire Interface"));
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t outputBuffer[32];
|
||||
uint8_t outputLength = 0;
|
||||
uint8_t inputBuffer[32];
|
||||
uint8_t inputLength = 0;
|
||||
uint8_t inputPos = 0;
|
||||
uint8_t i2cAddress;
|
||||
};
|
||||
|
||||
static IO_Wire Wire;
|
||||
|
||||
#else
|
||||
#include <Wire.h>
|
||||
#endif
|
||||
#endif
|
@ -485,10 +485,10 @@
|
||||
<text x="32.86" y="598.05" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Accessories <v:newlineChar/><tspan
|
||||
x="30.42" dy="1.2em" class="st5">(</tspan>Output.cpp)</text> </g>
|
||||
</a>
|
||||
<a xlink:href="https://github.com/DCC-EX/CommandStation-EX/blob/master/LCDDisplay.cpp">
|
||||
<a xlink:href="https://github.com/DCC-EX/CommandStation-EX/blob/master/Display.cpp">
|
||||
<g id="shape14-81" v:mID="14" v:groupContext="shape" v:layerMember="0" transform="translate(288,-116.156)">
|
||||
<title>Process.14</title>
|
||||
<desc>Other Utilities (LCDDisplay.cpp)</desc>
|
||||
<desc>Other Utilities (Display.cpp)</desc>
|
||||
<v:custProps>
|
||||
<v:cp v:nameU="Cost" v:lbl="Cost" v:prompt="" v:type="7" v:format="@" v:sortKey="" v:invis="false"
|
||||
v:ask="false" v:langID="1033" v:cal="0"/>
|
||||
@ -522,7 +522,7 @@
|
||||
</g>
|
||||
<rect x="0" y="580.5" width="90" height="31.5" rx="13.5" ry="13.5" class="st3"/>
|
||||
<text x="19.29" y="593.55" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Other Utilities<v:newlineChar/><tspan
|
||||
x="14.29" dy="1.2em" class="st5">(</tspan>LCDDisplay.cpp)</text> </g>
|
||||
x="14.29" dy="1.2em" class="st5">(</tspan>Display.cpp)</text> </g>
|
||||
</a>
|
||||
<g id="shape3-88" v:mID="3" v:groupContext="shape" v:layerMember="1" transform="translate(108,-443.812)">
|
||||
<title>Dynamic connector</title>
|
||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
517
ST7735-TFT.h
Normal file
517
ST7735-TFT.h
Normal file
@ -0,0 +1,517 @@
|
||||
/* Tiny TFT Graphics Library v5 - see http://www.technoblogy.com/show?3WAI
|
||||
David Johnson-Davies - www.technoblogy.com - 26th October 2022
|
||||
|
||||
CC BY 4.0
|
||||
Licensed under a Creative Commons Attribution 4.0 International license:
|
||||
http://creativecommons.org/licenses/by/4.0/
|
||||
*/
|
||||
|
||||
#include "FSH.h"
|
||||
#include "DisplayInterface.h"
|
||||
|
||||
|
||||
#if defined(MEGATINYCORE)
|
||||
// ATtiny402/412 PORTA positions. Change these for the chip you're using
|
||||
int const dc = 7;
|
||||
int const mosi = 1;
|
||||
int const sck = 3;
|
||||
int const cs = 6;
|
||||
|
||||
// ATtiny 0-, 1-, and 2-series port manipulations - assumes all pins in same port
|
||||
#define PORT_TOGGLE(x) PORTA.OUTTGL = (x)
|
||||
#define PORT_LOW(x) PORTA.OUTCLR = (x)
|
||||
#define PORT_HIGH(x) PORTA.OUTSET = (x)
|
||||
#define PORT_OUTPUT(x) PORTA.DIRSET = (x)
|
||||
|
||||
#else
|
||||
// ATtiny45/85 PORTB positions. Change these for the chip you're using
|
||||
int const dc = 0;
|
||||
int const mosi = 1;
|
||||
int const sck = 2;
|
||||
int const cs = 3;
|
||||
|
||||
// Classic ATtiny port manipulations - assumes all pins in same port
|
||||
#define PORT_TOGGLE(x) PINB = (x)
|
||||
#define PORT_LOW(x) PORTB = PORTB & ~((x));
|
||||
#define PORT_HIGH(x) PORTB = PORTB | ((x))
|
||||
#define PORT_OUTPUT(x) DDRB = (x)
|
||||
|
||||
#endif
|
||||
|
||||
// Display parameters - uncomment the line for the one you want to use
|
||||
|
||||
// Adafruit 1.44" 128x128 display
|
||||
// int const xsize = 128, ysize = 128, xoff = 2, yoff = 1, invert = 0, rotate = 3, bgr = 1;
|
||||
|
||||
// AliExpress 1.44" 128x128 display
|
||||
// int const xsize = 128, ysize = 128, xoff = 2, yoff = 1, invert = 0, rotate = 3, bgr = 1;
|
||||
|
||||
// Adafruit 0.96" 160x80 display
|
||||
// int const xsize = 160, ysize = 80, xoff = 0, yoff = 24, invert = 0, rotate = 6, bgr = 0;
|
||||
|
||||
// AliExpress 0.96" 160x80 display
|
||||
// int const xsize = 160, ysize = 80, xoff = 1, yoff = 26, invert = 1, rotate = 0, bgr = 1;
|
||||
|
||||
// Adafruit 1.8" 160x128 display
|
||||
// int const xsize = 160, ysize = 128, xoff = 0, yoff = 0, invert = 0, rotate = 0, bgr = 1;
|
||||
|
||||
// AliExpress 1.8" 160x128 display (red PCB)
|
||||
int const xsize = 160, ysize = 128, xoff = 0, yoff = 0, invert = 0, rotate = 0, bgr = 1;
|
||||
|
||||
// AliExpress 1.8" 160x128 display (blue PCB)
|
||||
// int const xsize = 160, ysize = 128, xoff = 0, yoff = 0, invert = 0, rotate = 6, bgr = 0;
|
||||
|
||||
// Adafruit 1.14" 240x135 display
|
||||
// int const xsize = 240, ysize = 135, xoff = 40, yoff = 53, invert = 1, rotate = 6, bgr = 0;
|
||||
|
||||
// AliExpress 1.14" 240x135 display
|
||||
// int const xsize = 240, ysize = 135, xoff = 40, yoff = 52, invert = 1, rotate = 0, bgr = 0;
|
||||
|
||||
// Adafruit 1.3" 240x240 display
|
||||
// int const xsize = 240, ysize = 240, xoff = 0, yoff = 80, invert = 1, rotate = 5, bgr = 0;
|
||||
|
||||
// Adafruit 1.54" 240x240 display
|
||||
// int const xsize = 240, ysize = 240, xoff = 0, yoff = 80, invert = 1, rotate = 5, bgr = 0;
|
||||
|
||||
// AliExpress 1.54" 240x240 display
|
||||
// int const xsize = 240, ysize = 240, xoff = 0, yoff = 80, invert = 1, rotate = 5, bgr = 0;
|
||||
|
||||
// Adafruit 1.9" 320x170 display
|
||||
// int const xsize = 320, ysize = 170, xoff = 0, yoff = 35, invert = 1, rotate = 0, bgr = 0;
|
||||
|
||||
// AliExpress 1.9" 320x170 display
|
||||
// int const xsize = 320, ysize = 170, xoff = 0, yoff = 35, invert = 1, rotate = 0, bgr = 0;
|
||||
|
||||
// Adafruit 1.47" 320x172 rounded rectangle display
|
||||
// int const xsize = 320, ysize = 172, xoff = 0, yoff = 34, invert = 1, rotate = 0, bgr = 0;
|
||||
|
||||
// AliExpress 1.47" 320x172 rounded rectangle display
|
||||
// int const xsize = 320, ysize = 172, xoff = 0, yoff = 34, invert = 1, rotate = 0, bgr = 0;
|
||||
|
||||
// Adafruit 2.0" 320x240 display
|
||||
// int const xsize = 320, ysize = 240, xoff = 0, yoff = 0, invert = 1, rotate = 6, bgr = 0;
|
||||
|
||||
// AliExpress 2.0" 320x240 display
|
||||
// int const xsize = 320, ysize = 240, xoff = 0, yoff = 0, invert = 1, rotate = 0, bgr = 0;
|
||||
|
||||
// Adafruit 2.2" 320x240 display
|
||||
// int const xsize = 320, ysize = 240, xoff = 0, yoff = 0, invert = 0, rotate = 4, bgr = 1;
|
||||
|
||||
// AliExpress 2.4" 320x240 display
|
||||
// int const xsize = 320, ysize = 240, xoff = 0, yoff = 0, invert = 0, rotate = 2, bgr = 1;
|
||||
|
||||
// Character set for text - stored in program memory
|
||||
const uint8_t CharMap[96][6] FLASH = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x07, 0x00, 0x07, 0x00, 0x00 },
|
||||
{ 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00 },
|
||||
{ 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00 },
|
||||
{ 0x23, 0x13, 0x08, 0x64, 0x62, 0x00 },
|
||||
{ 0x36, 0x49, 0x56, 0x20, 0x50, 0x00 },
|
||||
{ 0x00, 0x08, 0x07, 0x03, 0x00, 0x00 },
|
||||
{ 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00 },
|
||||
{ 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00 },
|
||||
{ 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00 },
|
||||
{ 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00 },
|
||||
{ 0x00, 0x80, 0x70, 0x30, 0x00, 0x00 },
|
||||
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x00 },
|
||||
{ 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 },
|
||||
{ 0x20, 0x10, 0x08, 0x04, 0x02, 0x00 },
|
||||
{ 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00 },
|
||||
{ 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00 },
|
||||
{ 0x72, 0x49, 0x49, 0x49, 0x46, 0x00 },
|
||||
{ 0x21, 0x41, 0x49, 0x4D, 0x33, 0x00 },
|
||||
{ 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00 },
|
||||
{ 0x27, 0x45, 0x45, 0x45, 0x39, 0x00 },
|
||||
{ 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00 },
|
||||
{ 0x41, 0x21, 0x11, 0x09, 0x07, 0x00 },
|
||||
{ 0x36, 0x49, 0x49, 0x49, 0x36, 0x00 },
|
||||
{ 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00 },
|
||||
{ 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x40, 0x34, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x08, 0x14, 0x22, 0x41, 0x00 },
|
||||
{ 0x14, 0x14, 0x14, 0x14, 0x14, 0x00 },
|
||||
{ 0x00, 0x41, 0x22, 0x14, 0x08, 0x00 },
|
||||
{ 0x02, 0x01, 0x59, 0x09, 0x06, 0x00 },
|
||||
{ 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00 },
|
||||
{ 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00 },
|
||||
{ 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00 },
|
||||
{ 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00 },
|
||||
{ 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00 },
|
||||
{ 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00 },
|
||||
{ 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00 },
|
||||
{ 0x3E, 0x41, 0x41, 0x51, 0x73, 0x00 },
|
||||
{ 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00 },
|
||||
{ 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00 },
|
||||
{ 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00 },
|
||||
{ 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00 },
|
||||
{ 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00 },
|
||||
{ 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00 },
|
||||
{ 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00 },
|
||||
{ 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00 },
|
||||
{ 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00 },
|
||||
{ 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00 },
|
||||
{ 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00 },
|
||||
{ 0x26, 0x49, 0x49, 0x49, 0x32, 0x00 },
|
||||
{ 0x03, 0x01, 0x7F, 0x01, 0x03, 0x00 },
|
||||
{ 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00 },
|
||||
{ 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00 },
|
||||
{ 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00 },
|
||||
{ 0x63, 0x14, 0x08, 0x14, 0x63, 0x00 },
|
||||
{ 0x03, 0x04, 0x78, 0x04, 0x03, 0x00 },
|
||||
{ 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00 },
|
||||
{ 0x00, 0x7F, 0x41, 0x41, 0x41, 0x00 },
|
||||
{ 0x02, 0x04, 0x08, 0x10, 0x20, 0x00 },
|
||||
{ 0x00, 0x41, 0x41, 0x41, 0x7F, 0x00 },
|
||||
{ 0x04, 0x02, 0x01, 0x02, 0x04, 0x00 },
|
||||
{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00 },
|
||||
{ 0x00, 0x03, 0x07, 0x08, 0x00, 0x00 },
|
||||
{ 0x20, 0x54, 0x54, 0x78, 0x40, 0x00 },
|
||||
{ 0x7F, 0x28, 0x44, 0x44, 0x38, 0x00 },
|
||||
{ 0x38, 0x44, 0x44, 0x44, 0x28, 0x00 },
|
||||
{ 0x38, 0x44, 0x44, 0x28, 0x7F, 0x00 },
|
||||
{ 0x38, 0x54, 0x54, 0x54, 0x18, 0x00 },
|
||||
{ 0x00, 0x08, 0x7E, 0x09, 0x02, 0x00 },
|
||||
{ 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x00 },
|
||||
{ 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00 },
|
||||
{ 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00 },
|
||||
{ 0x20, 0x40, 0x40, 0x3D, 0x00, 0x00 },
|
||||
{ 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00 },
|
||||
{ 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00 },
|
||||
{ 0x7C, 0x04, 0x78, 0x04, 0x78, 0x00 },
|
||||
{ 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00 },
|
||||
{ 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 },
|
||||
{ 0xFC, 0x18, 0x24, 0x24, 0x18, 0x00 },
|
||||
{ 0x18, 0x24, 0x24, 0x18, 0xFC, 0x00 },
|
||||
{ 0x7C, 0x08, 0x04, 0x04, 0x08, 0x00 },
|
||||
{ 0x48, 0x54, 0x54, 0x54, 0x24, 0x00 },
|
||||
{ 0x04, 0x04, 0x3F, 0x44, 0x24, 0x00 },
|
||||
{ 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00 },
|
||||
{ 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00 },
|
||||
{ 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00 },
|
||||
{ 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 },
|
||||
{ 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00 },
|
||||
{ 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00 },
|
||||
{ 0x00, 0x08, 0x36, 0x41, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x77, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x41, 0x36, 0x08, 0x00, 0x00 },
|
||||
{ 0x00, 0x06, 0x09, 0x06, 0x00, 0x00 }, // degree symbol = '~'
|
||||
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }
|
||||
};
|
||||
|
||||
// TFT colour display **********************************************
|
||||
|
||||
int const CASET = 0x2A; // Define column address
|
||||
int const RASET = 0x2B; // Define row address
|
||||
int const RAMWR = 0x2C; // Write to display RAM
|
||||
|
||||
int const White = 0xFFFF;
|
||||
int const Black = 0;
|
||||
|
||||
// Current plot position and colours
|
||||
int xpos, ypos;
|
||||
int fore = White;
|
||||
int back = Black;
|
||||
int scale = 1; // Text scale
|
||||
|
||||
// Send a byte to the display
|
||||
|
||||
void Data (uint8_t d) {
|
||||
for (uint8_t bit = 0x80; bit; bit >>= 1) {
|
||||
PORT_TOGGLE(1<<sck);
|
||||
if (d & bit) PORT_HIGH(1<<mosi); else PORT_LOW(1<<mosi);
|
||||
PORT_TOGGLE(1<<sck);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a command to the display
|
||||
void Command (uint8_t c) {
|
||||
PORT_TOGGLE(1<<dc);
|
||||
Data(c);
|
||||
PORT_TOGGLE(1<<dc);
|
||||
}
|
||||
|
||||
// Send a command followed by two data words
|
||||
void Command2 (uint8_t c, uint16_t d1, uint16_t d2) {
|
||||
PORT_TOGGLE(1<<dc);
|
||||
Data(c);
|
||||
PORT_TOGGLE(1<<dc);
|
||||
Data(d1>>8); Data(d1); Data(d2>>8); Data(d2);
|
||||
}
|
||||
|
||||
void InitDisplay () {
|
||||
PORT_OUTPUT(1<<dc | 1<<cs | 1<<mosi | 1<<sck); // All outputs
|
||||
PORT_HIGH(1<<dc | 1<<cs | 1<<sck); // Outputs high
|
||||
PORT_TOGGLE(1<<cs);
|
||||
Command(0x01); // Software reset
|
||||
delay(250); // delay 250 ms
|
||||
Command(0x36); Data(rotate<<5 | bgr<<3); // Set orientation and rgb/bgr
|
||||
Command(0x3A); Data(0x55); // Set color mode - 16-bit color
|
||||
Command(0x20+invert); // Invert
|
||||
Command(0x11); // Out of sleep mode
|
||||
delay(150);
|
||||
PORT_TOGGLE(1<<cs);
|
||||
}
|
||||
|
||||
void DisplayOn () {
|
||||
PORT_TOGGLE(1<<cs);
|
||||
Command(0x29); // Display on
|
||||
delay(150);
|
||||
PORT_TOGGLE(1<<cs);
|
||||
}
|
||||
|
||||
void ClearDisplay () {
|
||||
PORT_TOGGLE(1<<cs);
|
||||
Command2(CASET, yoff, yoff + ysize - 1);
|
||||
Command2(RASET, xoff, xoff + xsize - 1);
|
||||
Command(0x3A); Data(0x03); // 12-bit colour
|
||||
Command(RAMWR); // Leaves mosi low
|
||||
for (int i=0; i<xsize*4; i++) {
|
||||
for (int j=0; j<ysize*3; j++) {
|
||||
PORT_TOGGLE(1<<sck);
|
||||
PORT_TOGGLE(1<<sck);
|
||||
}
|
||||
}
|
||||
Command(0x3A); Data(0x05); // Back to 16-bit colour
|
||||
PORT_TOGGLE(1<<cs);
|
||||
}
|
||||
|
||||
unsigned int Colour (int r, int g, int b) {
|
||||
return (r & 0xf8)<<8 | (g & 0xfc)<<3 | b>>3;
|
||||
}
|
||||
|
||||
// Move current plot position to x,y
|
||||
void MoveTo (int x, int y) {
|
||||
xpos = x; ypos = y;
|
||||
}
|
||||
|
||||
// Plot point at x,y
|
||||
void PlotPoint (int x, int y) {
|
||||
PORT_TOGGLE(1<<cs);
|
||||
Command2(CASET, yoff+y, yoff+y);
|
||||
Command2(RASET, xoff+x, xoff+x);
|
||||
Command(RAMWR); Data(fore>>8); Data(fore & 0xff);
|
||||
PORT_TOGGLE(1<<cs);
|
||||
}
|
||||
|
||||
// Draw a line to x,y
|
||||
void DrawTo (int x, int y) {
|
||||
int sx, sy, e2, err;
|
||||
int dx = abs(x - xpos);
|
||||
int dy = abs(y - ypos);
|
||||
if (xpos < x) sx = 1; else sx = -1;
|
||||
if (ypos < y) sy = 1; else sy = -1;
|
||||
err = dx - dy;
|
||||
for (;;) {
|
||||
PlotPoint(xpos, ypos);
|
||||
if (xpos==x && ypos==y) return;
|
||||
e2 = err<<1;
|
||||
if (e2 > -dy) { err = err - dy; xpos = xpos + sx; }
|
||||
if (e2 < dx) { err = err + dx; ypos = ypos + sy; }
|
||||
}
|
||||
}
|
||||
|
||||
void FillRect (int w, int h) {
|
||||
PORT_TOGGLE(1<<cs);
|
||||
Command2(CASET, ypos+yoff, ypos+yoff+h-1);
|
||||
Command2(RASET, xpos+xoff, xpos+xoff+w-1);
|
||||
Command(RAMWR);
|
||||
uint8_t hi = fore>>8;
|
||||
uint8_t lo = fore & 0xff;
|
||||
for (int i=0; i<w; i++) {
|
||||
for (int j=0; j<h; j++) {
|
||||
Data(hi); Data(lo);
|
||||
}
|
||||
}
|
||||
PORT_TOGGLE(1<<cs);
|
||||
}
|
||||
|
||||
void DrawRect (int w, int h) {
|
||||
int x1 = xpos, y1 = ypos;
|
||||
FillRect(w-1, 1); MoveTo(x1, y1+1);
|
||||
FillRect(1, h-1); MoveTo(x1+1, y1+h-1);
|
||||
FillRect(w-1, 1); MoveTo(x1+w-1, y1);
|
||||
FillRect(1, h-1);
|
||||
xpos = x1; ypos = y1;
|
||||
}
|
||||
|
||||
void FillCircle (int radius) {
|
||||
int x1 = xpos, y1 = ypos, dx = 1, dy = 1;
|
||||
int x = radius - 1, y = 0;
|
||||
int err = dx - (radius<<1);
|
||||
while (x >= y) {
|
||||
MoveTo(x1-x, y1+y); FillRect(x<<1, 1);
|
||||
MoveTo(x1-y, y1+x); FillRect(y<<1, 1);
|
||||
MoveTo(x1-y, y1-x); FillRect(y<<1, 1);
|
||||
MoveTo(x1-x, y1-y); FillRect(x<<1, 1);
|
||||
if (err > 0) {
|
||||
x = x - 1; dx = dx + 2;
|
||||
err = err - (radius<<1) + dx;
|
||||
} else {
|
||||
y = y + 1; err = err + dy;
|
||||
dy = dy + 2;
|
||||
}
|
||||
}
|
||||
xpos = x1; ypos = y1;
|
||||
}
|
||||
|
||||
void DrawCircle (int radius) {
|
||||
int x1 = xpos, y1 = ypos, dx = 1, dy = 1;
|
||||
int x = radius - 1, y = 0;
|
||||
int err = dx - (radius<<1);
|
||||
while (x >= y) {
|
||||
PlotPoint(x1-x, y1+y); PlotPoint(x1+x, y1+y);
|
||||
PlotPoint(x1-y, y1+x); PlotPoint(x1+y, y1+x);
|
||||
PlotPoint(x1-y, y1-x); PlotPoint(x1+y, y1-x);
|
||||
PlotPoint(x1-x, y1-y); PlotPoint(x1+x, y1-y);
|
||||
if (err > 0) {
|
||||
x = x - 1; dx = dx + 2;
|
||||
err = err - (radius<<1) + dx;
|
||||
} else {
|
||||
y = y + 1; err = err + dy;
|
||||
dy = dy + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plot an ASCII character with bottom left corner at x,y
|
||||
void PlotChar (char c) {
|
||||
int colour;
|
||||
PORT_TOGGLE(1<<cs);
|
||||
Command2(CASET, yoff+ypos, yoff+ypos+8*scale-1);
|
||||
Command2(RASET, xoff+xpos, xoff+xpos+6*scale-1);
|
||||
Command(RAMWR);
|
||||
for (int xx=0; xx<6; xx++) {
|
||||
int bits = pgm_read_byte(&CharMap[c-32][xx]);
|
||||
for (int xr=0; xr<scale; xr++) {
|
||||
for (int yy=0; yy<8; yy++) {
|
||||
if (bits>>(7-yy) & 1) colour = fore; else colour = back;
|
||||
for (int yr=0; yr<scale; yr++) {
|
||||
Data(colour>>8); Data(colour & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PORT_TOGGLE(1<<cs);
|
||||
xpos = xpos + 6*scale;
|
||||
}
|
||||
|
||||
// Plot text starting at the current plot position
|
||||
void PlotText(PGM_P p) {
|
||||
while (1) {
|
||||
char c = pgm_read_byte(p++);
|
||||
if (c == 0) return;
|
||||
PlotChar(c);
|
||||
}
|
||||
}
|
||||
|
||||
void PlotInt(int n) {
|
||||
bool lead = false;
|
||||
for (int d=10000; d>0; d = d/10) {
|
||||
char j = (n/d) % 10;
|
||||
if (j!=0 || lead || d==1) { PlotChar(j + '0'); lead = true; }
|
||||
}
|
||||
}
|
||||
|
||||
void TestChart () {
|
||||
DrawRect(xsize, ysize);
|
||||
scale = 8;
|
||||
fore = Colour(255, 0, 0);
|
||||
MoveTo((xsize-40)/2, (ysize-64)/2); PlotChar('F');
|
||||
scale = 1;
|
||||
}
|
||||
|
||||
// Demos **********************************************
|
||||
|
||||
void BarChart () {
|
||||
int x0 = 0, y0 = 0, w = xsize, h = ysize, x1 = 15, y1 = 11;
|
||||
MoveTo(x0+(w-x1-90)/2+x1, y0+h-8); PlotText(PSTR("Sensor Readings"));
|
||||
// Horizontal axis
|
||||
int xinc = (w-x1)/20;
|
||||
MoveTo(x0+x1, y0+y1); DrawTo(x0+w-1, y0+y1);
|
||||
for (int i=0; i<=20; i=i+4) {
|
||||
int mark = x1+i*xinc;
|
||||
MoveTo(x0+mark, y0+y1); DrawTo(x0+mark, y0+y1-2);
|
||||
// Draw histogram
|
||||
if (i != 20) {
|
||||
int bar = xinc*4/3;
|
||||
for (int b=2; b>=0; b--) {
|
||||
fore = Colour(255, 127*b, 0); // Red, Orange, Yellow
|
||||
MoveTo(x0+mark+bar*b-b+1, y0+y1+1); FillRect(bar, 5+random(h-y1-20));
|
||||
}
|
||||
fore = White;
|
||||
}
|
||||
if (i > 9) MoveTo(x0+mark-7, y0+y1-11); else MoveTo(x0+mark-3, y0+y1-11);
|
||||
PlotInt(i);
|
||||
}
|
||||
// Vertical axis
|
||||
int yinc = (h-y1)/20;
|
||||
MoveTo(x0+x1, y0+y1); DrawTo(x0+x1, y0+h-1);
|
||||
for (int i=0; i<=20; i=i+5) {
|
||||
int mark = y1+i*yinc;
|
||||
MoveTo(x0+x1, y0+mark); DrawTo(x0+x1-2, y0+mark);
|
||||
if (i > 9) MoveTo(x0+x1-15, y0+mark-4); else MoveTo(x0+x1-9, y0+mark-4);
|
||||
PlotInt(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Waterfall () {
|
||||
int x0 = 0, y0 = 0, w = xsize, h = ysize, x1 = 15, y1 = 11;
|
||||
int factor = 5160/h*10;
|
||||
MoveTo(x0+(w-x1-60)/2+x1, y0+h-8); PlotText(PSTR("Luminance"));
|
||||
// Horizontal axis
|
||||
int xinc = (w-x1-15)/30;
|
||||
MoveTo(x0+x1, y0+y1); DrawTo(x0+x1+xinc*20, y0+y1);
|
||||
for (int i=0; i<=20; i=i+5) {
|
||||
int mark = x1+i*xinc;
|
||||
MoveTo(x0+mark, y0+y1); DrawTo(x0+mark, y0+y1-2);
|
||||
if (i > 9) MoveTo(x0+mark-7, y0+y1-11); else MoveTo(x0+mark-3, y0+y1-11);
|
||||
PlotInt(i);
|
||||
}
|
||||
// Vertical axis
|
||||
int yinc = (h-y1)/20;
|
||||
MoveTo(x0+x1, y0+y1); DrawTo(x0+x1, y0+h-1);
|
||||
for (int i=0; i<=20; i=i+5) {
|
||||
int mark = y1+i*yinc;
|
||||
MoveTo(x0+x1, y0+mark); DrawTo(x0+x1-2, y0+mark);
|
||||
if (i > 9) MoveTo(x0+x1-15, y0+mark-4); else MoveTo(x0+x1-9, y0+mark-4);
|
||||
PlotInt(i);
|
||||
}
|
||||
// Diagonal axis
|
||||
yinc = xinc/2;
|
||||
// MoveTo(x0+x1, y0+y1); DrawTo(x0+x1+10*xinc, y0+y1+10*xinc);
|
||||
MoveTo(x0+x1+20*xinc, y0+y1); DrawTo(x0+x1+30*xinc, y0+y1+10*xinc);
|
||||
for (int i=0; i<=20; i=i+5) {
|
||||
MoveTo(x0+x1+20*xinc+i*xinc/2, y0+y1+i*xinc/2);
|
||||
DrawTo(x0+x1+20*xinc+i*xinc/2+3, y0+y1+i*xinc/2);
|
||||
MoveTo(x0+x1+20*xinc+i*xinc/2+6, y0+y1+i*xinc/2-4); PlotInt(i);
|
||||
}
|
||||
// Plot data
|
||||
for (int y=20; y>=0; y--) {
|
||||
for (int i=0; i<=20; i++) {
|
||||
int fn0 = 180-(i-10)*(i-10)-(y-10)*(y-10);
|
||||
int fn1 = 180-(i+1-10)*(i+1-10)-(y-10)*(y-10);
|
||||
fore = Colour(255, 255, 0);
|
||||
MoveTo(x0+x1+y*yinc+i*xinc, y0+y1+y*yinc+fn0*fn0/factor);
|
||||
DrawTo(x0+x1+y*yinc+(i+1)*xinc, y0+y1+y*yinc+fn1*fn1/factor);
|
||||
fore = White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup **********************************************
|
||||
|
||||
void setup() {
|
||||
InitDisplay();
|
||||
ClearDisplay();
|
||||
DisplayOn();
|
||||
MoveTo(0,0);
|
||||
// TestChart();
|
||||
}
|
||||
|
||||
void loop () {
|
||||
BarChart();
|
||||
// Waterfall();
|
||||
for (;;);
|
||||
}
|
@ -31,6 +31,7 @@ include_dir = .
|
||||
[env]
|
||||
build_flags = -Wall -Wextra
|
||||
monitor_filters = time
|
||||
; lib_deps = adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.0
|
||||
|
||||
[env:samd21-dev-usb]
|
||||
platform = atmelsam
|
||||
@ -59,7 +60,7 @@ framework = arduino
|
||||
lib_deps = ${env.lib_deps}
|
||||
monitor_speed = 115200
|
||||
monitor_echo = yes
|
||||
build_flags = -std=c++17 -DI2C_EXTENDED_ADDRESS ; -DI2C_USE_WIRE -DDIAG_LOOPTIMES -DDIAG_IO
|
||||
build_flags = -std=c++17 ; -DI2C_USE_WIRE -DDIAG_LOOPTIMES -DDIAG_IO
|
||||
|
||||
[env:mega2560-debug]
|
||||
platform = atmelavr
|
||||
@ -71,7 +72,7 @@ lib_deps =
|
||||
SPI
|
||||
monitor_speed = 115200
|
||||
monitor_echo = yes
|
||||
build_flags = -DI2C_EXTENDED_ADDRESS -DDIAG_IO -DDIAG_LOOPTIMES
|
||||
build_flags = -DDIAG_IO=2 -DDIAG_LOOPTIMES
|
||||
|
||||
[env:mega2560-no-HAL]
|
||||
platform = atmelavr
|
||||
@ -83,7 +84,7 @@ lib_deps =
|
||||
SPI
|
||||
monitor_speed = 115200
|
||||
monitor_echo = yes
|
||||
build_flags = -DIO_NO_HAL
|
||||
build_flags = -DIO_NO_HAL
|
||||
|
||||
[env:mega2560-I2C-wire]
|
||||
platform = atmelavr
|
||||
@ -107,7 +108,7 @@ lib_deps =
|
||||
SPI
|
||||
monitor_speed = 115200
|
||||
monitor_echo = yes
|
||||
build_flags = -mcall-prologues
|
||||
build_flags = ; -DDIAG_LOOPTIMES
|
||||
|
||||
[env:mega328]
|
||||
platform = atmelavr
|
||||
@ -143,7 +144,7 @@ lib_deps =
|
||||
monitor_speed = 115200
|
||||
monitor_echo = yes
|
||||
upload_speed = 19200
|
||||
build_flags = -DDIAG_IO
|
||||
build_flags =
|
||||
|
||||
[env:uno]
|
||||
platform = atmelavr
|
||||
@ -187,7 +188,7 @@ platform = ststm32
|
||||
board = nucleo_f446re
|
||||
framework = arduino
|
||||
lib_deps = ${env.lib_deps}
|
||||
build_flags = -std=c++17 -Os -g2 -Wunused-variable
|
||||
build_flags = -std=c++17 -Os -g2 -Wunused-variable -DDIAG_LOOPTIMES ; -DDIAG_IO
|
||||
monitor_speed = 115200
|
||||
monitor_echo = yes
|
||||
|
||||
|
34
version.h
34
version.h
@ -5,22 +5,24 @@
|
||||
|
||||
|
||||
#define VERSION "4.2.18"
|
||||
// 4.2.18 I2C Multiplexer support through Extended Addresses,
|
||||
// added for Wire, 4209 and AVR I2C drivers.
|
||||
// I2C retries when fail.
|
||||
// I2C timeout handling and recovery completed.
|
||||
// I2C SAMD Driver Read code completed.
|
||||
// PCF8575 I2C GPIO driver added.
|
||||
// EX-RAIL ANOUT function for triggering analogue
|
||||
// HAL drivers (e.g. analogue outputs, DFPlayer, PWM).
|
||||
// Installable HAL OLED Display Driver, with
|
||||
// support for multiple displays.
|
||||
// Layered HAL Drivers PCA9685pwm and Servo added for
|
||||
// (1) native PWM on PCA9685 module and
|
||||
// (2) animations of servo movement via PCA9685pwm.
|
||||
// This is intended to support EXIOExpander and also
|
||||
// replace the existing PCA9685 driver.
|
||||
// Add <D HAL RESET> to reinitialise failed drivers.
|
||||
// 4.2.18 - I2C Multiplexer support through Extended Addresses,
|
||||
// added for Wire, 4209 and AVR I2C drivers.
|
||||
// - I2C retries when an operation fails.
|
||||
// - I2C timeout handling and recovery completed.
|
||||
// - I2C SAMD Driver Read code completed.
|
||||
// - PCF8575 I2C GPIO driver added.
|
||||
// - EX-RAIL ANOUT function for triggering analogue
|
||||
// HAL drivers (e.g. analogue outputs, DFPlayer, PWM).
|
||||
// - Installable HALDisplay Driver, with support
|
||||
// for multiple displays.
|
||||
// - Layered HAL Drivers PCA9685pwm and Servo added for
|
||||
// native PWM on PCA9685 module and
|
||||
// for animations of servo movement via PCA9685pwm.
|
||||
// This is intended to support EXIOExpander and also
|
||||
// replace the existing PCA9685 driver.
|
||||
// - Add <D HAL RESET> to reinitialise failed drivers.
|
||||
// - Add UserAddin facility to allow a user-specific C++
|
||||
// function to be added in myHal.cpp.
|
||||
// 4.2.17 LCN bugfix
|
||||
// 4.2.16 Move EX-IOExpander servo support to the EX-IOExpander software
|
||||
// 4.2.15 Add basic experimental PWM support to EX-IOExpander
|
||||
|
Loading…
Reference in New Issue
Block a user