在 Arduino UNO 上通过 UART0 接收字符串的问题
Problems with receiving strings via UART0 on Arduino UNO
我正在尝试编写自己的库,用于通过 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 显示一些随机标记并停止。我不能再插入任何东西了。
我认为您遇到的主要问题是您没有分配任何缓冲区来存储接收到的数据(正如@WeatherVane 在评论中指出的那样)。
当用 C 语言编写一个要 return 字符串的函数时,您有两种选择:要么调用者提供缓冲区并将指向它的指针传递给函数,要么函数分配缓冲区returns 是指向它的指针。
如果函数要分配它,则必须使用 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 个字符后退出循环,因此您有足够的 space 用于 0.
我不确定你为什么打电话给 strncpy
?您似乎正在将字符串复制到自身。
运行 具有完整大小的杂散指针的程序 OS 通常会导致段错误。嵌入式环境通常缺乏检测不正确内存访问的复杂性,因此您可能会得到不太可预测的行为(例如从串行端口出来的随机垃圾)。
我正在尝试编写自己的库,用于通过 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 显示一些随机标记并停止。我不能再插入任何东西了。
我认为您遇到的主要问题是您没有分配任何缓冲区来存储接收到的数据(正如@WeatherVane 在评论中指出的那样)。
当用 C 语言编写一个要 return 字符串的函数时,您有两种选择:要么调用者提供缓冲区并将指向它的指针传递给函数,要么函数分配缓冲区returns 是指向它的指针。
如果函数要分配它,则必须使用 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 个字符后退出循环,因此您有足够的 space 用于 0.
我不确定你为什么打电话给
strncpy
?您似乎正在将字符串复制到自身。运行 具有完整大小的杂散指针的程序 OS 通常会导致段错误。嵌入式环境通常缺乏检测不正确内存访问的复杂性,因此您可能会得到不太可预测的行为(例如从串行端口出来的随机垃圾)。