-
Mini audio card
12/07/2017 at 20:52 • 0 comments -
I2S DMA transfer / interrupt
11/20/2017 at 14:28 • 0 commentsSetting 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. */ } }
-
Overclocking
10/20/2017 at 13:37 • 0 commentsThis experiment shows the overclocking results of the Atmel SAMG55 ARM Cortex M4F MCU.
The settings used:
- 3.3V supply voltage
- CPU clock from the external 32.768 KHz crystal multiplied by the internal PLL
- USB HID stack was running parallel
- 16K cache
- 5 flash wait states
- application runs from on-chip flash
- test application is the encoding + decoding of a 320 sample frame with CODEC2 700C algorithm (http://www.rowetel.com/?page_id=452)
The dataset:
freq. 120 MHz 130 MHz 140 MHz 150 MHz 160 MHz encode +
decode time28,5 ms 26,5 ms 25 ms 23 ms 21,5 ms 120 MHz is the maximal CPU clock frequency allowed officially. The MCU was running stable at 160 MHz with continuous USB HID data communication.
The experiment shows, that 33% overclocking is still stable.
-
Cache performance
10/20/2017 at 13:25 • 0 commentsHere are some statistics of the cache performance. The MCU is an Atmel SAMG55 Cortex M4 with FPU.
The test algorithm is the state of the art CODEC2 voice coder (http://www.rowetel.com/?page_id=452)
The test application uses the 700C coding setting. The chart below shows the average encoding + decoding time in ms @ 120MHz with different cache size settings.
The dataset:
noCache 1K 2K 4K 8K 16K 42 ms 29,5 ms 28,5 ms 29,5 ms 28,5 ms 28,5 ms -
First prototypes
10/04/2017 at 10:52 • 0 commentsFirst heartbeat(s)
Atmel Software Framework (ASF) components work like charm!