| 1 | #include <avr/io.h>
 | 
| 2 | #include <avr/pgmspace.h>
 | 
| 3 | 
 | 
| 4 | #ifndef FONT_H_
 | 
| 5 | #define FONT_H_
 | 
| 6 | 
 | 
| 7 | #define MAX_CHARS 59
 | 
| 8 | #define CHAR_OFFSET 0x20
 | 
| 9 | 
 | 
| 10 | const uint8_t font[] PROGMEM = {
 | 
| 11 |   // 3 chars bitmap, 1 char length
 | 
| 12 |    0x00, 0x00, 0x00, 0x01 ,    // 0x20, 32, ' '  
 | 
| 13 |    0x17, 0x00, 0x00, 0x01 ,    // 0x21, 33, !  
 | 
| 14 |    0x03, 0x00, 0x03, 0x03 ,    // 0x22, 34, "  
 | 
| 15 |    0x00, 0x00, 0x00, 0x03 ,    // 0x23, 35, #  
 | 
| 16 |    0x00, 0x00, 0x00, 0x03 ,    // 0x24, 36, $  
 | 
| 17 |    0x00, 0x00, 0x00, 0x03 ,    // 0x25, 37, %  
 | 
| 18 |    0x00, 0x00, 0x00, 0x03 ,    // 0x26, 38, &  
 | 
| 19 |    0x03, 0x00, 0x00, 0x01 ,    // 0x27, 39, '  
 | 
| 20 |    0x0e, 0x11, 0x00, 0x02 ,    // 0x28, 40, (  
 | 
| 21 |    0x11, 0x0e, 0x00, 0x02 ,    // 0x29, 41, )  
 | 
| 22 |    0x00, 0x00, 0x00, 0x03 ,    // 0x2A, 42, *  
 | 
| 23 |    0x08, 0x1c, 0x08, 0x03 ,    // 0x2B, 43, +  
 | 
| 24 |    0x18, 0x00, 0x00, 0x01 ,    // 0x2C, 44, ,  
 | 
| 25 |    0x04, 0x04, 0x00, 0x03 ,    // 0x2D, 45, -  
 | 
| 26 |    0x10, 0x00, 0x00, 0x01 ,    // 0x2E, 46, .  
 | 
| 27 |    0x18, 0x04, 0x03, 0x03 ,    // 0x2F, 47, /  
 | 
| 28 |    0x1f, 0x11, 0x1f, 0x03 ,    // 0x30, 48, 0
 | 
| 29 |    0x01, 0x1f, 0x00, 0x02 ,    // 0x31, 49, 1
 | 
| 30 |    0x1d, 0x15, 0x17, 0x03 ,    // 0x32, 50, 2
 | 
| 31 |    0x15, 0x15, 0x0e, 0x03 ,    // 0x33, 51, 3
 | 
| 32 |    0x0f, 0x08, 0x1f, 0x03 ,    // 0x34, 52, 4
 | 
| 33 |    0x17, 0x15, 0x1d, 0x03 ,    // 0x35, 53, 5
 | 
| 34 |    0x1f, 0x12, 0x1e, 0x03 ,    // 0x36, 54, 6
 | 
| 35 |    0x11, 0x09, 0x07, 0x03 ,    // 0x37, 55, 7
 | 
| 36 |    0x1f, 0x15, 0x1f, 0x03 ,    // 0x38, 56, 8
 | 
| 37 |    0x0f, 0x09, 0x1f, 0x03 ,    // 0x39, 57, 9  
 | 
| 38 |    0x0a, 0x00, 0x00, 0x01 ,    // 0x3A, 58, :
 | 
| 39 |    0x1a, 0x00, 0x00, 0x01 ,    // 0x3B, 59, ;  
 | 
| 40 |    0x08, 0x14, 0x00, 0x02 ,    // 0x3C, 60, <  
 | 
| 41 |    0x14, 0x14, 0x00, 0x02 ,    // 0x3D, 61, =  
 | 
| 42 |    0x14, 0x08, 0x00, 0x02 ,    // 0x3E, 62, >  
 | 
| 43 |    0x15, 0x05, 0x02, 0x03 ,    // 0x3F, 63, ?  
 | 
| 44 |    0x0e, 0x17, 0x16, 0x03 ,    // 0x40, 64, @  
 | 
| 45 |    0x1f, 0x09, 0x1e, 0x03 ,    // 0x41, 65, A
 | 
| 46 |    0x1f, 0x15, 0x0a, 0x03 ,    // 0x42, 66, B
 | 
| 47 |    0x0e, 0x11, 0x11, 0x03 ,    // 0x43, 67, C
 | 
| 48 |    0x1f, 0x11, 0x0e, 0x03 ,    // 0x44, 68, D
 | 
| 49 |    0x0e, 0x15, 0x15, 0x03 ,    // 0x45, 69, E
 | 
| 50 |    0x1e, 0x05, 0x05, 0x03 ,    // 0x46, 70, F
 | 
| 51 |    0x0e, 0x11, 0x1d, 0x03 ,    // 0x47, 71, G
 | 
| 52 |    0x1f, 0x04, 0x1f, 0x03 ,    // 0x48, 72, H
 | 
| 53 |    0x1f, 0x00, 0x00, 0x01 ,    // 0x49, 73, I
 | 
| 54 |    0x08, 0x10, 0x0f, 0x03 ,    // 0x4A, 74, J
 | 
| 55 |    0x1f, 0x04, 0x1b, 0x03 ,    // 0x4B, 75, K
 | 
| 56 |    0x0f, 0x10, 0x10, 0x03 ,    // 0x4C, 76, L
 | 
| 57 |    0x1f, 0x02, 0x1f, 0x03 ,    // 0x4D, 77, M
 | 
| 58 |    0x1f, 0x01, 0x1e, 0x03 ,    // 0x4E, 78, N
 | 
| 59 |    0x0e, 0x11, 0x0e, 0x03 ,    // 0x4F, 79, O
 | 
| 60 |    0x1f, 0x09, 0x06, 0x03 ,    // 0x50, 80, P
 | 
| 61 |    0x06, 0x19, 0x06, 0x03 ,    // 0x51, 81, Q
 | 
| 62 |    0x1f, 0x09, 0x16, 0x03 ,    // 0x52, 82, R
 | 
| 63 |    0x12, 0x15, 0x09, 0x03 ,    // 0x53, 83, S
 | 
| 64 |    0x01, 0x1f, 0x01, 0x03 ,    // 0x54, 84, T
 | 
| 65 |    0x0f, 0x10, 0x0f, 0x03 ,    // 0x55, 85, U
 | 
| 66 |    0x1f, 0x10, 0x0f, 0x03 ,    // 0x56, 86, V
 | 
| 67 |    0x1f, 0x08, 0x1f, 0x03 ,    // 0x57, 87, W
 | 
| 68 |    0x1b, 0x04, 0x1b, 0x03 ,    // 0x58, 88, X
 | 
| 69 |    0x03, 0x1c, 0x03, 0x03 ,    // 0x59, 89, Y
 | 
| 70 |    0x19, 0x15, 0x13, 0x03 ,    // 0x5A, 90, Z
 | 
| 71 |    0x1f, 0x11, 0x00, 0x02 ,    // 0x5B, 91, [
 | 
| 72 |    0x03, 0x04, 0x18, 0x03 ,    // 0x5C, 92, 
 | 
| 73 |    0x11, 0x1f, 0x00, 0x02 ,    // 0x5D, 93, [
 | 
| 74 |    0x11, 0x1f, 0x00, 0x02 ,    // 0x5E, 94, ^
 | 
| 75 |    0x1f, 0x1f, 0x1f, 0x03     // 0x5F, 95, _, used as block, all on
 | 
| 76 | };
 | 
| 77 | 
 | 
| 78 | /* -----------------------------------------------------------------------
 | 
| 79 |  * Title:    8x8 LED dot matrix animations
 | 
| 80 |  * Author:   Alexander Weber alex@tinkerlog.com
 | 
| 81 |  * Date:     21.12.2008
 | 
| 82 |  * Hardware: ATtiny2313V
 | 
| 83 |  * Software: AVRMacPack
 | 
| 84 |  * 
 | 
| 85 |  */
 | 
| 86 | 
 | 
| 87 | #include <inttypes.h>
 | 
| 88 | #include <stdlib.h>
 | 
| 89 | #include <string.h>
 | 
| 90 | #include <avr/io.h>
 | 
| 91 | #include <avr/interrupt.h>
 | 
| 92 | #include <avr/eeprom.h>
 | 
| 93 | #include <util/delay.h>
 | 
| 94 | #include <avr/pgmspace.h>
 | 
| 95 | #include "font.h"
 | 
| 96 | 
 | 
| 97 | // Change these values to adjust scroll speeds and animation iterations
 | 
| 98 | #define ANIMATION_SCROLL_SPEED 80  // how fast to scroll the animations
 | 
| 99 | #define TEXT_SCROLL_SPEED 120      // how fast to scrill the text
 | 
| 100 | #define REPEAT_ANIMATION 10        // how often to repeat the animation if in cycling mode
 | 
| 101 | #define REPEAT_TEXT 5              // how often to repeat the text if in cycling mode
 | 
| 102 | #define F_CPU
 | 
| 103 | // How to add a new message:
 | 
| 104 | // * add the new message (only upper case, see font.h)
 | 
| 105 | // * adjust MAX_MESSAGES
 | 
| 106 | // * add the new message to messages
 | 
| 107 | // NOTE: messages may not be longer than 59 chars. Otherwise they will not fit in the buffer.
 | 
| 108 | //                                              123456789012345678901234567890123456789012345678901234567890
 | 
| 109 | const prog_char PROGMEM message_00[] PROGMEM = "   WTF!?! ";
 | 
| 110 | const prog_char PROGMEM message_01[] PROGMEM = "   I AM NO BOMB! ";
 | 
| 111 | const prog_char PROGMEM message_02[] PROGMEM = "   5   4   3   2   1  ...  BOOM! ";
 | 
| 112 | const prog_char PROGMEM message_03[] PROGMEM = "   I'M SORRY DAVE, I'M AFRAID I CAN'T DO THAT.  ";
 | 
| 113 | const prog_char PROGMEM message_04[] PROGMEM = "   NOW BYE ME A SOLDERING STATION ";
 | 
| 114 | const prog_char PROGMEM message_05[] PROGMEM = "   MAKE STUFF ";
 | 
| 115 | const prog_char PROGMEM message_06[] PROGMEM = "   IF YOU CAN'T OPEN IT, YOU DON'T OWN IT ";
 | 
| 116 | const prog_char PROGMEM message_07[] PROGMEM = "   1337 3L3X7RON!C5 !1!! ";
 | 
| 117 | const prog_char PROGMEM message_08[] PROGMEM = "   MY KUNG FU IS BETTER THAN YOURS ";
 | 
| 118 | const prog_char PROGMEM message_09[] PROGMEM = "   SUDO MAKE ME A SANDWICH ";
 | 
| 119 | const prog_char PROGMEM message_10[] PROGMEM = "   ZOMBIES AHEAD ";
 | 
| 120 | const prog_char PROGMEM message_11[] PROGMEM = "   HTTP://TINKERLOG.COM ";
 | 
| 121 | 
 | 
| 122 | #define MAX_MESSAGES 12
 | 
| 123 | PGM_P PROGMEM messages[] = {
 | 
| 124 |   message_00
 | 
| 125 |   ,message_01
 | 
| 126 |   ,message_02
 | 
| 127 |   ,message_03
 | 
| 128 |   ,message_04
 | 
| 129 |   ,message_05
 | 
| 130 |   ,message_06
 | 
| 131 |   ,message_07
 | 
| 132 |   ,message_08
 | 
| 133 |   ,message_09
 | 
| 134 |   ,message_10
 | 
| 135 |   ,message_11
 | 
| 136 | }; 
 | 
| 137 | 
 | 
| 138 | #define MAX_ANIMATIONS 3
 | 
| 139 | const prog_uint8_t PROGMEM sprite_00[] = 
 | 
| 140 |   {
 | 
| 141 |     0x18,    // ___XX___
 | 
| 142 |     0x3C,    // __XXXX__
 | 
| 143 |     0x7E,    // _XXXXXX_
 | 
| 144 |     0xDB,    // X_XXXX_X
 | 
| 145 |     0xFF,    // XXXXXXXX
 | 
| 146 |     0x24,    // __X__X__
 | 
| 147 |     0x5A,    // _X_XX_X_
 | 
| 148 |     0xA5     // X_X__X_X
 | 
| 149 |   };
 | 
| 150 | 
 | 
| 151 | const prog_uint8_t PROGMEM sprite_01[8] =
 | 
| 152 |   {
 | 
| 153 |     0x18,    // ___XX___ 
 | 
| 154 |     0x3C,    // __XXXX__
 | 
| 155 |     0x7E,    // _XXXXXX_
 | 
| 156 |     0xDB,    // X_XXXX_X
 | 
| 157 |     0xFF,    // XXXXXXXX
 | 
| 158 |     0x24,    // __X__X__
 | 
| 159 |     0x42,    // _X____X_
 | 
| 160 |     0x24     // __X__X__
 | 
| 161 |   };
 | 
| 162 | const prog_uint8_t PROGMEM sprite_02[8] =
 | 
| 163 |   {
 | 
| 164 |     0x00,    // ________ 
 | 
| 165 |     0x00,    // ________
 | 
| 166 |     0x14,    // ___X_X__
 | 
| 167 |     0x3E,    // __XXXXX_
 | 
| 168 |     0x3E,    // __XXXXX_
 | 
| 169 |     0x1C,    // ___XXX__
 | 
| 170 |     0x08,    // ____X___
 | 
| 171 |     0x00     // ________
 | 
| 172 |   };
 | 
| 173 | 
 | 
| 174 | const prog_uint8_t PROGMEM sprite_03[8] =
 | 
| 175 |   {
 | 
| 176 |     0x00,    // ________ 
 | 
| 177 |     0x66,    // _XX__XX_
 | 
| 178 |     0xFF,    // XXXXXXXX
 | 
| 179 |     0xFF,    // XXXXXXXX
 | 
| 180 |     0xFF,    // XXXXXXXX
 | 
| 181 |     0x7E,    // _XXXXXX_
 | 
| 182 |     0x3C,    // __XXXX__
 | 
| 183 |     0x18     // ___XX___
 | 
| 184 |   };
 | 
| 185 | 
 | 
| 186 | const prog_uint8_t PROGMEM sprite_04[8] =
 | 
| 187 |   {
 | 
| 188 |     0x24,    // __X__X__
 | 
| 189 |     0x7E,    // _XXXXXX_
 | 
| 190 |     0xDB,    // XX_XX_XX
 | 
| 191 |     0xFF,    // XXXXXXXX
 | 
| 192 |     0xA5,    // X_X__X_X
 | 
| 193 |     0x99,    // X__XX__X
 | 
| 194 |     0x81,    // X______X
 | 
| 195 |     0xC3     // XX____XX
 | 
| 196 |   };
 | 
| 197 | 
 | 
| 198 | const prog_uint8_t PROGMEM sprite_05[8] =
 | 
| 199 |   {
 | 
| 200 |     0x24,    // __X__X__
 | 
| 201 |     0x18,    // ___XX___
 | 
| 202 |     0x7E,    // X_XXXX_X
 | 
| 203 |     0xDB,    // XX_XX_XX
 | 
| 204 |     0xFF,    // XXXXXXXX
 | 
| 205 |     0xDB,    // X_XXXX_X
 | 
| 206 |     0x99,    // X__XX__X
 | 
| 207 |     0xC3     // XX____XX
 | 
| 208 |   };
 | 
| 209 | 
 | 
| 210 | 
 | 
| 211 | 
 | 
| 212 | uint8_t mode_ee EEMEM = 0;                      // stores the mode in eeprom
 | 
| 213 | static uint8_t screen_mem[8];      // screen memory
 | 
| 214 | static uint8_t active_row;      // active row
 | 
| 215 | static uint8_t buffer[60];                      // stores the active message or sprite
 | 
| 216 | static uint8_t message_ptr = 0;                 // points to the active char in the message
 | 
| 217 | static uint8_t message_displayed = 0;           // how often has the message been displayed?
 | 
| 218 | static uint8_t active_char = 0;                 // stores the active char
 | 
| 219 | static uint8_t message_length = 0;              // stores the length of the active message
 | 
| 220 | static uint8_t char_ptr = 0;                    // points to the active col in the char
 | 
| 221 | static uint8_t char_length = 0;                 // stores the length of the active char
 | 
| 222 | static volatile uint16_t counter = 0;           // used for delay function
 | 
| 223 | 
 | 
| 224 | // prototypes
 | 
| 225 | void delay_ms(uint16_t delay);
 | 
| 226 | void copy_to_display(int8_t x, int8_t y, uint8_t sprite[]);
 | 
| 227 | void display_active_row(void);
 | 
| 228 | void show_char();
 | 
| 229 | void clear_screen(void);
 | 
| 230 | void copy_to_buffer(const prog_uint8_t sprite[8]);
 | 
| 231 | void scroll_animation(const prog_uint8_t sprite_1[], const prog_uint8_t sprite_2[]);
 | 
| 232 | 
 | 
| 233 | 
 | 
| 234 | 
 | 
| 235 | /*
 | 
| 236 |  * ISR TIMER0_OVF_vect
 | 
| 237 |  * Handles overflow interrupts of timer 0.
 | 
| 238 |  *
 | 
| 239 |  * 4MHz
 | 
| 240 |  * ----
 | 
| 241 |  * Prescaler 8 ==> 1953.1 Hz
 | 
| 242 |  * Complete display = 244 Hz
 | 
| 243 |  *
 | 
| 244 |  */
 | 
| 245 | ISR(TIMER0_OVF_vect) {  
 | 
| 246 |   display_active_row();
 | 
| 247 |   counter++;
 | 
| 248 | }
 | 
| 249 | 
 | 
| 250 | 
 | 
| 251 | 
 | 
| 252 | /*
 | 
| 253 |  * delay_ms
 | 
| 254 |  * Uses the counter that is incremented by the ISR.
 | 
| 255 |  * Max delay is 32767ms.
 | 
| 256 |  */
 | 
| 257 | void delay_ms(uint16_t delay) {
 | 
| 258 |   while (!(PIND & (1 << PD6))) {}   // used to stop the animation when PD6 goes LOW
 | 
| 259 |   uint16_t t = delay * 2;
 | 
| 260 |   counter = 0;
 | 
| 261 |   while (counter < t) {}
 | 
| 262 | }
 | 
| 263 | 
 | 
| 264 | 
 | 
| 265 | 
 | 
| 266 | /*
 | 
| 267 |  * copy_to_display
 | 
| 268 |  * Copies sprite data to the screen memory at the given position. 
 | 
| 269 |  */
 | 
| 270 | void copy_to_display(int8_t x, int8_t y, uint8_t sprite[8]) {
 | 
| 271 |   int8_t i, t;
 | 
| 272 |   uint8_t row;
 | 
| 273 |   for (i = 0; i < 8; i++) {
 | 
| 274 |     t = i-y;
 | 
| 275 |     row = ((t >= 0) && (t < 8)) ? sprite[t] : 0x00;
 | 
| 276 |     row = (x >= 0) ? (row >> x) : (row << -x);
 | 
| 277 |     screen_mem[i] = row;
 | 
| 278 |   }
 | 
| 279 | }
 | 
| 280 | 
 | 
| 281 | 
 | 
| 282 | 
 | 
| 283 | /*
 | 
| 284 |  * display_active_col
 | 
| 285 |  * Deactivates the active column and displays the next one.
 | 
| 286 |  * Data is read from screen_mem.
 | 
| 287 |  *
 | 
| 288 |  *      ATtiny2313
 | 
| 289 |  * 16 - PD0    PB7 - 1
 | 
| 290 |  * 15 - PD1    PB6 - 2
 | 
| 291 |  * 14 - PA1    PB5 - 3
 | 
| 292 |  * 13 - PA0    PB4 - 4
 | 
| 293 |  * 12 - PD2    PB3 - 5
 | 
| 294 |  * 11 - PD3    PB2 - 6
 | 
| 295 |  * 10 - PD4    PB1 - 7
 | 
| 296 |  *  9 - PD5    PB0 - 8
 | 
| 297 |  *
 | 
| 298 |  * NFM-12883 common anode          |
 | 
| 299 |  *     A0B5B4D4B2D3D1D0      +-----+
 | 
| 300 |  * PD5 o o o o o o o o       |     |
 | 
| 301 |  * PA1 o o o o o o o o      _+_    |
 | 
| 302 |  * PB0 o o o o o o o o      \ /    |
 | 
| 303 |  * PD2 o o o o o o o o     __V__   |
 | 
| 304 |  * PB7 o o o o o o o o       |     |
 | 
| 305 |  * PB1 o o o o o o o o    ---+-----C---
 | 
| 306 |  * PB6 o o o o o o o o             |
 | 
| 307 |  * PB3 o o o o o o o o
 | 
| 308 |  *
 | 
| 309 |  */
 | 
| 310 | void display_active_row(void) {
 | 
| 311 | 
 | 
| 312 |   uint8_t row;
 | 
| 313 | 
 | 
| 314 |   // shut down all rows and columns
 | 
| 315 |   PORTA = (0 << PA0) | (1 << PA1);
 | 
| 316 |   PORTB = (0 << PB5) | (0 << PB4) | (0 << PB2) | (1 << PB0) | 
 | 
| 317 |           (1 << PB7) | (1 << PB1) | (1 << PB6) | (1 << PB3);
 | 
| 318 |   PORTD = (0 << PD4) | (0 << PD3) | (0 << PD1) | (0 << PD0) |
 | 
| 319 |           (1 << PD5) | (1 << PD2) | (1 << PD6);
 | 
| 320 | 
 | 
| 321 |   // next row
 | 
| 322 |   active_row = (active_row+1) % 8;
 | 
| 323 |   row = screen_mem[active_row];
 | 
| 324 | 
 | 
| 325 |   // output all columns, switch leds on.
 | 
| 326 |   // column 1
 | 
| 327 |   if ((row & 0x80) == 0x80) {
 | 
| 328 |     PORTA |= (1 << PA0);    
 | 
| 329 |   }
 | 
| 330 |   // column 2
 | 
| 331 |   if ((row & 0x40) == 0x40) {
 | 
| 332 |     PORTB |= (1 << PB5);    
 | 
| 333 |   }
 | 
| 334 |   // column 3
 | 
| 335 |   if ((row & 0x20) == 0x20) {
 | 
| 336 |     PORTB |= (1 << PB4);    
 | 
| 337 |   }
 | 
| 338 |   // column 4
 | 
| 339 |   if ((row & 0x10) == 0x10) {
 | 
| 340 |     PORTD |= (1 << PD4);    
 | 
| 341 |   }
 | 
| 342 |   // column 5
 | 
| 343 |   if ((row & 0x08) == 0x08) {
 | 
| 344 |     PORTB |= (1 << PB2);    
 | 
| 345 |   }
 | 
| 346 |   // column 6
 | 
| 347 |   if ((row & 0x04) == 0x04) {
 | 
| 348 |     PORTD |= (1 << PD3);    
 | 
| 349 |   }
 | 
| 350 |   // column 7
 | 
| 351 |   if ((row & 0x02) == 0x02) {
 | 
| 352 |     PORTD |= (1 << PD1);    
 | 
| 353 |   }
 | 
| 354 |   // column 8
 | 
| 355 |   if ((row & 0x01) == 0x01) {
 | 
| 356 |     PORTD |= (1 << PD0);    
 | 
| 357 |   }
 | 
| 358 | 
 | 
| 359 |   // activate row
 | 
| 360 |   switch (active_row) {
 | 
| 361 |   case 0:
 | 
| 362 |     PORTD &= ~(1 << PD5);
 | 
| 363 |     break;
 | 
| 364 |   case 1:
 | 
| 365 |     PORTA &= ~(1 << PA1);
 | 
| 366 |     break;
 | 
| 367 |   case 2:
 | 
| 368 |     PORTB &= ~(1 << PB0);
 | 
| 369 |     break;
 | 
| 370 |   case 3:
 | 
| 371 |     PORTD &= ~(1 << PD2);
 | 
| 372 |     break;
 | 
| 373 |   case 4:
 | 
| 374 |     PORTB &= ~(1 << PB7);
 | 
| 375 |     break;
 | 
| 376 |   case 5:
 | 
| 377 |     PORTB &= ~(1 << PB1);
 | 
| 378 |     break;
 | 
| 379 |   case 6:
 | 
| 380 |     PORTB &= ~(1 << PB6);
 | 
| 381 |     break;
 | 
| 382 |   case 7:
 | 
| 383 |     PORTB &= ~(1 << PB3);
 | 
| 384 |     break;
 | 
| 385 |   }
 | 
| 386 | 
 | 
| 387 | }
 | 
| 388 | /*
 | 
| 389 |  * Use this method, if you have a common cathode matrix.
 | 
| 390 |  */
 | 
| 391 | /*
 | 
| 392 | void display_active_row(void) {
 | 
| 393 | 
 | 
| 394 |   uint8_t row;
 | 
| 395 | 
 | 
| 396 |   // shut down all rows and columns
 | 
| 397 |   PORTB = 0x34; 
 | 
| 398 |   PORTD = 0x1B; 
 | 
| 399 |   PORTA = 0x01;
 | 
| 400 | 
 | 
| 401 |   // next row
 | 
| 402 |   active_row = (active_row+1) % 8;
 | 
| 403 |   row = screen_mem[active_row];
 | 
| 404 | 
 | 
| 405 |   // output all columns, switch leds on.
 | 
| 406 |   // column 1
 | 
| 407 |   if ((row & 0x80) == 0x80) {
 | 
| 408 |     PORTA &= ~(1 << PA0);    
 | 
| 409 |   }
 | 
| 410 |   // column 2
 | 
| 411 |   if ((row & 0x40) == 0x40) {
 | 
| 412 |     PORTB &= ~(1 << PB5);    
 | 
| 413 |   }
 | 
| 414 |   // column 3
 | 
| 415 |   if ((row & 0x20) == 0x20) {
 | 
| 416 |     PORTB &= ~(1 << PB4);    
 | 
| 417 |   }
 | 
| 418 |   // column 4
 | 
| 419 |   if ((row & 0x10) == 0x10) {
 | 
| 420 |     PORTD &= ~(1 << PD4);    
 | 
| 421 |   }
 | 
| 422 |   // column 5
 | 
| 423 |   if ((row & 0x08) == 0x08) {
 | 
| 424 |     PORTB &= ~(1 << PB2);    
 | 
| 425 |   }
 | 
| 426 |   // column 6
 | 
| 427 |   if ((row & 0x04) == 0x04) {
 | 
| 428 |     PORTD &= ~(1 << PD3);    
 | 
| 429 |   }
 | 
| 430 |   // column 7
 | 
| 431 |   if ((row & 0x02) == 0x02) {
 | 
| 432 |     PORTD &= ~(1 << PD1);    
 | 
| 433 |   }
 | 
| 434 |   // column 8
 | 
| 435 |   if ((row & 0x01) == 0x01) {
 | 
| 436 |     PORTD &= ~(1 << PD0);    
 | 
| 437 |   }
 | 
| 438 | 
 | 
| 439 |   // activate row
 | 
| 440 |   switch (active_row) {
 | 
| 441 |   case 0:
 | 
| 442 |     PORTD |= (1 << PD5);
 | 
| 443 |     break;
 | 
| 444 |   case 1:
 | 
| 445 |     PORTA |= (1 << PA1);
 | 
| 446 |     break;
 | 
| 447 |   case 2:
 | 
| 448 |     PORTB |= (1 << PB0);
 | 
| 449 |     break;
 | 
| 450 |   case 3:
 | 
| 451 |     PORTD |= (1 << PD2);
 | 
| 452 |     break;
 | 
| 453 |   case 4:
 | 
| 454 |     PORTB |= (1 << PB7);
 | 
| 455 |     break;
 | 
| 456 |   case 5:
 | 
| 457 |     PORTB |= (1 << PB1);
 | 
| 458 |     break;
 | 
| 459 |   case 6:
 | 
| 460 |     PORTB |= (1 << PB6);
 | 
| 461 |     break;
 | 
| 462 |   case 7:
 | 
| 463 |     PORTB |= (1 << PB3);
 | 
| 464 |     break;
 | 
| 465 |   }
 | 
| 466 | 
 | 
| 467 | }
 | 
| 468 | */
 | 
| 469 | 
 | 
| 470 | 
 | 
| 471 | /*
 | 
| 472 |  * show_char
 | 
| 473 |  * Displays the actual message. 
 | 
| 474 |  * Scrolls the screen to the left and draws new pixels on the right.
 | 
| 475 |  */
 | 
| 476 | void show_char() {
 | 
| 477 |   uint8_t i;
 | 
| 478 |   uint8_t b;
 | 
| 479 | 
 | 
| 480 |   // blit the screen to the left
 | 
| 481 |   for (i = 0; i < 8; i++) {
 | 
| 482 |     screen_mem[i] <<= 1; 
 | 
| 483 |   }
 | 
| 484 |   // advance a char if needed
 | 
| 485 |   if (char_ptr == char_length) {
 | 
| 486 |     message_ptr++;
 | 
| 487 |     if (message_ptr == message_length) {
 | 
| 488 |       message_ptr = 0;
 | 
| 489 |       message_displayed++;
 | 
| 490 |     }
 | 
| 491 |     active_char = buffer[message_ptr] - CHAR_OFFSET;
 | 
| 492 |     char_length = pgm_read_byte(&font[active_char * 4 + 3]);
 | 
| 493 |     char_ptr = 0;
 | 
| 494 |     return; // this makes the space between two chars
 | 
| 495 |   }
 | 
| 496 |   // read pixels for current column of char
 | 
| 497 |   b = pgm_read_byte(&font[active_char * 4 + char_ptr++]);
 | 
| 498 |   // write pixels into screen memory
 | 
| 499 |   for (i = 0; i < 7; i++) {
 | 
| 500 |     if ((b & (1 << i)) == (1 << i)) {
 | 
| 501 |       screen_mem[i+1] |= 0x01;
 | 
| 502 |     } 
 | 
| 503 |   }
 | 
| 504 | }
 | 
| 505 | 
 | 
| 506 | 
 | 
| 507 | 
 | 
| 508 | /*
 | 
| 509 |  * clear_screen
 | 
| 510 |  */
 | 
| 511 | void clear_screen(void) {
 | 
| 512 |   uint8_t i;
 | 
| 513 |   for (i = 0; i < 8; i++) {
 | 
| 514 |     screen_mem[i] = 0x00;
 | 
| 515 |   }
 | 
| 516 | }
 | 
| 517 | 
 | 
| 518 | 
 | 
| 519 | 
 | 
| 520 | /*
 | 
| 521 |  * copy_to_buffer
 | 
| 522 |  * Copies the given sprite from PROGMEM to RAM.
 | 
| 523 |  */
 | 
| 524 | void copy_to_buffer(const prog_uint8_t sprite[8]) {
 | 
| 525 |   memcpy_P(buffer, sprite, 8);
 | 
| 526 | }
 | 
| 527 | 
 | 
| 528 | 
 | 
| 529 | 
 | 
| 530 | /*
 | 
| 531 |  * scroll_animation
 | 
| 532 |  * Uses sprite_1 and sprite_2 to draw a simple animation.
 | 
| 533 |  */
 | 
| 534 | void scroll_animation(const prog_uint8_t sprite_1[8], const prog_uint8_t sprite_2[8]) {
 | 
| 535 |   uint8_t i;
 | 
| 536 |   int8_t x;
 | 
| 537 |   for (i = 0; i < REPEAT_ANIMATION; i++) {
 | 
| 538 |     copy_to_buffer(sprite_1);
 | 
| 539 |     for (x = -8; x <= 0; x++) {
 | 
| 540 |       copy_to_display(x, 0, buffer);
 | 
| 541 |       delay_ms(ANIMATION_SCROLL_SPEED);
 | 
| 542 |     }
 | 
| 543 |     delay_ms(200);
 | 
| 544 |     copy_to_buffer(sprite_2);
 | 
| 545 |     copy_to_display(0, 0, buffer);
 | 
| 546 |     delay_ms(200);
 | 
| 547 |     copy_to_buffer(sprite_1);
 | 
| 548 |     copy_to_display(0, 0, buffer);
 | 
| 549 |     delay_ms(200);
 | 
| 550 |     copy_to_buffer(sprite_2);
 | 
| 551 |     copy_to_display(0, 0, buffer);
 | 
| 552 |     delay_ms(200);
 | 
| 553 |     copy_to_buffer(sprite_1);
 | 
| 554 |     for (x = 0; x < 8; x++) {
 | 
| 555 |       copy_to_display(x, 0, buffer);
 | 
| 556 |       delay_ms(ANIMATION_SCROLL_SPEED);
 | 
| 557 |     }
 | 
| 558 |   }
 | 
| 559 | }
 | 
| 560 | 
 | 
| 561 | 
 | 
| 562 | 
 | 
| 563 | int main(void) {
 | 
| 564 | 
 | 
| 565 |   uint8_t i = 0;
 | 
| 566 |   uint8_t mode = 0;
 | 
| 567 |   uint8_t cycle = 0;
 | 
| 568 | 
 | 
| 569 |   // timer 0 setup, prescaler 8
 | 
| 570 |   TCCR0B |= (1 << CS01);
 | 
| 571 |  
 | 
| 572 |   // enable timer 0 interrupt
 | 
| 573 |   TIMSK |= (1 << TOIE0);  
 | 
| 574 | 
 | 
| 575 |   // define outputs
 | 
| 576 |   DDRA |= 0x03;  
 | 
| 577 |   DDRB |= 0xFF;
 | 
| 578 |   DDRD |= 0x3F;
 | 
| 579 |   
 | 
| 580 |   // shut down all rows and columns, enable column 1
 | 
| 581 |   PORTA = (1 << PA0) | (1 << PA1);
 | 
| 582 |   PORTB = (0 << PB5) | (0 << PB4) | (0 << PB2) | (1 << PB0) | 
 | 
| 583 |           (1 << PB7) | (1 << PB1) | (1 << PB6) | (1 << PB3);
 | 
| 584 |   PORTD = (0 << PD4) | (0 << PD3) | (0 << PD1) | (0 << PD0) |
 | 
| 585 |           (1 << PD5) | (1 << PD2);
 | 
| 586 | 
 | 
| 587 |   // enable pull ups
 | 
| 588 |   PORTD |= (1 << PD6);
 | 
| 589 | 
 | 
| 590 |   // say hello, toggle row 1 (pixel 0,0)
 | 
| 591 |   for (i = 0; i < 5; i++) {
 | 
| 592 |     PORTD &= ~(1 << PD5);
 | 
| 593 |     _delay_ms(50);
 | 
| 594 |     PORTD |= (1 << PD5);
 | 
| 595 |     _delay_ms(50);
 | 
| 596 |   }
 | 
| 597 | 
 | 
| 598 |   // read last mode from eeprom
 | 
| 599 |   // 0 mean cycle through all modes and messages
 | 
| 600 |   mode = eeprom_read_byte(&mode_ee);
 | 
| 601 |   if ((mode == 0) || (mode >= (MAX_ANIMATIONS + MAX_MESSAGES + 1))) {
 | 
| 602 |     mode = 1;
 | 
| 603 |     cycle = 1;
 | 
| 604 |   }
 | 
| 605 |   eeprom_write_byte(&mode_ee, mode + 1);  
 | 
| 606 | 
 | 
| 607 |   sei();
 | 
| 608 | 
 | 
| 609 |   while (1) {
 | 
| 610 | 
 | 
| 611 |     switch (mode) {
 | 
| 612 |     case 1:
 | 
| 613 |       scroll_animation(sprite_00, sprite_01);
 | 
| 614 |       break;
 | 
| 615 |     case 2:
 | 
| 616 |       for (i = 0; i < REPEAT_ANIMATION; i++) {
 | 
| 617 |         copy_to_buffer(sprite_03);
 | 
| 618 |         copy_to_display(0, 0, buffer);
 | 
| 619 |         delay_ms(750);
 | 
| 620 |         copy_to_buffer(sprite_02);        
 | 
| 621 |         copy_to_display(0, 0, buffer);
 | 
| 622 |         delay_ms(180);
 | 
| 623 |       }
 | 
| 624 |       break;
 | 
| 625 |     case 3:
 | 
| 626 |       scroll_animation(sprite_04, sprite_05);
 | 
| 627 |       break;
 | 
| 628 |     default:
 | 
| 629 |       strcpy_P(buffer, (uint8_t*)pgm_read_word(&(messages[mode - (MAX_ANIMATIONS+1)])));
 | 
| 630 |       message_length = strlen(buffer);
 | 
| 631 |       while (message_displayed < REPEAT_TEXT) {
 | 
| 632 |         show_char();
 | 
| 633 |         delay_ms(TEXT_SCROLL_SPEED);
 | 
| 634 |       }
 | 
| 635 |       message_displayed = 0;
 | 
| 636 |       break;
 | 
| 637 |     }
 | 
| 638 | 
 | 
| 639 |     // cycle through all modes
 | 
| 640 |     if (cycle) {
 | 
| 641 |       mode++;
 | 
| 642 |       clear_screen();
 | 
| 643 |       if (mode >= (MAX_ANIMATIONS + MAX_MESSAGES + 1)) {
 | 
| 644 |         mode = 1;
 | 
| 645 |       }
 | 
| 646 |     }
 | 
| 647 | 
 | 
| 648 |   }
 | 
| 649 | 
 | 
| 650 |   return 0;
 | 
| 651 | 
 | 
| 652 | }
 |