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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
我们在 ubuntu 的 home/nfs/07 目录下新建 led.c 文件,可以在上次实验的驱动代码基础上进行修改,以
$ C4 o/ c/ `) c下代码为完整的驱动代码。
+ s* G& j5 D* a* O* S4 ?我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设 4 J8 q* Z4 f$ ^+ i2 Q! b. c& c
备,并生成了设备节点。接下来我们要完成控制 BEEP 的逻辑操作,那么控制 BEEP 就涉及到了对寄存器的
) ~- O' v! p0 X 操作,但是对寄存器的操作我们是不能直接访问的,因为 linux 不能直接访问我们的物理地址,需要把物理 - s1 {4 s9 z( `/ ?2 ?
地址先映射成虚拟地址,我们完成这一步转换需要用到 ioremap 函数。
( I7 i- X& T& `! U4 o. H 完整的驱动文件如下所示:   D( J8 r7 \% ?; d( y5 k; \
/*
/ f  V: M; D0 l( [8 {0 M! I9 p * @Descripttion: 基于杂项设备的 LED 驱动 3 g5 K2 M" }: D5 s: g1 W; o
* @version: ( b+ I2 L- |! e3 b3 L1 {) F
* @Author:
. _# G6 z! c1 `6 s+ |( P * @Date: 2021-02-23 13:54:49
1 h8 }* g! v3 {* I, c: q1 H */ + T. q! \3 Z! N
#include <linux/init.h>
  s. I& L3 H% m3 c //初始化头文件 ; S, u! |( L! A8 V8 r- h
#include <linux/module.h>
8 h8 A  z3 m  i0 f1 T, q //最基本的文件,支持动态添加和卸载模块。
2 m4 l+ D0 y1 S, y#include <linux/miscdevice.h> //包含了 miscdevice 结构的定义及相关的操作函数。 9 s- [' m& |. c& I
#include <linux/fs.h>
0 |1 J7 x+ y8 H" F: ~ //文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等)
3 B) `; }+ j: q, S* {0 c#include <linux/uaccess.h>
/ k; w+ n, B+ X# A/ v //包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函
5 S6 u0 T; c. I' h# s 数定义。
; C0 l7 C+ q4 P4 x% O+ C* Z7 {2 Q#include <linux/io.h>
$ ]2 `! k5 @- M //包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。 . X: }$ b( M! a( z- S' R1 s
#include <linux/kernel.h> ! _* |" M3 U' ?# b+ Q' q: e
//驱动要写入内核,与内核相关的头文件
9 Z$ k* j* I% Y) u. T" L5 G#define GPIO_DR 0xfdd60000 //LED 物理地址,通过查看原理图得知 " ?' E1 A9 v+ e# J( i. S- X& E
unsigned int *vir_gpio_dr; //存放映射完的虚拟地址的首地址
3 h* |% B8 m5 M- {, d, i2 A2 P/**
% I& v( P3 `; N. J( T. A * @name: misc_read
  ], ?% n# o) I5 W5 U7 B * @test: 从设备中读取数据,当用户层调用函数 read 时,对应的,内核驱动就会调用这个函数。
7 ^0 O+ f' V$ E& X* @msg:
( @" d" D1 i# p, t& \  Y5 r5 k' a) z * @param {structfile} *file file 结构体
$ n3 E. I  H; P* @param {char__user} *ubuf 这是对应用户层的 read 函数的第二个参数 void *buf $ U$ U4 g( v$ j2 z0 n/ U
* @param {size_t} size 对应应用层的 read 函数的第三个参数
7 ]: _8 ~" q  x2 F& c& G3 g* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使 0 t2 I% A7 o+ }; I, K4 j0 d9 l' C
偏移量往后移。
+ }3 |) N3 A3 B( O, i: V% G* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取
% c# `6 {# b5 }& M 的字节数。
+ n" q& Q! g* ~9 v& u 如果返回负数,内核就会认为这是错误,应用程序返回-1 9 ?7 g( w. y. W- K# N) T# _
*/   q" T8 M7 H! i. W  ]1 s
ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t) 2 X, p/ m2 n3 O5 P9 x8 g
{ 6 X1 s! I$ u, f8 d" c
printk("misc_read\n ");   x0 y0 r) R: X% o0 \  L; H1 O/ Q) L
return 0;
& X0 a# o4 `* V }
; z  n" P' O+ {2 p. f2 z2 a /** ' O+ i# q+ s1 ~1 X
* @name: misc_write
8 @  n, R$ S) R5 i2 L * @test: 往设备写入数据,当用户层调用函数 write 时,对应的,内核驱动就会调用这个函数。 " b, j6 j2 V' _) X* _8 ^( m
* @msg: $ T+ |- K4 v7 }: L
* @param {structfile} * filefile 结构体
) E; o0 C5 U" J! x* @param {constchar__user} *ubuf 这是对应用户层的 write 函数的第二个参数 const void *buf 0 z' i6 L8 f% I) m  G
* @param {size_t} size 对应用户层的 write 函数的第三个参数 count。
: B; H* T/ l+ X3 @  l$ w* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使 # v  r) q# b% [8 N" x: @$ X2 ^
偏移量往后移。 * W: \1 k9 Q' z; c
* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取 6 ]* ^" h& u1 w9 {- c
的字节数。 " x' B* K: _* z$ L9 f
如果返回负数,内核就会认为这是错误,应用程序返回-1。
* }0 n9 ^, T6 _* W/ `*/ & |' q7 Y4 }# o; n; ~+ ^8 ^, q4 w
ssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t) 3 G8 i) Q% |1 @8 l3 Z1 p6 A: M
{
" E! E- l0 y+ H /*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/
. S1 E2 [9 S/ T; o/ i# l2 D; ^ // kbuf 保存的是从应用层读取到的数据 ! W' ^, f  F7 _4 Q- K: f6 w* h
char kbuf[64] = {0};
0 U- v7 \! w3 H0 q8 A9 E // copy_from_user 从应用层传递数据给内核层 4 \3 ~8 b; @3 N  u7 D
if(copy_from_user(kbuf,ubuf,size)!= 0)   C% I; V# [/ ^$ i4 z
{ & I5 ~* |$ ]4 i7 }" L
// copy_from_user 传递失败打印 % s* S0 L! h2 S% ?
printk("copy_from_user error \n "); $ M" y3 r& |) |3 o4 w  _" m' ~
return -1;   ?; `! P2 t5 E, v: T  G
}* D4 W& F4 T5 e* N! W, [% M" s% Y
//打印传递进内核的数据 ! S. Y* \# a. y, a7 v) N/ F
printk("kbuf is %d\n ",kbuf[0]); * Z9 G: c" P7 _6 ?# r# d! M
if(kbuf[0]==1) //传入数据为 1 ,LED 亮
  f5 @8 e9 j, M# y' w{
* g) p3 W& U5 |! ]3 [4 Y6 n *vir_gpio_dr = 0x80008000;
2 j' F# u8 c& z  |; U }; z# ^2 w+ h6 U9 ~4 E, ]
else if(kbuf[0]==0) //传入数据为 0,LED 灭
! E4 c9 J! }+ l0 M*vir_gpio_dr = 0x80000000;
" {* E: S5 V* D6 I6 h7 K  a4 T return 0;
2 x. S% j6 h' F+ x) |" a0 ^ }
$ m' B# V# p6 S4 J# u /**
6 H9 X6 N- n* z) ~5 A * @name: misc_release
9 {( O, V% ^8 U2 u2 v7 N * @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为 NULL。关闭设 6 k# l4 v+ v! E6 X
备永远成功。 " R! D- p1 ^9 _# K+ x
* @msg:
( u( s$ e/ Y' ?" Q* |; I8 s9 { * @param {structinode} *inode 设备节点
& r! |8 m4 n; z, d* @param {structfile} *file filefile 结构体 - O8 A7 f3 |$ J$ O
* @return {0}
6 X. F* Z& i: d. b* Y */ 6 f( a- f' m0 X5 y2 W
int misc_release(struct inode *inode,struct file *file){
$ J+ d) L1 s6 l. K2 \0 h+ j* f1 S: l printk("hello misc_relaease bye bye \n ");
4 E- j% R/ h7 m% O% ^: p return 0; ' Z* m+ U& ]! E0 T0 v4 W' [
}: d% {5 z$ `) c, a6 I
/**
( y7 i! }) a9 f) r  ]0 a* D% W * @name: misc_open   c  o& V1 }1 X, C! d" b
* @test: 在操作设备前必须先调用 open 函数打开文件,可以干一些需要的初始化操作。 , Q7 m2 J0 z' |& ~  G" v& C
* @msg:
/ p: H8 n4 f3 s0 W, q7 z( V8 l& ? * @param {structinode} *inode 设备节点
% L' _1 m/ A7 O+ k( |* @param {structfile} *file filefile 结构体
+ Y- y/ }) K2 E8 X- x* @return {0} ) v) I4 ?# G5 k
*/
: j/ d" E3 t" V" F  D0 s int misc_open(struct inode *inode,struct file *file){ - b4 q$ }. i7 {& C8 I
printk("hello misc_open\n ");
4 U3 `' v& v3 s  V' s; o return 0;
8 v2 E& D8 F9 r/ E: n: a }
6 q" U" u" D0 n# w- D //文件操作集 $ r0 ?3 j6 n  @5 p: F2 g3 O
struct file_operations misc_fops={ ( ?- r- z, s# j" l+ R/ l& C
.owner = THIS_MODULE,
7 V1 Z) Z" Z" r9 h .open = misc_open,
$ p2 V6 j$ @$ a# m' u3 v! i/ b! B .release = misc_release,
4 b8 P: O$ A; Q1 |8 S5 `, L7 ` .read = misc_read,
. h: k# x% ^# a9 g- m( ? .write = misc_write,
! S0 j1 T# Q3 d0 T! r* E& g };
% D  |- _* a: Y //miscdevice 结构体
$ ]; |' D7 V- U# ^% tstruct miscdevice misc_dev = { ' m$ B4 R, `- I# H7 R
.minor = MISC_DYNAMIC_MINOR, 4 A* n1 y$ e$ M- p* A
.name = "hello_misc", ) a4 F$ j5 X' L. Q
.fops = &misc_fops,
- f- W0 ?5 \& n5 U* W, N. M- r };
' m# Y. X/ V$ U8 b* V static int misc_init(void) 3 u, D/ ^: c' e" {
{
0 z, o( J% N! _ int ret; 1 G( a' B5 e! O0 V
//注册杂项设备 6 g2 T) a; T1 r2 o! T1 W
ret = misc_register(&misc_dev); - ]4 D* m* u6 J! Q
if(ret<0) $ ]0 V4 m# T8 }( i+ c  R
{
. A9 ]* z# a* t printk("misc registe is error \n");
+ ]' Z5 W3 i/ j) D2 j }
: s; z& ~6 l7 u5 A6 p printk("misc registe is succeed \n");
! x$ e+ q& V5 x0 \, X //将物理地址转化为虚拟地址
7 ^: |8 X, v+ q0 H- F1 N& @vir_gpio_dr = ioremap(GPIO_DR,4);
/ b* G% Q" a! n7 N1 I3 B if(vir_gpio_dr == NULL) ' P7 S" J7 Z) z  B3 M, S* {2 n
{
3 O( {7 W8 H+ o% I printk("GPIO_DR ioremap is error \n");
6 T" f5 t2 S& o* R return EBUSY; 0 B7 K% r2 o  \( }8 C, h
}
4 `: Q2 v# n3 R9 Y# p printk("GPIO_DR ioremap is ok \n"); 4 s( F) p* T6 N! m* Y
return 0;
8 N: H" p/ P& v. t3 B* @2 c }3 M1 r0 ]& ], ^7 G% N
static void misc_exit(void){
0 }; K# V0 d/ C( R //卸载杂项设备
& A3 V# C% M, y; `; imisc_deregister(&misc_dev);
4 n5 O$ A6 G! n& Y, G& n( _ iounmap(vir_gpio_dr);   K; z' e2 G5 o% N
printk(" misc gooodbye! \n"); 1 j4 [( _9 }. f3 n8 c
}
0 j# [; N% y, Q module_init(misc_init); : R9 ]' a5 j: @6 h% K
module_exit(misc_exit);
* G7 [; I6 n9 u MODULE_LICENSE("GPL");; B: l& O# K8 r! g  M  ?
更多内容请关注:北京迅为- E+ A: E9 Q/ B% c: G0 y6 M5 S

5 g) d9 p9 o$ n. c; E) m

该用户从未签到

2#
发表于 2022-5-31 13:21 | 只看该作者
非常完整的8 @1 W. t3 N- a: q) z
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-23 18:00 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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