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.

Reply
  • /*
     * 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.

Children
Related