AVR将UART输入放入循环缓冲区并再次将其取回

问题描述

我无法将UART的输入输入到循环缓冲区,然后再次输出。循环缓冲区代码来自http://www.fourwalledcubicle.com/files/LightweightRingBuff.h,我之前曾成功使用过,但不在中断服务中使用。在这个项目中,我能够通过轮询UART来工作,但是现在我意识到我需要使用中断。因此,我将事情重新调整到了我认为可以用作中断服务的水平,但是uart_getchar()并没有任何用处。通过调试printf()s,我发现它被调用了两次,但从未真正返回。

这是最小的工作示例:

/* A proof of concept implementation of a command line on an AVR */
/*
 * Compile/build/program with:
 * avr-gcc -Os -DF_cpu=16000000UL -std=gnu99 -mmcu=atmega164p -o linetest.elf linetest.c
 * avr-objcopy -j .text -j .data -O ihex linetest.elf linetest.hex
 * avrdude -c usbtiny -p m164p -U flash:w:linetest.hex:i
 *
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>

#include <util/atomic.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>

#define UART_BAUD 9600
#define BAUdratE F_cpu/16/BAUD-1
#define BUFFER_SIZE 80

int uart_putchar(char,FILE *);
int uart_getchar(FILE *);
void uart_init(unsigned int);

typedef uint8_t rbuf_data_t;
typedef uint8_t rbuf_count_t;

typedef struct {
    rbuf_data_t buffer[80];
    rbuf_data_t *in;
    rbuf_data_t *out;
    rbuf_count_t    count;
} rbuf_t;

void rbuf_init(rbuf_t* const);
rbuf_count_t rbuf_getcount(rbuf_t* const);
bool rbuf_isempty(rbuf_t*);
void rbuf_insert(rbuf_t* const,const rbuf_data_t);
rbuf_data_t rbuf_remove(rbuf_t* const);

rbuf_t  rbuf;
char line[BUFFER_SIZE];
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar,uart_getchar,_FDEV_SETUP_RW);

int main(void)
{
    int i;

    sei();
    uart_init(UART_BAUD);
    printf_P(PSTR("\n\nHello World!\nType a line and see it printed back.\n"));
    for (;;) {
        printf_P(PSTR("> "));
        if (fgets(line,sizeof(line) - 1,stdin) == NULL) {
            putchar('\n');
            continue;
        }

        if (strlen(line) <= 1) continue;
        printf_P(PSTR("\n- "));

        for (i = 0; i < strlen(line); i++) {
            putchar(line[i]);
            _delay_ms(300);
        }
        printf_P(PSTR("\n"));
    }
    printf_P(PSTR("\nSomething barfed.\n"));
    return 0;
}


void rbuf_init(rbuf_t* const buffer)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        buffer->in    = buffer->buffer;
        buffer->out   = buffer->buffer;
        buffer->count = 0;
    }
    return;
}


rbuf_count_t rbuf_getcount(rbuf_t* const buffer)
{
    rbuf_count_t count;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        count = buffer->count;
    }
    return count;
}


bool rbuf_isempty(rbuf_t* buffer)
{
    return (rbuf_getcount(buffer) == 0);
}


void rbuf_insert(rbuf_t* const buffer,const rbuf_data_t data)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        if ((buffer->in) == &buffer->buffer[sizeof(buffer)])
            return;
        *buffer->in = data;
        ++buffer->in;
        buffer->count++;
    }
    return;
}


rbuf_data_t rbuf_remove(rbuf_t* const buffer)
{
    rbuf_data_t data;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        data = *buffer->out;
        if (++buffer->out == &buffer->buffer[sizeof(buffer)])
            buffer->out = buffer->buffer;
        buffer->count--;
    }
    return data;
}


ISR (USART0_RX_vect)
{
    uint8_t c;

//  if (UCSR0A & _BV(FE0))
//      return;
//  if (UCSR0A & _BV(DOR0))
//      return;
    c = UDR0;
    rbuf_insert(&rbuf,(rbuf_data_t) c);

    return;
}


/*
 * Receive a character from the UART Rx.
 *
 */
int uart_getchar(FILE *stream)
{
    uint8_t c;

    while (rbuf_isempty(&rbuf)); /* block until something's there */

    c = rbuf_remove(&rbuf);

    /* this executes twice then nothing more seen */
    printf_P(PSTR("+%c"),c);
    return c;
}


/*
 * Initialize the UART to baud/bps,tx/rx,8N1.
 */
void uart_init(unsigned int baud)
{
    rbuf_init(&rbuf);
    stdout = stdin = &uart_str;

#if F_cpu < 2000000UL && defined(U2X)
    UCSR0A = _BV(U2X);  /* improve baud rate error by using 2x clk */
    UBRR0L = (F_cpu / (8UL * baud)) - 1;
#else
    UBRR0L = (F_cpu / (16UL * baud)) - 1;
#endif
    /* Clear error flags,MODbus protocol: */
    UCSR0A=0x00;
    /* Enable TX,RX,and RX interrupt */
    UCSR0B = (1<<TXEN0) | (1<<RXEN0) | (1<<RXCIE0);

    return;
}


/*
 * Send character c down the UART Tx,wait until tx holding register
 * is empty.
 */
int uart_putchar(char c,FILE *stream)
{
    if (c == '\a') {
        fputs("*ring*\n",stderr);
        return 0;
    }

    if (c == '\n')
        uart_putchar('\r',stream);
    loop_until_bit_is_set(UCSR0A,UDRE0);
    UDR0 = c;

    return 0;
}

解决方法

在使用环形缓冲区时,我注意到了代码中的一些问题。

首先,在rbuf_insert中:

        if ((buffer->in) == &buffer->buffer[sizeof(buffer)])

rbuf_remove中的相同:

        if (++buffer->out == &buffer->buffer[sizeof(buffer)])

请注意,sizeof用于名为buffer的变量,它是函数参数rbuf_t* const buffer。它是一个指针,因此sizeof(buffer)将为2。

可能您想使用&buffer->buffer[sizeof(buffer->buffer)])

第二。在rbuf_insert中:

        if ((buffer->in) == &buffer->buffer[sizeof(buffer)])
            return;

buffer->in到达顶部之后(假设您将修复sizeof),该函数将始终返回并且不能在缓冲区中放置任何新数据,因为buffer-> in从未包装

可能您想要这样的东西:

void rbuf_insert(rbuf_t* const buffer,const rbuf_data_t data)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        if (buffer->count == sizeof(buffer->buffer)) // if the buffer is full..
            return;
        if ((buffer->in) == &buffer->buffer[sizeof(buffer->buffer)]) // if the pointer at the top
            buffer->in = buffer->buffer; // wrap around
        *buffer->in = data;
        ++buffer->in;
        buffer->count++;
    }
    return;
}
,

这是我一直在努力的最终版本。所有这一切的目的是通过AVR上的RS232串行端口获得命令行界面。我正在尝试类似于POTS调制解调器的操作方式。

/* A proof of concept implementation of a command line on an AVR */
/*
 * Compile/build/program with:
 * avr-gcc -Os -DF_CPU=16000000UL -std=gnu99 -mmcu=atmega164p -o linetest.elf linetest.c
 * avr-objcopy -j .text -j .data -O ihex linetest.elf linetest.hex
 * avrdude -c usbtiny -p m164p -U flash:w:linetest.hex:i
 *
 */

#include <util/atomic.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>

#define UART_BAUD 9600
#define BAUDRATE F_CPU/16/BAUD-1
#define BUFFER_SIZE 80

#define TRUE    1
#define FALSE   0

int uart_putchar(char,FILE *);
int uart_getchar(FILE *);
void uart_init(unsigned int);

typedef uint8_t rbuf_data_t;
typedef uint8_t rbuf_count_t;

typedef struct {
    rbuf_data_t buffer[80];
    rbuf_data_t *in;
    rbuf_data_t *out;
    rbuf_count_t    count;
} rbuf_t;

void rbuf_init(rbuf_t *);
rbuf_count_t rbuf_getcount(rbuf_t *);
bool rbuf_isempty(rbuf_t *);
void rbuf_insert(rbuf_t *,const rbuf_data_t);
rbuf_data_t rbuf_remove(rbuf_t *);

volatile bool command;      /* Command line active? */
volatile bool quit_early;   /* Abort processing. */

rbuf_t  rbuf;
char line[BUFFER_SIZE];
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar,uart_getchar,_FDEV_SETUP_RW);

int main(void)
{
    int i;
    char c;

    sei();
    uart_init(UART_BAUD);

    printf_P(PSTR("\n\nHello World!\nType a line and see it printed back.\n"));
    for (;;) {

        command = TRUE;
        printf_P(PSTR("> "));

        c = fgetc(stdin);
        i = 0;
        while (i < sizeof(line)) {
            if ((c == '\n') || (c == '\r')) {
                line[i] = 0;
                break;
            }

            line[i] = c;
            i++;
            printf("%c",c);
            c = fgetc(stdin);
        }
        command = FALSE;

        printf_P(PSTR("\n- "));

        for (i = 0; i < strlen(line); i++) {
            if (quit_early) {
                printf_P(PSTR(" ABORT"));
                break;
            }
            putchar(line[i]);
            _delay_ms(300);
        }
        printf_P(PSTR("\n"));
        quit_early = FALSE;
    }
    printf_P(PSTR("\nSomething barfed.\n"));
    return 0;
}


void rbuf_init(rbuf_t* const buffer)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        buffer->in    = buffer->buffer;
        buffer->out   = buffer->buffer;
        buffer->count = 0;
    }
    return;
}


rbuf_count_t rbuf_getcount(rbuf_t* const buffer)
{
    rbuf_count_t count;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        count = buffer->count;
    }
    return count;
}


bool rbuf_isempty(rbuf_t* buffer)
{
    return (rbuf_getcount(buffer) == 0);
}


void rbuf_insert(rbuf_t* const buffer,const rbuf_data_t data)
{
    *buffer->in = data;

    if (++buffer->in == &buffer->buffer[BUFFER_SIZE])
        buffer->in = buffer->buffer;

    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        buffer->count++;
    }
}


rbuf_data_t rbuf_remove(rbuf_t* const buffer)
 {
    rbuf_data_t data = *buffer->out;

    if (++buffer->out == &buffer->buffer[BUFFER_SIZE])
        buffer->out = buffer->buffer;

    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        buffer->count--;
    }

    return data;
}


ISR (USART0_RX_vect)
{
    uint8_t c;


    /* Maybe put these in the else below? */
//  if (UCSR0A & _BV(FE0))
//      return;
//  if (UCSR0A & _BV(DOR0))
//      return;

    c = UDR0;

    /* If command line is active,store the character. */
    if (command)
        rbuf_insert(&rbuf,(rbuf_data_t) c);
    else {  /* Otherwise check to see if we need to abort. */
        if (c == 0x03)
            quit_early = TRUE;
    }

    return;
}


/*
 * Receive a character from the UART Rx.
 *
 */
int uart_getchar(FILE *stream)
{
    uint8_t c;

    while (rbuf_isempty(&rbuf)); /* block until something's there */
    c = rbuf_remove(&rbuf);
    return c;
}


/*
 * Initialize the UART to baud/bps,tx/rx,8N1.
 */
void uart_init(unsigned int baud)
{
    rbuf_init(&rbuf);

#if F_CPU < 2000000UL && defined(U2X)
    UCSR0A = _BV(U2X);  /* improve baud rate error by using 2x clk */
    UBRR0L = (F_CPU / (8UL * baud)) - 1;
#else
    UBRR0L = (F_CPU / (16UL * baud)) - 1;
#endif
    /* Clear error flags,MODbus protocol: */
    UCSR0A=0x00;
    /* Enable TX,RX,and RX interrupt */
    UCSR0B = (1<<TXEN0) | (1<<RXEN0) | (1<<RXCIE0);

    stdout = stdin = &uart_str;

    return;
}


/*
 * Send character c down the UART Tx,wait until tx holding register
 * is empty.
 */
int uart_putchar(char c,FILE *stream)
{
    if (c == '\a') {
        fputs("*ring*\n",stderr);
        return 0;
    }

    if (c == '\n')
        uart_putchar('\r',stream);
    loop_until_bit_is_set(UCSR0A,UDRE0);
    UDR0 = c;

    return 0;
}