14 LED驅動

1. iTOP4412 LED硬件原理

LED2的控制腳爲KP_COL0,對應SOC GPL2_0,對應虛擬地址宏爲:EXYNOS4_GPL2(0)

LED3的控制腳爲VDD50_EN,對應GPK1_1,對應虛擬地址宏爲:EXYNOS4_GPK1(1)

2. LED管腳的調用、賦值以及配置

//(1)驅動模塊所需頭文件
#include <linux/init.h>
#include <linux/module.h>

/*驅動註冊的頭文件,包含驅動的結構體和註冊和卸載的函數*/
#include <linux/platform_device.h>

/*註冊雜項設備頭文件*/
#include <linux/miscdevice.h>

/*註冊設備節點的文件結構體*/
#include <linux/fs.h>

//(2)linux與三星的GPIO相關頭文件
#include <linux/gpio.h>
/*
Linux中申請GPIO的頭文件
位置:include/linux/gpio.h
gpio_request :linux GPIO申請函數
gpio_set_value:linux GPIO賦值函數
*/

#include <plat/gpio-cfg.h>
/*
三星平臺的GPIO配置函數頭文件,包括三星所有處理器的配置函數
位置:arch/arm/plat-samsung/include/plat/gpio-cfg.h
s3c_gpio_cfgpin:三星平臺GPIO配置函數,比如設置IO口爲輸入、輸出或者三態模式。
S3C_GPIO_OUTPUT:將IO腳設置爲輸出模式的宏變量:#define S3C_GPIO_OUTPUT	(S3C_GPIO_SPECIAL(1))
*/

#include <mach/gpio.h>
/*
三星EXYNOS系列平臺,GPIO配置參數宏定義頭文件
位置:arch/arm/mach-exynos/include/mach/gpio.h
GPIO管腳拉高拉低配置參數等等,包含gpio-exynos4.h。
EXYNOS4_GPL2(0):IO腳虛擬地址宏定義
*/

3. 代碼設計

3.1 內核驅動程序設計

代碼設計思路:

 前提:hello_ctl爲第03章Menuconfig_Kconfig創建的設備節點,在系統開機時自動完成初始化。

3.1.1 新建驅動結構體 led_driver

主要工作是成員函數led_probe的設計;
struct platform_driver led_driver = {
    .probe = led_probe,
    .remove = led_remove,
    .shutdown = led_shutdown,
    .suspend = led_suspend,
    .resume = led_resume,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
    }
};

3.1.2 probe需要做什麼?

(1)LED控制端口GPL2(0)的初始化,設置爲輸出端口,輸出值默認爲0;
向內核申請GPIO資源:
gpio_request(EXYNOS4_GPL2(0),"LEDS");
將GPL2(0)設置爲輸出口:
s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT);
將GPL2(0)的輸出值設置爲0:
gpio_set_value(EXYNOS4_GPL2(0),0);    

注意在模塊卸載時需要釋放gpio:gpio_free(EXYNOS4_GPL2(0));

(2)將設備節點hello_ctl對應設備設置爲雜項設備,主設備號爲10,次設備號自動獲取。
新建雜項設備led_dev,設備名稱DEVICE_NAME=hello_ctl:
static  struct miscdevice led_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &led_ops,
};
註冊雜項設備led_dev:
misc_register(&led_dev);

3.1.3 新建文件結構體led_ops

用於內核層與用戶層的命令交互,將用戶層的ioctl映射爲驅動程序led_ioctl,
led_ioctl用作IO端口GPL2(0)的輸出值控制。
static struct file_operations led_ops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_release,
    .unlocked_ioctl = led_ioctl,
};

3.1.4 驅動模塊調試信息

(1)加載模塊

[root@iTOP-4412]# insmod led_ctrl.ko
[ 7852.565424]  start led init!
[ 7852.566895]  led initialized
[ 7852.578159]  led init OK,DriverState is 0!

(2)查看模塊

[root@iTOP-4412]# lsmod
led_ctrl 2158 0 - Live 0xbf00c000

(3)查看設備節點

(4)卸載模塊

[root@iTOP-4412]# rmmod led_ctrl
[ 9016.412424] led module exit!
[ 9016.413922]  led removed!

卸載後hello_ctl節點消失:

3.1.5 驅動代碼

led_ctl.c

#include <linux/init.h>
#include <linux/module.h>

/*驅動註冊的頭文件,包含驅動的結構體和註冊和卸載的函數*/
#include <linux/platform_device.h>
/*註冊雜項設備頭文件*/
#include <linux/miscdevice.h>
/*註冊設備節點的文件結構體*/
#include <linux/fs.h>

/*Linux中申請GPIO的頭文件*/
#include <linux/gpio.h>
/*三星平臺的GPIO配置函數頭文件*/
/*三星平臺EXYNOS系列平臺,GPIO配置參數宏定義頭文件*/
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>

#define DRIVER_NAME "hello_ctl"
#define DEVICE_NAME "hello_ctl"

static long led_ioctl( struct file *files, unsigned int cmd, unsigned long arg){
	printk("cmd is %d,arg is %d\n",cmd,arg);
	if(cmd > 1){
		printk(KERN_EMERG "cmd is 0 or 1\n");
	}
	if(arg > 1){
		printk(KERN_EMERG "arg is only 1\n");
	}
	gpio_set_value(EXYNOS4_GPL2(0),cmd);
	return 0;
}

static int led_release(struct inode *inode, struct file *file){
	printk(KERN_EMERG "led release\n");
	return 0;
}

static int led_open(struct inode *inode, struct file *file){
	printk(KERN_EMERG "led open\n");
	return 0;
}

static struct file_operations led_ops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_release,
	.unlocked_ioctl = led_ioctl,
};

static  struct miscdevice led_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &led_ops,
};

static int led_probe(struct platform_device *pdv){
	int ret;	
	printk(KERN_EMERG "\tled initialized\n");	
	ret = gpio_request(EXYNOS4_GPL2(0),"LEDS");
	if(ret < 0){
		printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed!\n");
		return ret;
	}	
	s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT);	
	gpio_set_value(EXYNOS4_GPL2(0),0);	
	misc_register(&led_dev);	
	return 0;
}

static int led_remove(struct platform_device *pdv){
	printk(KERN_EMERG "\tled removed!\n");
	misc_deregister(&led_dev);
	return 0;
}

static void led_shutdown(struct platform_device *pdv){
	;
}

static int led_suspend(struct platform_device *pdv,pm_message_t pmt){
	return 0;
}

static int led_resume(struct platform_device *pdv){
	return 0;
}

struct platform_driver led_driver = {
	.probe = led_probe,
	.remove = led_remove,
	.shutdown = led_shutdown,
	.suspend = led_suspend,
	.resume = led_resume,
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
	}
};

static int led_init(void)
{
	int DriverState=0;	
	printk(KERN_EMERG "\tstart led init!\n");
	DriverState = platform_driver_register(&led_driver);
	if(DriverState==0)
	{
		printk(KERN_EMERG "\tled init OK,DriverState is %d!\n",DriverState);
	}	
	
	return 0;
}

static void led_exit(void)
{
	printk(KERN_EMERG "led module exit!\n");	
	platform_driver_unregister(&led_driver);	
	gpio_free(EXYNOS4_GPL2(0));
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");

3.2 用戶程序設計

3.2.1 思路

主要使用兩個函數open和ioctl來完成驅動的調用,實現led燈 亮-滅-亮,間隔三秒的效果。

主要操作如下:

fd = open(hello_node,O_RDWR|O_NDELAY))

ioctl(fd,cmd,arg)

說明:cmd爲命令,爲0代表將LED控制端口置0,爲1代表將端口置1,arg代表操作幾號端口

linux 內核 - ioctl 函數詳解_運維_歲月斑駁7的博客-CSDN博客
https://blog.csdn.net/qq_19923217/article/details/82698787

3.2.2 調試信息

[root@iTOP-4412]# ./user_led_ctl
[ 8283.451679] led open
[ 8283.452475] cmd is 1,arg is 1
[ 8286.455536] cmd is 0,arg is 1
[ 8289.457608] cmd is 1,arg is 1
[ 8289.459170] led release

3.2.3 程序代碼

user_led_ctl.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

main(){
	int fd;
	char *hello_node = "/dev/hello_ctl";
	
/*O_RDWR只讀打開,O_NDELAY非阻塞方式*/	
	if((fd = open(hello_node,O_RDWR|O_NDELAY))<0){
		printf("APP open %s failed",hello_node);
	}
	else{
		printf("APP open %s success",hello_node);
		ioctl(fd,1,1);
		sleep(3);
		ioctl(fd,0,1);
		sleep(3);
		ioctl(fd,1,1);
	}
	
	close(fd);
}

 

4. 遇到的問題

1. GPL2(0)被設備leds佔用,需要在內核的memuconfig中屏蔽掉!

改正前:

[root@iTOP-4412]# insmod led_ctrl.ko
[ 4321.091798]  start led init!
[ 4321.093275]  led initialized
[ 4321.096091] gpio_request EXYNOS4_GPL2(0) failed!
[ 4321.101157] hello_ctl: probe of hello_ctl failed with error -16
[ 4321.110816]  led init OK,DriverState is 0!

改正後:

[root@iTOP-4412]# insmod led_ctrl.ko
[   80.193404]  start led init!
[   80.195196]  led initialized
[   80.232298]  led init OK,DriverState is 0!

內核屏蔽leds後,設備節點中無leds:

2.led_ctl.ko第二次加載時報錯:

[root@iTOP-4412]# insmod led_ctrl.ko
[ 7653.904264]  start led init!
[ 7653.906275]  led initialized
[ 7653.908569] gpio_request EXYNOS4_GPL2(0) failed!
[ 7653.926442] hello_ctl: probe of hello_ctl failed with error -16
[ 7653.950942]  led init OK,DriverState is 0!

主要原因是,卸載模塊時沒有釋放gpio:

gpio_free(EXYNOS4_GPL2(0));

發佈了99 篇原創文章 · 獲贊 63 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章