CH32双ADC采样同步规则配置要点

一、使用双ADC的好处

1. 提高采样速度

当采样单通道的时候,ADC最快14M使用,1.5周期采样速度+12.5固定转换周期,最快可以到1M的采样速度,但是这仅限于一个通道连续采样的情况,对于想要对多个通道采样的用户来说,单个ADC的采样速度,

会因为采样通道的增加而减少,此时对于CH32FV20x_30x的用户来说,可以使用双ADC来增加多通道的采样速率(相当于在相同的时间内完成了两倍的采样工作,从而大大提高了整体的采样速度。)。

2. 减少通道间干扰

当使用单 ADC 对多个通道进行分时采样时,由于不同通道的信号可能在不同的时间点进行转换,可能会受到其他通道信号的干扰。而双 ADC 可以同时对两个通道进行采样,每个通道有独立的转换路径,减少了通道间的干扰,提高了采样的准确性。例如,在测量两个相互关联但又容易相互干扰的模拟信号时,双 ADC 能够更好地保证每个信号的真实特征。

3. 实现同步采样

在某些应用中,需要对多个模拟信号进行同步采样,以获取它们在同一时刻的状态。双 ADC 可以同时启动采样过程,确保两个通道的信号在同一时刻被采样,从而实现同步采样。这在电力系统监测、音频处理等领域非常重要,因为这些领域需要准确地分析多个信号之间的相位关系。

4. 提高系统的灵活性

双 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
    }
}
posted @ 2025-03-31 19:27  WCH_CH32  阅读(265)  评论(0)    收藏  举报