从内核中最简单的驱动程序入手,描述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软件实现(寄存器操作)相关推荐

  1. 08-ioctl控制LED软件实现(库函数操作)

    从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中):  01 - 第一个内核模块程序  02 - 注册字符设备驱动  03 - open & close 函数 ...

  2. Arduino基础入门篇07—按键控制LED灯

    前面介绍了Arduino数字I/O,通过控制数字引脚输出来控制LED灯亮灭.本篇将介绍数字I/O的输入功能,通过检测按键状态来控制LED灯亮灭,把LED的亮灭变成人为可控制的. 1. 实验材料 Uno ...

  3. 二:FPGA导航按键控制LED

    FPGA按键控制LED 软件 芯片 功能 代码 代码解释 软件 软件使用的是ISE14.6(因为穷没买7系列,劝大家买个7系列的板子,这个软件装着还挺费事,不如Vivado好用,且6和7软件不通用) ...

  4. STM32F103系列之按键控制LED灯

    上篇已经介绍了点灯的操作了,本篇主要介绍按键控制LED灯进行相关操作. 同样,我们需要对GPIO进行相关的初始化配置,这里我们将LED相关的代码全部放在LED.C和LED.H文件里面. LED.C文件 ...

  5. keil 4c语言读写寄存器,【STM32单片机学习】第四课:GPIO控制LED(用寄存器编程)

    [朱老师课程总结] 第一部分.章节目录 第二部分.章节介绍 3.5.1.STM32的GPIO模块数据手册详解1 本节讲解STM32数据手册中GPIO模块相关的部分,主要是GPIO的各类模式及其配置方法 ...

  6. 软件编程控制硬件的关键——寄存器

    为什么软件能操控硬件? 1. 什么是寄存器?寄存器属于CPU外设的硬件组成部分. 2. CPU可以像访问内存一样访问寄存器(IO与内存统一编址) 3. 寄存器是CPU硬件设计者制定的,目的是留作外设被 ...

  7. 嵌入式学习笔记——寄存器实现控制LED小灯

    文章目录 前言 GPIO通用输出模式 初始化LED小灯的GPIO 原理图 初始化代码 初始化的效果 功能函数封装 直接分开宏定义两个 使用条件运算符 封装函数实现简单的功能 KEIL MDK一些技巧 ...

  8. 鸿蒙系统分享----软件驱动使用JS控制LED灯实验方法

    第一:鸿蒙系统环境搭建方法 1.BearPi-HM Micro ubuntu 镜像下载和虚拟机VMware Workstation 安装(虚拟机环境) 2.VS Code的安装和安装RaiDrive工 ...

  9. 从ARM裸机看驱动之按键中断方式控制LED(二)

    硬件环境:Samsung Cortex-A9 Exynos4412 BSP 软件环境:Linux3.14 =============================================== ...

最新文章

  1. 【Sql Server】DateBase-视图
  2. Java复习回顾---7月10日回顾练习
  3. 【错误记录】Flutter 界面跳转报错 ( Navigator operation requested with a context that does not include a Naviga )
  4. 使用阿里云docker加速器
  5. 西工大java高级网络编程_奥鹏西工大16春《JAVA高级网络编程》平时作业
  6. 演示一个简单的Redis队列
  7. HDU3595_GG and MM
  8. postgresql学习笔记(五)备份与恢复
  9. 2023年山东大学社会工作考研成功上岸经验分享
  10. 计算机组装维修中级试题,维修电工中级培训考试题及答案
  11. Origin 2022b | 更新及安装 | 中英文切换
  12. 计算机学院java男默女泪,最新网络用语学习笔记,看了后,男默女泪
  13. CVPR 2022 | 基于密度与深度分解的自增强非成对图像去雾
  14. java版微信调小i机器人接口说明书_java版微信调用小i机器人
  15. 自学无果 报班学习的每日知识点总结与回顾 0基础学前端的小伙伴可以进来看看 一起学习一起进步(三)
  16. 故宫买票显示服务器开小差,比春运车票还难抢的故宫灯会 预约系统直接崩溃...
  17. 安装google输入法后,左shift键不能切换中英文
  18. EarthSDK 项目开发使用说明
  19. 计算机网络笔记(复习)
  20. Unity--分场景

热门文章

  1. Word插入图片后全篇文章都变换图片序号
  2. 简单制作飞机大战游戏。
  3. Tomcat问题Cannot find /data/tomcat/bin/setclasspath.sh
  4. 实验6 存储过程mysql_MySQL数据库实验:任务六 数据库存储过程设计
  5. 用html做照片墙的教程,HTML5开发项目实战:照片墙(示例代码)
  6. 5G学习笔记:NR帧结构
  7. Excel教程中INDEX和MATCH函数应用
  8. 基于nsct变换特征提取和模糊神经网络的无参考图像质量评价算法matlab仿真
  9. 《2021年轻人熬夜报告》出炉~
  10. zabbix源码安装3.4.11客户端和yum安装4.2.1客户端