Advertisement
Guest User

hexclock.c

a guest
Aug 7th, 2015
220
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*****************************************************************************
  2.  * hexclock.c
  3.  *
  4.  * Created: 7/24/2015 4:02:13 AM
  5.  *  Author: me
  6.  *
  7.  * Hexclock is a user friendly implementation of binary time for ATTINY2313.
  8.  ****************************************************************************/
  9. #define F_CPU 1024000UL
  10.  
  11. #include <avr/io.h>
  12. #include <avr/interrupt.h>
  13. #include <avr/power.h>
  14. #include <stdint.h>
  15. #include <stddef.h>
  16. #include <avr/pgmspace.h>
  17. #include <util/atomic.h>
  18.  
  19. /* Keys */
  20. #define DDR_SETK    DDRB
  21. #define PORT_SETK   PORTB
  22. #define PIN_SETK    PINB
  23. #define SETK_MASK   _BV(PINB0)
  24. #define DDR_PLUSK   DDRB
  25. #define PORT_PLUSK  PORTB
  26. #define PIN_PLUSK   PINB
  27. #define PLUSK_MASK  _BV(PINB1)
  28.  
  29. /* Shift Register */
  30. #define DDR_SREG    DDRD
  31. #define PORT_SREG   PORTD
  32. #define SRCK        _BV(PORTD0)
  33. #define RCK     _BV(PORTD1)
  34. #define SER     _BV(PORTD2)
  35.  
  36. /* Display Chip Select */
  37. #define DDR_CS      DDRD
  38. #define PORT_CS     PORTD
  39. #define CS_1        _BV(PORTD3)
  40. #define CS_2        _BV(PORTD4)
  41. #define CS_3        _BV(PORTD5)
  42. #define CS_4        _BV(PORTD6)
  43. #define CS_ALL      (CS_1 | CS_2 | CS_3 | CS_4)
  44.  
  45. /* Display D.P. */
  46. #define DISP_DP1    8
  47. #define DISP_DP2    4
  48. #define DISP_DP3    2
  49. #define DISP_DP4    1
  50.  
  51. #define OCR0A_VAL   127
  52. #define OCR1A_VAL   21092
  53. #define OCR1A_SLIP  3
  54.  
  55. struct kstate {
  56.     char set_state;
  57.     char set_prevstate;
  58.     char plus_state;
  59.     char plus_prevstate;
  60. };
  61.  
  62. void init_timer(void);
  63. void display_write(uint8_t data, uint8_t cursor);
  64. void set_select(volatile uint16_t *time);
  65. void display_update_buffer(uint16_t val, uint8_t dp_flags);
  66.  
  67. volatile uint16_t   g_time = 0x0000;
  68. volatile uint8_t    displaybuf[4];
  69. volatile struct kstate  g_keystate = { 0, 0, 0, 0 };
  70.  
  71. int main(void)
  72. {
  73.     /* Setup */
  74.     DDR_SREG |= (SRCK | RCK | SER);     // Config SREG Pins as Outputs
  75.     PORT_SREG &= ~(SRCK | RCK | SER);   // Set Shift Register Pins Low
  76.    
  77.     DDR_CS |= CS_ALL;           // Config Chip Select Pins Output
  78.     PORT_CS |= CS_ALL;          // Set Chip Select Pins High
  79.  
  80.     DDR_SETK &= ~SETK_MASK;         // Configure Key Pins as Input
  81.     DDR_PLUSK &= ~PLUSK_MASK;
  82.     PORT_SETK |= SETK_MASK;         // Enable Pullups
  83.     PORT_PLUSK |= PLUSK_MASK;
  84.    
  85.     init_timer();                      
  86.     sei();
  87.    
  88.         for (;;)
  89.     {
  90.         if (g_keystate.set_state && !g_keystate.set_prevstate) {
  91.             g_keystate.set_prevstate = 1;
  92.             set_select(&g_time);
  93.         } else if (!g_keystate.set_state && g_keystate.set_prevstate) {
  94.             g_keystate.set_prevstate = 0;
  95.         }
  96.        
  97.         display_update_buffer(g_time, 0);
  98.         }
  99. }
  100.  
  101. /**
  102.  * Updates display buffer with val. dp_flags can be used to set
  103.  * the display's decimal points on, or off. Flags can be combined
  104.  * using bitwise OR
  105.  */
  106. void display_update_buffer(uint16_t val, uint8_t dp_flags)
  107. {
  108.     static const uint8_t symtab[16] PROGMEM = {
  109.         0xfc, 0x60, 0xda, 0xf2,
  110.         0x66, 0xb6, 0xbe, 0xe0,
  111.         0xfe, 0xe6, 0xee, 0x3e,
  112.         0x9c, 0x7a, 0x9e, 0x8e
  113.     };
  114.    
  115.     for (unsigned char i = 0; i < 4; ++i, val >>= 4, dp_flags >>= 1) {
  116.         displaybuf[i] = pgm_read_byte(&symtab[val & 0xf]) | (dp_flags & 1);
  117.     }
  118. }
  119.  
  120. /**
  121.  * Sets up Timer/Counters
  122.  */
  123. void init_timer(void)
  124. {
  125.     /* -- Using 16 bit Timer1 -- */
  126.     // Clear TCCR1A
  127.     TCCR1A = 0;
  128.    
  129.     // Clock Select -- clk/64
  130.     TCCR1B = _BV(CS11) | _BV(CS10);
  131.    
  132.     // Clear on Compare Match (CTC)
  133.     TCCR1B |= _BV(WGM12);
  134.    
  135.     // Set Compare Match A Register
  136.         // Must slip clock every matches to reduce drift
  137.     OCR1A = OCR1A_VAL;  // (1.31...s interval target)
  138.  
  139.     /* Output Compare Match A interrupt disabled until time is set */
  140.    
  141.     /* -- Using 8-bit Timer0 -- */
  142.     // Clear on Compare Match (CTC)
  143.     TCCR0A = _BV(WGM01);
  144.    
  145.     // Clock Select clk/8
  146.     TCCR0B = _BV(CS01);
  147.    
  148.     // Set Compare Match A Register
  149.     OCR0A = OCR0A_VAL;  // 1ms tick ... experiment not permenant.
  150.    
  151.     // Output Compare Match A interrupt enable (TIMER0/1)
  152.     TIMSK = _BV(OCIE0A);
  153. }
  154.  
  155. /**
  156.  * Sets time on clock in groups of 4 bits
  157.  */
  158. void set_select(volatile uint16_t *time)
  159. {
  160.     static char unset = 1;          // has clock been initially set yet
  161.     uint16_t new_time;
  162.     signed char i = 12;
  163.     uint8_t dp_cursor = DISP_DP1;   // use dp to indicate position
  164.  
  165.         // new_time gets time
  166.      ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  167.              new_time = *time;
  168.         }
  169.  
  170.     while (i >= 0) {
  171.         uint8_t nibble = (new_time >> i) & 0x0f;
  172.        
  173.         /* Set/Select Key presses cycle through 4 bit chunks of time */
  174.         if (g_keystate.set_state && !g_keystate.set_prevstate) {
  175.             g_keystate.set_prevstate = 1;
  176.             dp_cursor >>= 1;
  177.             i -= 4;
  178.             continue;
  179.         } else if (!g_keystate.set_state && g_keystate.set_prevstate) {
  180.             g_keystate.set_prevstate = 0;
  181.         }
  182.        
  183.         /* Presses of increment key increments time by one unit */
  184.         if (g_keystate.plus_state && !g_keystate.plus_prevstate) {
  185.             g_keystate.plus_prevstate = 1;
  186.            
  187.             // Simulates a 4-bit register by clearing when 0xf is exceeded
  188.             nibble = (nibble < 0xf) ? nibble + 1 : 0;
  189.         } else if (!g_keystate.plus_state && g_keystate.plus_prevstate) {
  190.             g_keystate.plus_prevstate = 0;
  191.         }
  192.        
  193.         // shift nibble back and insert into new_time
  194.         new_time &= (~(0xf << i));
  195.         new_time |= ((uint16_t) nibble << i);
  196.        
  197.         // update display
  198.         display_update_buffer(new_time, dp_cursor);
  199.     }
  200.  
  201.     // Output Compare Match A enable first being set the first time
  202.     if (unset) {
  203.         TIMSK |= _BV(OCIE1A);
  204.  
  205.             // clock no longer unset
  206.             unset = 0;
  207.     }
  208.  
  209.         // time gets new time    
  210.         ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  211.             *time = new_time;
  212.         }
  213.  
  214.         // Clear Timer Value if g_time has been reset to ensure a full "1st second"
  215.         if (time == &g_time) {
  216.             ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  217.                 TCNT1 = 0;
  218.         }
  219.     }
  220. }
  221.  
  222. /**
  223.  * When called the next position in the update sequence is shown on the
  224.  * 7-segment display device.
  225.  */
  226. inline static void display_update_sequence(void)
  227. {
  228.     static uint8_t cursor;
  229.  
  230.     cursor = (cursor < 3) ? cursor + 1 : 0;
  231.     display_write(displaybuf[cursor], cursor);
  232. }
  233.  
  234. void display_write(uint8_t data, uint8_t cursor)
  235. {
  236.     /* Set Chip Select High (blank) */
  237.     PORT_CS |= CS_ALL;
  238.        
  239.     /* Bit-bang shift register */
  240.     // latch low
  241.     PORT_SREG &= ~RCK;
  242.     for (char i = 0; i < 8; ++i) {
  243.        
  244.         // clock low
  245.         PORT_SREG &= ~SRCK;
  246.         if (data & 1)
  247.             PORT_SREG |= SER;
  248.         else
  249.             PORT_SREG &= ~SER;
  250.        
  251.         // clock high
  252.         PORT_SREG |= SRCK;
  253.         data >>= 1;
  254.     }
  255.    
  256.     // latch high
  257.     PORT_SREG |= RCK;
  258.    
  259.     /**
  260.      * To Select and Enable 7-seg chips in sequence
  261.      * Symbols are stored least significant first by numerical power
  262.      */
  263.     switch (cursor) {
  264.         case 0:
  265.             PORT_CS &= ~CS_4;
  266.             break;
  267.         case 1:
  268.             PORT_CS &= ~CS_3;
  269.             break;
  270.         case 2:
  271.             PORT_CS &= ~CS_2;
  272.             break;
  273.         case 3:
  274.             PORT_CS &= ~CS_1;
  275.             break;
  276.     }
  277. }
  278.  
  279. /* The interrupt code here
  280.  ****************************************************************************/
  281. /**
  282.  * Timer0 -- Compare Match on OCR0A every 1ms
  283.  * Scan and update global key states
  284.  * Update Multiplexed 7-segment display
  285.  */
  286. ISR(TIMER0_COMPA_vect, ISR_NOBLOCK)
  287. {
  288.     static uint8_t tick_acc;
  289.  
  290.     /* 62.5hz full refresh */
  291.     if (!(tick_acc % 4))
  292.         display_update_sequence();
  293.    
  294.     /* Keys are active low, poll every 16ms*/
  295.     if (!(tick_acc % 16)) {
  296.         g_keystate.set_state = (!(PIN_SETK & SETK_MASK));
  297.         g_keystate.plus_state = (!(PIN_PLUSK & PLUSK_MASK));
  298.     }
  299.  
  300.         ++tick_acc;
  301. }
  302.  
  303. /**
  304.  * Timer1 -- Compare Match on OCR1A
  305.  * 86400/65536s tick
  306.  */
  307. ISR(TIMER1_COMPA_vect)
  308. {
  309.         static uint8_t tick_acc;
  310.         static uint8_t slipped;
  311.  
  312.     /*
  313.          * Every 4 ticks, slip clock compare register forward, a flag is set to
  314.          * indicate whether the clock has been slipped so the register can be
  315.          * reset to default but avoid doing this unless actually necessary.
  316.         */
  317.      if (tick_acc % 4) {
  318.             if (slipped) {
  319.                     OCR1A = OCR1A_VAL;
  320.                     slipped = 0;
  321.             }
  322.         } else {
  323.             // add to next compare match val to offset effects of clock drift
  324.             OCR1A += OCR1A_SLIP;
  325.             slipped = 1;
  326.         }
  327.  
  328.  
  329.     // Increment global time
  330.     ++g_time;
  331.  
  332.     ++tick_acc;
  333. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement