在AVR MCU上使用中断通过USART进行传输

时间:2019-11-21 21:18:30

标签: c interrupt avr 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进行编译。有人可以提供解释吗?

1 个答案:

答案 0 :(得分:2)

要理解的关键是,USART有两个用于数据传输的单独的硬件寄存器:void List::deleteLowerThan(int n) { Node* temp = head; int tmpdata; while(temp != nullptr) { tmpdata = temp->data; // save the nodes data temp = temp->next; // step before you delete if(tmpdata < n) { deleteFromList(tmpdata); } } } UDRn,我将在下面简称为Transmit Shift Register现在开始。

当您将数据写入TSR时,假设没有正在进行的发送,它将立即移至UDRn并触发UDRE irq告诉您TSR寄存器是空的”。请注意,此时传输才刚刚开始,但要点是您已经可以将下一个字节写入UDRn

当该字节已完全发送后,下一个字节将从UDRn移至UDRn,并且TSR再次触发。因此,您可以将下一个字节写入UDRE,依此类推。

您只能在UDRn为“空”时将数据写入其中,否则将覆盖其当前正在存储并等待传输的字节。

实际上,您通常不介意UDRn irq,而是想与TXC一起为USART模块提供更多数据。

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

关于您的代码

您的主要问题是,您在TXC中设置了两次UCSR0B,第二次写操作清除了刚设置的位,因此禁用了发送器。您想一次性设置所有位,或者在第二条语句上使用initUART()