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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
我们在 ubuntu 的 home/nfs/07 目录下新建 led.c 文件,可以在上次实验的驱动代码基础上进行修改,以 - l; _# W& s- H5 }& Z9 y, i( o
下代码为完整的驱动代码。4 B/ R- r* N4 o% S  J7 H
我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设
5 a! D1 ^9 R& v, P! ?6 u% J 备,并生成了设备节点。接下来我们要完成控制 BEEP 的逻辑操作,那么控制 BEEP 就涉及到了对寄存器的 # b2 v7 q+ i4 O" ]; r( U: a% L& n
操作,但是对寄存器的操作我们是不能直接访问的,因为 linux 不能直接访问我们的物理地址,需要把物理
# N1 m/ T  N3 M% ^. w. k: Z: H" `) K 地址先映射成虚拟地址,我们完成这一步转换需要用到 ioremap 函数。
0 p, I, X9 Z: v: r  l! A* H 完整的驱动文件如下所示:
. d+ I+ r9 b4 |% A6 s) _/*
: {. F, M' m5 u* O9 Q' {  e# {; A: I * @Descripttion: 基于杂项设备的 LED 驱动
3 J3 o5 m" a4 _( M. H8 L4 a* @version: % R- D# J; d" k4 A
* @Author:
) B' J& n/ V; y- O. E * @Date: 2021-02-23 13:54:49 " w+ V6 A" ]% u2 e
*/   q8 M$ O! D% k$ M6 V
#include <linux/init.h> + t% Q. x; j1 A: }1 x7 O' N( F
//初始化头文件 5 g" w2 M  [5 ]; P: U* y- \) m
#include <linux/module.h>
: D7 F/ h. q7 m, F3 b //最基本的文件,支持动态添加和卸载模块。
6 S' a3 C" Q4 j9 ~#include <linux/miscdevice.h> //包含了 miscdevice 结构的定义及相关的操作函数。
1 B. Q# e" T: ^& ]; _3 m, s6 d#include <linux/fs.h> 8 C. ~+ m( q0 D( \
//文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等) 1 b6 l3 g1 x! [4 s2 A
#include <linux/uaccess.h>   Q# ^+ ~6 w- |
//包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函
4 J  w: E- I/ y7 b* V; ^: b 数定义。* D, C% {+ T: S' E" T
#include <linux/io.h> 2 T$ m0 L* g! r+ E+ C! |/ A5 k
//包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。
/ o0 n5 [7 z/ S0 D# l0 g9 \) A2 r#include <linux/kernel.h>
/ ]& }/ W" t3 g) l2 v; e& N //驱动要写入内核,与内核相关的头文件
! l7 ^5 d5 y* b. J: g4 w& ~#define GPIO_DR 0xfdd60000 //LED 物理地址,通过查看原理图得知 4 }9 e; ^: s5 R9 L  e
unsigned int *vir_gpio_dr; //存放映射完的虚拟地址的首地址 4 u0 E! E7 P$ a5 H7 q
/** ! f6 K( B' ?( H1 V( |
* @name: misc_read
9 J# l7 D2 V8 N# D * @test: 从设备中读取数据,当用户层调用函数 read 时,对应的,内核驱动就会调用这个函数。 * F) y' B# D2 _4 ^; H( F) }9 U2 |9 n
* @msg:
- _) k' ^5 z" {; _) o * @param {structfile} *file file 结构体
2 V  B  p5 D. v* @param {char__user} *ubuf 这是对应用户层的 read 函数的第二个参数 void *buf
# z$ r4 ^6 T/ o  a. { * @param {size_t} size 对应应用层的 read 函数的第三个参数 $ ?/ y1 A3 c' k' L4 L) @
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使 ) h' |0 K% G) g4 Y
偏移量往后移。
( L1 g7 h/ [, t0 k+ ~! \, J: [* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取 + `) o2 n( Y- a, N3 H: Q
的字节数。 6 u4 i* }6 X0 C2 Y
如果返回负数,内核就会认为这是错误,应用程序返回-1 , z. U1 ]3 c2 Q; P( ~" o3 j
*/
' f4 Y9 G; g6 I. R- _7 a2 a- P: p& H ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t) ( ]; q3 @  Y) n4 a1 Z
{
# S  z5 E/ z6 k/ g: x2 M4 p printk("misc_read\n ");
" ]' W0 a+ R% E) m, t* h  ?1 ]# { return 0;
8 `! n1 w! Q6 O" x0 M }
5 e6 h4 x* L& W8 [1 J* o /**
: I. T4 i6 O) _+ |1 [  l& |& _% o * @name: misc_write
3 K; A8 M' b7 a% x2 X/ P * @test: 往设备写入数据,当用户层调用函数 write 时,对应的,内核驱动就会调用这个函数。
9 r' q: m: q' m( I0 W- ^( B* @msg:
$ _8 b* B5 k: \& ?: K8 D * @param {structfile} * filefile 结构体
* l. |( u* F8 [- V( y4 F- F* @param {constchar__user} *ubuf 这是对应用户层的 write 函数的第二个参数 const void *buf ! r/ r6 I+ ~* c- T
* @param {size_t} size 对应用户层的 write 函数的第三个参数 count。 0 u% |7 s( r( m' V
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使   k- X: l$ o# k& Q6 V: A
偏移量往后移。 7 {( |- T; j( d
* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取
- G' V, I% E* u+ q; v( e8 _* x 的字节数。 ! S+ B6 l# P& t2 b7 e- O/ i" I# ~
如果返回负数,内核就会认为这是错误,应用程序返回-1。   J: S2 X1 X6 I  S9 I! B
*/   D7 T0 `: R" y6 @5 l0 ?
ssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
& U0 m3 N. o; G5 i$ c { 6 j, f. b) I5 @$ G" ^+ K) a& ]
/*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/   O. R; L, z6 E
// kbuf 保存的是从应用层读取到的数据
6 x2 U8 d( ^; u) o) P; Vchar kbuf[64] = {0};
9 c2 p6 S  h( B$ a% Y, I // copy_from_user 从应用层传递数据给内核层 & U: L1 @# Z+ O2 Y1 @
if(copy_from_user(kbuf,ubuf,size)!= 0)
  C8 f0 }! N# |' `0 ?( p& H0 U9 F { ! {2 n) l9 e1 C
// copy_from_user 传递失败打印 ( G7 c* A& S  Q6 R3 e
printk("copy_from_user error \n ");
3 V, I: X/ f/ v  G return -1; ; X% I3 Y4 `9 D7 O. L
}
4 P8 E! @* }0 ~2 @0 r! f0 o //打印传递进内核的数据
; D2 n: o0 W6 s& G  iprintk("kbuf is %d\n ",kbuf[0]); 4 q! B% j" n6 Z7 k3 c
if(kbuf[0]==1) //传入数据为 1 ,LED 亮
' D6 j5 m5 o# I8 K8 U9 O! t7 }{
- r( u. |, M) A; p! M *vir_gpio_dr = 0x80008000;
$ f* g2 \7 ?) F; s4 k: k  u }
+ S. ?+ N; s3 [$ S else if(kbuf[0]==0) //传入数据为 0,LED 灭 3 L: k+ N0 T! e
*vir_gpio_dr = 0x80000000; * L  I3 p) P2 A# A) g. X% R
return 0; 5 m# m5 V2 Z/ a" M+ N
}
" e# n5 Y( t) o# W* e /** 0 _( ^- E2 T+ m, z4 d' ]
* @name: misc_release ! r% Y3 i( N+ P1 y. y9 a
* @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为 NULL。关闭设 4 P! L% u3 t- z9 }- l
备永远成功。
" I/ O/ Z8 ~  J) H6 A/ n/ E8 m6 f* @msg: 3 s" a6 [% E$ B. I* d) Y
* @param {structinode} *inode 设备节点
3 ]2 a$ C/ Z7 p. b! p) Q* @param {structfile} *file filefile 结构体
, ]6 u9 _2 H; ^. R* @return {0} 3 X, Q% [: C+ d! t1 h
*/
+ `8 ~3 Z, r3 Q6 I" r- S* Y int misc_release(struct inode *inode,struct file *file){# O8 r1 F( ~* M7 D" ?3 B
printk("hello misc_relaease bye bye \n "); + I9 }) @5 V1 a
return 0; * h! M/ q- B- b5 d& f: h
}9 h& }0 \. {2 g9 @  c
/** . P1 V4 L7 [2 |
* @name: misc_open 9 l% V3 y" q, ^9 f# w5 m
* @test: 在操作设备前必须先调用 open 函数打开文件,可以干一些需要的初始化操作。 4 w: O. \( {3 J# u5 m# ~6 ~9 ^2 ^- D
* @msg:
) a( t. U; u, G* j* K6 m5 x* q1 L) n1 j * @param {structinode} *inode 设备节点
9 T4 x* Q+ {) M5 a* @param {structfile} *file filefile 结构体 " B! n" ]2 r: F* K& N& l
* @return {0}
2 _# X; d( N% U  c */ - j, f8 W  K) x! ^# I0 K  \
int misc_open(struct inode *inode,struct file *file){
. y4 [6 f( G( }1 [4 z5 X  Z! m, x printk("hello misc_open\n ");
+ [/ d! E( s5 ?* F8 V5 ? return 0;
* C" }; D' h3 w# W6 x }1 ?% n4 Q: O# s0 X0 a* ^! ^
//文件操作集
7 D  k" @5 W7 Rstruct file_operations misc_fops={ * ^  @* ]# z' ?- v" f# t
.owner = THIS_MODULE,
' B6 h. \2 J0 }. r9 \, d .open = misc_open, 3 R3 V4 l! ~" }( L
.release = misc_release, ! L( V( M0 ~& B/ N0 L& W3 f
.read = misc_read, 8 B5 `) [0 s8 G+ N  F' X/ j
.write = misc_write, 1 e7 W) b* f: Y" f
};
4 @, j; O! a+ f //miscdevice 结构体
. {; s/ u) y2 o( Fstruct miscdevice misc_dev = { . I% F. K$ c! x
.minor = MISC_DYNAMIC_MINOR,
  \) U7 i& q+ p4 o .name = "hello_misc",
8 ]& ]3 `( c( S2 d .fops = &misc_fops,
" `$ D2 p% \9 ~. \ };
; O8 f0 a7 \9 c& [: r) f9 z$ { static int misc_init(void)
, n6 M! B4 F# @1 [. a7 y {
" ?% {( h: f2 R# Y8 e0 X8 P int ret; 4 b$ w5 d' n8 o1 A7 n7 G: q. d
//注册杂项设备
% z7 d) \2 l( {6 L) ]" lret = misc_register(&misc_dev);
9 F7 f8 j( b& v* T) m) U. f0 e+ d7 O if(ret<0) 1 b- z3 ]$ Y( T5 R
{
. f& |4 A  m" ?% X- \0 `+ X( Q printk("misc registe is error \n"); 0 g3 t; P' Y+ ^8 z9 N: l. Y4 F
}
- ], H# y  c8 Q+ F1 ]0 P; _6 l printk("misc registe is succeed \n");
8 h! T* j' u/ n! b //将物理地址转化为虚拟地址 0 [3 K1 I: u; s& g2 d& T
vir_gpio_dr = ioremap(GPIO_DR,4);
' H5 k, n8 ]5 F. J- s; o; n if(vir_gpio_dr == NULL)
. u$ S9 `6 M- q {0 Q7 b+ q( x1 g
printk("GPIO_DR ioremap is error \n");
$ @  [6 Y9 n$ |- a+ q3 Q- | return EBUSY;
& q5 u) C& h1 h }4 }$ R2 w6 J; @! S8 F
printk("GPIO_DR ioremap is ok \n");
5 ^+ m3 ?5 f8 d# B: V9 W& u* ] return 0; " T" ^& ]5 ]9 i* f
}) d  }( l6 I4 o9 |6 A) r% O
static void misc_exit(void){
2 I/ r' x9 S: j) Y/ H; b //卸载杂项设备 " \/ E8 e$ x8 k, l1 ?; K2 t
misc_deregister(&misc_dev); 6 C% ~4 ^8 ~9 X" o6 G4 t
iounmap(vir_gpio_dr);
3 C$ ^9 _) d3 N! a) t) z printk(" misc gooodbye! \n"); - q. e, B$ @1 ?- C+ z
}
5 g$ [" F4 _- e+ K7 X. m module_init(misc_init); " N3 q2 g0 Q; w. ]: |! r$ `
module_exit(misc_exit); 2 ?0 z( I) N( @' c9 l- g! X
MODULE_LICENSE("GPL");
3 _; t7 V" d1 X$ S' o' q: N7 w更多内容请关注:北京迅为
  r  `) Q8 G! `( G- |7 I
: E: ~+ n& l1 c) A2 d

该用户从未签到

2#
发表于 2022-5-31 13:21 | 只看该作者
非常完整的# U7 t) Q) W- A- R0 U
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-6-19 14:38 , Processed in 0.078125 second(s), 23 queries , Gzip On.

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

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

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