Ported to C++ for the ATMega328P
Added fancier display elements now that we've got room to spare
This commit is contained in:
parent
5f89b3efd2
commit
a5eab1427d
@ -1,3 +1,3 @@
|
||||
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
|
||||
@avrdude -c usbtiny -p m328p -e -U lfuse:w:0xE2:m -U hfuse:w:0xd9:m -U efuse:w:0x07:m
|
@ -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
|
||||
upload_protocol = usbtiny
|
||||
|
||||
lib_deps =
|
||||
Adafruit GFX Library
|
BIN
module/res/CommIcon.psd
Normal file
BIN
module/res/CommIcon.psd
Normal file
Binary file not shown.
BIN
module/res/WaitCursor.psd
Normal file
BIN
module/res/WaitCursor.psd
Normal file
Binary file not shown.
@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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(¤tX, firstStep);
|
||||
|
||||
ssd1306_drawminus_32px(currentX);
|
||||
currentX += CharOuterWidth;
|
||||
|
||||
display_drawNumber(¤tX, 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;
|
||||
}
|
197
module/src/display.cpp
Normal file
197
module/src/display.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
29
module/src/global.cpp
Normal file
29
module/src/global.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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;
|
@ -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
|
28
module/src/icons.h
Normal file
28
module/src/icons.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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
|
||||
};
|
1109
module/src/lib/Adafruit_SSD1306.cpp
Normal file
1109
module/src/lib/Adafruit_SSD1306.cpp
Normal file
File diff suppressed because it is too large
Load Diff
185
module/src/lib/Adafruit_SSD1306.h
Normal file
185
module/src/lib/Adafruit_SSD1306.h
Normal file
@ -0,0 +1,185 @@
|
||||
/*!
|
||||
* @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_
|
127
module/src/lib/PCA9685.cpp
Normal file
127
module/src/lib/PCA9685.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* PCA9685 library for Arduino
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "./PCA9685.h"
|
||||
#include <Wire.h>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
void PCA9685::setAddress(uint8_t address)
|
||||
{
|
||||
this->mAddress = address;
|
||||
}
|
||||
|
||||
|
||||
uint8_t PCA9685::read(uint8_t registerAddress)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
|
||||
Wire.beginTransmission(this->mAddress);
|
||||
Wire.write(registerAddress);
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.requestFrom(this->mAddress, (uint8_t)1);
|
||||
if (Wire.available())
|
||||
result = Wire.read();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PCA9685::write(uint8_t registerAddress, uint8_t value)
|
||||
{
|
||||
Wire.beginTransmission(this->mAddress);
|
||||
Wire.write(registerAddress);
|
||||
Wire.write(value);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
|
||||
inline void PCA9685::write(uint8_t data)
|
||||
{
|
||||
Wire.write(data);
|
||||
}
|
||||
|
||||
|
||||
static const float prescaleValue = 25000000 / 4096;
|
||||
|
||||
void PCA9685::setPWMFrequency(float frequency)
|
||||
{
|
||||
// Credit to the Adafruit PWM Servo Driver library for these calculations
|
||||
// https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/
|
||||
uint8_t prescale = floor(prescaleValue / (frequency * 0.9) - 0.5);
|
||||
|
||||
// Sleep while changing the frequency
|
||||
uint8_t oldMode = this->read(PCA9685::RegisterMode1) & !PCA9685::Mode1Sleep;
|
||||
uint8_t newMode = (oldMode & !PCA9685::Mode1Restart) | PCA9685::Mode1Sleep;
|
||||
|
||||
this->write(PCA9685::RegisterMode1, newMode);
|
||||
this->write(PCA9685::RegisterPrescale, prescale);
|
||||
this->write(PCA9685::RegisterMode1, oldMode);
|
||||
|
||||
// According to the datasheet:
|
||||
// It takes 500 us max. for the oscillator to be up and running once
|
||||
// SLEEP bit has been set to logic 0
|
||||
//
|
||||
// The Adafruit library uses 5 milliseconds, so I'll stick to that as well.
|
||||
delay(5);
|
||||
|
||||
// Restart and turn on auto-increment (required for SetPWM)
|
||||
this->write(PCA9685::RegisterMode1, oldMode | PCA9685::Mode1Restart | PCA9685::Mode1AI | PCA9685::Mode1AllCall);
|
||||
}
|
||||
|
||||
|
||||
void PCA9685::setPWM(uint8_t pin, uint16_t value)
|
||||
{
|
||||
if (value < 0) value = 0;
|
||||
if (value > 4095) value = 4095;
|
||||
|
||||
if (value == 4095)
|
||||
this->setPWM(pin, 4096, 0);
|
||||
else if (value == 0)
|
||||
this->setPWM(pin, 0, 4096);
|
||||
else
|
||||
this->setPWM(pin, 0, value);
|
||||
}
|
||||
|
||||
|
||||
void PCA9685::setPWM(uint8_t pin, uint16_t on, uint16_t off)
|
||||
{
|
||||
Wire.beginTransmission(this->mAddress);
|
||||
this->write(PCA9685::RegisterLED0OnL + (4 * pin));
|
||||
this->write(on);
|
||||
this->write(on >> 8);
|
||||
this->write(off);
|
||||
this->write(off >> 8);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PCA9685::setAll(uint16_t value)
|
||||
{
|
||||
if (value < 0) value = 0;
|
||||
if (value > 4095) value = 4095;
|
||||
|
||||
if (value == 4095)
|
||||
this->setAll(4096, 0);
|
||||
else if (value == 0)
|
||||
this->setAll(0, 4096);
|
||||
else
|
||||
this->setAll(0, value);
|
||||
}
|
||||
|
||||
|
||||
void PCA9685::setAll(uint16_t on, uint16_t off)
|
||||
{
|
||||
Wire.beginTransmission(this->mAddress);
|
||||
this->write(PCA9685::RegisterAllLEDOnL);
|
||||
this->write(on);
|
||||
this->write(on >> 8);
|
||||
this->write(off);
|
||||
this->write(off >> 8);
|
||||
Wire.endTransmission();
|
||||
}
|
48
module/src/lib/PCA9685.h
Normal file
48
module/src/lib/PCA9685.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* PCA9685 library for Arduino
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*/
|
||||
#ifndef __PCA9685
|
||||
#define __PCA9685
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
class PCA9685
|
||||
{
|
||||
private:
|
||||
uint8_t mAddress;
|
||||
|
||||
protected:
|
||||
uint8_t read(uint8_t registerAddress);
|
||||
void write(uint8_t registerAddress, uint8_t value);
|
||||
inline void write(uint8_t data);
|
||||
|
||||
public:
|
||||
static const uint16_t Off = 0;
|
||||
static const uint16_t On = 4095;
|
||||
|
||||
static const uint8_t RegisterMode1 = 0x0;
|
||||
static const uint8_t RegisterPrescale = 0xFE;
|
||||
|
||||
static const uint8_t RegisterLED0OnL = 0x6;
|
||||
static const uint8_t RegisterAllLEDOnL = 0xFA;
|
||||
|
||||
static const uint8_t Mode1Restart = 0x80;
|
||||
static const uint8_t Mode1AI = 0x20;
|
||||
static const uint8_t Mode1Sleep = 0x10;
|
||||
static const uint8_t Mode1AllCall = 0x01;
|
||||
|
||||
// Call this if you already initialized the I2C library
|
||||
void setAddress(uint8_t address);
|
||||
|
||||
void setPWMFrequency(float frequency);
|
||||
|
||||
void setPWM(uint8_t pin, uint16_t value);
|
||||
void setPWM(uint8_t pin, uint16_t on, uint16_t off);
|
||||
|
||||
void setAll(uint16_t value);
|
||||
void setAll(uint16_t on, uint16_t off);
|
||||
};
|
||||
|
||||
#endif
|
232
module/src/lib/RS485_non_blocking.cpp
Normal file
232
module/src/lib/RS485_non_blocking.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
RS485 protocol library - non-blocking.
|
||||
|
||||
Devised and written by Nick Gammon.
|
||||
Date: 4 December 2012
|
||||
Version: 1.0
|
||||
|
||||
Can send from 1 to 255 bytes from one node to another with:
|
||||
|
||||
* Packet start indicator (STX)
|
||||
* Each data byte is doubled and inverted to check validity
|
||||
* Packet end indicator (ETX)
|
||||
* Packet CRC (checksum)
|
||||
|
||||
|
||||
To allow flexibility with hardware (eg. Serial, SoftwareSerial, I2C)
|
||||
you provide three "callback" functions which send or receive data. Examples are:
|
||||
|
||||
size_t fWrite (const byte what)
|
||||
{
|
||||
return Serial.write (what);
|
||||
}
|
||||
|
||||
int fAvailable ()
|
||||
{
|
||||
return Serial.available ();
|
||||
}
|
||||
|
||||
int fRead ()
|
||||
{
|
||||
return Serial.read ();
|
||||
}
|
||||
|
||||
|
||||
PERMISSION TO DISTRIBUTE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
LIMITATION OF LIABILITY
|
||||
|
||||
The software is provided "as is", without warranty of any kind, express or implied,
|
||||
including but not limited to the warranties of merchantability, fitness for a particular
|
||||
purpose and noninfringement. In no event shall the authors or copyright holders be liable
|
||||
for any claim, damages or other liability, whether in an action of contract,
|
||||
tort or otherwise, arising from, out of or in connection with the software
|
||||
or the use or other dealings in the software.
|
||||
|
||||
*/
|
||||
|
||||
#include "RS485_non_blocking.h"
|
||||
|
||||
// allocate the requested buffer size
|
||||
void RS485::begin ()
|
||||
{
|
||||
data_ = (byte *) malloc (bufferSize_);
|
||||
reset ();
|
||||
errorCount_ = 0;
|
||||
} // end of RS485::begin
|
||||
|
||||
// get rid of the buffer
|
||||
void RS485::stop ()
|
||||
{
|
||||
reset ();
|
||||
free (data_);
|
||||
data_ = NULL;
|
||||
} // end of RS485::stop
|
||||
|
||||
// called after an error to return to "not in a packet"
|
||||
void RS485::reset ()
|
||||
{
|
||||
haveSTX_ = false;
|
||||
available_ = false;
|
||||
inputPos_ = 0;
|
||||
startTime_ = 0;
|
||||
} // end of RS485::reset
|
||||
|
||||
// calculate 8-bit CRC
|
||||
byte RS485::crc8 (const byte *addr, byte len)
|
||||
{
|
||||
byte crc = 0;
|
||||
while (len--)
|
||||
{
|
||||
byte inbyte = *addr++;
|
||||
for (byte i = 8; i; i--)
|
||||
{
|
||||
byte mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix)
|
||||
crc ^= 0x8C;
|
||||
inbyte >>= 1;
|
||||
} // end of for
|
||||
} // end of while
|
||||
return crc;
|
||||
} // end of RS485::crc8
|
||||
|
||||
// send a byte complemented, repeated
|
||||
// only values sent would be (in hex):
|
||||
// 0F, 1E, 2D, 3C, 4B, 5A, 69, 78, 87, 96, A5, B4, C3, D2, E1, F0
|
||||
void RS485::sendComplemented (const byte what)
|
||||
{
|
||||
byte c;
|
||||
|
||||
// first nibble
|
||||
c = what >> 4;
|
||||
fWriteCallback_ ((c << 4) | (c ^ 0x0F));
|
||||
|
||||
// second nibble
|
||||
c = what & 0x0F;
|
||||
fWriteCallback_ ((c << 4) | (c ^ 0x0F));
|
||||
|
||||
} // end of RS485::sendComplemented
|
||||
|
||||
// send a message of "length" bytes (max 255) to other end
|
||||
// put STX at start, ETX at end, and add CRC
|
||||
void RS485::sendMsg (const byte * data, const byte length)
|
||||
{
|
||||
// no callback? Can't send
|
||||
if (fWriteCallback_ == NULL)
|
||||
return;
|
||||
|
||||
fWriteCallback_ (STX); // STX
|
||||
for (byte i = 0; i < length; i++)
|
||||
sendComplemented (data [i]);
|
||||
fWriteCallback_ (ETX); // ETX
|
||||
sendComplemented (crc8 (data, length));
|
||||
} // end of RS485::sendMsg
|
||||
|
||||
// called periodically from main loop to process data and
|
||||
// assemble the finished packet in 'data_'
|
||||
|
||||
// returns true if packet received.
|
||||
|
||||
// You could implement a timeout by seeing if isPacketStarted() returns
|
||||
// true, and if too much time has passed since getPacketStartTime() time.
|
||||
|
||||
bool RS485::update ()
|
||||
{
|
||||
// no data? can't go ahead (eg. begin() not called)
|
||||
if (data_ == NULL)
|
||||
return false;
|
||||
|
||||
// no callbacks? Can't read
|
||||
if (fAvailableCallback_ == NULL || fReadCallback_ == NULL)
|
||||
return false;
|
||||
|
||||
while (fAvailableCallback_ () > 0)
|
||||
{
|
||||
byte inByte = fReadCallback_ ();
|
||||
|
||||
switch (inByte)
|
||||
{
|
||||
|
||||
case STX: // start of text
|
||||
haveSTX_ = true;
|
||||
haveETX_ = false;
|
||||
inputPos_ = 0;
|
||||
firstNibble_ = true;
|
||||
startTime_ = millis ();
|
||||
break;
|
||||
|
||||
case ETX: // end of text (now expect the CRC check)
|
||||
haveETX_ = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
// wait until packet officially starts
|
||||
if (!haveSTX_)
|
||||
break;
|
||||
|
||||
// check byte is in valid form (4 bits followed by 4 bits complemented)
|
||||
if ((inByte >> 4) != ((inByte & 0x0F) ^ 0x0F) )
|
||||
{
|
||||
reset ();
|
||||
errorCount_++;
|
||||
break; // bad character
|
||||
} // end if bad byte
|
||||
|
||||
// convert back
|
||||
inByte >>= 4;
|
||||
|
||||
// high-order nibble?
|
||||
if (firstNibble_)
|
||||
{
|
||||
currentByte_ = inByte;
|
||||
firstNibble_ = false;
|
||||
break;
|
||||
} // end of first nibble
|
||||
|
||||
// low-order nibble
|
||||
currentByte_ <<= 4;
|
||||
currentByte_ |= inByte;
|
||||
firstNibble_ = true;
|
||||
|
||||
// if we have the ETX this must be the CRC
|
||||
if (haveETX_)
|
||||
{
|
||||
if (crc8 (data_, inputPos_) != currentByte_)
|
||||
{
|
||||
reset ();
|
||||
errorCount_++;
|
||||
break; // bad crc
|
||||
} // end of bad CRC
|
||||
|
||||
available_ = true;
|
||||
return true; // show data ready
|
||||
} // end if have ETX already
|
||||
|
||||
// keep adding if not full
|
||||
if (inputPos_ < bufferSize_)
|
||||
data_ [inputPos_++] = currentByte_;
|
||||
else
|
||||
{
|
||||
reset (); // overflow, start again
|
||||
errorCount_++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
} // end of switch
|
||||
} // end of while incoming data
|
||||
|
||||
return false; // not ready yet
|
||||
} // end of RS485::update
|
||||
|
107
module/src/lib/RS485_non_blocking.h
Normal file
107
module/src/lib/RS485_non_blocking.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
RS485 protocol library - non-blocking.
|
||||
|
||||
Devised and written by Nick Gammon.
|
||||
Date: 4 December 2012
|
||||
Version: 1.0
|
||||
|
||||
Licence: Released for public use.
|
||||
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
|
||||
class RS485
|
||||
{
|
||||
|
||||
typedef size_t (*WriteCallback) (const byte what); // send a byte to serial port
|
||||
typedef int (*AvailableCallback) (); // return number of bytes available
|
||||
typedef int (*ReadCallback) (); // read a byte from serial port
|
||||
|
||||
enum {
|
||||
STX = '\2', // start of text
|
||||
ETX = '\3' // end of text
|
||||
}; // end of enum
|
||||
|
||||
// callback functions to do reading/writing
|
||||
ReadCallback fReadCallback_;
|
||||
AvailableCallback fAvailableCallback_;
|
||||
WriteCallback fWriteCallback_;
|
||||
|
||||
// where we save incoming stuff
|
||||
byte * data_;
|
||||
|
||||
// how much data is in the buffer
|
||||
const int bufferSize_;
|
||||
|
||||
// this is true once we have valid data in buf
|
||||
bool available_;
|
||||
|
||||
// an STX (start of text) signals a packet start
|
||||
bool haveSTX_;
|
||||
|
||||
// count of errors
|
||||
unsigned long errorCount_;
|
||||
|
||||
// variables below are set when we get an STX
|
||||
bool haveETX_;
|
||||
byte inputPos_;
|
||||
byte currentByte_;
|
||||
bool firstNibble_;
|
||||
unsigned long startTime_;
|
||||
|
||||
// helper private functions
|
||||
byte crc8 (const byte *addr, byte len);
|
||||
void sendComplemented (const byte what);
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
RS485 (ReadCallback fReadCallback,
|
||||
AvailableCallback fAvailableCallback,
|
||||
WriteCallback fWriteCallback,
|
||||
const byte bufferSize) :
|
||||
fReadCallback_ (fReadCallback),
|
||||
fAvailableCallback_ (fAvailableCallback),
|
||||
fWriteCallback_ (fWriteCallback),
|
||||
data_ (NULL),
|
||||
bufferSize_ (bufferSize)
|
||||
{}
|
||||
|
||||
// destructor - frees memory used
|
||||
~RS485 () { stop (); }
|
||||
|
||||
// allocate memory for buf_
|
||||
void begin ();
|
||||
|
||||
// free memory in buf_
|
||||
void stop ();
|
||||
|
||||
// handle incoming data, return true if packet ready
|
||||
bool update ();
|
||||
|
||||
// reset to no incoming data (eg. after a timeout)
|
||||
void reset ();
|
||||
|
||||
// send data
|
||||
void sendMsg (const byte * data, const byte length);
|
||||
|
||||
// returns true if packet available
|
||||
bool available () const { return available_; };
|
||||
|
||||
// once available, returns the address of the current message
|
||||
byte * getData () const { return data_; }
|
||||
byte getLength () const { return inputPos_; }
|
||||
|
||||
// return how many errors we have had
|
||||
unsigned long getErrorCount () const { return errorCount_; }
|
||||
|
||||
// return when last packet started
|
||||
unsigned long getPacketStartTime () const { return startTime_; }
|
||||
|
||||
// return true if a packet has started to be received
|
||||
bool isPacketStarted () const { return haveSTX_; }
|
||||
|
||||
}; // end of class RS485
|
||||
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*/
|
||||
#include "TinyMillis.h"
|
||||
#include <avr/io.h>
|
||||
#include <util/atomic.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
|
||||
volatile uint16_t tickCount;
|
||||
|
||||
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
tickCount++;
|
||||
}
|
||||
|
||||
|
||||
void millis_init(void)
|
||||
{
|
||||
uint32_t overflow;
|
||||
|
||||
overflow = ((F_CPU / 1000) / 8);
|
||||
|
||||
TCCR1B |= (1 << WGM12) | (1 << CS11);
|
||||
OCR1AH = (overflow >> 8);
|
||||
OCR1AL = overflow;
|
||||
|
||||
TIMSK |= (1 << OCIE1A);
|
||||
}
|
||||
|
||||
|
||||
uint16_t millis(void)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
ATOMIC_BLOCK(ATOMIC_FORCEON)
|
||||
{
|
||||
value = tickCount;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Counts the elapsed milliseconds similar to Arduino's millis(void).
|
||||
* Accuracy is not assumed to be important, and assumes no long-running
|
||||
* checks are done so it can get away with 16 bits (65 seconds max).
|
||||
*/
|
||||
#ifndef __TinyMillis
|
||||
#define __TinyMillis
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// call sei(void) afterwards
|
||||
void millis_init(void);
|
||||
|
||||
uint16_t millis(void);
|
||||
|
||||
#endif
|
@ -1,262 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Straight up port of Nick Gammon's non-blocking
|
||||
* RS485 library to C and removing the Arduino
|
||||
* framework dependency.
|
||||
*
|
||||
* Source:
|
||||
* https://www.gammon.com.au/forum/?id=11428
|
||||
* Devised and written by Nick Gammon.
|
||||
* Date: 4 December 2012
|
||||
* Version: 1.0
|
||||
*/
|
||||
#include "TinyRS485.h"
|
||||
#include "TinyMillis.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
ReadCallback doRead;
|
||||
AvailableCallback doAvailable;
|
||||
WriteCallback doWrite;
|
||||
|
||||
|
||||
// where we save incoming stuff
|
||||
uint8_t* data;
|
||||
|
||||
// how much data is in the buffer
|
||||
uint8_t bufferSize;
|
||||
|
||||
// this is true once we have valid data in buf
|
||||
bool available;
|
||||
|
||||
// an STX (start of text) signals a packet start
|
||||
bool haveSTX;
|
||||
|
||||
// count of errors
|
||||
uint16_t errorCount;
|
||||
|
||||
// variables below are set when we get an STX
|
||||
bool haveETX;
|
||||
uint8_t inputPos;
|
||||
uint8_t currentByte;
|
||||
bool firstNibble;
|
||||
uint16_t startTime;
|
||||
|
||||
|
||||
// helper private functions
|
||||
uint8_t crc8 (uint8_t* addr, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
while (len--)
|
||||
{
|
||||
uint8_t inbyte = *addr++;
|
||||
for (uint8_t i = 8; i; i--)
|
||||
{
|
||||
uint8_t mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix)
|
||||
crc ^= 0x8C;
|
||||
|
||||
inbyte >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
// send a byte complemented, repeated
|
||||
// only values sent would be (in hex):
|
||||
// 0F, 1E, 2D, 3C, 4B, 5A, 69, 78, 87, 96, A5, B4, C3, D2, E1, F0
|
||||
void sendComplemented(uint8_t what)
|
||||
{
|
||||
uint8_t c;
|
||||
|
||||
// first nibble
|
||||
c = what >> 4;
|
||||
doWrite((c << 4) | (c ^ 0x0F));
|
||||
|
||||
// second nibble
|
||||
c = what & 0x0F;
|
||||
doWrite((c << 4) | (c ^ 0x0F));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void rs485_begin(ReadCallback readCallback,
|
||||
AvailableCallback availableCallback,
|
||||
WriteCallback writeCallback,
|
||||
uint8_t bufferSize)
|
||||
{
|
||||
doRead = readCallback;
|
||||
doAvailable = availableCallback;
|
||||
doWrite = writeCallback;
|
||||
data = (uint8_t*)malloc(bufferSize);
|
||||
rs485_reset();
|
||||
errorCount = 0;
|
||||
}
|
||||
|
||||
|
||||
void rs485_stop(void)
|
||||
{
|
||||
rs485_reset();
|
||||
free(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
|
||||
void rs485_reset(void)
|
||||
{
|
||||
haveSTX = false;
|
||||
available = false;
|
||||
inputPos = 0;
|
||||
startTime = 0;
|
||||
}
|
||||
|
||||
|
||||
// send a message of "length" bytes (max 255) to other end
|
||||
// put STX at start, ETX at end, and add CRC
|
||||
void rs485_sendMsg(uint8_t* data, uint8_t length)
|
||||
{
|
||||
doWrite(STX); // STX
|
||||
for (uint8_t i = 0; i < length; i++)
|
||||
sendComplemented(data[i]);
|
||||
|
||||
doWrite(ETX); // ETX
|
||||
sendComplemented(crc8(data, length));
|
||||
}
|
||||
|
||||
|
||||
bool rs485_available(void)
|
||||
{
|
||||
return available;
|
||||
}
|
||||
|
||||
|
||||
uint8_t* rs485_getData(void)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
uint8_t rs485_getLength(void)
|
||||
{
|
||||
return inputPos;
|
||||
}
|
||||
|
||||
|
||||
uint16_t rs485_getErrorCount(void)
|
||||
{
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
|
||||
uint16_t rs485_getPacketStartTime(void)
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
|
||||
bool rs485_isPacketStarted(void)
|
||||
{
|
||||
return haveSTX;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// called periodically from main loop to process data and
|
||||
// assemble the finished packet in 'data_'
|
||||
|
||||
// returns true if packet received.
|
||||
|
||||
// You could implement a timeout by seeing if isPacketStarted() returns
|
||||
// true, and if too much time has passed since getPacketStartTime() time.
|
||||
bool rs485_update(void)
|
||||
{
|
||||
// no data? can't go ahead (eg. begin() not called)
|
||||
if (data == NULL)
|
||||
return false;
|
||||
|
||||
// no callbacks? Can't read
|
||||
if (doAvailable == NULL || doRead == NULL)
|
||||
return false;
|
||||
|
||||
while (doAvailable() > 0)
|
||||
{
|
||||
uint8_t inByte = doRead();
|
||||
|
||||
switch (inByte)
|
||||
{
|
||||
case STX: // start of text
|
||||
haveSTX = true;
|
||||
haveETX = false;
|
||||
inputPos = 0;
|
||||
firstNibble = true;
|
||||
startTime = millis();
|
||||
break;
|
||||
|
||||
case ETX: // end of text (now expect the CRC check)
|
||||
haveETX = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
// wait until packet officially starts
|
||||
if (!haveSTX)
|
||||
break;
|
||||
|
||||
// check byte is in valid form (4 bits followed by 4 bits complemented)
|
||||
if ((inByte >> 4) != ((inByte & 0x0F) ^ 0x0F))
|
||||
{
|
||||
rs485_reset();
|
||||
errorCount++;
|
||||
break; // bad character
|
||||
}
|
||||
|
||||
// convert back
|
||||
inByte >>= 4;
|
||||
|
||||
// high-order nibble?
|
||||
if (firstNibble)
|
||||
{
|
||||
currentByte = inByte;
|
||||
firstNibble = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// low-order nibble
|
||||
currentByte <<= 4;
|
||||
currentByte |= inByte;
|
||||
firstNibble = true;
|
||||
|
||||
// if we have the ETX this must be the CRC
|
||||
if (haveETX)
|
||||
{
|
||||
if (crc8(data, inputPos) != currentByte)
|
||||
{
|
||||
rs485_reset();
|
||||
errorCount++;
|
||||
break; // bad crc
|
||||
}
|
||||
|
||||
available = true;
|
||||
return true; // show data ready
|
||||
}
|
||||
|
||||
// keep adding if not full
|
||||
if (inputPos < bufferSize)
|
||||
data[inputPos++] = currentByte;
|
||||
else
|
||||
{
|
||||
rs485_reset(); // overflow, start again
|
||||
errorCount++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // not ready yet
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Straight up port of Nick Gammon's non-blocking
|
||||
* RS485 library to C and removing the Arduino
|
||||
* framework dependency.
|
||||
*
|
||||
* Source:
|
||||
* https://www.gammon.com.au/forum/?id=11428
|
||||
* Devised and written by Nick Gammon.
|
||||
* Date: 4 December 2012
|
||||
* Version: 1.0
|
||||
*/
|
||||
#ifndef __TinyRS485
|
||||
#define __TinyRS485
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef void (*WriteCallback) (uint8_t what); // send a byte to serial port
|
||||
typedef uint8_t (*AvailableCallback) (void); // return number of bytes available
|
||||
typedef uint8_t (*ReadCallback) (void); // read a byte from serial port
|
||||
|
||||
|
||||
enum {
|
||||
STX = '\2', // start of text
|
||||
ETX = '\3' // end of text
|
||||
}; // end of enum
|
||||
|
||||
|
||||
// allocate the requested buffer size
|
||||
void rs485_begin(ReadCallback readCallback,
|
||||
AvailableCallback availableCallback,
|
||||
WriteCallback writeCallback,
|
||||
uint8_t bufferSize);
|
||||
|
||||
// get rid of the buffer
|
||||
void rs485_stop(void);
|
||||
|
||||
// handle incoming data, return true if packet ready
|
||||
bool rs485_update(void);
|
||||
|
||||
// reset to no incoming data (eg. after a timeout)
|
||||
void rs485_reset(void);
|
||||
|
||||
// send data
|
||||
void rs485_sendMsg(uint8_t* data, uint8_t length);
|
||||
|
||||
// returns true if packet available
|
||||
bool rs485_available(void);
|
||||
|
||||
// once available, returns the address of the current message
|
||||
uint8_t* rs485_getData(void);
|
||||
uint8_t rs485_getLength(void);
|
||||
|
||||
// return how many errors we have had
|
||||
uint16_t rs485_getErrorCount(void);
|
||||
|
||||
// return when last packet started
|
||||
uint16_t rs485_getPacketStartTime(void);
|
||||
|
||||
// return true if a packet has started to be received
|
||||
bool rs485_isPacketStarted(void);
|
||||
|
||||
#endif
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Source:
|
||||
* https://github.com/akafugu/helloworld/blob/master/attiny2313/uart.c
|
||||
*/
|
||||
#include "TinyUART.h"
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
|
||||
volatile static uint8_t rx_buffer[BUFFER_SIZE] = "xxxxxxxxxxxxxxxx";
|
||||
volatile static uint8_t tx_buffer[BUFFER_SIZE] = "xxxxxxxxxxxxxxxx";
|
||||
volatile static uint8_t rx_head = 0;
|
||||
volatile static uint8_t rx_tail = 0;
|
||||
volatile static uint8_t tx_head = 0;
|
||||
volatile static uint8_t tx_tail = 0;
|
||||
|
||||
|
||||
void uart_init(void)
|
||||
{
|
||||
// set baud rate
|
||||
UBRRH = (uint8_t)(MYUBBR >> 8);
|
||||
UBRRL = (uint8_t)(MYUBBR);
|
||||
// enable receive and transmit
|
||||
UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
|
||||
// set frame format
|
||||
UCSRC = (1 << USBS) | (3 << UCSZ0); // asynchron 8n1
|
||||
}
|
||||
|
||||
|
||||
|
||||
void uart_send(uint8_t c)
|
||||
{
|
||||
// wait for empty data register
|
||||
while (!(UCSRA & (1<<UDRE)));
|
||||
// set data into data register
|
||||
UDR = c;
|
||||
}
|
||||
|
||||
|
||||
uint8_t uart_available(void)
|
||||
{
|
||||
return rx_head - rx_tail;
|
||||
}
|
||||
|
||||
|
||||
uint8_t uart_receive(void)
|
||||
{
|
||||
while (!(UCSRA & (1<<RXC)))
|
||||
;
|
||||
return UDR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t uart_getc(void)
|
||||
{
|
||||
uint8_t c = 0;
|
||||
uint8_t tmp_tail = 0;
|
||||
if (rx_head == rx_tail)
|
||||
return UART_NO_DATA;
|
||||
|
||||
tmp_tail = (rx_tail + 1) % BUFFER_SIZE;
|
||||
c = rx_buffer[rx_tail];
|
||||
rx_tail = tmp_tail;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t uart_getc_wait(void)
|
||||
{
|
||||
uint16_t c;
|
||||
while ((c = uart_getc()) == UART_NO_DATA) {}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void uart_putc(uint8_t c)
|
||||
{
|
||||
uint8_t tmp_head = (tx_head + 1) % BUFFER_SIZE;
|
||||
// wait for space in buffer
|
||||
while (tmp_head == tx_tail)
|
||||
;
|
||||
|
||||
tx_buffer[tx_head] = c;
|
||||
tx_head = tmp_head;
|
||||
// enable uart data interrupt (send data)
|
||||
UCSRB |= (1<<UDRIE);
|
||||
}
|
||||
|
||||
|
||||
void uart_puts(const char *s)
|
||||
{
|
||||
while (*s)
|
||||
{
|
||||
uart_putc(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void uart_puts_P(const char *s)
|
||||
{
|
||||
while (pgm_read_byte(s) != 0x00)
|
||||
{
|
||||
uart_putc(pgm_read_byte(s++));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ISR User Data Regiser Empty
|
||||
* Send a char out of buffer via UART. If sending is complete, the
|
||||
* interrupt gets disabled.
|
||||
*/
|
||||
ISR(USART_UDRE_vect)
|
||||
{
|
||||
uint8_t tmp_tail = 0;
|
||||
if (tx_head != tx_tail)
|
||||
{
|
||||
tmp_tail = (tx_tail + 1) % BUFFER_SIZE;
|
||||
UDR = tx_buffer[tx_tail];
|
||||
tx_tail = tmp_tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
// disable this interrupt if nothing more to send
|
||||
UCSRB &= ~(1 << UDRIE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ISR RX complete
|
||||
* Receives a char from UART and stores it in ring buffer.
|
||||
*/
|
||||
ISR(USART_RX_vect)
|
||||
{
|
||||
uint8_t tmp_head = 0;
|
||||
tmp_head = (rx_head + 1) % BUFFER_SIZE;
|
||||
if (tmp_head == rx_tail)
|
||||
{
|
||||
// buffer overflow error!
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_buffer[rx_head] = UDR;
|
||||
rx_head = tmp_head;
|
||||
}
|
||||
}
|
||||
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Source:
|
||||
* https://github.com/akafugu/helloworld/blob/master/attiny2313/uart.c
|
||||
*/
|
||||
#ifndef __TinyUART
|
||||
#define __TinyUART
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define UART_NO_DATA 0x0100
|
||||
|
||||
#define BAUD 38400
|
||||
// 4.096MHz
|
||||
// 4800: 52.3333333
|
||||
// 9600: 25.6666667
|
||||
// 14400: 16.7777778
|
||||
// 19600: 12.06
|
||||
// 28800: 7.8889
|
||||
// 38400: 5.6667
|
||||
|
||||
#define MYUBBR ((F_CPU / (BAUD * 16L)) - 1)
|
||||
#define BUFFER_SIZE 16
|
||||
|
||||
|
||||
void uart_init(void);
|
||||
|
||||
/*
|
||||
* uart_send
|
||||
* Sends a single char to UART without ISR
|
||||
*/
|
||||
void uart_send(uint8_t c);
|
||||
|
||||
|
||||
uint8_t uart_available(void);
|
||||
|
||||
|
||||
/*
|
||||
* uart_receive
|
||||
* Receives a single char without ISR
|
||||
*/
|
||||
uint8_t uart_receive(void);
|
||||
|
||||
|
||||
/*
|
||||
* uart_getc
|
||||
* Gets a single char from the receive buffer.
|
||||
* return uint16_r the received char or UART_NO_DATA
|
||||
*/
|
||||
uint16_t uart_getc(void);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* uart_getc_wait
|
||||
* Blocking call to getc. Will not return until a char is received.
|
||||
*/
|
||||
uint8_t uart_getc_wait(void);
|
||||
|
||||
|
||||
/*
|
||||
* uart_putc
|
||||
* Puts a single char. Will block until there is enough space in the
|
||||
* send buffer.
|
||||
*/
|
||||
void uart_putc(uint8_t c);
|
||||
|
||||
|
||||
/*
|
||||
* uart_puts
|
||||
* Sends a string.
|
||||
*/
|
||||
void uart_puts(const char *s);
|
||||
|
||||
|
||||
/*
|
||||
* uart_puts_P
|
||||
* Sends a PROGMEM string.
|
||||
*/
|
||||
void uart_puts_P(const char *s);
|
||||
|
||||
#endif
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*/
|
||||
#include "VeryTinyBigChars.h"
|
||||
#include "VeryTinySSD1306.h"
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
|
||||
/*
|
||||
* Each digit is composed of a 3x5 grid where each bit represents
|
||||
* whether or not to draw that area. It is then stretched.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* XXX
|
||||
* X
|
||||
* XXX
|
||||
* X
|
||||
* XXX
|
||||
*
|
||||
* The least significant bit is the top left corner, going down
|
||||
* after that before wrapping to the middle column.
|
||||
*/
|
||||
const uint16_t ssd1306_digits_32px [] PROGMEM = {
|
||||
0b0111111000111111, // 0
|
||||
0b0100001111110010, // 1
|
||||
0b0101111010111101, // 2
|
||||
0b0111111010110101, // 3
|
||||
0b0111110010000111, // 4
|
||||
0b0111011010110111, // 5
|
||||
0b0111011010111111, // 6
|
||||
0b0111110000100001, // 7
|
||||
0b0111111010111111, // 8
|
||||
0b0111111010110111 // 9
|
||||
};
|
||||
|
||||
|
||||
void ssd1306_drawchar_32px(uint8_t x, uint16_t matrix)
|
||||
{
|
||||
uint8_t column[4];
|
||||
|
||||
// Vertical mode
|
||||
ssd1306_setMemoryAddressingMode(0b01);
|
||||
|
||||
if (ssd1306_currentRenderFrame() == 1)
|
||||
{
|
||||
ssd1306_setPageStartAddress(4);
|
||||
ssd1306_setPageAddress(4, 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
ssd1306_setPageStartAddress(0);
|
||||
ssd1306_setPageAddress(0, 3);
|
||||
}
|
||||
|
||||
ssd1306_setCursor(x, 0);
|
||||
|
||||
ssd1306_startData();
|
||||
|
||||
// Three columns, each shifted 5 bits
|
||||
for (uint8_t shift = 0; shift < 3; shift++)
|
||||
{
|
||||
uint16_t columnValue = matrix >> (shift * 5);
|
||||
|
||||
// It's a bit rough, but it works! First, third and fifth "row" are 6 bits, in between are 7 bits = 32 total
|
||||
column[0] = 0;
|
||||
column[1] = 0;
|
||||
column[2] = 0;
|
||||
column[3] = 0;
|
||||
|
||||
// 0 = 0b0111111000111111
|
||||
|
||||
if (columnValue & 0b1)
|
||||
column[0] |= 0b00111111;
|
||||
|
||||
if (columnValue & 0b10) {
|
||||
column[0] |= 0b11000000;
|
||||
column[1] |= 0b00011111;
|
||||
}
|
||||
|
||||
if (columnValue & 0b100) {
|
||||
column[1] |= 0b11100000;
|
||||
column[2] |= 0b00000111;
|
||||
}
|
||||
|
||||
if (columnValue & 0b1000) {
|
||||
column[2] |= 0b11111000;
|
||||
column[3] |= 0b00000011;
|
||||
}
|
||||
|
||||
if (columnValue & 0b10000)
|
||||
column[3] |= 0b11111100;
|
||||
|
||||
// Each column is 6 pixels wide
|
||||
for (uint8_t repeat = 0; repeat < SSD1306_CharColumnWidth_32px; repeat++)
|
||||
{
|
||||
ssd1306_sendData(column[0]);
|
||||
ssd1306_sendData(column[1]);
|
||||
ssd1306_sendData(column[2]);
|
||||
ssd1306_sendData(column[3]);
|
||||
}
|
||||
}
|
||||
|
||||
ssd1306_endData();
|
||||
|
||||
// Default: horizontal mode
|
||||
ssd1306_setMemoryAddressingMode(0b00);
|
||||
ssd1306_setPageStartAddress(0);
|
||||
ssd1306_setPageAddress(0, 7);
|
||||
}
|
||||
|
||||
|
||||
void ssd1306_drawdigit_32px(uint8_t x, uint8_t value)
|
||||
{
|
||||
if (value < 0 || value > 9)
|
||||
return;
|
||||
|
||||
ssd1306_drawchar_32px(x, pgm_read_word(&ssd1306_digits_32px[value]));
|
||||
}
|
||||
|
||||
|
||||
void ssd1306_drawplus_32px(uint8_t x)
|
||||
{
|
||||
ssd1306_drawchar_32px(x, 0b0001000111000100);
|
||||
}
|
||||
|
||||
|
||||
void ssd1306_drawminus_32px(uint8_t x)
|
||||
{
|
||||
ssd1306_drawchar_32px(x, 0b0001000010000100);
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*/
|
||||
#ifndef __VeryTinyBigChars
|
||||
#define __VeryTinyBigChars
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define SSD1306_CharColumnWidth_32px 6
|
||||
#define SSD1306_CharWidth_32px (3 * SSD1306_CharColumnWidth_32px)
|
||||
|
||||
void ssd1306_drawchar_32px(uint8_t x, uint16_t matrix);
|
||||
|
||||
|
||||
// Draws a digit from 0 to 9, 32 pixels tall
|
||||
void ssd1306_drawdigit_32px(uint8_t x, uint8_t value);
|
||||
void ssd1306_drawplus_32px(uint8_t x);
|
||||
void ssd1306_drawminus_32px(uint8_t x);
|
||||
|
||||
#endif
|
@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Minimalistic I2C library for the ATTiny2313's limited space.
|
||||
* Based on David Johnson-Davies' TinyI2C:
|
||||
* https://github.com/technoblogy/tiny-i2c
|
||||
*/
|
||||
#include "VeryTinyI2C.h"
|
||||
|
||||
|
||||
// Constants
|
||||
// Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.
|
||||
const unsigned char USISR_8bit = 1<<USISIF | 1<<USIOIF | 1<<USIPF | 1<<USIDC | 0x0<<USICNT0;
|
||||
// Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges.
|
||||
const unsigned char USISR_1bit = 1<<USISIF | 1<<USIOIF | 1<<USIPF | 1<<USIDC | 0xE<<USICNT0;
|
||||
|
||||
|
||||
int32_t I2Ccount;
|
||||
|
||||
|
||||
|
||||
uint8_t i2c_transfer(uint8_t data)
|
||||
{
|
||||
USISR = data; // Set USISR according to data.
|
||||
// Prepare clocking.
|
||||
data = 0<<USISIE | 0<<USIOIE | // Interrupts disabled
|
||||
1<<USIWM1 | 0<<USIWM0 | // Set USI in Two-wire mode.
|
||||
1<<USICS1 | 0<<USICS0 | 1<<USICLK | // Software clock strobe as source.
|
||||
1<<USITC; // Toggle Clock Port.
|
||||
do {
|
||||
DELAY_T2TWI;
|
||||
USICR = data; // Generate positive SCL edge.
|
||||
while (!(PIN_USI_CL & 1<<PIN_USI_SCL)); // Wait for SCL to go high.
|
||||
DELAY_T4TWI;
|
||||
USICR = data; // Generate negative SCL edge.
|
||||
} while (!(USISR & 1<<USIOIF)); // Check for transfer complete.
|
||||
|
||||
DELAY_T2TWI;
|
||||
data = USIDR; // Read out data.
|
||||
USIDR = 0xFF; // Release SDA.
|
||||
DDR_USI |= (1<<PIN_USI_SDA); // Enable SDA as output.
|
||||
|
||||
return data; // Return the data from the USIDR
|
||||
}
|
||||
|
||||
|
||||
void i2c_init(void)
|
||||
{
|
||||
PORT_USI |= 1<<PIN_USI_SDA; // Enable pullup on SDA.
|
||||
PORT_USI_CL |= 1<<PIN_USI_SCL; // Enable pullup on SCL.
|
||||
|
||||
DDR_USI_CL |= 1<<PIN_USI_SCL; // Enable SCL as output.
|
||||
DDR_USI |= 1<<PIN_USI_SDA; // Enable SDA as output.
|
||||
|
||||
USIDR = 0xFF; // Preload data register with "released level" data.
|
||||
USICR = 0<<USISIE | 0<<USIOIE | // Disable Interrupts.
|
||||
1<<USIWM1 | 0<<USIWM0 | // Set USI in Two-wire mode.
|
||||
1<<USICS1 | 0<<USICS0 | 1<<USICLK | // Software stobe as counter clock source
|
||||
0<<USITC;
|
||||
USISR = 1<<USISIF | 1<<USIOIF | 1<<USIPF | 1<<USIDC | // Clear flags,
|
||||
0x0<<USICNT0; // and reset counter.
|
||||
}
|
||||
|
||||
|
||||
uint8_t i2c_read(void)
|
||||
{
|
||||
if ((I2Ccount != 0) && (I2Ccount != -1)) I2Ccount--;
|
||||
|
||||
/* Read a byte */
|
||||
DDR_USI &= ~1<<PIN_USI_SDA; // Enable SDA as input.
|
||||
uint8_t data = i2c_transfer(USISR_8bit);
|
||||
|
||||
/* Prepare to generate ACK (or NACK in case of End Of Transmission) */
|
||||
if (I2Ccount == 0) USIDR = 0xFF; else USIDR = 0x00;
|
||||
i2c_transfer(USISR_1bit); // Generate ACK/NACK.
|
||||
|
||||
return data; // Read successfully completed
|
||||
}
|
||||
|
||||
|
||||
uint8_t i2c_readLast(void)
|
||||
{
|
||||
I2Ccount = 0;
|
||||
return i2c_read();
|
||||
}
|
||||
|
||||
|
||||
bool i2c_write(uint8_t data)
|
||||
{
|
||||
/* Write a byte */
|
||||
PORT_USI_CL &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
|
||||
USIDR = data; // Setup data.
|
||||
i2c_transfer(USISR_8bit); // Send 8 bits on bus.
|
||||
|
||||
/* Clock and verify (N)ACK from slave */
|
||||
DDR_USI &= ~(1<<PIN_USI_SDA); // Enable SDA as input.
|
||||
if (i2c_transfer(USISR_1bit) & 1<<TWI_NACK_BIT) return false;
|
||||
|
||||
return true; // Write successfully completed
|
||||
}
|
||||
|
||||
|
||||
bool i2c_start(uint8_t address, int readcount)
|
||||
{
|
||||
if (readcount != 0) { I2Ccount = readcount; readcount = 1; }
|
||||
uint8_t addressRW = address<<1 | readcount;
|
||||
|
||||
/* Release SCL to ensure that (repeated) Start can be performed */
|
||||
PORT_USI_CL |= 1<<PIN_USI_SCL; // Release SCL.
|
||||
while (!(PIN_USI_CL & 1<<PIN_USI_SCL)); // Verify that SCL becomes high.
|
||||
#ifdef TWI_FAST_MODE
|
||||
DELAY_T4TWI;
|
||||
#else
|
||||
DELAY_T2TWI;
|
||||
#endif
|
||||
|
||||
/* Generate Start Condition */
|
||||
PORT_USI &= ~(1<<PIN_USI_SDA); // Force SDA LOW.
|
||||
DELAY_T4TWI;
|
||||
PORT_USI_CL &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
|
||||
PORT_USI |= 1<<PIN_USI_SDA; // Release SDA.
|
||||
|
||||
if (!(USISR & 1<<USISIF)) return false;
|
||||
|
||||
/*Write address */
|
||||
PORT_USI_CL &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
|
||||
USIDR = addressRW; // Setup data.
|
||||
i2c_transfer(USISR_8bit); // Send 8 bits on bus.
|
||||
|
||||
/* Clock and verify (N)ACK from slave */
|
||||
DDR_USI &= ~(1<<PIN_USI_SDA); // Enable SDA as input.
|
||||
if (i2c_transfer(USISR_1bit) & 1<<TWI_NACK_BIT) return false; // No ACK
|
||||
|
||||
return true; // Start successfully completed
|
||||
}
|
||||
|
||||
bool i2c_restart(uint8_t address, int readcount)
|
||||
{
|
||||
return i2c_start(address, readcount);
|
||||
}
|
||||
|
||||
void i2c_stop(void)
|
||||
{
|
||||
PORT_USI &= ~(1<<PIN_USI_SDA); // Pull SDA low.
|
||||
PORT_USI_CL |= 1<<PIN_USI_SCL; // Release SCL.
|
||||
while (!(PIN_USI_CL & 1<<PIN_USI_SCL)); // Wait for SCL to go high.
|
||||
DELAY_T4TWI;
|
||||
PORT_USI |= 1<<PIN_USI_SDA; // Release SDA.
|
||||
DELAY_T2TWI;
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Straight up port of David Johnson-Davies' TinyI2C to C
|
||||
* and without the Arduino framework dependency.
|
||||
* https://github.com/technoblogy/tiny-i2c
|
||||
*/
|
||||
#ifndef __VeryTinyI2C
|
||||
#define __VeryTinyI2C
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
// Defines
|
||||
//#define TWI_FAST_MODE
|
||||
|
||||
#ifdef TWI_FAST_MODE // TWI FAST mode timing limits. SCL = 100-400kHz
|
||||
#define DELAY_T2TWI (_delay_us(2)) // >1.3us
|
||||
#define DELAY_T4TWI (_delay_us(1)) // >0.6us
|
||||
#else // TWI STANDARD mode timing limits. SCL <= 100kHz
|
||||
#define DELAY_T2TWI (_delay_us(5)) // >4.7us
|
||||
#define DELAY_T4TWI (_delay_us(4)) // >4.0us
|
||||
#endif
|
||||
|
||||
#define TWI_NACK_BIT 0 // Bit position for (N)ACK bit.
|
||||
|
||||
|
||||
// These are valid for the ATTiny2313
|
||||
#define DDR_USI DDRB
|
||||
#define DDR_USI_CL DDR_USI
|
||||
#define PORT_USI PORTB
|
||||
#define PORT_USI_CL PORT_USI
|
||||
#define PIN_USI PINB
|
||||
#define PIN_USI_CL PIN_USI
|
||||
#define PORT_USI_SDA PB5
|
||||
#define PORT_USI_SCL PB7
|
||||
#define PIN_USI_SDA PINB5
|
||||
#define PIN_USI_SCL PINB7
|
||||
|
||||
|
||||
void i2c_init(void);
|
||||
uint8_t i2c_read(void);
|
||||
uint8_t i2c_readLast(void);
|
||||
bool i2c_write(uint8_t data);
|
||||
bool i2c_start(uint8_t address, int readcount);
|
||||
bool i2c_restart(uint8_t address, int readcount);
|
||||
void i2c_stop(void);
|
||||
|
||||
#endif
|
@ -1,517 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Port of Stephen Denne's Tiny4kOLED to C
|
||||
* and without the Arduino framework dependency.
|
||||
* https://github.com/technoblogy/tiny-i2c
|
||||
*/
|
||||
#include "VeryTinySSD1306.h"
|
||||
#include <avr/pgmspace.h>
|
||||
#include "VeryTinyI2C.h"
|
||||
|
||||
#define SSD1306_PAGES 4
|
||||
|
||||
#define SSD1306_COMMAND 0x00
|
||||
#define SSD1306_DATA 0x40
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Some code based on "IIC_without_ACK" by http://www.14blog.com/archives/1358
|
||||
|
||||
const uint8_t ssd1306_init_sequence [] PROGMEM = { // Initialization Sequence
|
||||
// 0xAE, // Display OFF (sleep mode)
|
||||
// 0x20, 0b10, // Set Memory Addressing Mode
|
||||
// 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode;
|
||||
// 10=Page Addressing Mode (RESET); 11=Invalid
|
||||
// 0xB0, // Set Page Start Address for Page Addressing Mode, 0-7
|
||||
0xC8, // Set COM Output Scan Direction
|
||||
// 0x00, // ---set low column address
|
||||
// 0x10, // ---set high column address
|
||||
// 0x40, // --set start line address
|
||||
// 0x81, 0x7F, // Set contrast control register
|
||||
0xA1, // Set Segment Re-map. A0=address mapped; A1=address 127 mapped.
|
||||
// 0xA6, // Set display mode. A6=Normal; A7=Inverse
|
||||
0xA8, 0x1F, // Set multiplex ratio(1 to 64)
|
||||
// 0xA4, // Output RAM to Display
|
||||
// 0xA4=Output follows RAM content; 0xA5,Output ignores RAM content
|
||||
// 0xD3, 0x00, // Set display offset. 00 = no offset
|
||||
// 0xD5, 0x80, // --set display clock divide ratio/oscillator frequency
|
||||
// 0xD9, 0x22, // Set pre-charge period
|
||||
0xDA, 0x02, // Set com pins hardware configuration
|
||||
// 0xDB, 0x20, // --set vcomh 0x20 = 0.77xVcc
|
||||
0x8D, 0x14 // Set DC-DC enable
|
||||
};
|
||||
|
||||
|
||||
const DCfont *oledFont = 0;
|
||||
uint8_t oledX = 0, oledY = 0;
|
||||
uint8_t renderingFrame = 0xB0, drawingFrame = 0x40;
|
||||
|
||||
|
||||
void ssd1306_send_command_start(void)
|
||||
{
|
||||
i2c_start(SSD1306, 0);
|
||||
i2c_write(SSD1306_COMMAND);
|
||||
}
|
||||
|
||||
void ssd1306_send_data_start(void)
|
||||
{
|
||||
i2c_start(SSD1306, 0);
|
||||
i2c_write(SSD1306_DATA);
|
||||
}
|
||||
|
||||
void ssd1306_send_command_byte(uint8_t byte)
|
||||
{
|
||||
if (i2c_write(byte) == 0)
|
||||
{
|
||||
i2c_stop();
|
||||
ssd1306_send_command_start();
|
||||
i2c_write(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_send_data_byte(uint8_t byte)
|
||||
{
|
||||
if (i2c_write(byte) == 0)
|
||||
{
|
||||
i2c_stop();
|
||||
ssd1306_send_data_start();
|
||||
i2c_write(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_send_command(uint8_t command)
|
||||
{
|
||||
ssd1306_send_command_start();
|
||||
i2c_write(command);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ssd1306_send_command2(uint8_t command1, uint8_t command2)
|
||||
{
|
||||
ssd1306_send_command_start();
|
||||
i2c_write(command1);
|
||||
i2c_write(command2);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ssd1306_send_command3(uint8_t command1, uint8_t command2, uint8_t command3)
|
||||
{
|
||||
ssd1306_send_command_start();
|
||||
i2c_write(command1);
|
||||
i2c_write(command2);
|
||||
i2c_write(command3);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ssd1306_send_command6(uint8_t command1, uint8_t command2, uint8_t command3, uint8_t command4, uint8_t command5, uint8_t command6)
|
||||
{
|
||||
ssd1306_send_command_start();
|
||||
i2c_write(command1);
|
||||
i2c_write(command2);
|
||||
i2c_write(command3);
|
||||
i2c_write(command4);
|
||||
i2c_write(command5);
|
||||
i2c_write(command6);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ssd1306_send_command7(uint8_t command1, uint8_t command2, uint8_t command3, uint8_t command4, uint8_t command5, uint8_t command6, uint8_t command7)
|
||||
{
|
||||
ssd1306_send_command_start();
|
||||
i2c_write(command1);
|
||||
i2c_write(command2);
|
||||
i2c_write(command3);
|
||||
i2c_write(command4);
|
||||
i2c_write(command5);
|
||||
i2c_write(command6);
|
||||
i2c_write(command7);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ssd1306_begin_default(void)
|
||||
{
|
||||
ssd1306_begin(sizeof(ssd1306_init_sequence), ssd1306_init_sequence);
|
||||
}
|
||||
|
||||
void ssd1306_begin(uint8_t init_sequence_length, const uint8_t init_sequence [])
|
||||
{
|
||||
i2c_init();
|
||||
|
||||
ssd1306_send_command_start();
|
||||
for (uint8_t i = 0; i < init_sequence_length; i++)
|
||||
{
|
||||
ssd1306_send_command_byte(pgm_read_byte(&init_sequence[i]));
|
||||
}
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ssd1306_setFont(const DCfont *font)
|
||||
{
|
||||
oledFont = font;
|
||||
}
|
||||
|
||||
void ssd1306_setCursor(uint8_t x, uint8_t y)
|
||||
{
|
||||
ssd1306_send_command3(renderingFrame | (y & 0x07), 0x10 | ((x & 0xf0) >> 4), x & 0x0f);
|
||||
oledX = x;
|
||||
oledY = y;
|
||||
}
|
||||
|
||||
void ssd1306_clear(void)
|
||||
{
|
||||
ssd1306_fill(0x00);
|
||||
}
|
||||
|
||||
void ssd1306_fill(uint8_t fill)
|
||||
{
|
||||
for (uint8_t m = 0; m < SSD1306_PAGES; m++)
|
||||
{
|
||||
ssd1306_setCursor(0, m);
|
||||
ssd1306_fillToEOL(fill);
|
||||
}
|
||||
ssd1306_setCursor(0, 0);
|
||||
}
|
||||
|
||||
void ssd1306_newLine_height(uint8_t fontHeight)
|
||||
{
|
||||
oledY+=fontHeight;
|
||||
if (oledY > SSD1306_PAGES - fontHeight)
|
||||
{
|
||||
oledY = SSD1306_PAGES - fontHeight;
|
||||
}
|
||||
ssd1306_setCursor(0, oledY);
|
||||
}
|
||||
|
||||
void ssd1306_newLine(void)
|
||||
{
|
||||
ssd1306_newLine_height(oledFont->height);
|
||||
}
|
||||
|
||||
uint8_t ssd1306_write(uint8_t c)
|
||||
{
|
||||
if (!oledFont)
|
||||
return 1;
|
||||
|
||||
if (c == '\r')
|
||||
return 1;
|
||||
|
||||
uint8_t h = oledFont->height;
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
ssd1306_newLine_height(h);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t w = oledFont->width;
|
||||
|
||||
if (oledX > ((uint8_t)128 - w))
|
||||
{
|
||||
ssd1306_newLine_height(h);
|
||||
}
|
||||
|
||||
uint16_t offset = ((uint16_t)c - oledFont->first) * w * h;
|
||||
uint8_t line = h;
|
||||
do
|
||||
{
|
||||
ssd1306_send_data_start();
|
||||
for (uint8_t i = 0; i < w; i++)
|
||||
{
|
||||
ssd1306_send_data_byte(pgm_read_byte(&(oledFont->bitmap[offset++])));
|
||||
}
|
||||
i2c_stop();
|
||||
if (h == 1)
|
||||
{
|
||||
oledX+=w;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line > 1)
|
||||
{
|
||||
ssd1306_setCursor(oledX, oledY + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ssd1306_setCursor(oledX + w, oledY - (h - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (--line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ssd1306_bitmap(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t bitmap[])
|
||||
{
|
||||
uint16_t j = 0;
|
||||
for (uint8_t y = y0; y < y1; y++)
|
||||
{
|
||||
ssd1306_setCursor(x0,y);
|
||||
ssd1306_send_data_start();
|
||||
for (uint8_t x = x0; x < x1; x++)
|
||||
{
|
||||
ssd1306_send_data_byte(pgm_read_byte(&bitmap[j++]));
|
||||
}
|
||||
i2c_stop();
|
||||
}
|
||||
ssd1306_setCursor(0, 0);
|
||||
}
|
||||
|
||||
void ssd1306_clearToEOL(void)
|
||||
{
|
||||
ssd1306_fillToEOL(0x00);
|
||||
}
|
||||
|
||||
void ssd1306_fillToEOL(uint8_t fill)
|
||||
{
|
||||
ssd1306_fillLength(fill, 128 - oledX);
|
||||
}
|
||||
|
||||
void ssd1306_fillLength(uint8_t fill, uint8_t length)
|
||||
{
|
||||
oledX += length;
|
||||
ssd1306_send_data_start();
|
||||
do
|
||||
{
|
||||
ssd1306_send_data_byte(fill);
|
||||
}
|
||||
while (--length);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ssd1306_startData(void)
|
||||
{
|
||||
ssd1306_send_data_start();
|
||||
}
|
||||
|
||||
void ssd1306_sendData(const uint8_t data)
|
||||
{
|
||||
ssd1306_send_data_byte(data);
|
||||
}
|
||||
|
||||
void ssd1306_endData(void)
|
||||
{
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
// Double Buffering Commands
|
||||
|
||||
void ssd1306_switchRenderFrame(void)
|
||||
{
|
||||
renderingFrame ^= 0x04;
|
||||
}
|
||||
|
||||
void ssd1306_switchDisplayFrame(void)
|
||||
{
|
||||
drawingFrame ^= 0x20;
|
||||
ssd1306_send_command(drawingFrame);
|
||||
}
|
||||
|
||||
void ssd1306_switchFrame(void)
|
||||
{
|
||||
ssd1306_switchDisplayFrame();
|
||||
ssd1306_switchRenderFrame();
|
||||
}
|
||||
|
||||
uint8_t ssd1306_currentRenderFrame(void)
|
||||
{
|
||||
return (renderingFrame >> 2) & 0x01;
|
||||
}
|
||||
|
||||
uint8_t ssd1306_currentDisplayFrame(void)
|
||||
{
|
||||
return (drawingFrame >> 5) & 0x01;
|
||||
}
|
||||
|
||||
// 1. Fundamental Command Table
|
||||
|
||||
void ssd1306_setContrast(uint8_t contrast)
|
||||
{
|
||||
ssd1306_send_command2(0x81,contrast);
|
||||
}
|
||||
|
||||
void ssd1306_setEntireDisplayOn(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
ssd1306_send_command(0xA5);
|
||||
else
|
||||
ssd1306_send_command(0xA4);
|
||||
}
|
||||
|
||||
void ssd1306_setInverse(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
ssd1306_send_command(0xA7);
|
||||
else
|
||||
ssd1306_send_command(0xA6);
|
||||
}
|
||||
|
||||
void ssd1306_off(void)
|
||||
{
|
||||
ssd1306_send_command(0xAE);
|
||||
}
|
||||
|
||||
void ssd1306_on(void)
|
||||
{
|
||||
ssd1306_send_command(0xAF);
|
||||
}
|
||||
|
||||
// 2. Scrolling Command Table
|
||||
|
||||
void ssd1306_scrollRight(uint8_t startPage, uint8_t interval, uint8_t endPage)
|
||||
{
|
||||
ssd1306_send_command7(0x26, 0x00, startPage, interval, endPage, 0x00, 0xFF);
|
||||
}
|
||||
|
||||
void ssd1306_scrollLeft(uint8_t startPage, uint8_t interval, uint8_t endPage)
|
||||
{
|
||||
ssd1306_send_command7(0x27, 0x00, startPage, interval, endPage, 0x00, 0xFF);
|
||||
}
|
||||
|
||||
void ssd1306_scrollRightOffset(uint8_t startPage, uint8_t interval, uint8_t endPage, uint8_t offset)
|
||||
{
|
||||
ssd1306_send_command6(0x29, 0x00, startPage, interval, endPage, offset);
|
||||
}
|
||||
|
||||
void ssd1306_scrollLeftOffset(uint8_t startPage, uint8_t interval, uint8_t endPage, uint8_t offset)
|
||||
{
|
||||
ssd1306_send_command6(0x2A, 0x00, startPage, interval, endPage, offset);
|
||||
}
|
||||
|
||||
void ssd1306_deactivateScroll(void)
|
||||
{
|
||||
ssd1306_send_command(0x2E);
|
||||
}
|
||||
|
||||
void ssd1306_activateScroll(void)
|
||||
{
|
||||
ssd1306_send_command(0x2F);
|
||||
}
|
||||
|
||||
void ssd1306_setVerticalScrollArea(uint8_t top, uint8_t rows)
|
||||
{
|
||||
ssd1306_send_command3(0xA3, top, rows);
|
||||
}
|
||||
|
||||
// 3. Addressing Setting Command Table
|
||||
|
||||
void ssd1306_setColumnStartAddress(uint8_t startAddress)
|
||||
{
|
||||
ssd1306_send_command2(startAddress & 0x0F, startAddress >> 4);
|
||||
}
|
||||
|
||||
void ssd1306_setMemoryAddressingMode(uint8_t mode)
|
||||
{
|
||||
ssd1306_send_command2(0x20, mode & 0x03);
|
||||
}
|
||||
|
||||
void ssd1306_setColumnAddress(uint8_t startAddress, uint8_t endAddress)
|
||||
{
|
||||
ssd1306_send_command3(0x21, startAddress & 0x7F, endAddress & 0x7F);
|
||||
}
|
||||
|
||||
void ssd1306_setPageAddress(uint8_t startPage, uint8_t endPage)
|
||||
{
|
||||
ssd1306_send_command3(0x22, startPage & 0x07, endPage & 0x07);
|
||||
}
|
||||
|
||||
void ssd1306_setPageStartAddress(uint8_t startPage)
|
||||
{
|
||||
ssd1306_send_command(0xB0 | (startPage & 0x07));
|
||||
}
|
||||
|
||||
// 4. Hardware Configuration (Panel resolution and layout related) Command Table
|
||||
|
||||
void ssd1306_setDisplayStartLine(uint8_t startLine)
|
||||
{
|
||||
ssd1306_send_command(0x40 | (startLine & 0x3F));
|
||||
}
|
||||
|
||||
void ssd1306_setSegmentRemap(uint8_t remap)
|
||||
{
|
||||
ssd1306_send_command(0xA0 | (remap & 0x01));
|
||||
}
|
||||
|
||||
void ssd1306_setMultiplexRatio(uint8_t mux)
|
||||
{
|
||||
ssd1306_send_command2(0xA8, (mux - 1) & 0x3F);
|
||||
}
|
||||
|
||||
void ssd1306_setComOutputDirection(uint8_t direction)
|
||||
{
|
||||
ssd1306_send_command(0xC0 | ((direction & 0x01)<<3));
|
||||
}
|
||||
|
||||
void ssd1306_setDisplayOffset(uint8_t offset)
|
||||
{
|
||||
ssd1306_send_command2(0xD3, offset & 0x3F);
|
||||
}
|
||||
|
||||
void ssd1306_setComPinsHardwareConfiguration(uint8_t alternative, uint8_t enableLeftRightRemap)
|
||||
{
|
||||
ssd1306_send_command2(0xDA, ((enableLeftRightRemap & 0x01) << 5) | ((alternative & 0x01) << 4) | 0x02 );
|
||||
}
|
||||
|
||||
// 5. Timing and Driving Scheme Setting Command table
|
||||
|
||||
void ssd1306_setDisplayClock(uint8_t divideRatio, uint8_t oscillatorFrequency)
|
||||
{
|
||||
ssd1306_send_command2(0xD5, ((oscillatorFrequency & 0x0F) << 4) | ((divideRatio -1) & 0x0F));
|
||||
}
|
||||
|
||||
void ssd1306_setPrechargePeriod(uint8_t phaseOnePeriod, uint8_t phaseTwoPeriod)
|
||||
{
|
||||
ssd1306_send_command2(0xD9, ((phaseTwoPeriod & 0x0F) << 4) | (phaseOnePeriod & 0x0F));
|
||||
}
|
||||
|
||||
void ssd1306_setVcomhDeselectLevel(uint8_t level)
|
||||
{
|
||||
ssd1306_send_command2(0xDB, (level & 0x07) << 4);
|
||||
}
|
||||
|
||||
void ssd1306_nop(void)
|
||||
{
|
||||
ssd1306_send_command(0xE3);
|
||||
}
|
||||
|
||||
// 6. Advance Graphic Command table
|
||||
|
||||
void ssd1306_fadeOut(uint8_t interval)
|
||||
{
|
||||
ssd1306_send_command2(0x23, (0x20 | (interval & 0x0F)));
|
||||
}
|
||||
|
||||
void ssd1306_blink(uint8_t interval)
|
||||
{
|
||||
ssd1306_send_command2(0x23, (0x30 | (interval & 0x0F)));
|
||||
}
|
||||
|
||||
void ssd1306_disableFadeOutAndBlinking(void)
|
||||
{
|
||||
ssd1306_send_command2(0x23, 0x00);
|
||||
}
|
||||
|
||||
void ssd1306_enableZoomIn(void)
|
||||
{
|
||||
ssd1306_send_command2(0xD6, 0x01);
|
||||
}
|
||||
|
||||
void ssd1306_disableZoomIn(void)
|
||||
{
|
||||
ssd1306_send_command2(0xD6, 0x00);
|
||||
}
|
||||
|
||||
// Charge Pump Settings
|
||||
|
||||
void ssd1306_enableChargePump(void)
|
||||
{
|
||||
ssd1306_send_command2(0x8D, 0x14);
|
||||
}
|
||||
|
||||
void ssd1306_disableChargePump(void)
|
||||
{
|
||||
ssd1306_send_command2(0x8D, 0x10);
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*
|
||||
* Port of Stephen Denne's Tiny4kOLED to C,
|
||||
* without the Arduino framework dependency but with
|
||||
* a hardcoded dependency on VeryTinyI2C.
|
||||
*
|
||||
* Original:
|
||||
* https://github.com/technoblogy/tiny-i2c
|
||||
*/
|
||||
#ifndef __VeryTinySSD1306
|
||||
#define __VeryTinySSD1306
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t *bitmap; // character bitmaps data
|
||||
uint8_t width; // character width in pixels
|
||||
uint8_t height; // character height in pages (8 pixels)
|
||||
uint8_t first, last; // ASCII extents
|
||||
} DCfont;
|
||||
|
||||
|
||||
#define SSD1306_DisplayWidth 128
|
||||
#define SSD1306_DisplayHeight 32
|
||||
|
||||
#ifndef SSD1306
|
||||
#define SSD1306 0x3C // Slave address
|
||||
#endif
|
||||
|
||||
|
||||
void ssd1306_begin_default(void);
|
||||
void ssd1306_begin(uint8_t init_sequence_length, const uint8_t init_sequence []);
|
||||
void ssd1306_switchRenderFrame(void);
|
||||
void ssd1306_switchDisplayFrame(void);
|
||||
void ssd1306_switchFrame(void);
|
||||
uint8_t ssd1306_currentRenderFrame(void);
|
||||
uint8_t ssd1306_currentDisplayFrame(void);
|
||||
void ssd1306_setFont(const DCfont *font);
|
||||
void ssd1306_setCursor(uint8_t x, uint8_t y);
|
||||
void ssd1306_newLine();
|
||||
void ssd1306_fill(uint8_t fill);
|
||||
void ssd1306_fillToEOL(uint8_t fill);
|
||||
void ssd1306_fillLength(uint8_t fill, uint8_t length);
|
||||
void ssd1306_clear(void);
|
||||
void ssd1306_clearToEOL(void);
|
||||
void ssd1306_bitmap(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t bitmap[]);
|
||||
void ssd1306_startData(void);
|
||||
void ssd1306_sendData(const uint8_t data);
|
||||
void ssd1306_endData(void);
|
||||
|
||||
// 1. Fundamental Command Table
|
||||
|
||||
void ssd1306_setContrast(uint8_t contrast);
|
||||
void ssd1306_setEntireDisplayOn(bool enable);
|
||||
void ssd1306_setInverse(bool enable);
|
||||
void ssd1306_off(void);
|
||||
void ssd1306_on(void);
|
||||
|
||||
// 2. Scrolling Command Table
|
||||
|
||||
void ssd1306_scrollRight(uint8_t startPage, uint8_t interval, uint8_t endPage);
|
||||
void ssd1306_scrollLeft(uint8_t startPage, uint8_t interval, uint8_t endPage);
|
||||
void ssd1306_scrollRightOffset(uint8_t startPage, uint8_t interval, uint8_t endPage, uint8_t offset);
|
||||
void ssd1306_scrollLeftOffset(uint8_t startPage, uint8_t interval, uint8_t endPage, uint8_t offset);
|
||||
void ssd1306_deactivateScroll(void);
|
||||
void ssd1306_activateScroll(void);
|
||||
void ssd1306_setVerticalScrollArea(uint8_t top, uint8_t rows);
|
||||
|
||||
// 3. Addressing Setting Command Table
|
||||
void ssd1306_setColumnStartAddress(uint8_t startAddress);
|
||||
void ssd1306_setMemoryAddressingMode(uint8_t mode);
|
||||
void ssd1306_setColumnAddress(uint8_t startAddress, uint8_t endAddress);
|
||||
void ssd1306_setPageAddress(uint8_t startPage, uint8_t endPage);
|
||||
void ssd1306_setPageStartAddress(uint8_t startPage);
|
||||
|
||||
// 4. Hardware Configuration (Panel resolution and layout related) Command Table
|
||||
|
||||
void ssd1306_setDisplayStartLine(uint8_t startLine);
|
||||
void ssd1306_setSegmentRemap(uint8_t remap);
|
||||
void ssd1306_setMultiplexRatio(uint8_t mux);
|
||||
void ssd1306_setComOutputDirection(uint8_t direction);
|
||||
void ssd1306_setDisplayOffset(uint8_t offset);
|
||||
void ssd1306_setComPinsHardwareConfiguration(uint8_t alternative, uint8_t enableLeftRightRemap);
|
||||
|
||||
// 5. Timing and Driving Scheme Setting Command table
|
||||
|
||||
void ssd1306_setDisplayClock(uint8_t divideRatio, uint8_t oscillatorFrequency);
|
||||
void ssd1306_setPrechargePeriod(uint8_t phaseOnePeriod, uint8_t phaseTwoPeriod);
|
||||
void ssd1306_setVcomhDeselectLevel(uint8_t level);
|
||||
void ssd1306_nop(void);
|
||||
|
||||
// 6. Advance Graphic Command table
|
||||
|
||||
void ssd1306_fadeOut(uint8_t interval);
|
||||
void ssd1306_blink(uint8_t interval);
|
||||
void ssd1306_disableFadeOutAndBlinking(void);
|
||||
void ssd1306_enableZoomIn(void);
|
||||
void ssd1306_disableZoomIn(void);
|
||||
|
||||
// Charge Pump Settings
|
||||
|
||||
void ssd1306_enableChargePump(void);
|
||||
void ssd1306_disableChargePump(void);
|
||||
|
||||
uint8_t ssd1306_write(uint8_t c);
|
||||
|
||||
#endif
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#include "lib/TinyMillis.h"
|
||||
#include "lib/TinyUART.h"
|
||||
#include "lib/TinyRS485.h"
|
||||
#include "global.h"
|
||||
#include "display.h"
|
||||
|
||||
#define STEPS_DELAY_SHORT 200
|
||||
#define STEPS_DELAY 800
|
||||
#define STEPS_DELAY_LONG 2000
|
||||
|
||||
|
||||
uint8_t uartRead(void)
|
||||
{
|
||||
return uart_getc();
|
||||
}
|
||||
|
||||
uint8_t uartAvailable(void)
|
||||
{
|
||||
return uart_available();
|
||||
}
|
||||
|
||||
void uartWrite(uint8_t what)
|
||||
{
|
||||
uart_putc(what);
|
||||
}
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WaitingForComm,
|
||||
Connected
|
||||
} State;
|
||||
|
||||
|
||||
State currentState = WaitingForComm;
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Allow peripherals to start up before sending messages
|
||||
_delay_ms(40);
|
||||
|
||||
global_init();
|
||||
millis_init();
|
||||
sei();
|
||||
|
||||
uart_init();
|
||||
rs485_begin(uartRead, uartAvailable, uartWrite, 20);
|
||||
|
||||
display_init();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (rs485_update())
|
||||
{
|
||||
// TODO get data
|
||||
currentState = Connected;
|
||||
}
|
||||
|
||||
switch (currentState)
|
||||
{
|
||||
case WaitingForComm:
|
||||
display_drawWait();
|
||||
break;
|
||||
|
||||
case Connected:
|
||||
display_drawIndex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
56
module/src/main.cpp
Normal file
56
module/src/main.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include "global.h"
|
||||
#include "display.h"
|
||||
|
||||
|
||||
enum struct State: uint8_t
|
||||
{
|
||||
WaitingForComm,
|
||||
Connected
|
||||
};
|
||||
|
||||
|
||||
State currentState = State::WaitingForComm;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
settings.init();
|
||||
|
||||
// Set up I2C devices: the SSD1306 OLED display and PCA9685 LED PWM driver
|
||||
Wire.begin();
|
||||
display.init();
|
||||
ledDriver.setAddress(0x40);
|
||||
|
||||
// At 16 Mhz we pick a baud rate with a very acceptable 0.2% error rate
|
||||
// Source: http://www.robotroom.com/Asynchronous-Serial-Communication-2.html
|
||||
Serial.begin(76800);
|
||||
comm.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (comm.update())
|
||||
{
|
||||
// TODO get data
|
||||
currentState = State::Connected;
|
||||
}
|
||||
|
||||
switch (currentState)
|
||||
{
|
||||
case State::WaitingForComm:
|
||||
display.show(Screen::WaitingForComm);
|
||||
break;
|
||||
|
||||
case State::Connected:
|
||||
display.show(Screen::ModuleIndex);
|
||||
break;
|
||||
}
|
||||
}
|
56
module/src/settings.cpp
Normal file
56
module/src/settings.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*/
|
||||
#include "settings.h"
|
||||
#include <EEPROM.h>
|
||||
|
||||
|
||||
const uint8_t ModuleIndexUndefined = 0xff;
|
||||
|
||||
|
||||
|
||||
// First byte is 0xAA to recognize uninitialised EEPROM
|
||||
const uint8_t EEPROMHeader = 0xAA;
|
||||
const uint8_t EEPROMCurrentVersion = 1;
|
||||
|
||||
const uint8_t EEPROMAddressHeader = 0;
|
||||
const uint8_t EEPROMAddressVersion = 1;
|
||||
const uint8_t EEPROMAddressModuleIndex = 2;
|
||||
|
||||
|
||||
void Settings::init()
|
||||
{
|
||||
if (EEPROM.read(EEPROMAddressHeader) != EEPROMHeader)
|
||||
{
|
||||
EEPROM.put(EEPROMAddressHeader, EEPROMHeader);
|
||||
EEPROM.put(EEPROMAddressVersion, EEPROMCurrentVersion);
|
||||
EEPROM.put(EEPROMAddressModuleIndex, mModuleIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t version = EEPROM.read(EEPROMAddressVersion);
|
||||
if (version >= 1)
|
||||
{
|
||||
mModuleIndex = EEPROM.read(EEPROMAddressModuleIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t Settings::getModuleIndex(void)
|
||||
{
|
||||
return mModuleIndex;
|
||||
}
|
||||
|
||||
|
||||
void Settings::setModuleIndex(uint8_t index)
|
||||
{
|
||||
if (index == mModuleIndex)
|
||||
return;
|
||||
|
||||
mModuleIndex = index;
|
||||
EEPROM.put(EEPROMAddressModuleIndex, mModuleIndex);
|
||||
}
|
27
module/src/settings.h
Normal file
27
module/src/settings.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Stairs lighting
|
||||
* Copyright 2017 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/Stairs
|
||||
*/
|
||||
#ifndef __settings
|
||||
#define __settings
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern const uint8_t ModuleIndexUndefined;
|
||||
|
||||
|
||||
class Settings
|
||||
{
|
||||
private:
|
||||
uint8_t mModuleIndex = ModuleIndexUndefined;
|
||||
|
||||
public:
|
||||
void init();
|
||||
|
||||
uint8_t getModuleIndex();
|
||||
void setModuleIndex(uint8_t index);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user