0

我正在开发直流电机的控制。该电机有一个编码器,可根据其运动产生脉冲。

我需要通过监控脉冲数来控制电机轴的转数。

我为此应用程序使用 SMT8S103F3。

这个想法是使用 Timer1 作为脉冲计数器。为此,我将定时器配置为接收外部信号,在这种情况下是编码器脉冲,并且每个脉冲都必须增加计数器。

我遵循文档 ST RM0016 第 17.4.3 章。但是该应用程序无法正常工作。计数器不递增。

遵循开发的代码。

void config_counter(){
  TIM1_PSCRH = 0;
  TIM1_PSCRL = 0; //Prescalar 1 division
  TIM1_ARRH = 0;
  TIM1_ARRL = 0;  //Auto counter disabled
  TIM1_CNTRH = 0;
  TIM1_CNTRL = 0; //Reset counter
  TIM1_IER = 0; //Interrupt disabled
  TIM1_SR1 = 0; //Clear Interrupt
  TIM1_CCMR2 |= 1<<0; //External pulse source T1C2
  TIM1_CCER1 |= ~(1<<5); //Rising edge
  TIM1_SMCR |= 3; //T1C2 input
  TIM1_CR1 |= ~(1<<0); //Counter disabled
  
  return;
}

void set_counter_enable(uint8_t enable){
  if(enable==1)
    TIM1_CR1 |= 1<<0;
  else
    TIM1_CR1 |= ~(1<<0);

  return;
}

void set_counter_updown(uint8_t updown){
  if(updown==1)
    TIM1_CR1 |= ~(1<<4);
  else
    TIM1_CR1 |= 1<<4;

  return;
}

uint8_t start_movement_monitor(uint8_t dir){

  while(1){
    if(dir == 1){
      if(((TIM1_CNTRH<<8)+TIM1_CNTRL)>200)
        return 1;
    }else if(dir == 2){
      if(((TIM1_CNTRH<<8)+TIM1_CNTRL)<50)
        return 1;
    }
    else{
      return 1;
    }
  }

  return 1;
}


int main() {

  config_gpio();
  config_counter();
  set_counter_updown(0);
  set_counter_enable(0);

  set_counter_enable(1);
  set_motor_enable(1);
  set_motor_movement(1);

  start_movement_monitor(1);

  set_motor_movement(0);
  set_motor_enable(0);
  set_counter_enable(0);
  return 0;
}

有任何想法吗?

如果定时器不是该应用的最佳选择,那么如何实现脉冲计数器?

脉冲频率为 700kHz。

4

3 回答 3

1

解决了。

要在 stm8 引脚上使用定时器,必须写入选项字节来更改引脚的功能。这是通过写 AFR0 来完成的。

使用定时器 1 通道 2 的定时器配置如下。

void config_counter(){
  TIM1_CNTRH = 0;
  TIM1_CNTRL = 0; //Reset counter
  TIM1_IER = 0; //Interrupt disabled
  TIM1_SR1 = 0; //Clear Interrupt
  TIM1_CCMR2 |= 1<<0; //External pulse source T1C2
  TIM1_CCER1 &= ~(1<<5); //Rising edge
  TIM1_SMCR |= (7<<0); //External Clock Source
  TIM1_SMCR |= (6<<4); //T1C2 Input Source
  TIM1_CR1 &= ~(1<<0); //Counter disabled
  
  return;
}

于 2021-01-06T19:24:34.840 回答
0

这个问题有两种可能的解决方案。

定时器的解决方案要求定时器的时钟输入与电机脉冲相关联,这需要一些外部硬件连接,我不知道你是否可用。好处是您只需读取计时器值即可了解圈数,并且使用预分频器可以缩放输入。缺点是定时器的数量通常是有限的(你只有一个或两个定时器),而且定时器总是被配置为计算电机转数,你不能将它用于其他事情。

另一种解决方案是为电机脉冲关联一条中断线并编写一个中断处理程序。您需要(通过硬件)将中断与电机脉冲相关联,因此当您收到中断时,中断处理程序会增加一个变量

一旦你有了这个......你只需要检查变量值是什么,因为变量的更新只反映了电机转动了多少次。

volatile long loops = 0;

void motor_pulse_handler()
{
    loops++;
}

int main()
{
    /* this function installs the handler to be called each time the motor starts a new turn. */
    install_handler(MOTOR_PULSE_INTERRUPT, motor_pulse_handler);
    ...
    for (;;) {
        printf("\rloops: %ld", loops);
        usleep(10000);
    }
}     

该函数install_handler是强烈依赖于硬件的函数。它通常由开发系统作为库函数提供,因为它需要了解一些处理器架构才能实现。在操作中断相关控件时,您首先需要完全禁用中断。然后它必须激活 cpu 才能在相应中断线中的脉冲上中断(这就是MOTOR_PULSE_INTERRUPT在参数列表中包含 a 的原因,这应该是#defined 具有适当的值以使电机脉冲成为所选信号,而不是其他信号)最后它必须再次重新启用中断。当一个中断进来时,你的函数将在处理器可以做的事情序列的中间被调用。

通常,安装中断处理程序是一个两阶段的过程。实际调用的例程不是您传递给的install_handler 例程,而是执行某些硬件操作的不同例程。中断处理程序在中断被禁止的情况下被调用,它应该:

  • 保存所有 cpu 状态(寄存器、标志等),以便在最后恢复状态。
  • 屏蔽这个设备的中断,所以不会再中断(中断还没有被设备确认,所以如果启用中断,cpu会再次中断)
  • 启用中断,以便其他更高优先级的中断可以从此时开始中断 cpu。
  • 调用用户中断处理程序,因此可以更新计数器。
  • 再次禁用中断。
  • 确认中断。这通常意味着读取一些芯片寄存器以向中断硬件指示此中断已被确认。这会重置设备,因此当您从中断返回时,它不会因为同样的原因再次中断。
  • 恢复寄存器和标志(所有 cpu 状态),以便我们可以返回到 cpu 中断的点。
  • 从中断中返回。这通常是一个特殊的返回指令(不是用于从普通函数调用返回的指令)

此解决方案的优点是您可以卸载信号处理程序,这将停止计数过程。同样,仅当您实现某种机制来路由中断时,才有可能将中断线重用于其他事情(此问题类似于计时器中的问题,其中脉冲到达所选计时器的输入)

于 2021-01-06T01:45:34.857 回答
0

TIM1_CR1 |= ~(1<<0); //Counter disabled不禁用计数器。它CEN保持不变,并将所有其他位设置为1- 包括OPM. 你想要&=。对于set_counter_updown.

于 2021-01-05T22:36:41.907 回答