首页 > 解决方案 > UDR 寄存器始终读取 0xFF

问题描述

我有一个应该通过 UART 接收命令的 ATTiny。我有一个简单的 8 个 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 解决了这个问题。

标签: embeddedavruartusartattiny

解决方案


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

  1. 控制器是否配置了正确的时钟?
    • 内部外部
    • 是否为 <util/setbaud.h> 定义了 F_CPU?
    • 是否为 <util/setbaud.h> 定义了 BAUD?
    • 您是否使用像 ATmega16 这样具有特殊寄存器访问权限的控制器?
    • 如果您使用的是外部时钟(不应分割),CKDIV8 是否在 FUSES 或某些控制器的特殊寄存器中被禁用?
  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()函数在做什么......


推荐阅读