普冉(PUYA)单片机开发笔记(8): ADC-DMA多路采样

概述

上一个实验完成了基于轮询的多路 ADC 采样,现在尝试跑一下使用 DMA 的 ADC 多路采样。厂家例程中有使用 DMA 完成单路采样的,根据这个例程提供的模板,再加上在 STM32 开发同样功能的基础,摸索着尝试。

经过多次修改和测试,最终完成了在开发板上使用 DMA 的 三路 ADC 采样的功能,和各位码神分享。

实现代码

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

利用 Keil 实现一个功能,无怪乎就是 xxx_init 进行初始化,然后在主循环中用 xxx_start, xxx_stop, xxx_action 这一类的函数实现预定功能。老套路,在 main.h 中增加

  • ADC_DMA_Init(void); // 初始化利用 DMA 的 ADC
  • ADC_DMA_Start(void); // 开始采样和搬运
  • ADC_DM_Sample(char *sampleResult); // 获取采样的当前值

的声明,至于如何实现的,main.h 中不用考虑,也不用做更多的 #define 和全局变量定义,代码解耦不是么,把这些函数用得到的(即使是全局变量)都放到各自的 .c 文件中去就好了。

/** ----------------------------------------------------------------------------
* @name   : void ADC_DMA_Init(void)
* @brief  : 使用 DMA 进行 ADC 的初始化
* @param  : [in] None
* @retval : [out] void
* @remark :
*** ----------------------------------------------------------------------------
*/
void ADC_DMA_Init(void);

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

/** ----------------------------------------------------------------------------
* @name   : HAL_StatusTypeDef ADC_DMA_Start(void);
* @brief  : 启动 ADC DMA 采样
* @param  : [in] None
* @retval : [out] HAL_HandleTypeDef. 操作成功返回 HAL_OK, 错误返回错误码。
* @remark : 
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef ADC_DMA_Start(void);

修改 py32_f0xx_hal_msp.c

重写(类似于 C++ 的 override)HAL_ADC_MspInit 函数。如果要用到定时器,中断,定时器,比较器等,都要在这个文件中加入(或者修改 HAL_xxx_MspInit 函数)。

/**
 * -----------------------------------------------------------------------
 * @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)
{
    if (hadc->Instance != ADC1) return;

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_SYSCFG_CLK_ENABLE(); // SYSCFG 时钟使能
    __HAL_RCC_DMA_CLK_ENABLE();    // DMA 时钟使能
    __HAL_RCC_GPIOA_CLK_ENABLE();  // GPIOA 时钟使能 
    __HAL_RCC_ADC_CLK_ENABLE();    // ADC 时钟使能

    /* ----------------
       ADC通道配置PA0/1/4
       ---------------- */
    GPIO_InitStruct.Pin  = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_SYSCFG_DMA_Req(0);                                      // DMA1_MAP 选择为 ADC
    /* ------------
        DMA配置
       ------------ */
                    HdmaCh1.Instance = DMA1_Channel1;          // 选择DMA通道1
              HdmaCh1.Init.Direction = DMA_PERIPH_TO_MEMORY;   // 方向为从外设到存储器
              HdmaCh1.Init.PeriphInc = DMA_PINC_DISABLE;       // 禁止外设地址增量
                 HdmaCh1.Init.MemInc = DMA_MINC_ENABLE;        // 使能存储器地址增量
    HdmaCh1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;    // 外设数据宽度为16位
       HdmaCh1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;    // 存储器数据宽度位16位
                   HdmaCh1.Init.Mode = DMA_CIRCULAR;           // 循环模式
               HdmaCh1.Init.Priority = DMA_PRIORITY_MEDIUM;    // 通道优先级为很高
               
    HAL_DMA_DeInit(&HdmaCh1);                                  // DMA 清除初始化
    HAL_DMA_Init(&HdmaCh1);                                    // 初始化 DMA 通道1
    __HAL_LINKDMA(hadc, DMA_Handle, HdmaCh1);                  // 连接 DMA 句柄
}

以上代码中,

  1. 首先使能相关的时钟,DMA_CLK 不说了;用到了 ADC,那肯定要使能 ADC_CLK 的啦;还要用到模拟信号的输入管脚,ADC1 的 CH0/1/5 分别是 PA0/1/4,那么 GPIOA_CLK 也要使能。
  2. 然后把 PA0/1/4 初始化成模拟输入管脚,内部拉低。拉高和拉低对采样结果的影响还是蛮大的,“踩坑记”中有说明。
  3. 使用 HAL_SYSCFG_DMA_Req(0) 把 DMA1 映射成“为 ADC 做数据搬运”。这是厂家例程里的一条语句,但是这句话什么意思,厂家 HAL 库中没找到完整的说明,暂且不动它。
  4. 再下一步就是对 DMA1 的通道1进行初始化,代码先放到这里,“踩坑记”中对这些参数有说明的。
  5. 最后一步是使用 __HAL_LINKDMA() 将 ADC1 和 DMA1 的通道1关联起来。这句话挺特别的,挺底层的(两个下划线开始的函数呢)。

修改 py32_f0xx_hal_it.c

在 DMA1_Channel1_IRQHandler 中直接调用 HAL_DMA_IRQHandler;在 ADC_COMP_IRQHandler 中直接调用 HAL_ADC_IRQHandler。为什么要这么做呢?这里简短节说:顺着 HAL_Init,HAL_ADC_Init 和 HAL_ADC_Start_DMA 几个函数一路嵌套地 F12 下去,就能定位到上面的这两个函数。

void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(hadcdma.DMA_Handle);
}

void DMA1_Channel2_3_IRQHandler(void)
{
}

void ADC_COMP_IRQHandler(void)
{
    HAL_ADC_IRQHandler(&hadcdma);
}

在 app_adc.c 中实现相关函数

  1. 首先在文件的开头部分,增加几个全局变量
  2. 然后实现 ADC_DMA_Init() 函数、ADC_DMA_Start() 函数和 ADC_DMA_Sample() 函数。

对 ADCDMA handler 的参数说明,在“踩坑记”里。

/**
 * Variables for ADC loop sample with DMA
*/
#define DMA_SAMP_COUNT 3
ADC_HandleTypeDef hadcdma;
uint32_t adc_dma_value[DMA_SAMP_COUNT] = {0};
void ADC_DMA_Init(void)
{
    ADC_ChannelConfTypeDef adConfig = {0};
    
    __HAL_RCC_ADC_FORCE_RESET();
    __HAL_RCC_ADC_RELEASE_RESET();
    __HAL_RCC_ADC_CLK_ENABLE();    //ADC时钟使能

    hadcdma.Instance = ADC1;
    if (HAL_ADCEx_Calibration_Start(&hadcdma) != HAL_OK)                // ADC 校准
        Error_Handler();
    
    hadcdma.Instance                   = ADC1;
    hadcdma.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV1;      // 模拟ADC时钟源为PCLK,无分频
    hadcdma.Init.Resolution            = ADC_RESOLUTION_12B;            // 转换分辨率12bit
    hadcdma.Init.DataAlign             = ADC_DATAALIGN_RIGHT;           // 右对齐
    hadcdma.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;    // 扫描序列方向:0-12
    hadcdma.Init.LowPowerAutoWait      = ENABLE;                        // 等待转换模式开启
    hadcdma.Init.ContinuousConvMode    = ENABLE;                        // 连续转换
    hadcdma.Init.DiscontinuousConvMode = DISABLE;                       // 使能连续模式
    hadcdma.Init.ExternalTrigConv      = ADC_SOFTWARE_START;            // ADC 无外部事件
    hadcdma.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE; // 无硬件驱动检测
    hadcdma.Init.DMAContinuousRequests = ENABLE;                        // DMA 连续传输
    hadcdma.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;      // 当过载发生时,ADC_DR 新值覆盖
    hadcdma.Init.SamplingTimeCommon    = ADC_SAMPLETIME_239CYCLES_5;    // 采样时间
    
    if (HAL_ADC_Init(&hadcdma) != HAL_OK)                               // ADC初始化
        Error_Handler();
    
    adConfig.Channel = ADC_CHANNEL_0; /* 配置 ADC 通道0 */
    adConfig.Rank    = ADC_RANK_CHANNEL_NUMBER;
    if (HAL_ADC_ConfigChannel(&hadcdma, &adConfig) != HAL_OK) Error_Handler();
    
    adConfig.Channel = ADC_CHANNEL_1; /* 配置 ADC 通道1 */
    adConfig.Rank    = ADC_RANK_CHANNEL_NUMBER;
    if (HAL_ADC_ConfigChannel(&hadcdma, &adConfig) != HAL_OK) Error_Handler();

    adConfig.Channel = ADC_CHANNEL_4; /* 配置 ADC 通道4 */
    adConfig.Rank    = ADC_RANK_CHANNEL_NUMBER;
    if (HAL_ADC_ConfigChannel(&hadcdma, &adConfig) != HAL_OK) Error_Handler();
    
    ADC_DMA_Start();
}

HAL_StatusTypeDef ADC_DMA_Start(void)
{
    for( uint8_t i = 0; i < DMA_SAMP_COUNT; i++) 
        adc_dma_value[i] = 0;
    
    if (HAL_ADC_Start_DMA(&hadcdma, &(adc_dma_value[0]), DMA_SAMP_COUNT) != HAL_OK)
        Error_Handler();
    
    return HAL_OK;
}

HAL_StatusTypeDef ADC_DMA_Sample(char* sampleResult)
{
    uint8_t i = 0;
    char res_part[20]={0};
    if(__HAL_DMA_GET_FLAG(DMA1->ISR, DMA_ISR_TCIF1))
    {
        sprintf(sampleResult, "[");
        for(i = 0; i< DMA_SAMP_COUNT; i++)
        {
            if(i > 0) strcat(sampleResult, ",");
            sprintf(res_part, "{\"C\":%d,\"D\":%4u}", i, adc_dma_value[i]);
            strcat(sampleResult, res_part);
        }
        strcat(sampleResult, "]");
        
        __HAL_DMA_CLEAR_FLAG(DMA1->ISR, DMA_IFCR_CTCIF1); 
    }
    
    return HAL_OK;
}

在 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_DMA_Init();
    printf("[SYS_INIT] ADC DMA 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_DMA_Sample(sres) == HAL_OK)
            {
                printf("%s\r\n", sres);
            }
        }
        
        sIndex ++;
        
        HAL_Delay(500);
    }
}

main() 函数中,只需要准备好一个足够长的字符串(也不能太长,要知道 RAM 总共就 8K字节)容纳采样结果就行了。本次实验中使用 JSON 串表示采样结果,64个字节了。MCU 编程中往往需要根据预期结果仔细地分配形参的尺寸,只要考虑完整,够用就行。本例中组装的 JSON 串,最大长度是 52 个字符,那么分配 53 个字节就行了(别忘记了末尾的那个 '\0')。

运行结果

用杜邦线吧 PA0 和 PA1 都接 3.3V,PA4 悬空。编译烧录程序,在 XCOM 上观察打印的信息,截图如下:

运行结果符合设计预期,但明显有一个缺点就是上一轮运行“剩下”的部分字符串会遗留下来,采样结果字符串的第一行是无法用 JSON 解析的。(我先放自己一马 ;)

根据运行结果,得到 PA0/1 采样 VCC(3.3V)的电压平均值为

                 4085 * 3.3 /4096 = 3.291V

PA4 悬空,实测电压平均值为

                17 * 3.3 / 4096 = 0.014V = 14 mV

这个精度对于毫伏级测量还是不够的,实用中还是要加电压跟随器才好。

踩坑记

厂家例程完成单路采样,不少参数都需要修改,要不的话,结果不是错误,就是颠倒,甚至莫名其妙。

  • GPIO_InitStructure.Pull 属性设置为 PULLDOWN 更符合实用场景,悬空时为一个接近于 0 的采样值,这一点在上一个实验中也说明了。
  • 多路采样时,adc_dma_value(在 app_adc.c 中定义的全局变量) 要设置为和采样通道数相同维数的数组,uint32_t 类型的。
  • HdmaCh1.Init.MemInc (在 HAL_ADC_MspInit 函数中)设置成了 DMA_MINC_DISABLE,在多路采样时,要改为 DMA_MINC_ENABLE。如果不 ENABLE,第二次的采样值将总会把 adc_dma_value[0] 的值覆盖掉,而 adc_dma_value[1] 和 adc_dma_value[2] 中没有值。
  • 和 adc_dma_value 的字长对应,HdmaCh1.Init.PeriphDataAlignment(在 HAL_ADC_MspInit 函数中)要设置成 DMA_PDATAALIGN_WORD,即外设数据宽度为32位,酱紫的设置在采样程序中不需要对 adc_dma_value 进行 16 位的分割。同样地,HdmaCh1.Init.MemDataAlignment 也要设置成 DMA_MDATAALIGN_WORD。厂家例程是 HALFWORD,如果用在多路采样当中,每一个 adc_dma_value[x] 中高16位和地16位分别存放一个通道的采样值,需要用“位与”操作和 16 移位操作把这两个数取出来。
  • 在 ADC_DMA_Init 函数中完成的 hadcdma 的初始化参数中,以下设置是多路采样可以正确运行的唯一组合:
hadcdma.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;
hadcdma.Init.LowPowerAutoWait      = ENABLE;
hadcdma.Init.ContinuousConvMode    = ENABLE;
hadcdma.Init.DiscontinuousConvMode = DISABLE;
  1. hadcdma.Init.ScanConvMode 设置成 FORWARD 才能保证采样的数据搬运顺序和 adc_dma_value 数组的下标顺序相同,要是设置成 BACKWORD,adc_dma_value 的存放顺序就是 2,1,0了。
  2. ContinuousConvMode 必须 ENABLE,DiscontinuousCovMode 必须 DISABLE。
  • 必须按照本文的描述修改 py32f0xx_hal_it.c,不改不行,少改也不行,放错了地方更不行。如果不照文件写,程序不会卡死,但无法得到正确的结果——我在这个地方白白耗费了 N 多的时间。

改好以后,main.c 的主循环只管在需要的时候,判断是否转换完成。在转换完成时直接读取 adc_mda_value[i] (i=0,1,2)就可以了,这就是利用 DMA 的好处。本例每半秒钟读取一次 DMA 的更新结果,而采样时间只有

                Tsample = (239.5+12.5)/24 = 10.5 us

DMA 搬运三个 uint32_t 的数更是不在话下。运行了近半个小时,没有遇到读不出来的情况。回头再试试把采样前的 if 改为 while 看看会等待多长时间。

初次试用,谬误之处,欢迎评论,指正。

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

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

相关文章

《安富莱嵌入式周报》第328期:自主微型机器人,火星探测器发射前失误故障分析,微软推出12周24期免费AI课程,炫酷3D LED点阵设计,MDK5.39发布

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 更新一期视频教程&#xff1a; 【实战技能】 单步运行源码分析&#xff0c;一期视频整明白FreeRTOS内核源码框架和运行…

解释Spring中一个bean的注入过程

目录 1、定义Bean&#xff1a; XML配置方式&#xff1a; 2、注入方式&#xff1a; 构造器注入&#xff08;Constructor Injection&#xff09;&#xff1a; Setter方法注入&#xff08;Setter Injection&#xff09;&#xff1a; 字段注入&#xff08;Field Injection&…

【AntDB 数据库】国产分布式数据库发展趋势与难点

引言&#xff1a; 日前&#xff0c;为更好地满足亚信科技客户对于数据管理的需求&#xff0c;提高通用型数据库的产品服务能力与业务拓展能力&#xff0c;亚信科技分布式数据库AntDB发布V7.0版本产品&#xff0c;助力运营商核心系统实现全方位的自主可控与业务系统的平稳上线。…

nodejs微信小程序+python+PHP北京地铁票务APP-计算机毕业设计推荐 -安卓

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

web漏洞的攻击及抓取攻击流量

以dvwa靶场为例&#xff0c;使用wireshark抓取攻击流量&#xff0c;并定位关键字段 SQL注入 攻击 在sql注入攻击 1 and 12 union select version(),2#流量特征 在数据包中可以清晰的查看源IP和目的IP 在Wireshark选中Ethernet0网卡 点击Submit&#xff0c;进行抓包 利用…

【STM32】TIM定时器编码器

1 编码器接口简介 Encoder Interface 编码器接口 编码器接口可接收增量&#xff08;正交&#xff09;编码器的信号&#xff0c;根据编码器旋转产生的正交信号脉冲&#xff0c;自动控制CNT自增或自减&#xff0c;从而指示编码器的位置、旋转方向和旋转速度 接收正交信号&#…

达梦数据库dm8守护集群部署手册

环境说明 操作系统&#xff1a;liunx-centos7.6 服务器&#xff1a;3台虚拟机&#xff08;主备数据库各一台&#xff0c;监视器一台(可选)&#xff09; 达梦数据库版本&#xff1a;达梦V8 一、安装前准备工作 参考达梦官方文档&#xff1a;https://eco.dameng.com/documen…

记一次堆内外内存问题的排查和优化

为优化淘宝带宽成本&#xff0c;我们在网关 SDK&#xff08;Java&#xff09;统一使用 ZSTD 替代 GZIP 压缩以获取更高的压缩比&#xff0c;从而得到更小的响应包。具体实现采用官方推荐的 zstd-jni 库。zstd-jni 会调用 zstd 的 c 库。 背景 在性能压测和优化过程中&#xff0…

基于 Gin 的 HTTP 中间人代理 Demo

前面实现的代理对于 HTTPS 流量是进行盲转的&#xff0c;也就是说直接在 TCP 连接上传输 TLS 流量&#xff0c;但是我们无法查看或者修改它的内容。当然了&#xff0c;通常来说这也是不必要的。不过对于某些场景下还是有必要的&#xff0c;例如使用 Fiddler 进行抓包或者监控其…

Git 五分钟教程速度入门

Git 五分钟教程速度入门 分类 编程技术 许多人认为 Git 太混乱&#xff0c;或认为它是一种复杂的版本控制系统&#xff0c;其实不然&#xff0c;这篇文章有助于大家快速上手使用 Git。 入门 使用Git前&#xff0c;需要先建立一个仓库(repository)。您可以使用一个已经存在的…

HLS实现图像膨胀和腐蚀运算--xf_dilation和xf_erosion

一、图像膨胀和图像腐蚀概念 我们先定义&#xff0c;需要处理的图片为二值化图像A。图片的背景色为黑色&#xff0c;即像素值为0。图片的目标色为白色&#xff0c;即像素值为1。 再定义一个结构元S&#xff0c;结构元范围内所有的像素为白色&#xff0c;像素值为1。 1、图像的…

自下而上-存储全栈(TiDB/RockDB/SPDK/fuse/ceph/NVMe/ext4)存储技术专家成长路线

数字化时代的到来带来了大规模数据的产生&#xff0c;各行各业都面临着数据爆炸的挑战。 随着云计算、物联网、人工智能等新兴技术的发展&#xff0c;对存储技术的需求也越来越多样化。不同应用场景对存储的容量、性能、可靠性和成本等方面都有不同的要求。具备存储技术知识和技…

HarmonyOS应用开发-闪屏启动页

这是鸿蒙开发者网站的一个应用《溪村小镇》示例代码&#xff0c;把闪屏启动页单拿出来&#xff0c;分析一下代码。 一、先上效果图 这是应用打开时的一个启动页&#xff0c;启动页会根据三个时间段&#xff08;白天、傍晚、晚上&#xff09;来分别展示溪村小镇不同的景色。 二…

RocketMQ-RocketMQ高性能核心原理与源码剖析(下)

融汇贯通阶段 ​ 开始梳理一些比较完整&#xff0c;比较复杂的完整业务线。 8、消息持久化设计 1、RocketMQ的持久化文件结构 ​ 消息持久化也就是将内存中的消息写入到本地磁盘的过程。而磁盘IO操作通常是一个很耗性能&#xff0c;很慢的操作&#xff0c;所以&#xff0c;…

MyBatis--07--启动过程分析、SqlSession安全问题、拦截器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 谈谈MyBatis的启动过程具体的操作过程如下&#xff1a;实现测试类,并测试SqlSessionFactorySqlSession SqlSession有数据安全问题?在MyBatis中&#xff0c;SqlSess…

App备案、ios备案Bundle ID查询、公钥信息、SHA-1值

App备案、ios备案Bundle ID查询、公钥信息、SHA-1值 Bundle ID这个就不说了&#xff0c;都知道是啥&#xff0c;主要说公钥信息和SHA-1值的获取 打开钥匙串访问&#xff0c;找到当前需要备案App的dis证书&#xff0c;如下&#xff1a; #####右键点击显示简介 #####可以看…

ThinkPHP生活用品商城系统

有需要请加文章底部Q哦 可远程调试 ThinkPHP生活用品商城系统 一 介绍 此生活用品商城系统基于ThinkPHP框架开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统分为用户和管理员。(附带配套设计文档) 技术栈&#xff1a;ThinkPHPmysqlbootstrapphpstudyvscode 二 …

小米手机锁屏时间设置为永不休眠_手机不息屏_保持亮屏

环境&#xff1a;打开手机自带的锁屏时间设置发现没有 永不息屏的选项 原因&#xff1a;采用了三星OLED屏幕&#xff0c;所以根据OLED屏幕特性&#xff0c;这个是为了防止烧屏而特意设计的。非OLED机型支持设置“永不” 解决方案1&#xff1a;原生系统是支持永不锁屏的&#…

【自定义Source、Sink】Flink自定义Source、Sink对redis进行读写操作

使用ParameterTool读取配置文件 Flink读取参数的对象 Commons-cli&#xff1a; Apache提供的&#xff0c;需要引入依赖ParameterTool&#xff1a;Flink内置 ParameterTool 比 Commons-cli 使用上简便&#xff1b; ParameterTool能避免Jar包的依赖冲突 建议使用第二种 使用Par…

STL(七)(map篇)

### 这里重点学习map ### 在实际做题过程中,multimap几乎用不到### unordered_map拥有极好的平均时间复杂度和极差的最坏时间复杂度,所以他的时间复杂度是不稳定的,unordered_map一般用不到,要做一个了解 1.map map是一种关联容器,用于存储一组键值对(key-value pairs),其中每…