// Change this to be at least as long as your pixel string (too long will work fine, just be a little slower) #define PIXELS 528 // Number of pixels in the string. I am using 4 meters of 96LED/M // These values depend on which pins your 8 strings are connected to and what board you are using // PORTD controls Digital Pins 0-7 on the Uno // You'll need to look up the port/bit combination for other boards. // Note that you could also include the DigitalWriteFast header file to not need to to this lookup. #define PIXEL_PORT PORTA // Port of the pin the pixels are connected to #define PIXEL_DDR DDRA // Port of the pin the pixels are connected to static const uint16_t onBits=0b11111111; // Bit pattern to write to port to turn on all pins connected to LED strips. // If you do not want to use all 8 pins, you can mask off the ones you don't want // Note that these will still get 0 written to them when we send pixels // TODO: If we have time, we could even add a variable that will and/or into the bits before writing to the port to support any combination of bits/values // These are the timing constraints taken mostly from // imperically measuring the output from the Adafruit library strandtest program // Note that some of these defined values are for reFFrnce only - the actual timing is determinted by the hard code. #define T1H 814 // Width of a 1 bit in ns - 13 cycles #define T1L 438 // Width of a 1 bit in ns - 7 cycles #define T0H 312 // Width of a 0 bit in ns - 5 cycles #define T0L 936 // Width of a 0 bit in ns - 15 cycles // Phase #1 - Always 1 - 5 cycles // Phase #2 - Data part - 8 cycles // Phase #3 - Always 0 - 7 cycles #define RES 50000 // Width of the low gap between bits to cause a frame to latch // Here are some convience defines for using nanoseconds specs to generate actual CPU delays #define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives #define CYCLES_PER_SEC (F_CPU) #define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) #define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) boolean textFreeze = false; boolean scrolling = !textFreeze; //No Need to change this. Automatically Switches scrolling to the opposite of what textFreeze is. boolean colorMode1 = true; //Change this value to change pattern of color switching. boolean colorMode2 = !colorMode1; //No Need to change this. Automatically Switches colorSwitch2 to the opposite of what colorSwitch1 is. // Sends a full 8 bits down all the pins, represening a single color of 1 pixel // We walk though the 8 bits in colorbyte one at a time. If the bit is 1 then we send the 8 bits of row out. Otherwise we send 0. // We send onBits at the first phase of the signal generation. We could just send 0xff, but that mught enable pull-ups on pins that we are not using. /// Unforntunately we have to drop to ASM for this so we can interleave the computaions durring the delays, otherwise things get too slow. // OnBits is the mask of which bits are connected to strips. We pass it on so that we // do not turn on unused pins becuase this would enable the pullup. Also, hopefully passing this // will cause the compiler to allocate a Register for it and avoid a reload every pass. static inline void sendBitx8( const uint16_t row , const uint16_t colorbyte , const uint16_t onBits ) { asm volatile ( "L_%=: \n\r" "out %[port], %[onBits] \n\t" // (1 cycles) - send either T0H or the first part of T1H. Onbits is a mask of which bits have strings attached. // Next determine if we are going to be sending 1s or 0s based on the current bit in the color.... "mov r0, %[bitwalker] \n\t" // (1 cycles) "and r0, %[colorbyte] \n\t" // (1 cycles) - is the current bit in the color byte set? "breq OFF_%= \n\t" // (1 cycles) - bit in color is 0, then send full zero row (takes 2 cycles if branch taken, count the extra 1 on the target line) // If we get here, then we want to send a 1 for every row that has an ON dot... "nop \n\t " // (1 cycles) "out %[port], %[row] \n\t" // (1 cycles) - set the output bits to [row] This is phase for T0H-T1H. // ========== // (5 cycles) - T0H (Phase #1) "nop \n\t nop \n\t " // (2 cycles) "nop \n\t nop \n\t " // (2 cycles) "nop \n\t nop \n\t " // (2 cycles) "nop \n\t " // (1 cycles) "out %[port], __zero_reg__ \n\t" // (1 cycles) - set the output bits to 0x00 based on the bit in colorbyte. This is phase for T0H-T1H // ========== // (8 cycles) - Phase #2 "ror %[bitwalker] \n\t" // (1 cycles) - get ready for next pass. On last pass, the bit will end up in C flag "brcs DONE_%= \n\t" // (1 cycles) Exit if carry bit is set as a result of us walking all 8 bits. We assume that the process around us will tak long enough to cover the phase 3 delay "nop \n\t \n\t " // (1 cycles) - When added to the 5 cycles in S:, we gte the 7 cycles of T1L "jmp L_%= \n\t" // (3 cycles) // (1 cycles) - The OUT on the next pass of the loop // ========== // (7 cycles) - T1L "OFF_%=: \n\r" // (1 cycles) Note that we land here becuase of breq, which takes takes 2 cycles "out %[port], __zero_reg__ \n\t" // (1 cycles) - set the output bits to 0x00 based on the bit in colorbyte. This is phase for T0H-T1H // ========== // (5 cycles) - T0H "ror %[bitwalker] \n\t" // (1 cycles) - get ready for next pass. On last pass, the bit will end up in C flag "brcs DONE_%= \n\t" // (1 cycles) Exit if carry bit is set as a result of us walking all 8 bits. We assume that the process around us will tak long enough to cover the phase 3 delay "nop \n\t nop \n\t " // (2 cycles) "nop \n\t nop \n\t " // (2 cycles) "nop \n\t nop \n\t " // (2 cycles) "nop \n\t nop \n\t " // (2 cycles) "nop \n\t " // (1 cycles) "jmp L_%= \n\t" // (3 cycles) // (1 cycles) - The OUT on the next pass of the loop // ========== //(15 cycles) - T0L "DONE_%=: \n\t" // Don't need an explicit delay here since the overhead that follows will always be long enough :: [port] "I" (_SFR_IO_ADDR(PIXEL_PORT)), [row] "d" (row), [onBits] "d" (onBits), [colorbyte] "d" (colorbyte ), // Phase 2 of the signal where the actual data bits show up. [bitwalker] "r" (0x80) // Alocate a register to hold a bit that we will walk down though the color byte ); // Note that the inter-bit gap can be as long as you want as long as it doesn't exceed the reset timeout (which is A long time) } // Just wait long enough without sending any bots to cause the pixels to latch and display the last sent frame void show() { delayMicroseconds( (RES / 1000UL) + 1); // Round up since the delay must be _at_least_ this long (too short might not work, too long not a problem) } // Send 3 bytes of color data (R,G,B) for a signle pixel down all the connected stringsat the same time // A 1 bit in "row" means send the color, a 0 bit means send black. static inline void sendRowRGB( uint16_t row , uint16_t r, uint16_t g, uint16_t b ) { sendBitx8( row , g , onBits); // WS2812 takes colors in GRB order sendBitx8( row , r , onBits); // WS2812 takes colors in GRB order sendBitx8( row , b , onBits); // WS2812 takes colors in GRB order } // This nice 5x7 font // Font details: // 1) Each char is fixed 5x8 pixels. // 2) Each byte is one column. // 3) Columns are left to right order, leftmost byte is leftmost column of pixels. // 4) Each column is 8 bits high. // 5) Bit #7 is top line of char, Bit #1 is bottom. // 6) Bit #0 is always 0, becuase this pin is used as Serial1 input and setting to 1 would enable the pull-up. // defines ascii characters 0x20-0x7F (32-127) // PROGMEM after variable name #define FONT_WIDTH 5 #define INTERCHAR_SPACE 1 #define ASCII_OFFSET 0x20 // ASSCI code of 1st char in font array const uint16_t Font5x8[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,// 0x00,0x00,0xFD,0x00,0x00,// ! 0x00,0xe0,0x00,0xe0,0x00,// " 0x24,0xFF,0x24,0xFF,0x24,// # 0x22,0x52,0xFF,0x52,0x4C,// $ 0xC1,0xC6,0x18,0x63,0x83,// % 0x76,0x89,0x96,0x66,0x09,// & 0x00,0xA0,0xC0,0x00,0x00,// ' 0x00,0x3C,0x42,0x81,0x00,// ( 0x81,0x42,0x3C,0x00,0x00,// ) 0x24,0x18,0x7E,0x18,0x24,// * 0x08,0x08,0x3E,0x08,0x08,// + 0x00,0x05,0x06,0x00,0x00,// , 0x10,0x10,0x10,0x10,0x10,// - 0x00,0x03,0x03,0x00,0x00,// . 0x01,0x06,0x18,0x60,0x80,// / 0x7E,0x87,0x99,0xE1,0x7E,// 0 0x00,0x41,0xFF,0x01,0x00,// 1 0x43,0x85,0x89,0x91,0x61,// 2 0x82,0x91,0xA9,0xC5,0x82,// 3 0x18,0x28,0x48,0xFF,0x08,// 4 0xF2,0x91,0x91,0x91,0x8E,// 5 0x3E,0x49,0x89,0x89,0x06,// 6 0x80,0x8F,0x90,0xA0,0xC0,// 7 0x6E,0x91,0x91,0x91,0x6E,// 8 0x62,0x91,0x91,0x92,0x7C,// 9 0x00,0x66,0x66,0x00,0x00,// : 0x00,0x65,0x66,0x00,0x00,// ; 0x18,0x24,0x42,0x81,0x00,// < 0x24,0x24,0x24,0x24,0x24,// = 0x81,0x42,0x24,0x18,0x00,// > 0x40,0x80,0x8D,0x90,0x60,// ? 0x4E,0x91,0x9F,0x81,0x7E,// @ 0x7F,0x88,0x88,0x88,0x7F,// A 0xFF,0x91,0x91,0x91,0x6E,// B 0x7E,0x81,0x81,0x81,0x42,// C 0xFF,0x81,0x81,0x42,0x3C,// D 0xFF,0x91,0x91,0x91,0x81,// E 0xFF,0x90,0x90,0x80,0x80,// F 0x7E,0x81,0x89,0x89,0x4E,// G 0xFF,0x10,0x10,0x10,0xFF,// H 0x81,0x81,0xFF,0x81,0x81,// I 0x06,0x01,0x01,0x01,0xFE,// J 0xFF,0x18,0x24,0x42,0x81,// K 0xFF,0x01,0x01,0x01,0x01,// L 0xFF,0x40,0x30,0x40,0xFF,// M 0xFF,0x60,0x18,0x06,0xFF,// N 0x7E,0x81,0x81,0x81,0x7E,// O 0xFF,0x88,0x88,0x88,0x70,// P 0x7E,0x81,0x85,0x82,0x7D,// Q 0xFF,0x88,0x8C,0x8A,0x71,// R 0x62,0x91,0x91,0x91,0x8E,// S 0x80,0x80,0xFF,0x80,0x80,// T 0xFE,0x01,0x01,0x01,0xFE,// U 0xF8,0x06,0x01,0x06,0xF8,// V 0xFF,0x02,0x0C,0x02,0xFF,// W 0xC3,0x24,0x18,0x24,0xC3,// X 0xC0,0x30,0x0F,0x30,0xC0,// Y 0x83,0x85,0x99,0xA1,0xC1,// Z 0x00,0xFF,0x81,0x81,0x00,// [ 0x80,0x60,0x18,0x06,0x01,// (backslash) 0x00,0x81,0x81,0xFF,0x00,// ] 0x10,0x60,0x80,0x60,0x10,// ^ 0x01,0x01,0x01,0x01,0x01,// _ 0x00,0x80,0x40,0x20,0x00,// ` 0x06,0x29,0x29,0x29,0x1F,// a 0xFF,0x09,0x11,0x11,0x0E,// b 0x1E,0x21,0x21,0x21,0x12,// c 0x0E,0x11,0x11,0x09,0xFF,// d 0x1E,0x29,0x29,0x29,0x1A,// e 0x00,0x08,0x7F,0x88,0x40,// f 0x38,0x49,0x49,0x49,0x3E,// g 0xFF,0x08,0x10,0x10,0x0F,// h 0x00,0x11,0x5F,0x01,0x00,// i 0x02,0x01,0x11,0x5E,0x00,// j 0xFF,0x18,0x24,0x43,0x00,// k 0x00,0x81,0xFF,0x01,0x00,// l 0x3F,0x20,0x18,0x20,0x1F,// m 0x3F,0x10,0x20,0x20,0x1F,// n 0x1E,0x21,0x21,0x21,0x1E,// o 0x3F,0x24,0x24,0x24,0x18,// p 0x18,0x24,0x24,0x14,0x3F,// q 0x3F,0x10,0x20,0x20,0x18,// r 0x19,0x25,0x25,0x25,0x02,// s 0x10,0x7E,0x11,0x01,0x02,// t 0x3E,0x01,0x01,0x02,0x3D,// u 0x3C,0x02,0x01,0x02,0x3C,// v 0x3E,0x01,0x06,0x01,0x3E,// w 0x31,0x0A,0x04,0x0A,0x31,// x 0x30,0x09,0x09,0x09,0x3E,// y 0x11,0x13,0x15,0x19,0x11,// z 0x00,0x08,0x76,0x81,0x00,// { 0x00,0x00,0xFF,0x00,0x00,// | 0x00,0x81,0x76,0x08,0x00,// } 0x18,0x20,0x18,0x04,0x18,// ~ 0x10,0x38,0x54,0x10,0x10,//  }; // Send the pixels to form the specified char, not including interchar space // skip is the number of pixels to skip at the begining to enable sub-char smooth scrolling // TODO: Subtract the offset from the char before starting the send sequence to save time if nessisary // TODO: Also could pad the begining of the font table to aovid the offset subtraction at the cost of 20*8 bytes of progmem // TODO: Could pad all chars out to 8 bytes wide to turn the the multiply by FONT_WIDTH into a shift static inline void sendChar( uint16_t c , uint16_t skip , uint16_t r, uint16_t g, uint16_t b ) { const uint16_t *charbase = Font5x8 + (( c -' ')* FONT_WIDTH ) ; uint16_t col=FONT_WIDTH; while (skip--) { charbase++; col--; } while (col--) { sendRowRGB( pgm_read_byte_near( charbase++ ) , r , g , b ); } col=INTERCHAR_SPACE; while (col--) { sendRowRGB( 0 , r , g , b ); // Interchar space } } // Show the passed string. The last letter of the string will be in the rightmost pixels of the display. // Skip is how many cols of the 1st char to skip for smooth scrolling static inline void sendString( const char *s , uint16_t skip , const uint16_t r, const uint16_t g, const uint16_t b ) { unsigned int l=PIXELS/(FONT_WIDTH+INTERCHAR_SPACE); sendChar( *s , skip , r , g , b ); // First char is special case becuase it can be stepped for smooth scrolling while ( *(++s) && l--) { sendChar( *s , 0, r , g , b ); } show(); } static char sp[] = " " ; #define BUFFER_LEN 500 char bf[BUFFER_LEN]; char gggg[BUFFER_LEN]; #include #include #include using namespace std; const byte numChars = 500; char receivedChars[numChars]= "Hello." ; boolean newData = false; void setup() { PIXEL_DDR |= onBits; // Set used pins to output mode pinMode( 19, INPUT_PULLUP ); // fix Serial1 Serial1.begin(9600); Serial1.println(""); Serial1.println(""); } void recvWithEndMarker() { static byte ndx = 0; char endMarker = '\n'; char rc; while (Serial1.available() > 0 && newData == false) { rc = Serial1.read(); if (rc != endMarker) { ndx = 0; receivedChars[ndx] = '\0'; receivedChars[ndx] = rc; ndx++; if (ndx >= numChars) { ndx = numChars - 1; } } else { receivedChars[ndx] = '\0'; // terminate the string ndx = 0; newData = true; } } } void showNewData() { if (newData == true) { Serial1.print("This just in ... "); Serial1.println(receivedChars); processData() newData = false; } } void checker() { if (scrolling == true){ strcpy( bf, sp ); strcat( bf, receivedChars ); } else{ strcpy( bf, receivedChars ); } } void checkndo() { if (scrolling == true){ if (colorMode1 == true){ ggg1(); ggg2(); ggg3(); } else{ ggg4(); ggg5(); ggg6(); } } else{ gggf(); } } void loop() { recvWithEndMarker(); showNewData(); checker(); checkndo(); } void ggg1(){ const char *m = bf; while (*m) { for( uint16_t step=0; step