GPIO硬件资源的申请,内核空间和用户空间的数据交换,ioctl(.....),设备文件的自动创建
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
4) 设备的文件自动创建
真正创建设备文件的软件:mdev
挂载proc sys文件系统 /proc /sys
procfs
sysyfs都是基于内存的文件系统
其中的内容都是linux在执行过程中动态创建的
procfs, 用来向用户空间导出内核态的执行信息
常用的一些系统软件 例如ps基于该文件系统实现的
每个进程都会在proc目录对应一个其进程ID命名的文件夹
cat /proc/cmdline
cat /proc/partitions
cat /proc/cpuinfo
cat /proc/meminfo
cat /proc/devices
....
cat /proc/sys/kernel/printk
echo 7 >/proc/sys/kernel/printk
echo /sbin/mdev >/proc/sys/kernel/hotplug
sysfs, 它是从procfs分家出来的
专门用来展示硬件驱动模型 层次关系
安装xxx.ko模块
会自动产生 /sys/module/xxx/
触发热插拔事件
class_create device_create
class_destroy device_destroy
- #include "../../global.h"
- #include <linux/fs.h>
- #include <linux/cdev.h>
- #include <linux/gpio.h>
- #include <mach/platform.h>
- unsigned int major = ;
- unsigned int minor = ;
- dev_t dev ; //设备号
- /*1 定义一个struct cdev变量*/
- struct cdev led_cdev;
- static int led_open(struct inode *inode,
- struct file *filp)
- {
- /*输出低电平*/
- gpio_set_value(PAD_GPIO_C+, );
- return ;
- }
- static int led_close(struct inode *inode,
- struct file *filp)
- {
- gpio_set_value(PAD_GPIO_C+, );
- return ;
- }
- struct file_operations led_fops =
- {
- .owner = THIS_MODULE,
- .open = led_open,
- .release = led_close,
- };
- int __init led_drv_init(void)
- {
- if(major) //静态
- {
- //dev = major<<20|minor;
- dev = MKDEV(major, minor);
- register_chrdev_region(dev, , "myleds");
- }
- else //动态注册
- {
- alloc_chrdev_region(&dev, minor, , "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, );
- /*申请GPIO管脚*/
- gpio_request(PAD_GPIO_C+, "led1");
- /*使用GPIO管脚*/
- gpio_direction_output(PAD_GPIO_C+, );
- return ;
- }
- void __exit led_drv_exit(void)
- {
- /*释放GPIO管脚*/
- gpio_free(PAD_GPIO_C+);
- /*4 注销cdev*/
- cdev_del(&led_cdev);
- unregister_chrdev_region(dev, );
- }
- module_init(led_drv_init);
- 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>
- unsigned int major = ;
- unsigned int minor = ;
- dev_t dev ; //设备号
- /*1 定义一个struct cdev变量*/
- struct cdev led_cdev;
- static int led_open(struct inode *inode,
- struct file *filp)
- {
- return ;
- }
- static int led_close(struct inode *inode,
- struct file *filp)
- {
- return ;
- }
- int k_stat = ; //记录灯的当前状态
- ssize_t led_write(struct file *filp,
- const char __user *buf,
- size_t len, loff_t *offset)
- {
- int ret = ;
- int k_cmd = ;
- //buf中保存的是用户空间地址
- //*buf在直接读取用户空间内存 没有权限检查 不安全
- //*buf
- ret = copy_from_user(&k_cmd, buf, len);
- /*
- *cmd=0 亮灯
- * =1 灭灯
- * */
- gpio_set_value(PAD_GPIO_C+, k_cmd);
- /*
- * 1,灭的状态
- * 0,亮的状态
- * */
- k_stat = k_cmd;
- return len;
- }
- ssize_t led_read(struct file *filp,
- char __user *buf,
- size_t len, loff_t *offset)
- {
- int ret = ;
- /*
- *buf 保存的是用户空间stat变量的地址
- *内核需要向其中写入数据应该先检查是否有写权限
- *以下做法不安全
- * */
- //*buf = k_stat;
- ret = copy_to_user(buf, &k_stat, len);
- return len;
- }
- struct file_operations led_fops =
- {
- .owner = THIS_MODULE,
- .open = led_open,
- .release = led_close,
- .write = led_write,
- .read = led_read,
- };
- int __init led_drv_init(void)
- {
- if(major) //静态
- {
- //dev = major<<20|minor;
- dev = MKDEV(major, minor);
- register_chrdev_region(dev, , "myleds");
- }
- else //动态注册
- {
- alloc_chrdev_region(&dev, minor, , "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, );
- /*申请GPIO管脚*/
- gpio_request(PAD_GPIO_C+, "led1");
- /*使用GPIO管脚*/
- gpio_direction_output(PAD_GPIO_C+, );
- return ;
- }
- void __exit led_drv_exit(void)
- {
- /*释放GPIO管脚*/
- gpio_free(PAD_GPIO_C+);
- /*4 注销cdev*/
- cdev_del(&led_cdev);
- unregister_chrdev_region(dev, );
- }
- module_init(led_drv_init);
- 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 = ;
- unsigned int minor = ;
- 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+, "led1"},
- {PAD_GPIO_C+ , "led2"},
- {PAD_GPIO_C+, "led3"},
- {PAD_GPIO_B+, "led4"},
- };
- static int led_open(struct inode *inode,
- struct file *filp)
- {
- return ;
- }
- static int led_close(struct inode *inode,
- struct file *filp)
- {
- return ;
- }
- 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 = ;
- int k_index = ;
- ret = copy_from_user(&k_index,
- (const void *)arg, );
- if(k_index> || k_index<)
- {
- return -EINVAL;
- }
- switch(cmd)
- {
- case CMD_LED_ON:
- gpio_set_value(leds[k_index-].gpio, );
- break;
- case CMD_LED_OFF:
- gpio_set_value(leds[k_index-].gpio, );
- break;
- default:
- return -EINVAL;
- }
- return ;
- }
- 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 = ;
- if(major) //静态
- {
- //dev = major<<20|minor;
- dev = MKDEV(major, minor);
- register_chrdev_region(dev, , "myleds");
- }
- else //动态注册
- {
- alloc_chrdev_region(&dev, minor, , "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, );
- for(i=; i<ARRAY_SIZE(leds); i++)
- {
- /*申请GPIO管脚*/
- gpio_request(leds[i].gpio, leds[i].name);
- /*使用GPIO管脚*/
- gpio_direction_output(leds[i].gpio, );
- }
- return ;
- }
- void __exit led_drv_exit(void)
- {
- int i = ;
- for(i=; i<ARRAY_SIZE(leds); i++)
- {
- /*释放GPIO管脚*/
- gpio_free(leds[i].gpio);
- }
- /*4 注销cdev*/
- cdev_del(&led_cdev);
- unregister_chrdev_region(dev, );
- }
- module_init(led_drv_init);
- 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>
- #include <linux/device.h>
- #define CMD_LED_ON (0x10001)
- #define CMD_LED_OFF (0x10002)
- struct class *cls = NULL;
- unsigned int major = ;
- unsigned int minor = ;
- 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+, "led1"},
- {PAD_GPIO_C+ , "led2"},
- {PAD_GPIO_C+, "led3"},
- {PAD_GPIO_B+, "led4"},
- };
- static int led_open(struct inode *inode,
- struct file *filp)
- {
- return ;
- }
- static int led_close(struct inode *inode,
- struct file *filp)
- {
- return ;
- }
- 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 = ;
- int k_index = ;
- ret = copy_from_user(&k_index,
- (const void *)arg, );
- if(k_index> || k_index<)
- {
- return -EINVAL;
- }
- switch(cmd)
- {
- case CMD_LED_ON:
- gpio_set_value(leds[k_index-].gpio, );
- break;
- case CMD_LED_OFF:
- gpio_set_value(leds[k_index-].gpio, );
- break;
- default:
- return -EINVAL;
- }
- return ;
- }
- 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 = ;
- if(major) //静态
- {
- //dev = major<<20|minor;
- dev = MKDEV(major, minor);
- register_chrdev_region(dev, , "myleds");
- }
- else //动态注册
- {
- alloc_chrdev_region(&dev, minor, , "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, );
- /*自动产生设备文件*/
- /*会产生 /sys/class/LEDS/*/
- cls = class_create(THIS_MODULE, "LEDS");
- /*会产生 /sys/class/LEDS/myleds/ */
- device_create(cls,NULL,dev, NULL,"myleds");
- for(i=; i<ARRAY_SIZE(leds); i++)
- {
- /*申请GPIO管脚*/
- gpio_request(leds[i].gpio, leds[i].name);
- /*使用GPIO管脚*/
- gpio_direction_output(leds[i].gpio, );
- }
- return ;
- }
- void __exit led_drv_exit(void)
- {
- int i = ;
- for(i=; i<ARRAY_SIZE(leds); i++)
- {
- /*释放GPIO管脚*/
- gpio_free(leds[i].gpio);
- }
- /*自动销毁设备文件*/
- device_destroy(cls, dev);
- class_destroy(cls);
- /*4 注销cdev*/
- cdev_del(&led_cdev);
- unregister_chrdev_region(dev, );
- }
- module_init(led_drv_init);
- module_exit(led_drv_exit);
GPIO硬件资源的申请,内核空间和用户空间的数据交换,ioctl(.....),设备文件的自动创建的更多相关文章
- Linux 内核空间与用户空间
本文以 32 位系统为例介绍内核空间(kernel space)和用户空间(user space). 内核空间和用户空间 对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间)为 4 ...
- linux内存(一) 内核空间与用户空间
来自如下网站 https://www.cnblogs.com/sparkdev/p/8410350.html 内核空间和用户空间 对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间 ...
- Linux操作系统,为什么需要内核空间和用户空间?
点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 本文以 32 位系统为例介绍内核空间(kernel sp ...
- 内核空间和用户空间的分界 PAGE_OFFSET
PAGE_OFFSET 首先看看PAGE_OFFSET的功能 内存映射 | 用户空间 | 内核空间 | |——————+———— ...
- linux内核空间和用户空间详解
linux驱动程序一般工作在内核空间,但也可以工作在用户空间.下面我们将详细解析,什么是内核空间,什么是用户空间,以及如何判断他们.Linux简化了分段机制,使得虚拟地址与线性地址总是一致,因此,Li ...
- Linux内核中断引入用户空间(异步通知机制)【转】
转自:http://blog.csdn.net/kingdragonfly120/article/details/10858647 版权声明:本文为博主原创文章,未经博主允许不得转载. 当Linux内 ...
- windbg 如何再内核模式调试用户空间的程序
1:使用!process 0 0 获取用户空间的所有的进程的信息 !process 0 0 **** NT ACTIVE PROCESS DUMP **** PROCESS 80a02a60 ...
- Oracle小技巧_不同表空间不同用户导入导出数据dmp
[博主注:数据库方面 ITPUB网站及博客内容非常丰富] 转载来源ITPUB 博客 经常有人会问:原来的数据在USERS表空间里面,我想把它IMP进APP表空间,我已经修改了目的用户的默认表空间, ...
- 【mark】OS是否使用svc方式分开系统空间和用户空间的优劣
对于Cortex-M单片机,用户程序调用RTOS系统函数有两种思路: 假设创建任务的RTOS函数是xxx_task_create() 第一类:FreeRTOS.RT-Thread中采用的方法,和调用普 ...
随机推荐
- html5视频常用API接口
一.虽然有的属性是boolean类型,但仍旧建议按照XHTML书写(属性名=”属性值”)格式,避免出现错误 (下面加粗的属性为常用属性) 属性 值 功能描述 controls controls 是否显 ...
- 直线扫描转换-DDA算法
直线扫描转换-DDA算法 直线段的扫描转换算法 已知两个点,求直线. 为了在光栅显示器上用这些离散的像素点逼近这条直线,需要知道这些像素点的x,y坐标. 求出过P0,P1的直线段方程: y=kx+b ...
- 微信小程序中事件
微信小程序中事件 一.常见的事件有 类型 触发条件 最低版本 touchstart 手指触摸动作开始 touchmove 手指触摸后移动 touchcancel 手指触摸动作被打断,如来电提醒,弹窗 ...
- 白话系列之实现自己简单的mvc式webapi框架
前言:此文为极简mvc式的api框架,只当做入门api的解析方式,并且这里也不算是mvc框架,因为没有view层,毕竟现在大部分都属于前后端分离,当然也可以提供view层,因为只是将view当做文本返 ...
- Luogu1119灾后重建
题目背景 BBB 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响.但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车.换句话说,只有连接着两个重建完成的村庄的公 ...
- 初识AutoIt
1.定义 AutoIt 目前最新是v3版本,这是一个使用类似BASIC脚本语言的免费软件,它设计用于Windows GUI(图形用户界面)中进行自动化操作.它利用模拟键盘按键,鼠标移动和窗口/控件的组 ...
- MFC中如何分割CString类型的数据
[才疏学浅,难免有纰漏,若有不正确的地方,欢迎指教] MFC中有一个库函数 Tokenize(); 函数原型:CStringT Tokenize( PCXSTR pszTokens , int& ...
- 图论-最小生成树<Kruskal>
昨天: 图论-最小生成树<Dijkstra,Floyd> 以上是昨天的Blog,有需要者请先阅读完以上再阅读今天的Blog. 可能今天的有点乱,好好理理,认真看完相信你会懂得 然而,文中提 ...
- Java 中的 final、finally、finalize 有什么不同?
Java 中 final.finally.finalize 有什么不同?这是在 Java 面试中经常问到的问题,他们究竟有什么不同呢? 这三个看起来很相似,其实他们的关系就像卡巴斯基和巴基斯坦一样有基 ...
- netty源码解析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage
PoolChunk用来分配大于或等于一个page的内存,如果需要小于一个page的内存,需要先从PoolChunk中分配一个page,然后再把一个page切割成多个子页-subpage,最后把内存以s ...