发新帖本帖赏金 20.00元(功能说明)我要提问
返回列表
打印
[单片机芯片]

基于CH32V103分享ADC DMA多通道间串扰和电压浮空问题的解决过程

[复制链接]
1158|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
AD, ADC, DM, DMA, dc
本帖最后由 lilijin1995 于 2023-3-16 23:48 编辑

问题背景:
这次客户需要实现八个模拟通道,32个按键的摇杆,如下图:


但是客户不需要全部摇杆都接上,只是接其中1~3个,而我们的ADC是浮空输入的,如下图:



如果不接摇杆的话会导致引脚浮空,造成电压不稳,如下图,如果Z和X旋转(CH2&CH3)悬空不接,摇动XY摇杆(CH0&CH1)会发现Z和Rx跟着动



解决方案
通过网上收集资料和论坛的提问和FAE的支持,学习总结如下解决方案:
1、将采样周期设置的大一些或增大相邻两个通道之间的采样延时,一定情况下会降低干扰;

2、把相邻通道分别配置到ADC1和ADC2上面,彻底隔离。

3、使用DMA的话可一次只配置一个通道,挨个使用DMA进行采集

4、在AD管脚上并联一个100nf左右的电容

首先第一个方案,原本ADC_SampleTime_55Cycles5改成ADC_SampleTime_239Cycles5
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5);
确实好了一点,然而不过也只是一点点,作用不大。第二种方案,因为CH32V103只有一个ADC1,所以排除了。
第四种方案,我们硬件本来就有RC滤波电路。
那么只剩下第三种方案了,这种方法确实可以,不过我没有用DMA;我直接一个一个通道采集,然后每个通道直接间隔1ms,因为跑RTOS,所以对实时性影响不大。
确确实实是解决了串扰的问题。

我在自己电脑验证完美OK,发个客户一测试,呵呵,问题又出来了,然后我发现,在我USB HUB上没啥问题,然后用电脑Root USB,嘿嘿,浮空电压乱跳问题出来了。客户要求浮空不接摇杆也能稳定居中。


软件修改
悬空不接,模拟输入会导致电压不稳定跳动问题,那么是否给它一个确定电压就OK了呢?于是我直接配置ADC如下:
//ADC对应GPIO初始化配置以及ADC初始化配置
void adc_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能GPIOA时钟和ADC

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //PA0~7对应ADC通道0~7
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //GPIO模式为模拟输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  //配置ADC为独立模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;        //多通道模式下开启扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  //设置开启连续转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //设置转换不是由外部触发启动,软件触发启动
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置ADC数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = LENGTH;           //规则转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure);                    //根据ADC_InitStructure中指定的参数初始化ADC1寄存器

    RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟分频为6分频

    ADC_Cmd(ADC1, ENABLE);      //使能ADC1

    ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器。

    while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束

    ADC_StartCalibration(ADC1); //开启AD校准

    while(ADC_GetCalibrationStatus(ADC1));      //等待校准结束
}
直接下拉电阻,稳定0V,你外接电位器基本没有影响。悬空时候就是0V,有朋友会问0V实现的效果肯定是在极点而不是在中间的。
于是我就想到了个办法:
开机时候,下拉到地,前2S开始检测电压,如果都是0,就是没有接电位器的,>0就是有接电位器的,接了电位器就正常采样,没有接直接赋中值;
但是这样的话也有局限性,这样就只能接好电位器再插USB供电。供电过程中拔插摇杆无效。
我们来看一下具体的代码实现过程:
void ADC_DMA_CONF(void)
{
    volatile u32 sumx=0,sumy=0,sumz=0,sumRx=0,sumRy=0,sumRz=0,sumSlider=0,sumDial=0;
    for(u8 i=0;i<20;i++)
    {
        Delay_Ms(50);
        sumx+=ADC_ConvertedValue[0];
        sumy+=ADC_ConvertedValue[1];
        sumz+=ADC_ConvertedValue[2];
        sumRx+=ADC_ConvertedValue[3];
        sumRy+=ADC_ConvertedValue[4];
        sumRz+=ADC_ConvertedValue[5];
        sumSlider+=ADC_ConvertedValue[6];
        sumDial+=ADC_ConvertedValue[7];

    }
    x=(u16)(sumx/20);
    y=(u16)(sumy/20);
    z=(u16)(sumz/20);
    Rx=(u16)(sumRx/20);
    Ry=(u16)(sumRy/20);
    Rz=(u16)(sumRz/20);
    Slider=(u16)(sumSlider/20);
    Dial=(u16)(sumDial/20);

    if((x<1000)&&(y<1000))
    {
        printf("摇杆1未接\r\n");
        Joystick1=NoREADY;
    }
    if((z<1000)&&(Rx<1000))
    {
        printf("摇杆2未接\r\n");
        Joystick2=NoREADY;
    }
    if((Ry<1000)&&(Rz<1000))
    {
        printf("摇杆3未接\r\n");
        Joystick3=NoREADY;
    }
    if((Slider<1000)&&(Dial<1000))
    {
        printf("摇杆4未接\r\n");
        Joystick4=NoREADY;
    }
}

只要Joystick是NoREADY的话,直接把对应的通道赋中值,那么就可以稳定摇杆了,也就是摇杆无效化。


使用特权

评论回复

打赏榜单

21ic小管家 打赏了 20.00 元 2023-04-14

沙发
zhuotuzi| | 2023-3-19 17:27 | 只看该作者
学习学习,这个是怎么玩的

使用特权

评论回复
板凳
tpgf| | 2023-4-10 14:32 | 只看该作者
找到多通道间串扰产生的原因了没有呀

使用特权

评论回复
地板
nawu| | 2023-4-10 15:05 | 只看该作者
多通道间的串扰感觉通过这种方式解决有点治标不治本啊

使用特权

评论回复
5
aoyi| | 2023-4-10 15:27 | 只看该作者
为什么不能把不用的引脚设置为普通的io引脚 需要的时候再重新设置成ad呢

使用特权

评论回复
6
zljiu| | 2023-4-10 15:54 | 只看该作者
aoyi 发表于 2023-4-10 15:27
为什么不能把不用的引脚设置为普通的io引脚 需要的时候再重新设置成ad呢

主要是楼主得随时监测当前引脚的电压变化吧

使用特权

评论回复
7
gwsan| | 2023-4-10 16:21 | 只看该作者
想来想去也没有一个不需要人工辅助的办法呀

使用特权

评论回复
8
tfqi| | 2023-4-10 16:31 | 只看该作者
考虑使用电压比较器 然后根据比较器的io中断信号开启ad采集吗

使用特权

评论回复
发新帖 本帖赏金 20.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

54

主题

162

帖子

7

粉丝