Page 1 of 1

Timed output with avr-usb

Posted: Thu Jan 01, 2009 9:44 pm
by SA007
Hi,

Ik trying to get a pretty precicely timed output with avr-usb, but i've got a small problem:
Because the mcu (attiny45) does not have a 16 bit timer ia have to partialy use software to make the output signal.

This mostly works without a problem, but if I start communicating with the device the usb interrupts mess the timing up.
At the moment I use cli() and sei() before and after the timing-crucial part, which makes the crucial part work fine with a consistent timing, but it has a problem.
During that time (which is about 10% of the running time) usb communication doesn't work and if I send signals to the device during that period (to change the output) I get errors.
There is plenty of time to get proper commincation since most of the time the device can be reached, but in batched mode there are to much errors to just ignore.

Is there some setting or message to send to the computer that says "don't communicate right now, i'm busy"?
I think disconnecting from the bus and reconnecting 50 times a second is not the solution :)

FYI: I'm building a miniature version to control a servo via usb using the attiny45 to get a controller about the same size as an usb-b plug.

Posted: Sun Jan 04, 2009 7:51 pm
by christian
You can't disconnect and re-connect 50 times a second, the procedures take much too long for such a frequency.

You also must not disable interrupts for more than a couple of cycles because AVR-USB MUST react within microseconds to a USB request.

Your best option is to work on the PWM code. You can use output compare mode with timer 0 and prepare a level change up to 256 timer clocks in advance.

Posted: Sun Jan 04, 2009 9:44 pm
by SA007
The problem with that is that the attiny45 doesn't have a 16 bit timer.

If I use the 8-bit PWM to control the servo, and would be able to get the prescaler to 50Hz.

A servo has a range of 90 degrees over 1ms pulse width, at the required 20ms pulse delay that would mean that each step in the PWM would give a massive 7 degrees rotation on the servo.

I was actually looking for a fluid motion on the servo part which requires a lot more steps.

Posted: Sun Jan 04, 2009 9:56 pm
by christian
I didn't mean to use timer 0 in PWM mode. Use it to turn a port pin on or off (or leave it as it is) at a given count. This is rather complex, admittedly, but it should be possible.

BTW: Servos have a dead range to avoid oscillation around the target position. This results in a resolution of ~ 100 steps for a cheap servo.

Posted: Sun Jan 04, 2009 10:29 pm
by SA007
100 steps is a LOT better than 12 :)

Do you mean making a timer that interrupts every 10µs or so and incement a counter and switch the servo on that?

That might actually be a good idea.

Posted: Sun Jan 04, 2009 10:42 pm
by christian
No! I mean something like that (it does not work as described below, but you get the general idea):

Configure timer 0 for PWM and enable the overflow interrupt. In the overflow interrupt routine (which enables global interrupts immediately, of course) set the PWM value to 0 so that no pulse is generated, except every 20th cycle where you set the servo position value. This way you can make an asymmetric PWM signal which can be in the range of 256*20 : 1 to 256*20 : 255.

It does not work as explained above because when you set the PWM value to 0, you still get a 1 cycle glitch. You would have to work with inverted output so that 255 is always-off. And then you would have to take care of timing: When to set the PWM value so that it is used in time.

My original idea was to extend the 8 bit counter to 16 bit in software (using the overflow interrupt). Then determine the on and off times of your PWM signal, handle the upper 8 bit in the overflow interrupt and program the counter to set/clear the output pin according to the lower 8 bit. You still have to take care of concurrency and other ugly stuff, but it should be possible in principle.

Posted: Sun Jan 04, 2009 11:36 pm
by SA007
Mm, a glitch is unacceptable, not matter if its on 255 or on 0.

The point is, if I use the PWM output to directly control the servo I don't have enough timers for multiple servo's.
So, some software is required, and I think using one timer to do the 50Hz part and one to 0.01ms steps would be more optimal that using the overflow of the timer.

I was thinking about somthing like this for the (pseudo)code:

Code: Select all

50hs_timer() {
  enable 10µs timer;
  servo1_output = 1;
  servo2_output = 1;
  ...
}

10µs_timer() {
  cli()
  counter++;
  servo1_output = counter < servo1_value;
  servo2_output = counter < servo2_value;
  if (counter=255) disable 10µs timer
  ...
  sei()
}


I've put the cli/sei there so that that part isn't interrupted by the usb part, at 16,5Mhz that should take less than a microsecond.

Posted: Mon Jan 05, 2009 1:44 pm
by christian
When you generate the signals from software (which may be required if you need more than one output), you get glitches from time to time when USB traffic interrupts the output pulse. The USB interrupt runs for ~ 100 microseconds and blocks all other activity during this time.

This may be acceptable for servos due to the mechanical inertia of the servo.

Instead of the 10 us timer, I'd recommend that you compute the next timer value where an action must be taken and schedule an interrupt for this count. You can do this by setting the timer so that an overflow occurs after so many counts or via the OCR register. But then you can as well configure the timer to turn off the bit when the OCR value is reached. This saves the high frequency interrupt.

Posted: Tue Jan 06, 2009 11:20 pm
by SA007
100 microseconds is very noticable on a servo output. That is why I was looking for a way to interrupt the usb traffic during the signal calculation.

It is indeed prossible to calculate the next interrupt but first I'm going to try the alot of interrupts approach to see if is improves/removes the problem i'm having with the usb interrupting.

Posted: Wed Jan 07, 2009 3:20 pm
by christian
Note that you cannot interrupt the USB interrupt! When the USB driver's interrupt is active, there's no way to perform higher priority tasks (unless they are implemented completely in hardware).

The USB driver stays active for an entire USB dialog: Request from the server and response from AVR-USB.