一、使用双ADC的好处
当采样单通道的时候,ADC最快14M使用,1.5周期采样速度+12.5固定转换周期,最快可以到1M的采样速度,但是这仅限于一个通道连续采样的情况,对于想要对多个通道采样的用户来说,单个ADC的采样速度,
会因为采样通道的增加而减少,此时对于CH32FV20x_30x的用户来说,可以使用双ADC来增加多通道的采样速率(相当于在相同的时间内完成了两倍的采样工作,从而大大提高了整体的采样速度。)。
当使用单 ADC 对多个通道进行分时采样时,由于不同通道的信号可能在不同的时间点进行转换,可能会受到其他通道信号的干扰。而双 ADC 可以同时对两个通道进行采样,每个通道有独立的转换路径,减少了通道间的干扰,提高了采样的准确性。例如,在测量两个相互关联但又容易相互干扰的模拟信号时,双 ADC 能够更好地保证每个信号的真实特征。
在某些应用中,需要对多个模拟信号进行同步采样,以获取它们在同一时刻的状态。双 ADC 可以同时启动采样过程,确保两个通道的信号在同一时刻被采样,从而实现同步采样。这在电力系统监测、音频处理等领域非常重要,因为这些领域需要准确地分析多个信号之间的相位关系。
双 ADC 可以独立配置每个 ADC 的参数,如采样率、分辨率等,以适应不同通道的信号特性。这样可以根据实际需求对每个通道进行优化,提高系统的灵活性和适应性。例如,一个通道需要高分辨率的采样,而另一个通道需要高采样率的采样,双 ADC 可以分别满足这两个需求。
二、重要配置
ADC1 为主ADC,ADC2 为从ADC。通过配置ADC1_CTLR1 中DUALMODE[3:0]以选择模式,实现ADC1 和 ADC2 交替触发或同步触发转换。
注:双ADC 模式中,选择外部触发事件触发时,用户必须使能主从ADC 的外部触发使能且需要将主
ADC 设为相应的触发,从ADC 设置为软件触发,防止不必要的触发使从ADC 进行转换。
图示为双ADC的各个模式。
![]()
注:1.双ADC 模式中,为了主数据寄存器能够读取从ADC 的转换数据,需要使能DMA 位。
2.只有ADC1 拥有DMA 功能。而ADC2 转化的数据只可以通过双ADC 模式,利用ADC1 的DMA 功能
传输。
为了实现加倍采样速率,一般选择同步规则模式。
此模式下用于转换规则通道序列,设置ADC1_CTLR2 中EXTSEL[2:0],以选择触发源,同时它也
将用于同步触发ADC2。转换完成时,将产生一个32 位DMA 传输请求,将数据寄存器ADC1_RDATAR
的内容传输到SRAM 中,高16 位包含ADC2 转换数据,低16 位包含ADC1 转换数据。若使能任一ADC
中断,将产生EOC 中断。
注:1.同一时刻ADC1 和ADC2 的转换通道不应重合;
2.同步模式下,ADC1 和ADC2 应有相同时间长度的转换序列或两者之中较长的转换序列的时长
小于触发时间间隔,以保证每次触发两个序列均能转换完成。
代码要点:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);//需要同时使能ADC1与ADC2的时钟。
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;//通过配置ADC1_CTLR1 中DUALMODE[3:0]以选择模式,此处为同步规则模式,ADC2的配置应与ADC1一致。
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//此处ADC2只能选择无触发,ADC12的配置此处应注意。
当选择判断EOC中断时,ADC12的单独的规则组寄存器中去读取,当使用DAM时,要选择字搬运,ADC1的结果存在低16位,ADC2的结果存在高16位,此处应该注意。
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
相对完整代码
u32 TxBuf[1];//buf也是字类型的。
u16 Adc_Val[4];
s16 Calibrattion_Val1 = 0;
s16 Calibrattion_Val2 = 0;
void DMA1_Channel1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void ADC_Function_Init(void)
{
ADC_InitTypeDef ADC_InitStructure={0};
GPIO_InitTypeDef GPIO_InitStructure={0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2 , ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 |GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_DeInit(ADC2);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_OutputBuffer = ADC_OutputBuffer_Disable;
ADC_InitStructure.ADC_Pga = ADC_Pga_1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_239Cycles5 );
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_BufferCmd(ADC1, DISABLE); //disable buffer
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
Calibrattion_Val1 = Get_CalibrationValue(ADC1);
ADC_Init(ADC2, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC2, ENABLE);
ADC_Cmd(ADC2, ENABLE);
ADC_BufferCmd(ADC2, DISABLE); //disable buffer
ADC_ResetCalibration(ADC2);
while(ADC_GetResetCalibrationStatus(ADC2));
ADC_StartCalibration(ADC2);
while(ADC_GetCalibrationStatus(ADC2));
Calibrattion_Val2 = Get_CalibrationValue(ADC2);
}
void DMA_Tx_Init( DMA_Channel_TypeDef* DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
{
DMA_InitTypeDef DMA_InitStructure={0};
NVIC_InitTypeDef NVIC_InitStructure={0};
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );
DMA_DeInit(DMA_CHx);
DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = bufsize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//此处都是字
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//此处都是字
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init( DMA_CHx, &DMA_InitStructure );
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig( DMA1_Channel1, DMA_IT_TC | DMA_IT_HT | DMA_IT_TE, ENABLE );
}
u16 Get_ConversionVal1(s16 val)
{
if((val+Calibrattion_Val1)<0|| val==0) return 0;
if((Calibrattion_Val1+val)>4095||val==4095) return 4095;
return (val+Calibrattion_Val1);
}
u16 Get_ConversionVal2(s16 val)
{
if((val+Calibrattion_Val2)<0|| val==0) return 0;
if((Calibrattion_Val2+val)>4095||val==4095) return 4095;
return (val+Calibrattion_Val2);
}
int main(void)
{
USART_Printf_Init(115200);
SystemCoreClockUpdate();
Delay_Init();
printf("SystemClk:%d\r\n",SystemCoreClock);
printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
ADC_Function_Init();
printf("CalibrattionValue1:%d\n", Calibrattion_Val1);
printf("CalibrattionValue2:%d\n", Calibrattion_Val2);
DMA_Tx_Init( DMA1_Channel1, (u32)&ADC1->RDATAR, (u32)TxBuf, 1 );
DMA_Cmd( DMA1_Channel1, ENABLE );
while(1)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
Delay_Ms(500);
}
}
void DMA1_Channel1_IRQHandler()
{
if(DMA_GetITStatus(DMA1_IT_TC1)==SET )
{
DMA_ClearITPendingBit(DMA1_IT_GL1);
Adc_Val[0]=TxBuf[0]&0xffff; //此处就是ADC1的值放在buf低16位的低12位。
Adc_Val[1]=(TxBuf[0]>>16)&0xffff;//ADC的值放在buf的高16位的低12位。
#if 0
printf("ADC1 ch2=%d\r\n",Get_ConversionVal1(Adc_Val[0]));
printf("ADC2 ch3=%d\r\n",Get_ConversionVal2(Adc_Val[1]));
#endif
}
}