找回密码
 注册
关于网站域名变更的通知
查看: 152|回复: 1
打印 上一主题 下一主题

迅为iTOP-RK3568开发板编写LED驱动

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2022-5-27 09:50 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
我们在 ubuntu 的 home/nfs/07 目录下新建 led.c 文件,可以在上次实验的驱动代码基础上进行修改,以
$ B# E+ P, g% _0 {9 r下代码为完整的驱动代码。' K- ?2 y5 q8 H) W' l
我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设 $ l% R/ W  g7 L, C8 S
备,并生成了设备节点。接下来我们要完成控制 BEEP 的逻辑操作,那么控制 BEEP 就涉及到了对寄存器的
9 a, L: H) h% {( `: N* Z 操作,但是对寄存器的操作我们是不能直接访问的,因为 linux 不能直接访问我们的物理地址,需要把物理
  h% E5 N, z# f, ^* G' ? 地址先映射成虚拟地址,我们完成这一步转换需要用到 ioremap 函数。 3 y/ i1 V- a+ {( M2 J; y
完整的驱动文件如下所示:
) _  Y' N) A( h- J: W/ a/*
2 s9 V  D/ d  P* E * @Descripttion: 基于杂项设备的 LED 驱动 6 E% ?$ G9 N7 g- B
* @version:
( X9 B* S  r1 K6 ^$ c7 ~% K$ O$ l/ | * @Author:
% _5 C4 X0 A$ O2 {+ d0 Z1 z) Z  L * @Date: 2021-02-23 13:54:49
0 C4 v- u8 ?3 \8 b/ ?- F% ?$ U */ 3 J. {6 n5 S8 o" u+ I! L
#include <linux/init.h>
5 C, h6 I8 n% r; z //初始化头文件 - w. i# \* B0 _
#include <linux/module.h> ; \. k- E( O! k& c2 [; z6 R% Y& s
//最基本的文件,支持动态添加和卸载模块。
; V3 `- J7 H7 S2 X; P9 K#include <linux/miscdevice.h> //包含了 miscdevice 结构的定义及相关的操作函数。
8 H% s" n8 b3 Q, G#include <linux/fs.h> # v/ m- F3 f7 R" `+ X, X) S
//文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等)
5 m/ ^/ M  v  h. c#include <linux/uaccess.h> 9 {. {+ U& e1 b* r0 v5 ?6 b
//包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函 / X1 k5 S8 _/ A1 P2 `% M& a; P/ H4 S
数定义。
# H/ w1 A& S5 a0 X: K* }2 H#include <linux/io.h> " a" Y* z( x0 |( n) F/ u
//包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。 4 Q9 j( j! ]. l+ ?  g+ F
#include <linux/kernel.h> - T/ V: n7 h$ c: f" j* _; m# \2 L1 I
//驱动要写入内核,与内核相关的头文件
; h! `) d0 p3 c1 Y4 @' B#define GPIO_DR 0xfdd60000 //LED 物理地址,通过查看原理图得知 # H( T" t6 y. O! M
unsigned int *vir_gpio_dr; //存放映射完的虚拟地址的首地址
& ?) k& a5 B/ b% c/ W8 H, y+ X/** 7 r3 T7 x5 z. w5 `& j
* @name: misc_read 2 o- O" Z5 E6 p" c. b
* @test: 从设备中读取数据,当用户层调用函数 read 时,对应的,内核驱动就会调用这个函数。
2 k' K7 S# G7 w* @msg:
& s- T, M/ S1 j) o * @param {structfile} *file file 结构体 1 [/ v- C( h! E& b  j1 ^6 G
* @param {char__user} *ubuf 这是对应用户层的 read 函数的第二个参数 void *buf
6 o6 \2 h/ k' Z2 E * @param {size_t} size 对应应用层的 read 函数的第三个参数
* V- K; J$ |  J* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使
: ]1 ]+ F7 m5 S+ D 偏移量往后移。
" y1 c- a. N' A5 E7 j7 `; `; R* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取
9 b* s. Y2 l4 _, o 的字节数。
: Q, h0 b9 W, x! r% T$ X) u8 e 如果返回负数,内核就会认为这是错误,应用程序返回-1
1 w+ J4 Y3 c+ e, I/ Y */
+ g- N$ b3 a" a/ Z4 m" C2 h8 j ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
  ~/ v) n1 D' F1 P {
) o6 S- P" G" B9 K% S printk("misc_read\n ");
# l1 V, y' s& H4 X return 0;
; n3 w9 f; J1 I8 b3 f. \- h }1 E- g3 f5 H1 ~5 ?0 @1 x
/** 2 H- ^( H4 D. C
* @name: misc_write% I. x( l- ?, T
* @test: 往设备写入数据,当用户层调用函数 write 时,对应的,内核驱动就会调用这个函数。 ' e0 r+ t  U) t9 i( l7 R
* @msg:
3 |6 T: L0 o- j6 B9 W, m4 V6 L * @param {structfile} * filefile 结构体 4 g4 @- m! E! O: x& ]1 Z. j
* @param {constchar__user} *ubuf 这是对应用户层的 write 函数的第二个参数 const void *buf
7 I2 N" s( G) Z4 `- r& f; I) \0 N * @param {size_t} size 对应用户层的 write 函数的第三个参数 count。 6 ]3 y1 q: |& q! B9 D
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使 7 b, h! Q5 ?! d% A6 X1 y5 x  h! G
偏移量往后移。 , ^6 F. C6 s% N
* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取
2 t0 u; q% u6 h' \ 的字节数。 5 Q* s1 Z7 o8 C% W) @+ o% y8 s' r
如果返回负数,内核就会认为这是错误,应用程序返回-1。 ) \" h) K% Q, A$ o
*/
1 R. d+ A" k& b' O& Xssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t) % Q' c- H4 i5 Z$ C- U1 c
{ 7 Z6 a. b) e1 W
/*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/
  S# N4 K7 Y4 ^ // kbuf 保存的是从应用层读取到的数据
. ?+ B. R! Z- R- S! c4 L# |char kbuf[64] = {0};
8 u# U: [/ l3 K6 ~& S- c0 h3 d  J // copy_from_user 从应用层传递数据给内核层 9 @: j: j- G. @; C+ ?
if(copy_from_user(kbuf,ubuf,size)!= 0)
; p$ ~% [; h/ N! \  s! @ {
2 J; [7 }. s8 f3 D* l9 O# q- W4 M2 r7 G // copy_from_user 传递失败打印
. j* O( [2 k- n4 Pprintk("copy_from_user error \n "); ) e& C2 L  W4 ^" b6 N
return -1; ! E% ^3 y- S- \# Z
}: T  S6 D4 s1 Y+ E  @5 j1 X/ l
//打印传递进内核的数据
4 f# Y7 U$ U( q! \7 Jprintk("kbuf is %d\n ",kbuf[0]); - V2 o& m$ y7 v6 N3 F
if(kbuf[0]==1) //传入数据为 1 ,LED 亮 . n" b! m% S$ o# X4 R
{ $ S) k, J8 R6 A& K. _
*vir_gpio_dr = 0x80008000; 9 r4 X% @1 L7 `2 t
}/ c4 D& `- m% g9 e3 o, ?, Q4 v
else if(kbuf[0]==0) //传入数据为 0,LED 灭
. Z) z! z$ S9 M" F5 B: D) u; F# Z( T*vir_gpio_dr = 0x80000000; 0 I/ U9 h: z: O) X; [
return 0;
1 v  w) v# W6 Q5 X/ H) }8 h' O4 z* E$ V }
5 o. m, b+ Y- J8 y& F /**   d/ i! O0 Q$ s7 S
* @name: misc_release / D! v  m* X( F& `4 M
* @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为 NULL。关闭设
- d# l- Z$ u, R 备永远成功。 , w+ z1 _6 |* P) D" s
* @msg:
7 j3 Y, c8 X  } * @param {structinode} *inode 设备节点
6 ~6 ?/ B0 F, ^) ~) c! A: T$ B* @param {structfile} *file filefile 结构体
% g1 T& {  _  ~4 y0 d" d. H  u* @return {0} $ K% f6 ]( q! Y  X) G" a
*/ " o* g* [" @0 w
int misc_release(struct inode *inode,struct file *file){
! e: E1 s. g7 [% j) j printk("hello misc_relaease bye bye \n ");
( s* v: y- C5 y return 0; 7 R  |/ [5 O1 W5 b+ q$ o, F/ |
}
+ g0 S+ W+ S# C9 G5 u# n: `( Y /**
: [0 J8 s* x. v' B3 ^, y; R * @name: misc_open 0 q5 G, ]4 x; d% o' W, t: C- U
* @test: 在操作设备前必须先调用 open 函数打开文件,可以干一些需要的初始化操作。
1 K+ C$ a, u, T0 E3 A6 g0 G* @msg:
7 r. o# {& ~1 M$ b * @param {structinode} *inode 设备节点
# m9 t8 E0 G* f* @param {structfile} *file filefile 结构体
8 h, M% ^2 [. X; g& p2 E+ _* @return {0}
+ h) @5 d, ~" `  e9 C3 c */
* k6 I% Z  Q+ ^; y: n& \ int misc_open(struct inode *inode,struct file *file){ , z/ ?4 I$ l. n9 [
printk("hello misc_open\n ");
" L5 q7 }: S& e return 0;
$ ~% ?/ {9 v9 u( L8 }/ l3 f7 _; e }
( L8 z1 B9 N) N. [9 H+ Y //文件操作集
) v! h3 c$ B% P% d5 _struct file_operations misc_fops={
" \* `/ p! ~" @ .owner = THIS_MODULE, ) f: I5 K- Q( Y) H8 `6 r5 a$ P7 q
.open = misc_open,
; ^; h5 J# v3 ~& u; }1 d% r .release = misc_release,
- }; F, Q6 [4 S; G1 i7 f8 m .read = misc_read,
" k  v3 v* q0 f9 e% H: ^% q .write = misc_write, & w5 n8 |1 J1 S$ |3 ^/ @6 ~
};( c4 {% g8 t0 [
//miscdevice 结构体 & f: {9 D1 o8 q8 s+ `) a, @$ l/ }, z
struct miscdevice misc_dev = {
# q& `* _" {+ @ .minor = MISC_DYNAMIC_MINOR,
$ f* w' O1 G3 }6 l .name = "hello_misc",
6 X& s& t0 X# I& Q  A. e5 H- n .fops = &misc_fops,
1 S$ z1 V  O# L };/ p' g: U$ b7 w
static int misc_init(void) ( z0 K% K! n% p5 E% I
{ ; [  y+ K3 K* N* q* H
int ret;
, ?! \2 e6 H7 R/ t //注册杂项设备
# @$ L( y! F$ Q1 d6 @0 Q1 H, fret = misc_register(&misc_dev); 4 n/ f4 U9 B0 e" @7 J& t6 D
if(ret<0) ! P( K1 {8 o7 t! P3 S! D
{ / R/ M$ a8 T) \8 ~! [/ z
printk("misc registe is error \n"); 1 V: i9 I' j$ C& f- J
}7 j2 g/ \( s7 p6 ^
printk("misc registe is succeed \n"); / R' r! {9 a0 l2 f$ }
//将物理地址转化为虚拟地址 6 a% Q5 \  f- {! D, Y3 R
vir_gpio_dr = ioremap(GPIO_DR,4);
/ F8 P3 k1 z# U# e5 V- R5 Y: h) O if(vir_gpio_dr == NULL) 0 g7 q0 \' b( Z9 R0 R
{7 U3 i5 \  N/ w
printk("GPIO_DR ioremap is error \n");
5 ?6 d6 H' Z* B2 L5 _9 ] return EBUSY; . C( G  p3 }+ I) p0 Q0 P4 f
}4 @  f% f5 R# q2 a
printk("GPIO_DR ioremap is ok \n"); ! y( A  V' s# ?% P' _
return 0; - \9 f+ t, T' w: F, F
}6 b/ Y1 ?# w: H
static void misc_exit(void){ 2 ^% i2 V; y4 c+ E9 h3 L
//卸载杂项设备 8 Y3 q& D6 o4 T8 I4 }
misc_deregister(&misc_dev); 9 m2 s8 Z5 m/ Y8 ^6 p" @- }
iounmap(vir_gpio_dr);
/ l7 P) Z# s! G7 J) ^: r printk(" misc gooodbye! \n");
9 d. y: ~; f" D% C5 h1 t }
5 b. J7 t2 S; j module_init(misc_init); % f' @6 R- }1 {& M+ M# F6 T1 U
module_exit(misc_exit);
+ ^: E8 B; R+ }$ T MODULE_LICENSE("GPL");' s# J3 q1 G& X6 m1 a
更多内容请关注:北京迅为3 ^. f5 N0 ?$ {1 j

" a7 A* R9 \; V/ D( [

该用户从未签到

2#
发表于 2022-5-31 13:21 | 只看该作者
非常完整的
3 U7 e0 \! i! e2 L! j8 j
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-23 21:33 , Processed in 0.140625 second(s), 24 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表