Последовательная связь Arduino с Arduino без библиотеки
Итак, я пытаюсь отправить массив с одного arduino на другой через последовательную связь, не используя программную последовательную библиотеку. Ардуино подключены rx-> tx, tx-> rx и заземлены. У меня есть структура, которая содержит массив данных и 2 тестовых символа. Затем вся структура отправляется по одному символу за раз. Затем получатель получает по 1 символу за раз, повторно вычисляет тестовые символы, и если xor отправленных и рассчитанных тестовых символов равен 1, мы предполагаем, что существует ошибка, и включаем красный светодиод. Если ошибки нет, то мы включаем зеленый светодиод. Однако у меня есть некоторые проблемы с моим кодом. Он никогда не отправляет данные на второй Arduino. Может ли кто-нибудь увидеть причину?
Отправитель:
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#define F_CPU 16000000L
#define BAUD_RATE 9600
#define MYUBRR (F_CPU / 16 / BAUD_RATE) - 1
#define END_OF_MESSAGE 47
#define END_OF_SECTION 35
#define START_OF_MESSAGE 2
#define _BV(bit) (1<<(bit))
struct packet{
/*Packet to send a part of the message. */
char *data;
char vert_check;
char horz_check;
};
void serialInit()
{
UBRR0H = (char) (MYUBRR>>8);
UBRR0L = (char) MYUBRR;
UCSR0C = (3 << UCSZ00);
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
}
unsigned char serialCheckTx()
{
return(UCSR0A & _BV(UDRE0));
}
void serial_send(unsigned char DataOut)
{
while(serialCheckTx() == 0){;}
UDR0 = DataOut;
}
int isOddParity(unsigned char myChar) {
int parity = 0;
for (myChar &= ~0x80; myChar != 0; myChar >>= 1) {
parity ^= (myChar & 1); // Переключить четность для каждого бита '1'.
}
return parity;
}
int main(void)
{
serialInit();
char hozcheck, vertcheck;
/* Add message to array below to send
* Initialised to toets.
*/
char *s = {'T', 'o', 'e', 't', 's'};
struct packet sendpacket;
sendpacket.data = s;
hozcheck = 0;
for(int i=0; i<strlen(s); i++){
hozcheck += s[i];
}
vertcheck = (isOddParity(s[0]) + '0');
sendpacket.horz_check = hozcheck;
sendpacket.vert_check = vertcheck;
serial_send(START_OF_MESSAGE);
serial_send(sendpacket.horz_check);
serial_send(END_OF_SECTION);
serial_send(sendpacket.vert_check);
serial_send(END_OF_SECTION);
for(int j=0; j<strlen(s); j++){
serial_send(s[j]);
}
serial_send(END_OF_MESSAGE);
}
Получатель:
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#define END_OF_MESSAGE 47
#define START_OF_MESSAGE 2
#define F_CPU 20000000L
#define BAUD_RATE 9600
#define MYUBRR (F_CPU / 16 / BAUD_RATE) - 1
#define _BV(bit) (1<<(bit))
#define START_OF_MESSAGE 2
#define END_OF_MESSAGE 47
#define END_OF_SECTION 35
struct packet{
char *data;
char vert_check;
char horz_check;
};
void serialInit()
{
UBRR0H = (char) (MYUBRR>>8);
UBRR0L = (unsigned) MYUBRR;
UCSR0C = (3 << UCSZ00);
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
}
unsigned char serialCheckRx()
{
return(UCSR0A & _BV(RXC0));
}
unsigned char serialReceive()
{
while (serialCheckRx() == 0){;}
return UDR0;
}
void *array_concat(char *a, int an, char b){
int s = sizeof(char);
char *p = malloc(s * (an + 1));
memcpy(p, a, an*s);
memcpy(p + an*s, b, s);
return p;
}
int isOddParity(unsigned char myChar) {
int parity = 0;
for (myChar &= ~0x80; myChar != 0; myChar >>= 1) {
parity ^= (myChar & 1); // Переключить четность для каждого бита '1'.
}
return parity;
}
int main(void)
{
DDRB = 0b111111;
serialInit();
char tmp = 0, recvert = 0, rechoz = 0, *s, testhoz, testvert;
int testbit = 0;
/*to rebuild our packages*/
struct packet packet1;
wait:
while((tmp = serialReceive()) != 2){
goto wait;
}
while((tmp = serialReceive()) != 35){
/*Should be a single char for horizontal*/
rechoz = tmp;
}
while((tmp = serialReceive()) != 35){
/*Should be a single char for "vert" parity*/
recvert = tmp;
}
while((tmp = serialReceive()) != 47){
/*While input is not end of message grow s*/
s = array_concat(s, strlen(s), tmp);
}
packet1.data = s;
packet1.vert_check = recvert;
packet1.horz_check = rechoz;
testhoz = 0;
for(int i=0; i<strlen(s); i++){
testhoz += s[i];
}
testvert = ((isOddParity(s[0])) + '0');
if((packet1.vert_check ^ testvert) == 1)
testbit = 1;
if((packet1.horz_check ^ testhoz) == 1)
testbit = 1;
if(testbit == 1){
/*RED LED*/
PORTB = 0b100000;
} else {
PORTB = 0b010000;
}
free(s);
}
@JVDM92, 👍5
Обсуждение2 ответа
Лучший ответ:
Одна очевидная проблема с кодом заключается в том, что s
не завершается 0. Использование strlen
такой строки технически является неопределенным поведением, и все ставки сняты.
Я могу только предположить, что он возвращает нечто большее, чем максимальное значение, которое может содержать целое число (имейте в виду, что он возвращает size_t
). Это означает, что цикл
for(int i=0; i<strlen(s); i++){
hozcheck += s[i];
}
фактически бесконечен, и у программы никогда не было возможности отправить что-либо.
Не уверен, что это имеет отношение к вашей проблеме или нет, но не могу не подчеркнуть следующее:
wait:
while((tmp = serialReceive()) != 2){
goto wait;
}
Это, должно быть, самый зловещий фрагмент кода, когда-либо написанный в истории человечества. Извините, но это так.
while((tmp = serialReceive()) != 2);
это все, что вам нужно. Или, если вы должны быть «педантичными»:
while((tmp = serialReceive()) != 2) {
continue;
}
Никогда не используется goto
(я не знаю, почему он вообще был включен в C) и, конечно же, не нужно повторно запускать while
. цикл, который все равно запустится снова.
В вашем ресивере вы используете динамическую память, и делаете это очень плохо.
Сначала вы начинаете с указателя
char *s;
Затем, когда вы получаете символ данных, вы передаете этот указатель своей подпрограмме array_concat()
в качестве источника для копирования данных:
s = array_concat(s, strlen(s), tmp);
В этот момент вы запрашиваете длину строки в неопределенном указателе, который сам по себе не определен. Это может быть 0, это может быть 65535, или это может быть где-то посередине.
Затем в array_concat вы выделяете память указателю в зависимости от длины этой строки (у Uno нет 65 535 байт ОЗУ, поэтому все, что больше доступной памяти, выделить не удастся). Затем вы копируете это количество байтов из неопределенного источника в место назначения (возможно, неопределенное). Тогда произойдут самые ужасные вещи.
Затем, если вы дошли до этого, вы возвращаете новый указатель и сразу же передаете его s
.
В следующей итерации (при условии, что первая итерация сработала) вы делаете все это снова, но теперь вы потеряли то, на что раньше указывал s
. Эта память ушла навсегда. Вы создали утечку памяти.
Итак, вам необходимо:
- Убедитесь, что
s
инициализирован равным NULL - Не используйте
strlen()
, так как вы работаете не со строкой, а с массивом данных — вместо этого подсчитывайте количество полученных байтов. - Выполняйте какое-либо копирование данных только в том случае, если
s
действительно указывает на некоторые допустимые данные (не NULL) free()
старый указатель данных после завершения копирования данных (если он указывал на реальную выделенную память)- Не используйте
memcpy
для копирования одного байта. Если необходимо, передайте ему адрес копируемого байта, а не сам байт, иначе он попытается скопировать данные из места, указанного значением байта, что на самом деле не то, что вам нужно.
TBH Я бы вообще не использовал динамическую память. Вместо этого я бы придерживался массивов фиксированного размера, которые достаточно велики, чтобы вместить самое длинное сообщение, которое вы отправляете/получаете. Это менее опасно, хотя и требует больше оперативной памяти.
В качестве альтернативы, если вы хотите использовать динамическую память, отправьте длину данных вместе с заголовком вашего пакета и выделите всю необходимую память в одном вызове malloc()
, прежде чем получать все данные.
Спасибо за совет. Я только что проверил, но это ничего не решает., @JVDM92
Я не думал, что это произойдет, так как он участвует в приеме, а не в передаче., @Majenko
- Почему эта программа на C++ не может прочитать Serial.write() моего arduino?
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Использовать все контакты как цифровые входы/выходы
- Float печатается только 2 десятичных знака после запятой
- Arduino как USB HID
- Serial1' was not declared in this scope
- Очень простая операция Arduino Uno Serial.readString()
Вы подтвердили, что из контакта TX определенно ничего не выходит?, @Majenko
Насколько я могу судить, ничего не выходит. Светодиод tx не загорается. Я также попытался подключить контакт к светодиоду и вывести массив 0b11111111 в непрерывном цикле while, полагая, что он включит его, но ничего не произошло., @JVDM92
Серийник вроде работает нормально. Я не смог скомпилировать вашу программу, потому что ей не нравился бит
char *s = { ... }
. Вместо этого он хотелchar s[5] = { ... }
., @MajenkoУ меня компилируется в atmel studio без ошибок., @JVDM92
И данные передаются нормально. Тогда, вероятно, что-то не так с вашим кодом приемника., @Majenko
В UECIDE я получаю «‣ скалярный объект 's' требует один элемент в инициализаторе». Какую версию GCC вы используете?, @Majenko
В вашем ресивере использование динамической памяти выглядит полностью нарушенным. Вы начинаете с
s
undefined, затем выделяете в своей подпрограмме concat, а затем назначаете это выделенноеs
. Затем вы передаете это обратно в concat, снова выделяете, копируете данные и оставляете старое распределение на месте. К концу этого вы выделили (и потеряли) кучу памяти - то есть, если он даже начнется, поскольку первый указатель, который вы передаете для копирования памяти, является полным мусором., @MajenkoGCC AVR (8-битный инструментарий) — 3.4.5.1061, @JVDM92
Как исправить распределение памяти в приемнике?, @JVDM92
Смотрите редактирование моего ответа., @Majenko
Спасибо за ответ. Я проверю это завтра, когда у меня будет возможность, и, надеюсь, это сработает., @JVDM92
В отправителе вы можете написать
char *s = "Toets";
. Тогда у вас есть правильно завершенная строка (компилятор добавляет в конце байт NULL), которую вы можете безопасно передать вstrlen()
., @Edgar BonetЯ не понимаю, почему никто больше не спросил **ПОЧЕМУ** «без использования серийной библиотеки программного обеспечения»., @Milliways