Linux 驱动入门(6)—— IRDA(红外遥控模块)驱动

文章目录

  • 一、编译替换内核和设备树
  • 二、IRDA(红外遥控模块)
    • 1. 红外遥控简介
    • 2. 红外遥控器协议
    • 3. 编程思路
  • 三、驱动代码
    • 1. GPIO 实现
      • 1.1 驱动层代码
      • 1.2 应用层代码
    • 2. 设备树实现
      • 2.1 修改设备树
      • 2.2 驱动层代码
      • 2.3 应用层代码
    • 3. 上机测试

一、编译替换内核和设备树

在编译驱动程序之前要先编译内核,原因有三点:

  • 驱动程序要用到内核文件
  • 编译驱动时用的内核、开发板上运行到内核,要一致
  • 更换板子上的内核后,板子上的其他驱动也要更换

编译内核步骤看我之前写过的文章:

  • 编译替换内核_设备树_驱动_IMX6ULL

二、IRDA(红外遥控模块)

1. 红外遥控简介

红外遥控被广泛应用于家用电器、工业控制和智能仪器系统中,像我们熟知的有电视机盒子遥控器、空调遥控器。红外遥控器系统分为发送端和接收端,如图下图所示:
在这里插入图片描述
发送端就是红外遥控器,上面有许多按键,当我们按下遥控器按键时,遥控器内部电路会进行编码和调制,再通过红外发射头,将信号以肉眼不可见的红外线发射出去。红外线线虽然肉眼不可见,但可以通过手机摄像头看到,常用该方法检查遥控器是否正常工作。
接收端是一个红外接收头,收到红外信号后,内部电路会进行信号放大和解调,再将数据传给板子上的 GPIO,板子收到数据后再解码才能确定是哪个按键被按下。

2. 红外遥控器协议

我们按下遥控器按键的时候,遥控器自动发送某个红外信号,接收头接收到红外信号,然后把红外信号转换成电平信号,通过IRD这根线,传给SOC。整个传输,只涉及单向传输,由HS0038向主芯片传送。
因此,我们只需要编写程序,从IRD上获取数据即可,在这之前,我们需要先了解下数据是怎么表示的,也就是传输的红外数据的格式。
红外协议有:NEC、SONY、RC5、RC6等,常用的就是NEC格式,因此我们主要对NEC进行讲解。 在分析文章中的波形之前,我们先想象一下怎么在一条数据线上传输信号。 开始传输数据之前,一般都会发出一个 start 起始信号,通知对方我开始传输数据了,后面就是每一位每一位的数据。
NEC 协议的开始是一段引导码:
在这里插入图片描述
这个引导码由一个9ms的低脉冲加上一个4.5ms的高脉冲组成,它用来通知接收方我要开始传输数据了。
在这里插入图片描述
然后接着的是数据,数据由4字节组成:地址、地址(取反)、数据、数据(取反),取反是用来校验用的。
地址是指遥控器的ID,每一类遥控器的ID都不一样,这样就可以防止操控
电视的遥控器影响空调。数据就是遥控器上的不同按键值。
从前面的图可以知道,NEC每次要发32位(地址、地址取反、数据、数据取反,每个8位)的数据。数据的1和0,开始都是0.56ms的低脉冲,对于数据1,后面的高脉冲比较长,对于数据0,后面的高脉冲比较短。
在这里插入图片描述
第一次按下按键时,它会发出引导码,地址,地址取反,数据,数据取反。
如果这时还没松开按键,这就是“长按”,怎么表示“长按”?遥控器会发送一个不一样的引导码,这个引导码由9ms 的低脉冲,2.25ms 的高脉冲组成,表示现在按的还是上次一样的按键,也叫连发码,它会一直发送,直到松开。
在这里插入图片描述

3. 编程思路

知道红外遥控器协议后就可以开始编写程序了。
编程思路如下:

  • 平时GPIO为高;
  • 发现GPIO为低时,判断它有9ms的低电平;
    对于引导码,或连发码,它们都有9ms的低电平,如下图:
    在这里插入图片描述
  • 分辨是引导码,还是连发码:
    在 9ms 的低电平之后,判断高电平持续时间,引导码的高电平维持时间是4.5ms,连发码的高电平维持时间是2.25ms。
    发现是连发码时,直接结束译码。
    发现是引导码时,还得继续接收32位数据。
  • 接收数据:
    关键在于如何得到一位数据,看看下图:
    在这里插入图片描述
    先等待低电平结束,一直等到出现高电平;然后延时800us,读取GPIO值:这就是该位的数据值。

三、驱动代码

实现步骤:

  • 1.记录中断发生的时刻;
  • 2.累计中断次数;
  • 3.次数达标后, 删除定时器, 解析数据, 放入buffer, 唤醒APP;

1. GPIO 实现

1.1 驱动层代码

irda_drv.c

#include "asm-generic/errno-base.h"
#include "linux/jiffies.h"
#include <linux/module.h>
#include <linux/poll.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 <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>struct gpio_desc{int gpio;int irq;char *name;int key;struct timer_list key_timer;
} ;static struct gpio_desc gpios[] = {{115, 0, "irda", },
};/* 主设备号       */
static int major = 0;
static struct class *gpio_class;/* 环形缓冲区 */
#define BUF_LEN 128
static unsigned char g_keys[BUF_LEN];
static int r, w;struct fasync_struct *button_fasync;static u64 g_irda_irq_times[68];
static int g_irda_irq_cnt = 0;#define NEXT_POS(x) ((x+1) % BUF_LEN)static int is_key_buf_empty(void)
{return (r == w);
}static int is_key_buf_full(void)
{return (r == NEXT_POS(w));
}static void put_key(unsigned char key)
{if (!is_key_buf_full()){g_keys[w] = key;w = NEXT_POS(w);}
}static unsigned char get_key(void)
{unsigned char key = 0;if (!is_key_buf_empty()){key = g_keys[r];r = NEXT_POS(r);}return key;
}static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);// static void key_timer_expire(struct timer_list *t)
static void key_timer_expire(unsigned long data)
{/* 超时 */g_irda_irq_cnt = 0;put_key(-1);put_key(-1);wake_up_interruptible(&gpio_wait);
}/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t irda_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{unsigned char kern_buf[2] ;int err;if (size != 2)return -EINVAL;if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_wait, !is_key_buf_empty());kern_buf[0] = get_key();  /* device */kern_buf[1] = get_key();  /* data   */if (kern_buf[0] == (unsigned char)-1  && kern_buf[1] == (unsigned char)-1)return -EIO;err = copy_to_user(buf, kern_buf, 2);return 2;
}/* 定义自己的file_operations结构体            */
static struct file_operations irda_fops = {.owner	 = THIS_MODULE,.read    = irda_read,
};static void parse_irda_datas(void)
{u64 time;int i;int m, n;unsigned char datas[4];unsigned char data = 0;int bits = 0;int byte = 0;/* 1. 判断前导码 : 9ms的低脉冲, 4.5ms高脉冲  */time = g_irda_irq_times[1] - g_irda_irq_times[0];if (time < 8000000 || time > 10000000){goto err;}time = g_irda_irq_times[2] - g_irda_irq_times[1];if (time < 3500000 || time > 55000000){goto err;}/* 2. 解析数据 */for (i = 0; i < 32; i++){m = 3 + i*2;n = m+1;time = g_irda_irq_times[n] - g_irda_irq_times[m];data <<= 1;bits++;if (time > 1000000){/* 得到了数据1 */data |= 1;}if (bits == 8){datas[byte] = data;byte++;data = 0;bits = 0;}}/* 判断数据正误 */datas[1] = ~datas[1];datas[3] = ~datas[3];if ((datas[0] != datas[1]) || (datas[2] != datas[3])){printk("data verify err: %02x %02x %02x %02x\n", datas[0], datas[1], datas[2], datas[3]);goto err;}put_key(datas[0]);put_key(datas[2]);wake_up_interruptible(&gpio_wait);return;err:g_irda_irq_cnt = 0;put_key(-1);put_key(-1);wake_up_interruptible(&gpio_wait);
}static int get_irda_repeat_datas(void)
{u64 time;/* 1. 判断重复码 : 9ms的低脉冲, 2.25ms高脉冲  */time = g_irda_irq_times[1] - g_irda_irq_times[0];if (time < 8000000 || time > 10000000){return -1;}time = g_irda_irq_times[2] - g_irda_irq_times[1];if (time < 2000000 || time > 2500000){return -1;}	return 0;
}static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_desc *gpio_desc = dev_id;u64 time;/* 1. 记录中断发生的时刻 */	time = ktime_get_ns();g_irda_irq_times[g_irda_irq_cnt] = time;/* 2. 累计中断次数 */g_irda_irq_cnt++;/* 3. 次数达标后, 删除定时器, 解析数据, 放入buffer, 唤醒APP */if (g_irda_irq_cnt == 4){/* 是否重复码 */if (0 == get_irda_repeat_datas()){/* device: 0, val: 0, 表示重复码 */put_key(0);put_key(0);wake_up_interruptible(&gpio_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);del_timer(&gpio_desc->key_timer);g_irda_irq_cnt = 0;return IRQ_HANDLED;}}if (g_irda_irq_cnt == 68){parse_irda_datas();del_timer(&gpio_desc->key_timer);g_irda_irq_cnt = 0;return IRQ_HANDLED;}/* 4. 启动定时器 */mod_timer(&gpio_desc->key_timer, jiffies + msecs_to_jiffies(100));return IRQ_HANDLED;
}/* 在入口函数 */
static int __init irda_init(void)
{int err;int i;int count = sizeof(gpios)/sizeof(gpios[0]);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);for (i = 0; i < count; i++){		gpios[i].irq  = gpio_to_irq(gpios[i].gpio);setup_timer(&gpios[i].key_timer, key_timer_expire, (unsigned long)&gpios[i]);//timer_setup(&gpios[i].key_timer, key_timer_expire, 0);err = request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[i].name, &gpios[i]);}/* 注册file_operations 	*/major = register_chrdev(0, "zgl_irda", &irda_fops);  /* /dev/gpio_desc */gpio_class = class_create(THIS_MODULE, "zgl_irda_class");if (IS_ERR(gpio_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "zgl_irda");return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "irda"); /* /dev/irda */return err;
}/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*/
static void __exit irda_exit(void)
{int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, "zgl_irda");for (i = 0; i < count; i++){free_irq(gpios[i].irq, &gpios[i]);del_timer(&gpios[i].key_timer);}
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(irda_init);
module_exit(irda_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("zgl <919426896@qq.com>");

1.2 应用层代码

irda_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>static int fd;/** ./irda_test /dev/irda**/
int main(int argc, char **argv)
{unsigned char buf[2];/* 1. 判断参数 */if (argc != 2) {printf("Usage: %s <dev>\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;}while (1){if (read(fd, buf, 2) == 2)printf("get irda: deivce 0x%02x, data 0x%02x\n", buf[0], buf[1]);elseprintf("get irda: -1\n");}close(fd);return 0;
}

2. 设备树实现

2.1 修改设备树

在这里插入图片描述

2.2 驱动层代码

irda_drv.c

#include "asm-generic/errno-base.h"
#include "linux/jiffies.h"
#include <linux/module.h>
#include <linux/poll.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 <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>struct gpio_desc{int gpio;int irq;char name[128];int key;struct timer_list key_timer;
} ;static int count;
static struct gpio_desc *gpios;/* 主设备号       */
static int major = 0;
static struct class *gpio_class;/* 环形缓冲区 */
#define BUF_LEN 128
static unsigned char g_keys[BUF_LEN];
static int r, w;struct fasync_struct *button_fasync;static u64 g_irda_irq_times[68];
static int g_irda_irq_cnt = 0;#define NEXT_POS(x) ((x+1) % BUF_LEN)static int is_key_buf_empty(void)
{return (r == w);
}static int is_key_buf_full(void)
{return (r == NEXT_POS(w));
}static void put_key(unsigned char key)
{if (!is_key_buf_full()){g_keys[w] = key;w = NEXT_POS(w);}
}static unsigned char get_key(void)
{unsigned char key = 0;if (!is_key_buf_empty()){key = g_keys[r];r = NEXT_POS(r);}return key;
}static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);// static void key_timer_expire(struct timer_list *t)
static void key_timer_expire(unsigned long data)
{/* 超时 */g_irda_irq_cnt = 0;put_key(-1);put_key(-1);wake_up_interruptible(&gpio_wait);
}/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t irda_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{unsigned char kern_buf[2] ;int err;if (size != 2)return -EINVAL;if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_wait, !is_key_buf_empty());kern_buf[0] = get_key();  /* device */kern_buf[1] = get_key();  /* data   */if (kern_buf[0] == (unsigned char)-1  && kern_buf[1] == (unsigned char)-1)return -EIO;err = copy_to_user(buf, kern_buf, 2);return 2;
}/* 定义自己的file_operations结构体            */
static struct file_operations irda_fops = {.owner	 = THIS_MODULE,.read    = irda_read,
};static void parse_irda_datas(void)
{u64 time;int i;int m, n;unsigned char datas[4];unsigned char data = 0;int bits = 0;int byte = 0;/* 1. 判断前导码 : 9ms的低脉冲, 4.5ms高脉冲  */time = g_irda_irq_times[1] - g_irda_irq_times[0];if (time < 8000000 || time > 10000000){goto err;}time = g_irda_irq_times[2] - g_irda_irq_times[1];if (time < 3500000 || time > 55000000){goto err;}/* 2. 解析数据 */for (i = 0; i < 32; i++){m = 3 + i*2;n = m+1;time = g_irda_irq_times[n] - g_irda_irq_times[m];data <<= 1;bits++;if (time > 1000000){/* 得到了数据1 */data |= 1;}if (bits == 8){datas[byte] = data;byte++;data = 0;bits = 0;}}/* 判断数据正误 */datas[1] = ~datas[1];datas[3] = ~datas[3];if ((datas[0] != datas[1]) || (datas[2] != datas[3])){printk("data verify err: %02x %02x %02x %02x\n", datas[0], datas[1], datas[2], datas[3]);goto err;}put_key(datas[0]);put_key(datas[2]);wake_up_interruptible(&gpio_wait);return;err:g_irda_irq_cnt = 0;put_key(-1);put_key(-1);wake_up_interruptible(&gpio_wait);
}static int get_irda_repeat_datas(void)
{u64 time;/* 1. 判断重复码 : 9ms的低脉冲, 2.25ms高脉冲  */time = g_irda_irq_times[1] - g_irda_irq_times[0];if (time < 8000000 || time > 10000000){return -1;}time = g_irda_irq_times[2] - g_irda_irq_times[1];if (time < 2000000 || time > 2500000){return -1;}	return 0;
}static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_desc *gpio_desc = dev_id;u64 time;/* 1. 记录中断发生的时刻 */	time = ktime_get_ns();g_irda_irq_times[g_irda_irq_cnt] = time;/* 2. 累计中断次数 */g_irda_irq_cnt++;/* 3. 次数达标后, 删除定时器, 解析数据, 放入buffer, 唤醒APP */if (g_irda_irq_cnt == 4){/* 是否重复码 */if (0 == get_irda_repeat_datas()){/* device: 0, val: 0, 表示重复码 */put_key(0);put_key(0);wake_up_interruptible(&gpio_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);del_timer(&gpio_desc->key_timer);g_irda_irq_cnt = 0;return IRQ_HANDLED;}}if (g_irda_irq_cnt == 68){parse_irda_datas();del_timer(&gpio_desc->key_timer);g_irda_irq_cnt = 0;return IRQ_HANDLED;}/* 4. 启动定时器 */mod_timer(&gpio_desc->key_timer, jiffies + msecs_to_jiffies(100));return IRQ_HANDLED;
}/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{int err = 0;int i;struct device_node *np = pdev->dev.of_node;struct resource *res;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 从platfrom_device获得引脚信息 * 1. pdev来自c文件* 2. pdev来自设备树*/if (np){/* pdev来自设备树 : 示例reg_usb_ltemodule: regulator@1 {compatible = "100ask,gpiodemo";gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>, <&gpio5 3 GPIO_ACTIVE_HIGH>;};*/count = of_gpio_count(np);if (!count)return -EINVAL;gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);for (i = 0; i < count; i++){gpios[i].gpio = of_get_gpio(np, i);sprintf(gpios[i].name, "%s_pin_%d", np->name, i);}}else{/* pdev来自c文件 static struct resource omap16xx_gpio3_resources[] = {{.start  = 115,.end    = 115,.flags  = IORESOURCE_IRQ,},{.start  = 118,.end    = 118,.flags  = IORESOURCE_IRQ,},		};		*/count = 0;while (1){res = platform_get_resource(pdev, IORESOURCE_IRQ, count);if (res){count++;}else{break;}}if (!count)return -EINVAL;gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);for (i = 0; i < count; i++){res = platform_get_resource(pdev, IORESOURCE_IRQ, i);gpios[i].gpio = res->start;sprintf(gpios[i].name, "%s_pin_%d", pdev->name, i);}}printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);for (i = 0; i < count; i++){		gpios[i].irq  = gpio_to_irq(gpios[i].gpio);setup_timer(&gpios[i].key_timer, key_timer_expire, (unsigned long)&gpios[i]);//timer_setup(&gpios[i].key_timer, key_timer_expire, 0);err = request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[i].name, &gpios[i]);}/* 注册file_operations 	*/major = register_chrdev(0, "zgl_irda", &irda_fops);  /* /dev/gpio_desc */gpio_class = class_create(THIS_MODULE, "zgl_irda_class");if (IS_ERR(gpio_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "zgl_irda");return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "irda"); /* /dev/irda */return err;}/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*/
static int gpio_drv_remove(struct platform_device *pdev)
{int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, "zgl_irda");for (i = 0; i < count; i++){free_irq(gpios[i].irq, &gpios[i]);del_timer(&gpios[i].key_timer);}return 0;
}static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "zgl,irda", },{ /* sentinel */ }
};static struct platform_driver gpio_platform_driver = {.driver		= {.name	= "zgl_irda_plat_drv",.of_match_table = gpio_dt_ids,},.probe		= gpio_drv_probe,.remove		= gpio_drv_remove,
};static int __init irda_drv_init(void)
{/* 注册platform_driver */return platform_driver_register(&gpio_platform_driver);
}static void __exit irda_drv_exit(void)
{/* 反注册platform_driver */platform_driver_unregister(&gpio_platform_driver);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(irda_drv_init);
module_exit(irda_drv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("zgl <919426896@qq.com>");

2.3 应用层代码

irda_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>static int fd;/** ./irda_test /dev/irda**/
int main(int argc, char **argv)
{unsigned char buf[2];/* 1. 判断参数 */if (argc != 2) {printf("Usage: %s <dev>\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;}while (1){if (read(fd, buf, 2) == 2)printf("get irda: deivce 0x%02x, data 0x%02x\n", buf[0], buf[1]);elseprintf("get irda: -1\n");}close(fd);return 0;
}

3. 上机测试

开发板上电,装载驱动,运行程序测试:在这里插入图片描述

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

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

相关文章

QSNCTF-WEB做题记录(2)

[第一章 web入门]常见的搜集 来自 <天狩CTF竞赛平台> 1&#xff0c;首先就是对网站进行目录枚举爆破 dirsearch -u http://challenge.qsnctf.com:31616 -x 404,403 得到如下的目录&#xff0c;分别查看一下内容 /.DS_Store /inde…

「软件设计模式」责任链模式(Chain of Responsibility)

深入解析责任链模式&#xff1a;用C打造灵活的请求处理链 引言&#xff1a;当审批流程遇上设计模式 在软件系统中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个请求需要经过多个处理节点的判断&#xff0c;每个节点都有权决定是否处理或传递请求。就像企业的请假审批…

Ocelot 请求聚合

请求聚合 当下游服务是返回404状态码&#xff0c;在返回结果中&#xff0c;其对应的值则为空值&#xff0c; 即使聚合路由中所有的下游服务都返回404状态码&#xff0c;聚合路由的返回结果也不会是404状态码。 Ocelot允许你声明聚合路由&#xff0c;这样你可以把多个正常的Ro…

MongoDB安装与配置 导入导出

1、MongoDB的安装 首先cd到目录 cd /usr/local/ 执行下载 wget -c https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-7.0.7.tgz 解压文件 tar -xvf mongodb-linux-x86_64-rhel80-7.0.7.tgz 将解压后的“mongodb-linux-x86_64-rhel80-7.0.7”文件夹重命名…

Kotlin 知识点二 延迟初始化和密封类

对变量延迟初始化 Kotlin 语言的许多特性&#xff0c;包括变量不可变&#xff0c;变量不可为空&#xff0c;等等。这些特性 都是为了尽可能地保证程序安全而设计的&#xff0c;但是有些时候这些特性也会在编码时给我们带来不 少的麻烦。 比如&#xff0c;如果你的类中存在很多…

简单介绍 SSL 证书类型: DV、OV、EV 的区别

SSL证书类型DV、OV、EV 区别&#xff1a; DV(域名验证型)SSL证书 OV(组织验证型)SSL证书 EV(扩展验证型)SSL证书

深度解析SmartGBD助力Android音视频数据接入GB28181平台

在当今数字化时代&#xff0c;视频监控与音视频通信技术在各行各业的应用愈发广泛。GB28181协议作为中国国家标准&#xff0c;为视频监控设备的互联互通提供了规范&#xff0c;但在实际应用中&#xff0c;许多Android终端设备并不具备国标音视频能力&#xff0c;这限制了其在相…

1分钟用DeepSeek编写一个PDF转Word软件

一、引言 如今&#xff0c;在线工具的普及让PDF转Word成为了一个常见需求&#xff0c;常见的pdf转word工具有收费的wps&#xff0c;免费的有pdfgear&#xff0c;见下文&#xff1a; PDFgear:一款免费的PDF编辑、格式转化软件-CSDN博客 还有网上在线的免费pdf转word工具smallp…

PyCharm Professional 2025 安装配置全流程指南(Windows平台)

一、软件定位与核心功能 PyCharm 2025 是 JetBrains 推出的智能 Python IDE&#xff0c;新增深度学习框架自动补全、实时性能热力图等功能1。相较于社区版&#xff0c;专业版支持&#xff1a; Web开发&#xff08;Django/Flask&#xff09;数据库工具&#xff08;PostgreSQL/…

DeepSeek回答:AI时代Go语言学习路线

最近有小伙伴经常会问&#xff1a;**该如何学习入门Go语言&#xff1f;怎样提升Go语言Coding水平&#xff1f;**这篇文章我们就使用DeepSeek来梳理下Go语言在AI时代的学习路线。 向DeepSeek提问的问题原文&#xff1a; 你现在是一名资深的Go语言工程师&#xff0c;精通Go语言并…

OpenGL ES -> GLSurfaceView绘制点、线、三角形、正方形、圆(顶点法绘制)

XML文件 <?xml version"1.0" encoding"utf-8"?> <com.example.myapplication.MyGLSurfaceViewxmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"…

嵌入式项目:STM32刷卡指纹智能门禁系统

本文详细介绍基于STM32的刷卡指纹智能门禁系统。 获取资料/指导答疑/技术交流/选题/帮助&#xff0c;请点链接&#xff1a; https://gitee.com/zengzhaorong/share_contact/blob/master/stm32.txt 1 系统功能 1.1 功能概述 本系统由STM32硬件端&#xff08;下位机&#xff09;…

Dubbo RPC 原理

一、Dubbo 简介 Apache Dubbo 是一款高性能、轻量级的开源 RPC 框架&#xff0c;支持服务治理、协议扩展、负载均衡、容错机制等核心功能&#xff0c;广泛应用于微服务架构。其核心目标是解决分布式服务之间的高效通信与服务治理问题。 二、Dubbo 架构设计 1. 核心组件 Prov…

RBAC授权

4 RBAC授权 4.1 什么是RBAC 在Kubernetes中&#xff0c;所有资源对象都是通过API进行操作&#xff0c;他们保存在etcd里。而对etcd的操作我们需要通过访问kube-apiserver来实现&#xff0c;上面的Service Account其实就是APIServer的认证过程&#xff0c;而授权的机制是通过RBA…

C/C++ | 每日一练 (4)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 C/C | 每日一练 (4)题目参考答案基础容器序列容器std:…

HarmonyOS 5.0应用开发——鸿蒙接入高德地图实现POI搜索

【高心星出品】 文章目录 鸿蒙接入高德地图实现POI搜索运行结果&#xff1a;准备地图编写ArkUI布局来加载HTML地图 鸿蒙接入高德地图实现POI搜索 在当今数字化时代&#xff0c;地图应用已成为移动设备中不可或缺的一部分。随着鸿蒙系统的日益普及&#xff0c;如何在鸿蒙应用中…

Linux系统:服务器常见服务默认IP端口合集

服务器的默认IP端口取决于所使用的协议和服务类型。以下是一些常见服务和协议的默认端口&#xff1a; 服务端口实例&#xff1a; HTTP服务 默认端口&#xff1a;80 说明&#xff1a;用于普通的HTTP网页访问。例如&#xff0c;访问 http://example.com 时&#xff0c;默认使用8…

一周学会Flask3 Python Web开发-flask3上下文全局变量session,g和current_app

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili flask3提供了session,g和current_app上下文全局变量来方便我们操作访问数据。 以下是一个表格&#xff0c;用于比较Flask中的…

学习路程四 向量数据库Milvus安装与连接

前序 在之前&#xff0c;已经简单完成了文档的加载&#xff0c;分割&#xff0c;向量化这些步骤&#xff0c;最后得到了结果。但是这些数据都是一次性的。假设一个律师所&#xff0c;有几千上万份卷宗&#xff0c;不可能每次使用都重新向量化数据吧。 所以我们需要有一个地方存…

Docker 搭建 Nginx 服务器

系列文章目录 Docker 搭建 Nginx 服务器 系列文章目录前言一、准备工作二、设置 Nginx 容器的目录结构三、启动一个临时的 Nginx 容器来复制配置文件四、复制 Nginx 配置文件到本地目录五、删除临时 Nginx 容器六、创建并运行 Nginx 容器&#xff0c;挂载本地目录七、修改 ngin…