nrf52840 QFAA easydma

I am using the nrf52840 in QFAA package, and the external DMIC example receives audio data through the PDM interface. I noticed that the official documentation mentions support for EasyDMA. How can I use the DMA mechanism, and are there any related example codes available?

Parents
  • There is a "DMIC" audio sample in NCS SDK that should demonstrate how to use the driver interface.

    DMA is used internally by the driver.

  • /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/audio/dmic.h>
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(dmic_sample);
    
    #define MAX_SAMPLE_RATE  16000
    #define SAMPLE_BIT_WIDTH 16
    #define BYTES_PER_SAMPLE sizeof(int16_t)
    /* Milliseconds to wait for a block to be read. */
    #define READ_TIMEOUT     1000
    
    /* Size of a block for 100 ms of audio data. */
    #define BLOCK_SIZE(_sample_rate, _number_of_channels) \
    	(BYTES_PER_SAMPLE * (_sample_rate / 10) * _number_of_channels)
    
    /* Driver will allocate blocks from this slab to receive audio data into them.
     * Application, after getting a given block from the driver and processing its
     * data, needs to free that block.
     */
    #define MAX_BLOCK_SIZE   BLOCK_SIZE(MAX_SAMPLE_RATE, 2)
    #define BLOCK_COUNT      4
    K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);
    
    static int do_pdm_transfer(const struct device *dmic_dev,
    			   struct dmic_cfg *cfg,
    			   size_t block_count)
    {
    	int ret;
    
    	LOG_INF("PCM output rate: %u, channels: %u",
    		cfg->streams[0].pcm_rate, cfg->channel.req_num_chan);
    
    	ret = dmic_configure(dmic_dev, cfg);
    	if (ret < 0) {
    		LOG_ERR("Failed to configure the driver: %d", ret);
    		return ret;
    	}
    
    	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
    	if (ret < 0) {
    		LOG_ERR("START trigger failed: %d", ret);
    		return ret;
    	}
    
    	for (int i = 0; i < block_count; ++i) {
    		void *buffer;
    		uint32_t size;
    
    		ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
    		if (ret < 0) {
    			LOG_ERR("%d - read failed: %d", i, ret);
    			return ret;
    		}
    
    		LOG_INF("%d - got buffer %p of %u bytes", i, buffer, size);
    
    		k_mem_slab_free(&mem_slab, buffer);
    	}
    
    	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
    	if (ret < 0) {
    		LOG_ERR("STOP trigger failed: %d", ret);
    		return ret;
    	}
    
    	return ret;
    }
    
    int main(void)
    {
    	const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
    	int ret;
    
    	LOG_INF("DMIC sample");
    
    	if (!device_is_ready(dmic_dev)) {
    		LOG_ERR("%s is not ready", dmic_dev->name);
    		return 0;
    	}
    
    	struct pcm_stream_cfg stream = {
    		.pcm_width = SAMPLE_BIT_WIDTH,
    		.mem_slab  = &mem_slab,
    	};
    	struct dmic_cfg cfg = {
    		.io = {
    			/* These fields can be used to limit the PDM clock
    			 * configurations that the driver is allowed to use
    			 * to those supported by the microphone.
    			 */
    			.min_pdm_clk_freq = 1000000,
    			.max_pdm_clk_freq = 3500000,
    			.min_pdm_clk_dc   = 40,
    			.max_pdm_clk_dc   = 60,
    		},
    		.streams = &stream,
    		.channel = {
    			.req_num_streams = 1,
    		},
    	};
    
    	cfg.channel.req_num_chan = 1;
    	cfg.channel.req_chan_map_lo =
    		dmic_build_channel_map(0, 0, PDM_CHAN_LEFT);
    	cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
    	cfg.streams[0].block_size =
    		BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);
    
    	ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
    	if (ret < 0) {
    		return 0;
    	}
    
    	cfg.channel.req_num_chan = 2;
    	cfg.channel.req_chan_map_lo =
    		dmic_build_channel_map(0, 0, PDM_CHAN_LEFT) |
    		dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT);
    	cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
    	cfg.streams[0].block_size =
    		BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);
    
    	ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
    	if (ret < 0) {
    		return 0;
    	}
    
    	LOG_INF("Exiting");
    	return 0;
    }
    

    The official example code does not use DMA. The datasheet for the nRF52840 only covers the hardware configuration for this part and does not provide specific configuration functions.

  • It very much does use DMA internally since that is the only way you can actually get audio data out of the peripherial.

    Code above does not show anything that is actually happening in drivers. Try running it in a debugger.

    I strongly recommend using the driver (and not NRFX stuff directly) here as it solves the otherwise non- trivial audio buffering issues.

  • Are you saying DMA is enabled without any configuration? The example code can be flashed normally, but it doesn't seem to be using the DMA mechanism, right?

Reply Children
Related