| Author |
Message |
|
|
Posted: Mar 11, 2010 - 07:20 AM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
Hi All -
Trying to figure out how to run 2 motors at the same speed using a simple control loop.
I have spent days trying to figure this one out.
In my endless quest to find out how to do this and what is causing it not to work at the moment I have attached 2 LEDs which each trigger when INT1 and INT2 interrupts are reached through
ISR(INT1_vect)
and
ISR(INT0_vect)
However I am getting quite different pulse rates from each encoder -> INTx. I have set up leds to toggle once every 2000 counts just to test if I was getting equal (or close) pulse rates and they are way out..
I have checked the pulses from each encoder on a scope and they are identical.
Can anybody see what I am doing wrong?
Code:
#define F_CPU 1000000UL // 1Mhz I think
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile uint16_t countz;
volatile uint16_t bcountz;
volatile uint16_t countbz;
volatile uint32_t timeza;
volatile uint32_t timezb;
volatile uint16_t phase;
volatile uint16_t overflows;
#define targetticks 50
int main (void)
{
//////////////////////////////////////////////
// SET UP ALL LED PORTS
DDRD |= (1 << DDD1) | (1 << DDD0); // Set PD0 and PD1 as outputs
DDRC |= (1 << DDC5) | (1 << DDC4);;
// SET
DDRB |= ((1 << DDB0) | (1 << DDB1) | (1 << DDB2));
TCCR1A = ((1 << WGM10) | (1 << COM1A1) | (1 << COM1B1)); // 8 bit fast PWM, see WGM12 below
TCCR1B = ((1 << CS11) | (1 << WGM12)); // /8 div by time
// SET UP INT1 STUFF
MCUCR |= ((1 << ISC11) | (1 << ISC01)); // enable interrupts
GICR |= ((1 << INT1) | (1 << INT0));
//TIFR = (1 << TOV1);
TIMSK |= (1 << TOIE1); // Set timer counter interrupts avaiable
sei(); // Enable global interrupts
uint8_t cnt;
//PORTC ^= (1 << 5); // Toggle PC5
/////////////////////////////////////////////
OCR1B=0x45;
OCR1A=0x45;
//OCR1A=40;
//PORTB ^= (1 << 0); // Toggle the LED
for (;;){
}
}
ISR(INT0_vect){
bcountz++;
if(bcountz==2000){
PORTB ^= (1 << 0); // Toggle the LED
bcountz=0;
}
}
ISR(INT1_vect){
////
countz++;
if(countz==2000){
PORTD ^= (1 << 1); // Toggle the LED
countz=0;
}
}
ISR(TIMER1_OVF_vect)
{
uint16_t ticks;
uint16_t ticksb;
ticks = countz;
ticksb = bcountz;
countz = 0;
bcountz = 0;
if(ticks == targetticks){
// do nothing
} else {
if(ticks > targetticks){
OCR1B--;
//PORTB ^= (1 << 0); // Toggle the LED
} else {
if(OCR1B + 1 <= 0xFF){
OCR1B++;
}
//PORTD ^= (1 << 0); // Toggle the LED
}
}
if(ticksb == targetticks){
// do nothing
} else {
if(ticksb > targetticks){
OCR1A--;
//PORTB ^= (1 << 0); // Toggle the LED
} else {
if(OCR1A + 1 <= 0xFF){
OCR1A++;
}
//PORTD ^= (1 << 0); // Toggle the LED
}
}
//timeza++;
//timezb++;
//overflows++; //increment overflows counter when an overflow happens
}
[/code] |
Last edited by ethought on Mar 11, 2010 - 07:50 AM; edited 1 time in total
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 07:45 AM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
updated code adding unique counts for ISRs
Code:
#define F_CPU 1000000UL // 1Mhz I think
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile uint16_t countz;
volatile uint16_t bcountz;
volatile uint16_t countbz;
volatile uint16_t int0count;
volatile uint16_t int1count;
volatile uint32_t timezb;
volatile uint16_t phase;
volatile uint16_t overflows;
#define targetticks 50
int main (void)
{
//////////////////////////////////////////////
// SET UP ALL LED PORTS
DDRD |= (1 << DDD1) | (1 << DDD0); // Set PD0 and PD1 as outputs
DDRC |= (1 << DDC5) | (1 << DDC4);;
// SET
DDRB |= ((1 << DDB0) | (1 << DDB1) | (1 << DDB2));
TCCR1A = ((1 << WGM10) | (1 << COM1A1) | (1 << COM1B1)); // 8 bit fast PWM, see WGM12 below
TCCR1B = ((1 << CS11) | (1 << WGM12)); // /8 div by time
// SET UP INT1 STUFF
MCUCR |= ((1 << ISC11) | (1 << ISC01)); // enable interrupts
GICR |= ((1 << INT1) | (1 << INT0));
//TIFR = (1 << TOV1);
TIMSK |= (1 << TOIE1); // Set timer counter interrupts avaiable
sei(); // Enable global interrupts
uint8_t cnt;
//PORTC ^= (1 << 5); // Toggle PC5
/////////////////////////////////////////////
OCR1B=0xFE;
OCR1A=0xFE;
//OCR1A=40;
//PORTB ^= (1 << 0); // Toggle the LED
for (;;){
}
}
ISR(INT0_vect){
int0count++;
bcountz++;
if(bcountz==2000){
PORTB ^= (1 << 0); // Toggle the LED
bcountz=0;
}
}
ISR(INT1_vect){
////
int1count++;
countz++;
if(countz==2000){
PORTD ^= (1 << 1); // Toggle the LED
countz=0;
}
}
ISR(TIMER1_OVF_vect)
{
uint16_t ticks;
uint16_t ticksb;
ticks = int0count;
ticksb = int1count;
int0count = 0;
int1count = 0;
if(ticks == targetticks){
// do nothing
} else {
if(ticks > targetticks){
OCR1B--;
//PORTB ^= (1 << 0); // Toggle the LED
} else {
if(OCR1B + 1 <= 0xFF){
OCR1B++;
}
//PORTD ^= (1 << 0); // Toggle the LED
}
}
if(ticksb == targetticks){
// do nothing
} else {
if(ticksb > targetticks){
OCR1A--;
//PORTB ^= (1 << 0); // Toggle the LED
} else {
if(OCR1A + 1 <= 0xFF){
OCR1A++;
}
//PORTD ^= (1 << 0); // Toggle the LED
}
}
//timeza++;
//timezb++;
//overflows++; //increment overflows counter when an overflow happens
}
|
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 09:14 AM |
|


Joined: May 04, 2007
Posts: 766
Location: Geelong Australia, Home of the "Cats"
|
|
You don't say how you are deriving tacho pulses from the motors. We will assume that you have done this correctly!
May I suggest that you connect the each motor in turn, to both Int0 & Int 1 simultaneous. That should proved that the code works. Measure the period of each motor & output to serial a serial port or use ICD.
Have you considered that the motors might be running at different rotational velocities?
I don't see any control loop implemented & I will be most interested how you tackle that. Synchronized PID loops? What you are trying to do is a non-trivial task! Make sure you post that code to projects when you finish that wont you! |
_________________ Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
(If you haven't already done so, edit your PostNuke profile and let let us know where you are, what you do & what your interests are.)
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 02:36 PM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
Hi -
I am trying to measure the pulse of 2 DC motors running with PWM.
Each motor has an HEDS-5505 encoder attached. I am ONLY using channel B from each encoder into INT1 and INT0 respectively.
The encoder is an incremental encoder, metal wheel, digital out put encoder.
I do not have a serial output (ordered an max232 but dont have it yet) so cannot dump values to see what is going on.
The encoder has 500 ticks per revolution.
I have not been able to find any specs for Globe Motor 415A832 except one old badly copied single page datasheet. I thought at first it said gearing was 187.68:1 but I now think it is 157.68:1
So I figure 1 full rotation of geared shaft =
157.68 x 500 = 78840 encoder ticks
I have tried putting a check into the ISR(INT1_vect){
volatile uint32_t intclicks;
ISR(INT1_vect){
intclicks++
if(intclicks==78840){
//TOGGLE LED
intclicks = 0;
}
}
The LED toggled just a few degrees short of a full rotation, so it is a ballpark figure, but seems to be counting more or less as expected.
At its fastest the geared shaft does a full rotation in about 4-5 seconds (say 4 for arguments sake)
78840 / 4 = 19710 encoder ticks per second.
Here is my logic:
if the Atmega8 clock is running at 1MHz with no prescaler
1000000 clock ticks per second.
so over 1 second at fastest motor speed I should be getting
1000000 / 19710 = 50.73 clock ticks per INT1 interrupt
At slowest speed say 30 seconds per full revolution.
(1000000 x 30) / 78840 = 380.51 clock ticks per INT1 interrupt
I want to be able to set a desired tick rate (clock ticks per INT1 interrupt) and have the Atmega8 adjust the PWM accordingly.
i.e:
desiredtickrate = 100;
if actual tick rate > 100;
OCR1B--
else if actual tick rate < 100;
OCR1B++
This is where I am having problems.
I also have tried this code for a single motor loop. But it always just spins motor constantly at start up speed 0xFF
Code:
#define F_CPU 1000000UL // 1Mhz I think
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile uint16_t overflows;
#define targetticks 100
int main (void)
{
//////////////////////////////////////////////
// SET UP ALL LED PORTS
DDRD |= (1 << DDD1) | (1 << DDD0); // Set PD0 and PD1 as outputs
DDRC |= (1 << DDC5) | (1 << DDC4);;
// SET
DDRB |= ((1 << DDB0) | (1 << DDB1) | (1 << DDB2));
TCCR1A = ((1 << WGM10) | (1 << COM1A1) | (1 << COM1B1)); // 8 bit fast PWM, see WGM12 below
TCCR1B = ((1 << CS10) | (1 << WGM12)); // /8 div by time
// SET UP INT1 STUFF
MCUCR |= ((1 << ISC11) | (1 << ISC01)); // enable interrupts
GICR |= ((1 << INT1) | (1 << INT0));
//TIFR = (1 << TOV1);
TIMSK |= (1 << TOIE1); // Set timer counter interrupts avaiable
sei(); // Enable global interrupts
/////////////////////////////////////////////
OCR1B=0xFF;
OCR1A=0xFF;
//PORTB ^= (1 << 0); // Toggle the LED
for (;;){
}
}
ISR(INT1_vect)
{
if(TCCR1B == 0x00) //If timer is stopped
{
overflows = 0; //Reset overflows counter
TCNT1 = 0x0000; //Reset timer
TCCR1B = (1 << CS10); //Start timer with /1 prescaler
} else { // If the timer isn't stopped...
// TCCR1B = ((0 << CS10) | (0 << CS11)); //Stop timer.
TCCR1B = (0 << CS10); //Stop timer.
if (overflows > 0) //If overfow happened...
{
OCR1B = 0xFF; //Max out PWM.
} else { //If it didn't...
if(TCNT1 < targetticks)
{
if(OCR1B + 1 <= 0xFF)
{
OCR1B++; //turn up the duty cycle
}
}
if(TCNT1 > targetticks)
{
OCR1B--; //turn down the duty cycle
}
}
}
}
ISR(TIMER1_OVF_vect)
{
overflows++; //increment overflows counter when an overflow happens
//PORTD ^= (1 << 1); // Toggle the LED
}
|
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 03:18 PM |
|


Joined: Feb 19, 2001
Posts: 18830
Location: Wisconsin USA
|
|
Is this uni-directional or bi-directional work?
(I'm guessing a single direction as you are only looking at one channel.)
You sure are crippling yourself at 1MHz. 80k edges/second is doable for full-quadrature at 4MHz.
If all you are concerned about is speed reporting, then consider using timer-as-counter into the T1 pin. Virtually no AVR overhead. |
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 03:28 PM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
It is bi-directional. The direction is known as it is set when the LM18201 H-bridge pwm signal is sent from the Atmega8. I just need to know speed irrespective of direction so I can adjust PWM.
It would only ever be max, as far as I can see, < 20K edges per second, is 1MHz not suitable?
The reason I cant use, as I understand, T1 is because I need 2 motors running and 2 return (single channel) encoder pulses.
So using OC1A and OC1B for PWM output
and INT0 and INT1 for encoder in pulses. |
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 03:38 PM |
|


Joined: Feb 19, 2001
Posts: 18830
Location: Wisconsin USA
|
|
|
Quote:
< 20K edges per second, is 1MHz not suitable?
Not with the kind of ISR coding above. I can just reach 80k edges/second at 3.6864MHz with a very skinny ISR setup, lots of use of register variables, and 16-bit results. (16 bits isn't going to go very far with you.) |
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 05:24 PM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
| I changed it to 4MHz internal - still cant get it working... |
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 06:20 PM |
|


Joined: Feb 19, 2001
Posts: 18830
Location: Wisconsin USA
|
|
|
Quote:
still cant get it working...
Can't get >>what<< working? What code are you using? What are you driving it with? What are the results? What happens if you use a signal generator or other controlled signal? |
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 06:59 PM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
Using the code below (same as posted above) the motors are not changing PWM output according to incoming encoder frequency.
I want to get both motors running at the same speed using 1 x Atmega8 and 2 DC motors running from pins OC1A and OC1B. The PWM generation and varying works well between values 0 - 255 its just the motors run at slightly different speeds so my robot will not be able to go straight.
Each motor has an encoder attached that outputs 500 ticks per full rotation of ungeared motor. Shaft is geared at 157.68:1
All I am trying to do is measure incoming encoder ticks against some sort of time interval and a set target value. So I can vary PWM signal via OCR1A or OCR1B to the Hbridges as needed. So that the motors will run at the same encoder pulse / RPM. Simple, I thought.
Using the code below, to try and drive just one motor to desired tick rate to try and figure out the problem before attempting to get 2 working. The motor just starts spinning at full rate 0xFF and does not vary. So with a slow target tick rate the motor should gradually slow down to where the actual tick rate matched the desired tick rate.
I am more or less a complete newbie so just want to know if I am vaguely on the right track or not.
Code:
#define F_CPU 1000000UL // 1Mhz I think
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile uint16_t overflows;
#define targetticks 100
int main (void)
{
//////////////////////////////////////////////
// SET UP ALL LED PORTS
DDRD |= (1 << DDD1) | (1 << DDD0); // Set PD0 and PD1 as outputs
DDRC |= (1 << DDC5) | (1 << DDC4);;
// SET
DDRB |= ((1 << DDB0) | (1 << DDB1) | (1 << DDB2));
TCCR1A = ((1 << WGM10) | (1 << COM1A1) | (1 << COM1B1)); // 8 bit fast PWM, see WGM12 below
TCCR1B = ((1 << CS10) | (1 << WGM12)); // /8 div by time
// SET UP INT1 STUFF
MCUCR |= ((1 << ISC11) | (1 << ISC01)); // enable interrupts
GICR |= ((1 << INT1) | (1 << INT0));
//TIFR = (1 << TOV1);
TIMSK |= (1 << TOIE1); // Set timer counter interrupts avaiable
sei(); // Enable global interrupts
/////////////////////////////////////////////
OCR1B=0xFF;
//OCR1A=0xFF;
//PORTB ^= (1 << 0); // Toggle the LED
for (;;){
}
}
ISR(INT1_vect)
{
if(TCCR1B == 0x00) //If timer is stopped
{
overflows = 0; //Reset overflows counter
TCNT1 = 0x0000; //Reset timer
TCCR1B = (1 << CS10); //Start timer with /1 prescaler
} else { // If the timer isn't stopped...
// TCCR1B = ((0 << CS10) | (0 << CS11)); //Stop timer.
TCCR1B = (0 << CS10); //Stop timer.
if (overflows > 0) //If overfow happened...
{
OCR1B = 0xFF; //Max out PWM.
} else { //If it didn't...
if(TCNT1 < targetticks)
{
if(OCR1B + 1 <= 0xFF)
{
OCR1B++; //turn up the duty cycle
}
}
if(TCNT1 > targetticks)
{
OCR1B--; //turn down the duty cycle
}
}
}
}
ISR(TIMER1_OVF_vect)
{
overflows++; //increment overflows counter when an overflow happens
//PORTD ^= (1 << 1); // Toggle the LED
}
|
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 07:13 PM |
|


Joined: Feb 19, 2001
Posts: 18830
Location: Wisconsin USA
|
|
|
Quote:
All I am trying to do is measure incoming encoder ticks against some sort of time interval
Do that part first. Display/log your "ticks". Is it correct for the input rate?
If so, then display/log how they are translated to output commands. |
|
|
| |
|
|
|
|
|
Posted: Mar 12, 2010 - 03:26 AM |
|


Joined: May 04, 2007
Posts: 766
Location: Geelong Australia, Home of the "Cats"
|
|
And as I mentioned before, connect ONE motor to BOTH interupts to make sure both interupt routines are consistant. Why not clock your AVR at 8 or 16 Mhz.
No need to put code listings in all your posts. I don't think that many will look at it until you have
a specific fault and you have some means of debugging.
Also, I asked about the control loop for each motor.
You have not answered that question, yet it is fairly critical. If you can't answer questions from those that are attempting to help you, then don't bother asking for help either. We do not have crystal balls
and are not endowed with magic powers. |
_________________ Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
(If you haven't already done so, edit your PostNuke profile and let let us know where you are, what you do & what your interests are.)
|
| |
|
|
|
|
|
Posted: Mar 12, 2010 - 10:48 AM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
LDEVRIES - thanks for your input.
Quote:
Also, I asked about the control loop for each motor.
You have not answered that question, yet it is fairly critical. If you can't answer questions from those that are attempting to help you, then don't bother asking for help either.
I thought I answered that and if I didnt it is because I either dont know how or thought I had already made that quite clear.
The control loop for each motor is simple, I thought.
start motor Atmega8 PWM -> LM18201 H-bridge
count encoder ticks into Atmega8 -> INTx
Calculate error and adjust PWM in software.
send correct PWM signal Atmega8 -> LM18201
loop
I dont know how else to explain the control loop.
theusch - thanks for the suggestion. I am working on it now. Since I changed the Atmega8 frequency to 8MHz I am now getting reliable readings for both encoders. And as it turns out the motor is geared at 187.68:1 / 93840 ticks per full revolution (so it turns out trying to read the encoder at 1Mhz was just not working properly which was really throwing me, so thanks part of the problem is now solved). I wrote a small program to run 1 and 10 full revolutions and it always stops at exactly where it started after 93840 or 938400 ticks regardless of speed. So I am getting the correct tick counts now from the encoder. Just need now to figure out how to time them and calculate error and adjust outgoing PWM signal to bring encoder ticks to specified level.
I am not used to programming without being able to dump variables etc to screen, using LEDs to diagnose problems is fun but not very helpful most of the time. I got my MAX232 board today so will hook it up tonight and finally get some data to see what is going on in actual numbers not LED flashes..!
Getting closer... |
|
|
| |
|
|
|
|
|
Posted: Mar 12, 2010 - 11:00 AM |
|


Joined: Jul 18, 2005
Posts: 33138
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
I am not used to programming without being able to dump variables
If you used mega88 rather than mega8 and a Dragon you could do this (via debugWire) |
_________________
|
| |
|
|
|
|
|
Posted: Mar 12, 2010 - 09:35 PM |
|


Joined: May 04, 2007
Posts: 766
Location: Geelong Australia, Home of the "Cats"
|
|
|
Quote:
Trying to figure out how to run 2 motors at the same speed using a simple control loop.
&
Quote:
The control loop for each motor is simple, I thought.
start motor Atmega8 PWM -> LM18201 H-bridge
count encoder ticks into Atmega8 -> INTx
Calculate error and adjust PWM in software.
send correct PWM signal Atmega8 -> LM18201
loop
The system will have quite a bit of lag due to inertia, so that it will not behave the way you expect. Electronic/Electrical Engineers learn this in a subject called Control Systems in their final year of a four year Engineering course.
If you want to short cut your development time.
I suggest you Google PID (Proportional Integral Derivative) control system and if that does not discourage you, implement that and then solve any twinning problems that you might have.
Your problems will increase when you start to load the motors!
Quote:
I am not used to programming without being able to dump variables
I just can't image that!
Your task is a non-trivial one! Good luck! Be sure to put your project in the Project part of the forum when it is finished. |
_________________ Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
(If you haven't already done so, edit your PostNuke profile and let let us know where you are, what you do & what your interests are.)
|
| |
|
|
|
|
|
Posted: Mar 14, 2010 - 02:39 PM |
|

Joined: Aug 30, 2009
Posts: 13
Location: Adelaide, South Australia
|
|
Finally figured out how to get my serial connection going and that has helped considerably. Figured out how to run the control loop but only on one motor at a time.
The main, and pretty dumb, reason the code above did not work was because I was trying to use the 16bit timer as a timer and the PWM source. Once I figured out how to use all the timers from reading Deans newbie AVR timers tutorial and the manual I tried the following:
1. Spin Motor: PWM 16bit (TIMER1) -> Hbridge -> Motor
2. Count clock ticks on 8bit TIMER0 per encoder pulse coming into INT1 interrupt.
3. Check Current clock tick per the encoder pulse against target preset value and adjust PWM up or down accordingly.
It worked, but far from usably. On average the resulting PWM oscillated about 10-20% and the motor sounded unhealthy. I could not manage to get any decent resolution to control the motor using the 16 bit timer driving the PWM and using the 8bit timer2 for clock tick counts, when getting 20000 - 300 ticks per second from the encoder.
So after more reading found the other timer, TIMER2 and saw it can generate PWM signals.
So changed the PWM output from OSC1 to OC2 and could now use the 16bit timer for timing and the 8bit timer for PWM. Only drawback is that you can only use 1 motor and encoder per Atmega8 in this way.
Instead of using the INT1 interrupt and counting clock ticks I tried using the example code from the timers turorial to set a CTC timer interrupt on TIMER1 and count the encoder ticks coming in on INT1. This proved to be much more accurate. Here are a snippet of the the results:
2015 = COUNTER - OCR2 = 204
Slow Down
1983 = COUNTER - OCR2 = 203
Speed Up
2017 = COUNTER - OCR2 = 204
Slow Down
1989 = COUNTER - OCR2 = 203
Speed Up
2015 = COUNTER - OCR2 = 204
Slow Down
1988 = COUNTER - OCR2 = 203
Speed Up
2021 = COUNTER - OCR2 = 204
Slow Down
1986 = COUNTER - OCR2 = 203
Speed Up
2015 = COUNTER - OCR2 = 204
Slow Down
1987 = COUNTER - OCR2 = 203
Speed Up
2010 = COUNTER - OCR2 = 204
Slow Down
1980 = COUNTER - OCR2 = 203
Speed Up
2016 = COUNTER - OCR2 = 204
Slow Down
So with a target of 2000 the encoder ticks being received ranged from about 1980 to 2020. Makes me think if you had a 16bit timer and a 16bit pwm with better input resolution you could get it pretty close to within a few ticks per revolution. Hmm, do any Atmegas have 2 or even 3 16bit timers I wonder?
One thing I have noticed with using PWM on the OC2 pin with Timer 2 is it seems to sort of surge twice at startup regardless of input. So it is not as "smooth" as the 16bit PWM but definately usable.
I have yet to try it with 2 motors side by side, that will be the next test.
The working stripped down code is shown below.
Code:
#define F_CPU 8000000UL // 8Mhz
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint16_t counter;
// RANGE 3000 - 100
#define targetticks 2000
int main (void)
{
DDRB |= (1 << 0); // Set LED as output
DDRB |= ((1 << DDB1) | (1 << DDB2) | (1 << DDB3));
TCCR2 |= ((1 << WGM20) | (1 << WGM21) | (1 << COM21) | (1 << CS20)); // Setup timer 2 for PWM
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TIMSK |= (1 << OCIE1A); // Enable CTC interrupt
MCUCR |= ((1 << ISC11) | (1 << ISC01)); // enable interrupts
GICR |= ((1 << INT1) | (1 << INT0));
sei(); // Enable global interrupts
OCR1A = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64
TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64
OCR2 = 255; // Spin Motor to Start
for (;;){
// Endless Loop
}
}
ISR(INT1_vect){
// Count Encoder Pulses
counter++;
}
ISR(TIMER1_COMPA_vect)
{
// PORTB ^= (1 << 0); // Toggle the LED
lastcounter = counter;
if(lastcounter < targetticks){
if(OCR2 + 1 <= 0xFF){
OCR2++; //turn up the duty cycle
//USART_string("Speed Up\n");
}
}
if(lastcounter > targetticks){
//if(OCR2 > 30){
USART_string("Slow Down\n");
// Check for quick slow down
if(lastcounter > (targetticks + 1000)){
OCR2 = OCR2 - 40;
} else if(lastcounter > (targetticks + 300)){
OCR2 = OCR2 - 10;
} else {
OCR2--; //turn down the duty cycle
}
}
}
|
|
|
| |
|
|
|
|
|