Экспериментальная хуета
Тут у нас приемник сообщений с DWIN панели, который принимает данные через хардварный UART в лупе и передает через софтварный UART на PB3:
#define F_CPU 16000000UL // Тактовая частота МК — 16 мегагерц #include <avr/io.h> #include <util/delay.h> #include <stdint.h> // Для uint8_t и т.д. // Настройки для аппаратного UART (115200 бод, F_CPU = 16 МГц, с U2X для лучшей точности) #define BAUD_HARD 115200 #define UBRR_HARD_VALUE ((F_CPU / (8UL * BAUD_HARD)) - 1) // Настройки для программного UART (9600 бод) #define BAUD_SOFT 9600 #define BIT_DELAY_US (1000000.0 / BAUD_SOFT) // Точный расчет: ~104.1667 us для снижения ошибки тайминга // Максимальный размер пакета (на основе примеров, добавляем запас) #define MAX_PACKET_SIZE 20 // Инициализация аппаратного UART с U2X void UART_Hard_Init(void) { UBRR0H = (UBRR_HARD_VALUE >> 8); // Старший байт делителя UBRR0L = UBRR_HARD_VALUE; // Младший байт делителя UCSR0A |= (1 << U2X0); // Включаем двойную скорость для снижения ошибки baud rate UCSR0B = (1 << RXEN0) | (1 << TXEN0); // Включение RX и TX UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8 бит данных, без паритета, 1 стоп-бит } // Чтение байта из аппаратного UART uint8_t UART_Hard_Receive(void) { while (!(UCSR0A & (1 << RXC0))); // Ждем, пока данные не придут return UDR0; } // Инициализация программного UART (PB3 - TX) void UART_Soft_Init(void) { DDRB |= (1 << PB3); // PB3 как выход PORTB |= (1 << PB3); // Устанавливаем высокий уровень (idle) } // Отправка байта через программный UART void UART_Soft_Transmit(uint8_t data) { // Стартовый бит (0) PORTB &= ~(1 << PB3); _delay_us(BIT_DELAY_US); // Отправка 8 бит данных (LSB first) for (uint8_t i = 0; i < 8; i++) { if (data & (1 << i)) { PORTB |= (1 << PB3); // Устанавливаем 1 } else { PORTB &= ~(1 << PB3); // Устанавливаем 0 } _delay_us(BIT_DELAY_US); } // Стоп-бит (1) PORTB |= (1 << PB3); _delay_us(BIT_DELAY_US); } // Функция для отправки строки через программный UART void UART_Soft_Send_String(const char *str) { while (*str) { UART_Soft_Transmit(*str++); } } // Функция для преобразования байта в HEX-строку и отправки (только два символа, без пробела) void UART_Soft_Send_Hex_Byte(uint8_t byte) { const char hex_chars[] = "0123456789ABCDEF"; // Высокий ниббл UART_Soft_Transmit(hex_chars[(byte >> 4) & 0x0F]); // Низкий ниббл UART_Soft_Transmit(hex_chars[byte & 0x0F]); } int main(void) { // Настройка PD6 как выход и установка высокого уровня DDRD |= (1 << PD6); PORTD |= (1 << PD6); // Инициализация UART UART_Hard_Init(); UART_Soft_Init(); // Тестовое сообщение при старте UART_Soft_Send_String("System initialized.\r\n"); while (1) { // Ждем начала пакета: 0x5A uint8_t header1 = UART_Hard_Receive(); if (header1 != 0x5A) continue; // Ждем второго байта заголовка: 0xA5 uint8_t header2 = UART_Hard_Receive(); if (header2 != 0xA5) continue; // Читаем длину (количество байт после length, включая command и data) uint8_t length = UART_Hard_Receive(); // Буфер для пакета (заголовок + length + data) uint8_t packet[MAX_PACKET_SIZE]; packet[0] = 0x5A; packet[1] = 0xA5; packet[2] = length; // Читаем оставшиеся байты (length байт) for (uint8_t i = 0; i < length; i++) { packet[3 + i] = UART_Hard_Receive(); } // Полный размер пакета uint8_t total_size = 3 + length; // Отправляем префикс без переноса строки UART_Soft_Send_String("Next data Received from DWIN: \n"); // Отправляем пакет как HEX-строку с пробелами между байтами, без trailing space for (uint8_t i = 0; i < total_size; i++) { UART_Soft_Send_Hex_Byte(packet[i]); if (i < total_size - 1) { UART_Soft_Transmit(' '); } } // Завершаем строку UART_Soft_Send_String("\r\n"); } return 0; }
А тут тоже самое, только уже через USARTRXvect
#define F_CPU 16000000UL // Тактовая частота МК — 16 МГц #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <stdint.h> // -------------------- Размеры буферов -------------------- #define MAX_PACKET_SIZE 64 // с запасом под типичные DWIN-кадры // -------------------- Программный UART TX (PB3) -------------------- static inline void UART_Soft_Init(void) { DDRB |= (1 << PB3); // PB3 — выход PORTB |= (1 << PB3); // idle = 1 (линия в "1" в простое) } static inline void UART_Soft_Transmit(uint8_t data) { cli(); // на время передачи блокируем прерывания PORTB &= ~(1 << PB3); // стартовый бит (0) _delay_us(104); // 104 мкс для 9600 бод for (uint8_t i = 0; i < 8; i++) { if (data & (1 << i)) PORTB |= (1 << PB3); // 1 else PORTB &= ~(1 << PB3); // 0 _delay_us(104); } PORTB |= (1 << PB3); // стоп-бит (1) _delay_us(104); sei(); } static inline void UART_Soft_Send_String(const char *s) { while (*s) UART_Soft_Transmit((uint8_t)*s++); } static inline void UART_Soft_Send_HEX(uint8_t b) { static const char H[] = "0123456789ABCDEF"; UART_Soft_Transmit(H[(b >> 4) & 0x0F]); UART_Soft_Transmit(H[b & 0x0F]); } static inline void UART_Soft_Send_Space(void) { UART_Soft_Transmit(' '); } // -------------------- Аппаратный UART0 (RX от DWIN) -------------------- static inline void UART_Hard_Init(void) { // Настройка скорости: 115200 бод при 16 МГц с U2X=1 UBRR0H = 0; // старший байт UBRR UBRR0L = 16; // UBRR = 16 для 115200 бод при U2X=1 UCSR0A = (1 << U2X0); // двойная скорость UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0); // RX, TX, прерывание RX UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8N1 } // -------------------- Парсер DWIN -------------------- // Текущий собираемый пакет volatile uint8_t packet[MAX_PACKET_SIZE]; volatile uint8_t packet_index = 0; // «Сырое» сообщение (готовый кадр) для внешней логики/другой программы volatile uint8_t dwin_raw[MAX_PACKET_SIZE]; volatile uint8_t dwin_len = 0; // фактическая длина готового кадра volatile uint8_t dwin_ready = 0; // флаг «готов к обработке/печати» static inline void parser_reset(void) { packet_index = 0; } // Вспомогательное: безопасная запись байта в packet[] static inline uint8_t packet_push(uint8_t byte) { if (packet_index >= MAX_PACKET_SIZE) return 0; packet[packet_index++] = byte; return 1; } // -------------------- Прерывание приёма UART0 -------------------- ISR(USART_RX_vect) { uint8_t data = UDR0; // Ждём заголовок 0x5A 0xA5 if (packet_index == 0) { if (data != 0x5A) return; // пока не 0x5A — игнор } else if (packet_index == 1) { if (data != 0xA5) { parser_reset(); return; } } // Пишем байт if (!packet_push(data)) { parser_reset(); // защита от переполнения return; } // Как только есть хотя бы 3 байта, можем проверить полноту if (packet_index >= 3) { uint8_t len = packet[2]; // длина полезной части после байта длины uint8_t need_total = (uint8_t)(3 + len); // 2 заголовка + 1 длина + len if (packet_index == need_total) { // Пакет полностью собран — копируем в «сырое» хранилище uint8_t copy_len = need_total; if (copy_len > MAX_PACKET_SIZE) copy_len = MAX_PACKET_SIZE; for (uint8_t i = 0; i < copy_len; i++) dwin_raw[i] = packet[i]; dwin_len = copy_len; dwin_ready = 1; // сигнал в main() parser_reset(); // готовимся к следующему кадру } else if (packet_index > need_total) { // рассинхронизация — сброс parser_reset(); } } } // -------------------- main -------------------- int main(void) { // PD6 — индикатор (по желанию) DDRD |= (1 << PD6); PORTD |= (1 << PD6); // подтянем в "1" UART_Hard_Init(); UART_Soft_Init(); sei(); // глобальные прерывания UART_Soft_Send_String("System initialized.\r\n"); for (;;) { if (dwin_ready) { // Делаем атомарный «снимок» готового кадра, чтобы ISR не переписал хвост uint8_t local_len; uint8_t local_buf[MAX_PACKET_SIZE]; cli(); local_len = dwin_len; if (local_len > MAX_PACKET_SIZE) local_len = MAX_PACKET_SIZE; for (uint8_t i = 0; i < local_len; i++) local_buf[i] = dwin_raw[i]; dwin_ready = 0; // сбрасываем флаг только после копирования sei(); // Красивый вывод всего пакета за один раз UART_Soft_Send_String("Next data Received from DWIN: \n"); for (uint8_t i = 0; i < local_len; i++) { UART_Soft_Send_HEX(local_buf[i]); if ((i + 1) < local_len) UART_Soft_Send_Space(); } UART_Soft_Send_String("\r\n"); // --- Место для твоей логики работы с «сырым» кадром --- // Используй local_buf (длина local_len) или // dwin_raw/dwin_len, если удобнее. // Пример: номер кнопки часто лежит в последнем байте local_buf[local_len - 1]. } // Остальной код твоего приложения ... } // До сюда не дойдём // return 0; }