初级会员

- 积分
- 145
- 金钱
- 145
- 注册时间
- 2017-12-4
- 在线时间
- 56 小时
|
3金钱
我手上现有一个这样的推子,其实就是一个电位器带有直流电机,电位器是10K电阻。
推子
1.现在我自己的做法是使用ADC读取电位器的电压,读取到的数值是10-4095的范围,把读取到的数据转换成1-100的数据。
接口1是接GND,接口3是接VCC 3.3V,接口2是给ADC读取数据,推动推子会改变电位器的电阻,总阻止为10K,T接口是轨迹跟踪,不知道怎么用。附上电位器的说明。
电位器
电位器电路
2.推子的电机我是使用DRV8833芯片来驱动,STM32 IO两针脚接入带IN1和IN2,给IN1高电平,IN2低电平,推子向上移动,给IN1低电平,IN2高电平,推子向下移动,这样就实现了电机的正反转;
采用PWM互补通道来驱动的,72MHz主频,定时器预分频为1分频,定时周期为900,那么450为占空比50%,电机不运行,900为占空比100%,推子全速向上移动,0为占空比0%,推子全速向下移动,这样也实现了电机的正反转,而且可以实现快慢速移动;
如果采用PWM亮通道来驱动,72MHz主频,定时器预分频为1分频,定时周期为500,那么250为占空比50%,,两IO都是250占空比50%,那么电机不运行,IO1设置为500占空比为100%,IO2设置为0占空比为0%,那么推子向上移动,IO1设置为0占空比为0%,IO2设置为100占空比为100%,那么推子向下移动,这样也实现了电机的正反转,而且可以实现快慢速移动;
3.
这个是我写的读取AD滤波的代码,使用的是HAL库,STM32CubeMX生成的代码,读取8个电位器的电压,每个通道取10个数据减去最大值和最小值取平均值,本次的值占6成,上一次的值占4成作为第一次滤波;第二次滤波是获取每个通道10个数据先由大到小排序,然后去除前后4个数,取3-8位6个数取平均值;最后每个通道的AD值是,本次二次滤波占5成+一次滤波占3成+上次二次滤波占2成作为最终AD的值。
3.1.这样滤波的话,不知是否有问题?我读取的数据是10多到4094,如果不经过滤波的话是10多到4095.
3.2.我在二次滤波最后赋值到通道的时候,AD_CH=(sum2*5+After_filter*3+AD_CH*2)/10;在main函数里调用AD_CH的时候,值也是10多到4094,AD_CH*100/4094就转成1-100的数值。而我在二次滤波那里改成AD_CH=((sum2*5+After_filter*3+AD_CH*2)/10)*100/4094;,那么再在main函数里调用AD_CH的值就会变成1-80,数据少了20,这个是什么原因?
[mw_shl_code=c,true] /**************ADC一次滤波*****************/
void ADC_Filter(void)
{
int max=0,min=60000;
int sum = 0,sum2=0;
uint16_t count;
int t;
uint8_t i,j,k;
for(i=0;i<channel;i++)//8个通道
{
for(count=0;count<data/8+1;count++)//80个数据,每个通道占10个数据
{
sum += AD_Value[count];//10个数据的总和
// printf("i=%d count=%d sum=%d AD_Value=%d\n",i,count,sum,AD_Value[count]);
if(AD_Value[count]>max) max=AD_Value[count];//获取最大值
if(AD_Value[count]<min) min=AD_Value[count];//获取最小值
}
sum=sum-max-min;//10个数据的总和减去最大值和最小值
After_filter=(sum/((data+1)/8-2)*6+After_filter*4)/10; //一次过滤后的值,本次过滤值占6成,上一次过滤值占4成
// printf("max=%d,min=%d,sum=%d,After_filter[%d]=%d\n",max,min,sum,i,After_filter);
/**************ADC二次滤波*****************/
for(k=0;k<data/8+1;k++)
{
AD_Value1[k]=AD_Value[k];//获取每个通道的数据作为二次过滤的数据
// printf("k=%d,AD_Value1=%d,i=%d\n",k,AD_Value1[k],i);
}
for(k=0;k<data/8+1;k++){
for(j=0;j<(data/8+1)-k;j++)
if(AD_Value1[j]>AD_Value1[j+1])
{
t=AD_Value1[j];
AD_Value1[j]=AD_Value1[j+1];
AD_Value1[j+1]=t;
}//每个通道按大到小排序
}
/*累加ADC的值*/
for(k=(data/8+1)/5;k<channel;k++)
{
//3到8累加
sum2 += AD_Value1[k];
// printf("k=%d,sum2=%d,AD_Value1=%d\n",k,sum2,AD_Value1[k]);
}
/*3到8的平均值*/
sum2 /= (channel-1-1);
AD_CH=(sum2*5+After_filter*3+AD_CH*2)/10;
sum=0;
sum2=0;
max=0;
min=6000;
}
}[/mw_shl_code]
4.我想要实现的功能是:
1.我推动推子,AD读取的数据为1-100,把这个数据发给DSP来控制音量,1为最小声,100为最大声,如果我写的获取电位器电压的滤波没有问题的话,这个已经解决了;
2.在外部我会使用软件来控制推子的位置,例如我需要推子去到50这个位置,AD读取到的数据是30,那么推子就需要向上移动到50的位置,电机就需要把推子推到50的位置。
现在的问题是不知道怎么控制电机把推子推到需要设定的位置。
第一我是使用IO输出高低电平控制推子的 方向,全速运行.这样的情况是去到那个位置的时候不停地跳动,不能准确地确认那个位置。
[mw_shl_code=c,true]void PID_PWM(uint32_t SetPoint)
{
uint32_t NextPoint;
int iError,iError2;//偏差值
while(1)
{
ADC_Filter();//ADC过滤
/* 得到AD获取值,数值为1-100 */
NextPoint=AD_CH[0]*100/4094;
/*计算偏差值,确定推子移动的方向*/
iError=SetPoint-NextPoint;
iError2=NextPoint-SetPoint;
printf("设定目标位置 -> %d\n",SetPoint);
printf("当前目标位置 -> %d\n",NextPoint);
printf("差值1 -> %d,差值12 -> %d\n",iError,iError2);
if(iError > 0)
{
HAL_GPIO_WritePin(MD1_GPIO_Port,MD1_Pin,0);
HAL_GPIO_WritePin(MD2_GPIO_Port,MD2_Pin,1);
if(iError2<10 && iError2>=0)
{
HAL_GPIO_WritePin(MD1_GPIO_Port,MD1_Pin,0);
HAL_GPIO_WritePin(MD2_GPIO_Port,MD2_Pin,0);
break;
}
}
else if(iError < 0)
{
HAL_GPIO_WritePin(MD1_GPIO_Port,MD1_Pin,1);
HAL_GPIO_WritePin(MD2_GPIO_Port,MD2_Pin,0);
if(iError2<10 && iError2>=0)
{
HAL_GPIO_WritePin(MD1_GPIO_Port,MD1_Pin,0);
HAL_GPIO_WritePin(MD2_GPIO_Port,MD2_Pin,0);
break;
}
}
else
{
break;
}
}
}[/mw_shl_code]
第二,我是使用PWM来控制电机的速度也是一样的做法,设定的值和AD读取到的值对比,相减小于10的时候降低电机速度,相等的时候停止,但是也是一样不停地在那个位置跳动。
我想知道用什么方法可以实现这样的功能?或者有那位大神可以帮助小弟的,小弟万分感激,最好可以有做过类似的案例参考一下,小弟也附上我的项目,麻烦懂的朋友帮忙修改一下。
ADC_DMA_Test1.zip
(18.38 MB, 下载次数: 322)
|
|