Linux 驱动开发基础知识—— 具体单板的 LED 驱动程序(五)

 个人名片:

🦁作者简介:一名喜欢分享和记录学习的在校大学生
🐯个人主页:妄北y

🐧个人QQ:2061314755

🐻个人邮箱:2061314755@qq.com
🦉个人WeChat:Vir2021GKBS
🐼本文由妄北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 = 0x10002,size=4,ioremap 的内部实现是: 

        (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_H

struct 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.h"

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 |= (3<<30);
        
        /* 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 |= (1<<3);
    }
    
    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 &= ~(1<<3);
        }
        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 |= (1<<3);
        }
    
    }
    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 模式、设置为输出引脚

        第 35~38 行,对于寄存器要先使用 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 |= (3<<30);
        
        /* 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 |= (1<<3);
    }
    
    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 &= ~(1<<3);
        }
        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 |= (1<<3);
        }
    
    }
    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 ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-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.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o ledtest ledtest.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -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,则执行如下命令:

[root@100ask:~]# echo none > /sys/class/leds/XXX/trigger

        使用我们的系统时,要先禁止内核中原来的 LED 驱动,把“heatbeat”功能关闭,执行以下命令即可:

[root@100ask:~]# echo none > /sys/class/leds/cpu/trigger

这样就可以使用我们的驱动程序做操作了: 

[root@100ask:~]#insmod 100ask_led.ko
[root@100ask:~]#/ledtest /dev/100ask_led0 on
[root@100ask:~]#/ledtest /dev/100ask_led0 off

如果想恢复原来的心跳功能,可以执行: 

[root@100ask:~]#echo heartbeat > /sys/class/leds/cpu/trigger
[root@100ask:/mnt]# ls /dev/100ask_led0* -l                    //查询驱动
crw------- 1 root root 240, 0 Jan  1 00:08 /dev/100ask_led0    //查询结果
[root@100ask:/mnt]# ./ledtest /dev/100ask_led0 on              //打开灯
[root@100ask:/mnt]# ./ledtest /dev/100ask_led0 off             //关闭灯

 4.4 实验效果

 点亮的是红灯旁边的黄色灯,照片可能看的效果不是很明显

        执行/mnt/ledtest /dev/myled on 这里有一个黄色小灯会亮起来

        执行/mnt/ledtest /dev/myled off 这里有一个黄色小灯会熄灭

大佬觉得有用的话点个赞 👍🏻 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥任务在无形中完成,价值在无形中升华,让我们一起加油吧!🌙🌙🌙

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥小白赛4 乘飞机 抽屉原理 枚举

&#x1f468;‍&#x1f3eb; 乘飞机 &#x1f437; 抽屉原理 import java.util.Scanner;public class Main {static int N 100010;static int[] a new int[N];public static void main(String[] args){Scanner sc new Scanner(System.in);int n sc.nextInt();int q s…

大数据安全 | 期末复习(下)

文章目录 &#x1f4da;安全策略和攻击&#x1f34b;&#x1f407;安全协议&#x1f407;IPsee&#x1f407;SSL&#x1f407;SSH&#x1f407;S/MIME协议&#x1f407;公钥基础设施PKI&#x1f407;PGP&#x1f407;HTTPS&#x1f407;防火墙&#x1f407;防毒墙&#x1f407;…

Django学习之小试牛刀

六、Django学习之小试牛刀 其他关于Python Web开发笔记&#xff1a;&#xff08;如果遇到问题可以一起交流~&#xff09; 一、Flask学习之HTML-CSDN博客 二、Flask学习之CSS-CSDN博客 【接上篇】二、Flask学习之CSS&#xff08;下篇&#xff09;-CSDN博客 三、Flask学习之B…

支付宝开通GPT4.0,最新经验分享

ChatGPT是由OpenAI开发的一种生成式对话模型&#xff0c;具有生成对话响应的能力。它是以GPT&#xff08;Generative Pre-trained Transformer&#xff09;为基础进行训练的&#xff0c;GPT是一种基于Transformer架构的预训练语言模型&#xff0c;被广泛用于各种自然语言处理任…

MYSQL库和表的操作(修改字符集和校验规则,备份和恢复数据库及库和表的增删改查)

文章目录 一、MSYQL库的操作1.连接MYSQL2.查看当前数据库3.创建数据库4.字符集和校验规则5.修改数据库6.删除数据库7.备份和恢复8.查看连接 二、表的操作1.创建表2.查看表结构3.修改表4.删除表 一、MSYQL库的操作 1.连接MYSQL 我们使用下面的语句来连接MSYQL&#xff1a; my…

Android发展历程及安装

目录 发展历程 下载网址 安装过程 发展历程 安卓基于Linux内核&#xff0c;Linux内核相当于房屋的地基 开源不等于免费&#xff0c;不能商用 安卓一般每半年小更新&#xff0c;一年大更新 对应API相当于别名 现在安卓安全性越来越高&#xff0c;性能越来越快&#xff0c…

基于Javaweb开发的二手图书零售系统详细设计【附源码】

基于Javaweb开发的二手图书零售系统详细设计【附源码】 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制系统…

Netty源码一:服务端启动

示例 public class Server {public static void main(String[] args) throws InterruptedException {// todo 创建两个 eventGroup boss 接受客户端的连接, 底层就是一个死循环, 不断的监听事件 处理事件// new NioEventLoopGroup(1); todo 入参1 表示设置boss设置为1个线程,…

Linux系统——点菜名

Linux系统可以点菜啦&#xff01; [rootlocalhost ~]#vim menu1.sh #!/bin/bash sum0 PS3"请输入(1-6):" MENU" 宫保鸡丁 酸菜鱼 鱼香肉丝 佛跳墙 水煮肉片 点菜结束 "select menu in $MENU do case $REPLY in 1) echo $menu 价格是20 let sum20 ;; 2) ec…

一个基于electron自动化桌面应用-流程图构建

前期工作已搞定&#xff0c;现在可以搭建桌面应用了。这个阶段可以结合前面定义好的数据格式构建流程图。 模板 还是使用熟悉的技术栈vite react electron&#xff0c;模板 流程图 官方文档 自定义 节点样式 因为配置化的操作类型较多&#xff0c;因此可以利用自定义节…

数据结构和算法笔记5:堆和优先队列

今天来讲一下堆&#xff0c;在网上看到一个很好的文章&#xff0c;不过它实现堆是用Golang写的&#xff0c;我这里打算用C实现一下&#xff1a; Golang: Heap data structure 1. 基本概念 满二叉树&#xff08;二叉树每层节点都是满的&#xff09;&#xff1a; 完全二叉树&a…

JAVA_Set系列集合:HashSet、LinkedHashSet、TreeSet底层详解

先看看 Set 系列集合的位置&#xff1a; Set 系列集合的特点&#xff1a; 无序&#xff1a;存取顺序不一致 如存入张三、李四、王五。而遍历获取到的是李四, 张三, 王五 不重复&#xff1a;可以去除重复无索引&#xff1a;没有带索引的方法&#xff0c;所以不能使用普通for循…

Redis缓存设计与性能优化

文章目录 多级缓存架构缓存设计缓存穿透缓存失效(击穿)缓存雪崩热点缓存key重建优化缓存与数据库双写不一致 开发规范与性能优化一、键值设计1. key名设计2. value设计bigkey的危害&#xff1a;bigkey的产生&#xff1a;如何优化bigkey 二、命令使用三、客户端使用Redis对于过期…

SpringBoot系列之MybatisPlus实现分组查询

SpringBoot系列之MybatisPlus实现分组查询 我之前博主曾记写过一篇介绍SpringBoot2.0项目怎么集成MybatisPlus的教程&#xff0c;不过之前的博客只是介绍了怎么集成&#xff0c;并没有做详细的描述各种业务场景&#xff0c;本篇博客是对之前博客的补充&#xff0c;介绍在mybat…

GitHub 一周热点汇总第7期(2024/01/21-01/27)

GitHub一周热点汇总第7期 (2024/01/21-01/27) &#xff0c;梳理每周热门的GitHub项目&#xff0c;离春节越来越近了&#xff0c;不知道大家都买好回家的票没有&#xff0c;希望大家都能顺利买到票&#xff0c;一起来看看这周的项目吧。 #1 rustdesk 项目名称&#xff1a;rust…

3个精美的wordpress律师网站模板

暗红色WordPress律师事务所网站模板 演示 https://www.zhanyes.com/qiye/23.html 暗橙色WordPress律师网站模板 演示 https://www.zhanyes.com/qiye/18.html 红色WordPress律所网站模板 演示 https://www.zhanyes.com/qiye/22.html

最新国内GPT4.0使用教程,AI绘画-Midjourney绘画V6 ALPHA绘画模型,GPT语音对话使用,DALL-E3文生图+思维导图一站式解决方案

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

STM32实现软件IIC协议操作OLED显示屏(2)

时间记录&#xff1a;2024/1/27 一、OLED相关介绍 &#xff08;1&#xff09;显示分辨率128*64点阵 &#xff08;2&#xff09;IIC作为从机的地址0x78 &#xff08;3&#xff09;操作步骤&#xff1a;主机先发送IIC起始信号S&#xff0c;然后发送OLED的地址0x78&#xff0c;然…

Unity 光照

光照烘培 光照模式切换为 Baked 或 Mixed&#xff0c;Baked 模式完全使用光照贴图模拟光照&#xff0c;运行时修改光照颜色不生效&#xff0c;Mixed 模式也使用光照贴图&#xff0c;并且进行一些实时运算&#xff0c;运行时修改光照颜色会生效 受光照影响的物体勾选 Contribute…

【RH850U2A芯片】Reset Vector和Interrupt Vector介绍

目录 前言 正文 1. 什么是Reset Vector 1.1 S32K144芯片的Reset Vector 1.2 RH850芯片的Reset Vector 2. 什么是Interrupt Vector 2.1 S32K144芯片的Interrupt Vector 2.2 RH850芯片的Interrupt Vector 3. Reset Vector等价于Interrupt Vector吗 4. 总结 前言 最近在…