embedded - 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 解决了这个问题。
解决方案
uart 通信可能存在几个问题。首先检查一些事情:
- 控制器是否配置了正确的时钟?
- 内部外部
- 是否为 <util/setbaud.h> 定义了 F_CPU?
- 是否为 <util/setbaud.h> 定义了 BAUD?
- 您是否使用像 ATmega16 这样具有特殊寄存器访问权限的控制器?
- 如果您使用的是外部时钟(不应分割),CKDIV8 是否在 FUSES 或某些控制器的特殊寄存器中被禁用?
- 是:
- 波特率,
- 奇偶校验位,
- 发送器和接收器上的停止位设置正确
调试:
- 如果您使用 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()
函数在做什么......
推荐阅读
- java - Max Live Size 和 Max Live Count 没有意义
- android - 如何在 SQLiteDatabase 中只记录一次?
- javascript - 如何在香草 javascript 中定位兄弟姐妹?
- firebase - 交易中删除的 Firestore 节点
- sql - 如何将 SQL 查询中一个字段的值用于 Oracle Apex 上另一个字段的值列表?
- ssl - 如何在 Kubernetes 中的微服务之间进行 TLS?
- r - 如何在 R 中将字符 Ym 格式化为月份?
- python-3.x - 将字符串拆分为两个不同的列 pandas
- javascript - 如何在浏览器中显示 MVIMG(谷歌相机动态照片)?
- python - 抓取数据并推送到弹性搜索时出现序列化错误