《智能家居系统》5
发布日期:2021-05-08 03:56:14 浏览次数:16 分类:精选文章

本文共 5583 字,大约阅读时间需要 18 分钟。

Linux驱动程序开发

1. 设置一个模块

在Linux系统中,每个驱动程序都可以看作是一个独立的模块。驱动开发的第一步是创建一个模块,用来存放驱动的相关代码。

1.1 模块的入口和出口

模块的入口和出口是通过函数接口实现的。在Linux中,模块的入口函数需要注册到内核中,以便系统能够加载和卸载该模块。

static int __init gec6818_led_init(void){      // 安装驱动  }  static void __exit gec6818_led_exit(void){      // 卸载驱动  }  module_init(gec6818_led_init);  module_exit(gec6818_led_exit);

1.2 模块的描述

// 模块描述

#modinfo led_drv.ko

MODULE_AUTHOR("bobeyfeng@163.com");
MODULE_DESCRIPTION("led driver for GEC6818");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");

2. 定义一个cdev

cdev是Linux内核中用于描述字符设备的数据结构。在大多数字符设备驱动中,使用cdev模型来实现设备的注册和操作。

static struct cdev led_cdev;

3. 申请设备号

设备号是字符设备驱动的唯一标识符。在Linux系统中,可以通过申请设备号来注册字符设备。

if(led_major == 0){          ret = alloc_chrdev_region(&led_dev_num, led_minor, 1, "led_device");      } else{          led_dev_num = MKDEV(led_major, led_minor);          ret = register_chrdev_region(led_dev_num, 1, "led_device");      }      if(ret < 0){          printk("can not get led device number\n");          return ret;      }

4. 提供文件操作接口

为了让应用程序能够与驱动程序交互,需要定义文件操作接口(file operations)。这些接口包括open、write、release等函数。

4.1 open函数

static int gec6818_led_open(struct inode *inode, struct file *filp){      printk("led driver is opening\n");      return 0;  }

4.2 write函数

static ssize_t gec6818_led_write(struct file *filp, const char __user *buf, size_t len, loff_t *off){      // 接收应用程序写下来的数据,并用这些数据来控制LED灯      // 定义一个数据协议:应用程序写下来的数据有两个字节      // buf[1] -> 某一站灯:8, 9, 10, 11      // buf[0] -> LED灯的状态:1---亮,0---灯灭      ret;      char led_flag[2];      if(len != 2){          return -EINVAL;      }      ret = copy_from_user(led_flag, buf, len);      if(ret < 0){          return -EFAULT;      }      switch(led_flag[1]){          case 8:              // GPIOC17              if(led_flag[0] == 1){                  *(unsigned int *)GPIOCOUT &= ~(1<<17);              } else if(led_flag[0] == 0){                  *(unsigned int *)GPIOCOUT |= (1<<17);              } else{                  return -EINVAL;              }              break;          case 9:              // GPIOC8              if(led_flag[0] == 1){                  *(unsigned int *)GPIOCOUT &= ~(1<<8);              } else if(led_flag[0] == 0){                  *(unsigned int *)GPIOCOUT |= (1<<8);              } else{                  return -EINVAL;              }              break;          case 10:              // GPIOC7              if(led_flag[0] == 1){                  *(unsigned int *)GPIOCOUT &= ~(1<<7);              } else if(led_flag[0] == 0){                  *(unsigned int *)GPIOCOUT |= (1<<7);              } else{                  return -EINVAL;              }              break;          case 11:              // GPIOC12              if(led_flag[0] == 1){                  *(unsigned int *)GPIOCOUT &= ~(1<<12);              } else if(led_flag[0] == 0){                  *(unsigned int *)GPIOCOUT |= (1<<12);              } else{                  return -EINVAL;              }              break;          default:              return -EINVAL;      }      return len;  }

4.3 release函数

static int gec6818_led_release(struct inode *inode, struct file *filp){      printk("led driver is closing\n");      return 0;  }
static const struct file_operations gec6818_led_fops = {      .owner = THIS_MODULE,      .write = gec6818_led_write,      .open = gec6818_led_open,      .release = gec6818_led_release,  };

5. 初始化cdev并将其加入内核

// 4. 初始化cdev      cdev_init(&led_cdev, &gec6818_led_fops);      // 5. 将初始化好的cdev加入内核      ret = cdev_add(&led_cdev, led_dev_num, 1);      if(ret < 0){          printk("cdev add error\n");          unregister_chrdev_region(led_dev_num, 1);          return -EBUSY;      }

6. 创建class和device

// 创建class  led_class = class_create(THIS_MODULE, "led_class");  if(IS_ERR(led_class)){      ret = PTR_ERR(led_class);      printk("led driver class create error\n");      cdev_del(&led_cdev);      unregister_chrdev_region(led_dev_num, 1);      return ret;  }  // 创建device  led_device = device_create(led_class, NULL, led_dev_num, NULL, "led_drv");  if(IS_ERR(led_device)){      ret = PTR_ERR(led_device);      printk("led driver device create error\n");      class_destroy(led_class);      cdev_del(&led_cdev);      unregister_chrdev_region(led_dev_num, 1);      return ret;  }

7. 获取物理地址对应的虚拟地址

// 8. 申请物理内存区  led_res = request_mem_region(0xC001C000, 0x1000, "GPIOC_MEM");  if(led_res == NULL){      printk("request memory error\n");      device_destroy(led_class, led_dev_num);      class_destroy(led_class);      cdev_del(&led_cdev);      unregister_chrdev_region(led_dev_num, 1);      return -EBUSY;  }  // 9. IO内存的动态映射  GPIOC_BASE = ioremap(0xC001C000, 0x1000);  if(GPIOC_BASE == NULL){      printk("ioremap error\n");      release_mem_region(0xC001C000, 0x1000);      device_destroy(led_class, led_dev_num);      class_destroy(led_class);      cdev_del(&led_cdev);      unregister_chrdev_region(led_dev_num, 1);      return -EFAULT;  }  GPIOCOUT = GPIOC_BASE + 0x00; // 0x00  GPIOCOUTENB = GPIOC_BASE + 0x04; // 0x04  GPIOCALTFN0 = GPIOC_BASE + 0x20; // 0x20  GPIOCALTFN1 = GPIOC_BASE + 0x24; // 0x24

8. 通过虚拟地址访问寄存器(裸机方法)

// LED的初始化,通过虚拟地址-->物理地址-->寄存器-->控制GPIO  *(unsigned int *)GPIOCALTFN0 &= ~(3<<24)|(3<<16)|(3<<14));  *(unsigned int *)GPIOCALTFN0 |= (1<<24)|(1<<16)|(1<<14);  *(unsigned int *)GPIOCALTFN1 &= ~(3<<2);  *(unsigned int *)GPIOCALTFN1 |= (1<<2);  *(unsigned int *)GPIOCOUTENB |=((1<<17) + (1<<12) + (1<<8) + (1<<7));  *(unsigned int *)GPIOCOUT |=((1<<17) + (1<<12) + (1<<8) + (1<<7));

思考

如何将一个数的第20位和第10位设置为1,而其他位保持不变?

a |= (1<<20);  a |= (1<<10);

等价于:

a |= ((1<<20) + (1<<10));
上一篇:《智能家居系统》6
下一篇:《智能家居系统》4

发表评论

最新留言

关注你微信了!
[***.104.42.241]2025年04月15日 21时23分21秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

第01问:MySQL 一次 insert 刷几次盘? 2019-03-13
非迅捷|PDF、Word、PPT、Excel、图片等互相在线转换:免费、简单、快速、零错误、无套路 2019-03-15
laravel server error 服务器内部错误 2019-03-15
一道简单的访问越界、栈溢出pwn解题记录 2019-03-15
2025最新智能优化算法:改进型雪雁算法(Improved Snow Geese Algorithm, ISGA)求解23个经典函数测试集 2023-01-23
Docker部署postgresql-11以及主从配置 2023-01-23
EnvironmentNotWritableError: The current user does not have write permissions to the target environm 2023-01-23
#C8# UVM中的factory机制 #S8.2.3# 重载sequence哪些情形 2023-01-24
java教师管理系统(ssm) 2023-01-24
el-select下拉框修改背景色 2023-01-24
elasticsearch 7.7.0 单节点配置x-pack 2023-01-24
Elasticsearch 之(16)_filter执行原理深度剖析(bitset机制与caching机制) 2023-01-24
Elasticsearch入门教程(Elasticsearch7,linux) 2023-01-24
ElasticSearch设置字段的keyword属性 2023-01-24
elasticsearch配置文件里的一些坑 [Failed to load settings from [elasticsearch.yml]] 2023-01-24
Elasticsearch面试题 2023-01-24
15个Python数据处理技巧(非常详细)零基础入门到精通,收藏这一篇就够了 2023-01-24
2024年全国程序员平均薪资排名:同样是程序员,为什么差这么多?零基础到精通,收藏这篇就够了 2023-01-24
2024大模型行业应用十大典范案例集(非常详细)零基础入门到精通,收藏这一篇就够了 2023-01-24
2024年全球顶尖杀毒软件,从零基础到精通,收藏这篇就够了! 2023-01-24