Traumflug / Teacup_Firmware

Firmware for RepRap and other 3D printers
http://forums.reprap.org/read.php?147
GNU General Public License v2.0
310 stars 198 forks source link

Any pointers on how to attach a servo? #311

Open thomaskilian opened 5 years ago

thomaskilian commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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

thomaskilian commented 5 years ago

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

thomaskilian commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

Please share your complete project/code somewhere.

thomaskilian commented 5 years ago

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 commented 5 years ago

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

thomaskilian commented 5 years ago

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 commented 5 years ago

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.