【GD32F470紫藤派使用手册】第八讲 ADC-规则组多通道采样实验

8.1 实验内容

通过本实验主要学习以下内容:

  • ADC的简介
  • GD32F470 ADC工作原理
  • DMA原理
  • 规则组多通道循环采样

8.2 实验原理

8.2.1 ADC原理

我们知道,自然界中有非常多的模拟信号,比如光照强度,还有其他的例如温度、声音等等,那么人们是怎么来衡量一个模拟信号的呢?

我们通常会说今天光照度达到了3万Lux(照度单位),现在测量到的体温是36.5℃,我们所处的环境是40分贝,没错,人们就是通过将这些模拟信号数字化,从而达到衡量这些模拟信号的目的。那对于MCU来说,如果要测量一个模拟量,可以通过自带的ADC(Analog-to-Digital converters)模块,即模-数转换器将模拟量转化为可以被MCU读取到的数字量。

8.2.2 GD32F470 ADC工作原理

GD32F470有3个12位逐次逼近型ADC(SAR ADC),这三个ADC可以独立工作,也可以工作在同步模式下。有最多24个外部ADC引脚可用于将连接到这些引脚的电压值转换为数字量,这些引脚号可以通过Datasheet获得。

表中ADC012_INx的意思是:该IO口可以作为通道x用于ADC0、ADC1和ADC2。如ADC012_IN0,表示PA0可以用于ADC0的通道0使用,也可以作为ADC1和ADC2的通道0使用。但要注意:不能在同一个时刻让不同的ADC去转换同一个通道,否则会有无法预料的结果

 以下总结了GD32F470 ADC的特性:

  •  高性能:

– ADC采样分辨率: 12位、 10位、 8位、或者6位分辨率;

– ADC采样率: 12位分辨率为2.6 MSPs, 10位分辨率为3.0 MSPs。分辨率越低,转换越快;

– 自校准时间: 131个ADC时钟周期;

– 可编程采样时间;

– 数据存储模式:最高有效位对齐和最低有效位对齐;

– DMA请求。

  •  模拟输入通道:

– 16个外部模拟输入通道;

– 1个内部温度传感通道(VSENSE);

– 1个内部参考电压输入通道(VREFINT);

– 1个外部监测电池VBAT供电引脚输入通道。

  •  转换开始的发起:

– 软件触发;

– 硬件触发。

  •  运行模式:

– 转换单个通道,或者扫描一序列的通道;

– 单次运行模式,每次触发转换一次选择的输入通道;

– 连续运行模式,连续转换所选择的输入通道;

– 间断运行模式;

– 同步模式(适用于具有两个或多个ADC的设备)。

  • 转换结果阈值监测器功能: 模拟看门狗。
  • 中断产生:

– 常规转换结束;

– 模拟看门狗事件;

– 溢出事件。

  • 过采样:  

 – 16位的数据寄存器;
 – 可调整的过采样率,从2x到256x;
 – 高达8位的可编程数据移位。

  •  ADC供电要求:
     – 2.4V到3.6V,一般供电电压为3.3V。
  •  ADC输入范围: VREFN ≤VIN ≤VREFP  。

下面介绍下GD32F470的ADC框图:

标注1:输入电压和参考电压

输入电压引脚定义如下表:

大于等于100pin的GD32F470,ADC参考电压等于VREFP,100pin以下的GD32F470,ADC参考电压等于VDDA

GD32F470的ADC是12bit有效位的,满量程对应的转换值是4095,即当采样引脚上的电压等于ADC参考电压时,得到的转换值即为4095。故理论采样是指可通过以下公式得到:

标注2:输入通道

前面提到,ADC有最多16个外部模拟通道和3个内部通道,外部通道号从IN0~IN15,由IO口号来决定,两个内部通道是IN16(温度传感器)和IN17(内部Vrefint,典型值1.2V),下表给出了IO口号对应的ADC通道:

标注3:规则组

GD32F470的ADC转换组称为规则组,也叫常规序列。

规则组有两个重要的参数,其一为转换的个数,其二为转换的序列,规定好这两个参数后,一旦开始规则组的转换,则ADC就按照转换序列一个一个的进行模-数转换,直到达到要求的转换个数。

规则组的转换个数由ADC_RSQ0寄存器的RL[3:0]位规定,转换的总数目为RL[3:0]+1,转换总数目最大为16个;转换序列由ADC_RSQ0~ADC_RSQ2共同决定,我们来看下这几个寄存器。

ADC_RSQ0寄存器:

ADC_RSQ1寄存器:

ADC_RSQ2寄存器:

 举个例子,现需要按照CH3->CH2->CH1的顺序进行规则组转换,则设定RL[3:0] = 2,然后设定RSQ0为CH3,RSQ1为CH2,RSQ2为CH1,则当开始规则组转换时,ADC首先进行RSQ0规定的通道即CH3的转换,再进行RSQ1规定的通道即CH2的转换,最后进行RSQ2规定的通道即CH1转换,当这三个通道转换完后,规则组转换结束。

需要注意的是,每转换一个规则组通道,转换结果都会放在寄存器ADC_RDATA中,所以CPU一定要在下一个通道转换完成前将上一个通道转换结果读走,否则会导致上一个通道数据被新的数据覆盖。所以在多通道规则组转换时,为了保证能读到所有通道的数据,一定要使用DMA(直接存储器访问控制器),每个通道转换结束后,都会给DMA发送请求,DMA就会将最新的ADC_RDATA中的数据搬走。关于ADC配合DMA的使用,后面会详细介绍。

标注4:触发源

ADC的规则组需要选特定的触发源用于触发ADC转换,注意,ADC的Enable(即ADC_CTL1寄存器的ADC_ON位置“1”)不会触发ADC转换,而是当选定的触发源来临后ADC才开始转换。

触发源分为内部触发和外部触发,内部触发是指软件触发;外部触发源是除了内部触发源以外的触发源,外部触发源可以通过ADC_CTL1寄存器查看:

ADC_CTL1寄存器:

标注5:规则组数据寄存器

如标注3规则组的表述,每个ADC的规则组只有一个数据寄存器ADC_RDATA,每转换一个通道,转换结果放在这个寄存器中,在下一通道转换结束前必须要将上一个通道的转换结果取走。

标注6:ADC中断及标志位

ADC的中断总共有两种:规则组转换结束中断以及模拟看门狗,可以通过将ADC_CTL0中的EOCIE和WDEIE置“1”来开启相应中断。

ADC_STAT寄存器中的EOC和WDE表示相应事件发生,EOC置“1”表示规则组的转换已经结束。

8.2.3 DMA原理

本实验中ADC通道有两个,分别为PF7和PF10,所以我们用规则组多通道采样实现双电压读取,从上一节内容中可以知道,ADC规则组实现多通道转换时,必须要用到DMA。下面我们介绍下DMA原理。

DMA(直接存储器访问控制器)是一个非常好用的外设,它提供了一种硬件的方式在外设和存储器之间或者存储器和存储器之间传输数据,而无需 CPU 的介入,从而使 CPU 可以专注在处理其他系统功能上。GD32F470有两个DMA,其中DMA0有8个通道,DMA1也有8个通道。

GD32F470支持DMA的单一传输和多数据传输模式。这里只讲解单一传输。DMA实现很简单,只要配置好以下几要素即可。

  1. 源地址和目标地址:DMA进行数据搬运过程为从源地址读取到数据,再搬运到目标地址。本实验中,需要把ADC转换结果搬运到自定义的buffer中,所以源地址就要设置为ADCx_RDATA寄存器地址,目标地址为buffer地址。
  1. 源和目标的地址增量方式:地址增量方式有固定模式和增量模式两种,固定模式是指进行一次DMA搬运后,下次搬运的源地址或目标地址保持不变;增量模式指进行一次DMA搬运后,下次搬运的源地址或目标地址会加1。本实验中,源地址始终都应该为ADCx_RDATA地址,所以源地址增量方式需要设置为固定模式,而目标地址为自定义buffer,我们需要用buffer[0]存储x轴数据,buffer[1]存储y轴数据,所以目标地址增量方式需要设置为增量模式。
  1. DMA传输方向:DMA传输方向有三种,分别为外设地址->存储器地址、存储器地址->外设地址以及存储器->存储器。本实验中源地址是外设地址,目标地址为自定义buffer地址即存储器地址,故传输方向需设置为外设地址->存储器地址。
  1. 源和目标数据位宽:源和目标数据位宽表示每次搬运的数据长度,可以设置为8bit、16bit和32bit。本实验中ADC的数据只占用ADCx_RDATA寄存器的低半字即16bit,所以源和目标位宽选择16bit即可。
  1. DMA传输个数和循环模式:传输个数表示一轮DMA传输可以搬运的次数。循环模式表示当一轮DMA传输结束后,是否直接进行下一轮搬运,当开启循环模式后,当上一轮DMA传输结束后,源地址和目标地址会恢复到最开始的状态。本实验中,需要转换2个通道ADC,故DMA传输个数设置为2,循环模式开启。
  1. DMA通道优先级:DMA的每个通道都有一个软件优先级,当DMA控制器在同一时间接收到多个外设请求时,仲裁器将根据外设请求的优先级来决定响应哪一个外设请求。优先级包括软件优先级和硬件优先级,优先级规则如下:
    软件优先级:分为4级,低,中,高和极高。可以通过寄存器DMA_CHxCTL的PRIO位域来配置。
    硬件优先级:当通道具有相同的软件优先级时,编号低的通道优先级高。例:通道0和通道2配置为相同的软件优先级时,通道0的优先级高于通道2。

上面描述了DMA配置的一些要素,那么DMA是如何被触发的呢,我们来看下DMA请求映射表:

DMA0各通道请求表:

DMA1各通道请求表:

本实验中是ADC配合DMA来使用,如果使用DMA去搬运ADC0的数据,从上表查询得知需要使用DMA1的通道0,如果是搬运ADC2的数据,也可以用到DMA1的通道0。如现在设置DMA1的通道0去搬运ADC2的数据,当ADC2每转换一个通道,ADC2_RDATA会更新一次数据,此时ADC2会自动向DMA1的通道0发出一次搬运请求,DMA收到请求后会进行一次数据搬运。DMA的请求和应答方式见下图:

8.3 硬件设计

本实验是使用PF7和PF10来进行电压采集,读者可以采用飞线方式外接电压到这两个引脚进行测试。

8.4 代码解析

本实验用到两个ADC2通道,使用ADC2规则组搭配DMA1通道0进行数据转换和搬运,ADC2规则组和DMA1通道0都开启循环模式,一旦开始ADC2规则组转换,会持续PF7和PF10上的电压进行转换和数据搬运。

8.4.1 DMA和ADC初始化

在driver_adc.c中定义driver_adc_regular_ch_dma_config函数,该函数实现DMA和ADC的初始化。

C
void driver_adc_regular_ch_dma_config(typdef_adc_ch_general *ADC, typdef_adc_ch_parameter *ADC_CH,void *buffer)
{        
    dma_single_data_parameter_struct dma_single_data_parameter;
    rcu_periph_clock_enable(ADC->dma_parameter.rcu_dma);                                                                                                        /*DMA时钟开启*/
    dma_deinit(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);                /*DMA通道参数复位*/
    /*DMA源地址、目标地址、增量方式、传输位宽、传输方向、传输个数、优先级设置*/
    dma_single_data_parameter.periph_addr  = (uint32_t)(&ADC_RDATA(ADC->adc_port));
    dma_single_data_parameter.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_single_data_parameter.memory0_addr  = (uint32_t)(buffer);
    dma_single_data_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    if(ADC->adc_mode == ADC_DAUL_ROUTINE_PARALLEL)
    {
        dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
    }
    else
    {
        dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
    }
    dma_single_data_parameter.direction    = DMA_PERIPH_TO_MEMORY;
    dma_single_data_parameter.number       = ADC->dma_parameter.dma_number;
    dma_single_data_parameter.priority     = ADC->dma_parameter.dma_priority;
    dma_single_data_mode_init(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel, &dma_single_data_parameter);
    dma_channel_subperipheral_select(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel,ADC->dma_parameter.dma_subperipheral_num);
    /*DMA循环模式设置*/
    if(ADC->dma_parameter.dma_circulation_mode == ENABLE)
    {
        dma_circulation_enable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
    }
    else
    {
        dma_circulation_disable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
    }
    dma_channel_enable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);        /* 使能DMA */
    driver_adc_config(ADC,ADC_CH);                                                                                                                                                                                                                        /* ADC初始化 */
}

在driver_adc.h中声明了ADC DMA的结构体:

C
typedef struct __typdef_adc_dma_parameter
{
    rcu_periph_enum rcu_dma;                //DMA时钟
    uint32_t dma_periph;                                //DMA号
    dma_channel_enum dma_channel;//DMA通道号
    dma_subperipheral_enum dma_subperipheral_num;//外设请求号
    uint32_t dma_number;                                //DMA传输个数
    uint32_t dma_priority;                        //DMA通道优先级
    EventStatus dma_circulation_mode;//循环模式
}typdef_adc_dma_parameter;

这段代码比较简单,请读者按照前面介绍的DMA原理进行解析。

8.4.2 BSP_ADC设置所需要的参数及IO口结构体定义

在bsp_adc.c中,对BSP_ADC设置所需要的参数及IO扩结构体数组进行了定义:

C
typdef_adc_ch_general  BSP_ADC= {
    .rcu_adc = RCU_ADC2,                                                                                                                        /* ADC2的时钟 */
    .adc_psc = ADC_ADCCK_PCLK2_DIV8,                                                                        /* ADC2设置为APB2 8分频 */
    .adc_port = ADC2,                                                                                                                                        /* ADC口为ADC2 */
    .adc_mode = ADC_SYNC_MODE_INDEPENDENT,                                                /* ADC模式为独立模式 */
    .adc_channel_group = ADC_ROUTINE_CHANNEL,                                        /* 使用规则组 */
    .adc_scan_function = ENABLE,                                                                                        /* 开启扫描模式 */
    .adc_continuous_function = ENABLE,                                                                /* 开启循环模式 */
    .ch_count = 2,                                                                                                                                                /* 转换长度为2 */
    .adc_external_trigger_mode = EXTERNAL_TRIGGER_DISABLE,
    .dma_parameter =
    {
        .rcu_dma = RCU_DMA1,                                                                                                                /* DMA1的时钟 */
        .dma_periph = DMA1,                                                                                                                        /* 使用DMA1 */
        .dma_channel = DMA_CH0,                                                                                                        /* 使用通道4 */
        .dma_number = 2,                                                                                                                                /* DMA传输长度为2 */
        .dma_subperipheral_num = DMA_SUBPERI2,
        .dma_priority = DMA_PRIORITY_HIGH,                                                        /* DMA通道优先级 */
        .dma_circulation_mode = ENABLE                                                                        /* DMA循环模式打开 */
    },
    .trigger_source = ADC_EXTTRIG_ROUTINE_T0_CH0,        /* ADC触发源选择为软件触发 */
    .DMA_mode = ENABLE                                                                                                                                /* 使用DMA */
};

typdef_adc_ch_parameter BSP_ADC_ch[2] =
{
    {
    .rcu_port = RCU_GPIOF,                                                                                                                /* GPIOF时钟 */
    .port = GPIOF,                                                                                                                                                /* GPIO port */
    .pin = GPIO_PIN_7,                                                                                                                                /* PF7 */
    .gpio_speed = GPIO_OSPEED_2MHZ,                                                                                /* PF7速度设置为10MHz */
    .adc_channel = ADC_CHANNEL_5,                                                                                        /* PF7是ADC2的通道5 */
    .sample_time = ADC_SAMPLETIME_144                                                /* 设置采样周期为55.5 */
    }
    ,
    {
    .rcu_port = RCU_GPIOF,                                                                                                                /* GPIOF时钟 */
    .port = GPIOF,                                                                                                                                                /* GPIO port */
    .pin = GPIO_PIN_10,                                                                                                                                /* PF8 */
    .gpio_speed = GPIO_OSPEED_2MHZ,                                                                                /* PF8速度设置为10MHz */
    .adc_channel = ADC_CHANNEL_8,                                                                                        /* PF8是ADC2的通道10 */
    .sample_time = ADC_SAMPLETIME_144                                                /* 设置采样周期为55.5 */
    }
};/* ADC通道参数配置,包括IO口,和对应通道以及采样周期 */

8.4.3 BSP_ ADC初始化和触发ADC转换的具体实现函数

在bsp_adc.c中定义了 DMA和ADC初始化和触发ADC转换的函数:

C
uint16_t BSP_ADC_data[2] ;
void bsp_ADC_config()
{
    driver_adc_regular_ch_dma_config(&BSP_ADC,BSP_ADC_ch,(uint16_t*)BSP_ADC_data);
    driver_adc_software_trigger_enable(&BSP_ADC);        
}

8.4.4 main函数实现

C
int main(void)
{
    driver_init();//延时函数初始化
    bsp_uart_init(&BOARD_UART);//BOARD_UART串口初始化
    bsp_ADC_config();//ADC配置
    while (1)
    {
        delay_ms(100);//延时100ms
        printf_log(" the BSP_ADC data is %d,%d \r\n", BSP_ADC_data[0],BSP_ADC_data[1]);//打印ADC数据
    }
}

本例程main函数首先进行了延时函数初始化,为了演示实验结果,这里初始化了BOARD_UART串口,关于串口的使用,请读者参考串口章节,然后是BSP_ADC配置。在主循环中,每100ms打印一次PF7和PF10的ADC转换数据。

8.5 实验结果

如上main函数实现说明。

由聚沃科技原创,来源于GD32F470紫藤派 - 文章中心 - 苏州聚沃电子科技有限公司

GD32MCU技术交流群:859440462

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

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

相关文章

【C++】stack和queue 适配器

🔥个人主页:北辰水墨 🔥专栏:C学习仓 本节内容我们来讲解栈和队列的模拟实现,文末会赋上模拟实现的代码 一、stack的使用和模拟实现 stack适配器的介绍: 1. stack是一种容器适配器,专门用在具…

【操作系统期末速成】​内存管理|内存的装入模块在装入内存的方式|分配管理方式|页面置换算法|页面置换

🎥 个人主页:深鱼~🔥收录专栏:操作系统🌄欢迎 👍点赞✍评论⭐收藏 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到…

Qt之QMqtt 发送图片数据

简述 MQTT(消息队列遥测传输)是ISO标准下基于发布/订阅范式的消息协议;它工作在TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件; MQTT是一个基于客户端-服务器的消息发布/订阅传输协议;MQT…

chmod -R 777 / 抢救,看这篇就够了

chmod -R 777抢救全过程记录 背景 在两台Ubuntu 20.04的服务器上错误执行了chmod -R 777 /命令,结果非常酸爽,sudo权限失效,而且ssh也没有用了。在经过了10多个小时的踩坑以后最后在不重装系统的情况下解决了问题,以下记录只记录…

LOTO示波器动作编程功能(命令批处理)

动作编程功能是为了方便客户根据自己的应用场景,做到一个按键就连续做多个示波器操作,从而降低了对操作人员的技术要求,做到傻瓜式操作。之前LOTO有个类似的功能,是把示波器的基础设置根据不同的测试场景存成不同的设置文件&#…

【FFmpeg】Filter 过滤器 ① ( FFmpeg 过滤器简介 | 过滤器概念 | 过滤器用法 | 过滤器工作流程 | 过滤器文档 | 过滤器分类 )

文章目录 一、FFmpeg 过滤器 Filter 简介1、FFmpeg 过滤器概念2、FFmpeg 过滤器用法3、FFmpeg 过滤器工作流程4、FFmpeg 过滤器文档 二、FFmpeg 过滤器 分类1、过滤器分类 - 根据处理数据类型分类2、过滤器分类 - 根据编码器位置分类3、过滤器分类 - 根据功能分类 FFmpeg 相关文…

在vue3中,如何优雅的使用echarts之实现大屏项目

前置知识 效果图 使用技术 Vue3 Echarts Gasp Gasp:是一个 JavaScript动画库,它支持快速开发高性能的 Web 动画。在本项目中,主要是用于做轨迹运动 所需安装的插件 npm i echarts npm i countup.js 数字滚动特效 npm i gsap javascript动画库 np…

竞赛课第十周(巴什游戏,尼姆博弈)

目录 目的: 实验内容: 第一题 思路: 【参考代码】 【运行结果】 第二题 输入: 输出: 【参考代码】 【运行结果】 目的: 熟悉并掌握公平组合游戏 (1)巴什游戏、尼姆游戏 (2)图游戏…

【C++】list原理讲解及其实现

目录 一、认识list底层结构 二、list的构造类函数 三、迭代器 四、数据的访问 五、容量相关的函数 六、关于数据的增删查改操作 前言 要模拟实现list,必须要熟悉list的底层结构以及其接口的含义,在上一篇我们仔细讲解了list的常见接口的使用及其含义&…

2024中国(重庆)人工智能展览会8月举办

2024中国(重庆)人工智能展览会8月举办 邀请函 主办单位: 中国航空学会 重庆市南岸区人民政府 招商执行单位: 重庆港华展览有限公司 【报名I59交易会 2351交易会 9466】 展会背景: 2024中国航空科普大会暨第八届全国青少年无人机大赛在…

linux Nginx安装与启动

一、先到官网下载Nginx 官网地址: http://nginx.org/en/download.html 我下载的是nginx-1.20.2 二、下载好的文件上传到服务器,然后解压 1、上传到指定的服务器地址,我这里是公司服务器,目录都是定义好的,自己玩建…

分层存储无法拯救 Kafka

01 引言 Apache Kafka 自诞生之日起,就以其卓越的设计和强大的功能,成为了流处理领域的标杆。它不仅定义了现代流处理架构,更以其独特的分布式日志抽象,为实时数据流的处理和分析提供了前所未有的能力。Kafka 的成功&#xff0…

element ui输入框后面带输入的字符数量

使用el-input的属性&#xff1a; maxlength&#xff1a;最长字符限制&#xff1b; show-word-limit&#xff1a;显示输入字符数量&#xff1b; 例&#xff1a; <el-input v-model"title" placeholder"请输入名称" maxlength"200" show-wor…

阮怀俊谈如何盘活和挖掘乡村文旅资源

近年来&#xff0c;浙江凭借高水平建设新时代美丽乡村&#xff0c;各项工作持续走在全国前列&#xff0c;最近&#xff0c;在国家发展改革委关于恢复和扩大消费措施的通知中也提到&#xff1a; “推广浙江‘千万工程’经验&#xff0c;建设宜居宜业和美乡村。实施文化产业赋能乡…

vue3专栏项目 -- 四、前后端结合(上)

一、前后端分离是什么 前面我们一直在和静态数据打交道&#xff0c;虽然流程可以跑个半通&#xff0c;但是静态数据还是给我们造成了诸多不便&#xff0c;现在我们是时候用上后端了。 现在的应用开发模式&#xff0c;自从SPA出现以后&#xff0c;前端和后端可以平行的进行对应…

智慧法治:AI技术如何赋能法律行业创新

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

AI绘画动漫转真人详细教程

从小到大&#xff0c;我们看过的动漫、玩过的游戏有很多很多 但我们会发现里面的角色或者人物都是二次元的 我就会好奇这些动漫人物在现实中会长什么样 而现在&#xff0c;我们通过AI绘画竟然就能还原出来他们现实中的样子 除了动漫角色和游戏人物&#xff0c;古代的画像、经典…

【笔记】从零开始做一个男性人体的流程/躯干篇(超级详细)

躯干整体 大体 1.创建一个正方体&#xff0c;摆好位置 2.实例呀啥的都搞好 3.胸部它是一个前窄后宽的结构 斜方肌 臀部 1.臀部是前宽后窄的结构 2.我们再去侧面调整以下 胸椎向上倾斜&#xff0c;盆骨向下倾斜。脊椎是s形的 3.真实的身体没有这么方正&#xff0c;所以微调…

3kCTF2021 echo klibrary

文章目录 前言echoklibrary 前言 今天状态不好&#xff0c;很多事情都不想干&#xff0c;就做一做简单的题目 echo 内核版本&#xff1a;v5.9.10smap/smep/kaslr 开启modprobe_path 可写 题目给了源码&#xff0c;非常简单就是无限次的任意地址读写&#xff1a; #include …

Lombok注解详解

文章目录 注解详解lombok包下注解汇总- Getter- Setter- ToString- EqualsAndHashCode- Data- Value- NonNull- NoArgsConstructor- AllArgsConstructor- RequiredArgsConstructor- Builder- Synchronized- Cleanup- Singular- Generated- SneakyThrows- val- var experimental…