Bootstrap

STM32——ADC篇(基于stm32f103)

技术笔记!
一、ADC简介(了解)

1.1,什么是ADC

1.2,常见的ADC类型

1.3,并联比较型工作示意图

1.4,逐次逼近型工作示意图

1.5,ADC的特性参数

1.6,STM32各系列ADC的主要特性

二、ADC工作原理(掌握)

2.1,ADC框图简介

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

2.3,输入通道

2.4,转换序列

2.5,触发源

2.6,转换时间

 

2.7,数据寄存器

2.8,中断

2.9,单次转换模式和连续转换模式

2.10,扫描模式

三、单通道ADC采集实验(熟悉)

3.1,实验简要(了解)

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

3.3,单通道ADC采集实验配置步骤(掌握)

3.4,编程实战:单通道ADC采集实验(掌握)

mian.c

int main(void)
{
    uint16_t adcx;
    float temp;
    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    //lcd_init();                             /* 初始化LCD */
    adc_init();                             /* 初始化ADC */

    //lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    //lcd_show_string(30, 70, 200, 16, 16, "ADC TEST", RED);
    //lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    //lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH1_VAL:", BLUE);
    //lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    while (1)
    {
        adcx = adc_get_result();
        //lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE);  /* 显示ADCC采样后的原始值 */
        printf("原始值:%d\n",adcx);
 //void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color)
        temp = (float)adcx * (3.3 / 4096);              /* 获取计算后的带小数的实际电压值,比如3.1111 */
        adcx = temp;                                    /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
        //lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);  /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */
        printf("电压值(整数):%d\n",adcx);
        temp -= adcx;                                   /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
        temp *= 1000;                                   /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
        //lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);/* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */
        printf("电压值(小数):%d\n",(int)temp);
        LED0_TOGGLE();
        delay_ms(100);
    }
}

adc.c

ADC_HandleTypeDef g_adc_handle;     //句柄

/* ADC单通道 */
void adc_init(void)
{
    ADC_ChannelConfTypeDef adc_ch_conf;         //通道

    g_adc_handle.Instance = ADC1;               //基地址
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;      //数据对齐方式
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;      //扫描模式
    g_adc_handle.Init.ContinuousConvMode = DISABLE;         //连续模式
    g_adc_handle.Init.NbrOfConversion = 1;                  //转换数量
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;      //间断模式
    g_adc_handle.Init.NbrOfDiscConversion = 0;              //间断数量
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;//外部触发源选择
    HAL_ADC_Init(&g_adc_handle);                //ADC初始化
    
    HAL_ADCEx_Calibration_Start(&g_adc_handle);  //ADC校验
    
    adc_ch_conf.Channel = ADC_CHANNEL_1;        //通道选择
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;       //转换顺序
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;//ADC采样周期
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);//ADC通道配置
}

/* ADC MSP初始化函数 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)      //判断是否对应地址
    {
        GPIO_InitTypeDef gpio_init_struct;      //GPIO结构日体
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};//外部时钟 
        
        __HAL_RCC_GPIOA_CLK_ENABLE();       //GPIO时钟使能
        __HAL_RCC_ADC1_CLK_ENABLE();        //ADC1使能

        gpio_init_struct.Pin = GPIO_PIN_1;      //引脚
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;   //模拟模式
        HAL_GPIO_Init(GPIOA, &gpio_init_struct); 
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;//时钟源
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;//分频
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
}

/* 获得ADC转换后的结果函数 */
uint32_t adc_get_result(void)
{
    HAL_ADC_Start(&g_adc_handle);       //开始ADC
    HAL_ADC_PollForConversion(&g_adc_handle, 10);  //等待规则通道转换完成
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);       //返回结果
}

四、单通道ADC采集(DMA读取)实验(熟悉)

4.1  实验简要

4.2  实验配置步骤

4.3  编程实战

adc.c

#include "./BSP/ADC/adc.h"


DMA_HandleTypeDef g_dma_adc_handle;         //DMA句柄
ADC_HandleTypeDef g_adc_dma_handle;         //ADC句柄
uint8_t g_adc_dma_sta;

/* ADC DMA读取 初始化函数 */
void adc_dma_init(uint32_t mar)
{
    ADC_ChannelConfTypeDef adc_ch_conf;     //ADC通道结构体
    
    __HAL_RCC_DMA1_CLK_ENABLE();            //DMA1时钟使能
    
    g_dma_adc_handle.Instance = DMA1_Channel1;      //DMA1通道1的基地址
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;     //传输方向
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;     //外设(非)增量模式
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;         //存储器(非)增量模式
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据宽度
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       //存储器宽度
    g_dma_adc_handle.Init.Mode = DMA_NORMAL;        //操作模式
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;       //DMA优先级
    HAL_DMA_Init(&g_dma_adc_handle);        //通过HAL库实现对DMA的初始化
    
    //将DMA和ADC句柄联系起来
    __HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);

    g_adc_dma_handle.Instance = ADC1;       //ADC1的基地址
    g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;      //数据对齐方式,通常使用右对齐
    g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;      //扫描模式
    g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;          //是否开启连续扫描
    g_adc_dma_handle.Init.NbrOfConversion = 1;                  //转换通道数目
    g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;      //是否使用规则通道组间断模式
    g_adc_dma_handle.Init.NbrOfDiscConversion = 0;              //间断规则通道个数
    g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;    //ADC外部触发源选择
    HAL_ADC_Init(&g_adc_dma_handle);                //通过HAL库实现对ADC的初始化
    
    HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);     //ADC校准
    
    adc_ch_conf.Channel = ADC_CHANNEL_1;        //ADC转换通道选择
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;      //ADC转换顺序
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;      //ADC采样周期
    HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);     //ADC通道配置
    
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3);     //中断优先级
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);             //中断使能

    HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);   //启动DMA,开启传输完成中断
    HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar ,0);                      //触发ADC转换,DMA传输数据
}

/* ADC MSP初始化函数 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)
    {
        GPIO_InitTypeDef gpio_init_struct;
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_ADC1_CLK_ENABLE();

        gpio_init_struct.Pin = GPIO_PIN_1;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct); 
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
}

/* 使能一次ADC DMA传输函数 */
void adc_dma_enable(uint16_t cndtr)
{
    ADC1->CR2 &= ~(1 << 0);
    
    DMA1_Channel1->CCR &= ~(1 << 0);
    while (DMA1_Channel1->CCR & (1 << 0));
    DMA1_Channel1->CNDTR = cndtr;
    DMA1_Channel1->CCR |= 1 << 0;

    ADC1->CR2 |= 1 << 0;
    ADC1->CR2 |= 1 << 22;
    
//    __HAL_ADC_DISABLE(&g_adc_dma_handle);
//    
//    __HAL_DMA_DISABLE(&g_dma_adc_handle);
//    while (__HAL_DMA_GET_FLAG(&g_dma_adc_handle, __HAL_DMA_GET_TC_FLAG_INDEX(&g_dma_adc_handle)));
//    DMA1_Channel1->CNDTR = cndtr;
//    __HAL_DMA_ENABLE(&g_dma_adc_handle);
//    
//    __HAL_ADC_ENABLE(&g_adc_dma_handle);
//    HAL_ADC_Start(&g_adc_dma_handle);
}

/* ADC DMA采集中断服务函数 */
void DMA1_Channel1_IRQHandler(void)
{
    if (DMA1->ISR & (1<<1))
    {
        g_adc_dma_sta = 1;
        DMA1->IFCR |= 1 << 1;
    }
}

main.c

include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"


#define ADC_DMA_BUF_SIZE        100         /* ADC DMA采集 BUF大小 */
uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];   /* ADC DMA BUF */

extern uint8_t g_adc_dma_sta;               /* DMA传输状态标志, 0,未完成; 1, 已完成 */

int main(void)
{
    uint16_t i;
    uint16_t adcx;
    uint32_t sum;
    float temp;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */

    adc_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "ADC DMA TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH1_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    adc_dma_enable(ADC_DMA_BUF_SIZE);   /* 启动ADC DMA采集 */

    while (1)
    {
        if (g_adc_dma_sta == 1)
        {
            /* 计算DMA 采集到的ADC数据的平均值 */
            sum = 0;

            for (i = 0; i < ADC_DMA_BUF_SIZE; i++)   /* 累加 */
            {
                sum += g_adc_dma_buf[i];
            }

            adcx = sum / ADC_DMA_BUF_SIZE;           /* 取平均值 */

            /* 显示结果 */
            lcd_show_xnum(134, 110, adcx, 4, 16, 0, BLUE);      /* 显示ADCC采样后的原始值 */

            temp = (float)adcx * (3.3 / 4096);                  /* 获取计算后的带小数的实际电压值,比如3.1111 */
            adcx = temp;                                        /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
            lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);      /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

            temp -= adcx;                                       /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
            temp *= 1000;                                       /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
            lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);   /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */

            g_adc_dma_sta = 0;                                  /* 清除DMA采集完成状态标志 */
            adc_dma_enable(ADC_DMA_BUF_SIZE);                   /* 启动下一次ADC DMA采集 */
        }

        LED0_TOGGLE();
        delay_ms(100);
    }
}

五、多通道ADC采集(DMA读取)实验(熟悉)

5.1  实验简要

5.2  编程实战

adc.c

#include "./BSP/ADC/adc.h"


DMA_HandleTypeDef g_dma_nch_adc_handle;
ADC_HandleTypeDef g_adc_nch_dma_handle;
uint8_t g_adc_dma_sta;

/* ADC N通道(6通道) DMA读取 初始化函数 */
void adc_nch_dma_init(uint32_t mar)
{
    ADC_ChannelConfTypeDef adc_ch_conf;
    
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    g_dma_nch_adc_handle.Instance = DMA1_Channel1;   //基地址
    g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;  //传输方向
    g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;         //外设增量
    g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE;             //地址增量
    g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;//外设数据长度
    g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;//内存数据长度
    g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL;//模式
    g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//优先级
    HAL_DMA_Init(&g_dma_nch_adc_handle);            //DMA初始化
    
    //DMA和ADC句柄连接
    __HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle);

    g_adc_nch_dma_handle.Instance = ADC1;    //基地址
    g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;  //数据对齐方式 
    g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;       //扫描转换方式
    g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;          //连续转换模式
    g_adc_nch_dma_handle.Init.NbrOfConversion = 6;                  //连续转换个数
    g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;      //规则通道组间断模式
    g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0;          //间断模式规则通道个数
    g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;    //ADC外部触发源选择
    HAL_ADC_Init(&g_adc_nch_dma_handle);        //ADC初始化
    
    HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle);     //ADC校准
    
    adc_ch_conf.Channel = ADC_CHANNEL_0;        //转换通道
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;      //转换顺序
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;//采样周期
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); //配置相应通道参数
    
    adc_ch_conf.Channel = ADC_CHANNEL_1;        
    adc_ch_conf.Rank = ADC_REGULAR_RANK_2;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);
    
    adc_ch_conf.Channel = ADC_CHANNEL_2;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_3;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);
    
    adc_ch_conf.Channel = ADC_CHANNEL_3;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_4;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);
    
    adc_ch_conf.Channel = ADC_CHANNEL_4;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_5;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);
    
    adc_ch_conf.Channel = ADC_CHANNEL_5;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_6;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);
    
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3);     //中断优先级
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);             //使能中断

    HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0);//开启DMA中断
    HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar ,0);//启动A/D转换
}

/* ADC MSP初始化函数 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)
    {
        GPIO_InitTypeDef gpio_init_struct;
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_ADC1_CLK_ENABLE();

        gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct); 
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
}

/* 使能一次ADC DMA传输函数 */
void adc_dma_enable(uint16_t cndtr)
{
    ADC1->CR2 &= ~(1 << 0);
    
    DMA1_Channel1->CCR &= ~(1 << 0);
    while (DMA1_Channel1->CCR & (1 << 0));
    DMA1_Channel1->CNDTR = cndtr;
    DMA1_Channel1->CCR |= 1 << 0;

    ADC1->CR2 |= 1 << 0;
    ADC1->CR2 |= 1 << 22;
}

/* ADC DMA采集中断服务函数 */
void DMA1_Channel1_IRQHandler(void)
{
    if (DMA1->ISR & (1<<1))
    {
        g_adc_dma_sta = 1;
        DMA1->IFCR |= 1 << 1;
    }
}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"


#define ADC_DMA_BUF_SIZE        50 * 6      /* ADC DMA采集 BUF大小, 应等于ADC通道数的整数倍 */
uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];   /* ADC DMA BUF */

extern uint8_t g_adc_dma_sta;               /* DMA传输状态标志, 0,未完成; 1, 已完成 */

int main(void)
{
    uint16_t i,j;
    uint16_t adcx;
    uint32_t sum;
    float temp;

    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */

    adc_nch_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "ADC 6CH DMA TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);

    lcd_show_string(30, 110, 200, 12, 12, "ADC1_CH0_VAL:", BLUE);
    lcd_show_string(30, 122, 200, 12, 12, "ADC1_CH0_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */
    
    lcd_show_string(30, 140, 200, 12, 12, "ADC1_CH1_VAL:", BLUE);
    lcd_show_string(30, 152, 200, 12, 12, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    lcd_show_string(30, 170, 200, 12, 12, "ADC1_CH2_VAL:", BLUE);
    lcd_show_string(30, 182, 200, 12, 12, "ADC1_CH2_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    lcd_show_string(30, 200, 200, 12, 12, "ADC1_CH3_VAL:", BLUE);
    lcd_show_string(30, 212, 200, 12, 12, "ADC1_CH3_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    lcd_show_string(30, 230, 200, 12, 12, "ADC1_CH4_VAL:", BLUE);
    lcd_show_string(30, 242, 200, 12, 12, "ADC1_CH4_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    lcd_show_string(30, 260, 200, 12, 12, "ADC1_CH5_VAL:", BLUE);
    lcd_show_string(30, 272, 200, 12, 12, "ADC1_CH5_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    adc_dma_enable(ADC_DMA_BUF_SIZE);   /* 启动ADC DMA采集 */

    while (1)
    {
        if (g_adc_dma_sta == 1)
        {
            /* 循环显示通道0~通道5的结果 */
            for(j = 0; j < 6; j++)  /* 遍历6个通道 */
            {
                sum = 0; /* 清零 */
                for (i = 0; i < ADC_DMA_BUF_SIZE / 6; i++)  /* 每个通道采集了50次数据,进行50次累加 */
                {
                    sum += g_adc_dma_buf[(6 * i) + j];      /* 相同通道的转换数据累加 */
                }
                adcx = sum / (ADC_DMA_BUF_SIZE / 6);        /* 取平均值 */
                
                /* 显示结果 */
                lcd_show_xnum(108, 110 + (j * 30), adcx, 4, 12, 0, BLUE);   /* 显示ADC采样后的原始值 */

                temp = (float)adcx * (3.3 / 4096);  /* 获取计算后的带小数的实际电压值,比如3.1111 */
                adcx = temp;                        /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
                lcd_show_xnum(108, 122 + (j * 30), adcx, 1, 12, 0, BLUE);   /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

                temp -= adcx;                       /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
                temp *= 1000;                       /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
                lcd_show_xnum(120, 122 + (j * 30), temp, 3, 12, 0X80, BLUE);/* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */
            }
 
            g_adc_dma_sta = 0;                      /* 清除DMA采集完成状态标志 */
            adc_dma_enable(ADC_DMA_BUF_SIZE);       /* 启动下一次ADC DMA采集 */
        }

        LED0_TOGGLE();
        delay_ms(100);
    }
}

六、单通道ADC过采样实验(熟悉)

6.1  用过采样和求平均值的方式提高ADC的分辨率

6.2  实验就要

6.3  编程实战

adc.c

#include "./BSP/ADC/adc.h"


DMA_HandleTypeDef g_dma_adc_handle;
ADC_HandleTypeDef g_adc_dma_handle;
uint8_t g_adc_dma_sta;

/* ADC DMA读取 初始化函数 */
void adc_dma_init(uint32_t mar)
{
    ADC_ChannelConfTypeDef adc_ch_conf;
    
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    g_dma_adc_handle.Instance = DMA1_Channel1;
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    g_dma_adc_handle.Init.Mode = DMA_NORMAL;
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
    HAL_DMA_Init(&g_dma_adc_handle);
    
    __HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);

    g_adc_dma_handle.Instance = ADC1;
    g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;
    g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;
    g_adc_dma_handle.Init.NbrOfConversion = 1;
    g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;
    g_adc_dma_handle.Init.NbrOfDiscConversion = 0;
    g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    HAL_ADC_Init(&g_adc_dma_handle);
    
    HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);
    
    adc_ch_conf.Channel = ADC_CHANNEL_1;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
    HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);
    
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);
    HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar ,0);
}

/* ADC MSP初始化函数 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)
    {
        GPIO_InitTypeDef gpio_init_struct;
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_ADC1_CLK_ENABLE();

        gpio_init_struct.Pin = GPIO_PIN_1;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct); 
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
}

/* 使能一次ADC DMA传输函数 */
void adc_dma_enable(uint16_t cndtr)
{
    ADC1->CR2 &= ~(1 << 0);
    
    DMA1_Channel1->CCR &= ~(1 << 0);
    while (DMA1_Channel1->CCR & (1 << 0));
    DMA1_Channel1->CNDTR = cndtr;
    DMA1_Channel1->CCR |= 1 << 0;

    ADC1->CR2 |= 1 << 0;
    ADC1->CR2 |= 1 << 22;
    
//    __HAL_ADC_DISABLE(&g_adc_dma_handle);
//    
//    __HAL_DMA_DISABLE(&g_dma_adc_handle);
//    while (__HAL_DMA_GET_FLAG(&g_dma_adc_handle, __HAL_DMA_GET_TC_FLAG_INDEX(&g_dma_adc_handle)));
//    DMA1_Channel1->CNDTR = cndtr;
//    __HAL_DMA_ENABLE(&g_dma_adc_handle);
//    
//    __HAL_ADC_ENABLE(&g_adc_dma_handle);
//    HAL_ADC_Start(&g_adc_dma_handle);
}

/* ADC DMA采集中断服务函数 */
void DMA1_Channel1_IRQHandler(void)
{
    if (DMA1->ISR & (1<<1))
    {
        g_adc_dma_sta = 1;
        DMA1->IFCR |= 1 << 1;
    }
}

main.c

#define ADC_OVERSAMPLE_TIMES    256                         /* ADC过采样次数, 这里提高4bit分辨率, 需要256倍采样 */
#define ADC_DMA_BUF_SIZE        ADC_OVERSAMPLE_TIMES * 10   /* ADC DMA采集 BUF大小, 应等于过采样次数的整数倍 */

uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];                   /* ADC DMA BUF */

extern uint8_t g_adc_dma_sta;                               /* DMA传输状态标志, 0,未完成; 1, 已完成 */

int main(void)
{
    uint16_t i;
    uint32_t adcx;
    uint32_t sum;
    float temp;

    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */

    adc_dma_init((uint32_t)&g_adc_dma_buf);     /* 初始化ADC DMA采集 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "ADC OverSample TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH1_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    adc_dma_enable(ADC_DMA_BUF_SIZE);           /* 启动ADC DMA采集 */

    while (1)
    {
        if (g_adc_dma_sta == 1)
        {
            /* 计算DMA 采集到的ADC数据的平均值 */
            sum = 0;

            for (i = 0; i < ADC_DMA_BUF_SIZE; i++)   /* 累加 */
            {
                sum += g_adc_dma_buf[i];
            }

            adcx = sum / (ADC_DMA_BUF_SIZE / ADC_OVERSAMPLE_TIMES); /* 取平均值 */
            adcx >>= 4;   /* 除以2^4倍, 得到12+4位 ADC精度值, 注意: 提高 N bit精度, 需要 >> N */

            /* 显示结果 */
            lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE);      /* 显示ADC采样后的原始值 */

            temp = (float)adcx * (3.3 / 65536);                 /* 获取计算后的带小数的实际电压值,比如3.1111 */
            adcx = temp;                                        /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
            lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);      /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

            temp -= adcx;                                       /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
            temp *= 1000;                                       /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
            lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);   /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */

            g_adc_dma_sta = 0;                                  /* 清除DMA采集完成状态标志 */
            adc_dma_enable(ADC_DMA_BUF_SIZE);                   /* 启动下一次ADC DMA采集 */
        }

        LED0_TOGGLE();
        delay_ms(100);
    }
}

七、内部温度传感器实验(熟悉)

7.1  STM32内部温度传感器简介(了解)

7.2  温度计算方法(熟悉)

f1系列:

7.3  实验简要(了解)

7.4  编程实战:内部温度传感器实验(掌握)

adc.c

#include "./BSP/ADC/adc.h"


ADC_HandleTypeDef g_adc_handle;

/* ADC 内部温度传感器 初始化函数 */
void adc_temperature_init(void)
{
    ADC_ChannelConfTypeDef adc_ch_conf;

    g_adc_handle.Instance = ADC1;
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;
    g_adc_handle.Init.ContinuousConvMode = DISABLE;
    g_adc_handle.Init.NbrOfConversion = 1;
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;
    g_adc_handle.Init.NbrOfDiscConversion = 0;
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    HAL_ADC_Init(&g_adc_handle);
    
    HAL_ADCEx_Calibration_Start(&g_adc_handle);
    
    adc_ch_conf.Channel = ADC_CHANNEL_16;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);
}

/* ADC MSP初始化函数 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};

        __HAL_RCC_ADC1_CLK_ENABLE();

        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
}

/* 获得ADC转换后的结果函数 */
uint32_t adc_get_result(void)
{
    HAL_ADC_Start(&g_adc_handle);
    HAL_ADC_PollForConversion(&g_adc_handle, 10);
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);
}

/* 获取内部温度传感器温度值 */
short adc_get_temperature(void)
{
    uint32_t adcx;
    short result;
    double temperature;

    adcx = adc_get_result();
    temperature = adcx * (3.3 / 4096);          //转化为电压值
    temperature = (1.43 - temperature) / 0.0043 + 25;   //计算温度
    result = temperature *= 100;
    return result;
}

main.c

int main(void)
{
    short temp;

    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */
    adc_temperature_init();                     /* 初始化ADC内部温度传感器采集 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "Temperature TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 120, 200, 16, 16, "TEMPERATE: 00.00C", BLUE);

    while (1)
    {

        temp = adc_get_temperature();   /* 得到温度值 */

        if (temp < 0)
        {
            temp = -temp;
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, "-", BLUE);   /* 显示负号 */
        }
        else
        {
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, " ", BLUE);   /* 无符号 */
        }
        lcd_show_xnum(30 + 11 * 8, 120, temp / 100, 2, 16, 0, BLUE);    /* 显示整数部分 */
        lcd_show_xnum(30 + 14 * 8, 120, temp % 100, 2, 16, 0X80, BLUE); /* 显示小数部分 */
        
        LED0_TOGGLE();  /* LED0闪烁,提示程序运行 */
        delay_ms(250);
    }
}

八、光敏传感器实验(熟悉)

8.1  光敏二极管简介

8.2  实验原理

8.3  实验简要

8.4  编程实战

adc.c

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


ADC_HandleTypeDef g_adc_handle;

/* ADC单通道 */
void adc3_init(void)
{
    ADC_ChannelConfTypeDef adc_ch_conf;

    g_adc_handle.Instance = ADC3;
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;
    g_adc_handle.Init.ContinuousConvMode = DISABLE;
    g_adc_handle.Init.NbrOfConversion = 1;
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;
    g_adc_handle.Init.NbrOfDiscConversion = 0;
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    HAL_ADC_Init(&g_adc_handle);
    
    HAL_ADCEx_Calibration_Start(&g_adc_handle);
    
    adc_ch_conf.Channel = ADC_CHANNEL_6;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);
}

/* ADC MSP初始化函数 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC3)
    {
        GPIO_InitTypeDef gpio_init_struct;
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        
        __HAL_RCC_GPIOF_CLK_ENABLE();
        __HAL_RCC_ADC3_CLK_ENABLE();

        gpio_init_struct.Pin = GPIO_PIN_8;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
        HAL_GPIO_Init(GPIOF, &gpio_init_struct); 
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
}

/* 获得ADC转换后的结果函数 */
uint32_t adc_get_result(void)
{
    HAL_ADC_Start(&g_adc_handle);                   //启动AD转换器
    HAL_ADC_PollForConversion(&g_adc_handle, 10);       //等待上一次转换完成
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);
}

/* 读取光敏传感器值 */
uint8_t lsens_get_val(void)
{
    uint32_t temp_val;
    
    temp_val = adc_get_result();
    temp_val /= 40;
    
    if (temp_val > 100) temp_val = 100;
    
    return (uint8_t)(100 - temp_val);
}

main.c

int main(void)
{
    short adcx;

    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */
    adc3_init();                                /* 初始化ADC3 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "LSENS TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "LSENS_VAL:", BLUE);

    while (1)
    { 
        adcx = lsens_get_val();
        lcd_show_xnum(30 + 10 * 8, 110, adcx, 3, 16, 0, BLUE); /* 显示ADC的值 */
        LED0_TOGGLE();                                         /* LED0闪烁,提示程序运行 */
        delay_ms(250);
    }
}

;