STM32MP157驱动开发——Linux ADC驱动

news2024/11/20 8:24:38

STM32MP157驱动开发——Linux ADC驱动

  • 0.前言
  • 一、ADC 简介
    • 1.ADC 简介
    • 2.STM32MP157 ADC简介
  • 二、ADC 驱动源码解析
    • 1.设备树下的 ADC 节点
    • 2.ADC 驱动源码分析
      • 1)stm32_adc 结构体
      • 2)stm32_adc_probe 函数
      • 3)stm32_adc_iio_info 结构体
  • 三、驱动开发
    • 1.修改设备树
    • 2.使能 ADC 驱动
  • 四、运行测试


0.前言

  上一节学习了 Linux 下的 IIO 驱动框架,并使用 IIO 子系统对板载的 icm20608 设备进行了驱动开发。此外,由于传感器采集数据的速率较快,还引入了数据缓冲区以及文件流操作的读写方式。
  本节就学习如何使用 STM32MP1 内部的 ADC 设备,也借此巩固一下 IIO 子系统的使用方式。

一、ADC 简介

1.ADC 简介

  ADC,Analog to Digital Converter 的缩写,也就是数模转换器,可以将外部的模拟信号转化成数字信号,将一个范围内的电压精确的读取出来。ADC 有几个比较重要的参数:

测量范围:测量范围对于 ADC 来说就好比尺子的量程,ADC 测量范围决定了外接的设备其信号输出电压范围,如果所使用的外部传感器输出的电压信号范围和所使用的 ADC 测量范围不符合,那么就需要自行设计相关电压转换电路。
分辨率:就是尺子上的能量出来的最小测量刻度,假如 ADC 的测量范围为 0-5V,分辨率设置为 12 位,那么能测出来的最小电压就是 5V 除以 2 的 12 次方,也就是 5/4096=0.00122V。很明显,分辨率越高,采集到的信号越精确,所以分辨率是衡量 ADC 的一个重要指标。
精度:是影响结果准确度的因素之一。经过计算 ADC 在 12 位分辨率下的最小测量值是 0.00122V,但是 ADC 的精度最高只能到 11 位也就是 0.00244V。也就是 ADC 测量出 0.00244V 的结果是要比 0.00122V 要可靠,也更准确。
采样时间:当 ADC 在某时刻采集外部电压信号时,此时外部的信号应该保持不变,但实际上外部的信号是不停变化的。所以在 ADC 内部有一个保持电路,保持某一时刻的外部信号,这样 ADC 就可以稳定采集了,保持这个信号的时间就是采样时间。
采样率:也就是在一秒的时间内采集多少次。很明显,采样率越高越好,当采样率不够时可能会丢失部分信息,所以 ADC 采样率是衡量 ADC 性能的另一个重要指标。

2.STM32MP157 ADC简介

  STM32MP157 有两个 ADC:ADC1 和 ADC2,ADC1 和 ADC2 紧密耦合,可在双重模式下运行(ADC1 为主器件)。每个 ADC 由一个 16 位逐次逼近模数转换器组成,每个 ADC 有 20 个通道,每个通道支持单次、连续、扫描或不连续采样模式。转换结果存储在一个左对齐或右对齐的 32 位数据寄存器中。ADC 主要特性如下:

① 多达 2 个 ADC,可在双重模式下运行
② 可以配置为 16、14、12、10 或 8 位分辨率
③ 自校准
④ 可独立配置各通道采样时间

二、ADC 驱动源码解析

1.设备树下的 ADC 节点

  STM32MP157 有 2 个 ADC,因此对应 2 个 ADC 控制器,在设备树里就有 2 个 ADC控制器节点。这 2 个 ADC 的设备树节点内容都是一样的,除了 reg 属性不同。本节使用 PA5 引脚来完成 ADC 驱动开发,该引脚是 ADC1_INP19 通道引脚。stm32mp151.dtsi 文件中的 adc 节点信息如下:
在这里插入图片描述
根据 compatible 属性值“st,stm32mp1-adc-core”,可以找到驱动核心文件为 drivers/iio/adc/stm32-adc-core.c。另一个 compatible 属性值“st,stm32mp1-adc”,可以找到 ADC 驱动文件 drivers/iio/adc/stm32-adc.c。ADC 相关的绑定文档为 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt。根据该文档中的要求,就可以创建自己的 ADC 节点。

ADC 首先需要创建一个根节点,属性如下:
必要属性:

  • compatible:兼容性属性,必须的,可以设置为“st,stm32mp1-adc-core”
  • reg:ADC 控制器寄存器信息
  • interrupts:中断属性,ADC1 和 ADC2 各对应一个中断信息
  • clocks:时钟属性
  • clock-names:时钟名字,可选“adc”或“bus”
  • interrupt-controller:中断控制器
  • vdda-supply:此属性对应 vdda 输入模拟电压句柄
  • vref-supply:此属性对应 vref 参考电压句柄
  • interrupt-cells:设置为 1
  • address-cells:设置为 1
  • size-cells:设置为 0

可选属性:

  • pinctrl 引脚配置信息
  • booster-supply:嵌入式 booster 调节器句柄
  • vdd-supply:vdd 输入电压句柄
  • st,syscfg:系统配置控制器句柄
  • st,max-clk-rate-hz:最大时钟

STM32MP157 有两个 ADC,每个 ADC 对应一个子节点,ADC 子节点相关属性如下:
必要属性:

  • compatible:兼容性属性,必须的,可以设置为“st,stm32mp1-adc”
  • reg:不同 ADC 控制器寄存器地址偏移信息。
  • interrupts:中断线信息,adc@0 为 0,adc@100 为 1。
  • st,adc-channels:ADC 通道信息,可以设置 0~19,分别对应 20 个通道
  • st,adc-diff-channels:ADC 差分通道信息 (如果使用差分 ADC 功能)
  • io-channel-cells:设置为1

可选属性:

  • dmas:DMA 通道句柄
  • dma-names:dma 名字,必须设置成“rx”
  • assigned-resolution-bits:ADC 分辨率,可以设置为 8、10、12、14 或 16
  • st,min-sample-time-nsecs:最小采样时间,单位 ns

2.ADC 驱动源码分析

STM32MP157 ADC 驱动文件有两个:stm32-adc-core.c 和 stm32-adc.c。stm32-adc-core.c 是 ADC 核心层,主要用于 ADC 电源等初始化,stm32-adc.c 主体框架是 platform,配合 IIO 驱动框架实现 ADC 驱动 (重点关注此驱动)。

1)stm32_adc 结构体

196 struct stm32_adc {
197 	struct stm32_adc_common *common; /* ADC 通用数据 */
198 	u32 offset; /* ADC 控制器偏移地址 */
199 	const struct stm32_adc_cfg *cfg; /* 配置信息 */
200 	struct completion completion; /* 单次转换完成量 */
201 	u16 buffer[STM32_ADC_MAX_SQ]; /* 数据缓冲区 */
202 	struct clk *clk; /* 时钟 */
203 	int irq; /* 中断 */
204 	spinlock_t lock; /* 自旋锁 */
206 	unsigned int num_conv; /* 扫描转换编号 */
205 	unsigned int bufi; /* 数据缓冲区索引 */
207 	u32 res; /* 数据分辨率 */
208 	u32 trigger_polarity; /* 外部触发优先级 */
209 	struct dma_chan *dma_chan; /* dma 通道 */
210 	u8 *rx_buf; /* dma 接收缓冲区 */
211 	dma_addr_t rx_dma_buf; /* dma 接收缓冲总线地址 */
212 	unsigned int rx_buf_sz; /* dma 接收缓冲大小 */
213 	u32 difsel; /* 单次结束/差分掩码 */
214 	u32 pcsel; /* 预选通道掩码 */
215 	u32 smpr_val[2]; /* 采样时间(smpr1 和 smpr2)*/
216 	struct stm32_adc_calib cal; /* 校准值 */
217 	char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];/* 通道名字 */
218 };

2)stm32_adc_probe 函数

1 static int stm32_adc_probe(struct platform_device *pdev)
2 {
3 		struct iio_dev *indio_dev;
4 		struct device *dev = &pdev->dev;
5 		irqreturn_t (*handler)(int irq, void *p) = NULL;
6 		struct stm32_adc *adc;
7 		int ret;
8 
9 		if (!pdev->dev.of_node)
10 			return -ENODEV;
11
12 		indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
13 		if (!indio_dev)
14 			return -ENOMEM;
15
16 		adc = iio_priv(indio_dev);
17 		adc->common = dev_get_drvdata(pdev->dev.parent);
18 		spin_lock_init(&adc->lock);
19 		init_completion(&adc->completion);
20 		adc->cfg = (const struct stm32_adc_cfg *)of_match_device(dev->driver->of_match_table, dev)->data;
21
22
23 		indio_dev->name = dev_name(&pdev->dev);
24 		indio_dev->dev.parent = &pdev->dev;
25 		indio_dev->dev.of_node = pdev->dev.of_node;
26		indio_dev->info = &stm32_adc_iio_info;
27 		indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
28
29 		platform_set_drvdata(pdev, adc);
30
31 		ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
32 		if (ret != 0) {
33 		dev_err(&pdev->dev, "missing reg property\n");
34 		return -EINVAL;
35 }
36
37 		adc->irq = platform_get_irq(pdev, 0);
38 		if (adc->irq < 0)
39 			return adc->irq;
40
41 		ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr, stm32_adc_threaded_isr, 0, pdev->name, adc);
44 		if (ret) {
45 			dev_err(&pdev->dev, "failed to request IRQ\n");
46 			return ret;
47 		}
48
49 		adc->clk = devm_clk_get(&pdev->dev, NULL);
50 		if (IS_ERR(adc->clk)) {
51 			ret = PTR_ERR(adc->clk);
52 			if (ret == -ENOENT && !adc->cfg->clk_required) {
53 				adc->clk = NULL;
54 			} else {
55 				dev_err(&pdev->dev, "Can't get clock\n");
56 				return ret;
57 			}
58 		}
59
60 		ret = stm32_adc_of_get_resolution(indio_dev);
61 		if (ret < 0)
62 			return ret;
63
64 		ret = stm32_adc_chan_of_init(indio_dev);
65 		if (ret < 0)
66 			return ret;
67
68 		ret = stm32_adc_dma_request(indio_dev);
69 		if (ret < 0)
70 			return ret;
71
72 		if (!adc->dma_chan)
73 			handler = &stm32_adc_trigger_handler;
74
75 		ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, handler, &stm32_adc_buffer_setup_ops);
78 		if (ret) {
79 			dev_err(&pdev->dev, "buffer setup failed\n");
80 			goto err_dma_disable;
81 		}
82
83 		/* Get stm32-adc-core PM online */
84 		pm_runtime_get_noresume(dev);
85 		pm_runtime_set_active(dev);
86 		pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS);
87 		pm_runtime_use_autosuspend(dev);
88 		pm_runtime_enable(dev);
89
90 		ret = stm32_adc_hw_start(dev);
91 		if (ret)
92 			goto err_buffer_cleanup;
93
94 		ret = iio_device_register(indio_dev);
95 		if (ret) {
96 			dev_err(&pdev->dev, "iio dev register failed\n");
97 			goto err_hw_stop;
98 		}
......
123 	return ret;
124 }

第 12 行,调用 devm_iio_device_alloc 函数申请 iio_dev,这里也连 stm32_adc 内存一起申请了。
第 16 行,调用 iio_priv 函数从 iio_dev 里面的到 stm32_adc 首地址。
第 23~27 行,初始化 iio_dev,重点是第 26 行的 stm32_adc_iio_info,因为用户空间读取 ADC 数据最终就是由 stm32_adc_iio_info 来完成的。
第 37 行,调用 platform_get_irq 获取中断号。
第 41 行,调用 devm_request_threaded_irq 函数申请中断,这里使用的是中断线程化。
第 60 行,调用 stm32_adc_of_get_resolution 函数获取 ADC 的分辨率。
第 64 行,调用 stm32_adc_chan_of_init 函数初始化 ADC 通道。
第 68 行,调用 stm32_adc_dma_request 函数初始化 DMA。
第 75 行,调用 iio_triggered_buffer_setup 函数设置 IIO 触发缓冲区。
第 90 行,调用 stm32_adc_hw_start 函数开启 ADC。
第 94 行,调用 iio_device_register 函数向内核注册 iio_dev。

可以看出 stm32_adc_probe 函数核心就是初始化 ADC,然后建立 ADC 的 IIO 驱动框架。

3)stm32_adc_iio_info 结构体

1 static const struct iio_info stm32_adc_iio_info = {
2 		.read_raw = stm32_adc_read_raw,
3 		.validate_trigger = stm32_adc_validate_trigger,
4 		.hwfifo_set_watermark = stm32_adc_set_watermark,
5 		.update_scan_mode = stm32_adc_update_scan_mode,
6 		.debugfs_reg_access = stm32_adc_debugfs_reg_access,
7 		.of_xlate = stm32_adc_of_xlate,
8 };

第2行的 stm32_adc_read_raw 函数就是最终向用户空间发送 ADC 原始数据的。
碍于篇幅限制,这里就不在赘述,可以自行查阅相关代码。

三、驱动开发

原理图:
在这里插入图片描述
JP2 是一个 3P 的排针,1 脚连接到 STM32MP157 的 DAC 引脚上(PA4),2 脚连接到 ADC 引脚上(PA5),3 脚连接到 VR1 这个可调电位器上。
本节使用 ADC 来采集 VR1 可调电位器的电压,所以将2-3连接。
在这里插入图片描述

1.修改设备树

ADC 驱动已经由 ST 编写好,只需要修改设备树即可。
在 stm32mp15-pinctrl.dtsi 中添加 ADC 使用的 PA5 引脚配置信息:

adc1_in19_pins_a: adc1-in19 {
	pins {
		pinmux = <STM32_PINMUX('A', 5, ANALOG)>;
	};
};

然后在 stm32mp157d-atk.dts 文件中向根节点添加 vdd 子节点:

vdd: regulator-vdd {
	compatible = "regulator-fixed";
	regulator-name = "vdd";
	regulator-min-microvolt = <3300000>;
	regulator-max-microvolt = <3300000>;
	regulator-always-on;
	regulator-boot-on;
};

最后在 stm32mp157d-atk.dts 文件中向 adc 节点追加一些内容:

&adc {
	pinctrl-names = "default";
	pinctrl-0 = <&adc1_in19_pins_a>;
	vdd-supply = <&vdd>;
	vdda-supply = <&vdd>;
	vref-supply = <&vdd>;
	status = "okay";

	adc1: adc@0 {
		st,adc-channels = <19>;
		st,min-sample-time-nsecs = <10000>;
		assigned-resolution-bits = <16>;
		status = "okay";
	};
};

①配置adc引脚
②设置电压属性
③adc1 子节点,st,adc-channels 属性设置 adc 通道为 19,st,min-sample-time-nsecs 属性设置最小采样时间为 10000ns;设置分辨率为16位

2.使能 ADC 驱动

在 Linux 内核的 menuconfig 中,使能自带的 ADC 驱动:
在这里插入图片描述
修改完成后,就可以编译出设备树文件和系统镜像文件,启动开发板

四、运行测试

启动开发板后,在 /sys/bus/iio/devices 目录下,会存在 ADC 对应的 iio 设备,在该目录中存在以下文件:
在这里插入图片描述

  • in_voltage19_raw:ADC1 通道 19 原始值文件
  • in_voltage_offset:ADC1 偏移文件
  • in_voltage_scale:ADC1 比例文件(分辨率),单位为 mV。实际电压值(mV)=in_voltage19_raw* in_voltage_scale

测试App:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>

/* 字符串转数字,将浮点小数字符串转换为浮点数数值 */
#define SENSOR_FLOAT_DATA_GET(ret, index, str, member)      \
                ret = file_data_read(file_path[index], str);\
                dev->member = atof(str);                    \

/* 字符串转数字,将整数字符串转换为整数数值 */
#define SENSOR_INT_DATA_GET(ret, index, str, member)        \
                ret = file_data_read(file_path[index], str);\
                dev->member = atoi(str);                    \


/* adc iio 框架对应的文件路径 */
static char *file_path[] = {
    "/sys/bus/iio/devices/iio:device0/in_voltage_scale",
    "/sys/bus/iio/devices/iio:device0/in_voltage19_raw",
};

/* 文件路径索引,要和 file_path 里面的文件顺序对应 */
enum path_index {
    IN_VOLTAGE_SCALE = 0,
    IN_VOLTAGE_RAW,
};

/*
 * ADC 数据设备结构体
 */
struct adc_dev{
    int raw;
    float scale;
    float act;
};

struct adc_dev stm32adc;

/*
 * @description : 读取指定文件内容
 * @param – filename : 要读取的文件路径
 * @param - str : 读取到的文件字符串
 * @return : 0 成功;其他 失败
 */
static int file_data_read(char *filename, char *str)
{
    int ret = 0;
    FILE *data_stream;

    data_stream = fopen(filename, "r"); /* 只读打开 */
    if(data_stream == NULL) {
        printf("can't open file %s\r\n", filename);
        return -1;
    }

    ret = fscanf(data_stream, "%s", str);
    if(!ret) {
        printf("file read error!\r\n");
    } else if(ret == EOF) {
        /* 读到文件末尾的话将文件指针重新调整到文件头 */
        fseek(data_stream, 0, SEEK_SET);
    }
    fclose(data_stream); /* 关闭文件 */
    return 0;
}

 /*
 * @description : 获取 ADC 数据
 * @param - dev : 设备结构体
 * @return : 0 成功;其他 失败
 */
static int adc_read(struct adc_dev *dev)
{
    int ret = 0;
    char str[50];

    SENSOR_FLOAT_DATA_GET(ret, IN_VOLTAGE_SCALE, str, scale);
    SENSOR_INT_DATA_GET(ret, IN_VOLTAGE_RAW, str, raw);

    /* 转换得到实际电压值 mV */
    dev->act = (dev->scale * dev->raw)/1000.f;
    return ret;
}

/*
* @description : main 主程序
* @param – argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
    int ret = 0;

    if (argc != 1) {
        printf("Error Usage!\r\n");
        return -1;
    }

    while (1) {
        ret = adc_read(&stm32adc);
        if(ret == 0) { /* 数据读取成功 */
            printf("ADC 原始值: %d,电压值: %.3fV\r\n", stm32adc.raw,
            stm32adc.act);
        }
        usleep(100000); /*100ms */
    }
    return 0;
}

编译测试App:

arm-none-linux-gnueabihf-gcc -march=armv7-a -mfpu=neon -mfloat-abi=hard adcApp.c -o adcApp

中间的参数为使能浮点运算。

测试结果:
在这里插入图片描述

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

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

相关文章

【深度学习】经典算法解读及代码复现AlexNet-VGG-GoogLeNet-ResNet(二)

链接: 【深度学习】经典算法解读及代码复现AlexNet-VGG-GoogLeNet-ResNet(一) 4.GoogLeNet 4.1.网络模型 GoogLeNet的名字不是GoogleNet&#xff0c;而是GoogLeNet&#xff0c;这是为了致敬LeNet。GoogLeNet和AlexNet/VGGNet这类依靠加深网络结构的深度的思想不完全一样。Go…

创建Vue3项目以及引入Element-Plus

创建Vue3项目以及引入Element-Plus 前提条件&#xff1a;本地需要有node环境以及安装了npm&#xff0c;最好设置了镜像&#xff0c;这样下载包的时候会快些。 1、安装vue脚手架vue-cli3 npm install vue/cli -g2、安装后查看vue的版本 vue -V3、创建Vue项目&#xff0c;项目…

通信电子、嵌入式类面试题刷题计划01

文章目录001——什么是奈奎斯特采样定理&#xff1f;002——有源滤波器和无源滤波器的区别是什么&#xff1f;003——什么是反馈电路&#xff1f;请举出相关应用004——什么是竞争冒险现象&#xff1f;如何消除和避免此类现象005——什么是基尔霍夫定理&#xff1f;006——if e…

揣着一口袋的阳光满载而归--爱摸鱼的美工(13)

-----------作者&#xff1a;天涯小Y 揣着一口袋的阳光满载而归&#xff01; 慷懒周末 睡到自然醒&#xff0c;阳光洒在书桌上 套进宽松自在的衣服里 出门&#xff0c;去楼下坐坐 在阳光里吃午餐 在阳光里打个盹 在阳光里看猫咪上蹿下跳 在阳光里点个咖啡外卖 虚度时光&#xf…

【CANN训练营第三季】TBE算子开发

文章目录直播学习结业考核直播学习 安装准备&#xff1a;https://www.hiascend.com/document/detail/zh/mindstudio/50RC3/instg/instg_000022.html 开发参考: https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/600alpha003/operatordevelopment/opdevg/atla…

基础算法(八)——离散化

离散化 介绍 这里的离散化&#xff0c;特指整数的、保序的离散化 有些题目可能需要以数据作为下标来操作&#xff0c;但题目给出的数据的值比较大&#xff0c;但是数据个数比较小。此时就需要将数据映射到和数据个数数量级相同的区间&#xff0c;这就是离散化&#xff0c;即…

Java学习笔记——继承(上)

目录继承入门继承的好处继承的特点继承中成员变量的访问特点this和super访问成员的格式继承中成员方法的访问特点方法重写概述和应用场景方法重写的注意事项权限修饰符继承入门 继承的好处 好处&#xff1a; 提高了代码的复用性。 提高了代码的维护性。 让类与类之间产生了关系…

static关键字分别在C和C++中的作用

static用于实现多个对象之间的数据共享 隐藏使用静态成员不会破坏隐藏规则默认初始化为0 1. C语言中static的特性&#xff08;面向过程设计中&#xff09; 局部变量&#xff1a;在任意一个函数内部定义的变量&#xff08;不加static&#xff09;&#xff0c;初始值不确定&am…

11、JS笔记-内置内置对象

1.内置对象 js中对象分为三种&#xff1a; 自定义对象、内置对象、浏览器对象&#xff08;js独有&#xff09; 内置对象&#xff1a; js语言自带的对象&#xff0c;供开发者使用&#xff0c;提供一些常用或基本的功能&#xff08;属性和方法&#xff09; 2.Math对象 Math中所…

Docker核心概念总结

文章目录容器容器概念物理机,虚拟机与容器的区别Docker简介Docker介绍Docker思想Docker容器的特点使用Docker的原因容器VS虚拟机对比图容器与虚拟机总结容器与虚拟机可以共存Docker 基本概念镜像&#xff08;Image&#xff09;一个特殊的文件系统容器&#xff08;Container&…

SpringSecurity认证功能的快速上手

简介 SpringSecurity是Spring家族中的一个安全管理框架。相比于另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。一般来说中大型项目都是使用SpringSecurity来做安全框架&#xff0c;小项目用Shiro的比较多&#xff0c;因为相比于S…

LabVIEW VI服务器功能

LabVIEW VI服务器功能VI 服务器是在LabVIEW 5.0中引入的&#xff0c;它提供了允许用户动态控制前面板控件、VI和LabVIEW环境的一系列函数。使用VI服务器&#xff0c;您还可以在同一台机器或通过网络动态加载运行VI和LabVIEW。 VI服务器函数位于 函数应用程序控制 子面板上。所有…

3-1内存管理-内存管理概念

文章目录一.内存管理的基本原理和要求1.逻辑地址和物理地址2.程序的装入和链接/从写程序到程序运行/将程序和数据装入内存/将用户源程序变为可在内存中执行的程序需要经过的步骤3.程序的链接方式4.内存的装入模块在装入内存时的方式5.操作系统对内存的管理二.覆盖与交换三.连续…

C#,图像二值化(17)——全局阈值的ISODATA算法(亦称作InterMeans法)及其源程序

二值算法综述请阅读&#xff1a; C#&#xff0c;图像二值化&#xff08;01&#xff09;——二值化算法综述与二十三种算法目录https://blog.csdn.net/beijinghorn/article/details/128425225?spm1001.2014.3001.5502 支持函数请阅读&#xff1a; C#&#xff0c;图像二值化&…

Smart Finance成为火必投票竞选项目,参与火必投票获海量奖励

最近&#xff0c;Huobi推出了新一期的“投票上币”活动&#xff0c;即用户可以通过HT为候选项目投票&#xff0c;在投票截止后&#xff0c;符合条件的优质项目将直接上线Huobi。而Smart Finance成为了新一期投票上币活动的竞选项目之一&#xff0c;并备受行业关注&#xff0c;与…

android 11+后台启动FGS的while-in-use权限限制

while-in-use权限限制 为了帮助保护用户隐私&#xff0c;Android 11&#xff08;API 级别 30&#xff09;对前台服务何时可以访问设备的位置、摄像头或麦克风进行了限制。 当您的应用程序在后台运行时启动前台服务时&#xff0c;前台服务有以下限制&#xff1a; 除非用户已向您…

智能家居创意产品一智能插座

WiFi智能插座对于新手接触智能家居产品更加友好&#xff0c;不需要额外购买网关设备 很多智能小配件也给我们得生活带来极大的便捷&#xff0c;智能插座就是其中之一&#xff0c;比如外出忘记关空调&#xff0c;可以拿起手机远程关闭。 简单说就是&#xff1a;插座可以连接wi…

python深拷贝和浅拷贝

python深拷贝和浅拷贝&#xff08;一&#xff09; 定义 直接赋值&#xff1a;其实就是对象的引用。浅拷贝&#xff1a;拷贝父对象&#xff0c;不会拷贝对象的内部的子对象。深拷贝&#xff1a; copy 模块的 deepcopy 方法&#xff0c;完全拷贝了父对象及其子对象。 浅拷贝&am…

【自学C++】C++变量

C变量 C变量教程 不论是使用哪种高级程序语言编写程序&#xff0c;变量都是其程序的基本组成单位。变量相当于内存中一个数据存储空间的表示&#xff0c;通过变量名可以访问到变量的具体的值。 C 的变量&#xff08;variable&#xff09;是有明确 类型 的。编译器会检查 函数…

Linux 安装OpenSSL及解决遇到的问题

OpenSSL下载openssl安装包解压配置相应检查编译安装测试创建软链接N次测试下载openssl安装包 wget https://www.openssl.org/source/openssl-3.0.1.tar.gz执行后如果拉不下来&#xff0c;出现证书过期 需要加上 --no-check-certificate 不做检查 wget https://www.openssl.o…