Linux驱动入门实验班——基础驱动模板(附百问网视频链接)
目录
一、GPIO子系统
1.确定引脚编号
2.写程序
二、中断函数
使用中断的流程
三、定时器
1.定时器两要素
2.使用定时器
四、交互流程解读
1、非阻塞访问和阻塞访问
2、POLL
3、异步通知
课程链接
一、GPIO子系统
如何驱动GPIO
1.确定引脚编号
可以在开发板上,执行以下命令查看
cat /sys/kernel/debug/gpio
2.写程序
①获得GPIO(gpio_request)
函数原型
int gpio_request(unsigned int gpio, const char *label);
unsigned int gpio
:要请求的GPIO引脚号。const char *label
:用于标识GPIO引脚的字符串。unsigned long flags
:标志位,用于设置GPIO引脚的属性。返回值为整型,返回0表示请求成功,返回负数表示请求失败。
②设置方向(gpio_direction_input、gpio_direction_output)
函数原型:
int gpio_direction_input(unsigned int gpio);
参数:
- unsigned gpio:指定的GPIO引脚编号
返回值:
- int:成功返回0,失败返回负数错误码
int gpio_direction_output(unsigned int pin, int value);
参数:
- unsigned int pin: 指定的GPIO引脚编号
- int value: 初始化输出的值,0代表低电平,1代表高电平
返回值:
- 成功返回0,失败返回负数错误码
③读值、写值(gpio_get_value\gpio_set_value)
int gpio_get_value(unsigned int gpio);
参数:GPIO的引脚号
返回值:int类型,返回GPIO引脚的值,0表示引脚低电平,1表示引脚高电平
int gpio_set_value(unsigned int gpio, unsigned int value);
参数:GPIO的编号和要设置的值,
返回值:为0表示成功,其他值表示失败。
④释放GPIO(gpio_free)
二、中断函数
使用中断的流程
● 确定中断号
● 注册中断处理函数
○ request_irq函数
函数原型:int request_irq(unsigned int irq,irqreturn_t (*handler)(int, void *, struct pt_regs *),unsigned long flags, const char *dev_name,void *dev_id);void free_irq(unsigned int irq, void *dev_id);
返回值:成功为0,失败负值,-EBUSY表示一个驱动正在使用这个中断线(不常见);
参数说明:unsigned int irq:请求的中断号,这个是系统预先设定好的,可以在irq.h中找到对应值;irqreturn_t (*handler) (int ,void *, struct pt_regs *):中断处理函数指针;unsigned long flags:中断标志位,可以是一个或者多个,它跟中断处理相关;const char *dev_name:中断名称,这个可以在系统运行后查看/proc/interrupts找到对应名称void *dev_id:主要用在共享型中断线中,它一般指向对应驱动的私有数据,以便中断释放的时候,只释放 指定驱动中的中断,它具有唯一性;当它不是共享型的时候,可以设定为NULL,但是建议将它指向设备结构体;
○ 参数细节
request_irq函数的第一个参数是中断号,可以根据GPIO函数获得中断号:
int gpio_to_irq(unsigned gpio);
int gpiod_to_irq(const struct gpio_desc *desc);
第三个参数:
IRQF_DISABLED:如果被指定它,那么它会禁用其他所有中断,如果不设定,程序可以和其他中断程序同时运行;
IRQF_TIMER:为系统的定时器中断而准备的。
IRQF_SHARED:表示多个中断处理程序可以共享中断号,即如果没有设定只有一个中断程序使用这个中断号,如果设定了,多个中断程序可以共享这个中断号。
● 中断处理函数
○ 分辨中断
○ 处理中断
○ 清楚中断
三、定时器
1.定时器两要素
● 超时时间
● 处理函数
2.使用定时器
init_timer(timer)
● 函数功能:
初始化timer。
void setup_timer(timer, fn, data)
● 函数功能:
与init_timer()类似,fn为定时器回调函数,data为回调函数的参数。
● 参数:
- timer_id:定时器的ID,用于区分不同的定时器。
- interval:定时器的间隔时间,单位为毫秒。
- callback:定时器触发时调用的回调函数。
● 返回值:
无返回值。
void add_timer(struct timer_list *timer)
● 函数功能:
用于向 Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后,定时器就会开始运行。
int mod_timer(struct timer_list *timer, unsigned long expires)
● 函数功能:
用于修改定时值,如果定时器还没有激活的话, mod_timer 函数会激活定时器。
● 参数:
timer:要修改超时时间的定时器。
expires:修改后的超时时间。
● 返回值:
0,调用 mod_timer 函数前定时器未被激活;
1,调用 mod_timer 函数前定时器已被激活。
del_timer(struct timer_list * timer)
● 函数功能:
用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。
四、交互流程解读
1、非阻塞访问和阻塞访问
当应用程序传入O_NONBLOCK标志位后,驱动程序会判断这个标志位,当然驱动程序可以选择判不判断这个标志位。
○ 如果APP传入该标志位当数组为空时,驱动程序就会立即返回。
○ 如果APP没有传入该标志位当数组为空时,驱动程序就会往下执行
wait_event_interruptible(gpio_wait, !is_key_buf_empty());
一直等待到当 !is_key_buf_empty()条件成立后,才继续往下执行。
2、POLL
poll 函数原型:#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);参数说明:1. fds:传入传出参数,指向struct pollfd类型数组的首元素,每个数组元素指定一个描述符以及对其关心的状态,关于这个结构体的说明在本小节后面阐述。2. nfds:指明fds指向的数组元素个数。3. timeout:该参数指定poll阻塞等待文件描述符就绪的毫秒数。会被四舍五入到系统时钟粒度。这个参数有三种可能:○ timeout设置为负数:一直阻塞等待,直到有描述符准备就绪;○ timeout设置为 0:不等待,检查描述符后直接返回。○ timeout设置为正数:阻塞等待timeout设置的毫秒数,期间有描述符准备就绪就返回,没有就等到时间结束返回。返回值:成功返回已准备就绪的描述符个数;超时返回0;失败返回-1。
3、异步通知
APP
驱动
课程链接
21_GPIO子系统简述_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1XK411D7wK?p=22&vd_source=3a9afee9fda50350a1c881b4325e007d