Я хочу использовать передачу 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 взаимодействием уже несколько дней.
Буду благодарен за любые идеи!