Has anyone created a simple lib to use the DMA on UART (Serial) Output?
Thanks
Has anyone created a simple lib to use the DMA on UART (Serial) Output?
Thanks
Found this so far:
Ok, I'm having actual fun with this, as I read the PDF of the SAM IC
http://www.atmel.com/Images/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf
Under the DMA section you can find all details, just replace PERIPH with UART
void ConfigureUart( void )
{
PIOA->PIO_IDR = PIO_PA8A_URXD | PIO_PA9A_UTXD;
PIOA->PIO_PDR = PIO_PA8A_URXD | PIO_PA9A_UTXD;
PIOA->PIO_ABSR &= ~(PIO_PA8A_URXD | PIO_PA9A_UTXD);
PIOA->PIO_PUER = PIO_PA8A_URXD | PIO_PA9A_UTXD;
PMC->PMC_PCER0 = 1 << ID_UART;
UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS |UART_CR_TXDIS;
// Set the baudrate to 115200
UART->UART_BRGR = 45 ; // 84000000 / 16 * x = BaudRate (write x into UART_BRGR)
// No Parity
UART->UART_MR = UART_MR_PAR_NO;
//UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
UART->UART_IDR = 0xFFFFFFFF ;
NVIC_EnableIRQ((IRQn_Type)ID_UART);
UART->UART_IER = UART_IER_ENDTX; //<--makierte Zeile
UART->UART_CR |= UART_CR_TXEN;
}
void UartSend(uint8_t* value,uint16_t size)
{
UART->UART_TPR = (uint32_t)value;
UART->UART_TCR = (uint32_t)size;
UART->UART_PTCR |= UART_PTCR_TXTEN;
}
Did you have any success using rblilja's DmaSerial library?
I couldn't make it work commenting the lines it said to comment and everything, but I don't have much experience using 3rd party libraries.
I'm trying to use it as I had a code writing to DAC and reading from ADC at a reasonable speed but if I try to send the data (both the written and read) through serial using Serial.println() it slows down a lot the code. Is DMA the solution to this? And if so, how do you send and read data? I don't get why your UartSend function needs two arguments.
DMA for both ADC and DAC should considerably improve your code, moreover if a DMA is used for UART the CPU utilization should be pretty minimal.
Here is an example sketch to echo data between Serial Monitor and the DUE with a PDC DMA. Note that reception side is less obvious than the transmission side.
Stepwise:
First off, you will have to make some modifications in variant.h. in order to write your own code for the UART Handler. Go to File>Preferences and click in the url at the bottom of the parameters window (C:\Users...\AppData\Local\Arduino 15\preferences.txt), then follow this path:
packages/arduino/sam/1.6.x/variants/arduino_due_x/variant.h
Copy variant.h into a blank sketch (without setup() and loop()), alter the code around void UART_Handler(void) as shown in the below sketch, copy and save the new variant.h.
/*********************************************************************************************/
/* UART PDC DMA for Reception and Transmission */
/* Hook a jumper between UART input pin (PA8) and TIOB0 (PB27) to trigger the timer counter */
/*********************************************************************************************/
#define UART_RX_WAIT_MS (100) // For 100 milliseconds timestamp
#define TimeStamp (CHIP_FREQ_SLCK_RC * UART_RX_WAIT_MS / 1000)
#define BUF_SIZE 8
uint8_t BUF1[BUF_SIZE];
uint8_t BUF2[BUF_SIZE];
uint8_t* RX_BUF;
extern void (*uart_isr)(void); // variant file previously modified
/*
*********** in variant.h *****************
// IT handlers
void (*uart_isr)(void) = (0UL);
void UART_Handler(void)
{
if (uart_isr) uart_isr();
else Serial.IrqHandler();
}
*/
void setup() {
// Handy to initialize some UART registers
Serial.begin(250000);
tc_init();
RX_BUF = BUF1;
UART->UART_RPR = (uint32_t)RX_BUF;
UART->UART_RCR = BUF_SIZE;
UART->UART_PTCR = UART_PTCR_RXTEN | UART_PTCR_TXTEN;
uart_isr = UART_ISR;
UART->UART_IER = UART_IER_RXBUFF;
}
void loop() {
}
void UART_ISR(void) {
if (UART->UART_SR & UART_SR_RXBUFF) {
TransferRxBufAndRec(BUF_SIZE);
}
}
void tc_init() {
PMC->PMC_PCER0 = PMC_PCER0_PID27; // TC0 power ON
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKDIS ;
TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_WAVE | // Waveform
TC_CMR_TCCLKS_TIMER_CLOCK5 | // Slow clock selected : 32 KHz
TC_CMR_WAVSEL_UP_RC |
TC_CMR_CPCSTOP | // Stop on RC compare
TC_CMR_EEVT_TIOB | // TIOB input is the external event
TC_CMR_EEVTEDG_FALLING | // Trigger on falling edge of external event
TC_CMR_ENETRG ; // Enable trigger on external event
TC0->TC_CHANNEL[0].TC_RC = TimeStamp;
TC0->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN;
NVIC_EnableIRQ(TC0_IRQn);
}
void TC0_Handler(void) {
TC0->TC_CHANNEL[0].TC_SR; // Read and clear status register
UART->UART_PTCR = UART_PTCR_RXTDIS;
uint32_t rec_size = BUF_SIZE - (UART->UART_RCR);
if (rec_size) {
TransferRxBufAndRec(rec_size);
}
UART->UART_PTCR = UART_PTCR_RXTEN;
}
inline void TransferRxBufAndRec(int size){
while (!(UART->UART_SR & UART_SR_TXBUFE)); // Wait for PDC DMA Transmission ready
UART->UART_TPR = (uint32_t) RX_BUF;
UART->UART_TCR = size;
RX_BUF = (RX_BUF == BUF1) ? BUF2 : BUF1;
UART->UART_RPR = (uint32_t) RX_BUF;
UART->UART_RCR = BUF_SIZE;
}
Because the DMA feature may be very interesting in some cases, after UART with PDC DMA, I wanted to try USART with PDC DMA and USART with AHB DMA for reception and transmission. AHB DMA gives the opportunity to trigger a transfer from a peripheral to another peripheral (or memory and vice versa) unlike PDC DMA limited to transfers between memory and a peripheral and vice versa.
The 2 sketches below are only for demonstration purpose.
With a PDC DMA:
/****************************************************************************/
/* Echo USART1 with PDC DMA for reception and transmission */
/* Hook a jumper between RX1 and TX2 and another one between RX2 and TX1 */
/****************************************************************************/
#define USART_RX_WAIT_MS (10) // For 10 milliseconds timestamp
#define TimeStamp (CHIP_FREQ_SLCK_RC * UART_RX_WAIT_MS / 1000)
#define BUF_SIZE 8
uint8_t BUF1[BUF_SIZE];
uint8_t BUF2[BUF_SIZE];
uint8_t* RX_BUF;
extern void USART1_Handler(void);
/*
*********** in variant.h *****************
void USART1_Handler(void) __attribute__((weak));
void USART1_Handler(void)
{
Serial2.IrqHandler();
}
*/
char c = 0;
void setup() {
Serial.begin(250000);
Serial1.begin(250000);
// Handy to initialize some USART1 registers
Serial2.begin(250000); // USART1 init
int wait_bit_time = USART_RX_WAIT_MS * 250000 / 1000;
if (wait_bit_time > 0x1FFFF)
wait_bit_time = 0x1FFFF;
USART1->US_RTOR = US_RTOR_TO(wait_bit_time);
RX_BUF = BUF1;
USART1->US_RPR = (uint32_t)RX_BUF;
USART1->US_RCR = BUF_SIZE;
USART1->US_PTCR = US_PTCR_RXTEN | US_PTCR_TXTEN;
USART1->US_IER = US_IER_RXBUFF | US_IER_TIMEOUT;
Serial1.print("**** Hello World ****");
}
void loop() {
String s;
s = "";
while (Serial1.available() > 0) {
c = Serial1.read();
s += c;
}
if (s.length() > 0) {
Serial.println(s);
Serial1.print(s);
}
delay(1000);
}
void USART1_Handler(void)
{
USART1->US_CSR; // Read and clear status register
USART1->US_CR = US_CR_RTSDIS;
int rec_size = BUF_SIZE - USART1->US_RCR;
if (rec_size)
TransferRxBufAndRec(rec_size);
USART1->US_CR = US_CR_STTTO;
USART1->US_CR = US_CR_RTSEN;
}
inline void TransferRxBufAndRec(int size) {
while (!(USART1->US_CSR & US_CSR_TXBUFE)); // Wait for PDC DMA Transmission ready
USART1->US_TPR = (uint32_t) RX_BUF;
USART1->US_TCR = size;
RX_BUF = (RX_BUF == BUF1) ? BUF2 : BUF1;
USART1->US_RPR = (uint32_t) RX_BUF;
USART1->US_RCR = BUF_SIZE;
}
With an AHB DMA:
/****************************************************************************/
/* Echo USART1 with AHB DMA for reception and transmission */
/* Hook a jumper between RX1 and TX2 and another one between RX2 and TX1 */
/****************************************************************************/
#define DMAC_CH 0
extern void USART1_Handler(void);
/*
*********** in variant.h *****************
void USART1_Handler(void) __attribute__((weak));
void USART1_Handler(void)
{
Serial2.IrqHandler();
}
*/
char c = 0;
void setup() {
Serial.begin(250000);
Serial1.begin(250000);
// Handy to initialize some USART1 registers
Serial2.begin(250000); // USART1 init
PMC->PMC_PCER1 |= PMC_PCER1_PID39; // DMAC power ON
DMAC->DMAC_EN = DMAC_EN_ENABLE;
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_DSCR = 0;
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_SADDR = (uint32_t)&USART1->US_RHR;
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_DADDR = (uint32_t)&USART1->US_THR;
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_CTRLA = DMAC_CTRLA_BTSIZE(16) |
DMAC_CTRLA_SRC_WIDTH_BYTE |
DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_CTRLB = DMAC_CTRLB_FC_PER2PER_DMA_FC |
DMAC_CTRLB_SRC_INCR_FIXED |
DMAC_CTRLB_DST_INCR_FIXED;
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_CFG = DMAC_CFG_SRC_H2SEL_HW |
DMAC_CFG_DST_H2SEL_HW |
DMAC_CFG_SRC_PER(14) |
DMAC_CFG_DST_PER(13) |
DMAC_CFG_SOD_DISABLE |
DMAC_CFG_FIFOCFG_ASAP_CFG;
DMAC->DMAC_EBCIER = DMAC_EBCIER_CBTC0 << DMAC_CH;
NVIC_EnableIRQ(DMAC_IRQn);
NVIC_DisableIRQ(USART1_IRQn);
DMAC->DMAC_CHER = DMAC_CHER_ENA0 << DMAC_CH;
Serial1.print("**** Hello World ****");
}
void loop() {
String s;
s = "";
while (Serial1.available() > 0) {
c = Serial1.read();
s += c;
}
if (s.length() > 0) {
Serial.println(s);
Serial1.print(s);
}
delay(1000);
}
void DMAC_Handler(void)
{
DMAC->DMAC_EBCISR; // Read and clear status register
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_CTRLA &= ~(uint32_t)DMAC_CTRLA_BTSIZE_Msk;
DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_CTRLA |= DMAC_CTRLA_BTSIZE(16);
DMAC->DMAC_CHER = DMAC_CHER_ENA0 << DMAC_CH;
}