Передача DMA повреждена другим каналом (ATSAMC21) - PullRequest
0 голосов
/ 09 апреля 2020

Я хочу использовать передачу DAM для расчета CR C с MCU ATSAMC21G17A (E-revision).

Я уже реализовал 2 независимых канала DMA, управляющих передачей UART, они работают хорошо, у меня нет проблемы с ними.

Теперь пришло время получить CR C, вычисляющий работу для вычисления CR C памяти Fla sh. Я просто использую один таймер для генерации событий запуска для DMA, я знаю размер моей прошивки и просто хочу, чтобы она работала. Таймер работает с периодом 100use c.

Но на самом деле он не работает, я получаю ошибки с другими каналами после запуска канала DMA с вычислением CR C, процесс передачи другого канала может быть прерван и не работает потом. Это означает, что он может передавать только некоторые байты, но не все. Что я делаю не так?

Я уже прочитал все опечатки о моем MCU и не нашел ничего подобного. Я не использую дескрипторы ссылок.

Вот мой исходный код:

Инициализация DMA:

volatile bool DMA_TransferComplete[DMA_CHANNELS_NUM];

static DmacDescriptor dma_descr[DMA_CHANNELS_NUM]  __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;
static DmacDescriptor writeback[DMA_CHANNELS_NUM] __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;
static DmacDescriptor descriptor  __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;

/** \brief Initialize DMA module
 *
 * \return Nothing
 *
 */
void DMA_Init(void)
{
  uint8_t i;

  MCLK->AHBMASK.reg |= MCLK_AHBMASK_DMAC;

  DMAC->CTRL.reg &= ~DMAC_CTRL_DMAENABLE;
  DMAC->CTRL.reg = DMAC_CTRL_SWRST;

  DMAC->BASEADDR.reg = (uint32_t)dma_descr;
  DMAC->WRBADDR.reg = (uint32_t)writeback;

  DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);

  for (i = 0; i < DMA_CHANNELS_NUM; i++)
    DMA_TransferComplete[i] = true;

 NVIC_EnableIRQ(DMAC_IRQn);
 NVIC_SetPriority(DMAC_IRQn, 1);
}

Настройка для канала DMA:

/** \brief Setup DMA transfer for selected channel
 *
 * \param [in] channel Number of the channel to act
 * \param [in] settings Pointer to TDmaSettings structure with DMA settings
 * \return Nothing
 *
 */
void DMA_SetupChannel(uint8_t channel, TDmaSettings *settings)
{
  uint16_t btctrlVal;

  if (channel >= DMA_CHANNELS_NUM)
    return;

  /**< Setup channel */
  DMAC->CHID.reg = DMAC_CHID_ID(channel);
  DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
  DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
  DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << channel));

  if (settings->trig_src == 0)
    DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(settings->priority) | DMAC_CHCTRLB_TRIGACT_BLOCK;
  else
    DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(settings->priority) | DMAC_CHCTRLB_TRIGSRC(settings->trig_src) | DMAC_CHCTRLB_TRIGACT_BEAT;
  /**< Enable interrupts */
  DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK;
  /**< Prepare descriptor block */
  descriptor.DESCADDR.reg = 0;          // only one block to transfer, so 0
  descriptor.BTCNT.reg = settings->len;
  btctrlVal = DMAC_BTCTRL_VALID | DMAC_BTCTRL_BEATSIZE(settings->beat_size);
  descriptor.DSTADDR.reg = (uint32_t)settings->dst_addr;
  if (settings->dst_inc == true)
  {
    descriptor.DSTADDR.reg += settings->len;
    btctrlVal |= DMAC_BTCTRL_DSTINC;
  }
  descriptor.SRCADDR.reg = (uint32_t)settings->src_addr;
  if (settings->src_inc == true)
  {
    descriptor.SRCADDR.reg += settings->len;
    btctrlVal |= DMAC_BTCTRL_SRCINC;
  }
  descriptor.BTCTRL.reg = btctrlVal;

  memcpy(&dma_descr[channel], &descriptor, sizeof(DmacDescriptor));
}

Функция прерывания для обнаружения события «передача завершена»:

/**< Interrupt for DMA transfer completion */
void DMAC_Handler(void)
{
  uint8_t channel;
  uint8_t old_channel;
  uint32_t res;

  /**< Get old channel number */
  old_channel = DMAC->CHID.reg;
  res = DMAC->INTSTATUS.reg;
  /**< Processing all affected channels */
  for (channel = 0; channel < DMA_CHANNELS_NUM; channel++)
  {
    if (res & (1 << channel))
    {
      /**< Set transfer complete flag */
      DMA_TransferComplete[channel] = true;
      /**< Select channel to work with */
      DMAC->CHID.reg = DMAC_CHID_ID(channel);
      /**< Clear all flags for DMA channel */
      DMAC->CHINTFLAG.reg = (DMAC_CHINTENCLR_TCMPL | DMAC_CHINTENCLR_TERR | DMAC_CHINTENCLR_SUSP);
      /**< Disable DMA transfer */
      DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
    }
  }
  DMAC->CHID.reg = old_channel;
}

Запуск передачи DMA:

/** \brief Start DMA transfer, channel should be already initialized
 *
 * \param [in] channel Number of the channel to act
 * \return Nothing
 *
 */
void DMA_StartChannel(uint8_t channel)
{
  if (channel >= DMA_CHANNELS_NUM)
    return;

  /**< Setup channel */
  DMAC->CHID.reg = DMAC_CHID_ID(channel);
  if (DMA_TransferComplete[channel] == false)
    DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;

  /**< Start DMA transfer */
  DMA_TransferComplete[channel] = false;
  DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
}

Настройка CR C Расчет:

/** \brief Initialize CRC module
 *
 * \param
 * \param
 * \return Nothing
 *
 */
void DMA_InitCRC(void)
{
  /**< Power on timer */
  MCLK->APBCMASK.reg |= DMA_CRC_TIMER_MCLK;
  GCLK->PCHCTRL[DMA_CRC_TIMER_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
  /**< Setup timer for 100usec events */
  DMA_CRC_TIMER->COUNT8.CTRLA.reg = TC_CTRLA_SWRST;
  while (DMA_CRC_TIMER->COUNT8.SYNCBUSY.reg & TC_SYNCBUSY_SWRST);
  DMA_CRC_TIMER->COUNT8.CTRLA.reg |= (DMA_CRC_TIMER_PRESCALER << TC_CTRLA_PRESCALER_Pos) | TC_CTRLA_MODE_COUNT8;
  DMA_CRC_TIMER->COUNT8.CTRLBSET.bit.DIR = 0;
  DMA_CRC_TIMER->COUNT8.PER.reg = DMA_CRC_TIMER_TICK;
  DMA_CRC_TIMER->COUNT8.COUNT.reg = 0;
  DMA_CRC_TIMER->COUNT8.CTRLA.reg |= (TC_CTRLA_ENABLE);

  DMAC->CTRL.reg &= ~DMAC_CTRL_CRCENABLE;
  /**< Configure the CRC engine */
  DMAC_CRCCTRL_Type crcctrl =
  {
    .bit.CRCSRC = DMAC_CRCCTRL_CRCSRC_IO_Val, /* I/O interface */
    .bit.CRCPOLY = DMAC_CRCCTRL_CRCPOLY_CRC16_Val, /* CRC-16 (CRC-CCITT)  */
    .bit.CRCBEATSIZE = DMAC_CRCCTRL_CRCBEATSIZE_BYTE_Val, /* Byte bus access */
  };
  DMAC->CRCCTRL.reg = crcctrl.reg;
}

Запустить новый расчет CR C:

/** \brief Start CRC conversion
 *
 * \param [in] crc_init CRC initial value
 * \return Nothing
 *
 */
void DMA_StartCRC(uint32_t crc_init)
{
  /**< Clear the busy bit */
  DMAC->CRCSTATUS.bit.CRCBUSY = 1;
  DMAC->CTRL.bit.CRCENABLE = 0;
  /**< Initialize start CRC value for the CRC16 */
  DMAC->CRCCHKSUM.reg = crc_init;
  /**< Enable the CRC engine */
  DMAC->CTRL.bit.CRCENABLE = 1;
}

Получить результат расчета CR C:

/** \brief Get CRC result from DMA module
 *
 * \return CRC as uint16_t
 *
 */
uint16_t DMA_GetCRC(void)
{
  return (uint16_t)DMAC->CRCCHKSUM.reg;
}

А теперь исходный код потока с вычислением CR C (Я использую FreeRTOS):

  /**< CRC initialization */
  DMA_InitCRC();

  /**< Setup DMA channel for transmission */
  DmaSett.beat_size = DMAC_BTCTRL_BEATSIZE_BYTE_Val;
  DmaSett.trig_src = TC1_DMAC_ID_OVF;
  DmaSett.dst_addr = (void*)&DMAC->CRCDATAIN.reg;
  DmaSett.src_addr = APP_ADDRESS;
  DmaSett.src_inc = true;
  DmaSett.dst_inc = false;
  DmaSett.len = addr;
  DmaSett.priority = 0;
  DMA_SetupChannel(DMA_CHANNEL_CRC, &DmaSett);

  DMA_StartCRC(CRC16_INIT_VAL);

  DMA_StartChannel(DMA_CHANNEL_CRC);

  while (1)
  {
    // some periodical actions
    // ...

    if (DMA_IsReady(DMA_CHANNEL_CRC) == true)
    {
      crc = DMA_GetCRC();
      DMA_StartCRC(CRC16_INIT_VAL);
      DMA_StartChannel(DMA_CHANNEL_CRC);
      // do something with CRC here
      // ...
    }
  }

Кто-нибудь имеет опыт работы с модулем DMA ATSAMC21? На самом деле он работает нормально с UART, но я не могу решить эту проблему с CR C взаимодействием уже несколько дней.

Буду благодарен за любые идеи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...