Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define FONT_WIDTH 8
- #define INTERCHAR_SPACE 1
- #define ASCII_OFFSET 0x20 // ASSCI code of 1st char in font array
- String myMessage = " /c#CC00CCWelcome to /c#00CC00/bZillow/c#CC00CC!/b";
- const uint8_t Font5x7[] PROGMEM = {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x60,0xfa,0xfa,0x60,0x00,0x00,
- 0x00,0xc0,0xc0,0x00,0xc0,0xc0,0x00,0x00,
- 0x28,0xfe,0xfe,0x28,0xfe,0xfe,0x28,0x00,
- 0x24,0x74,0xd6,0xd6,0x5c,0x48,0x00,0x00,
- 0x62,0x66,0x0c,0x18,0x30,0x66,0x46,0x00,
- 0x0c,0x5e,0xf2,0xba,0xec,0x5e,0x12,0x00,
- 0x20,0xe0,0xc0,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x38,0x7c,0xc6,0x82,0x00,0x00,0x00,
- 0x00,0x82,0xc6,0x7c,0x38,0x00,0x00,0x00,
- 0x10,0x54,0x7c,0x38,0x38,0x7c,0x54,0x10,
- 0x10,0x10,0x7c,0x7c,0x10,0x10,0x00,0x00,
- 0x00,0x01,0x07,0x06,0x00,0x00,0x00,0x00,
- 0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
- 0x00,0x00,0x06,0x06,0x00,0x00,0x00,0x00,
- 0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,
- 0x7c,0xfe,0x8e,0x9a,0xb2,0xfe,0x7c,0x00,
- 0x02,0x42,0xfe,0xfe,0x02,0x02,0x00,0x00,
- 0x46,0xce,0x9a,0x92,0xf6,0x66,0x00,0x00,
- 0x44,0xc6,0x92,0x92,0xfe,0x6c,0x00,0x00,
- 0x18,0x38,0x68,0xca,0xfe,0xfe,0x0a,0x00,
- 0xe4,0xe6,0xa2,0xa2,0xbe,0x9c,0x00,0x00,
- 0x3c,0x7e,0xd2,0x92,0x9e,0x0c,0x00,0x00,
- 0xc0,0xc0,0x8e,0x9e,0xf0,0xe0,0x00,0x00,
- 0x6c,0xfe,0x92,0x92,0xfe,0x6c,0x00,0x00,
- 0x60,0xf2,0x92,0x96,0xfc,0x78,0x00,0x00,
- 0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,
- 0x00,0x01,0x67,0x66,0x00,0x00,0x00,0x00,
- 0x10,0x38,0x6c,0xc6,0x82,0x00,0x00,0x00,
- 0x24,0x24,0x24,0x24,0x24,0x24,0x00,0x00,
- 0x00,0x82,0xc6,0x6c,0x38,0x10,0x00,0x00,
- 0x40,0xc0,0x8a,0x9a,0xf0,0x60,0x00,0x00,
- 0x7c,0xfe,0x82,0xba,0xba,0xf8,0x78,0x00,
- 0x3e,0x7e,0xc8,0xc8,0x7e,0x3e,0x00,0x00,
- 0x82,0xfe,0xfe,0x92,0x92,0xfe,0x6c,0x00,
- 0x38,0x7c,0xc6,0x82,0x82,0xc6,0x44,0x00,
- 0x82,0xfe,0xfe,0x82,0xc6,0x7c,0x38,0x00,
- 0x82,0xfe,0xfe,0x92,0xba,0x82,0xc6,0x00,
- 0x82,0xfe,0xfe,0x92,0xb8,0x80,0xc0,0x00,
- 0x38,0x7c,0xc6,0x82,0x8a,0xce,0x4e,0x00,
- 0xfe,0xfe,0x10,0x10,0xfe,0xfe,0x00,0x00,
- 0x00,0x82,0xfe,0xfe,0x82,0x00,0x00,0x00,
- 0x0c,0x0e,0x02,0x82,0xfe,0xfc,0x80,0x00,
- 0x82,0xfe,0xfe,0x10,0x38,0xee,0xc6,0x00,
- 0x82,0xfe,0xfe,0x82,0x02,0x06,0x0e,0x00,
- 0xfe,0xfe,0x70,0x38,0x70,0xfe,0xfe,0x00,
- 0xfe,0xfe,0x60,0x30,0x18,0xfe,0xfe,0x00,
- 0x38,0x7c,0xc6,0x82,0xc6,0x7c,0x38,0x00,
- 0x82,0xfe,0xfe,0x92,0x90,0xf0,0x60,0x00,
- 0x78,0xfc,0x84,0x8e,0xfe,0x7a,0x00,0x00,
- 0x82,0xfe,0xfe,0x90,0x98,0xfe,0x66,0x00,
- 0x64,0xf6,0xb2,0x9a,0xce,0x4c,0x00,0x00,
- 0xc0,0x82,0xfe,0xfe,0x82,0xc0,0x00,0x00,
- 0xfe,0xfe,0x02,0x02,0xfe,0xfe,0x00,0x00,
- 0xf8,0xfc,0x06,0x06,0xfc,0xf8,0x00,0x00,
- 0xfe,0xfe,0x0c,0x18,0x0c,0xfe,0xfe,0x00,
- 0xc2,0xe6,0x3c,0x18,0x3c,0xe6,0xc2,0x00,
- 0xe0,0xf2,0x1e,0x1e,0xf2,0xe0,0x00,0x00,
- 0xe2,0xc6,0x8e,0x9a,0xb2,0xe6,0xce,0x00,
- 0x00,0xfe,0xfe,0x82,0x82,0x00,0x00,0x00,
- 0x80,0xc0,0x60,0x30,0x18,0x0c,0x06,0x00,
- 0x00,0x82,0x82,0xfe,0xfe,0x00,0x00,0x00,
- 0x10,0x30,0x60,0xc0,0x60,0x30,0x10,0x00,
- 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
- 0x00,0x00,0xc0,0xe0,0x20,0x00,0x00,0x00,
- 0x04,0x2e,0x2a,0x2a,0x3c,0x1e,0x02,0x00,
- 0x82,0xfe,0xfc,0x12,0x12,0x1e,0x0c,0x00,
- 0x1c,0x3e,0x22,0x22,0x36,0x14,0x00,0x00,
- 0x0c,0x1e,0x12,0x92,0xfc,0xfe,0x02,0x00,
- 0x1c,0x3e,0x2a,0x2a,0x3a,0x18,0x00,0x00,
- 0x12,0x7e,0xfe,0x92,0xc0,0x40,0x00,0x00,
- 0x19,0x3d,0x25,0x25,0x1f,0x3e,0x20,0x00,
- 0x82,0xfe,0xfe,0x10,0x20,0x3e,0x1e,0x00,
- 0x00,0x22,0xbe,0xbe,0x02,0x00,0x00,0x00,
- 0x06,0x07,0x01,0x01,0xbf,0xbe,0x00,0x00,
- 0x82,0xfe,0xfe,0x08,0x1c,0x36,0x22,0x00,
- 0x00,0x82,0xfe,0xfe,0x02,0x00,0x00,0x00,
- 0x3e,0x3e,0x18,0x1c,0x38,0x3e,0x1e,0x00,
- 0x3e,0x3e,0x20,0x20,0x3e,0x1e,0x00,0x00,
- 0x1c,0x3e,0x22,0x22,0x3e,0x1c,0x00,0x00,
- 0x21,0x3f,0x1f,0x25,0x24,0x3c,0x18,0x00,
- 0x18,0x3c,0x24,0x25,0x1f,0x3f,0x21,0x00,
- 0x22,0x3e,0x1e,0x32,0x20,0x38,0x18,0x00,
- 0x12,0x3a,0x2a,0x2a,0x2e,0x24,0x00,0x00,
- 0x00,0x20,0x7c,0xfe,0x22,0x24,0x00,0x00,
- 0x3c,0x3e,0x02,0x02,0x3c,0x3e,0x02,0x00,
- 0x38,0x3c,0x06,0x06,0x3c,0x38,0x00,0x00,
- 0x3c,0x3e,0x0e,0x1c,0x0e,0x3e,0x3c,0x00,
- 0x22,0x36,0x1c,0x08,0x1c,0x36,0x22,0x00,
- 0x39,0x3d,0x05,0x05,0x3f,0x3e,0x00,0x00,
- 0x32,0x26,0x2e,0x3a,0x32,0x26,0x00,0x00,
- 0x10,0x10,0x7c,0xee,0x82,0x82,0x00,0x00,
- 0x00,0x00,0x00,0xee,0xee,0x00,0x00,0x00,
- 0x82,0x82,0xee,0x7c,0x10,0x10,0x00,0x00,
- 0x40,0xc0,0x80,0xc0,0x40,0xc0,0x80,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
- };
- #define PIXELS 60*4 // Number of pixels in the string. I am using 4 meters of 96LED/M
- #define PIXEL_PORT PORTD // Port of the pin the pixels are connected to
- #define PIXEL_DDR DDRD // Port of the pin the pixels are connected to
- #define READY_FOR_MESSAGE_SIGNAL "READYMESSAGE#_-#CANARY#_-#READYMESSAGE"
- #define BAUD_RATE 9600
- #define WAIT_FOR_INPUT_DURATION 100
- #define DEBUG false
- static const uint8_t onBits = 0b11111111; // Bit pattern to write to port to turn on all pins connected to LED strips.
- // Phase #1 - Always 1 - 5 cycles, Phase #2 - Data part - 8 cycles, Phase #3 - Always 0 - 7 cycles
- #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
- #define RES 500000 // Width of the low gap between bits to cause a frame to latch
- #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 )
- struct FormattingState {
- uint8_t red;
- uint8_t green;
- uint8_t blue;
- bool isBlinkEnabled;
- bool isEscapedSlash;
- };
- struct FormattingState currentFormattingState;
- struct FormattingState startOfOutputFormattingState;
- bool isBlinkFrame;
- void setup() {
- PIXEL_DDR |= onBits; // Set used pins to output mode
- // initialize formatting state
- currentFormattingState.red = 0x00;
- currentFormattingState.green = 0x00;
- currentFormattingState.blue = 0x40;
- currentFormattingState.isBlinkEnabled = false;
- currentFormattingState.isEscapedSlash = false;
- startOfOutputFormattingState = currentFormattingState;
- }
- void loop() {
- const char* pMessage = myMessage.c_str();
- while (*pMessage) {
- // restore the startOfOutputFormattingState (since we're starting to output the message again)
- currentFormattingState = startOfOutputFormattingState;
- // process the escape characters at the current position of the message
- pMessage = processEscapeCharacters(pMessage);
- // after processing escape characters, it's possible that we are now at the end of our string, in which case stop
- if (!*pMessage) {
- break;
- }
- // update the startOfOutputFormatting state in case it has now changed
- startOfOutputFormattingState = currentFormattingState;
- // step though each column of the 1st char for smooth scrolling
- for (uint8_t step = 0; step < FONT_WIDTH + INTERCHAR_SPACE ; step++) {
- // toggle the isBlinkFrame (for any blinking text)
- isBlinkFrame = !isBlinkFrame;
- noInterrupts();
- sendString(pMessage, step);
- interrupts();
- delay(10);
- }
- pMessage++;
- }
- requestNewMessage();
- }
- // 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 *str , uint8_t skip) {
- unsigned int maximumNumberOfVisibleCharacters = PIXELS / (FONT_WIDTH + INTERCHAR_SPACE);
- // First char is special case because it can be stepped for smooth scrolling
- sendChar(*str, skip, currentFormattingState.red, currentFormattingState.green, currentFormattingState.blue);
- while (++str && maximumNumberOfVisibleCharacters-- > 0) {
- str = processEscapeCharacters(str);
- // after processing escape characters, it's possible that we are now at the end of our string, in which case stop
- if(!*str) {
- break;
- }
- sendChar(*str, 0, currentFormattingState.red, currentFormattingState.green, currentFormattingState.blue);
- }
- show();
- }
- // 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(uint8_t character, uint8_t skip, uint8_t r, uint8_t g, uint8_t b) {
- const uint8_t *charbase = Font5x7 + (( character - ' ') * FONT_WIDTH ) ;
- uint8_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
- }
- }
- // 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( uint8_t row, uint8_t r, uint8_t g, uint8_t b ) {
- // on a blink frame when blink is enabled, don't draw anything for this row
- if (currentFormattingState.isBlinkEnabled && isBlinkFrame) {
- row = 0;
- }
- if (DEBUG) {
- // output to serial for testing
- printRowToSerial(row);
- }
- 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
- }
- // 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.
- // 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 uint8_t row, const uint8_t colorbyte, const uint8_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
- );
- }
- // 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)
- }
- ////////////////////////////////
- // Escape characters helpers
- ////////////////////////////////
- static inline int hexCharToInt(const char* c) {
- return (c > '9') ? c - 'A' + 10 : c - '0';
- }
- static inline char* processColor(const char* str) {
- if (*str == '#') {
- *str++;
- currentFormattingState.red = (hexCharToInt(*str++) << 4) + hexCharToInt(*str++);
- currentFormattingState.green = (hexCharToInt(*str++) << 4) + hexCharToInt(*str++);
- currentFormattingState.blue = (hexCharToInt(*str++) << 4) + hexCharToInt(*str++);
- }
- return str;
- }
- static inline char* processEscapeCharacter(const char* str) {
- switch (*++str) {
- case 'c':
- // process color
- str = processColor(++str);
- break;
- case 'b':
- // toggle blinkEnabled
- currentFormattingState.isBlinkEnabled = !currentFormattingState.isBlinkEnabled;
- str++;
- break;
- case '/':
- currentFormattingState.isEscapedSlash = true;
- break;
- }
- return str;
- }
- static inline char* processEscapeCharacters(const char* str) {
- currentFormattingState.isEscapedSlash = false;
- while (*str == '/' && *(str + 1)) {
- // if this slash is for an escaped slash, then skip the character
- if (currentFormattingState.isEscapedSlash) {
- str++;
- } else {
- str = processEscapeCharacter(str);
- }
- }
- // if we encountered an escapedSlash, then return to it so it can be printed
- // NOTE: this code will not work if there is an escaped slash followed immediately by another slash :(
- if (currentFormattingState.isEscapedSlash) {
- str--;
- }
- return str;
- }
- ////////////////////////////////
- // Communication
- ////////////////////////////////
- // Requests a new message via serial
- static inline void requestNewMessage() {
- Serial.begin(BAUD_RATE);
- // inform connection that we're ready
- Serial.println(READY_FOR_MESSAGE_SIGNAL);
- // wait for input to arrive
- delay(WAIT_FOR_INPUT_DURATION);
- // see if we got anything
- if (Serial.available()) {
- myMessage = Serial.readString();
- }
- Serial.end();
- }
- ////////////////////////////////
- // Debug
- ////////////////////////////////
- // Debug method to print a row to serial
- static inline void printRowToSerial(uint8_t row) {
- Serial.begin(BAUD_RATE);
- for (int i = 0; i < 7; i++ ) {
- Serial.print( ((row >> i) & 0x01) ? " " : "##");
- }
- Serial.println( ((row >> 7) & 0x01) ? " " : "##");
- Serial.end();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement