本帖最后由 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的话,直接把对应的通道赋中值,那么就可以稳定摇杆了,也就是摇杆无效化。
|