0

good afternoon, I am making a 7x10 led matrix in which I use a CD4017 to handle the 7 rows and 2 cascaded shift registers to handle the 10 columns. I first tried a programming to turn on my entire led matrix and it worked fine. Then, I tried to make a character appear statically and it worked fine. Next, I tried to make that character sweep from right to left and it worked fine. But, what I am having a hard time with is sweeping the message from right to left smoothly with a column of character to character spacing.

What I originally thought is that if I wanted to move a character from right to left, I have to shift the column values for all the rows by an appropriate amount (shift step) and once the character has been shifted enough, I start feeding the column values of the next character in the message for which, the formula that would create the right to left shift effect is: (current_char<scroll) | (next_char>>(8-shift_step)-scroll) But I don't get the expected result, the message scrolls from right to left but it doesn't do it with a column of separation between character and character and it doesn't do it in a fluid way either. In addition to that at the end of sweeping the message appears something strange that should not be there.

I recorded a video showing what I am saying: https://drive.google.com/file/d/1ebO5zassWY060mvWACPhcSLeOi5JmZAC/view

Code:

/*
 char_data is a two dimensional constant array that holds the 5-bit column values
 of individual rows for ASCII characters that are to be displayed on a 7x10 matrix. 
*/
const byte char_data[95][7]={
    {0, 0, 0, 0, 0, 0, 0}, // space
    {0b100, 0b100, 0b100, 0b100, 0b100, 0, 0b100}, // !
    {0b1010, 0b1010, 0b1010, 0, 0, 0, 0}, // "
    {0b1010, 0b1010, 0b11111, 0b1010, 0b11111, 0b1010, 0b1010}, // #
    {0b100, 0b1111, 0b10100, 0b1110, 0b101, 0b11110, 0b100}, // $
    {0b11000, 0b11001, 0b10, 0b100, 0b1000, 0b10011, 0b11}, // %
    {0b1000, 0b10100, 0b10100, 0b1000, 0b10101, 0b10010, 0b1101}, // &
    {0b100, 0b100, 0b100, 0, 0, 0, 0}, // '
    {0b100, 0b1000, 0b10000, 0b10000, 0b10000, 0b1000, 0b100}, // (
    {0b100, 0b10, 0b1, 0b1, 0b1, 0b10, 0b100}, // )
    {0b100, 0b10101, 0b1110, 0b100, 0b1110, 0b10101, 0b100}, // *
    {0, 0b100, 0b100, 0b11111, 0b100, 0b100, 0}, // +
    {0, 0, 0, 0, 0b100, 0b100, 0b1000}, // ,
    {0, 0, 0, 0b11111, 0, 0, 0}, // -
    {0, 0, 0, 0, 0, 0, 0b100}, // .
    {0, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0}, // /
    {0b1110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b1110}, // 0
    {0b110, 0b1100, 0b100, 0b100, 0b100, 0b100, 0b1110}, // 1
    {0b1110, 0b10001, 0b1, 0b110, 0b1000, 0b10000, 0b11111}, // 2
    {0b11111, 0b1, 0b10, 0b110, 0b1, 0b10001, 0b1111}, // 3
    {0b10, 0b110, 0b1010, 0b10010, 0b11111, 0b10, 0b10}, // 4
    {0b11111, 0b10000, 0b11110, 0b1, 0b1, 0b10001, 0b1110}, // 5
    {0b01110, 0b10001, 0b10000, 0b11110, 0b10001, 0b10001, 0b1110}, // 6
    {0b11111, 0b1, 0b10, 0b100, 0b1000, 0b1000, 0b1000}, // 7
    {0b01110, 0b10001, 0b10001, 0b1110, 0b10001, 0b10001, 0b1110}, // 8
    {0b01110, 0b10001, 0b10001, 0b1111, 0b1, 0b10001, 0b1110}, // 9
    {0, 0, 0b100, 0, 0b100, 0, 0}, // :
    {0, 0, 0b100, 0, 0b100, 0b100, 0b1000}, // ;
    {0b10, 0b100, 0b1000, 0b10000, 0b1000, 0b100, 0b10}, // <
    {0, 0, 0b11111, 0, 0b11111, 0, 0}, // =
    {0b1000, 0b100, 0b10, 0b1, 0b10, 0b100, 0b1000}, // >
    {0b1110, 0b10001, 0b1, 0b10, 0b100, 0, 0b100}, // ?
    {0b1110, 0b10001, 0b10111, 0b10101, 0b10110, 0b10000, 0b1111}, // @
    {0b1110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}, // A
    {0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110}, // B
    {0b1110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b1110}, // C
    {0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110}, // D
    {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111}, // E
    {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000}, // F
    {0b1110, 0b10001, 0b10000, 0b10111, 0b10101, 0b10001, 0b1110}, // G
    {0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}, // H
    {0b11111, 0b100, 0b100, 0b100, 0b100, 0b100, 0b11111}, // I
    {0b1, 0b1, 0b1, 0b1, 0b1, 0b10001, 0b1110}, // J
    {0b10001, 0b10001, 0b10010, 0b11100, 0b10010, 0b10001, 0b10001}, // K
    {0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}, // L
    {0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001}, // M
    {0b10001, 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001}, // N
    {0b1110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b1110}, // O
    {0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000}, // P
    {0b1110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10011, 0b1111}, // Q
    {0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b10001}, // R
    {0b1111, 0b10000, 0b10000, 0b1110, 0b1, 0b1, 0b11110}, // S
    {0b11111, 0b100, 0b100, 0b100, 0b100, 0b100, 0b100}, // T
    {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b1110}, // U
    {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b1010, 0b100}, // V
    {0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001}, // W
    {0b10001, 0b10001, 0b1010, 0b100, 0b1010, 0b10001, 0b10001}, // X
    {0b10001, 0b10001, 0b1010, 0b100, 0b100, 0b100, 0b100}, // Y
    {0b11111, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b11111}, // Z
    {0b1110, 0b1000, 0b1000, 0b1000, 0b1000, 0b1000, 0b1110}, // [
    {0, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0}, // backward slash
    {0b1110, 0b10, 0b10, 0b10, 0b10, 0b10, 0b1110}, // ]
    {0, 0, 0b100, 0b1010, 0b10001, 0, 0}, // ^
    {0, 0, 0, 0, 0, 0, 0b11111}, // _
    {0b1000, 0b100, 0b10, 0, 0, 0, 0}, // `
    {0, 0, 0b1110, 0b1, 0b1111, 0b10001, 0b1111}, // a
    {0b10000, 0b10000, 0b10000, 0b11110, 0b10001, 0b10001, 0b11110}, // b
    {0, 0, 0b1110, 0b10001, 0b10000, 0b10001, 0b1110}, // c
    {0b1, 0b1, 0b1, 0b1111, 0b10001, 0b10001, 0b1111}, // d
    {0, 0, 0b1110, 0b10001, 0b11111, 0b10000, 0b1111}, // e
    {0b1110, 0b1001, 0b11100, 0b1000, 0b1000, 0b1000, 0b1000}, // f
    {0, 0, 0b1110, 0b10001, 0b1111, 0b1, 0b1110}, // g
    {0b10000, 0b10000, 0b10000, 0b11110, 0b10001, 0b10001, 0b10001}, // h
    {0b100, 0, 0b100, 0b100, 0b100, 0b100, 0b1110}, // i
    {0b1, 0, 0b11, 0b1, 0b1, 0b10001, 0b1110}, // j
    {0b10000, 0b10000, 0b10001, 0b10010, 0b11100, 0b10010, 0b10001}, // k
    {0b1100, 0b100, 0b100, 0b100, 0b100, 0b100, 0b1110}, // l
    {0, 0, 0b11110, 0b10101, 0b10101, 0b10101, 0b10101}, // m
    {0, 0, 0b11110, 0b10001, 0b10001, 0b10001, 0b10001}, // n
    {0, 0, 0b1110, 0b10001, 0b10001, 0b10001, 0b1110}, // o
    {0, 0, 0b1111, 0b1001, 0b1110, 0b1000, 0b1000}, // p
    {0, 0, 0b1111, 0b10001, 0b1111, 0b1, 0b1}, // q
    {0, 0, 0b10111, 0b11000, 0b10000, 0b10000, 0b10000}, // r
    {0, 0, 0b1111, 0b10000, 0b1110, 0b1, 0b11110}, // s
    {0b100, 0b100, 0b1110, 0b100, 0b100, 0b100, 0b11}, // t
    {0, 0, 0b10001, 0b10001, 0b10001, 0b10011, 0b1101}, // u
    {0, 0, 0b10001, 0b10001, 0b10001, 0b1010, 0b100}, // v
    {0, 0, 0b10001, 0b10001, 0b10101, 0b11111, 0b10101}, // w
    {0, 0, 0b10001, 0b1010, 0b100, 0b1010, 0b10001}, // x
    {0, 0, 0b10001, 0b10001, 0b1111, 0b1, 0b11110}, // y
    {0, 0, 0b11111, 0b10, 0b100, 0b1000, 0b11111}, // z
    {0b10, 0b100, 0b100, 0b1000, 0b100, 0b100, 0b10}, // {
    {0b100, 0b100, 0b100, 0b100, 0b100, 0b100, 0b100}, // |
    {0b1000, 0b100, 0b100, 0b10, 0b100, 0b100, 0b1000}, // }
    {0, 0, 0, 0b1010, 0b10101, 0, 0} // ~
};
char message[] = "MATRIX LED 7X10 WITH ATMEGA328p";
int string_length = strlen(message);
byte shift_step = 1;

void setup(){
    // PORTB as output.
    // Pin 13: ClockRegister
    // Pin 12: Latch
    // Pin 11: Data
    // Pin 10: Reset
    // Pin 9: Clock4017
    DDRB = 0b111111;
    // Makes sure the 4017 value is 0.
    PORTB |= (1 << PB2);
    PORTB &= ~(1 << PB2);
    Serial.begin(9600);
}

void send_data(uint16_t data){
    uint16_t mask = 1, flag = 0;
    for (byte i=0; i<10; i++){
        flag = data & mask;
        if (flag) PORTB |= (1 << PB3);
        else PORTB &= ~(1 << PB3);
        PORTB |= (1 << PB5);
        PORTB &= ~(1 << PB5);
        mask <<= 1;
    }
    PORTB |= (1 << PB4);
    PORTB &= ~(1 << PB4);
}

void display_pattern(byte loops){
    for (byte c=0; c<string_length; c++){ // For each character
        for (byte scroll=0; scroll<10; scroll++){ // For each column.
            for (byte t=0; t<loops; t++){ // The delay we get with loops.
                for (byte z=0; z<7; z++){ // For each row.
                    // To obtain the position of the current character in the char_data array, subtract 32 from the ASCII value of the character itself.     
                    byte index = message[c];
                    byte temp = char_data[index-32][z];
                    byte index_2 = message[c+1];
                    byte temp_2 = char_data[index_2-32][z];
                    send_data((temp<<scroll) | (temp_2>>(8-shift_step)-scroll));
                    delayMicroseconds(800);
                    // Clear the row so we can go on to the next row without smearing.
                    send_data(0);
                    // On to the next row.
                    PORTB |= (1 << PB1);
                    PORTB &= ~(1 << PB1);
                }
                // Select the first row.
                PORTB |= (1 << PB2);
                PORTB &= ~(1 << PB2);
            }
        }
    }
}

void loop(){
    display_pattern(10);
    delay(100000);
}

I appreciate the help.

2
  • For the strange char at the end of the display, I think your index_2 is accessing beyond the strlen(message) toward the end of the message.
    – hcheung
    Oct 27, 2022 at 2:35
  • Slow down your Youtube video to playback rate of 0.25, you will see why it is not display the char smoothly...
    – hcheung
    Oct 27, 2022 at 2:54

1 Answer 1

2

Here are a few issues that I noticed in this code. I think that, if you manage to address them, you should get a smooth display:

  1. In the expression (temp_2>>(8-shift_step)-scroll), the amount of shift will be negative when scroll gets larger than 7. A righ shift by a negative amount is not a left shift: it is undefined behavior. Judging by the video, it seems the compiler does no shit at all in this case.

  2. If you want a one pixel space between the characters, you should move to the next character in the string every six pixels, thus the scroll variable should count from 0 to 5.

  3. Be aware that there could be up to three characters simultaneously (partially) displayed on the screen: when one character is centered, you should be able to see the edges of both its neighbors.

  4. As noted by hcheung in a comment, when you get to the last character in the string, the expression message[c+1] is reading past the end of that string.

9
  • You are right about the scrolling, I reduced the range of the scroll for to scroll<7 and it doesn't get stuck but the character fades earlier. I can't figure out the correct formula for when the scroll is greater than 6. Video: drive.google.com/file/d/1bkDzEiGrz1c6avpHSnl6ox7D34MD-mp9/… Oct 28, 2022 at 21:31
  • I tried to do this: if (scroll < 7) send_data((temp<<scroll) + (temp_2 >> 6-scroll)); else send_data((temp<<scroll) + (temp_2 << (scroll-6))); But it behaves strangely, the first character goes through, then it blinks, goes back and goes on again. Oct 28, 2022 at 21:33
  • @LucioMazzini: Hard to help debugging without seeing the whole code. That being said, I think the right formula should be send_data(temp<<scroll+6|temp_2<<scroll|temp_3>>6-scroll);. Oct 29, 2022 at 9:52
  • Where temp would be the current character, temp_2 the next character and temp_3 the character that comes after temp_2 right? Oct 29, 2022 at 11:57
  • 1
    Forget it, I already solved it. I started the mask at 0b10000 and scroll the mask to the right and the binary offset is 4-column. It works fine now, thanks. Oct 31, 2022 at 22:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.