驱动开发,stm32mp157a开发板的led灯控制实验(再优化),使用ioctl函数,通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定

 1.实验目的

        编写LED灯的驱动,在应用程序中编写控制LED灯亮灭的代码逻辑实现LED灯功能的控制;

 

 2. LED灯相关寄存器分析

 

LED1->PE10 LED1亮灭:

RCC寄存器[4]->1 0X50000A28

GPIOE_MODER[21:20]->01 (输出) 0X50006000

GPIOE_ODR[10]->1(输出高电平) 0(输出低电平)0X50006014

LED2->PF10 LED2亮灭:

RCC寄存器[5]->1 0X50000A28

GPIOE_MODER[21:20]->01 (输出) 0X50006000

GPIOE_ODR[10]->1(输出高电平) 0(输出低电平)0X50006014

LED3->PE8 LED3亮灭:

RCC寄存器[4]->1 0X50000A28

GPIOE_MODER[17:16]->01 (输出) 0X50006000

GPIOE_ODR[8]->1(输出高电平) 0(输出低电平)0X50006014

GPIOE_OTYPER默认为00

GPIOE_PUPDR默认为0

GPIOE_OSPEEDR默认为00

 

 3.字符设备驱动内部注册过程 

  • 分配struct cdev对象空间
  • 初始化struct cdev对象
  • 设备号的申请(静态/动态申请)
  • 注册cdev对象

 

 4.编写代码

---Makefile---工程管理文件

modname?=demo
arch?=arm
ifeq ($(arch),arm)
KERNELDIR:= /home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61 #编译生成ARM架构
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build #编译生成X86架构
endifPWD:=$(shell pwd) #模块化编译文件路径
all:make -C $(KERNELDIR) M=$(PWD) modules
clean:make -C $(KERNELDIR) M=$(PWD) cleanobj-m:=$(modname).o

---head.h---头文件

#ifndef __HEAD_H__
#define __HEAD_H__typedef struct
{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR;   
}gpio_t;//LED1和LED3寄存器地址
#define LED1_ADDR 0x50006000
#define LED2_ADDR 0x50007000
#define LED3_ADDR 0x50006000
#define RCC_ADDR 0x50000A28//构建LED开关功能码,不添加ioctl第三个参数
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',0)#endif

---cdev.c---驱动程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include "head.h"
#include <linux/io.h>struct cdev *cdev = NULL;
unsigned major = 0;
unsigned minor = 0;
module_param(major, uint, 0664); // 方便再命令行传递major的值
dev_t devno;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{// 设备文件和设备的绑定// 根据打开的文件对应的设备号获取 次设备号int min = MINOR(inode->i_rdev);file->private_data = (void *)min;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int min = (int)file->private_data;switch (min){case 0: // 控制LED1switch (cmd){case LED_ON: // 开灯vir_led1->ODR |= (0x1 << 10);break;case LED_OFF: // 关灯vir_led1->ODR &= (~(0x1 << 10));break;}break;case 2: // 控制LED2switch (cmd){case LED_ON: // 开灯vir_led2->ODR |= (0x1 << 10);break;case LED_OFF: // 关灯vir_led2->ODR &= (~(0x1 << 10));break;}break;case 3: // 控制LED3switch (cmd){case LED_ON: // 开灯vir_led3->ODR |= (0x1 << 8);break;case LED_OFF: // 关灯vir_led3->ODR &= (~(0x1 << 8));break;}break;}return 0;
}int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}struct file_operations fops = {.open = mycdev_open,.unlocked_ioctl = mycdev_ioctl,.release = mycdev_close,
};// 相关寄存器地址映射及初始化
int all_led_init(void)
{// 相关寄存器的内存映射vir_led1 = ioremap(LED1_ADDR, sizeof(gpio_t));if (vir_led1 == NULL){printk("物理内存映射失败%d\n", __LINE__);return -ENOMEM;}vir_led2 = ioremap(LED2_ADDR, sizeof(gpio_t));if (vir_led2 == NULL){printk("物理内存映射失败%d\n", __LINE__);return -ENOMEM;}vir_led3 = vir_led1;vir_rcc = ioremap(RCC_ADDR, 4);if (vir_rcc == NULL){printk("物理内存映射失败%d\n", __LINE__);return -ENOMEM;}printk("寄存器内存映射成功\n");// 硬件寄存器的初始化(*vir_rcc) |= (0x3 << 4);// LED1vir_led1->MODER &= (~(0x3 << 20));vir_led1->MODER |= (0x1 << 20);vir_led1->ODR &= (~(0x1 << 10));// LED2vir_led2->MODER &= (~(0x3 << 20));vir_led2->MODER |= (0x1 << 20);vir_led2->ODR &= (~(0x1 << 10));// LED3vir_led3->MODER &= (~(0x3 << 16));vir_led3->MODER |= (0x1 << 16);vir_led3->ODR &= (~(0x1 << 8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{int ret;// 分配字符设备驱动对象空间cdev = cdev_alloc();if (cdev == NULL){printk("字符设备驱动对象申请空间失败\n");ret = -EFAULT;goto out1;}printk("字符设备驱动对象申请空间成功\n");// 字符设备驱动对象初始化cdev_init(cdev, &fops);// 设备号的申请if (major > 0) // 静态指定设备号{ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");if (ret){printk("静态申请设备号失败\n");goto out2;}}else if (major == 0) 动态申请设备号{ret = alloc_chrdev_region(&devno, minor, 3, "myled");if (ret){printk("静态申请设备号失败\n");goto out2;}major = MAJOR(devno);minor = MINOR(devno);}printk("申请设备号成功\n");// 注册字符设备驱动对象ret = cdev_add(cdev, MKDEV(major, minor), 3);if (ret){printk("注册字符设备驱动对象失败\n");goto out3;}printk("注册字符设备驱动对象成功\n");相关寄存器地址映射及初始化all_led_init();// 向上提交目录信息cls = class_create(THIS_MODULE, "myled");if (IS_ERR(cls)){printk("向上提交目录信息失败\n");ret = -PTR_ERR(cls);goto out4;}printk("向上提交目录信息成功\n");// 向上提交设备节点信息int i;for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(dev)){printk("向上提交设备节点信息失败\n");ret = -PTR_ERR(dev);goto out5;}}printk("向上提交设备节点信息成功\n");return 0;
out5:// 释放前一次提交的设备信息for (--i; i >= 0; i--){device_destroy(cls, MKDEV(major, i));}class_destroy(cls); // 释放目录信息
out4:cdev_del(cdev);
out3:unregister_chrdev_region(MKDEV(major, minor), 3);
out2:kfree(cdev);
out1:return ret;
}static void __exit mycdev_exit(void)
{// 释放节点信息int i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 销毁目录class_destroy(cls);// 注销驱动对象cdev_del(cdev);// 释放申请的设备号和设备资源unregister_chrdev_region(MKDEV(major, minor), 3);// 释放字符设备驱动对象空间kfree(cdev);// 取消物理内存的映射iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_rcc);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

---test.c---应用程序测试程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "head.h"
#include <sys/ioctl.h>int main(int argc, char const *argv[])
{int a;char buf[128] = {0};int fd = open("/dev/myled0", O_RDWR);if (fd < 0){printf("设备文件打开失败\n");exit(-1);}while (1){printf("请输入对LED灯的控制:1(开灯) 0(关灯)>> ");scanf("%d",&a);getchar();switch (a){case 1:ioctl(fd, LED_ON); // 第三个参数为指针break;case 0:ioctl(fd, LED_OFF);break;}}close(fd);return 0;
}

 

5.测试现象

         int fd = open("/dev/myled0", O_RDWR);

        测试程序中只打开一个设备文件,对应LED1的次设备号,所以只控制LED1灯的亮灭

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/131603.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

024 - STM32学习笔记 - 液晶屏控制(一) - LTDC与DMA2D初始

024- STM32学习笔记 - LTDC控制液晶屏 在学习如何控制液晶屏之前&#xff0c;先了解一下显示屏的分类&#xff0c;按照目前市场上存在的各种屏幕材质&#xff0c;主要分为CRT阴极射线管显示屏、LCD液晶显示屏、LED显示屏、OLED显示屏&#xff0c;在F429的开发板上&#xff0c;…

Linux HTTP协议

目录 1.浏览器与服务器通信过程2.HTTP请求报头&#xff08;1&#xff09;HTTP的请求报头结构&#xff08;2&#xff09;HTTP的请求方法 3.HTTP应答报头&#xff08;1&#xff09;HTTP的应答报头结构&#xff08;2&#xff09; HTTP的应答状态 1.浏览器与服务器通信过程 浏览器…

Unity3D之动态生成指定数量带间隔的地面

文章目录 准备代码实现实现效果 准备 空物体生成脚本地面预制体 代码实现 using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine;public class CreateGround : MonoBehaviour {[SerializeField]public i…

Java版的数据结构——栈和队列

目录 1. 栈&#xff08;Stack&#xff09; 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1.4.1 改变元素的序列 1.4.2 将递归转化为循环 2. 队列&#xff08;Queue&#xff09; 2.1 概念 2.2 队列的使用 2.3 队列模拟实现 2.4 循环队列 3. 双端队列&…

Navicat15 /16 已连接数据库密码解密

前言 相信你会遇到使用navicat忘记已连接数据密码的问题吧&#xff01;实在是&#xff0c;密码太多容易忘记&#xff01;&#xff01;&#xff01; 感谢大佬as_dmy的文章如何查看navicat已连接数据库密码&#xff0c;然后才有了此文&#xff01; 1.0版本需要手动查看导出的co…

垃圾收集算法

1.如何判断对象是否存活&#xff1f; 1.1引用计数算法 基本思路&#xff1a; 在对象中添加一个引用计数器每当有一个地方引用它的时候&#xff0c;计数器就加1每当有一个引用失效的时候&#xff0c;计数器就减-1当计数器的值为0的时候&#xff0c;那么该对象就是可被GC回收的…

vue基础知识八:为什么data属性是一个函数而不是一个对象?

一、实例和组件定义data的区别 vue实例的时候定义data属性既可以是一个对象&#xff0c;也可以是一个函数 const app new Vue({el:"#app",// 对象格式data:{foo:"foo"},// 函数格式data(){return {foo:"foo"}} })组件中定义data属性&#xff…

网站文章生成技术-网站文章生成工具免费

大家好&#xff0c;今天我想和大家分享一些关于网站文章生成的疑虑和期待。作为一个常常需要在网站上发布文章的人&#xff0c;我对这项技术的发展充满了好奇和担忧。在这篇文章中&#xff0c;我将坦率地表达我的想法&#xff0c;希望能引发一些思考。 让我谈一谈我的疑虑。网站…

基于SSM的农产品仓库管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

TypeScript命名空间和模块

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 命名空间&#xff08;Namespace&#xff09; 命名空间&#xff08;Namespace&#xff09;使用场景 第三方库 兼容…

【C语言】【strcpy的使用和模拟实现】

1.strcpy的使用&#xff1a; char* strcpy(char* destination,const char* source)返回类型是字符指针&#xff0c;参数是接受方字符串的首地址和要拷贝的字符串的首地址 从接受地的‘\0’开始拷贝&#xff0c;会将源字符串中的’\0’也拷贝过来目标空间必须足够大&#xff0…

【JavaEE】_CSS引入方式与选择器

目录 1. 基本语法格式 2. 引入方式 2.1 内部样式 2.2 内联样式 2.3 外部样式 3. 基础选择器 3.1 标签选择器 3.2 类选择器 3.3 ID选择器 4. 复合选择器 4.1 后代选择器 4.2 子选择器 4.3 并集选择器 4.4 伪类选择器 1. 基本语法格式 选择器若干属性声明 2. 引入…

terraform简单的开始-安装和一些配置

terraform的安装&#xff1a; 官方下载&#xff1a; 浏览器打开terraform官方主页https://www.terraform.io/ 点击Download Terraform 跳转到程序下载页面&#xff1a; 找到自己对应的操作系统&#xff0c;按照操作系统选择安装terraform的方式&#xff1a; linux为例&…

LabVIEW利用人工神经网络辅助进行结冰检测

LabVIEW利用人工神经网络辅助进行结冰检测 结冰对各个领域构成重大威胁&#xff0c;包括但不限于航空航天和风力涡轮机行业。在起飞过程中&#xff0c;飞机机翼上轻微积冰会导致升力降低25%。研究报告称&#xff0c;涡轮叶片上的冰堆积可在19个月的运行时间内造成29MWh的功率损…

《86盒应用于家居中控》——实现智能家居的灵动掌控

近年来&#xff0c;智能家居产品受到越来越多消费者的关注&#xff0c;其便捷、舒适的生活方式让人们对未来生活充满期待。作为智能家居方案领域的方案商&#xff0c;启明智显生产设计的86盒凭借出色的性能和良好的用户体验&#xff0c;成功应用于家居中控系统&#xff0c;让家…

数据在内存中的存储——练习3

题目&#xff1a; 3.1 #include <stdio.h> int main() {char a -128;printf("%u\n",a);return 0; }3.2 #include <stdio.h> int main() {char a 128;printf("%u\n",a);return 0; }思路分析&#xff1a; 首先二者极其相似%u是无符号格式进行…

基于SSM的旅游网站系统

基于SSM的旅游网站系统【附源码文档】、前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色&#xff1a;管理员、用户 管理员&#xff1a;用户管理、景点…

【Linux】多线程互斥与同步

文章目录 一、线程互斥1. 线程互斥的引出2. 互斥量3. 互斥锁的实现原理 二、可重入和线程安全三、线程和互斥锁的封装1. 线程封装1. 互斥锁封装 四、死锁1. 死锁的概念2. 死锁的四个必要条件3. 避免死锁 五、线程同步1. 线程同步的理解2. 条件变量 一、线程互斥 1. 线程互斥的…

教你制作作业查询系统

嗨&#xff0c;各位老师们&#xff0c;今天我要给你们介绍一个超级方便的工具——易查分&#xff01;你知道吗&#xff0c;利用易查分&#xff0c;我们可以轻松制作一个便捷高效的作业查询系统哦&#xff01; 是不是想有个自己的分班or成绩查询页面&#xff1f;博主给老师们争取…

使用js搭建简易的WebRTC实现视频直播

首先需要一个信令服务器&#xff0c;我们使用nodejs来搭建。两个端&#xff1a;发送端和接收端。我的目录结构如下图&#xff1a;流程 创建一个文件夹 WebRTC-Test。进入文件夹中&#xff0c;新建一个node的文件夹。使用终端并进入node的目录下&#xff0c;使用 npm init 创建p…