模块学习(一)——编码电机

想实现对电机的测速,因此开始接触编码电机。此次采用的是RS365编码器电机。

一、编码电机的初步了解

通过编码电机可以测出速度。常见一般编码电机分成两种,一是光电编码器,另一个是霍尔编码器。有六个接口,两个是电机的正负极,两个是编码器的正负极,还有两个是A,B相。

倍频的概念:如果只采集A/B相的上升/下降沿,即一倍频。若采集A/B的上升和下降沿,即二倍频,最多可以达到四倍频。这样测出来的速度是非常精确的。
在这里插入图片描述

具体直观了解可由上图可见。

在这里插入图片描述

以上是此次采用的编码电机的各个参数。所以可以得知,减速电机转动一周,可以产生30390个脉冲。若采用四倍频,则可以产生430*390个脉冲,可以大大提高精确度。

软件测量方面,可以采用外部中断捕获上升沿或者下降沿来实现测速。

二、如何测量一定时间内的脉冲

这就要用到TIM的输入捕获功能了,正好之前学习TIM的时候,仅仅涉及到普通PWM波的输出,今天顺便一起学习一下TIMA的输入捕获功能。

1.TIMA

根据手册可知TIMA具有7个输入捕获通道,并且具有广泛的中断功能。

在这里插入图片描述
由上图可初步了解定时器A的工作框图。

具体配置方法如下,这个配置方法之中,参阅了许多资料,最后还是只能靠自己一点点啃数据手册,如有不完善,还希望各位一起交流。

第一步:配置IO口
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN2);//配置为外围输入

配置P2.0为外设输出

第二步:连续模式初始化
 //Start timer in continuous mode sourced by SMCLK
   Timer_A_initContinuousModeParam initContParam = {0};//定义连续模式初始化结构体
   initContParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;//确定时钟源SMCLK
   initContParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;//不分频
   initContParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;//禁用Tim A中断
   initContParam.timerClear = TIMER_A_DO_CLEAR;//重置计数方向等.....
   initContParam.startTimer = false;//先不开启计数器
   Timer_A_initContinuousMode(TIMER_A0_BASE, &initContParam);
第三步:清除比较模式中断
//清楚比较模式中断(初始化比较模式)
   Timer_A_clearCaptureCompareInterrupt(TIMER_A0_BASE,
       TIMER_A_CAPTURECOMPARE_REGISTER_1
       );
第四步:捕获模式初始化
Timer_A_initCaptureModeParam initCapParam = {0};//定义比较模式初始化结构体
   initCapParam.captureRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;//选择TIM_A0的捕获比较通道1
   initCapParam.captureMode = TIMER_A_CAPTUREMODE_RISING_EDGE;//捕获上升沿
   initCapParam.captureInputSelect = TIMER_A_CAPTURE_INPUTSELECT_CCIxA;//确定输入选择为CCIxA(这个不知道怎么看怎么确定)
   initCapParam.synchronizeCaptureSource = TIMER_A_CAPTURE_SYNCHRONOUS;//与时钟同步
   initCapParam.captureInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;//使能捕获中断
   Timer_A_initCaptureMode(TIMER_A0_BASE, &initCapParam);
第五步:开始计时,打开全局中断
Timer_A_startCounter( TIMER_A0_BASE,//开启计时
           TIMER_A_CONTINUOUS_MODE
               );

   //Enter LPM0, enable interrupts开启全局中断
   __bis_SR_register(LPM0_bits + GIE);

   //For debugger
   __no_operation();
第六步:中断服务函数

在main.c中,写上输入捕获的中断服务函数

#pragma vector = TIMER0_A1_VECTOR//定时器中断
__interrupt void Timer_A(void)//生命一个函数为中断函数
{
  static int CNT = -1;
  CNT++;
  switch(__even_in_range(TA0IV,14))//中断标志位——对应捕获通道
  {
    case 2 ://如果是CCR1产生的中断
    GPIO_setOutputHighOnPin(GPIO_PORT_P4,GPIO_PIN7);
    if(CNT % 2 == 0)//记录第一次的上升沿Counter计数值
    {
        edge_count1 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);//将此时的上升沿计数值存入变量

    }
    else if(CNT % 2 == 1)//检测到下降沿触发
    {
        edge_count2 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);//将此时的下降沿计数值存入变量
    }
    PWM_Frequency  = (unsigned int)(1000000/(abs(edge_count1 - edge_count2) * 0.948));
    break;
    case 4 : break;
    case 10: break;
    case 14:break;//此时发生计数器溢出
    default: break;
  }
}

中断服务函数中这种编写方式,对于低频50HZ,100HZ是比较准确的。

看效果图
在这里插入图片描述
在这里插入图片描述

问题:

一旦频率上升超过1Khz,就不准确了。对于此时才用的是MCLK时钟,频率为1.054Mhz,也就是说CNT计数器每次+1,对应的时间是0.948us,按理来说,可测量频率范围应该在0-1.054Mhz之间,而实际可测频率仅有几百HZ,这是为什么呢??

采用一下测量方法,效果依然不好,这是为啥啊啊啊啊啊
下面这部分进行了10倍分频…因此是105400

#pragma vector = TIMER0_A1_VECTOR//定时器中断
__interrupt void Timer_A(void)//生命一个函数为中断函数
{
  static int CNT = -1;
  CNT++;
  switch(__even_in_range(TA0IV,14))//中断标志位——对应捕获通道
  {
    case 2 ://如果是CCR1产生的中断
        PWM_Frequency = 105400/Timer_A_getCounterValue(TIMER_A0_BASE);
        Timer_A_clear(TIMER_A0_BASE);
        break;
    case 4 : break;
    case 10: break;
    case 14:break;//此时发生计数器溢出
    default: break;
  }
}

等我解决后再来继续补充!如果各位大佬有解决办法,也麻烦留言评论一下,谢谢!

问题在昨日晚八点解决!但是具体原因我还是不太清楚为啥。在那之后,我将主频升到16MHZ之后,发现对于频率的测量可以精准到1KHZ误差之内,再改进一下算法,现在误差只有十几赫兹了,代码如下。

基本配置

 GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN2);//配置为外围输入

    //Start timer in continuous mode sourced by SMCLK
   Timer_A_initContinuousModeParam initContParam = {0};//定义连续模式初始化结构体
   initContParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;//确定时钟源SMCLK
   initContParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_10;//不分频
   initContParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;//禁用Tim A中断
   initContParam.timerClear = TIMER_A_DO_CLEAR;//重置计数方向等.....
   initContParam.startTimer = false;//先不开启计数器
   Timer_A_initContinuousMode(TIMER_A0_BASE, &initContParam);

   //清除比较模式中断(初始化比较模式)
   Timer_A_clearCaptureCompareInterrupt(TIMER_A0_BASE,
       TIMER_A_CAPTURECOMPARE_REGISTER_1
       );

   Timer_A_initCaptureModeParam initCapParam = {0};//定义比较模式初始化结构体
   initCapParam.captureRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;//选择TIM_A0的捕获比较通道1
   initCapParam.captureMode = TIMER_A_CAPTUREMODE_FALLING_EDGE;//捕获上升沿
   initCapParam.captureInputSelect = TIMER_A_CAPTURE_INPUTSELECT_CCIxA;//确定输入选择为CCIxA(这个不知道怎么看怎么确定)
   initCapParam.synchronizeCaptureSource = TIMER_A_CAPTURE_SYNCHRONOUS;//与时钟同步
   initCapParam.captureInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;//使能捕获中断
   Timer_A_initCaptureMode(TIMER_A0_BASE, &initCapParam);

   Timer_A_startCounter( TIMER_A0_BASE,//开启计时
           TIMER_A_CONTINUOUS_MODE
               );

   //Enter LPM0, enable interrupts开启全局中断
   __bis_SR_register(LPM0_bits + GIE);

   //For debugger
   __no_operation();

中断服务函数

#pragma vector = TIMER0_A1_VECTOR//定时器中断
__interrupt void Timer_A(void)//生命一个函数为中断函数
{
  static unsigned int CNT = 0;

  switch(__even_in_range(TA0IV,14))//中断标志位——对应捕获通道
  {
    case 2 ://如果是CCR1产生的中断
        if(CNT % 2 == 0)//记录第一次的上升沿Counter计数值
        {
//            edge_count1 = Timer_A_getCounterValue(TIMER_A0_BASE);//将此时的上升沿计数值存入变量
            edge_count1 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
        }
        else if(CNT % 2 == 1)//记录第二次上升沿Counter计数值
        {
            edge_count2 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);//将此时的下降沿计数值存入变量
            Timer_A_clear(TIMER_A0_BASE);
        }
        PWM_Frequency  = (unsigned int)(1600000/(float)(abs(edge_count1 - edge_count2)));
        //PWM_Frequency = 1600000/Timer_A_getCounterValue(TIMER_A0_BASE);
        //Timer_A_clear(TIMER_A0_BASE);
        break;
    case 4 : break;
    case 10: break;
    case 14:break;//此时发生计数器溢出
    default: break;
  }
  CNT++;

  // Enable global interrupt
  _BIC_SR_IRQ(LPM0_bits);
}

尤其注意中断服务函数中的最后一句话,作用大概是是能全局中断的同时退出低功耗模式,如果缺少这句话,会使得你的代码一直在跑中断函数,而无法跑主函数!别问我为啥知道,我试了一整个上午才解决!!!望各位避坑!

接下来就是进行测速了!

unsigned int V_detect(unsigned int FRQ)
{
    //我们测出的频率就是单位时间内的脉冲数
    //对应该编码电机轮子一圈产生4.5 * 12 = 54个脉冲
    //轮子半径 32.5mm
    unsigned int speed = 0;
    float circle = 3.141*2*0.0325;
    speed = (unsigned int)(((float)FRQ/54)*circle);

    return speed;
}

这样测速就完成啦!

  • 6
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
STM32编码电机测速是通过使用编码器来获取电机的转速信息。编码器是一种将角位移或者角速度转换成一串电数字脉冲的旋转式传感器。在STM32中,我们可以使用霍尔编码器来实现测速功能。 首先,我们需要连接编码器和STM32开发板。编码器的接线图可以根据具体的编码器型号进行设置。然后,我们需要在STM32CubeMX中设置相应的引脚,并生成keil文件。 在编码器的工作原理中,我们可以通过测量单位时间内A相输出的脉冲数来得到速度信息。具体的转速计算方法是使用捕获值(一秒内输出的脉冲数)除以编码器线数(转速一圈输出脉冲数)再除以电机减数比(内部电机转动圈数与电机输出轴转动圈数比,即减速齿轮比)。 在源码中,我们可以使用定时器中断回调函数来计算电机的转速。具体的实现可以参考STM32的编码器模式来读取旋钮编码器的脉冲数的文章。在回调函数中,我们可以计算转速并进行相应的控制。 综上所述,通过连接电机、单片机、L298N电机驱动模块,并使用编码器来测量脉冲数,我们可以实现STM32编码电机的测速功能。 #### 引用[.reference_title] - *1* [STM32单片机—编码器测速](https://blog.csdn.net/Susquehanna/article/details/77504066)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [STM32(HAL库)——光电编码器、M/T法测量电机转速](https://blog.csdn.net/DIVIDADA/article/details/130198779)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值