Всем добрый день!
Имеется 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);
Ошибка возникает только при чтении с небольшим периодом или быстрой последовательной записи. Если редко читать и редко писать - ошибки не возникает
Заранее благодарю за помощь