Linux第72步_使用“新字符设备的一般模板”编写LED驱动

使用“新字符设备的一般模板”编写LED驱动,使用寄存器直接开关灯。

1、创建LED目录

输入“cd /home/zgq/linux/Linux_Drivers/回车

切换到“/home/zgq/linux/Linux_Drivers/

输入“ls回车”,查看“/home/zgq/linux/Linux_Drivers/

输入“mkdir MyNewLED回车”,创建“MyNewLED”目录

输入“ls回车”,查看“/home/zgq/linux/Linux_Drivers/

2、LED.c文件如下:

#include "LED.h"

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/string.h>

#include <linux/errno.h>

#include <linux/gpio.h>

/* 映射后的寄存器虚拟地址指针 */

static void __iomem *MPU_AHB4_PERIPH_RCC_PI;

/*RCC_MP_AHB4ENSETR寄存器*/

static void __iomem *GPIOI_MODER_PI; /*GPIOx_MODER寄存器,x=A to K, Z*/

static void __iomem *GPIOI_OTYPER_PI;/*GPIOx_OTYPER,x=A to K,Z*/

static void __iomem *GPIOI_OSPEEDR_PI;/*GPIOx_OSPEEDR,x=A to K, Z*/

static void __iomem *GPIOI_PUPDR_PI; /*GPIOx_PUPDR,x=A to K, Z*/

static void __iomem *GPIOI_BSRR_PI;/*GPIOx_BSRR,x=A to K, Z*/

void led_ioremap(void);

void led_iounmap(void);

void led_Pin_Init(void);

void led_switch(u8 sta);

/* 寄存器地址映射 */

void led_ioremap(void)

{

   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);

}

/*取消“寄存器地址映射”*/

void led_iounmap(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);

}

void led_Pin_Init(void)

{

u32 val = 0;

/* 2、使能RCC时钟 */

val = readl(MPU_AHB4_PERIPH_RCC_PI);/* 读RCC_MP_AHB4ENSETR寄存器 */

val &= ~(0X1 << 8);/* 清除以前的bit8设置 */

val |= (0X1 << 8); /* 设置新的bit8值 */

writel(val, MPU_AHB4_PERIPH_RCC_PI);

/* 将val的值写入RCC_MP_AHB4ENSETR寄存器 */

/* 3、将PI0输出引脚。*/

val = readl(GPIOI_MODER_PI);/*读GPIOI_MODER寄存器*/

val &= ~(0X3 << 0); /* bit0:1清零 */

val |= (0X1 << 0); /* bit0:1设置01,配置为输出模式 */

writel(val, GPIOI_MODER_PI);

/* 将val的值写入GPIOI_MODER寄存器 */

/* 4、设置PI0为推挽模式 */

val = readl(GPIOI_OTYPER_PI);/*读GPIOI_OTYPER寄存器*/

val &= ~(0X1 << 0); /* bit0清零,设置为上拉*/

writel(val, GPIOI_OTYPER_PI);

/* 将val的值写入GPIOI_OTYPER寄存器 */

/* 5、设置PI0为极高速 */

val = readl(GPIOI_OSPEEDR_PI);/*读GPIOI_OSPEEDR寄存器*/

val &= ~(0X3 << 0); /* bit0:1 清零 */

val |= (0x3 << 0); /* bit0:1 设置为11,极高速*/

writel(val, GPIOI_OSPEEDR_PI);

/* 将val的值写入GPIOI_OSPEEDR寄存器 */

/* 6、设置PI0为上拉。*/

val = readl(GPIOI_PUPDR_PI);/*读GPIOI_PUPDR寄存器*/

val &= ~(0X3 << 0); /* bit0:1 清零*/

val |= (0x1 << 0); /*bit0:1 设置为01,配置为上拉*/

writel(val,GPIOI_PUPDR_PI);

/* 将val的值写入GPIOI_PUPDR寄存器 */

/* 6、默认打开LED,PI0=0 */

val = readl(GPIOI_BSRR_PI);/*读GPIOI_BSRR寄存器*/

val &= ~(0X1 << 16); /* bit16 清零*/

val |= (0x1 << 16); /*bit16 设置为1,令PI0输出低电平*/

writel(val, GPIOI_BSRR_PI);

/* 将val的值写入GPIOI_BSRR寄存器 */

/* 6、默认关闭LED,PI0=1 */

val = readl(GPIOI_BSRR_PI);/*读GPIOI_BSRR寄存器*/

val &= ~(0X1 << 0); /* bit0 清零*/

val |= (0x1 << 0);/*bit0 设置为1,令PI0输出高电平*/

writel(val, GPIOI_BSRR_PI);

/* 将val的值写入GPIOI_BSRR寄存器 */

}

void led_switch(u8 sta)

{

u32 val = 0;

if(sta == LEDON) {

val = readl(GPIOI_BSRR_PI);/*读GPIOI_BSRR寄存器*/

    val &= ~(0X1 << 16); /* bit16 清零*/

val |= (0x1 << 16); /*bit16 设置为1,令PI0输出低电平*/

writel(val, GPIOI_BSRR_PI);

/* 将val的值写入GPIOI_BSRR寄存器 */

}

else if(sta == LEDOFF) {

val = readl(GPIOI_BSRR_PI);/*读GPIOI_BSRR寄存器*/

val &= ~(0X1 << 0); /* bit0 清零*/

val |= (0x1 << 0);/*bit0 设置为1,令PI0输出高电平*/

writel(val, GPIOI_BSRR_PI);

/* 将val的值写入GPIOI_BSRR寄存器 */

}

}

3、LED.h文件如下:

#ifndef __LED_H

#define __LED_H

#include <linux/types.h>

/*

数据类型重命名

使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t

使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t

*/

#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)

extern void led_ioremap(void);

extern void led_iounmap(void);

extern void led_Pin_Init(void);

extern void led_switch(u8 sta);

#endif

4、LEDInterface.c文件如下:

#include "LED.h"

#include <linux/types.h>

//数据类型重命名

//使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t

//使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/string.h>

#include <linux/cdev.h> //使用字符设备结构

#include <linux/mdev.h>

#define MyNewLED_CNT    1   //定义设备数量为1

#define MyNewLED_NAME  "MyNewLEDName"//定义设备的名字

struct MyNewLED_dev{

  dev_t devid; /*声明32位变量devid用来给保存设备号 */

  int major; /* 主设备号 */

  int minor; /* 次设备号 */

  struct cdev  cdev; /*字符设备结构变量cdev */

  struct class *class; /* 类 */

struct device *device;/*设备*/

};

struct MyNewLED_dev strMyNewLED;

/* 打开设备 */

static int MyNewLED_open(struct inode *inode, struct file *filp)

{

  /* 用户实现具体功能 */

  printk("MyNewLED_open!\r\n");

  return 0;

}

/* 从设备读取数据,保存到首地址为buf的数据块中,长度为cnt个字节 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t MyNewLED_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)

{

  return 0;

}

/* 向设备写数据,将数据块首地址为buf的数据,长度为cnt个字节,发送给用户 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t MyNewLED_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

  int ret = 0;

  unsigned char databuf[1];

  unsigned char ledstat;

  ret = copy_from_user(databuf, buf, cnt);

  if(ret <0){

    printk("kernel write failed!\r\n");

    ret = -EFAULT;

  }

  ledstat = databuf[0];/*获取到应用传递进来的开关灯状态*/

  led_switch(ledstat);/*执行开灯或执行关灯*/

  return ret;

}

/* 关闭/释放设备 */

static int MyNewLED_release(struct inode *inode, struct file *filp)

{

  /* 用户实现具体功能 */

  printk("MyNewLED_release!\r\n");

  return 0;

}

/*声明file_operations结构变量MyCharDevice_fops*/

/*它是指向设备的操作函数集合变量*/

const struct file_operations MyNewLED_fops = {

  .owner = THIS_MODULE,

  .open = MyNewLED_open,

  .read = MyNewLED_read,

  .write = MyNewLED_write,

  .release = MyNewLED_release,

};

/*驱动入口函数 */

static int  __init MyNewLED_init(void)

{

  int ret;

/* 1、寄存器地址映射 */

  led_ioremap();//寄存器地址映射

  led_Pin_Init();//LED灯引脚初始化

  /*2、申请设备号*/

  strMyNewLED.major=0;

  if(strMyNewLED.major)/*如果指定了主设备号*/

  {

    strMyNewLED.devid = MKDEV(strMyNewLED.major, 0);

    //输入参数strMyNewLED.major为“主设备号”

    //输入参数0为“次设备号”,大部分驱动次设备号都选择0

//将strMyNewLED.major左移20位,再与0相或,就得到“Linux设备号”

    ret=register_chrdev_region(strMyNewLED.devid, MyNewLED_CNT, MyNewLED_NAME);

    //strMyNewLED.devid表示起始设备号

    //MyNewLED_CNT表示次设备号的数量

    //MyNewLED_NAME表示设备名

    if(ret < 0)

      goto fail_map;

  }

  else

  { /* 没有定义设备号 */

ret=alloc_chrdev_region(&strMyNewLED.devid, 0, MyNewLED_CNT,MyNewLED_NAME);

/* 申请设备号 */

    //strMyNewLED.devid:保存申请到的设备号

    //0:次设备号的起始地址

    //MyNewLED_CNT:要申请的次设备号数量;

    //MyNewLED_NAME:表示“设备名字”

    if(ret < 0)

      goto fail_map;

strMyNewLED.major = MAJOR(strMyNewLED.devid);

/* 获取分配号的主设备号 */

    //输入参数strMyNewLED.devid为“Linux设备号”

    //将strMyNewLED.devid右移20位得到“主设备号”

strMyNewLED.minor = MINOR(strMyNewLED.devid);

/* 获取分配号的次设备号 */

   //输入参数strMyNewLED.devid为“Linux设备号”

   //将strMyNewLED.devid与0xFFFFF相与后得到“次设备号”

  }

  /*3、注册字符设备*/

  strMyNewLED.cdev.owner = THIS_MODULE;

//使用THIS_MODULE将owner指针指向当前这个模块

  cdev_init(&strMyNewLED.cdev,&MyNewLED_fops);

  //注册字符设备,初始化“字符设备结构变量strMyNewLED.cdev”

  //strMyNewLED.cdev是等待初始化的结构体变量

  //MyNewLED_fops就是字符设备文件操作函数集合

/*4、添加字符设备*/      ret=cdev_add(&strMyNewLED.cdev,strMyNewLED.devid,MyNewLED_CNT);

//添加字符设备

/*&strMyNewLED.cdev表示指向要添加的字符设备,即字符设备结构strMyNewLED.cdev变量*/

//strMyNewLED.devid表示设备号

//MyNewLED_CNT表示需要添加的设备数量

  if(ret < 0 ) //添加字符设备失败

    goto del_register;

  printk("dev id major = %d,minor = %d\r\n", strMyNewLED.major, strMyNewLED.minor);

  printk("MyNewLED_init is ok!!!\r\n");

  /*5、自动创建设备节点 */

  strMyNewLED.class =class_create(THIS_MODULE, MyNewLED_NAME);

  if (IS_ERR(strMyNewLED.class)){

    goto del_cdev;

  }

  /*6、创建设备 */

  strMyNewLED.device = device_create(strMyNewLED.class, NULL, strMyNewLED.devid, NULL, MyNewLED_NAME);

  //创建设备

  //设备要创建在strMyNewLED.class类下面

  //NULL表示没有父设备

  //strMyNewLED.devid是设备号;

  //参数drvdata=NULL,设备没有使用数据

  //MyNewLED_NAME是设备名字

  //如果设置fmt=MyNewLED_NAME 的话,就会生成/dev/MyNewLED_NAME设备文件。

  //返回值就是创建好的设备。

  if (IS_ERR(strMyNewLED.device)){

    goto destroy_class;

  }

  return 0;

destroy_class:

  class_destroy(strMyNewLED.class);

  //删除类

  //strMyNewLED.class就是要删除的类

del_cdev:

   cdev_del(&strMyNewLED.cdev);

   //删除字符设备

   //&strMyNewLED.cdev表示指向需要删除的字符设备,即字符设备结构strMyNewLED.cdev变量

del_register:

  unregister_chrdev_region(strMyNewLED.devid, MyNewLED_CNT);

/* 释放设备号 */

//strMyNewLED.devid:需要释放的起始设备号

//MyNewLED_CNT:需要释放的次设备号数量;

fail_map://申请设备号失败

  /*若有释放的内存,则释放内存*/

  led_iounmap();

  return -EIO;

}

/*驱动出口函数 */

static void __exit MyNewLED_exit(void)

{

  /*1、释放内存*/

  led_iounmap();

  /*2、 释放设备号 */

  unregister_chrdev_region(strMyNewLED.devid, MyNewLED_CNT);

/*释放设备号 */

//strMyNewLED.devid:需要释放的起始设备号

//MyNewLED_CNT:需要释放的次设备号数量;

/*3、删除字符设备*/

  cdev_del(&strMyNewLED.cdev);

 /*删除字符设备*/

 /*&strMyNewLED.cdev表示指向需要删除的字符设备,即字符设备结构strMyNewLED.cdev变量*/

/*4、 删除设备 */

device_destroy(strMyNewLED.class, strMyNewLED.devid);

//删除创建的设备

//newchrled.class是要删除的设备所处的类

//newchrled.devid是要删除的设备号

/*5、删除类*/

class_destroy(strMyNewLED.class);

//删除类

//strMyNewLED.class就是要删除的类

}

module_init(MyNewLED_init);

//指定MyNewLED_init()为驱动入口函数

module_exit(MyNewLED_exit);

//指定MyNewLED_exit()为驱动出口函数

MODULE_AUTHOR("Zhanggong");//添加作者名字

MODULE_LICENSE("GPL");//LICENSE采用“GPL协议”

MODULE_INFO(intree,"Y");

//去除显示“loading out-of-tree module taints kernel.”

5、LED_APP.c文件如下:

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

//APP运行命令:./LED_APP filename <1>|<0>如果是1表示打开LED,如果是0表示关闭LED

#define LEDOFF 0 /* 关灯 */

#define LEDON 1 /* 开灯 */

/*

参数argc: argv[]数组元素个数

参数argv[]:是一个指针数组

返回值: 0 成功;其他 失败

*/

int main(int argc, char *argv[])

{

  int fd, retvalue;

  char *filename;

  unsigned char databuf[1];

  if(argc != 3)

  {

    printf("Error Usage!\r\n");

    return -1;

  }

  //argv[]是指向输入参数“./LED_App” “/dev/LMyNewLEDName” “1”

  filename = argv[1];

  //argv[1]指向字符串“/dev/MyNewLEDName”

  fd = open(filename, O_RDWR);

  //如果打开“/dev/MyNewLEDName”文件成功,则fd为“文件描述符”

  //fd=0表示关灯; fd=1表示开灯

  if(fd < 0)

  {

    printf("Can't open file %s\r\n", filename);

    return -1;

  }

  databuf[0]= atoi(argv[2]); /* 写入的数据,是数字的,表示打开或关闭 */

  retvalue = write(fd, databuf, 1);

  //将databuf[]中前1个字节发送给用户

  //返回值大于0表示写入的字节数;

  //返回值等于0表示没有写入任何数据;

  //返回值小于0表示写入失败

  if(retvalue < 0)

  {

    printf("write file %s failed!\r\n", filename);

    close(fd);

    //fd表示要关闭的“文件描述符”

    //返回值等于0表示关闭成功

    //返回值小于0表示关闭失败

    return -1;

  }

  /* 关闭设备 */

  retvalue = close(fd);

  //fd表示要关闭的“文件描述符”

  //返回值等于0表示关闭成功

  //返回值小于0表示关闭失败

  if(retvalue < 0)

  {

    printf("Can't close file %s\r\n", filename);

    return -1;

  }

  return 0;

}

6、Makefile文件如下:

KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31

#使用“:=”将其后面的字符串赋值给KERNELDIR

CURRENT_PATH := $(shell pwd)

#采用“shell pwd”获取当前打开的路径

#使用“$(变量名)”引用“变量的值”

MyAPP := LED_APP

MyNewLED_Module-objs = LEDInterface.o LED.o

obj-m := MyNewLED_Module.o

CC := arm-none-linux-gnueabihf-gcc

drv:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

app:

$(CC)  $(MyAPP).c  -o $(MyAPP)

clean:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

rm $(MyAPP)

install:

sudo cp *.ko $(MyAPP) /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f

7、添加“c_cpp_properties.json

按下“Ctrl+Shift+P”,打开VSCode控制台,然后输入“C/C++:Edit Configurations(JSON)”,打开以后会自动在“.vscode ”目录下生成一个名为“c_cpp_properties.json” 的文件。

修改c_cpp_properties.json内容如下所示:

{

    "configurations": [

        {

            "name": "Linux",

            "includePath": [

                "${workspaceFolder}/**",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31",

                "/home/zgq/linux/Linux_Drivers/MyNewLED",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/include",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include/generated"

            ],

            "defines": [],

            "compilerPath": "/usr/bin/gcc",

            "cStandard": "gnu11",

            "cppStandard": "gnu++14",

            "intelliSenseMode": "gcc-x64"

        }

    ],

    "version": 4

}

8、编译

输入“make clean回车

输入“make drv回车

输入“make app回车

输入“make install回车

输入“ls /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -l回车”产看是存在“LED_APP和MyNewLED_Module.ko

10、测试

启动开发板,从网络下载程序

输入“root

输入“cd /lib/modules/5.4.31/回车

切换到“/lib/modules/5.4.31/”目录

注意:“lib/modules/5.4.31/在虚拟机中是位于“/home/zgq/linux/nfs/rootfs/”目录下,但在开发板中,却是位于根目录中

输入“ls -l”查看“MyNewLED_Module.ko和LED_APP”是否存在

输入“depmod”,驱动在第一次执行时,需要运行“depmod”

输入“modprobe MyNewLED_Module.ko”,加载“MyNewLED_Module.ko”模块

输入“lsmod”查看有哪些驱动在工作

输入“ls /dev/MyNewLEDName -l回车”,发现节点文件“/dev/MyNewLEDName

输入“./LED_APP /dev/MyNewLEDName 1回车”执行开灯

输入“./LED_APP /dev/MyNewLEDName 0回车”执行关灯

输入“rmmod MyNewLED_Module.ko”,卸载“MyNewLED_Module.ko”模块

注意:输入“rmmod MyNewLED_Module”也可以卸载“MyNewLED_Module.ko”模块

输入“lsmod”查看有哪些驱动在工作。

输入“ls /dev/MyNewLEDName -l回车”,查询节点文件“/dev/MyNewLEDName”是否存在

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

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

相关文章

优先完善功能测试,再考虑其他事项

软件测试是对项目研发过程产物&#xff08;文档、代码、程序等&#xff09;进行审查&#xff0c;保障产品质量的过程。 测试人员应具备从用户角度、开发角度和业务角度审查研发过程产物的能力&#xff0c;从而促使最终的产品达到用户、开发和业务三方要求。 测试人员的价值是…

(C语言)字符分类函数

目录 字符分类函数 1. iscntrl 2. isspace 3. isdigit 4. isxdigit 5. islower 6. isupper 7. isalpha 8. isalnum 9. ispunct 10. isgraph 11. isprint 字符分类函数 C语言中有一系列的函数是专门做字符分类的 &#xff0c;也就是一个字符是属于什么类型的字符的。…

安泰ATG-3090功率信号源的输出信号是什么意思

功率信号源是指能够提供电能输出的设备&#xff0c;其输出信号通常以功率的形式存在。功率信号源在各种应用中发挥着重要作用&#xff0c;涵盖了广泛的领域&#xff0c;包括电力系统、通信、电子设备等。以下是关于功率信号源输出信号的详细介绍。 功率信号源是一种能够将输入能…

操作系统:进程优先级

目录 1.进程优先级 1.1.基本概念 1.2.其他概念 1.3.进程切换 2.解读Linux2.6内核进程的调度队列 1.进程优先级 1.1.基本概念 我们在日常生活中的经验&#xff0c;排队的本质就是确认优先级&#xff0c;排队的原因就是资源不足&#xff0c;需要进行分配。那么在内存资源有…

【Spring知识体系】1.1 Java 注解(Annotation)

文章目录 1.1 注解&#xff08;Annotation&#xff09;1.1.1 什么是注解1.1.2 内置注解1.1.3 元注解&#xff08;5种&#xff09;1.14 自定义注解1.15 注解使用场景介绍※ 本文小结 1.1 注解&#xff08;Annotation&#xff09; 1.1.1 什么是注解 注解的定义&#xff1a;它提…

电力物联网系统设计

电力物联网系统设计 简介 在新能源行业从业多年&#xff0c;参与和负责过大大小小的的项目&#xff0c;发电侧、电网侧、用户侧系统都有过实际的项目经验&#xff0c;这些项目或多或少都有物联网采集方面的需求&#xff0c;本篇文章将会对电力行业物联网经验做一个总结分享。 …

2.4_1 死锁的概念

文章目录 2.4_1 死锁的概念&#xff08;一&#xff09;什么是死锁&#xff08;二&#xff09;死锁、饥饿、死循环的区别&#xff08;三&#xff09;死锁产生的必要条件&#xff08;四&#xff09;什么时候会发生死锁&#xff08;五&#xff09;死锁的处理策略 总结 2.4_1 死锁的…

Windows 安装 Xinference

Windows 安装 Xinference 0. 引言1. 创建虚拟环境2. 安装 pytorch3. 安装 llama_cpp_python4. 安装 chatglm-cpp5. 安装 Xinference6. 设置 model 路径7. 启动 Xinference8. 查看 Cluster Information 0. 引言 Xorbits Inference&#xff08;Xinference&#xff09;是一个性能…

3D-Genome | Hi-C互作矩阵归一化指南

Hi-C 是一种基于测序的方法&#xff0c;用于分析全基因组染色质互作。它已广泛应用于研究各种生物学问题&#xff0c;如基因调控、染色质结构、基因组组装等。Hi-C 实验涉及一系列生物化学反应&#xff0c;可能会在输出中引入噪声。随后的数据分析也会产生影响最终输出噪声&…

蓝桥杯2023年-买瓜(dfs,类型转换同样耗时)

题目描述 小蓝正在一个瓜摊上买瓜。瓜摊上共有 n 个瓜&#xff0c;每个瓜的重量为 Ai 。 小蓝刀功了得&#xff0c;他可以把任何瓜劈成完全等重的两份&#xff0c;不过每个瓜只能劈一刀。 小蓝希望买到的瓜的重量的和恰好为 m 。 请问小蓝至少要劈多少个瓜才能买到重量恰好…

匿名内部类

隐藏了名字的内部类 javap 反编译命令 格式: new 类名或者接口名(){重写方法};