Copy project

This commit is contained in:
schmelczerandras 2020-04-19 12:48:06 +02:00
commit 26a0fd884f
57 changed files with 157458 additions and 1 deletions

View file

@ -0,0 +1,129 @@
#include "display.h"
#include <avr/pgmspace.h>
#include "bitwise.h"
#include "math.h"
#include "../../hardware_access/hardware_access.h"
static uint8_t const configuration[] PROGMEM = {
0xD5, 0xF0, // set clock frequency
0x8D, 0x14, // enable charge pump
0x20, 0x00, // horizontal addressing mode
0xD6, 0x01, // 2 times vertical zoom
0x22, 0x00, 0x03, // only draw to the top half of the screen
0xAF // display on
};
static struct {
uint8_t compositingBuffer[DISPLAY_WIDTH_IN_PIXELS];
Rectangle compositingWindow;
DrawFunction drawEverything;
bool wasIntersection;
} display;
static inline void commandMode() {
setOutputPin(DISPLAY_DC_OUTPUT_PIN, false);
}
static inline void dataMode() {
setOutputPin(DISPLAY_DC_OUTPUT_PIN, true);
}
void setDisplayContrast(uint8_t value) {
commandMode();
sendByteOnSPI(0x81);
sendByteOnSPI(value);
dataMode();
}
void initializeDisplay(DrawFunction drawEverything) {
setOutputPin(DISPLAY_RESET_OUTPUT_PIN, false);
for (volatile uint8_t i = 0; i != 255; i++)
;
// some time has to elapse before the next line gets called,
// otherwise the display wont turn on
setOutputPin(DISPLAY_RESET_OUTPUT_PIN, true);
for (uint8_t i = 0; i < sizeof(configuration); i++) {
sendByteOnSPI(pgm_read_byte(configuration + i));
}
display.drawEverything = drawEverything;
setDisplayContrast(255);
dataMode();
}
void startIntersectionTest(Rectangle compositingWindow) {
display.wasIntersection = false;
display.compositingWindow = compositingWindow;
}
bool endIntersectionTest() {
for (uint8_t x = 0; x < DISPLAY_WIDTH_IN_PIXELS; x++) {
display.compositingBuffer[x] = 0;
}
return display.wasIntersection;
}
void drawFrame() {
display.compositingWindow = DEFAULT_COMPOSITING_WINDOW;
for (display.compositingWindow.position.y = 0; display.compositingWindow.position.y < DISPLAY_HEIGHT_IN_PIXELS; display.compositingWindow.position.y += 8) {
display.drawEverything(display.compositingWindow);
for (uint8_t x = 0; x < DISPLAY_WIDTH_IN_PIXELS; x++) {
sendByteOnSPI(display.compositingBuffer[x]);
sendByteOnSPI(display.compositingBuffer[x]);
display.compositingBuffer[x] = 0;
}
}
}
static void compositePixelColumn(uint8_t x, uint8_t invertedMask, uint8_t fill) {
if (display.compositingBuffer[x] & fill) {
display.wasIntersection = true;
}
display.compositingBuffer[x] = (display.compositingBuffer[x] & (~invertedMask)) | fill;
}
void drawBitmapFromProgMem(Rectangle boundingBox, uint16_t const data[boundingBox.size.x][(boundingBox.size.y + 7) / 8], bool isMirrored) {
boundingBox.position = substract(boundingBox.position, display.compositingWindow.position);
uint8_t spriteY = max(0, -boundingBox.position.y);
uint8_t spriteYByte = spriteY >> 3;
for (uint8_t x = max(0, -boundingBox.position.x); x < boundingBox.size.x && boundingBox.position.x + x < DISPLAY_WIDTH_IN_PIXELS; x++) {
uint8_t spriteX = isMirrored ? boundingBox.size.x - x - 1 : x;
uint16_t currentPixelColumn = loadWordEEPROM(&data[spriteX][spriteYByte]);
uint8_t fill, invertedMask;
if (boundingBox.position.y >= 0) {
fill = (currentPixelColumn & 0x00FF) << boundingBox.position.y;
invertedMask = currentPixelColumn >> 8 << boundingBox.position.y;
} else {
uint16_t lowerPixelColumn = spriteYByte + 1 < (boundingBox.size.y + 7) / 8 ? loadWordEEPROM(&data[spriteX][spriteYByte + 1]) : 0;
uint8_t shift = spriteY % 8;
uint8_t inverseShift = 8 - shift;
fill = ((currentPixelColumn & 0x00FF) >> shift) | ((lowerPixelColumn & 0x0FF) << inverseShift);
invertedMask = (currentPixelColumn >> 8 >> shift) | (lowerPixelColumn >> 8 << inverseShift);
}
compositePixelColumn(boundingBox.position.x + x, invertedMask, fill);
}
}
void drawFilledRectangle(Rectangle box, uint8_t invertedMask, uint8_t fill) {
box.position = substract(box.position, display.compositingWindow.position);
uint8_t upperGapHeight = min(8, max(0, box.position.y));
uint8_t lowerGapHeight = min(8, max(0, 8 - (box.position.y + box.size.y)));
uint8_t actualFill = (fill >> lowerGapHeight) & (fill << upperGapHeight);
uint8_t actualInvertedMask = (invertedMask >> lowerGapHeight) & (invertedMask << upperGapHeight);
for (uint8_t x = max(0, box.position.x); x < box.position.x + box.size.x && x < DISPLAY_WIDTH_IN_PIXELS; x++) {
compositePixelColumn(x, actualInvertedMask, actualFill);
}
}

View file

@ -0,0 +1,34 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <stdbool.h>
#include <avr/io.h>
#include "../../util/rectangle/rectangle.h"
#define DISPLAY_RESET_OUTPUT_PIN PB0
#define DISPLAY_DC_OUTPUT_PIN PB4
#define DISPLAY_WIDTH_IN_PIXELS 64
#define DISPLAY_HEIGHT_IN_PIXELS 32
#define WINDOW ((Rectangle){(Vec2){0, 0}, (Vec2){DISPLAY_WIDTH_IN_PIXELS, DISPLAY_HEIGHT_IN_PIXELS}})
#define DEFAULT_COMPOSITING_WINDOW ((Rectangle){(Vec2){0, 0}, (Vec2){DISPLAY_WIDTH_IN_PIXELS, 8}})
typedef void (*DrawFunction)(Rectangle);
void initializeDisplay(DrawFunction drawEverything);
void setDisplayContrast(uint8_t value);
void startIntersectionTest();
bool endIntersectionTest();
void drawFrame();
void drawBitmapFromProgMem(Rectangle boundingBox, uint16_t const data[boundingBox.size.x][(boundingBox.size.y + 7) / 8], bool isMirrored);
// for white rectangle: invertedMask: don't care, fill: 0xFF
// for black rectangle: invertedMask: 0xFF, fill: 0x00
void drawFilledRectangle(Rectangle box, uint8_t invertedMask, uint8_t fill);
#endif

View file

@ -0,0 +1,144 @@
#include "infra.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include "bitwise.h"
#include "../../hardware_access/hardware_access.h"
// (0.5625 + (0.5625 + 1.6875) / 2) / 1000 / timer interval
#define MEAN_OF_0_1_BIT_TIMES 53
// 9 / 2 / 1000 / timer interval
#define MAYBE_ONE_CHECK_TIME 141
// some large value
#define TIMEOUT 254
typedef enum {
idle, maybeLeadingOne, leadingOneConfirmed, activeFirstBit, active, timingOut
} ProtocolState;
typedef enum {
noMatch, foundStartingByte, significantByte, waitingForEndOfCommand, waitingForRepeat
} CommandState;
static struct {
uint8_t current;
uint8_t bitPosition;
ProtocolState protocolState;
CommandState commandState;
OnCommandReceived onCommandReceived;
} infra;
static void saveCurrentByte() {
uint8_t byte = infra.current;
infra.current = 0;
infra.bitPosition = 0;
if (byte == INFRA_ADDRESS) {
infra.commandState = foundStartingByte;
return;
}
switch (infra.commandState) {
case foundStartingByte:
infra.commandState = significantByte;
break;
case significantByte:
infra.onCommandReceived(byte);
infra.commandState = waitingForEndOfCommand;
break;
case waitingForEndOfCommand:
if (byte == 0) {
infra.commandState = waitingForRepeat;
}
break;
case waitingForRepeat:
if (byte == 0) {
infra.onCommandReceived(REPEAT_CODE);
}
break;
default:
break;
}
}
static void saveBit(uint8_t bit) {
infra.current <<= 1;
infra.current |= bit;
if (++infra.bitPosition == 8) {
saveCurrentByte();
}
}
static inline uint8_t isIrOff() {
return getBit(PINB, IR_PIN);
}
static inline uint8_t isIrOn() {
return !isIrOff();
}
ISR(PCINT0_vect) {
switch (infra.protocolState) {
case idle:
if (isIrOn()) {
enableTimerB(MAYBE_ONE_CHECK_TIME);
infra.protocolState = maybeLeadingOne;
}
break;
case maybeLeadingOne:
infra.protocolState = idle;
break;
case leadingOneConfirmed:
if (isIrOff()) {
infra.protocolState = activeFirstBit;
}
break;
case activeFirstBit:
case timingOut:
if (isIrOn()) {
infra.protocolState = active;
enableTimerB(MEAN_OF_0_1_BIT_TIMES);
}
break;
case active:
if (isIrOn()) {
saveBit(1);
enableTimerB(MEAN_OF_0_1_BIT_TIMES);
}
break;
}
}
ISR(TIM0_COMPB_vect) {
switch (infra.protocolState) {
case maybeLeadingOne:
if (isIrOn()) {
infra.protocolState = leadingOneConfirmed;
}
disableTimerB();
break;
case active:
saveBit(0);
infra.protocolState = timingOut;
enableTimerB(TIMEOUT);
break;
case timingOut:
infra.protocolState = idle;
saveCurrentByte();
disableTimerB();
break;
default:
disableTimerB();
break;
}
}
void initializeInfra(OnCommandReceived onCommandReceived) {
setBit(PORTB, IR_PIN); // enable pull-up
setBit(PCMSK, IR_PIN); // specific pin change interrupt enable
setBit(GIMSK, PCIE); // global on pin change interrupt enable;
infra.onCommandReceived = onCommandReceived;
}

View file

@ -0,0 +1,16 @@
#ifndef INFRA_H
#define INFRA_H
#include <stdbool.h>
#include <avr/io.h>
#define INFRA_ADDRESS 255
#define IR_PIN PB3
#define REPEAT_CODE 1
typedef void (*OnCommandReceived)(uint8_t);
void initializeInfra(OnCommandReceived onCommandReceived);
#endif

View file

@ -0,0 +1,120 @@
#include "redundant_storage.h"
#include "../../hardware_access/hardware_access.h"
#define offsetof(type, member) __builtin_offsetof (type, member)
#define OBJECT_VALIDITY_MASK (0b00111111)
#define IS_USING_PAGE_A_BIT 6
#define IS_VALID_BIT 7
#define METADATA_REDUNDANCY 4
typedef struct {
uint8_t redundantBufferA[REDUNDANT_BUFFER_SIZE];
uint8_t redundantBufferB[REDUNDANT_BUFFER_SIZE];
uint8_t metadata[METADATA_REDUNDANCY];
} MemoryLayout;
static volatile struct {
MemoryLayout buffer;
uint8_t saveIndex;
uint8_t* savePointer;
uint8_t loadIndex;
uint8_t* loadPointer;
uint8_t validMetadata;
bool isEEPROMWriting;
} storage;
static inline bool isUsingPageA() {
return getBit(storage.validMetadata, IS_USING_PAGE_A_BIT);
}
bool isValid() {
return getBit(storage.validMetadata, IS_VALID_BIT);
}
static inline void fixMetadata() {
uint8_t a = storage.buffer.metadata[0];
uint8_t b = storage.buffer.metadata[1];
uint8_t c = storage.buffer.metadata[2];
uint8_t d = storage.buffer.metadata[3];
if (a == b || a == c || a == d) {
storage.validMetadata = a;
} else if (b == c || b == d) {
storage.validMetadata = b;
} else {
storage.validMetadata = c;
}
}
void initializeRedundantStorage() {
for (uint8_t i = 0; i < sizeof(MemoryLayout); i++) {
((uint8_t*)&storage.buffer)[i] = loadSavedByteEEPROM(i);
}
fixMetadata();
}
void invalidateEEPROM() {
for (uint8_t i = 0; i < sizeof(MemoryLayout); i++) {
((uint8_t*)&storage.buffer)[i] = 0;
}
storage.validMetadata = 0;
}
void onEEPROMWriteFinished(__attribute__((unused)) uint8_t* _) {
storage.isEEPROMWriting = false;
}
bool startSchedulingObjectsForSaving() {
if (storage.isEEPROMWriting) {
return false;
}
toggleBit(storage.validMetadata, IS_USING_PAGE_A_BIT);
storage.validMetadata &= ~OBJECT_VALIDITY_MASK;
storage.saveIndex = 0;
storage.savePointer = isUsingPageA() ?
storage.buffer.redundantBufferA
: storage.buffer.redundantBufferB;
return true;
}
void scheduleNextObjectForSave(uint8_t* object, uint8_t size) {
setBit(storage.validMetadata, storage.saveIndex);
storage.saveIndex++;
for (uint8_t i = 0; i < size; i++) {
*storage.savePointer = object[i];
storage.savePointer++;
}
}
void saveScheduledObjects() {
setBit(storage.validMetadata, IS_VALID_BIT);
for (uint8_t i = 0; i < METADATA_REDUNDANCY; i++) {
storage.buffer.metadata[i] = storage.validMetadata;
}
storage.isEEPROMWriting = true;
asyncWriteEEPROM((uint8_t*)&storage.buffer, onEEPROMWriteFinished);
}
bool loadNextObject(uint8_t* holder, uint8_t size) {
if (storage.loadIndex++ == 0) {
storage.loadPointer = isUsingPageA() ?
storage.buffer.redundantBufferA
: storage.buffer.redundantBufferB;
}
if (getBit(storage.validMetadata, storage.loadIndex - 1)) {
for (uint8_t i = 0; i < size; i++) {
holder[i] = *storage.loadPointer++;
}
return true;
}
return false;
}

View file

@ -0,0 +1,22 @@
#ifndef REDUNDANT_STORAGE_H
#define REDUNDANT_STORAGE_H
#include <stdbool.h>
#include <avr/io.h>
#define REDUNDANT_BUFFER_SIZE 24
void initializeRedundantStorage();
bool isValid();
void invalidateEEPROM();
bool startSchedulingObjectsForSaving();
void scheduleNextObjectForSave(uint8_t* object, uint8_t size);
void saveScheduledObjects();
// Returns is loaded object valid
bool loadNextObject(uint8_t* holder, uint8_t size);
#endif

View file

@ -0,0 +1,34 @@
#include "sleep.h"
#include <stdbool.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "bitwise.h"
#include "../../hardware_access/hardware_access.h"
#define TICKS_IN_MILISECOND 31
volatile int8_t milisecondsSinceFrameStart;
void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds) {
sleep_enable();
uint8_t previousFrameTime = 0;
while (function(previousFrameTime)) {
previousFrameTime = milisecondsSinceFrameStart;
while (milisecondsSinceFrameStart < frameLengthInMilliseconds) {
sleep_cpu();
}
milisecondsSinceFrameStart = 0;
}
}
ISR(TIM0_COMPA_vect) {
milisecondsSinceFrameStart++;
enableTimerA(TICKS_IN_MILISECOND);
}

View file

@ -0,0 +1,13 @@
#ifndef SLEEP_H
#define SLEEP_H
#include <stdbool.h>
#include <avr/io.h>
typedef bool (*FrameFunction)(uint8_t);
// frameFunction gets previousFrameTime (in milliseconds) as argument
void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds);
#endif

View file

@ -0,0 +1,75 @@
#include "eeprom.h"
#include <stdbool.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include "bitwise.h"
uint8_t memory[STORAGE_SIZE] EEMEM;
static volatile struct {
uint8_t* buffer;
uint8_t positon;
bool isWriting;
OnEEPROMFinished onFinished;
} eeprom;
uint8_t loadByteEEPROM(uint8_t* address) {
while(getBit(EECR, EEPE)) {}
EEAR = (uint16_t)address;
setBit(EECR, EERE);
return EEDR;
}
uint8_t loadSavedByteEEPROM(uint8_t address) {
return loadByteEEPROM(memory + address);
}
uint16_t loadWordEEPROM(uint16_t const* address) {
uint16_t high = ((uint16_t)loadByteEEPROM((uint8_t*)address + 1)) << 8;
uint16_t low = (uint16_t)loadByteEEPROM((uint8_t*)address);
return high | low;
}
static inline void saveByteEEPROM(uint8_t* address, uint8_t byte) {
if (byte != loadByteEEPROM(address)) {
EEDR = byte;
setBit(EECR, EEMPE);
setBit(EECR, EEPE);
}
}
void enableWritingEEPROM() {
modifyBit(EECR, EERIE, eeprom.isWriting);
}
void disableWritingEEPROM() {
clearBit(EECR, EERIE); // clear eeprom ready interrupt enable
}
bool asyncWriteEEPROM(uint8_t* buffer, OnEEPROMFinished onFinished) {
if (eeprom.isWriting) {
return false;
}
eeprom.buffer = buffer;
eeprom.onFinished = onFinished;
eeprom.positon = 0;
eeprom.isWriting = true;
setBit(EECR, EERIE); // set eeprom ready interrupt
return true;
}
ISR(EE_RDY_vect) {
saveByteEEPROM(memory + eeprom.positon, eeprom.buffer[eeprom.positon]);
if (++eeprom.positon == STORAGE_SIZE) {
clearBit(EECR, EERIE);
eeprom.isWriting = false;
eeprom.onFinished(eeprom.buffer);
}
}

View file

@ -0,0 +1,15 @@
#ifndef EEPROM_H
#define EEPROM_H
#include <avr/io.h>
#define STORAGE_SIZE 52
typedef void (*OnEEPROMFinished)(uint8_t*);
inline void initializeEEPROM() {
EECR = 0; // atomic write
}
#endif

View file

@ -0,0 +1,13 @@
#include "hardware_access.h"
#include <avr/interrupt.h>
void initializeHardwareAccess() {
sei();
initializePowerSaving();
initializeSPI();
initializeEEPROM();
initializeTiming();
initializeOutputPins();
}

View file

@ -0,0 +1,33 @@
#ifndef HARDWARE_ACCESS_H
#define HARDWARE_ACCESS_H
#include <stdbool.h>
#include <avr/io.h>
#include "bitwise.h"
#include "power_saving/power_saving.h"
#include "spi/spi.h"
#include "eeprom/eeprom.h"
#include "timing/timing.h"
#include "output_pins/output_pins.h"
void initializeHardwareAccess();
// writing of unchanged byte is atomic
bool asyncWriteEEPROM(uint8_t* buffer, OnEEPROMFinished onFinished);
uint8_t loadSavedByteEEPROM(uint8_t address);
uint8_t loadByteEEPROM(uint8_t* address);
uint16_t loadWordEEPROM(uint16_t const* address);
void enableWritingEEPROM();
void disableWritingEEPROM();
void enableTimerA(uint8_t triggerInterruptInXTicks);
void enableTimerB(uint8_t triggerInterruptInXTicks);
void disableTimerB();
void sendByteOnSPI(uint8_t byte);
void setOutputPin(uint8_t id, bool value);
#endif

View file

@ -0,0 +1,11 @@
#include "output_pins.h"
#include "bitwise.h"
#include <avr/io.h>
#include <stdbool.h>
void setOutputPin(uint8_t id, bool value) {
setBit(DDRB, id);
modifyBit(PORTB, id, value);
}

View file

@ -0,0 +1,7 @@
#ifndef GPIO_OUTPUT_PINS_H
#define GPIO_OUTPUT_PINS_H
inline void initializeOutputPins() {}
#endif

View file

@ -0,0 +1,13 @@
#ifndef POWER_SAVING_H
#define POWER_SAVING_H
#include <avr/io.h>
#include "bitwise.h"
inline void initializePowerSaving() {
setBit(ACSR, ACD); // disable ADC to save power
PRR = BV(PRTIM1) | BV(PRADC); // disable power of timer1 and ADC
}
#endif

View file

@ -0,0 +1,30 @@
#include "spi.h"
#include <avr/interrupt.h>
#define SWAP_BIT (BV(USIWM0) | BV(USICLK) | BV(USITC) | BV(USICS1))
void sendByteOnSPI(uint8_t byte) {
USIDR = byte;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
}

View file

@ -0,0 +1,17 @@
#ifndef SPI_H
#define SPI_H
#include <avr/io.h>
#include "bitwise.h"
#define DO_PIN PB1
#define USCK_PIN PB2
inline void initializeSPI() {
DDRB |= BV(DO_PIN) | BV(USCK_PIN); // set pin directions for MOSI and SCK
}
void sendByteOnSPI(uint8_t byte);
#endif

View file

@ -0,0 +1,17 @@
#include "timing.h"
#include "bitwise.h"
void enableTimerA(uint8_t triggerInterruptInXTicks) {
OCR0A = TCNT0 + triggerInterruptInXTicks;
}
void enableTimerB(uint8_t triggerInterruptInXTicks) {
setBit(TIFR, OCF0B);
OCR0B = TCNT0 + triggerInterruptInXTicks;
setBit(TIMSK, OCIE0B);
}
void disableTimerB() {
clearBit(TIMSK, OCIE0B);
}

View file

@ -0,0 +1,13 @@
#ifndef TIMING_H
#define TIMING_H
#include <avr/io.h>
#include "bitwise.h"
inline void initializeTiming() {
TCCR0B = BV(CS02); // CLK / 256
setBit(TIMSK, OCIE0A);
}
#endif

View file

@ -0,0 +1,11 @@
#ifndef BITWISE_H
#define BITWISE_H
#define BV(x) (1 << (x))
#define modifyBit(P, B, V) ((P) = ((P) & ~BV(B)) | ((V) << B))
#define setBit(P, B) ((P) |= BV(B))
#define clearBit(P, B) ((P) &= ~BV(B))
#define toggleBit(P, B) ((P) ^= BV(B))
#define getBit(P, B) (((P) & BV(B)) >> (B))
#endif

Some files were not shown because too many files have changed in this diff Show more