Close

I2S DMA transfer / interrupt

A project log for Atmel SAM G55 development board

Small, simple SAM G55 development board with extra functions

x-labzx-labz 11/20/2017 at 14:280 Comments

Setting up the I2S DMA transfer with interrupt was not that straightforward, the datasheet is a little bit "tight-lipped" :-) ...and the official ASF example isn't commented good enough...

So my findings:

I started with the I2S example from the ASF 3.35.1  using Atmel Studio 7

In my opinion it works as expected, but You should be aware of some rules:

ATMEL freaks post: https://community.atmel.com/forum/samg55-i2s-dmapdc-endtx-interrupt-issue#comment-2327756 

The original ASF example  modified for mono transfer only:

#include <asf.h>
#include <conf_test.h>
#include <string.h>

#define SOUND_SAMPLES 0x100
int16_t output_samples_left[SOUND_SAMPLES] = {
    0x7F, 0x7F, 0x7D, 0x7E, 0x7D, 0x7E, 0x7D, 0x7E,
    0x7D, 0x7D, 0x7D, 0x7F, 0x7E, 0x7D, 0x7E, 0x7D,
    0x7D, 0x7D, 0x7C, 0x7A, 0x7B, 0x7C, 0x7A, 0x7A,
    0x7C, 0x7B, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x80,
    0x81, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84,
    0x86, 0x83, 0x81, 0x81, 0x83, 0x83, 0x83, 0x84,
    0x82, 0x84, 0x84, 0x83, 0x85, 0x85, 0x82, 0x83,
    0x82, 0x82, 0x82, 0x82, 0x7F, 0x80, 0x81, 0x7E,
    0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E,
    0x7C, 0x7E, 0x7E, 0x7F, 0x7F, 0x7D, 0x7D, 0x7C,
    0x7D, 0x7D, 0x7D, 0x7B, 0x7C, 0x7B, 0x7C, 0x7D,
    0x7E, 0x7E, 0x7F, 0x7E, 0x7D, 0x7F, 0x7E, 0x7D,
    0x7D, 0x7B, 0x7D, 0x7D, 0x7E, 0x7D, 0x7D, 0x7E,
    0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7C, 0x7E, 0x7E,
    0x7F, 0x7F, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x81,
    0x7F, 0x80, 0x81, 0x80, 0x81, 0x81, 0x81, 0x81,
    0x82, 0x81, 0x82, 0x82, 0x81, 0x80, 0x7F, 0x80,
    0x7F, 0x7F, 0x7E, 0x80, 0x81, 0x82, 0x83, 0x82,
    0x83, 0x84, 0x81, 0x82, 0x82, 0x81, 0x82, 0x81,
    0x80, 0x80, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x81, 0x80,
    0x80, 0x7F, 0x7F, 0x80, 0x81, 0x80, 0x80, 0x7E,
    0x7E, 0x80, 0x7F, 0x7F, 0x80, 0x80, 0x7F, 0x7F,
    0x80, 0x80, 0x81, 0x7E, 0x7F, 0x80, 0x7E, 0x7E,
    0x7E, 0x7F, 0x7E, 0x7E, 0x7E, 0x7C, 0x7D, 0x7C,
    0x81, 0x7D, 0x7C, 0x7C, 0x7B, 0x7D, 0x7C, 0x7D,
    0x7D, 0x7D, 0x7B, 0x7D, 0x80, 0x80, 0x82, 0x80,
    0x7F, 0x80, 0x83, 0x82, 0x80, 0x82, 0x84, 0x86,
    0x86, 0x84, 0x84, 0x86, 0x87, 0x84, 0x85, 0x85,
    0x85, 0x85, 0x86, 0x85, 0x85, 0x84, 0x83, 0x80,
    0x81, 0x82, 0x83, 0x7F, 0x7E, 0x7F, 0x7F, 0x80,
    0x7E, 0x7E, 0x7E, 0x7C, 0x7C, 0x7D, 0x7D, 0x7C
};
int16_t output_samples_right[SOUND_SAMPLES] = {
    0x5A, 0x7F, 0x7D, 0x7E, 0x7D, 0x7E, 0x7D, 0x7E,
    0x7D, 0x7D, 0x7D, 0x7F, 0x7E, 0x7D, 0x7E, 0x7D,
    0x7D, 0x7D, 0x7C, 0x7A, 0x7B, 0x7C, 0x7A, 0x7A,
    0x7C, 0x7B, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x80,
    0x81, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84,
    0x86, 0x83, 0x81, 0x81, 0x83, 0x83, 0x83, 0x84,
    0x82, 0x84, 0x84, 0x83, 0x85, 0x85, 0x82, 0x83,
    0x82, 0x82, 0x82, 0x82, 0x7F, 0x80, 0x81, 0x7E,
    0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E,
    0x7C, 0x7E, 0x7E, 0x7F, 0x7F, 0x7D, 0x7D, 0x7C,
    0x7D, 0x7D, 0x7D, 0x7B, 0x7C, 0x7B, 0x7C, 0x7D,
    0x7E, 0x7E, 0x7F, 0x7E, 0x7D, 0x7F, 0x7E, 0x7D,
    0x7D, 0x7B, 0x7D, 0x7D, 0x7E, 0x7D, 0x7D, 0x7E,
    0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7C, 0x7E, 0x7E,
    0x7F, 0x7F, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x81,
    0x7F, 0x80, 0x81, 0x80, 0x81, 0x81, 0x81, 0x81,
    0x82, 0x81, 0x82, 0x82, 0x81, 0x80, 0x7F, 0x80,
    0x7F, 0x7F, 0x7E, 0x80, 0x81, 0x82, 0x83, 0x82,
    0x83, 0x84, 0x81, 0x82, 0x82, 0x81, 0x82, 0x81,
    0x80, 0x80, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x81, 0x80,
    0x80, 0x7F, 0x7F, 0x80, 0x81, 0x80, 0x80, 0x7E,
    0x7E, 0x80, 0x7F, 0x7F, 0x80, 0x80, 0x7F, 0x7F,
    0x80, 0x80, 0x81, 0x7E, 0x7F, 0x80, 0x7E, 0x7E,
    0x7E, 0x7F, 0x7E, 0x7E, 0x7E, 0x7C, 0x7D, 0x7C,
    0x81, 0x7D, 0x7C, 0x7C, 0x7B, 0x7D, 0x7C, 0x7D,
    0x7D, 0x7D, 0x7B, 0x7D, 0x80, 0x80, 0x82, 0x80,
    0x7F, 0x80, 0x83, 0x82, 0x80, 0x82, 0x84, 0x86,
    0x86, 0x84, 0x84, 0x86, 0x87, 0x84, 0x85, 0x85,
    0x85, 0x85, 0x86, 0x85, 0x85, 0x84, 0x83, 0x80,
    0x81, 0x82, 0x83, 0x7F, 0x7E, 0x7F, 0x7F, 0x80,
    0x7E, 0x7E, 0x7E, 0x7C, 0x7C, 0x7D, 0x7D, 0xA5
};
int16_t input_samples_left[SOUND_SAMPLES];
int16_t input_samples_right[SOUND_SAMPLES];

/* Flag indicate */
volatile uint32_t flag = true;

volatile uint32_t g_ms_ticks = 0;

struct i2s_dev_inst dev_inst;

void SysTick_Handler(void)
{
    g_ms_ticks++;
}

bool volatile i2s_done = false;
uint32_t volatile  test_i2s_irq_cnt = 0;

static void i2s_transfer_done(const struct i2s_dev_inst *const module)
{
    i2s_disable_interrupt(&dev_inst,I2S_INTERRUPT_TXRDY); 
    i2s_done = true;
    test_i2s_irq_cnt++;    
}

static void run_i2s_test(const struct test_case *test)
{
    uint32_t i;
    struct i2s_config config;
    
    Pdc *p_i2sc_pdc;
    Pdc *p_i2sc_pdc2;
    pdc_packet_t pdc_i2sc_packet_tx, pdc_i2sc_packet_rx;
    pdc_packet_t pdc2_i2sc_packet_tx, pdc2_i2sc_packet_rx;

    // clock
    Matrix *p_matrix = MATRIX;
    p_matrix->CCFG_I2SCLKSEL = CCFG_I2SCLKSEL_CLKSEL0 ;

    pmc_set_writeprotect(0);
    pmc_pck_set_prescaler(PMC_PCK_4,111);
    pmc_pck_set_source(PMC_PCK_4, PMC_PCK_CSS_PLLA_CLK);
    pmc_enable_periph_clk(ID_I2SC0);
    pmc_enable_pck(PMC_PCK_4);


    /* Set the configuration */
    i2s_get_config_defaults(&config);
    config.data_format = I2S_DATE_16BIT;
    config.fs_ratio = I2S_FS_RATE_2048;
    config.loopback = false; //true;
    config.tx_dma = I2S_ONE_DMA_CHANNEL_FOR_BOTH_CHANNELS;
    config.tx_channels = I2S_CHANNEL_MONO;
    i2s_init(&dev_inst, I2SC0, &config);

    /* Enable the I2SC module. */
    i2s_enable(&dev_inst);

    /* Get pointer to I2SC PDC register base */
    p_i2sc_pdc = i2s_get_pdc_base(&dev_inst);
    p_i2sc_pdc2 = (Pdc *)((uint32_t)p_i2sc_pdc + 0x100U);
    /* Initialize PDC data packet for transfer */
    //pdc_i2sc_packet_tx.ul_addr = (uint32_t) output_samples_left;
    //pdc_i2sc_packet_tx.ul_size = SOUND_SAMPLES;
    //pdc_i2sc_packet_rx.ul_addr = (uint32_t) input_samples_left;
    //pdc_i2sc_packet_rx.ul_size = SOUND_SAMPLES;
    pdc2_i2sc_packet_tx.ul_addr = (uint32_t) output_samples_right;
    pdc2_i2sc_packet_tx.ul_size = SOUND_SAMPLES;
    //pdc2_i2sc_packet_rx.ul_addr = (uint32_t) input_samples_right;
    //pdc2_i2sc_packet_rx.ul_size = SOUND_SAMPLES;
    /* Configure PDC for data transfer */
    //pdc_tx_init(p_i2sc_pdc, &pdc_i2sc_packet_tx, NULL);
    //pdc_rx_init(p_i2sc_pdc, &pdc_i2sc_packet_rx, NULL);
    pdc_tx_init(p_i2sc_pdc2, &pdc2_i2sc_packet_tx, NULL);
    //pdc_rx_init(p_i2sc_pdc2, &pdc2_i2sc_packet_rx, NULL);
    /* Enable PDC transfers */
    //pdc_enable_transfer(p_i2sc_pdc, PERIPH_PTCR_TXTEN | PERIPH_PTCR_RXTEN);
    pdc_enable_transfer(p_i2sc_pdc2, PERIPH_PTCR_TXTEN );
        
    i2s_set_callback(&dev_inst,I2S_INTERRUPT_TXRDY,i2s_transfer_done,1) ;
    i2s_enable_interrupt(&dev_inst,I2S_INTERRUPT_TXRDY);
    
    i2s_enable_clocks(&dev_inst);

    i2s_enable_transmission(&dev_inst);
    //for (i = 0; i < 0x80; i++) {
        //input_samples_left[i] = i;
    //}

    //i2s_enable_reception(&dev_inst);

    while (p_i2sc_pdc2->PERIPH_TCR != 0);
    
    while ( 1 ) {
        if (i2s_done) {
            i2s_done = false;
            pdc2_i2sc_packet_tx.ul_addr = (uint32_t) output_samples_right;
            pdc2_i2sc_packet_tx.ul_size = SOUND_SAMPLES;
            pdc_tx_init(p_i2sc_pdc2, &pdc2_i2sc_packet_tx, NULL);
            pdc_enable_transfer(p_i2sc_pdc2, PERIPH_PTCR_TXTEN );
            i2s_enable_interrupt(&dev_inst,I2S_INTERRUPT_TXRDY);
        }
    }
    

    /* Disable the PDC module. */
    pdc_disable_transfer(p_i2sc_pdc, PERIPH_PTCR_RXTDIS| PERIPH_PTCR_TXTDIS);
    pdc_disable_transfer(p_i2sc_pdc2, PERIPH_PTCR_RXTDIS| PERIPH_PTCR_TXTDIS);

    /* Disable the I2SC module. */
    i2s_disable(&dev_inst);

    /* Compare the data. */
    //for (i = 0; i < SOUND_SAMPLES; i++) {
        //if ((input_samples_left[i] != output_samples_left[i]) ||
            //(input_samples_right[i] != output_samples_right[i])) {
            //flag = false;
        //}
    //}
//
    //test_assert_true(test, flag == true, "Audio data did not match!");
}


int main(void)
{
    sysclk_init();

    if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
        while (1);
    }

    board_init();

    const usart_serial_options_t usart_serial_options = {
        .baudrate = CONF_UART_BAUDRATE,
        .paritytype = CONF_UART_PARITY,
#if SAMG55
        .charlength = CONF_UART_CHAR_LENGTH,
        .stopbits = CONF_UART_STOP_BITS,
#endif
    };

    stdio_serial_init(CONF_TEST_USART, &usart_serial_options);

    /* Define all the test cases. */
    DEFINE_TEST_CASE(i2s_test, NULL, run_i2s_test, NULL,
            "SAM I2S transfer test");

    /* Put test case addresses in an array. */
    DEFINE_TEST_ARRAY(i2s_tests) = {
        &i2s_test,
    };

    /* Define the test suite. */
    DEFINE_TEST_SUITE(i2s_suite, i2s_tests, "SAM I2S driver test suite");

    /* Run all tests in the test suite. */
    test_suite_run(&i2s_suite);

    while (1) {
        /* Busy-wait forever. */
    }
}

Discussions