07-ioctl控制LED软件实现(寄存器操作)
从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中):
01 - 第一个内核模块程序
02 - 注册字符设备驱动
03 - open & close 函数的应用
04 - read & write 函数的应用
05 - ioctl 的应用
06 - ioctl LED灯硬件分析
07 - ioctl 控制LED软件实现(寄存器操作)
08 - ioctl 控制LED软件实现(库函数操作)
09 - 注册字符设备的另一种方法(常用)
10 - 一个cdev实现对多个设备的支持
11 - 四个cdev控制四个LED设备
12 - 虚拟串口驱动
13 - I2C驱动
14 - SPI协议及驱动讲解
15 - SPI Linux驱动代码实现
16 - 非阻塞型I/O
17 - 阻塞型I/O
18 - I/O多路复用之 select
19 - I/O多路复用之 poll
20 - I/O多路复用之 epoll
21 - 异步通知
文章目录
- 1. 预备知识
- 1.1 ioremap
- 1.2 readl
- 1.3 writel
- 1.4 iounmap
- 2. 代码实现
- 2.1 demo.c
- 2.2 test.c
- 2.3 chrdev.h
- 2.4 Makefile
- 2.5 输出结果
&emsp:上一节讲到了LED的硬件分析,得到了LED控制的流程,详见
ioctl LED硬件分析
这一节将接着上一节的分析,对LED控制的软件部分进行实现,本节实现依据的是直接对相关的寄存器进行操作,下一节将利用库函数对LED的软件控制部分进行实现。
1. 预备知识
在驱动中不能对实际的物理地址进行操作,操作的都是虚拟地址。因此需要先了解物理地址和虚拟地址进行转换的相关函数。
1.1 ioremap
将物理地址映射为虚拟地址的函数是ioremap,函数定义在 include/asm/io.h 中,原型如下:
void __iomem *ioremap(phys_addr_t paddr, unsigned long size)
各参数的定义如下
@param1: 要映射的起始的IO地址(物理地址)
@param2: 要映射的空间的大小
@return: 返回映射后的内核虚拟地址(3G-4G). 接着便可以通过读写该返回的内核虚拟地址去访问之这段I/O内存资源
示例:#define CONF_GPMC_AD4 0x44E10810void __iomem *conf_gpmc_ad4_vir = NULL;conf_gpmc_ad4_vir = (unsigned int *)ioremap(CONF_GPMC_AD4, 4);
1.2 readl
内存映射之后,可以通过 readl 函数获取寄存器的状态,函数定义在 include/asm/io.h 中,原型如下
static inline u32 readl(const volatile void __iomem *addr)
各参数的定义如下
@param1: 映射后的内核虚拟地址,ioremap的返回值
@return: 读到的虚拟地址中的值
示例:readl(conf_gpmc_ad4_vir);
1.3 writel
将内存中的数据读出来后,可以通过 writel 往内存映射的 I/O 空间上写数据,函数定义在 include/asm/io.h 中,原型如下
static inline void writel(u32 b, volatile void __iomem *addr)
各参数的定义如下
@param1: 要写入的数据
@param2: 映射后的内核虚拟地址,ioremap的返回值
@return: 无返回值
示例:writel(( readl(conf_gpmc_ad4_vir) | 0x7 | 0x1<<5), conf_gpmc_ad4_vir);// 读出寄存器中的值,设置相应位然后写进去
1.4 iounmap
有映射函数就有相对应的取消映射函数,函数定义在 include/asm/io.h 中,原型如下
static inline void iounmap(volatile void __iomem *addr)
各参数的定义如下
@param1: 映射后的内核虚拟地址,ioremap的返回值
@return: 无返回值
示例:iounmap(conf_gpmc_ad4_vir);
2. 代码实现
2.1 demo.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>#include "chrdev.h"int major;
const char *name = "demoname";struct class *cls;
struct device *dev;#define CONF_GPMC_AD4 0x44E10810
#define GPIO1_OE 0x4804C134
#define GPIO1_DATAOUT 0x4804C13Cvoid __iomem *conf_gpmc_ad4_vir = NULL;
void __iomem *conf_gpmc_ad5_vir = NULL;
void __iomem *conf_gpmc_ad6_vir = NULL;
void __iomem *conf_gpmc_ad7_vir = NULL;
void __iomem *gpio1_oe_vir = NULL;
void __iomem *gpio1_dataout_vir = NULL; int demo_open(struct inode *inode, struct file *filp)
{printk("%s -- %d.\n", __FUNCTION__, __LINE__);return 0;
}int demo_release(struct inode *inode, struct file *filp)
{printk("%s -- %d.\n", __FUNCTION__, __LINE__);return 0;
}ssize_t demo_read(struct file *filp, char __user *userbuf, size_t size, loff_t *offset)
{printk("%s -- %d.\n", __FUNCTION__, __LINE__);//copy_to_user(void __user * to, const void * from, size_t n);return 0;
}ssize_t demo_write(struct file *filp, const char __user *userbuf, size_t size, loff_t *offset)
{printk("%s -- %d.\n", __FUNCTION__, __LINE__);//copy_from_user(void * to, const void __user * from, unsigned long n);return 0;
}long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{ led_node_t led_t;int ret, led_num;printk("%s -- %d.\n", __FUNCTION__, __LINE__);if ( _IOC_TYPE(cmd) != 'd') /* 判断命令与头文件定义的是否一致 */{return -ENOTTY; /* not a typewriter */}ret = copy_from_user(&led_t, (led_node_t *)args, sizeof(led_t)); //成功返回0,失败返回有多少个Bytes未完成copy。if (ret){printk("copy_from_user failed.\n");goto err0;}led_num = led_t.which;switch(cmd){case LEDON:if (led_num == 1){printk("led1 on.\n");writel(readl(gpio1_dataout_vir) | (0X1 << 4), gpio1_dataout_vir); // 置1,高电平}else if (led_num == 2){printk("led2 on.\n");writel(readl(gpio1_dataout_vir) | (0X1 << 5), gpio1_dataout_vir); // 置1,高电平}else if (led_num == 3){printk("led3 on.\n");writel(readl(gpio1_dataout_vir) | (0X1 << 6), gpio1_dataout_vir); // 置1,高电平}else if (led_num == 4){printk("led4 on.\n");writel(readl(gpio1_dataout_vir) | (0X1 << 7), gpio1_dataout_vir); // 置1,高电平}break;case LEDOFF:if (led_num == 1){printk("led1 off.\n");writel(readl(gpio1_dataout_vir) & (~(0X1 << 4)), gpio1_dataout_vir); // 清0,低电平}else if (led_num == 2){printk("led2 off.\n");writel(readl(gpio1_dataout_vir) & (~(0X1 << 5)), gpio1_dataout_vir); // 清0,低电平}else if (led_num == 3){printk("led3 off.\n");writel(readl(gpio1_dataout_vir) & (~(0X1 << 6)), gpio1_dataout_vir); // 清0,低电平}else if (led_num == 4){printk("led4 off.\n");writel(readl(gpio1_dataout_vir) & (~(0X1 << 7)), gpio1_dataout_vir); // 清0,低电平}break;default:printk("cmd id error.\n");}return 0;err0:return ret;
}const struct file_operations fops = {.open = demo_open,.release = demo_release,.read = demo_read,.write = demo_write,.unlocked_ioctl = demo_ioctl,
};void gpio_iounmap(void)
{iounmap(gpio1_oe_vir);
}void gpio_ioremap(void)
{conf_gpmc_ad4_vir = (unsigned int *)ioremap(CONF_GPMC_AD4, 4);conf_gpmc_ad5_vir = conf_gpmc_ad4_vir + 4;conf_gpmc_ad6_vir = conf_gpmc_ad4_vir + 8;conf_gpmc_ad7_vir = conf_gpmc_ad4_vir + 12;gpio1_oe_vir = (unsigned int *)ioremap(GPIO1_OE, 4);gpio1_dataout_vir = gpio1_oe_vir + 8;
}void led_init(void)
{gpio_ioremap();writel(( readl(conf_gpmc_ad4_vir) | 0x7 | 0x1<<5), conf_gpmc_ad4_vir); /* 引脚复用,选择MOD7,即GPIO */writel(( readl(conf_gpmc_ad5_vir) | 0x7 | 0x1<<5), conf_gpmc_ad5_vir);writel(( readl(conf_gpmc_ad6_vir) | 0x7 | 0x1<<5), conf_gpmc_ad6_vir);writel(( readl(conf_gpmc_ad7_vir) | 0x7 | 0x1<<5), conf_gpmc_ad7_vir);writel(( readl(gpio1_oe_vir) & (~(0xF << 4)) ), gpio1_oe_vir); /* GPIO1_4~GPIO1_7引脚设置为输出功能(0表示输出) */
}static int __init demo_init(void)
{printk("%s -- %d.\n", __FUNCTION__, __LINE__);major = register_chrdev(0, name, &fops);if (major <= 0){printk("register_chrdev failed.\n");goto err0;}cls = class_create(THIS_MODULE, "char_class");if (cls == NULL){printk("class_create failed.\n");goto err1;}dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "chrdev%d", 0);if (dev ==NULL){printk("device_create failed.\n");goto err2;}led_init(); // 设置为输出模式return 0;err2:class_destroy(cls);
err1:unregister_chrdev(major, name);
err0:return major;
}static void __exit demo_exit(void)
{printk("%s -- %d.\n", __FUNCTION__, __LINE__);gpio_iounmap(); //解除映射device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, name);
}module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
2.2 test.c
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include "chrdev.h"const char *pathname = "/dev/chrdev0";
#define LED_NUM 4int main(void)
{int fd, i;led_node_t led;fd = open(pathname, O_RDWR, 0666);if (fd <= 0){printf("open failed.\n");return -1;}for (i=1; i<=LED_NUM; i++){led.which = i;led.status = 0; // 0表示灭ioctl(fd, LEDOFF, &led);sleep(1);led.status = 1; // 1表示亮ioctl(fd, LEDON, &led);sleep(1);}close(fd);return 0;
}
2.3 chrdev.h
#ifndef _CHRDEV_H_
#define _CHRDEV_H_typedef struct led_node
{int which;int status;
}led_node_t;#define LED_MAGIC 'q'
#define LEDON _IOW(LED_MAGIC, 0, struct led_node)
#define LEDOFF _IOW(LED_MAGIC, 1, struct led_node)#endif /* chrdev.h */
2.4 Makefile
KERNELDIR ?= /home/linux/ti-processor-sdk-linux-am335x-evm-05.02.00.10/board-support/linux-4.14.79/
PWD := $(shell pwd)all:make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) modulesarm-linux-gnueabihf-gcc test.c -o app
install:sudo cp *.ko app /tftpbootmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) cleanrm app
clean:make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) cleanrm appobj-m += demo.o
2.5 输出结果
root@am335x-evm:~# insmod demo.ko
[ 3485.051113] demo_init -- 169.
root@am335x-evm:~# ./app
[ 3486.920446] demo_open -- 28.
[ 3486.923853] demo_ioctl -- 81.
[ 3486.927475] led1 off.
[ 3487.930225] demo_ioctl -- 81.
[ 3487.934385] led1 on.
[ 3488.936823] demo_ioctl -- 81.
[ 3488.939868] led2 off.
[ 3489.948584] demo_ioctl -- 81.
[ 3489.951628] led2 on.
[ 3490.960095] demo_ioctl -- 81.
[ 3490.964276] led3 off.
[ 3491.966745] demo_ioctl -- 81.
[ 3491.969787] led3 on.
[ 3492.972221] demo_ioctl -- 81.
[ 3492.975268] led4 off.
[ 3493.977737] demo_ioctl -- 81.
[ 3493.980786] led4 on.
[ 3494.992661] demo_release -- 35.
root@am335x-evm:~# rmmod demo.ko
[ 3503.501455] demo_exit -- 206.
07-ioctl控制LED软件实现(寄存器操作)相关推荐
- 08-ioctl控制LED软件实现(库函数操作)
从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中): 01 - 第一个内核模块程序 02 - 注册字符设备驱动 03 - open & close 函数 ...
- Arduino基础入门篇07—按键控制LED灯
前面介绍了Arduino数字I/O,通过控制数字引脚输出来控制LED灯亮灭.本篇将介绍数字I/O的输入功能,通过检测按键状态来控制LED灯亮灭,把LED的亮灭变成人为可控制的. 1. 实验材料 Uno ...
- 二:FPGA导航按键控制LED
FPGA按键控制LED 软件 芯片 功能 代码 代码解释 软件 软件使用的是ISE14.6(因为穷没买7系列,劝大家买个7系列的板子,这个软件装着还挺费事,不如Vivado好用,且6和7软件不通用) ...
- STM32F103系列之按键控制LED灯
上篇已经介绍了点灯的操作了,本篇主要介绍按键控制LED灯进行相关操作. 同样,我们需要对GPIO进行相关的初始化配置,这里我们将LED相关的代码全部放在LED.C和LED.H文件里面. LED.C文件 ...
- keil 4c语言读写寄存器,【STM32单片机学习】第四课:GPIO控制LED(用寄存器编程)
[朱老师课程总结] 第一部分.章节目录 第二部分.章节介绍 3.5.1.STM32的GPIO模块数据手册详解1 本节讲解STM32数据手册中GPIO模块相关的部分,主要是GPIO的各类模式及其配置方法 ...
- 软件编程控制硬件的关键——寄存器
为什么软件能操控硬件? 1. 什么是寄存器?寄存器属于CPU外设的硬件组成部分. 2. CPU可以像访问内存一样访问寄存器(IO与内存统一编址) 3. 寄存器是CPU硬件设计者制定的,目的是留作外设被 ...
- 嵌入式学习笔记——寄存器实现控制LED小灯
文章目录 前言 GPIO通用输出模式 初始化LED小灯的GPIO 原理图 初始化代码 初始化的效果 功能函数封装 直接分开宏定义两个 使用条件运算符 封装函数实现简单的功能 KEIL MDK一些技巧 ...
- 鸿蒙系统分享----软件驱动使用JS控制LED灯实验方法
第一:鸿蒙系统环境搭建方法 1.BearPi-HM Micro ubuntu 镜像下载和虚拟机VMware Workstation 安装(虚拟机环境) 2.VS Code的安装和安装RaiDrive工 ...
- 从ARM裸机看驱动之按键中断方式控制LED(二)
硬件环境:Samsung Cortex-A9 Exynos4412 BSP 软件环境:Linux3.14 =============================================== ...
最新文章
- 【Sql Server】DateBase-视图
- Java复习回顾---7月10日回顾练习
- 【错误记录】Flutter 界面跳转报错 ( Navigator operation requested with a context that does not include a Naviga )
- 使用阿里云docker加速器
- 西工大java高级网络编程_奥鹏西工大16春《JAVA高级网络编程》平时作业
- 演示一个简单的Redis队列
- HDU3595_GG and MM
- postgresql学习笔记(五)备份与恢复
- 2023年山东大学社会工作考研成功上岸经验分享
- 计算机组装维修中级试题,维修电工中级培训考试题及答案
- Origin 2022b | 更新及安装 | 中英文切换
- 计算机学院java男默女泪,最新网络用语学习笔记,看了后,男默女泪
- CVPR 2022 | 基于密度与深度分解的自增强非成对图像去雾
- java版微信调小i机器人接口说明书_java版微信调用小i机器人
- 自学无果 报班学习的每日知识点总结与回顾 0基础学前端的小伙伴可以进来看看 一起学习一起进步(三)
- 故宫买票显示服务器开小差,比春运车票还难抢的故宫灯会 预约系统直接崩溃...
- 安装google输入法后,左shift键不能切换中英文
- EarthSDK 项目开发使用说明
- 计算机网络笔记(复习)
- Unity--分场景
热门文章
- Word插入图片后全篇文章都变换图片序号
- 简单制作飞机大战游戏。
- Tomcat问题Cannot find /data/tomcat/bin/setclasspath.sh
- 实验6 存储过程mysql_MySQL数据库实验:任务六 数据库存储过程设计
- 用html做照片墙的教程,HTML5开发项目实战:照片墙(示例代码)
- 5G学习笔记:NR帧结构
- Excel教程中INDEX和MATCH函数应用
- 基于nsct变换特征提取和模糊神经网络的无参考图像质量评价算法matlab仿真
- 《2021年轻人熬夜报告》出炉~
- zabbix源码安装3.4.11客户端和yum安装4.2.1客户端