Skip to content

Instantly share code, notes, and snippets.

@buserror
Created Feb 2, 2017
Embed
What would you like to do?
protothreads, aka duff's device in use...
/*
*
* 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