驱动开发作业2 —— ioctl

通过ioctl函数选择不同硬件的控制,如实现对LED、蜂鸣器、马达、风扇的控制

1.将GPIO的相关寄存器封装成结构体  --------> gpio.h
2.LED相关驱动文件 --------> led.c
3.蜂鸣器相关驱动文件  --------> beep.c
4.风扇相关驱动文件 --------> fan.c
5.马达相关驱动文件  --------> motor.c

6.应用层测试文件  --------> test.c

 gpio.h

/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-07-05 16:21:43* @LastEditTime: 2023-07-05 16:47:21* @FilePath: /B_Drive/day3/led_beep_fan_motor/gpio.h* @version:* @Description:*/
#ifndef __GPIO_H_
#define __GPIO_H_typedef struct {volatile unsigned int MODER; // 0x00volatile unsigned int OTYPER; // 0x04volatile unsigned int OSPEEDR; // 0x08volatile unsigned int PUPDR; // 0x0Cvolatile unsigned int IDR; // 0x10volatile unsigned int ODR; // 0x14volatile unsigned int BSRR; // 0x18volatile unsigned int LCKR; // 0x1Cvolatile unsigned int AFRL; // 0x20volatile unsigned int AFRH; // 0x24volatile unsigned int BRR; // 0x28volatile unsigned int res;volatile unsigned int SECCFGR; // 0x30
} gpio_t;#define PHY_RCC_GPIO 0x50000A28
#define PHY_GPIOB_BASE 0x50003000
#define PHY_GPIOE_BASE 0x50006000
#define PHY_GPIOF_BASE 0x50007000#define LED1_ON _IO('l', 1)
#define LED1_OFF _IO('l', 0)
#define LED_ON _IOW('l', 1, int)
#define LED_OFF _IOW('l', 0, int)#define FAN_ON _IO('f', 1)
#define FAN_OFF _IO('f', 0)#define BEEP_ON _IO('b', 1)
#define BEEP_OFF _IO('b', 0)#define MOTOR_ON _IO('m', 1)
#define MOTOR_OFF _IO('m', 0)#endif

led.c

/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:24:06* @FilePath: /B_Drive/day3/led_beep_fan_motor/led.c* @version:* @Description: 驱动控制LED灯*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "myled"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOE_BASE;
gpio_t* KERNEL_GPIOF_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOE_BASE = ioremap(PHY_GPIOE_BASE, sizeof(gpio_t));if (KERNEL_GPIOE_BASE == NULL) {printk("ioremap GPIOE BASE error!\n");return -ENOMEM;}KERNEL_GPIOF_BASE = ioremap(PHY_GPIOF_BASE, sizeof(gpio_t));if (KERNEL_GPIOF_BASE == NULL) {printk("ioremap GPIOF BASE error!\n");return -ENOMEM;}/* LED1(PE10)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 4), KERNEL_RCC_GPIO); /* GPIOE时钟使能 */writel(readl(&KERNEL_GPIOE_BASE->MODER) & (~(0x3 << 20)), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器21~20bit清零 */writel(readl(&KERNEL_GPIOE_BASE->MODER) | (0x1 << 20), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOE_BASE->ODR); /* LED1熄灭 *//* LED2(PF10)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 5), KERNEL_RCC_GPIO); /* GPIOF时钟使能 */writel(readl(&KERNEL_GPIOF_BASE->MODER) & (~(0x3 << 20)), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器21~20bit清零 */writel(readl(&KERNEL_GPIOF_BASE->MODER) | (0x1 << 20), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOF_BASE->ODR); /* LED2熄灭 *//* LED3(PE8)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 4), KERNEL_RCC_GPIO); /* GPIOE时钟使能 */writel(readl(&KERNEL_GPIOE_BASE->MODER) & (~(0x3 << 16)), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器21~20bit清零 */writel(readl(&KERNEL_GPIOE_BASE->MODER) | (0x1 << 16), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 8)), &KERNEL_GPIOE_BASE->ODR); /* LED3熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}// switch (kbuf[0]) {// case '1': /* LED1点亮 *///     writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOE_BASE->ODR);//     break;// case '2': /* LED1熄灭 *///     writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOE_BASE->ODR);//     break;// case '3': /* LED2点亮 *///     writel(readl(&KERNEL_GPIOF_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOF_BASE->ODR);//     break;// case '4': /* LED2熄灭 *///     writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOF_BASE->ODR);//     break;// case '5': /* LED3点亮 *///     writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 8), &KERNEL_GPIOE_BASE->ODR);//     break;// case '6': /* LED3熄灭 *///     writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 8)), &KERNEL_GPIOE_BASE->ODR);//     break;// }// kbuf[0] == 1 ? (writel(readl(&KERNEL_GPIOE_BASE->ODR) | ((1 << 10)), &KERNEL_GPIOE_BASE->ODR)) : (writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(1 << 10)), &KERNEL_GPIOE_BASE->ODR));return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case LED_ON:switch (arg) {case 1:writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOE_BASE->ODR);break;case 2:writel(readl(&KERNEL_GPIOF_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOF_BASE->ODR);break;case 3:writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 8), &KERNEL_GPIOE_BASE->ODR);break;default:break;}break;case LED_OFF:switch (arg) {case 1:writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOE_BASE->ODR);break;case 2:writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOF_BASE->ODR);break;case 3:writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 8)), &KERNEL_GPIOE_BASE->ODR);break;default:break;}break;default: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,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled");if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOE_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");
beep.c
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:36:58* @FilePath: /B_Drive/day3/led_beep_fan_motor/beep.c* @version:* @Description: 驱动控制风扇*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "mybeep"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOB_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOB_BASE = ioremap(PHY_GPIOB_BASE, sizeof(gpio_t));if (KERNEL_GPIOB_BASE == NULL) {printk("ioremap GPIOB BASE error!\n");return -ENOMEM;}/* 蜂鸣器(PB6)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 1), KERNEL_RCC_GPIO); /* GPIOB时钟使能 */writel(readl(&KERNEL_GPIOB_BASE->MODER) & (~(0x3 << 12)), &KERNEL_GPIOB_BASE->MODER); /* MODER寄存器13~12bit清零 */writel(readl(&KERNEL_GPIOB_BASE->MODER) | (0x1 << 12), &KERNEL_GPIOB_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOB_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOB_BASE->ODR); /* 蜂鸣器熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case BEEP_ON:writel(readl(&KERNEL_GPIOB_BASE->ODR) | (0x1 << 6), &KERNEL_GPIOB_BASE->ODR);break;case BEEP_OFF:writel(readl(&KERNEL_GPIOB_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOB_BASE->ODR);break;default: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,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOB_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");

fan.c

/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:59:43* @FilePath: /B_Drive/day3/led_beep_fan_motor/fan.c* @version:* @Description: 驱动控制风扇*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "myfan"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOE_BASE;
gpio_t* KERNEL_GPIOF_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOE_BASE = ioremap(PHY_GPIOE_BASE, sizeof(gpio_t));if (KERNEL_GPIOE_BASE == NULL) {printk("ioremap GPIOE BASE error!\n");return -ENOMEM;}/* 风扇(PE9)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 4), KERNEL_RCC_GPIO); /* GPIOE时钟使能 */writel(readl(&KERNEL_GPIOE_BASE->MODER) & (~(0x3 << 18)), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器19~18bit清零 */writel(readl(&KERNEL_GPIOE_BASE->MODER) | (0x1 << 18), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 9)), &KERNEL_GPIOE_BASE->ODR); /* 风扇熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case FAN_ON:writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 9), &KERNEL_GPIOE_BASE->ODR);break;case FAN_OFF:writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 9)), &KERNEL_GPIOE_BASE->ODR);break;default: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,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOE_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");

motor.c

/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:53:57* @FilePath: /B_Drive/day3/led_beep_fan_motor/motor.c* @version:* @Description: 驱动控制马达*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "mymotor"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOF_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOF_BASE = ioremap(PHY_GPIOF_BASE, sizeof(gpio_t));if (KERNEL_GPIOF_BASE == NULL) {printk("ioremap GPIOB BASE error!\n");return -ENOMEM;}/* 马达(PF6)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 5), KERNEL_RCC_GPIO); /* GPIOB时钟使能 */writel(readl(&KERNEL_GPIOF_BASE->MODER) & (~(0x3 << 12)), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器13~12bit清零 */writel(readl(&KERNEL_GPIOF_BASE->MODER) | (0x1 << 12), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOF_BASE->ODR); /* 马达熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case MOTOR_ON:writel(readl(&KERNEL_GPIOF_BASE->ODR) | (0x1 << 6), &KERNEL_GPIOF_BASE->ODR);break;case MOTOR_OFF:writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOF_BASE->ODR);break;default: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,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOF_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");
test.c
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-07-03 14:44:33* @LastEditTime: 2023-07-05 16:59:23* @FilePath: /B_Drive/day3/led_beep_fan_motor/test.c* @version:* @Description: 驱动控制硬件的测试文件*/#include "gpio.h"
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>#define ERR_MSG(msg)                              \do {                                          \fprintf(stderr, "line : %d\n", __LINE__); \perror(msg);                              \} while (0)char buf[128] = { 0 };
int main(int argc, const char* argv[])
{int ledfd, beepfd, fanfd, motorfd; /* 文件描述符 */int hardware_which; /* 硬件设备选择 */int choice; /* 设备选择 */int whichled; /* LED选择 *//* 打开LED驱动文件 */if ((ledfd = open("/dev/myled", O_RDWR)) == -1)ERR_MSG("myled open error");/* 打开蜂鸣器驱动文件 */if ((beepfd = open("/dev/mybeep", O_RDWR)) == -1)ERR_MSG("mybeep open error");/* 打开风扇驱动文件 */if ((fanfd = open("/dev/myfan", O_RDWR)) == -1)ERR_MSG("myfan open error");/* 打开马达驱动文件 */if ((motorfd = open("/dev/mymotor", O_RDWR)) == -1)ERR_MSG("mymotor open error");while (1) {printf("请输入你要执行的硬件设备\n");printf("1(LED灯) 2(蜂鸣器) 3(风扇) 4(马达) > ");scanf("%d", &hardware_which);switch (hardware_which) {case 1:printf("请选择要控制的LED\n");printf("1(LED1) 2(LED2) 3(LED3) > ");scanf("%d", &whichled);printf("请选择要实现的功能\n");printf("0(关灯) 1(开灯) > ");scanf("%d", &choice);if (choice == 0) {ioctl(ledfd, LED_OFF, whichled);} else {ioctl(ledfd, LED_ON, whichled);}break;case 2:printf("请选择要实现的蜂鸣器功能\n");printf("0(不响) 1(响) > ");scanf("%d", &choice);if (choice == 0) {ioctl(beepfd, BEEP_OFF);} else {ioctl(beepfd, BEEP_ON);}break;case 3:printf("请选择要实现的风扇功能\n");printf("0(停止) 1(转动) > ");scanf("%d", &choice);if (choice == 0) {ioctl(fanfd, FAN_OFF);} else {ioctl(fanfd, FAN_ON);}break;case 4:printf("请选择要实现的马达功能\n");printf("0(停止) 1(振动) > ");scanf("%d", &choice);if (choice == 0) {ioctl(motorfd, MOTOR_OFF);} else {ioctl(motorfd, MOTOR_ON);}break;default:break;}}close(ledfd);close(beepfd);close(fanfd);close(motorfd);return 0;
}
效果如下图:

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

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

相关文章

Spring 与 Servlet-2

学习笔记&#xff08;加油呀&#xff09;&#xff1a; Spring的通知类型 Spring 通知类型按切面功能调用的不同时刻&#xff0c;可以分为提供了 5 种 Advice 类型 1、前置通知 Before advice&#xff1a;在某连接点之前执行的通知&#xff0c;但这个通知不能阻止连接点之前的…

解决vmWare ESXI 7.3报错,客户机操作系统已禁用 CPU。请关闭或重置虚拟机(ESXI使用遇到问题解决记录文持续使用持续更新)

一&#xff1a;分析客户机操作系统已禁用 CPU" 这个错误通常是由以下原因之一引起的&#xff1a; 1. 虚拟机配置不正确&#xff1a;可能是您在虚拟机配置中选择了不受支持的 CPU 类型或功能。某些操作系统可能需要特定的 CPU 功能才能正常运行。如果您的虚拟机配置与操作…

关于windows本机开放端口后,同一个网络下的其他电脑telnet不通的解决办法

前提&#xff1a;电脑A与电脑B均在同一个wifi下。 本地电脑A 启动了一个kong网关&#xff08;不管是什么应用&#xff0c;只要开启了一个监听端口就行&#xff09;&#xff0c;并且将docker里面的8000端口映射到本机的8000端口&#xff0c; 此时在电脑A上的命令行运行&#xf…

github Couldn‘t connect to server

Couldnt connect to server 问题描述解决git clone 出错直接访问github没啥问题ping github.com手动指定域名映射关系再次测试git命令 总结参考 问题描述 前一天还是好好的&#xff0c;能git pull和git push&#xff0c;昨天回来之后怎么操作都是Couldnt connect to server。百…

小程序 web-view h5页面背景音乐自动播放

/*** 年度账单-登录首页*/ import React,{useEffect} from react import swiper/swiper-bundle.min.css import styles from ./styles.less import bgm from ./bgm2.mp3 // 主体 const annualAccountLoginIndex (props) > {const goAnnualAccount ()>{const {location: …

Methodot低代码开发教程——玩转表格增删改查分页

目录 1、背景介绍 2、连接数据源 2.1 新增数据源 2.2 填写数据源信息 3、表格数据的展示 3.1 新增查询&#xff0c;编写查询语句 3.2 使用表格组件 3.3 同步数据源与表格列名 4、表格的数据新增 4.1 新增查询&#xff0c;编写新增语句 4.2 表格配置新增一行&#xff0…

centos7系统一键离线安装docker

离线安装脚本 # 离线安装docker rpm -Uvh --force --nodeps *.rpm # 启动docker systemctl start docker sudo systemctl daemon-reload # 设置开机自动启动docker systemctl enable docker.service下载 程序包下载地址 https://gitcode.net/zenglg/centos7_docker_offline_…

Spring BeanFactory FactoryBean的区别?

文章目录 前言一、BeanFactory二、FactoryBean 前言 面试中被问到过Spring BeanFactory FactoryBean的区别&#xff1f;当时没答上来&#xff0c;感觉这就是一个文字游戏&#xff0c;后面仔细的了解了一下&#xff0c;分享给大家。 一、BeanFactory 在 Spring 中最核心的就是…

HJ76 尼科彻斯定理

描述 验证尼科彻斯定理&#xff0c;即&#xff1a;任何一个整数m的立方都可以写成m个连续奇数之和。 例如&#xff1a; 1^31 2^335 3^37911 4^313151719 输入一个正整数m&#xff08;m≤100&#xff09;&#xff0c;将m的立方写成m个连续奇数之和的形式输出。 数…

Linux进度条

Linux进度条 一.基本概念1.回车和换行2.缓冲区2.实现倒计时 二.进度条1.前置工作2.代码实现 一.基本概念 1.回车和换行 回车&#xff1a;指光标移到该行的起始位置&#xff08;\r&#xff09;。 换行&#xff1a;换到下一行&#xff08;\n&#xff09;。 在c语音里\n将回车和换…

Jvm内存模型剖析优化-JVM(四)

上篇文章代码实例详解如何自定义双亲委派&#xff0c;主要实现ClassLoader&#xff0c;有两个方法&#xff0c;一个直接loadClass用父类的&#xff0c;如果想在破坏&#xff0c;则需要重写loadClass&#xff0c;一个findClass必须要重新&#xff0c;因为父类是空的&#xff0c;…

arcgis的属性显示bug

arcgis中&#xff0c;右键图层属性可以查看图层的属性信息&#xff0c;比如坐标系、波段数、行列数等。 但是今天实验的时候发现&#xff0c;这个属性中显示的波段最大最小值并非真值。 该图层实际范围为&#xff1a;30~711。 在arcgis属性中&#xff0c;显示范围为0~651。 在…