Use Rotary Encoder module on DEBIX

2024.4.7by debix.io

Overview

  • The rotary encoder module is also known as the HW-40 encoder module.
  • The rotary encoder rotates at an angle of 360°, and the module comes with a keypad which is integrated into the encoder.


Hardware installation

  • Rotary Encoder module's VCC terminal is connected to J2 Pin2 of DEBIX Model A I/O board
  • Rotary Encoder module's CLK terminal is connected to J2 Pin33 of DEBIX Model A I/O board
  • Rotary Encoder module's DT terminal is connected to J2 Pin31 of DEBIX Model A I/O board
  • Rotary Encoder module's SW terminal is connected to J2 Pin32 of DEBIX Model A I/O board
  • Rotary Encoder module's GND terminal is connected to J2 Pin39 of DEBIX Model A I/O board


Modify the device tree imx8mp-evk.dts

1. Add a sub node under / node:

/ {

    Encoder: Encoder {

         compatible = "Encoder";

         pinctrl-0  = <&pinctrl_Encoder>;

         SW-gpios   = <&gpio1 13 GPIO_ACTIVE_LOW>;

         CLK-gpios   = <&gpio5 3 GPIO_ACTIVE_LOW>;  

         DT-gpios   = <&gpio5 4 GPIO_ACTIVE_LOW>;

         status = "okay";

     };

};


2. Add child nodes under iomuxc node:

pinctrl_Encoder: Encodergroup{

            fsl,pins = <

                  MX8MP_IOMUXC_SPDIF_RX__GPIO5_IO04  0x40000


                  MX8MP_IOMUXC_SPDIF_TX__GPIO5_IO03  0x40000

                  MX8MP_IOMUXC_GPIO1_IO13__GPIO1_IO13  0x40000

            >;

     };


3. Compile and replace the device tree file

  • Compile the device tree in the source directory: make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- dtbs
  • Copy the compiled new device tree file arch/arm64/boot/dts/freescale/imx8mp-evk.dtb to the /boot directory of DEBIX and restart DEBIX.

4. Check whether dts contains the modified device tree node.

  • Run the command ls /sys/firmware/devicetree/base/Encoder

  • Run the command cat /sys/firmware/devicetree/base/hc_sr04/compatible

Write and Load Drivers

The driver code is as follows:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/miscdevice.h>

#include <linux/fs.h>

#include <linux/platform_device.h>

#include <linux/device.h>

#include <linux/err.h>

#include <linux/of.h>

#include <linux/of_device.h>

#include <linux/gpio.h>

#include <linux/of_gpio.h>

#include <linux/delay.h>

#include <linux/irq.h>

#include <linux/interrupt.h>

struct device_node *Encoder_device_node;

struct property *Encoder_node_property;

int Clk_gpio_index=0;

int DT_gpio_index=0;

int SW_gpio_index=0;

int CLK_irq;

int SW_irq;

irqreturn_t Encoder_CLK_irq(int irq, void *args){

static u32 count;

u16 direction;

if(gpio_get_value(Clk_gpio_index) == 0) {

if(gpio_get_value(DT_gpio_index) == 1){

count++;

direction=1;

}

if(gpio_get_value(DT_gpio_index) == 0){

count--;

direction=0;

}

printk("Encoder: count=%d,diretion=%d..\n",count,direction);

}

return IRQ_RETVAL(IRQ_HANDLED);

}

irqreturn_t Encoder_SW_irq(int irq, void *args){

if(gpio_get_value(SW_gpio_index) == 0) {

printk("Encoder: key pressed..\n");

}

return IRQ_RETVAL(IRQ_HANDLED);

}

int Encoder_probe(struct platform_device *pd){

int ret;

Encoder_device_node = of_find_node_by_path("/Encoder");

if(Encoder_device_node == NULL){

printk("of_find_node_by_path err..\n");

return -1;

}

// MX8MP_IOMUXC_SPDIF_RX__GPIO5_IO04 DT

// MX8MP_IOMUXC_SPDIF_TX__GPIO5_IO03 Clk

// MX8MP_IOMUXC_SPDIF_TX__GPIO1_IO013 SW

Clk_gpio_index = of_get_named_gpio(Encoder_device_node,"CLK-gpios",0);

if(Clk_gpio_index<0){

printk("of_get_name_gpio Clk err..\n");

return -1;

}

DT_gpio_index = of_get_named_gpio(Encoder_device_node,"DT-gpios",0);

if(DT_gpio_index<0){

printk("of_get_name_gpio DT err..\n");

return -1;

}

SW_gpio_index = of_get_named_gpio(Encoder_device_node,"SW-gpios",0);

if(SW_gpio_index<0){

printk("of_get_name_gpio SW err..\n");

return -1;

}

ret = gpio_request(Clk_gpio_index, "Clk");

if (ret<0){

printk("gpio_request Clk is error \n");

return -1;

}

ret = gpio_request(DT_gpio_index, "DT");

if (ret<0){

printk("gpio_request DT is error \n");

return -1;

}

ret = gpio_request(SW_gpio_index, "SW");

if (ret<0){

printk("gpio_request SW is error \n");

return -1;

}

gpio_direction_input(Clk_gpio_index);

gpio_direction_input(DT_gpio_index);

gpio_direction_input(SW_gpio_index);

CLK_irq = gpio_to_irq(Clk_gpio_index);

SW_irq = gpio_to_irq(SW_gpio_index);

printk("CLK_irq is %d \n", CLK_irq);

printk("SW_irq is %d \n", SW_irq);

ret = request_irq(CLK_irq, Encoder_CLK_irq, IRQF_TRIGGER_FALLING, "CLK_irq", NULL);

if (ret < 0){

printk("request CLK_irq is error \n");

return -1;

}

ret = request_irq(SW_irq, Encoder_SW_irq, IRQF_TRIGGER_FALLING, "SW_irq", NULL);

if (ret < 0){

printk("request CLK_irq is error \n");

return -1;

}

printk("Encoder_probe..\n");

return 0;

}

int Encoder_remove(struct platform_device *pd){

gpio_free(Clk_gpio_index);

gpio_free(DT_gpio_index);

gpio_free(SW_gpio_index);

free_irq(CLK_irq, NULL);

free_irq(SW_irq, NULL);

printk("Encoder_remove..\n");

return 0;

}

static const struct platform_device_id Encoder_idtable[] = {

{.name = "Encoder",},

{ /* sentinel */ }

};

static const struct of_device_id Encoder_data_dt_ids[] = {

{ .compatible = "Encoder"},

{ /* sentinel */ }

};

static struct platform_driver Encoder_driver = {

.driver = {

.owner = THIS_MODULE,

.name = "Encoder",

.of_match_table = of_match_ptr(Encoder_data_dt_ids),

},

.probe = Encoder_probe,

.remove = Encoder_remove,

.id_table = Encoder_idtable,

};

module_platform_driver(Encoder_driver);

MODULE_LICENSE("GPL");


1. Run the above command to compile the driver make will get the .ko file

2. Copy the .ko file to DEBIX, run the command sudo insmod Encoder.ko

3. Run the command sudo dmesg | tail -3 , check the driver print information:

4. You can see that the probe function matches the device tree successfully.


Test result

Modify the print level of kernel messages so that the terminal prints all messages:

echo 7 4 1 7 > /proc/sys/kernel/printk

  • Press the key to see:

  • Positively rotate the encoder, and see:

  • Reverse rotary encoder, and see: