Teacup_Firmware icon indicating copy to clipboard operation
Teacup_Firmware copied to clipboard

Any pointers on how to attach a servo?

Open thomaskilian opened this issue 6 years ago • 16 comments

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).

thomaskilian avatar Nov 04 '18 18:11 thomaskilian

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 avatar Nov 06 '18 02:11 phord

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

phord avatar Nov 06 '18 02:11 phord

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

thomaskilian avatar Nov 06 '18 08:11 thomaskilian

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 avatar Nov 13 '18 13:11 thomaskilian

@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 avatar Nov 18 '18 19:11 thomaskilian

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 avatar Nov 24 '18 13:11 thomaskilian

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???

thomaskilian avatar Nov 24 '18 18:11 thomaskilian

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.

Wurstnase avatar Nov 25 '18 14:11 Wurstnase

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).

thomaskilian avatar Nov 25 '18 14:11 thomaskilian

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

Wurstnase avatar Nov 25 '18 14:11 Wurstnase

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

thomaskilian avatar Nov 25 '18 14:11 thomaskilian

Please share your complete project/code somewhere.

Wurstnase avatar Nov 25 '18 14:11 Wurstnase

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).

thomaskilian avatar Nov 25 '18 15:11 thomaskilian

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

Wurstnase avatar Nov 25 '18 17:11 Wurstnase

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 avatar Nov 25 '18 18:11 thomaskilian

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.

thomaskilian avatar Nov 28 '18 17:11 thomaskilian