1 | //#include <inttypes.h>
|
2 | //#include <stdlib.h>
|
3 | //#include <string.h>
|
4 | #include <avr/io.h>
|
5 | #include <avr/interrupt.h>
|
6 | //#include <avr/eeprom.h>
|
7 | //#include <util/delay.h>
|
8 | #include <avr/pgmspace.h>
|
9 | #include "font.h"
|
10 |
|
11 | // Change these values to adjust scroll speeds and animation iterations
|
12 | #define ANIMATION_SCROLL_SPEED 200 // how fast to scroll the animations
|
13 | #define ANIMATION_SPEED 140 // how fast to change to next picture
|
14 | #define TEXT_SCROLL_SPEED 120 // how fast to scrill the text (wait)
|
15 | #define REPEAT_ANIMATION 2 // how often to repeat the animation if in cycling mode
|
16 | #define REPEAT_TEXT 1 // how often to repeat the text if in cycling mode
|
17 |
|
18 | // How to add a new message:
|
19 | // * add the new message (only upper case, see font.h)
|
20 | // * adjust MAX_MESSAGES
|
21 | // * add the new message to messages
|
22 | // NOTE: messages may not be longer than 255 chars.
|
23 | const prog_char message_00[] = " ! MERRY CHRISTMAS AND A HAPPY NEW YEAR ! ";
|
24 | const prog_char message_01[] = " COUNTDOWN...";
|
25 | const prog_char message_02[] = " 5 4 3 2 1 ... BOOM!! ";
|
26 |
|
27 | #define MAX_MESSAGES 3
|
28 | PGM_P PROGMEM messages[] = {
|
29 | message_00
|
30 | ,message_01
|
31 | ,message_02
|
32 | };
|
33 |
|
34 | //#define MAX_ANIMATIONS 1
|
35 |
|
36 | const uint8_t dancer1[][8] PROGMEM = {
|
37 | { 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}, // fight
|
38 | { 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}, // fight
|
39 | { 0x00, 0x01, 0x0B, 0x01, 0x00, 0x00, 0x00, 0x00}, // fight
|
40 | { 0x00, 0x01, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00}, // fight
|
41 | { 0x00, 0x01, 0x23, 0x01, 0x00, 0x00, 0x00, 0x00}, // fight
|
42 | { 0x00, 0x00, 0x41, 0x03, 0x01, 0x00, 0x00, 0x00}, // fight
|
43 | { 0x00, 0x00, 0x81, 0x03, 0x01, 0x00, 0x00, 0x00}, // fight
|
44 | { 0x00, 0x00, 0x00, 0x01, 0x0B, 0x01, 0x00, 0x00}, // fight
|
45 | { 0x00, 0x00, 0x00, 0x01, 0x13, 0x01, 0x00, 0x00}, // fight
|
46 | { 0x00, 0x00, 0x00, 0x01, 0x23, 0x01, 0x00, 0x00}, // fight
|
47 | { 0x00, 0x00, 0x00, 0x01, 0x43, 0x01, 0x00, 0x00}, // fight
|
48 | { 0x00, 0x00, 0x00, 0x01, 0x83, 0x01, 0x00, 0x00}, // fight
|
49 | { 0x40, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00}, // fight
|
50 | { 0xE0, 0x40, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00}, // fight
|
51 | { 0xE0, 0xE0, 0x41, 0x03, 0x01, 0x00, 0x00, 0x00}, // fight
|
52 | { 0x40, 0xE1, 0xE3, 0x41, 0x00, 0x00, 0x00, 0x00}, // fight
|
53 | { 0x00, 0x41, 0xEB, 0xE1, 0x40, 0x00, 0x00, 0x00}, // fight
|
54 | { 0x00, 0x41, 0xF3, 0xE1, 0x40, 0x00, 0x00, 0x00}, // fight
|
55 | { 0x00, 0x41, 0xC3, 0xE1, 0x40, 0x00, 0x00, 0x00}, // fight
|
56 | { 0x00, 0x41, 0xC3, 0x81, 0x50, 0x00, 0x00, 0x00}, // fight
|
57 | { 0x40, 0x01, 0x83, 0x81, 0x10, 0x40, 0x00, 0x00}, // fight
|
58 | { 0x00, 0x81, 0x03, 0x01, 0x00, 0x08, 0x80, 0x00}, // fight
|
59 | { 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}, // fight
|
60 |
|
61 |
|
62 | };
|
63 |
|
64 |
|
65 | //uint8_t mode_ee EEMEM = 0; // stores the mode in eeprom
|
66 | uint8_t screen_mem[8]; // screen memory
|
67 | uint8_t active_row=1; // active row
|
68 | uint8_t message_ptr = 0; // points to the active char in the message
|
69 | uint8_t message_displayed = 0; // how often has the message been displayed?
|
70 | uint8_t active_char = 0; // stores the active char
|
71 | uint8_t message_length = 0; // stores the length of the active message
|
72 | uint8_t char_ptr = 0; // points to the active col in the char
|
73 | uint8_t char_length = 0; // stores the length of the active char
|
74 | static volatile uint16_t counter = 0; // used for delay function
|
75 |
|
76 |
|
77 | // prototypes
|
78 | void delay_ms(uint16_t delay);
|
79 | void copy_to_display(int8_t x, int8_t y, uint8_t sprite[]);
|
80 | void display_active_row(void);
|
81 | void show_char();
|
82 | //void clear_screen(void);
|
83 | void copy_to_buffer(const prog_uint8_t sprite[8]);
|
84 | //void scroll_animation(const prog_uint8_t sprite_1[], const prog_uint8_t sprite_2[], uint8_t flash, uint16_t flash);
|
85 |
|
86 | /*
|
87 | * ISR TIMER0_OVF_vect
|
88 | * Handles overflow interrupts of timer 0.
|
89 | *
|
90 | * 4MHz
|
91 | * ----
|
92 | * Prescaler 8 ==> 1953.1 Hz
|
93 | * Complete display = 244 Hz
|
94 | *
|
95 | */
|
96 |
|
97 |
|
98 | /*
|
99 | * display_active_col
|
100 | * Deactivates the active column and displays the next one.
|
101 | * Data is read from screen_mem.
|
102 | *
|
103 | * ATtiny2313
|
104 | * 16 - PD0 PB7 - 1
|
105 | * 15 - PD1 PB6 - 2
|
106 | * 14 - PA1 PB5 - 3
|
107 | * 13 - PA0 PB4 - 4
|
108 | * 12 - PD2 PB3 - 5
|
109 | * 11 - PD3 PB2 - 6
|
110 | * 10 - PD4 PB1 - 7
|
111 | * 9 - PD5 PB0 - 8
|
112 | *
|
113 | * NFM-12883 common anode |
|
114 | * A0B5B4D4B2D3D1D0 +-----+
|
115 | * PD5 o o o o o o o o | |
|
116 | * PA1 o o o o o o o o _+_ |
|
117 | * PB0 o o o o o o o o \ / |
|
118 | * PD2 o o o o o o o o __V__ |
|
119 | * PB7 o o o o o o o o | |
|
120 | * PB1 o o o o o o o o ---+-----C---
|
121 | * PB6 o o o o o o o o |
|
122 | * PB3 o o o o o o o o
|
123 | *
|
124 | * if you have a common cathode matrix rotate your pin layout 90 Degree
|
125 | * switch row with column
|
126 | */
|
127 |
|
128 |
|
129 | ISR(TIMER0_OVF_vect) {
|
130 |
|
131 | uint8_t row=0,i;
|
132 |
|
133 | // timer for delay
|
134 | counter++;
|
135 |
|
136 | // shut down all rows and columns
|
137 | PORTA = (0 << PB3) | (1 << PB5);
|
138 | PORTB = (0 << PB6) | (0 << PB1) | (0 << PD2) | (1 << PB4) |
|
139 | (1 << PB2) | (1 << PD3) | (1 << PD1) | (1 << PD0);
|
140 | PORTD = (0 << PB7) | (0 << PB0) | (0 << PA1) | (0 << PD5) |
|
141 | (1 << PA0) | (1 << PD4) | (1 << PD1);
|
142 |
|
143 | // next row (1,2,4,8,16,32..)
|
144 | active_row = (active_row << 7) | (active_row >> 1);
|
145 |
|
146 | //row = screen_mem[active_row];
|
147 | for (i=0; i<8; i++) {
|
148 | row <<= 1;
|
149 | if (screen_mem[i] & active_row) {
|
150 | row++ ;
|
151 | }
|
152 | }
|
153 |
|
154 | // output all columns, switch leds on.
|
155 | // column 1
|
156 | if ((row & 0x80) == 0x80) {
|
157 | PORTA |= (1 << PA0);
|
158 | }
|
159 | // column 2
|
160 | if ((row & 0x40) == 0x40) {
|
161 | PORTB |= (1 << PB5);
|
162 | }
|
163 | // column 3
|
164 | if ((row & 0x20) == 0x20) {
|
165 | PORTB |= (1 << PB4);
|
166 | }
|
167 | // column 4
|
168 | if ((row & 0x10) == 0x10) {
|
169 | PORTD |= (1 << PD4);
|
170 | }
|
171 | // column 5
|
172 | if ((row & 0x08) == 0x08) {
|
173 | PORTB |= (1 << PB2);
|
174 | }
|
175 | // column 6
|
176 | if ((row & 0x04) == 0x04) {
|
177 | PORTD |= (1 << PD3);
|
178 | }
|
179 | // column 7
|
180 | if ((row & 0x02) == 0x02) {
|
181 | PORTD |= (1 << PD1);
|
182 | }
|
183 | // column 8
|
184 | if ((row & 0x01) == 0x01) {
|
185 | PORTD |= (1 << PD0);
|
186 | }
|
187 |
|
188 | //activate row
|
189 | if (active_row==0x80) PORTD &= ~(1 << PD5);
|
190 | if (active_row==0x40) PORTA &= ~(1 << PA1);
|
191 | if (active_row==0x20) PORTB &= ~(1 << PB0);
|
192 | if (active_row==0x10) PORTD &= ~(1 << PD2);
|
193 | if (active_row==0x08) PORTB &= ~(1 << PB7);
|
194 | if (active_row==0x04) PORTB &= ~(1 << PB1);
|
195 | if (active_row==0x02) PORTB &= ~(1 << PB6);
|
196 | if (active_row==0x01) PORTB &= ~(1 << PB3);
|
197 |
|
198 | }
|
199 |
|
200 |
|
201 |
|
202 | /*
|
203 | * delay_ms
|
204 | * Uses the counter that is incremented by the ISR.
|
205 | * Max delay is 32767ms.
|
206 | */
|
207 | void delay_ms(uint16_t delay) {
|
208 | //while (!(PIND & (1 << PD6))) {} // used to stop the animation when PD6 goes LOW
|
209 | uint16_t t = delay * 2;
|
210 | counter = 0;
|
211 | while (counter < t) {}
|
212 | }
|
213 |
|
214 |
|
215 | /*
|
216 | void copy_to_display(int8_t x, int8_t y, uint8_t sprite[8]) {
|
217 | int8_t i, t;
|
218 | uint8_t column;
|
219 | clear_screen();
|
220 | for (i = 0; i < 8; i++) {
|
221 | t = i-y;
|
222 | column = ((t >= 0) && (t < 8)) ? sprite[t] : 0x00;
|
223 | column = (x >= 0) ? (column >> x) : (column << -x);
|
224 |
|
225 | screen_mem[i]=column;
|
226 | }
|
227 | }
|
228 | */
|
229 |
|
230 | void show_char(const prog_uint8_t string[]) {
|
231 | uint8_t i;
|
232 | uint8_t b;
|
233 |
|
234 | // shift the screen to the left
|
235 | for (i = 0; i < 7; i++) {
|
236 | screen_mem[i] = screen_mem[i+1];
|
237 | }
|
238 | // advance a char if needed
|
239 | if (char_length == 0x80) {
|
240 |
|
241 | char_length =0; //reset stop bit
|
242 |
|
243 | //read next char from progmem
|
244 | memcpy_P(&active_char,&string[message_ptr],1);
|
245 | message_ptr++;
|
246 |
|
247 | //string stop byte 0x00
|
248 | if (active_char == 0) {
|
249 | message_ptr = 0;
|
250 | message_displayed++;
|
251 | char_length =0x80; // immediately read next char
|
252 | }
|
253 |
|
254 | active_char -= CHAR_OFFSET;
|
255 | char_ptr = 0;
|
256 |
|
257 | // this makes the space between two chars
|
258 | screen_mem[7]=0;
|
259 | return;
|
260 | }
|
261 |
|
262 | // read pixels for current column of char
|
263 | b = pgm_read_byte(&font[active_char * 5 + char_ptr]);
|
264 | char_ptr++;
|
265 | char_length= (b & 0x80);
|
266 | b = (b & 0x7F);
|
267 | // write pixels into screen memory
|
268 | screen_mem[7] =b;
|
269 | }
|
270 |
|
271 |
|
272 | /*
|
273 | * copy_to_buffer
|
274 | * Copies the given sprite from PROGMEM to 8x8 LED RAM.
|
275 | */
|
276 | void copy_to_buffer(const prog_uint8_t sprite[8]) {
|
277 | memcpy_P(screen_mem, sprite, 8);
|
278 | }
|
279 |
|
280 |
|
281 | /*
|
282 | * scroll_animation
|
283 | * Uses sprite_1 and sprite_2 to draw a simple animation.
|
284 | * blink=1 : change sprite_1/2 every step // blink=0 : blink only in the middle
|
285 | * scroll_speed: factor time for next step
|
286 | */
|
287 | void scroll_animation(const prog_uint8_t sprite_1[8], const prog_uint8_t sprite_2[8], uint8_t blink) {
|
288 | uint8_t i,j;
|
289 | int8_t x,z;
|
290 | for (i = 0; i < REPEAT_ANIMATION; i++) {
|
291 | //you can scroll from right with (x = -8; x < 8; x++)
|
292 | for (x = 8; x > -9; x--) {
|
293 | //move sprite 1
|
294 | for (j = 0; j < 8; j++) {
|
295 | screen_mem[j] = 0x00;
|
296 | z = x+j;
|
297 | if ((z>=0) && (z<8)) {
|
298 | if (blink & x) {
|
299 | memcpy_P(&screen_mem[j],&sprite_2[z],1);
|
300 | }
|
301 | else {
|
302 | memcpy_P(&screen_mem[j],&sprite_1[z],1);
|
303 | }
|
304 | }
|
305 | }
|
306 | delay_ms(ANIMATION_SCROLL_SPEED);
|
307 |
|
308 | if (x==0) {
|
309 | //in the middle shift betwenn sprite_1 and sprite_2
|
310 | for (j=0; j<4;j++) {
|
311 | copy_to_buffer(sprite_2);
|
312 | delay_ms(ANIMATION_SPEED);
|
313 | copy_to_buffer(sprite_1);
|
314 | delay_ms(ANIMATION_SPEED);
|
315 | }
|
316 | }
|
317 | }
|
318 | }
|
319 | }
|
320 |
|
321 | /*
|
322 | * step_animation
|
323 | * Uses sprite_1 to draw a simple animation.
|
324 | * size of array
|
325 | */
|
326 | void step_animation(const prog_uint8_t sprite_1[][8], uint8_t size) {
|
327 | uint8_t i;
|
328 | for (i = 0; i < size; i++) {
|
329 | copy_to_buffer(sprite_1[i]); //array: step pointer by 8
|
330 | delay_ms(ANIMATION_SPEED);
|
331 | }
|
332 | }
|
333 |
|
334 |
|
335 | int main(void) {
|
336 |
|
337 | int8_t i,j,k;
|
338 |
|
339 | // timer 0 setup, prescaler 8
|
340 | TCCR0B |= (1 << CS01);
|
341 |
|
342 | // enable timer 0 interrupt
|
343 | TIMSK |= (1 << TOIE0);
|
344 |
|
345 | // define outputs
|
346 | DDRA |= 0x03;
|
347 | DDRB |= 0xFF;
|
348 | DDRD |= 0x3F;
|
349 |
|
350 | /*
|
351 | // killed by code size
|
352 | // read last mode from eeprom
|
353 | // 0 mean cycle through all modes and messages
|
354 | mode = eeprom_read_byte(&mode_ee);
|
355 | if ((mode == 0) || (mode >= (MAX_ANIMATIONS + MAX_MESSAGES + 1))) {
|
356 | mode = 4;
|
357 | }
|
358 | eeprom_write_byte(&mode_ee, mode + 1);
|
359 | */
|
360 | sei();
|
361 |
|
362 | while (1) {
|
363 |
|
364 | for (i = 0; i < REPEAT_ANIMATION; i++) {
|
365 | step_animation(dancer1[0], 23);
|
366 | }
|
367 |
|
368 | }
|
369 |
|
370 | return 0;
|
371 |
|
372 | }
|