C语言 使用中断通过 AVR MCU 上的 USART 进行传输



我相信我了解如何使用中断在 ATmega328p 的 UART 上接收串行数据,但我不了解如何传输数据的机制。

这是一个基本程序,我想用它来使用中断来传输字符串"hello"来驱动传输。 我知道字符"o"可能会被传输两次,我对此表示满意。

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#define BAUD 19200
#define DOUBLE_SPEED 1
void initUART(unsigned int baud, unsigned int speed);
volatile uint8_t charIndex = 0;
volatile unsigned char command[5] = "hello";
int main(void)
{
    //initialize UART
    initUART(BAUD, DOUBLE_SPEED);
    sei();
    //What do I put here to initiate transmission of character string command?
    //Is this even correct?
    UDR0 = command[0];
    while(1)
    { 
    }   
}
ISR(USART_TX_vect) 
{
   // Transmit complete interrupt triggered
    if (charIndex >= 4) 
    {
        //Reach the end of command, end transmission
        return; 
    }
    //transmit the first char or byte
    UDR0 = command[charIndex];
    //Step to the next place of the command
    charIndex++; 
}
void initUART(unsigned int baud, unsigned int speed)
{
    unsigned int ubrr;
    if(speed)
    {
        //double rate mode
        ubrr = F_CPU/8/baud-1;
        //set double speed mode
        UCSR0A = (speed << U2X0);
    }
    else
    {
        //normal rate mode
        ubrr = F_CPU/16/baud-1;
    }
    //set the baud rate
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)(ubrr);
    //enable Tx and Rx pins on MCU
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);
    //enable transmit interrupt
    UCSR0B = (1 << TXCIE0);
    //set control bits, 8 bit char, 0 stop, no parity
    UCSR0C = (1 <<UCSZ00) | (1 <<UCSZ01);
}

我的理解是,如果我将第一个字符写入UDR0(就像我在main((中所做的那样(,这将触发传输完成中断,然后下一个字节将通过ISR传输。 这似乎行不通。

此处显示的代码使用 gcc 进行编译。 有人可以提供解释吗?

要理解的关键是,USART 有 2 个用于数据传输的独立硬件寄存器:UDRnTransmit Shift Register ,从现在开始

我就称之为TSR

当您将数据写入UDRn时,假设没有正在进行 tx,它将立即移动到TSR,并且 UDRE irq 触发以告诉您UDRn寄存器为"空"。请注意,此时传输刚刚开始,但关键是您已经可以将下一个字节写入UDRn

当字节完全传输后,下一个字节从UDRn移动到TSRUDRE再次触发。因此,您可以将下一个字节写入UDRn等等。

您只能在UDRn为"空"时将数据写入该,否则您将覆盖它当前存储和等待传输的字节。

在实践中,您通常不介意 irq TXC,您希望与 UDRE 合作,将更多数据提供给 USART 模块。

但是,如果您需要在传输实际完成时执行某些操作,则 irq TXC很有用。处理RS485时的一个常见示例是在发送完数据后禁用发射器,并可能重新启用本可以禁用的接收器以避免回声。

关于您的代码

您的主要问题是您在initUART()中设置了 2 次UCSR0B,第二次写入清除了您刚刚设置的位,因此它禁用了发射器。您希望一次性设置所有位,或者在第二个语句上使用|=

最新更新