How to use device tree to set up PDM pin configurations with nrfx pdm directly (no dmic)

I've implemented a PDM microphone driver using the nrfx_pdm.c module directly, NOT the zephyr dmic module. We are in the early stages of development, so we are still supporting the nrf7002-dk development kit in addition to the first custom prototype. The PDM pins on the dev kit are different from the prototype, and I'd like to be able to use the device tree for the PDM configuration of pins to have a different build for each of the boards. We have the build already setup, so it's a matter of properly setting up and using the overlay contents to configure and initialize PDM.  

nrfx_pdm_config_t has a way to specify clk_pin and din_pin. If I hard code those to the values and set skip_gpio_cfg and skip_psel_cfg to false I have PDM working and can get audio data. But I need that to be different pins for the different boards. So my questions are:
1) What should I change in the overlay configuration below? and
2) How do I initialize the module in code, or alternatively, access the pin numbers to be able to pass to nrfx_pdm_config_t and do the initialization there?
Based on examples, my overlay is shown below, and I can get a pointer to the device using DEVICE_DT_GET(DT_NODELABEL(mic_dev));  

 

&pinctrl {
	pdm0_default_alt: pdm0_default_alt {
		group1 {
			psels = <NRF_PSEL(PDM_CLK, 0, 25)>,
				<NRF_PSEL(PDM_DIN, 0, 26)>;
		};
	};
};

// Additional settings for the PDM device
mic_dev: &pdm0 {
	status = "okay";
	pinctrl-0 = <&pdm0_default_alt>;
	pinctrl-names = "default";
	clock-source = "ACLK";
};

Thanks,

KE

  • Hi KE,

    But I need that to be different pins for the different boards. So my questions are:
    1) What should I change in the overlay configuration below? and

    The pins are defined in the pinctrl node. In particular, the NRF_PSEL macro lines.

    For example, NRF_PSEL(PDM_CLK, 0, 25) means P0.25 is used for PDM_CLK.

    2) How do I initialize the module in code, or alternatively, access the pin numbers to be able to pass to nrfx_pdm_config_t and do the initialization there?

    I dived a bit into the drivers' implementation and was able to derive the pin numbers in the DMIC sample with this code:

    PINCTRL_DT_DEFINE(DT_NODELABEL(dmic_dev));
    
    int main(void)
    {
    	const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
    	int ret;
    
    	const struct pinctrl_dev_config* p_pinctrl_dev_cfg = 
    							PINCTRL_DT_DEV_CONFIG_GET(DT_NODELABEL(dmic_dev));
    	printk("===================================================\n");
    	
    	uint8_t t_state_cnt = p_pinctrl_dev_cfg->state_cnt;
    	printk("pinctrl state_cnt = %d\n", t_state_cnt);
    	
    	for (uint8_t i = 0; i < t_state_cnt; i++)
    	{
    		const struct pinctrl_state* tp_pinctrl_state = &p_pinctrl_dev_cfg->states[i];
    
    		printk("  state #%02d - id = %d\n", i, tp_pinctrl_state->id);
    
    		uint8_t t_pin_cnt = tp_pinctrl_state->pin_cnt;
    		printk("  t_pin_cnt = %d\n", t_pin_cnt);
    
    		for (uint8_t j = 0; j < t_pin_cnt; j++)
    		{
    			const pinctrl_soc_pin_t t_pin = tp_pinctrl_state->pins[j];
    			uint32_t nrfx_abs_pin_num = t_pin & 0x0000FFFFU;
    			printk("    pin #%02d = 0x%08x - nrfx_abs_pin_num = 0x%08x\n", 
    						j, 
    						t_pin,
    						nrfx_abs_pin_num);
    		}
    	}
    
    	printk("===================================================\n");
    	
    	...
    }

    I arrived at that after these steps:

    1. Look into the PDM driver implementation to see how the pinctrl node are associated with the device node.
      Found: https://github.com/nrfconnect/sdk-zephyr/blob/v3.5.99-ncs1/drivers/audio/dmic_nrfx_pdm.c#L570

    2. Look into where the pinctrl macro above is defined, and investigate the definitions in the same file.
      Refer: https://github.com/nrfconnect/sdk-zephyr/blob/v3.5.99-ncs1/include/zephyr/drivers/pinctrl.h

    3. Associate the value of the pin variables with the definition of the NRF_PSEL macro used in the Devicetree.
      Refer: https://github.com/nrfconnect/sdk-zephyr/blob/v3.5.99-ncs1/include/zephyr/dt-bindings/pinctrl/nrf-pinctrl.h#L202-L204

    I hope this helps.

    Hieu

  • When I tried the following (modified to align with my overlay file):

    const struct pinctrl_dev_config* p_pinctrl_dev_cfg =
    PINCTRL_DT_DEV_CONFIG_GET(DT_NODELABEL(mic_dev));

    it fails with error: "identifier "__pinctrl_dev_config__device_dts_ord_107" is undefined"

    I see documentation saying that means the device is not allocated, usually due to an issue with Kconfig. I am not sure what to change in Kconfig, and the documentation makes it appear non-trivial to make changes. Hopefully it is something else that needs changing.  

    If we're using dmic, the magic must happen when we enable CONFIG_AUDIO_DMIC in prj.conf, but I can't easily trace how that happens. But we are not using dmic, so I have to make that connection myself. There must be some step I'm missing to actually get the device allocated. Any ideas?

    Our Kconfig, which I believe is unchanged from example projects:

    #
    # Copyright (c) 2022 Nordic Semiconductor
    #
    # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    #
    
    source "Kconfig.zephyr"
    
    menu "Nordic Sta sample"
    
    config CONNECTION_IDLE_TIMEOUT
    	int "Time to be waited for a station to connect"
    	default 30
    
    config STA_SAMPLE_SSID
    	string "SSID"
    	help
    	  Specify the SSID to connect
    
    choice  STA_KEY_MGMT_SELECT
    	prompt "Security Option"
    	default STA_KEY_MGMT_WPA3
    
    config STA_KEY_MGMT_NONE
    	bool "Open Security"
    	help
    	  Enable for Open Security
    
    config STA_KEY_MGMT_WPA2
    	bool "WPA2 Security"
    	help
    	  Enable for WPA2 Security
    
    config STA_KEY_MGMT_WPA2_256
    	bool "WPA2 SHA 256 Security"
    	help
    	  Enable for WPA2-PSK-256 Security
    
    config STA_KEY_MGMT_WPA3
    	bool "WPA3 Security"
    	help
    	  Enable for WPA3 Security
    endchoice
    
    config STA_SAMPLE_PASSWORD
    	string "Passphrase (WPA2) or password (WPA3)"
    	help
    	  Specify the Password to connect
    
    config NRF700X_QSPI_ENCRYPTION_KEY
    	string "16 bytes QSPI encryption key, only for testing purposes"
    	depends on BOARD_NRF7002DK_NRF5340_CPUAPP
    	help
    	  Specify the QSPI encryption key
    endmenu
    

    Thanks,

    KE

  • Hi KE,

    It is actually more often due to a Devicetree issue than due to Kconfig. 
    The line that you meet the error only deal with Devicetree, and should not have any problem from Kconfig. However, the overlay in your opening post should have made it work...
    Could you please follow the recommendations my colleague Maria writes in this DevZone case, and let me know if it help?
    SPI and GPIOs defined via device tree report as "undefined references" at linking time.

    If that doesn't help, please share the compiled zephyr.dts and .config files, located in <build directory>/zephyr.

    By the way, the Kconfig file you gave is not your project configuration, but rather the definition of additional Kconfig. The configurations are done in prj.conf file and any additional overlay files you included in the build.
    For more information, refer to: Configuration System (Kconfig) — Zephyr Project documentation (nRF Connect SDK) (nordicsemi.com).

    Regards,

    Hieu

  • Ok, I found the issue--I missed the top line of your example, PINCTRL_DT_DEFINE(DT_NODELABEL(dmic_dev));

    Everything after that worked!

    And also, I found out about a way to figure out which pin is which:

    #define PDM0 DT_NODELABEL(pdm0)
    PINCTRL_DT_DEFINE(PDM0);
    static const struct pinctrl_dev_config* p_pinctrl_dev_cfg = PINCTRL_DT_DEV_CONFIG_GET(PDM0);
        
    void init_function()
    {
        const struct pinctrl_state *state = &(p_pinctrl_dev_cfg->states[0]);
    
        for (unsigned i = 0; i < state->pin_cnt; ++i)
        {
            switch (NRF_GET_FUN(state->pins[i]))
            {
            case NRF_FUN_PDM_CLK:
                pdm_config.clk_pin = NRF_GET_PIN(state->pins[i]);
                LOG_DBG("pdm_config.clk_pin = %d\n", pdm_config.clk_pin);
                break;
            case NRF_FUN_PDM_DIN:
                pdm_config.din_pin = NRF_GET_PIN(state->pins[i]);
                LOG_DBG("pdm_config.din_pin = %d\n", pdm_config.din_pin);
                break;
            default:
                __ASSERT(0,"Unknown pin function\n");
            }
        }
    }

    Thanks for your help!

Related