timer1 registers-setting up 3 different frequency interrupts

I have a project that requires a min of 2 stepper motors to be running at different speeds. Currently I am using the timerOne class to generate a higher pulse frequency, then using software to calculate the number of those base periods required for the appropriately frequency to each motor. Couple days ago I set out to write a class that would allow 3 different frequencies to be generated as individual interrupts. Here is the general understanding of how this should work:

  1. set up TCCR1A & TCCRA to CTC mode & with appropriate prescalers
  2. set the OCCR1A, OCCR1B, OCCR1C register to the appropriate compare value
  3. write interrupt function for ISR(TIMER1_COMPA_vect){}, ISR(TIMER1_COMPB_vect){}, ISR(TIMER1_COMPC_vect){}.
  4. Set bits 3, 2, 1 in TIMSK1 to turn on interrupts for those ISR's

So I have it at the point where compValA functions 100% right when run on its own, however, B & C will not run without OCCR1A set & then will only run at that frequency. I am starting to wonder if it is even possible to do what I want with this, could anyone confirm that this is possible?

Also, I noticed that the interrupt vectors defined in ioxx.h are all shifted by one as compared the the atmega2560 data sheet...shifting them to match breaks pretty much everything in arduino....so anyone know why these don't match the data sheet?

One thing that I dont yet know about is TCCR1C specifically the forced output compare bits. What do these do, what would one use it for, should I be using it?

thanks for your attention, happy coding!

quick update:

after consulting the logic diagram for timer1, it seems that OCR1A is different then B&C in the sense that OCR1A sets the value that timer1 runs to (and then toggles clears/reset dependant on how the COM bits are set). So that explains why they all seem linked....but then my question is, whats the point in having ORC1B & OCR1C? I'm sure there is one, but with my limited knowledge I cannot see it. I'd even bet the answer to that question will help me do what I need to.

You could use Mode 12 (TOP stored in ICR1) instead of Mode 4 (TOP stored in OCR1A).

What I would do is use Mode 0 (TOP is 0xFFFF) and treat OCR1A the same as OCR1B and OCR1C: Figure out how many timer tick before the next interrupt, add that to the current timer value, and set that as the new interrupt time. That's going to be much easier to get right when TOP isn't a moving target!

It will be interesting to see if you can get this to work.

The data sheet says this about "Normal Mode"

The Output Compare units can be used to generate interrupts at some given time. Using the Output Compare to
generate waveforms in Normal mode is not recommended, since this will occupy too much of the CPU time.

You will need to use interrupts instead of the hardware outputs because if you want different frequencies for the three outputs you will need to both change the value of the next OCR1n and toggle a pin at each interrupt.

What frequencies are you trying to achieve?

Your current approach of counting a number of base periods to control the individual outputs does not seem bad. What problems are you finding with it?

Setting up three different software timers based on micros() or millis() would also be an approach.

that line you quoted is the carrot at the end of the stick I've been chasing for way too long.

cattledog:
What frequencies are you trying to achieve?

those are not fixed...it is generating a step signal for 2 stepper, one is fixed (but adjustable), the second runs in coordination with the fixed speed of the first & readings off various sensors. the period range for these falls between 80-3000 uSecs.

cattledog:
Your current approach of counting a number of base periods to control the individual outputs does not seem bad. What problems are you finding with it?

I v'e worked out how to get the ISR to be triggered, if I run OCR1A exclusively, I have great precision control over the period. but if I add in either OCR1B or OCR1C, the interrupts all come in sequence with OCR1A. all seem to be link together....ive just recently learned how those comparators are linked, i know i had it wrong but not sure where to make changes....honestly its all kinda coming to me as I type this reply...:slight_smile:

cattledog:
Setting up three different software timers based on micros() or millis() would also be an approach.

I have this hardware system working with something like that right now...I use the TimerOne library(which i now know uses the overflow reg, making it pretty much a micros/millis() running off timer1) to generate an ISR every x uSecs & then softwaring, scaling to call the steps. This run quite well on its own, but there are just sooo many steps happening that its leaving much cpu time for polling of sensors and such...I started writing this class in hopes of shifting that same process into hardware.

cattledog:
you will need to both change the value of the next OCR1n and toggle a pin at each interrupt.

until I read that, setting OCR1n at any point other that the start, never even crossed my mind. But that's totally how I need to do it! I think I had been thinking of OCR1n as timer itself, rather than just a register that holds a value.

Well at least I have something to try now, thank you for your assistance.

johnwasser:
You could use Mode 12 (TOP stored in ICR1) instead of Mode 4 (TOP stored in OCR1A).

What I would do is use Mode 0 (TOP is 0xFFFF) and treat OCR1A the same as OCR1B and OCR1C: Figure out how many timer tick before the next interrupt, add that to the current timer value, and set that as the new interrupt time. That's going to be much easier to get right when TOP isn't a moving target!

isnt OCR1n disconnected in normal? or is that why your suggesting this mode? Forgive me, I am just a hobby level programer, this is my first time using timers at all....the various systems have yet to coalesce in my mind...

isnt OCR1n disconnected in normal?

No, you can enable and use the output compare interrupts in Normal and CTC Modes. When using the hardware outputs the options are different than those available in the PWM modes.

I use the TimerOne library(which i now know uses the overflow reg, making it pretty much a micros/millis() running off timer1) to generate an ISR every x uSecs & then softwaring, scaling to call the steps. This run quite well on its own, but there are just sooo many steps happening that its leaving much cpu time for polling of sensors and such...I started writing this class in hopes of shifting that same process into hardware.

I'm not sure you will have less happening with multiple output compare interrupts.

The behaviour of timer output pin controlling the fastest stepper can be generated in hardware, but once you start getting into interrupts and interrupt service routines you start increasing the demands on the processor.

I'm not clear if you are trying to control two or three steppers, and how the slower ones are scaled from the first, and why there are "sooo many steps"? Can you provide a simple sketch which demonstrates what you are trying to do?

2 steppers for sure, there are 3 OCR’s so I figured might as well write the class for 3. They are microstepping with a base of 200 steps/rot…so that 3200 steps for 1 rot of the stepper shaft, then one has a 19:1 gear box…on top of that I have interrupts hitting every 200ms, optical & magnetic tachometers, temp probs ect…every pin of my mega2560 is in use…just trying to optimize.

I'm not sure you will have less happening with multiple output compare interrupts.

The behaviour of timer output pin controlling the fastest stepper can be generated in hardware, but once you start getting into interrupts and interrupt service routines you start increasing the demands on the processor.

I am not sure either, that’s what I am to test. With the timer1 version, the ISR was quite long and had to check if it was time to fire each motor…point is, it was longer than what I would like to see in an ISR. Per the help of the 2 prev posters, I was able to get this working last night…the isr’s are pretty short, for example:

fastDigitalWrite(pin, HIGH);
fastDigitalWrite(pin, HIGH);
r_tcnt1=TCNT1; //grab the time
r_ocr1b=r_tcnt1+compValB; //calc next time
OCR1B=r_ocr1b; set next time
that all runs pretty quick…

I am gonna clean up my code & post it later today for critique, please feel to look it over & tell me what you think.