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:
- You should use the second PDC channel if you are using only one PDC! That is reffered as the right channel in the datasheet: (0x200–0x224 Reserved for PDC registers for right side) page:874
- The TXRDY interrupt also works, but the interrupt should be disabled immediately in the Interrupt service function, and reenabled after the next PDC transfer has been configured and enabled.
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
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.