1

试图让 ATmega162 USART 启动并运行。这段代码完全符合我的期望:

#define F_CPU 14745600UL
#define UBRR_1 F_CPU / 16 / 9600 - 1
#define UBRR_2 F_CPU / 16 / 31250 - 1

#include <inttypes.h>

#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>

int main(){
  uint16_t ubrr1 = UBRR_1;

  UBRR0H = (uint8_t)(ubrr1 >> 8);
  UBRR0L = (uint8_t)ubrr1;
  UCSR0B = _BV(TXEN0);
  UCSR0C = _BV(URSEL0) | _BV(UCSZ00) | _BV(UCSZ01);

  uint16_t ubrr2 = UBRR_2;

  UBRR1H = (uint8_t)(ubrr2 >> 8);
  UBRR1L = (uint8_t)ubrr2;
  UCSR1B = _BV(RXEN1);
  UCSR1C = _BV(URSEL1) | _BV(UCSZ10) | _BV(UCSZ11);

  DDRB = _BV(PB0) | _BV(PB1);

  PORTB |= _BV(PB0);

  while (1){
    PORTB ^= _BV(PB0);
    _delay_ms(50);

    // byte received on usart 1
    if ((UCSR1A & _BV(RXC1)) != 0){

      // usart 0 ready to write
      if ((UCSR0A & _BV(UDRE0)) != 0){
        uint8_t b = UDR1;
        UDR0 = b;
      }
    }
  }

  return 0;
}

也就是说,以不同的波特率初始化两个 USART,从 USART1 读取并写入 USART0。效果很好。是的,我知道 _delay_ms() 弄乱了时间,但在这个例子中它工作得很好。现在,只要我在 USART1 上启用 RX 中断并添加适当的向量,主循环就会停止运行(至少 LED 不闪烁):

#define F_CPU 14745600UL
#define UBRR_1 F_CPU / 16 / 9600 - 1
#define UBRR_2 F_CPU / 16 / 31250 - 1

#include <inttypes.h>

#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>

int main(){
  uint16_t ubrr1 = UBRR_1;

  UBRR0H = (uint8_t)(ubrr1 >> 8);
  UBRR0L = (uint8_t)ubrr1;
  UCSR0B = _BV(TXEN0);
  UCSR0C = _BV(URSEL0) | _BV(UCSZ00) | _BV(UCSZ01);

  uint16_t ubrr2 = UBRR_2;

  UBRR1H = (uint8_t)(ubrr2 >> 8);
  UBRR1L = (uint8_t)ubrr2;
  UCSR1B = _BV(RXEN1);
  UCSR1C = _BV(URSEL1) | _BV(UCSZ10) | _BV(UCSZ11);

  DDRB = _BV(PB0) | _BV(PB1);

  // enable usart1 rx interrupt
  UCSR1B |= _BV(RXCIE1);

  PORTB |= _BV(PB0);

  // enable interrupts
  sei();

  while (1){
    PORTB ^= _BV(PB0);
    _delay_ms(50);
  }

  return 0;
}

ISR(USART1_RXC_vect){
  uint8_t byte = UDR1;

  if ((UCSR0A & _BV(UDRE0)) != 0){
    UDR0 = byte;
  }
}

最奇怪的是,让程序停止工作的不是sei();和行——而是 ISR 的存在。UCSR1B |= _BV(RXCIE1);一旦我注释掉该函数,主循环就会正常执行。我在某处错过了一面旗帜吗?

4

1 回答 1

0

这可能是由 M161C 熔丝位(在扩展熔丝字节中)被编程引起的。这会将 ATmega162 置于 ATmega161 兼容模式,这会导致设备具有不同的中断向量表布局(编译器不会知道)。有关详细信息,请参见第 57 页此处的输入链接描述。您可以通过使用 -mmcu=atmega161 进行编译来测试它,看看它是否能解决问题。

会导致类似行为的另一件事是,如果此代码在(几乎相同的)ATmega16 而不是 ATmega162 上运行,因为 UDR1 寄存器位于 IO 寄存器映射中的不同位置,这意味着 RXC 中断标志​​永远不会被清除,处理程序将永远重新进入。您可以通过使用 avr-objdump 反汇编来检查编译器正在使用的寄存器值。

于 2013-10-19T14:11:51.917 回答