使用 UDRE 和 ATmega328P 的中断驱动 USART

Posted

技术标签:

【中文标题】使用 UDRE 和 ATmega328P 的中断驱动 USART【英文标题】:Interrupt-driven USART using UDRE with ATmega328P 【发布时间】:2018-04-11 14:04:54 【问题描述】:

我在用 C 语言为 Arduino 编写串行时遇到问题。应用程序需要速度,所以我需要用 C 语言来完成。我正在使用 Codeblocks,因为它很容易编译。

由于我还希望串行功能不会阻塞应用程序并提供调试功能,因此我正在尝试编写循环缓冲区类型的串行库。应用程序不需要接收数据,只需要打印。

问题始于串行中断显然没有触发并且程序阻塞,编译器确实给出了警告:

main.c|11|warning: ‘UART_UDRE_vect’ appears to be a misspelled signal handler|

测试程序如下:

#include<avr/io.h>
#include<avr/interrupt.h>

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU/(USART_BAUDRATE*16UL)))-1)

char ok = 0;

ISR(UART_UDRE_vect) 
    ok = 1;
    UCSR0B &= ~(1<<5);


int main(void) 
  UBRR0H  = (BAUD_PRESCALE >> 8);
  UBRR0L  = BAUD_PRESCALE;
  UCSR0B |= (1<<TXEN0);
  UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01);
  sei();
  while(1)
    // write the byte to the serial port
    UDR0 = '0';
    UCSR0B |= (1<<5);
    while(ok != 1)
    ok = 0;
    UDR0 = '\n';
    UCSR0B |= (1<<5);
    while(ok != 1)
    ok = 0;
  
  return 0;

配置和波特率是正确的,因为找到 here 的 echo 示例确实有效。

还有其他示例不使用 UDRE,仅使用 RX 中断,这不是我想要的。

我是不是忘记了什么?

【问题讨论】:

我根本不知道这个工具链,但是一个常见的问题是忘记在中断向量表中注册中断。这是怎么做到的? 中断列在 avr interrupt.h 库中,如下所述:link Arduino 使用 C++,不是吗? 好的。您的特定 AVR 衍生产品未在 UART_UDRE_vect 下方列出。不应该命名为USART_UDRE_vect 吗? 与中断共享的变量必须声明为volatile,否则编译器可能无法理解它们已被使用,因此在优化时会破坏您的程序。 static volatile char ok; 【参考方案1】:

两个问题:

根据您链接的documentation,您的特定零件ATmega328p 的中断向量应命名为USART_UDRE_vect

与中断共享的变量必须始终声明为volatile,否则编译器可能无法理解它们已被使用,因此在优化时会破坏您的程序。基本上,编译器认为变量ok 永远不会得到比0 更大的值,因为它在任何地方都看不到对ISR 的函数调用(因为它是由硬件调用的,而不是由程序调用的)。将声明更改为static volatile char ok;

【讨论】:

以上是关于使用 UDRE 和 ATmega328P 的中断驱动 USART的主要内容,如果未能解决你的问题,请参考以下文章

328p芯片引脚间距

使用 FTDI Basic 回读 ATmega328p 程序

Atmega328P 中的奇怪延迟行为

atmega328p 单片机里面有自带蜂鸣器吗

ATMEGA328P引脚定义图

SPI 在 atmega328p 上不起作用