STM32中的ADC

ADC的介绍

什么是ADC?

全称:Analog-to-Digital Converter,指模拟/数字转换器。

ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。

  • 12 位 ADC 是一种 逐次逼近型模拟数字转换器(0~4095 。它有多达 **18**个通道,可测量 16****个外部和 2 个 内部信号源
  • 各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。
  • ADC 的结果 可以 左对齐右对齐方式存储在 16 位数据寄存器中
  • 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高 / 低阀值。
  • ADC 的输入时钟不得超过 14MHz,它是由 PCLK2 经分频产生。
  • STM32F103C8T6 ADC资源:ADC1、ADC2,10 个外部输入通道(这个芯片中引出的引脚个数,最大有18个)。

ADC的工作原理(逐次逼近型)

说明:

  • 当输入模拟量时,控制与定时器会给逐次逼近寄存器发出指令,逐次逼近寄存器会产生数字信号,经过D/A转化器生成模拟信号,与输入的模拟量经过比较器进行比较。
  • 当产生的模拟量相比于输入的模拟量过大过小时,会反馈给逐次逼近寄存器,重新修正,当产生的模拟量与输入的模拟量近似相同时,逐次逼近寄存器中的数字量会转运到输出缓冲器中D0~D7的位置。

ADC的性能指标:(量程、分辨率、转化时间)

  • **量程:**能测量的 电压范围 。
  • 分辨率:ADC 能辨别的最小模拟量,通常以输出二进制数的位数表示,比如:8、10、12 、16位等;位数越多,分辨率越高,一般来说分辨率越高,转化时间越长。
  • 转化时间(采样时间):****从转换开始到获得稳定的数字量输出所需要的时间称为转换时间。转换时间越长,转换结果相对越准确,但是转换速度就越慢。

ADC特性:(供电电压、ADC输入范围、转化速度)

  • 12 位精度下转换速度可高达1MHZ
  • 供电电压:VSSA :0VVDDA :2.4V~3.6V
  • ADC输入范围:VREF- ≤ VIN ≤ VREF+,0~3.3V。
  • ADC 的结果可以 左对齐 或 右对齐 方式存储在16 **位数据寄存器(低16位是ADC1的数据位,高16位是ADC2的数据位)**中。

ADC的框图

  • 简图:
  • 参考手册上的框图:

ADC的输入通道

(16个外部通道和2个内部通道**(温度传感器和内部参考电压)****)**

其中16个通道**在转化时又分成****规则组通道(最多有16路)**和注入组通道(最多有4路)

外部的16个通道在转换时又分为规则通道和注入通道,其中规则通道最多有16路,注入通道最多有4路。

转化部分

规则组/注入组

解释:规则组和注入组-----(利用厂里员工检测零部件进行对比)

  • 规则组好比是待检测的零部件,最多有16个,工人进行依次按顺序进行检测;
  • 注入组好比是老板临时加入的紧急待测件,最多有4个,工人放下手中的规则组零部件,开始按顺序依次检测注入组的零部件,检测完成后,开始检测规则组的零部件。

转化顺序

  • 规则组的转化顺序

原因: 每个 ADC 规则通道只有一个数据寄存器,16 个通道一起共用这个寄存器,所以需要指定规则转换通道的转换顺序。

控制转化顺序的3个寄存器:SQR1、SQR2、SQR3(都是32位寄存器)

SQR 寄存器控制着转换通道的数目和转换顺序,只要在对应的寄存器位SQx 中写入相应的通道,这个通道就是第 x 个转换。

  • 注入组的转化顺序

和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有1个JSQR寄存器来控制,控制关系如下:

注入序列的转换顺序 是从JSQx[ 4 : 0 ] (x= 4- JL[1:0] 开始。
**例:**只有当 JL=4 的时候,注入通道的转换顺序才会按照 JSQ1 、 JSQ2 、 JSQ3 、 JSQ4 的顺序执行。

触发转化的方式

方式1:软件触发

通过向 控制寄存器 ADC-CR2 的 ADON****位写 1 来开启 ADC ,再将 SWSTART****位置 1 ,启动规则通道转换

方式2:外部事件(如定时器)触发

由寄存器 EXTSEL 中的 3位进行控制,所以,总共有2^3=8中触发方式。

转化时间

转换时间 = 采样时间 + 12.5 个周期
补充:周期为ADC经PCLK2分频后得到的频率的倒数

周期: ADC 是挂载在 APB2 总线(PCLK2)上的,经过分频器得到 ADC 时钟(ADCCLK),最高 14 MHz。但是,在STM32F103C8T6芯片上的只能进行2/4/6/8分频,因此,最高 12 MHz。

采样时间(可以自己手动设置):共8种

举例:

  • 在STM32F103C8T6开发板中,最少的转化时间: 采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us。
  • 若其他芯片中,周期为14MHz的倒数,最低采样时间是1us。

单次转换和连续转换

  • 单次转换:只转换一次。
  • **连续转换:**转换一次之后,立马进行下一次转换。

扫描模式

  • **关闭扫描模式:**只转换 ADC_SQRx 或 ADC_JSQR 选中的第一个通道。
  • 打开扫描模式: 扫描所有被 ADC_SQRx 或 ADC_JSQR 选中的所有通道。

输出部分 (中断及事件)

  • DMA请求(只适用于规则组:只有一个数据寄存器,防止数据进行覆盖)

规则组每个通道转换结束后,除了可以产生中断外,还可以产生DMA请求,我们利用DMA及时把转换好的数据传输到指定的内存里,防止数据被覆盖。

校准

  • ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。
  • 通过设置ADC_CR2寄存器的CAL位启动校准。一旦校准结束, CAL位被硬件复位,可以开始正常转换。建议在上电时执行一次ADC校准。校准阶段结束后,校准码储存在ADC_DR中。
  • 建议在每次上电后执行一次校准。

ADC的寄存器

ADC状态寄存器(ADC_SR)

ADC控制寄存器1(ADC_CR1)

ADC控制寄存器2(ADC_CR2)

ADC采样时间寄存器1(ADC_SMPR1)

ADC采样时间寄存器2(ADC_SMPR2)

ADC看门狗高阀值寄存器(ADC_HTR)

ADC看门狗低阀值寄存器(ADC_LRT)

ADC规则序列寄存器 1(ADC_SQR1)

ADC规则序列寄存器 2(ADC_SQR2)

ADC规则序列寄存器 3(ADC_SQR3)

ADC注入序列寄存器**(ADC_JSQR)**

ADC注入数据寄存器x (ADC_JDRx) (x= 1..4)

ADC规则数据寄存器(ADC_DR)

ADC的库函数

  • stm32f1xx_hal_adc.c
  • stm32f1xx_hal_adc_ex.c

小实验1:ADC单通道采集实验

实验目的

使用ADC1采集单通道1的电压值,通道1接光敏电阻传感器。

硬件清单

开发板、光敏传感器、ST-Link、USB转TTL

配置代码流程

文件代码

  • adc.c文件代码
cs 复制代码
#include "adc.h"

ADC_HandleTypeDef adc_handle = {0}; 
void adc_init(void){
    adc_handle.Instance = ADC1;
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                    /* 数据对齐:常用右对齐 */
    adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;                    /* 需不需扫描:这里只有一个通道,不扫描 */
    adc_handle.Init.ContinuousConvMode = DISABLE;                       /* 连续转化 */
    adc_handle.Init.NbrOfConversion = 1;                                /* 转化的个数 */
    adc_handle.Init.DiscontinuousConvMode = DISABLE;                    /* 间断模式 */
    adc_handle.Init.NbrOfDiscConversion = 0;                            /* 间断的个数 */
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;              /* 触发模式的选择:软件触发 */
     
    HAL_ADC_Init(&adc_handle);
    HAL_ADCEx_Calibration_Start(&adc_handle);                           /* ADC的校准 */
}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){
    if(hadc->Instance == ADC1){
        RCC_PeriphCLKInitTypeDef  adc_clk_init = {0};
        
        __HAL_RCC_ADC1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;          /* 外设时钟的选择 */
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;             /* ADC的预分频:这里6分频,12MHz */
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                       /* 由于ADC转化的时钟是12MHz,要进行改变,进行预分频 */
        
        GPIO_InitTypeDef gpio_initstruct;
        gpio_initstruct.Pin = GPIO_PIN_1;
        gpio_initstruct.Mode = GPIO_MODE_ANALOG;
        gpio_initstruct.Pull = GPIO_PULLUP;
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
     
        HAL_GPIO_Init(GPIOA,&gpio_initstruct);
        
    }
}

/**
* @breif    封装一个配置ADC通道的函数。
* @param    配置的ADC1,通道,通道序列,采样时间
* @retval   无
*/

void adc_channel_config(ADC_HandleTypeDef* hadc,uint32_t ch,uint32_t rank,uint32_t stime){
    
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    adc_ch_config.Channel = ch;                                     /* 选择配置的通道 */
    adc_ch_config.Rank = rank;                                      /* 通道的优先级,序列 */
    adc_ch_config.SamplingTime = stime;                             /* 通道的采样时间 */
    
    HAL_ADC_ConfigChannel(&adc_handle,&adc_ch_config);
}

/**
* @breif    封装一个获取DR寄存器值得函数
* @note     配置的流程:配置通道,打开ADC,阻塞函数等待转化完成,获取DR寄存器数据的值,(uint16_t)将数据强转为十进制数。
* @param    通道,uint32_t ch
* @retval   获取的DR寄存器中的数值
*/
uint32_t adc_get_result(uint32_t ch){
    
    adc_channel_config(&adc_handle,ch,ADC_REGULAR_RANK_1,ADC_SAMPLETIME_239CYCLES_5);       /* 通道的配置 */
    HAL_ADC_Start(&adc_handle);                                    /* 打开ADC */
    HAL_ADC_PollForConversion(&adc_handle,10);                     /* 等待转化完成,超时时间:10ms*/
     return (uint16_t)HAL_ADC_GetValue(&adc_handle);
    /* ADC1转化的结果放在DR寄存器的低16位,这里强转为uint16_t就可以直接获取。
    DR寄存器的高16位存放的是ADC2转化的结果。 */
    
}
  • adc.h文件代码
cs 复制代码
#ifndef __ADC_H__
#define __ADC_H__

#include "stm32f1xx.h"

void adc_init(void);
uint32_t adc_get_result(uint32_t ch);

#endif
  • mian.c文件代码
cs 复制代码
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "adc.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* LED初始化 */
    uart1_init(115200);
    printf("hello,world");
    adc_init();
    
    while(1)
    { 
        printf("adc:result:%f v\r\n",(float)adc_get_result(ADC_CHANNEL_1) / 4096 * 3.3);
        delay_ms(500);
    }
}

总结: (代码流程图)

  1. 初始化ADC的函数;
  2. 初始化Msp的函数;
  3. 配置ADC的通道;
  4. 封装一个函数,取出DR寄存器中的数值。

**注意:**在HAL_DMA_MspInit( )函数中,要对ADC的时钟进行分频,一般采用的分频系数是6分频。调用的函数如下:

小实验2:ADC单通道采集实验+DMA读取

实验目的

使用ADC1采集通道1的电压值+DMA读取,通道1连接光敏传感器。

硬件清单

开发板、ST-Link、光敏电阻传感器、USB转TTL

配置流程

文件代码

  • adc.c文件代码
cs 复制代码
#include "adc.h"

ADC_HandleTypeDef adc_handle = {0}; 
void adc_config(void){
    adc_handle.Instance = ADC1;
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                    /* 数据对齐:常用右对齐 */
    adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;                    /* 需不需扫描:这里只有一个通道,不扫描 */
    adc_handle.Init.ContinuousConvMode = ENABLE;                       /* 连续转化 */
    adc_handle.Init.NbrOfConversion = 1;                                /* 转化的个数 */
    adc_handle.Init.DiscontinuousConvMode = DISABLE;                    /* 间断模式 */
    adc_handle.Init.NbrOfDiscConversion = 0;                            /* 间断的个数 */
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;              /* 触发模式的选择:软件触发 */
     
    HAL_ADC_Init(&adc_handle);
    HAL_ADCEx_Calibration_Start(&adc_handle);                           /* ADC的校准 */
}

DMA_HandleTypeDef dma_handle = {0};
void dma_config(void){
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    dma_handle.Instance = DMA1_Channel1;
    dma_handle.Init.Direction  = DMA_PERIPH_TO_MEMORY;
    
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;  /*ADC的DR寄存器是16,传输的数据是半字符。*/
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;
    
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    dma_handle.Init.PeriphInc = DMA_PINC_ENABLE;
    
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
    dma_handle.Init.Mode = DMA_CIRCULAR;
    
    __HAL_LINKDMA(&adc_handle,DMA_Handle,dma_handle);
    
   HAL_DMA_Init(&dma_handle);
}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){
    if(hadc->Instance == ADC1){
        RCC_PeriphCLKInitTypeDef  adc_clk_init = {0};
        
        __HAL_RCC_ADC1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;          /* 外设时钟的选择 */
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;             /* ADC的预分频:这里6分频,12MHz */
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                       /* 由于ADC转化的时钟是12MHz,要进行改变,进行预分频 */
        
        GPIO_InitTypeDef gpio_initstruct;
        gpio_initstruct.Pin = GPIO_PIN_1;
        gpio_initstruct.Mode = GPIO_MODE_ANALOG;
        gpio_initstruct.Pull = GPIO_PULLUP;
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
     
        HAL_GPIO_Init(GPIOA,&gpio_initstruct); 
    }
}

/**
* @breif    封装一个配置ADC通道的函数。
* @param    配置的ADC1,通道,通道序列,采样时间
* @retval   无
*/

void adc_channel_config(ADC_HandleTypeDef* hadc,uint32_t ch,uint32_t rank,uint32_t stime){
    
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    adc_ch_config.Channel = ch;                                     /* 选择配置的通道 */
    adc_ch_config.Rank = rank;                                      /* 通道的优先级,序列 */
    adc_ch_config.SamplingTime = stime;                             /* 通道的采样时间 */
    
    HAL_ADC_ConfigChannel(&adc_handle,&adc_ch_config);
}

/**
* @breif    封装一个获取DR寄存器值得函数
* @note     配置的流程:配置通道,打开ADC,阻塞函数等待转化完成,获取DR寄存器数据的值,(uint16_t)将数据强转为十进制数。
* @param    通道,uint32_t ch
* @retval   获取的DR寄存器中的数值
*/
uint32_t adc_get_result(uint32_t ch){
    
    adc_channel_config(&adc_handle,ch,ADC_REGULAR_RANK_1,ADC_SAMPLETIME_239CYCLES_5);       /* 通道的配置 */
    HAL_ADC_Start(&adc_handle);                                    /* 打开ADC */
    HAL_ADC_PollForConversion(&adc_handle,10);                     /* 等待转化完成,超时时间:10ms*/
     return (uint16_t)HAL_ADC_GetValue(&adc_handle);
    /* ADC1转化的结果放在DR寄存器的低16位,这里强转为uint16_t就可以直接获取。
    DR寄存器的高16位存放的是ADC2转化的结果。 */
    
}

void adc_dma_init(uint32_t *mar){
    adc_config();
    adc_channel_config(&adc_handle,ADC_CHANNEL_1,ADC_REGULAR_RANK_1,ADC_SAMPLETIME_239CYCLES_5);
    dma_config();
    HAL_ADC_Start_DMA(&adc_handle,mar,1);          
    /* Length: The length of data to be transferred from ADC peripheral to memory.  转运一个数据 */
}
  • adc.h文件代码
cs 复制代码
#ifndef __ADC_H__
#define __ADC_H__

#include "stm32f1xx.h"

void adc_dma_init(uint32_t *mar);

#endif
  • mian.c文件代码
cs 复制代码
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "adc.h"

uint32_t adc_result = 0;
int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* LED初始化 */
    uart1_init(115200);
    printf("hello,world");
    adc_dma_init(&adc_result);
    
    while(1)
    { 
        printf("adc:result:%f v\r\n",(float)adc_result / 4096 * 3.3);
        delay_ms(500);
    }
}

注意事项:

  • 在初始化ADC的函数中,开启连续模式
  • 在初始化DMA的函数中,数据位是半字符,模式是循环模式
  • 一定要注意使用**__HAL_LINKDMA( )函数**中的参数。第一个外设的据饼,第二个外设句柄的参数,第三个,DMA的句柄;

adc.c中的文件的书写流程:

  1. 初始化ADC;
  2. 初始化ADC相关的外设,MSP函数;
  3. 初始化DMA,并用函数将ADC外设与内存进行连接;
  4. 配置ADC的通道(ADCx,channelx,rank,sampleTime);
  5. 书写一个封装函数:ADC初始化函数->ADC通道的配置->初始化DMA的函数->调用打开ADC和DMA的函数。

小实验3:ADC多通道采集实验+DMA读取

实验目的

使用ADC1采集通道0~3的电压值+DMA读取,通道1连接光敏电阻传感器。

硬件清单

开发板、光敏电阻传感器、ST-Link、USB转TTL

配置流程

和单通道+DMA转运的流程基本相同

文件代码

  • adc.c文件代码
cs 复制代码
#include "adc.h"

ADC_HandleTypeDef adc_handle = {0}; 
void adc_config(void){
    adc_handle.Instance = ADC1;
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                    /* 数据对齐:常用右对齐 */
    adc_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;                    /* 需不需扫描:这里只有一个通道,不扫描 */
    adc_handle.Init.ContinuousConvMode = ENABLE;                       /* 连续转化 */
    adc_handle.Init.NbrOfConversion = 3;                                /* 转化的个数 */
    adc_handle.Init.DiscontinuousConvMode = DISABLE;                    /* 间断模式 */
    adc_handle.Init.NbrOfDiscConversion = 0;                            /* 间断的个数 */
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;              /* 触发模式的选择:软件触发 */
     
    HAL_ADC_Init(&adc_handle);
    HAL_ADCEx_Calibration_Start(&adc_handle);                           /* ADC的校准 */
}

DMA_HandleTypeDef dma_handle = {0};
void dma_config(void){
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    dma_handle.Instance = DMA1_Channel1;
    dma_handle.Init.Direction  = DMA_PERIPH_TO_MEMORY;
    
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;  /*ADC的DR寄存器是16,传输的数据是半字符。*/
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;
    
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
    
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
    dma_handle.Init.Mode = DMA_CIRCULAR;
    
    __HAL_LINKDMA(&adc_handle,DMA_Handle,dma_handle);
    
   HAL_DMA_Init(&dma_handle);
}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){
    if(hadc->Instance == ADC1){
        RCC_PeriphCLKInitTypeDef  adc_clk_init = {0};
        
        __HAL_RCC_ADC1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;          /* 外设时钟的选择 */
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;             /* ADC的预分频:这里6分频,12MHz */
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                       /* 由于ADC转化的时钟是12MHz,要进行改变,进行预分频 */
        
        GPIO_InitTypeDef gpio_initstruct;
        gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2;
        gpio_initstruct.Mode = GPIO_MODE_ANALOG;
        gpio_initstruct.Pull = GPIO_PULLUP;
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
     
        HAL_GPIO_Init(GPIOA,&gpio_initstruct); 
    }
}

/**
* @breif    封装一个配置ADC通道的函数。
* @param    配置的ADC1,通道,通道序列,采样时间
* @retval   无
*/

void adc_channel_config(ADC_HandleTypeDef* hadc,uint32_t ch,uint32_t rank,uint32_t stime){
    
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    adc_ch_config.Channel = ch;                                     /* 选择配置的通道 */
    adc_ch_config.Rank = rank;                                      /* 通道的优先级,序列 */
    adc_ch_config.SamplingTime = stime;                             /* 通道的采样时间 */
    
    HAL_ADC_ConfigChannel(&adc_handle,&adc_ch_config);
}

void adc_dma_init(uint32_t *mar){
    adc_config();
    
    adc_channel_config(&adc_handle,ADC_CHANNEL_0,ADC_REGULAR_RANK_1,ADC_SAMPLETIME_239CYCLES_5);
    adc_channel_config(&adc_handle,ADC_CHANNEL_1,ADC_REGULAR_RANK_2,ADC_SAMPLETIME_239CYCLES_5);
    adc_channel_config(&adc_handle,ADC_CHANNEL_2,ADC_REGULAR_RANK_3,ADC_SAMPLETIME_239CYCLES_5);
   
    dma_config();
    HAL_ADC_Start_DMA(&adc_handle,mar,3);          
    /* Length: The length of data to be transferred from ADC peripheral to memory.  转运3个数据 */
}

遇到的问题:

在DMA初始化函数中,关于外设指针递增,要采取不递增,如下代码所示:

如果采用指针递增的模式的话,通道2和通道3没有数据显示,因为外设只有一个数据,递增的话第一个数据后面没有数据,是空数据。

  • adc.h文件代码
cs 复制代码
#ifndef __ADC_H__
#define __ADC_H__

#include "stm32f1xx.h"

void adc_dma_init(uint32_t *mar);

#endif
  • main.c文件代码
cs 复制代码
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "adc.h"

uint32_t adc_result[3] = {0};
int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* LED初始化 */
    uart1_init(115200);
    printf("hello,world");
    adc_dma_init(adc_result);
    
    while(1)
    { 
        printf("通道1电压:%f v\r\n",(float)adc_result[0] / 4096 * 3.3);
        printf("通道2电压:%f v\r\n",(float)adc_result[1] / 4096 * 3.3);
        printf("通道3电压:%f v\r\n\r\n",(float)adc_result[2] / 4096 * 3.3);
        
        delay_ms(500);
    }
}

相比于单通道+DMA转运,多通道+DMA转运的区别和一些注意事项:

  • 利用多通道配置的话,在初始化ADC的函数中,要开启扫描模式
  • DMA初始化函数中,外设指针不递增。
  • 封装一个配置通道的函数,方便在后面函数中配置不同的通道。
  • 在主函数中创建一个数组接收不同通道传来的数据。
相关推荐
智者知已应修善业1 小时前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
智商偏低8 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen9 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森11 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白11 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D11 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术14 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt15 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘15 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang15 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c