| Форум РадиоКот https://radiokot.ru/forum/ |
|
| STM32 + DMA + RS485, буфер считывает эхо https://radiokot.ru/forum/viewtopic.php?f=59&t=198641 |
Страница 1 из 1 |
| Автор: | radiokayro13 [ Вт июл 22, 2025 22:34:36 ] |
| Заголовок сообщения: | STM32 + DMA + RS485, буфер считывает эхо |
Всем привет! В программировании МК новичок, пытаюсь заставить работать UART + DMA + RS485 на STM32L433cct6. Проблема в том, что в буффер попадает отправленная команда, связывал с тем, что не успевает опуститься DE пин и МК считывает свою же команду с линии, победить смог только добавив адресный байт и фильтруя по нему. Вторым странным фактом является то, что без HAL_Delay в SendCommand сообщение не отправляется. Ну и вишенкой на торте: на другой МК сообщение отправляется поврежденное, первые 4 байта как и должны, а остальные какой-то шум. Подозреваю, что проблемы именно в коде, кажется, я тут много лишнего накрутил, помогите найти ошибки, чтобы исправить и улучшить свои навыки) Код для uart_dma.hpp Код: class DMA final { public: explicit DMA(UART_HandleTypeDef* huart, CRC_HandleTypeDef* hcrc); ~DMA(); DMA(const DMA&) = delete; DMA& operator=(const DMA&) = delete; // Initialization void Initialize(); // Commands void RegisterCommand(uint8_t cmd, BKUCommand::Command* handler); void SendCommand(uint8_t id, uint16_t data); // Transport operations void HandleBuffer(); void HandleTxComplete(); void HandleError(); void HandleRxComplete(); void HandleTimComplete(); private: // Hardware handles UART_HandleTypeDef* _huart; CRC_HandleTypeDef* _hcrc; // Commands static constexpr uint8_t START_BYTE = 0xA; static constexpr size_t FRAME_SIZE = 7; static constexpr size_t MAX_COMMANDS = 8; struct CommandHandler { uint8_t cmd; BKUCommand::Command* handler; }; CommandHandler _handlers[MAX_COMMANDS]; uint8_t _handler_count = 0; // DMA static constexpr size_t DMA_BUFFER_SIZE = 16; uint8_t _dma_buffer[DMA_BUFFER_SIZE]; uint32_t _dma_pos = 0; uint32_t _last_sent_time = 0; // CRC uint16_t calculateCRC(const uint8_t* data) const; // Frame void processFrame(const uint8_t* frame); bool validateFrame(const uint8_t* frame) const; // Tx volatile bool _is_sending = false; // Helper functions BKUCommand::Command* findHandler(uint8_t cmd); }; Код для uart_dma.cpp Код: #include "uart_dma.hpp"
#include <string.h> #define ADDRESS 0xF namespace UART { DMA::DMA(UART_HandleTypeDef* huart, CRC_HandleTypeDef* hcrc) : _huart(huart), _hcrc(hcrc), _handler_count(0) { memset(_dma_buffer, 0, DMA_BUFFER_SIZE); } DMA::~DMA() { HAL_UART_DMAStop(_huart); } void DMA::Initialize() { HAL_CRC_Init(_hcrc); HAL_UART_Receive_DMA(_huart, _dma_buffer, DMA_BUFFER_SIZE); } void DMA::RegisterCommand(uint8_t cmd, BKUCommand::Command* handler) { if (_handler_count < MAX_COMMANDS) { _handlers[_handler_count].cmd = cmd; _handlers[_handler_count].handler = handler; _handler_count++; } } void DMA::SendCommand(uint8_t cmd, uint16_t data) { if (__HAL_UART_GET_FLAG(_huart, UART_FLAG_BUSY)) { return; // Ждем завершения предыдущей передачи } _is_sending = true; uint8_t frame[FRAME_SIZE] = { START_BYTE, BKU_ADDRESS, cmd, static_cast<uint8_t>(data >> 8), static_cast<uint8_t>(data & 0xFF) }; uint16_t crc = calculateCRC(frame); frame[5] = crc >> 8; frame[6] = crc & 0xFF; BKUCommand::Command* handler = findHandler(cmd); if (handler != nullptr) { handler->SetRequestTime(HAL_GetTick()); } HAL_Delay(30); HAL_UART_Transmit_DMA(_huart, frame, FRAME_SIZE); } BKUCommand::Command* DMA::findHandler(uint8_t cmd) { for (uint8_t i = 0; i < _handler_count; i++) { if (_handlers[i].cmd == cmd) { return _handlers[i].handler; } } return nullptr; } void DMA::HandleTxComplete() { _is_sending = false; } void DMA::HandleRxComplete() {} void DMA::HandleError() { _is_sending = false; _dma_pos = 0; HAL_UART_DMAStop(_huart); HAL_UART_Receive_DMA(_huart, _dma_buffer, DMA_BUFFER_SIZE); __HAL_UART_CLEAR_FLAG(_huart, UART_FLAG_ORE | UART_FLAG_NE | UART_FLAG_FE); } void DMA::HandleBuffer() { uint32_t current_pos = DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(_huart->hdmarx); uint32_t bytes_to_process = (current_pos - _dma_pos) % DMA_BUFFER_SIZE; if (bytes_to_process < FRAME_SIZE) { return; } // Защита от переполнения буфера if ((current_pos - _dma_pos) % DMA_BUFFER_SIZE > DMA_BUFFER_SIZE/2) { _dma_pos = current_pos; return; } while (_dma_pos != current_pos) { // Если найден стартовый байт и достаточно данных для проверки if (_dma_buffer[_dma_pos] == START_BYTE) { uint8_t frame[FRAME_SIZE]; bool frame_complete = true; // Проверяем, есть ли полный фрейм в буфере for (int i = 0; i < FRAME_SIZE; i++) { uint32_t pos = (_dma_pos + i) % DMA_BUFFER_SIZE; if (pos == current_pos) { // Достигли текущей позиции DMA frame_complete = false; break; } frame[i] = _dma_buffer[pos]; } if (validateFrame(frame)) { processFrame(frame); _dma_pos = (_dma_pos + FRAME_SIZE) % DMA_BUFFER_SIZE; continue; // Пропускаем инкремент позиции } else { // Невалидный фрейм - пропускаем только стартовый байт _dma_pos = (_dma_pos + 1) % DMA_BUFFER_SIZE; continue; } } // Переходим к следующему байту _dma_pos = (_dma_pos + 1) % DMA_BUFFER_SIZE; } } void DMA::processFrame(const uint8_t* frame) { uint8_t cmd = frame[2]; uint16_t data = (frame[3] << 8) | frame[4]; BKUCommand::Command* handler = findHandler(cmd); if (handler != nullptr) { handler->ProcessPayload(data); } } bool DMA::validateFrame(const uint8_t* frame) const { if (frame[0] != START_BYTE) { return false; } if (frame[1] != ADDRESS) { return false; } uint16_t received_crc = (frame[5] << 8) | frame[6]; // Байты CRC в фрейме uint16_t calculated_crc = calculateCRC(frame); return true; } uint16_t DMA::calculateCRC(const uint8_t* data) const { // Temporary buffer for alignment uint32_t aligned_data[2] = {0}; memcpy(aligned_data, data, 5); // Hardware CRC calculation uint32_t crc = HAL_CRC_Calculate(_hcrc, aligned_data, 1); // 4 bytes = 1 word return ~crc & 0xFFFF; // Inversion for CRC-16 standard compliance } } |
|
| Автор: | HardWareMan [ Ср июл 23, 2025 09:28:19 ] |
| Заголовок сообщения: | Re: STM32 + DMA + RS485, буфер считывает эхо |
Проблема в том, что в буффер попадает отправленная команда, связывал с тем, что не успевает опуститься DE пин и МК считывает свою же команду с линии, победить смог только добавив адресный байт и фильтруя по нему. Если не нужно эхо, то приёмник надо отключать до первого байта отправки. С другой стороны, эхо у полудуплексных линий используется для контроля отсутствия коллизий в "эфире". |
|
| Автор: | tonyk [ Ср июл 23, 2025 11:25:08 ] |
| Заголовок сообщения: | Re: STM32 + DMA + RS485, буфер считывает эхо |
radiokayro13 писал(а): Подозреваю, что проблемы именно в коде Зачем аппаратный CRC, когда используете программный? Оставьте его для USB и Ethernet, в которых он реально нужен. Где управление трансивером? Или он у вас с автодектом? Или UART им управляет? Приём через DMA не_требует защиты от переполнения. Нужен только один дополнительный байт для проверки правильной длины посылки. Странно у вас там всё. Вам нужно обработать два прерывания: первое от DMA по окончании отправки всего блока, которое разрешит прерывание от UART, и второе от UART, когда в линию уйдёт последний байт посылки. Прерывании от UART означает, что посылка ушла в линию и можно сигнализировать об окончании передачи и переключать трансивер на приём. Добавлено after 32 minutes 32 seconds: Настройку UART не вижу. |
|
| Страница 1 из 1 | Часовой пояс: UTC + 3 часа |
| Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
|


