I'm thinking about how to hook a UART up to the DMAC. Right now the Serial implementation in the core is blocking. That's right, blocking.
/* -------------------------------------------------------------------------- */
size_t UART::write(uint8_t c) {
/* -------------------------------------------------------------------------- */
if(init_ok) {
tx_done = false;
R_SCI_UART_Write(&uart_ctrl, &c, 1);
while (!tx_done) {} /// <<<<<<----- EMPTY WHILE LOOP!
return 1;
}
else {
return 0;
}
}
/* -------------------------------------------------------------------------- */
size_t UART::write_raw(uint8_t* c, size_t len) {
/* -------------------------------------------------------------------------- */
size_t i = 0;
while (i < len) {
uart_ctrl.p_reg->TDR = *(c+i);
while (uart_ctrl.p_reg->SSR_b.TEND == 0) {} ///<<<<< BLOCKS FOR THE WHOLE TRANSMISSION
i++;
}
return len;
}
There's an interrupt running:
/* -------------------------------------------------------------------------- */
void UART::WrapperCallback(uart_callback_args_t *p_args) {
/* -------------------------------------------------------------------------- */
uint32_t channel = p_args->channel;
UART *uart_ptr = UART::g_uarts[channel];
if(uart_ptr == nullptr) {
return;
}
switch (p_args->event){
case UART_EVENT_ERR_PARITY:
case UART_EVENT_ERR_FRAMING:
case UART_EVENT_ERR_OVERFLOW:
case UART_EVENT_RX_COMPLETE: // This is called when all the "expected" data are received
{
break;
}
case UART_EVENT_TX_COMPLETE:
case UART_EVENT_TX_DATA_EMPTY:
{
//uint8_t to_enqueue = uart_ptr->txBuffer.available() < uart_ptr->uart_ctrl.fifo_depth ? uart_ptr->txBuffer.available() : uart_ptr->uart_ctrl.fifo_depth;
//while (to_enqueue) {
uart_ptr->tx_done = true;
break;
}
case UART_EVENT_RX_CHAR:
{
if (uart_ptr->rxBuffer.availableForStore()) {
uart_ptr->rxBuffer.store_char(p_args->data);
}
break;
}
case UART_EVENT_BREAK_DETECT:
{
break;
}
}
}
but all it does is set the tx_done
value to true. And write waits on that.
Really???
I see comments there where someone thought about using a FIFO. Serial already has a ring buffer that could just as easily be used. That's how it's done in the AVR world.
But we've also got a DMA controller. And that doesn't even require interrupts. The block mode is perfect for a ring buffer. Whenever you write to the buffer, you also set the counter register in the DMAC to send out that many bytes. When it gets to the end of the ring buffer in block mode it will automatically go back to the start.
It can be triggered off of the uart finishing the transmission. I'll have to experiment with how to start the initial transfer. But that's just code.
I know how to use the DMAC directly. I submitted a library for it. But the FSP... Should I use it? Should I even bother to figure it out? I think I want to just because it's something I don't fully understand how to use.
It will take two DMA channels out of the 4 we have available. That's a pretty high resource cost, but there's also the DTC. Since it handles more complicated chain transfers and such, I think I'll use the DMA channels.
I think I'll make copies of Serial.h and Serial.cpp and make a branch in my development core where Serial1 is turned off. Then I'll try to pick up those pins with new code. It shouldn't be that hard, but those are famous last words I know.
Any thoughts on getting started?