1. 程式人生 > >LINUX的IIC驅動從這開始(三)

LINUX的IIC驅動從這開始(三)

這一篇主要是在友善的Smart210開發板上寫一個符合linux的iic驅動模型的裝置驅動程式,這樣能有一個更感性的認識。

開發環境介紹:

主機linux版本:fedora14

開發板:友善的Smart210

嵌入式linux版本:linux-3.0.8(友善光碟自帶的)

交叉編譯器:arm-linux-gcc-4.5.1(友善光碟自帶的)

硬體簡單介紹:

這是從友善的原理圖上截下的圖,這個圖沒有什麼複雜的,從圖可以看出來EEPROM是和s5pv210上的第0個iic介面卡連線的,但我是用第1個介面卡寫的,所以用線直接把介面卡1的引腳和EEPROM相連的,我這邊正好有這個專案需要,所以這樣寫了,你可以直接用介面卡0就行了,在寫驅動的時候我會說怎麼選iic介面卡0和iic介面卡1。

我們前面說過iic驅動模型是採用分層思想的,也即匯流排驅動和裝置驅動是分開的。那它們怎麼相互聯絡了?總得要一個什麼東西來做個匹配吧,就像以前的地下工作者,需要接頭暗號,要不然就亂套了,哈哈!iic匯流排和裝置之間是用名字做匹配的,那好了,那就先得把裝置的名字告訴匯流排吧,下面就是如何在總線上註冊裝置資訊了。

註冊裝置資訊

閱讀linux下的Documentation/i2c/instantiating-devices文件可以知道有兩種方式可以註冊,咱們只說前一種。開啟:linux-3.0.8/arch/arm/mach-s5pv210/mach-mini210.c這個.c檔案。就是在這個檔案中填寫咱們裝置的資訊的,這就是所說的bsp檔案。首先新增標頭檔案#include <linux/i2c/at24.h>

因為linux專門問iic介面的eeprom提供了相應的資料結構,要是不加,肯定要報錯。接下來新增如下資訊:

static struct at24_platform_data at24c08 = {
.byte_len = SZ_8K / 8,  //eeprom的容量大小(地址的總數)
.page_size = 16,        //eeprom的一頁中包含的位元組數
}; 

然後新增如下的資訊,主要把eeprom的資訊包裝成符合iic模型中的裝置資訊的格式

static struct i2c_board_info i2c_devices[] __initdata = {
    { 
        I2C_BOARD_INFO("at24c08b", 0x50),  //後邊的0x50是eeprom的地址,可能有人說應該是0xa0,但linux中需要的是7bit的地址,所以向右移一位,正好是0x50。當然了,最終會在linux的iic的讀函式和寫函式中變成0xa0和0xa1的格式
        .platform_data = &at24c08, 
    },
}; 

最後在mini210_machine_init函式中把上面寫的資訊註冊到iic總線上

static void __init mini210_machine_init(void)
{
        ...
	s3c_i2c2_set_platdata(&i2c2_data);
	i2c_register_board_info(0, mini210_i2c_devs0,
			ARRAY_SIZE(mini210_i2c_devs0));
	//i2c_register_board_info(1, mini210_i2c_devs1,
			//ARRAY_SIZE(mini210_i2c_devs1));  //把友善原來帶的遮蔽掉
        i2c_register_board_info(1, i2c_devices,            //仿照上面的新增如下的,主要這裡分為0、1和2,你可以修改介面卡0的,這樣不需要連線
                        ARRAY_SIZE(i2c_devices));
	i2c_register_board_info(2, mini210_i2c_devs2,
			ARRAY_SIZE(mini210_i2c_devs2));
        ...
}
這就算把裝置資訊註冊上了,重新編譯一下你的linux核心吧,然後把編譯好的核心燒進開發板,下面開始就是真真的驅動部分了。

裝置驅動編寫

首先咱們是用eeprom讀寫一些資料,資料量不會很大,所以它應該是個字元裝置,儘管它從iic驅動模型的角度說,是iic裝置,起始這並不矛盾。因為字元裝置裡包括了一部分的iic裝置,下面就是整個驅動了

#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<asm/uaccess.h>
#include<linux/i2c.h>
#include<linux/miscdevice.h>
#include<linux/slab.h>
#include<linux/list.h>
#include<linux/delay.h>

#define DEVICE_NAME  "at24c08"
//#define DEBUG

struct At24c08_dev
{
  char name[30];
  struct i2c_client *at24c08_client;
  struct miscdevice  at24c08_miscdev;  //因為本身是一個字元裝置,所以定義成一個雜項裝置
  unsigned short current_pointer;
};

struct At24c08_dev *At24c08_devp;  //定義一個全域性的,因為結構體力裡面的atc08_client需要從probe函式中獲得

//open函式主要是把全域性的At24c08_devp賦給file檔案的私有資料,這樣在其他的函式中呼叫方便
static int at24c08_open(struct inode *inode,struct file *file){
  
  file->private_data = At24c08_devp;
  return 0;
}

//這就是雜項裝置的read方法,跟普通的雜項裝置的read方法沒什麼不一樣的,只是呼叫的i2c_read_byte_data用來實際傳輸資料
static ssize_t
at24c08_read(struct file *file,char *buf,size_t count,loff_t *ppos)
{
  int i = 0;
  int transferred = 0;
  char value;
  char my_buff[50];

  struct At24c08_dev *dev = (struct At24c08_dev *)file->private_data;

  dev->current_pointer = 0;
  if(i2c_check_functionality(dev->at24c08_client->adapter,I2C_FUNC_SMBUS_READ_BYTE_DATA))
  {
    while(transferred < count)
    {
      msleep(10);    //這裡一定注意,要不這個延時加上,因為cpu速度比較快,eeprom速度比較慢,所以不加會出問題,我除錯時就出問題了,後加的
      value = i2c_smbus_read_byte_data(dev->at24c08_client,dev->current_pointer +i);
      my_buff[i++] = value;
      transferred ++;
    }
    if(!copy_to_user(buf,(void *)my_buff,transferred))
      printk("The data copying from kernel to userspace success!\n");
    else
      printk("Mybe some errors has occured\n");
    dev->current_pointer +=transferred;
  }
  return transferred;
}

//這就是註冊的雜項裝置的write方法
static ssize_t
at24c08_write(struct file *file,const char *buf,size_t count,loff_t *ppos)
{
  int i = 0;
  int transferred = 0;
  char  my_buff[50];

  struct At24c08_dev *dev = (struct At24c08_dev *)file->private_data;
  dev->current_pointer = 0;
  if(i2c_check_functionality(dev->at24c08_client->adapter,I2C_FUNC_SMBUS_BYTE_DATA))
  {
    if(!copy_from_user(my_buff,buf,count))
    {
      printk("The data copying from userspace to kernel success!\n");
      while(transferred < count)
      {
         msleep(10);//與上面的read函式中的類似
         i2c_smbus_write_byte_data(dev->at24c08_client,dev->current_pointer + i,my_buff[i]); //這個函式通過adapter的通訊方法把一個位元組的資料傳送          //到iic裝置中去
         i ++;
         transferred ++;
      }
      dev->current_pointer +=transferred;
    }
    else
      printk("Mybe some errors has occured\n");
  }
  return transferred;
}

static const struct file_operations at24c08_fops ={
  .owner = THIS_MODULE,
  .open = at24c08_open,
  .read = at24c08_read,
  .write = at24c08_write,
};

//當把裝置掛接到總線上時,只有當at24c08b_id所起的名字和之前註冊到匯流排當中的名字一樣時,才會呼叫probe函式。在probe函式裡會分配i2c_client,通過這個//i2c_client,當呼叫註冊的字元裝置時,iic介面卡就知道把資料跟那個iic裝置互動。
static int __devinit at24c08b_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
  int ret;
  
  #ifdef DEBUG
    printk("The routine of probe has started(for binding device)\n");
  #endif
  
  At24c08_devp = kmalloc(sizeof(struct At24c08_dev),GFP_KERNEL);
  if(!At24c08_devp)
  {
    return ret = -ENOMEM;
  }
  memset(At24c08_devp,0,sizeof(struct At24c08_dev));

  At24c08_devp->at24c08_client = client; //把分配的i2c_client賦給定義的全域性變數

  At24c08_devp->at24c08_miscdev.minor = MISC_DYNAMIC_MINOR; 
  At24c08_devp->at24c08_miscdev.name = DEVICE_NAME;
  At24c08_devp->at24c08_miscdev.fops = &at24c08_fops; //把雜項裝置的一些域用我們具體的方法定義
  
  ret = misc_register(&At24c08_devp->at24c08_miscdev);  //註冊雜項裝置
  #ifdef DEBUG
    printk("The driver of at24c08 has registered!\n");
  #endif
  return ret;
}

static int __devexit at24c08b_remove(struct i2c_client *client)
{
  misc_deregister(&At24c08_devp->at24c08_miscdev);
  #ifdef DEBUG
    printk("The routine of remove has implemented!\n");
  #endif
  return 0;
}
static const struct i2c_device_id at24c08b_id[]={
  {"at24c08",0},
  {}
};   //當把裝置掛接到總線上時,就呼叫這裡面的名字和註冊在匯流排裡的名字比對,如果一樣就會呼叫probe函式,同時給掛接的裝置分配i2c_client結構體

MODULE_DEVICE_TABLE(i2c,at24c08b_id);

static struct i2c_driver at24c08b_driver = {

  .driver = {
     .name = "at24c08",
     .owner=THIS_MODULE,
  },
  .probe = at24c08b_probe,
  .remove=__devexit_p(at24c08b_remove),
  .id_table =at24c08b_id,
};

static int __init at24c08b_init(void)
{
  #ifdef DEBUG
    printk(KERN_NOTICE"The driver of at24c08 is insmod!\n");
  #endif
  return i2c_add_driver(&at24c08b_driver);  //把iic裝置掛接到總線上
}

void at24c08b_exit(void)
{
  #ifdef DEBUG
    printk(KERN_NOTICE"at24c0b is rmmod!\n");
  #endif
  i2c_del_driver(&at24c08b_driver); //把iic裝置移除,這時會呼叫remove函式,所以在remove函式中一般會幹一些登出裝置的工作等
}


MODULE_DESCRIPTION("at24c08b eeprom driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("xie yingdong");

module_init(at24c08b_init);
module_exit(at24c08b_exit);
上面就是完整的eeprom驅動,當然驅動寫完了,需要寫個簡單的Makefile來編譯這個驅動,好吧,下面就是Makefile檔案的內容
obj-m:=eeprom-driver.o
KDIR = /tmp/linux-3.0.8  //這裡需要你根據自己的實際的linux原始碼放的位置來設定

all:
	$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
	@rm -rf eeprom-driver*.o
上面的Makefile檔案很是簡單,就不做過多的解釋了。當把驅動編譯好了,用動態的方式掛載到了linux核心上後,你還得做個簡單的測試程式,來驗證咱們寫的驅動工作是否正常,下面就直接貼出來吧。
#include<stdio.h>
#include<linux/types.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>

int main(void)
{
  int i;
  char value[19] = "eeprom-driver test!";
  char backvalue[19];

  int fd;
  fd = open("/dev/at24c08",O_RDWR);  //這裡的名字一定要和驅動裡註冊的雜項裝置的名字一樣,但跟iic裝置的名字無關,這裡只是正好取的一樣而已
  if(fd<0){
    printf("Open at24c08 device failed!\n");
    exit(1);
  }
  write(fd,value,19);
  printf("The string writing to eeprom : %s\n",value);
  printf("##################################################\n");
  sleep(1);
  read(fd,backvalue,19);
  printf("The string reading from eeprom : %s\n",backvalue);
  close(fd);
  return 0;}

哈哈,驅動就寫完了,我自己測試了,沒問題,你可以試試,下一篇我們會分析iic匯流排驅動。

相關推薦

LINUX的IIC驅動開始

這一篇主要是在友善的Smart210開發板上寫一個符合linux的iic驅動模型的裝置驅動程式,這樣能有一個更感性的認識。 開發環境介紹: 主機linux版本:fedora14 開發板:友善的Smart210 嵌入式linux版本:linux-3.0.8(友善光碟自帶的)

LINUX的IIC驅動開始

轉載地址:http://blog.csdn.net/xie0812/article/details/24291153 首先這篇文章主要介紹IIC匯流排的實現過程,怎麼說了,本人也是一個小菜鳥,可能有許多錯誤在裡面,如有大神發現,請指出來,多謝多謝! 注意:平臺還是和前面的一

Git開始

16px pop 普通 遠程服務 git clone one img 分享 模式 一、遠程倉庫管理   1、將本地內容推送到遠程庫   先關聯遠程庫,執行命令: git remote add origin https://github.com/Hollydan/gitsto

Java基礎總結0開始:Java反射原理

反射:Java虛擬機器允許執行時獲取類的資訊。  2.1 反射的常用方法:        a.forName(String className) :           返回與帶有給定字串名的類或介面相關聯的 Class 物件。        b.forName(String

Hyperledger Fabric 1.0 開始——環境構建內網/準離線

1:環境構建與測試 在本文中用到的宿主機環境是Centos ,版本為Centos.x86_647.2,通過Docker 容器來執行Fabric的節點,版本為v1.0。因此,啟動Fabric網路中的節點需要先安裝Docker、Docker-compose和Go語言環境,然後在網上拉取相關的Docker映象

JVM開始-常用垃圾回收器及原理

一、垃圾回收器有哪些 由於有些年輕代和老年代回收器無法相容,一般使用都是用以下四個組合。 檢視命令 java -XX:+Print

驅動開發 —— 開始1 配置vs20xx+wdkxx+winX環境並附錄常見問題解決方式

網上教程很多、如何去安裝如何去配置 但是也有些坑感覺並不是那麼的完善 wdk+vs下載連結:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk 只要wdk版本與sdk版本對應就行,不用在意是vs20xx 我這邊以

Kotlin開發基礎0開始

code ... 多說 lang 學習 function ria lis lac 為什麽學習kotlin kotlin在今年的Google IO 上正式被確認為Android的官方開發語言,想必原因大家也能猜到一二,一是Google與oracle關於Java相關的版權相關的

Hyperledger Fabric 1.0 開始——公網環境構建

1.3 項目 htm move 自己 lvm2 fast 情況 tor 1:環境構建 在本文中用到的宿主機環境是Centos ,版本為Centos.x86_647.2,通過Docker 容器來運行Fabric的節點,版本為v1.0。因此,啟動Fabric網絡中的節點需要先安

Hyperledger Fabric 1.0 開始——創建Fabric多節點集群

_id 測試 es2017 xtra 去掉 compose 多個 服務 執行命令 4:創建Fabric多節點集群 4.1、配置說明 首先可以根據官方Fabric自帶的e2e_cli列子中的集群方案來生成我們自己的集群,與案例不同的是我們需要把容器都分配到不同的服務器上,彼此

Mysql存儲過程0開始

mysql存儲過程1、首先你要明白,mysql也是一種語言,他也可以編寫程序,也是支持邏輯判斷,if,elseif,else,switch,while等等的判斷2、mysql賦值一個變量的值操作:set @a = 1; 查看這個變量為select @a;3、當你創建存儲過程的時候你要先選擇Mysql的數據庫,

[深度學習]實現一個博弈型的AI,五子棋開始1

com class svm 顏色 display 深度 images += have 好久沒有寫過博客了,多久,大概8年???最近重新把寫作這事兒撿起來……最近在折騰AI,寫個AI相關的給團隊的小夥伴們看吧。 搞了這麽多年的機器學習,從分

docker開始容器初體驗

osi build 技術分享 框架 log 註冊表 代碼 content doc 使用定義容器 Dockerfile Dockerfile定義容器內所需要的環境。對網絡接口和磁盤驅動器等資源的訪問在此環境中進行虛擬化,該環境與系統的其他部分隔離,因此您需要將端口映射到外部

docker開始堆棧初體驗,stacks

開始 services 信息 工作 run cer cal tail int 先決條件 安裝Docker 1.13或更高版本。 獲取Docker Compose,請參考第三節 按照第四節中的描述獲取Docker Machine。 在第二節中了解如何創建容器。

【視訊】Kubernetes1.12開始程式碼編譯到自動部署

作者: 李佶澳   轉載請保留:原文地址   釋出時間:2018/11/10 16:14:00 說明 kubefromscratch-ansible和kubefromscratch介紹 使用前準備

Centos Docker 開始2之 mssql 的資料庫檔案儲存在主機

Docker mmsql新建資料庫如果能夠把資料庫檔案儲存在主機上就好了,centos好像可以掛載的 docker 的 run 命令: -v ~/nginx/www:/www :將主機中專案的目錄www掛載到容器的/www 準備命令一下: docker run -e

從新撿起c++,stl開始1

容器包括關聯容器和順序容器。 關聯容器是通過鍵(key)儲存和讀取元素的,而順序容器則通過元素在容器中的位置順序儲存和訪問元素。 順序容器包括:vector  list  deque等,vector是表示一串連續的記憶體地址,基於陣列實現.   list是不連續的記憶體地

從新撿起c++,stl開始2

list: list類似於連結串列,它的儲存空間不是連續的,但是可以快速的插入和刪除,但是隨機訪問比較慢。 assign() 給list賦值        back()返回最後一個元素          begin()返回指向第一個元素的迭代器 clear() 清空li

Spring Cloud0開始服務治理:Spring Clound Eureka

一 . 完成一個Eureka(服務端)專案 1.登入網址 https://start.spring.io/ 選擇需要的依賴 -> Eureka Server 解壓至本地 2.通過IDEA 以Maven專案匯入。 (1)添加註解@EnableEurekaS

Hyperledger Fabric 1.0 開始——執行測試e2e

3:執行測試e2e 3.1、執行fabric-samples的問題說明 該問題說明能夠解決6.1、平臺特定使用的二進位制檔案配置第一步的問題。可以選擇繼續閱讀該說明,或者等參考到6.1小節時再反向閱讀本說明,具體在6.1中會重新指向本步驟。 一般情況下,我們會參照官網來完成第一個網路測試,在該線上文件中會讓我