rk3288实践:第一个嵌入式Linux驱动程序
目标:驱动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驱动程序相关推荐
- 嵌入式Linux驱动程序开发
嵌入式Linux驱动程序开发 1.设备驱动程序的概念... 2 2.处理器与设备间数据交换方式... 2 21.查询方式... 2 2.2.中断方式... 3 2.3.直接访问内存(DMA)方式... ...
- 嵌入式Linux驱动程序
转自:一塌糊涂的BLOG 在Linux操作系统下有3类主要的设备文件类型:块设备.字符设备和网络设备.这种分类方法可以将控制输入/输出设备的驱动程序与其他操作系统软件分离开来. 字符设备与块设备的主要 ...
- 使用eclipse集成开发环境开发第一个嵌入式Linux驱动
1.准备工作 首先得安装好gcc工具链,以及开发环境,可以看看我的前面的几步. 还得编译好内核,一般开发板都带了,现在我还不知道配置内核,只能按照开发板默认的去编译,编译前需要先编译uboot,建议像 ...
- 我的第一个嵌入式linux驱动3_完善2
/* 写此文章主要是记录自己的学习笔记,和一些函数的注释,方便自己以后和别人参考,自己是个初学者,有很多不足之处,不喜勿喷,仅供参考 */ /*目的:通过应用函数来实现在点灯 *2016年5月1日08 ...
- ARM嵌入式linux开发入门视频教程
mini2440体验篇1-2 拿到开发板之后,我要做什么事情 -2 mini2440体验篇2-1 嵌入式系统基础.PPT mini2440体验篇3-1 熟悉mini2440开发板硬件资源 mini24 ...
- 嵌入式linux的驱动程序
[摘至嵌入式linux设备驱动开发详解] 1.4 嵌入式linux驱动程序 1.4.1 嵌入式linux的内核空间和用户空间 目前,各种处理器都能防止资源的未经授权访问,包括嵌入式处理器.一般都是给C ...
- 嵌入式Linux系统工程师都学些什么?
教学大纲 课程安排 课 程 简 介 第一周 嵌入式C语言编程基础 主要介绍在嵌入式开发编程中C语言的重要概念和编程技巧中的重点难点,以复习串讲和实例分析的形式,重点介绍包括函数与程序结构,指针 ...
- 分享:嵌入式Linux入门学习指导
很多嵌入式linux初学者对嵌入式linux学习十分迷茫,不知道该怎么一步步学习,嵌入式linux学习方法,学习的流程步骤以及学习过程中需要看哪些好的书籍.下面凌阳教育嵌入式培训网就为大家整理的嵌入式 ...
- 目录 - 《ARM嵌入式Linux系统开发从入门到精通》 - 免费试读 - book.csdn.net
第一部分 ARM Linux系统移植 第1章 嵌入式系统开发入门...... 2 1.1 嵌入式系统介绍..... 2 1.1.1 嵌入式系统概述..... 2 1.1.2 嵌入式系统组成..... ...
最新文章
- LeetCode——Find the Difference
- php 取url根域名,php中取得URL的根域名的代码
- 滴滴是如何搭建起PB级数据中台的?
- a标签传值到另一个页面_vue-router页面传值及接收值
- 第三章 熟悉常用的HDFS操作
- mysql无关子查询_mysql中相关,无关子查询,表与表之间的关系以及编码和乱码的解决...
- 【python】整理的 Python 库
- URAL 1004 Sightseeing trip
- Linux 命令(105)—— service 命令
- 一个很可爱的二次元风格的个人技术博客
- 新型冠状病毒数据可视化分析
- 千年鸿蒙盼尔来兮,古言爱情誓言
- 教你在线网页批量免费拆分PDF
- MySQL函数关键字(五)子查询 ANY/SOME/ALL/IN/EXISTS/USING
- 黑苹果NVIDIA显卡驱动程序【WebDriver-378.05.05.25f16 +支持	macOS 10.12.6 Sierra (16G2016)版本】
- mysql 添加/删除列(column)
- 【应用多元统计分析】——第三章(1)
- Proteus 8 Professional 创建新项目
- Java忽略返回字段
- Python学习 Day28 JS函数(二)
热门文章
- 全球及中国安防电源行业竞争状况及供需前景预测报告(新版)2022-2027
- Win10 + Ubuntu双系统U盘安装,详细过程(带图解)
- Echarts 思维导图(二)
- 数学与神经网络关系大吗,神经网络与算法的关系
- 微信公众号开发---微信开发学习路线(及供参考)
- Eclipse出现An error has occurred问题解决方案
- python制作生日礼物_TurnipBit:和孩子一起动手DIY“滚动”的生日礼物
- 什么是证书透明度(Certificate Transparency,CT)?
- 小数点怎么进行进制转换?
- java中将毫秒值转换为小数点形式(几点几格式)的方法