IIO子系统一

  • 十七、IIO子系统(一)
    • 17.1 简介
    • 17.2 数模转换——DAC实验
      • 17.2.1 IIO缓冲区
      • 17.2.2 触发器
      • 17.2.3 工业I/O事件
      • 17.2.4 iio工具
      • 17.2.5 LTC2607——DAC模块介绍
      • 17.2.5.1 设备树
      • 17.2.5.2 LTC2607驱动模块介绍
      • 17.2.5.2.1 用作I2C交互的工业框架
      • 17.2.5.2.2 用作IIO设备的工业框架
      • 17.2.5.3 源代码
    • 17.3 模数转换——ADC实验
      • 17.3.1 ADC模块设备树
      • 17.3.2 ADC模块用作spi交互的工业框架分析
      • 17.3.3 ADC模块用作iio设备的工业框架分析
      • 17.3.4 ADC模块源代码
      • 17.3.5 应用代码
      • 17.3.6 ADC调试

十七、IIO子系统(一)

17.1 简介

IIO是支持模数转换/数模转换,即ADC/DAC以及多种传感器的Linux子系统。

以下是IIO子系统支持的传感器:
******* ADC——模-数转换器
******* DAC——数模转换器
******* CDC——电容-数字转换器
******* 加速度计
******* 陀螺仪
******* IMU——惯性测量仪
******* 颜色和光传感器
******* 磁力计
******* 压力传感器
******* 距离传感器
******* 温度传感器

关于Linux工业I/O子系统详细位于Linux驱动实现API指南,如下:

https://www.kernel.org/doc/html/latest/driver-api/iio/core.html

IIO核心提供如下功能:
为各类嵌入式传感器驱动程序编写提供统一框架;
为操作传感器的用户太应用程序提供标准接口,如下;

IIO框架提供几种接口,如下:
******* 1、/sys/bus/iio/iio:deviceX  代表一个硬件传感器,按照通道呈现;
******* 2、/dev/iio:deviceX 代表字符设备节点,用于输出事件和传感器数据,可以使用open()、read()、write()、close()函数来访问;

iio内存申请函数使用,如下:

devm_iio_device_alloc()

将设备注册到内核,如下:

devm_iio_device_register()

IIO设备的sysfs接口

在 /sys/bus/iio/iio:deviceX/目录下,部分属性如下:
******* name 是对物理芯片的描述
******* dev 是节点对应的主/从设备号
******* 设备配置属性
******* 数据通道访问属性,如out_voltage0_raw

17.2 数模转换——DAC实验

iio属性定义来自,如下:

driver/iio/industrialio-core.c

17.2.1 IIO缓冲区

IIO缓冲区的使用可以降低CPU消耗。

17.2.2 触发器

驱动程序大多情况下,是基于外部事件(触发器)来捕获数据,。而不是周期性的轮询数据。该驱动能有一个产生硬件事件的设备,也可以有一个具备独立终端源,如接收到外部GPIO线、定时器中断或者用户态特定sysfs文件的写操作的驱动来提供。

17.2.3 工业I/O事件

 展示到用户态的sysfs事件属性,几乎所有的IIO事件都是对应着从传感器督导一个或多个原始数据阈值,例如:
******* 越过电压阈值;
******* 均值超过阈值;
******* 运动检测器;
******* 平方和均方根的阈值;
******* 变化率阈值;

向用户态传递IIO事件

include<linux/iio/events.h>iio_push_event()

17.2.4 iio工具

位于/tools/iio/目录,如下:
******* lsiio
******* iio_event_monitor
******* iio_generic_buffer
******* libiio

17.2.5 LTC2607——DAC模块介绍

内核模块将会操作Analog Device 公司的LTC2607设备。双路12bit、100kHZ和400KHZ 电压输出的DAC,使用I2C串行接口。

该驱动包括三部分,如下:
******* 设备树
******* 操作I2C交互的工业框架
******* IIO设备工业框架

17.2.5.1 设备树

         i2c2: i2c@600 {compatible = "atmel,sama5d2-i2c";reg = <0x600 0x200>;interrupts = <20 IRQ_TYPE_LEVEL_HIGH 7>;dmas = <0>, <0>;dma-names = "tx", "rx";#address-cells = <1>;#size-cells = <0>;clocks = <&flx1_clk>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_mikrobus_i2c>;atmel,fifo-size = <16>;status = "okay";ltc2607@72 {compatible = "arrow,ltc2607";reg = <0x72>;   #I2C设备地址};ltc2607@73 {compatible = "arrow,ltc2607";reg = <0x73>;   #I2C设备地址};};

17.2.5.2 LTC2607驱动模块介绍

17.2.5.2.1 用作I2C交互的工业框架

必须包含的头文件,如下:

#include <linux/i2c.h>
通过包含该头文件,可以引用 struct i2c_driver、 struct i2c_client()、i2c_get_clientdata()、i2c_set_clientdata()

创建有一个i2c_driver数据结构,如下:

static struct i2c_driver ltc2607_driver = {.driver = {.name    = LTC2607_DRV_NAME,.owner  = THIS_MODULE,.of_match_table = dac_dt_ids,},.probe       = ltc2607_probe,.remove        = ltc2607_remove,.id_table = ltc2607_id,
};

作为i2c驱动注册到I2C总线,如下:

module_i2c_driver(ltc2607_driver);

添加到驱动支持的设备列表,如下:

static const struct of_device_id dac_dt_ids[] = {{ .compatible = "arrow,ltc2607", },{ }
};
MODULE_DEVICE_TABLE(of, dac_dt_ids);

定义i2c_device_id类型数组,如下:

static const struct i2c_device_id ltc2607_id[] = {{ "ltc2607", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, ltc2607_id);

17.2.5.2.2 用作IIO设备的工业框架

包含的头文件,如下:

#include <linux/iio/iio.h>
通过引用上面头文件,可以使用 iio_priv、devm_iio_device_alloc()

LTC2607驱动力的物理和逻辑设备数据结构之间关联,如下:

static const struct iio_info ltc2607_info = {.write_raw = ltc2607_write_raw,.driver_module = THIS_MODULE,
};ltc2607_write_raw()函数,调用 ltc2607_set_value()函数再调用,i2c_master_send()函数。

计算公式:
DAC值的范围是0 ~ 0xFFFF(65535)
输出电压 = Vref * DAC值 / 65535
Vref = 5V

17.2.5.3 源代码

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>#define LTC2607_DRV_NAME "ltc2607"struct ltc2607_device {struct i2c_client *client;char name[8];
};static const struct iio_chan_spec ltc2607_channel[] = {{.type      = IIO_VOLTAGE,.indexed = 1,.output        = 1,.channel   = 0,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},{.type        = IIO_VOLTAGE,.indexed = 1,.output        = 1,.channel   = 1,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},{.type        = IIO_VOLTAGE,.indexed = 1,.output        = 1,.channel   = 2,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}};static int ltc2607_set_value(struct iio_dev *indio_dev, int val, int channel)
{struct ltc2607_device *data = iio_priv(indio_dev);u8 outbuf[3];int ret;int chan;if (channel == 2)chan = 0x0F;elsechan = channel;if (val >= (1 << 16) || val < 0)return -EINVAL;outbuf[0] = 0x30 | chan; /* write and update DAC */outbuf[1] = (val >> 8) & 0xff; /* MSB byte of dac_code */outbuf[2] = val & 0xff; /* LSB byte of dac_code */ret = i2c_master_send(data->client, outbuf, 3);if (ret < 0)return ret;else if (ret != 3)return -EIO;elsereturn 0;
}static int ltc2607_write_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int val, int val2, long mask)
{int ret;switch (mask) {case IIO_CHAN_INFO_RAW:ret = ltc2607_set_value(indio_dev, val, chan->channel);return ret;default:return -EINVAL;}
}static const struct iio_info ltc2607_info = {.write_raw = ltc2607_write_raw,.driver_module = THIS_MODULE,
};static int ltc2607_probe(struct i2c_client *client,const struct i2c_device_id *id)
{static int counter = 0;struct iio_dev *indio_dev;struct ltc2607_device *data;u8 inbuf[3];u8 command_byte;int err;dev_info(&client->dev, "DAC_probe()\n");command_byte = 0x30 | 0x00; /* Write and update register with value 0xFF*/inbuf[0] = command_byte;inbuf[1] = 0xFF;inbuf[2] = 0xFF;/* allocate the iio_dev structure */indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));if (indio_dev == NULL) return -ENOMEM;data = iio_priv(indio_dev);   i2c_set_clientdata(client, data);data->client = client;sprintf(data->name, "DAC%02d", counter++);dev_info(&client->dev, "data_probe is entered on %s\n", data->name);indio_dev->name = data->name;indio_dev->dev.parent = &client->dev;indio_dev->info = &ltc2607_info;indio_dev->channels = ltc2607_channel;indio_dev->num_channels = 3;indio_dev->modes = INDIO_DIRECT_MODE;err = i2c_master_send(client, inbuf, 3); /* write DAC value */if (err < 0) {dev_err(&client->dev, "failed to write DAC value");return err;}dev_info(&client->dev, "the dac answer is: %x.\n", err);err = devm_iio_device_register(&client->dev, indio_dev);if (err)return err;dev_info(&client->dev, "ltc2607 DAC registered\n");return 0;
}static int ltc2607_remove(struct i2c_client *client)
{dev_info(&client->dev, "DAC_remove()\n");return 0;
}static const struct of_device_id dac_dt_ids[] = {{ .compatible = "arrow,ltc2607", },{ }
};
MODULE_DEVICE_TABLE(of, dac_dt_ids);static const struct i2c_device_id ltc2607_id[] = {{ "ltc2607", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, ltc2607_id);static struct i2c_driver ltc2607_driver = {.driver = {.name  = LTC2607_DRV_NAME,.owner  = THIS_MODULE,.of_match_table = dac_dt_ids,},.probe       = ltc2607_probe,.remove        = ltc2607_remove,.id_table = ltc2607_id,
};
module_i2c_driver(ltc2607_driver);MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("LTC2607 16-bit DAC");
MODULE_LICENSE("GPL");

17.3 模数转换——ADC实验

首先,介绍开发一个不具有硬件触发功能的ADC,该ADC——SPI设备LTC2422. LTC2422是20bit 双通道ADC,即模拟——数字转换芯片,LTC串行输出数据流,如下:

LTC2422输出数据流长度是24bit.
******* 片选引脚——CS
******* 数据线——SDO  即MISO管脚
******* 串行时钟——SCK

在driver/spi/spidev.c是通用SPI设备驱动,可以通过内核配置来启动它,即 CONFIG_SPI_SPIDEV.

17.3.1 ADC模块设备树

             spi3: spi@400 {compatible = "atmel,at91rm9200-spi";reg = <0x400 0x200>;interrupts = <23 IRQ_TYPE_LEVEL_HIGH 7>;clocks = <&flx4_clk>;clock-names = "spi_clk";#address-cells = <1>;#size-cells = <0>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_mikrobus_spi &pinctrl_mikrobus1_spi_cs &pinctrl_mikrobus2_spi_cs>;atmel,fifo-size = <16>;status = "okay"; /* Conflict with uart6 and i2c3. */spidev@0 {compatible = "spidev";spi-max-frequency = <2000000>;reg = <0>;};ADC: ltc2422@0 {compatible = "arrow,ltc2422";spi-max-frequency = <2000000>;reg = <0>;  #片选信号CS值pinctrl-0 = <&pinctrl_key_gpio_default>;int-gpios = <&pioA PIN_PA29 GPIO_ACTIVE_LOW>;interrupt-parent = <&pioA>;interrupts = <29  IRQ_TYPE_EDGE_FALLING>;};};

17.3.2 ADC模块用作spi交互的工业框架分析

包含头文件,如下:

#include <linux/spi/spi.h>

定义spi_driver数据结构,如下:


static struct spi_driver ltc2422_driver = {.driver = {.name   = "ltc2422",.owner   = THIS_MODULE,.of_match_table = ltc2422_dt_ids,},.probe       = ltc2422_probe,.id_table  = ltc2422_id,
};

将其作为驱动注册到SPI总线上,如下:

module_spi_driver(ltc2422_driver);

定义一个spi_device_id数据结构数组,如下:


static const struct spi_device_id ltc2422_id[] = {{ .name = "ltc2422", },{ }
};
MODULE_DEVICE_TABLE(spi, ltc2422_id);

17.3.3 ADC模块用作iio设备的工业框架分析

包含头文件,如下:

#include <linux/iio/iio.h>
用于devm_iio_alloc()、iio_priv()

创建用于管理设备的私有数据结构,如下:

struct ltc2422_state {struct spi_device *spi;u8 buffer[4];
};

将设备注册到IIO核心里,如下:

devm_iio_device_register(&spi->dev, indio_dev);

一个IIO设备代表一个数据通道,一个IIO设备可以有一个或多个数据通道。添加iio:device生成多个通道,如下:

static const struct iio_chan_spec ltc2422_channel[] = {{.type       = IIO_VOLTAGE,.indexed = 1,.output        = 1,.channel   = 0,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}};

给iio_info数据结构赋值,用户态对sysfs数据通道属性的操作都会被映射到内核的回调函数上,如下:

static const struct iio_info ltc2422_info = {.read_raw = &ltc2422_read_raw,.driver_module = THIS_MODULE,
};

ltc2422_read_raw()函数调用spi_read()函数,如下:

static int ltc2422_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan, int *val, int *val2, long m)
{int ret;struct ltc2422_state *st = iio_priv(indio_dev);switch (m) {case IIO_CHAN_INFO_RAW:ret = spi_read(st->spi, &st->buffer, 3);if (ret < 0)return ret;*val  = st->buffer[0] << 16;*val |= st->buffer[1] << 8;*val |= st->buffer[2];dev_info(&st->spi->dev, "the value is %x\n", *val);return IIO_VAL_INT;default:return -EINVAL;}
}

17.3.4 ADC模块源代码

#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>struct ltc2422_state {struct spi_device *spi;u8 buffer[4];
};static const struct iio_chan_spec ltc2422_channel[] = {{.type        = IIO_VOLTAGE,.indexed = 1,.output        = 1,.channel   = 0,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}};static int ltc2422_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan, int *val, int *val2, long m)
{int ret;struct ltc2422_state *st = iio_priv(indio_dev);switch (m) {case IIO_CHAN_INFO_RAW:ret = spi_read(st->spi, &st->buffer, 3);if (ret < 0)return ret;*val  = st->buffer[0] << 16;*val |= st->buffer[1] << 8;*val |= st->buffer[2];dev_info(&st->spi->dev, "the value is %x\n", *val);return IIO_VAL_INT;default:return -EINVAL;}
}static const struct iio_info ltc2422_info = {.read_raw = &ltc2422_read_raw,.driver_module = THIS_MODULE,
};static int ltc2422_probe(struct spi_device *spi)
{struct iio_dev *indio_dev;struct ltc2422_state *st;int err;dev_info(&spi->dev, "my_probe() function is called.\n");const struct spi_device_id *id = spi_get_device_id(spi);indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));if (indio_dev == NULL)return -ENOMEM;st = iio_priv(indio_dev);st->spi = spi;indio_dev->dev.parent = &spi->dev;indio_dev->channels = ltc2422_channel;indio_dev->info = &ltc2422_info;indio_dev->name = id->name;indio_dev->num_channels = 1;indio_dev->modes = INDIO_DIRECT_MODE;err = devm_iio_device_register(&spi->dev, indio_dev);if (err < 0)return err;return 0;
}static const struct of_device_id ltc2422_dt_ids[] = {{ .compatible = "arrow,ltc2422", },{ }
};MODULE_DEVICE_TABLE(of, ltc2422_dt_ids);static const struct spi_device_id ltc2422_id[] = {{ .name = "ltc2422", },{ }
};
MODULE_DEVICE_TABLE(spi, ltc2422_id);static struct spi_driver ltc2422_driver = {.driver = {.name  = "ltc2422",.owner   = THIS_MODULE,.of_match_table = ltc2422_dt_ids,},.probe       = ltc2422_probe,.id_table  = ltc2422_id,
};
module_spi_driver(ltc2422_driver);MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("LTC2422 DUAL ADC");
MODULE_LICENSE("GPL");

17.3.5 应用代码

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>int8_t read_adc();/* The LTC2422 least significant bit value with 5V full-scale */
float LTC2422_lsb = 4.7683761E-6;  /* The LTC2422 least significant bit value with 3.3V full-scale */
/* float LTC2422_lsb = 3.1471252E-6; *//* check which number is the ADC iio:deviceX and replace x by the number */
#define LTC2422_FILE_VOLTAGE    "/sys/bus/iio/devices/iio:device2/out_voltage0_raw"
#define SPI_DATA_CHANNEL_OFFSET 22
#define SPI_DATA_CHANNEL_MASK   (1 << SPI_DATA_CHANNEL_OFFSET)
#define LTC2422_CONVERSION_TIME     137 /* ms *//* * Returns the Data and Channel Number(0- channel 0, 1-Channel 1)* Returns the status of the SPI read. 0=successful, 1=unsuccessful.*/
int8_t LTC2422_read(uint8_t *adc_channel, int32_t *code);/* Returns the Calculated Voltage from the ADC Code */
float LTC2422_voltage(uint32_t adc_code, float LTC2422_lsb);int8_t LTC2422_read(uint8_t *adc_channel, int32_t *code)
{int a2dReading = 0;FILE *f = fopen(LTC2422_FILE_VOLTAGE, "r");int read = fscanf(f, "%d", &a2dReading);if (read <= 0) {printf("ERROR: Unable to read values from voltage input file.\n");exit(-1);}/* Determine the channel number */*adc_channel = (a2dReading & SPI_DATA_CHANNEL_MASK) ? 1 : 0;*code = a2dReading;fclose(f);return(0);
}/* Returns the Calculated Voltage from the ADC Code */
float LTC2422_voltage(uint32_t adc_code, float LTC2422_lsb)
{float adc_voltage;if (adc_code & 0x200000){adc_code &= 0xFFFFF; /* Clears Bits 20-23 */adc_voltage=((float)adc_code)*LTC2422_lsb;}else{adc_code &= 0xFFFFF; /* Clears Bits 20-23 */adc_voltage = -1*((float)adc_code)*LTC2422_lsb;}return(adc_voltage);
}void delay(unsigned int ms)
{usleep(ms*1000);
}int8_t read_adc()
{float adc_voltage;int32_t adc_code;uint8_t adc_channel;int32_t  adc_code_array;           int8_t return_code;int a2dReading = 0;LTC2422_read(&adc_channel, &adc_code);delay(LTC2422_CONVERSION_TIME);LTC2422_read(&adc_channel, &adc_code);adc_voltage = LTC2422_voltage(adc_code, LTC2422_lsb);printf("the value of ADC channel %d\n", adc_channel);printf("     is : %6.4f\n", adc_voltage);delay(LTC2422_CONVERSION_TIME);LTC2422_read(&adc_channel, &adc_code);adc_voltage = LTC2422_voltage(adc_code, LTC2422_lsb);printf("the value of ADC channel %d\n", adc_channel);printf("     is : %6.4f\n", adc_voltage);return(0);
}int main(void)
{read_adc();printf("Application termined\n");return 0;
}

17.3.6 ADC调试

insmod  ltc2422_daul_device.ko
cd /sys/bus/iio/devices
cat out_voltage0_raw
echo 65535 > out_voltage0_rawrmmod  ltc2422_daul_device.ko

感谢阅读,祝君成功!
-by aiziyou

嵌入式Linux设备驱动程序开发指南17(IIO子系统一)——读书笔记相关推荐

  1. 嵌入式Linux设备驱动程序开发指南18(IIO子系统(二)具有硬件触发功能的IIO子系统ADC模块)——读书笔记

    IIO子系统二 具有硬件触发功能的IIO子系统ADC模块 十八.IIO子系统(二) 具有硬件触发功能的IIO子系统ADC模块 18.1 简介 18.2 设备树 18.3 硬件触发驱动功能分析 18.3 ...

  2. 嵌入式Linux设备驱动程序开发指南14(Linux设备驱动使用DMA)——读书笔记

    Linux设备驱动使用DMA 十四.Linux设备驱动使用DMA 14.1 简介 14.2 缓存一致性 14.3 DMA控制器接口 14.4 流式DMA模块 14.4.1 sdma_sam_m2m.c ...

  3. 嵌入式Linux设备驱动程序开发指南3(构建Microchip SAMA5D2嵌入式 Linux系统)——读书笔记

    构建Microchip SAMA5D2嵌入式 Linux系统 三.构建Microchip SAMA5D2嵌入式 Linux系统 3.1 获取驱动代码 3.2 配置编译 3.2.1 bootstrap编 ...

  4. 嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记

    Linux USB设备驱动 二十.Linux USB设备驱动 20.1 USB简介 20.1.1 USB2.0总线拓扑 20.1.2 USB总线枚举和设备布局 20.1.3 USB数据传输 20.1. ...

  5. 嵌入式Linux设备驱动程序开发指南9(平台设备驱动)——读书笔记

    平台设备驱动 九.平台设备驱动 9.1 平台设备驱动概述 9.2 GPIO驱动 9.2.1 简介 9.2.2 硬件名称 9.2.3 引脚控制器 9.2.4 引脚控制子系统 9.2.5 GPIO控制器驱 ...

  6. 嵌入式Linux设备驱动程序:在运行时读取驱动程序状态

    嵌入式Linux设备驱动程序:在运行时读取驱动程序状态 Embedded Linux device drivers: Reading driver state at runtime 在运行时了解驱动程 ...

  7. VxWorks设备驱动程序开发指南---驱动程序的分类

    8D Spaces Reliability & Stability & Efficiency 目录视图 摘要视图 订阅 VxWorks设备驱动程序开发指南(三)---驱动程序的分类 2 ...

  8. 《精通Linux设备驱动程序开发》——1.5 Linux发行版

    本节书摘来自异步社区<精通Linux设备驱动程序开发>一书中的第1章,第1.5节,作者:[印]Sreekrishnan Venkateswaran(斯里克里斯汉 温卡特斯瓦兰)著,更多章节 ...

  9. 嵌入式linux设备驱动开发,嵌入式Linux设备驱动开发简介.pdf

    清远见--嵌入式培训专家 http :// "黑色经典"系列之<嵌入式Linux 应用程序开发详解> 11 章 嵌入式Linux 设备驱动开发 本章目标 本书从 6 章 ...

最新文章

  1. AIFramework框架Jittor特性(上)
  2. 关于for和foreach,兼顾效率与安全
  3. 一文读懂 HTTP/1HTTP/2HTTP/3
  4. 生成GUID唯一值的方法汇总(dotnet/javascript/sqlserver)
  5. Windows 10系统永久关闭Windows Defender Antivirus防病毒程序方法
  6. 字符串拼接+和concat的区别
  7. 算法工程师思维导图—数据结构与算法
  8. 常见Java开发过程中遇到的问题及其解决办法
  9. iOS开发探索-Base64编码
  10. 烟雾传感器的matlab程序,单片机烟雾传感器proteus仿真+程序+PCB原理图
  11. java redis hscan_redis操作之迭代器(scan和hscan)讲解
  12. OPPO R17忘记用户账户密码强制清除登录账号
  13. 电视盒子装android,智能电视能不能装安卓应用市场,怎么安装
  14. 《居里夫人自传》的读后感作文1700字
  15. win10 网络发现 打开保存后,自动关闭
  16. Windows Server 2022 英文版、简体中文版下载 (updated Dec 2021)(2022 年 1 月发布)
  17. 几分钟搞定,文件名称中文转英文
  18. MTK 平台TP调试遇坑
  19. 小红书笔记api_odoo与小红书对接笔记
  20. 腾讯视频下载的qlv格式转换为MP4格式

热门文章

  1. python调用谷歌地图api_python显示地图与谷歌地图
  2. 数据库相关基础知识总结
  3. 更加安全便捷的印章管理——区块链电子印章
  4. 苹果手机在未激活的状况下待机时间长了会自动关机吗
  5. linux进阶-网络安全系统网站服务
  6. python新打包工具,最好用的版本之一,不接受反驳
  7. Linux MTD子系统学习(二)
  8. ES6之什么是箭头函数?
  9. css基本语法选择器
  10. 团队协作常见问题分析与解决