|  | 
 
| |  极简陋的模拟电路 (原文件名:磁悬浮(STM8 PWM).png)
 
 源代码(STVD + Cosmic)ourdev_511866.zip(文件大小:53K) (原文件名:maglev_stm8.zip)
 
 悬浮视频ourdev_511458.zip(文件大小:1.41M) (原文件名:maglev.zip)
 
 
 电路基于ST三合一开发板的STM8S Mini Kit,ADC基准电压3.3V。
 STM8使用内部16MHz RC振荡,分频比为1,核心频率16MHz。
 ADC2时钟分频比为12,频率1.333MHz,工作在单次采样模式,采样时间为14 * 0.75?s + 7?s = 17.5?s,加上中断处理时间,采样率约为55KS/s。
 TIM1时钟分频比为4,频率4MHz,计数器TOP值(ARR)为999,因此PWM频率为4KHz。
 
 完成初始化后,主程序进入死循环,系统完全依赖中断服务例程运作。
 
 设计要点详解:
 1.ADC2采样完成(EOC)中断
 <pre>/*
 * ADC2 End Of Conversion interrupt handler
 * Positions are filtered by a 7th-order moving average filter.
 */
 @far @interrupt void ADC2_IRQHandler(void)
 {
 static u8 xFilterPos, yFilterPos;
 static s16 xFilterBuf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 static s16 yFilterBuf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 
 s16 adcValue, acc = 0;
 u8 i;
 
 adcValue = ADC2_GetConversionValue();
 if (currentChannel == ADC2_CHANNEL_6)
 {
 xFilterBuf[xFilterPos & 7] = adcValue;
 xFilterPos++;
 for (i = 0; i < 8; i++)
 {
 acc += xFilterBuf;
 }
 xPos = acc >> 3;
 currentChannel = ADC2_CHANNEL_7;
 }
 else
 {
 yFilterBuf[yFilterPos & 7] = adcValue;
 yFilterPos++;
 for (i = 0; i < 8; i++)
 {
 acc += yFilterBuf;
 }
 yPos = acc >> 3;
 currentChannel = ADC2_CHANNEL_6;
 }
 
 // Switch the channel and start new conversion.
 ADC2_ClearFlag();
 ADC2_ConversionConfig(ADC2_CONVERSIONMODE_SINGLE, currentChannel, ADC2_ALIGN_RIGHT);
 ADC2_StartConversion();
 }</pre>
 
 EOC中断轮流采集CH6和CH7通道的数据,并对采样值进行7阶移动平均滤波,结果存放于xPos和yPos变量。
 
 
 2.TIM1更新/溢出中断
 <pre>/*
 * TIM1 update/overflow interrupt handler
 * This IRQ is executed every 250us.
 */
 @far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
 {
 static u8 direction;
 s16 xError, yError;
 s16 xPWM, yPWM;
 
 // Change X direction
 if (direction & X_DIRECTION_FLAG)
 {
 GPIO_WriteHigh(GPIOC, GPIO_PIN_3);
 }
 else
 {
 GPIO_WriteLow(GPIOC, GPIO_PIN_3);
 }
 
 // Change Y direction
 if (direction & Y_DIRECTION_FLAG)
 {
 GPIO_WriteHigh(GPIOC, GPIO_PIN_5);
 }
 else
 {
 GPIO_WriteLow(GPIOC, GPIO_PIN_5);
 }
 
 // PID calculation
 xError = xPID.targetValue - xPos;
 yError = yPID.targetValue - yPos;
 xPWM = calcPID(&xPID, xError);
 yPWM = calcPID(&yPID, yError);
 
 // Set the direction and OC polarity based on the PID output (takes effect on next PWM cycle)
 if (xPWM >= 0)
 {
 direction &= (u8) ~X_DIRECTION_FLAG;
 TIM1_OC1PolarityConfig(TIM1_OCPOLARITY_LOW);
 }
 else
 {
 direction |= X_DIRECTION_FLAG;
 xPWM = -xPWM;
 TIM1_OC1PolarityConfig(TIM1_OCPOLARITY_HIGH);
 }
 
 if (yPWM >= 0)
 {
 direction &= (u8) ~Y_DIRECTION_FLAG;
 TIM1_OC2PolarityConfig(TIM1_OCPOLARITY_LOW);
 }
 else
 {
 direction |= Y_DIRECTION_FLAG;
 yPWM = -yPWM;
 TIM1_OC2PolarityConfig(TIM1_OCPOLARITY_HIGH);
 }
 
 // Update the PWM (takes effect on next PWM cycle)
 TIM1_SetCompare1(xPWM);
 TIM1_SetCompare2(yPWM);
 
 TIM1_ClearITPendingBit(TIM1_IT_UPDATE);
 }</pre>
 
 TIM1更新/溢出中断每个PWM周期开始时(250?s)执行一次,每次执行会根据当前xPos和yPos进行PID运算和更新PWM占空比。10-bit ADC采样范围0~1023,因此PID的参考点设在中点511。
 为了防止更新占空比造成当前PWM周期占空比不正常,输出比较器使用了预载功能,即更新会在下一个PWM周期才生效。
 控制方向根据PID运算结果的符号来判断,保存在direction变量中,在下一个PWM周期才对应更新GPIO的输出状态,与PWM占空比更新同步。
 
 PWM控制时序如下图(只画了X轴,Y轴同理):
 
  PWM时序 (原文件名:磁悬浮(PWM时序).png)
 当PID输出为正数时,PC3输出低电平,PC1/TIM1_CC1输出高电平有效的PWM脉冲。
 当PID输出为负数时,PC3输出高电平,PC1/TIM1_CC1输出低电平有效的PWM脉冲,图中T3为占空比20%的PWM,T4为占空比40%的反向输出PWM。
 结合L293D,图中T1, T2周期输出占空比20%的正向PWM至线圈,而T3, T4周期分别输出20%和40%占空比的反向PWM至线圈,从而实现双向磁场控制。
 
 
 3.PID参数
 <pre>        xPID.Kp = 4;
 xPID.Ki = 0;
 xPID.Kd = 30;
 xPID.integrationError = 0;
 xPID.prevError = 0;
 xPID.targetValue = 511;</pre>
 
 由上面的代码可见,本系统只使用了PD控制,Ki为0,积分项不参与计算。Kp, Kd的值需要根据悬浮物的质量调整,整定方法可参考有关资料,这里就不详述了。
 
 
 4.霍尔采样电路
 霍尔采样电路采用了2片有源线性霍尔UGN3503,后接一级运放反相放大器,放大倍数为30倍,并且由一个10K多圈电位器调整输出中点电平。
 运放和霍尔使用5V单电源供电,UGN3503的输出中点电压约2.5V,运放LM324输出电压范围约50mV~3.6V,因此STM8的ADC参考电压使用3.3V,分别调整10K电位器使LM324输出1.65V,此时STM8的ADC采样结果应该接近511,误差虽然越小越好,但不需要绝对准确(霍尔、运放都有温漂,不可能绝对准确),一般调整至480~550范围内即可。
 
 
 5.硬件安装
 
  线圈及霍尔安装 (原文件名:磁悬浮(线圈及霍尔安装).png)
 每组两个线圈同名端相连(即反向串接)。霍尔置于4个线圈中间的空隙,高度约在线圈中部,需注意霍尔作用面应位于线圈中轴线上,不应采用图中虚线标示的安装方式,否则会造成定位误差。
 磁环置于电路板下面,需要注意的是磁环的放置会影响霍尔输出,因此运放输出中点调节最好配合磁环定位同时调节。
 
 
 | 
 
 | 
 |