Skip to content
This repository has been archived by the owner on Aug 27, 2023. It is now read-only.

Any pointers on how to attach a servo? #311

Open
thomaskilian opened this issue Nov 4, 2018 · 16 comments
Open

Any pointers on how to attach a servo? #311

thomaskilian opened this issue Nov 4, 2018 · 16 comments

Comments

@thomaskilian
Copy link

I wonder if there are any pointers on how to attach a servo? I know that M280 is not supported (because it's a > 255 code) but would not mind misusing anything here. Basically I need to send just two servo positions (aka two PWM codes but with the servo PWM freq. of 50 Hz).

@phord
Copy link
Collaborator

phord commented Nov 6, 2018

In Teacup a servo is configured like a heater. It's not well-documented as it's rarely used. M106 is used to control the PWM for heaters and other devices. Servo math is funny, though. It's based on a 20ms duty cycle, regardless of the actual pulse repeat period. I guess you know this, already. 20ms = 50Hz.

I did some math here to attach a servo-like probe in the BLTouch z-probe. The resultant pulse-widths are dependent on the CPU frequency, among other things.

The math is pulse-width * 1024 * 256 / F_CPU (F_CPU is 16MHz or 20MHz on AVRs).

The required pulse width seems to be 600 + DEGREES * 10.

So, (600 + DEGREES * 10) * 1024 * 256 / F_CPU gets your desired angle in heater steps for M106. For 90 degrees on a 16MHZ chip, (600 + 900) * 1024 * 256 / 16000000 = 24.6 ==> M106 P3 S25. (YMMV. I found S24 worked better on the Z-probe.)

I considered writing this into some servo-specific M-command a few months ago, but had trouble getting it to work reliably. I thought my math was overflowing somewhere, but I'm not sure I remember that right anymore. Since I don't have a known-quantity servo to test with here, I decided it was better to leave it for another day.

@phord
Copy link
Collaborator

phord commented Nov 6, 2018

Make sure to choose a PWM pin that uses Timer0. The other pins are probably not configured correctly, and Timer1 is used internally.

@thomaskilian
Copy link
Author

Great, thanks for that! I'll see whether I can get it to work somehow. If so, I'll post feedback then.

@thomaskilian
Copy link
Author

Hmm. I implemented timer0 on a test sketch and it worked nicely. Once put into Teacup I realized that timer0 is already scaled for use with the heater PWM. I tried using timer2 but that instantly drove my extruder to send smoke signal. Seems to be more tricky than I thought :-/

@thomaskilian
Copy link
Author

@phord I'm a bit lost now. I tried a simple sketch (on a Mega2560) which produces nice servo ticks. But once I implant that (adapted) code into Teacup it loop-resets permanently once I start timer3. Any idea?

void setup() {
  // put your setup code here, to run once:
  for (int i = 3; i <= 13; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, 1);
  }
  digitalWrite(3, 0);
  digitalWrite(9, 0);

  TCCR3A = 0;
  TCCR3B = 0;
  TCNT3  = 0;
  OCR3A = 199;
  TCCR3A |= (1 << WGM31);
  TCCR3B |= (1 << CS31);
}


int servoTicks;
int servoWidth;
int servoDuty;
void timer0_start(int i) {
  cli();
  servoTicks = 0;
  servoWidth = i;
  servoDuty = 10; // 10 ticks ~ 0.2 sec
  digitalWrite(3, 1);
  TIMSK3 |= (1 << OCIE3A);
  sei();
}

ISR(TIMER3_COMPA_vect){
  servoTicks++;
  if (servoTicks >= 80) {
    servoTicks = 0;
    digitalWrite(3, 1);
    servoDuty--;
  } else if (servoTicks == servoWidth) {
    t[ptr++] = micros();
    digitalWrite(3, 0);
    if (servoDuty <= 0)
      TIMSK3 &= ~ (1 << OCIE3A);
  }
}

int w = 10;
void loop() {
  delay(2000);
  cli();
  timer0_start(w);
  w = 15-w;
  sei();
}

@thomaskilian
Copy link
Author

Never mind.It seems as if I need to enclose TIMSK3 |= (1 << OCIE3A); in cli/sei.

Not working still, but the reset loop is gone.

@thomaskilian
Copy link
Author

I'd really appreciate some help now. I rather figured out how the heating PWM works. I'm using DIO7, 9 and 10 for bed, extruder and fan. On my Mega this will involve timers 2 and 4 for the PWM. So I used timer3 for my own purpose. I did set it like in the code snippet I posted above (without the ISR just resetting TIMSK3 once called). But after 5 seconds or so the Arduino gets a reset. Where is it that timer3 interferes somewhere the Arduino is reset???

@Wurstnase
Copy link
Collaborator

First I would do very simple thing with the ISR for testing. Probably you could send one single char (ISR should always do its stuff very fast) through the serial line.

@thomaskilian
Copy link
Author

I don't know what would help me in that. I'm after a way to create a 50Hz signal with varying duty lengths (of about 1ms).

@Wurstnase
Copy link
Collaborator

So your timer looks like doing the right thing? And after 5 seconds the Arduino is reseting?
Where do you exactly implement your code?

@thomaskilian
Copy link
Author

Yes. The timer is definitely triggered, but after some 5 seconds the whole thing resets. I implemented it in heater-avr.c in the #ifdef TCCR3A part and below the timer start routine/ISR

@Wurstnase
Copy link
Collaborator

Please share your complete project/code somewhere.

@thomaskilian
Copy link
Author

Dropbox

I removed stuff I did not need. heater-avr.c and gcode_process.c contain the most important modifications. It's just hardcoded for the Atmega use (port 3 shall receive the servo PWM).

@Wurstnase
Copy link
Collaborator

How did you test your changes? I see that you've implement a test gcode for yourself.

@thomaskilian
Copy link
Author

thomaskilian commented Nov 25, 2018

Yup. I just send a "G2", "G2 P0" or "G2 P1". They all trigger the timer once. For my purpose that will do since I anyway write the SW for the servo movement myself (rather than using the unreachable position servo code somewhere above 255).

@thomaskilian
Copy link
Author

thomaskilian commented Nov 28, 2018

I have just altered the PWM to SW and the reset is gone. I could live with that but still curious why the HW PWM interferes that way.

So (finally) I can confirm this works. You need to modify the timer setting in heater_avr.c and add an ISR like in the above example code. To trigger the servo movements for one of its (for me 2) positions you need to implement a M- or G-code. I just used G2 Px to call the timer_start routine. You could as well implement M24 as a substitute for M280 (servo position) as long as you don't have
a SD-card. M24 because 280%256 = 24; Teacup just recognizes one byte for the code.

And as additional remark: The PWM now does not longer send the short pulses but are steadily off when not in use. So the noise from the fan during idle is now completely gone.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants