STM32G474使用定时器1触发1次ADC转换,然后交给DMA循环执行,实现多通道ADC转换和多通道数据传输。若定时器1停止工作,则ADC转换也会随之停止,当然也不会再有DMA数据传输。
1、ADC触发信号分配
2、DMA多路复合器分配,指定数据数据源
3、测试程序
ADC_HandleTypeDef hadc1; //ADC句柄
DMA_HandleTypeDef hDMA; //DMA句柄
uint32_t ADC1_RESULT[2];
//虽然ADC的值为16位,但是HAL库用32位存放,否则会导致内存溢出
//DAM在搬运数据时,也要使用32位,否则读到的ADC值会超过0xFFF,这个HAL库中的问题
TIM_HandleTypeDef htim1; //TIM1句柄
uint32_t TRGO_cnt;//触发计数器
void ADC1_Init(void);
void TIM1_Init(void);
void DMA1_Read_ADC_Value_Use_Tim1_Triger(void);
void ADC1_Init(void)
{
ADC_ChannelConfTypeDef ADC1_ChanConf; //定义ADC1相关结构体
ADC_MultiModeTypeDef multimode ; //定义ADC模式相关结构体
__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=90/4=22.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;
//ADC_SCAN_DISABLE:仅对一个通道进行转换
//ADC_SCAN_ENABLE:按顺序对多个通道进行转换
//由于我们是多通道,所以用“扫描模式”
//转换以顺序模式执行,扫描方向是向上:从排名1到排名“n”。
hadc1.Init.ContinuousConvMode=DISABLE;
//DISABLE:ADC 在每次转换完成后停止,适用于单次数据采集。
//ENABLE:ADC 在转换完成后自动开始下一次转换,适用于连续数据采集
//这里采用“定时器1触发1次,则执行一次ADC转换”
//设置ADC_CFGR寄存器bit13(CONT位),令CONT=1,ADC采用连续转换模式
hadc1.Init.DiscontinuousConvMode=ENABLE;
//ENABLE:每次触发仅进行指定数量的通道转换,然后等待下一个触发信号。
//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_BULB;
//ADC转换采样阶段在转换结束后立即开始,并在触发事件时停止。
//ADC_SAMPLING_MODE_NORMAL;
//设置ADC_CFGR2寄存器bit26(BULB),令BULB=1
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;
//转换通道个数,2个转换在规则序列中
//设置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_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);//初始化ADC
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]=18,即AD通道18的序号为1
//AD转换顺序排列:配置通道3位于“第1个序列”
ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_640CYCLES_5;
//采样时间
//设置ADC_SMPR1寄存器bit8:6(SMP2[2:0]),640.5 ADC clock cycles
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转换顺序排列:配置通道16位于“第2个序列”
//设置ADC_SQR1寄存器bit16:12(SQ2[4:0]),SQ2[4:0]=16,即AD通道16的序号为2
ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_640CYCLES_5;
//采样时间
//设置ADC_SMPR1寄存器bit5:3(SMP1[2:0]),640.5 ADC clock cycles
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);
//将ADC2与DMA联系起来
//把hadc1结构体的参数赋给DMA
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中断
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]保存的是外设地址:ADC2_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通道在等待传输
TIM1_Init();
HAL_TIM_Base_Start(&htim1); //Start Timer trigger
}
void TIM1_Init(void)
{
RCC_ClkInitTypeDef clkconfig;
uint32_t uwTimclock = 0;
uint32_t pFLatency;
uint32_t uwPrescalerValue = 0;
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
__HAL_RCC_TIM1_CLK_ENABLE();//使能“定时器1”的时钟,Enable TIM1 clock
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);//Get clock configuration
uwTimclock = HAL_RCC_GetPCLK2Freq();
//读取PCLK2的时钟频率,Return the PCLK2 frequency
//若PCLK2的分频器值为1,则和SystemCoreClock的值相等
uwPrescalerValue = (uint32_t) ((uwTimclock / 10000U) - 1U);
//uwPrescalerValue=17000
htim1.Instance = TIM1;
htim1.Init.Period = (1000000U / 1000U) - 1U;
//定时器周期999
htim1.Init.Prescaler = uwPrescalerValue;
//设置TIM1预分频器为uwPrescalerValue
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
//设置时钟分频系数,TIM1_CR1中的CKD[9:8]=00b,tDTS=ttim_ker_ck;
//溢出时间为(999+1)*1*17000/170000000/1=0.1秒
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.RepetitionCounter = 0;//重复计数(1-0),产生一次中断
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;//TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim1);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
//TIM1_TRGO是adc_ext_trg9,用来触发ADC1/2/3/4/5
// sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
//TIM1_TRGO2是adc_ext_trg10,用来触发ADC1/2/3/4/5
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
//Configures the TIM in master mode.
HAL_TIM_Base_Start_IT(&htim1);
HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);//使能TIM1产生中断
HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0U);
//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
//选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
//这里设置TIM1中断优先级为5
}
//TIM1更新中断和TIM16全局中断,他们公用一个中断源
void TIM1_UP_TIM16_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim1);
// 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]保存的是外设地址:ADC2_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通道在等待传输
TRGO_cnt++;
if(TRGO_cnt>20)
{
TRGO_cnt=0;
__HAL_TIM_DISABLE(&htim1);
//TIMx_CR1寄存器bit0(CEN),令CEN=0,定时器计数器停止计数,ADC失去触发源,则也会停止
}
// LED1_Toggle(); //LED1引脚输出电平翻转
}
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的中断标志位
LED1_Toggle(); //LED1引脚输出电平翻转
}
HAL_DMA_IRQHandler(&hDMA);
}
void DMA1_Read_ADC_Value_Use_Tim1_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);
}
4、测试结果
当 __HAL_TIM_DISABLE(&htim1);
TIMx_CR1寄存器bit0(CEN),令CEN=0,定时器计数器停止计数,ADC转换也会停止。
CPU reset!
fHCLK=170MHz
fPCLK1=170MHz
fPCLK2=170MHz
ADC1_RESULT[0]=0x0
ADC1_RESULT[1]=0x3CF
Vrefint=1237.5mV
Temperature=23.7
ADC1_RESULT[0]=0x609
ADC1_RESULT[1]=0x3D5
Vrefint=1244.8mV
Temperature=25.6
ADC1_RESULT[0]=0x607
ADC1_RESULT[1]=0x3D3
Vrefint=1243.1mV
Temperature=25.0
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D4
Vrefint=1243.9mV
Temperature=25.3
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D4
Vrefint=1243.9mV
Temperature=25.3
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D4
Vrefint=1243.9mV
Temperature=25.3
ADC1_RESULT[0]=0x607
ADC1_RESULT[1]=0x3D4
Vrefint=1243.1mV
Temperature=25.3
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D4
Vrefint=1243.9mV
Temperature=25.3
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D5
Vrefint=1243.9mV
Temperature=25.6
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D5
Vrefint=1243.9mV
Temperature=25.6
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D4
Vrefint=1243.9mV
Temperature=25.3
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D5
Vrefint=1243.9mV
Temperature=25.6
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D5
Vrefint=1243.9mV
Temperature=25.6
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D5
Vrefint=1243.9mV
Temperature=25.6
ADC1_RESULT[0]=0x609
ADC1_RESULT[1]=0x3D6
Vrefint=1244.8mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D5
Vrefint=1243.9mV
Temperature=25.6
ADC1_RESULT[0]=0x607
ADC1_RESULT[1]=0x3D6
Vrefint=1243.1mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x607
ADC1_RESULT[1]=0x3D6
Vrefint=1243.1mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608 后面的数据不变化了,说明定时器1停止触发,导致ADC停止
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9
ADC1_RESULT[0]=0x608
ADC1_RESULT[1]=0x3D6
Vrefint=1243.9mV
Temperature=25.9