1

I'm using an Arduino Mega 2560 to control a DRV8871 motor driver connected to a DC brushed motor. The setup includes:

  • Three buttons: UP, DOWN, and FAST
  • One limit switch input, where the upper and lower limit switches are connected in series
  • PWM speed control using slow decay, as explained in this TI forum post

Goals:

  1. Speed control: Run the motor at ~30% speed when UP or DOWN is pressed, and ~70% when UP or DOWN is pressed along with FAST.
  2. Acceleration: Implement a configurable acceleration curve.
  3. Non-blocking code: Ensure the program does not use delay() or block execution.

Issue:

I wrote the following code: GitHub link. However, when pressing UP or DOWN, the motor makes noise but doesn't move (possibly stalling, even with PWM = 100).

Interestingly, if I bypass PWM control and use:

digitalWrite(in1Head, HIGH);
analogWrite(in2Head, LOW);

the motor runs correctly.

Questions:

  1. Could this issue be due to incorrect PWM control?
  2. Is the motor stalling due to insufficient startup current?
  3. Any suggestions for debugging or improving the code?

Any insights would be greatly appreciated! ###update: I've updated the code trying to follow suggestions. Please let me know if I understood correctly.

  1. I put a while loop on the limitswitch. I want that when limitswitch is hit the motor stops, then reverse the direction and disengage the limitswitch moving for the disengage_ls time.
//Head motor
int in1Head = 44; //on ARDUINO MEGA 
int in2Head = 45;
int limSwHead = 19; //2 normally close limit switches connected in series
int FAST = 16; //push button
int UP = 17; //push button
int DOWN = 15; //push button
volatile bool limSwHeadTriggered = false;
// Direction enum and flag
enum Direction { FORWARD, BACKWARD };
Direction currentDirection = FORWARD; // Initialize with default direction
uint8_t PWM_fast =70; //value in % of motor speed
uint8_t PWM_slow =30; //value in % of motor speed
uint8_t PWM = 20; //min value of PWM (to be changed also below)
unsigned long zero = 0;
uint16_t accel_interval = 10;//time in millis to change the speed in acceleration
uint8_t speedIncrease = 5; //% of speed increase for each accel_interval
uint16_t disengage_ls = 500; //time in millis to disangage Limitswitch when hit
boolean fastState = false;
boolean upState = false;
boolean downState = false;
//----FUNCTIONS----------------------------------------------------------------------------------------
//INTERRUPTS
void headISR() {
 limSwHeadTriggered = true;
}
void HeadForward(uint8_t PWM) { 
 analogWrite(in1Head, 255);
 analogWrite(in2Head,(255 * (100 - PWM)) / 100); //inverse of the PWM in range 0-255
 currentDirection = FORWARD; // Set direction flag
}
void HeadBackward(uint8_t PWM) {
 analogWrite(in1Head,(255 * (100 - PWM))/ 100); //inverse of the PWM in range 0-255
 analogWrite(in2Head, 255);
 currentDirection = BACKWARD; // Set direction flag 
}
void HeadStop() {
 analogWrite(in1Head,255);
 analogWrite(in2Head, 255);
 PWM=20; 
}
void HeadCoast() {
 analogWrite(in1Head,0);
 analogWrite(in2Head, 0);
 PWM=20; 
}
void limSwHit() {
 HeadStop();
 if(currentDirection == FORWARD){
 HeadBackward(PWM_slow);
 }
 else if (currentDirection == BACKWARD){
 HeadForward(PWM_slow);
 }
}
void setup() {
 Serial.begin(9600);
 //head motor
 pinMode(in1Head, OUTPUT);
 pinMode(in2Head, OUTPUT);
 //limitswitch
 pinMode(limSwHead, INPUT_PULLUP);
 attachInterrupt(digitalPinToInterrupt(limSwHead), headISR, RISING); // Trigger on HIGH signal
 //Buttons
 pinMode(FAST,INPUT_PULLUP);
 pinMode(UP,INPUT_PULLUP);
 pinMode(DOWN,INPUT_PULLUP);
}
void loop() {
if(limSwHeadTriggered){
 unsigned long initialMillis = millis();
 while(millis()- initialMillis<= disengage_ls){
 limSwHit();
 }
 HeadCoast()
 limSwHeadTriggered=digitalRead(limSwHead);
}
// read the state of the pushbutton value, inverted due to pullup resistors:
 upState = !digitalRead(UP);
 downState = !digitalRead(DOWN);
 fastState = !digitalRead(FAST);
 if (upState && !downState && (millis()- zero >= accel_interval)){
 if (PWM < (fastState ? PWM_fast : PWM_slow)) {
 PWM += speedIncrease;
 }
 HeadForward(PWM);
 Serial.println("moving up");
 zero = millis();
 } 
 else if(!upState && downState && (millis()- zero >= accel_interval)) {
 if (PWM < (fastState ? PWM_fast : PWM_slow)) {
 PWM += speedIncrease;
 }
 HeadBackward(PWM);
 Serial.println("moving down:");
 zero = millis();
 }
 
 else{
 HeadCoast();
 }
}
InBedded16
2261 silver badge7 bronze badges
asked Jan 29 at 15:39
1
  • ok, question updated Commented Jan 29 at 16:45

1 Answer 1

1

If you have a voltmeter, voltage readings on the pins you're trying to use would be very helpful. Use them to verify you're setting up PWM correctly. Make a new script and add in only very basic code, ie setup the pins and then try to drive the motor using PWM in a single direction for 5 seconds on 5 seconds off. Play around with the PWM values and pay attention to current draw, that could be your issue. Once you get the motor movement working, then you can start integrating it into your main program.

Separate from the motor movements, check the part of your main loop that deals with the limit switch. You keep assigning a new time to your initialMillis variable, so you will only hit that 500ms benchmark if the function you call takes 500ms, which it doesn't. A while loop would fix this. Its unclear what you want the limit switch to do, but right now it doesn't really do anything - momentarily stops the motor and prints out a statement after the delay, but otherwise doesn't prevent you from still moving in the same direction or anything. So take a look at that and figure out what functionality you want. You might have to switch towards watching for the states of the buttons to change rather than if they are currently pressed, that way you can pause and move the other way after a limit switch hit even if the user doesn't let go of the button, which I believe is your intended behavior.

You have "!fastState" in your logic evaluation for the upState move, which means if you try to move UP and FAST you won't move at all. You correctly implemented this for the downState logic so I assume this is a relic from a previous code iteration you forgot to change.

answered Jan 29 at 20:03
6
  • Thanks for the in depth analysis. I'm going to make the trials with PWM, in the meanwhile I've updated the code trying to follow your suggestions., please let me know if I understood correctly. I have some doubts on the comment on zero subtraction: I'm setting the time to zero after each time I increase the speed, in order to reset the counter for the next speed increase (where accel_interval is the time between 2 different speed set. I don't understand why you suggest to remove it Commented Jan 30 at 16:56
  • @Luigi ah okay I missed where you set zero to millis() in those loops. Yes, that's fine, although something like zeroRef may be a less confusing variable name. Commented Jan 30 at 17:12
  • @Luigi two more quick notes: in the while loop for waiting the 500ms after a LS hit, you're continuously calling limSwHit() and then HeadCoast(). This will just continously stop and start the motor. You want to move those two function calls outside the while loop so it runs once, ends in the COAST state, and then enters the while loop to wait those 500ms. Second thing is that you have HeadCoast() inside an else statement, so everytime the accel_interval has not been reached, you switch to the COAST state. Move that to an else if statement that triggers when none of the buttons are pressed. Commented Jan 30 at 17:20
  • @Luigi and all this stuff is normal to iterate through, its why I recommend a simple script to get movement working first - otherwise how do you know if its the PWM thats the problem or the logic in your code? Start from your basic functionality, then build everything else in step by step and make sure it works before you move to the next thing. Commented Jan 30 at 17:25
  • 1
    Thanks. I've tried a simple script and it's working, so the issues are related to my code. I'm improving thanks to your hints. Regarding the while loop when LS hit, is it the same if I write delay(disengage_ls) instead? Commented Jan 31 at 11:09

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.