Created
February 2, 2017 16:27
-
-
Save buserror/9407adb6d52153e16caad5e8a0844023 to your computer and use it in GitHub Desktop.
protothreads, aka duff's device in use...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* Ascii art http://www.network-science.de/ascii/ (font "mini") | |
* | |
* -Wall -g -Os -std=gnu99 -mcall-prologues -DF_CPU=8000000 | |
* 8366 118 1373 9857 2681 atmega644_sure_led_clock.axf | |
* | |
* -mcall-prologues -fno-inline-small-functions \ | |
* -ffunction-sections -fdata-sections \ | |
* -Wl,--relax,--gc-sections \ | |
* 7322 118 1372 8812 226c atmega644_sure_led_clock.axf | |
* | |
*/ | |
#undef F_CPU | |
#define F_CPU 20000000 | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <avr/pgmspace.h> | |
#include <avr/sleep.h> | |
#include <util/delay.h> | |
#include <pt/pt.h> | |
#include "fifo_declare.h" | |
#ifndef ISR // for eclipse parser | |
#define ISR(vv) void vv() | |
#endif | |
// for linker, emulator, and programmer's sake | |
#include "avr_mcu_section.h" | |
AVR_MCU(F_CPU, "atmega644"); | |
#define TRACE 1 | |
#if TRACE | |
#include <stdio.h> | |
/* ------------------------------------------------------------------------- */ | |
static int uart_putchar(char c, FILE *stream) { | |
if (c == '\n') | |
uart_putchar('\r', stream); | |
loop_until_bit_is_set(UCSR0A, UDRE0); | |
UDR0 = c; | |
return 0; | |
} | |
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, | |
_FDEV_SETUP_WRITE); | |
#define V(w) w | |
#else | |
#define V(w) | |
#endif | |
enum ESPI_Commands { | |
CMD_DCF77_Init = 0, | |
CMD_SETPWM = 0x01, // 01xy, X = bitfield for lines, y brightness | |
CMD_DisplayString = 0x02, // 02text ... | |
CMD_SetClock = 0x3, | |
CMD_SetScrollDelay = 0x4, // 040xyy[yy] set scroll delay in ticks | |
CMD_SetScrollCount = 0x5, // 050xyy yy = 0: disabled 0xff:forever | |
CMD_DCF77_Disable = 0x6, | |
CMD_DCF77_Enable = 0x7, | |
CMD_DEBUG_DCF_Start = 0x8, | |
CMD_DEBUG_DCF_End = 0x9, | |
CMD_GetClock = 0x70, | |
}; | |
/* | |
___ | |
| o._ _ _ ._ _ | |
| || | |(/_| _> | |
*/ | |
#define TICK_HZ 64 | |
enum { | |
TICK_SECOND = TICK_HZ, | |
TICK_250MS = (TICK_SECOND / 4), | |
TICK_500MS = (TICK_SECOND / 2), | |
TICK_750MS = (TICK_500MS + TICK_250MS), | |
TICK_MAX = 0x7f, | |
TICK_TIMER_DISABLED = 0xff | |
}; | |
enum { | |
timer_Scrolling, | |
timer_ClockDisplay, | |
timer_MAX | |
}; | |
volatile uint32_t tickCount; | |
struct tick_t { | |
uint16_t delay; | |
}; | |
struct tick_t timer[timer_MAX] = { | |
[timer_Scrolling] = { .delay = TICK_SECOND / 16 }, | |
[timer_ClockDisplay] = { .delay = TICK_SECOND / 2 }, | |
}; | |
struct clock_t { | |
uint8_t ticks,hour,min,sec; | |
}; | |
struct clock_t clock; | |
ISR(TIMER2_COMPA_vect) // handler for Output Compare 1 overflow interrupt | |
{ | |
sei(); | |
tickCount++; | |
// decrement delay lines | |
for (char i = 0; i < timer_MAX; i++) | |
if (timer[(int)i].delay) { | |
timer[(int)i].delay--; | |
} | |
clock.ticks++; | |
if (clock.ticks == TICK_SECOND) { | |
clock.ticks = 0; | |
clock.sec++; | |
if (clock.sec == 60) { | |
clock.sec = 0; | |
clock.min++; | |
if (clock.min == 60) { | |
clock.min = 0; | |
clock.hour++; | |
if (clock.hour == 24) | |
clock.hour = 0; | |
} | |
} | |
} | |
} | |
void tick_init() | |
{ | |
/* | |
Timer 2 as RTC | |
*/ | |
// needs to do that before changing the timer registers | |
// ASYNC timer using a 32k crystal | |
ASSR |= (1 << AS2); | |
TCCR2A = (1 << WGM21); | |
// use CLK/8 prescale value, clear timer/counter on compareA match | |
TCCR2B = (2 << CS20); | |
/* -- MathPad | |
clock=32768 | |
prescaler=8 | |
hz=64 | |
(clock/prescaler/hz)-1:63 -- */ | |
OCR2A = 63; | |
TIMSK2 |= (1 << OCIE2A); | |
} | |
#define tick_timer_fired(_t) (timer[(_t)].delay == 0) | |
#define tick_timer_reset(_t, _v) timer[(_t)].delay = (_v) | |
#if 0 | |
/* | |
|/ _ |_| _.._ _||o._ _ | |
|\(/_\/ | |(_|| |(_|||| |(_| | |
/ _| | |
*/ | |
#define PORT_KEYS PORTA | |
#define DDR_KEYS DDRA | |
enum EKeys { | |
KEY1 = PA7, | |
KEY2 = PA6, | |
KEY3 = PA5, | |
KEY4 = PA4, | |
}; | |
#endif | |
#define DCF77_PORT PORTA | |
#define DCF77_DATA PA4 | |
#define DCF77_ENABLE PA5 | |
#define DCF77_IN PINA | |
#define DCF77_DDR DDRA | |
void dcf77_valid_time(); | |
#define DCF77_VALID_TIME() dcf77_valid_time() | |
#define DCF77_PCINT_vect PCINT0_vect | |
#include "mod_dcf77.c" | |
/* | |
_ _ _ | |
| |_ | \ | \o _ ._ | _. |\/| _. _ ._ _ _ | |
|_|_ |_/ |_/|_> |_)|(_|\/ | |(_|(_ | (_)_> | |
| / | |
*/ | |
#define PORT_SURE PORTD | |
#define DDR_SURE DDRD | |
#include "nokia.c" | |
#include "bitdict.c" | |
enum ESurePins { | |
PIN_WR = PD0, | |
PIN_RD = PD1, | |
PIN_DATA = PD2, | |
PIN_CS1 = PD4, | |
PIN_CS2 = PD5, | |
PIN_CS3 = PD6, | |
PIN_CS4 = PD7, | |
}; | |
uint8_t cs = (1 << PIN_CS1); | |
#define SURE_WW 512 | |
#define SURE_HH 16 | |
#define SURE_RB (SURE_WW/8) | |
#define CLOCK_BEGIN() \ | |
uint8_t current = 0xff; // all on | |
#define CLOCK_PAUSE() | |
#define CLOCK_BIT(_bit) \ | |
{ \ | |
current = (current & ~((1 << PIN_DATA) | (1 << PIN_WR)));\ | |
current |= (((_bit) & 1) << PIN_DATA);\ | |
PORT_SURE = current; \ | |
CLOCK_PAUSE();\ | |
current |= (1 << PIN_WR); \ | |
PORT_SURE = current; \ | |
} | |
#define CLOCK_START(_command) \ | |
{\ | |
PORT_SURE = current;\ | |
current &= ~cs;\ | |
PORT_SURE = current; \ | |
CLOCK_BIT(_command >> 2);\ | |
CLOCK_PAUSE();\ | |
CLOCK_BIT(_command >> 1);\ | |
CLOCK_PAUSE();\ | |
CLOCK_BIT(_command);\ | |
} | |
#define CLOCK_END() \ | |
{\ | |
current |= cs;\ | |
PORT_SURE = current; \ | |
} | |
#define CLOCK_FINISH() | |
#include "sure_led_draw.c" | |
uint8_t vram_bits[SURE_RB * SURE_HH]; | |
/* | |
__ _ ___ _ | |
(_ |_) | / _ ._ _ ._ _ _.._ _| _ | |
__)| _|_ \_(_)| | || | |(_|| |(_|_> | |
*/ | |
#define SPI_PORT PORTB | |
#define SPI_DDR DDRB | |
#define SPI_IN PINB | |
enum ESPIPins { | |
PIN_SPI_SS = PB4, | |
PIN_SPI_MOSI = PB5, | |
PIN_SPI_MISO = PB6, | |
PIN_SPI_SCK = PB7, | |
}; | |
DECLARE_FIFO(uint8_t, spi_in, 128); | |
DEFINE_FIFO(uint8_t, spi_in); | |
DECLARE_FIFO(uint8_t, spi_out, 32); | |
DEFINE_FIFO(uint8_t, spi_out); | |
spi_in_t spi_in = FIFO_NULL; | |
spi_out_t spi_out = FIFO_NULL; | |
FIFO_CURSOR_TYPE spi_write_len; | |
void spi_slave_init() | |
{ | |
// Enable the SPI machinery in slave mode, | |
// and enable the SPI interrupt: | |
SPCR = /* (1 << SPIE) | */ (1 << SPE) | (0 << CPHA) | (1 << CPOL); | |
// Make MISO an output | |
// | |
SPI_DDR = (SPI_DDR & ~((1 << PIN_SPI_SS)|(1 << PIN_SPI_MOSI)|(1 << PIN_SPI_MISO)|(1 << PIN_SPI_SCK))) | | |
(1 << PIN_SPI_MISO); | |
SPDR = SPSR; | |
SPDR = 0xff; | |
PCMSK1 |= (1 << PCINT12); // enable interrupt for SS for SPI start/end detect | |
PCICR |= (1 << PCIE1); // PCIE1 enable pin interrupt PCINT15..8. | |
} | |
/* | |
* Pin change interrupt on !SS pin to detect start and end of transaction | |
* the end of transaction detects if any bytes were received and update the | |
* FIFO write pointer if so. | |
*/ | |
ISR(PCINT1_vect) | |
{ | |
uint8_t ignore = SPDR; // flush data register, make sure pipe is empty | |
if (SPI_IN & (1 << PIN_SPI_SS)) { | |
// spi stopped, ready the buffer if any, switch to receiving the new one | |
if (spi_write_len) { | |
SPCR &= ~(1 << SPIE); | |
spi_in_write_offset(&spi_in, spi_write_len); | |
spi_write_len = 0; | |
} | |
} else { | |
// spi started, flush registers | |
ignore = 0xff; | |
SPCR |= (1 << SPIE); | |
SPDR = ignore; | |
spi_out_reset(&spi_out); | |
} | |
} | |
void spi_handler_command_response(uint8_t command); | |
ISR(SPI_STC_vect) | |
{ | |
uint8_t dat = SPDR; | |
if (spi_write_len == 0) | |
spi_handler_command_response(dat); | |
spi_in_write_at(&spi_in, spi_write_len++, dat); | |
if (!spi_out_isempty(&spi_out)) | |
dat = spi_out_read(&spi_out); | |
SPDR = dat; | |
} | |
uint16_t RenderMessage(uint16_t dx, uint8_t dy, const char * text) | |
{ | |
uint16_t w = dx; | |
dy += nokia[o_baseline]; | |
V(printf("render %d,%d '%s'\n", dx, dy, text)); | |
DrawText(nokia, text, &dy, &w, vram_bits, SURE_RB); | |
V(printf("done %d\n", w)); | |
w -= dx; | |
return w; | |
} | |
/* | |
_ | |
|_)._ _ _|_ _ _|_ |_ ._ _ _. _| _ | |
| | (_) |_(_) |_ | || (/_(_|(_|_> | |
*/ | |
enum EThreadIndex { | |
thread_LedDisplay0, | |
thread_LedDisplay1, | |
thread_LedScroll, | |
thread_SPI, | |
thread_DCF77_Debug, | |
thread_ClockDisplay, | |
thread_MAX | |
}; | |
struct pt pt_thread[thread_MAX]; | |
struct led_line_t { | |
uint8_t cs; | |
uint8_t pwm; | |
uint8_t draw; | |
uint16_t width; | |
uint16_t x; | |
uint8_t y; | |
uint16_t delay; | |
uint16_t timer; | |
uint8_t scrollCount; | |
}; | |
struct led_line_t line[2] = { | |
[0] = { .cs = (1 << PIN_CS1), .x = 0, .y = 0, .draw = 1, .pwm = 0x7, | |
.scrollCount = 0xff, .delay = 0x50 | |
}, | |
[1] = { .cs = (1 << PIN_CS4), .x = 0, .y = 8, .draw = 1, .pwm = 0x7, }, | |
}; | |
PT_THREAD(pt_clock(struct pt *pt)) | |
{ | |
PT_BEGIN(pt); | |
static struct led_line_t *l = &line[1]; | |
static uint16_t w; | |
static uint8_t dy; | |
static uint16_t dot; | |
do { | |
PT_WAIT_UNTIL(pt, tick_timer_fired(timer_ClockDisplay)); | |
tick_timer_reset(timer_ClockDisplay, TICK_SECOND/2); | |
for (int y = l->y; y < l->y + 8; y++) | |
for (int x = 0; x < 4; x++) | |
vram_bits[(y * SURE_RB) + x] = 0; | |
static char clk[8]; | |
sprintf(clk, "%d", clock.hour); | |
w = 0; | |
dy = l->y + 1 + nokia[o_baseline]; | |
DrawText(nokia, clk, &dy, &w, vram_bits, SURE_RB); | |
dot = w + 1; | |
w += 3; | |
sprintf(clk, "%02d", clock.min); | |
DrawText(nokia, clk, &dy, &w, vram_bits, SURE_RB); | |
l->x = dot - 15; | |
l->width = w + 1; | |
l->draw = 1; | |
PT_WAIT_UNTIL(pt, tick_timer_fired(timer_ClockDisplay)); | |
tick_timer_reset(timer_ClockDisplay, TICK_SECOND/2); | |
sure_setpixel(vram_bits, dot, l->y + 5, 1); | |
sure_setpixel(vram_bits, dot, l->y + 7, 1); | |
l->draw = 1; | |
} while (1); | |
PT_END(pt); | |
} | |
void dcf77_valid_time() | |
{ | |
clock.ticks = TICK_SECOND; | |
clock.hour = dcf77_bcd_to_dec(dcf77_time.hour, 6); | |
clock.min = dcf77_bcd_to_dec(dcf77_time.min, 7); | |
clock.sec = 0; | |
} | |
void dcf77_debug_start() | |
{ | |
} | |
void dcf77_debug_stop() | |
{ | |
} | |
PT_THREAD(pt_debug_dcf77(struct pt *pt)) | |
{ | |
static struct led_line_t *l = &line[0]; | |
PT_BEGIN(pt); | |
static uint8_t old; | |
do { | |
PT_WAIT_UNTIL(pt, dcf77_flags != old); | |
old = dcf77_flags; | |
uint16_t u = SURE_RB * l->y; | |
vram_bits[u] = dcf77_flags; | |
u += SURE_RB; | |
vram_bits[u] = dcf77.shift[0]; | |
vram_bits[u+1] = dcf77.shift[0+1]; | |
vram_bits[u+2] = dcf77.shift[0+2]; | |
vram_bits[u+3] = dcf77.shift[0+3]; | |
u += SURE_RB; | |
vram_bits[u] = dcf77.shift[4]; | |
vram_bits[u+1] = dcf77.shift[4+1]; | |
vram_bits[u+2] = dcf77.shift[4+2]; | |
vram_bits[u+3] = dcf77.shift[4+3]; | |
u += SURE_RB; | |
u += SURE_RB; | |
vram_bits[u] = dcf77_good[0]; | |
vram_bits[u+1] = dcf77_good[0+1]; | |
vram_bits[u+2] = dcf77_good[0+2]; | |
vram_bits[u+3] = dcf77_good[0+3]; | |
u += SURE_RB; | |
vram_bits[u] = dcf77_good[4]; | |
vram_bits[u+1] = dcf77_good[4+1]; | |
vram_bits[u+2] = dcf77_good[4+2]; | |
vram_bits[u+3] = dcf77_good[4+3]; | |
u += SURE_RB; | |
vram_bits[u] = dcf77_time.hour; | |
// vram_bits[u+1] = dcf77_bcd_to_dec(dcf77_time.hour); | |
vram_bits[u+2] = dcf77_time.min; | |
// vram_bits[u+3] = dcf77_bcd_to_dec(dcf77_time.min); | |
l->draw = 1; | |
} while (1); | |
PT_END(pt); | |
} | |
PT_THREAD(pt_led_line_display(struct pt *pt, struct led_line_t * l)) | |
{ | |
PT_BEGIN(pt); | |
V(printf("pt_led_line_display\n");) | |
cs = l->cs; | |
sure_write_command(4, (0L << 24) , 9); | |
PT_YIELD(pt); | |
sure_write_command(4, (1L << 24) | (3L << 15) | (0x18L << 6), 3*9); | |
do { | |
cs = l->cs; | |
l->pwm &= ~0x80; | |
sure_write_command(4, ((0xa0L | l->pwm) << 24), 9); | |
PT_YIELD(pt); | |
do { | |
PT_WAIT_UNTIL(pt, l->width && l->draw); | |
cs = l->cs; | |
sure_draw_bits(vram_bits, l->x, l->y); | |
l->draw = 0; | |
} while(!(l->pwm & 0x80)); | |
} while (1); | |
PT_END(pt); | |
} | |
PT_THREAD(pt_horizontal_scroll(struct pt *pt)) | |
{ | |
PT_BEGIN(pt); | |
V(printf("pt_horizontal_scroll\n");) | |
do { | |
PT_WAIT_UNTIL(pt, tick_timer_fired(timer_Scrolling)); | |
tick_timer_reset(timer_Scrolling, TICK_SECOND/16); | |
for (int li = 0; li < 1; li++) { | |
struct led_line_t * ll = &line[li]; | |
if (ll->timer) | |
ll->timer--; | |
else if (ll->scrollCount && ll->width) { | |
ll->x++; | |
ll->draw = 1; | |
if (ll->x >= ll->width) { | |
ll->x -= ll->width; | |
ll->timer = ll->delay; | |
} | |
if (line->scrollCount != 0xff && line->scrollCount) | |
line->scrollCount--; | |
} | |
} | |
} while(1); | |
PT_END(pt); | |
} | |
/* this one is called via interupt, don't linger! */ | |
void spi_handler_command_response(uint8_t command) | |
{ | |
switch (command) { | |
case CMD_GetClock: { | |
spi_out_write_at(&spi_out, 0, clock.hour); | |
spi_out_write_at(&spi_out, 0, clock.min); | |
spi_out_write_at(&spi_out, 0, clock.sec); | |
spi_out_write_offset(&spi_out, 3); | |
} break; | |
} | |
} | |
PT_THREAD(pt_spi_handler(struct pt *pt)) | |
{ | |
int i; | |
static char msg[128]; | |
PT_BEGIN(pt); | |
do { | |
PT_WAIT_UNTIL(pt, !spi_in_isempty(&spi_in)); | |
FIFO_CURSOR_TYPE l = spi_in_get_read_size(&spi_in); | |
uint8_t command = spi_in_read_at(&spi_in, 0); | |
V(printf("spi cmd %02x\n", command);) | |
switch (command) { | |
case CMD_DCF77_Init: { | |
cs = (1 << PIN_CS1); | |
sure_write_command(4, (0L << 24) , 9); | |
sure_write_command(4, (1L << 24) | (3L << 15) | (0x18L << 6), 3*9); | |
cs = (1 << PIN_CS4); | |
sure_write_command(4, (0L << 24) , 9); | |
sure_write_command(4, (1L << 24) | (3L << 15) | (0x18L << 6), 3*9); | |
} break; | |
case CMD_SETPWM: { | |
if (l < 2) | |
break; | |
uint8_t pwm = spi_in_read_at(&spi_in, 1); | |
if ((pwm & 0xf0) == 0) | |
pwm |= 0x30; | |
for (i = 0; i < 2; i++) { | |
if (pwm & (0x10 << i)) | |
line[i].pwm = (pwm & 0xf) | 0x80; | |
} | |
} break; | |
case CMD_DisplayString: { | |
for (uint16_t bi = 0; bi < SURE_RB*8; bi++) | |
vram_bits[bi]=0; | |
char *d = msg; | |
for (uint8_t bi = 1; bi < l; bi++) | |
*d++ = spi_in_read_at(&spi_in, bi); | |
line[0].width = RenderMessage(32, 0, msg) + 32 + 1; | |
} break; | |
case CMD_SetClock: { | |
if (l < 4) | |
break; | |
clock.ticks = TICK_SECOND; | |
clock.hour = spi_in_read_at(&spi_in, 1); | |
clock.min = spi_in_read_at(&spi_in, 2); | |
clock.sec = spi_in_read_at(&spi_in, 3); | |
} break; | |
case CMD_SetScrollDelay: { // set the delay for lines. | |
/* 0x04<bit mask for lines><scroll delay> | |
* ex 040300 disable scrolling for both lines | |
* 0401ff scrolls forever line zero | |
*/ | |
if (l < 3) | |
break; | |
uint8_t mask = spi_in_read_at(&spi_in, 1); | |
uint16_t dd = 0; | |
for (int i = 2; i < l; i++) | |
dd = (dd << 8) | spi_in_read_at(&spi_in, i); | |
for (i = 0; i < 2; i++) | |
if (mask & (1 << i)) | |
line[i].delay = dd; | |
} break; | |
case CMD_SetScrollCount: { | |
if (l < 3) | |
break; | |
uint8_t mask = spi_in_read_at(&spi_in, 1); | |
uint8_t count = spi_in_read_at(&spi_in, 2); | |
for (i = 0; i < 2; i++) | |
if (mask & (1 << i)) | |
line[i].scrollCount = count; | |
} break; | |
case CMD_DCF77_Disable: | |
dcf77_disable(); | |
break; | |
case CMD_DCF77_Enable: | |
dcf77_enable(); | |
break; | |
case CMD_DEBUG_DCF_Start: | |
break; | |
case CMD_DEBUG_DCF_End: | |
break; | |
default: { | |
sprintf(msg, "spi %d %02x%02x%02x%02x", l, | |
spi_in_read_at(&spi_in, 0), | |
spi_in_read_at(&spi_in, 1), | |
spi_in_read_at(&spi_in, 2), | |
spi_in_read_at(&spi_in, 3)); | |
for (uint16_t bi = 0; bi < 512; bi++) | |
vram_bits[bi]=0; | |
line[0].width = line[1].width = RenderMessage(64, 0, msg) + 64 + 1; | |
} break; | |
} | |
spi_in_read_offset(&spi_in, l); | |
} while(1); | |
PT_END(pt); | |
} | |
int main() | |
{ | |
CLKPR = (1 << CLKPCE); | |
CLKPR = 0; // cancel div8 fuse | |
DDR_SURE = 0xff; | |
PORT_SURE = 0xff; | |
V(stdout = &mystdout;) | |
// DDRA = 0xff; | |
// PORTA = 0; | |
PCMSK0 = (1 << PCINT4); // enable interupt for PA4 for DATA clock | |
PCICR = (1 << PCIE0); // PCIE0 enable pin interupt PCINT7..0. | |
spi_slave_init(); | |
tick_init(); | |
dcf77_init(); | |
sei(); | |
line[0].width = RenderMessage(32, 0, "LED display testing!") + 32 + 1; | |
V(printf("Running...\n");) | |
for (int pi = 0; pi < thread_MAX; pi++) | |
PT_INIT(pt_thread + pi); | |
do { | |
pt_led_line_display(pt_thread + thread_LedDisplay0, line); | |
pt_led_line_display(pt_thread + thread_LedDisplay1, line+1); | |
pt_spi_handler(pt_thread + thread_SPI); | |
pt_horizontal_scroll(pt_thread + thread_LedScroll); | |
// pt_debug_dcf77(pt_thread + thread_DCF77_Debug); | |
pt_clock(pt_thread + thread_ClockDisplay); | |
V(sleep_mode();) | |
} while (1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment