嵌入式Linux开发: 编写EEPROM驱动_采用杂项字符设备框架

简介: 嵌入式Linux开发: 编写EEPROM驱动_采用杂项字符设备框架

一、环境介绍

宿主机: Redhat 6.3  32位

目标开发板型号: 友善之臂Tiny4412

目标开发板操作系统:  使用busybox制作,内核使用官方3.5内核。

使用的编译器版本:  友善之臂光盘里自带的交叉编译器版本4.5.1

二、EEPROM芯片介绍

image.png

image.png

image.png

三、驱动代码

  下面使用IIC子系统框架编写EEPROM的驱动,驱动端代码使用杂项字符设备框架,并且实现了文件指针偏移;在应用层可以将EEPROM当做一个255字节大小的文件进行编程读写。

3.1 设备端代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
static struct i2c_client *i2c_dev=NULL;
static struct i2c_adapter *adap=NULL;
static struct i2c_board_info info=
{
  .type="tiny4412_eeprom",
  .addr=0x50, /*设备地址*/
};
static int __init tiny4412_drv_init(void)
{ 
  /*根据总线编号获取是适配器*/
  adap=i2c_get_adapter(0);
  /*注册IIC设备端*/
  i2c_dev=i2c_new_device(adap,&info);
    printk("IIC设备端: 驱动安装成功\n");
    return 0;
}
static void __exit tiny4412_drv_cleanup(void)
{
  /*注销IIC设备*/
  i2c_unregister_device(i2c_dev);
  i2c_put_adapter(adap);
    printk("IIC设备端: 驱动卸载成功\n");
}
module_init(tiny4412_drv_init);    /*驱动入口--安装驱动的时候执行*/
module_exit(tiny4412_drv_cleanup); /*驱动出口--卸载驱动的时候执行*/
MODULE_LICENSE("GPL");  /*设置模块的许可证--GPL*/

3.2 驱动端代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
static struct work_struct work;
static struct i2c_client *eeprom_client;
#define MAX_SIZE 255  //EEPROM大小
#define EEPROM_PAGE  16 //页字节大小
static u8 eeprom_buff[255];
static int tiny4412_open(struct inode *inode, struct file *file)
{
  printk("tiny4412_open-->ok\n");
  return 0;
}
static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t size, loff_t *seek)
{ 
  unsigned long err;
  //判断位置是否超出范围
  if(*seek+size>MAX_SIZE)
  {
    size=MAX_SIZE-*seek;
  }
  //读取数据
  i2c_smbus_read_i2c_block_data(eeprom_client,*seek,size,eeprom_buff);
  err=copy_to_user(buf,eeprom_buff,size);
  if(err!=0)return -1;
  *seek+=size;
  return size;
}
static ssize_t tiny4412_write(struct file *file, const char __user *buf, size_t size, loff_t *seek)
{
  size_t write_ok_cnt=0;
  unsigned long err;
  err=copy_from_user(eeprom_buff,buf,size);
  if(err!=0)return -1;
  //判断位置是否超出范围
  if(*seek+size>MAX_SIZE)
  {
    size=MAX_SIZE-*seek;
  }
  int write_byte=0;
  u8 *write_p=eeprom_buff;
  while(1)
  {
    if(size>EEPROM_PAGE)
    {
      write_byte=EEPROM_PAGE;
      size-=EEPROM_PAGE;
    }
    else
    {
      write_byte=size;
    }
    //写数据
    i2c_smbus_write_i2c_block_data(eeprom_client,*seek,write_byte,write_p);
    *seek+=write_byte;
    write_p+=write_byte;
    write_ok_cnt+=write_byte;  //记录写成功的字节数
    //等待写完成
    msleep(10);
    if(write_byte==size)break; //写完毕
  }
  return write_ok_cnt;
}
/*
filp:待操作的设备文件file结构体指针
off:待操作的定位偏移值(可正可负)
whence:待操作的定位起始位置
返回:返回移位后的新文件读、写位置,并且新位置总为正值
定位起始位置
  SEEK_SET:0,表示文件开头
  SEEK_CUR:1,表示当前位置
  SEEK_END:2,表示文件尾
*/
static loff_t tiny4412_llseek(struct file *filp, loff_t offset, int whence)
{
  loff_t newpos = 0;
  switch(whence)
  {
    case SEEK_SET:
      newpos = offset;
      break;
    case SEEK_CUR:
      newpos = filp->f_pos + offset;
      break;
    case SEEK_END:
      if(MAX_SIZE+offset>=MAX_SIZE)
      {
        newpos=MAX_SIZE;
      }
      else
      {
        newpos = MAX_SIZE + offset;
      }
      break;
    default:
      return -EINVAL;//无效的参数
  }
  filp->f_pos = newpos;
  return newpos;
}
static int tiny4412_release(struct inode *inode, struct file *file)
{
  printk("tiny4412_release-->ok\n");
  return 0;
}
static struct file_operations fops=
{
  .open=tiny4412_open,
  .read=tiny4412_read,
  .write=tiny4412_write,
  .release=tiny4412_release,
  .llseek=tiny4412_llseek
};
/*
Linux内核管理驱动---设备号
设备号是一个unsigned int 的变量--32位。
设备号=主设备号+次设备号
*/
static struct miscdevice misc=
{
  .minor = MISC_DYNAMIC_MINOR,  /*次设备号填255表示自动分配     主设备号固定为10*/
  .name = "tiny4412_eeprom",  /*/dev目录下文件名称*/
  .fops = &fops, /*文件操作接口*/
};
static int tiny4412_probe(struct i2c_client *client, const struct i2c_device_id *device_id)
{
  printk("probe调用成功:%#X\n",client->addr);
  eeprom_client=client;
  /*1. 杂项设备的注册函数*/
  misc_register(&misc);
  return 0;
}
static int tiny4412_remove(struct i2c_client *client)
{
  /*2. 杂项设备的注销函数*/
  misc_deregister(&misc);
  printk("remove调用成功.\n");
  return 0;
}
static struct i2c_device_id id_table[]=
{
  {"tiny4412_eeprom",0},
  {}
};
static struct i2c_driver drv=
{
  .probe=tiny4412_probe,
  .remove=tiny4412_remove,
  .driver=
  {
    .name="eeprom_iic"
  },
  .id_table=id_table
};
static int __init tiny4412_drv_init(void)
{ 
  /*注册IIC驱动端*/
  i2c_add_driver(&drv);
    printk("IIC驱动端: 驱动安装成功\n");
    return 0;
}
static void __exit tiny4412_drv_cleanup(void)
{
  /*注销IIC驱动端*/
  i2c_del_driver(&drv);
    printk("IIC驱动端: 驱动卸载成功\n");
}
module_init(tiny4412_drv_init);    /*驱动入口--安装驱动的时候执行*/
module_exit(tiny4412_drv_cleanup); /*驱动出口--卸载驱动的时候执行*/
MODULE_LICENSE("GPL");  /*设置模块的许可证--GPL*/

3.3  应用端测试代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define EEPROM_DEV "/dev/tiny4412_eeprom"
int main(int argc,char **argv)
{
    /*1. 打开设备文件*/
    int fd=open(EEPROM_DEV,O_RDWR);
    if(fd<0)
    {
        printf("%s 设备驱动打开失败.\n",EEPROM_DEV);
        return 0;
  }
    /*3.读写数据*/
  unsigned char buff[255];
  int cnt;
  int i;
  for(i=0;i<255;i++)buff[i]=i;
  cnt=write(fd,buff,255);
    printf("write成功:%d Byte\n",cnt);
  //偏移文件指针
  lseek(fd,SEEK_SET,0);
  unsigned char buff_r[255];
  cnt=read(fd,buff_r,255);
  printf("read成功:%d Byte\n",cnt);
  for(i=0;i<cnt;i++)
  {
    printf("%d ",buff_r[i]);
  }
  printf("\n");
    return 0;
}

3.4 编译Makefile

KER_DRI=/home/wbyq/work/linux-3.5/linux-3.5
all:
  make -C $(KER_DRI) M=`pwd` modules
  cp *.ko /home/wbyq/work/rootfs/code -f
  make -C $(KER_DRI) M=`pwd` modules clean
  arm-linux-gcc app.c -o app
  cp app /home/wbyq/work/rootfs/code -f
  rm app -f
obj-m += iic_drv.o
obj-m += iic_dev.o

3.5 测试效果

image.png

目录
相关文章
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
116 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
3月前
|
存储 Linux 开发工具
如何进行Linux内核开发【ChatGPT】
如何进行Linux内核开发【ChatGPT】
|
4月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
58 6
|
3月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
1月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
107 8
|
1月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
375 6
|
1月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
86 3
|
1月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
79 2
|
19天前
|
Linux Shell
Linux 10 个“who”命令示例
Linux 10 个“who”命令示例
49 14
Linux 10 个“who”命令示例
|
8天前
|
Ubuntu Linux
Linux 各发行版安装 ping 命令指南
如何在不同 Linux 发行版(Ubuntu/Debian、CentOS/RHEL/Fedora、Arch Linux、openSUSE、Alpine Linux)上安装 `ping` 命令,详细列出各发行版的安装步骤和验证方法,帮助系统管理员和网络工程师快速排查网络问题。
76 20