177 lines
2.6 KiB
C
177 lines
2.6 KiB
C
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
#include "ascii.h"
|
|
|
|
#define bit(reg, bit, on) reg = (on ? (reg | _BV(bit)) : (reg & ~_BV(bit)));
|
|
|
|
#define sce(on) bit(PORTA, 0, on)
|
|
#define rst(on) bit(PORTA, 1, on)
|
|
#define dc(on) bit(PORTA, 2, on)
|
|
#define sclk(on) bit(PORTA, 4, on)
|
|
#define dn(on) bit(PORTA, 5, on)
|
|
|
|
#define LED _BV(OC0A)
|
|
|
|
#define LCD_X 84
|
|
#define LCD_Y 48
|
|
|
|
#define LCD_COMMAND false
|
|
#define LCD_DATA true
|
|
|
|
volatile uint32_t jiffies = 0;
|
|
volatile bool tick = false;
|
|
|
|
void
|
|
initPWM0()
|
|
{
|
|
DDRA |= (1 << PA7); // Set OC0B to output
|
|
|
|
// Phase-correct PWM, non-inverting mode
|
|
TCCR0A |= _BV(COM0A1) | _BV(WGM00);
|
|
|
|
// No clock prescaling
|
|
TCCR0B |= ((1 << CS00));
|
|
}
|
|
|
|
inline void
|
|
setPWM0A(uint8_t num)
|
|
{
|
|
OCR0A = num;
|
|
}
|
|
|
|
void
|
|
LCDWrite(bool datap, uint8_t v)
|
|
{
|
|
int i;
|
|
|
|
dc(datap);
|
|
sce(false);
|
|
|
|
// Push out over SPI
|
|
USIDR = v;
|
|
USISR = _BV(USIOIF);
|
|
while ((USISR & _BV(USIOIF)) == 0) {
|
|
USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC);
|
|
}
|
|
|
|
sce(true);
|
|
}
|
|
|
|
void
|
|
LCDChar(char c)
|
|
{
|
|
int i;
|
|
|
|
LCDWrite(true, 0);
|
|
for (i = 0; i < 5; i += 1) {
|
|
LCDWrite(true, ASCII[c - 0x20][i]);
|
|
}
|
|
LCDWrite(true, 0);
|
|
}
|
|
|
|
void
|
|
LCDString(char *s)
|
|
{
|
|
for (; *s; s += 1) {
|
|
LCDChar(*s);
|
|
}
|
|
}
|
|
|
|
void
|
|
LCDGoto(int x, int y)
|
|
{
|
|
LCDWrite(false, 0x80 | x); // Column.
|
|
LCDWrite(false, 0x40 | y); // Row. ?
|
|
}
|
|
|
|
void
|
|
LCDClear(void)
|
|
{
|
|
int i;
|
|
|
|
LCDGoto(0, 0);
|
|
for (i = 0; i < (LCD_X * LCD_Y / 8); i++) {
|
|
LCDWrite(true, 0x00);
|
|
}
|
|
|
|
LCDGoto(0, 0); // After we clear the display, return to the home position
|
|
}
|
|
|
|
void
|
|
write_digit(uint8_t d)
|
|
{
|
|
LCDChar('0' + (d % 10));
|
|
}
|
|
|
|
void
|
|
write_time(uint16_t secs)
|
|
{
|
|
uint16_t min;
|
|
|
|
min = secs / 60;
|
|
secs = secs % 60;
|
|
|
|
if (min >= 10) {
|
|
write_digit(min / 10);
|
|
} else {
|
|
LCDChar(' ');
|
|
}
|
|
write_digit(min % 10);
|
|
LCDChar(':');
|
|
write_digit(secs / 10);
|
|
write_digit(secs % 10);
|
|
}
|
|
|
|
void
|
|
loop(void)
|
|
{
|
|
static uint8_t v = 0;
|
|
|
|
if (tick) {
|
|
int i;
|
|
|
|
tick = 0;
|
|
|
|
OCR0A = (jiffies % 10) * 2;
|
|
|
|
if (jiffies % 10 == 0) {
|
|
LCDGoto(0, 0);
|
|
LCDString("Jam ");
|
|
write_time(jiffies / 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
init();
|
|
initPWM0();
|
|
|
|
DDRA = 0xFF;
|
|
|
|
// Reset display
|
|
rst(false);
|
|
rst(true);
|
|
|
|
LCDWrite(false, 0x21); // Tell LCD that extended commands follow
|
|
LCDWrite(false, 0xB0); // Set LCD Vop (Contrast): Try 0xB1(good @ 3.3V) or 0xBF if your display is too dark
|
|
LCDWrite(false, 0x04); // Set Temp coefficent
|
|
LCDWrite(false, 0x13); // LCD bias mode 1:48: Try 0x13 or 0x14
|
|
|
|
LCDWrite(false, 0x20); // We must send 0x20 before modifying the display control mode
|
|
LCDWrite(false, 0x0C); // Set display control, normal mode. 0x0D for inverse
|
|
|
|
LCDClear();
|
|
|
|
for (;;) {
|
|
loop();
|
|
}
|
|
return 0;
|
|
}
|