Последовательная связь 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
В вашем ресивере использование динамической памяти выглядит полностью нарушенным. Вы начинаете с
sundefined, затем выделяете в своей подпрограмме concat, а затем назначаете это выделенноеs. Затем вы передаете это обратно в concat, снова выделяете, копируете данные и оставляете старое распределение на месте. К концу этого вы выделили (и потеряли) кучу памяти - то есть, если он даже начнется, поскольку первый указатель, который вы передаете для копирования памяти, является полным мусором., @MajenkoGCC AVR (8-битный инструментарий) — 3.4.5.1061, @JVDM92
Как исправить распределение памяти в приемнике?, @JVDM92
Смотрите редактирование моего ответа., @Majenko
Спасибо за ответ. Я проверю это завтра, когда у меня будет возможность, и, надеюсь, это сработает., @JVDM92
В отправителе вы можете написать
char *s = "Toets";. Тогда у вас есть правильно завершенная строка (компилятор добавляет в конце байт NULL), которую вы можете безопасно передать вstrlen()., @Edgar BonetЯ не понимаю, почему никто больше не спросил **ПОЧЕМУ** «без использования серийной библиотеки программного обеспечения»., @Milliways