ДОМО насИнформация об отрасли Практическое руководство по созданию цифровых раци...

Практическое руководство по созданию цифровых раций на базе ESP32 (часть 4): разработка прошивки и драйверов

11

Sep . 2025

Автор: SDGA:

Для эффективного и надёжного управления DMR858M рекомендуется использовать объектно-ориентированный подход, создав класс драйвера для инкапсуляции всех взаимодействий с модулем. Эта архитектура аналогична библиотекам, разработанным для других модулей AT-команд (например, GSM или Wi-Fi), и обеспечивает хорошую модульность и возможность повторного использования.

Архитектурный метод: класс DMR858M_Controller

Мы разработаем класс C++ под названием DMR858M_Controller. Этот класс будет отвечать за управление коммуникациями по UART, формирование и анализ кадров данных, обработку команд и ответов, а также управление состоянием модуля.

// DMR858M_Controller.h #include <Arduino.h> class DMR858M_Controller { public:     DMR858M_Controller(HardwareSerial& serial, int pttPin, int csPin);     void begin(long speed);     bool setFrequency(uint32_t txFreq, uint32_t rxFreq);     bool setPowerLevel(bool highPower);     bool getFirmwareVersion(String& version);     void setPTT(bool active);     // ... другие прототипы функций private:     HardwareSerial& _serial;     int _pttPin;     int _csPin;     void sendCommand(uint8_t cmd, uint8_t rw, const uint8_t* data, uint16_t len);     bool waitForResponse(uint8_t* буфер, uint16_t& len, uint32_t таймаут = 1000);     uint16_t calculateChecksum(const uint8_t* данные, size_t len); };






















Подробности реализации ядра (пример кода)

Формирование и передача пакетов

sendCommand — это ядро ​​всех операций записи. Он отвечает за сборку полного двоичного пакета, вычисление контрольной суммы и отправку его через UART.

// DMR858M_Controller.cpp void DMR858M_Controller::sendCommand(uint8_t cmd, uint8_t rw, const uint8_t* data, uint16_t len) {     const uint16_t totalFrameLen = 9 + len;     uint8_t frame[totalFrameLen];     frame[0] = 0x68; // Head     frame[1] = cmd; // CMD     frame[2] = rw; // R/W     frame[3] = 0x01; // S/R (Запрос)     frame[4] = 0x00; // CKSUM_HI (временный)     frame[5] = 0x00; // CKSUM_LO (временный)     frame[6] = (len >> 8) & 0xFF; // LEN_HI     frame[7] = len & 0xFF; // LEN_LO     если (data && len > 0) {         memcpy(&frame[8], data, len);     }     frame[8 + len] = 0x10; // Хвост     // Вычислить контрольную сумму от CMD до конца DATA     uint16_t checksum = calculateChecksum(&frame[1], 7 + len);     frame[4] = (checksum >> 8) & 0xFF; // CKSUM_HI     frame[5] = checksum & 0xFF; // CKSUM_LO     _serial.write(frame, totalFrameLen); } uint16_t DMR858M_Controller::calculateChecksum(const uint8_t* buf, size_t len) {     uint32_t sum = 0;     const uint8_t* current_buf = buf;     size_t current_len = len;     while (current_len > 1) {         сумма += (uint16_t)((*current_buf << 8) | *(current_buf + 1));         current_buf += 2;         current_len -= 2;     }     if (current_len > 0) {         сумма += (uint16_t)(*current_buf << 8);     }     while (sum >> 16) {         сумма = (sum & 0xFFFF) + (sum >> 16);     }     return (uint16_t)(sum ^ 0xFFFF); }








   


   


   



   

   


   


   






















Важность обработки ответов и асинхронных операций

Во встраиваемых системах блокирующее ожидание — это шаблон программирования, которого следует избегать. Простая функция waitForResponse, использующая цикл типа while(!_serial.available()){}, заморозит весь основной цикл, не давая микроконтроллеру выполнять другие задачи, такие как обновление дисплея или реагирование на нажатия кнопок, что приведёт к зависанию системы.

Более надёжная конструкция должна быть неблокируемой . В основном цикле программа должна постоянно проверять последовательный порт на наличие данных и использовать конечный автомат для обработки входящего кадра данных. Такой подход гарантирует, что система сможет обрабатывать другие события реального времени, ожидая ответа от модуля. Для такой платформы, как ESP32, поддерживающей FreeRTOS, лучшим решением будет создание отдельной задачи RTOS для управления взаимодействием с модулем DMR. Эта задача может блокироваться при отсутствии данных, не влияя на выполнение других задач.

Вот упрощенный пример неблокирующей логики чтения, подходящей для функции Arduino loop():

// Упрощенная неблокирующая логика обработки ответа void loop() {     // ... другие задачи...     if (_serial.available()) {         // Считывание байта и помещение его в буфер         // Использование конечного автомата для анализа кадра данных (поиск заголовка 0x68, чтение указанной длины, проверка контрольной суммы и хвоста 0x10)         // После успешного анализа обработать данные ответа     } }










Подробный пример: программа проверки концепции

Ниже приведен полный пример Arduino/PlatformIO, демонстрирующий, как инициализировать модуль, управлять PTT с помощью кнопки и отправлять SMS через последовательный монитор.

#include <Arduino.h> #include "DMR858M_Controller.h" #define PTT_BUTTON_PIN 25 #define PTT_MODULE_PIN 26 #define LED_PIN 2 HardwareSerial SerialTwo(2); DMR858M_Controller dmr(SerialTwo, PTT_MODULE_PIN, -1); void setup() {     Serial.begin(115200);     pinMode(PTT_BUTTON_PIN, INPUT_PULLUP);     pinMode(LED_PIN, OUTPUT);     dmr.begin(57600);     delay(500);     String fwVersion;     if (dmr.getFirmwareVersion(fwVersion)) {         Serial.println("Прошивка DMR858M: " + fwVersion);     } else {         Serial.println("Не удалось связаться с модулем DMR858M.");     }     // Пример: Установить частоту для канала 1 на 433,500 МГц     dmr.setFrequency(433500000, 433500000); } void loop() {     // Логика управления PTT     if (digitalRead(PTT_BUTTON_PIN) == LOW) {         dmr.setPTT(true);         digitalWrite(LED_PIN, HIGH); // Индикатор передачи     } else {         dmr.setPTT(false);         digitalWrite(LED_PIN, LOW);     }     // ... здесь можно добавить неблокирующую логику обработки последовательного ответа...     // Пример: Отправить SMS через последовательный монитор     if (Serial.available()) {         String cmd = Serial.readStringUntil('\n');         if (cmd.startsWith("sms")) {             // Анализ содержимого SMS и идентификатора цели             // Вызов dmr.sendSMS(...)             Serial.println("Команда SMS получена.");         }     } }



















































Практическое руководство по созданию серий цифровых раций ESP32


Часть 1: Углубленный анализ модуля DMR858M

Часть 2: Интеграция оборудования и эталонный дизайн

Часть 3: Разбор последовательного протокола управления

Часть 4: Разработка прошивки и проектирование драйверов

Часть 5: Изучение расширенных функций и заключение



    Связаться с нами

     +86-755-23080616

     sales@nicerf.com

    Сайт: https://www.nicerf.com/ .

    Адрес: 309-314, 3/F, корпус A, деловое здание Хунду, зона 43, район Баоань, Шэньчжэнь, Китай

    Связаться с нами
    политика конфиденциальности

    политика конфиденциальности

    · Политика конфиденциальности

    В настоящее время нет доступного контента


               

    Электронная почта: sales@nicerf.com

    Тел:+86-755-23080616

    Адрес: 309-314, 3/F, корпус A, деловое здание Хунду, зона 43, район Баоань, Шэньчжэнь, Китай


    ×