Последовательная связь 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);
}

, 👍5

Обсуждение

Вы подтвердили, что из контакта 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, снова выделяете, копируете данные и оставляете старое распределение на месте. К концу этого вы выделили (и потеряли) кучу памяти - то есть, если он даже начнется, поскольку первый указатель, который вы передаете для копирования памяти, является полным мусором., @Majenko

GCC AVR (8-битный инструментарий) — 3.4.5.1061, @JVDM92

Как исправить распределение памяти в приемнике?, @JVDM92

Смотрите редактирование моего ответа., @Majenko

Спасибо за ответ. Я проверю это завтра, когда у меня будет возможность, и, надеюсь, это сработает., @JVDM92

В отправителе вы можете написать char *s = "Toets";. Тогда у вас есть правильно завершенная строка (компилятор добавляет в конце байт NULL), которую вы можете безопасно передать в strlen()., @Edgar Bonet

Я не понимаю, почему никто больше не спросил **ПОЧЕМУ** «без использования серийной библиотеки программного обеспечения»., @Milliways


2 ответа


Лучший ответ:

3

Одна очевидная проблема с кодом заключается в том, что s не завершается 0. Использование strlen такой строки технически является неопределенным поведением, и все ставки сняты.

Я могу только предположить, что он возвращает нечто большее, чем максимальное значение, которое может содержать целое число (имейте в виду, что он возвращает size_t). Это означает, что цикл

for(int i=0; i<strlen(s); i++){
    hozcheck += s[i];
}

фактически бесконечен, и у программы никогда не было возможности отправить что-либо.

,

6

Не уверен, что это имеет отношение к вашей проблеме или нет, но не могу не подчеркнуть следующее:

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. Эта память ушла навсегда. Вы создали утечку памяти.

Итак, вам необходимо:

  1. Убедитесь, что s инициализирован равным NULL
  2. Не используйте strlen(), так как вы работаете не со строкой, а с массивом данных — вместо этого подсчитывайте количество полученных байтов.
  3. Выполняйте какое-либо копирование данных только в том случае, если s действительно указывает на некоторые допустимые данные (не NULL)
  4. free() старый указатель данных после завершения копирования данных (если он указывал на реальную выделенную память)
  5. Не используйте memcpy для копирования одного байта. Если необходимо, передайте ему адрес копируемого байта, а не сам байт, иначе он попытается скопировать данные из места, указанного значением байта, что на самом деле не то, что вам нужно.

TBH Я бы вообще не использовал динамическую память. Вместо этого я бы придерживался массивов фиксированного размера, которые достаточно велики, чтобы вместить самое длинное сообщение, которое вы отправляете/получаете. Это менее опасно, хотя и требует больше оперативной памяти.

В качестве альтернативы, если вы хотите использовать динамическую память, отправьте длину данных вместе с заголовком вашего пакета и выделите всю необходимую память в одном вызове malloc(), прежде чем получать все данные.

,

Спасибо за совет. Я только что проверил, но это ничего не решает., @JVDM92

Я не думал, что это произойдет, так как он участвует в приеме, а не в передаче., @Majenko