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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
我们在 ubuntu 的 home/nfs/07 目录下新建 led.c 文件,可以在上次实验的驱动代码基础上进行修改,以 1 p0 n8 O$ ?2 }  \
下代码为完整的驱动代码。2 n& h+ r# b0 G3 n
我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设
6 [$ s2 e6 I, ^9 X- R! D. t 备,并生成了设备节点。接下来我们要完成控制 BEEP 的逻辑操作,那么控制 BEEP 就涉及到了对寄存器的 6 i8 e* O8 I. o- D
操作,但是对寄存器的操作我们是不能直接访问的,因为 linux 不能直接访问我们的物理地址,需要把物理
6 _  J' {( ^) N1 Z 地址先映射成虚拟地址,我们完成这一步转换需要用到 ioremap 函数。 2 `5 y& M2 v  M. J) C9 o
完整的驱动文件如下所示:
  J6 f& z/ Y, L4 q/*
5 f5 W: e( ?4 B2 V' P. c * @Descripttion: 基于杂项设备的 LED 驱动
: `2 j, S3 ~, |$ _* n) I' G* @version: * O* L( O# @* U# j, A" Y
* @Author: ) q+ y3 y4 t. g% a0 J8 k$ U
* @Date: 2021-02-23 13:54:49 7 d/ r/ r+ j  e/ i! ?2 M
*/
5 {6 Y7 A9 Q1 _6 J- g# e, k) q #include <linux/init.h> 2 ]2 r( W/ E! Y3 W0 }  t# @
//初始化头文件 5 {* P: r2 `* K! @. t6 }
#include <linux/module.h>
% Q& \* T" v0 {" k' Y" h //最基本的文件,支持动态添加和卸载模块。
  d, ?: v. V" g% p4 O2 \' i& H0 q#include <linux/miscdevice.h> //包含了 miscdevice 结构的定义及相关的操作函数。
, L" L) J7 }# ?#include <linux/fs.h>
( d' M' o  G, [; B //文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等) - U6 ]* b1 {0 ~# O- L
#include <linux/uaccess.h>
# e$ B- @' [* o! J //包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函 . |9 w$ N+ [+ v
数定义。8 |# y5 K, X0 k6 P* U8 u6 S
#include <linux/io.h> 0 U( A3 ]9 `8 ~# [
//包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。 ; C7 g$ L# _- E9 X8 f' ^+ S( F' ]
#include <linux/kernel.h>
1 g9 D& L$ [3 w  ]( s' ? //驱动要写入内核,与内核相关的头文件
- R6 p  C. {7 V% j, ?" u#define GPIO_DR 0xfdd60000 //LED 物理地址,通过查看原理图得知 . t* ]1 M0 o2 p- e2 S) `
unsigned int *vir_gpio_dr; //存放映射完的虚拟地址的首地址 ) G/ D& A) j* z$ t5 H% q, }) j
/** 2 w6 C( y; n2 {* O  d# \
* @name: misc_read / P7 Q2 k3 x$ I( A
* @test: 从设备中读取数据,当用户层调用函数 read 时,对应的,内核驱动就会调用这个函数。 : |$ R5 f( P9 `0 N) ]7 R( B
* @msg: - A% f( Q" Y; N5 ?0 i" m/ `
* @param {structfile} *file file 结构体 1 ~( o# {% Q  S- Z2 ^! Q# s
* @param {char__user} *ubuf 这是对应用户层的 read 函数的第二个参数 void *buf
" j; m# T& Q$ M, e5 Y * @param {size_t} size 对应应用层的 read 函数的第三个参数   T0 [& o& S2 l/ F: l) h  \, A
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使
. _% q- W3 A( B; i* \% r+ r 偏移量往后移。 # i: V& B: d4 U! w) o1 P1 T6 L
* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取
: _( U! A/ I0 N  {3 A 的字节数。
1 z$ A* J6 R. V; I  E3 y 如果返回负数,内核就会认为这是错误,应用程序返回-1 5 `* @' W: N; [8 r, H% N7 U' H
*/ - g& w7 m3 Q$ I0 J
ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
4 f7 A; S. `3 l' t; I {
- K9 l- G6 \/ _% R printk("misc_read\n ");
8 _9 ]; G2 c% t* C! a; Z: L+ G return 0; - j# ?" w; r- r
}
! D' _1 s; G$ X6 W /**
' _+ K: [* t$ S& B; c* q6 J * @name: misc_write
6 W$ I1 W# }5 Z * @test: 往设备写入数据,当用户层调用函数 write 时,对应的,内核驱动就会调用这个函数。
) U) |/ S, y0 ]5 h9 p3 K7 }* @msg:
! S" F8 h0 t" e * @param {structfile} * filefile 结构体 ) L# \. ?0 {8 ?( w/ ~  P3 C
* @param {constchar__user} *ubuf 这是对应用户层的 write 函数的第二个参数 const void *buf
. y7 \. l: b  h# ^. @* X. N0 Y! j * @param {size_t} size 对应用户层的 write 函数的第三个参数 count。
1 t) \: B/ t5 s; i1 O. Q* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使
* V8 R4 o  l  U7 Q0 ~: ^ 偏移量往后移。
) H2 ?2 f" Q, E5 J  C* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取 : e/ ?/ G& k5 i' y0 h& }) T4 Y) \
的字节数。
2 b: _( ?% A, h 如果返回负数,内核就会认为这是错误,应用程序返回-1。
8 |/ P5 }: z$ t4 V" x4 B/ Y3 X*/ ) u/ @) ^. K; G
ssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)   p+ m  Y4 y- d7 d4 D
{ ( [3 y0 W1 u2 f' V; L& D6 C& w; G; Z
/*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/
+ S7 ~/ A( N9 y- K' G // kbuf 保存的是从应用层读取到的数据
; u$ u; j  }/ ~+ ?$ _char kbuf[64] = {0};
3 b& @9 }$ @, ]3 S // copy_from_user 从应用层传递数据给内核层
% h! C9 h. j/ h3 `8 S1 A! pif(copy_from_user(kbuf,ubuf,size)!= 0)
" c, I# y2 K' c { ; B7 p4 L' j2 T8 r% N' O
// copy_from_user 传递失败打印 8 [$ B! D! E3 x4 ~/ H$ J1 ?' P4 x
printk("copy_from_user error \n ");
+ V7 x' M- ]7 I1 ? return -1;
; |- c  ~4 a9 c2 u# b- R2 J: ~ }! S: B+ Q$ j& S8 s4 a$ `: o$ ^
//打印传递进内核的数据
+ a$ p" z6 z  m6 E, }. m2 G6 \printk("kbuf is %d\n ",kbuf[0]);
  _; G' C) Y* |# B if(kbuf[0]==1) //传入数据为 1 ,LED 亮
$ s7 ^& G3 Q$ P& n- ^. z1 i+ N4 P{
& o" G* m2 u6 B- g' T8 X7 s *vir_gpio_dr = 0x80008000;
/ H' N9 U- ~5 }+ c }
+ D# Q$ M$ Y8 e1 D, ]& z else if(kbuf[0]==0) //传入数据为 0,LED 灭
6 ?4 C1 p% \& h% T9 ^*vir_gpio_dr = 0x80000000;
& ~/ C7 f) p+ n( \: w9 p return 0;
% d& u$ j; _; j }0 m! T( t& }4 s5 D7 L
/** 9 @/ p7 Q% n4 X% w
* @name: misc_release # ~& Y. v; a, ?0 I, A
* @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为 NULL。关闭设
! C' T1 Q: i! E5 {( g 备永远成功。
+ m8 j& ]2 X5 D; V* @msg: 7 D. C5 a; l  j, X. Y
* @param {structinode} *inode 设备节点 0 [2 Q; ]2 }) M+ R* l2 E. ^
* @param {structfile} *file filefile 结构体 5 K2 K# `# W& D: Z, ^. w9 F
* @return {0} ! ^  J- h6 \0 H1 R
*/
% b% J+ g+ y8 N6 S; t9 S( L' b int misc_release(struct inode *inode,struct file *file){
# N% ^. K5 F3 `6 |; S( z# ? printk("hello misc_relaease bye bye \n ");
+ B; E* l4 e  R0 z return 0; 4 g  a/ S7 C( P; W* r
}0 C, ^0 N: j) x! B$ J- \3 \
/**
$ ]8 ]* r' f. J4 Y; h * @name: misc_open
( Y7 c+ Y& D! j1 r  D * @test: 在操作设备前必须先调用 open 函数打开文件,可以干一些需要的初始化操作。 ! f! E0 \$ @) M$ W5 Z2 [
* @msg: ( k, }2 w. n# C6 j( j: g& C4 G
* @param {structinode} *inode 设备节点 , ^8 e1 Y) v5 H+ K; p1 k" X
* @param {structfile} *file filefile 结构体
5 K, H, M9 J: }& q* @return {0} ; D( z% I7 d, E- Y; s, F) V
*/
2 Z8 F- p+ m" i- |: H4 r. `+ { int misc_open(struct inode *inode,struct file *file){ ; r! {5 U, H, b6 {
printk("hello misc_open\n ");
& _% t# d; F# o0 Y5 R0 s return 0; ( g5 K$ X% n, P4 f' e$ s# n
}
8 }6 w0 S# E7 ^  N //文件操作集 + x/ `# j, O3 k; |" L" T' N, m
struct file_operations misc_fops={ - q& R, `7 }* x8 a9 I" k  i
.owner = THIS_MODULE,
3 x2 b+ q8 E6 q3 r' | .open = misc_open, 3 a& r0 K, b  Z: o; F9 s; Q3 l1 c
.release = misc_release, & T8 I+ n9 d8 |2 A
.read = misc_read,
, }3 G; C5 L- j& g: p0 G .write = misc_write, 9 ~! a6 z  L. x- V5 Z; A
};* y& G. i6 v9 M% `; V
//miscdevice 结构体 8 |$ @1 b: y2 t2 l+ S5 w' ~' `
struct miscdevice misc_dev = {
. x0 Q1 R7 t8 D5 `  b .minor = MISC_DYNAMIC_MINOR,
+ O$ ~9 q& B; ]" ?3 q .name = "hello_misc", % K/ f+ H' J  B* m% i' R
.fops = &misc_fops,
$ F9 [& O( ^& G };
3 \) U9 f5 ~. ]: R) D5 O' f% R3 @/ P static int misc_init(void)
" M' B* {! L* e {
, l% Q& u; X0 |. }$ F7 k' V# P int ret;
, _7 I3 o, H4 u //注册杂项设备 * `# D# Q- h1 E% n* d2 W3 k
ret = misc_register(&misc_dev);   E8 [. C: O/ F
if(ret<0)
0 ~# K$ `2 I5 I2 y {
& L9 r2 t  D) O, B7 m printk("misc registe is error \n"); 3 h# U6 a/ |1 y' B. h4 K- L0 w
}
- S! ]' k3 i& P7 a9 {8 Z3 E printk("misc registe is succeed \n");
" |) f) V) B8 |0 y+ _+ x. N# X$ {1 B //将物理地址转化为虚拟地址 + Z8 O! W/ P! S0 K1 R
vir_gpio_dr = ioremap(GPIO_DR,4);
/ r7 w4 ^% k0 a0 M( b if(vir_gpio_dr == NULL)
2 @3 t0 _( R3 v, [6 e4 W {4 n  ]; J1 ]3 q0 N. \. \$ C
printk("GPIO_DR ioremap is error \n");
1 Q, h; _* r+ E( M return EBUSY; 8 U7 b; u) U. K
}  p* C' h6 D0 M# d3 O8 h
printk("GPIO_DR ioremap is ok \n"); # Y) x! E7 S. J  G1 S
return 0;
% e0 G6 b% E2 e }  g" ~$ E, q1 [7 \( k, B  n. l! Q
static void misc_exit(void){
' b. l/ R! p! S9 M0 {/ @3 v1 _/ u //卸载杂项设备
  t4 S$ P4 g' D$ Q9 |misc_deregister(&misc_dev);
8 P/ v3 o7 S# G( L, ~ iounmap(vir_gpio_dr); ) D6 n0 B. {" c% X! x
printk(" misc gooodbye! \n"); 9 G% y' r) W) r9 c  P
}
) Y! ?5 E4 j! q2 ^1 m, \$ n module_init(misc_init);
1 X1 r7 c5 W" L7 \" T" ?9 E6 q module_exit(misc_exit); ; |# C% Z  u9 x
MODULE_LICENSE("GPL");& O) a2 Y) h; p* {) s9 I
更多内容请关注:北京迅为4 @! m  c: K. t- r

2 X$ d. D7 `1 m. G5 w* `% A/ ~# d

该用户从未签到

2#
发表于 2022-5-31 13:21 | 只看该作者
非常完整的
! X5 N/ k1 s+ B' [; B1 V5 R
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-7-31 04:53 , Processed in 0.109375 second(s), 23 queries , Gzip On.

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

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

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