#include #include #include #include #include #include volatile struct uart_dma_state uart_st; static void uart_schedule_dma(bool retransmit); static int uart_putc_nonblocking(uint8_t c); static void uart_dma_reset(void); void uart_dma_reset() { memset((void *)&uart_st, 0, sizeof(uart_st)); uart_st.tx_buf.xfr_start = -1; for (size_t i=0; iCPAR = (uint32_t)&(usart->TDR); uart_st.tx_dma_ch->CCR = (0<CR1 = /* 8-bit -> M1, M0 clear */ /* OVER8 clear. Use default 16x oversampling */ /* CMIF clear */ /* MME clear */ /* WAKE clear */ /* PCE, PS clear */ USART_CR1_RXNEIE /* Enable receive interrupt */ /* other interrupts clear */ | USART_CR1_TE | USART_CR1_RE; /* Set baudrate */ usart->BRR = 192; /* 250kBd */ usart->CR3 |= USART_CR3_DMAT /* TX DMA enable */ | USART_CR3_DEM; /* Output DE signal on RTS pin */ /* And... go! */ usart->CR1 |= USART_CR1_UE; } void uart_interrupt(uint32_t isr) { if (isr & USART_ISR_ORE) { uart_st.error = ERR_RX_OVERRUN; uart_st.last_error = sys_time_us; return; } if (isr & USART_ISR_RXNE) { uint8_t c = uart_st.usart->RDR; int rc = cobs_decode_incremental((struct cobs_decode_state *)&uart_st.cobs_state, (char *)&uart_st.rx_buf, sizeof(uart_st.rx_buf), c); if (rc == 0) /* packet still incomplete */ return; if (rc < 0) { uart_st.error = ERR_FRAMING; uart_st.last_error = sys_time_us; return; } /* A complete frame received */ if (rc < (int)sizeof(struct ll_pkt)) { uart_st.error = ERR_PROTOCOL; uart_st.last_error = sys_time_us; return; } volatile struct ll_pkt *pkt = (volatile struct ll_pkt *)uart_st.rx_buf; if (pkt->dst != uart_st.addr && pkt->dst != SER_ADDR_BROADCAST) { /* we are not addressed */ return; } uart_st.last_packet = sys_time_us; uart_handle_user_packet((struct ll_pkt *)pkt, rc); } } void uart_schedule_dma(bool retransmit) { volatile struct dma_tx_buf *buf = &uart_st.tx_buf; ssize_t xfr_start, xfr_end, xfr_len; if (buf->wraparound) { buf->wraparound = false; xfr_start = 0; xfr_len = buf->xfr_end; xfr_end = buf->xfr_end; } else if (!retransmit) { if (buf->packet_end[buf->xfr_next] == -1) return; /* Nothing to transmit */ xfr_start = buf->xfr_end; xfr_end = buf->packet_end[buf->xfr_next]; buf->packet_end[buf->xfr_next] = -1; buf->xfr_next = (buf->xfr_next + 1) % COUNT_OF(buf->packet_end); if (xfr_end > xfr_start) { /* no wraparound */ xfr_len = xfr_end - xfr_start; } else { /* wraparound */ if (xfr_end != 0) buf->wraparound = true; xfr_len = sizeof(buf->data) - xfr_start; } } else { /* retransmit */ /* First, send a zero to delimit any garbage from the following good packet */ uart_st.usart->TDR = 0x00; xfr_start = buf->xfr_start; xfr_end = buf->xfr_end; if (xfr_end > xfr_start) { /* no wraparound */ xfr_len = xfr_end - xfr_start; } else { /* wraparound */ if (xfr_end != 0) buf->wraparound = true; xfr_len = sizeof(buf->data) - xfr_start; } uart_st.retransmissions++; } buf->xfr_start = xfr_start; buf->xfr_end = xfr_end; /* Wait for channel to turn off */ while (uart_st.tx_dma_ch->CCR & DMA_CCR_EN) { /* do nothing */ } /* initiate transmission of new buffer */ uart_st.tx_dma_ch->CMAR = (uint32_t)(buf->data + xfr_start); uart_st.tx_dma_ch->CNDTR = xfr_len; uart_st.tx_dma_ch->CCR |= DMA_CCR_EN; } int uart_putc_nonblocking(uint8_t c) { volatile struct dma_tx_buf *buf = &uart_st.tx_buf; if (buf->wr_pos == buf->xfr_start) return ERR_BUSY; buf->data[buf->wr_pos] = c; buf->wr_pos = (buf->wr_pos + 1) % sizeof(buf->data); return 0; } void uart_dma_interrupt(DMA_Channel_TypeDef *channel, uint32_t flags) { uart_st.tx_dma_ch->CCR &= ~DMA_CCR_EN; if (flags & DMA_ISR_TEIF1) { uart_st.error = ERR_DMA; uart_st.last_error = sys_time_us; } else if (flags & DMA_ISR_TCIF1 && channel == uart_st.tx_dma_ch) { uart_schedule_dma(false); } } /* len is the packet length including headers */ int uart_send_packet_nonblocking(struct ll_pkt *pkt, size_t pkt_len) { if (uart_st.tx_buf.packet_end[uart_st.tx_buf.wr_idx] != -1) { return ERR_TX_OVERRUN; } uart_st.last_transmission = sys_time_us; /* make the value this wonky-ass CRC implementation produces match zlib etc. */ CRC->CR = CRC_CR_REV_OUT | (1<DR = ((uint8_t *)pkt)[i]; pkt->crc32 = ~CRC->DR; int rc = cobs_encode_usart((int (*)(char))uart_putc_nonblocking, (char *)pkt, pkt_len); if (rc) return rc; uart_st.tx_buf.packet_end[uart_st.tx_buf.wr_idx] = uart_st.tx_buf.wr_pos; uart_st.tx_buf.wr_idx = (uart_st.tx_buf.wr_idx + 1) % COUNT_OF(uart_st.tx_buf.packet_end); uart_st.packet_count++; if (!(uart_st.tx_dma_ch->CCR & DMA_CCR_EN)) uart_schedule_dma(false); return 0; }