#include #include "pcan_uart_stm32f4.h" /* --- Работа с флагами DMA ------------------------------------------------- * У DMA на F4 флаги четырёх потоков лежат в LISR/LIFCR, ещё четырёх - * в HISR/HIFCR, причём внутри регистра смещения неравномерны. */ static const uint8_t s_flag_shift[4] = { 0U, 6U, 16U, 22U }; #define DMA_FLAG_FEIF 0x01U #define DMA_FLAG_DMEIF 0x04U #define DMA_FLAG_TEIF 0x08U #define DMA_FLAG_HTIF 0x10U #define DMA_FLAG_TCIF 0x20U #define DMA_FLAG_ALL (DMA_FLAG_FEIF | DMA_FLAG_DMEIF | DMA_FLAG_TEIF | \ DMA_FLAG_HTIF | DMA_FLAG_TCIF) static inline uint32_t dma_isr(const DMA_TypeDef *dma, uint8_t stream) { uint32_t reg = (stream < 4U) ? dma->LISR : dma->HISR; return (reg >> s_flag_shift[stream & 3U]) & DMA_FLAG_ALL; } static inline void dma_clear(DMA_TypeDef *dma, uint8_t stream, uint32_t flags) { uint32_t value = (flags & DMA_FLAG_ALL) << s_flag_shift[stream & 3U]; if (stream < 4U) { dma->LIFCR = value; } else { dma->HIFCR = value; } } static inline void dma_disable(DMA_Stream_TypeDef *stream) { stream->CR &= ~DMA_SxCR_EN; /* Аппаратура снимает EN не мгновенно; трогать регистры потока до этого момента нельзя. */ while ((stream->CR & DMA_SxCR_EN) != 0U) { } } /* --- Управление драйвером RS485 ------------------------------------------- */ static inline void de_assert(const pcan_uart_t *u) { if (u->cfg.de_port == NULL) { return; } uint32_t pin = 1UL << u->cfg.de_pin; u->cfg.de_port->BSRR = u->cfg.de_active_low ? (pin << 16) : pin; } static inline void de_release(const pcan_uart_t *u) { if (u->cfg.de_port == NULL) { return; } uint32_t pin = 1UL << u->cfg.de_pin; u->cfg.de_port->BSRR = u->cfg.de_active_low ? pin : (pin << 16); } /* --- Передача ------------------------------------------------------------- * Кольцо заворачивается, а DMA нужен непрерывный участок, поэтому за раз * отдаём кусок до конца буфера. Остаток уйдёт следующей посылкой. */ static void tx_start(pcan_uart_t *u) { if (u->tx_busy) { return; } const uint8_t *ptr = NULL; uint16_t len = pcan_ring_linear(&u->tx, &ptr); if (len == 0U) { return; } u->tx_busy = 1U; u->tx_chunk = len; de_assert(u); /* Флаг TC мог остаться от прошлой посылки - иначе прерывание придёт сразу и отпустит DE посреди новой. */ (void)u->cfg.uart->SR; u->cfg.uart->SR = ~USART_SR_TC; dma_clear(u->cfg.dma, u->cfg.tx_stream_idx, DMA_FLAG_ALL); u->cfg.tx_stream->M0AR = (uint32_t)(uintptr_t)ptr; u->cfg.tx_stream->NDTR = len; u->cfg.tx_stream->CR |= DMA_SxCR_EN; } void pcan_uart_dma_tx_irq(pcan_uart_t *u) { uint32_t flags = dma_isr(u->cfg.dma, u->cfg.tx_stream_idx); if ((flags & (DMA_FLAG_TEIF | DMA_FLAG_DMEIF | DMA_FLAG_FEIF)) != 0U) { dma_clear(u->cfg.dma, u->cfg.tx_stream_idx, DMA_FLAG_ALL); dma_disable(u->cfg.tx_stream); /* Ошибка передачи: кусок считаем отданным, иначе встанет очередь. */ pcan_ring_consume(&u->tx, u->tx_chunk); u->tx_busy = 0U; u->uart_errors++; tx_start(u); return; } if ((flags & DMA_FLAG_TCIF) == 0U) { return; } dma_clear(u->cfg.dma, u->cfg.tx_stream_idx, DMA_FLAG_ALL); dma_disable(u->cfg.tx_stream); pcan_ring_consume(&u->tx, u->tx_chunk); u->tx_chunk = 0U; u->tx_busy = 0U; if (!pcan_ring_empty(&u->tx)) { tx_start(u); return; } /* Данных больше нет. DMA лишь дописал последний байт в DR - в линии он ещё не весь. Отпускаем DE только по TC. */ if (u->cfg.de_port != NULL) { u->cfg.uart->CR1 |= USART_CR1_TCIE; } } void pcan_uart_irq(pcan_uart_t *u) { uint32_t sr = u->cfg.uart->SR; if ((sr & (USART_SR_ORE | USART_SR_FE | USART_SR_NE | USART_SR_PE)) != 0U) { (void)u->cfg.uart->DR; /* чтение SR + DR снимает флаги */ u->uart_errors++; } if (((sr & USART_SR_TC) != 0U) && ((u->cfg.uart->CR1 & USART_CR1_TCIE) != 0U)) { u->cfg.uart->SR = ~USART_SR_TC; u->cfg.uart->CR1 &= ~USART_CR1_TCIE; /* Если пока ждали TC успел встать новый кадр - DE не опускаем. */ if (!u->tx_busy && pcan_ring_empty(&u->tx)) { de_release(u); } } } static size_t io_write(void *ctx, const uint8_t *data, size_t len) { pcan_uart_t *u = (pcan_uart_t *)ctx; if (len > 0xFFFFU) { return 0U; } if (!pcan_ring_write(&u->tx, data, (uint16_t)len)) { return 0U; } /* Прерывание DMA может добраться до очереди одновременно с нами, но tx_busy защищает от двойного запуска потока. */ tx_start(u); return len; } static size_t io_space(void *ctx) { pcan_uart_t *u = (pcan_uart_t *)ctx; return pcan_ring_free(&u->tx); } void pcan_uart_io(pcan_uart_t *u, pcan_io_t *io) { io->write = io_write; io->tx_space = io_space; io->ctx = u; } /* --- Приём ---------------------------------------------------------------- */ size_t pcan_uart_read(pcan_uart_t *u, uint8_t *out, size_t max) { uint16_t head = (uint16_t)(u->rx_size - (uint16_t)u->cfg.rx_stream->NDTR); uint16_t tail = u->rx_tail; if ((head == tail) || (max == 0U)) { return 0U; } uint16_t pending = (uint16_t)((head - tail) & (uint16_t)(u->rx_size - 1U)); if (pending > (uint16_t)(u->rx_size - (u->rx_size / 4U))) { /* Кольцо почти догнало нас: часть байт, вероятно, уже затёрта. Точнее аппаратура не скажет - в circular-режиме DMA не сигнализирует переполнение. */ u->rx_overruns++; } size_t n = (pending > max) ? max : pending; for (size_t i = 0U; i < n; i++) { out[i] = u->rx_buf[tail]; tail = (uint16_t)((tail + 1U) & (uint16_t)(u->rx_size - 1U)); } u->rx_tail = tail; return n; } size_t pcan_uart_poll(pcan_uart_t *u, pcan_link_t *link) { uint8_t chunk[64]; size_t frames = 0U; size_t n; while ((n = pcan_uart_read(u, chunk, sizeof chunk)) != 0U) { frames += pcan_link_feed(link, chunk, n); } return frames; } /* --- Инициализация -------------------------------------------------------- */ 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) { if ((u == NULL) || (cfg == NULL) || (cfg->uart == NULL) || (cfg->dma == NULL) || (cfg->tx_stream == NULL) || (cfg->rx_stream == NULL) || (cfg->baud == 0U)) { return false; } if ((rx_buf == NULL) || (rx_size < 2U) || ((rx_size & (uint16_t)(rx_size - 1U)) != 0U)) { return false; } u->cfg = *cfg; if (!pcan_ring_init(&u->tx, tx_buf, tx_size)) { return false; } u->rx_buf = rx_buf; u->rx_size = rx_size; u->rx_tail = 0U; u->tx_chunk = 0U; u->tx_busy = 0U; u->rx_overruns = 0U; u->uart_errors = 0U; /* -- USART: 8N1, OVER8 = 0 -- */ cfg->uart->CR1 = 0U; cfg->uart->CR2 = 0U; cfg->uart->CR3 = 0U; cfg->uart->BRR = (uint16_t)((cfg->pclk_hz + (cfg->baud / 2U)) / cfg->baud); /* -- DMA передачи: память -> периферия, обычный режим -- */ dma_disable(cfg->tx_stream); dma_clear(cfg->dma, cfg->tx_stream_idx, DMA_FLAG_ALL); cfg->tx_stream->PAR = (uint32_t)(uintptr_t)&cfg->uart->DR; cfg->tx_stream->FCR = 0U; /* прямой режим, без FIFO */ cfg->tx_stream->CR = ((uint32_t)cfg->tx_channel << DMA_SxCR_CHSEL_Pos) | DMA_SxCR_DIR_0 /* мем -> периферия */ | DMA_SxCR_MINC | (2UL << DMA_SxCR_PL_Pos) /* высокий приоритет */ | DMA_SxCR_TCIE | DMA_SxCR_TEIE; /* -- DMA приёма: периферия -> память, кольцевой, работает всегда -- */ dma_disable(cfg->rx_stream); dma_clear(cfg->dma, cfg->rx_stream_idx, DMA_FLAG_ALL); cfg->rx_stream->PAR = (uint32_t)(uintptr_t)&cfg->uart->DR; cfg->rx_stream->M0AR = (uint32_t)(uintptr_t)rx_buf; cfg->rx_stream->NDTR = rx_size; cfg->rx_stream->FCR = 0U; cfg->rx_stream->CR = ((uint32_t)cfg->rx_channel << DMA_SxCR_CHSEL_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC | (2UL << DMA_SxCR_PL_Pos); cfg->rx_stream->CR |= DMA_SxCR_EN; de_release(u); cfg->uart->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; cfg->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; return true; }