Коллизии нет никакой - надо просто правильно сделать (ошибку заложить конечно можно). когда вызван SendBuffer(), когда DMA еще не окончил предыдущее задание.
Если ДМА ещё работает, то естественно его не трогаем. Только изменяем TxNext (обязательно в критической секции).
У нас получается 2 случая:
1) Дма не активно - копируем данные, запускаем транзакцию и разрешаем прерывания ДМА
2) Дма работает - просто копируем данные и переносим TxNext.
В прерывании ДМА запускаем новую транзакцию или запрещаем прерывания ДМА (в зависимости от того пуст буфер или нет).
Типа как с прерыванием USARTxx_DRE:
void ExtUartDriver_t::Write(uint8_t *data, uint16_t length)
{
uint16_t tx_next=TxBuffer.Next;
for (uint16_t i=0; i<length; i++) {
TxBuffer.Buffer[tx_next]=*data++;
if (++tx_next>=sizeof(TxBuffer.Buffer))
tx_next=0;
}
CRITICAL_SECTION();
TxBuffer.Next=tx_next;
if ((EXT_UART.CTRLA&USART_DREINTLVL_gm)==0)
EXT_UART.CTRLA|=USART_DREINTLVL_LO_gc;
}
ISR(USARTF0_DRE_vect)
{
if (TxBuffer.NotEmpty())
EXT_UART.DATA=TxBuffer.ReadByte();
else
EXT_UART.CTRLA&=~USART_DREINTLVL_gm;
}