首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >矩阵7x10与arduino

矩阵7x10与arduino
EN

Code Review用户
提问于 2022-11-05 21:38:14
回答 1查看 96关注 0票数 2

下午好,几天前,我完成了一个用ATMEGA328p微控制器编程的7x10 led矩阵的个人项目。为了控制矩阵,我使用2 74HC595移位寄存器级联在10列和CD4017十年计数器在7行。所述LED矩阵的8行独立地连接到2N3904 NPN晶体管,该晶体管提供一条接地路径以将所有LED的合并电流汇到一行中。我已经对微控制器进行了几个月的编程,我希望得到一些改进我的代码的建议,无论是在良好的实践方面还是在程序优化方面。

代码:

代码语言:javascript
复制
#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();
    }
}

视频:https://www.youtube.com/shorts/neCM424JKCY

EN

回答 1

Code Review用户

回答已采纳

发布于 2022-11-06 21:57:43

代码一点也不坏,所以大多数情况下,这些都是关于样式的注释,不会对底层机器代码产生重大影响,我们将看到。

尽量不要混淆读者

这句话有点奇怪:

代码语言:javascript
复制
char message[MAX_CHARS+2] = "MATRIZ LED 7X10 ";

很明显,MAX_CHARS并不是指MAX_CHARS?我建议简单地将MAX_CHARS的值增加两倍。就不会那么奇怪了。

为可读性

添加函数

当前代码包括此函数。

代码语言:javascript
复制
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);
}

当然,这是可行的,但它很容易出错。当您指的是|=时,输入&=或忘记~是很容易的。此外,处理时钟从高到低的代码行必须成对操作。出于所有这些原因,我宁愿这样写:

代码语言:javascript
复制
void send_data(uint16_t data){
    for (uint16_t mask{1}; mask & 0x3ff; mask <<= 1) {
        bit(SERIAL_DATA, data & mask);
        bithilo(SH_CLK);
    }
    bithilo(ST_CLK);
}

您可以使用Arduino平台提供的digitalWrite函数,也可以像我在这里所做的那样创建更高效的函数:

代码语言:javascript
复制
inline void setbit(int bitnum) {
    PORTB |= (1 << bitnum);
}
inline void clrbit(int bitnum) {
    PORTB &= ~(1 << bitnum);
}
inline void bit(int bitnum, bool val) {
    if (val)
        setbit(bitnum);
    else
        clrbit(bitnum);
}
inline void bithilo(int bitnum) {
    setbit(bitnum);
    clrbit(bitnum);
}

有人可能会反对它是更多的代码,或者它会使程序变慢,但实际上,作为这个在线示例显示,这两个版本产生相同的汇编语言指令序列。

将这些函数标记为inline是向编译器建议的一种方法,即这段代码的内联替换可能比将代码作为子例程实际调用更好。在这种特殊情况下,它允许编译器的优化器认识到可以使用非常高效的sbicbi汇编语言指令来完成操作。

这句话:for (uint16_t mask{1}; mask & 0x3ff; mask <<= 1)一开始看起来很混乱,但它是一个常见的成语,尤其是对于嵌入式系统。我们想一次提取一位,十位。0x3ff是一个设置了低10位的uint16_t。当我们将位移到左边时,mask & 0x3ff的值将是一个非零的数字,直到我们到达第11位为止。此时,表达式为0,C++将其解释为false,因此循环结束。

正如您可以从在线编译器演示中看到的那样,这两个版本都会产生相同的代码,因此我主张使用您认为最容易阅读和维护的代码。

使识别数据错误变得更容易

下面是“0”字符的代码,但其中有一个错误:

代码语言:javascript
复制
    {0xE, 0x11, 0x13, 0x15, 0x1B, 0x11, 0xE}, // 0 but with an error

你需要多长时间才能发现错误?我建议做两件事之一。要么使用运行在计算机上的外部程序(而不是Arduino)生成可视化和十六进制表单,要么以二进制方式编写数据,以便更容易地查看所发生的事情:

代码语言:javascript
复制
    { 0b01110,
      0b10001,
      0b10011,
      0b10101,
      0b11011,  // <-- this line has the error
      0b10001,
      0b01110
    },

这仍然不是很好,但我认为,对于一个人来说,要验证所有行都是5位,2)每个字符都有一个合理的表示法要容易得多。考虑一下,如果您被要求更改此字符,使其没有对角线在其中。在这种形式下会简单得多。

考虑使用对象

Arduino的语言实际上是一个C++变体,它具有一些非常有用的面向对象特性。例如,您可以将显示包装到一个对象中,以便如果您想要更改它以容纳两个10x7显示器,并行显示作为20x7显示器,则代码其余部分的接口可以保持不变。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/281001

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档