1. 程式人生 > >【複習】linux之LED驅動的裝置樹方式

【複習】linux之LED驅動的裝置樹方式

【前言】感謝陸老師

linux中led驅動(裝置樹)程式設計筆記

1.模組三要素

用到的標頭檔案

#include <linux/kernel.h>
#include <linux/module.h>

1.1 初始化模組

int __init led_init(void)
{
  printk("led_init!!!\n");
  return 0;
}
module_init(led_init);

1.2 退出模組

void __exit led_exit(void)
{
  printk("led_exit!!!\n");
}
module_exit
(led_exit);

1.3 模組簽證

MODULE_LICENSE("GPL"):

2.platform匯流排的三個步驟

用到的標頭檔案

#include <linux/platform_device.h>
#include <linux/of.h> //of_match_ptr

2.1 例項化platform_driver結構體

i)例項化回掉函式probe,併為成員.probe賦值
ii)例項化回掉函式remove,併為成員.remove賦值
iii)選擇一種匹配方式進行匹配(裝置樹,ACPI,名字,ID四選一)
const struct of_device_id led_dt_table[]={
  {
    .compatible
= "dt,led", }, { }, }; struct platform_driver pdev{ .probe = led_probe, .remove = led_remove, .driver = { .name = "led_platform",//名字必須加不然會報段錯誤(血的教訓T_T) .of_match_table = of_match_ptr(led_dt_table), }, };

2.2 註冊

//在led_init中註冊
platform_driver_register(&pdev);

2.3 登出

//在led_exit中登出
platform_driver_unregister(&pdev);

3.字元裝置框架流程(天龍八部)

用到的標頭檔案

#include <linux/fs.h>
#include <linux/cdev.h>

3.1 申請裝置號

#define DEV_MAJOR 500
#define DEV_MINOR 0
#define DEV_NUM   1
#define DEV_NAME  "ledx"
dev_t devno;
devno = MKDEV(DEV_MAJOR,DEV_MINOR);

3.2 註冊裝置號

ret = register_chrdev_region(devno,DEV_NUM,DEV_NAME);
if(ret < 0)
{
  printk("register_chrdev_region fail!!!\n");
  ret = alloc_chrdev_region(&devno,DEV_MINOR,DEV_NUM,DEV_NUM);
  if(ret < 0)
  {
    printk("alloc_chrdev_region fail!!!\n");
    return -1;
  }
}
else
  printk("register_chrdev_region success!!!\n");
printk("MAJOR=%d MINOR=%d\n",MAJOR(devno),MINOR(devno));

3.3 例項化cdev結構體

struct cdev led_cdev;

3.4 例項化file_operations結構體,並初始化

int led_open(struct inode *inode, struct file *file)
{
  printk("led_open!!!\n");
  return 0;
}
int led_close(struct inode *inode, struct file *file)
{
  printk("led_close!!!\n");
  return 0;
}
long led_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
  printk("led_ioctl!!!\n");
  return 0;
}
struct file_operations fops={
  .owner = THIS_MODULE,
  .open = led_open,
  .release = led_close,
  .unlocked_ioctl = led_ioctl,
};

3.5 初始化cdev結構體

cdev_init(&led_cdev,&fops);
led_cdev.owner = THIS_MODULE;

3.6 註冊cdev結構體

cdev_add(&led_cdev,devno,DEV_NUM);

3.7 登出cdev結構體

cdev_del(&led_cdev);

3.8 登出裝置號

unregister_chrdev_region(devno,DEV_NUM);

4.自動建立裝置節點

用到的標頭檔案

#include <linux/device.h>

4.1 建立class

#define   CLS_NAME  "cls_led"
struct class *cls;
cls = class_create(THIS_MODULE,CLS_NAME);
if(IS_ERR(cls))
{
  printk("class_create fail!!!\n");
  return -1;
}

4.2 建立裝置節點

#define   NODE_LED "led"
device_create(cls,NULL,devno,NULL,NODE_LED);

4.3 登出裝置節點

device_destroy(cls,devno);

4.4 登出class

class_destroy(cls);

5.修改裝置樹檔案

5.1 開啟裝置樹檔案

cd linux-3.14.2/arch/arm/boot/dts
vim xxx.dts

5.2 修改裝置樹檔案

在最後面新增

led_platform {
  compatible = "dt,led";
  reg = <暫存器地址 位元組數 暫存器地址 位元組數>;
}

6.make編譯並測試

Makefile

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /home/linux/kernel/linux-3.14.2/  這裡是編譯好並能正常執行的核心的路徑
PWD ?= $(shell pwd)
modules:
  $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
  $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
.PHONY: modules clean
else
  obj-m += led_dt.o 模組原始碼的.o檔案
endif

7.led部分程式

驅動的框架搭好了,現在開始LED部分的程式設計

//在全域性中
static volatile unsigned int * gpx2con;
static volatile unsigned int * gpx1con;
static volatile unsigned int * gpf3con;
static volatile unsigned int * gpx2dat;
static volatile unsigned int * gpx1dat;
static volatile unsigned int * gpf3dat;

//在led_probe函式中
gpx2con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);
gpx1con = ioremap(pdev->resource[1].start,pdev->resource[1].end - pdev->resource[1].start);
gpf3con = ioremap(pdev->resource[2].start,pdev->resource[2].end - pdev->resource[2].start);

gpx2dat = gpx2con + 1;
gpx1dat = gpx1con + 1;
gpf3dat = gpf3con + 1;

*gpx2con = (*gpx2con &~ (0xf << 28))|(0x1 << 28);
*gpx1con = (*gpx1con &~ (0xf <<  0))|(0x1 <<  0);
*gpf3con = (*gpf3con &~ (0xf << 16))|(0x1 << 16);
*gpf3con = (*gpf3con &~ (0xf << 20))|(0x1 << 20);

*gpx2dat = (*gpx2dat |  (0x1 << 7));
*gpx1dat = (*gpx1dat |  (0x1 << 0));
*gpf3dat = (*gpf3dat |  (0x1 << 4));
*gpf3dat = (*gpf3dat |  (0x1 << 5));

//在led_remove中
iounmap(gpx2con);
iounmap(gpx1con);
iounmap(gpf3con);

原理圖
schema

暫存器設定
register