STM32G474之ADC多通道采集和DMA传输

STM32G474之ADC多通道采集和DMA传输,在ADC转换中,使用软件触发ADC转换,然后采用DMA循环搬运多个ADC通道中的数据,自动读取AD数据。

1、测试程序

#if DMA_Interrupt ==1
//需要测试DMA中断
DMA_HandleTypeDef      hDMA;   //DMA句柄
uint32_t LED_cnt;
//用来测试DMA能否进入中断
//为了能看到DAM是否能产生中断,这里使用LED_cnt作为“软计数器”

#endif

uint32_t ADC1_RESULT[2];
//虽然ADC的值为16位,但是HAL库用32为存放,否则会导致内存溢出
//DAM在搬运数据时,也要使用32位,否则读到的ADC值会超过0xFFF,这个HAL库中的问题

void ADC1_Init(void);
void DMA1_Read_ADC_Value_Use_SOFTWARE_Triger(void);

void ADC1_Init(void)
{
    ADC_HandleTypeDef      hadc1;          //ADC1句柄
    ADC_ChannelConfTypeDef ADC1_ChanConf;  //定义ADC1相关结构体

#if DMA_Interrupt ==0
    //不用测试DMA中断
    DMA_HandleTypeDef      hDMA;           //DMA句柄
#endif
    
  __HAL_RCC_ADC12_CLK_ENABLE();
    //使能ADC1时钟
    //设置RCC->AHB2ENR寄存器bit13(ADC12EN位),ADC12EN=1,使能ADC1或ADC2时钟

    __HAL_RCC_DMA1_CLK_ENABLE();                                               //DMA1时钟使能
    __HAL_RCC_DMAMUX1_CLK_ENABLE();//设置RCC->AHB1ENR寄存器bit2(DMAMUX1EN位),令DMAMUX1EN=1,DMAMUX1时钟使能

///
    hadc1.Instance=ADC1;                                                //选中ADC1
    hadc1.Init.ClockPrescaler=ADC_CLOCK_SYNC_PCLK_DIV4;
    //4分频,ADCCLK=PCLK2/4=170/4=42.5MHZ
    //设置ADCx_CCR寄存器bit17:16(CKMODE[1:0]]),CKMODE[1:0]=11,adc_hclk/4

    hadc1.Init.Resolution=ADC_RESOLUTION_12B;
    //12位模式
    //设置ADC_CFGR寄存器bit4:3(RES[1:0]),令RES[1:0]=00b,AD转换结果为12位

    hadc1.Init.DataAlign=ADC_DATAALIGN_RIGHT;
    //右对齐
    //设置ADC_CFGR寄存器bit15(ALIGN位),令ALIGN=0,AD转换结果为“右对齐”

    hadc1.Init.ScanConvMode=ADC_SCAN_ENABLE;
    //由于我们是多通道,所以用“扫描模式”
    //转换以顺序模式执行,扫描方向是向上:从排名1到排名“n”。
 
    hadc1.Init.ContinuousConvMode=ENABLE;
    //打开连续转换
    //设置ADC_CFGR寄存器bit13(CONT位),令CONT=1,ADC采用连续转换模式

    hadc1.Init.DiscontinuousConvMode=DISABLE;
    //不用“间断模式”;注意:不连续采样模式,只适用于“扫描模式”
    //设置ADC_CFGR寄存器bit16(DISCEN位),令DISCEN=0,禁止不连续采样模式

    hadc1.Init.NbrOfDiscConversion=0;
    //设置ADC_CFGR寄存器bit19:17(DISCNUM[2:0]),令DISCNUM[2:0]=0,不连续采样通道数为0
    hadc1.Init.ExternalTrigConvEdge=ADC_EXTERNALTRIGCONVEDGE_RISING;
    //上升沿触发
    //设置ADC->CFGR寄存器bit11:10(EXTEN[1:0]),EXTEN[1:0]=01b,硬件触发检测为上升沿

    hadc1.Init.DMAContinuousRequests=ENABLE;                                                      
    //设置ADC_CFGR寄存器bit0(DMAEN),令DMAEN=1,使用DMA连续请求数据
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
    //数据溢出覆盖;当过载发生时,使用ADC_DR的新值覆盖
    //设置ADC_CFGR寄存器bit12(OVRMOD),令OVRMOD=1,当检测到溢出时,将用最后一个转换结果覆盖ADC_DR寄存器

    hadc1.Init.SamplingMode=ADC_SAMPLING_MODE_NORMAL;
    //设置ADC_CFGR2寄存器bit27(SMPTRIG),令SMPTRIG=0,禁用“ADC轮询采样模式”
    hadc1.Init.GainCompensation = 0;
    //ADC增益设置为0
    //设置ADC_CFGR2寄存器bit16(GCOMP),令GCOMP=0,常规ADC工作模式

  hadc1.Init.OversamplingMode = DISABLE;
    //设置ADC_CFGR2寄存器bit0(ROVSE),令ROVSE=0,不使用数据溢出覆盖功能,禁止过采样

    hadc1.Init.NbrOfConversion=2;
    //转换通道个数,3个转换在规则序列中
    //设置ADC_SQR1寄存器的bit3:0(L[3:0]),令L[3:0]=2-1,表示“正则通道序列长度”为2,有2个AD转换

    hadc1.Init.LowPowerAutoWait = DISABLE;
    //关闭低功耗模式
    //配置ADC_CR寄存器bit29(DEEPPWD位),令DEEPPWD=0,ADC not in Deep-power down

  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    //ADC触发源选择“软件触发AD转换”
    //设置HRTIM->ADC1R寄存器bit5(ADC1EEV1位),ADC1EEV1=0,使用“内部软件”触发一次ADC转换
//    hadc1.Init.ExternalTrigConv=ADC_EXTERNALTRIG_T1_TRGO;
    //ADC group regular conversion trigger from external peripheral: TIM1_TRGO
    //ADC触发源选择“TIM1_TRGO”作为AD转换触发源

    hadc1.Init.EOCSelection=DISABLE;
    //关闭EOC中断
    //配置ADC_IER寄存器bit3(EOSIE位),EOSIE=0禁用ADC转换完成产生中断

    HAL_ADC_Init(&hadc1);//初始化
    HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
    //采用“单端输入”校准,而不是“差动输入”校准

    
    ADC1_ChanConf.Channel=ADC_CHANNEL_VREFINT;//内参考电压(Vrefint),连接到“ADC1_INP18通道”
    ADC1_ChanConf.Rank=ADC_REGULAR_RANK_1;
    //设置ADC_SQR1寄存器bit10:6(SQ1[4:0]),SQ1[4:0]=3,即AD通道3的序号为1
    //AD转换顺序排列:配置通道3位于“第1个序列”

    ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_640CYCLES_5;
    //采样时间
    //设置ADC_SMPR1寄存器bit8:6(SMP2[2:0]),640.5 ADC clock cycles
    //4分频,ADCCLK=PCLK2/4=170/4=42.5MHZ,640.5/42.5=15.07us

    ADC1_ChanConf.OffsetNumber=ADC_OFFSET_NONE; //无偏移数量
    ADC1_ChanConf.Offset = 0;                    //偏移量=0
    ADC1_ChanConf.SingleDiff=ADC_SINGLE_ENDED;   //采用“单端输入”,而不是“差动输入”
    HAL_ADC_ConfigChannel(&hadc1,&ADC1_ChanConf);//通道配置
    
    ADC1_ChanConf.Channel=ADC_CHANNEL_TEMPSENSOR_ADC1;//内部温度传感器(VTS),连接到“ADC1_INP16通道”
    ADC1_ChanConf.Rank=ADC_REGULAR_RANK_2;
    //AD转换顺序排列:配置通道2位于“第2个序列”
    //设置ADC_SQR1寄存器bit16:12(SQ2[4:0]),SQ2[4:0]=2,即AD通道2的序号为2

    ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_640CYCLES_5;
    //采样时间
    //设置ADC_SMPR1寄存器bit5:3(SMP1[2:0]),640.5 ADC clock cycles
    //4分频,ADCCLK=PCLK2/4=170/4=42.5MHZ,640.5/42.5=15.07us

    ADC1_ChanConf.OffsetNumber=ADC_OFFSET_NONE;   //无偏移数量
    ADC1_ChanConf.Offset = 0;                     //偏移量=0
    ADC1_ChanConf.SingleDiff=ADC_SINGLE_ENDED;    //采用“单端输入”,而不是“差动输入”
    HAL_ADC_ConfigChannel(&hadc1,&ADC1_ChanConf); //通道配置
    
    hDMA.Instance = DMA1_Channel1;        //使用DMA1的通道1
  hDMA.Init.Request = DMA_REQUEST_ADC1; //DMA需求来源ADC1
  hDMA.Init.Direction = DMA_PERIPH_TO_MEMORY;
    //设置DMA_CCRx寄存器bit4(DIR位),令DIR=0,数据传输方向:外设到内存
  hDMA.Init.PeriphInc = DMA_PINC_DISABLE;
    //设置DMA_CCRx寄存器bit6(PINC位),令PINC=0,外设地址不增加
  hDMA.Init.MemInc = DMA_MINC_ENABLE;
    //设置DMA_CCRx寄存器bit7(MINC位),令MINC=1,内存地址增加
  hDMA.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    //设置DMA_CCRx寄存器bit9:8(PSIZE[1:0]位),令PSIZE[1:0]=10,外设数据宽度32bits
    //在
“ADC多通道DMA采集”中,不能使用DMA_PDATAALIGN_HALFWORD配置,否则会造成读到的数据是32位的
  hDMA.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    //设置DMA_CCRx寄存器bit11:10(MSIZE[1:0]]位),令MSIZE[1:0]=10,内存数据宽度32bits
    //在
“ADC多通道DMA采集”中,不能使用DMA_MDATAALIGN_HALFWORD,否则会造成读到的数据是32位的
  hDMA.Init.Mode = DMA_CIRCULAR;
    //DMA_CIRCULAR:“DAM循环搬运数据”
    //DMA_NORMAL;若启动一次DMA,则DMA搬运一次数据
    //设置DMA_CCRx寄存器bit5(CIRC位),令CIRC=1,这里设置“DAM循环搬运数据”

  hDMA.Init.Priority = DMA_PRIORITY_VERY_HIGH ;
    //设置DMA_CCRx寄存器bit13:12(PL[1:0]),令PL[1:0]=11b,DMA抢占优先级最高
    HAL_DMA_Init(&hDMA);
    //初始化DMA

    __HAL_LINKDMA(&hadc1,DMA_Handle,hDMA);
    //将ADC1与DMA联系起来
    //把hadc1结构体的参数赋给DMA

#if DMA_Interrupt ==1
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
    //设置NVIC中断分组4:4位抢占优先级,0位响应优先级
    //选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0

    //DMA1_Channel1_IRQn抢占优先级为0,响应优先级为0
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);//使能DMA1_Channel1_IRQn中断
    LED_cnt=0;
#endif

    HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_RESULT,2);
    //启动DMA“循环搬运2个数据”,并保存到ADC1_RESULT[2]中
    //通过调用HAL_DMA_Start(),然后调用DMA_SetConfig()配置DMA_CPARx,DMA_CMARx和DMA_CNDTRx
    //DMA_CPARx寄存器bit31:0(MA[31:0]),MA[31:0]保存的是外设地址:ADC1_DR
    //DMA_CMARx寄存器bit31:0(MA[31:0]),MA[31:0]保存的是内存首地址:ADC1_RESULT[]数组的首地址
    //DMA_CNDTRx寄存器bit15:0(NDT[15:0]),NDT[15:0]保存的是有3个ADC通道在等待传输

  HAL_ADC_Start(&hadc1);
    //因为DMA是循环搬运数据的,所以只需要启动一次AD转换,就可以实现连续执行ADC转换,并搬运数据
    //所以这里用HAL_ADC_Start()启动

}

#if DMA_Interrupt ==1
//DMA通道1中断服务程序
void DMA1_Channel1_IRQHandler(void)
{
    if(__HAL_DMA_GET_FLAG(&hDMA, DMA_FLAG_TC1)!=RESET)
    {
        __HAL_DMA_CLEAR_FLAG(&hDMA, DMA_FLAG_TC1);
        //清除DMA1通道1的中断标志位
        LED_cnt++;
        if(LED_cnt>10000)
        {
            //4分频,ADCCLK=PCLK2/4=170/4=42.5MHZ,640.5/42.5=15.07us
            //
15.07*2*10000=301.4毫秒
            LED_cnt=0;
          LED1_Toggle(); //LED1引脚输出电平翻转
        }
    }
  HAL_DMA_IRQHandler(&hDMA);
}
#endif

//打印读到的ADC数据
void DMA1_Read_ADC_Value_Use_SOFTWARE_Triger(void)
{
    float f;

  printf("ADC1_RESULT[0]=0x%X\r\n",ADC1_RESULT[0]);
    printf("ADC1_RESULT[1]=0x%X\r\n",ADC1_RESULT[1]);
  f=ADC1_RESULT[0];f=f/4096;f=f*3300;
    printf("Vrefint=%0.1fmV\r\n",f);


    f=ADC1_RESULT[1];f=f*3300;f=f/4096;
    f=CalculateInternalTemperature(f);
    printf("Temperature=%0.1f\r\n",f);
}
 

2、ADC.h程序

#ifndef __ADC_H__
#define __ADC_H__

#include "stm32g4xx_hal.h"
#include "main.h"

//#define DMA_Interrupt  0  //不用测试DMA中断
#define DMA_Interrupt  1    //测试DMA中断

#define TS_CAL1 (float)( *((__IO uint16_t *)0x1FFF75A8) )  //单位为mV
#define TS_CAL2 (float)( *((__IO uint16_t *)0x1FFF75CA) )  //单位为mV
 
#define TS_TEMP_CAL1 (float)30.0    //温度为30.0度时,其AD值为TS_CAL1
#define TS_TEMP_CAL2 (float)130.0   //温度为130.0度,其AD值为TS_CAL2

//(TS_TEMP_CAL2 - TS_TEMP_CAL1)/(TS_CAL2 - TS_CAL1))表示“度/mV”

//#define CalculateInternalTemperature(TS_DATA) (TS_TEMP_CAL2 - TS_TEMP_CAL1)/(TS_CAL2 - TS_CAL1) * ( (TS_DATA*1) - TS_CAL1)  + TS_TEMP_CAL1
//Vref+ = 3.0V

#define CalculateInternalTemperature(TS_DATA) (TS_TEMP_CAL2 - TS_TEMP_CAL1)/(TS_CAL2 - TS_CAL1) * ( (TS_DATA*1.1) - TS_CAL1)  + TS_TEMP_CAL1
//当Vref+ = 3.3V,3.3/3.0=1.1,这个1.1系数是这么来的
//if VREF+ = 3.3 V and TS_CAL data are acquired at VREF+= 3.0 V, TS_DATA must be replaced by TS_DATA x (3.3/3.0).

extern void ADC1_Init(void);
extern void DMA1_Read_ADC_Value_Use_SOFTWARE_Triger(void);

#endif /* __ADC_H */

3、串口输出结果

 4、LED灯闪烁周期

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值