arduino 用伺服电机中断

Posted

技术标签:

【中文标题】arduino 用伺服电机中断【英文标题】:arduino interrupts with servo motor 【发布时间】:2016-03-07 09:57:45 【问题描述】:

目前正在开展使用 arduino UNO 和伺服电机打开带有访问代码的门的项目。正常操作需要使用工作正常的键盘输入访问代码。另一种选择需要按下一个按钮,该按钮会导致伺服电机旋转中断。我的问题是我的中断只工作一次,再也不会工作。加上我如何放置for循环以延迟旋转中断函数内的伺服电机。我知道这是不可能的,但我正在调用另一个具有 delayMicroseconds 的函数,但这一切都不起作用。下面是我的实现,请帮忙

#include <Keypad.h>
#include <LiquidCrystal.h>
#include <Servo.h>

Servo servo;


const int openButtonPin = 2;

void setup() 
  // put your setup code here, to run once:

  servo.attach(5);

  pinMode(openButtonPin, INPUT); //Pin 2 is input
  attachInterrupt(0, enforceOpenAccess, HIGH); // PIN 2




void(* resetFunc)(void) = 0;

void loop()

  //My other keypad implementations go here


void myDelay(int x)  // function to cause delay in the interrupt

  for(int i = 0; i<x; i++)
  
    delayMicroseconds(1000); 
  



void enforceOpenAccess() // ISR

   for(int k =0; k<=180; k+=2)
     
     servo.write(k); //rotate the servo
     myDelay(30);  //delay the rotation of the servo
   

上面的代码是在 proteus 中模拟的 arduino UNO 上运行的,中断按钮是一个按钮。如果有其他实现方式但与我上面描述的行为相同,请提供帮助。非常感谢

【问题讨论】:

为什么服务例程在按下按钮时只运行一次而不再运行 在中断处理程序中做需要很长时间的工作是不好的。我认为你应该让你的中断处理程序只提高一个标志,loop() 应该轮询标志并在标志被提高时执行工作。 【参考方案1】:

您发布的代码片段存在一些问题。为了完整起见,您应该发布循环函数,因为我们无法猜测您在里面写了什么。

只有一条评论:你做了引体向上吗?否则使用 INPUT_PULLUP 而不是 INPUT 作为按钮 pinmode。

主要的一个是您为 HIGH 模式附加了中断,这将在引脚启动时触发中断,而不是在上升沿。请使用宏digitalPinToInterrupt 映射到正确的引脚:

attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);

那么.. 让我们改进代码。只有当您必须立即(= 少于几毫秒)响应输入时,您才真正应该使用中断。在这里你不必这样做,所以最好检查循环中的按钮(更多关于转动电机跟随)

uint8_t lastState;

void setup()

    ...
    lastState = LOW;


void loop()

    uint8_t currentState = digitalRead(openButtonPin);
    if ((currentState != lastState) && (currentState == HIGH))
    
        // Start turning the motor
    
    lastState = currentState;
    ...

这也能让你正确地去抖动按钮:

#include <Bounce2.h>
Bounce debouncer = Bounce(); 

void setup()

    ...
    pinMode(openButtonPin, INPUT); //Pin 2 is input
    debouncer.attach(openButtonPin);
    debouncer.interval(5); // interval in ms


void loop()

    debouncer.update();
    if (debouncer.rose())
    
        // Start turning the motor
    
    ...

另一方面,如果您真的想使用中断(因为等待几毫秒对您来说太长了),您应该这样做:

#include <Bounce2.h>
Bounce debouncer = Bounce(); 

void setup()

    ...
    pinMode(openButtonPin, INPUT);
    attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);


void loop()

    ...


void enforceOpenAccess() // ISR

    // Start turning the motor

看起来像你的代码?不,因为现在我们将讨论转动马达

您不应该使用延迟来执行步骤,否则您将等待 30 毫秒 * 180 步骤 = 5.4 秒才能执行任何其他操作。

但是,您可以制作一种简化的状态机。你希望你的伺服器以 1 的步长从 0 移动到 180。所以让我们用任何大于 180 的值来编码“不移动”状态,因此我们可以在循环中做这样的事情:

unsigned long lastServoTime;
uint8_t servoPosition = 255;
const int timeBetweenSteps_in_ms = 30;

void loop()

    ...
    if (servoPosition <= 180)
     // servo should move
        if ((millis() - lastServoTime) >= timeBetweenSteps_in_ms)
        
            lastServoTime += timeBetweenSteps_in_ms;
            servoPosition++;
            if (servoPosition <= 180)
                servo.write(servoPosition);
        
    

然后,使用前面的任何示例,而不是 // Start turning the motor 写入

lastServoTime = millis();
servoPosition = 0;
servo.write(servoPosition);

这样即使按下按钮也不会阻塞主循环

【讨论】:

这是我的循环()中的内容 你的循环中有什么? 我刚刚发布了它,再次检查我的答案,现在考虑您的答案,我应该这样做的更好方法是轮询而不是中断,是这样吗? 通常你应该编辑你的问题而不是写一个答案来添加数据。无论如何,是的,首选方式是轮询,因为它们是低优先级任务。而我首选的解决方案是第二种(即使用Bounce2类来实现去抖动) 非常感谢,这真的很有帮助,现在让我开始工作并更改我的代码【参考方案2】:

这就是我的循环()中的内容

 char key = keypad.getKey();
  if(key)
     

    if(j < 10)
      

        studentNumber[j] = key;
        //holdMaskedNumber[j] = '*';
        lcd.setCursor(0,2);
        lcd.print(String(studentNumber));

        if(j == 9)
        
          studentNumber[9] = '\0';
          //holdMaskedNumber[9] = 0;
          lcd.clear();
          //String number = String(studentNumber);
          //lcd.print(number);

          //delay(1000);
          //lcd.clear();
          lcd.print("Access Code");

        

        j++;
      


    else
    
       if(i < 5)
    
      accessCode[i] = key;
      holdMaskedCode[i] = '*';
      lcd.setCursor(1,2);
      lcd.print(String(holdMaskedCode));
      if(i == 4)
      
        holdMaskedCode[5] = '\0';
        accessCode[5] = '\0';
        //lcd.clear();
        //lcd.setCursor(0,0);
        //accessCodeString = String(accessCode);
        //lcd.print(accessCodeString);
        //delay(1000);
        lcd.clear();


     for(int i =0; i<6; i++)
          
            lcd.print("Please wait.");
            delay(500);
            lcd.clear();
            lcd.print("Please wait..");
            delay(500);
            lcd.clear();
            lcd.print("Please wait...");
            delay(500);
            lcd.clear();

          
          digitalWrite(4, HIGH);
          lcd.print("Access Granted"); 
          for(int k =0; k<=180; k+=2)
            
            servo.write(k);
            delay(30);
          
          resetFunc();

      

      i++;
     
      
  

【讨论】:

以上是关于arduino 用伺服电机中断的主要内容,如果未能解决你的问题,请参考以下文章

Arduino Uno+步进电机28BYJ-48 简单应用之摇头转圈自定义模式demo

Arduino STM32+霍尔传感器+OLED显示屏制作中断计数和电机转速显示二

Arduino STM32+霍尔传感器+OLED显示屏制作中断计数和电机转速显示

如何使用arduino控制一个5V电机

求助:Arduino同时执行2个步进电机命令

arduino怎么用ks103