RAM -via DMA to USART

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi there could anyone help me with simple DMA issue.

I'm learning the XmegaA1 on the Xplain PCB.
What i want to achieve is:
Send a table of 6 chars array to USARTC0 with DMA.

I initiated UART, DMA but the issue is that i would like to send whole table with DMA usage.

Right now when i call the TRFREQ from DMA.CHA2.CTRLA as a result i get only two signs and if i continue to manually set TRFREQ i get the rest also. But the issue was to send whole array with one single call of TRFREQ.

Here the code. (i skip the USART initiation).

unsigned char guct_Table[10]= {'G', 'R', 'E', 'G', 'O', 'R'};

//----------------------------DMA INIT
DMA.CTRL |=0x80; //Enable DMA with default settings priority 
DMA.CH2.REPCNT=0x06;  //Number of repeats, just to be sure it's 6
DMA.CH2.CTRLA |=0x20; //Repeat mode  
DMA.CH2.ADDRCTRL=0xD0; //Destination address must stay the same, Source address incremental after each byte 
DMA.CH2.TRIGSRC=0x00; //0x00 Manual trigger by software
DMA.CH2.TRFCNT=0x000A; //the block size is whole array


DMA.CH2.SRCADDR0=(( (uint16_t) &guct_Table) >> 0*8 ) & 0xFF; //Point begining of the source Array
DMA.CH2.SRCADDR1=(( (uint16_t) &guct_Table) >> 1*8 ) & 0xFF; //Point begining of the source Array
DMA.CH2.SRCADDR2=0; //Point begining of the source Array

DMA.CH2.DESTADDR0=0xA0; //Point destination register
DMA.CH2.DESTADDR1=0x08; //Point destination register 
DMA.CH2.DESTADDR2=0; //Point destination register

DMA.CH2.CTRLA |=0x80;     //enable DMA.CH2
DMA.CH2.CTRLA |=0x10;     //TRFREQ to transfer Bytes from the Array


while(1)
{
//do some stuff
}


Unfortunately the result which i get from the ComPort in my terminal is only
GR
the rest is missing unless i repeat calling the

DMA.CH2.CTRLA |=0x10;     //TRFREQ to transfer Bytes

What i do wrong? It's not a single shoot so don't get it why only "GR"

Thanks for help
Gregor

Gregor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I could be way off beam here but in another thread I think someone found that DMA only worked if you also provided/enabled an interrupt. If that's really the case (and this is a bit of a hazy memory I admit) then it kind of makes the whole idea of DMA a bit pointless.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found this description int the A1 manual

Quote:
When the block is transferred, the channel will wait for the next trigger to arrive
before it start transferring the next block. It is possible to select the trigger to start a burst transfer
instead of a block transfer. This is called a single shot transfer. A new trigger will then start a new
burst transfer. When repeat mode is enabled, the start of transfer of the next block does not
require a transfer trigger. It will start as soon as the previous block is done.

According to this i should get the whole block 6x1Byte transmitted after one trigger.

But maybe i did some mistake in the code.

Gregor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought about one thing which i might be doing wrong.

Should i bother to check after each DMA transfered Byte if the USART is able to get the data. Because when i step through the Code i see that DMA takes data from each Memory cell. Maybe the USART just doesn't manage and the bytes are rejected ?

Thanks
Sorry if it's amature question but i'm just learning these things.

Gregor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm just guessing here, but I think the "GR" you are seeing is the first "G" and the last "R" in "GREGOR".

I've only tried to get DMA working once and I failed... But, in looking over the "XMEGA A" manual, it looks to me as if you need to set the both the burst length and block length to one and set the trigger source to 0x4C (USARTC0 + DRE). You then initiate a transfer by enabling the DRE interrupt on USARTC0. At the end of a transfer, you could use either the USARTC0 TXC interrupt, or configure the DMA channel to generate an interrupt, to disable the USARTC0 DRE interrupt and reset the source pointer.

You could mask the unnecessary interrupt Cliff is referring to by setting TXC (or DMA) interrupt level to medium or high, while setting the DRE interrupt level to low, then telling to PMIC to honor only medium and high level interrupts.

That's my $0.02 worth.

Gamu The Killer Narwhal
Portland, OR, US
_________________
Atmel Studio 6.2
Windows 8.1 Pro
Xplained boards mostly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Clawson wrote:
I could be way off beam here but in another thread I think someone found that DMA only worked if you also provided/enabled an interrupt.
That's for timers .

McGregor1980, I would think you'd need to check for the UART being ready for each byte, which you don't . Try using UART_DRE as a trigger . You also didn't enable single shot mode .

DMA.CH2.REPCNT=0x06;

REPCNT doesn't repeat bytes, but BLOCKS of data .

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The following works and no interrupts are required, although it relies on ASF drivers, which seem people avoid like the plague. It borrows from Application Note AVR1522 and the ASF DMA Units Tests example project. A complete AS5.1 solution is in the attached ZIP file.

// symbolic constant for DMA Channel
#define DMA_CHANNEL    0


// the buffer to transfer
uint8_t *buffer = "Gregory\r\n";


// prototypes for functions declared below
void dma_init(void);
void usart_init(void);


// main
int main (void)
{
    uint8_t c;
    uint8_t *ptr;

    // ASF inits
    board_init();
    sysclk_init();

    // my inits
    usart_init();

    while (1) {
        dma_init();
        dma_channel_enable(DMA_CHANNEL);
        while (dma_channel_is_busy(DMA_CHANNEL));
        dma_channel_disable(DMA_CHANNEL);
    }

}


/*
 * void usart_init(void) initializes USARTC0
 */
void usart_init(void)
{
    usart_rs232_options_t usart_cfg = {
        .baudrate = 9600,
        .charlength = USART_CHSIZE_8BIT_gc,
        .paritytype = USART_PMODE_DISABLED_gc,
        .stopbits = false
    };


    usart_init_rs232(&USARTC0, &usart_cfg);
    usart_set_rx_interrupt_level(&USARTC0, USART_RXCINTLVL_OFF_gc);
    usart_set_tx_interrupt_level(&USARTC0, USART_TXCINTLVL_OFF_gc);
}


/*
 * void dma_init(void) initializes the DMA Channel DMA_CHANNEL to use burst
 * length DMA_BURST and block length DMA_BLOCK.
 */
void dma_init(void)
{
    // declare and zero-out the DMA config struct
    struct dma_channel_config config_params;
    memset(&config_params, 0, sizeof(config_params));

    // Enable the DMA controller
    dma_enable();

    /*
     * source reload = off
     * source direction = increment
     * source address = buffer
     */
    dma_channel_set_src_reload_mode(&config_params,
            DMA_CH_SRCRELOAD_NONE_gc);
    dma_channel_set_src_dir_mode(&config_params,
            DMA_CH_SRCDIR_INC_gc);
    dma_channel_set_source_address(&config_params,
            (uint16_t)(uintptr_t)buffer);


    /*
     * destination reload = off
     * destination direction = fixed
     * destination address = USARTC0 Data Register
     */
    dma_channel_set_dest_reload_mode(&config_params,
            DMA_CH_DESTRELOAD_NONE_gc);
    dma_channel_set_dest_dir_mode(&config_params,
            DMA_CH_DESTDIR_FIXED_gc);
    dma_channel_set_destination_address(&config_params,
            (uint16_t)(uintptr_t) &USARTC0.DATA);

    /*
     * enable single shot mode
     * set transfer count to length of buffer
     * set burst length to 1
     * set trigger source to USARTC0 DRE
     * write config to DMA controller
     */
    dma_channel_set_single_shot(&config_params);
    dma_channel_set_transfer_count(&config_params, strlen(buffer));
    dma_channel_set_burst_length(&config_params, DMA_CH_BURSTLEN_1BYTE_gc);
    dma_channel_set_trigger_source(&config_params,
        DMA_CH_TRIGSRC_USARTC0_DRE_gc );
    dma_channel_write_config(DMA_CHANNEL, &config_params);

}

Attachment(s): 

Gamu The Killer Narwhal
Portland, OR, US
_________________
Atmel Studio 6.2
Windows 8.1 Pro
Xplained boards mostly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The following are artifacts from a "test it without DMA" incarnation. They are unneeded, extraneous and/or superfluous.

uint8_t c;
uint8_t *ptr;

Gamu The Killer Narwhal
Portland, OR, US
_________________
Atmel Studio 6.2
Windows 8.1 Pro
Xplained boards mostly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All of You were right,...which means i was wrong ;).

The missing part was:

dma_channel_set_trigger_source(&config_params,
        DMA_CH_TRIGSRC_USARTC0_DRE_gc ); 

Thanks for the code GTKNarwahl.

...but do i need to enable USARTC0_DRE interrupt to make it working or is it enough to point in DMA which trigger source it should use?

Gregor

Gregor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

McGregor1980 wrote:
... or is it enough to point in DMA which trigger source it should use?

Gregor

Bingo !

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
...but do i need to enable USARTC0_DRE interrupt to make it working or is it enough to point in DMA which trigger source it should use?

As IndianaJones11 pointed out, all you need to do is specify the trigger source. You do not need to enable the interrupt on the USART.

I played around some more with this yesterday evening and produced a non-ASF version that is directly based on the ASF version. The code below does still use the ASF to configure system timing and the USART.

Optimization breaks this code (it only outputs the first byte over and over and over...) and requires the insertion of NOPs before and after the line of code that enables the DMA channel. I tired using memory barriers before and after the lines of code that enable the channel, wait to the transaction to complete, disable the channel, and reset the SRCADDR, but the code still breaks.

/*
 * USART using DMA example
 */

/*
 * Include header files for all drivers that have been imported from
 * AVR Software Framework (ASF).
 */
#include 
#include 


// these must match
#define DMA_CHANNEL_NAME	CH0
#define DMA_CHANNEL_NUMBER	0


// memory (optimization) barrier and NOP
#define barrier()	asm volatile("" ::: "memory") 
#define nop()		asm volatile("nop;" ::: "memory")


// the buffer to transfer
uint8_t *buffer = "This is a test.  This is only a test.\r\n";


// prototypes for functions declared below
void dma_init(void);
void usart_init(void);


// main
int main (void)
{

    // ASF inits
    board_init();
    sysclk_init();

    // my inits
    usart_init();
    dma_init();

    while (1) {

        // enable the DMA channel
        // NOPs are required if optimization is not off (-O0)
        nop();
        DMA.DMA_CHANNEL_NAME.CTRLA |= DMA_CH_ENABLE_bm;
        nop();

        // wait until DMA channel is neither busy nor pending
        while (DMA.STATUS & (0x11 << DMA_CHANNEL_NUMBER));

        // disable the DMA channel
        DMA.DMA_CHANNEL_NAME.CTRLA &= ~DMA_CH_ENABLE_bm;

        // reset SRCADDR
        DMA.DMA_CHANNEL_NAME.SRCADDR0 = buffer;
        DMA.DMA_CHANNEL_NAME.SRCADDR1 =	(uint16_t)buffer >> 8;
        DMA.DMA_CHANNEL_NAME.SRCADDR2 = 0;

    }

}


/*
 * void usart_init(void) initializes USARTC0.
 */
void usart_init(void)
{

    usart_rs232_options_t usart_cfg = {
        .baudrate = 9600,
        .charlength = USART_CHSIZE_8BIT_gc,
        .paritytype = USART_PMODE_DISABLED_gc,
        .stopbits = false
    };

    usart_init_rs232(&USARTC0, &usart_cfg);
    usart_set_rx_interrupt_level(&USARTC0, USART_RXCINTLVL_OFF_gc);
    usart_set_tx_interrupt_level(&USARTC0, USART_TXCINTLVL_OFF_gc);

}


/*
 * void dma_init(void) initializes DMA Channel DMA_CHANNEL_NAME.
 */
void dma_init(void)
{
    /* if using the ASF "TIMING - Clock Control Service", which disables
     * the peripheral clocks to ALL peripherals, sysclk_enable_module()
     * must be called to enable the PER clock to the DMA controller.
     */
    sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_DMA);

    // reset & enable the DMA controller
    DMA.CTRL = DMA_RESET_bm;
    DMA.CTRL = DMA_ENABLE_bm;

    /* configure the DMA channel.
     * the (uint16_t) cast is necessary to prevent the compiler from whining.
     * REPCNT and CTRLB are not used in this example.
     */
    DMA.DMA_CHANNEL_NAME.DESTADDR0 = &USARTC0.DATA;
    DMA.DMA_CHANNEL_NAME.DESTADDR1 = (uint16_t)&USARTC0.DATA >> 8;
    DMA.DMA_CHANNEL_NAME.DESTADDR2 = 0;
    DMA.DMA_CHANNEL_NAME.SRCADDR0 = buffer;
    DMA.DMA_CHANNEL_NAME.SRCADDR1 =	(uint16_t)buffer >> 8;
    DMA.DMA_CHANNEL_NAME.SRCADDR2 =	0;
    DMA.DMA_CHANNEL_NAME.ADDRCTRL = DMA_CH_SRCDIR_INC_gc;
    DMA.DMA_CHANNEL_NAME.TRIGSRC = DMA_CH_TRIGSRC_USARTC0_DRE_gc;
    DMA.DMA_CHANNEL_NAME.TRFCNT = strlen(buffer);
//  DMA.DMA_CHANNEL_NAME.REPCNT = 0;
//  DMA.DMA_CHANNEL_NAME.CTRLB =	DMA_CH_ERRINTLVL_LO_gc | DMA_CH_TRNINTLVL_LO_gc;
    DMA.DMA_CHANNEL_NAME.CTRLA =	DMA_CH_SINGLE_bm;

}

Gamu The Killer Narwhal
Portland, OR, US
_________________
Atmel Studio 6.2
Windows 8.1 Pro
Xplained boards mostly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks a lot guys. Now it is clear for me.
Or at least it's working :)

Have a nice weekend
Gregor

Gregor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GTKNarwhal wrote:
The following works and no interrupts are required, although it relies on ASF drivers, which seem people avoid like the plague. It borrows from Application Note AVR1522 and the ASF DMA Units Tests example project. A complete AS5.1 solution is in the attached ZIP file.

...
    while (1) {
        dma_init();
        dma_channel_enable(DMA_CHANNEL);
        while (dma_channel_is_busy(DMA_CHANNEL));
        dma_channel_disable(DMA_CHANNEL);
    }

}

Maybe somebody can answer me why code above disable dma_channel manually since when block transfer is complete the channel is automatically disabled ? :(

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

hello guys,

 

for receiving data from usart via DMA, we simply enable DMA channel ( dma_channel_enable(DMA_CHANNEL) and when data is available DMA will transfert it or we have to define a function that notify when data is available like :

if data.available()

{

readdata();

}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have to do more than enable the DMA. Once you set up the ENTIRE DMA system, it will automatically move received bytes to where you desire it to go ( RAM buffer or whatever ).

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please post an example code for receiving usart using DMA.