嵌入式驱动开发案例实例过程

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 嵌入式驱动开发案例实例过程

嵌入式驱动开发案例实例过程

编写LED字符设备驱动实现Linux下控制LED灯的亮灭。

总结编写字符设备驱动的详细步骤

  • 先搭建驱动框架:

  • 头文件
  • 入口函数
  • 出口函数
  • 此时先不要写入口和出口
  • 各种该:

  • 该声明的声明
  • 该定义的定义
  • 该初始化的初始化
  • 先搞硬件后搞软件【变量】
  • 填充入口和出口

  • 先写注释
  • 后塞代码【体力活】
  • 最后编写各个接口函数


编写驱动程序 led_drv.c‘’

/*************************************************************************
  > File Name: led_drv.c
  > Author: 
  > Mail: 
  > Created Time: 2019年10月09日 星期三 20时03分33秒
 ************************************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/cdev.h> //struct cdev
#include <linux/fs.h> //struct file_operations
#include <mach/platform.h>
//声明描述LED硬件信息的数据结构
struct led_resource{
    char *name;//名称
    int gpio;//gpio 编号
};
//定义初始化硬件信息对象
static struct led_resource led_info[] = {
    {
        .name = "LED1",
        .gpio = PAD_GPIO_C + 12
    },
    {
        .name = "LED2",
        .gpio = PAD_GPIO_C + 17
    },
    {
        .name = "LED3",
        .gpio = PAD_GPIO_C + 11
    },
    {
        .name = "LED4",
        .gpio = PAD_GPIO_B + 26
    }
};
//open device func
//return -0 success -1-fail
static int led_open(struct inode *inode, struct file *file)
{
    int i = 0;
    for(i = 0; i < ARRAY_SIZE(led_info); i++)
    {
        gpio_set_value(led_info[i].gpio, 0);
        printk("%s:打开第%d个灯。\n", __func__, i+1);
    }
    return 0;
}
//close device func
static int led_close(struct inode *inode, struct file *file)
{
    int i = 0;
    for(i = 0; i < ARRAY_SIZE(led_info); i++)
    {
        gpio_set_value(led_info[i].gpio, 1);
        printk("%s:关闭第%d个灯。\n", __func__, i+1);
    }
    return 0;
}
//定义初始化硬件操作接口对象
static struct file_operations led_fops = {
    .open = led_open,//open device
    .release = led_close //close device 
};
//定义字符设备对象
static struct cdev led_cdev;
//定义设备号对象
static dev_t dev;
//init device func
static int led_init(void)
{
    //申请GPIO资源,配置为输出:1
    int i;
    for(i = 0; i < ARRAY_SIZE(led_info); i++)
    {
        gpio_request(led_info[i].gpio, led_info[i].name);
        gpio_direction_output(led_info[i].gpio, 1);
    }
    //申请设备号,
    alloc_chrdev_region(&dev, 0, 1, "myled");
    printk("major:%d, minor:%d\n", MAJOR(dev), MINOR(dev));
    //初始化字符设备对象,添加硬件操作接口
    cdev_init(&led_cdev, &led_fops);
    //想内核中注册字符设备对象
    cdev_add(&led_cdev, dev, 1);
    return 0;
}
//exit device func
static void led_exit(void)
{
    int i = 0;
    //从内核中卸载字符设备对象
    cdev_del(&led_cdev);
    //释放设备号
    unregister_chrdev_region(dev, 1);
    //输出1,释放GPIO资源,GPIO输出1为置空
    for(i = 0; i < ARRAY_SIZE(led_info); i++)
    {
        gpio_set_value(led_info[i].gpio, 1);
        gpio_free(led_info[i].gpio);
    }
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");


led_test.c

/*************************************************************************
  > File Name: led_test.c
  > Author: 
  > Mail: 
  > Created Time: 2019年10月09日 星期三 21时15分21秒
 ************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int fd;
    while(1){
        fd = open("/dev/myled", O_RDWR);
        if(fd < 0)
        {
            printf("open led device failed.\n");
            return -1;
        }
        sleep(3);
        close(fd);
    }
    return 0;
}


Makefile

obj-m += led_drv.o
all:
  make -C /home/ww/ww/ARM/kernel SUBDIRS=$(PWD) modules
clean:
  make -C /home/ww/ww/ARM/kernel SUBDIRS=$(PWD) clean


遇到的问题

共享库libgcc_s.so.1找不到的问题


  • 解决方法:添加相关共享库

在交叉编译工具下找到相应的共享库添加到文件系统下的/lib下。

//查看执行文件需要的依赖共享库
arm-cortex_a9-linux-gnueabi-readelf -d led_test
Dynamic section at offset 0x754 contains 25 entries:
  标记        类型                         名称/值
 0x00000001 (NEEDED)                     共享库:[libgcc_s.so.1]
 0x00000001 (NEEDED)                     共享库:[libc.so.6]
 0x0000000c (INIT)                       0x8420
 0x0000000d (FINI)                       0x8690
 0x00000019 (INIT_ARRAY)                 0x10748
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x1074c
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x00000004 (HASH)                       0x818c
 0x00000005 (STRTAB)                     0x82a4
 0x00000006 (SYMTAB)                     0x81d4
 0x0000000a (STRSZ)                      224 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x10844
 0x00000002 (PLTRELSZ)                   56 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x83e8
 0x00000011 (REL)                        0x83e0
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x83a0
 0x6fffffff (VERNEEDNUM)                 2
 0x6ffffff0 (VERSYM)                     0x8384
 0x00000000 (NULL)                       0x0



lsmod: /proc/modules: No such file or directory


  • 解决方法:修改启动脚本

/etc/init.d/rcS内容,解决/proc下的设备挂载问题。

/bin/mount -n -t ramfs /var
/bin/mount -n -t ramfs ramfs /tmp
/bin/mount -n -t sysfs none /sys
/bin/mount -n -t ramfs none /dev
/bin/mkdir /var/tmp
/bin/mkdir /var/modules
/bin/mkdir /var/run
/bin/mkdir /var/log
/bin/mkdir -p /dev/pts
/bin/mkdir -p /dev/shm
/sbin/mdev -s
/bin/mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
echo "" > /proc/sys/kernel/hotplug
mount -n -o mode=0755 -t tmpfs tmpfs /dev
mknod /dev/console c 5 1
mknod /dev/null c 1 3
echo "starting the hotplug events dispatcher udevd"
udevd --daemon
echo "synthesizing initial hotplug events"
udevtrigger
udevsettle --timeout=300
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mkdir /dev/shm
echo "hello world"


printk打印不显示问题


  • 内核显示级别设置问题,修改启动参数部分的内核显示等级为console = … debug即可。

有时调试内核模块,打印信息太多了,可以通过修改/proc/sys/kernel/printk文件内容来控制。

默认设置是7   4   1   7

cat /proc/sys/kernel/printk

7       4       1      7

该文件有四个数字值,它们根据日志记录消息的重要性,定义将其发送到何处。关于不同日志级别的更多信息,请查阅syslog(2)联机帮助。上面显示的4个数据分别对应:控制台日志级别:优先级高于该值的消息将被打印至控制台默认的消息日志级别:将用该优先级来打印没有优先级的消息最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)默认的控制台日志级别:控制台日志级别的缺省值 数值越小,优先级越高

查看申请到的设备号及创建相关设备文件


cd /home/drivers
insmod led_drv.ko //调用入口函数
cat /proc/devices //查看申请到的主设备号
mknod /dev/myled c 主设备号 0 //创建设备文件,代表LED0
./led_test
//open device fail 测试失败
cd /home/drivers
insmod led_drv.ko //调用入口函数
cat /proc/devices //查看申请到的主设备号
    character devices://当前系统支持的字符设备,主设备号和设备名称
    1   mem
    5   /dev/tty
    5   /dev/console
    5   /dev/ptmx
    ...
    244 myled  //LED驱动申请到的主设备号就是244,设备名称为myled
    ...
mknod /dev/myled c 主设备号 0 //创建设备文件,它代表LED设备。
./led_test //再次执行
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
6月前
|
vr&ar 图形学
2D丨3D元宇宙游戏系统开发详细规则/需求步骤/逻辑方案/源码步骤
Developing a 2D/3D metaverse game system involves multiple aspects, including game design, graphics engines, virtual world construction, social interaction, and economic systems. The following is a summary of a development plan:
|
4月前
|
设计模式 安全 关系型数据库
PHP开发涉及一系列步骤和技术
【7月更文挑战第2天】PHP开发涉及一系列步骤和技术
136 57
|
4月前
|
C++ 运维
开发与运维函数问题之C++类的简单示例如何解决
开发与运维函数问题之C++类的简单示例如何解决
59 10
|
5月前
|
存储 设计模式 C语言
技术笔记:QOM模型初始化流程
技术笔记:QOM模型初始化流程
30 0
|
6月前
|
搜索推荐 编译器 开发者
应用程序的运行:原理、过程与代码实践
应用程序的运行:原理、过程与代码实践
184 1
|
Java 数据库 开发者
自动配置要点解读
自动配置要点解读
|
NoSQL 分布式数据库 Redis
103 云笔记案例(代码流程分析)
103 云笔记案例(代码流程分析)
55 0
|
安全
项目实战典型案例19——临时解决方案和最终解决方案
项目实战典型案例19——临时解决方案和最终解决方案
144 0
【项目实战典型案例】19.临时解决方案和最终解决方案
【项目实战典型案例】19.临时解决方案和最终解决方案
|
SQL 消息中间件 Java
28个实战案例最终篇
28个实战案例最终篇
126 0