Good afternoon, a few days ago I finished a personal project of a 7x10 led matrix programmed with the ATMEGA328p microcontroller. To control the matrix I use 2 74HC595 shift registers in cascade in the 10 columns and a CD4017 decade counter in the 7 rows. The 8 rows of the LED matrix are independently connected to a 2N3904 NPN transistor that provides a ground path to sink the combined current of all the LEDs in a row. I've been programming microcontrollers for a few months and I would like some advice to improve my code, both in terms of good practices and program optimization.
Code:
#define CD4017_CLK PORTB1 #define CD4017_RST PORTB2 #define SERIAL_DATA PORTB3 #define ST_CLK PORTB4 #define SH_CLK PORTB5 #define MAX_CHARS 100 #define DELAY 30 #define SHIFT_STEP 1 /* 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]={ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, // space {0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x4}, // ! {0xA, 0xA, 0xA, 0x0, 0x0, 0x0, 0x0}, // " {0xA, 0xA, 0x1F, 0xA, 0x1F, 0xA, 0xA}, // # {0x4, 0xF, 0x14, 0xE, 0x5, 0x1E, 0x4}, // $ {0x18, 0x19, 0x2, 0x4, 0x8, 0x13, 0x3}, // % {0x8, 0x14, 0x14, 0x8, 0x15, 0x12, 0xD}, // & {0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x0}, // ' {0x4, 0x8, 0x10, 0x10, 0x10, 0x8, 0x4}, // ( {0x4, 0x2, 0x1, 0x1, 0x1, 0x2, 0x4}, // ) {0x4, 0x15, 0xE, 0x4, 0xE, 0x15, 0x4}, // * {0x0, 0x4, 0x4, 0x1F, 0x4, 0x4, 0x0}, // + {0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x8}, // , {0x0, 0x0, 0x0, 0x1F, 0x0, 0x0, 0x0}, // - {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4}, // . {0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x0}, // / {0xE, 0x11, 0x13, 0x15, 0x19, 0x11, 0xE}, // 0 {0x6, 0xC, 0x4, 0x4, 0x4, 0x4, 0xE}, // 1 {0xE, 0x11, 0x1, 0x6, 0x8, 0x10, 0x1F}, // 2 {0x1F, 0x1, 0x2, 0x6, 0x1, 0x11, 0xF}, // 3 {0x2, 0x6, 0xA, 0x12, 0x1F, 0x2, 0x2}, // 4 {0x1F, 0x10, 0x1E, 0x1, 0x1, 0x11, 0xE}, // 5 {0xE, 0x11, 0x10, 0x1E, 0x11, 0x11, 0xE}, // 6 {0x1F, 0x1, 0x2, 0x4, 0x8, 0x8, 0x8}, // 7 {0xE, 0x11, 0x11, 0xE, 0x11, 0x11, 0xE}, // 8 {0xE, 0x11, 0x11, 0xF, 0x1, 0x11, 0xE}, // 9 {0x0, 0x0, 0x4, 0x0, 0x4, 0x0, 0x0}, // : {0x0, 0x0, 0x4, 0x0, 0x4, 0x4, 0x8}, // ; {0x2, 0x4, 0x8, 0x10, 0x8, 0x4, 0x2}, // < {0x0, 0x0, 0x1F, 0x0, 0x1F, 0x0, 0x0}, // = {0x8, 0x4, 0x2, 0x1, 0x2, 0x4, 0x8}, // > {0xE, 0x11, 0x1, 0x2, 0x4, 0x0, 0x4}, // ? {0xE, 0x11, 0x17, 0x15, 0x16, 0x10, 0xF}, // @ {0xE, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, // A {0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E}, // B {0xE, 0x11, 0x10, 0x10, 0x10, 0x11, 0xE}, // C {0x1E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E}, // D {0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F}, // E {0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10}, // F {0xE, 0x11, 0x10, 0x17, 0x15, 0x11, 0xE}, // G {0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, // H {0x1F, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1F}, // I {0x1, 0x1, 0x1, 0x1, 0x1, 0x11, 0xE}, // J {0x11, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x11}, // K {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F}, // L {0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11}, // M {0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11}, // N {0xE, 0x11, 0x11, 0x11, 0x11, 0x11, 0xE}, // O {0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10}, // P {0xE, 0x11, 0x11, 0x11, 0x15, 0x13, 0xF}, // Q {0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x11}, // R {0xF, 0x10, 0x10, 0xE, 0x1, 0x1, 0x1E}, // S {0x1F, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4}, // T {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0xE}, // U {0x11, 0x11, 0x11, 0x11, 0x11, 0xA, 0x4}, // V {0x11, 0x11, 0x11, 0x15, 0x15, 0x1B, 0x11}, // W {0x11, 0x11, 0xA, 0x4, 0xA, 0x11, 0x11}, // X {0x11, 0x11, 0xA, 0x4, 0x4, 0x4, 0x4}, // Y {0x1F, 0x1, 0x2, 0x4, 0x8, 0x10, 0x1F}, // Z {0xE, 0x8, 0x8, 0x8, 0x8, 0x8, 0xE}, // [ {0x0, 0x10, 0x8, 0x4, 0x2, 0x1, 0x0}, // backward slash {0xE, 0x2, 0x2, 0x2, 0x2, 0x2, 0xE}, // ] {0x0, 0x0, 0x4, 0xA, 0x11, 0x0, 0x0}, // ^ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F}, // _ {0x8, 0x4, 0x2, 0x0, 0x0, 0x0, 0x0}, // ` {0x0, 0x0, 0xE, 0x1, 0xF, 0x11, 0xF}, // a {0x10, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x1E}, // b {0x0, 0x0, 0xE, 0x11, 0x10, 0x11, 0xE}, // c {0x1, 0x1, 0x1, 0xF, 0x11, 0x11, 0xF}, // d {0x0, 0x0, 0xE, 0x11, 0x1F, 0x10, 0xF}, // e {0xE, 0x9, 0x1C, 0x8, 0x8, 0x8, 0x8}, // f {0x0, 0x0, 0xE, 0x11, 0xF, 0x1, 0xE}, // g {0x10, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x11}, // h {0x4, 0x0, 0x4, 0x4, 0x4, 0x4, 0xE}, // i {0x1, 0x0, 0x3, 0x1, 0x1, 0x11, 0xE}, // j {0x10, 0x10, 0x11, 0x12, 0x1C, 0x12, 0x11}, // k {0xC, 0x4, 0x4, 0x4, 0x4, 0x4, 0xE}, // l {0x0, 0x0, 0x1E, 0x15, 0x15, 0x15, 0x15}, // m {0x0, 0x0, 0x1E, 0x11, 0x11, 0x11, 0x11}, // n {0x0, 0x0, 0xE, 0x11, 0x11, 0x11, 0xE}, // o {0x0, 0x0, 0xF, 0x9, 0xE, 0x8, 0x8}, // p {0x0, 0x0, 0xF, 0x11, 0xF, 0x1, 0x1}, // q {0x0, 0x0, 0x17, 0x18, 0x10, 0x10, 0x10}, // r {0x0, 0x0, 0xF, 0x10, 0xE, 0x1, 0x1E}, // s {0x4, 0x4, 0xE, 0x4, 0x4, 0x4, 0x3}, // t {0x0, 0x0, 0x11, 0x11, 0x11, 0x13, 0x13}, // u {0x0, 0x0, 0x11, 0x11, 0x11, 0xA, 0x4}, // v {0x0, 0x0, 0x11, 0x11, 0x15, 0x1F, 0x15}, // w {0x0, 0x0, 0x11, 0xA, 0x4, 0xA, 0x11}, // x {0x0, 0x0, 0x11, 0x11, 0xF, 0x1, 0x1E}, // y {0x0, 0x0, 0x1F, 0x2, 0x4, 0x8, 0x1F}, // z {0x2, 0x4, 0x4, 0x8, 0x4, 0x4, 0x2}, // { {0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4}, // | {0x8, 0x4, 0x4, 0x2, 0x4, 0x4, 0x8}, // } {0x0, 0x0, 0x0, 0xA, 0x15, 0x0, 0x0}, // ~ }; uint16_t frame_buffer[7]; char message[MAX_CHARS+2] = "MATRIZ LED 7X10 "; int string_length = strlen(message); 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 << SERIAL_DATA); else PORTB &= ~(1 << SERIAL_DATA); PORTB |= (1 << SH_CLK); PORTB &= ~(1 << SH_CLK); mask <<= 1; } PORTB |= (1 << ST_CLK); PORTB &= ~(1 << ST_CLK); } void send_frame_buffer(){ for (byte t=0; t<DELAY; t++){ // The delay we get with loops. for (byte row=0; row<7; row++){ // For each row. // Send 10 bits to shift registers. send_data(frame_buffer[row]); // This delay defines the time to play each pattern. 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 << CD4017_CLK); PORTB &= ~(1 << CD4017_CLK); } // Select the first row. PORTB |= (1 << CD4017_RST); PORTB &= ~(1 << CD4017_RST); } } void display_message(){ for (byte c=0; c<string_length; c++){ // For each character. byte mask = 0x10; for (byte column=0; column<5; column++){ // For each column. for (byte row=0; row<7; row++){ // 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][row]; frame_buffer[row] = (frame_buffer[row]<<1) | ((temp&mask)>>4-column); } send_frame_buffer(); mask >>= 1; } // One column of separation between characters. for (byte row=0; row<7; row++){ frame_buffer[row] <<= SHIFT_STEP; } send_frame_buffer(); } } int main(){ // PORTB as output. DDRB = 0b111111; // Makes sure the 4017 value is 0. PORTB |= (1 << CD4017_RST); PORTB &= ~(1 << CD4017_RST); while (true){ display_message(); } }