seo网站优化做什么,批量替换wordpress页面文字,展厅设计包括哪些内容,企业erp生产管理系统Linux系统构建完成后#xff0c;就可以基于该环境方便地进行开发了#xff0c;相关的开发流程与MCU类似#xff0c;但是引入了设备树的概念#xff0c;编写应用代码要相对复杂一点。但是省去了很多配置工作。
学习视频地址#xff1a;【正点原子】STM32MP157开发板
字符…
Linux系统构建完成后就可以基于该环境方便地进行开发了相关的开发流程与MCU类似但是引入了设备树的概念编写应用代码要相对复杂一点。但是省去了很多配置工作。
学习视频地址【正点原子】STM32MP157开发板
字符设备驱动开发
驱动流程 在 Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过对这个名为“/dev/xxx”(xxx 是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作。 比如现在有个叫做/dev/led 的驱动文件,此文件是 led 灯的驱动文件。 open 函数来打开文件/dev/led,使用完成以后使用 close 函数关闭/dev/led 这个文件。 open和 close 就是打开和关闭 led 驱动的函数点亮或关闭 led,那么就使用 write 函数来操作,也就是向此驱动写入数据,这个数据就是要关闭还是要打开 led 的控制参数如果要获取led 灯的状态,就用 read 函数从驱动中读取相应的状态。 内核驱动函数及变量 owner拥有该结构体的模块的指针变量, 一般设置为 THIS_MODULE llseek修改文件当前的读写位置 read读取设备文件 write向设备文件写入数据 poll轮询函数用于查询设备是否可以进行非阻塞的读写。 unlocked_ioctl提供对于设备的控制功能,与应用程序中的 ioctl 函数对应 compat_ioctl函数与 unlocked_ioctl 函数功能一样,区别在于在 64 位系统上, 32 位的应用程序调用将会使用此函数。在 32 位的系统上运行 32 位的应用程序调用的是unlocked_ioctl。 mmap用于将将设备的内存映射到进程空间中(也就是用户空间),一般帧 缓冲设备会使用此函数,比如 LCD 驱动的显存,将帧缓冲(LCD 显存)映射到用户空间中以后应用程序就可以直接操作显存了,这样就不用在用户空间和内核空间之间来回复制。 open打开设备文件 release用于释放(关闭)设备文件,与应用程序中的 close 函数对应 fasync用于刷新待处理的数据,用于将缓冲区中的数据刷新到磁盘中
驱动开发步骤
1. 驱动模块的加载和卸载
将驱动编译进Linux内核中内核启动时就会自动运行驱动程序将驱动编译为模块(Linux 下模块扩展名为.ko)Linux内核启动后采用modprobe指令加载模块。
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数2. 字符设备的注册与注销
字符设备的注册与注销放在模块的加载和卸载函数中。
// 只指定主设备号会造成资源浪费
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major,const char *name)// 用多少申请多少比较合理
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
int register_chrdev_region(dev_t from, unsigned count, const char *name)
void unregister_chrdev_region(dev_t from, unsigned count)3. 类和设备的创建
这部分是为了设备可以自动创建和删除文件节点
// 类的创建和删除
struct class *class_create (struct module *owner, const char *name)
void class_destroy(struct class *cls)// 设备的创建和删除
struct device *device_create(struct class *cls,struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
void device_destroy(struct class *cls, dev_t devt)4. 实例代码
字符设备驱动
#include linux/types.h
#include linux/kernel.h
#include linux/delay.h
#include linux/ide.h
#include linux/init.h
#include linux/module.h#define CHRDEVBASE_MAJOR 100 /* 主设备号 */
#define CHRDEVBASE_NAME devbase /* 设备名 */static char readbuf[100]; /* 读缓冲区 */
static char writebuf[100]; /* 写缓冲区 */
static char kerneldata[] {kernel data!};/** description : 打开设备* param - inode : 传递给驱动的inode* param - filp : 设备文件file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* return : 0 成功;其他 失败*/
static int chrdevbase_open(struct inode *inode, struct file *filp)
{printk(devbase open! \r\n);return 0;
}/** description : 从设备读取数据 * param - filp : 要打开的设备文件(文件描述符)* param - buf : 返回给用户空间的数据缓冲区* param - cnt : 要读取的数据长度* param - offt : 相对于文件首地址的偏移* return : 读取的字节数如果为负值表示读取失败*/
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int retvalue 0;/* 向用户空间发送数据 */memcpy(readbuf, kerneldata, sizeof(kerneldata));retvalue copy_to_user(buf, readbuf, cnt);if(retvalue 0){printk(kernel senddata ok!\r\n);}else{printk(kernel senddata failed!\r\n);}return 0;
}/** description : 向设备写数据 * param - filp : 设备文件表示打开的文件描述符* param - buf : 要写给设备写入的数据* param - cnt : 要写入的数据长度* param - offt : 相对于文件首地址的偏移* return : 写入的字节数如果为负值表示写入失败*/
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue 0;/* 接收用户空间传递给内核的数据并且打印出来 */retvalue copy_from_user(writebuf, buf, cnt);if(retvalue 0){printk(kernel recevdata:%s\r\n, writebuf);}else{printk(kernel recevdata failed!\r\n);}return 0;
}/** description : 关闭/释放设备* param - filp : 要关闭的设备文件(文件描述符)* return : 0 成功;其他 失败*/
static int chrdevbase_release(struct inode *inode, struct file *filp)
{printk(devbase release! \r\n);return 0;
}/** 设备操作函数结构体*/
static struct file_operations devbase_fops {.owner THIS_MODULE, .open chrdevbase_open,.read chrdevbase_read,.write chrdevbase_write,.release chrdevbase_release,
};/** description : 驱动入口函数 * param : 无* return : 0 成功;其他 失败*/
static int __init chrdevbase_init(void)
{int retvalue 0;/* 注册字符设备驱动 */retvalue register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, devbase_fops);if(retvalue 0){printk(chrdevbase driver register failed\r\n);}printk(chrdevbase init!\r\n);return 0;
}/** description : 驱动出口函数* param : 无* return : 无*/
static void __exit chrdevbase_exit(void)
{/* 注销字符设备驱动 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk(chrdevbase exit!\r\n);
}/* * 将上面两个函数指定为驱动的入口和出口函数 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);/* * LICENSE和作者信息*/
MODULE_LICENSE(GPL);
MODULE_AUTHOR(JozenLee);新字符驱动代码以LED为例
#include linux/types.h
#include linux/kernel.h
#include linux/delay.h
#include linux/ide.h
#include linux/init.h
#include linux/module.h
#include linux/errno.h
#include linux/gpio.h
#include linux/cdev.h
#include linux/device.h
#include asm/mach/map.h
#include asm/uaccess.h
#include asm/io.h#define NEWCHRLED_CNT 1 /* 设备号个数 */
#define NEWCHRLED_NAME newchrled /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 *//* 寄存器物理地址 */
#define PERIPH_BASE (0x40000000)
#define MPU_AHB4_PERIPH_BASE (PERIPH_BASE 0x10000000)
#define RCC_BASE (MPU_AHB4_PERIPH_BASE 0x0000)
#define RCC_MP_AHB4ENSETR (RCC_BASE 0XA28)
#define GPIOI_BASE (MPU_AHB4_PERIPH_BASE 0xA000)
#define GPIOI_MODER (GPIOI_BASE 0x0000)
#define GPIOI_OTYPER (GPIOI_BASE 0x0004)
#define GPIOI_OSPEEDR (GPIOI_BASE 0x0008)
#define GPIOI_PUPDR (GPIOI_BASE 0x000C)
#define GPIOI_BSRR (GPIOI_BASE 0x0018)/* 映射后的寄存器虚拟地址指针 */
static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
static void __iomem *GPIOI_MODER_PI;
static void __iomem *GPIOI_OTYPER_PI;
static void __iomem *GPIOI_OSPEEDR_PI;
static void __iomem *GPIOI_PUPDR_PI;
static void __iomem *GPIOI_BSRR_PI;/* newchrled设备结构体 */
struct newchrled_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */
};struct newchrled_dev newchrled; /* led设备 *//** description : LED打开/关闭* param - sta : LEDON(0) 打开LEDLEDOFF(1) 关闭LED* return : 无*/
void led_switch(u8 sta)
{u32 val 0;if(sta LEDON) {val readl(GPIOI_BSRR_PI);val | (1 16); writel(val, GPIOI_BSRR_PI);}else if(sta LEDOFF) {val readl(GPIOI_BSRR_PI);val| (1 0); writel(val, GPIOI_BSRR_PI);}
}/** description : 取消映射* return : 无*/
void led_unmap(void)
{/* 取消映射 */iounmap(MPU_AHB4_PERIPH_RCC_PI);iounmap(GPIOI_MODER_PI);iounmap(GPIOI_OTYPER_PI);iounmap(GPIOI_OSPEEDR_PI);iounmap(GPIOI_PUPDR_PI);iounmap(GPIOI_BSRR_PI);
}/** description : 打开设备* param - inode : 传递给驱动的inode* param - filp : 设备文件file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* return : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp-private_data newchrled; /* 设置私有数据 */return 0;
}/** description : 从设备读取数据 * param - filp : 要打开的设备文件(文件描述符)* param - buf : 返回给用户空间的数据缓冲区* param - cnt : 要读取的数据长度* param - offt : 相对于文件首地址的偏移* return : 读取的字节数如果为负值表示读取失败*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** description : 向设备写数据 * param - filp : 设备文件表示打开的文件描述符* param - buf : 要写给设备写入的数据* param - cnt : 要写入的数据长度* param - offt : 相对于文件首地址的偏移* return : 写入的字节数如果为负值表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue copy_from_user(databuf, buf, cnt);if(retvalue 0) {printk(kernel write failed!\r\n);return -EFAULT;}ledstat databuf[0]; /* 获取状态值 */if(ledstat LEDON) { led_switch(LEDON); /* 打开LED灯 */} else if(ledstat LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}return 0;
}/** description : 关闭/释放设备* param - filp : 要关闭的设备文件(文件描述符)* return : 0 成功;其他 失败*/
static int led_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations newchrled_fops {.owner THIS_MODULE,.open led_open,.read led_read,.write led_write,.release led_release,
};/** description : 驱动出口函数* param : 无* return : 无*/
static int __init led_init(void)
{u32 val 0;int ret;/* 初始化LED *//* 1、寄存器地址映射 */MPU_AHB4_PERIPH_RCC_PI ioremap(RCC_MP_AHB4ENSETR, 4);GPIOI_MODER_PI ioremap(GPIOI_MODER, 4);GPIOI_OTYPER_PI ioremap(GPIOI_OTYPER, 4);GPIOI_OSPEEDR_PI ioremap(GPIOI_OSPEEDR, 4);GPIOI_PUPDR_PI ioremap(GPIOI_PUPDR, 4);GPIOI_BSRR_PI ioremap(GPIOI_BSRR, 4);/* 2、使能PI时钟 */val readl(MPU_AHB4_PERIPH_RCC_PI);val ~(0X1 8); /* 清除以前的设置 */val | (0X1 8); /* 设置新值 */writel(val, MPU_AHB4_PERIPH_RCC_PI);/* 3、设置PI0通用的输出模式。*/val readl(GPIOI_MODER_PI);val ~(0X3 0); /* bit0:1清零 */val | (0X1 0); /* bit0:1设置01 */writel(val, GPIOI_MODER_PI);/* 3、设置PI0为推挽模式。*/val readl(GPIOI_OTYPER_PI);val ~(0X1 0); /* bit0清零设置为上拉*/writel(val, GPIOI_OTYPER_PI);/* 4、设置PI0为高速。*/val readl(GPIOI_OSPEEDR_PI);val ~(0X3 0); /* bit0:1 清零 */val | (0x2 0); /* bit0:1 设置为10*/writel(val, GPIOI_OSPEEDR_PI);/* 5、设置PI0为上拉。*/val readl(GPIOI_PUPDR_PI);val ~(0X3 0); /* bit0:1 清零*/val | (0x1 0); /*bit0:1 设置为01*/writel(val,GPIOI_PUPDR_PI);/* 6、默认关闭LED */val readl(GPIOI_BSRR_PI);val | (0x1 0);writel(val, GPIOI_BSRR_PI);/* 注册字符设备驱动 *//* 1、创建设备号 */if (newchrled.major) { /* 定义了设备号 */newchrled.devid MKDEV(newchrled.major, 0);ret register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);if(ret 0) {pr_err(cannot register %s char driver [ret%d]\n,NEWCHRLED_NAME, NEWCHRLED_CNT);goto fail_map;}} else { /* 没有定义设备号 */ret alloc_chrdev_region(newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */if(ret 0) {pr_err(%s Couldnt alloc_chrdev_region, ret%d\r\n, NEWCHRLED_NAME, ret);goto fail_map;}newchrled.major MAJOR(newchrled.devid); /* 获取分配号的主设备号 */newchrled.minor MINOR(newchrled.devid); /* 获取分配号的次设备号 */}printk(newcheled major%d,minor%d\r\n,newchrled.major, newchrled.minor); /* 2、初始化cdev */newchrled.cdev.owner THIS_MODULE;cdev_init(newchrled.cdev, newchrled_fops);/* 3、添加一个cdev */ret cdev_add(newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);if(ret 0)goto del_unregister;/* 4、创建类 */newchrled.class class_create(THIS_MODULE, NEWCHRLED_NAME);if (IS_ERR(newchrled.class)) {goto del_cdev;}/* 5、创建设备 */newchrled.device device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);if (IS_ERR(newchrled.device)) {goto destroy_class;}return 0;destroy_class:class_destroy(newchrled.class);
del_cdev:cdev_del(newchrled.cdev);
del_unregister:unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);
fail_map:led_unmap();return -EIO;}/** description : 驱动出口函数* param : 无* return : 无*/
static void __exit led_exit(void)
{/* 取消映射 */led_unmap();/* 注销字符设备驱动 */cdev_del(newchrled.cdev);/* 删除cdev */unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */device_destroy(newchrled.class, newchrled.devid);class_destroy(newchrled.class);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE(GPL);
MODULE_AUTHOR(ALIENTEK);
MODULE_INFO(intree, Y);设备应用
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include stdlib.h
#include string.h/* 用户数据 用于写入设备测试 */
static char usrdata[] {usr data!};int main(int argc, char *argv[])
{int fd, retvalue;char *filename;char readbuf[100], writebuf[100];if(argc ! 3){printf(Error Usage!\r\n);return -1;}filename argv[1];/* 打开驱动文件 */fd open(filename, O_RDWR);if(fd 0){printf(Cant open file %s\r\n, filename);return -1;}/* 从驱动文件读取数据测试 */if(atoi(argv[2]) 1){ retvalue read(fd, readbuf, 50);if(retvalue 0){printf(read file %s failed!\r\n, filename);}else{printf(read data:%s\r\n,readbuf);}}/* 向设备驱动写数据测试 */if(atoi(argv[2]) 2){memcpy(writebuf, usrdata, sizeof(usrdata));retvalue write(fd, writebuf, 50);if(retvalue 0){printf(write file %s failed!\r\n, filename);}}/* 关闭设备 */retvalue close(fd);if(retvalue 0){printf(Cant close file %s\r\n, filename);return -1;}return 0;
}编译
将以下内容写入MakeFile
KERNELDIR : /home/zuozhongkai/linux/atk-mp1/linux/my_linux/linux-5.4.31 #Linux内核路径
CURRENT_PATH : $(shell pwd)obj-m : newchrled.o #要跟设备驱动代码文件名称一致build: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) clean编译驱动
make -j16编译应用文件
arm-none-linux-gnueabihf-gcc xxx.c -o xxx #xxx为应用代码文件名将编译所得的库文件如led.ko和应用文件如ledApp放至arm的根目录下即可在开发板中使用。
代码运行 进入到上述文件所在的目录
depmod # 第一次加载模块时需运行
modprobe led # 加载驱动模块
./ledApp /dev/newchrled 1 # 执行应用代码pinctrl和gpio子系统
pinctrl
作用根据所提供的信息配置PIN功能
pinctrl: pin-controller50002000 {#address-cells 1;#size-cells 1;compatible st,stm32mp157-pinctrl;ranges 0 0x50002000 0xa400;interrupt-parent exti;st,syscfg exti 0x60 0xff;hwlocks hsem 0 1;pins-are-numbered;......
};pinctrl下需要有子节点子节点中有pins节点以配置特定的PIN属性
pinctrl {
......m_can1_pins_a: m-can1-0 {pins1 {pinmux STM32_PINMUX(H, 13, AF9); /* CAN1_TX */slew-rate 1;drive-push-pull;bias-disable;};pins2 {pinmux STM32_PINMUX(I, 9, AF9); /* CAN1_RX */bias-disable;};};
};pins属性
pinmux 属性
pinmux STM32_PINMUX(port, line, mode);
#例
pinmux STM32_PINMUX(H, 13, AF9);port:表示用那一组 GPIO例:H 表示为 GPIO 第 H 组,也就是 GPIOH line:表示这组 GPIO 的第几个引脚例:13 表示为 GPIOH_13,也就是 PH13 mode:表示当前引脚要做那种复用功能例:AF9 表示为用第 9 个复用功能 电气属性配置
gpio子系统
作用用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。
创建节点
led {compatible atk,led;gpio gpioi 0 GPIO_ACTIVE_LOW;status okay;
};API函数
int gpio_request(unsigned gpio, const char *label) // 申请GPIO管脚
void gpio_free(unsigned gpio) // 释放GPIO
int gpio_direction_input(unsigned gpio) // 设置GPIO为输入
int gpio_direction_output(unsigned gpio, int value) // 设置GPIO为输出
int __gpio_get_value(unsigned gpio) // 获取GPIO当前值
void __gpio_set_value(unsigned gpio, int value) // 设定GPIO当前值of_gpio_named_count用于获取设备树某个属性里面定义了几个 GPIO 信息
int of_gpio_named_count(struct device_node *np, const char *propname)of_gpio_count和 of_gpio_named_count 函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的 GPIO 信息
int of_gpio_count(struct device_node *np)of_get_named_gpio获取 GPIO 编号
int of_get_named_gpio(struct device_node *np,const char *propname,int index)