Linux platform tree下的单总线驱动程序设计(DHT11)

news2024/11/25 6:27:36

目录

概述

1 认识DHT11

1.1 DHT11特性

1.2 DHT11数据格式

1.3 DHT11与MCU通信

1.4 DHT11信号解析

1.4.1 起始信号

1.4.2 解析信号0

1.4.3 解析信号1

2 驱动开发

2.1 硬件接口

2.2 更新设备树

2.2.1 添加驱动节点

2.2.2 编译.dts

2.2.3 更新板卡中的.dtb

2.3 驱动程序实现

2.3.1 编写驱动程序

 2.3.2 编写Makefile 

3 测试程序

3.1 编写测试程序

3.2 编写Makefile

4 编译和运行

4.1 编译和安装驱动程序

4.2 编译和运行测试程序

5 波形分析

5.1 起始信号波形

5.2 信息bit = 0波形

5.3 信息bit = 1波形


概述

        本文介绍platform tree下,如何设计一个单总线设备的驱动,根据datasheeet提供的波形图,使用代码来实现该驱动程序。然后用逻辑分析仪捕捉信号波形,分析其是否和datasheet中的波形一致。

1 认识DHT11

1.1 DHT11特性

        DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。 它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。

由上表可得:

温度范围: 0~ 50℃, 低于或者高于这个范围的温度不能测量

湿度范围:20~90%RH

1.2 DHT11数据格式

       DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,读一次数据总共包括8bytes( 40 bit )具体格式如下:

Byte-0: 8bit 湿度 整数 数据

Byte-1: 8bit 湿度 小数 数据

Byte-2: 8bit 温度 整数 数据

Byte-3: 8bit 温度 小数 数据

byte-4: 8bit校验和( Byte-0 + Byte-1 + Byte-2 + Byte-3)

1.3 DHT11与MCU通信

Step-1: Master 发送起始信号------->dth11, 信号变化规律为 1 - > 0 -> 1

Step-2: dht11发出响应信号,信号特征为 0 ->1

Step-3:dht11发送数据bit位,总共40个bit

1.4 DHT11信号解析

1.4.1 起始信号

Step-1: Master 发出触发信号:1 -> 0, 该信号至少持续18ms

step-2: Master电平0 ->1,该电平持续20~40us

Step-3: dht11发送响应信号0->1,该电平持续80us

step-4: dht11发送信号1,准备发送数据信息,该电平持续时间80us

1.4.2 解析信号0

信号0特征:

1)0 ->1持续 50us

2)1->0持续26~28us

1.4.3 解析信号1

信号1特征:

1)0 ->1持续 50us

2)1->0持续70us

2 驱动开发

2.1 硬件接口

DHT-11与MCU之间的连接图:

在板卡ATK-DL6Y2C上DTH-11的对应接口:

GPIO4_19:  DHT11-IO  

硬件实物图:

DHT11引脚说明:

2.2 更新设备树

2.2.1 添加驱动节点

DHT11引脚和IMX.6ULL引脚对应关系:

GPIO4_19:  DHT11-IO  

.dts文件路径:

/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c/arch/arm/boot/dts/imx6ull-14x14-evk.dts

 在.dts文件中添加如下代码:

  //mftang: user's dht11, 2024-2-14
	// IO: GPIO-4-PIN19
	mftangdht11 {
		compatible = "atk-dl6y2c,dht11";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpio_mftang_1_wire>;
		gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;
		status = "okay";
	};

其在imx6ull-14x14-evk.dts中位置:

2.2.2 编译.dts

编译.dts文件,并把编译生成的.dtb文件发送到NFS共享目录下,便于在板卡中操作该文件。

1)在内核根目录下使用如下命令编译.dts文件

make dtbs

2) 复制 .dtb 文件至NFS共享目录

cp arch/arm/boot/dts/imx6ull-14x14-emmc-4.3-480x272-c.dtb  /home/mftang/nfs/atk_dl6y2c/

2.2.3 更新板卡中的.dtb

复制.dtb文件到相应的运行目录,然后重新板卡

cp /mnt/atk_dl6y2c/imx6ull-14x14-emmc-4.3-480x272-c.dtb /run/media/mmcblk1p1

       reboot板卡后,内核会重新读取.dtb文件。然后在/proc/device-tree目录下查看板卡device tree,使用如下命令:

cd /proc/device-tree 
ls -l

 运行该命令后,在该目录下可以看见sensor信息,说明device已经加载到内核:

2.3 驱动程序实现

2.3.1 编写驱动程序

创建drv_dht11.c,并在该文件中编写驱动程序,驱动程序代码地址

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : drv_14_dht11.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : dht11 驱动程序, GPIO4_PIN19-----DHT11 IO port
其他       : 无
日志       : 初版V1.0 2024/1/30  

使用方法:
1) 在.dts文件中定义节点信息
    //mftang: user's dht11, 2024-2-14
    // IO: GPIO-4-PIN19
    mftangdht11 {
        compatible = "atk-dl6y2c,dht11";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_gpio_mftang_1_wire>;
        gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;
        status = "okay";
    };
    
2) 在驱动匹配列表 
static const struct of_device_id dht11_of_match[] = {
    { .compatible = "atk-dl6y2c,dht11" },
    { } // Sentinel
};
***************************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ktime.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 <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define DEVICE_NAME      "treedht11"     // dev/treedht11

/* dht11dev设备结构体 */
struct dht11stru_dev{
    dev_t   devid;                /* 设备号         */
    struct  cdev cdev;            /* cdev           */
    struct  class *class;         /* 类             */
    struct  device *device;       /* 设备           */
    int     major;                /* 主设备号       */
    struct  device_node *node;    /* dht11设备节点 */
    int     userdht11;            /* dht11 GPIO标号*/
    struct  gpio_desc *pin;
};

struct dht11stru_dev dht11dev;    /* dht11设备 */ 

int us_low_array[40];
int us_low_index;
int us_array[40];
int time_array[40];
int us_index;

/*
    dht11 driver 
*/
static void dht11_release( void )
{
    gpiod_direction_output(dht11dev.pin, 1);
}

static void dht11_start(void)
{
    gpiod_direction_output(dht11dev.pin, 1);
    mdelay(30);
    
    gpiod_set_value( dht11dev.pin, 0);
    mdelay(20);
    
    gpiod_set_value(dht11dev.pin, 1);
    udelay(40);
    
    gpiod_direction_input(dht11dev.pin);
}

static int dht11_wait_ack(void)
{
    int timeout_us = 20000;

    /* 等待低电平 */
    while (gpiod_get_value(dht11dev.pin) && --timeout_us)
    {
        udelay(1);
    }
    if (!timeout_us)
    {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }

    /* 现在是低电平 */
    /* 等待高电平 */
    timeout_us = 200;
    
    while (!gpiod_get_value(dht11dev.pin) && --timeout_us)
    {
        udelay(1);
    }
    
    if (!timeout_us)
    {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }

    /* 现在是高电平 */
    /* 等待低电平 */
    timeout_us = 200;
    while (gpiod_get_value(dht11dev.pin) && --timeout_us)
    {
        udelay(1);
    }
    
    if (!timeout_us)
    {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }
    
    return 0;
}



static int dht11_read_byte( unsigned char *datalist )
{
    int i;
    int us = 0;
    unsigned char data = 0;
    int timeout_us = 200;
    u64 pre, last;
    
    for (i = 0; i < 8; i++)
    {
        /* 现在是低电平 */
        /* 等待高电平 */
        timeout_us = 400;
        us = 0;
        while (!gpiod_get_value(dht11dev.pin) && --timeout_us)
        {
            udelay(1);
            us++;
        }
        
        if (!timeout_us)
        {
            printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
            return -1;
        }
        us_low_array[us_low_index++] = us;

        /* 现在是高电平 */
        /* 等待低电平,累加高电平的时间 */
        timeout_us = 20000000;
        us = 0;

        /* set another gpio low  */
        pre = ktime_get_boot_ns();
        while (1) 
        {
            last = ktime_get_boot_ns();
            if (last - pre >= 40000)
                break;
        }

        if (gpiod_get_value(dht11dev.pin))
        {
            /* get bit 1 */
            data = (data << 1) | 1;
            /* 当前位的高电平未结束, 等待 */
            timeout_us = 400;
            us = 0;
            while (gpiod_get_value(dht11dev.pin) && --timeout_us)
            {
                udelay(1);
                us++;
            }
            if (!timeout_us)
            {
                printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
                return -1;
            }
        }
        else
        {
            /* get bit 0 */
            data = (data << 1) | 0;
        }
    }

    *datalist = data;
    return 0;
}


static int dht11_get_value( unsigned char *data )
{
    unsigned long flags;
    int i;

    local_irq_save(flags);  // 关中断
    us_index = 0;
    us_low_index = 0;

    /* 1. 发送高脉冲启动DHT11 */
    dht11_start();
    
    /* 2. 等待DHT11就绪 */
    if (dht11_wait_ack())
    {
        local_irq_restore(flags); // 恢复中断
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -EAGAIN;
    }
    
    /* 3. 读5字节数据 */
    for (i = 0; i < 5; i++)
    {
        if (dht11_read_byte(&data[i]))
        {
            local_irq_restore(flags); // 恢复中断
            printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
            return -EAGAIN;
        }
    }
    
    /* 4. 释放总线 */
    dht11_release();
    local_irq_restore(flags); // 恢复中断
    
    /* 5. 根据校验码验证数据 */
    if (data[4] != (data[0] + data[1] + data[2] + data[3]))
    {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }
    
    return 0;
}


/*
    linux driver 驱动接口: 
    实现对应的open/read/write等函数,填入file_operations结构体
*/
static ssize_t dht11_drv_read ( struct file *file, char __user *buf, 
                                size_t size, loff_t *offset)
{
    unsigned char data[4];
    int err;
    
    if( !dht11_get_value( data ) ){
        printk(" %s line %d \r\n",  __FUNCTION__, __LINE__);
        err = copy_to_user(buf, data, 4);
        return 4;
    }

    return -1;
}

static int dht11_drv_close(struct inode *node, struct file *file)
{
    printk(" %s line %d \r\n",  __FUNCTION__, __LINE__);

    return 0;
}

static int dht11_drv_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &dht11dev; /* 设置私有数据  */
    return 0;
}

/* 
    定义driver的file_operations结构体
*/
static struct file_operations dht11_fops = {
    .owner   = THIS_MODULE,
    .read    = dht11_drv_read,
    .open    = dht11_drv_open,

    .release = dht11_drv_close,
};


/* 1. 从platform_device获得GPIO
 * 2. gpio=>irq
 * 3. request_irq
 */
static int dht11_probe(struct platform_device *pdev)
{
    printk("dht11 driver and device was matched!\r\n");
    
    /* 1. 获得硬件信息 */
    dht11dev.pin = gpiod_get(&pdev->dev, NULL, 0);
    if (IS_ERR(dht11dev.pin))
    {
        printk("%s line %d get pin parameter error! \n", __FUNCTION__, __LINE__);
    }
    
    /* 2. device_create */
    device_create( dht11dev.class, NULL, 
                   MKDEV( dht11dev.major, 0 ), NULL, 
                   DEVICE_NAME);        // device name 
    
    return 0;
}

static int dht11_remove(struct platform_device *pdev)
{
    printk("%s line %d\n", __FUNCTION__, __LINE__);
    
    device_destroy( dht11dev.class, MKDEV( dht11dev.major, 0));
    gpiod_put(dht11dev.pin);
    
    return 0;
}


static const struct of_device_id atk_dl6y2c_dht11[] = {
    { .compatible = "atk-dl6y2c,dht11" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver dht11_driver = {
    .probe      = dht11_probe,
    .remove     = dht11_remove,
    .driver     = {
        .name   = "atk_dht11",
        .of_match_table = atk_dl6y2c_dht11,
    },
};

/* 
  2. 在入口函数注册platform_driver 
*/
static int __init dht11_init(void)
{
    int err;
    
    printk("%s line %d\n",__FUNCTION__, __LINE__);
     
    /* register file_operations  */
    dht11dev.major = register_chrdev( 0, 
                                    DEVICE_NAME,     /* device name */
                                    &dht11_fops);  

    /* create the device class  */
    dht11dev.class = class_create(THIS_MODULE, "dht11_class");
    
    if (IS_ERR(dht11dev.class)) {
        printk("%s line %d\n", __FUNCTION__, __LINE__);
        unregister_chrdev( dht11dev.major, DEVICE_NAME);
        return PTR_ERR( dht11dev.class );
    }
    
    err = platform_driver_register(&dht11_driver); 
    
    return err;
}

/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 *    卸载platform_driver
 */
static void __exit dht11_exit(void)
{
    printk("%s line %d\n", __FUNCTION__, __LINE__);

    platform_driver_unregister(&dht11_driver);
    class_destroy(dht11dev.class);
    unregister_chrdev(dht11dev.major, DEVICE_NAME);
}

/*
 4. 驱动入口和出口函数
*/
module_init(dht11_init);
module_exit(dht11_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("tangmingfei2013@126.com");

 2.3.2 编写Makefile 

在驱动程序同级目录中创建Makefile,然后编写如下代码

PWD := $(shell pwd)

KERNEL_DIR=/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c
ARCH=arm
CROSS_COMPILE=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-

export  ARCH  CROSS_COMPILE

obj-m:= drv_14_dht11.o

all:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules


clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *.symvers

3 测试程序

3.1 编写测试程序

编写一个测试程序,目的是验证驱动程序是否能正常工作

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : test_14_dht11.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : 测试dth11驱动程序
其他       : 无
日志       : 初版V1.0 2024/02/15

***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>

#define DEV_FILE                              "/dev/treedht11"


int main(void)
{
    int fd;
    int count_run = 0;
    unsigned char data[4];

    fd = open(DEV_FILE, 0);
    if (fd == -1){
        printf("can not open file: %s \n", DEV_FILE);
        return -1;
    }

    while( count_run < 10000)
    {
        count_run++;
        if (read(fd, data, 4) == 4) {
            printf("get humidity  : %d.%d\n", data[0], data[1]);
            printf("get temprature: %d.%d\n", data[2], data[3]);
        } 
        else {
           perror("read dht11 device fail!\n");
        }
        sleep(1);
    }

    close(fd);

    return 0;
}

3.2 编写Makefile

CFLAGS= -Wall -O2
CC=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
STRIP=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip


test_14_dht11: test_14_dht11.o
	$(CC) $(CFLAGS) -o test_14_dht11 test_14_dht11.o
	$(STRIP) -s test_14_dht11

clean:
	rm -f test_14_dht11 test_14_dht11.o

4 编译和运行

4.1 编译和安装驱动程序

1) 编译驱动程序,并将其copy到NFS的共享目录中,方便在板卡中安装该程序

2) 在板卡中安装该驱动程序 , 使用命令

insmod dev_14_dth11.ko

安装成功后,使用命令查看驱动

ls /dev -l

4.2 编译和运行测试程序

1) 编译测试程序,并将其copy到NFS的共享目录中,方便在板卡中运行该程序

2)在板卡中运行测试程序

5 波形分析

在板卡上运行测试程序,然后使用逻辑分析仪捕捉DHT11-IO上的波形,分析其信号特征,以更好的理解驱动程序。

5.1 起始信号波形

datasheet 上提供的波形

逻辑分析仪上捕捉的波形:

查看电平持续时间:

5.2 信息bit = 0波形

datasheet 上提供的波形

逻辑分析仪上捕捉的波形:

5.3 信息bit = 1波形

datasheet 上提供的波形

逻辑分析仪上捕捉的波形:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【开源】SpringBoot框架开发企业项目合同信息系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合同签订模块2.4 合同预警模块2.5 数据可视化模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 合同审批表3.2.2 合同签订表3.2.3 合同预警表 四、系统展示五、核心代码5.1 查询合同…

蓝桥杯嵌入式学习记录——按键的使用

目录 一、按键原理简介 二、cubeMX的配置 三、按键的短按代码 四、按键的长按代码 一、按键原理简介 在STM32中&#xff0c;按键连接通常使用GPIO&#xff08;通用输入/输出&#xff09;端口来实现。当按键未被按下时&#xff0c;GPIO端口处于高电平状态&#xff08;即1&am…

【Go语言】第一个Go程序

第一个 Go 程序 1 安装 Go Go语言官网&#xff1a;Download and install - The Go Programming Language&#xff0c;提供了安装包以及引导流程。 以 Windows 为例&#xff0c;进入windows安装包下载地址&#xff1a;All releases - The Go Programming Language&#xff0c…

【Midjourney】解密Midjourney付费订阅:畅享全新体验!(详细流程与各版本一览)

一、Midjourney 付费订阅流程 1、在首页点击Purchase plan 2、进入到midjourney年月选择页面 3、这里续费一个最便宜的版本 , 按年付费 8 , 按月 10 4、输入银行卡信息 , 用的WildCard虚拟信用卡 &#xff0c;打开 5、填写完银行卡信息就订阅成功 二、Midjourney 各版本介绍…

洛谷_P1923 【深基9.例4】求第 k 小的数_python写法

哪位大佬可以出一下这个的题解&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;话说蓝桥杯可以用numpy库吗&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; 这道题有一个很简单的思路就是排序完成之后再访问。 but有很大的问题&…

SAP PP学习笔记- 豆知识01 - 怎么查询既存品目

SAP系统当中已经有哪些品目要怎么查询呢&#xff1f; 1&#xff0c;MM60 品目一览 这里可以输入Plant&#xff0c;然后可以查询该工厂的所有品目。 2&#xff0c;SE16 > MARA MARA 品目一般データ&#xff0c;存放的是品目基本信息。 要查询该品目属于哪个Plant&#xff…

如何在30天内使用python制作一个卡牌游戏

如何在30天内使用python制作一个卡牌游戏 第1-5天&#xff1a;规划和设计第6-10天&#xff1a;搭建游戏框架第11-20天&#xff1a;核心游戏机制开发第21-25天&#xff1a;游戏界面和用户体验第26-30天&#xff1a;测试和发布附加建议游戏类型游戏规则设计界面设计技术选型第6-…

下一代Windows系统曝光:基于GPT-4V,Agent跨应用调度,代号UFO

下一代Windows操作系统提前曝光了&#xff1f;&#xff1f; 微软首个为Windows而设的智能体&#xff08;Agent&#xff09; 亮相&#xff1a; 基于GPT-4V&#xff0c;一句话就可以在多个应用中无缝切换&#xff0c;完成复杂任务。整个过程无需人为干预&#xff0c;其执行成功…

MySQL 基础知识(四)之表操作

目录 1 约束 2 查看已有表 3 创建表 4 查看表结构 5 修改表 6 删除表 1 约束 主键约束 primary key&#xff1a;唯一&#xff0c;标识表中的一行数据&#xff0c;此列的值不可重复&#xff0c;且不能为 NULL&#xff0c;此外&#xff0c;可以多个列组成主键唯一约束 uniq…

新机Word/PowerPoint新建空白文档后闪退问题

首先可以尝试一下常规的修复&#xff1a; 设置-应用-安装的应用-搜索office-点击Micros Office Home and Student...右侧三个点-选择修改-点击是-快速修复-修复 再不行就按上面的选择联机修复&#xff0c;这个会卸载现有Office然后自动帮你重新下载 我做了以上两个都没有解决问…

java基础实现的图书管理系统

文章目录 项目介绍项目功能代码讲解如何实现不同用户之间的操作权限不同 项目介绍 该项目是用的是javase的一些知识包括了类和对象封装&#xff0c;继承多态等面向对象的三大特性。主要是为了让我们能够更好的使用之前学到的知识。 接下来给大家讲解一下这个项目的一个特点。首…

FL Studio 21.2.3.4004 All Plugins Edition Win/Mac音乐软件

FL Studio 21.2.3.4004 All Plugins Edition 是一款功能强大的音乐制作软件&#xff0c;提供了丰富的音频处理工具和插件&#xff0c;适用于专业音乐制作人和爱好者。该软件具有直观的用户界面&#xff0c;支持多轨道录音、混音和编辑&#xff0c;以及各种音频效果和虚拟乐器。…

blender在几何节点中的这些变换中的旋转,其实可以是两种旋转顺序

看似xyz的旋转角度&#xff0c;但如果按照欧拉角来谈它的旋转&#xff0c;就大有学问了。 我们知道&#xff0c;在blender中有局部旋转和全局旋转。但其实这两者在某种情况下可以等价。 那就是&#xff0c;如果参照全局坐标系&#xff0c;按xyz的顺序进行欧拉旋转&#xff0c;…

【Linux】并发解决(上)-中断屏蔽,原子操作

&#x1f525;博客主页&#xff1a;PannLZ &#x1f38b;系列专栏&#xff1a;《Linux系统之路》 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 文章目录 并发解决1.中断屏蔽2.原子操作2.1整形原子操作2.2位原子操作原子变量使用例子 并发解决…

【Linux学习】线程池

目录 23.线程池 23.1 什么是线程池 23.2 为什么需要线程池 23.3 线程池的应用场景 23.4 实现一个简单的线程池 23.4.1 RAII风格信号锁 23.4.2 线程的封装 23.4.3 日志打印 22.4.4 定义队列中存放Task类任务 23.4.5 线程池的实现(懒汉模式) 为什么线程池中需要有互斥锁和条件变…

无监督学习:探索数据的潜在结构与规律

目录 前言1. 概念2. 聚类2.1 定义2.2 应用场景 3. 异常检测3.1 定义3.2 应用场景 结语 前言 在当今信息时代&#xff0c;数据扮演着至关重要的角色&#xff0c;其海量、多样的形式为我们提供了前所未有的机会和挑战。在这个大数据的背景下&#xff0c;无监督学习的概念日益引起…

《剑指offer》--字符串左旋【超详细建议收藏】

字符串左旋的三种方法 1. 一个一个字符挪2. 库函数---strcpy和strncat3. 三段逆置法 1. 一个一个字符挪 代码实现如下&#xff1a; #include <stdio.h> #include <string.h>void Left_Reverse(char* str,int k) {int len strlen(str);//6int time 0;time k % …

开源≠不赚钱,开源软件盈利的7大模式。

开源不是目的&#xff0c;目的是圈用户&#xff0c;留住用户&#xff0c;盈利自然不成问题。 开源系统可以通过多种方式赚钱&#xff0c;以下是其中几种常见的方式&#xff1a; 提供付费支持&#xff1a; 开源系统可以提供付费的技术支持服务&#xff0c;包括安装、配置、维…

代码随想录 Leetcode406. 根据身高重建队列

题目&#xff1a; 代码(首刷看解析 2024年2月15日&#xff09;&#xff1a; class Solution { static bool cmp(const vector<int>& A, const vector<int>& B) {if(A[0] B[0]) return A[1] < B[1];return A[0] > B[0]; } public:vector<vector&…

2001-2022年368个地级市平均气温数据

2001-2022年368个地级市平均气温数据 1、时间:2001-2022年 2、范围&#xff1a;368个地级市 3、来源&#xff1a;基于NOAA下属NCEI提供的原始数据编制而成的。 4、指标&#xff1a;年份、省份、省份代码、城市、城市代码、平均气温 5、指标解释&#xff1a;平均气温指某一…