This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Using PDM Mic on nRF9160DK

I was wondering if there were any good examples of how to get a PDM device (in this case a microphone) working on the nRF9160DK. I've read parts of the datasheet and I've been able to find the nrfx_pdm driver in the SDK but am unaware what pins to use to get it to read data and how to use the easy-dma feature.

Any pointers in the right direction are appreciated. Thank you

Parents
  • Hi,

    There is no PDM example that I am aware of. However, the nrfx PDM driver has decent API documentation, so you can use it to get a pointer on how to use it.

    Regarding pin configuration, any two of the GPIO pins can be used. EasyDMA is the only way to get data, so it is handled for you. You just have to provide a buffer (pointer and size) using the nrfx_pdm_buffer_set() function.

  • I tried using nrfx_pdm.h however it does not seem to be configured to the nrf9160 as the nrf9160.h does not have NRF_PDM define but DOES have NRF_PDM_NS and NRF_PDM_S defined.

    I did a search and replace to replace NRF_PDM w/ NRF_PDM_NS in the nrf_pdm.h and saved it as nrf_pdm_ns.h

    That seems to be all well and good until I tried to build and gives me some errors

    #include <nrf9160.h>
    #include <zephyr.h>
    #include <misc/printk.h>
    #include <string.h>
    #include <stdlib.h>
    #include <nrfx_pdm_ns.h>
    
    
    
    
    #include <gpio.h>
    
    #define PDM_BUF_ADDRESS 0x20000000 // buffer address in RAM
    #define PDM_BUF_SIZE 256 //size in 32 bit words --> 40 bytes
    
    int16_t pdm_buf[PDM_BUF_SIZE];
    
    
    void nrfx_pdm_event_handler(nrfx_pdm_evt_t const *const p_evt)
    {
    	if (p_evt->buffer_requested) {
    		nrfx_pdm_buffer_set(pdm_buf, PDM_BUF_SIZE);						<-----HERE!!!
    	}
    	if (p_evt->buffer_released != 0) {
    		printk("Out: %.2x %2x\r\n", (uint16_t)pdm_buf[0],
    		       (uint16_t)pdm_buf[1]);
    	}
    }
    
    static void pdm_init(void)
    {
    	nrfx_pdm_config_t pdm_config = NRFX_PDM_DEFAULT_CONFIG(10, 11); /*configures CLK to pin 10 and Din to pin 11*/
    	nrfx_pdm_init(&pdm_config, nrfx_pdm_event_handler);						<------AND HERE!!
    }
    
    void main(void)
    {
    	printk("Starting PDM program!\n");
    	printk("PDM Buffer size: %d\n", (PDM_BUF_SIZE * 4));
    	printk("PDM Starting Address: %x\n", PDM_BUF_ADDRESS);
    
    	pdm_init();
    }

    once I try to build SES gives me these errors

    despite that my nrfx_pdm_ns.h file includes the prototypes (I believe).

    Did I do something wrong or am I missing something?

    Thanks for your help

  • okay, so I tried adding 

    #ifndef NRFX_PDM_ENABLED
    #warning NRFX_PDM_ENABLED is undefined
    #else
    #if !NRFX_PDM_ENABLED
    #warning NRFX_PDM_ENABLED is defined but set to 0
    #endif
    #endif

    to nrfx_pdm_ns.h, nrf_pdm_ns.h both to no results

    however, once I added it to nrfx.h I get this warning

    so I then proceeded to add this to both nrfx_glue.h and nrfx_glue.c

    #ifndef NRFX_PDM_ENABLED
    #define NRFX_PDM_ENABLED 1
    #endif

    But even after adding that, I still get the same exact errors and the same exact warnings

  • So I changed up my code to test if the PDM was enabled using 

    nrfx_pdm_enable_check()

    shockingly it did not give me an undefined reference error, however when I tried to run the code it did give me this error

    here is my edited code

    /*  
    		C Program to run a pulse density modulation (PDM) Microphone on the nRF9160 Dev Kit using the Nordic SDK
    */
    
    #include <nrf9160.h>
    #include <zephyr.h>
    #include <misc/printk.h>
    #include <string.h>
    #include <stdlib.h>
    #include <nrfx_pdm_ns.h>
    #include <nrfx_pdm.h>
    
    #include <gpio.h>
    
    
    #define PDM_BUF_ADDRESS 0x20000000 // buffer address in RAM
    #define PDM_BUF_SIZE 256 //size in 32 bit words --> 1KB
    
    int16_t pdm_buf[PDM_BUF_SIZE];
    
    /*
    void nrfx_pdm_event_handler(nrfx_pdm_evt_t const *const p_evt)
    {
    	if (p_evt->buffer_requested) {
    		//nrfx_pdm_buffer_set(pdm_buf, PDM_BUF_SIZE);
    	}
    	if (p_evt->buffer_released != 0) {
    		printk("Out: %.2x %2x\r\n", (uint16_t)pdm_buf[0],
    		       (uint16_t)pdm_buf[1]);
    	}
    }
    */ 
    
    //static void pdm_init(void)
    //{
    //	nrfx_pdm_config_t pdm_config = NRFX_PDM_DEFAULT_CONFIG(10, 11); /*configures CLK to pin 10 and Din to pin 11*/
    //	//nrfx_pdm_init(&pdm_config, nrfx_pdm_event_handler);
    //}
    
    void main(void)
    {
    	printk("Starting PDM program!\n");
    	//printk("PDM Buffer size: %d\n", (PDM_BUF_SIZE * 4));
    	//printk("PDM Starting Address: %x\n", PDM_BUF_ADDRESS);
    
            bool enabled = nrfx_pdm_enable_check();
            printk(enabled ? "true/n" : "false\n");
    
    	//pdm_init();
    }

    I am very confused

  • Hi,

    This seems to be because of the Secure Partition Manager (SPM) is not configured to make the PDM peripheral non-secure. You can fix it by adding PDM to the periph_cfg list in spm.c. You can add the following line in the list:

    PERIPH("NRF_PDM", NRF_PDM_S, 1),

  • Hi Einar,

    Would that work to make it non-secure? Most the other ones I can see have an NS at the end whereas this has PDM_S at the end. Is that correct?

  • Awesome, so I tried your fix, and am no longer getting a bus fault error. Also for some reason my undefined reference to buffer set is gone, but my undefined nrfx_pdm_init is still there

Reply Children
  • So, uh, I forgot to uncomment out my buffer_set function, they're both still undefined reference.

  • Hi,

    I just thought of one obvious thing. Since PDM is not supported yet, nrfx_pdm.c is not even included in zephyr\ext\hal\nordic\CMakeLists.txt, so it is never compiled. So it is no wonder you get the undefined reference. You need to add it to the list of tiles in zephyr\ext\hal\nordic\CMakeLists.txt.

  • that makes sense, I'll check on that. One thing I find odd though, how am I able to use the nrfx_pdm_enable_check() function if it's part of the same header file?

  • Hi Einar,

    so I added these lines to zephyr\ext\hal\nordic\CMakeLists.txt

    zephyr_sources_ifdef(CONFIG_NRFX_PDM nrfx/drivers/src/nrfx_pdm.c)
    zephyr_sources_ifdef(CONFIG_NRFX_PDM_NS nrfx/drivers/src/nrfx_pdm_ns.c)

    I then re-built the project and tried to build the solution, and still got the same undefined reference errors. Did I add the correct lines, or was there something I missed?

    Thanks

  • Hi,

     

    This isn't straight-forward due to the PDM not being natively supported.

    1. First thing is to add the sources in zephyr/ext/hal/nordic/CMakeLists.txt:

    zephyr_sources_ifdef(CONFIG_NRFX_PDM nrfx/drivers/src/nrfx_pdm.c)

     

    2. And you also need to add these two lines to zephyr/ext/hal/nordic/Kconfig:

    config NRFX_PDM
        bool

     

    3. Now we start on the application side. you need to append this to your prj.conf:

    CONFIG_COMPILER_OPT="-DNRF_PDM=NRF_PDM_NS -DNRFX_PDM_ENABLED=1"

     

    This will enable the module, and define NRF_PDM to NRF_PDM_NS.

     

    4. Next up is selecting the NRFX_PDM in Kconfig. This is a symbol that needs to be selected by a module, so let's add one to your application.

    Create the file "Kconfig" in nrf/samples/nrf9160/my_sample/ and paste into the file:

    mainmenu "PDM test"
    
    config FORCE_PDM
    	bool "Test to force PDM"
    	select NRFX_PDM
        default y
    
    source "$ZEPHYR_BASE/Kconfig.zephyr"

     

    You can name the config what-ever you'd like, in this case it is "FORCE_PDM", and it's default y (so no need to manually add it to your prj.conf in this case).

      

    Could you try these steps and see if it compiles properly now?

    Sorry for the comprehensive workaround and the inconvenience.

     

    *edit* in order to use the non-blocking (ie: the handler function), you have to add this to your main to forward the interrupt vector:

    ISR_DIRECT_DECLARE(pdm_isr_handler)
    {
    	nrfx_pdm_irq_handler();
    	ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency
    			  */
    
    	return 1; /* We should check if scheduling decision should be made */
    }
    
    void main(void)
    {
    	IRQ_DIRECT_CONNECT(PDM_IRQn, 0,
    			   pdm_isr_handler, 0);
    	/* Rest of main */

     

    Kind regards,

    Håkon

Related