View difference between Paste ID: 8Agr5eSP and K8sjHydz
SHOW: | | - or go back to the newest paste.
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
}