Commit a5eab142 authored by Mark van Renswoude's avatar Mark van Renswoude

Ported to C++ for the ATMega328P

Added fancier display elements now that we've got room to spare
parent 5f89b3ef
REM Internal 8 Mhz oscillator, no clock division
REM http://www.engbedded.com/fusecalc/
@avrdude -c usbtiny -p t2313 -e -U lfuse:w:0xe4:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
\ No newline at end of file
@avrdude -c usbtiny -p m328p -e -U lfuse:w:0xE2:m -U hfuse:w:0xd9:m -U efuse:w:0x07:m
\ No newline at end of file
......@@ -13,7 +13,11 @@ env_default = board
[env:board]
platform = atmelavr
board = attiny4313
framework = arduino
board = 328p8m
board_build.f_cpu = 8000000L
upload_speed = 115200
upload_protocol = usbtiny
\ No newline at end of file
upload_protocol = usbtiny
lib_deps =
Adafruit GFX Library
\ No newline at end of file
/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "display.h"
#include <stdint.h>
#include <stdbool.h>
#include <avr/pgmspace.h>
#include "lib/VeryTinySSD1306.h"
#include "lib/VeryTinyBigChars.h"
#include "lib/TinyMillis.h"
#include "global.h"
#define CharSpacing 4
#define CharOuterWidth (SSD1306_CharWidth_32px + CharSpacing)
#define SingleCharCenteredX ((SSD1306_DisplayWidth - SSD1306_CharWidth_32px) / 2)
#define WaitAnimationInterval 250
#define WaitAnimationMaxStep 3
const uint16_t WaitAnimation [] PROGMEM = {
0b0000000000000100, // Left
0b0000000010000000, // Middle
0b0001000000000000, // Right
0b0000000010000000 // Middle
};
/*
#define WaitAnimationMaxStep 7
const uint16_t WaitAnimation [] PROGMEM = {
0b0000000001000000, // Top
0b0000100000000000, // Top-right
0b0001000000000000, // Right
0b0010000000000000, // Bottom-right
0b0000000100000000, // Bottom
0b0000000000001000, // Bottom-left
0b0000000000000100, // Left
0b0000000000000010 // Top-left
};
*/
bool isWait = false;
uint16_t lastWait = 0;
uint8_t waitAnimationStep = 0;
inline void display_drawNumber(uint8_t* x, uint8_t value)
{
if (value > 9)
{
ssd1306_drawdigit_32px(*x, value / 10);
*x += CharOuterWidth;
ssd1306_drawdigit_32px(*x, value % 10);
}
else
ssd1306_drawdigit_32px(*x, value);
*x += CharOuterWidth;
}
void display_init(void)
{
ssd1306_begin_default();
ssd1306_clear();
ssd1306_on();
ssd1306_switchRenderFrame();
}
void display_drawIndex(void)
{
ssd1306_clear();
uint8_t moduleIndex = global_getModuleIndex();
if (moduleIndex == ModuleIndexUndefined)
{
ssd1306_drawminus_32px(SingleCharCenteredX);
}
else
{
uint8_t firstStep = (moduleIndex * 2) + 1;
uint8_t totalWidth = 3 * CharOuterWidth;
if (firstStep == 9)
totalWidth += CharOuterWidth;
else if (firstStep > 9)
totalWidth += 2 * CharOuterWidth;
uint8_t currentX = (SSD1306_DisplayWidth - totalWidth) / 2;
display_drawNumber(&currentX, firstStep);
ssd1306_drawminus_32px(currentX);
currentX += CharOuterWidth;
display_drawNumber(&currentX, firstStep + 1);
}
ssd1306_switchFrame();
}
void display_drawWait(void)
{
uint16_t currentTime = millis();
if (isWait && currentTime - lastWait < WaitAnimationInterval)
return;
ssd1306_clear();
ssd1306_drawchar_32px(SingleCharCenteredX, pgm_read_word(&WaitAnimation[waitAnimationStep]));
ssd1306_switchFrame();
waitAnimationStep++;
if (waitAnimationStep > WaitAnimationMaxStep)
waitAnimationStep = 0;
isWait = true;
lastWait = currentTime;
}
\ No newline at end of file
/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "display.h"
#include <Fonts/FreeSans12pt7b.h>
#include "global.h"
#include "icons.h"
#define WaitAnimationInterval 150
void Display::init()
{
mDisplay = new Adafruit_SSD1306(128, 32, &Wire, -1);
mDisplay->begin(SSD1306_SWITCHCAPVCC, 0x3C);
}
void Display::show(Screen screen)
{
switch (screen)
{
case Screen::Blank:
off();
break;
case Screen::WaitingForComm:
drawWaitingForComm();
break;
case Screen::ModuleIndex:
drawModuleIndex();
break;
}
}
void Display::drawWaitingForComm()
{
uint32_t currentTime = millis();
if (mLastScreen == Screen::WaitingForComm && currentTime - mLastWaiting < WaitAnimationInterval)
return;
checkOn();
mDisplay->clearDisplay();
drawTitle("Waiting for signal");
uint8_t xOffset = (mWaitAnimationStep == 1 || mWaitAnimationStep == 2) ? WaitCursorSegmentWidth : 0;
uint8_t yOffset = (mWaitAnimationStep == 2 || mWaitAnimationStep == 3) ? WaitCursorSegmentHeight : 0;
drawRotatedBitmap(xOffset, mDisplay->height() - (WaitCursorSegmentHeight * 2) + yOffset, WaitCursorSegment, WaitCursorSegmentWidth, WaitCursorSegmentHeight, mWaitAnimationStep);
mDisplay->display();
mWaitAnimationStep++;
if (mWaitAnimationStep > 3)
mWaitAnimationStep = 0;
mLastScreen = Screen::WaitingForComm;
mLastWaiting = currentTime;
}
void Display::drawModuleIndex()
{
uint8_t moduleIndex = settings.getModuleIndex();
if (mLastScreen == Screen::ModuleIndex && mLastModuleIndex == moduleIndex)
return;
checkOn();
mDisplay->clearDisplay();
drawTitle("Steps");
drawCommIcon();
mDisplay->setFont(&FreeSans12pt7b);
mDisplay->setCursor(0, mDisplay->height());
// ToDo show numbers based on module index
uint8_t firstStep = (moduleIndex * 2) + 1;
mDisplay->print(firstStep);
mDisplay->print(" - ");
mDisplay->print(firstStep + 1);
mDisplay->display();
mLastScreen = Screen::ModuleIndex;
mLastModuleIndex = moduleIndex;
}
void Display::off()
{
if (mLastScreen != Screen::Blank)
{
mDisplay->ssd1306_command(SSD1306_DISPLAYOFF);
mLastScreen = Screen::Blank;
}
}
void Display::checkOn()
{
if (mLastScreen == Screen::Blank)
mDisplay->ssd1306_command(SSD1306_DISPLAYON);
}
void Display::drawTitle(const char* title)
{
mDisplay->setTextSize(1);
mDisplay->setTextColor(WHITE);
mDisplay->setFont(NULL);
mDisplay->setCursor(0, 0);
mDisplay->print(title);
}
void Display::drawCommIcon()
{
// ToDo show actual state depending on last received message (should never be more than half a second ago)
mDisplay->drawBitmap(mDisplay->width() - IconCommWidth, 0, IconCommOff, IconCommWidth, IconCommHeight, 1);
}
typedef void (*WritePixelProc)(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY);
void writePixel0(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY)
{
display->writePixel(boundsX + relativeX, boundsY + relativeY, 1);
}
void writePixel90(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY)
{
display->writePixel(boundsX + boundsW - relativeY, boundsY + relativeX, 1);
}
void writePixel180(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY)
{
display->writePixel(boundsX + boundsW - relativeX, boundsY + boundsH - relativeY, 1);
}
void writePixel270(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY)
{
display->writePixel(boundsX + relativeY, boundsY + boundsH - relativeX, 1);
}
void Display::drawRotatedBitmap(int8_t x, int8_t y, const uint8_t bitmap[], int8_t w, int8_t h, uint8_t rotation)
{
int16_t byteWidth = (w + 7) / 8;
int8_t byte = 0;
mDisplay->startWrite();
WritePixelProc writePixel;
switch (rotation)
{
case 1: // 90 degrees
writePixel = writePixel90;
break;
case 2: // 180 degrees
writePixel = writePixel180;
break;
case 3: // 270 degrees
writePixel = writePixel270;
break;
default: // 0 degrees
writePixel = writePixel0;
break;
}
for(int8_t relativeY = 0; relativeY < h; relativeY++)
{
for(int8_t relativeX = 0; relativeX < w; relativeX++)
{
if(relativeX & 7)
byte <<= 1;
else
byte = pgm_read_byte(&bitmap[relativeY * byteWidth + relativeX / 8]);
if (byte & 0x80)
writePixel(mDisplay, x, y, w, h, relativeX, relativeY);
}
}
mDisplay->endWrite();
}
......@@ -7,8 +7,41 @@
#ifndef __display
#define __display
void display_init(void);
void display_drawIndex(void);
void display_drawWait(void);
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "lib/Adafruit_SSD1306.h"
enum struct Screen: uint8_t
{
Blank,
WaitingForComm,
ModuleIndex
};
class Display
{
private:
Adafruit_SSD1306* mDisplay;
Screen mLastScreen = Screen::Blank;
uint8_t mLastModuleIndex;
uint32_t mLastWaiting;
uint8_t mWaitAnimationStep = 0;
void drawRotatedBitmap(int8_t x, int8_t y, const uint8_t bitmap[], int8_t w, int8_t h, uint8_t rotation);
void checkOn();
void drawTitle(const char* title);
void drawCommIcon();
void drawWaitingForComm();
void drawModuleIndex();
public:
void init();
void show(Screen screen);
void off();
};
#endif
\ No newline at end of file
/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "global.h"
#include <avr/eeprom.h>
// EEPROM
// First byte is 0xAA to recognize uninitialised EEPROM
#define EEPROMHeader 0xAA
// Second byte is a version indicator
#define EEPROMCurrentVersion 1
// Version 1 fields
// index: 2
uint8_t moduleIndex = ModuleIndexUndefined;
void global_init(void)
{
uint8_t address = 0;
if (eeprom_read_byte(&address) != EEPROMHeader)
{
eeprom_write_byte(&address, EEPROMHeader); address++;
eeprom_write_byte(&address, EEPROMCurrentVersion); address++;
eeprom_write_byte(&address, moduleIndex); address++;
}
else
{
address++;
uint8_t version = eeprom_read_byte(&address); address++;
if (version >= 1)
{
moduleIndex = eeprom_read_byte(&address); address++;
}
}
}
uint8_t global_getModuleIndex(void)
{
return moduleIndex;
}
void global_setModuleIndex(uint8_t index)
{
if (index == moduleIndex)
return;
moduleIndex = index;
uint8_t address = 2;
eeprom_write_byte(&address, moduleIndex);
}
\ No newline at end of file
/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "global.h"
int serialRead()
{
return Serial.read();
}
int serialAvailable()
{
return Serial.available();
}
size_t serialWrite(const byte what)
{
return Serial.write(what);
}
Settings settings;
Display display;
RS485 comm(serialRead, serialAvailable, serialWrite, 20);
PCA9685 ledDriver;
\ No newline at end of file
......@@ -7,13 +7,14 @@
#ifndef __global
#define __global
#include <stdint.h>
#include "lib/RS485_non_blocking.h"
#include "lib/PCA9685.h"
#include "settings.h"
#include "display.h"
#define ModuleIndexUndefined 0xff
void global_init(void);
uint8_t global_getModuleIndex(void);
void global_setModuleIndex(uint8_t index);
extern Settings settings;
extern Display display;
extern RS485 comm;
extern PCA9685 ledDriver;
#endif
\ No newline at end of file
/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
// http://javl.github.io/image2cpp/
// Draw mode: horizontal
const uint8_t IconCommWidth = 12;
const uint8_t IconCommHeight = 8;
const uint8_t PROGMEM IconCommOff[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const uint8_t PROGMEM IconCommOn[] = {
0x40, 0x20, 0x40, 0x20, 0x90, 0x90, 0xa6, 0x50, 0xa6, 0x50, 0x90, 0x90, 0x40, 0x20, 0x40, 0x20
};
const uint8_t WaitCursorSegmentWidth = 10;
const uint8_t WaitCursorSegmentHeight = 10;
const uint8_t PROGMEM WaitCursorSegment[] = {
0x07, 0x80, 0x1f, 0x80, 0x3e, 0x00, 0x78, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xc0, 0x00,
0xc0, 0x00, 0x00, 0x00
};
\ No newline at end of file
This diff is collapsed.
/*!
* @file Adafruit_SSD1306.h
*
* This is part of for Adafruit's SSD1306 library for monochrome
* OLED displays: http://www.adafruit.com/category/63_98
*
* These displays use I2C or SPI to communicate. I2C requires 2 pins
* (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK,
* select, data/command) and optionally a reset pin. Hardware SPI or
* 'bitbang' software SPI are both supported.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Limor Fried/Ladyada for Adafruit Industries, with
* contributions from the open source community.
*
* BSD license, all text above, and the splash screen header file,
* must be included in any redistribution.
*
*/
/*
Modifications for Stairs project:
- Removed the Adafruit splash screen. I'm not going to show it anyways, so all it
does is take up valuable program space.
*/
#ifndef _Adafruit_SSD1306_H_
#define _Adafruit_SSD1306_H_
// ONE of the following three lines must be #defined:
//#define SSD1306_128_64 ///< DEPRECTAED: old way to specify 128x64 screen
#define SSD1306_128_32 ///< DEPRECATED: old way to specify 128x32 screen
//#define SSD1306_96_16 ///< DEPRECATED: old way to specify 96x16 screen
// This establishes the screen dimensions in old Adafruit_SSD1306 sketches
// (NEW CODE SHOULD IGNORE THIS, USE THE CONSTRUCTORS THAT ACCEPT WIDTH
// AND HEIGHT ARGUMENTS).
#if defined(ARDUINO_STM32_FEATHER)
typedef class HardwareSPI SPIClass;
#endif
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#if defined(__AVR__)
typedef volatile uint8_t PortReg;
typedef uint8_t PortMask;
#define HAVE_PORTREG
#elif defined(__SAM3X8E__)
typedef volatile RwReg PortReg;
typedef uint32_t PortMask;
#define HAVE_PORTREG
#elif defined(__arm__) || defined(ARDUINO_FEATHER52)
typedef volatile uint32_t PortReg;
typedef uint32_t PortMask;
#define HAVE_PORTREG
#endif
#define BLACK 0 ///< Draw 'off' pixels
#define WHITE 1 ///< Draw 'on' pixels
#define INVERSE 2 ///< Invert pixels
#define SSD1306_MEMORYMODE 0x20 ///< See datasheet
#define SSD1306_COLUMNADDR 0x21 ///< See datasheet
#define SSD1306_PAGEADDR 0x22 ///< See datasheet
#define SSD1306_SETCONTRAST 0x81 ///< See datasheet
#define SSD1306_CHARGEPUMP 0x8D ///< See datasheet
#define SSD1306_SEGREMAP 0xA0 ///< See datasheet
#define SSD1306_DISPLAYALLON_RESUME 0xA4 ///< See datasheet
#define SSD1306_DISPLAYALLON 0xA5 ///< Not currently used
#define SSD1306_NORMALDISPLAY 0xA6 ///< See datasheet
#define SSD1306_INVERTDISPLAY 0xA7 ///< See datasheet
#define SSD1306_SETMULTIPLEX 0xA8 ///< See datasheet
#define SSD1306_DISPLAYOFF 0xAE ///< See datasheet
#define SSD1306_DISPLAYON 0xAF ///< See datasheet
#define SSD1306_COMSCANINC 0xC0 ///< Not currently used
#define SSD1306_COMSCANDEC 0xC8 ///< See datasheet
#define SSD1306_SETDISPLAYOFFSET 0xD3 ///< See datasheet
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 ///< See datasheet
#define SSD1306_SETPRECHARGE 0xD9 ///< See datasheet
#define SSD1306_SETCOMPINS 0xDA ///< See datasheet
#define SSD1306_SETVCOMDETECT 0xDB ///< See datasheet
#define SSD1306_SETLOWCOLUMN 0x00 ///< Not currently used
#define SSD1306_SETHIGHCOLUMN 0x10 ///< Not currently used
#define SSD1306_SETSTARTLINE 0x40 ///< See datasheet
#define SSD1306_EXTERNALVCC 0x01 ///< External display voltage source
#define SSD1306_SWITCHCAPVCC 0x02 ///< Gen. display voltage from 3.3V
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 ///< Init rt scroll
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 ///< Init left scroll
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 ///< Init diag scroll
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A ///< Init diag scroll
#define SSD1306_DEACTIVATE_SCROLL 0x2E ///< Stop scroll
#define SSD1306_ACTIVATE_SCROLL 0x2F ///< Start scroll
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 ///< Set scroll range
// Deprecated size stuff for backwards compatibility with old sketches
#if defined SSD1306_128_64
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_64 defined
#define SSD1306_LCDHEIGHT 64 ///< DEPRECATED: height w/SSD1306_128_64 defined
#endif
#if defined SSD1306_128_32
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_32 defined
#define SSD1306_LCDHEIGHT 32 ///< DEPRECATED: height w/SSD1306_128_32 defined
#endif
#if defined SSD1306_96_16
#define SSD1306_LCDWIDTH 96 ///< DEPRECATED: width w/SSD1306_96_16 defined
#define SSD1306_LCDHEIGHT 16 ///< DEPRECATED: height w/SSD1306_96_16 defined
#endif
/*!
@brief Class that stores state and functions for interacting with
SSD1306 OLED displays.
*/
class Adafruit_SSD1306 : public Adafruit_GFX {
public:
// NEW CONSTRUCTORS -- recommended for new projects
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi=&Wire, int8_t rst_pin=-1,
uint32_t clkDuring=400000UL, uint32_t clkAfter=100000UL);
Adafruit_SSD1306(uint8_t w, uint8_t h, int8_t mosi_pin, int8_t sclk_pin,
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi,
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, uint32_t bitrate=8000000UL);
// DEPRECATED CONSTRUCTORS - for back compatibility, avoid in new projects
Adafruit_SSD1306(int8_t mosi_pin, int8_t sclk_pin, int8_t dc_pin,
int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(int8_t rst_pin = -1);
~Adafruit_SSD1306(void);
boolean begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC,
uint8_t i2caddr=0, boolean reset=true,
boolean periphBegin=true);
void display(void);
void clearDisplay(void);
void invertDisplay(boolean i);
void dim(boolean dim);
void drawPixel(int16_t x, int16_t y, uint16_t color);
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void startscrollright(uint8_t start, uint8_t stop);
void startscrollleft(uint8_t start, uint8_t stop);
void startscrolldiagright(uint8_t start, uint8_t stop);
void startscrolldiagleft(uint8_t start, uint8_t stop);
void stopscroll(void);
void ssd1306_command(uint8_t c);
boolean getPixel(int16_t x, int16_t y);
uint8_t *getBuffer(void);
private:
inline void SPIwrite(uint8_t d) __attribute__((always_inline));
void drawFastHLineInternal(int16_t x, int16_t y, int16_t w,
uint16_t color);
void drawFastVLineInternal(int16_t x, int16_t y, int16_t h,
uint16_t color);
void ssd1306_command1(uint8_t c);
void ssd1306_commandList(const uint8_t *c, uint8_t n);
SPIClass *spi;
TwoWire *wire;
uint8_t *buffer;
int8_t i2caddr, vccstate, page_end;
int8_t mosiPin , clkPin , dcPin , csPin, rstPin;
#ifdef HAVE_PORTREG
PortReg *mosiPort , *clkPort , *dcPort , *csPort;
PortMask mosiPinMask, clkPinMask, dcPinMask, csPinMask;
#endif
#if defined(SPI_HAS_TRANSACTION)
SPISettings spiSettings;
#endif
#if ARDUINO >= 157
uint32_t wireClk; // Wire speed for SSD1306 transfers
uint32_t restoreClk; // Wire speed following SSD1306 transfers
#endif
};
#endif // _Adafruit_SSD1306_H_
/*
* PCA9685 library for Arduino
* Copyright 2017 (c) Mark van Renswoude
*/
#include <stdint.h>
#include "./PCA9685.h"
#include <Wire.h>
#include <Arduino.h>