/** * @file pcan_uart_stm32f4.h * @brief Порт транспорта на USART + DMA для STM32F4. * * Передача - пакетная: DMA отдаёт непрерывный кусок очереди целиком, * одно прерывание на пакет вместо одного на байт. * Приём - кольцевой DMA, позиция вычисляется по NDTR, поэтому длина * посылки заранее не нужна и байты не теряются между вызовами. * * Чего порт НЕ делает намеренно (это забота платы): * - не включает такты RCC для USART, DMA и GPIO; * - не настраивает выводы и альтернативные функции; * - не разрешает прерывания в NVIC. * Так один и тот же порт живёт на любой плате без правок. * * ВАЖНО про буферы: на STM32F407 DMA не имеет доступа к CCM RAM * (0x10000000). Буферы обязаны лежать в основном SRAM (0x20000000), * иначе передача молча не пойдёт. * * Прерывания, которые нужно прокинуть из вектора: * DMAx_StreamN_IRQHandler -> pcan_uart_dma_tx_irq() * USARTx_IRQHandler -> pcan_uart_irq() */ #ifndef PCAN_UART_STM32F4_H #define PCAN_UART_STM32F4_H #include "stm32f4xx.h" #include "pcan_link.h" #include "pcan_ring.h" #ifdef __cplusplus extern "C" { #endif typedef struct { USART_TypeDef *uart; uint32_t pclk_hz; /**< частота шины этого USART */ uint32_t baud; DMA_TypeDef *dma; DMA_Stream_TypeDef *tx_stream; uint8_t tx_stream_idx; /**< 0..7, нужен для флагов LISR/HISR */ uint8_t tx_channel; /**< 0..7 */ DMA_Stream_TypeDef *rx_stream; uint8_t rx_stream_idx; uint8_t rx_channel; /** Управление драйвером RS485. NULL - обычный полнодуплексный UART. */ GPIO_TypeDef *de_port; uint8_t de_pin; uint8_t de_active_low; /**< 1, если DE активен низким уровнем */ } pcan_uart_cfg_t; typedef struct { pcan_uart_cfg_t cfg; pcan_ring_t tx; /**< очередь передачи */ uint8_t *rx_buf; /**< кольцо приёма DMA */ uint16_t rx_size; uint16_t rx_tail; volatile uint16_t tx_chunk; /**< длина текущей DMA-посылки */ volatile uint8_t tx_busy; uint32_t rx_overruns; /**< подозрение на потерю в кольце */ uint32_t uart_errors; /**< ORE / FE / NE / PE */ } pcan_uart_t; /** * @brief Настраивает USART и оба потока DMA. * @param tx_buf,tx_size очередь передачи, размер - степень двойки; * @param rx_buf,rx_size кольцо приёма, размер - степень двойки. * @return false при неверных аргументах. */ bool pcan_uart_init(pcan_uart_t *u, const pcan_uart_cfg_t *cfg, uint8_t *tx_buf, uint16_t tx_size, uint8_t *rx_buf, uint16_t rx_size); /** Заполняет интерфейс для pcan_link_init(). */ void pcan_uart_io(pcan_uart_t *u, pcan_io_t *io); /** * @brief Забирает принятые DMA байты в буфер вызывающего. * * Порт не знает, какой протокол поверх него живёт, поэтому отдаёт сырые * байты: на одном и том же UART так работают и кадр полевой шины, * и GUI-протокол. * * @return число скопированных байт; 0 - новых данных нет. */ size_t pcan_uart_read(pcan_uart_t *u, uint8_t *out, size_t max); /** * @brief Отдаёт принятые DMA байты в канал pcan_link. Из главного цикла. * @return число разобранных кадров. */ size_t pcan_uart_poll(pcan_uart_t *u, pcan_link_t *link); /** Обработчик прерывания потока DMA передачи. */ void pcan_uart_dma_tx_irq(pcan_uart_t *u); /** Обработчик прерывания USART: снимает DE по флагу TC, считает ошибки. */ void pcan_uart_irq(pcan_uart_t *u); #ifdef __cplusplus } #endif #endif /* PCAN_UART_STM32F4_H */