gpt4 book ai didi

c - AVR ATmega 在主循环之前使用 printf 时不断重置

转载 作者:太空狗 更新时间:2023-10-29 16:07:34 27 4
gpt4 key购买 nike

我正在使用 avr-libc 开发 C 应用程序在 AVR 上 ATmega328P微 Controller 。因为我没有它的 ICE 调试器,所以我遵循了 these instructionsthis tutorial用于使 stdio.h 函数(例如 printf)能够将硬件 UART 作为 stdout 使用。

这行得通,我可以在连接到我的目标板的 PC 终端上看到输出,但奇怪的是:当我在主程序上只有一个 printf 时,但在主循环之前导致处理器重置,而如果我只有在主循环内或主循环之前和循环内有 printf,它就可以正常工作。像这样:

#include <stdio.h>

/* stream definitions for UART input/output */
FILE uart_output = FDEV_SETUP_STREAM(uart_drv_send_byte, NULL, _FDEV_SETUP_WRITE);
FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_drv_read_byte, _FDEV_SETUP_READ);

int main() {
/* Definition of stdout and stdin */
stdout = &uart_output;
stdin = &uart_input;

/* Configures Timer1 for generating a compare interrupt each 1ms (1kHz) */
timer_init()

/* UART initialization */
uart_drv_start(UBRRH_VALUE, UBRRL_VALUE, USE_2X, &PORTB, 2);

/* Sets the sleep mode to idle */
set_sleep_mode(SLEEP_MODE_IDLE);

printf("START ");

/* main loop */
while(1) {
printf("LOOP ");

/* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */
sleep_mode();
}
}

上面的代码产生以下输出:

START LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP

这是预期的。如果我们注释 printf("START ") 行,它会产生这个:

LOOP LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP

这也很好。问题是,如果我在 while 循环中没有任何 printf,它会像这样:

START START START START START START ... START

这清楚地表明处理器正在重新启动,因为预期的输出将只是一个 START,而无限循环仅在 1 kHz 定时器中断时才被唤醒。为什么会这样?我应该强调没有配置看门狗定时器(如果有,只打印 LOOP 的情况也会被新的 START 中断)。

使用 GPIO 引脚监控执行

为了尝试深入了解情况,我在主循环中围绕有问题的 print("START ")sleep_mode 打开和关闭 GPIO 引脚:

int main() {

/* Irrelevant parts suppressed... */

GPIO1_ON;
printf("START ");
GPIO1_OFF;

/* Main loop */
while(1) {

/* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */
GPIO2_ON;
sleep_mode();
GPIO2_OFF;
}
}

事实证明,GPIO1 保持打开状态 132 微秒(printf("START ") 调用时间)然后关闭 6.6 毫秒 - 大致是以 9600 位/秒传输六个字符的时间- GPIO2 切换 12 次(6 次两次中断:UART 准备传输中断和 UART 空数据寄存器中断),在 GPIO1 再次打开之前显示 sleep 事件另外 1.4 毫秒,指示新的 printf("START ") - 因此在重置之后。我可能不得不检查 UART 代码,但我很确定非中断 UART 版本也显示了同样的问题,而且这也不能解释为什么 printf 在主循环工作正常,没有发生重置(我希望如果 UART 代码有问题,无论如何都会发生重置)。


(已解决!):为了完整起见,UART 初始化和 TX 代码如下**

这是我第一次尝试为 AVR 编写中断驱动的 UART 驱动程序,但它可以用于 RS-232 或 RS-485,这需要在传输数据时激活 TX_ENABLE 引脚。结果是,因为我必须让代码在 ATmega328P 或 ATmega644 上可用,所以中断 vector 有不同的名称,所以我使用 #define TX_VECTOR 根据所使用的处理器采用正确的名称.在制作和测试驱动程序的过程中,为 UDRE 数据空中断选择“TX_VECTOR”最终掩盖了我还没有定义 USART0_TX_vect 的事实(这是正在进行的工作,我可能无论如何甚至都不需要...)

现在我刚刚定义了一个空的 interrupt service routine (ISR) for USART0_TX_vect 并且该东西不再重置,显示@PeterGibson 将其钉牢。非常感谢!

// Interrupt vectors for Atmega328P
#if defined(__AVR_ATmega328P__)
#define RX_VECTOR USART_RX_vect
#define TX_VECTOR USART_UDRE_vect
// Interrupt vectors for Atmega644
#elif defined(__AVR_ATmega644P__)
#define RX_VECTOR USART0_RX_vect
#define TX_VECTOR USART0_UDRE_vect
#endif

ISR(TX_VECTOR)
{
uint8_t byte;

if (!ringbuffer_read_byte(&txrb, &byte)) {

/* If RS-485 is enabled, sets TX_ENABLE high */
if (TX_ENABLE_PORT)
*TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN);
UDR0 = byte;
}
else {
/* No more chars to be read from ringbuffer, disables empty
* data register interrupt */
UCSR0B &= ~_BV(UDRIE0);
}

/* If RS-485 mode is on and the interrupt was called with TXC0 set it
* means transmission is over. TX_ENABLED should be cleared. */
if ((TX_ENABLE_PORT) && (UCSR0A & _BV(TXC0) & _BV(UDR0))) {
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
UCSR0B &= ~_BV(UDRIE0);
}
}

void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x,
volatile uint8_t* rs485_tx_enable_io_port,
uint8_t rs485_tx_enable_io_pin)
{
/* Initializes TX and RX ring buffers */
ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE);
ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE);

/* Disables UART */
UCSR0B = 0x00;

/* Initializes baud rate */
UBRR0H = ubrrh;
UBRR0L = ubrrl;
if (use2x)
UCSR0A |= _BV(U2X0);
else
UCSR0A &= ~_BV(U2X0);

/* Configures async 8N1 operation */
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);

/* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE,
* configures the pin as output and enables the TX data register empty
* interrupt so it gets disabled in the end of transmission */
if (rs485_tx_enable_io_port) {
TX_ENABLE_PORT = rs485_tx_enable_io_port;
TX_ENABLE_PIN = rs485_tx_enable_io_pin;
/* Configures the RS-485 driver as an output (on the datasheet the data
* direction register is always on the byte preceding the I/O port addr) */
*(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN);
/* Clears TX_ENABLE pin (active high) */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
/* Enables end of transmission interrupt */
UCSR0B = _BV(TXCIE0);
}
/* Enables receptor, transmitter and RX complete interrupts */
UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);
}

固定的 UART 代码(现在可以 100% 工作!)

为了帮助任何有兴趣或为 AVR ATmega 开发类似中断驱动的 UART 驱动程序的人,这里是修复并测试了上述问题的代码。感谢所有帮助我发现缺失 ISR 问题的人!

// Interrupt vectors for Atmega328P
#if defined(__AVR_ATmega328P__)
#define RX_BYTE_AVAILABLE USART_RX_vect
#define TX_FRAME_ENDED USART_TX_vect
#define TX_DATA_REGISTER_EMPTY USART_UDRE_vect
// Interrupt vectors for Atmega644
#elif defined(__AVR_ATmega644P__)
#define RX_BYTE_AVAILABLE USART0_RX_vect
#define TX_FRAME_ENDED USART0_TX_vect
#define TX_DATA_REGISTER_EMPTY USART0_UDRE_vect
#endif

/* I/O port containing the pin to be used as TX_ENABLE for the RS-485 driver */
static volatile uint8_t* TX_ENABLE_PORT = NULL;

/** Pin from the I/O port to be used as TX_ENABLE for the RS-485 driver */
static volatile uint8_t TX_ENABLE_PIN = 0;

ISR(RX_BYTE_AVAILABLE)
{
// Read the status and RX registers.
uint8_t status = UCSR0A;
// Framing error - treat as EOF.
if (status & _BV(FE0)) {
/* TODO: increment statistics */
}
// Overrun or parity error.
if (status & (_BV(DOR0) | _BV(UPE0))) {
/* TODO: increment statistics */
}
ringbuffer_write_byte(&rxrb, UDR0);
}

ISR(TX_FRAME_ENDED)
{
/* The end of frame interrupt will be enabled only when in RS-485 mode, so
* there is no need to test, just turn off the TX_ENABLE pin */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
}

ISR(TX_DATA_REGISTER_EMPTY)
{
uint8_t byte;

if (!ringbuffer_read_byte(&txrb, &byte)) {
/* If RS-485 is enabled, sets TX_ENABLE high */
if (TX_ENABLE_PORT)
*TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN);
UDR0 = byte;
}
else {
/* No more chars to be read from ringbuffer, disables empty
* data register interrupt */
UCSR0B &= ~_BV(UDRIE0);
}
}

void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x,
volatile uint8_t* rs485_tx_enable_io_port,
uint8_t rs485_tx_enable_io_pin)
{
/* Initializes TX and RX ring buffers */
ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE);
ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE);

cli();

/* Disables UART */
UCSR0B = 0x00;

/* Initializes baud rate */
UBRR0H = ubrrh;
UBRR0L = ubrrl;
if (use2x)
UCSR0A |= _BV(U2X0);
else
UCSR0A &= ~_BV(U2X0);

/* Configures async 8N1 operation */
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);

/* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE,
* configures the pin as output and enables the TX data register empty
* interrupt so it gets disabled in the end of transmission */
if (rs485_tx_enable_io_port) {
TX_ENABLE_PORT = rs485_tx_enable_io_port;
TX_ENABLE_PIN = rs485_tx_enable_io_pin;

/* Configures the RS-485 driver as an output (on the datasheet the data
* direction register is always on the byte preceding the I/O port addr) */
*(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN);

/* Clears TX_ENABLE pin (active high) */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);

/* Enables end of transmission interrupt */
UCSR0B = _BV(TXCIE0);
}
/* Enables receptor, transmitter and RX complete interrupts */
UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);

sei();
}

void uart_drv_send_byte(uint8_t byte, FILE *stream)
{
if (byte == '\n') {
uart_drv_send_byte('\r', stream);
}
uint8_t sreg = SREG;
cli();

/* Write byte to the ring buffer, blocking while it is full */
while(ringbuffer_write_byte(&txrb, byte)) {
/* Enable interrupts to allow emptying a full buffer */
SREG = sreg;
_NOP();
sreg = SREG;
cli();
}

/* Enables empty data register interrupt */
UCSR0B |= _BV(UDRIE0);
SREG = sreg;
}

uint8_t uart_drv_read_byte(FILE *stream)
{
uint8_t byte;
uint8_t sreg = SREG;
cli();
ringbuffer_read_byte(&rxrb, &byte);
SREG = sreg;
return byte;
}

最佳答案

您可能已经启用了 UDRE(Uart 数据寄存器空)中断并且没有为其设置 vector ,因此当中断触发时处理器会重置(根据默认设置)。当在主循环中连续调用printf时,永远不会触发此中断。

来自 the docs

Catch-all interrupt vector

If an unexpected interrupt occurs (interrupt is enabled and no handler is installed, which usually indicates a bug), then the default action is to reset the device by jumping to the reset vector. You can override this by supplying a function named BADISR_vect which should be defined with ISR() as such. (The name BADISR_vect is actually an alias for __vector_default. The latter must be used inside assembly code in case is not included.)

关于c - AVR ATmega 在主循环之前使用 printf 时不断重置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21562348/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com