stm32——hal库学习笔记(DAC)

news2024/12/29 11:18:07

这里写目录标题

  • 一、DAC简介(了解)
    • 1.1,什么是DAC?
    • 1.2,DAC的特性参数
    • 1.3,STM32各系列DAC的主要特性
  • 二、DAC工作原理(掌握)
    • 2.1,DAC框图简介(F1)
    • 2.2,参考电压/模拟部分电压
    • 2.3,DAC数据格式
    • 2.4,触发源
    • 2.5,DMA请求
    • 2.6,DAC输出电压
  • 三、DAC输出实验(熟悉)
    • 3.1,实验简要(了解)
    • 3.2,DAC寄存器介绍(了解)
    • 3.3,DAC输出实验配置步骤(掌握)
    • 3.4,编程实战:DAC输出实验(掌握)
  • 四、DAC输出三角波实验(熟悉)
    • 4.1,实验简要(了解)
    • 4.2,编程实战:DAC输出三角波实验(掌握)
  • 五、DAC输出正弦波实验(熟悉)
    • 5.1,实验简要(了解)
    • 5.2,DAC输出正弦波实验配置步骤(掌握)
    • 5.3,产生正弦波序列函数介绍(熟悉)
    • 5.4,编程实战:DAC输出正弦波实验(掌握)
  • 六、PWM DAC实验(熟悉)
    • 6.1, PWM DAC应用背景(了解)
    • 6.2, PWM DAC技术实现原理(了解)
      • 6.2.1,什么是PWM DAC技术?
      • 6.2.2,用分段函数表示PWM波
      • 6.2.3,将PWM波分段函数进行傅里叶级数展开
      • 6.2.4,PWM DAC的分辨率
      • 6.2.5,8位分辨率下对RC滤波器的设计要求
      • 6.2.6,PWM DAC二阶低通滤波器原理图
    • 6.3,编程实战: PWM DAC实验(掌握)

一、DAC简介(了解)

1.1,什么是DAC?

在这里插入图片描述

1.2,DAC的特性参数

在这里插入图片描述

1.3,STM32各系列DAC的主要特性

在这里插入图片描述

二、DAC工作原理(掌握)

2.1,DAC框图简介(F1)

在这里插入图片描述

2.2,参考电压/模拟部分电压

在这里插入图片描述

2.3,DAC数据格式

在这里插入图片描述

2.4,触发源

在这里插入图片描述
在这里插入图片描述

2.5,DMA请求

在这里插入图片描述

2.6,DAC输出电压

在这里插入图片描述

三、DAC输出实验(熟悉)

3.1,实验简要(了解)

在这里插入图片描述

3.2,DAC寄存器介绍(了解)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3,DAC输出实验配置步骤(掌握)

在这里插入图片描述
相关HAL库函数介绍
在这里插入图片描述
关键结构体介绍(F1为例)
在这里插入图片描述

3.4,编程实战:DAC输出实验(掌握)

在这里插入图片描述
adc3.c

#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc3_handle;         /* ADC句柄 */
/**
 * @brief       ADC3初始化函数
 *   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
 *              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
 *              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
 * @param       无
 * @retval      无
 */
void adc3_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    RCC_PeriphCLKInitTypeDef adc_clk_init = {0};

    ADC3_CHY_GPIO_CLK_ENABLE();                                /* IO口时钟使能 */
    ADC3_CHY_CLK_ENABLE();                                     /* ADC时钟使能 */

    adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;     /* ADC外设时钟 */
    adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;        /* 分频因子6时钟为72M/6=12MHz */
    HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                  /* 设置ADC时钟 */

    /* 设置AD采集通道对应IO引脚工作模式 */
    gpio_init_struct.Pin = ADC3_CHY_GPIO_PIN;                  /* ADC通道对应的IO引脚 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;                  /* 模拟 */
    HAL_GPIO_Init(ADC3_CHY_GPIO_PORT, &gpio_init_struct);

    g_adc3_handle.Instance = ADC_ADCX;                         /* 选择哪个ADC */
    g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;        /* 数据对齐方式:右对齐 */
    g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;        /* 非扫描模式,仅用到一个通道 */
    g_adc3_handle.Init.ContinuousConvMode = DISABLE;           /* 关闭连续转换模式 */
    g_adc3_handle.Init.NbrOfConversion = 1;                    /* 1个转换在规则序列中 也就是只转换规则序列1 */
    g_adc3_handle.Init.DiscontinuousConvMode = DISABLE;        /* 禁止规则通道组间断模式 */
    g_adc3_handle.Init.NbrOfDiscConversion = 0;                /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
    g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;  /* 触发转换方式:软件触发 */
    HAL_ADC_Init(&g_adc3_handle);                              /* 初始化 */

    HAL_ADCEx_Calibration_Start(&g_adc3_handle);               /* 校准ADC */
}

/**
 * @brief       设置ADC通道采样时间
 * @param       adcx : adc句柄指针,ADC_HandleTypeDef
 * @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
 * @param       stime: 采样时间  0~7, 对应关系为:
 *   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期
 * @param       rank: 多通道采集时需要设置的采集编号,
                假设你定义channle1的rank=1,channle2 的rank=2,
                那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 
                单通道DMA设置为 ADC_REGULAR_RANK_1
 *   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
 * @retval      无
 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    ADC_ChannelConfTypeDef adc_ch_conf;
    
    adc_ch_conf.Channel = ch;                            /* 通道 */
    adc_ch_conf.Rank = rank;                             /* 序列 */
    adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */
    HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}

/**
 * @brief       获得ADC转换后的结果
 * @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17
 * @retval      无
 */
uint32_t adc3_get_result(uint32_t ch)
{
    adc3_channel_set(&g_adc3_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */
    
    HAL_ADC_Start(&g_adc3_handle);                            /* 开启ADC */
    HAL_ADC_PollForConversion(&g_adc3_handle, 10);            /* 轮询转换 */
    return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}

/**
 * @brief       获取通道ch的转换值,取times次,然后平均
 * @param       ch      : 通道号, 0~17
 * @param       times   : 获取次数
 * @retval      通道ch的times次转换结果平均值
 */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;

    for (t = 0; t < times; t++)     /* 获取times次数据 */
    {
        temp_val += adc3_get_result(ch);
        delay_ms(5);
    }

    return temp_val / times;        /* 返回平均值 */
}

adc3.h

#ifndef __ADC_H
#define __ADC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* ADC及引脚 定义 */
#define ADC3_CHY_GPIO_PORT                  GPIOA
#define ADC3_CHY_GPIO_PIN                   GPIO_PIN_1 
#define ADC3_CHY_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */

#define ADC_ADCX                            ADC3 
#define ADC3_CHY                            ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC3_CHY_CLK_ENABLE()               do{ __HAL_RCC_ADC3_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 */


/******************************************************************************************/

void adc3_init(void);                                          /* ADC3初始化 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);   /* ADC3通道设置 */
uint32_t adc3_get_result(uint32_t ch);                         /* 获得某个通道值  */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times);  /* 得到某个通道给定次数采样的平均值 */

#endif 

dac.c

#include "./BSP/DAC/dac.h"
#include "./SYSTEM/delay/delay.h"
DAC_HandleTypeDef g_dac_handle;         /* DAC句柄 */

/**
 * @brief       DAC初始化函数
 *   @note      本函数支持DAC1_OUT1/2通道初始化
 *              DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
 *              DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
 *              因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
 *
 * @param       outx: 要初始化的通道. 1,通道1; 2,通道2
 * @retval      无
 */
void dac_init(uint8_t outx)
{
    GPIO_InitTypeDef gpio_init_struct;
    DAC_ChannelConfTypeDef dac_ch_conf;
    
    __HAL_RCC_DAC_CLK_ENABLE();                                 /* 使能DAC1的时钟 */
    __HAL_RCC_GPIOA_CLK_ENABLE();                               /* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
    
    gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5;  /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);

    g_dac_handle.Instance = DAC;
    HAL_DAC_Init(&g_dac_handle);                                /* 初始化DAC */


    dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE;                 /* 不使用触发功能 */
    dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;    /* DAC1输出缓冲关闭 */
    
    switch(outx)
    {
        case 1:
            HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1);  /* 配置DAC通道1 */
            HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_1);                         /* 开启DAC通道1 */
            break;
        case 2:
            HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2);  /* 配置DAC通道2 */
            HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_2);                         /* 开启DAC通道2 */
            break;
        default:break;
    }

}

/**
 * @brief       设置通道1/2输出电压
 * @param       outx: 1,通道1; 2,通道2
 * @param       vol : 0~3300,代表0~3.3V
 * @retval      无
 */
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
    double temp = vol;
    temp /= 1000;
    temp = temp * 4096 / 3.3;

    if (temp >= 4096)temp = 4095;   /* 如果值大于等于4096, 则取4095 */

    if (outx == 1)   /* 通道1 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
    }
    else            /* 通道2 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
    }
}

dac.h

#ifndef __DAC_H
#define __DAC_H
#include "./SYSTEM/sys/sys.h"

void dac_init(uint8_t outx);                        /* DAC通道1初始化 */ 
void dac_set_voltage(uint8_t outx, uint16_t vol);   /* 设置通道1/2输出电压 */ 

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DAC/dac.h"
#include "./BSP/ADC/adc3.h"

extern DAC_HandleTypeDef g_dac_handle;

int main(void)
{
    uint16_t adcx;
    float temp;
    uint8_t t = 0;
    uint16_t dacval = 0;
    uint8_t key;

    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    usmart_dev.init(72);                        /* 初始化USMART */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */
    key_init();                                 /* 初始化按键 */
    adc3_init();                                /* 初始化ADC3 */
    dac_init(1);                                /* 初始化DAC1_OUT1通道 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32F103", RED);
    lcd_show_string(30,  70, 200, 16, 16, "DAC TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "WK_UP:+  KEY1:-", RED);

    lcd_show_string(30, 130, 200, 16, 16, "DAC VAL:", BLUE);
    lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);
    lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);

    while (1)
    {
        t++;
        key = key_scan(0);          /* 按键扫描 */

        if (key == WKUP_PRES)
        {
            if (dacval < 4000)dacval += 200;

            HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dacval);/* 输出增大200 */
        }
        else if (key == KEY1_PRES)
        {
            if (dacval > 200)dacval -= 200;
            else dacval = 0;

            HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dacval); /* 输出减少200 */
        }

        if (t == 10 || key == KEY1_PRES || key == WKUP_PRES)        /* WKUP/KEY1按下了,或者定时时间到了 */
        {
            adcx = HAL_DAC_GetValue(&g_dac_handle, DAC_CHANNEL_1);  /* 读取前面设置DAC1_OUT1的值 */
            lcd_show_xnum(94, 130, adcx, 4, 16, 0, BLUE);           /* 显示DAC寄存器值 */
            
            temp = (float)adcx * (3.3 / 4096);                      /* 得到DAC电压值 */
            adcx = temp;
            lcd_show_xnum(94, 150, temp, 1, 16, 0, BLUE);           /* 显示电压值整数部分 */
            
            temp -= adcx;
            temp *= 1000;
            lcd_show_xnum(110, 150, temp, 3, 16, 0X80, BLUE);       /* 显示电压值的小数部分 */
            
            adcx = adc3_get_result_average(ADC3_CHY, 20);           /* 得到ADC3通道1的转换结果 */
            temp = (float)adcx * (3.3 / 4096);                      /* 得到ADC电压值(adc是16bit的) */
            adcx = temp;
            lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE);           /* 显示电压值整数部分 */
                
            temp -= adcx;
            temp *= 1000;
            lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE);       /* 显示电压值的小数部分 */
            
            LED0_TOGGLE();  /* LED0闪烁 */
            t = 0;
        }

        delay_ms(10);
    }
}

四、DAC输出三角波实验(熟悉)

4.1,实验简要(了解)

在这里插入图片描述

4.2,编程实战:DAC输出三角波实验(掌握)

在这里插入图片描述
dac.c

#include "./BSP/DAC/dac.h"
#include "./SYSTEM/delay/delay.h"

DAC_HandleTypeDef g_dac_handle;         /* DAC句柄 */

/**
 * @brief       DAC初始化函数
 *   @note      本函数支持DAC1_OUT1/2通道初始化
 *              DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
 *              DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
 *              因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
 *
 * @param       outx: 要初始化的通道. 1,通道1; 2,通道2
 * @retval      无
 */
void dac_init(uint8_t outx)
{
    GPIO_InitTypeDef gpio_init_struct;
    DAC_ChannelConfTypeDef dac_ch_conf;
    
    __HAL_RCC_DAC_CLK_ENABLE();                                 /* 使能DAC1的时钟 */
    __HAL_RCC_GPIOA_CLK_ENABLE();                               /* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
    
    gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5;  /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);

    g_dac_handle.Instance = DAC;
    HAL_DAC_Init(&g_dac_handle);                                /* 初始化DAC */


    dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE;                 /* 不使用触发功能 */
    dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;    /* DAC1输出缓冲关闭 */
    
    switch(outx)
    {
        case 1:
            HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1);  /* 配置DAC通道1 */
            HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_1);                         /* 开启DAC通道1 */
            break;
        case 2:
            HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2);  /* 配置DAC通道2 */
            HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_2);                         /* 开启DAC通道2 */
            break;
        default:break;
    }

}

/**
 * @brief       设置通道1/2输出电压
 * @param       outx: 1,通道1; 2,通道2
 * @param       vol : 0~3300,代表0~3.3V
 * @retval      无
 */
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
    double temp = vol;
    temp /= 1000;
    temp = temp * 4096 / 3.3;

    if (temp >= 4096)temp = 4095;   /* 如果值大于等于4096, 则取4095 */

    if (outx == 1)   /* 通道1 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
    }
    else            /* 通道2 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
    }
}

/***************************************DAC输出三角波实验代码*****************************************/

/**
 * @brief       设置DAC_OUT1输出三角波
 *   @note      输出频率 ≈ 1000 / (dt * samples) Khz, 不过在dt较小的时候,比如小于5us时, 由于delay_us
 *              本身就不准了(调用函数,计算等都需要时间,延时很小的时候,这些时间会影响到延时), 频率会偏小.
 * 
 * @param       maxval : 最大值(0 < maxval < 4096), (maxval + 1)必须大于等于samples/2
 * @param       dt     : 每个采样点的延时时间(单位: us)
 * @param       samples: 采样点的个数, samples必须小于等于(maxval + 1) * 2 , 且maxval不能等于0
 * @param       n      : 输出波形个数,0~65535
 *
 * @retval      无
 */
void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n)
{
    uint16_t i, j;
    float incval;                           /* 递增量 */
    float Curval;                           /* 当前值 */
    
    if((maxval + 1) <= samples)return ;     /* 数据不合法 */
        
    incval = (maxval + 1) / (samples / 2);  /* 计算递增量 */
    
    for(j = 0; j < n; j++)
    { 
        Curval = 0;
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);    /* 先输出0 */
        for(i = 0; i < (samples / 2); i++)  /* 输出上升沿 */
        {
            Curval  +=  incval;             /* 新的输出值 */
            HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
            delay_us(dt);
        }
        for(i = 0; i < (samples / 2); i++)  /* 输出下降沿 */
        {
            Curval  -=  incval;             /* 新的输出值 */
            HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
            delay_us(dt);
        }
    }
}

dac.h

#ifndef __DAC_H
#define __DAC_H

#include "./SYSTEM/sys/sys.h"


void dac_init(uint8_t outx);                        /* DAC通道1初始化 */
void dac_set_voltage(uint8_t outx, uint16_t vol);   /* 设置通道1/2输出电压 */

void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n);   /* 输出三角波 */

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DAC/dac.h"

int main(void)
{
    uint8_t t = 0; 
    uint8_t key;

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    usmart_dev.init(72);                /* 初始化USMART */
    led_init();                         /* 初始化LED */
    lcd_init();                         /* 初始化LCD */
    key_init();                         /* 初始化按键 */
    dac_init(1);                        /* 初始化DAC1_OUT1通道 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "DAC Triangular WAVE TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY0:Wave1  KEY1:Wave2", RED);
    lcd_show_string(30, 130, 200, 16, 16, "DAC None", BLUE); /* 提示无输出 */

    while (1)
    {
        t++;
        key = key_scan(0);                           /* 按键扫描 */

        if (key == KEY0_PRES)                        /* 高采样率 , 约1Khz波形 */
        {
            lcd_show_string(30, 130, 200, 16, 16, "DAC Wave1 ", BLUE);
            dac_triangular_wave(4095, 5, 2000, 100); /* 幅值4095, 采样点间隔5us, 200个采样点, 100个波形 */
            lcd_show_string(30, 130, 200, 16, 16, "DAC None  ", BLUE);
        }
        else if (key == KEY1_PRES)                   /* 低采样率 , 约1Khz波形 */
        {
            lcd_show_string(30, 130, 200, 16, 16, "DAC Wave2 ", BLUE);
            dac_triangular_wave(4095, 500, 20, 100); /* 幅值4095, 采样点间隔500us, 20个采样点, 100个波形 */
            lcd_show_string(30, 130, 200, 16, 16, "DAC None  ", BLUE);
        }

        if (t == 10)                                 /* 定时时间到了 */
        {
            LED0_TOGGLE();                           /* LED0闪烁 */
            t = 0;
        }

        delay_ms(10);
    }
}

五、DAC输出正弦波实验(熟悉)

5.1,实验简要(了解)

在这里插入图片描述

5.2,DAC输出正弦波实验配置步骤(掌握)

在这里插入图片描述

5.3,产生正弦波序列函数介绍(熟悉)

在这里插入图片描述

5.4,编程实战:DAC输出正弦波实验(掌握)

dac3.c

#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"

ADC_HandleTypeDef g_adc3_handle;         /* ADC句柄 */

/**
 * @brief       ADC3初始化函数
 *   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
 *              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
 *              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
 * @param       无
 * @retval      无
 */
void adc3_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    RCC_PeriphCLKInitTypeDef adc_clk_init = {0};

    ADC3_CHY_GPIO_CLK_ENABLE();                                /* IO口时钟使能 */
    ADC3_CHY_CLK_ENABLE();                                     /* ADC时钟使能 */

    adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;     /* ADC外设时钟 */
    adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;        /* 分频因子6时钟为72M/6=12MHz */
    HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                  /* 设置ADC时钟 */

    /* 设置AD采集通道对应IO引脚工作模式 */
    gpio_init_struct.Pin = ADC3_CHY_GPIO_PIN;                  /* ADC通道对应的IO引脚 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;                  /* 模拟 */
    HAL_GPIO_Init(ADC3_CHY_GPIO_PORT, &gpio_init_struct);

    g_adc3_handle.Instance = ADC_ADCX;                         /* 选择哪个ADC */
    g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;        /* 数据对齐方式:右对齐 */
    g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;        /* 非扫描模式,仅用到一个通道 */
    g_adc3_handle.Init.ContinuousConvMode = DISABLE;           /* 关闭连续转换模式 */
    g_adc3_handle.Init.NbrOfConversion = 1;                    /* 1个转换在规则序列中 也就是只转换规则序列1 */
    g_adc3_handle.Init.DiscontinuousConvMode = DISABLE;        /* 禁止规则通道组间断模式 */
    g_adc3_handle.Init.NbrOfDiscConversion = 0;                /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
    g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;  /* 触发转换方式:软件触发 */
    HAL_ADC_Init(&g_adc3_handle);                              /* 初始化 */

    HAL_ADCEx_Calibration_Start(&g_adc3_handle);               /* 校准ADC */
}

/**
 * @brief       设置ADC通道采样时间
 * @param       adcx : adc句柄指针,ADC_HandleTypeDef
 * @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
 * @param       stime: 采样时间  0~7, 对应关系为:
 *   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期
 * @param       rank: 多通道采集时需要设置的采集编号,
                假设你定义channle1的rank=1,channle2 的rank=2,
                那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 
                单通道DMA设置为 ADC_REGULAR_RANK_1
 *   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
 * @retval      无
 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    ADC_ChannelConfTypeDef adc_ch_conf;
    
    adc_ch_conf.Channel = ch;                            /* 通道 */
    adc_ch_conf.Rank = rank;                             /* 序列 */
    adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */
    HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}

/**
 * @brief       获得ADC转换后的结果
 * @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17
 * @retval      无
 */
uint32_t adc3_get_result(uint32_t ch)
{
    adc3_channel_set(&g_adc3_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */
    
    HAL_ADC_Start(&g_adc3_handle);                            /* 开启ADC */
    HAL_ADC_PollForConversion(&g_adc3_handle, 10);            /* 轮询转换 */
    return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}

/**
 * @brief       获取通道ch的转换值,取times次,然后平均
 * @param       ch      : 通道号, 0~17
 * @param       times   : 获取次数
 * @retval      通道ch的times次转换结果平均值
 */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;

    for (t = 0; t < times; t++)     /* 获取times次数据 */
    {
        temp_val += adc3_get_result(ch);
        delay_ms(5);
    }

    return temp_val / times;        /* 返回平均值 */
}

dac3.h

#ifndef __ADC_H
#define __ADC_H

#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* ADC及引脚 定义 */

#define ADC3_CHY_GPIO_PORT                  GPIOA
#define ADC3_CHY_GPIO_PIN                   GPIO_PIN_1 
#define ADC3_CHY_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */

#define ADC_ADCX                            ADC3 
#define ADC3_CHY                            ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC3_CHY_CLK_ENABLE()               do{ __HAL_RCC_ADC3_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 */


/******************************************************************************************/

void adc3_init(void);                                          /* ADC3初始化 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);   /* ADC3通道设置 */
uint32_t adc3_get_result(uint32_t ch);                         /* 获得某个通道值  */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times);  /* 得到某个通道给定次数采样的平均值 */

#endif 

dac.c

#include "./BSP/DAC/dac.h"
#include "./SYSTEM/delay/delay.h"

DAC_HandleTypeDef g_dac_handle;         /* DAC句柄 */

/**
 * @brief       DAC初始化函数
 *   @note      本函数支持DAC1_OUT1/2通道初始化
 *              DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
 *              DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
 *              因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
 *
 * @param       outx: 要初始化的通道. 1,通道1; 2,通道2
 * @retval      无
 */
void dac_init(uint8_t outx)
{
    GPIO_InitTypeDef gpio_init_struct;
    DAC_ChannelConfTypeDef dac_ch_conf;
    
    __HAL_RCC_DAC_CLK_ENABLE();                                 /* 使能DAC1的时钟 */
    __HAL_RCC_GPIOA_CLK_ENABLE();                               /* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
    
    gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5;  /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);

    g_dac_handle.Instance = DAC;
    HAL_DAC_Init(&g_dac_handle);                                /* 初始化DAC */


    dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE;                 /* 不使用触发功能 */
    dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;    /* DAC1输出缓冲关闭 */
    
    switch(outx)
    {
        case 1:
            HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1);  /* 配置DAC通道1 */
            HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_1);                         /* 开启DAC通道1 */
            break;
        case 2:
            HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2);  /* 配置DAC通道2 */
            HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_2);                         /* 开启DAC通道2 */
            break;
        default:break;
    }
}

/**
 * @brief       设置通道1/2输出电压
 * @param       outx: 1,通道1; 2,通道2
 * @param       vol : 0~3300,代表0~3.3V
 * @retval      无
 */
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
    double temp = vol;
    temp /= 1000;
    temp = temp * 4096 / 3.3;

    if (temp >= 4096)temp = 4095;   /* 如果值大于等于4096, 则取4095 */

    if (outx == 1)   /* 通道1 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
    }
    else            /* 通道2 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
    }
}

/***************************************DAC输出三角波实验代码*****************************************/

/**
 * @brief       设置DAC_OUT1输出三角波
 *   @note      输出频率 ≈ 1000 / (dt * samples) Khz, 不过在dt较小的时候,比如小于5us时, 由于delay_us
 *              本身就不准了(调用函数,计算等都需要时间,延时很小的时候,这些时间会影响到延时), 频率会偏小.
 * 
 * @param       maxval : 最大值(0 < maxval < 4096), (maxval + 1)必须大于等于samples/2
 * @param       dt     : 每个采样点的延时时间(单位: us)
 * @param       samples: 采样点的个数, samples必须小于等于(maxval + 1) * 2 , 且maxval不能等于0
 * @param       n      : 输出波形个数,0~65535
 *
 * @retval      无
 */
void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n)
{
    uint16_t i, j;
    float incval;                           /* 递增量 */
    float Curval;                           /* 当前值 */
    
    if((maxval + 1) <= samples)return ;     /* 数据不合法 */
        
    incval = (maxval + 1) / (samples / 2);  /* 计算递增量 */
    
    for(j = 0; j < n; j++)
    { 
        Curval = 0;
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);    /* 先输出0 */
        for(i = 0; i < (samples / 2); i++)  /* 输出上升沿 */
        {
            Curval  +=  incval;             /* 新的输出值 */
            HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
            delay_us(dt);
        }
        for(i = 0; i < (samples / 2); i++)  /* 输出下降沿 */
        {
            Curval  -=  incval;             /* 新的输出值 */
            HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
            delay_us(dt);
        }
    }
}

/***************************************DAC输出正弦波实验代码*****************************************/

DMA_HandleTypeDef g_dma_dac_handle;             /* 定义要搬运DAC数据的DMA句柄 */
DAC_HandleTypeDef g_dac_dma_handle;             /* 定义DAC(DMA输出)句柄 */

extern uint16_t g_dac_sin_buf[4096];            /* 发送数据缓冲区 */

/**
 * @brief       DAC DMA输出波形初始化函数
 *   @note      本函数支持DAC1_OUT1/2通道初始化
 *              DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
 *              DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
 *              因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
 *
 * @param       outx: 要初始化的通道. 1,通道1; 2,通道2
 * @param       par         : 外设地址
 * @param       mar         : 存储器地址
 * @retval      无
 */
void dac_dma_wave_init(uint8_t outx, uint32_t par, uint32_t mar)
{
    GPIO_InitTypeDef gpio_init_struct;
    DAC_ChannelConfTypeDef dac_ch_conf={0};
    DMA_Channel_TypeDef *dmax_chy;

    if (outx == 1)
    {
        dmax_chy = DMA2_Channel3;       /* OUT1对应DMA2_Channel3 */
    }
    else
    {
        dmax_chy = DMA2_Channel4;       /* OUT2对应DMA2_Channel4 */
    }

    __HAL_RCC_GPIOA_CLK_ENABLE();                                           /* DAC通道引脚端口时钟使能 */
    __HAL_RCC_DAC_CLK_ENABLE();                                             /* 使能DAC1的时钟 */
    __HAL_RCC_DMA2_CLK_ENABLE();                                            /* DMA2时钟使能 */

    gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5;              /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;                               /* 模拟 */
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);

    /* 初始化DMA */
    g_dma_dac_handle.Instance = dmax_chy;                                   /* 设置DMA通道 */
    g_dma_dac_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;                 /* 从存储器到外设模式 */
    g_dma_dac_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_dac_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_dac_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_dac_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_dac_handle.Init.Mode = DMA_CIRCULAR;                              /* 外设流控模式 */
    g_dma_dac_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_dac_handle);                                        /* 初始化DMA */
    HAL_DMA_Start(&g_dma_dac_handle, mar, par, 0);                          /* 配置DMA传输参数 */

    __HAL_LINKDMA(&g_dac_dma_handle, DMA_Handle1, g_dma_dac_handle);        /* DMA句柄与DAC句柄关联 */

    /* 初始化DAC */
    g_dac_dma_handle.Instance = DAC;
    HAL_DAC_Init(&g_dac_dma_handle);                                        /* 初始化DAC */

    /* 配置DAC通道 */
    dac_ch_conf.DAC_Trigger = DAC_TRIGGER_T7_TRGO;                          /* 不使用触发功能 */
    dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;                /* DAC1输出缓冲关闭 */

    switch(outx)
    {
        case 1:
            HAL_DAC_ConfigChannel(&g_dac_dma_handle, &dac_ch_conf, DAC_CHANNEL_1);   /* 配置DAC通道1 */
            break;
        case 2:
            HAL_DAC_ConfigChannel(&g_dac_dma_handle, &dac_ch_conf, DAC_CHANNEL_2);   /* 配置DAC通道2 */
            break;
        default:break;
    }
}

/**
 * @brief       DAC DMA使能波形输出
 *   @note      TIM7的输入时钟频率(f)来自APB1, f = 36M * 2 = 72Mhz.
 *              DAC触发频率 ftrgo = f / ((psc + 1) * (arr + 1))
 *              波形频率 = ftrgo / ndtr; 
 *
 * @param       ndtr        : DMA通道单次传输数据量
 * @param       arr         : TIM7的自动重装载值
 * @param       psc         : TIM7的分频系数
 * @retval      无
 */
void dac_dma_wave_enable(uint16_t cndtr, uint16_t arr, uint16_t psc)
{
    TIM_HandleTypeDef tim7_handle= {0};
    TIM_MasterConfigTypeDef tim_master_config= {0};
    
    __HAL_RCC_TIM7_CLK_ENABLE();                              /* TIM7时钟使能 */ 

    tim7_handle.Instance = TIM7;                              /* 选择定时器7 */
    tim7_handle.Init.Prescaler = psc;                         /* 预分频 */
    tim7_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 递增计数器 */
    tim7_handle.Init.Period = arr;                            /* 自动装载值 */
    HAL_TIM_Base_Init(&tim7_handle);                          /* 初始化定时器7 */

    tim_master_config.MasterOutputTrigger = TIM_TRGO_UPDATE;                  /* 定时器更新事件用于触发 */
    tim_master_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&tim7_handle, &tim_master_config);  /* 配置定时器7的更新事件触发DAC转换 */
    HAL_TIM_Base_Start(&tim7_handle);                                         /* 启动定时器7 */

    HAL_DAC_Stop_DMA(&g_dac_dma_handle, DAC_CHANNEL_1);                       /* 先停止之前的传输 */
    HAL_DAC_Start_DMA(&g_dac_dma_handle, DAC_CHANNEL_1, (uint32_t *)g_dac_sin_buf, cndtr, DAC_ALIGN_12B_R);
}

dac.h

#ifndef __DAC_H
#define __DAC_H

#include "./SYSTEM/sys/sys.h"


void dac_init(uint8_t outx);                        /* DAC通道1初始化 */ 
void dac_set_voltage(uint8_t outx, uint16_t vol);   /* 设置通道1/2输出电压 */

void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n);   /* 输出三角波 */

void dac_dma_wave_init(uint8_t outx, uint32_t par, uint32_t mar);                       /* DAC DMA输出波形初始化函数 */
void dac_dma_wave_enable(uint16_t cndtr, uint16_t arr, uint16_t psc);                   /* DAC DMA输出波形使能 */

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DAC/dac.h"
#include "./BSP/ADC/adc3.h"
#include "math.h"


extern ADC_HandleTypeDef g_adc3_handle;  /* ADC句柄 */

uint16_t g_dac_sin_buf[4096];            /* 发送数据缓冲区 */

/**
 * @brief       产生正弦波函序列
 *   @note      需保证: maxval > samples/2
 *
 * @param       maxval : 最大值(0 < maxval < 2048)
 * @param       samples: 采样点的个数
 *
 * @retval      无
 */
void dac_creat_sin_buf(uint16_t maxval, uint16_t samples)
{
    uint8_t i;
    float inc = (2 * 3.1415962) / samples; /* 计算增量(一个周期DAC_SIN_BUF个点)*/
    float outdata = 0;

    for (i = 0; i < samples; i++)
    {
        outdata = maxval * (1 + sin(inc * i)); /* 计算以dots个点为周期的每个点的值,放大maxval倍,并偏移到正数区域 */
        if (outdata > 4095)
            outdata = 4095; /* 上限限定 */
        //printf("%f\r\n",outdata);
        g_dac_sin_buf[i] = outdata;
    }
}

/**
 * @brief       通过USMART设置正弦波输出参数,方便修改输出频率.
 * @param       arr : TIM7的自动重装载值
 * @param       psc : TIM7的分频系数
 * @retval      无
 */
void dac_dma_sin_set(uint16_t arr, uint16_t psc)
{
    dac_dma_wave_enable(100, arr, psc);
}

int main(void)
{
    uint16_t adcx;
    float temp;
    uint8_t t = 0;
    uint8_t key;

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    usmart_dev.init(72);                /* 初始化USMART */
    led_init();                         /* 初始化LED */
    lcd_init();                         /* 初始化LCD */
    key_init();                         /* 初始化按键 */
    adc3_init();                        /* 初始化ADC */

    adc3_channel_set(&g_adc3_handle, ADC3_CHY, ADC_CHANNEL_0, ADC_SAMPLETIME_1CYCLE_5);

    /* 初始化DAC通道1 DMA波形输出 */
    dac_dma_wave_init(1, (uint32_t)&DAC1->DHR12R1, (uint32_t)g_dac_sin_buf);

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "DAC DMA Sine WAVE TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY0:3Khz  KEY1:30Khz", RED);

    lcd_show_string(30, 130, 200, 16, 16, "DAC VAL:", BLUE);
    lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);
    lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);

    dac_creat_sin_buf(2048, 100);
    dac_dma_wave_enable(100, 10 - 1, 72 - 1);               /* 100Khz触发频率, 100个点, 得到1Khz的正弦波 */

    while (1)
    {
        t++;
        key = key_scan(0);                                  /* 按键扫描 */

        if (key == KEY0_PRES)                               /* 高采样率 , 约1Khz波形 */
        {
            dac_creat_sin_buf(2048, 100);
            dac_dma_wave_enable(100, 10 - 1, 24 - 1);       /* 300Khz触发频率, 100个点, 得到最高3KHz的正弦波. */
        }
        else if (key == KEY1_PRES)                          /* 低采样率 , 约1Khz波形 */
        {
            dac_creat_sin_buf(2048, 10);
            dac_dma_wave_enable(10, 10 - 1, 24 - 1);        /* 300Khz触发频率, 10个点, 可以得到最高30KHz的正弦波. */
        }

        adcx = DAC1->DHR12R1;                               /* 获取DAC1_OUT1的输出状态 */
        lcd_show_xnum(94, 130, adcx, 4, 16, 0, BLUE);       /* 显示DAC寄存器值 */

        temp = (float)adcx * (3.3 / 4096);                  /* 得到DAC电压值 */
        adcx = temp;
        lcd_show_xnum(94, 150, temp, 1, 16, 0, BLUE);       /* 显示电压值整数部分 */

        temp -= adcx;
        temp *= 1000;
        lcd_show_xnum(110, 150, temp, 3, 16, 0X80, BLUE);   /* 显示电压值的小数部分 */

        adcx = adc3_get_result_average(ADC3_CHY, 10);       /* 得到ADC3通道1的转换结果 */
        temp = (float)adcx * (3.3 / 4096);                  /* 得到ADC电压值(adc是12bit的) */
        adcx = temp;
        lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE);       /* 显示电压值整数部分 */

        temp -= adcx;
        temp *= 1000;
        lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE);   /* 显示电压值的小数部分 */

        if (t == 40)        /* 定时时间到了 */
        {
            LED0_TOGGLE();  /* LED0闪烁 */
            t = 0;
        }

        delay_ms(5);
    }
}

六、PWM DAC实验(熟悉)

6.1, PWM DAC应用背景(了解)

在这里插入图片描述

6.2, PWM DAC技术实现原理(了解)

6.2.1,什么是PWM DAC技术?

在这里插入图片描述

6.2.2,用分段函数表示PWM波

在这里插入图片描述
在这里插入图片描述

6.2.3,将PWM波分段函数进行傅里叶级数展开

在这里插入图片描述

6.2.4,PWM DAC的分辨率

在这里插入图片描述

6.2.5,8位分辨率下对RC滤波器的设计要求

在这里插入图片描述

6.2.6,PWM DAC二阶低通滤波器原理图

在这里插入图片描述

6.3,编程实战: PWM DAC实验(掌握)

在这里插入图片描述

adc3.c

#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"


ADC_HandleTypeDef g_adc3_handle;         /* ADC句柄 */

/**
 * @brief       ADC3初始化函数
 *   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
 *              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
 *              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
 * @param       无
 * @retval      无
 */
void adc3_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    RCC_PeriphCLKInitTypeDef adc_clk_init = {0};

    ADC3_CHY_GPIO_CLK_ENABLE();                                /* IO口时钟使能 */
    ADC3_CHY_CLK_ENABLE();                                     /* ADC时钟使能 */

    adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;     /* ADC外设时钟 */
    adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;        /* 分频因子6时钟为72M/6=12MHz */
    HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                  /* 设置ADC时钟 */

    /* 设置AD采集通道对应IO引脚工作模式 */
    gpio_init_struct.Pin = ADC3_CHY_GPIO_PIN;                  /* ADC通道对应的IO引脚 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;                  /* 模拟 */
    HAL_GPIO_Init(ADC3_CHY_GPIO_PORT, &gpio_init_struct);

    g_adc3_handle.Instance = ADC_ADCX;                         /* 选择哪个ADC */
    g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;        /* 数据对齐方式:右对齐 */
    g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;        /* 非扫描模式,仅用到一个通道 */
    g_adc3_handle.Init.ContinuousConvMode = DISABLE;           /* 关闭连续转换模式 */
    g_adc3_handle.Init.NbrOfConversion = 1;                    /* 1个转换在规则序列中 也就是只转换规则序列1 */
    g_adc3_handle.Init.DiscontinuousConvMode = DISABLE;        /* 禁止规则通道组间断模式 */
    g_adc3_handle.Init.NbrOfDiscConversion = 0;                /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
    g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;  /* 触发转换方式:软件触发 */
    HAL_ADC_Init(&g_adc3_handle);                              /* 初始化 */

    HAL_ADCEx_Calibration_Start(&g_adc3_handle);               /* 校准ADC */
}

/**
 * @brief       设置ADC通道采样时间
 * @param       adcx : adc句柄指针,ADC_HandleTypeDef
 * @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
 * @param       stime: 采样时间  0~7, 对应关系为:
 *   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期
 * @param       rank: 多通道采集时需要设置的采集编号,
                假设你定义channle1的rank=1,channle2 的rank=2,
                那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 
                单通道DMA设置为 ADC_REGULAR_RANK_1
 *   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
 * @retval      无
 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    ADC_ChannelConfTypeDef adc_ch_conf;
    
    adc_ch_conf.Channel = ch;                            /* 通道 */
    adc_ch_conf.Rank = rank;                             /* 序列 */
    adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */
    HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}

/**
 * @brief       获得ADC转换后的结果
 * @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17
 * @retval      无
 */
uint32_t adc3_get_result(uint32_t ch)
{
    adc3_channel_set(&g_adc3_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */
    
    HAL_ADC_Start(&g_adc3_handle);                            /* 开启ADC */
    HAL_ADC_PollForConversion(&g_adc3_handle, 10);            /* 轮询转换 */
    return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}

/**
 * @brief       获取通道ch的转换值,取times次,然后平均
 * @param       ch      : 通道号, 0~17
 * @param       times   : 获取次数
 * @retval      通道ch的times次转换结果平均值
 */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;

    for (t = 0; t < times; t++)     /* 获取times次数据 */
    {
        temp_val += adc3_get_result(ch);
        delay_ms(5);
    }

    return temp_val / times;        /* 返回平均值 */
}

adc3.h

#ifndef __ADC_H
#define __ADC_H

#include "./SYSTEM/sys/sys.h"

/******************************************************************************************/
/* ADC及引脚 定义 */

#define ADC3_CHY_GPIO_PORT                  GPIOA
#define ADC3_CHY_GPIO_PIN                   GPIO_PIN_1 
#define ADC3_CHY_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */

#define ADC_ADCX                            ADC3 
#define ADC3_CHY                            ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC3_CHY_CLK_ENABLE()               do{ __HAL_RCC_ADC3_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 */


/******************************************************************************************/

void adc3_init(void);                                          /* ADC3初始化 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);   /* ADC3通道设置 */
uint32_t adc3_get_result(uint32_t ch);                         /* 获得某个通道值  */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times);  /* 得到某个通道给定次数采样的平均值 */

#endif 

pwmdac.c

#include "./BSP/PWMDAC/pwmdac.h"


TIM_HandleTypeDef g_tim1_handle;         /* 定时器句柄 */

/**
 * @brief       PWM DAC初始化, 实际上就是初始化定时器
 * @note
 *              定时器的时钟来自APB1 / APB2, 当APB1 / APB2 分频时, 定时器频率自动翻倍
 *              所以, 一般情况下, 我们所有定时器的频率, 都是72Mhz 等于系统时钟频率
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft = 定时器工作频率, 单位: Mhz
 *
 * @param       arr: 自动重装值。
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void pwmdac_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef timx_oc_pwmdac = {0};

    PWMDAC_TIMX_CLK_ENABLE();                                                     /* PWM DAC 定时器时钟使能 */

    g_tim1_handle.Instance = TIM1;                                                /* 定时器1 */
    g_tim1_handle.Init.Prescaler = psc;                                           /* 定时器分频 */
    g_tim1_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                          /* 递增计数模式 */
    g_tim1_handle.Init.Period = arr;                                              /* 自动重装载值 */
    g_tim1_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;         /* 使能TIMx_ARR进行缓冲 */
    HAL_TIM_PWM_Init(&g_tim1_handle);                                             /* 初始化PWM */

    timx_oc_pwmdac.OCMode = TIM_OCMODE_PWM1;                                      /* CH1/2 PWM模式1 */
    timx_oc_pwmdac.Pulse = 0;                                                     /* 设置比较值,此值用来确定占空比 */
    timx_oc_pwmdac.OCPolarity = TIM_OCPOLARITY_HIGH;                              /* 输出比较极性为高 */
    HAL_TIM_PWM_ConfigChannel(&g_tim1_handle, &timx_oc_pwmdac, PWMDAC_TIMX_CHY);  /* 配置TIM1通道1 */

    HAL_TIM_PWM_Start(&g_tim1_handle, TIM_CHANNEL_1);                             /* 开启定时器1通道1 */
}

/**
 * @brief       定时器底层驱动,时钟使能,引脚配置
 * @note
 *              此函数会被HAL_TIM_PWM_Init()调用
 * @param       htim:定时器句柄
 * @retval      无
 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (htim->Instance == TIM1)
    {
        __HAL_RCC_TIM1_CLK_ENABLE();                           /* 使能定时器1 */
        __HAL_AFIO_REMAP_TIM1_PARTIAL();                       /* TIM1通道引脚部分重映射使能 */
        PWMDAC_GPIO_CLK_ENABLE();                              /* GPIO 时钟使能 */

        gpio_init_struct.Pin = PWMDAC_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(PWMDAC_GPIO_PORT, &gpio_init_struct);    /* TIMX PWM CHY 引脚模式设置 */
    }
}

/**
 * @brief       设置PWM DAC输出电压
 * @param       vol : 0~3300,代表0~3.3V
 * @retval      无
 */
void pwmdac_set_voltage(uint16_t vol)
{
    float temp = vol;
    temp /= 100;                                                    /* 缩小100倍, 得到实际电压值 */
    temp = temp * 256 / 3.3;                                        /* 将电压转换成PWM占空比 */
    __HAL_TIM_SET_COMPARE(&g_tim1_handle, PWMDAC_TIMX_CHY, temp);   /* 设置新的占空比 */
}

pwmdac.h

#ifndef __PWMDAC_H
#define __PWMDAC_H

#include "./SYSTEM/sys/sys.h"

/******************************************************************************************/
/* PWM DAC 引脚 和 定时器 定义 */

/* PWMDAC 默认是使用 PA8, 对应的定时器为 TIM1_CH1, 如果你要修改成其他IO输出, 则相应
 * 的定时器及通道也要进行修改. 请根据实际情况进行修改.
 */
#define PWMDAC_GPIO_PORT                    GPIOA
#define PWMDAC_GPIO_PIN                     GPIO_PIN_8
#define PWMDAC_GPIO_CLK_ENABLE()            do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PA口时钟使能 */

#define PWMDAC_TIMX                         TIM1
#define PWMDAC_TIMX_CHY                     TIM_CHANNEL_1                               /* 通道Y,  1<= Y <=4 */
#define PWMDAC_TIMX_CCRX                    PWMDAC_TIMX->CCR1                           /* 通道Y的输出比较寄存器 */
#define PWMDAC_TIMX_CLK_ENABLE()            do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0)  /* TIM1 时钟使能 */

/******************************************************************************************/

void pwmdac_init(uint16_t arr, uint16_t psc);   /* PWM DAC初始化 */
void pwmdac_set_voltage(uint16_t vol);          /* PWM DAC设置输出电压 */

#endif

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

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

相关文章

用结构减法比较平面上4点结构的顺序

( A, B )---6*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有6个节点&#xff0c;AB训练集各由6张二值化的图片组成&#xff0c;A有4个点&#xff0c;B全是0.收敛误差7e-4&#xff0c;收敛199次&#xff0c;统计迭代次数平均值并排序。 如果行和列自由变换&#xff0c;迭代次数不…

英伟达推出免训练,可生成连贯图片的文生图模型

目前&#xff0c;多数文生图模型皆使用的是随机采样模式&#xff0c;使得每次生成的图像效果皆不同&#xff0c;在生成连贯的图像方面非常差。 例如&#xff0c;想通过AI生成一套图像连环画&#xff0c;即便使用同类的提示词也很难实现。虽然DALLE 3和Midjourney可以对图像实现…

【UI自动化】使用poco框架进行元素唯一定位

直接选择&#xff1a; 1.poco(text买入).click() 2.poco("android.widget.ImageView").click()相对选择、空间选择&#xff1a; 3.poco(text/name).parent().child()[0].click()正则表达式&#xff1a; 4.listpoco(textMatches".*ETF")今天主要想记录下…

门控时钟基础知识

什么是门控时钟 RTL中的门控时钟 通常情况下&#xff0c;时钟树由大量的缓冲器和反相器组成&#xff0c;时钟信号为设计中翻转率最高的信号&#xff0c;时钟树的功耗可能高达整个设计功耗40%。 加入门控时钟电路后&#xff0c;由于减少了时钟树的翻转&#xff0c;节省了翻转功…

windows下采用 nginx配置websocket支持wss流程

第一步、安装OpenSSL &#xff08;1&#xff09;下载OpenSSL软件包 地址&#xff1a;https://slproweb.com/products/Win32OpenSSL.html OpenSSL版本说明&#xff1a; Win64 OpenSSL v1.1.1wLight&#xff0c;安装Win64 OpenSSL v1.1.1w最常用的软件包 Win64 OpenSSL v1.1…

真实的数据如何获取

数据 在信息过载的互联网信息中挖掘有价值的信息是非常难的。 我自己退一步&#xff0c;要想寻求真实的信息也是非常难的。 那么关于个人博客具体浏览量的真实信息到底如何呢&#xff1f; 请看下图&#xff1a; 真相残酷&#xff0c;现实骨感。 核心 如果要抛开自欺欺人&a…

第二证券:5天骤降14!转融券市场再降温

转融券存量在快速下降。 到2月21日&#xff0c;转融券的存量股数为50.36亿股&#xff0c;近5个交易日内下降超20%。券商两融人士向券商我国记者表明&#xff0c;2月6日&#xff0c;证监会表明暂停转融券新增规划&#xff0c;存量逐渐了断&#xff0c;从目前的数据来看&#xf…

[corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape

前言 题目来源&#xff1a;竞赛官网 – 建议这里下载&#xff0c;文件系统/带符号的 vmlinux 给了 参考 [corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape Exploiting poll_list Objects In The Linux Kernel – 原作者文章&#xff0c;poll_list 利用方式…

7 数据迁移至达梦数据库

无论使用哪种解决方案很大可能性都需要进行数据迁移&#xff0c;即将旧的非 达梦数据库的数据迁移到达梦数据库。 我们要把 Nacos 的数据或者 SQL 语句迁移到达梦数据库。借助 DM 数据迁移工具 &#xff0c;完成 Nacos 配置数据表迁移到达梦数据库。

ros自定义action记录

文章目录 自定义action1. 定义action文件2. 修改 package.xml3. 修改 CMakeLists.txt4. 运行 catkin build5. simple_action_server.py6. simple_action_client.py 测试 自定义action ros 版本&#xff1a;kinetic 自定义test包的文件结构如下 |-- test | |-- CMakeLists.t…

【9-1】实验——Neo4j实战操作

目录 一、Neo4j操作——CQL 1、常用CQL命令 2.常用CQL函数 3.图数据的形式 二、实战代码1.create命令 2. MATCH命令 三、使用neo4j工具导入知识图谱 1、工具&#xff1a;neo4j-admin 2、图谱导入&#xff1a; 3、更新图谱&#xff1a; 一、Neo4j操作——CQL 1、常用…

RMAN备份与恢复

文章目录 一、RMAN介绍二、全量备份三、增量备份0级备份1级增量备份累积性差量备份总结 四、压缩备份压缩备份介绍压缩备份操作压缩备份优缺点 五、异常恢复1、恢复前的准备2、恢复数据库 六、RMAN相关参数 一、RMAN介绍 RMAN&#xff08;Recovery Manager&#xff09;是Oracl…

啤酒:精酿啤酒与炸鸡的香脆搭配

炸鸡与啤酒&#xff0c;这对美食界的黄金搭档&#xff0c;早已成为了人们心目中的经典。而当Fendi Club啤酒遇上炸鸡&#xff0c;一场味觉的狂欢就此展开。 Fendi Club啤酒&#xff0c;以其醇厚的口感和淡淡的麦芽香气而著称。这款啤酒在酿造过程中采用了特别的工艺&#xff0c…

Android加载富文本

直接用webview加载&#xff1a; package com.example.testcsdnproject;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.webk…

模型转换案例学习:等效替换不支持算子

文章介绍 Qualcomm Neural Processing SDK &#xff08;以下简称SNPE&#xff09;支持Caffe、ONNX、PyTorch和TensorFlow等不同ML框架的算子。对于某些特定的不支持的算子&#xff0c;我们介绍一种算子等效替换的方法来完成模型转换。本案例来源于https://github.com/quic/qidk…

js谐音梗创意小游戏《望子成龙》

&#x1f33b; 前言 龙年到来&#xff0c;祥瑞满天。愿您如龙般矫健&#xff0c;事业腾飞&#xff1b;如龙鳞闪耀&#xff0c;生活美满。祝您龙年大吉&#xff0c;万事如意&#xff01; 龙年伊始&#xff0c;我给各位设计了一款原创的小游戏&#xff0c;话不多说&#xff0c;直…

D5020——外围元件少,内含压缩器和扩展器静噪电路,可应用在1.5V立体声耳机上,响应时间可调

D5020是一块增益可调 的压缩、扩展电路。它有两个通道组成&#xff0c;一个通道作扩展用&#xff0c;另一个通道能作压缩或扩展用。电路内部含有小信号全波整流、检测信号的大小&#xff0c;用于调节输入或反馈通道的增益大小。含有温度特性较好的带隙精密基准源&#xff0c;静…

Leetcode 11.盛水最多的容器

题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容器。…

浏览器---浏览器/http相关面试题

1.localStorage和sessionStorage 共同点&#xff1a;二者都是以key-value的键值对方式存储在浏览器端&#xff0c;大小大概在5M。 区别&#xff1a; &#xff08;1&#xff09;数据有效期不同&#xff1a;sessionStorage仅在当前浏览器窗口关闭之前有效&#xff1b;localStorag…

C语言第二十八弹---整数在内存中的存储

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 目录 1、整数在内存中的存储 2、大小端字节序和字节序 2.1、什么是大小端&#xff1f; 2.2、为什么有大小端? 2.3、练习 2.3.1、练习1 2.3.2、练习2 2.…