Beispiel #1
0
/** Write out data over the USART using DMA. Note that this function is not
 * reentrant and does not guard against DMA IRQs running at the same time which
 * will also cause spurious behaviours. Ensure that the calling function
 * appropriately prevents these things from happeing.
 *
 * \param data A pointer to the data to write out.
 * \param len  The number of bytes to write.
 *
 * \return The number of bytes that will be written, may be less than len.
 */
u32 usart_write_dma(u8 data[], u32 len) {
  /* Check if the write would cause a buffer overflow, if so only write up to
   * the end of the buffer. */
  u32 n_free = usart_tx_n_free();
  if (len > n_free)
    len = n_free;

  /* If there is no data to write, just return. */
  if (len == 0) return 0;

  u32 old_wr = wr;
  wr = (wr + len) % USART_TX_BUFFER_LEN;

  if (old_wr + len <= USART_TX_BUFFER_LEN) {
    memcpy(&buff[old_wr], data, len);
  } else {
    /* Deal with case where write wraps the buffer. */
    memcpy(&buff[old_wr], &data[0], USART_TX_BUFFER_LEN - old_wr);
    memcpy(&buff[0], &data[USART_TX_BUFFER_LEN-old_wr],
           len - (USART_TX_BUFFER_LEN - old_wr));
  }

  /* Check if there is a DMA transfer either in progress or waiting for its
   * interrupt to be serviced. Its very important to also check the interrupt
   * flag as EN will be cleared when the transfer finishes but we really need
   * to make sure the ISR has been run to finish up the bookkeeping for the
   * transfer. Also, make sure that this is done atomically without a DMA
   * interrupt squeezing in there. */
  if (!((DMA2_S7CR & DMA_SxCR_EN) || (DMA2_HISR & DMA_HISR_TCIF7)))
    dma_schedule();

  return len;
}
Beispiel #2
0
/** USART TX DMA interrupt service routine.
 * Should be called from the relevant DMA stream ISR.
 * \param s The USART DMA state structure.
 */
void usart_tx_dma_isr(usart_tx_dma_state* s)
{
    if (dma_get_interrupt_flag(s->dma, s->stream,
                               DMA_TEIF | DMA_DMEIF))
        /* TODO: Handle error interrupts! */
        screaming_death("DMA TX error interrupt");

    if (dma_get_interrupt_flag(s->dma, s->stream, DMA_TCIF)) {
        /* Interrupt is Transmit Complete. */

        /* Clear the DMA transmit complete and half complete interrupt flags. */
        dma_clear_interrupt_flags(s->dma, s->stream, DMA_HTIF | DMA_TCIF);

        /* Now that the transfer has finished we can increment the read index. */
        s->rd = (s->rd + s->xfer_len) % USART_TX_BUFFER_LEN;

        if (s->wr != s->rd)
            /* Buffer not empty. */
            dma_schedule(s);
    }

    if (dma_get_interrupt_flag(s->dma, s->stream, DMA_FEIF))
        /* Clear FIFO error flag */
        dma_clear_interrupt_flags(s->dma, s->stream, DMA_HTIF | DMA_FEIF);
}
Beispiel #3
0
static void usart_tx_dma_isr(struct usart_tx_dma_state* s, u32 flags)
{
  if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0)
    screaming_death("USART TX DMA error interrupt");

  osalSysLockFromISR();
  /* Now that the transfer has finished we can increment the read index. */
  s->rd = (s->rd + s->xfer_len) % USART_TX_BUFFER_LEN;

  if (s->wr != s->rd)
    /* Buffer not empty. */
    dma_schedule(s);
  else
    s->busy = false;
  osalSysUnlockFromISR();
}
Beispiel #4
0
/** USART1 TX DMA interrupt handler. */
void dma2_stream7_isr() {
  if (DMA2_HISR & DMA_HISR_TCIF7) {
    /* Interrupt is Transmit Complete. */

    /* Clear the DMA transmit half and complete interrupt flags. */
    DMA2_HIFCR = DMA_HIFCR_CTCIF7;
    DMA2_HIFCR = DMA_HIFCR_CHTIF7;

    /* Now that the transfer has finished we can increment the read index. */
    rd = (rd + xfer_len) % USART_TX_BUFFER_LEN;

    if (wr != rd)
      /* FIFO not empty. */
      dma_schedule();
  } else {
    /* TODO: Handle error interrupts! */
    speaking_death("DMA TX error interrupt");
  }
}
Beispiel #5
0
u32 usart_support_write(void *sd, const u8 data[], u32 len)
{
  struct usart_tx_dma_state *s = &((struct usart_support_s *)sd)->tx;

  /* If there is no data to write, just return. */
  if (len == 0) return 0;

  chSysLock();

  /* Check if the write would cause a buffer overflow, if so only write up to
   * the end of the buffer. */
  u32 n_free = usart_tx_n_free(sd);
  if (len > n_free) {
    chSysUnlock();
    return 0;
  }

  u32 old_wr = s->wr;
  s->wr = (s->wr + len) % USART_TX_BUFFER_LEN;

  if (old_wr + len <= USART_TX_BUFFER_LEN)
    memcpy(&(s->buff[old_wr]), data, len);
  else {
    /* Deal with case where write wraps the buffer. */
    memcpy(&(s->buff[old_wr]), &data[0], USART_TX_BUFFER_LEN - old_wr);
    memcpy(&(s->buff[0]), &data[USART_TX_BUFFER_LEN - old_wr],
           len - (USART_TX_BUFFER_LEN - old_wr));
  }

  /* Check if there is a DMA transfer either in progress or waiting for its
   * interrupt to be serviced. Its very important to also check the interrupt
   * flag as EN will be cleared when the transfer finishes but we really need
   * to make sure the ISR has been run to finish up the bookkeeping for the
   * transfer. Also, make sure that this is done atomically without a DMA
   * interrupt squeezing in there. */
  if (!s->busy)
    dma_schedule(s);

  chSysUnlock();
  return len;
}