目标:驱动LED

对于一个外设,一般包含输入时钟、配置寄存器。

步骤一:查看硬件原理图,找到要是用的LED所连接的引脚号,查看手册控制GPIO的寄存器,寄存器用法,GPIO外设的时钟使能。

①查看原理图:

就一个工作指示灯。连接在引脚PS2_DATA/GPIO8_A1_U,输出低电平时点亮,高电平熄灭。

②查找手册:

摘出以下内容

时钟控制:

CRU基地址:FF760000

GPIO8_A0到GPIO8_A7共8个引脚。

控制引脚的寄存器:

data register (GPIO_SWPORTA_DR),

direction control register (GPIO_SWPORTA_DDR),The direction of the external I/O pad is controlled by a write to the Porta data direction register (GPIO_SWPORTA_DDR)。

External data are input on the external data signal, GPIO_EXT_PORTA。这3个分别是数据、方向控制、外部IO方向控制。

查找寄存器地址:GPIO1~8 are in PD_ALIVE subsystem. There are 9 GPIOs (GPIO0 ~ GPIO8), and each of 
them has same register group. Therefore, 9 GPIOs’ register groups have 9 different base addr。

GPIO8基地址:FF7F_0000。

IO复用设置寄存器:GRF_GPIO8A_IOMUX  offset:0x0080  W  0x00000000  GPIO8A iomux control 。此寄存器描述如下,

基地址:GRF:FF77_0000。

硬件设置流程:使能外设时钟-》设置IO-》控制IO

步骤二:建立驱动原文件,映射寄存器物理地址到虚拟地址,通过操作虚拟地址来控制GPIO。

①在open函数中初始化(因为设备在打开后才会被使用,所以在打开时初始化),获取寄存器映射地址、设置GPIO模式、使能GPIO时钟 。

②在write函数操作GPIO输出高低电平。

//源码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h> #define CHRDEVBASE_MAJOR           200                /* 主设备号    */
#define CHRDEVBASE_NAME            "RK3288_GPIO8A"         /* 设备名       */#define GRF_BASE                   (0xFF770000)
#define GRF_GPIO8A_IOMUX_OFFSET    (0x0080)
#define GRF_GPIO8A_IOMUX_ADDR      (GRF_BASE + GRF_GPIO8A_IOMUX_OFFSET)
//#define CRU_BASE                   (0xFF760000)
#define CRU_CLKGATE14_CON_OFFSET   (0x0198)
#define CRU_CLKGATE14_CON_ADDR     (CRU_BASE + CRU_CLKGATE14_CON_OFFSET)#define GPIO8_BASE                 (0xFF7F0000)
#define GPIO8_SWPORTA_DR_OFFSET     (0x0)
#define GPIO8_SWPORTA_DDR_OFFSET    (0x4)
#define GPIO8_INTEN _OFFSET         (0x30)#define GPIO8_SWPORTA_DR_ADDR       (GPIO8_BASE + GPIO8_SWPORTA_DR_OFFSET)
#define GPIO8_SWPORTA_DDR_ADDR      (GPIO8_BASE + GPIO8_SWPORTA_DDR_OFFSET)static unsigned int *pGRF_GPIO8A_IOMUX      = NULL;
static unsigned int *pCRU_CLKGATE14_CON     = NULL;static unsigned int *pGPIO8_SWPORTA_DR      = NULL;
static unsigned int *pGPIO8_SWPORTA_DDR     = NULL;static char readbuf[100];                /* 读缓冲区    */static int rk_led_open (struct inode *node, struct file *pfile)
{pGRF_GPIO8A_IOMUX = ioremap(GRF_GPIO8A_IOMUX_ADDR,  4);pCRU_CLKGATE14_CON = ioremap(CRU_CLKGATE14_CON_ADDR,  4);pGPIO8_SWPORTA_DR = ioremap(GPIO8_SWPORTA_DR_ADDR,  4);pGPIO8_SWPORTA_DDR = ioremap(GPIO8_SWPORTA_DDR_ADDR,  4);//set io8 function as gpio*pGRF_GPIO8A_IOMUX |= 0xFFFF << 15;  //enable write*pGRF_GPIO8A_IOMUX &= ~(0x3<<2);     //set A1 as gpio*pGRF_GPIO8A_IOMUX &= ~(0xFFFF << 15); //disable write//set io direction as output*pGPIO8_SWPORTA_DDR |= 1<<1;//set io data*pGPIO8_SWPORTA_DR |= 1<<1;//enable gpio clk*pCRU_CLKGATE14_CON |= 0xFFFF << 15; //enable write*pCRU_CLKGATE14_CON |= 1<<8;         //enable gpio8 clk*pCRU_CLKGATE14_CON &= ~(0xFFFF << 15);//disable writereturn 0;
}//num:which
//out:electric level   1:high 0:low
void gpio8A_set_output(int num,int out)
{*pGPIO8_SWPORTA_DDR |= 1<<num;if(out == 1)*pGPIO8_SWPORTA_DR |= 1<<num;if(out == 0)*pGPIO8_SWPORTA_DR &= ~(1<<num);
}//buf[0]: Ax pin buf[1] = electric level;
static ssize_t rk_led_write (struct file *pfile, const char __user * buf, size_t size, loff_t *offset)
{   int retvalue;retvalue = copy_from_user(readbuf, buf, size);if( retvalue == 0){printk("kernel senddata ok!\r\n");if(readbuf[0]>7)return 1;if(readbuf[1] != 0){gpio8A_set_output(readbuf[0],1);}else{gpio8A_set_output(readbuf[0],0);}}else{printk("kernel senddata failed!\r\n"); }return 1;
}static int rk_led_release (struct inode *node, struct file *pfile)
{printk("led_gpio release!\r\n");return 0;
}static struct file_operations my_led_fops = {.owner = THIS_MODULE,.open = rk_led_open,//.read = rk_led_read,.write = rk_led_write,.release = rk_led_release,
};static int __init  rk_led_drv_init(void)
{//platform_driver_register(&rk_led_drv);int retvalue = 0; printk(KERN_INFO "%s\n", __func__);/* 注册字符设备驱动 */ retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &my_led_fops);if(retvalue < 0){ /* 字符设备注册失败,自行处理 */ printk( KERN_DEBUG"hello_module register fail!\n");}else{printk(KERN_DEBUG"GPIO_LED, I'm ready!\n");}return 0;
}static void __exit rk_led_drv_exit(void)
{printk(KERN_INFO "%s\n", __func__);//platform_driver_unregister(&rk_led_drv);unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
}module_init(rk_led_drv_init);
module_exit(rk_led_drv_exit);
MODULE_LICENSE("GPL");

makefile

KERN_DIR = /home/book/100ask_firefly-rk3288/linux-4.4all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m += rk_led_drv.o

编译完成,得到如下文件

将驱动文件复制到nfs文件夹。

安装驱动模块*.ok文件命令 :insmod *.ko

查看安装的模块:lsmod

到此,可以看见模块安装完成,但是/dev下没有对应的设备文件,由于驱动代码没有创建设备,这里需要在命令行手动创建设备

mknod /dev/RK3288_GPIO8 c 200 0

mknode命令安装失败,修改源码,在运行时生成设备文件。

增加源码如下

//修改初始化函数如下
struct class *led_class;
static int __init  rk_led_drv_init(void)
{//platform_driver_register(&rk_led_drv);int retvalue = 0; printk(KERN_INFO "%s\n", __func__);/* 注册字符设备驱动 */ retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &my_led_fops);if(retvalue < 0){ /* 字符设备注册失败,自行处理 */ printk( KERN_DEBUG"hello_module register fail!\n");}else{printk(KERN_DEBUG"GPIO_LED, I'm ready!\n");}led_class = class_create(THIS_MODULE, "my_led_class");device_create(led_class, NULL, MKDEV(CHRDEVBASE_MAJOR, 0), NULL, "my_led%d",0); return 0;
}

安装驱动模块*.ok文件命令 :insmod *.ko。dev目录下出现设备my_led0.

步骤三:编写应用层程序

源码:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h" //exe  /dev/devname gpio_num  0orXstatic char usrdata[50] = {0}; /* * @description    : main 主程序 * @param - argc   : argv 数组元素个数 * @param - argv   : 具体参数 * @return          : 0 成功;其他 失败 */
int main(int argc, char *argv[])
{ int fd, retvalue; char *filename; char readbuf[100], writebuf[100]; if(argc != 4){ printf("Error Usage!\r\n"); return -1; } filename = argv[1]; /* 打开驱动文件 */ fd  = open(filename, O_RDWR); if(fd < 0){ printf("Can't open file %s\r\n", filename);return -1; } if(atoi(argv[2]) < 8){ /* 向设备驱动写数据 */ usrdata[0] = atoi(argv[2]);if(atoi(argv[3]) != 0)usrdata[1] = 1;elseusrdata[1] = 0;memcpy(writebuf, usrdata, sizeof(usrdata)); retvalue = write(fd, writebuf, 2); if(retvalue < 0){ printf("write file %s failed!\r\n", filename);} } /* 关闭设备 */ retvalue = close(fd); if(retvalue < 0){ printf("Can't close file %s\r\n", filename); return -1; } return 0;
}

编译程序 名RK3288_GPIO_CTRL。

使用应用程序控制led:

应用程序第一个参数是指定的设备,第二个参数gpio编号,三是点亮或熄灭。

熄灭命令./RK3288_GPIO_CTRL /dev/my_led0 1 1

点亮命令./RK3288_GPIO_CTRL /dev/my_led0 1 0

点灯成功。芜湖

rk3288实践:第一个嵌入式Linux驱动程序相关推荐

  1. 嵌入式Linux驱动程序开发

    嵌入式Linux驱动程序开发 1.设备驱动程序的概念... 2 2.处理器与设备间数据交换方式... 2 21.查询方式... 2 2.2.中断方式... 3 2.3.直接访问内存(DMA)方式... ...

  2. 嵌入式Linux驱动程序

    转自:一塌糊涂的BLOG 在Linux操作系统下有3类主要的设备文件类型:块设备.字符设备和网络设备.这种分类方法可以将控制输入/输出设备的驱动程序与其他操作系统软件分离开来. 字符设备与块设备的主要 ...

  3. 使用eclipse集成开发环境开发第一个嵌入式Linux驱动

    1.准备工作 首先得安装好gcc工具链,以及开发环境,可以看看我的前面的几步. 还得编译好内核,一般开发板都带了,现在我还不知道配置内核,只能按照开发板默认的去编译,编译前需要先编译uboot,建议像 ...

  4. 我的第一个嵌入式linux驱动3_完善2

    /* 写此文章主要是记录自己的学习笔记,和一些函数的注释,方便自己以后和别人参考,自己是个初学者,有很多不足之处,不喜勿喷,仅供参考 */ /*目的:通过应用函数来实现在点灯 *2016年5月1日08 ...

  5. ARM嵌入式linux开发入门视频教程

    mini2440体验篇1-2 拿到开发板之后,我要做什么事情 -2 mini2440体验篇2-1 嵌入式系统基础.PPT mini2440体验篇3-1 熟悉mini2440开发板硬件资源 mini24 ...

  6. 嵌入式linux的驱动程序

    [摘至嵌入式linux设备驱动开发详解] 1.4 嵌入式linux驱动程序 1.4.1 嵌入式linux的内核空间和用户空间 目前,各种处理器都能防止资源的未经授权访问,包括嵌入式处理器.一般都是给C ...

  7. 嵌入式Linux系统工程师都学些什么?

    教学大纲 课程安排 课 程 简 介 第一周 嵌入式C语言编程基础     主要介绍在嵌入式开发编程中C语言的重要概念和编程技巧中的重点难点,以复习串讲和实例分析的形式,重点介绍包括函数与程序结构,指针 ...

  8. 分享:嵌入式Linux入门学习指导

    很多嵌入式linux初学者对嵌入式linux学习十分迷茫,不知道该怎么一步步学习,嵌入式linux学习方法,学习的流程步骤以及学习过程中需要看哪些好的书籍.下面凌阳教育嵌入式培训网就为大家整理的嵌入式 ...

  9. 目录 - 《ARM嵌入式Linux系统开发从入门到精通》 - 免费试读 - book.csdn.net

    第一部分 ARM Linux系统移植 第1章 嵌入式系统开发入门...... 2 1.1 嵌入式系统介绍..... 2 1.1.1 嵌入式系统概述..... 2 1.1.2 嵌入式系统组成..... ...

最新文章

  1. LeetCode——Find the Difference
  2. php 取url根域名,php中取得URL的根域名的代码
  3. 滴滴是如何搭建起PB级数据中台的?
  4. a标签传值到另一个页面_vue-router页面传值及接收值
  5. 第三章 熟悉常用的HDFS操作
  6. mysql无关子查询_mysql中相关,无关子查询,表与表之间的关系以及编码和乱码的解决...
  7. 【python】整理的 Python 库
  8. URAL 1004 Sightseeing trip
  9. Linux 命令(105)—— service 命令
  10. 一个很可爱的二次元风格的个人技术博客
  11. 新型冠状病毒数据可视化分析
  12. 千年鸿蒙盼尔来兮,古言爱情誓言
  13. 教你在线网页批量免费拆分PDF
  14. MySQL函数关键字(五)子查询 ANY/SOME/ALL/IN/EXISTS/USING
  15. 黑苹果NVIDIA显卡驱动程序【WebDriver-378.05.05.25f16 +支持 macOS 10.12.6 Sierra (16G2016)版本】
  16. mysql 添加/删除列(column)
  17. 【应用多元统计分析】——第三章(1)
  18. Proteus 8 Professional 创建新项目
  19. Java忽略返回字段
  20. Python学习 Day28 JS函数(二)

热门文章

  1. 全球及中国安防电源行业竞争状况及供需前景预测报告(新版)2022-2027
  2. Win10 + Ubuntu双系统U盘安装,详细过程(带图解)
  3. Echarts 思维导图(二)
  4. 数学与神经网络关系大吗,神经网络与算法的关系
  5. 微信公众号开发---微信开发学习路线(及供参考)
  6. Eclipse出现An error has occurred问题解决方案
  7. python制作生日礼物_TurnipBit:和孩子一起动手DIY“滚动”的生日礼物
  8. 什么是证书透明度(Certificate Transparency,CT)?
  9. 小数点怎么进行进制转换?
  10. java中将毫秒值转换为小数点形式(几点几格式)的方法