QDEC sensor_attr_set() returns -88 - not implemented

Hello,

I need to increase the sampling frequency for the QDEC peripheral (desktop mouse) using the Zephyr sensor module, but the needed function seems to be unimplemented, even in the most recent SDK version (2.6.0)

The following code return error -88:

const struct device *const qdec_dev = DEVICE_DT_GET(DT_ALIAS(qdec0));
struct sensor_value sampl_freq;

sampl_freq.val1 = 200; // deafult is 100 per sensor_attr_get()
sampl_freq.val2 = 0;

int rc = sensor_attr_set(qdec_dev, SENSOR_CHAN_ROTATION, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampl_freq);
if (rc != 0)
{
	printk("sensor_attr_set error: %d\n", rc);
	return 1;
}

Which comes from this code in zephyr/drivers/sensor.h:

static inline int z_impl_sensor_attr_set(const struct device *dev,
					 enum sensor_channel chan,
					 enum sensor_attribute attr,
					 const struct sensor_value *val)
{
	const struct sensor_driver_api *api =
		(const struct sensor_driver_api *)dev->api;

	if (api->attr_set == NULL) {
		return -ENOSYS;
	}

Isn't there really a way to set this attribute in zephyr yet?

Do I have to resort back to the nrfx drivers? If so can you provide a minimal sample?

Around the forum I can find code snippet relevant to it but never a complete, minimal sample.

Thanks.

  • Looks like you are correct, I suggest to check out this thead as workaround:
    Setting QDEC sampleper value via driver API. 

    Kenneth

  • I wrote what I believe is a minimal example based on that thread, but I don't get any interrupt firings, can you check my code? The hardware is confirmed working because it works with the Zephyr sensor module.

    proj.config

    CONFIG_NRFX_QDEC=y
    
    CONFIG_GPIO=y
    
    CONFIG_PINCTRL=y
    
    CONFIG_LOG=y
    CONFIG_LOG_MODE_IMMEDIATE=y
    CONFIG_USE_SEGGER_RTT=y
    
    # needed, no 32KHz crystal on board
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y

    .dts

    /dts-v1/;
    #include <nordic/nrf52833_qiaa.dtsi>
    
    / {
    	model = "Nordic nRF52833 Dongle NRF52833";
    	compatible = "nordic,nrf52833-dongle-nrf52833";
    
    	chosen {
    		zephyr,console = &uart0;
    		zephyr,sram = &sram0;
    		zephyr,flash = &flash0;
    		zephyr,code-partition = &slot0_partition;
    	};
    
    	aliases {
    		qdec0 = &qdec;
    	};
    };
    &gpiote {
    	status = "okay";
    };
    
    &gpio0 {
    	status = "okay";
    };
    
    &flash0 {
    	/*
    	 * For more information, see:
    	 * http: //docs.zephyrproject.org/latest/guides/dts/index.html#flash-partitions
    	 */
    	partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    
    		boot_partition: partition@0 {
    			label = "mcuboot";
    			reg = <0x000000000 0xC000>;
    		};
    		slot0_partition: partition@c000 {
    			label = "image-0";
    			reg = <0x0000C000 0x32000>;
    		};
    		slot1_partition: partition@3e000 {
    			label = "image-1";
    			reg = <0x0003E000 0x32000>;
    		};
    		scratch_partition: partition@70000 {
    			label = "image-scratch";
    			reg = <0x00070000 0xA000>;
    		};
    		storage_partition: partition@7a000 {
    			label = "storage";
    			reg = <0x0007A000 0x00006000>;
    		};
    	};
    
    };
    
    &qdec {
    	status = "okay";
    	led-pre = <0>;
    	steps = <24>;
    	pinctrl-0 = <&qdec_default>;
    	pinctrl-1 = <&qdec_sleep>;
    	pinctrl-names = "default", "sleep";
    };
    
    &pinctrl {
    	qdec_default: qdec_default {
    		group1 {
    			psels = <NRF_PSEL(QDEC_A, 0, 29)>,
    						<NRF_PSEL(QDEC_B, 0, 30)>;
    		};
    	};
    
    	qdec_sleep: qdec_sleep {
    		group1 {
    			psels = <NRF_PSEL(QDEC_A, 0, 29)>,
    						<NRF_PSEL(QDEC_B, 0, 30)>;
    		};
    	};
    };
    

    main.c

    #include <zephyr/kernel.h>
    #include <nrfx_qdec.h>
    
    #define NRF_GPIO_PIN_MAP(port, pin) ((uint32_t)(((port) << 5) | (pin)))
    
    // Use NRF_GPIO_PIN_MAP macro to get the uint32_t representation
    #define PIN_A_VALUE NRF_GPIO_PIN_MAP(0, 29)
    #define PIN_B_VALUE NRF_GPIO_PIN_MAP(0, 30)
    
    static void qdec_nrfx_event_handler(nrfx_qdec_event_t event)
    {
    	printk("Enetered qdec_nrfx_event_handler \n");
    	switch (event.type)
    	{
    	case NRF_QDEC_EVENT_SAMPLERDY:
    
    		printk("SAMPLERDY interrupt \n");
    		break;
    
    	case NRF_QDEC_EVENT_REPORTRDY:
    
    		// accumulate(&qdec_nrfx_data, event.data.report.acc);
    		printk("REPORTRDY event handled %d\n", event.data.report.acc);
    		break;
    	default:
    
    		printk("unhandled event (0x%x) \n", event.type);
    		break;
    	}
    }
    
    int main(void)
    {
    
    	// interrupt setup
    	IRQ_DIRECT_CONNECT(QDEC_IRQn, 0, nrfx_qdec_irq_handler, 0);
    	irq_enable(QDEC_IRQn);
    
    	nrfx_qdec_config_t qdec_config = NRFX_QDEC_DEFAULT_CONFIG(PIN_A_VALUE, PIN_B_VALUE, 0xFFFFFFFF);
    
    	// qdec init
    	nrfx_qdec_init(&qdec_config, qdec_nrfx_event_handler);
    	nrfx_qdec_enable();
    
    	while (true)
    	{
    		k_sleep(K_MSEC(100));
    	}
    
    	return 0;
    }
    

    The only doubts I have are about the NRFX_QDEC_DEFAULT_CONFIG() arguments, is the NRF_GPIO_PIN_MAP macro the correct way to set them? Also, given that I'm not using the Zephyr sensor module, do I have to set qdec node in the .dts file or not? I assume not.

    Thanks

  • My suggestion is to check out the zephyr driver for qdec, and modify or use it as guidance:
    \zephyr\drivers\sensor\qdec_nrfx\qdec_nrfx.c

    For testing also use it as-is just to confirm pins and hardware works as intended, even if the sampling frequency is wrong.

    I notice you are using IRQ_DIRECT_CONNECT(), there are some limitation using this handler, please use IRQ_CONNECT() instead.

    Kenneth

Related