RGB LED cube 5x5x5  1.0
Martin Stejskal, Schmidt Dominik
 All Files Functions Variables Macros Groups Pages
rgb_led_cube_5x5x5.c
Go to the documentation of this file.
1 
9 #include <avr/io.h> // Definitions I/O
10 #include <inttypes.h> // Data types
11 #include <avr/interrupt.h> // Interrupts
12 #include <avr/pgmspace.h> // For operations with flash memory
13 
14 #include "settings.h" // Defined symbolic names, F_CPU, and so on
15 
16 #include <util/delay.h> // Delays library
17 
18 #include "bit_operations.h" // Basic operations with bits and I/O
19 
20 #include "framebuffer.h" // Framebuffer functions for
21 #include "pwm_rgb.h" // Driver for PWM
22 
23 #include "animation_codes.h" // Command codes in animations
24 
25 // Animations
26 #include "anim_snake.h"
27 //#include "anim_test.h"
28 
29 /*-----------------------------------------*
30  | Global variables |
31  *-----------------------------------------*/
32 // Usually needed because of interrupt functions
41 /*
42 volatile uint16_t i_fb_data[5][5] =
43 // Fill with demo data - cube
44 {
45  {0x7C00, 0x4400, 0x4400, 0x4400, 0x7C00},
46  {0x0220, 0x0000, 0x0000, 0x0000, 0x0220},
47  {0x0220, 0x0000, 0x0000, 0x0000, 0x0220},
48  {0x0220, 0x0000, 0x0000, 0x0000, 0x0220},
49  {0x001F, 0x0011, 0x0011, 0x0011, 0x001F}
50 };
51 */
52 /*
53 volatile uint16_t i_fb_data[5][5] =
54 // Fill with demo data - white
55 {
56  {0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF},
57  {0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF},
58  {0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF},
59  {0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF},
60  {0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF}
61 };
62 */
63 volatile uint16_t i_fb_data[5][5] =
64 // Fill with demo data - nothing - no LED on
65 {
66  {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
67  {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
68  {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
69  {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
70  {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}
71 };
72 
73 
74 
96 volatile uint8_t i_stat_cfg_anime = 0b00000000;
97 
104 volatile uint16_t *p_anime = 0;
105 
116 volatile uint8_t anime_slowdown_factor = 1;
117 
125 volatile uint8_t anime_slowdown_counter = 1;
126 
127 /*-----------------------------------------*
128  | Function prototypes |
129  *-----------------------------------------*/
130 void set_io_to_defaults(void);
131 
132 void init_animation_counter(void);
133 
134 inline void animation_process_word(uint16_t i_tmp, uint8_t *p_framebuffer_cnt);
135 
136 /*-----------------------------------------*
137  | Interrupts |
138  *-----------------------------------------*/
147 ISR(TIMER2_COMP_vect)
148 {
149  /* Let's find out in witch time phase function is. If in phase 3 (anim_cnt0
150  * and anim_cnt1 must be in "1"). It is used masking to get non-shifted value
151  */
152 
153  /* Save phase to variable. Use masking and then value is shifted, so we can
154  * get value 0~3
155  */
156  uint8_t i_phase = i_stat_cfg_anime &\
157  ((1<<anim_cnt0)|(1<<anim_cnt1)); // Get mask
158  i_phase = i_phase>>anim_cnt0; // Shift to left -> now we get 0~3
159 
160  // Test if run out 40 ms
161  if( i_phase == 3 )
162  {
163  i_phase = 0; // Set phase to begin (always if i_phase == 3)
164 
165  // Check slowdown factor
167  {
168  // OK, first reset slowdown counter to 1
170 
171 
172  // Check animation status - Run/Stop (masking)
173  if( (i_stat_cfg_anime & (1<<anim_run)) != 0 )
174  {// Animation run
175  // Variable for temporary data
176  uint16_t i_tmp = 0;
177 
178  // Counter for bytes loaded to framebuffer
179  uint8_t i_framebuffer_cnt = 0;
180 
181 
182  /* OK, if status is "run", then is needed to know from witch memory
183  * data will be loaded. Options are flash and SRAM. So let's find
184  * out...
185  */
186  if( (i_stat_cfg_anime & (1<<anim_read_FLASH)) != 0)
187  { // Data are stored in flash memory
188 
189  // OK, so fill whole framebuffer
190  while( i_framebuffer_cnt < 25 )
191  {
192  i_tmp = pgm_read_word( p_anime ); // Load word (16 bits)
193  p_anime++; // Increment pointer by 1
194 
195  // if random is enabled
196  if( ( ( i_stat_cfg_anime & (1<<anim_random) ) != 0) &&
197  // And address is bigger then max....
199  {
200  p_anime = 0x00; // set pointer to begin
201  }
202 
203  /* And process loaded data. If data were written to framebuffer,
204  * then increase "i_framebuffer_cnt" by 1
205  */
206  animation_process_word( i_tmp, &i_framebuffer_cnt );
207  }
208  }
209  else
210  { // Else data are stored in SRAM
211  // OK, so fill whole framebuffer
212  while( i_framebuffer_cnt < 25)
213  {
214  i_tmp = *p_anime; // Load word (16 bits)
215  p_anime++; // Increase pointer py 1
216 
217  /* And process loaded data. If data were written to framebuffer,
218  * then increase "i_framebuffer_cnt" by 1
219  */
220  animation_process_word( i_tmp,& i_framebuffer_cnt );
221  }
222  }
223  }
224  // Else animation Stop -> nothing to do
225 
226  }
227  else
228  {// If anime_slowdown_counter != anime_slowdown_factor
230  }
231  }
232  else
233  {// If i_phase != 3 -> increase
234  i_phase++; // Increase phase number
235  }
236 
237  // Use mask to clear counter values
238  i_stat_cfg_anime &= ~((1<<anim_cnt1)|(1<<anim_cnt0));
239 
240  // Anyway, i_phase has to be written back as updated value
241  i_phase = i_phase << anim_cnt0; // Shift back to left
242 
243  // Mask & i_phase value -> i_phase saved
244  i_stat_cfg_anime |= ((1<<anim_cnt1)|(1<<anim_cnt0)) & i_phase;
245 }
246 
247 
248 
249 
258 int main(void)
259 {
260  // First initialize all pins
263  // Initialize framebuffer
265 
266  // Initialize PWM driver
267  init_pwm_rgb();
268 
269  // Initialize Timer2 for animation process
271 
272  // DEBUG
273  i_stat_cfg_anime = 0x81;
274  //i_stat_cfg_anime |= (1<<anim_random);
275  //anime_slowdown_factor = 7;
276  //anime_slowdown_factor = 1;
277  p_anime = bin_anim_snake;
278  //p_anime = 0x0000;
279  // /DEBUG
280 
281  while(1)
282  { // Run framebuffer in infinite loop
283  framebuffer( &i_fb_data ); // Give pointer to frame buffer
284  }
285 
286  return(0);
287 }
288 
289 
297 {
298  /* OK, first (just for case) set all PORTx and DDRx to 0x00 -> Hi-Z on all
299  * pins. Because of possible port on other AVR devices there are used "ifdef"
300  * conditions. If needed, define another
301  */
302 #ifdef PORTA // If on HW exist this port -> set whole port to Hi-Z
303  PORTA = 0x00;
304  DDRA = 0x00;
305 #endif
306 #ifdef PORTB // If on HW exist this port -> set whole port to Hi-Z
307  PORTB = 0x00;
308  DDRB = 0x00;
309 #endif
310 #ifdef PORTC // If on HW exist this port -> set whole port to Hi-Z
311  PORTC = 0x00;
312  DDRC = 0x00;
313 #endif
314 #ifdef PORTD // If on HW exist this port -> set whole port to Hi-Z
315  PORTD = 0x00;
316  DDRD = 0x00;
317 #endif
318 #ifdef PORTE // If on HW exist this port -> set whole port to Hi-Z
319  PORTE = 0x00;
320  DDRE = 0x00;
321 #endif
322 #ifdef PORTF // If on HW exist this port -> set whole port to Hi-Z
323  PORTF = 0x00;
324  DDRF = 0x00;
325 #endif
326 #ifdef PORTG // If on HW exist this port -> set whole port to Hi-Z
327  PORTG = 0x00;
328  DDRG = 0x00;
329 #endif
330 #ifdef PORTH // If on HW exist this port -> set whole port to Hi-Z
331  PORTH = 0x00;
332  DDRH = 0x00;
333 #endif
334 #ifdef PORTI // If on HW exist this port -> set whole port to Hi-Z
335  PORTI = 0x00;
336  DDRI = 0x00;
337 #endif
338 
339  // Keyboard - KBRD_RDx - all input and enabled pull-up
340  io_set_dir_in( KBRD_RD0_PORT, KBRD_RD0_pin );
341  io_set_1( KBRD_RD0_PORT, KBRD_RD0_pin ); // Activate pull-up
342  io_set_dir_in( KBRD_RD1_PORT, KBRD_RD1_pin );
343  io_set_1( KBRD_RD1_PORT, KBRD_RD1_pin ); // Activate pull-up
344  io_set_dir_in( KBRD_RD2_PORT, KBRD_RD2_pin );
345  io_set_1( KBRD_RD2_PORT, KBRD_RD2_pin ); // Activate pull-up
346  io_set_dir_in( KBRD_RD3_PORT, KBRD_RD3_pin );
347  io_set_1( KBRD_RD3_PORT, KBRD_RD3_pin ); // Activate pull-up
348  io_set_dir_in( KBRD_RD4_PORT, KBRD_RD4_pin );
349  io_set_1( KBRD_RD4_PORT, KBRD_RD4_pin ); // Activate pull-up
350  io_set_dir_in( KBRD_RD5_PORT, KBRD_RD5_pin );
351  io_set_1( KBRD_RD5_PORT, KBRD_RD5_pin ); // Activate pull-up
352 
353  // Keyboard - KBRD_PWRx - all as Hi-Z -> set as input without pull-up
354  io_set_dir_in( KBRD_PWR0_PORT, KBRD_PWR0_pin );
355  io_set_dir_in( KBRD_PWR1_PORT, KBRD_PWR1_pin );
356  io_set_dir_in( KBRD_PWR2_PORT, KBRD_PWR2_pin );
357  io_set_dir_in( KBRD_PWR3_PORT, KBRD_PWR3_pin );
358  io_set_dir_in( KBRD_PWR4_PORT, KBRD_PWR4_pin );
359 
360  // UART - PC
361  io_set_dir_in( RX_PC_PORT, RX_PC_pin );
362  io_set_dir_out( TX_PC_PORT, TX_PC_pin );
363 }
364 
365 
372 {
373  // normal pin mode ; CTC mode ; prescaller 1024 -> 64us / 1 cycle
374  TCCR2 = (0<<COM21)|(0<<COM20)|\
375  (1<<WGM21)|(0<<WGM20)|\
376  (1<<CS22) |(0<<CS21) |(1<<CS20);
377 
378  // Fill OCR2 -> approx 10ms
379  OCR2 = animation_delay_timer;
380 
381  // Enable interrupt if counter counts to value in OCR2
382  TIMSK |= (1<<OCIE2);
383 }
384 
385 
386 
397 inline void animation_process_word(uint16_t i_tmp, uint8_t *p_framebuffer_cnt)
398 {
399  // Test for special functions -> first bit is set
400  if( (( i_tmp & 0x8000 ) != 0) &&
401  (((i_stat_cfg_anime) & (1<<anim_random)) == 0) )
402  {/* Special function -> i_tmp contains 2 parts: command number and value ->
403  * -> let's split them -> easier for AVR to work with 8bit variables than
404  * 16bit
405  */
406  uint8_t cmd_number = (i_tmp & 0x7F00)>>8; // Save command number
407  uint8_t cmd_value = (i_tmp & 0x00FF); // And save value
408  switch( cmd_number )
409  {
410  // Command animation start
411  case (cmd_anim_start>>8)& 0x7F : // From 16 bit number makes cmd number
412  anime_slowdown_factor = cmd_value; // Save slowdown factor
413  // Set PWM to max
414  pwm_set_r(255);
415  pwm_set_g(255);
416  pwm_set_b(255);
417  break;
418 
419  // Command animation stop
420  case (cmd_anim_stop>>8)& 0x7F :
421  if(cmd_value == 0) // Test for option "0"
422  {
423  for(uint8_t i_wall = 4 ; i_wall <5 ; i_wall--)
424 
425  for(uint8_t i_column = 4 ; i_column <5 ; i_column--)
426  {
427  i_fb_data[i_wall][i_column] = 0;
428  }
429  }
430  if(cmd_value == 1) // Test for option "1"
431  {
432  // Nothing to do (framebuffer stay same)
433  }
434  // And set animation to "stop"
435  i_stat_cfg_anime = i_stat_cfg_anime & ~(1<<anim_run); // Anime stop
436  *p_framebuffer_cnt = 25; /* And set counter to 25 -> stop loading data
437  * to framebuffer.
438  */
439  break;
440 
441  // Command Set PWM for RED channel
442  case (cmd_set_pwm_r>>8)& 0x7F :
443  pwm_set_r( cmd_value ); break;
444 
445  // Command Set PWM for GREEN channel
446  case (cmd_set_pwm_g>>8)& 0x7F :
447  pwm_set_g( cmd_value ); break;
448 
449  // Command Set PWM for BLUE channel
450  case (cmd_set_pwm_b>>8)& 0x7F :
451  pwm_set_b( cmd_value ); break;
452 
453 
454  // Parameters for 2D frame (active wall)
455  case (cmd_param_2D_frame>>8)& 0x7F :
456  // Decide according option
457  switch( cmd_value )
458  {
459  case param_2D_frame_void: // Void 2D frame
460  if(*p_framebuffer_cnt < 5 )
461  {
462  // Power off all LED in WALL0
464  // And set counter on following wall
465  *p_framebuffer_cnt = 5;
466  }
467  else if(*p_framebuffer_cnt < 10)
468  {
469  // Power off all LED in WALL1
471  // And set counter on following wall
472  *p_framebuffer_cnt = 10;
473  }
474  else if(*p_framebuffer_cnt < 15)
475  {
476  // Power off all LED in WALL2
478  // And set counter on following wall
479  *p_framebuffer_cnt = 15;
480  }
481  else if(*p_framebuffer_cnt < 20)
482  {
483  // Power off all LED in WALL3
485  // And set counter on following wall
486  *p_framebuffer_cnt = 20;
487  }
488  else
489  {
490  // Power off all LED in WALL4
492  // And set counter on following wall
493  *p_framebuffer_cnt = 25;
494  }
495  break; // param_2D_frame_void
496  case param_2D_frame_same: // Same 2D frame as previous
497  // Just copy data and move on
498  if(*p_framebuffer_cnt < 5)
499  {// Error - WALL0 has no reference -> clear all data
501  // And set counter on following wall
502  *p_framebuffer_cnt = 5;
503  }
504  else if(*p_framebuffer_cnt < 10)
505  {// Active WALL1 -> copy from WALL0
507  // And set counter on following wall
508  *p_framebuffer_cnt = 10;
509  }
510  else if(*p_framebuffer_cnt < 15)
511  {// Active WALL2 -> copy from WALL1
513  // And set counter on following wall
514  *p_framebuffer_cnt = 15;
515  }
516  else if(*p_framebuffer_cnt < 20 )
517  {// Active WALL3 -> copy from WALL2
519  // And set counter on following wall
520  *p_framebuffer_cnt = 20;
521  }
522  else
523  {// Active WALL4 -> copy from WALL3
525  // And set counter on following wall
526  *p_framebuffer_cnt = 25;
527  }
528  break; // param_2D_frame_same
529  case param_2D_frame_ones: // Whole 2D frame is on
530  if(*p_framebuffer_cnt < 5 )
531  {
532  // Turn on all LEDs in WALL0
534  // And set counter on following wall
535  *p_framebuffer_cnt = 5;
536  }
537  else if(*p_framebuffer_cnt < 10)
538  {
539  // Turn on all LEDs in WALL1
541  // And set counter on following wall
542  *p_framebuffer_cnt = 10;
543  }
544  else if(*p_framebuffer_cnt < 15)
545  {
546  // Turn on all LEDs in WALL2
548  // And set counter on following wall
549  *p_framebuffer_cnt = 15;
550  }
551  else if(*p_framebuffer_cnt < 20)
552  {
553  // Turn on all LEDs in WALL3
555  // And set counter on following wall
556  *p_framebuffer_cnt = 20;
557  }
558  else
559  {
560  // Turn on all LEDs in WALL4
562  // And set counter on following wall
563  *p_framebuffer_cnt = 25;
564  }
565  break; // param_2D_frame_ones
566  }
567  break;// For case: cmd_param_2D_frame
568 
569  case (cmd_param_3D_frame>>8)& 0x7F :
570  // Ok, there will be some options
571  switch(cmd_value)
572  {
573  case param_3D_frame_void: // Set all segments to 0
579  break; // param_3D_frame_void
580 
581  case param_3D_frame_same: /* Framebuffer same -> do not change
582  * framebuffer -> just change p_framebuffer_cnt
583  */
584  *p_framebuffer_cnt = 25;
585  break; // param_3D_frame_same
586  case param_3D_frame_ones: // Turn on all LEDs
592  break; // param_3D_frame_ones
593  }
594  break; // cmd_param_3D_frame
595  }
596 
600  }
601  else
602  {// Else ordinary data
603 
604  /* Depend on i_framebuffer_cnt store data on different position.
605  * Used switch because of speed. Because framebuffer is 16 bit,
606  * data must be written in "two cycles"
607  */
608  switch(*p_framebuffer_cnt)
609  {
610  case 0: i_fb_data[0][0] = i_tmp; break;
611  case 1: i_fb_data[0][1] = i_tmp; break;
612  case 2: i_fb_data[0][2] = i_tmp; break;
613  case 3: i_fb_data[0][3] = i_tmp; break;
614  case 4: i_fb_data[0][4] = i_tmp; break;
615  case 5: i_fb_data[1][0] = i_tmp; break;
616  case 6: i_fb_data[1][1] = i_tmp; break;
617  case 7: i_fb_data[1][2] = i_tmp; break;
618  case 8: i_fb_data[1][3] = i_tmp; break;
619  case 9: i_fb_data[1][4] = i_tmp; break;
620  case 10: i_fb_data[2][0] = i_tmp; break;
621  case 11: i_fb_data[2][1] = i_tmp; break;
622  case 12: i_fb_data[2][2] = i_tmp; break;
623  case 13: i_fb_data[2][3] = i_tmp; break;
624  case 14: i_fb_data[2][4] = i_tmp; break;
625  case 15: i_fb_data[3][0] = i_tmp; break;
626  case 16: i_fb_data[3][1] = i_tmp; break;
627  case 17: i_fb_data[3][2] = i_tmp; break;
628  case 18: i_fb_data[3][3] = i_tmp; break;
629  case 19: i_fb_data[3][4] = i_tmp; break;
630  case 20: i_fb_data[4][0] = i_tmp; break;
631  case 21: i_fb_data[4][1] = i_tmp; break;
632  case 22: i_fb_data[4][2] = i_tmp; break;
633  case 23: i_fb_data[4][3] = i_tmp; break;
634  case 24: i_fb_data[4][4] = i_tmp; break;
635  }
636 
637  (*p_framebuffer_cnt)++; // Increase counter by 1
638  }
639 
640 }