• 正文
    • 復(fù)用GPIO
    • 驅(qū)動源碼myled.c編寫
    • 完整的驅(qū)動myled.c示例源碼
    • 編譯
    • 編寫測試應(yīng)用源碼led_app.c
    • 編譯應(yīng)用
    • 測試
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

飛凌嵌入式ElfBoard ELF 1板卡-Pinctrl和GPIO子系統(tǒng)之LED驅(qū)動

03/27 08:55
205
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動例程源碼4_Pinctrl和GPIO子系統(tǒng)myled

前面章節(jié)的驅(qū)動,沒有對實際的硬件進行操作,主要通過一些打印信了解了驅(qū)動的加載流程和調(diào)用流程。本章節(jié)開始,以實際操作硬件為例進行講解,操作之前需要先解壓一份從NXP官網(wǎng)下載的源碼,避免加載驅(qū)動時獲取不到硬件資源。如果有使用git進行管理源碼,只需要新建一個分支,然后切換到第一次提交的commit號即可。

首先使用GPIO子系統(tǒng)API函數(shù)編寫一個用戶層能夠控制LED_Y亮滅的驅(qū)動。

復(fù)用GPIO

(一)查看原理圖引腳復(fù)用表格,可以得到LED_Y由GPIO1_18控制,所以我們需要配置GPIO1_18引腳為輸出,而且能夠在用戶空間控制它輸出高電平還是低電平。

(二)在設(shè)備樹中的IOMUX中添加引腳的復(fù)用,設(shè)備樹路徑:linux-imx-imx_4.1.15_2.0.0_ga/arch/arm/boot/dts/imx6ull-elf1-emmc.dts,將其復(fù)用成GPIO功能,并檢查設(shè)備樹中是否有其它的地方也用到了此引腳,如果用到了就需要將其屏蔽掉,避免復(fù)用沖突。

pinctrl_hog_1: hoggrp-1 {

????????????????????????fsl,pins = <

????????????????????????????????MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 ??????0x17059 /* SD1 CD */

????????????????????????????????MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT ???0x17059 /* SD1 VSELECT */

????????????????????????????????MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 ???????0x17059 /* SD1 RESET */

????????????????????????????????MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 ??????0x17059 /*LED_Y*/

????????????????????????>;

????????????????};

添加后效果如下:

(三)編譯設(shè)備樹

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs

編譯生成的設(shè)備樹文件為imx6ull-elf1-emmc.dtb,參考《01-0 ELF1、ELF1S開發(fā)板_快速啟動手冊_V1》4.4節(jié)單獨更新設(shè)備樹。

驅(qū)動源碼myled.c編寫

(一)頭文件引用

#include <linux/module.h> ??????// 包含模塊相關(guān)函數(shù)的頭文件

#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關(guān)函數(shù)的頭文件

#include <linux/uaccess.h> ?????// 包含用戶空間數(shù)據(jù)訪問函數(shù)的頭文件

#include <linux/cdev.h> ????????//包含字符設(shè)備頭文件

#include <linux/device.h> ??????//包含設(shè)備節(jié)點相關(guān)的頭文件

#include <linux/gpio.h> ????????//包含gpio操作函數(shù)的相關(guān)頭文件

(二)創(chuàng)建相關(guān)宏定義

#define DEVICE_NAME "mydevice" ?// 設(shè)備名稱

#define LED_IOC_MAGIC 'k' ???????//定義ioctl幻數(shù)

#define SET_LED_ON _IO(LED_IOC_MAGIC, 0) ?//定義LED開命令

#define SET_LED_OFF _IO(LED_IOC_MAGIC, 1) ?//定義LED關(guān)命令

#define GPIO_LED_PIN_NUM 18 ???//定義操作的GPIO編號

(1)ioctl幻數(shù)

ioctl幻數(shù)是一個用于區(qū)分ioctl命令的標識符。它是一個唯一的整數(shù)值,并且通常使用ASCII字符來表示?;脭?shù)的目的是確保ioctl命令的唯一性,以免不同設(shè)備的命令發(fā)生沖突。

(2)_IO宏定義

_IO宏定義用于創(chuàng)建無參數(shù)的ioctl命令代碼。

_IO(LED_IOC_MAGIC, 0),LED_IOC_MAGIC表示定義的幻數(shù),0或者1表示具體的命令代碼。

(3)GPIO編號

在imx6ull上確定GPIO編號的公式為:GPIOn_IOx=(n-1)*32+x。

因為選擇的引腳為GPIO1_IO18,所以通過(n-1)*32+x計算得到的編號為18。

(三)相關(guān)變量定義

static dev_t dev_num; ??//分配的設(shè)備號

struct ?cdev my_cdev; ?????????//字符設(shè)備指針

int major; ?//主設(shè)備號

int minor; ?//次設(shè)備號

static struct class *my_led;

static struct device *my_device;

(四)mydevice_init(void)函數(shù)的實現(xiàn)

static int __init mydevice_init(void)

{

????int ret;

?????//釋放之前申請的GPIO,避免申請失敗

?????gpio_free(GPIO_LED_PIN_NUM);

????// 申請GPIO,并判斷是否申請成功

????????if (gpio_request(GPIO_LED_PIN_NUM, "led_run")) {

????????????????printk("request %s gpio faile n", "led_run");

?????????????????return -1;

?????????}

????// 注冊字符設(shè)備驅(qū)動程序

????ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

????if (ret < 0) {

????????printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

????????return ret;

}

???major=MAJOR(dev_num);

???minor=MINOR(dev_num);

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

?

my_cdev.owner = THIS_MODULE;

????????cdev_init(&my_cdev,&fops);

????????cdev_add(&my_cdev,dev_num,1);

?

???????// 創(chuàng)建設(shè)備類

????my_led = class_create(THIS_MODULE, "my_led");

????if (IS_ERR(my_led)) {

????????pr_err("Failed to create classn");

????????return PTR_ERR(my_led);

}

??// 創(chuàng)建設(shè)備節(jié)點并關(guān)聯(lián)到設(shè)備類

????my_device = device_create(my_led, NULL, MKDEV(major, minor), NULL, "my_device");

????if (IS_ERR(my_device)) {

????????pr_err("Failed to create devicen");

????????class_destroy(my_led);

????????return PTR_ERR(my_device);

????}

???????????printk(KERN_INFO "Device registered successfully.n");

????return 0;

}

主要添加了gpio_free()和gpio_request()函數(shù)的調(diào)用,gpio_free()函數(shù)原型如下:

void gpio_free(unsigned int gpio);

(1)參數(shù)說明

gpio:要釋放的GPIO引腳的編號。

(2)返回值

無返回值。

(3)函數(shù)功能

gpio_free()函數(shù)釋放先前請求的GPIO引腳,并將其解除分配。釋放后的GPIO引腳可以被其他驅(qū)動程序或設(shè)備重新請求使用。

gpio_request()函數(shù)原型如下:

int gpio_request(unsigned int gpio, const char *label);

(1)參數(shù)說明

gpio:要請求的GPIO引腳的編號。

label:用于標識該GPIO引腳的描述字符串。

(2)返回值

成功時,返回0表示請求成功。

失敗時,返回負數(shù)錯誤代碼,表示請求失敗。

(3)函數(shù)功能

gpio_request()函數(shù)請求一個GPIO引腳并進行相關(guān)的配置,以便將其用于后續(xù)的操作。

它會檢查GPIO引腳是否已經(jīng)被其他驅(qū)動程序或內(nèi)核使用,如果被占用則請求失敗。

請求成功后,該GPIO引腳將被鎖定,使其他驅(qū)動程序無法再次請求該引腳。

(五)mydevice_exit(void)函數(shù)的實現(xiàn)

static void __exit mydevice_exit(void)

{

// 在這里執(zhí)行驅(qū)動程序的清理操作

//釋放申請的GPIO資源

gpio_free(GPIO_LED_PIN_NUM);

// 銷毀設(shè)備節(jié)點

?device_destroy(my_led, MKDEV(major, minor));

// 銷毀設(shè)備類

?class_destroy(my_led);

// 刪除字符設(shè)備

?cdev_del(&my_cdev);

// 注銷字符設(shè)備驅(qū)動程序

?unregister_chrdev(0, DEVICE_NAME);

?

????printk(KERN_INFO "Device unregistered.n");

}

(六)定義fops結(jié)構(gòu)體

static struct file_operations fops = {

????.owner = THIS_MODULE,

????.open = device_open,

????.release = device_release,

????.unlocked_ioctl = myled_ioctl,

};

此處用到了unlocked_ioctl函數(shù),unlocked_ioctl函數(shù)是Linux內(nèi)核中用于設(shè)備控制和通信的重要函數(shù)之一。它允許用戶空間程序與設(shè)備驅(qū)動程序進行交互,發(fā)送特定的命令和參數(shù)來執(zhí)行設(shè)備相關(guān)的操作。

unlocked_ioctl()函數(shù)原型如下:

long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);

(1)參數(shù)說明

filp:指向struct file結(jié)構(gòu)的指針,表示要執(zhí)行ioctl操作的文件。

cmd:請求代碼,用于指定要執(zhí)行的具體操作。請求代碼通常是一個整數(shù),可以是預(yù)定義的常量或自定義的命令。

arg:參數(shù),根據(jù)具體的請求代碼而定。

(2)返回值

unlocked_ioctl函數(shù)返回一個long類型的值,通常用于表示操作的成功與否,返回值為負數(shù)表示出現(xiàn)錯誤。

(七)device_open()函數(shù)的實現(xiàn):

static int device_open(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備打開的操作

//設(shè)置GPIO引腳為輸出模式,并將其初始化為高電平

gpio_direction_output(GPIO_LED_PIN_NUM, 1);

printk(KERN_INFO "This is device_open.n");

????return 0;

}

gpio_direction_output()函數(shù)原型如下:

int gpio_direction_output(unsigned int gpio, int value);

(1)參數(shù)說明

gpio:要配置的GPIO引腳的編號。

value:設(shè)置GPIO引腳的初始輸出值,0表示低電平,非零表示高電平。

(2)返回值

成功時,返回0表示設(shè)置成功。

失敗時,返回負數(shù)錯誤代碼,表示設(shè)置失敗。

(3)函數(shù)功能

gpio_direction_output()函數(shù)將指定的GPIO引腳配置為輸出模式,并設(shè)置其初始輸出值。

在配置為輸出模式后,可以使用其他函數(shù)(如gpio_set_value())來更改GPIO引腳的輸出值。

(八)myled_ioctl()函數(shù)的實現(xiàn)

static long myled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

?

????switch (cmd) {

????????????case SET_LED_ON:

????????????// 設(shè)置GPIO引腳為低電平

????????????gpio_set_value(GPIO_LED_PIN_NUM, 0);

????????????break;

?

????????case SET_LED_OFF:

??????????//設(shè)置GPIO引腳為高電平

??????????gpio_set_value(GPIO_LED_PIN_NUM, 1);

????????????break;

?

????????default:

????????????return -ENOTTY;

????}

?

????return 0;

}

gpio_set_value()函數(shù)原型如下:

void gpio_set_value(unsigned int gpio, int value);

(1)參數(shù)說明

gpio:要設(shè)置的GPIO引腳的編號。

value:要設(shè)置的輸出值,0表示低電平,非零表示高電平。

(2)返回值

無返回值。

(3)函數(shù)功能

gpio_set_value()函數(shù)用于設(shè)置指定GPIO引腳的輸出值。該函數(shù)會將GPIO引腳配置為輸出模式(如果尚未配置),并設(shè)置其輸出值為指定的值。

(九)device_release()函數(shù)的實現(xiàn):

static int device_release(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備關(guān)閉的操作

//設(shè)置GPIO引腳為輸出模式,并將置為高電平

gpio_direction_output(GPIO_LED_PIN_NUM, 1);

printk(KERN_INFO "This is device_release.n");

?

????return 0;

}

完整的驅(qū)動myled.c示例源碼

#include <linux/module.h> ??????// 包含模塊相關(guān)函數(shù)的頭文件

#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關(guān)函數(shù)的頭文件

#include <linux/uaccess.h> ?????// 包含用戶空間數(shù)據(jù)訪問函數(shù)的頭文件

#include <linux/cdev.h> ????????//包含字符設(shè)備頭文件

#include <linux/device.h>

#include <linux/gpio.h>

?

#define DEVICE_NAME "mydevice" ?// 設(shè)備名稱

#define LED_IOC_MAGIC 'k'

#define SET_LED_ON _IO(LED_IOC_MAGIC, 0)

#define SET_LED_OFF _IO(LED_IOC_MAGIC, 1)

#define GPIO_LED_PIN_NUM 18

static dev_t dev_num; ??//分配的設(shè)備號

struct ?cdev my_cdev; ?????????//字符設(shè)備指針

int major; ?//主設(shè)備號

int minor; ?//次設(shè)備號

static struct class *my_led;

static struct device *my_device;

?

?

?

?

?

static int device_open(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備打開的操作

//設(shè)置GPIO引腳為輸出模式,并將其初始化為高電平

gpio_direction_output(GPIO_LED_PIN_NUM, 1);

printk(KERN_INFO "This is device_open.n");

????return 0;

}

?

static int device_release(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備關(guān)閉的操作

gpio_direction_output(GPIO_LED_PIN_NUM, 1);

printk(KERN_INFO "This is device_release.n");

?

????return 0;

}

?

static long myled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

?

????switch (cmd) {

????????????case SET_LED_ON:

????????????// 設(shè)置GPIO引腳為低電平

????????????gpio_set_value(GPIO_LED_PIN_NUM, 0);

????????????break;

?

????????case SET_LED_OFF:

??????????//設(shè)置GPIO引腳為高電平

??????????gpio_set_value(GPIO_LED_PIN_NUM, 1);

????????????break;

?

????????default:

????????????return -ENOTTY;

????}

?

????return 0;

}

?

static struct file_operations fops = {

????.owner = THIS_MODULE,

????.open = device_open,

????.release = device_release,

????.unlocked_ioctl = myled_ioctl,

};

static int __init mydevice_init(void)

{

????int ret;

?

// 在這里執(zhí)行驅(qū)動程序的初始化操作

//釋放之前申請的GPIO,避免申請失敗

????gpio_free(GPIO_LED_PIN_NUM);

????????if (gpio_request(GPIO_LED_PIN_NUM, "led_run")) {

????????????????printk("request %s gpio faile n", "led_run");

?????????????????return -1;

?????????}

????// 注冊字符設(shè)備驅(qū)動程序

????ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

????if (ret < 0) {

????????printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

????????return ret;

}

???major=MAJOR(dev_num);

???minor=MINOR(dev_num);

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

?

my_cdev.owner = THIS_MODULE;

????????cdev_init(&my_cdev,&fops);

????????cdev_add(&my_cdev,dev_num,1);

?

???????// 創(chuàng)建設(shè)備類

????my_led = class_create(THIS_MODULE, "my_led");

????if (IS_ERR(my_led)) {

????????pr_err("Failed to create classn");

????????return PTR_ERR(my_led);

}

??// 創(chuàng)建設(shè)備節(jié)點并關(guān)聯(lián)到設(shè)備類

????my_device = device_create(my_led, NULL, MKDEV(major, minor), NULL, "my_device");

????if (IS_ERR(my_device)) {

????????pr_err("Failed to create devicen");

????????class_destroy(my_led);

????????return PTR_ERR(my_device);

????}

???????????printk(KERN_INFO "Device registered successfully.n");

????return 0;

}

static void __exit mydevice_exit(void)

{

// 在這里執(zhí)行驅(qū)動程序的清理操作

//釋放申請的GPIO資源

gpio_free(GPIO_LED_PIN_NUM);

// 銷毀設(shè)備節(jié)點

?device_destroy(my_led, MKDEV(major, minor));

// 銷毀設(shè)備類

?class_destroy(my_led);

// 刪除字符設(shè)備

????cdev_del(&my_cdev);

????// 注銷字符設(shè)備驅(qū)動程序

????unregister_chrdev(0, DEVICE_NAME);

?

????printk(KERN_INFO "Device unregistered.n");

}

?

module_init(mydevice_init);

module_exit(mydevice_exit);

?

MODULE_LICENSE("GPL"); ?????// 指定模塊的許可證信息

MODULE_AUTHOR("Your Name"); // 指定模塊的作者信息

MODULE_DESCRIPTION("A simple character device driver"); // 指定模塊的描述信息

編譯

復(fù)制7.4.3驅(qū)動中的Makefile文件,將其中的copy_form_user.o修改為myled.o,效果如下:

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/04_Pinctrl和GPIO子系統(tǒng)/myled$ make

編譯成ko模塊并拷貝到開發(fā)板中。

編寫測試應(yīng)用源碼led_app.c

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <errno.h>

#include <fcntl.h>

?

#define DEV_NAME "/dev/my_device"

#define LED_IOC_MAGIC 'k'

#define SET_LED_ON _IO(LED_IOC_MAGIC, 0)

#define SET_LED_OFF _IO(LED_IOC_MAGIC, 1)

?

int main(int argc, char *argv[])

{

????????int i;

????????int fd = 0;

????????fd = open (DEV_NAME, O_RDONLY);

????????if (fd < 0) {

????????????????perror("Open "DEV_NAME" Failed!n");

?????????exit(1);

}

?

????????for (i=0; i<5; i++)

????????{

????????????????ioctl(fd, SET_LED_ON);

????????????????sleep(1);

????????????????ioctl(fd, SET_LED_OFF);

????????????????sleep(1);

????????}

?

????????close(fd);

????????return 0;

}

編譯應(yīng)用

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/04_Pinctrl和GPIO子系統(tǒng)/led_app$ $CC led_app.c -o led_app

將編譯生成的應(yīng)用拷貝到開發(fā)板中。

測試

root@ELF1:~# insmod myled.ko

major number: 245

minor number: 0

Device registered successfully.

root@ELF1:~#?./led_app

This is device_open.

This is device_release.

root@ELF1:~# rmmod myled.ko

執(zhí)行l(wèi)ed_app后,黃色LED燈循環(huán)閃爍5次。

相關(guān)推薦