Copy project
This commit is contained in:
parent
ce7a467af7
commit
26a0fd884f
8 changed files with 157458 additions and 1 deletions
129
ad_astra/AdAstra/src/driver/display/display.c
Normal file
129
ad_astra/AdAstra/src/driver/display/display.c
Normal 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);
|
||||
}
|
||||
}
|
||||
34
ad_astra/AdAstra/src/driver/display/display.h
Normal file
34
ad_astra/AdAstra/src/driver/display/display.h
Normal 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
|
||||
144
ad_astra/AdAstra/src/driver/infra/infra.c
Normal file
144
ad_astra/AdAstra/src/driver/infra/infra.c
Normal 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;
|
||||
}
|
||||
16
ad_astra/AdAstra/src/driver/infra/infra.h
Normal file
16
ad_astra/AdAstra/src/driver/infra/infra.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
34
ad_astra/AdAstra/src/driver/sleep/sleep.c
Normal file
34
ad_astra/AdAstra/src/driver/sleep/sleep.c
Normal 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);
|
||||
}
|
||||
13
ad_astra/AdAstra/src/driver/sleep/sleep.h
Normal file
13
ad_astra/AdAstra/src/driver/sleep/sleep.h
Normal 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
|
||||
Loading…
Reference in a new issue