安丘做网站的公司社区服务流程的文献
安丘做网站的公司,社区服务流程的文献,蒙古语网站建设,国内大型电子网站建设个人名片#xff1a; #x1f981;作者简介#xff1a;一名喜欢分享和记录学习的在校大学生 #x1f42f;个人主页#xff1a;妄北y #x1f427;个人QQ#xff1a;2061314755 #x1f43b;个人邮箱#xff1a;2061314755qq.com #x1f989;个人WeChat#xff1a;V… 个人名片 作者简介一名喜欢分享和记录学习的在校大学生 个人主页妄北y 个人QQ2061314755 个人邮箱2061314755qq.com 个人WeChatVir2021GKBS 本文由妄北y原创首发CSDN 座右铭大多数人想要改造这个世界但却罕有人想改造自己。 专栏导航 妄北y系列专栏导航: C/C的基础算法C/C是一种常用的编程语言可以用于实现各种算法这里我们对一些基础算法进行了详细的介绍与分享。 QT基础入门学习对QT的基础图形化页面设计进行了一个简单的学习与认识利用QT的基础知识进行了翻金币小游戏的制作 Linux基础编程初步认识什么是Linux为什么学Linux安装环境进行基础命令的学习入门级的shell编程。 Linux应用开发基础开发分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等网络编程相关知识TCP/IP 协议、套接字Socket编程等可以实现网络通信功能。 Linux项目开发Linux基础知识的实践做项目是最锻炼能力的一个学习方法这里我们会学习到一些简单基础的项目开发与应用而且都是毕业设计级别的哦。 非常期待和您一起在这个小小的互联网世界里共同探索、学习和成长。 ✨✨ 欢迎订阅本专栏 ✨✨ 文章介绍 本篇文章对Linux驱动基础学习的相关知识进行分享 如果您觉得文章不错期待你的一键三连哦你的鼓励是我创作动力的源泉让我们一起加油一起奔跑让我们顶峰相见 感谢大家点赞收藏⭐评论✍️ 目录 目录 一、怎么写 LED 驱动程序 1.1 ioremap 函数的使用 1.1.1 函数原型 1.1.2 作用 1.1.3 不再使用该段虚拟地址时要 iounmap(virt_addr) 1.1.4为什么需要映射为虚拟地址 1.2 volatile 函数的使用 二、IMX6ULL 的 LED 驱动程序 2.1 led 原理图 2.2 所涉及的寄存器操作 2.3 代码操作硬件 编辑 2.3.1使能 GPIO5 2.3.2 设置 GPIO5_IO03 为 GPIO 模式 2.3.3 设置 GPIO5_IO03 为输出引脚设置其输出电平 三、写程序 3.1 led_opr.h 3.2 board_100ask_imx6ull.c 3.3 leddrv.c 3.4 ledtest.c 3.5 Makefile 四、上机测试 4.1编译 4.2 挂载到开发板 4.3 测试 4.4 实验效果 一、怎么写 LED 驱动程序 详细步骤如下 1看原理图确定引脚确定引脚输出什么电平才能点亮/熄灭 LED 2看主芯片手册确定寄存器操作方法哪些寄存器哪些位地址是 3编写驱动先写框架再写硬件操作的代码 注意在芯片手册中确定的寄存器地址被称为物理地址在 Linux 内核中无法直接使用。 需要使用内核提供的 ioremap 把物理地址映射为虚拟地址使用虚拟地址。
1.1 ioremap 函数的使用
1.1.1 函数原型
void __iomem *ioremap(resource_size_t res_cookie, size_t size)使用时要包含头文件
#include asm/io.h
1.1.2 作用 把物理地址 phys_addr 开始的一段空间(大小为 size)映射为虚拟地址返回值是该段虚拟地址的首地址。
virt_addr ioremap(phys_addr, size); 实际上它是按页(4096 字节)进行映射的是整页整页地映射的。 假设 phys_addr 0x10002size4ioremap 的内部实现是 1 phys_addr 按页取整得到地址 0x10000 2 size 按页取整得到 4096 3把起始地址 0x10000大小为 4096 的这一块物理地址空间映射到虚拟地址空间 假设得到的虚拟空间起始地址为 0xf0010000 4 那么 phys_addr 0x10002 对应的 virt_addr 0xf0010002
1.1.3 不再使用该段虚拟地址时要 iounmap(virt_addr)
void iounmap(volatile void __iomem *cookie)1.1.4为什么需要映射为虚拟地址 1.2 volatile 函数的使用
编译器很聪明会帮我们做些优化比如
int a;
a 0; // 这句话可以优化掉不影响 a 的结果
a 1;有时候编译器会自作聪明比如
int *p ioremap(xxxx, 4); // GPIO 寄存器的地址
*p 0; // 点灯但是这句话被优化掉了
*p 1; // 灭灯
对于上面的情况为了避免编译器自动优化需要加上 volatile告诉它 “这是容易出错的别乱优化”
volatile int *p ioremap(xxxx, 4); // GPIO 寄存器的地址
*p 0; // 点灯这句话不会被优化掉
*p 1; // 灭灯
二、IMX6ULL 的 LED 驱动程序
2.1 led 原理图 LED 原理图它使用 GPIO5_IO03引脚输出低电平时 LED 被点亮 输出高电平时 LED 被熄灭
2.2 所涉及的寄存器操作 2.3 代码操作硬件 2.3.1使能 GPIO5 设置 b[31:30]就可以使能 GPIO5 注意在 imx6ullrm.pdf 中CCM_CCGR1 的 b[31:30]是保留位 不去设置 b[31:30]GPIO5 也是默认使能的。
设置为 0b11 00该 GPIO 模块全程被关闭 01该 GPIO 模块在 CPU run mode 情况下是使能的在 WAIT 或 STOP 模式下关闭 10保留 11该 GPIO 模块全程使能
/* GPIO5_IO03 */
/* a. 使能 GPIO5
* set CCM to enable GPIO5
* CCM_CCGR1[CG15] 0x20C406C
* bit[31:30] 0b11
*/
2.3.2 设置 GPIO5_IO03 为 GPIO 模式
设置如下寄存器 /* b. 设置 GPIO5_IO03 用于 GPIO
* set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3
* to configure GPIO5_IO03 as GPIO
* IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 0x2290014
* bit[3:0] 0b0101 alt5
*/
2.3.3 设置 GPIO5_IO03 为输出引脚设置其输出电平
设置 GPIO5_IO03 为输出引脚寄存器地址为 设置方向寄存器方向寄存器把引脚设置为输出引脚 设置数据寄存器设置引脚的输出电平 /* c. 设置 GPIO5_IO03 作为 output 引脚
* set GPIO5_GDIR to configure GPIO5_IO03 as output
* GPIO5_GDIR 0x020AC000 0x4
* bit[3] 0b1
*/
/* d. 设置 GPIO5_DR 输出低电平
* set GPIO5_DR to configure GPIO5_IO03 output 0
* GPIO5_DR 0x020AC000 0
* bit[3] 0b0
*/
/* e. 设置 GPIO5_IO3 输出高电平
* set GPIO5_DR to configure GPIO5_IO03 output 1
* GPIO5_DR 0x020AC000 0
* bit[3] 0b1
*/ 三、写程序
3.1 led_opr.h
#ifndef _LED_OPR_H
#define _LED_OPR_Hstruct led_operations {int num;int (*init) (int which); /* 初始化LED, which-哪个LED */ int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};struct led_operations *get_board_led_opr(void);#endif
3.2 board_100ask_imx6ull.c
#include linux/module.h#include linux/fs.h
#include linux/errno.h
#include linux/miscdevice.h
#include linux/kernel.h
#include linux/major.h
#include linux/mutex.h
#include linux/proc_fs.h
#include linux/seq_file.h
#include linux/stat.h
#include linux/init.h
#include linux/device.h
#include linux/tty.h
#include linux/kmod.h
#include linux/gfp.h
#include asm/io.h#include led_opr.hstatic volatile unsigned int *CCM_CCGR1 ;
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_GDIR ;
static volatile unsigned int *GPIO5_DR ;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{unsigned int val;//printk(%s %s line %d, led %d\n, __FILE__, __FUNCTION__, __LINE__, which);if (which 0){if (!CCM_CCGR1){CCM_CCGR1 ioremap(0x20C406C, 4);IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 ioremap(0x2290014, 4);GPIO5_GDIR ioremap(0x020AC000 0x4, 4);GPIO5_DR ioremap(0x020AC000 0, 4);}/* GPIO5_IO03 *//* a. 使能GPIO5* set CCM to enable GPIO5* CCM_CCGR1[CG15] 0x20C406C* bit[31:30] 0b11*/*CCM_CCGR1 | (330);/* b. 设置GPIO5_IO03用于GPIO* set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3* to configure GPIO5_IO03 as GPIO* IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 0x2290014* bit[3:0] 0b0101 alt5*/val *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;val ~(0xf);val | (5);*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 val;/* b. 设置GPIO5_IO03作为output引脚* set GPIO5_GDIR to configure GPIO5_IO03 as output* GPIO5_GDIR 0x020AC000 0x4* bit[3] 0b1*/*GPIO5_GDIR | (13);}return 0;
}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{//printk(%s %s line %d, led %d, %s\n, __FILE__, __FUNCTION__, __LINE__, which, status ? on : off);if (which 0){if (status) /* on: output 0*/{/* d. 设置GPIO5_DR输出低电平* set GPIO5_DR to configure GPIO5_IO03 output 0* GPIO5_DR 0x020AC000 0* bit[3] 0b0*/*GPIO5_DR ~(13);}else /* off: output 1*/{/* e. 设置GPIO5_IO3输出高电平* set GPIO5_DR to configure GPIO5_IO03 output 1* GPIO5_DR 0x020AC000 0* bit[3] 0b1*/ *GPIO5_DR | (13);}}return 0;
}static struct led_operations board_demo_led_opr {.num 1,.init board_demo_led_init,.ctl board_demo_led_ctl,
};struct led_operations *get_board_led_opr(void)
{return board_demo_led_opr;
} 它首先构造了一个 led_operations 结构体用来表示 LED 的硬件操作 static struct led_operations board_demo_led_opr {.num 1,.init board_demo_led_init,.ctl board_demo_led_ctl,
}; led_operations 结构体中有init函数指针 它指向board_demo_led_init 函数在里面将会初始化 LED 引脚使能、设置为 GPIO 模式、设置为输出引脚。 第 3538 行对于寄存器要先使用 ioremap 得到它的虚拟地址以后使用虚拟地址访问寄存器 static volatile unsigned int *CCM_CCGR1 ;
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_GDIR ;
static volatile unsigned int *GPIO5_DR ;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{unsigned int val;//printk(%s %s line %d, led %d\n, __FILE__, __FUNCTION__, __LINE__, which);if (which 0){if (!CCM_CCGR1){CCM_CCGR1 ioremap(0x20C406C, 4);IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 ioremap(0x2290014, 4);GPIO5_GDIR ioremap(0x020AC000 0x4, 4);GPIO5_DR ioremap(0x020AC000 0, 4);}/* GPIO5_IO03 *//* a. 使能GPIO5* set CCM to enable GPIO5* CCM_CCGR1[CG15] 0x20C406C* bit[31:30] 0b11*/*CCM_CCGR1 | (330);/* b. 设置GPIO5_IO03用于GPIO* set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3* to configure GPIO5_IO03 as GPIO* IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 0x2290014* bit[3:0] 0b0101 alt5*/val *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;val ~(0xf);val | (5);*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 val;/* b. 设置GPIO5_IO03作为output引脚* set GPIO5_GDIR to configure GPIO5_IO03 as output* GPIO5_GDIR 0x020AC000 0x4* bit[3] 0b1*/*GPIO5_GDIR | (13);}return 0;
} led_operations 结构体中有 ctl 函数指针它指向 board_demo_led_ctl 函数在里面将会根据参数设置 LED 引脚的输出电平 static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{//printk(%s %s line %d, led %d, %s\n, __FILE__, __FUNCTION__, __LINE__, which, status ? on : off);if (which 0){if (status) /* on: output 0*/{/* d. 设置GPIO5_DR输出低电平* set GPIO5_DR to configure GPIO5_IO03 output 0* GPIO5_DR 0x020AC000 0* bit[3] 0b0*/*GPIO5_DR ~(13);}else /* off: output 1*/{/* e. 设置GPIO5_IO3输出高电平* set GPIO5_DR to configure GPIO5_IO03 output 1* GPIO5_DR 0x020AC000 0* bit[3] 0b1*/ *GPIO5_DR | (13);}}return 0;
}下面的 get_board_led_opr 函数供上层调用 给上层提供led_operations 结构体 struct led_operations *get_board_led_opr(void)
{return board_demo_led_opr;
} 3.3 leddrv.c
#include linux/module.h#include linux/fs.h
#include linux/errno.h
#include linux/miscdevice.h
#include linux/kernel.h
#include linux/major.h
#include linux/mutex.h
#include linux/proc_fs.h
#include linux/seq_file.h
#include linux/stat.h
#include linux/init.h
#include linux/device.h
#include linux/tty.h
#include linux/kmod.h
#include linux/gfp.h#include led_opr.h/* 1. 确定主设备号 */
static int major 0;
static struct class *led_class;
struct led_operations *p_led_opr;#define MIN(a, b) (a b ? a : b)/* 3. 实现对应的open/read/write等函数填入file_operations结构体 */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);return 0;
}/* write(fd, val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;char status;struct inode *inode file_inode(file);int minor iminor(inode);printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);err copy_from_user(status, buf, 1);/* 根据次设备号和status控制LED */p_led_opr-ctl(minor, status);return 1;
}static int led_drv_open (struct inode *node, struct file *file)
{int minor iminor(node);printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);/* 根据次设备号初始化LED */p_led_opr-init(minor);return 0;
}static int led_drv_close (struct inode *node, struct file *file)
{printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 2. 定义自己的file_operations结构体 */
static struct file_operations led_drv {.owner THIS_MODULE,.open led_drv_open,.read led_drv_read,.write led_drv_write,.release led_drv_close,
};/* 4. 把file_operations结构体告诉内核注册驱动程序 */
/* 5. 谁来注册驱动程序啊得有一个入口函数安装驱动程序时就会去调用这个入口函数 */
static int __init led_init(void)
{int err;int i;printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);major register_chrdev(0, 100ask_led, led_drv); /* /dev/led */led_class class_create(THIS_MODULE, 100ask_led_class);err PTR_ERR(led_class);if (IS_ERR(led_class)) {printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, led);return -1;}p_led_opr get_board_led_opr();for (i 0; i p_led_opr-num; i)device_create(led_class, NULL, MKDEV(major, i), NULL, 100ask_led%d, i); /* /dev/100ask_led0,1,... */return 0;
}/* 6. 有入口函数就应该有出口函数卸载驱动程序时就会去调用这个出口函数 */
static void __exit led_exit(void)
{int i;printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);for (i 0; i p_led_opr-num; i)device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, 100ask_led);
}/* 7. 其他完善提供设备信息自动创建设备节点 */module_init(led_init);
module_exit(led_exit);MODULE_LICENSE(GPL); 第98~101行获取灯的个数 p_led_opr get_board_led_opr(); //获得单板提供的结构体for (i 0; i p_led_opr-num; i) //获取灯的个数device_create(led_class, NULL, MKDEV(major, i), NULL, 100ask_led%d, i); /* /dev/100ask_led0,1,... */3.4 ledtest.c #include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include stdio.h
#include string.h/** ./ledtest /dev/100ask_led0 on* ./ledtest /dev/100ask_led0 off*/
int main(int argc, char **argv)
{int fd;char status;/* 1. 判断参数 */if (argc ! 3) {printf(Usage: %s dev on | off\n, argv[0]);return -1;}/* 2. 打开文件 */fd open(argv[1], O_RDWR);if (fd -1){printf(can not open file %s\n, argv[1]);return -1;}/* 3. 写文件 */if (0 strcmp(argv[2], on)){status 1;write(fd, status, 1);}else{status 0;write(fd, status, 1);}close(fd);return 0;
}
3.5 Makefile # 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCHarm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILEaarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册KERN_DIR /home/book/100ask_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) Mpwd modules $(CROSS_COMPILE)gcc -o ledtest ledtest.c clean:make -C $(KERN_DIR) Mpwd modules cleanrm -rf modules.orderrm -f ledtest# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y : a.o b.o
# obj-m ab.o# leddrv.c board_100ask_imx6ull.c 编译成 100ask_led.ko
100ask_led-y : leddrv.o board_100ask_imx6ull.o
obj-m 100ask_led.o内核路径 四、上机测试
4.1编译
编译程序把代码上传代服务器后执行 make 命令。 cp 100ask_led.ko ledtest.c ~/nfs_rootfs/4.2 挂载到开发板
在开发板上挂载 NFS 4.3 测试
注意如果要使用板子自带的系统需要关闭原有 LED 驱动也是进 入开发板/sys/class/leds/目录对于每一个 LED 在该目录下都有一个子目录 假设某个子目录名为 XXX则执行如下命令
[root100ask:~]# echo none /sys/class/leds/XXX/trigger 使用我们的系统时要先禁止内核中原来的 LED 驱动把“heatbeat”功能关闭执行以下命令即可
[root100ask:~]# echo none /sys/class/leds/cpu/trigger
这样就可以使用我们的驱动程序做操作了
[root100ask:~]#insmod 100ask_led.ko
[root100ask:~]#/ledtest /dev/100ask_led0 on
[root100ask:~]#/ledtest /dev/100ask_led0 off
如果想恢复原来的心跳功能可以执行
[root100ask:~]#echo heartbeat /sys/class/leds/cpu/trigger
[root100ask:/mnt]# ls /dev/100ask_led0* -l //查询驱动
crw------- 1 root root 240, 0 Jan 1 00:08 /dev/100ask_led0 //查询结果
[root100ask:/mnt]# ./ledtest /dev/100ask_led0 on //打开灯
[root100ask:/mnt]# ./ledtest /dev/100ask_led0 off //关闭灯4.4 实验效果 点亮的是红灯旁边的黄色灯照片可能看的效果不是很明显 执行/mnt/ledtest /dev/myled on 这里有一个黄色小灯会亮起来 执行/mnt/ledtest /dev/myled off 这里有一个黄色小灯会熄灭 大佬觉得有用的话点个赞 呗。 ❤️❤️❤️本人水平有限如有纰漏欢迎各位大佬评论批评指正 如果觉得这篇文对你有帮助的话也请给个点赞、收藏下吧非常感谢! 任务在无形中完成价值在无形中升华,让我们一起加油吧
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/89745.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!