Я пытаюсь написать консоль Win, которая будет связываться с моей платой ATMega2560 через UART. Теперь он должен отправить строку, сохраненную в stringToSend
, на MCU, который должен быть отправлен обратно на ПК. Строка, отправленная из MCU, должна быть сохранена в receivedString
, а затем записана в окно консоли Win.
Что он делает сейчас, так это то, что после того, как я отправлю строку в MCU, она вернется, но на консоли я вижу неожиданную последовательность символов «╠», предшествующую эхо-строке, вот так:
Ты отправил:
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠Отправлена тестовая строка!
Обновлено: количество символов постоянно, если BUFFER_SIZE
постоянно, но масштабируется с BUFFER_SIZE
:
BUFFER_SIZE = 124
, то таких символов 132BUFFER_SIZE = 1024
, то таких символов 1032BUFFER_SIZE = 1
, то таких символов 9receivedString
, строки из stringToSend
там нет, НО она появится в консоли.#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <tchar.h>
#include <windows.h>
#define BUFFER_SIZE 124
// This is code from microsoft website which I just transfered into function
HANDLE SerialCommSetup(TCHAR* comName, DWORD baudRate, BYTE byteSize, BYTE parity, BYTE stopBits )
{
DCB dcb;
BOOL fSuccess;
TCHAR* pcCommPort = comName; // Most systems have a COM1 port
COMMTIMEOUTS timeouts = { 0 };
// Open a handle to the specified com port.
HANDLE hCom = CreateFile( pcCommPort,
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // not overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if ( hCom == INVALID_HANDLE_VALUE )
{
printf( "CreateFile failed with error %d.\n", GetLastError() );
return hCom;
}
// Initialize the DCB structure.
SecureZeroMemory( &dcb, sizeof( DCB ) );
dcb.DCBlength = sizeof( DCB );
// Build on the current configuration by first retrieving all current settings.
fSuccess = GetCommState( hCom, &dcb );
if ( !fSuccess )
{
printf( "GetCommState failed with error %d.\n", GetLastError() );
return hCom;
}
// Fill in some DCB values and set the com state:
dcb.BaudRate = baudRate; // baud rate
dcb.ByteSize = byteSize; // data size, xmit and rcv
dcb.Parity = parity; // parity bit
dcb.StopBits = stopBits; // stop bit
fSuccess = SetCommState( hCom, &dcb );
if ( !fSuccess )
{
printf( "SetCommState failed with error %d.\n", GetLastError() );
return hCom;
}
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if ( SetCommTimeouts( hCom, &timeouts ) == 0 )
{
fprintf( stderr, "Error setting timeouts\n" );
CloseHandle( hCom );
return hCom;
}
// Get the comm config again.
fSuccess = GetCommState( hCom, &dcb );
if ( !fSuccess )
{
printf( "GetCommState failed with error %d.\n", GetLastError() );
return hCom;
}
_tprintf( TEXT( "\nSerial port %s successfully reconfigured.\n" ), pcCommPort );
return hCom;
}
int main( void )
{
char stringToSend[] = "Sended test string!\0";
char receivedString[BUFFER_SIZE];
TCHAR wroteCom[5]; // Variable for name of port where the MCU is connected (find it in device manager or avrdude)
// Enter on which port you have connected the MCU to your PC (COM0 - COM9) - mine is on COM3
printf( "Enter COM name: " );
if ( !scanf( "%ls", wroteCom ) )
return -1;
if ( !wroteCom )
return -2;
wroteCom[4] = '\0';
/* Serial communication inicialization with parameters:
* - Port: (which you typed)
* - Baud rate: 9600
* - Data bits: 8
* - Parity: NONE
* - Stop bits: 1
*/
HANDLE mainCom = SerialCommSetup( wroteCom, CBR_9600, 8, NOPARITY, 1 );
if ( !mainCom )
return 1;
// Send string to MCU
if ( !WriteFile( mainCom, stringToSend, sizeof( stringToSend ), NULL, NULL ) )
{
CloseHandle( mainCom );
return 2;
}
// Receive string from MCU and write it into console
if ( !ReadFile( mainCom, receivedString, sizeof( stringToSend ), NULL, NULL ) )
{
CloseHandle( mainCom );
return 3;
}
printf( "\nYou sent: \n\n%s\n\n", receivedString );
CloseHandle( mainCom );
return 0;
}
#define F_CPU 16000000 // Set the clock speed
#define BAUD_RATE 9600 // Set the baud rate
#define MYUBRR F_CPU/16/BAUD_RATE-1 // Calculate the value for UBRR0 - from datasheet
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned int newString = 0;
unsigned int index = 0;
char receivedString[1024];
// USART init - from datasheet
void USART_Init( unsigned int ubrr)
{
/* Set baud rate */
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;
/* Enable receiver and transmitter */
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
/* Set frame format: 8data, 1stop bit */
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
} // USART_Init
// USART receiving - from datasheet
unsigned char USART_Receive( void )
{
/* Wait for data to be received */
while ( !(UCSR0A & (1<<RXC0)) )
;
/* Get and return received data from buffer */
return UDR0;
}
// USART sending - from datasheet
void USART_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1<<UDRE0)) )
;
/* Put data into buffer, sends the data */
UDR0 = data;
}
void SendString(char* stringToSend)
{
for(unsigned int index = 0; stringToSend[index] != '\0'; index++)
USART_Transmit(stringToSend[index]);
USART_Transmit('\0');
}
ISR(USART0_RX_vect) //
{
char c = USART_Receive();
if ( c == '\n' || c == '\0'|| index >= 1023)
{
receivedString[index] = '\0';
index = 0;
newString = 1; // Indication of receiving whole string
}
else
receivedString[index++] = c;
}
int main(void)
{
USART_Init(MYUBRR);
sei(); // Enable interrupts
UCSR0B |= (1<<RXCIE0); // Enable complete interrupt
while (1)
{
if (newString == 1)
{
newString = 0;
SendString(receivedString);
}
}
}
Я был бы очень признателен за любую помощь, даже с рефакторингом/другими советами по кодированию.
Выход:
ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc 53 65 6e 64 65 64 20 74 65 73 74 20 73 74 72 69 6e 67 21
Количество этих символов всегда равно
BUFFER_SIZE + 8
, а десятичный символ представлен как-52
WriteFile()
и ReadFile()
, и проблемы там не было (но это определенно может вызвать некоторые проблемы, потому что в документации сказано, что последние два параметра не могут быть оба NULL).ReadFile()
всегда должно наступать после того, как происходит событие, и функция WaitCommEvent()
его перехватывает. Тип события задается SetCommMask(mainCom, EV_RXCHAR)
, поэтому оно происходит, когда во входном буфере есть символ (есть документация по функции SetCommMask() ). Это решение показывает, что ошибка вызвана платой, которая не отправляет данные, потому что WaitCommEvent()
попадет в бесконечный цикл ожидания получения данных. Однако без использования WaitCommEvent()
кажется, что отправленные данные хранятся в какой-то памяти и считываются оттуда, поэтому, даже когда плата их не отправляет, они печатаются (я действительно не понимаю, почему, но см. @thebusybee ответ). Что на самом деле устраняет проблему, так это простое повторное подключение платы каждый раз, когда я загружаю Windows (после повторного подключения все работает даже с использованием WaitCommEvent()
), что приводит меня к совершенно другому вопросу, если Windows отправляет какие-то странные вещи в USB-порты при загрузке, поэтому это мешает доска, но это для другой темы. Это также может быть вызвано тем, что Windows использует разные параметры связи (например, скорость передачи данных и т. д.) во время загрузки. Но это для другого/другого вопроса.@thebusybee Итак, я отредактировал вопрос и уточнил, что моя проблема в том, что это ╠
перед правильной строкой, а не пустые строки.
Замечу, что receivedString
объявлен со 124 элементами, но в слоте in, представленном соответствующим %s
в выходном формате, печатается более 124 символов. Однако я не вижу причин, по которым представленный вызов ReadFile()
будет читать столько байтов. Возможно, стоит использовать четвертый параметр ReadFile()
, чтобы получить собственное представление этой функции о том, сколько байтов она прочитала.
Постоянно ли количество ╠
символов от запуска к запуску?
Я не знаю, важно ли это, но ваш массив stringToSend
заканчивается двумя нулевыми символами (один явный в инициализаторе и один неявный). Вы записываете оба в последовательный порт и разрешаете чтение обоих обратно. Однако код MCU ожидает, что только один нулевой символ или символ новой строки завершает сообщение, и он отправит обратно только один (для каждого сообщения).
@JohnBollinger Итак, я быстро проверил, сколько там символов, и да, оно постоянно, но если я изменю BUFFER_SIZE
, то их появится больше, поэтому оно увеличивается с BUFFER_SIZE
. Для информации, если BUFFER_SIZE = 124
, то этих символов 132. Когда я увеличиваю значение до BUFFER_SIZE = 1024
, таких символов становится 1032. Если я изменю stringToSend[] = "A\0"
и уменьшу BUFFER_SIZE = 1
, то таких символов будет 9.
Тогда это BUFFER_SIZE + 8
. Интересный. Это очень говорит о том, что проблема на стороне Windows, потому что размер буфера никогда не передается на MCU.
Я не думаю, что в этом случае это необходимо, но что, если вы объявите receivedString
с инициализатором? char receivedString[BUFFER_SIZE] = {0};
Согласно документации, четвертый и пятый параметры ReadFile()
не могут быть оба нулевыми.
Итак, если я инициализирую receivedString
таким образом, он ничего не вернет, а в конце программы receivedString
просто строка, полная \0
Кажется, что происходит то, что данные, считанные ReadFile()
, идут не в то место — через 8 байтов после конца буфера receivedString
. Я не знаю, почему это так, но не пропустите мой предыдущий комментарий о параметрах ReadFile()
— последние два не должны оба быть нулевыми. То же самое относится и к WriteFile()
.
Возможно, вам придется отредактировать заголовок, потому что он явно спрашивает об этой пустой строке и ничего не говорит о реальной проблеме.
Что ж, спасибо за попытку. К сожалению, он говорит то же самое другими словами. Но ваша настоящая проблема не в пустой строке, это ясно из вашей строки формата. Настоящая проблема заключается в неправильном месте хранения полученной строки, как вы тем временем показали. Не могли бы вы отредактировать его снова?
@thebusybee Не могли бы вы сказать мне, как бы вы назвали эту проблему? Честно говоря, я думал так же, как «Что я искал, чтобы найти решение, прежде чем задать вопрос здесь», и все. У меня нет проблем с редактированием, просто я действительно не знаю, как это лучше назвать.
Что вы думаете о «Почему я получаю символы со смещением в буфере при использовании ReadFile()?» а теги для winapi и серийной связи?
Звучит хорошо для меня, поэтому я изменил его, надеюсь, теперь все в порядке. Спасибо, что помогли мне с этим
receivedString, sizeof( stringToSend )
- ошибка копирования/вставки? Должно быть sizeof( receivedString )
. Вы также не проверили, сколько байтов было прочитано. Я подозреваю, что было прочитано ноль байтов, поэтому вы печатаете неинициализированные данные.
@RaymondChen Этот параметр функции указывает максимально возможное количество байтов, которые будут прочитаны, поэтому все в порядке, потому что в этом примере я пытаюсь отправить строку, поэтому я не могу прочитать больше байтов, чем я отправил.
Почему, @RaymondChen? Программа действительно ожидает получить sizeof( stringToSend )
байт. Если бы stringToSend
было больше, чем receivedString
, то это было бы проблемой, но это не так.
Другой эксперимент: вместо того, чтобы печатать полученные символы в виде одной строки, не могли бы вы вывести каждый из них в шестнадцатеричном виде? А количество полученных символов? Было бы полезно увидеть байты, которые отображаются здесь как ╠
.
Вам повезло, что sizeof(stringToSend)
меньше BUFFER_SIZE
. Если вы действительно намеревались получить только sizeof(stringToSend)
байт, то почему размер receivedString
не связан с sizeof(stringToSend)
? Когда-нибудь вы сделаете stringToSend
больше, и тогда receivedString
станет недостаточно большим, и вы получите переполнение буфера и CVE.
@RaymondChen О да, ты определенно прав. Я не понимал, что на самом деле я создаю строку только для 124 символов, поэтому она может очень легко переполниться. Спасибо за это.
ReadFile()
не считывает ни одного байта с последовательного устройства из-за вашего ошибочного вызова с обоими последними параметрами, установленными на NULL
. Однако ReadFile()
не вернулся FALSE
.
Значения 0xCC (распечатанные с расширенным знаком как 0xFFFFFFCC), которые заполняются в receivedString
, используются в качестве помощи при отладке.
Компилятор вашего ПК, по-видимому, находит переменную receivedString
перед переменной stringToSend
в стеке. Дополнительные байты, скорее всего, являются канареечными словами.
Теперь, когда вы печатаете receivedString
, функция printf
сканирует память, пока не найдет завершающую '\0'
. Это происходит после того, как содержимое stringToSend
было дополнительно отсканировано и скопировано на вывод. Итак, то, что вы видите, получено не ReadFile()
, а существующими персонажами stringToSend
, и это именно то, что вы ожидаете получить.
Это визуализирует ситуацию с памятью:
receivedString
набивка
stringToSend
...(более высокий адрес)
0xCC, 0xCC, ... 0xCC
0xCC, ... 0xCC
'С', 'е', ... '!', '\0'
Что вы должны сделать?
У вас есть два символа новой строки в строке формата для вывода:
"\nYou sent: \n\n%s\n\n"
. Это вставляет пустую строку.