UDR 寄存器始终读取 0xFF

UDR register always reads 0xFF

我有一个 ATTiny,它应该通过 UART 接收命令。我有一个由八个 LED 组成的简单显示器,应该显示最近接收到的字节的内容。我正在使用中断来读取接收到的数据。无论我发送什么数据,UDR 总是在中断中读取 0xFF。我知道中断被触发,因为显示从 0x00 变为 0xFF,但它从不显示我通过串行总线发送的值。

这就是我启用 UART 的方式。

UBRRH = UBRRH_VALUE;
UBRRL = UBRRL_VALUE;

#if USE_2X
UCSRA |= (1U << U2X);
#else
UCSRA &= ~(1U << U2X);
#endif

// Enable receiver and interrupt
UCSRB = (1U << RXEN) | (1U << RXCIE);

// No parity, 8 Data Bits, 1 Stop Bit
UCSRC = (1U << UCSZ1) | (1U << UCSZ0);

这是中断中的代码。我已经测试了 display() 并且它自己可以正常运行因此暗示 message 总是 0xFF.

ISR(USART_RXC_vect) {
    uint8_t message = UDR;
    display(message);
}

我确信我的计算机正在发送正确的信息,但我只用伪终端测试它以打印出发送的字节。我打算用示波器窥探硬件连接,但我不认为这是问题所在。是否有什么原因导致 UDR 始终读取为 0xFF?

编辑: 我已经窥探了与示波器的连接,并验证了计算机正在以正确的速率发送正确的数据。然而,ATTiny 没有以正确的波特率运行。在 2400 波特率下,脉冲长度应约为 400 微秒,但微控制器产生的脉冲长度超过 3 毫秒。这解释了为什么它总是读取 0xFF,当控制器认为它正在接收起始位时,计算机将发送几乎整个字节,当控制器试图读取剩余数据时,线路将未被驱动,导致它读取所有数据。我仍然不知道为什么会这样,因为我相信我在控制器上正确设置了波特率。

编辑: 问题已解决。默认情况下,时钟预分频器设置为 8,因此该设备仅以 1MHz 而不是 8MHz 运行。将时钟预分频器设置为 1 解决了问题。

uart 通信可能存在几个问题。首先检查一些东西:

  1. 控制器是否配置了正确的时钟?
    • Internal/External
    • 是否为 定义了 F_CPU?
    • 是否为 定义了波特率?
    • 您是否使用像 ATmega16 这样具有特殊寄存器访问权限的控制器?
    • 如果您使用的是外部时钟(不应分离),是否在某些控制器的 FUSES 或特殊寄存器中禁用了 CKDIV8?
  2. 是:
    • 波特率,
    • 奇偶校验位,
    • 停止位 在发射器和接收器上设置正确

调试:

  • 如果您使用 PC 进行通信,请在 UART 适配器上创建环回并使用终端(TeraTerm、Putty 等)检查您发送的消息是否被正确接收。
  • 您还可以启用 TX 控制器并检查环回是否在您的 uC 上工作。
  • 如果可能尝试将接收到的数据写入一些 LED 以检查是否接收到某个日期
  • 接收器和发射器之间的GND是否连接?
  • 发射器和接收器之间的电压电平是否相同?
  • 发射器和接收器有自己的信号源吗? (那就不要接VCC!)
  • 检查控制器上的时钟是否正确(每秒打开一个具有_delay_ms()功能的led)

示例程序

#define F_CPU 12000000UL
#define BAUD 9600UL

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

ISR(USART_RXC_vect)
{
    volatile unsigned char message = UDR;
    
    // If it is possible try to write the received data to
    // LEDs (if there are some at your board)
    
    display(message);
}

int main()
{
    // To allow changes to clock prescaler it is necessary to set the
    // CCP register (Datasheet page 23)!
    CCP = 0xD8;

    // RESET the clock prescaler from /8 to /1 !!!!
    // Or it is necessary to divide F_CPU through the CLK_PRESCALER 
    CLKPSR = 0x00;

    UBRRH = UBRRH_VALUE;
    UBRRL = UBRRL_VALUE;

    #if USE_2X
        UCSRA |= (1<<U2X);
    #else
        UCSRA &= ~(1<<U2X);
    #endif

    // Enable receiver and interrupt
    UCSRB = (1U << RXEN) | (1U << RXCIE);

    // No parity, 8 Data Bits, 1 Stop Bit
    
    // Not necessary! Mostly ATmega controller
    // have 8 bit mode initialized at startup
    //UCSRC = (1U << UCSZ1) | (1U << UCSZ0);
    
    // If you are using ATmega8/16 it is necessary to do some
    // special things to write to the UBRRH and UCSRC register!
    // See ATmega16 datasheet at page 162
    
    // Do not forget to enable interrupts globally!
    sei();
    
    while(1);
}

请解释 display() 函数的作用...