openharmony GPIO driver development


GPIO basics

GPIO Basics - Concepts

GPIO: Input or output high and low levels, any combination of high and low levels and waveforms, without any protocol requirements, can drive LEDs, buttons and other peripherals Dedicated IO: IO with protocol constraints, high and low levels of input and output The number, combination of waveforms, and duration of waveforms follow the corresponding protocols, such as I2C, SPI, UART, PWM

  • PWM
    insert image description here
  • 2c
    insert image description here

GPIO basics - IO multiplexing

The chip should provide as many functions and external interfaces as possible, but the number of pins (Pin) of the chip is limited, and many IO pins are used to have multiple functions, and the time-division multiplexing of the same pin is realized through software configuration. Taking HI3516DV300 as an example, there are 92 GPIOs in total, and the multiplexing relationship of GPIO3_6 is as follows:

insert image description here
insert image description here

Not all IO pins can be used as GPIOI, some can only be used as dedicated IO, such as external memory chips, and some pins can only be used as GPIO.

GPIO Basics - GPIO Grouping and Numbering

A large number of GPIOs are managed by grouping, so each GPIO has a group number and group number (group offset, offset). Different chips have different definitions of the number of GPIO groups and the number of GPIO pins in a group.

For example: RK3399/RK3399Pro provides 5 groups of GPIO (GPIO0~GPIO4) totaling 122, all GPIO can be used as interrupt, GPIO0/GPIO1 can be used as system wake-up pin, all GPIO can be configured as pull-up or pull-down by software, all GPIO is input by default, and the driving capability of GPIO can be configured by software. Regarding the corresponding relationship between the GPIO on the schematic diagram and the GPIO in the dts, such as GPIO4c0, the corresponding dts should be "gpio4 16". Because GPIO4A has 8 pins, and GPIO4B also has 8 pins, the c0 port is 16, the c1 port is 17, and so on; for the use of GPIO, please refer to "Rockchip Pin" in the docs\Kernel\Pin-Ctrl\ directory -Ctrl Development Guide V1.0-20160725.pdf".

GPIO basics - user mode test

  • Determine GPIO pin number and level status
  • Multiplex the pin as GPIO function (default is GPIO after reset)
  • For example, the GPIO3_6 pin is calculated as GPIO30, you can execute echo 30 > export
  • At this time, a gpio30 directory will be added under gpio, and operations can be performed in this directory to control the GPIO30 pins, such as direction (in, out), level value (1/0)

GPIO driver under HDF framework

GPIO driver under HDF framework - case description (take HI3516DV300 platform as an example, provide code)

  • GPIO0_6 external LED, output low level to light up the LED, high level to turn off the LED
  • GPIO3_6 is connected to an external KEY, configured as an interrupt, and the trigger mode is double-edge trigger
  • The user mode program sends instructions to the driver to realize the operation of turning on and off the LED, and the driver program returns the level status of the corresponding pin of the LED to the user mode. The driver program realizes the data interaction with the application program through formal parameters and events.
  • The key triggers an external interrupt, and the interrupt service routine can turn on or off the LED

GPIO driver under HDF framework - application binding service

  • The application program under the Linux system opens the device node in the /dev/ directory through the open system call, obtains the device file handle, and calls the read/write/ioctl and other system call interfaces through this file handle to realize the operation of the device
  • Under the HDF framework, the user mode application calls a specific interface to obtain the service provided by the driver, and realizes the binding of the application and the driver. After the application obtains the service, it can realize the operation of the driver and the device based on the service
//应用程序绑定服务
struct HdfIoService *serv = HdfIoServiceBind("GPIO_TEST_SERVICE");
if(serv == NULL){
    HDF_LOGE("fail to get service %s", "GPIO_TEST_SERVICE");
    return HDF_FAILURE;
}

gpio_drv_test_host::host{
    hostName = "gpio_drv_test";
    priority = 100;
    device_test_driver::device{
        device0::deviceNode{
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "GPIO_TEST_DRIVER";
            serviceName = "GPIO_TEST_SERVICE";
        }
    }
}

GPIO driver under the HDF framework - user mode HdfSBuf

  • After the application obtains the driver service, it can use the service to communicate with the driver. The carrier of communication data is HdfSBuf, and the application can call HdfSBufObtainDefaultSize to obtain a memory heap space with a default size of 256 bytes. HDF organizes this memory space as a circular queue. The application writes data to this queue, and the driver can read data from the queue, and vice versa. Since it is a circular queue, it is necessary to ensure that the order of the read data, the type of the read data is consistent with the written data, and follow the principle of first in first out.
//用户态申请空间
struct HdfSBuf *data = HdfSBufObtainDefaultSize();
if(data == NULL){
    printf("fail to obtain sbuf data\n");
    ret = HDF_DEV_ERR_NO_MEMORY;
    goto out;
}

//用户态写数据
if(!HdfSbufWriteString(data, eventData)){
    printf("fail to write data\n");
    goto HDF_FAILURE;
}

//用户态读数据
char *replyData = HdfSbufReadString(data);
if(replyData == NULL){
    printf("fail to read data\n");
    goto HDF_FAILURE;
}

GPIO driver under HDF framework - application and driver communication 1

  • The application applies for two buffers (circular queues) for data interaction with the driver
  • The application program writes String type data to the data buffer
  • The application sends data to the driver through the service's Dispatch function, causing the driver's Dispatch function to be executed
  • The user program reads the data returned by the driver: first obtain the String type, and then obtain the uint16 type
struct HdfSBuf  *data = HdfSBufObtainDefaultSize();
struct HdfSBuf *reply = HdfSBufObtainDefaultSize();

if(id == LED_ON){
    SbufWriteString(data, eventData);
    ret = ser->dispatcher->Dispatch(&serv->object, LED_ON, data, reply);
    string = HdfSbufReadString(data)l;
    HdfSbufReadUint16(reply, &pin_val);
}

GPIO driver under the HDF driver framework - application and driver communication 2

The application registers an event listener through the obtained service. When the driver calls the event sending function HdfDeviceSendEvent, it will trigger the execution of the callback function of the event listener, and receive the data sent by the driver in the callback function.

static struct HdfDevEventlistener listener = {
    .callBack = OnDevEventReceived,
    .priv = "Service0"
};

//应用程序注册服务
if(HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCESS){
    HDF_LOGE("fail to register event listener");
    return HDF_FAILURE;
}
  • The driver calls the event sending interface HdfDeviceSendEvent to send events to the user mode program, triggering the execution of the user mode event listener
  • The application program reads the data in the order the driver writes the buffer: first read the string type data; then read the uint16 type data
static int32_t OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *reply)
{
    uint16_t pin_val = 0;
    
    const char *string  = HdfSbufReadString(reply);
    if(string == NULL){
        printf("fail to read string in event reply\n");
        return HDF_FAILURE;
    }

    if(!HdfSbufReadUint16(reply, &pin_val){
        printf("fail to read uint16 in event reply\n");
        return HDF_FAILURE;
    }

    printf("%s: event reply received : id %u, string %s, pin val %u\n", (char *)priv, id, string, pin_val);
    return HDF_SUCCESS;
}

GPIO driver under HDF framework - driver entry

  • The driver entry g_GPIODriverEntry defines three functions and the driver module name moduleName;
  • A new node gpio_drv_test_host is added in device_info.hcs, which contains an attribute named moduleName;
  • When the values ​​of the two moduleNames are equal, it means that hcs and the driver match successfully, and then call the Bind function and Init function of the driver entry.

This process is similar to the compatible field in dts and Linux drivers, when the two match, call the probe function in the driver

struct HdfDriverEntry g_GPIODriverEntry = {
    .moduleVersion = 1,
    .moduleName = "GPIO_TEST_DRIVER",
    .Bind = HdfGPIODriverBind,
    .Init = HdfGPIODriverInit,
    .Release = HdfGPIODriverRelease,
}

HDF_INIT(g_GPIODriverEntry);

//device_info.hcs
gpio_drv_test_host :: host{
    hostName = "gpio_drv_test";
    priority = 100;
    device_test_driver :: device{
        device0 :: deviceNode{
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "GPIO_TEST_DRIVER";
            serviceName = "GPIO_TEST_SERVICE";
        }
    }
}
int32_t HdfGPIODriverBind(strutc HdfDeviceObject *deviceObject)
{
    if(deviceObject == NULL){
        return HDF_FAILURE;
    }
    static struct IDeviceIoService gpioTestService = {
        .Dispatch = HdfGPIODriverDispatch,
    };
    
    deviceObject->service = &gpioTestService;
    HDF_LOGE("GPIO driver bind success");
    return HDF_SUCCESS;
}
* Bind 函数中定义了一个结构体,它是一个名为 gpioTestService 的服务,需要实现 Dispatch 成员函数,该函数用来接收用户态程序发送到内核态的消息,实现用户态和内核态之间的通信。
* 驱动中定义了一个服务,即驱动可以对外提供服务,应用程序可以使用该服务。

GPIO driver under the HDF framework - the driver receives and sends data

  • The application program first obtains the service, calls the Dispatch interface defined in the service to trigger the execution of the function, and distinguishes instructions from the user program through the parameter id

  • Read the string type data sent by the user state, and perform the operation of turning on or off the LED

  • Return a string and the level status of the LED corresponding pin to the user program. The user mode program should first read the first string type data, and then read the uint16 type data, following the FIFO principle

  • Note the two ways the driver returns data to the user program:

      * 返回值和发送事件,针对不同的方式
      * 用户态获取驱动数据的方式也不同
    
int32_tHdfGPIODriverDispatch(struct HdfDeviceIoClient *client, int32_t id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    //驱动程序通过接口读取来自用户态的数据,并向用户态返回数据

    if(id == LED_ON){
        const char *readData = HdfSbufReadString(data);
        if(readData != NULL){
            HDF_LOGE("%s: read data is : %s", __func__, readData);
            led_on();
            GpioRead(LED_PIN, &led_pin_val);
        }
        HdfSbufWriteString(reply, "ledon");
        HdfSbufWriteUint16(reply, led_pin_val);
    }
    else if(id == LED_OFF){
        const char *readData = HdfSbufReadString(data);
        if(readData != NULL){
            HDF_LOGE("%s: read data is :%s", __func__, readData);
            led_off();
            GpioRead(LED_PIN, &led_pin_val);
        }
        HdfSbufWriteString(reply, "ledoff");
        HdfSbufWriteUint16(reply, led_pin_val);
        if(HdfDeviceSend(client->device, id, reply) != HDF_SUCCESS)
            return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

GPIO driver under HDF framework - GPIO configuration

Functional Classification interface name describe
GPIO read and write GpioRead Read pin level value
GpioWrite Write pin level value
GPIO configuration GpioSetDir Set pin direction
GpioGetDir Get pin direction
GPIO interrupt settings GpioSetIrq Set the interrupt service function corresponding to the pin
GpioUnSetIrq Cancel the interrupt service function corresponding to the pin
GpioEnableIrq enable pin interrupt
GpioDisableIrq disable pin interrupt
#define LED_PIN    6// GPIO0_6,    0*8+6 = 6
#define IRQ_PIN    30//GPIO3_6    3*8+6 = 30

static int32_t GpioSetup()
{
    //驱动程序,配置 GPIO
    if(GpioSetDir(LED_PIN, GPIO_DIR_OUT) != HDF_SUCCESS){
        HDF_LOGE("GPIOsetDir: LED_PIN failed\n");
        return HDF_FAILURE;
    }

    GpioSetDir(IRQ_PIN, GPIO_DIR_IN);
    GpioDisableIrq(IRQ_PIN);
    GpioSetIrq(IRQ_PIN, OSAL_IRQF_IRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING, gpio_test_irq, NULL);
    GpioEnableIrq(IRQ_PIN);
}

Where to call GpioSetup, Init() or Dispatch?

GPIO driver under HDF framework - GPIO configuration (interrupt)

  • Interrupt trigger mode
parameter Interrupt trigger mode
OSAL_IRQF_TRIGGER_RISING Rising edge trigger
OSAL_IRQF_TRIGGER_FALLING Falling edge trigger
OSAL_IRQF_TRIGGER_HIGH High level trigger
OSAL_IRQF_TRIGGER_LOW Low level trigger

insert image description here

int32_t gpio_testr_irq(uint16_t gpio, void *data)
{
    //驱动,中断服务程序
    if(GpioDisableIrq(gpio) != HDF_SUCCESS){
        HDF_LOGE("%s: disable irq failed", __func__);
        return HDF_FAILURE;
    }

    GpioRead(IRQ_PIN, &irq_pin_val);
    if(irq_pin_val == 0)
        led_off();
    else
        led_on();

    GpioEnableIrq(gpio);
}

GPIO driver under HDF framework - anti-shake and floating

Interrupt jitter is often caused by using buttons as GPIO interrupt trigger sources. Due to the mechanical nature of buttons, it is difficult to fundamentally eliminate the jitter. It is necessary to shield the impact of jitter. This technology is called anti-shake:

  • Hardware: A platform supports GPIO deburring, configurable interrupt trigger level and other technologies
  • Software: Read the level value of the interrupt pin multiple times in the interrupt service routine until the level is stable

The state when the GPIO pin is neither pulled high nor low externally is called the floating state. The GPIO in the floating state is unstable. When the program reads the corresponding value of the GPIO, frequent high and low jumps may occur. If the floating pin is used as an external interrupt, the interrupt will be triggered frequently. To avoid this situation:

  • GPIO external circuit is clearly connected to GND or VCC
  • Use pull-up or pull-down resistors

insert image description here

GPIO driver under HDF framework - LED control

#define LED_PIN    6// GPIO0_6,    0*8+6 = 6
#define IRQ_PIN    30//GPIO3_6    3*8+6 = 30

//高电平熄灭LED
static int32_t led_off(void)
{
    if(GpioWrite(LED_PIN, 1) != HDF_SUCCESS){
        HDF_LOGE("GpioWrite: LED_PIN failed\n");
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

//低电平点亮 LED
static int32_t led_on(void)
{
    if(GpioWrite(LED_PIN, 0) != HDF_SUCCESS){
        HDF_LOGE("GpioWrite: LED_PIN failed\n");
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

GPIO driver under HDF framework - driver directory and structure

  • Driver source directory:drivers/adapter/khdf/linux/gpio_test_drv/
  • Add the compilation target to the Makefile of the previous directory:drivers/adapter/khdf/linux/Makefile
obj-$(CONFIG_DRIVERS_HDF)    += gpio_test_drv/
  • Add a device node definition to the hcs configuration file:vendor/hisilicon/Hi3516DV300/hdf_config/khdf/device_info/device_info.hcs

GPIO driver under HDF framework - application directory structure

  • Create a subdirectory examples/gpio_test_app/ under the root directory of the openharmony source code, where examples is a subsystem and gpio_test_app is a component of the subsystem
  • Create application source and build files in the above directory
  • Add the examples subsystem and gpio_test_app component in the product definition file productdefine/common/products/Hi3516DV300.json to make it compiled
  • Clear the out directory, compile all the code, compile the driver into the kernel, and test the program gpio_test_app in the bin directory

Summarize

  • GPIO: the difference between general-purpose and dedicated IO, the grouping and numbering of GPIO under different platforms, and common debugging methods for GPIO
  • HDF driver: GPIO interface configuration, read and write operations, interrupts, two ways to realize the communication between the application and the driver, the basic operation of the buffer, basically covering all GPIO interfaces
  • Provide a complete set of drivers and applications, and give their directory structure

reference link

openharmony official website: https://www.openharmony.cn/mainPlay
openharmony official video link: https://www.bilibili.com/video/BV1z34y1t76h/

Guess you like

Origin blog.csdn.net/qq_37596943/article/details/128534964