Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*****************************************************************************
- * hexclock.c
- *
- * Created: 7/24/2015 4:02:13 AM
- * Author: me
- *
- * Hexclock is a user friendly implementation of binary time for ATTINY2313.
- ****************************************************************************/
- #define F_CPU 1024000UL
- #include <avr/io.h>
- #include <avr/interrupt.h>
- #include <avr/power.h>
- #include <stdint.h>
- #include <stddef.h>
- #include <avr/pgmspace.h>
- #include <util/atomic.h>
- /* Keys */
- #define DDR_SETK DDRB
- #define PORT_SETK PORTB
- #define PIN_SETK PINB
- #define SETK_MASK _BV(PINB0)
- #define DDR_PLUSK DDRB
- #define PORT_PLUSK PORTB
- #define PIN_PLUSK PINB
- #define PLUSK_MASK _BV(PINB1)
- /* Shift Register */
- #define DDR_SREG DDRD
- #define PORT_SREG PORTD
- #define SRCK _BV(PORTD0)
- #define RCK _BV(PORTD1)
- #define SER _BV(PORTD2)
- /* Display Chip Select */
- #define DDR_CS DDRD
- #define PORT_CS PORTD
- #define CS_1 _BV(PORTD3)
- #define CS_2 _BV(PORTD4)
- #define CS_3 _BV(PORTD5)
- #define CS_4 _BV(PORTD6)
- #define CS_ALL (CS_1 | CS_2 | CS_3 | CS_4)
- /* Display D.P. */
- #define DISP_DP1 8
- #define DISP_DP2 4
- #define DISP_DP3 2
- #define DISP_DP4 1
- #define OCR0A_VAL 127
- #define OCR1A_VAL 21092
- #define OCR1A_SLIP 3
- struct kstate {
- char set_state;
- char set_prevstate;
- char plus_state;
- char plus_prevstate;
- };
- void init_timer(void);
- void display_write(uint8_t data, uint8_t cursor);
- void set_select(volatile uint16_t *time);
- void display_update_buffer(uint16_t val, uint8_t dp_flags);
- volatile uint16_t g_time = 0x0000;
- volatile uint8_t displaybuf[4];
- volatile struct kstate g_keystate = { 0, 0, 0, 0 };
- int main(void)
- {
- /* Setup */
- DDR_SREG |= (SRCK | RCK | SER); // Config SREG Pins as Outputs
- PORT_SREG &= ~(SRCK | RCK | SER); // Set Shift Register Pins Low
- DDR_CS |= CS_ALL; // Config Chip Select Pins Output
- PORT_CS |= CS_ALL; // Set Chip Select Pins High
- DDR_SETK &= ~SETK_MASK; // Configure Key Pins as Input
- DDR_PLUSK &= ~PLUSK_MASK;
- PORT_SETK |= SETK_MASK; // Enable Pullups
- PORT_PLUSK |= PLUSK_MASK;
- init_timer();
- sei();
- for (;;)
- {
- if (g_keystate.set_state && !g_keystate.set_prevstate) {
- g_keystate.set_prevstate = 1;
- set_select(&g_time);
- } else if (!g_keystate.set_state && g_keystate.set_prevstate) {
- g_keystate.set_prevstate = 0;
- }
- display_update_buffer(g_time, 0);
- }
- }
- /**
- * Updates display buffer with val. dp_flags can be used to set
- * the display's decimal points on, or off. Flags can be combined
- * using bitwise OR
- */
- void display_update_buffer(uint16_t val, uint8_t dp_flags)
- {
- static const uint8_t symtab[16] PROGMEM = {
- 0xfc, 0x60, 0xda, 0xf2,
- 0x66, 0xb6, 0xbe, 0xe0,
- 0xfe, 0xe6, 0xee, 0x3e,
- 0x9c, 0x7a, 0x9e, 0x8e
- };
- for (unsigned char i = 0; i < 4; ++i, val >>= 4, dp_flags >>= 1) {
- displaybuf[i] = pgm_read_byte(&symtab[val & 0xf]) | (dp_flags & 1);
- }
- }
- /**
- * Sets up Timer/Counters
- */
- void init_timer(void)
- {
- /* -- Using 16 bit Timer1 -- */
- // Clear TCCR1A
- TCCR1A = 0;
- // Clock Select -- clk/64
- TCCR1B = _BV(CS11) | _BV(CS10);
- // Clear on Compare Match (CTC)
- TCCR1B |= _BV(WGM12);
- // Set Compare Match A Register
- // Must slip clock every matches to reduce drift
- OCR1A = OCR1A_VAL; // (1.31...s interval target)
- /* Output Compare Match A interrupt disabled until time is set */
- /* -- Using 8-bit Timer0 -- */
- // Clear on Compare Match (CTC)
- TCCR0A = _BV(WGM01);
- // Clock Select clk/8
- TCCR0B = _BV(CS01);
- // Set Compare Match A Register
- OCR0A = OCR0A_VAL; // 1ms tick ... experiment not permenant.
- // Output Compare Match A interrupt enable (TIMER0/1)
- TIMSK = _BV(OCIE0A);
- }
- /**
- * Sets time on clock in groups of 4 bits
- */
- void set_select(volatile uint16_t *time)
- {
- static char unset = 1; // has clock been initially set yet
- uint16_t new_time;
- signed char i = 12;
- uint8_t dp_cursor = DISP_DP1; // use dp to indicate position
- // new_time gets time
- ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
- new_time = *time;
- }
- while (i >= 0) {
- uint8_t nibble = (new_time >> i) & 0x0f;
- /* Set/Select Key presses cycle through 4 bit chunks of time */
- if (g_keystate.set_state && !g_keystate.set_prevstate) {
- g_keystate.set_prevstate = 1;
- dp_cursor >>= 1;
- i -= 4;
- continue;
- } else if (!g_keystate.set_state && g_keystate.set_prevstate) {
- g_keystate.set_prevstate = 0;
- }
- /* Presses of increment key increments time by one unit */
- if (g_keystate.plus_state && !g_keystate.plus_prevstate) {
- g_keystate.plus_prevstate = 1;
- // Simulates a 4-bit register by clearing when 0xf is exceeded
- nibble = (nibble < 0xf) ? nibble + 1 : 0;
- } else if (!g_keystate.plus_state && g_keystate.plus_prevstate) {
- g_keystate.plus_prevstate = 0;
- }
- // shift nibble back and insert into new_time
- new_time &= (~(0xf << i));
- new_time |= ((uint16_t) nibble << i);
- // update display
- display_update_buffer(new_time, dp_cursor);
- }
- // Output Compare Match A enable first being set the first time
- if (unset) {
- TIMSK |= _BV(OCIE1A);
- // clock no longer unset
- unset = 0;
- }
- // time gets new time
- ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
- *time = new_time;
- }
- // Clear Timer Value if g_time has been reset to ensure a full "1st second"
- if (time == &g_time) {
- ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
- TCNT1 = 0;
- }
- }
- }
- /**
- * When called the next position in the update sequence is shown on the
- * 7-segment display device.
- */
- inline static void display_update_sequence(void)
- {
- static uint8_t cursor;
- cursor = (cursor < 3) ? cursor + 1 : 0;
- display_write(displaybuf[cursor], cursor);
- }
- void display_write(uint8_t data, uint8_t cursor)
- {
- /* Set Chip Select High (blank) */
- PORT_CS |= CS_ALL;
- /* Bit-bang shift register */
- // latch low
- PORT_SREG &= ~RCK;
- for (char i = 0; i < 8; ++i) {
- // clock low
- PORT_SREG &= ~SRCK;
- if (data & 1)
- PORT_SREG |= SER;
- else
- PORT_SREG &= ~SER;
- // clock high
- PORT_SREG |= SRCK;
- data >>= 1;
- }
- // latch high
- PORT_SREG |= RCK;
- /**
- * To Select and Enable 7-seg chips in sequence
- * Symbols are stored least significant first by numerical power
- */
- switch (cursor) {
- case 0:
- PORT_CS &= ~CS_4;
- break;
- case 1:
- PORT_CS &= ~CS_3;
- break;
- case 2:
- PORT_CS &= ~CS_2;
- break;
- case 3:
- PORT_CS &= ~CS_1;
- break;
- }
- }
- /* The interrupt code here
- ****************************************************************************/
- /**
- * Timer0 -- Compare Match on OCR0A every 1ms
- * Scan and update global key states
- * Update Multiplexed 7-segment display
- */
- ISR(TIMER0_COMPA_vect, ISR_NOBLOCK)
- {
- static uint8_t tick_acc;
- /* 62.5hz full refresh */
- if (!(tick_acc % 4))
- display_update_sequence();
- /* Keys are active low, poll every 16ms*/
- if (!(tick_acc % 16)) {
- g_keystate.set_state = (!(PIN_SETK & SETK_MASK));
- g_keystate.plus_state = (!(PIN_PLUSK & PLUSK_MASK));
- }
- ++tick_acc;
- }
- /**
- * Timer1 -- Compare Match on OCR1A
- * 86400/65536s tick
- */
- ISR(TIMER1_COMPA_vect)
- {
- static uint8_t tick_acc;
- static uint8_t slipped;
- /*
- * Every 4 ticks, slip clock compare register forward, a flag is set to
- * indicate whether the clock has been slipped so the register can be
- * reset to default but avoid doing this unless actually necessary.
- */
- if (tick_acc % 4) {
- if (slipped) {
- OCR1A = OCR1A_VAL;
- slipped = 0;
- }
- } else {
- // add to next compare match val to offset effects of clock drift
- OCR1A += OCR1A_SLIP;
- slipped = 1;
- }
- // Increment global time
- ++g_time;
- ++tick_acc;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement