(七)嵌入式 Linux驱动程序设计之第一个相对完整的驱动

接上篇:(六)嵌入式 Linux驱动程序设计之物理地址到虚拟地址映射

总结:
杂项设备框架
1注册杂项设备:
 misc _register(&misc)
2构础杂项设备结构体
static struct miscdevice misc={
.minor=12,
.name="filename"//dev/下名称
.fops=&filename_fops,
};
3.构建file-operation
open
write  copy_from_user
read   copy_to_user
close
4.卸载杂项设备
misc_deregister(&misc)

字符设备框架
1.驱动初始化
1.1分配设备号:
静态分配设备号(register_chrdev_region)
动态分配设备号(alloc_chrdev_region)
1.1.1操作设备号dev_t:
宏MAJOR用于从dev_t中获取主设备号
宏MINOR用于从dev_t中获取次设备号
宏MKDEV用于将给定的主设备号和次设备号的值组合成dev_t类型的设备号
1.2初始化cdev  cdev_init
1.3注册cdev  cdev_add
1.4初始化硬件
2.构建file-operation
open
write  copy_from_user
read   copy_to_user
close
3.生成设备节点
3.1自动生成设备节点 
创建一个class  class_create
创建一个设备  device_create
3.2手动生成设备节点
mknod命令
4.驱动卸载
4.1释放设备号:unregister_chrdev_region
卸载cdev   cdev_del
卸载设备   device_destroy
卸载class  class_destroy

应用层打开设备节点(通过file_operation操作硬件设备)
fd=open("/dev/xxx",O_RDWR)   xxx_open()
read(fd,date,1);             xxx_read()
write(fd,date,1);            xxx_write()

杂项设备驱动流程图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

目标

使用杂项设备完成一个蜂鸣器的驱动
完成一个上层测试应用
应用要求:在上层应用中传入参数1为打开蜂鸣器,传入参数0为关闭蜂鸣器

任务分析

想要操作蜂鸣器,就要完成read函数 open函数,等等,我们做驱动,大部分情况下也都是使用这几个函数。
要完成上层应用的测试,就需要应用层和内核层传输数据,copy_to_user和copy_from_user
注意:确定蜂鸣器地址:先从原理图找到对应GPIO管脚,然后在手册中搜索管教名称找到具体的GPIO管教(GPIO5)然后根据此管教搜索此管教的DR寄存器地址即可,如GPIO5_DR
因为内核已经有蜂鸣器驱动,所以复用关系和电气属性寄存器都不要设置,只需要设置数据寄存器即可。

源码如下:
beep.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#define GPIO5_DR 0x020AC000

unsigned int *vir_gpio5_dr;
int misc_open (struct inode *inode, struct file *file){ 
    printk("hello misc_open!!!\n");
    return 0;
}
int misc_release(struct inode *inode, struct file *file){
    printk("bye bye misc_release!!!\n");
    return 0;
}
ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t){
    
    char kbuf[64] = "copy to user!!!\n";
    if( copy_to_user(ubuf, kbuf, size) != 0 ){
        printk("copy_to_user error!!!\n");
        return -1;
    }
    printk("hello misc_read!!!\n");
    return 0;
}

ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t){
    
    char kbuf[64] = "copy from user!!!\n";
    printk("hello misc_write!!!\n");
    if( copy_from_user(kbuf, ubuf, size) != 0 ){
        printk("copy_from_user error!!!\n");
        return -1;
    }
    printk("buf is:%s\n", kbuf);
    if(kbuf[0] == 1){                       //对蜂鸣器的控制,如果是1,控制gpio口
        *vir_gpio5_dr |= (1 << 1);          //因为是gpio5的01,所以左移一位就可以,给一个高电平,使蜂鸣器工作
    }else if(kbuf[0] == 0){
        *vir_gpio5_dr &= ~(1 << 1);                   
    }
    return 0;
}
struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .read = misc_read,
    .write = misc_write,
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "hello_misc",
    .fops = &misc_fops
};
static int misc_init(void)
{
    int ret;
    ret = misc_register(&misc_dev);
    if(ret < 0){
        printk("misc_register failed!!!\n");
        return -1;
    }
    printk("misc_register succeed!!!\n");           // 在内核中无法使用c语言库,所以不用printf
    
    vir_gpio5_dr = ioremap(GPIO5_DR, 4);            // 物理地址到虚拟地址的映射

    if(vir_gpio5_dr == NULL){                       //如果映射失败
        printk("GPIO5_DR ioremap error!!!\n");
        return -EBUSY;                              //EBUSY是Linux的预留参数,前面加个负号
    }

    return 0;
}
static void misc_exit(void)
{
    misc_deregister(&misc_dev);
    iounmap(vir_gpio5_dr);                          //取消虚拟地址的映射
    printk("misc exit!!!\n");
}

module_init(misc_init);
module_exit(misc_exit);

MODULE_LICENSE("GPL");              //声明模块拥有开源许可

app.c

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

int main(int argc, char *argv[])
{
    int fd;
    char buff[64] = {0};
    
    fd = open("/dev/hello_misc", O_RDWR);
    if(fd < 0){
        perror("open error\n");                // perror在应用中打印
        return fd;
    }
    buff[0] = atoi(argv[1]);                    //字符串转化成整形                            
    // read(fd, buff, sizeof(buff));
    write(fd, buff,sizeof(buff));               //在write中就传数据到底层,这样可以调用驱动的mis_write的操作了,直接操作 ./app 1或者./app 0
    // printf("buf is:%s\n", buff);
    close(fd);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值