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(.....),设备文件的自动创建的更多相关文章

  1. Linux 内核空间与用户空间

    本文以 32 位系统为例介绍内核空间(kernel space)和用户空间(user space). 内核空间和用户空间 对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间)为 4 ...

  2. linux内存(一) 内核空间与用户空间

    来自如下网站 https://www.cnblogs.com/sparkdev/p/8410350.html 内核空间和用户空间 对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间 ...

  3. Linux操作系统,为什么需要内核空间和用户空间?

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 本文以 32 位系统为例介绍内核空间(kernel sp ...

  4. 内核空间和用户空间的分界 PAGE_OFFSET

      PAGE_OFFSET 首先看看PAGE_OFFSET的功能   内存映射 |            用户空间                  |   内核空间   | |——————+———— ...

  5. linux内核空间和用户空间详解

    linux驱动程序一般工作在内核空间,但也可以工作在用户空间.下面我们将详细解析,什么是内核空间,什么是用户空间,以及如何判断他们.Linux简化了分段机制,使得虚拟地址与线性地址总是一致,因此,Li ...

  6. Linux内核中断引入用户空间(异步通知机制)【转】

    转自:http://blog.csdn.net/kingdragonfly120/article/details/10858647 版权声明:本文为博主原创文章,未经博主允许不得转载. 当Linux内 ...

  7. windbg 如何再内核模式调试用户空间的程序

    1:使用!process 0 0 获取用户空间的所有的进程的信息 !process 0 0 **** NT ACTIVE PROCESS DUMP ****    PROCESS 80a02a60   ...

  8. Oracle小技巧_不同表空间不同用户导入导出数据dmp

    [博主注:数据库方面 ITPUB网站及博客内容非常丰富]   转载来源ITPUB 博客 经常有人会问:原来的数据在USERS表空间里面,我想把它IMP进APP表空间,我已经修改了目的用户的默认表空间, ...

  9. 【mark】OS是否使用svc方式分开系统空间和用户空间的优劣

    对于Cortex-M单片机,用户程序调用RTOS系统函数有两种思路: 假设创建任务的RTOS函数是xxx_task_create() 第一类:FreeRTOS.RT-Thread中采用的方法,和调用普 ...

随机推荐

  1. spring5 源码深度解析----- 事务的回滚和提交(100%理解事务)

    上一篇文章讲解了获取事务,并且通过获取的connection设置只读.隔离级别等,这篇文章讲解剩下的事务的回滚和提交 回滚处理 之前已经完成了目标方法运行前的事务准备工作,而这些准备工作最大的目的无非 ...

  2. UVA - 1160 X-Plosives

    A secret service developed a new kind of explosive that attain its volatile property only when a spe ...

  3. PHP中sha1()函数和md5()函数的绕过

    相信大家都知道,sha1函数和md5都是哈希编码的一种,在PHP中,这两种编码是存在绕过漏洞的. PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值 ...

  4. CentOS 7 单机安装Redis Cluster(3主3从)

    首先,本篇要基于单实例的安装,你的机器上已经有一个Redishttps://gper.club/articles/7e7e7f7ff7g5egc4g6b 为了节省机器,我们直接把6个Redis实例安装 ...

  5. 微信小程序与用户交互

    微信小程序与用户交互 一.显示消息提示框 wx.showToast({属性名:属性值}) 自定义一个提示框,时间到了会自动关闭 wx.showToast({ title:"成功", ...

  6. insert增数据详解

    查看表结构: desc 表名; describe的缩写,意为描述 增加数据不会改变表的结构,只是增加了行. 创建一张表: mysql> create table class( -> id ...

  7. PHP krsort

    1.什么都不想说了,干么没事放那么悲伤的歌呢?回忆里我还是对代码懵懵懂懂的无知青年!也许不是青年,只是少年... <?php $arr = [ 1 => 'Zhangbiyu', 2 =& ...

  8. 自学maya三月,为啥还是95%都还不会,那是因为你不懂这几个技巧

    有一些学员经常会有这种疑问,为什么学习MAYA软件这么难,为什么自己怎么学都学不会? 结果调查,发现了下面几个问题. 游戏建模 第一: 走弯路 很多人一开始学习Maya的时候肯定第一步是安装软件,但是 ...

  9. Flink实战| Flink+Redis实时防刷接口作弊

    随着人口红利的慢慢削减,互联网产品的厮杀愈加激烈,大家开始看好下沉市场的潜力,拼多多,趣头条等厂商通过拉新奖励,购物优惠等政策率先抢占用户,壮大起来.其他各厂商也紧随其后,纷纷推出自己产品的极速版,如 ...

  10. Dubbo 在 K8s 下的思考

    作者 | 曹胜利  Apache Dubbo PMC 导读:Dubbo 作为高性能 Java RPC 框架的刻板印象早已深入人心,在 Cloud Native 的架构选型上,Spring Cloud ...