在Arduino UNO上通过UART0接收字符串的问题

时间:2016-09-28 11:09:32

标签: c string avr uart serial-communication

我正在尝试编写自己的库,用于通过UART0从我的Arduino UNO向我的计算机发送消息。

除了我想要接收字符串的部分之外,库成功运行。该库的代码是:

#define F_CPU 16000000

#define EVEN_P 0
#define ODD_P 1

#define BAUD_RATE 57600

#include <avr/io.h>
#include <math.h>
#include <avr/io.h>
#include <string.h>
#include <util/delay.h>

// Initialize UART0 communication
void UART0_Init_Custom(unsigned long BaudRate, char AsyncDoubleSpeed, char DataSizeInBits, char ParityEVENorODD, char StopBits)
{
    uint16_t UBBR_Value = lrint (F_CPU / 16 / BaudRate - 1 ); // maybe 16L??

    // Setting the U2X bit to 1 for double speed asynchronous (default = 0, normal speed)
    if (AsyncDoubleSpeed == 1) UCSR0A = (1 << U2X0);

    // Upper part of the baud number (bits 8 to 11)
    UBRR0H = (unsigned char)(UBBR_Value >> 8);
    // Rest of the baud number
    UBRR0L = (unsigned char)(UBBR_Value);

    // Enable the receiver and transmitter
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);

    // Set 2 stop bits (default = 1)
    if (StopBits == 2) UCSR0C = (1 << USBS0);

    // Set parity
    if (ParityEVENorODD == EVEN_P) UCSR0C |= (1 << UPM01);
    if (ParityEVENorODD == ODD_P) UCSR0C |= (3 << UPM00);

    // Set data length (default = 5 bits)
    if (DataSizeInBits == 6) UCSR0C |= (1 << UCSZ00); // 6-bit
    if (DataSizeInBits == 7) UCSR0C |= (2 << UCSZ00); // 7-bit
    if (DataSizeInBits == 8) UCSR0C |= (3 << UCSZ00); // 8-bit
    if (DataSizeInBits == 9) UCSR0C |= (7 << UCSZ00); // 9-bit
}

void UART0_Init(unsigned long BaudRate)
{
    if (BaudRate == 115200)
    {
        UART0_Init_Custom((BaudRate/2),1,8,0,2);
    }
    else
    {
        UART0_Init_Custom(BaudRate,0,8,0,2);
    }
}

// Receive Data UART0
char UART0_GET(void)
{
    while (!(UCSR0A & (1 << RXC0)));
    return UDR0;
}

// Transmit Data UART0
void UART0_PUT(char data)
{
    while (!(UCSR0A & (1 << UDRE0)));
    UDR0 = data;
}

// Transmit Data-String UART0
void UART0_PRINT(char* String)
{
    while(*String)
    {
        UART0_PUT(*String++);
    }   
}

// Receive Data-String UART0
char* UART0_READ(void)
{
    char* ReceivedString;
    char ReceivedBit;
    int StringBit = 0;
    memset(&ReceivedString,0,sizeof(ReceivedString));
    while ((ReceivedBit=UART0_GET())!=13)
    {
        UART0_PUT(ReceivedBit);
        ReceivedString[StringBit] = ReceivedBit;
        StringBit++;
    }
    ReceivedString[StringBit] = 13;
    ReceivedString[StringBit++] = 10;
    UART0_PUT(10);
    strncpy(ReceivedString, ReceivedString, StringBit+1);
    return(ReceivedString);
}

char* Input;
int main(void)
{
    UART0_Init(BAUD_RATE);
    UART0_PRINT((char*)"Give a message and I will return it\r\n");
    while(1)
    {   
        Input = UART0_READ();
        UART0_PRINT((char*)"The message was:");
        UART0_PRINT(Input);
    }
}

运行此代码时,PuTTY会显示一些随机令牌并停止。我不能再插入任何东西了。

1 个答案:

答案 0 :(得分:2)

我认为你遇到的主要问题是你没有分配任何存储接收数据的缓冲区(正如@WeatherVane在评论中指出的那样)。

在C中写一个要返回字符串的函数时,你有两个选择:调用者提供一个缓冲区并将指针传递给函数,或者函数分配缓冲区并返回一个指向它的指针。

如果函数要分配它,那么它必须使用malloc函数在堆上分配它。您无法在函数内部使用局部变量,因为如果您在退出函数后立即执行该变量,则该变量超出范围。从理论上讲,您可以使用静态变量作为缓冲区,但如果您这样做,则一次只能读取一个字符串。

如果调用者要提供缓冲区,那么它可以使用局部变量,静态变量或从堆中分配。

当在堆上分配缓冲区时,无论是谁分配它,调用者必须使用free函数在完成后释放它。

嵌入式系统通常最大限度地减少堆分配变量的使用 - 请参阅this question以获取有关其原因的一些信息 - 因此您可能最好允许调用者分配缓冲区,如下所示:

void UART0_READ(char* buffer, int buflen)
{
//  ... here goes code to read into buffer
}

#define BUFSIZE 100

int main(void)
{
    char input[BUFSIZE];
    UART0_READ(input, BUFSIZE);
    ...
}

请注意,我将UART0_READ函数定义为采用两个参数:缓冲区的地址及其长度。你的程序缺少的另一件事就是防止缓冲区溢出。

在输入例程中,使用例如的buflen参数。如果读取了太多字符,则调用memset,并在循环内部读取字符以提前退出。

缓冲区被定义为调用函数中的本地字符数组。

其他一些说明:

  • 不要忘记添加一个零,超过添加到数组的最后一个字符。这也意味着你必须在读取buflen-1字符后退出循环,这样你才有足够的空间容纳0。

  • 我不确定你打电话给strncpy的原因?你似乎把字符串复制到自己身上。

  • 在全尺寸操作系统中运行带有散乱指针的程序通常会导致段错误。嵌入式环境通常缺乏检测不正确的内存访问的复杂性,因此您可能会获得较少的可预测行为(例如随机垃圾从串行端口出来)。