Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

STM32 - отправляет нули в UART

Сб дек 03, 2022 20:47:29

Всем добрый день!
Имеется STM32F103 и стоит задача сделать устройство MODBUS slave
Реализовал так:

Создал структуру:
Код:
typedef struct {
   uint8_t update; // флаг того что данные обновились
   uint8_t rx_buffer_uart[256]; // буфер по максимальному размеру пакета MODBUS
   uint8_t size; // размер принятных данных
} receive_data;


Затем разрешил приём:
Код:
  HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t*)receive_data_slave.rx_buffer_uart, BUFFER_SIZE); // разрешить ожидание приёма пакета


В бесконечном цикле основной программы проверяю не обновились ли данные и если данные обновились, то приступаю к обработке:
Код:
  while (1)
  {
     if (receive_data_slave.update == 1)  // если данные обновились
     {
        uint8_t response[256] = {0};  // создать пустой массив для ответа
        process_receive_slave(&receive_data_slave, &slave_hold_reg, &response);  // передать в обработчик полученные данные, регистры для чтения/записи, указатель на массив для ответа
        if (response[0] != 0)  // если массив для ответа не пустой
           while(HAL_UART_Transmit_DMA(&huart1, (uint8_t*)response, sizeof(response)) == HAL_BUSY);  // отправить ответ
        receive_data_slave.update = 0;  // данные обработаны, снимаю флаг обновления
        HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t*)receive_data_slave.rx_buffer_uart, BUFFER_SIZE);  // разрешаю приём следующих данных
     }
  }


Обработчик выглядит так:
Код:
void process_receive_slave (receive_data* data, uint16_t *reg, uint8_t *response)
{
   if (data->rx_buffer_uart[0] != SLAVE_ADDRESS)  // если данные не для нашего адреса - сразу выходим
      return;

   int crc;
   crc = crc_chk(data->rx_buffer_uart, (data->size)-2);
   if ((data->rx_buffer_uart[(data->size)-1] == (crc & 0xFF00) >> 8) &&  //проверка чек-суммы пакета
         (data->rx_buffer_uart[(data->size)-2] == (crc & 0x00FF)))
   {
      uint16_t addr_reg, quantity;
      addr_reg = ((data->rx_buffer_uart[2]) << 8) + (data->rx_buffer_uart[3]);  // определение адреса регистра указанного в пакете
      switch(data->rx_buffer_uart[1]) {
      case 3:  // если команда на чтение
         quantity = ((data->rx_buffer_uart[4]) << 8) + (data->rx_buffer_uart[5]); // сколько запрошено регистров считать
         response[0] = SLAVE_ADDRESS;  // далее формирование ответа
         response[1] = 0x03;  // команда
         response[2] = quantity * 2;
         uint8_t i = 0;
         uint8_t j = 3;
         while(i < quantity)
         {
            response[j] = (reg[addr_reg-SLAVE_REG_START+i] & 0xFF00) >> 8;
            j++;
            response[j] = reg[addr_reg-SLAVE_REG_START+i] & 0x00FF;
            j++;
            i++;
         }
         crc = crc_chk(response, j);
         response[j] = crc & 0x00FF;
         j++;
         response[j] = (crc & 0xFF00) >> 8;
         break;
      case 6:  // если команда на запись
         reg[addr_reg-SLAVE_REG_START] = ((data->rx_buffer_uart[4]) << 8) + (data->rx_buffer_uart[5]);  // записать данные в регистр

         for (int i=0; i < data->size; i++)  // сформировать ответ - копия запроса
          {
             response[i] = data->rx_buffer_uart[i];
          }
         break;
      default:
         break;
      }
   }
}


Читаю 100 регистров разом с периодом 100 мс - всё ОК. (командой 01 03 00 01 00 64 15 E1)
Но есть проблема, при попытке записи (командой 01 06 00 01 01 F4 D8 1D) (при этом чтение идёт всё также с периодом 100 мс) я иногда получаю ответ состоящий из всех нулей либо на команду записи, либо на команду чтения, следующую за командой записи.

Подскажите, пожалуйста, откуда берутся эти нули?
Ведь стоит защита от оправки пустого ответа и ожидание освобождения передатчика:
Код:
        if (response[0] != 0)
           while(HAL_UART_Transmit_DMA(&huart1, (uint8_t*)response, sizeof(response)) == HAL_BUSY);


Ошибка возникает только при чтении с небольшим периодом или быстрой последовательной записи. Если редко читать и редко писать - ошибки не возникает

Заранее благодарю за помощь

Re: STM32 - отправляет нули в UART

Вс дек 04, 2022 08:55:01

uint8_t rx_buffer_uart[256];

HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t*)receive_data_slave.rx_buffer_uart, BUFFER_SIZE);

Так не хорошо, или везде число, или- имя, что гораздо лучше.

uint8_t response[256] = {0}; // создать пустой массив для ответа

Зачем ещё один массив? Если не собираешься передавать пакеты из RTU-сегмента в TCP/UDP-сегмент, то достаточно одного массива.

while(HAL_UART_Transmit_DMA(&huart1, (uint8_t*)response, sizeof(response)) == HAL_BUSY);

То есть тупо стоим и жгём время, пока посылка не уйдёт? А ведь могли бы заниматься чем-то полезным.

crc = crc_chk(data->rx_buffer_uart, (data->size)-2);

Считай КС _всего_ пакета. В целом пакете она будет 0. Нет?

Я ХАЛом не пользуюсь, но не вижу управления направлением приёма-передачей у трансивера. Или Tx и Rx в физической линии разделены?

я иногда получаю ответ состоящий из всех нулей

В чём смотришь? Я пользовался Termit. Покажи весь ответ.
Ответить