UART 上的 UTF-8 输入

UTF-8 input on UART

基于Mika Tuupola's tutorial,我正在尝试将来自 UART 的串行输入用作 stdin。底层串口通信代码如下:

#ifndef BAUD
#define BAUD 9600
#endif

#include <avr/io.h>
#include <stdio.h>
#include <util/setbaud.h>

void uart_init ()
{
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;

#if USE_2X
    UCSR0A |= _BV(U2X0);
#else
    UCSR0A &= ~(_BV(U2X0));
#endif

    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);
}

void uart_putchar (char c)
{
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;
}

char uart_getchar ()
{
    loop_until_bit_is_set(UCSR0A, RXC0);
    return UDR0;
}

下一层将 uart_getcharuart_putchar 公开为流处理程序兼容函数:

int uart_putchar_s (char c, FILE *stream)
{
    if (c == '\n')
        uart_putchar('\r');
    uart_putchar(c);
    return 0;
}

int uart_getchar_s (FILE *stream)
{
    return uart_getchar();
}

FILE uart_output = FDEV_SETUP_STREAM(uart_putchar_s, NULL, _FDEV_SETUP_WRITE);
FILE uart_input =  FDEV_SETUP_STREAM(NULL, uart_getchar_s, _FDEV_SETUP_READ);

void uart_init_stdio()
{
    stdout = &uart_output;
    stdin  = &uart_input;
}

然后我的 main 只是读入一个字符并将其打印出来。它故意使用 printf 以便输出始终通过 stdout 流抽象。

int main ()
{
    uart_init();
    uart_init_stdio();

    for (;;)
    {
        char c = uart_getchar();
        printf("%c", c);
    }
}

如果我从 UTF-8 串行终端连接到此,并输入例如á,我正确地看到我在 UTF-8 中取回了代表 á 的两个字节:本地回显显示我的终端发送了字节 C3 A1,这些是相同的两个字节我回来。到目前为止,还不错。

但是,如果我随后将 main 更改为使用 getchar() 而不是 uart_getchar()(即,如果我通过 stdin 流抽象进行输入),那么发送相同的 C3 A1 字节后,我得到的只是 FF FF。这是为什么?从 UTF-8 的角度来看,除了发送和接收恰好编码单个字形的单个字节之外,这里还有其他事情吗?

问题是,uart_getchar() return 是 "char",它已在您的平台上签名。 因此,> 0x7f 的字符被解释为负 return 值,因此,stdio 层 returns EOF,在您的平台上为 -1(如果解释为无符号则为 0xFF)。

尝试将 uart_getchar() 声明为 returning "int":

int uart_getchar ()
{
    loop_until_bit_is_set(UCSR0A, RXC0);
    return (unsigned char) UDR0;
}