1. 程式人生 > >基於樹莓派Raspberry: 字元裝置核心驅動程式框架編寫

基於樹莓派Raspberry: 字元裝置核心驅動程式框架編寫

        之前寫了一篇移植2.4寸TFT驅動到樹莓派的文章,那篇博文中的驅動程式碼是國外大牛寫的,看了一下,還是有很多地方沒理解,是得好好再學習一下核心驅動的編寫,這裡就從字元裝置驅動開始,採用最簡單的LED驅動來建立核心驅動移植的驅動框架.

       個人原創,轉載請註明原文出處:      

      參考文章:

      核心驅動與普通微控制器模組驅動的差別就是在於,寫核心驅動的時候,要提供核心呼叫的介面,使核心能找到相應的驅動入口,使用者通過告訴核心要幹嘛,核心再去呼叫那個驅動,驅動的最底層和微控制器模組是一樣的,同樣是對GPIO的操作,配置輸入輸出,以及某些特殊的暫存器. LED的點亮就是對GPIO的操作 .

       對於ARM的GPIO呼叫需要通過IO對映的方法,要操作記憶體上對應的地址.

       對於bcm2708上的io對應關係,可以檢視bcm2835的手冊,和stm32基本上是一樣的,因為同為ARM體系:

              

     我參考的那部落格講這個比較清楚,可以參考下,由於樹莓派的核心以及很好的提供了GPIO呼叫的介面,即把記憶體操作封裝了很好,這裡就不像那篇部落格那樣再自己寫函式通過記憶體操作來進行GPIO操作,覺得有點麻煩,但是對於學好底層很有用.

  一  首先上個驅動程式

        這裡直接把該程式新增到核心的原始碼目錄裡面,也可在自己的目錄下,但是要寫Makefile.

        在/drivers/char/新建rasp_led.c,核心中的kconfig檔案和makefile檔案,參照前一篇文章

 led.c:

/********************************************************************/
/***************Rasp led 驅動程式************************************/
/***************作者: Embbnux Ji*************************************/
/***************部落格: http://blog.csdn.net/embbnux/ *****************/
/********************************************************************/

#include <linux/kernel.h>  
#include <linux/module.h>
#include <linux/device.h> 
#include <mach/platform.h>       
#include <linux/platform_device.h>
#include <linux/types.h>  
#include <linux/fs.h>   
#include <linux/ioctl.h>  
#include <linux/cdev.h>  
#include <linux/delay.h>  
#include <linux/uaccess.h>
#include <linux/init.h> 
#include <linux/gpio.h>

#define DEVICE_NAME "Pi_Led"
#define DRIVER_NAME "pi_led"

//class宣告核心模組驅動資訊,是UDEV能夠自動生成/dev下相應檔案
static dev_t pi_led_devno; //裝置號
static struct class *pi_led_class;
static struct cdev pi_led_class_dev;

struct gpio_chip *gpiochip;

#define led_pin 4  //gpio 4

//這部分函式為核心呼叫後open的裝置IO操作,和裸板程式一樣
int open_flag=0;
static int pi_led_open(struct inode *inode, struct file *filp)  
{   
    printk("Open led ing!\n");  
    if(open_flag ==0){
       open_flag =1;
       printk("Open led success!\n");
       return 0;
    }
    else{
       printk("Led has opened!\n");     
    }
    return 0;  
} 
//這部分函式為核心呼叫後ioctl的裝置IO操作,和裸板程式一樣
static long pi_led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{  
    switch(cmd){
      case 0: 
          gpiochip->set(gpiochip, led_pin, 0);
            printk("Led  up!\n");
           break;
      case 1:
            gpiochip->set(gpiochip, led_pin, 1);
            printk("Led  down!\n");
           break;
    }
   
   
    return 0;
} 

static int pi_led_release(struct inode *inode,struct file *file){
     printk("Led has release!\n");
     return 0;
}

//file_operations使系統的open,ioctl等函式指標指向我們所寫的led_open等函式,
//這樣系統才能夠呼叫
static struct file_operations pi_led_dev_fops = {  
   .owner          =THIS_MODULE,  
   .open	   =pi_led_open, 
   .unlocked_ioctl = pi_led_ioctl, 
   .release       = pi_led_release,
}; 

static int is_right_chip(struct gpio_chip *chip, void *data)
{

	if (strcmp(data, chip->label) == 0)
		return 1;
	return 0;
}

//核心載入後的初始化函式.
static int __init pi_led_init(void)
{
   struct device *dev;
   int major; //自動分配主裝置號
   major = alloc_chrdev_region(&pi_led_devno,0,1,DRIVER_NAME);
   //register_chrdev 註冊字元裝置使系統知道有LED這個模組在.
   
   cdev_init(&pi_led_class_dev, &pi_led_dev_fops);
   major = cdev_add(&pi_led_class_dev,pi_led_devno,1);
   //註冊class
   pi_led_class = class_create(THIS_MODULE,DRIVER_NAME);
   
   dev = device_create(pi_led_class ,NULL,pi_led_devno,NULL,DRIVER_NAME);
   
   gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
   gpiochip->direction_output(gpiochip, led_pin, 1);
   gpiochip->set(gpiochip, led_pin, 0);
   printk("pi led init ok!\n");
   return 0;
}
//核心解除安裝後的銷燬函式.
void pi_led_exit(void)
{
   gpio_free(led_pin);
   device_destroy(pi_led_class,pi_led_devno);
   class_destroy(pi_led_class);
   cdev_del(&pi_led_class_dev);
   unregister_chrdev_region(pi_led_devno, 1);
   printk("pi led exit ok!\n");
   
}

module_init(pi_led_init);
module_exit(pi_led_exit);

MODULE_DESCRIPTION("Rasp Led Driver");
MODULE_AUTHOR("Embbnux Ji < http://blog.csdn.net/embbnux >");
MODULE_LICENSE("GPL");

二  原始碼框架分析

    我們首先從核心模組的入口,module_init(pi_led_init)這個函式看起,可以看出初始化後呼叫pi_led_init這個函式.

//核心載入後的初始化函式.
static int __init pi_led_init(void)
{
   struct device *dev;
   int major; //自動分配主裝置號
   major = alloc_chrdev_region(&pi_led_devno,0,1,DRIVER_NAME);
   //register_chrdev 註冊字元裝置使系統知道有LED這個模組在.
   
   cdev_init(&pi_led_class_dev, &pi_led_dev_fops);
   major = cdev_add(&pi_led_class_dev,pi_led_devno,1);
   //註冊class
   pi_led_class = class_create(THIS_MODULE,DRIVER_NAME);
   
   dev = device_create(pi_led_class ,NULL,pi_led_devno,NULL,DRIVER_NAME);
   
   gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
   gpiochip->direction_output(gpiochip, led_pin, 1);
   gpiochip->set(gpiochip, led_pin, 0);
   printk("pi led init ok!\n");
   return 0;
}
   初始化時首先分配給這個函式裝置號,註冊該裝置,通過class註冊使能夠在/dev/目錄下自動生成相應的裝置檔案,使用者通過操作這個檔案,來告訴核心怎麼做.

   由於是字元裝置,所以對該檔案的操作通過open,write,ioctl等函式,所以要把這個函式和底層的操作函式對應起來,這就要用到file_operation這個結構體,來宣告:

//file_operations使系統的open,ioctl等函式指標指向我們所寫的led_open等函式,
//這樣系統才能夠呼叫
static struct file_operations pi_led_dev_fops = {  
   .owner          =THIS_MODULE,  
   .open	   =pi_led_open, 
   .unlocked_ioctl = pi_led_ioctl, 
   .release       = pi_led_release,
};

    這裡就讓open函式對應到pi_led_open函式,ioctl函式對應到pi_led_ioctl函式;

    然後我們就只需要編寫相應的pi_led_open以及pi_led_ioctl;這些函式裡面的操作就是最底層的GPIO操作,和微控制器是一樣的.

三  GPIO操作

     核心裡面的GPIO操作函式,被定義在#include <linux/gpio.h>,這個標頭檔案裡面,樹莓派官方做好了樹莓派的GPIO在核心裡面的註冊,所以呼叫gpio.h裡面的函式即可進行樹莓派的GPIO操作.

gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
    通過上面這個函式把核心的GPIO操作和BCM2708的GPIO操作關聯起來;

     bcm2708的操作可以檢視/arch/arm/mach-bcm2708/bcm2708_gpio.c檔案,具體也是對記憶體地址的操作:

#define GPIOFSEL(x)  (0x00+(x)*4)
#define GPIOSET(x)   (0x1c+(x)*4)
#define GPIOCLR(x)   (0x28+(x)*4)
#define GPIOLEV(x)   (0x34+(x)*4)
#define GPIOEDS(x)   (0x40+(x)*4)
#define GPIOREN(x)   (0x4c+(x)*4)
#define GPIOFEN(x)   (0x58+(x)*4)
#define GPIOHEN(x)   (0x64+(x)*4)
#define GPIOLEN(x)   (0x70+(x)*4)
#define GPIOAREN(x)  (0x7c+(x)*4)
#define GPIOAFEN(x)  (0x88+(x)*4)
#define GPIOUD(x)    (0x94+(x)*4)
#define GPIOUDCLK(x) (0x98+(x)*4)

    這裡定義的就是相應的GPIO暫存器的地址.

四  測試程式

    ssh進入樹莓派,在主目錄下新建led.c

 #include<stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
 int main(int argc, char **argv)
 {
    int on;
    int led_no;
    int fd;
    int i;

    fd = open("/dev/pi_led", 0);
    if (fd < 0) {
       fd = open("/dev/pi_led", 0);
    }
    if (fd < 0) {
      perror("open device led");
      exit(1);
    }
    for(i=0;i<=20;i++){
      on = i%2;
      ioctl(fd, on, led_no);
      sleep(1);
    }
    close(fd);
    return 0;
 }




相關推薦

基於樹莓Raspberry: 字元裝置核心驅動程式框架編寫

        之前寫了一篇移植2.4寸TFT驅動到樹莓派的文章,那篇博文中的驅動程式碼是國外大牛寫的,看了一下,還是有很多地方沒理解,是得好好再學習一下核心驅動的編寫,這裡就從字元裝置驅動開始,採用最簡單的LED驅動來建立核心驅動移植的驅動框架.        個人原創,

基於樹莓raspberry: 移植 2.4寸TFT顯示屏以及原始碼分析

有了樹莓派,但是沒有hdmi顯示器,這是個蛋疼的事,但是樹莓派就是樹莓派,他的GPIO管腳就是我們發揮想象力的地方.可以通過它的GPIO管腳來驅動一個顯示屏.GOOGLE了一下,這個專案有個老外做好了,而且提供了patch檔案,很容易就能移植到核心裡面去.這裡我就在這裡

基於樹莓Raspberry Pi)平臺的MQ-2煙霧報警系統以及結合Zabbix監控的實現(一)

Raspberry Pi Zabbix和嵌入式系統的結合 Python3 樹莓派和MQ-2氣體檢測 一、前期準備 達成目標:   利用Rapberry Pi 驅動MQ-2煙霧報警模塊,對信息進行采集和提取,而後Zabbix監控系統來收集和處理信息采集到的信息。

基於樹莓Raspberry Pi)平臺的智能家居實現(一)----繼電器模塊,DHT11模塊

Raspberry 繼電器模塊 DHT11溫濕度模塊 智能家居 前言:    ??其實做這個智能家居系統我還是因為學校的畢業設計,距離上篇文章發布已經過去了20多天了,之前想著只是做一個煙霧報警,然後通過Zabbix進行報警,但是通過這20多天的設計,我發現實現報警的功能其

樹莓Raspberry Pi實戰之命令列下實現USB儲存裝置自動掛載

簡單介紹實現命令列下USB儲存裝置自動掛載的方法,Linux gnome/kde視窗環境下有移動儲存的管理程式,可以實現自動掛載移動儲存裝置,但是在命令列下 通常需要用mount命令手動掛載USB儲存裝置。   通過給linux下的裝置管理服務udev新增規則配置檔案,可以實現命令列下U

Linux裝置驅動程式學習(基於2440的GPIO字元裝置驅動)

GPIO驅動程式如下:  #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <li

基於stm32f429的uclinux-W5500網路裝置核心驅動

       之前那篇寫w5500驅動只是單純的應用程式驅動,雖然可以實現一定的目的,但是沒有充分利用到linux的核心,在一些應用場合就顯得不合時宜,於是就進行w5500網路裝置核心驅動的學習,幸運的是w5500網路裝置驅動的檔案是在4.8版本的linux核心

樹莓raspberry使用spi介面oled顯示屏:基於python和c

  之前寫過一篇樹莓派使用12864介面的2.3寸顯示屏的文章,當時用的是並口,佔用了太多的gpio資源,於是考慮使用spi介面的顯示屏,最近的專案正好用到了spi介面的oled的顯示屏,於是考慮把它用到樹莓派上,先介紹下這款螢幕:0.96寸的oled屏,spi介面ssd1

樹莓raspberry pi配置

hang 開啟 鍵盤布局設置 jin -i icon ccf ron load (1)國際化語言 樹莓派初裝系統之後,首次啟動會出現“raspi-config”工具,如下圖:(若不是初次啟動,在命令模式下,請輸入 sudo raspi-

樹莓Raspberry命令行配置無線網絡連接

lin 成功 兩個 隱藏 無線網絡連接 studio sch ip地址 add 前言: 樹莓派有多種聯網的方式,通過有線網或者通過無線網。通過有線網連接是比較簡單的,在開啟dhcp的路由器下,直接插上網線就可以聯網,本文介紹樹莓派無線聯網的方式。再沒聯網的情況下,如果沒有屏

基於樹莓2代的DIY無線路由器

edi 無線網卡 AD uri sub 效果 eth ipv4 ant 最近手上多了一個樹莓派2代,於是折騰就這麽開始了。 因為總是得要個顯示屏或者路由器或者插根網線才能玩,有點麻煩,所以有了此文。 設備清單: 樹莓派2代 EDUP EP-N8508GS無線網卡(USB)

樹莓 Raspberry Pi 啟用 root 登陸賬戶

樹莓派使用指南樹莓派 Raspberry Pi 啟用 root 登陸賬戶樹莓派系統使用的linux是debian系統,所以樹莓派啟用root和debian是相同的。debian裏root賬戶默認沒有密碼,但賬戶鎖定。當需要root權限時,由默認賬戶經由sudo執行,Raspberry pi 系統中的Raspb

樹莓 Raspberry PI基礎

數字 -c san 波特率 fin block dddddd exp org 樹莓派 Raspberry PI基礎 官網網址:https://www.raspberrypi.org 下載地址:https://www.raspberrypi.org/downloads/ 官方

樹莓 Raspberry PI之GPIO

document ble lock back ocs 輸入輸出 indent 1.5 res 樹莓派 Raspberry PI之GPIO 樹莓派各版本硬件原理圖:https://www.raspberrypi.org/documentation/hardware/raspb

樹莓Raspberry Pi和Micro:bit做一個自拍器

clear microsoft 編程 告訴 pac 文本編程 裝配 -a 按鈕 在這個項目中,我們將使用Python來構建一個由Micro:bit觸發樹莓派Raspberry Pi和相機模塊的自拍器。這是開始使用硬件和簡單文本編程的好方法。 我們將學習: 如何設置Raspb

樹莓(Raspberry Pi 3) - 系統燒錄及系統使用

         樹莓派(Raspberry pi)是一塊整合度極高的ARM開發板,不僅包含了HDMI,RCA,CSI,HDMI,GPIO等埠,還支援藍芽以及無線通訊。由於Raspberry Pi幾乎是為Linux而生的一款卡片式微型電腦,所以R

基於樹莓的語音對話機器人

第一部分程式碼 arecord -D "plughw:1" -f S16_LE -r 16000 -d 3 /home/pi/Desktop/voice.wav 第二部分程式碼 # coding: utf-8 import sys import json import url

樹莓(Raspberry Pi 3)的系統燒錄及使用

今天我們將詳細一步一步地講解樹莓派3的燒錄和使用。樹莓派3是整合度很高的較為先進的ARM開發板,功能豐富,我們先來看一下它的結構以及40個GPIO口分別的用處,以免接線的時候搞錯。 APP開發、一元建站、貨運APP、冷練車APP、直播系統、小程式開發,找上海捌

樹莓 raspberry pi 能象Arduino一樣外接感測器,控制器嗎

                答案是YES樹莓派板子上有26只管腳,這些GPIO (general purpose I/O) 包括 SPI, I2C, 串列埠 UART, 3V3 and 5V 電源。國內論壇就有文章“Raspbmc 設定紅外線接收器”介紹如何為樹莓派安裝Raspbmc 作業系統,安裝配置紅

樹莓Raspberry Pi的嵌入式QT平臺

在樹莓派上設計桌面應用一般情況會依賴於X11環境,如果是Windows平臺,就目前而言,我們也可以選擇Windows 10 IoT環境進行開發。UWP目前也支援在樹莓派2上進行部署。至於常見的Linux X11或者Wayland環境,相比之下比較冗餘,但是QT Emebe