问题描述
我无法将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;
}