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));