PX4IO_serial::~PX4IO_serial() { if (_tx_dma != nullptr) { stm32_dmastop(_tx_dma); stm32_dmafree(_tx_dma); } if (_rx_dma != nullptr) { stm32_dmastop(_rx_dma); stm32_dmafree(_rx_dma); } /* reset the UART */ rCR1 = 0; rCR2 = 0; rCR3 = 0; /* detach our interrupt handler */ up_disable_irq(PX4IO_SERIAL_VECTOR); irq_detach(PX4IO_SERIAL_VECTOR); /* restore the GPIOs */ stm32_unconfiggpio(PX4IO_SERIAL_TX_GPIO); stm32_unconfiggpio(PX4IO_SERIAL_RX_GPIO); /* and kill our semaphores */ sem_destroy(&_completion_semaphore); sem_destroy(&_bus_semaphore); perf_free(_pc_txns); perf_free(_pc_dmasetup); perf_free(_pc_retries); perf_free(_pc_timeouts); perf_free(_pc_crcerrs); perf_free(_pc_dmaerrs); perf_free(_pc_protoerrs); perf_free(_pc_uerrs); perf_free(_pc_idle); perf_free(_pc_badidle); if (g_interface == this) g_interface = nullptr; }
static void i2c_rx_complete(void) { rx_len = sizeof(rx_buf) - stm32_dmaresidual(rx_dma); stm32_dmastop(rx_dma); if (rx_len >= 2) { selected_page = rx_buf[0]; selected_offset = rx_buf[1]; /* work out how many registers are being written */ unsigned count = (rx_len - 2) / 2; if (count > 0) { registers_set(selected_page, selected_offset, (const uint16_t *)&rx_buf[2], count); } else { /* no registers written, must be an address cycle */ uint16_t *regs; unsigned reg_count; /* work out which registers are being addressed */ int ret = registers_get(selected_page, selected_offset, ®s, ®_count); if (ret == 0) { tx_buf = (uint8_t *)regs; tx_len = reg_count * 2; } else { tx_buf = junk_buf; tx_len = sizeof(junk_buf); } /* disable interrupts while reconfiguring DMA for the selected registers */ irqstate_t flags = irqsave(); stm32_dmastop(tx_dma); i2c_tx_setup(); irqrestore(flags); } } /* prepare for the next transaction */ i2c_rx_setup(); }
static void dma_reset(void) { /* kill any pending DMA */ stm32_dmastop(tx_dma); stm32_dmastop(rx_dma); /* reset the RX side */ stm32_dmasetup( rx_dma, (uint32_t)&rDR, (uint32_t)&dma_packet, sizeof(dma_packet), DMA_CCR_MINC | DMA_CCR_PSIZE_8BITS | DMA_CCR_MSIZE_8BITS | DMA_CCR_PRIMED); /* start receive DMA ready for the next packet */ stm32_dmastart(rx_dma, rx_dma_callback, NULL, false); }
static void i2c_tx_complete(void) { tx_count = tx_len - stm32_dmaresidual(tx_dma); stm32_dmastop(tx_dma); /* for debug purposes, save the length of the last transmit as seen by the DMA */ /* leave tx_buf/tx_len alone, so that a retry will see the same data */ /* prepare for the next transaction */ i2c_tx_setup(); }
int PX4IO_serial::ioctl(unsigned operation, unsigned &arg) { switch (operation) { case 1: /* XXX magic number - test operation */ switch (arg) { case 0: lowsyslog("test 0\n"); /* kill DMA, this is a PIO test */ stm32_dmastop(_tx_dma); stm32_dmastop(_rx_dma); rCR3 &= ~(USART_CR3_DMAR | USART_CR3_DMAT); for (;;) { while (!(rSR & USART_SR_TXE)) ; rDR = 0x55; } return 0; case 1: { unsigned fails = 0; for (unsigned count = 0;; count++) { uint16_t value = count & 0xffff; if (write((PX4IO_PAGE_TEST << 8) | PX4IO_P_TEST_LED, &value, 1) != 0) { fails++; } if (count >= 5000) { lowsyslog("==== test 1 : %u failures ====\n", fails); perf_print_counter(_pc_txns); perf_print_counter(_pc_dmasetup); perf_print_counter(_pc_retries); perf_print_counter(_pc_timeouts); perf_print_counter(_pc_crcerrs); perf_print_counter(_pc_dmaerrs); perf_print_counter(_pc_protoerrs); perf_print_counter(_pc_uerrs); perf_print_counter(_pc_idle); perf_print_counter(_pc_badidle); count = 0; } } return 0; } case 2: lowsyslog("test 2\n"); return 0; } default: break; } return -1; }
static int serial_interrupt(int irq, void *context, FAR void *arg) { static bool abort_on_idle = false; uint32_t sr = rSR; /* get UART status register */ (void)rDR; /* required to clear any of the interrupt status that brought us here */ if (sr & (USART_SR_ORE | /* overrun error - packet was too big for DMA or DMA was too slow */ USART_SR_NE | /* noise error - we have lost a byte due to noise */ USART_SR_FE)) { /* framing error - start/stop bit lost or line break */ perf_count(pc_errors); if (sr & USART_SR_ORE) { perf_count(pc_ore); } if (sr & USART_SR_NE) { perf_count(pc_ne); } if (sr & USART_SR_FE) { perf_count(pc_fe); } /* send a line break - this will abort transmission/reception on the other end */ rCR1 |= USART_CR1_SBK; /* when the line goes idle, abort rather than look at the packet */ abort_on_idle = true; } if (sr & USART_SR_IDLE) { /* * If we saw an error, don't bother looking at the packet - it should have * been aborted by the sender and will definitely be bad. Get the DMA reconfigured * ready for their retry. */ if (abort_on_idle) { abort_on_idle = false; dma_reset(); return 0; } /* * The sender has stopped sending - this is probably the end of a packet. * Check the received length against the length in the header to see if * we have something that looks like a packet. */ unsigned length = sizeof(dma_packet) - stm32_dmaresidual(rx_dma); if ((length < 1) || (length < PKT_SIZE(dma_packet))) { /* it was too short - possibly truncated */ perf_count(pc_badidle); dma_reset(); return 0; } /* * Looks like we received a packet. Stop the DMA and go process the * packet. */ perf_count(pc_idle); stm32_dmastop(rx_dma); rx_dma_callback(rx_dma, DMA_STATUS_TCIF, NULL); } return 0; }
PX4IO_serial_f4::PX4IO_serial_f4() : _tx_dma(nullptr), _rx_dma(nullptr), _current_packet(nullptr), _rx_dma_status(_dma_status_inactive), _completion_semaphore(SEM_INITIALIZER(0)), #if 0 _pc_dmasetup(perf_alloc(PC_ELAPSED, "io_dmasetup ")), _pc_dmaerrs(perf_alloc(PC_COUNT, "io_dmaerrs ")) #else _pc_dmasetup(nullptr), _pc_dmaerrs(nullptr) #endif { } PX4IO_serial_f4::~PX4IO_serial_f4() { if (_tx_dma != nullptr) { stm32_dmastop(_tx_dma); stm32_dmafree(_tx_dma); } if (_rx_dma != nullptr) { stm32_dmastop(_rx_dma); stm32_dmafree(_rx_dma); } /* reset the UART */ rCR1 = 0; rCR2 = 0; rCR3 = 0; /* detach our interrupt handler */ up_disable_irq(PX4IO_SERIAL_VECTOR); irq_detach(PX4IO_SERIAL_VECTOR); /* restore the GPIOs */ px4_arch_unconfiggpio(PX4IO_SERIAL_TX_GPIO); px4_arch_unconfiggpio(PX4IO_SERIAL_RX_GPIO); /* Disable APB clock for the USART peripheral */ modifyreg32(PX4IO_SERIAL_RCC_REG, PX4IO_SERIAL_RCC_EN, 0); /* and kill our semaphores */ px4_sem_destroy(&_completion_semaphore); perf_free(_pc_dmasetup); perf_free(_pc_dmaerrs); } int PX4IO_serial_f4::init() { /* initialize base implementation */ int r; if ((r = PX4IO_serial::init(&_io_buffer_storage)) != 0) { return r; } /* allocate DMA */ _tx_dma = stm32_dmachannel(PX4IO_SERIAL_TX_DMAMAP); _rx_dma = stm32_dmachannel(PX4IO_SERIAL_RX_DMAMAP); if ((_tx_dma == nullptr) || (_rx_dma == nullptr)) { return -1; } /* Enable the APB clock for the USART peripheral */ modifyreg32(PX4IO_SERIAL_RCC_REG, 0, PX4IO_SERIAL_RCC_EN); /* configure pins for serial use */ px4_arch_configgpio(PX4IO_SERIAL_TX_GPIO); px4_arch_configgpio(PX4IO_SERIAL_RX_GPIO); /* reset & configure the UART */ rCR1 = 0; rCR2 = 0; rCR3 = 0; /* eat any existing interrupt status */ (void)rSR; (void)rDR; /* configure line speed */ uint32_t usartdiv32 = PX4IO_SERIAL_CLOCK / (PX4IO_SERIAL_BITRATE / 2); uint32_t mantissa = usartdiv32 >> 5; uint32_t fraction = (usartdiv32 - (mantissa << 5) + 1) >> 1; rBRR = (mantissa << USART_BRR_MANT_SHIFT) | (fraction << USART_BRR_FRAC_SHIFT); /* attach serial interrupt handler */ irq_attach(PX4IO_SERIAL_VECTOR, _interrupt, this); up_enable_irq(PX4IO_SERIAL_VECTOR); /* enable UART in DMA mode, enable error and line idle interrupts */ rCR3 = USART_CR3_EIE; rCR1 = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE | USART_CR1_IDLEIE; /* create semaphores */ px4_sem_init(&_completion_semaphore, 0, 0); /* _completion_semaphore use case is a signal */ px4_sem_setprotocol(&_completion_semaphore, SEM_PRIO_NONE); /* XXX this could try talking to IO */ return 0; }
PX4IO_serial_f7::PX4IO_serial_f7() : _tx_dma(nullptr), _rx_dma(nullptr), _current_packet(nullptr), _rx_dma_status(_dma_status_inactive), _completion_semaphore(SEM_INITIALIZER(0)), #if 0 _pc_dmasetup(perf_alloc(PC_ELAPSED, "io_dmasetup ")), _pc_dmaerrs(perf_alloc(PC_COUNT, "io_dmaerrs ")) #else _pc_dmasetup(nullptr), _pc_dmaerrs(nullptr) #endif { } PX4IO_serial_f7::~PX4IO_serial_f7() { if (_tx_dma != nullptr) { stm32_dmastop(_tx_dma); stm32_dmafree(_tx_dma); } if (_rx_dma != nullptr) { stm32_dmastop(_rx_dma); stm32_dmafree(_rx_dma); } /* reset the UART */ rCR1 = 0; rCR2 = 0; rCR3 = 0; /* detach our interrupt handler */ up_disable_irq(PX4IO_SERIAL_VECTOR); irq_detach(PX4IO_SERIAL_VECTOR); /* restore the GPIOs */ px4_arch_unconfiggpio(PX4IO_SERIAL_TX_GPIO); px4_arch_unconfiggpio(PX4IO_SERIAL_RX_GPIO); /* Disable APB clock for the USART peripheral */ modifyreg32(PX4IO_SERIAL_RCC_REG, PX4IO_SERIAL_RCC_EN, 0); /* and kill our semaphores */ px4_sem_destroy(&_completion_semaphore); perf_free(_pc_dmasetup); perf_free(_pc_dmaerrs); } int PX4IO_serial_f7::init() { /* initialize base implementation */ int r; if ((r = PX4IO_serial::init((IOPacket *)ROUND_UP_TO_POW2_CT((uintptr_t)_io_buffer_storage, CACHE_LINE_SIZE))) != 0) { return r; } /* allocate DMA */ _tx_dma = stm32_dmachannel(PX4IO_SERIAL_TX_DMAMAP); _rx_dma = stm32_dmachannel(PX4IO_SERIAL_RX_DMAMAP); if ((_tx_dma == nullptr) || (_rx_dma == nullptr)) { return -1; } /* Enable the APB clock for the USART peripheral */ modifyreg32(PX4IO_SERIAL_RCC_REG, 0, PX4IO_SERIAL_RCC_EN); /* configure pins for serial use */ px4_arch_configgpio(PX4IO_SERIAL_TX_GPIO); px4_arch_configgpio(PX4IO_SERIAL_RX_GPIO); /* reset & configure the UART */ rCR1 = 0; rCR2 = 0; rCR3 = 0; /* clear data that may be in the RDR and clear overrun error: */ if (rISR & USART_ISR_RXNE) { (void)rRDR; } rICR = rISR & rISR_ERR_FLAGS_MASK; /* clear the flags */ /* configure line speed */ uint32_t usartdiv32 = (PX4IO_SERIAL_CLOCK + (PX4IO_SERIAL_BITRATE) / 2) / (PX4IO_SERIAL_BITRATE); rBRR = usartdiv32; /* attach serial interrupt handler */ irq_attach(PX4IO_SERIAL_VECTOR, _interrupt, this); up_enable_irq(PX4IO_SERIAL_VECTOR); /* enable UART in DMA mode, enable error and line idle interrupts */ rCR3 = USART_CR3_EIE; /* TODO: maybe use DDRE */ rCR1 = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE | USART_CR1_IDLEIE; /* TODO: maybe we need to adhere to the procedure as described in the reference manual page 1251 (34.5.2) */ /* create semaphores */ px4_sem_init(&_completion_semaphore, 0, 0); /* _completion_semaphore use case is a signal */ px4_sem_setprotocol(&_completion_semaphore, SEM_PRIO_NONE); /* XXX this could try talking to IO */ return 0; }
void PX4IO_serial_f7::_do_interrupt() { uint32_t sr = rISR; /* get UART status register */ if (sr & USART_ISR_RXNE) { (void)rRDR; /* read DR to clear RXNE */ } rICR = sr & rISR_ERR_FLAGS_MASK; /* clear flags */ if (sr & (USART_ISR_ORE | /* overrun error - packet was too big for DMA or DMA was too slow */ USART_ISR_NF | /* noise error - we have lost a byte due to noise */ USART_ISR_FE)) { /* framing error - start/stop bit lost or line break */ /* * If we are in the process of listening for something, these are all fatal; * abort the DMA with an error. */ if (_rx_dma_status == _dma_status_waiting) { _abort_dma(); perf_count(_pc_uerrs); /* complete DMA as though in error */ _do_rx_dma_callback(DMA_STATUS_TEIF); return; } /* XXX we might want to use FE / line break as an out-of-band handshake ... handle it here */ /* don't attempt to handle IDLE if it's set - things went bad */ return; } if (sr & USART_ISR_IDLE) { /* if there is DMA reception going on, this is a short packet */ if (_rx_dma_status == _dma_status_waiting) { /* Invalidate _current_packet, so we get fresh data from RAM */ arch_invalidate_dcache((uintptr_t)_current_packet, (uintptr_t)_current_packet + ALIGNED_IO_BUFFER_SIZE); /* verify that the received packet is complete */ size_t length = sizeof(*_current_packet) - stm32_dmaresidual(_rx_dma); if ((length < 1) || (length < PKT_SIZE(*_current_packet))) { perf_count(_pc_badidle); /* stop the receive DMA */ stm32_dmastop(_rx_dma); /* complete the short reception */ _do_rx_dma_callback(DMA_STATUS_TEIF); return; } perf_count(_pc_idle); /* stop the receive DMA */ stm32_dmastop(_rx_dma); /* complete the short reception */ _do_rx_dma_callback(DMA_STATUS_TCIF); } } }