嵌入式Linux字符设备入门之--LED驱动详解

嵌入式Linux驱动开发(二)——字符设备驱动之控制LED 关于驱动程序框架,请参考: 嵌入式Linux驱动开发(一)——字符设备驱动框架入门 同时,在这篇文章里面留下了两个问题,现在先来解决其中的第一个问题,就是如何用驱动程序来操作需要操作的硬件。 关于开发板点亮LED的问题,在这里简单介绍一下,也可以参考之前我写的文章: 嵌入式Linux开发——裸板程序点亮开发板上的LED灯 首先为了操作硬件,看接线原理图是必不可少的一步,这几个LED的原理图相对来说 阅读详情

一、实验环境

PC机:Redhat9.0  内核版本:Linux2.4

开发板:FL2440     内核版本:Linux2.6      CPU:s3c2440

二、实验步骤

1、硬件连接。

       FL2440有四个可编程控制的共阳极LED,分别与CPU的GPB5,GPB6,GPB8,GPB10相连。

2、驱动程序

       由于是刚刚接触驱动,实在是不知道怎么写,只好用开发板自带的led驱动了(手动分配设备号),我还写了一个自动分配设备号的程序。我所做的就是分析这个程序,争取把它给读懂。下面是开发板led驱动的源程序。

程序里的注释是我参考网上的资料自己加的,主要参考的是mini2440的led驱动程序的注释内容,不知道对不对!

a:手动分配设备号

/*-----------------s3c2440_leds.c--------------------------------*/

#include <linux/config.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/devfs_fs_kernel.h>//?????????????????

#include <linux/miscdevice.h>//???????????????

#include <linux/delay.h>

#include <asm/irq.h>

#include <asm/arch/regs-gpio.h>

#include <asm/hardware.h>

 

#define DEVICE_NAME       "leds"    //设备名

#define LED_MAJOR 231           //主设备号

 

 

/*----------------------------------------------------------------------------------*/

//定义引脚的寄存器数组(无符号长整形,对应于引脚的地址)

 

static unsigned long led_table [] = {  

       S3C2410_GPB5,

       S3C2410_GPB6,

       S3C2410_GPB8,

       S3C2410_GPB10,

      

};

 

/*---------------------------------------------------------------------------------*/

//定义引脚功能,为输出(无符号整形)

 

static unsigned int led_cfg_table [] = {      

 

       S3C2410_GPB5_OUTP,            //0x01<<10 defined in  refg-gpio.h

       S3C2410_GPB6_OUTP,

       S3C2410_GPB8_OUTP,

       S3C2410_GPB10_OUTP,

};

 

/*-------------------------------------------------------------------------------*/

//测试程序调用此函数,实现对led的控制

//设备节点,文件描述符,LED灯状态,LED灯编号四个命令参数

 

static int s3c2440_leds_ioctl(

                                       struct inode *inode,

                                       struct file *file,

                                       unsigned int cmd,

                                       unsigned long arg)

{                                   

       switch(cmd) {

       case 0:

       case 1:

              if (arg > 4)

              {

                     return -EINVAL; /* Invalid argument,非法参数 */

 

              }

 

             

               //低电平有效,用户程序传来的cmd取反

 

              s3c2410_gpio_setpin(led_table[arg], !cmd);//设置数据寄存器gpbdat,cmd的非

              return 0;

       default:

              return -EINVAL;

       }

}

/*----------------------------------------------------------------------------*/

 

 

static struct file_operations s3c2440_leds_fops = {

       .owner    =     THIS_MODULE,

       .ioctl       =     s3c2440_leds_ioctl,

};

/*---------------------------------------------------------------------------*/

//初始化函数

static int __init s3c2440_leds_init(void)

{

       int ret;

       int i;

 

       ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c2440_leds_fops);//注册设备

       if (ret < 0)

       {

         printk(DEVICE_NAME " can't register major number/n");

         return ret;

       }

 

       devfs_mk_cdev(MKDEV(LED_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);

       //上句的作用是干甚??????

 

       for (i = 0; i < 4 ; i++)

       {

        /*-----------------------------------------------------------------------*/    

              //设置GPIO对应的配置寄存器GPIOCON为输出状态,在头文件

              //linux-2.6.12/include/asm-arm/arch-s3c2410/hardware.h中的函数原型为:

        //extern void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function);

          

              s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);

 

        /*-----------------------------------------------------------------------*/

        //设置GPIO对应的数据寄存器GPIODAT为高电平

              //在模块加载结束后,四个LED应该是全部都是不发光状态

 

              s3c2410_gpio_setpin(led_table[i], 1);

 

              /*-----------------------------------------------------------------------*/

       }

 

       printk(DEVICE_NAME " initialized/n");

       return 0;

}

/*------------------------------------------------------------------------------------*/

//注销设备

static void __exit s3c2440_leds_exit(void)

{

       devfs_remove(DEVICE_NAME);

       unregister_chrdev(LED_MAJOR, DEVICE_NAME);

}

 

/*------------------------------------------------------------------------------------*/

module_init(s3c2440_leds_init);//执行insmod是调用该函数

module_exit(s3c2440_leds_exit);//执行rmmod是调用该函数

/*-----------------------------------------------------------------------------------*/

 

b:自动分配设备号

#include <linux/config.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/devfs_fs_kernel.h>

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <asm/arch/regs-gpio.h>

#include <asm/hardware.h>

 

#define DEVICE_NAME "leds"

 

static unsigned long led_table [] = {

                     S3C2410_GPB5,

                     S3C2410_GPB6,

                     S3C2410_GPB8,

                     S3C2410_GPB10,

};

 

static unsigned int led_cfg_table [] = {

                     S3C2410_GPB5_OUTP,

                     S3C2410_GPB6_OUTP,

                     S3C2410_GPB8_OUTP,

                     S3C2410_GPB10_OUTP,

};

 

static int s3c2440_leds_ioctl(

                     struct inode *inode,

                     struct file *file,

                     unsigned int cmd,

                     unsigned long arg)

{

switch(cmd) {

              case 0:

              case 1:

       if (arg > 4)

              {

                         return -EINVAL;

            }    

     s3c2410_gpio_setpin(led_table[arg], !cmd);

     return 0;

       default:

            return -EINVAL;

              }

              }

                    

static struct file_operations s3c2440_leds_fops = {

              .owner = THIS_MODULE,

              .ioctl = s3c2440_leds_ioctl,

};

 

static struct miscdevice misc = {

              .minor = MISC_DYNAMIC_MINOR,

              .name = DEVICE_NAME,

              .fops = &s3c2440_leds_fops,

};

 

static int __init s3c2440_leds_init(void)

{

       int ret;

       int i;

 

       for (i = 0; i < 4; i++)

       {

       s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);

       s3c2410_gpio_setpin(led_table[i], 1);

       }

       ret = misc_register(&misc);

 

       printk (DEVICE_NAME"/tinitialized/n");

       return ret;

}

 

static void __exit s3c2440_leds_exit(void)

{

       misc_deregister(&misc);

}

module_init(s3c2440_leds_init);

module_exit(s3c2440_leds_exit);

这个程序我没有做注释,参考上一个程序吧!不过这里有些需要说明的,用insmod命令能加载到内核,用lsmod命令也可以查到,但是运行测试程序时却出不来该有的现象,并且提说找不到文件。(下面做了解释)

3、编译驱动程序

写个Makefile文件编译,2.6版本的内核好像只能这么编译驱动,不能在命令行里一步步的通过命令编译,不过2.4内核的可以。(不知道是不是这么回事?)

当然Makefile我也不会写,用的板子带的那个!

 

#Makefile for s3c2440_leds.c

obj-m :=s3c2440_leds.o

KERNELDIR ?=/usr/linux-2.6.12

PWD := $(shell pwd)

default:

       $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

 

经make后生成了四个文件

           

我只知道s3c2440_leds.ko是我想要的,其他三个不道是干嘛的?

4、将驱动加载到内核

       有两种方式:

一是把驱动程序直接编译进内核;细节问题我没有怎么研究,大致上就是写好驱动程序之后,放到内核代码的目录里,当然还得修改几个文件,与内核一同编译。这样内核启动后会自动加载,免去很多麻烦,但要占用资源!

二是动态的将驱动加载进内核;这种方式比较适合实验开发,挺方便的,就是每次用到驱动是都要手动加载,我采用的就是这种方式!不过有这种方式有关前题,就是你的内核必须支持动态的加载模块,这个在你配置内核的时候要选择此项支持!

到你的内核的目录下,make menuconfig,第三项“Loadable module support”,选上“Enable loadable module support”,其他的随便,我就多选了个“Module unloading”。然后,重新编译内核。(参考的别人的,我没弄过!!!)

   下面是加载的过程:

       将PC机的根目录挂到开发板的tmp目录下

       #mount 192.168.2.32:/ /tmp

       加载驱动模块

       #insmod s3c2440_led.ko

       可以看看有没有加载上

       #lsmod

                     Module           size                usedby                  tainted:P

                     S3c2440_leds 1664         0            

       Ok了,加载成功,现在可以写个测试程序测试一下了!

       顺便说一下如何卸载驱动模块吧!

       #rmmod s3c2440_leds.ko

 

       注意:如果你加载的是a驱动程序,可以到/dev目录下查看一下,发现在字符设备中有关叫leds的设备,设备号231。但是如果你加载的是b驱动程序,你在/dev目录下是找不到leds的,不过你可以进的一个叫misc的目录,原来leds在这里,我试验内核分给它的设备号是63,不道为啥没有想a程序那样直接出现在/dev目录下。这也带来了麻烦,就是写测试程序时,针对两种不同的驱动程序,测试程序也略有不同!(后面会说,见测试程序)

 

5、测试程序

/*-----------------------led--------------------------------------------*/

/*这个程序实现的是跑马灯*/

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/ioctl.h"

#include "stdlib.h"

#include "termios.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "sys/time.h"

int main(void)

{

      

       int on=1;

       int led;

       int fd;

       fd=open("/dev/leds",0);

/*如果有加载的是b驱动程序模块,这里要写出:fp=open(“/dev/misc/leds”,0)*/

       if(fd<0)

       {

              perror("open device leds");

              exit(1);

       }

       printf("leds test show,press ctrl+c to exit /n");

       while(1)

       {

              for(led=0;led<4;led++)

              {

                     ioctl(fd,on,led);

                     usleep(50000);

              }

              on=!on;

       }

       close(fd);

       return 0;

}

6、编译测试程序,完成测试

将测试程序交叉编译,在板子上执行:

#./led

看效果吧,漂亮!!!!!

三、实验总结:

这个实验其实做了好多天,在做之前也做了很多准备,因为以前也没接触过这些,也没有什么基础,确实挺费劲的!而且我之前看的都是2.4内核的驱动,2.4的驱动与2.6的驱动相比有很多不同的地方,这是值得注意的。我还是觉得2.4内核的驱动更容易理解!

       通过这个实验对驱动的编写有了一些了解,当然这只是入门的入门,还需更加努力。由于才粗学浅,一定有很多不对的地方,当然也有很多不懂得地方,请多多指教!

ZYNQ之嵌入式驱动开发——字符设备驱动 本文是在ZYNQ 7020平台上进行的Linux驱动实验,通过写一个字符设备驱动程序控制开发板上的LED,然后通过应用程序向里面写值控制LED的亮灭。 阅读详情

相关推荐

Linux字符设备驱动模型

本文详细解析Linux字符设备驱动的三种实现模型。首先介绍字符设备驱动的基本概念和核心结构体file_operations,它是连接应用层与硬件的关键接口。然后重点讲解杂项设备驱动模型,其主设备号固定为10,开发简单但资源有限;早期经典模型支持多个设备但需手动创建设备节点;Linux 2.6标准模型采用dev_t设备号和cdev结构体,资源充足且扩展性强。文章通过完整代码示例演示各模型的实现方法,包括驱动注册、设备节点创建等关键流程,并提供三种模型的对比总结,建议按照杂项→早期→标准的顺序学习。

Arockdj的博客 614

字符设备驱动的加载与卸载

字符设备驱动的加载与卸载

wojiaxiaohuang2014的博客 349

Linux 驱动开发基础知识——编写LED驱动程序(三)

我们基于 Hello 驱动程序先写出最简单的 LED 驱动程序

嵌入式技术开发 7513

objdump命令解析

objdump objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。   参数选项:  --archive-headers  -a  显示档案库的成员信息,类似ls -l将lib*.a的信息列出。  -b bfdname  --target=bfdname  指定目标码格式。这不是必须的,objdump能自动...

u014770486的专栏 4254

冒险岛脚本函数大全:cm.sendYesNo和cm.warp的20个实战用法

本文深入解析了冒险岛脚本中cm.sendYesNo和cm.warp两个核心函数的20个实战应用场景。从基础的确认对话框交互、条件判断,到复杂的装备强化系统、动态传送与副本入口逻辑,文章通过大量代码示例展示了如何组合运用这两个函数,实现从简单NPC到复杂任务系统的进阶脚本开发,并分享了状态管理、错误处理与性能优化等最佳实践。

t4y5u6i7o的博客 1061

【时间序列预测算法】——Holt-Winters算法介绍及代码实现

背景 一阶二阶三阶指数平滑方法拟合效果均较好,通过结合数据自然属性,数据自身、趋势变化、季节性,提出了这三个角度的衡量量,可以说,理解起来更自然、更亲切。 公式 一阶: sts_tst​是t时刻对xtx_txt​的估计值,这里只有一个参数α,是数据本身的平滑属性的体现。 二阶: 引入了时间趋势变量btb_tbt​,优化了一下一阶的公式。同时还可以预测t+m时刻的值: 三阶: 又引入了季节性周期描述变量ctc_tct​,优化了一下二阶的公式。同时还可以预测t+m时刻的值: NOTE:ct

BlackValley的博客 1万+

linux驱动 不创建dev,字符设备驱动,但是在/dev下没有相应的设备

在网上找到解决办法有两种,手动mknod创建一个设备或者自动生成一个设备。具体代码如下1.添加头文件#include2.修改函数intVirtualDisk_init(void),添加“自动创建设备”,代码如下:structclass*virtual_disk_class; /*byyinhf.*//*设备驱动模块加载函数*/intVirtualDisk_init(void){...

weixin_31226805的博客 1747

从CPRI到eCPRI:手把手教你用Wireshark分析基站前传协议帧结构

本文手把手教你使用Wireshark工具,深入解析从CPRI到eCPRI的基站前传协议帧结构。通过搭建分析环境、编写解析脚本,并对比两者在时延管理、帧格式及数据封装上的差异,为无线通信领域的研发与测试人员提供了一套实用的逆向分析与故障排查方法。

x8y9z0的博客 605

Linux2.6.32内核笔记(1)内核模块helloworld

什么是内核模块?内核模块和应用程序的区别

Deep_l_zh的博客 1274

微信小程序利用Echarts加载中国地图的步骤

微信小程序如何利用Echarts加载地图?可按照如下四个步骤进行。(阅读时间大概3分钟)。

成都都成 5276

5-EP4CE10F17C8-引脚配置

EP4CE10F17C8-引脚配置

weixin_44479192的博客 720

LINUX内核模块

《深入LINUX设备驱动程序nzh》    内核模块可以在系统运行期间动态扩展系统功能而无须重新启动系统,更无须为这些新增的功能编译一个新的系统内核映像。...

qq_20095107的博客 767

Android15音频进阶之AudioReach架构agmcap bringup调试(一百二十三)

本篇目的:Android15音频进阶之AudioReach架构agmcap录音调试。

Android系统攻城狮 2231

韦东山嵌入式Liunx入门驱动开发一(Hello 驱动编程、字符设备、GPIO基础知识、LED驱动、总线设备驱动模型)

对于硬件资源,用结构体来表示;对于硬件的操作,用结构体表示。在内核中有一个虚拟的总线,它有2个链表结构,左边是设备Dev链表,右边是驱动Drv链表。左边的设备链表和右边的驱动链表会进行一一比较(通过platform_match函数),若匹配成功,就会调用中的probe函数。如何进行匹配?(2) platform_device结构体(3) platform_driver结构体总线设备驱动编写程序步骤(1) 分配、设置、注册结构体,在里面定义所用资源,指定设备名字。

DZJ_Nirvana 3127

cursor AI代码编辑器IDE对接deepseek模型api;字节ai编辑器trae

参考: https://www.cursor.com/ https://platform.deepseek.com/https://www.cursor.com/下载安装后,注册账号登录即可 再高级里选择设置 再模型这注册模型: 模型名称一定要填正确,url地址填好,最后填上秘钥验证

weixin_42357472的博客 1万+

基于深度学习的堰塞坝表层颗粒物质识别研究

本文提出一种基于深度学习的堰塞坝表层颗粒物质智能识别方法,针对颗粒分布散乱、背景复杂的问题,采用"大颗粒逐个识别、密集小颗粒分块识别"策略。结合Mask R-CNN、SLIC和AlexNet构建识别模型,通过无人机影像实现自动识别。实验表明该方法精度超过90%,具备迁移能力。文章提供了Python实现代码,涵盖数据预处理、SLIC分割、模型构建(Mask R-CNN和AlexNet)及训练流程,为堰塞坝应急抢险提供技术支持。

huanghm88的专栏 181

嵌入式Linux_字符设备驱动开发_驱动开发案例_LED编写代码详解(直接操作寄存器版)

代码解析

qq_45083790的博客 1431

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Linux 驱动学习笔记 ——(1)字符设备驱动

字符设备是 Linux 驱动中最基本的一类设备驱动,字节设备就是按照字节流来读写的设备,常见的字符设备包括:LED、蜂鸣器、按键、I2C 以及 SPI 等。

Super辉sir的博客 5036

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【Linux 字符设备驱动:控制 LED 灯】

本文介绍了如何在Linux系统下开发字符设备驱动来控制LED灯。

highersister的博客 1143

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

嵌入式Linux_字符设备驱动开发流程_最简单|最完整|最入门|零基础|保姆级|驱动开发案例_LED_(通过TFTP启动Linux内核和NFS挂载根文件系统+LED驱动编写+亮灭应用测试)

通过TFTP启动Linux内核和NFS挂载根文件系统,编写了一个简单的字符设备驱动-LED驱动,并且还编写了一个简单的用户测试APP,来使用我们所编写的驱动,尝试点亮led成功。

qq_45083790的博客 1949

嵌入式Linux LED驱动开发

本笔记详细介绍了基于IMX6ULL处理器的Linux LED驱动开发全过程。内容涵盖硬件寄存器配置、地址映射、时钟使能、GPIO方向设置等底层操作,以及字符设备驱动注册、文件操作函数实现等核心模块。驱动通过ioremap映射物理地址,使用readl/writel操作寄存器,实现了LED开关控制功能。配套用户空间测试程序通过设备节点进行LED控制验证。文章还提供了完整的Makefile示例和常见问题解决方法,为嵌入式Linux驱动开发提供了实用参考。

愿青春不负梦想 832

嵌入式Linux驱动开发 - 新字符设备LED驱动

本文介绍了基于IMX6ULL开发板的LED字符设备驱动程序开发。该驱动通过操作GPIO寄存器控制LED状态,实现了完整的字符设备驱动框架,包括设备号分配、cdev注册、文件操作接口等关键功能。硬件初始化部分详细展示了GPIO时钟使能、复用配置、方向设置等底层操作。配套应用程序提供了用户控制接口,支持通过命令行参数控制LED开关。该驱动采用新字符设备注册方式,代码结构清晰,可作为嵌入式Linux驱动开发的参考实例。

愿青春不负梦想 1074

嵌入式Linux开发——字符设备驱动开发

本文介绍了嵌入式Linux字符设备驱动开发的核心流程。首先讲解了字符设备的概念,即以字节流形式进行数据传输的设备类型,并通过设备文件与应用程序交互。随后详细阐述了驱动开发的关键步骤:1)实现file_operations结构体中的操作函数(open/read/write等);2)注册字符设备;3)硬件初始化。以LED驱动为例,提供了完整的代码模板,标注出通用部分和需要修改的硬件相关部分。最后给出了配套测试应用程序的开发方法,实现LED状态控制、切换和闪烁等功能。文章强调驱动开发需遵循Linux框架,重点在于

m0_75029474的博客 986

嵌入式Linux驱动——2 LED驱动(简单、面向对象、分层、分离)

字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI, LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。

m0_74375685的博客 1072

韦东山嵌入式linux系列-LED 驱动程序框架

③ 实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体;④ 把 file_operations 结构体告诉内核:通过 register_chrdev 函数;⑤ 谁来注册驱动程序?需要一个入口函数:安装驱动程序时,就会去调用这个入口函数;⑥ 有入口函数就应该有出口函数:卸载驱动程序是,出口函数调用unregister_chrdev;

StudyWinter的博客 1215

Linux驱动开发之LED驱动

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define S3C2410_GPFCON S3C2410_GPIOREG(0x50) #define S3C2410_GPFDAT S3C2

suaoyang的专栏 2161
azeale321
博客等级 码龄17年 24粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值