OnStep telescope control on teensy.

Status
Not open for further replies.

Macona

Active member
I have the mechanical portion of my servo driven telescope mount almost finished so it is time to start messing with the controller for it. I plan on using OnStep, an arduino based controller here:

http://www.stellarjourney.com/index.php?r=site/equipment_onstep

Firmware here (remove "-experimental" from the folder name and open the OnStep.ino):

https://github.com/hjd1964/OnStep

The arduino sketch was intended to run on an Arduino 2560 but I would really like to use a Teensy 3.1, I am hoping to get a higher step pulse rate than what he is getting since my motors are much higher resolution than his. He had some timing issues from the serial port so there are direct register calls bypassing the arduino stuff.

There are three sections that need modification, the main OnStep.ino, Serial.ino, and Timer.ino. They all directly access the serial port and timers. How do I change it to work with the Arm?

-Jerry

IMG_8775 by macona, on Flickr
 
What is it you need help with? It's definitely not the CNC part...

I'm willing to help you for a skull or for a small turned part I need for my explorad's optical encoder ;) (Just joking - I'll have a look at that arduino code and see what I can do)

Regards

Christoph
 
Mostly just getting the code to work on a Teensy which may all be moot now, the guy who wrote the OnStep software just ordered a Teensy 3.1 to try.

If you need a part made just send me a drawing.
 
OK, the first sketch (OnStep.ino) is pretty huge so I skipped that and looked at Serial.ino.

Serial.ino should be fairly easy to port to Teensy 3.1 because what it does is something used everywhere in the microcontroller world: prepare food, and take small bites until nothing is left. The food, here, is a string that has to be sent through the serial port, and the main loop sends single characters and receives single characters until a terminating zero is found.

Timer.ino is a bit more complex. It's not totally clear to me with what other parts of the application it is connected, but apparently it sets up 3 timers (Timer1, Timer3 and Timer4). Timer one seems to be used for general control of sidereal tracking and also controls the other timers, which output pulses to the motors. And that's a point where you might need to customize the OnStep code. My impression is that it was made for far lower resolution.

You have 31,457,280 steps per revolution as per your project description on hackaday. To track an object you will rotate your telescope at 1 revolution per day, that's 1 rev/(24 h/d * 60 min/h * 60 s/min). Those make up for about 364 steps per second. That's not too high, after all. When I first saw your steps per rev number I thought "OMG he'll need a very fast clock for that" but I was wrong.

Now back to topic: porting the timer code to Teensy 3.1 should also be low-pain when ready libraries are used, such as the IntervalTimer library that comes with teensyduino.

Regards

Christoph
 
Using the IntervalTimer library in this case won't work out of the box, because it sets up a *fixed* interval. OnStep, however, adjusts the update period of Timer3 and Timer4 during runtime. So you'd need a modified version of IntervalTimer that allows this - or use something else. In any case, the IntervalTimer library is a very good base to start from!
 
Paul, how do you update the Timer3 and Timer4 intervals during runtime? I haven't seen any method for that in the IntervalTimer code, and it would be vital if I'm understanding the OnStep code correctly. The overflow ISRs for Timer3 and Timer4 change the respective OCR values.
 
Ok, I've edited the code enough so it at least compiles. I don't have the hardware for testing, but I'm at least pretty sure these edits preserve everything as-is on Arduino Mega.

https://github.com/PaulStoffregen/OnStep

Since he's getting a Teensy 3.1 for testing, I believe I'll submit this untested stuff as a pull request. It should at least save a lot of time on converting to Teensy 3.1.
 
If the Serial1 & Serial (USB) interrupts cause timing problems (as they obviously do on AVR), I've got an idea or two about using NVIC priorities so the timer interrupts will interrupt the serial and USB interrupts.
 
Paul, how do you update the Timer3 and Timer4 intervals during runtime? I haven't seen any method for that in the IntervalTimer code,

You can call IntervalTimer's begin() function, even while the timer is already running, to reconfigure for a new interval.

and it would be vital if I'm understanding the OnStep code correctly. The overflow ISRs for Timer3 and Timer4 change the respective OCR values.

Yeah, that could be an issue. I saw that while hacking on the code. My change causes the Timer1 interrupt to immediately change the Timer3 & Timer4 settings. The original code appears to set things up in such a way that Timer3 & Timer4 continue on their original setting and then change upon the next interrupt. If that exact behavior is really needed, then the code will need to be edited a bit, so the timer change happens not when Timer3SetRate() and Timer4SetRate() are actually called. Some extra logic might be needed to deal with the first time.

This porting effort should be considered only an initial effort. It's absolutely untested. But it at least gets the code to compile and it might be at least close or a good starting point. It's also the best I could reasonably do without the hardware and only about 20 minutes fiddling with the code.
 
I took a closer look and my conclusion is that Timers 3 and 4 are first configured for a very late overflow: in OnStep.ino::setup(), lines 553 and 557, the OCRs are set to 32767. The compare match ISRs update the OCRs to a value that never changes later, but they do that in every call to the ISR. So it seems to be OK to set up Timer1 first, and Timers 3 and 4 afterwards.

Speculation: The way the timers are handled in the OnStep might be a result of some timing problems during startup that were solved by simply having Timers 3 and 4 begin overflowing later.
 
Last edited:
Paul I forked your repo and created a pull request. When it comes to git, I have absolutely no idea what I'm doing but I think all went well (this time).
 
The guy that made is said the serial can probably just go back to just the standard serial routines with the Teensy, it ought to be fast enough, as for the timers he says this:

For the current version to be ported:
1. Three 16bit Timers with pre-scalers (1X to 256X) would be nice. One is fixed rate for clocking... The other two have rates that need to be adjusted frequently and over a wide range to provide for acceleration and deceleration of each motor independently.
2. Possibly replace the digitalWrite with the equivalent of the Atmel register writes again for speed.
3. Remove the custom Serial I/O code and go back to using the library. Was there for getting that last little bit of speed from the '2560 doesn't matter here.
 
1) The IntervalTimer library provides three timers afaik, so that should be fine. Regarding readjusting them, Paul stated that this can be done using the begin() method of each timer object (see his branch he linked to)
2) digitalWriteFast should do just that
3) seems to be done already in Paul's branch.
 
Geesh, I didnt even see all of Paul's responses. Thanks Paul.

I need to put together a test bed for this and need to order up a couple line 26ls31 line drivers for the input. Heck, I can probably get away with single ended for now.

-Jerry
 
If speed is a big concern, using digitalWriteFast() with pin numbers as constants would be slightly faster than going through the AVR register names and avr_emulation.h. Maybe SET() and CLR() could be redefined, to limit the number of lines of code touched.

But Teensy 3.1 is so much faster, so I'm guessing this won't be an issue.

The NVIC priorities might need to be raised (lower numbers) for the 3 timers. In fact, I'm considering adding a priority() function to the IntervalTimer object in a future version of Teensyduino....
 
Paul,

Hello! I'm the author of OnStep... I wanted to make sure that the timer fired before switching to the next rate, not cut it off mid way and started again. Lots of this stuff is/was trial and error for me, learning as I go, and not too many examples of timers used this way. The timing of the pulse train to the stepper motors (or servos in this case) is critical and that change really cleaned up the jitter (edit: while accelerating/decelerating.) Timer3 and Timer4 max out at rates of 12 to 16uS on a '2560 (depending on how much jitter you're willing to tolerate.) It was about 24uS max until I replaced the Serial I/O code. Hopefully this can be brought down to the 4uS range, or lower. That'll allow the very high speed movements Jerry's mount is capable of (10 or 20 degrees/second) while still providing a wonderfully tiny minimum movement (1/8 to 1/4 arc-second.) I'm personally looking forward to finally implementing some advanced features I have in mind that require double precision fp. I'll check in from time to time to answer questions, and keep and eye on the progress - and thanks for your efforts.
 
Last edited:
I loaded up christoph's firmware and it does load up. I downloaded the ascom driver for it and maxim dl talked to it. It so happens that the led on the teensy is on one of the step pins so that makes some basic diagnostic possible without connecting to anything.

So, maxim talks to it. When I connect the step led starts blinking but when I try to slew it maxim locks up. Also the pulse length is inconsistent. Then I tried stellarium with the LX200 mode, it says it is connected but thats about it. No output when I try to slew to a position.
 
Please tell us how it's wired up now and if you changed any of the SET/CLR code (the macros in OnStep.ino or the code that uses them in Timer.ino).
 
"I loaded up christoph's firmware and it does load up. I downloaded the ascom driver for it and maxim dl talked to it. It so happens that the led on the teensy is on one of the step pins so that makes some basic diagnostic possible without connecting to anything."
Assuming the Teensy has the Led on the RA step pin as the Arduino does... it should be ON with some possibly visible slight dimming at high speed. The Pulse width on that pin is something on the order of a few uS on a '2560. On a Teensy it is, likely under 1uS? In fact, it's probably now under the spec. for the stepper drivers (1uS to 1.9uS for the ones most commonly used). What's the signaling spec. for your servo's?

"So, maxim talks to it. When I connect the step led starts blinking but when I try to slew it maxim locks up."
Have to say that I have no idea why it would lock up. Do Teensy's reset (using DTR) like Arduino's do, possibly why the led is blinking. I'll check on Maxim, not something I use frequently but everything else thrown at that driver works.

"Also the pulse length is inconsistent."
Not sure what you mean, but... Pulse jitter should be well under the <0.01 second level (0.15 arc-second) while tracking at the resolution you're looking at. This doesn't effect the tracking rate since it doesn't accumulate. Seeing normally limits what you can see/image to the 2-3 arc-second level (rarely reaching 1), the 10" 'scopes optics can resolve down to the 0.45 arc-second level. Seems to me to be a non-issue, but even so a redesign for this and other reasons, is planned.

I did glance at the Teensy OnStep code, and it seems like the Timer design has been changed with the rate programming being moved out of the ISR's. If this is what's happening, and the Timers work similar to the Mega2560, it won't work as it should.


"Then I tried stellarium with the LX200 mode, it says it is connected but thats about it. No output when I try to slew to a position."
Stellarium works in LX200 mode with the current design.

Once I get the Teensy, I'll check this out. It's an excellent start, again thanks to all.
 
The arduino 2560 has the LED on pin 13, which is connected to the controller's PB7.

PORTB is emulated in teensyduino to give it a look and feel similar to that of AVRs. The teensy 3.1 also has the LED on pin 13, but the PORTB emulation maps PB5 to pin 13.

Everything that configures/reads/sets/clears I/O lines manually will need to be rewritten. Until now I spotted candidates for that in OnStep.ino (setup) and Timer.ino (motor output) only.
 
Timer3 and Timer4 max out at rates of 12 to 16uS on a '2560 (depending on how much jitter you're willing to tolerate.) It was about 24uS max until I replaced the Serial I/O code. Hopefully this can be brought down to the 4uS range, or lower.

For best performance using the existing software design, you probably want to increase the timer interrupt priority.

On 8 bit AVR, there's no interrupt priority levels. Interrupts are simply enabled or disabled. By default, interrupts are disabled when your ISR runs. You can turn the AVR's global interrupt enable back on, which is quite risky if your interrupt might occur again before you're done (perhaps because other interrupts took too much time). Usually on AVR, all other interrupts limit how rapidly your ISR could get serviced, which is why you had to get rid of the serial interrupts.

32 bit ARM has a much more advanced interrupt controller, which supports proper prioritized interrupt nesting, all implemented efficiently in hardware. You just assign priority levels to individual interrupts. While your ISR is running, the hardware automatically disables all equal and lower priority interrupts. Your ISR can't interrupt itself, so there's no risk of runaway recursion. Higher priority interrupts are able to interrupt the ISRs of lower priority ones. It's perfect for projects like this, where you want certain interrupts to always get serviced as fast as possible. You don't need to avoid other interrupts, just make sure they're a lower priority. :)

To set those 3 timers to the highest priority, the code would look something like this:

Code:
  NVIC_SET_PRIORITY(IRQ_PIT_CH0, 0);
  NVIC_SET_PRIORITY(IRQ_PIT_CH1, 0);
  NVIC_SET_PRIORITY(IRQ_PIT_CH2, 0);

With ARM interrupt priorities, lower numbers are higher priority, so zero is the best you can get. If the serial or USB interrupt is running, this will allow your timer to interrupt it.

I believe the systick interrupt (used for the Arduino millis and micros timing) is running at priority zero. You might try reducing it (a larger number for lower priority). This cryptic code ought to set the systick interrupt to priority 32.

Code:
  SCB_SHPR3 = (32 << 24) | (SCB_SHPR3 & 0x00FFFFFF);
 
Last edited:
Status
Not open for further replies.
Back
Top