Приём сообщения по UART - странно обрабатывается последний байт сообщения.

RUMBUFDSI

New member
Всем привет!
Имеется отладочная плата (https://tellur-el.ru/upload/iblock/ac0/r4rucl3gy3hbt0f9sw90cgoz8qmq20p6.pdf).
Пытаюсь принять набор байтов по UART. На UART'е сигнал формируется через преобразователь UART-RS485 на плате (DA2),- точную модель не скажу, в дш её нет, а физически на плате маркировка стёрлась, но по сути это что-то около MAX485. На неё сигнал идёт от преобразователя USB-RS (он точно абсолютно исправен, проверял 100 раз)
Условимся, что я передаю сообщение 0x02320203. Принимаю его в прерывании, код следующий:
C:
if (EPIC_CHECK_UART_0())
    {
        if (UART_IsRxFifoFull(UART_0)) //данные принялись
        {
            if((rx_buffer.length == 0)) //проверяю условие на длину информации, которая лежит в буфере
            {
                rx_buffer.address = UART_0->RXDATA;//если буфер пустой, первый принятый байт засунь в  rx_buffer.address
                rx_buffer.length++; // увеличь переменную - теперь в буфере лежит 1 байт
            }
            else if((rx_buffer.length == 1)) // аналогичная проверка
                        {
                            rx_buffer.comand = UART_0->RXDATA; // положи второй принятый байт в rx_buffer.comand
                            switch(rx_buffer.comand) // в зависимости от команды задай значение переменной expected_lenght
                            {
                                case 0x32:
                                    expected_lenght = 4;
                                break;
                                case 0x46:
                                case 0x47:
                                case 0x48:
                                    expected_lenght = 2;
                                break;
                                case 0x55:
                                    expected_lenght = 3;
                                break;
                                default:
                                    expected_lenght = 0;
                            }
                            rx_buffer.length++; //ещё раз увеличь length
                        }
                     
                        if((rx_buffer.length < expected_lenght)) // Если сейчас в буфере лежит меньше даты, чем ожидается, то продолжаем принимать
                                {
                                    rx_buffer.data[rx_buffer.length-2] = UART_ReadByte(UART_0); // клади принятый байт в rx_buffer.data[rx_buffer.length-2]. В первой итерации rx_buffer.length-2 = 0, во второй - единице. Соответственно, нулевой и первый элемент массива.
                                    rx_buffer.length++;
                                    if(rx_buffer.length == expected_lenght) rx_buffer.data[2] = 0xFF;//когда длина станет равной ожидаемой - третий элемент массива сделай 0xFF.
                                }
            if(rx_buffer.length == expected_lenght)
                {
                    flag_msg_received = 1; //если длина равна ожидаемой - установи флаг принятого полностью сообщения.
                }
        }
    }

rx_buffer имеет вид

C:
typedef struct
{
    volatile uint8_t address;
    volatile uint8_t comand;
    volatile uint8_t data[8];
    volatile uint8_t length;
    volatile uint8_t error;
    volatile uint16_t error_counter;
} uart_buffer;
volatile uart_buffer rx_buffer = {0};

В main'е следующее:

C:
int main()
{
    write_csr(mtvec, &__TEXT_START__);

    SystemClock_Config();

    GPIO_Init();
    Timer16_0_Init();

    HAL_MspInit();

    HAL_EPIC_Clear(0xFFFFFFFF);
    HAL_EPIC_MaskLevelSet(HAL_EPIC_UART_0_MASK);
    HAL_IRQ_EnableInterrupts();

    GREEN_LED_OFF();
    RED_LED_OFF();

    UART_Init(UART_0, (uint32_t)555, UART_CONTROL1_TE_M | UART_CONTROL1_RE_M | UART_CONTROL1_M_8BIT_M
            | UART_CONTROL1_RXNEIE_M, 0, UART_CONTROL3_OVRDIS_M);

    RS_Receive_MSG();

    while (1)
    {
     
         
        if(flag_msg_received == 1)
        {
            comand_processing();
        }
    }
}

Т.е. просто инициализация и установка приёмника на состояние "принимай". В while'е - проверка флага и простенькая функция обработки принятого сообщения:
C:
void comand_processing(void)
{
    if(rx_buffer.address == 0x02)
    {
        if(rx_buffer.comand == 0x32)
        {
            if (rx_buffer.data[0] == 0x02)
            {
                RED_LED_ON();
                if(rx_buffer.data[1] == 0x03)
                {
                    GREEN_LED_ON();
                }
            }
        }
    }
}

По идее, я должен передать сообщение контроллеру с ПК, контроллер это сообщение обработает и засветит мне 2 светодиода, если сообщение принято верно, т.е. адрес, команда и 2 информационных байта корректны (напомню - от ПК идёт сообщение 0x02320203).

Итак, сама проблема. Если я засуну функции RED_LED_ON() и GREEN_LED_ON() в обработчик прерывания, вот сюда:
C:
if((rx_buffer.length < expected_lenght))
    {
        rx_buffer.data[rx_buffer.length-2] = UART_ReadByte(UART_0);
        rx_buffer.length++;
        if(rx_buffer.data[0] == 0x02) RED_LED_ON();
        if(rx_buffer.data[1] == 0x03) GREEN_LED_ON();
        if(rx_buffer.length == expected_lenght) rx_buffer.data[2] = 0xFF;
    }
То загорится ТОЛЬКО красный диод! Т.е. последний байт (0x03) почему-то не записывается.
И это ещё не всё.
Если сделать такую же проверку, но уже в основной функции (после проверки флага, зайдя в функцию comand_processing()) - вообще ничего не сработает, т.е. comand_processing(), описанная выше, вообще не зажжёт светодиоды, однако если немного поменять условие:

C:
void comand_processing(void)
{
    if(rx_buffer.address == 0x02)
    {
        if(rx_buffer.comand == 0x32)
        {
            if (rx_buffer.data[1] == 0x02)
            {
                RED_LED_ON();
                if(rx_buffer.data[2] == 0xFF)
                {
                    GREEN_LED_ON();
                }
            }
        }
    }
}

То красный диод загорается! Т.е. в прерывании, после обработки принятых байтов, 0x02 лежит в rx_buffer.data[0], а когда я делаю проверку в основной функции, то байт перемещается в rx_buffer.data[1]. Последний байт всё ещё не принимается. Однако зелёный светодиод загорается, т.е. в этом элементе массива (rx_buffer.data[2]) изменение при переходе от прерывания в основную функцию не происходит.
Дополню, что в результате проверки выяснил, что в rx_buffer.data[1] (при обработке прерывания) просто ничего не записывается. Этот байт остается равен нулю, как при инициализации.

Подскажите, где тут собака зарыта)
Уже кажется, что что-то абсолютно очевидное, просто глаза замылились и я этого в упор не вижу.

P.s. на линии всё ок - все байты на месте в нужной последовательности.
1729776759018.png
 
Последнее редактирование:

ejsanyo

Active member
Как-то сложно понять суть из всего написанного, но поскольку у нас тут RS485, то вопрос: вы контроллером только принимаете сейчас или передаёте что-то тоже? Как-то не вижу, чтобы ноги RE, DE драйвера у вас в коде были выставлены в однозначное состояние. А это важно! Если верить схеме, это P0.0 и P1.14.
 

RUMBUFDSI

New member
Как-то сложно понять суть из всего написанного, но поскольку у нас тут RS485, то вопрос: вы контроллером только принимаете сейчас или передаёте что-то тоже? Как-то не вижу, чтобы ноги RE, DE драйвера у вас в коде были выставлены в однозначное состояние. А это важно! Если верить схеме, это P0.0 и P1.14.
RS_Receive_MSG() выставляет ноги в состояние "принимай", забыл это указать)
Пока что контроллер только принимает, ничего не передаёт.
 
Сверху