Linux thermal框架介绍

RK3568温控

cat /sys/class/thermal/thermal_zone0/temp
cat /sys/class/thermal/thermal_zone1/temp
cat /sys/class/thermal/cooling_device0/cur_state
cat /sys/class/thermal/cooling_device1/cur_state
cat /sys/class/thermal/cooling_device2/cur_state

thermal_zone0:是soc的温度;

thermal_zone1: 是gpu的温度。

冷却设备有三个:

功能介绍

Linux的Thermal机制是基于Zone为单位的热管理机制,核心包括三个部分:获取区域温度的设备thermal_zone_device、区域降温的设备thermal_cooling_device、温控策略thermal_governor。thermal_governor从thermal_zone_device获取区域温度,然后根据当前温度,决定调用哪个降温设备来为该区域降温。

(1)Thermal sensor driver:SoC内部CPU和GPU的旁边通常会有用于获取它们温度的传感器,比如 tsadc(Temperature Sensor ADC)。

注:

ADC,即Analog-to-Digital Converter(模拟数字转换器)

  • ADC的作用:将连续变化的模拟信号转换离散的数字信号的器件

  • 常见的模拟信号:温度、压力、声音

  • AD转换步骤:采样、量化、编码

(2)Thermal cooling device:降温设备,比如风扇。这里有点特殊的是,CPU和GPU不仅是发热设备(即需要实施温控策略的设备),也可以是降温设备。当我们降低CPU/GPU的运行频率的时候,它们就在充当降温设备(降低产热量即是在降温)。

(3)Thermal governer:温控策略,Linux内核中的温控策略要比上面的空调控制精细得多,而且也提供了多种策略。

(4)Thermal core:组织并管理上面三个组件,并通过sysfs和用户空间交互。

归纳一下:核心为thermal_core;可以获取温度的设备抽象为thermal_zone_device,如Temp Sensor、NTC(板上的热敏电阻)等;控制温度的设备抽象为thermal_cooling_device,如风扇、CPU、DDR、GPU等;温控策略抽象为thermal_governor,如step_wise、bang_bang等。

linux thermal框架

Linux Thermal框架可以分为Thermal Core、Thermal Governor、Thermal Cooling、Thermal Driver以及Thermal Device Tree五大部分。

Thermal Core:用于和user space、Thermal Governor、Thermal Driver交互。

Thermal Governor:主要包括gov_bang_bang、gov_fair_share、gov_power_allocator、gov_step_wise、gov_user_space等,最常用的为gov_power_allocator.

Thermal Cooling:主要包括cpufreq_cooling、cpuidle_cooling、devfreq_cooling等。

thermal core

内核将采集区域温度的设备抽象为结构体struct thermal_zone_device,主要成员包括char type[]设备名称;int temperature当前温度;int last_temperature上次采集问题;struct thermal_governer *governor对应governor; int polling_delay温度采集时间间隔等等。其中struct thermal_zone_device_ops *ops是采集区域温度设备的操作抽象,包括绑定降温设备,获取设备温度等。

kernel/linux/thermal.h中定义了thernal_zone_device & thermal_zone_device_ops、thermal_governor、thermal_cooling_device & thermal_cooling_device_ops结构体。

struct thermal_zone_device {
        int id;    // 设备的唯一标识符
        char type[THERMAL_NAME_LENGTH];    // 设备名称
        struct device device;    // 设备相关联的struct device结构体
        struct thermal_attr *trip_temp_attrs;    // 温度触发器(trip)的温度属性链表
        struct thermal_attr *trip_type_attrs;    // 温度触发器的触发类型属性链表
        struct thermal_attr *trip_hyst_attrs;    // 温度触发器的滞后属性链表
        void *devdata;
        int trips;
        unsigned long trips_disabled;        /* bitmap for disabled trips */
        int passive_delay;
        int polling_delay;    // 采集温度的时间间隔
        int temperature;    // 当前采集的温度
        int last_temperature;    // 上次采集的温度
        int emul_temperature;
        int passive;
        unsigned int forced_passive;    // 强制进入被动散热模式的标志
        atomic_t need_update;
        struct thermal_zone_device_ops *ops;    // 区域温度设备的操作
        struct thermal_zone_params *tzp;    // 记录一些信息,如governor name
        struct thermal_governor *governor;    // 温控策略
        void *governor_data;    
        struct list_head thermal_instances;    // 降温设备
        struct idr idr;    // 管理热区设备实例的ID
        struct mutex lock;
        struct list_head node;    // 热区设备的链表节点
        struct delayed_work poll_queue;    // 用于轮询区域温度
};
 
struct thermal_zone_params {
        char governor_name[THERMAL_NAME_LENGTH];
 
        /*
         * a boolean to indicate if the thermal to hwmon sysfs interface
         * is required. when no_hwmon == false, a hwmon sysfs interface
         * will be created. when no_hwmon == true, nothing will be done
         */
        bool no_hwmon;
 
        int num_tbps;        /* Number of tbp entries */
        struct thermal_bind_params *tbp;
 
        /*
         * Sustainable power (heat) that this thermal zone can dissipate in
         * mW
         */
        u32 sustainable_power;
 
        /*
         * Proportional parameter of the PID controller when
         * overshooting (i.e., when temperature is below the target)
         */
        s32 k_po;
 
        /*
         * Proportional parameter of the PID controller when
         * undershooting
         */
        s32 k_pu;
 
        /* Integral parameter of the PID controller */
        s32 k_i;
 
        /* Derivative parameter of the PID controller */
        s32 k_d;
 
        /* threshold below which the error is no longer accumulated */
        s32 integral_cutoff;
 
        /*
         * @slope:        slope of a linear temperature adjustment curve.
         *                 Used by thermal zone drivers.
         */
        int slope;
        /*
         * @offset:        offset of a linear temperature adjustment curve.
         *                 Used by thermal zone drivers (default 0).
         */
        int offset;
};
 
struct thermal_zone_device_ops {
        // 绑定一个降温设备到该热区设备
        int (*bind) (struct thermal_zone_device *,
                     struct thermal_cooling_device *);
        // 解绑一个降温设备从该热区设备
        int (*unbind) (struct thermal_zone_device *,
                       struct thermal_cooling_device *);
        // 获取当前热区设备的温度
        int (*get_temp) (struct thermal_zone_device *, int *);
        // 获取当前热区设备的工作模式
        int (*get_mode) (struct thermal_zone_device *,
                         enum thermal_device_mode *);
        // 设置当前热区设备的工作模式              
        int (*set_mode) (struct thermal_zone_device *,
                enum thermal_device_mode);
        // 获取指定温度触发器的触发类型        
        int (*get_trip_type) (struct thermal_zone_device *, int,
                enum thermal_trip_type *);
        // 获取触发等级对应的温度     
        int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
        // 设置触发等级对应的温度
        int (*set_trip_temp) (struct thermal_zone_device *, int, int);
        int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
        int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
        int (*get_crit_temp) (struct thermal_zone_device *, int *);
        int (*set_emul_temp) (struct thermal_zone_device *, int);
        // 获取温度的变化趋势
        int (*get_trend) (struct thermal_zone_device *, int,
                          enum thermal_trend *);
        int (*notify) (struct thermal_zone_device *, int,
                       enum thermal_trip_type);
};
 
// 内核将温控策略抽象为结构体struct thermal_governor,
//主要成员包括:char name[THERMAL_NAME_LENGTH]策略名称;int (*throttle)()温控决策等等。
struct thermal_governor {
        char name[THERMAL_NAME_LENGTH];
        int (*bind_to_tz)(struct thermal_zone_device *tz);
        void (*unbind_from_tz)(struct thermal_zone_device *tz);
        int (*throttle)(struct thermal_zone_device *tz, int trip);
        struct list_head        governor_list;
};
// 执行温控策略的设备成为区域降温设备,
//内核抽象为结构体struct thermal_cooling_device,struct thermal_cooling_device_ops是区域降温设备的操作集合。
struct thermal_cooling_device {
        int id;                           //每个thermal_cooling_device有独立的id
        char type[THERMAL_NAME_LENGTH];   // 名称
        struct device device;
        struct device_node *np;
        void *devdata;
        const struct thermal_cooling_device_ops *ops;
        bool updated; /* true if the cooling device does not need update */
        struct mutex lock; /* protect thermal_instances list */
        struct list_head thermal_instances;
        struct list_head node;
};
 
struct thermal_cooling_device_ops {
        //获取总的状态数,相当于降温等级
        int (*get_max_state) (struct thermal_cooling_device *, unsigned long *); 
        //获取当前状态  
        int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);  
        //设置状态  
        int (*set_cur_state) (struct thermal_cooling_device *, unsigned long); 
        // 获取所请求的功率     
        int (*get_requested_power)(struct thermal_cooling_device *,
                                   struct thermal_zone_device *, u32 *);
        // 将指定状态(降温等级)转换为对应的功率                          
        int (*state2power)(struct thermal_cooling_device *,
                           struct thermal_zone_device *, unsigned long, u32 *);
        // 将指定功率转换为对应的状态(降温等级)                   
        int (*power2state)(struct thermal_cooling_device *,
                           struct thermal_zone_device *, u32, unsigned long *);
};

初始化

thermal_governor注册

以step_wise governor为例:

int thermal_gov_step_wise_register(void)
{
        // 调用thermal_core.c中的方法
        return thermal_register_governor(&thermal_gov_step_wise);
}
static int __init thermal_init(void)
{
        int result;
        // 注册所有的governors       
        result = thermal_register_governors();
        if (result)
                goto error;
 
        result = class_register(&thermal_class);
        if (result)
                goto unregister_governors;
 
        result = genetlink_init();
        if (result)
                goto unregister_class;
 
        result = of_parse_thermal_zones();
        if (result)
                goto exit_netlink;
 
        result = register_pm_notifier(&thermal_pm_nb);
        if (result)
                pr_warn("Thermal: Can not register suspend notifier, return %d\n",
                        result);
 
        return 0;
 
exit_netlink:
        genetlink_exit();
unregister_class:
        class_unregister(&thermal_class);
unregister_governors:
        thermal_unregister_governors();
error:
        idr_destroy(&thermal_tz_idr);
        idr_destroy(&thermal_cdev_idr);
        mutex_destroy(&thermal_idr_lock);
        mutex_destroy(&thermal_list_lock);
        mutex_destroy(&thermal_governor_lock);
        return result;
}
 
static int __init thermal_register_governors(void)
{
        int result;
        // 调用step_wise governor中的方法,为系统默认的gov
        result = thermal_gov_step_wise_register();
        if (result)
                return result;
 
        result = thermal_gov_fair_share_register();
        if (result)
                return result;
 
        result = thermal_gov_bang_bang_register();
        if (result)
                return result;
 
        result = thermal_gov_user_space_register();
        if (result)
                return result;
        // 注册IPA governor
        return thermal_gov_power_allocator_register();
}
 
// 将第一个注册的governor设置为系统默认governor,即step_wise governor
int thermal_register_governor(struct thermal_governor *governor)
{
        int err;
        const char *name;
        struct thermal_zone_device *pos;
 
        if (!governor)
                return -EINVAL;
 
        mutex_lock(&thermal_governor_lock);
 
        err = -EBUSY;
        if (__find_governor(governor->name) == NULL) {
                err = 0;
                //链接到thermal_governor_list
                list_add(&governor->governor_list, &thermal_governor_list);
                if (!def_governor && !strncmp(governor->name,
                        DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
                        def_governor = governor; //第一个设置为def_governor
        }
    .......
}

 thermal_zone_device注册

struct thermal_zone_device *thermal_zone_device_register(const char *type,
        int trips, int mask, void *devdata,
        struct thermal_zone_device_ops *ops,
        struct thermal_zone_params *tzp,
        int passive_delay, int polling_delay)
{
        struct thermal_zone_device *tz;
        enum thermal_trip_type trip_type;
        int trip_temp;
        int result;
        int count;
        int passive = 0;
        struct thermal_governor *governor;
        .........................................................................
    //分配内存
        tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
    .........................................................................
        //初始化idr,并获取id
        idr_init(&tz->idr);
        mutex_init(&tz->lock);
        result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
        ..........................................................................
        strlcpy(tz->type, type ? : "", sizeof(tz->type));  //设置名称
        tz->ops = ops;    //操作集合
        tz->tzp = tzp;    //参数
        tz->device.class = &thermal_class;
        tz->devdata = devdata;
        tz->trips = trips;
        tz->passive_delay = passive_delay;
        tz->polling_delay = polling_delay;   //采集时间间隔
        /* A new thermal zone needs to be updated anyway. */
        atomic_set(&tz->need_update, 1);
    ........................................................................
        //根据governor name,设置降温策略
        if (tz->tzp)
                governor = __find_governor(tz->tzp->governor_name);
        else
                governor = def_governor;
    .........
        //链接到thermal_tz_list
        mutex_lock(&thermal_list_lock);
        list_add_tail(&tz->node, &thermal_tz_list);
        mutex_unlock(&thermal_list_lock);
 
        /* 尝试绑定已注册的降温设备 */
        bind_tz(tz);
 
        thermal_zone_device_reset(tz);
        /* Update the new thermal zone and mark it as already updated. */
        if (atomic_cmpxchg(&tz->need_update, 1, 0))
                thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return tz;
    ..........
}

thermal_cooling_device注册

struct thermal_cooling_device *
thermal_cooling_device_register(char *type, void *devdata,
                                const struct thermal_cooling_device_ops *ops)
{
        return __thermal_cooling_device_register(NULL, type, devdata, ops);
}
 
static struct thermal_cooling_device *
__thermal_cooling_device_register(struct device_node *np,
                                  char *type, void *devdata,
                                  const struct thermal_cooling_device_ops *ops)
{
        struct thermal_cooling_device *cdev;
        struct thermal_zone_device *pos = NULL;
        int result;
 
        if (type && strlen(type) >= THERMAL_NAME_LENGTH)
                return ERR_PTR(-EINVAL);
 
        if (!ops || !ops->get_max_state || !ops->get_cur_state ||
            !ops->set_cur_state)
                return ERR_PTR(-EINVAL);
        // 分配内存
        cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
        if (!cdev)
                return ERR_PTR(-ENOMEM);
 
        result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
        if (result) {
                kfree(cdev);
                return ERR_PTR(result);
        }
 
        strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
        mutex_init(&cdev->lock);
        INIT_LIST_HEAD(&cdev->thermal_instances);
        // 初始化成员,将mtk的ops和devdata赋值给thermal_cooling_device 
        cdev->np = np;
        cdev->ops = ops;
        cdev->updated = false;
        cdev->device.class = &thermal_class;
        cdev->device.groups = cooling_device_attr_groups;
        cdev->devdata = devdata;
        dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
        // 注册device
        result = device_register(&cdev->device);
        if (result) {
                release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
                kfree(cdev);
                return ERR_PTR(result);
        }
 
        /* Add 'this' new cdev to the global cdev list */
        // 新的thermal_cooling_device加入到thermal_cdev_list链表
        mutex_lock(&thermal_list_lock);
        list_add(&cdev->node, &thermal_cdev_list);
        mutex_unlock(&thermal_list_lock);
 
        /* Update binding information for 'this' new cdev */
        // 尝试绑定到已注册的温度采集设备thermal_zone_device 
        bind_cdev(cdev);
 
        mutex_lock(&thermal_list_lock);
        list_for_each_entry(pos, &thermal_tz_list, node)
                if (atomic_cmpxchg(&pos->need_update, 1, 0))
                        thermal_zone_device_update(pos);
        mutex_unlock(&thermal_list_lock);
 
        return cdev;
}
 
static void bind_cdev(struct thermal_cooling_device *cdev)
{
        int i, ret;
        const struct thermal_zone_params *tzp;
        struct thermal_zone_device *pos = NULL;
 
        mutex_lock(&thermal_list_lock);
        // 遍历thermal_zone_device list,逐个绑定thermal_cooling_device 
        list_for_each_entry(pos, &thermal_tz_list, node) {
                if (!pos->tzp && !pos->ops->bind)
                        continue;
 
                if (pos->ops->bind) {
                        // 调用 thermal_zone_device中thermal_zone_device_ops成员中的bind方法
                        ret = pos->ops->bind(pos, cdev);
                        if (ret)
                                print_bind_err_msg(pos, cdev, ret);
                        continue;
                }
 
                tzp = pos->tzp;
                if (!tzp || !tzp->tbp)
                        continue;
 
                for (i = 0; i < tzp->num_tbps; i++) {
                        if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
                                continue;
                        if (tzp->tbp[i].match(pos, cdev))
                                continue;
                        tzp->tbp[i].cdev = cdev;
                        __bind(pos, tzp->tbp[i].trip_mask, cdev,
                               tzp->tbp[i].binding_limits,
                               tzp->tbp[i].weight);
                }
        }
 
        mutex_unlock(&thermal_list_lock);
}

温度采集设备与降温设备的联系

同一个温度采集设备可以对应多个降温设备,结构体struct thermal_instance用于连接温度采集设备与降温设备,成员struct thermal_zone_device *tz是对应的温度采集设备,struct thermal_cooling_device *cdev是对应的降温设备,int trip触发登记(对应一个温度),当温度采集设备采集的温度达到一定值时,调用对应trip登记的降温设备。

struct thermal_instance {
    .................................................................
        struct thermal_zone_device *tz;      //对应温度采集设备
        struct thermal_cooling_device *cdev; //对应降温设备
        int trip;                            //触发等级
        struct list_head tz_node;            //链接到温度采集设备
        struct list_head cdev_node;          //链接到降温设备
    .................................................................
};

以温度采集设备绑定降温设备为例,当温度采集设备注册时会尝试绑定所有已经注册的降温设备。以CPU为例,bind接口对应的是tscpu_bind(),从代码中可以看出如果降温设备的名称为g_bind0--g_bind9中的一个将会绑定CPU温度采集设备和降温设备。tscpu_bind()接口中也定义了各种名称降温设备对应的触发等级。

static void bind_tz(struct thermal_zone_device *tz)
{
        int i, ret;
        struct thermal_cooling_device *pos = NULL;
        const struct thermal_zone_params *tzp = tz->tzp;
 
        if (!tzp && !tz->ops->bind)
                return;
 
        mutex_lock(&thermal_list_lock);
        if (tz->ops->bind) {
            //尝试绑定所有的已经注册的降温设备
                list_for_each_entry(pos, &thermal_cdev_list, node) {
                        ret = tz->ops->bind(tz, pos);
                        if (ret)
                                print_bind_err_msg(tz, pos, ret);
                }
                goto exit;
        }
    ...........................................................
}
 
static int tscpu_bind(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev)
{
        int table_val = 0;
 
        if (!strcmp(cdev->type, g_bind0)) {
                table_val = 0;
                tscpu_config_all_tc_hw_protect(trip_temp[0], tc_mid_trip);
        } else if (!strcmp(cdev->type, g_bind1)) {
                table_val = 1;
                tc_mid_trip = trip_temp[1];
                tscpu_config_all_tc_hw_protect(trip_temp[0], tc_mid_trip);
        } else if (!strcmp(cdev->type, g_bind2)) {
                table_val = 2;
        } else if (!strcmp(cdev->type, g_bind3)) {
                table_val = 3;
        } else if (!strcmp(cdev->type, g_bind4)) {
    .....................................................
        } else {
                return 0;
        }
    //以table_val为触发等级绑定发热设备和降温设备
        if (mtk_thermal_zone_bind_cooling_device(thermal, table_val, cdev)) {
                tscpu_warn("tscpu_bind error binding cooling dev\n");
                return -EINVAL;
        }
 
        tscpu_printk("tscpu_bind binding OK, %d\n", table_val);
        return 0;
}

 温度采集设备知道了触发等级和降温温度,还需要知道触发等级对应的温度。thermal_zone_device_ops的get_trip_temp()用于查询触发等级对应的温度,以mtkcpu为例,所有降温设备的触发温度保存在数据中,触发等级就是该数组的下标。

static int tscpu_get_trip_temp
(struct thermal_zone_device *thermal, int trip, int *temp)
{
        *temp = trip_temp[trip];
        return 0;
}

 cooling device 

以cpu coolig为例:

cpufreq_state2power:根据cpu cooling state换算cpu power。

static int cpufreq_state2power(struct thermal_cooling_device *cdev,
                               struct thermal_zone_device *tz,
                               unsigned long state, u32 *power)
{
        unsigned int freq, num_cpus;
        cpumask_t cpumask;
        u32 static_power, dynamic_power;
        int ret;
        struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
 
        cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
        // 根据cpumask得到在线cpu核数
        num_cpus = cpumask_weight(&cpumask);
 
        /* None of our cpus are online, so no power */
        if (num_cpus == 0) {
                *power = 0;
                return 0;
        }
        // 根据cpu state得到当前的频率
        freq = cpufreq_device->freq_table[state];
        if (!freq)
                return -EINVAL;
        // 计算当前频率下的cpu动态功耗
        dynamic_power = cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
        // 计算当前频率下的cpu静态功耗
        ret = get_static_power(cpufreq_device, tz, freq, &static_power);
        if (ret)
                return ret;
        // 计算当前频率下的cpu总的功耗
        *power = static_power + dynamic_power;
        return 0;
}

cpufreq_power2state:根据cpu power换算cpu cooling state.

static int cpufreq_power2state(struct thermal_cooling_device *cdev,
                               struct thermal_zone_device *tz, u32 power,
                               unsigned long *state)
{
        unsigned int cpu, cur_freq, target_freq;
        int ret;
        s32 dyn_power;
        u32 last_load, normalised_power, static_power;
        struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
 
        cpu = cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
 
        /* None of our cpus are online */
        if (cpu >= nr_cpu_ids)
                return -ENODEV;
        // 计算当前cpu频率
        cur_freq = cpufreq_quick_get(cpu);
        // 计算当前频率下的静态功耗
        ret = get_static_power(cpufreq_device, tz, cur_freq, &static_power);
        if (ret)
                return ret;
        // 计算当前频率下的动态功耗
        dyn_power = power - static_power;
        dyn_power = dyn_power > 0 ? dyn_power : 0;
        last_load = cpufreq_device->last_load ?: 1;
        // 计算归一化功耗
        normalised_power = (dyn_power * 100) / last_load;
        // 根据归一化功耗计算出目标频率
        target_freq = cpu_power_to_freq(cpufreq_device, normalised_power);
        // 根据目标频率得到cpu state
        *state = cpufreq_cooling_get_level(cpu, target_freq);
        if (*state == THERMAL_CSTATE_INVALID) {
                dev_warn_ratelimited(&cdev->device,
                                     "Failed to convert %dKHz for cpu %d into a cdev state\n",
                                     target_freq, cpu);
                return -EINVAL;
        }
 
        trace_thermal_power_cpu_limit(&cpufreq_device->allowed_cpus,
                                      target_freq, *state, power);
        return 0;
}
 
static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev,
                             u32 freq)
{
        int i;
 
        for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) {
                if (freq > cpufreq_cdev->em->table[i].frequency)
                        break;
        }
        // 查表获取
        return cpufreq_cdev->em->table[i + 1].power;
}

 遍历了一下cpufrep_cdev里的em->table,这个table蕴含了freq和power的对应关系,这个table是跟芯片密切相关的,往往在出厂的时候就已经预制好了。

Linux Thermal框架-CSDN博客

模拟量转数字量原理

模拟量转数字量的过程通常涉及一下几个关键步骤:

采样:该步骤将连续的模拟信号转换为时间上离散变化的信号。

保持:该步骤存储采样结果,直到下一次采样。

量化:该步骤将采样电平转换为与之最接近的离散数字电平;

编码:该步骤将量化后的结果便是为特定的数制形式。

模拟信号转化为数字信号的过程中,会使用到模数转换器(ADC),这种转换通常需要一个参考模拟量作为转换的标准,常见的参考标准为ADC芯片最大的可转换信号大小。

ADC的分辨率用于标识模拟输入信号的位数,提高分辨率可以更准确低复现模拟信号并降低量化误差,但这也可能增加成本。例如一个8位的ADC可以将5V的模拟量分为256等分,从而得出相应的数字量。

此外,还有间接比较型的模数转换,该转换中输入模拟量不是直接参考电压比较,而是将二者变为中间的某种物理量再进行比较,然后将比较所得的结果进行数字编码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

翻页电子图书制作小技巧分享给你

当今社会&#xff0c;二维码已经成为了信息传递的重要方式之一&#xff0c;其在电子商务、广告营销、活动推广等领域广泛应用。而如何将二维码巧妙地融入电子画册中&#xff0c;制作出高端、具有吸引力的作品&#xff0c;成为了许多设计师和营销人员关注的焦点 但是很多人却不知…

ABeam×StartUp丨蓝因机器人访问ABeam旗下德硕管理咨询(深圳)新创部门,展开合作交流

近日&#xff0c;深圳蓝因机器人科技有限公司&#xff08;以下简称“蓝因机器人”&#xff09;创始人陈卜铭先生来访ABeam旗下德硕管理咨询&#xff08;深圳&#xff09;有限公司&#xff08;以下简称“ABeam-SZ”&#xff09;&#xff0c;与新创部门展开合作交流。 交流中&am…

六西格玛管理培训:我的转变与成长之旅

4月初&#xff0c;我参与了天行健咨询的六西格玛管理培训&#xff0c;这次经历不仅极大地提升了我的工作效率&#xff0c;还帮助我在工作中实现了卓越。现在&#xff0c;我想分享一些我在这次培训中的学习心得和实践经验&#xff0c;希望能对正在寻求提升绩效和卓越之路的大家有…

【无线通信】OQPSK

调制 sps 8; RolloffFactor 0.2; FilterSpanInSymbols 10;bits randi([0, 1], 224*8, 1); % 1792symbols bits*2 - 1; % 1792 re -symbols(2:2:end); % 896 im -symbols(1:2:end); % 896pFilterTx comm.RaisedCosineTransmitFilter(...Shape, Square root, ...Rollo…

MySQL主从结构搭建

说明&#xff1a;本文介绍如何搭建MySQL主从结构&#xff1b; 原理 主从复制原理如下&#xff1a; &#xff08;1&#xff09;master数据写入&#xff0c;更新binlog&#xff1b; &#xff08;2&#xff09;master创建一个dump线程向slave推送binlog&#xff1b; &#xff…

GoJudge环境部署本地调用云服务器部署go-judge判题机详细部署教程go-judge多语言支持

前言 本文基于go-judge项目搭建&#xff0c;由于go-judge官网项目GitHub - criyle/go-judge: Sandbox Server in REST / gRPC API. Based on Linux container technologies.&#xff0c;资料太少&#xff0c;而且只给了C语言的调用样例&#xff0c;无法知道其他常见语言比如&am…

Python基础06-日期和时间的操作方法

在Python中处理日期和时间是编程中常见的需求&#xff0c;无论是安排任务、记录日志还是分析数据。本文将介绍如何在Python中获取当前日期和时间、创建特定日期和时间、格式化日期和时间、解析字符串中的日期和时间、使用时间差、比较日期和时间、从日期/时间中提取组件、处理时…

uni-app开发canvas绘图画画,如何实现后退功能

在uni-app中使用canvas进行绘图时&#xff0c;实现后退功能通常意味着你需要保存用户的每一步操作&#xff0c;然后提供一个机制来撤销最近的步骤。下面是一个基本的实现思路&#xff1a; 保存绘图步骤&#xff1a; 每当用户在canvas上绘制时&#xff08;比如通过touchMove事件…

出海不出局 | 小游戏引爆高线市场,新竞争态势下的应用出海攻略

出海小游戏&#xff0c;出息了&#xff01; 根据 Sensor Tower 近期发布的“2024 年 3 月中国手游收入 TOP30”榜单&#xff0c;出海小游戏在榜单中成了亮眼的存在。 其中&#xff0c;《菇勇者传说》3 月海外收入环比增长 63%&#xff0c;斩获出海手游收入增长冠军&#xff0c…

学习经验分享【33】YOLOv5 / YOLOv7 / YOLOv8 / YOLOv9 / RTDETR 基于 Pyside6 的图形化界面

大论文可以写两章关于算法创新模型&#xff0c;最后一章可以写对前两章提出方法进行封装&#xff0c;利用PyQT5搭建YOLOv5可视化界面&#xff0c;并打包成exe程序&#xff0c;构建检测平台实现简单的应用。用来凑大论文的字数和工作量&#xff0c;是简单又快速的方法&#xff0…

《龙之谷》游戏(客户端+服务端+视频架设教程+工具),本人收集的8个版本,云盘下载

龙之谷这个游戏本人觉得挺好玩的。你们可以下载研究一下看看&#xff0c;有能力的话&#xff0c;可以提取服务端文件出来&#xff0c;做成外网&#xff0c;让大家一起玩。。。。 《龙之谷》游戏&#xff08;客户端服务端视频架设教程工具&#xff09;&#xff0c;本人收集的8个…

WEB前端-笔记(三)

目录 一、事件 1.1类型 1.2对象 1.3页面加载事件 1.4滚动事件 1.5尺寸事件 1.6捕获&冒泡事件 1.7阻止表单提交 1.8全选案例 1.9事件委托 ​编辑 1.10client&offset 1.11换取元素的位置 1.12创建节点 1.13克隆节点 1.14删除节点 1.15setTimeout 1.16s…

【后端】PyCharm的安装指引与基础配置

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、PyCharm是什么二、PyCharm安装指引安装PyCharm社区版安装PyCharm专业版 三、配置PyCharm&#xff1a;四、总结 前言 随着开发语言及人工智能工具的普及&am…

MS1000TA超声波测量模拟前端

产品简述 MS1000TA 是一款超声波测量模拟前端芯片&#xff0c;广 泛应用于汽车工业和消费类电子。该芯片具有高度 的灵活性&#xff0c;发射脉冲个数、频率、增益及信号阈值 均可配置。同时&#xff0c;接收通道参数也可以灵活配置&#xff0c; 从而适用于不同尺寸容器、不…

Java——继承与组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法 (诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段。 继承表示对象之间是is-a的关系&#xff0c;比如&#xff1a;狗是动物&#xff0c;猫是动…

ROM修改进阶教程------安卓7_____安卓13去除签名验证操作步骤解析

同类博文: 安卓玩机搞机技巧综合资源-----修改rom 制作rom 解包rom的一些问题解析【二十一】_qcn改区域锁-CSDN博客 安卓系列机型rom修改。如果你删减了系统相关的app。那么严重会导致开机系统卡米 定屏等问题。这类一般都是系统签名验证导致的。而破解签名验证一般都是修改…

计算机java项目|springboot校园一卡通

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、Python项目、前端项目、人工智能与大数据、简…

【Linux】文件系统——那就浅聊一下吧

前言 在上一篇文件描述符详解中谈论的都是打开的文件&#xff0c;但是在我们的系统中不仅有打开的文件还有许多未打开的文件&#xff0c;那么这些未打开的文件又该如何理解呢?阅读完本篇文章相信你会得到答案。 如果觉得文章内容对你有所帮助的话&#xff0c;可以给博主一键三…

请编写函数fun,该函数的功能是:移动字符串中的内容,移动的规则如下:把第1到第m个字符,平移到字符串的最后,把第m+l到最后的字符移到字符串的前部。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 请编…

PLSQL数据库

目录 什么是PLSQL数据库 PL数据库的实现方法 PL数据库的基本语法 1.作用 2.语法 3.赋值输出 4.引用 5.异常处理 6.if 判断 7.loop循环 8.while循环 9.for循环 10.游标 11.参数游标 12.索引 13.分区表 什么是PLSQL数据库 PL/SQL&#xff08;Procedure Language/…