【问题标题】:pgm_read_ and PROGMEM - unexpected behaviourpgm_read_ 和 PROGMEM - 意外行为
【发布时间】:2017-10-30 01:30:53
【问题描述】:

我制作了一个播放歌曲的程序(使用蜂鸣器)保存在数组中:

(...)
//function which plays a single note
void playNote(int8 wavelength, int duration) {
    if (wavelength != 1) {
        OCR0A = wavelength; /* set pitch */
        SPEAKER_DDR |= (1 << SPEAKER); /* enable output on speaker */
    }
    while (duration) { /* Variable delay */
        _delay_ms(1);
        duration--;
    }
    SPEAKER_DDR &= ~(1 << SPEAKER); /* turn speaker off */    

//function which plays song from array
void playSong( int8 *song,int tempo){
    int length_16_note = tempo;
    for( int8 i = 0;song[i]) ; i += 2){
        playNote(song[i],song[i+1]*length_16_note);
        _delay_ms(15);
    }
}
int main(void){
//array of macros and lenghts of notes
int8 song1[] = {
    C,4, E1,4, F1,3, E1,3, F1,2, 
    F1,2, F1, 2, B1,2, A1,2, G1,1, F1,2, G1,5,
    G1,4, B1,4, C1,3, F1,3, E1,2,
    B1,2, B1,2, G1,2, B1,2, 
    B1,3, C1,13, 0};
//initialize a timer
initTimer();
//play song
playSong(song1, 150)
}

我省略了其中的某些部分,但这段代码运行良好。现在我想将我的歌曲保存到程序内存中,所以我进行了更改:

#include <avr/pgmspace.h>
(...)      
int8 song1[] PROGMEM = {...}

void playSong( int8 *song, int tempo){
    int length_16_note = tempo;
    for( int8 i = 0; pgm_read_byte(&(song[i])) ; i += 2){
        playNote(pgm_read_byte(&(song[i])), pgm_read_byte(&(song[i+1]))*length_16_note);
        _delay_ms(15);
    }
}

当我在 Arduino 上运行该代码时,我会收到随机的 哔声,持续时间随机(比预期的要长得多)。看起来pgm_read_byte(&amp;(song[i])) 返回随机值。

我试图从函数playSongmain 中提取代码,以便不将数组作为参数传递给函数并且没有任何改变。那么这段代码有什么问题呢?

【问题讨论】:

    标签: c arduino avr atmega


    【解决方案1】:

    我不确定这是否是原因,但我的第一个猜测是您的持续时间比预期的要长,因为您的 for 循环如下所示:

    for( int8 i = 0; pgm_read_byte(&(song[i])) ; i += 2)
    

    这意味着每次执行for循环时,程序都会检查程序空间中的数据,因为它是循环条件。

    avr-libc 用户手册中有一条关于在循环和函数中使用程序存储器读取的警告,见下文。

    用于从程序空间检索数据的宏和函数必须生成一些额外的代码才能实际从程序空间加载数据。这会在代码空间(额外的操作码)和执行时间方面产生一些额外的开销。 (...) 但是您应该意识到这一点,这样您就可以最大限度地减少从程序空间获取相同数据的单个函数中的调用次数。

    此外,您在 for 循环中对 flash 程序进行了两次额外调用。所有这一切意味着,要更改音符,程序需要等待从程序存储器读取 3 个字节并额外延迟 15 毫秒。因此,笔记比预期的要长得多。

    我建议程序首先将整首歌曲读入 RAM 中的某种缓冲区,然后直接从 RAM 中播放。

    【讨论】:

    • 感谢您的回答!问题是不仅音符的持续时间不同,而且音高也不同。我几乎可以肯定 pgm_read_byte 返回的东西与数组中的原始值不同。我将playNote(pgm_read_byte(&amp;(song[i])), pgm_read_byte(&amp;(song[i+1]))*length_16_note); 更改为playNote(pgm_read_byte(&amp;(song[i])), 150);,它播放的“歌曲”完全不同。
    【解决方案2】:

    问题解决了。这里的问题是数组song1 是在main() 函数中声明的。我必须在函数定义之前将数组声明为全局数据:

    #include (...)
    #define (...)
    
    const int8 song1[] PROGMEM = {
    C,4, E1,4, F1,3, E1,3, F1,2,
    F1,2, F1, 2, B1,2, A1,2, G1,1, F1,2, G1,5,
    G1,4, B1,4, C1,3, F1,3, E1,2,
    B1,2, B1,2, G1,2, B1,2,
    B1,3, C1,13, 0};
    
    void playNote(int8 wavelength, int16 duration){...}
    void playSong( int8 *song, int16 tempo){...}
    int main(void){...}
    

    在上面的问题中很难看出问题所在,因为我以误导性的方式编写了代码(在第二个引用中,我在函数定义之前编写了数组 - 但在原始代码中它与之前的位置相同)。对此我很抱歉。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-28
      • 2021-09-14
      • 2017-03-27
      • 2011-05-30
      • 2011-12-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多