1、通过GPIO库函数控制LED
open("/dev/myleds",...) close(fd)
---------------------------------------------
sys_open sys_close
led_open(...) led_release(...)
{ {
亮灯 灭灯
} }
电路原理图:
控制LED1 实则控制CPU上的管脚GPIOC12输出低/高电平
如何控制GPIOC12输出低/高电平:cpu datasheet
GPIOCALTFN0 0xc001c020
GPIOCOUTENB 0xc001c004
GPIOCOUT 0xc001c000
linux系统中如何控制GPIO管脚输出:本质上就是操作以上特殊功能寄存器
1)将物理地址映射为虚拟地址
然后通过虚拟地址访问特殊功能寄存器
2)内核中提供了一套控制GPIO的库函数
GPIO库函数控制GPIO管脚有固定的步骤:
1)申请GPIO管脚 (可选的)
int gpio_request(unsigned gpio, const char *name)
gpio,要申请的管脚编号
arch/arm/plat-s5p6818/common/cfg_type.h
name,名称
返回值,0 申请成功
非0 失败
2)使用GPIO管脚
//将参数指定的管脚设置为输入模式
int gpio_direction_input(unsigned gpio)
//获取指定管脚的电平状态
// 0/1, 低/高
int gpio_get_value(unsigned gpio)
//将参数指定的管脚设置为输出模式
//value 为0 默认输出低电平
非0 默认输出高电平
int gpio_direction_output(unsigned gpio, int value)
//设置指定管脚输出高/低电平
void gpio_set_value(unsigned gpio, int value)
3)释放GPIO管脚 (可选的)
//释放管脚
void gpio_free(unsigned gpio)
安装模块前将内核中自带的LED驱动裁剪掉
cd kernel/
make menuconfig
Device Drivers --->
-*- LED Support --->
< > LED Support for GPIO connected LEDs
< > PWM driven LED Support
[ ] LED Trigger support
make uImage
让开发板使用新内核
cp arch/arm/boot/uImage /tftpboot/
setenv bootcmd ping 192.168.1.8\;ping 192.168.1.8 \; tftp 48000000 uImage \; bootm 48000000
saveenv
insmod led_drv.ko
mknod /dev/myleds c 244 5
./test
观察LED1的变化
练习:LED1/2/3/4
2、内核空间和用户空间的数据交换
用户空间代码不能(直接)访问内核空间
内核空间代码访问用户空间时也加了限制
long copy_to_user(void __user *to,
const void *from, unsigned long n)
to, 目标地址
该地址应该介于0~3G
from, 源数据地址
该地址应该介于3G~4G
n, 希望连续拷贝的字节数
返回值,拷贝失败的字节数
long copy_from_user(void *to,
const void __user * from, unsigned long n)
to, 目标地址 (3G~4G)
from,源数据地址(0~3G)
n, 连续拷贝的字节数
返回值,操作失败的字节数
练习:
用户空间向led1设备写入1时灭灯
0 亮
读取 1 灯是灭
3、ioctl
对LED写操作实现LED亮灭控制不合理?
如果需要去实现uart驱动程序
应用程序通过uart发数据 应该实现write
收 read
如何在用户空间改变通信时的波特率?
用户空间使用ioctl----->unlocked_ioctl
ioctl,用于设置或者获取设备工作属性信息
例如uart 通信时 设置使用8n1 115200
查询当前uart通信时使用的波特率
函数原型:int ioctl(int d, int request, ...)
通常使用
1)ioctl(fd, cmd)
2)ioctl(fd, cmd, &arg)
练习:./test on/off 1/2/3/4
4、设备文件的自动创建
1) 根文件系统中有mdev
2) 挂载了proc sys 文件系统
rootfs/etc/init.d/rcS
mount -a
rootfs/etc/fstab
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
3)配置热插拔事件产生后要执行的动作
echo /sbin/mdev > /proc/sys/kernel/hotplug
产生热插拔事件后,自动到proc/sys/kernel/hotplug执行/sbin/mdev产生相应的设备文件。
热插拔事件:
狭义: U盘的插入和拔出
广义: 也包括/sys目录的文件变化
4)编程产生热插拔事件
class_create
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
class, 该device属于那种类型的设备
该果实挂在哪个树枝上
parent, 该device的父设备
devt, 设备号
drvdata,创建设备时传递给设备的附加信息
通常给NULL
fmt, ...:决定了将来/dev目录下产生的设备文件的名称
例如: "ttySAC%d",i
for(i=0; i<4; i++)
{
device_create(...., "ttySAC%d",i);
}
device_destroy
class_destroy
1 #include "../../global.h" 2 #include <linux/fs.h> 3 #include <linux/cdev.h> 4 #include <linux/gpio.h> 5 #include <mach/platform.h> 6 7 unsigned int major = 0; 8 unsigned int minor = 5; 9 dev_t dev ; //设备号 10 11 /*1 定义一个struct cdev变量*/ 12 struct cdev led_cdev; 13 14 15 static int led_open(struct inode *inode, 16 struct file *filp) 17 { 18 /*输出低电平*/ 19 gpio_set_value(PAD_GPIO_C+12, 0); 20 return 0; 21 } 22 static int led_close(struct inode *inode, 23 struct file *filp) 24 { 25 gpio_set_value(PAD_GPIO_C+12, 1); 26 return 0; 27 } 28 29 struct file_operations led_fops = 30 { 31 .owner = THIS_MODULE, 32 .open = led_open, 33 .release = led_close, 34 }; 35 int __init led_drv_init(void) 36 { 37 if(major) //静态 38 { 39 //dev = major<<20|minor; 40 dev = MKDEV(major, minor); 41 42 register_chrdev_region(dev, 1, "myleds"); 43 } 44 else //动态注册 45 { 46 alloc_chrdev_region(&dev, minor, 1, "myleds"); 47 printk("<1>" "major=%d minor=%d\n", 48 MAJOR(dev), MINOR(dev)); 49 } 50 /*2 初始化cdev变量*/ 51 cdev_init(&led_cdev, &led_fops); 52 /*3 注册cdev变量*/ 53 cdev_add(&led_cdev, dev, 1); 54 /*申请GPIO管脚*/ 55 gpio_request(PAD_GPIO_C+12, "led1"); 56 /*使用GPIO管脚*/ 57 gpio_direction_output(PAD_GPIO_C+12, 1); 58 59 return 0; 60 } 61 void __exit led_drv_exit(void) 62 { 63 /*释放GPIO管脚*/ 64 gpio_free(PAD_GPIO_C+12); 65 /*4 注销cdev*/ 66 cdev_del(&led_cdev); 67 68 unregister_chrdev_region(dev, 1); 69 } 70 module_init(led_drv_init); 71 module_exit(led_drv_exit);
1 #include "../../global.h" 2 #include <linux/fs.h> 3 #include <linux/cdev.h> 4 #include <linux/gpio.h> 5 #include <mach/platform.h> 6 #include <linux/uaccess.h> 7 8 unsigned int major = 0; 9 unsigned int minor = 5; 10 dev_t dev ; //设备号 11 12 /*1 定义一个struct cdev变量*/ 13 struct cdev led_cdev; 14 15 16 static int led_open(struct inode *inode, 17 struct file *filp) 18 { 19 return 0; 20 } 21 static int led_close(struct inode *inode, 22 struct file *filp) 23 { 24 return 0; 25 } 26 int k_stat = 0; //记录灯的当前状态 27 ssize_t led_write(struct file *filp, 28 const char __user *buf, 29 size_t len, loff_t *offset) 30 { 31 int ret = 0; 32 int k_cmd = 0; 33 //buf中保存的是用户空间地址 34 //*buf在直接读取用户空间内存 没有权限检查 不安全 35 //*buf 36 ret = copy_from_user(&k_cmd, buf, len); 37 /* 38 *cmd=0 亮灯 39 * =1 灭灯 40 * */ 41 gpio_set_value(PAD_GPIO_C+12, k_cmd); 42 /* 43 * 1,灭的状态 44 * 0,亮的状态 45 * */ 46 k_stat = k_cmd; 47 return len; 48 49 } 50 ssize_t led_read(struct file *filp, 51 char __user *buf, 52 size_t len, loff_t *offset) 53 { 54 int ret = 0; 55 /* 56 *buf 保存的是用户空间stat变量的地址 57 *内核需要向其中写入数据应该先检查是否有写权限 58 *以下做法不安全 59 * */ 60 //*buf = k_stat; 61 62 ret = copy_to_user(buf, &k_stat, len); 63 64 return len; 65 66 } 67 struct file_operations led_fops = 68 { 69 .owner = THIS_MODULE, 70 .open = led_open, 71 .release = led_close, 72 .write = led_write, 73 .read = led_read, 74 }; 75 int __init led_drv_init(void) 76 { 77 if(major) //静态 78 { 79 //dev = major<<20|minor; 80 dev = MKDEV(major, minor); 81 82 register_chrdev_region(dev, 1, "myleds"); 83 } 84 else //动态注册 85 { 86 alloc_chrdev_region(&dev, minor, 1, "myleds"); 87 printk("<1>" "major=%d minor=%d\n", 88 MAJOR(dev), MINOR(dev)); 89 } 90 /*2 初始化cdev变量*/ 91 cdev_init(&led_cdev, &led_fops); 92 /*3 注册cdev变量*/ 93 cdev_add(&led_cdev, dev, 1); 94 /*申请GPIO管脚*/ 95 gpio_request(PAD_GPIO_C+12, "led1"); 96 /*使用GPIO管脚*/ 97 gpio_direction_output(PAD_GPIO_C+12, 1); 98 99 return 0; 100 } 101 void __exit led_drv_exit(void) 102 { 103 /*释放GPIO管脚*/ 104 gpio_free(PAD_GPIO_C+12); 105 /*4 注销cdev*/ 106 cdev_del(&led_cdev); 107 108 unregister_chrdev_region(dev, 1); 109 } 110 module_init(led_drv_init); 111 module_exit(led_drv_exit);
#include "../../global.h" #include <linux/fs.h> #include <linux/cdev.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/uaccess.h> #define CMD_LED_ON (0x10001) #define CMD_LED_OFF (0x10002) unsigned int major = 0; unsigned int minor = 5; dev_t dev ; //设备号 /*1 定义一个struct cdev变量*/ struct cdev led_cdev; typedef struct led_desc { int gpio;//管脚编号 char *name;//名称 }led_desc_t; led_desc_t leds[]= { {PAD_GPIO_C+12, "led1"}, {PAD_GPIO_C+7 , "led2"}, {PAD_GPIO_C+11, "led3"}, {PAD_GPIO_B+26, "led4"}, }; static int led_open(struct inode *inode, struct file *filp) { return 0; } static int led_close(struct inode *inode, struct file *filp) { return 0; } ssize_t led_write(struct file *filp, const char __user *buf, size_t len, loff_t *offset) { return len; } ssize_t led_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) { return len; } long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret = 0; int k_index = 0; ret = copy_from_user(&k_index, (const void *)arg, 4); if(k_index> 4 || k_index<1) { return -EINVAL; } switch(cmd) { case CMD_LED_ON: gpio_set_value(leds[k_index-1].gpio, 0); break; case CMD_LED_OFF: gpio_set_value(leds[k_index-1].gpio, 1); break; default: return -EINVAL; } return 0; } struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_close, .write = led_write, .read = led_read, .unlocked_ioctl = led_ioctl, }; int __init led_drv_init(void) { int i = 0; if(major) //静态 { //dev = major<<20|minor; dev = MKDEV(major, minor); register_chrdev_region(dev, 1, "myleds"); } else //动态注册 { alloc_chrdev_region(&dev, minor, 1, "myleds"); printk("<1>" "major=%d minor=%d\n", MAJOR(dev), MINOR(dev)); } /*2 初始化cdev变量*/ cdev_init(&led_cdev, &led_fops); /*3 注册cdev变量*/ cdev_add(&led_cdev, dev, 1); for(i=0; i<ARRAY_SIZE(leds); i++) { /*申请GPIO管脚*/ gpio_request(leds[i].gpio, leds[i].name); /*使用GPIO管脚*/ gpio_direction_output(leds[i].gpio, 1); } return 0; } void __exit led_drv_exit(void) { int i = 0; for(i=0; i<ARRAY_SIZE(leds); i++) { /*释放GPIO管脚*/ gpio_free(leds[i].gpio); } /*4 注销cdev*/ cdev_del(&led_cdev); unregister_chrdev_region(dev, 1); } module_init(led_drv_init); module_exit(led_drv_exit);
1 #include "../../global.h" 2 #include <linux/fs.h> 3 #include <linux/cdev.h> 4 #include <linux/gpio.h> 5 #include <mach/platform.h> 6 #include <linux/uaccess.h> 7 #include <linux/device.h> 8 9 #define CMD_LED_ON (0x10001) 10 #define CMD_LED_OFF (0x10002) 11 12 struct class *cls = NULL; 13 14 unsigned int major = 0; 15 unsigned int minor = 5; 16 dev_t dev ; //设备号 17 18 /*1 定义一个struct cdev变量*/ 19 struct cdev led_cdev; 20 21 typedef struct led_desc 22 { 23 int gpio;//管脚编号 24 char *name;//名称 25 }led_desc_t; 26 27 led_desc_t leds[]= 28 { 29 {PAD_GPIO_C+12, "led1"}, 30 {PAD_GPIO_C+7 , "led2"}, 31 {PAD_GPIO_C+11, "led3"}, 32 {PAD_GPIO_B+26, "led4"}, 33 }; 34 35 36 static int led_open(struct inode *inode, 37 struct file *filp) 38 { 39 return 0; 40 } 41 static int led_close(struct inode *inode, 42 struct file *filp) 43 { 44 return 0; 45 } 46 ssize_t led_write(struct file *filp, 47 const char __user *buf, 48 size_t len, loff_t *offset) 49 { 50 return len; 51 } 52 ssize_t led_read(struct file *filp, 53 char __user *buf, 54 size_t len, loff_t *offset) 55 { 56 return len; 57 } 58 59 long led_ioctl(struct file *filp, 60 unsigned int cmd, 61 unsigned long arg) 62 { 63 int ret = 0; 64 int k_index = 0; 65 66 ret = copy_from_user(&k_index, 67 (const void *)arg, 4); 68 69 if(k_index> 4 || k_index<1) 70 { 71 return -EINVAL; 72 } 73 74 switch(cmd) 75 { 76 case CMD_LED_ON: 77 gpio_set_value(leds[k_index-1].gpio, 0); 78 break; 79 case CMD_LED_OFF: 80 gpio_set_value(leds[k_index-1].gpio, 1); 81 break; 82 default: 83 return -EINVAL; 84 } 85 86 return 0; 87 } 88 89 struct file_operations led_fops = 90 { 91 .owner = THIS_MODULE, 92 .open = led_open, 93 .release = led_close, 94 .write = led_write, 95 .read = led_read, 96 .unlocked_ioctl = led_ioctl, 97 }; 98 int __init led_drv_init(void) 99 { 100 int i = 0; 101 if(major) //静态 102 { 103 //dev = major<<20|minor; 104 dev = MKDEV(major, minor); 105 106 register_chrdev_region(dev, 1, "myleds"); 107 } 108 else //动态注册 109 { 110 alloc_chrdev_region(&dev, minor, 1, "myleds"); 111 printk("<1>" "major=%d minor=%d\n", 112 MAJOR(dev), MINOR(dev)); 113 } 114 /*2 初始化cdev变量*/ 115 cdev_init(&led_cdev, &led_fops); 116 /*3 注册cdev变量*/ 117 cdev_add(&led_cdev, dev, 1); 118 /*自动产生设备文件*/ 119 /*会产生 /sys/class/LEDS/*/ 120 cls = class_create(THIS_MODULE, "LEDS"); 121 /*会产生 /sys/class/LEDS/myleds/ */ 122 device_create(cls,NULL,dev, NULL,"myleds"); 123 124 for(i=0; i<ARRAY_SIZE(leds); i++) 125 { 126 /*申请GPIO管脚*/ 127 gpio_request(leds[i].gpio, leds[i].name); 128 /*使用GPIO管脚*/ 129 gpio_direction_output(leds[i].gpio, 1); 130 } 131 return 0; 132 } 133 void __exit led_drv_exit(void) 134 { 135 int i = 0; 136 for(i=0; i<ARRAY_SIZE(leds); i++) 137 { 138 /*释放GPIO管脚*/ 139 gpio_free(leds[i].gpio); 140 } 141 /*自动销毁设备文件*/ 142 device_destroy(cls, dev); 143 class_destroy(cls); 144 145 /*4 注销cdev*/ 146 cdev_del(&led_cdev); 147 148 unregister_chrdev_region(dev, 1); 149 } 150 module_init(led_drv_init); 151 module_exit(led_drv_exit);