了解 ATMEGA168A 下的 UART
Understanding UART under an ATMEGA168A
我正在尝试创建一个 C 程序,它通过 UART 接收一个字符,"prints" 通过在我的面包板上打开 8 个 LED 并将字符发送回发射器来接收对应的二进制文件。
这是我使用的代码:
//CPU clock
#define F_CPU 1000000UL
//Baud
#define BAUD 9600
//Baud rate
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
#include <avr/io.h>
#include <util/delay.h>
#include <util/setbaud.h>
#include <avr/interrupt.h>
#include <stdint.h>
//Communication Parameters:
//8 bits of data
//1 bit stop
//No parity
void uart_init(void){
//Bit 7 - RXCIEn: RX complete interrupt enable
//Bit 6 - TXCIEn: TX complete interrupt enable
//Bit 5 - UDRIE: USART data register empty interrupt enable
//Bit 4 - RXENn: Receiver enable
//Bit 3 - TXENn: Transmitter enable
UCSR0B = 0b10011000;
//Bit 7 - RXCn: USART receive complete.
//Bit 6 - TXCn: USART transmit complete
//Bit 5 - UDREn: USART data register empty
UCSR0A = 0b00000000;
//Bit 11:0 – UBRR11:0: USART baud rate register
//Whereas H are the higher bits and L the lower bits
//It comes from the setbaud.h
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
//Bit 7:6 - UMSELn1:0: USART mode select
//00 Asynchronous USART
//01 Synchronous USART
//11 Master SPI
//Bit 5:3 - Reserved bits in MSPI mode
//Bit 2 - UDORDn: Data order
//Bit 1 - UCPHAn: Clock phase
//Bit 0 - UCPOLn: Clock polarity
UCSR0C = 0b10000110;
}
// function to send data
void uart_transmit (uint8_t data)
{
while (!( UCSR0A & (1<<UDRE0))); // wait while register is free
UDR0 = data; // load data in the register
}
int main (void)
{
//Starts UART
uart_init();
//All led GPIOs as output
DDRB = 0xFF;
DDRC = 0x01;
//Enabling interrupts
sei();
while(1)
{
;
}
return 0;
}
ISR(USART_RX_vect)
{
//Variable to hold the incoming char
uint8_t received_bit = UDR0;
PORTC ^= 0x01;
PORTB = 0x00;
PORTB = received_bit;
uart_transmit(received_bit);
}
当我将其闪存到芯片并开始使用时,出现了一种奇怪的行为。
我正在发送一个 "U",这是一个很好的二进制 01010101 可以与之比较。
然而,我从我的芯片中得到了奇怪的答案:
我关于 ATMEGA168a 下的 UART 的问题如下:
- 设置
F_CPU
时,我应该使用 ATMEGA168a 使用的 1MHZ 还是必须使用我的发射器之一(Intel i7)?会不会是问题所在?
- UDR0 何时获得 "updated"?每当我按下回车键通过终端将字符发送到芯片时?
- 是什么导致了这个问题?
在函数 uart_init()
中,您将位 7:6
设置为 10
,根据 ATMega 168A 手册,这是保留状态。要获得所需的异步 UART 功能,请将它们设置为 00
:
UCSR0C = 0b00000110;
您的示例不起作用的另一个原因是波特率设置,如我在下面的评论中所述。
您已经包含了 <util/setbaud.h>
头文件,其中包含使 UART 设置更容易的宏。查看 here 文档。这些宏采用您在 F_CPU
和 BAUDRATE
中提供的输入并计算 UART 配置寄存器(UBRRH_VALUE
和 UBRRL_VALUE
)的设置。
您几乎正确地使用了它,但是要利用 ATmega 的 UART 波特率加倍功能,请在设置 UBRR0H/L 值后添加以下代码:
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
根据 setbaud 宏的计算设置或清除 U2X0
位。
此外,我相信您可以删除该行
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
因为这正是 setbaud.h 所做的。
我正在尝试创建一个 C 程序,它通过 UART 接收一个字符,"prints" 通过在我的面包板上打开 8 个 LED 并将字符发送回发射器来接收对应的二进制文件。
这是我使用的代码:
//CPU clock
#define F_CPU 1000000UL
//Baud
#define BAUD 9600
//Baud rate
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
#include <avr/io.h>
#include <util/delay.h>
#include <util/setbaud.h>
#include <avr/interrupt.h>
#include <stdint.h>
//Communication Parameters:
//8 bits of data
//1 bit stop
//No parity
void uart_init(void){
//Bit 7 - RXCIEn: RX complete interrupt enable
//Bit 6 - TXCIEn: TX complete interrupt enable
//Bit 5 - UDRIE: USART data register empty interrupt enable
//Bit 4 - RXENn: Receiver enable
//Bit 3 - TXENn: Transmitter enable
UCSR0B = 0b10011000;
//Bit 7 - RXCn: USART receive complete.
//Bit 6 - TXCn: USART transmit complete
//Bit 5 - UDREn: USART data register empty
UCSR0A = 0b00000000;
//Bit 11:0 – UBRR11:0: USART baud rate register
//Whereas H are the higher bits and L the lower bits
//It comes from the setbaud.h
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
//Bit 7:6 - UMSELn1:0: USART mode select
//00 Asynchronous USART
//01 Synchronous USART
//11 Master SPI
//Bit 5:3 - Reserved bits in MSPI mode
//Bit 2 - UDORDn: Data order
//Bit 1 - UCPHAn: Clock phase
//Bit 0 - UCPOLn: Clock polarity
UCSR0C = 0b10000110;
}
// function to send data
void uart_transmit (uint8_t data)
{
while (!( UCSR0A & (1<<UDRE0))); // wait while register is free
UDR0 = data; // load data in the register
}
int main (void)
{
//Starts UART
uart_init();
//All led GPIOs as output
DDRB = 0xFF;
DDRC = 0x01;
//Enabling interrupts
sei();
while(1)
{
;
}
return 0;
}
ISR(USART_RX_vect)
{
//Variable to hold the incoming char
uint8_t received_bit = UDR0;
PORTC ^= 0x01;
PORTB = 0x00;
PORTB = received_bit;
uart_transmit(received_bit);
}
当我将其闪存到芯片并开始使用时,出现了一种奇怪的行为。 我正在发送一个 "U",这是一个很好的二进制 01010101 可以与之比较。 然而,我从我的芯片中得到了奇怪的答案:
我关于 ATMEGA168a 下的 UART 的问题如下:
- 设置
F_CPU
时,我应该使用 ATMEGA168a 使用的 1MHZ 还是必须使用我的发射器之一(Intel i7)?会不会是问题所在? - UDR0 何时获得 "updated"?每当我按下回车键通过终端将字符发送到芯片时?
- 是什么导致了这个问题?
在函数 uart_init()
中,您将位 7:6
设置为 10
,根据 ATMega 168A 手册,这是保留状态。要获得所需的异步 UART 功能,请将它们设置为 00
:
UCSR0C = 0b00000110;
您的示例不起作用的另一个原因是波特率设置,如我在下面的评论中所述。
您已经包含了 <util/setbaud.h>
头文件,其中包含使 UART 设置更容易的宏。查看 here 文档。这些宏采用您在 F_CPU
和 BAUDRATE
中提供的输入并计算 UART 配置寄存器(UBRRH_VALUE
和 UBRRL_VALUE
)的设置。
您几乎正确地使用了它,但是要利用 ATmega 的 UART 波特率加倍功能,请在设置 UBRR0H/L 值后添加以下代码:
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
根据 setbaud 宏的计算设置或清除 U2X0
位。
此外,我相信您可以删除该行
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
因为这正是 setbaud.h 所做的。