普冉(PUYA)单片机开发笔记(7): ADC-轮询式多路采样

news2024/11/27 12:41:13

概述

应用中经常会有使用单片机进行模数转换的需求。PY32F003 具有 1 个 12 位的模拟数字转换器(ADC),今天我们一起来使用一下这个 ADC。

数据手册中对 ADC 简介如下。

SAR ADC:逐次逼近式 ADC,原理参见“参考链接:什么是SAR ADC? - 知乎”。12位采样值的最大值4095。数据手册上标明的最大可用通道数量是 8 个外部通道,但对照 PY32F003F18P 的管脚复用表,如果应用中还要使用 GPIO,LED,定时器 和 UART 的话,可使用的外部 ADC 通道数最多不超过 6 个。对比于 PY32F003F18P 的 20 脚封装和低廉的芯片价格,这样的 MCU 可以在应用中采样 6 个外部模拟量通道也是相当可观的数量了。

PY32F003 可以在不使用外部晶振的情况下完成数模转换,但其采样精度还需要验证。今天先尝试着把 ADC 的功能跑通先。

实现代码

参考在 STM32F103 上实现 ADC 的思路,在 PY32F003 上完成一下看。大致的步骤如下:

  1. 为 ADC1 指定 GPIO 管脚,并设置其复用功能
  2. 对 ADC1 进行初始化
  3. 在主循环中进行采样和打印输出

在 main.h 中增加和 ADC 相关的函数声明

/** ----------------------------------------------------------------------------
* @name   : void ADC_Init(void)
* @brief  : ADC 初始化
* @param  : [in] None
* @retval : [out] void
* @remark :
*** ----------------------------------------------------------------------------
*/
void ADC_Init(void);

/** ----------------------------------------------------------------------------
* @name   : HAL_StatusTypeDef ADC_Sample(char * sampleResult)
* @brief  : 获取 ADC 的采样结果,结果存放在 sampleResult 字符串中
* @param  : [in] None
* @retval : [out] HAL_HandleTypeDef. 操作成功返回 HAL_OK, 错误返回错误码。
* @remark : sampleResult 是格式化的字符串,需要解析
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef ADC_Sample(char * sampleResult);

在 app_adc.c 文件中实现函数功能

在 Application/User 组增加 app_adc.c 文件,完整代码如下。

/**
 ******************************************************************************
 * @file    app_adc.c
 * @brief   Application level Analog-Digital Conveter codes.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2023 CuteModem Intelligence.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
 
#include "main.h"

ADC_HandleTypeDef hadc;
uint32_t adc_value[3];

/********************************************************************************************************
* @name   : HAL_StatusTypeDef ADC_Sample(char * sampleResult)
* @brief  : 获取 ADC 的采样结果,结果存放在 sampleResult 字符串中
* @param  : [in] None
* @retval : [out] HAL_HandleTypeDef. 操作成功返回 HAL_OK, 错误返回错误码。
* @remark : sampleResult 是格式化的字符串,需要解析
********************************************************************************************************/
HAL_StatusTypeDef ADC_Sample(char * sampleResult)
{
    uint8_t i=0;
    
    if(HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK) 
        return HAL_ERROR;
    
    HAL_ADC_Start(&hadc);                       //开始采样
    for (i = 0; i < 3; i++)
    {
      HAL_ADC_PollForConversion(&hadc, 10000);  //等待ADC转换
      adc_value[i] = HAL_ADC_GetValue(&hadc);   //获取AD值
    }
    
#if(1)
    // excel format
    sprintf(sampleResult, 
        "%d,%d,%d",
        (uint16_t)adc_value[0],
        (uint16_t)adc_value[1],
        (uint16_t)adc_value[2]);
#else
    // JSON format
    sprintf(sampleResult, 
        "[{\"C\":0,\"D\":%d}"
        ",{\"C\":1,\"D\":%d}"
        ",{\"C\":5,\"D\":%d}"
        "]",
        (uint16_t)adc_value[0],
        (uint16_t)adc_value[1],
        (uint16_t)adc_value[2]);
#endif    
    
    HAL_ADC_Stop(&hadc); // 停止采样
    
    return HAL_OK;
}

void ADC_Init(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};
    
    __HAL_RCC_ADC_FORCE_RESET();
    __HAL_RCC_ADC_RELEASE_RESET();
    __HAL_RCC_ADC_CLK_ENABLE();

    hadc.Instance = ADC1;
    if (HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK)                 //AD校准
        Error_Handler();

    /* Configure global features of the ADC1  */
    hadc.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV1;       //ADC_CLOCK_SYNC_PCLK_DIV2/4,分频系数
    hadc.Init.Resolution            = ADC_RESOLUTION_12B;             //设置采样位数
    hadc.Init.DataAlign             = ADC_DATAALIGN_RIGHT;            //右对齐
    hadc.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;     //扫描方向设置
    hadc.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;            //ADC_EOC_SINGLE_CONV:单次采样 ; ADC_EOC_SEQ_CONV:序列采样
    hadc.Init.LowPowerAutoWait      = ENABLE;                         //ENABLE:读取ADC值后,开始下一次转换; DISABLE:直接转换
    hadc.Init.ContinuousConvMode    = DISABLE;                        //ENABLE:连续模式, DISABLE:单次模式
    hadc.Init.DiscontinuousConvMode = DISABLE;                        //非连续转换模式设置
    hadc.Init.ExternalTrigConv      = ADC_SOFTWARE_START;             //触发模式设置
    hadc.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;  //外部触发沿设置
    hadc.Init.DMAContinuousRequests = DISABLE;                        //DMA连续模式设置
    hadc.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;       //ADC_OVR_DATA_OVERWRITTEN:过载时覆盖,ADC_OVR_DATA_PRESERVED:保留旧值
    if (HAL_ADC_Init(&hadc) != HAL_OK) Error_Handler();               //初始化ADC

    /* Configure selected ADC channels  */
    sConfig.Channel      = ADC_CHANNEL_0;                             
    sConfig.Rank         = ADC_RANK_CHANNEL_NUMBER;                   
    sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;                 
    if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)             
        Error_Handler();

    sConfig.Channel = ADC_CHANNEL_1;                                  
    sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;                           
    sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;                 
    if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)             
        Error_Handler();

    sConfig.Channel = ADC_CHANNEL_4;                                  
    sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;                           
    sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;                 
    if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)             
        Error_Handler();
}

在 app_adc.c 中定义了业务所需的变量,功能函数也在一个 .c 文件中全部实现。这样做是参考了面向对象的编程模式,遵循代码/变量和功能解耦的原则,ADC 所需的全局变量都在 app_adc.c 中定义,main.c 中就不用再引用 ADC 相关的变量,也不用关心实现的细节了。唯一的接口就是 ADC_Sample() 函数的 sampleResult,sampleResult 定义为一个字符串具有很好的通用性,并隐藏了实现的细节。这里例子中被注释掉的 JSON 串返回结果的代码,在实际应用中,在上一层的业务逻辑处理是很方便的。当然 MCU 编程,一般不会采用 JSON 这种富文本的格式,这里只作为一种示例。

ADC_Sample() 函数中每次采样之前都对 ADC 进行了校准,校准完成后开始采样,采样完毕后停止 ADC。

在 py32f0xx_hal_msp.c 文件中指定 GPIO 及其复用功能

/**
 * -----------------------------------------------------------------------
 * @name   : void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
 * @brief  : 初始化 ADC 相关 MSP
 * @param  : [in] *hadc, ADC handler pointer
 * @retval : void
 * @remark :
 * -----------------------------------------------------------------------
*/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    /*
      =============
      PA0/1/4初始化
      =============
    */
    if (hadc->Instance == ADC1)
    {
        __HAL_RCC_ADC_CLK_ENABLE();   /* Peripheral clock enable */
        __HAL_RCC_GPIOA_CLK_ENABLE(); 
        
        /*
        ADC GPIO Configuration
        PA0     ------> ADC_IN0
        PA1     ------> ADC_IN1
        PA4     ------> ADC_IN5
        */
        GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4; // 指定 PA0/1/4
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;                    // 设置为模拟端口
        GPIO_InitStruct.Pull = GPIO_PULLDOWN;                       // 下拉:无输入时采样值接近零
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);                     // 执行初始化
    }
}

按照厂家例程的文件组织,所有的 HAL_xxx_MspInit() 集中在 py32_f0xx_hal_msp.c 文件中,由于在 ADC_Init() 函数中调用了 HAL_ADC_Init() 函数,要调用 HAL_ADC_MspInit(),这个函数在 HAL 库中的原型是 weak 类型的,并且是一个空函数,因此需要在实用中重写。

当然,把 HAL_ADC_MspInit() 函数在 app_adc.c 文件中实现也是可以的。

修改 DEBUG 口的管脚映射

PY32F003 ADC1 的通道 0/1/5 复用了 PA0/1/4,之前的实验中,PA1/0 被用作了 DEBUG 口 UART2,和 ADC1 的通道是冲突的,所以需要把 DEBUG 口对应的管脚挪走。查了数据手册,AF4 组的 PA2/3 可以用作 UART2,修改 UART_Config() 如下。

除了修改管脚映射以外,中断优先级等的不做修改。

HAL_StatusTypeDef USART_Config(void)
{
    // Using PA2/PA3 (TX/RX)
    HAL_StatusTypeDef conf_res = HAL_OK;
    GPIO_InitTypeDef GPIO_InitStruct;
    gUartInited = 0;    
    
    //====================
    // USART2初始化
    //====================
    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    UartHandle.Instance = USART2;
    UartHandle.Init.BaudRate = 115200;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits = UART_STOPBITS_1;
    UartHandle.Init.Parity = UART_PARITY_NONE;
    UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode = UART_MODE_TX_RX;

    conf_res = HAL_UART_Init(&UartHandle);
    if(conf_res != HAL_OK) return conf_res;
    
    /**USART2 GPIO Configuration
     PA2     ------> USART2_TX
     PA3     ------> USART2_RX
     */
    GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_USART2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 3);    // 使能NVIC
    HAL_NVIC_EnableIRQ(USART2_IRQn);            // 使能USART2中断
    
    gUartInited = 1;
    return conf_res;
}

在 main.c 的主循环中采样

int main(void)
{
    HAL_Init();             // systick初始化
    SystemClock_Config();   // 配置系统时钟
    GPIO_Config();
    
    if(USART_Config() != HAL_OK) Error_Handler();         
    printf("[SYS_INIT] Debug port initilaized.\r\n");
    
    ADC_Init();
    printf("[SYS_INIT] ADC initilaized.\r\n");
    
    printf("\r\n+---------------------------------------+"
           "\r\n|        PY32F003 MCU is ready.         |"
           "\r\n+---------------------------------------+"
           "\r\n         10 digits sent to you!          "
           "\r\n+---------------------------------------+"
           "\r\n");
           
    if (DBG_UART_Start() != HAL_OK) Error_Handler();

    char sres[64]={0};
    uint8_t sIndex = 0;
    while (1)
    { 
        BSP_LED_Toggle(LED3);
        
        if(sIndex % 2 == 0)
        {
            if(ADC_Sample(sres) == HAL_OK)
            {
                printf("%s\r\n", sres);
            }
            else
            {
                printf("Sample error.\r\n");
            }
        }
        
        sIndex ++;
    }
    
    HAL_Delay(500);
}

代码中,主循环每 0.5s 翻转一次 LED,每 1s 采样一次。

实验结果

初次跑通

按照上述步骤编写好代码,编译烧录,在 XCOM 上得到的结果如图。初次运行,PA0/1/4 出于悬空状态,得到的采样值是随机的。

注意到在 HAL_ADC_MspInt() 函数中,将 PA0/1/4 这三个管脚的 PULL 属性都设置成了 PULLDOWN,本想着即使悬空的话仍可得到接近 0 的采样值。但实验结果中,PA0 的悬空状态采样值仍在 1480 多的值,折合成电压为

  1480/4096*3.3 = 1.192V

这个值挺高的,而 PA1/4 管脚换算得到的电压值分别为 0.661/0.524V,这两个值也不低。这说明 PY32F003 的内部下拉应该是“弱下拉”——或许,在 HAL_ADC_Init() 函数中又对这几个管脚做了什么配置?这个问题留着以后关注。

基于此,在实际项目中用到 PY32F003 进行 ADC 时,在信号管脚接入前,要使用一个(或一组)运放做一下电压跟随才好。

采样时长

在 HAL_ADC_ConfigChannel() 中,设置了采样周期均为 71.5,加上转换的耗费 12.5 周期,合计84 个时钟周期,计算得到采样时间为 3.5us 一次,也挺快了了。

对 GND 和 VCC 的采样值

将 PA0 接地,然后再观察其采样值,得到了全“0”的采样结果。

将 PA0 接 3.3V 管脚,50次采样得到的平均值是 4087.22,换算得到 3.293V,也还好。

PULLUP 还是 PULLDOWN,还是 NOPULL?

把 PA0/1/4 都设置为内部上拉/下拉/无上下拉状态时,PA0 接地,测得 PA1/4 的采用值分别是:

PULLUP:2.159/2.191V,PULLDOWN:0.242/0.322V,NOPULL:1.990/3.061V

PA1和PA4的特性略有不同。

PA0 得到的采样值均为0,这说明管脚的 PULL 被初始化的状态不会对采样的测量值产生影响。

在 PA0 接 VCC 时,不论其 PULL 属性如何,对采样值也没有影响。

总结

  • 根据厂家例程移植,跑通 ADC 的轮询式采样是比较简单的。如果熟悉对 STM32 的 ADC 配置,可以照搬 STM32 的步骤。
  • 分配 ADC1 的采样通道时,要把开发板默认的 UART2 管脚和 ADC1 的采样通道管脚错开。
  • 当某一管脚配置为模拟信号时,其管脚的 PULL 属性对测量结果无影响。
  • 实用中,ADC1 的采样输入管脚最好使用运放做一个电压跟随器。
  • 0~VCC 中间值的采样精度如何,尚未验证,留待后续实验完成。

后续还会继续尝试使用 DMA 的 ADC,敬请期待。

谬误之处,恳请指正。

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

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

相关文章

六何分析法分析uniApp

一、什么是 uniApp&#xff08;What&#xff09; uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布iOS、Android、H5、以及各种小程序( 微信/支付宝/百度/头条/00/钉钉/淘宝)、快应用等多个平台。uni-app 在手&#xff0c;…

AI隆重软件,AI原创文章隆重软件

随着信息量的急剧增加&#xff0c;许多写作者、网站管理员和内容创作者们纷纷感受到了文章降重的压力。原始文本的降重&#xff0c;需要保留关键信息的同时避免重复&#xff0c;这是一项既繁琐又耗时的任务。 改写软件的批量降重功能 147SEO改写软件在降重领域的卓越表现主要体…

小目标检测模型设计的一点思考

1. 小目标的特性 目标之间的交叠概率比较低&#xff0c;即使有交叠&#xff0c;其IoU多数情况下也是比较小的 AI-TOD Tiny Person Dateset 小目标自身的纹理显著度有强弱区别&#xff0c;但是总体来说纹理特征都较弱&#xff0c;很多时候需要借助一定的图像上下文来帮助确认 …

自动驾驶学习笔记(十七)——视觉感知

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo 社区开发者圆桌会》免费报名—>传送门 文章目录 前言 分类 目标检测 语义分割 实例分割 …

Angular 从零开始,快速上手

Angular 从零开始&#xff0c;快速上手 一、AngularJS 简介1.1 AngularJS 的背景1.2 AngularJS 的简介1.3 AngularJS 概念概述1.4 AngularJS 特性1.5 AngularJS 和 JQuery 比较 二、安装 AngularJS2.1 方式一&#xff1a;使用在线 cdn2.2 方式二&#xff1a;使用依赖管理工具 n…

三. LiDAR和Camera融合的BEV感知算法-BEV-SAN

目录 前言0. 简述1. 算法动机&开创性思路2. 主体结构3. 损失函数4. 性能对比总结下载链接参考 前言 自动驾驶之心推出的《国内首个BVE感知全栈系列学习教程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考。 本次课程我们来学习下课程第三章——LiDAR和Ca…

弧形导轨的结构特点

弧形导轨&#xff0c;顾名思义就是滑座沿着导轨做弧线运动&#xff0c;在工业自动化的许多运用中&#xff0c;直线运动很遍及&#xff0c;但是有些运用&#xff0c;需求弧线运动&#xff0c;或者两个相交或平行的直线运动&#xff0c;需求通过弧线运动衔接起来&#xff0c;那么…

qt可以详细写的项目或技术

1.QT 图形视图框架 2.QT 模型视图结构 3.QT列表显示大量信息 4.QT播放器 5.QT 编解码 6.QT opencv

高级Linux监控堡垒机学习指南

高级Linux监控堡垒机学习指南 在现代复杂的网络环境中&#xff0c;安全性和监控是系统管理的核心关注点。Linux监控堡垒机作为一种安全管理工具&#xff0c;不仅可以追踪系统活动&#xff0c;还能提供对服务器和网络资源的高级监控。本文将深入探讨高级Linux监控堡垒机的学习内…

windows端口被占用怎么办 怎么关闭那个占用的端口

目录 这是出现的情况怎么解决了1.请打开这玩意2.输入下面---查询 先关端口的信息根据id获得服务 上图的8888 对应的ip 上图就是134243.杀死进程134244.重启服务 这是出现的情况 怎么解决了 1.请打开这玩意 2.输入下面—查询 先关端口的信息 netstat -ano过滤信息查询想要的端…

用 Python 自动创建 Markdown 表格

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Markdown表格是文档中整理和展示数据的重要方式之一。然而&#xff0c;手动编写大型表格可能会费时且容易出错。本文将介绍如何使用Python自动创建Markdown表格&#xff0c;通过示例代码详细展示各种场景下的创建…

Python列表的排序方法:从基础到高级

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python列表的排序方法&#xff1a;从基础到高级&#xff0c;全文3400字&#xff0c;阅读大约10分钟。 在Python中&#xff0c;列表是一种常用的数据结构&#xff0c;而对列表…

排序算法之六:快速排序(非递归)

快速排序是非常适合使用递归的&#xff0c;但是同时我们也要掌握非递归的算法 因为操作系统的栈空间很小&#xff0c;如果递归的深度太深&#xff0c;容易造成栈溢出 递归改非递归一般有两种改法&#xff1a; 改循环借助栈&#xff08;数据结构&#xff09; 图示算法 不是…

P1317 低洼地题解

题目 一组数&#xff0c;分别表示地平线的高度变化。高度值为整数&#xff0c;相邻高度用直线连接。找出并统计有多少个可能积水的低洼地&#xff1f; 如图&#xff1a;地高变化为 [0,1,0,2,1,2,0,0,2,0]。 输入输出格式 输入格式 两行&#xff0c;第一行n, 表示有n个数。第…

【Unity动画】什么是任意状态(Any state)

&#xff08;Any state&#xff09;可以从某个状态A直接切换到另一个状态 B\C\D\E\F 比如A到C的过渡&#xff0c;直接设置从Any state 到C的过渡线触发参数即可。而不需要让A到C直接在连接&#xff0c;同样&#xff0c;B到C之间也无需直接链接。 这样设计是在每一个动画之间都…

5_CSS三大特性盒子模型

第5章-盒子模型【比屋教育】 本课目标&#xff08;Objective&#xff09; 掌握CSS三大特性理解什么是盒子模型掌握内边距padding的用法掌握外边距margin的用法 1. CSS的层叠&#xff0c;继承&#xff0c;优先级 1.1 CSS层叠 层叠&#xff1a;是指多个CSS样式叠加到同一个元…

Java 类加载器和双亲委派机制原理剖析

目录 一、类加载器(ClassLoader)二、四种类加载器之间的关系三、双亲委派机制3.1、为什么要设计双亲委派机制3.2、类加载器loadClass(String name) 源码解析3.3、打破双亲委派机制 一、类加载器(ClassLoader) JVM的类加载机制是按需加载的模式运行的&#xff0c;也就是代表着&a…

万户 ezOFFICE convertFile 文件读取漏洞复现

0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息维护、客户服务、互动交流和日…

Linux环境下socket本地通信

最近项目有用到了socket本地通信&#xff0c;故复习一下。之前都是基于本地虚拟机的ip地址通信的&#xff0c;现在项目&#xff0c;Linux单板上面有2个进程需要通信&#xff0c;故用到了本地socket通信&#xff0c;主要其实就是用了sockfd,文件描述符&#xff0c;也叫句柄。 服…

MySQL概述-安装与启动

数据库相关概念 MySQL数据库 下载地址 MySQL :: Download MySQL Installer (Archived Versions) 启动方法 启动密令&#xff1a;net start mysql80 停止密令&#xff1a;net stop mysql80 客户端链接方法 注意用系统自带的命令行工具执行指令需要设置环境在高级系统设置中…