1

有一个奇怪的问题。我终于想出了如何将一个在循​​环内递增的变量“i”转换为一个字符串,我可以将它传递给一个函数,该函数将它转换为一个输出到显示器的字符。

问题是,值增加了 2 而不是 1!我尝试了一些不同的方法,但我不确定问题出在哪里。

最初,我想也许,如果你有一个调用另一个函数的函数,并且它们都使用相同的变量来递增(即 for(int i=0; i<10; ++i)),那么“外部”函数将使“i”增加 2,因为它在“外部”循环中增加一次,在“内部”循环中增加一次。但是,我认为情况并非如此。如果是这样,在我的情况下,“i”会增加两个以上,我尝试将所有计数器变量更改为不同的名称,而没有任何变化。无论如何,这将是一种愚蠢的语言工作方式。除非当然是这样,否则我很想开悟。

这是给我带来麻烦的代码块:

for (int i=0; i<100; i++){
    char c[1]={0};                    // Create variable to hold character
    sprintf(c,"%d", i);               // Copy value of "i" as string to variable
    writeText(c,0,0,WHITE,BLACK,3);   // Write the character "c" at position 0,0. Size 3
    OLED_buffer();                    // Send display buffer
    delay_ms(500);                    // Delay before next increment
}

这是 writeText():

void writeText(unsigned char *string, int16_t x, int16_t y, uint16_t color, uint16_t bgcolor, uint8_t size){
    unsigned char letter;
    for (int i=0; i<strlen(string); ++i){
        letter = string[i];
        if (letter != NULL){
            drawChar(x+(i*6*size),y,letter,color,bgcolor,size);
        }
    }
}

这是由 writeText 调用的 drawChar:

void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size) {

    if((x >= _width)            ||  // Clip right
    (y >= _height)              ||  // Clip bottom
    ((x + 5 * size - 1) < 0)    ||  // Clip left
    ((y + 8 * size - 1) < 0))       // Clip top
    return;

    for (int8_t i=0; i<6; i++ ) {
        uint8_t line;
        if (i == 5)
        line = 0x0;
        else
        line = font[(c*5)+i];
        for (int8_t j = 0; j<8; j++) {
            if (line & 0x1) {
                if (size == 1) // default size
                drawPixel(x+i, y+j, color);
                else {  // big size
                    fillRect(x+(i*size), y+(j*size), size, size, color);
                }
                } else if (bg != color) {
                if (size == 1) // default size
                drawPixel(x+i, y+j, bg);
                else {  // big size
                    fillRect(x+i*size, y+j*size, size, size, bg);
                }
            }
            line >>= 1;
        }
    }
}

最后是 drawPixel,由 drawChar 调用(尽管我真诚地怀疑问题是否如此之深):

void drawPixel(int16_t x, int16_t y, uint16_t color) {
    if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
    return;

    // check rotation, move pixel around if necessary
    switch (getRotation()) {
        case 1:
        swap(x, y);
        x = WIDTH - x - 1;
        break;
        case 2:
        x = WIDTH - x - 1;
        y = HEIGHT - y - 1;
        break;
        case 3:
        swap(x, y);
        y = HEIGHT - y - 1;
        break;
    }

    // x is which column
    if (color == WHITE)
    buffer[x+(y/8)*SSD1306_LCDWIDTH] |= _BV((y%8));
    else
    buffer[x+(y/8)*SSD1306_LCDWIDTH] &= ~_BV((y%8));
}

所有这一切的结果是,显示器显示的数字增加了我的延迟长度的两倍。例如这里的延迟是 500 毫秒,所以它每 1 秒更新一次。而不是去

1、2、3、4、5...

正如它应该的那样

1、3、5、7、9……

有没有人可以提供任何建议?我确信在我的初始循环中这是一个愚蠢的简单问题,但我现在看不到它。

我正在使用 Atmel Studio 6 对 xmega32a4u 进行编程。显示的库函数是我移植到 Atmel Studio 的 SSD1306 128x32 OLED 的 Adafruit 图形库的一部分。

非常感谢你的帮忙!

更新:虽然我的代码确实存在一些问题,但真正的问题实际上在于 OLED 的处理方式。显然 Adafruit 忘记在他们的库中为显示设置正确的页面地址。由于显示器上的控制器可以支持 128x64 和 128x32 显示器,因此必须正确设置显示器的“结束”地址,以便控制器知道要访问显示 RAM 的哪些部分。那个功能不见了。由于显示器如何写入数据内存,并且因为它不“知道”显示器只有 32 像素高,所以发送到显示器的每隔一帧实际上都被写入了显示器内存的“底部”部分(即如果显示器是 128x64,高两倍时会出现的部分)。所以现在一切都很好!

非常感谢 unwind,如果不是因为他关于显示时间的建议让我想到了问题的另一面,我可能需要很长时间才能找出问题所在。

4

1 回答 1

2

你有一个缓冲区溢出。

这个:

char c[1]={0};                    // Create variable to hold character
sprintf(c,"%d", i);   

没有在字符串缓冲区中分配足够的空间c来保存一位数的字符串。请记住,C 中的字符串以 0 结尾,因此 1 位字符串需要 2 个字符。由于您的循环达到 100,您最终将向缓冲区写入 3 + 1 个字符,从而覆盖更多字符。不知道你想象这是如何工作的。

这很可能sprintf()会覆盖您的循环索引变量,尽管由于您遇到未定义的行为,任何事情都可能发生。

将这两行更改为:

char c[8];
sprintf(c, "%d, i);

或者,如果有,请使用snprintf()

snprintf(c, sizeof c, "%d", i);

以防止缓冲区溢出。

如果您只想要 的最低有效数字i,请执行以下操作:

snprintf(c, sizeof c, "%d", i % 10);

这使用模(%在 C 中)运算符来计算除以 10 时的余数,即“个数”数字。

更新阅读您的评论后,我倾向于认为您的问题是时间问题之一,也许显示内容没有按照您的预期刷新,因此您只能看到您构建的每一秒“帧”。您应该能够使用调试器很容易地看到您确实构建并显示了每个数值,方法是sprintf()在根循环中中断。

更新2:因为它困扰我,你的writeText()功能可以简化很多,一个字符的比较NULL是奇怪的(NULL是一个指针,而不是一个字符)并且毫无意义,因为你已经检查过strlen()

void writeText(const unsigned char *string, int16_t x, int16_t y, uint16_t color,
               uint16_t bgcolor, uint8_t size)
{
  while(*string != '\0')
  {
    drawChar(x + (i * 6 * size), y, *string, color, bgcolor, size);
    ++string;
  }
}

还要注意const; 应始终声明采用指向仅由函数读取const的数据的指针的函数。

于 2013-06-04T07:50:05.097 回答