Search |
 |
|
 |
| Author |
Message |
|
|
Posted: Sep 27, 2011 - 07:11 AM |
|

Joined: Oct 07, 2009
Posts: 248
|
|
Good night dear Freaks, here I'm again asking the help of the smarter ones.
As usual here is the story:
As you might already read I'm designing a small robot for a contest, and I'm still in the motor control stage.
After some searching around google and here I found some nice code to read my encoders at 2x because 1x resolution was a bit on the short side, my problem is that the encoder that is connected to external INT0 works flawlessy, but the one connected to INT1 only counts down, which seems strange, I have already checked everything and swapped the motors and the problem only happens with the INT0 pin..
Here is what I think that is the relevant code if someone as some time to read it and maybe help me
Code:
void motorInit(void){
serialInit();
DIRDDR |= ((1<<DIR1A)|(1<<DIR1B)|(1<<DIR2A)|(1<<DIR2B)); //sets direction control pins as outputs
ENCADDR &= ~((1<<ENC1A)|(1<<ENC2A)); //Sets encoder pins as inputs
ENCBDDR &= ~((1<<ENC1B)|(1<<ENC2B)); //sets the rest of the encoder pins as inputs
PWMDDR |= ((1<<PWMMPIN1)|(1<<PWMMPIN2)); //Sets pwm pins as outputs
LEDDDR |= ((1<<LEDERR)|(1<<LEDOK)); //Set status leds as outputs
MOTORDIRDDR = ((1<<MOTOR1A)|(1<<MOTOR1B)|(1<<MOTOR2A)|(1<<MOTOR2B)); //Sets H bridge dir select pins as outputs
EICRA |= ((1<<ISC10)|(1<<ISC00)); //Ext int 0 and 1 respond to any edges in the respective pins
EIMSK |= ((1<<INT1)|(1<<INT0)); //Enable external int 0 and 1 in the interrupt mask, dont forget the sei() in main
MOTORPORT |= ((1<<MOTOR1A)|(1<<MOTOR1B)|(1<<MOTOR2A)|(1<<MOTOR2B)); //Stop the motors in the init
//Pwm init and config
TCCR0A = ((1<<COM0A1)|(1<<COM0B1)|(1<<WGM00)); //Enable pwm in both timer 0 pins with phase correct pwm
TCCR0B = (1<<CS00); //No prescaled clock, feed 16Mhz to the timer
PWMMOTOR1 = 0; //Fpwm = (Fio/(N*510)), with N=1, Fpwm = 31Khz
PWMMOTOR2 = 0; //This is to avoid stupid noises from the motors, and init the values to 0
//Timer 1 init and config
TCCR1B = ((1<<WGM12)|(1<<CS10)); //Timer in CTC mode, no prescaling of Fclock
OCR1A = 15999; //Magic number to get 1ms with an 16Mhz clock
TIMSK1 = (1<<OCIE1A); //Enable compare interrupt
//TImer 2 init and config
//Used to calculate the motor speed
//So, its very essential
TCCR2A = (1<<WGM21); //Timer in CTC mode
TCCR2B = ((1<<CS22)|(1<<CS21)); //prescaler is 256
OCR2A = 250; //Target count to have 250Hz interrupt
TIMSK2 = (1<<OCIE2A); //Enable timer compare match interrupt
}
Code:
#define DIRDDR DDRC //Motor control port
#define DIR1A PC0 //Pins to choose motor direction
#define DIR1B PC1 //Pins to choose motor direction
#define DIR2A PC4 //Pins to choose motor direction
#define DIR2B PC5 //Pins to choose motor direction
#define ENCADDR DDRD //Port where external In for encoders are located
#define ENCAPIN PIND
#define ENC1A PD2 //External Int 0 pin, encoder 1 pin A
#define ENC2A PD3 //External Int 1 pin, encoder 2 pin A
#define ENCBDDR DDRB //Port where pin B of encoders are located
#define ENCBPIN PINB //Used to read encoder pins
#define ENC1B PB0 //Encoder 1, pin B
#define ENC2B PB1 //Encoder 2, pin B
#define PWMDDR DDRD //Port where the pwm pins are located
#define PWMMPIN1 PD6 //Pwm pin for motor 1
#define PWMMPIN2 PD5 //Pwm pin for motor 2
#define PWMMOTOR1 OCR0A //To write speed values for the motor 1
#define PWMMOTOR2 OCR0B //To write speed values for the motor 2
#define MOTORDIRDDR DDRC //Port where the H bridge dir pins are connected
#define MOTORPORT PORTC
#define MOTOR1A PC0
#define MOTOR1B PC1
#define MOTOR2A PC2
#define MOTOR2B PC3
Code:
ISR(INT0_vect){
if(!(ENCAPIN & (1<<ENC1A))){ //This means a falling edge
if((ENCBPIN & (1<<ENC1B)) == 0){
encoderA++;
}
else {
encoderA--;
}
}
else{
if((ENCBPIN & (1<<ENC1B)) == 1){
encoderA++;
}
else {
encoderA--;
}
}
}
ISR(INT1_vect){
if(!(ENCAPIN & (1<<ENC2A))){ //This means a falling edge
if((ENCBPIN & (1<<ENC2B)) == 0){
encoderB++;
}
else {
encoderB--;
}
}
else{
if((ENCBPIN & (1<<ENC2B)) == 1){
encoderB++;
}
else {
encoderB--;
}
}
}
PS.: I'm sorry but the code in gVim is perfectly indented, but then here and in Avr Studio it gets all messed up :/ |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:31 AM |
|

Joined: Sep 22, 2011
Posts: 240
Location: Berlin
|
|
It looks as if theres a little typo in INT1 . Change the first line from
Code:
if(!(ENCAPIN & (1<<ENC2A))){ //This means a falling edge
to
Code:
if(!(ENCBPIN & (1<<ENC2B))){ //This means a falling edge
Hope that helps |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:33 AM |
|

Joined: May 02, 2007
Posts: 3010
Location: Nieuwegein, Netherlands
|
|
a couple of things you can check:
what happens if you only use 1 encoder at a time. so first only write code for INT0, then only for INT1. INT0 you have already working you state, so that should be easy and fast to do. Use the same encoder for writing both versions of the code. Then swap the encoder to make sure that the second encoder also works on both INT1 and 0. this to rule out a defective encoder.
if the 2 loose parts work, then combine them and only use 1 encoder to see if that works, then only use the other and finally use them both.
I assume you did not forget the sei() in main, as per comment in your code.
regards |
_________________ 1)Datasheet and application notes checked?
2)tutorial forum
3)Newbie start here
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:39 AM |
|

Joined: Aug 11, 2010
Posts: 76
Location: Porsgrunn, Norway
|
|
Hi
May be you have selected device, but the Xmega series has inbuild Quadrature Decoders.
This works nice. I have tested it on full speed on my Dremel (20000 rpm). |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 06:25 PM |
|

Joined: Oct 07, 2009
Posts: 248
|
|
I have been doing all my testing with only one encoder, using INT0, I have already swapped the motors and they both work fine when connected to INT0 pin.
My nomenclature got a bit weird, ENCA means that its channel A of the encoder and encoder 1 and 2 means the pair of encoder channel A and B of each motor, so ENCA is the A channel that is connected respectively to PD2 and PD3 that have the INT0 and INT1 capability and ENCB are the channel B that are connected to PB0 and 1 as the channel B from each encoder, yes I have sei() in my code, because I have a timer generating interrupts at 1ms rate to do soft timing in my code and also timer 2 is being used as a time base to calculate rpm/wheel speed at regular intervals to feed the pid loop to maintain steady speed.
I will look more into the code to see if I can discover the mistake, but this is really bugging me.
EDIT
One thing that I found that is confusing me is that the generated code for the two ISR's is not equal, in the INT1 the first encoder B++ is missing!
So, is this a compiler bug?
Code:
ISR(INT0_vect){
164: 1f 92 push r1
166: 0f 92 push r0
168: 0f b6 in r0, 0x3f ; 63
16a: 0f 92 push r0
16c: 11 24 eor r1, r1
16e: 8f 93 push r24
170: 9f 93 push r25
172: af 93 push r26
174: bf 93 push r27
if(!(ENCAPIN & (1<<ENC1A))){ //This means a falling edge
176: 4a 99 sbic 0x09, 2 ; 9
178: 0e c0 rjmp .+28 ; 0x196 <__vector_1+0x32>
if((ENCBPIN & (1<<ENC1B)) == 0){
17a: 19 99 sbic 0x03, 1 ; 3
17c: 0d c0 rjmp .+26 ; 0x198 <__vector_1+0x34>
encoderA++;
17e: 80 91 4c 01 lds r24, 0x014C
182: 90 91 4d 01 lds r25, 0x014D
186: a0 91 4e 01 lds r26, 0x014E
18a: b0 91 4f 01 lds r27, 0x014F
18e: 01 96 adiw r24, 0x01 ; 1
190: a1 1d adc r26, r1
192: b1 1d adc r27, r1
194: 0c c0 rjmp .+24 ; 0x1ae <__vector_1+0x4a>
encoderA--;
}
}
else{
if((ENCBPIN & (1<<ENC1B)) == 1){
196: 83 b1 in r24, 0x03 ; 3
encoderA++;
}
else {
encoderA--;
198: 80 91 4c 01 lds r24, 0x014C
19c: 90 91 4d 01 lds r25, 0x014D
1a0: a0 91 4e 01 lds r26, 0x014E
1a4: b0 91 4f 01 lds r27, 0x014F
1a8: 01 97 sbiw r24, 0x01 ; 1
1aa: a1 09 sbc r26, r1
1ac: b1 09 sbc r27, r1
1ae: 80 93 4c 01 sts 0x014C, r24
1b2: 90 93 4d 01 sts 0x014D, r25
1b6: a0 93 4e 01 sts 0x014E, r26
1ba: b0 93 4f 01 sts 0x014F, r27
}
}
}
1be: bf 91 pop r27
1c0: af 91 pop r26
1c2: 9f 91 pop r25
1c4: 8f 91 pop r24
1c6: 0f 90 pop r0
1c8: 0f be out 0x3f, r0 ; 63
1ca: 0f 90 pop r0
1cc: 1f 90 pop r1
1ce: 18 95 reti
000001d0 <__vector_2>:
ISR(INT1_vect){
1d0: 1f 92 push r1
1d2: 0f 92 push r0
1d4: 0f b6 in r0, 0x3f ; 63
1d6: 0f 92 push r0
1d8: 11 24 eor r1, r1
1da: 8f 93 push r24
1dc: 9f 93 push r25
1de: af 93 push r26
1e0: bf 93 push r27
if(!(ENCAPIN & (1<<ENC2A))){ //This means a falling edge
1e2: 4b 99 sbic 0x09, 3 ; 9
1e4: 03 c0 rjmp .+6 ; 0x1ec <__vector_2+0x1c>
if((ENCBPIN & (1<<ENC2B)) == 0){
1e6: 18 99 sbic 0x03, 0 ; 3
1e8: 0f c0 rjmp .+30 ; 0x208 <__vector_2+0x38>
1ea: 02 c0 rjmp .+4 ; 0x1f0 <__vector_2+0x20>
encoderB--;
}
}
else{
if((ENCBPIN & (1<<ENC2B)) == 1){
1ec: 18 9b sbis 0x03, 0 ; 3
1ee: 0c c0 rjmp .+24 ; 0x208 <__vector_2+0x38>
encoderB++;
1f0: 80 91 50 01 lds r24, 0x0150
1f4: 90 91 51 01 lds r25, 0x0151
1f8: a0 91 52 01 lds r26, 0x0152
1fc: b0 91 53 01 lds r27, 0x0153
200: 01 96 adiw r24, 0x01 ; 1
202: a1 1d adc r26, r1
204: b1 1d adc r27, r1
206: 0b c0 rjmp .+22 ; 0x21e <__vector_2+0x4e>
}
else {
encoderB--;
208: 80 91 50 01 lds r24, 0x0150
20c: 90 91 51 01 lds r25, 0x0151
210: a0 91 52 01 lds r26, 0x0152
214: b0 91 53 01 lds r27, 0x0153
218: 01 97 sbiw r24, 0x01 ; 1
21a: a1 09 sbc r26, r1
21c: b1 09 sbc r27, r1
21e: 80 93 50 01 sts 0x0150, r24
222: 90 93 51 01 sts 0x0151, r25
226: a0 93 52 01 sts 0x0152, r26
22a: b0 93 53 01 sts 0x0153, r27
}
}
}
22e: bf 91 pop r27
230: af 91 pop r26
232: 9f 91 pop r25
234: 8f 91 pop r24
236: 0f 90 pop r0
238: 0f be out 0x3f, r0 ; 63
23a: 0f 90 pop r0
23c: 1f 90 pop r1
23e: 18 95 reti
EDIT2:
Looks like a compiler problem, just commented out the INT0 ISR code and now the INT1 ISR is working perfectly counting up and down, I'm stumbled  |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 06:57 PM |
|


Joined: Jul 18, 2005
Posts: 62281
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
Looks like a compiler problem, just commented out the INT0 ISR code and now the INT1 ISR is working perfectly counting up and down, I'm stumbled
Or a programmer problem perhaps? Have you timed how long the ISRs take? How often do they occur. If INT0 (which has a higher priority than INT1) prevents INT1 from occuring it's likely because it's absorbing almost all the CPU time. |
_________________
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:11 PM |
|

Joined: Oct 07, 2009
Posts: 248
|
|
You are righ Clawson, but right now I'm rotating the motors by hand, and one at a time, so when I rotate the motor that is using INT1 the motor that is using INT0 is totally stopped.
Now I uncommented the INT0 ISR and its the INT0 that only down counts, this is starting to seem very strange!
I think that I'm not overloading the micro, because each motor "only" outputs 116 pulses per revolution. |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:14 PM |
|


Joined: Jul 18, 2005
Posts: 62281
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
OK so tell us more about this "compiler problem". What evidence do you have that the compiler has generated code that does not match what the source has asked for?
I see threads here where there are "bugs in the silicon" - with the exception of Xmega, there aren't any of those either(*).
(*) OK there are rare faults like the AVcc-Vcc internal connection in some old chips - but they are very rare! |
_________________
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:21 PM |
|

Joined: Oct 07, 2009
Posts: 248
|
|
I'm not blamming the compiler and I should have said this first I'm almost sure that this is my problem, but here is the actual state:
The encoder connected to INT0 pin works 100%, perfect in fact, I can swap the motors and both work perfectly, but when I try to connect one of the motors to INT1 it only down-counts, this means that if I rotate it in one direction it keeps the count at 0, but in the other direction the values start to go negative, again rotate to the other side and it doesn't increment the count.
If I comment the ISR(INT0_vect) code ou the INT1 start working perfectly like INT0 does, if I uncomment it, it stops working again and only downcounts.
I dont understand almost nothing about assembler but it seems that the code generated for both INT0 and INT1 are different when the source is exactly the same. |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:33 PM |
|


Joined: Aug 04, 2004
Posts: 1822
Location: Davie, FL
|
|
Try leaving both interrupt routines in the code but only enable interrupt 1, then try only enabling interrupt 0. This way the code for both will be present but only one interrupt can actually happen since only one will be enabled.
BTW another way of handling this is to use a timer interrupt to schedule a polling of the encoders. Figure out the highest speed that the encoders will be switching (rev per sec * pulses per rotation) and set your timer to interrupt faster than that (now you will know if the micro is fast enough!). On each timer interrupt poll both encoders and note the state changes to figure out forward, backward, or stable (write a state machine, there are 4 possible encoder states). You can always expand this method to handle more than two encoders (if you have enough IO and the processor is fast enough). |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:39 PM |
|


Joined: Aug 04, 2004
Posts: 1822
Location: Davie, FL
|
|
|
Quote:
Now you mention it the two RJMP at 0x1e8 and 0x1ea do, on the surface of things, look a bit odd? The second looks to be unreachable?
I think the second jump might be a trampoline.
[just to note that I deleted by post (quoted above) when I realised what an idiot I was. The two rjmps are preceded by SBIC so of course it's valid to have two consecutive RJMPs - I'll now return to sleep - BTW he is right that seemingly identical ISRs do seem to generate different code though - Cliff] |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 07:42 PM |
|

Joined: Oct 07, 2002
Posts: 2018
Location: Denmark
|
|
| And you are 100% that all varibles used between ISR and main are decleared volatile ! |
|
|
| |
|
|
|
|
|
Posted: Sep 27, 2011 - 08:34 PM |
|

Joined: Oct 07, 2009
Posts: 248
|
|
| Yes, everything used in the ISR's is volatile, if someone wants to look at the project I will attach everything in a .zip. |
|
|
| |
|
|
|
|
|
Posted: Sep 30, 2011 - 01:11 AM |
|

Joined: Oct 07, 2009
Posts: 248
|
|
I have "temporarily" fixed this using a simpler method to read the encoders at the expense of reduced resolution, still I have a 5 degree resolution which seems enough for a small robot, here is the now working code:
Code:
ISR(INT0_vect){
if((ENCBPIN & (1<<ENC1B)) == 0){
encoderA++;
}
else {
encoderA--;
}
}
ISR(INT1_vect){
if((ENCBPIN & (1<<ENC2B)) == 0){
encoderB++;
}
else {
encoderB--;
}
}
If someone as the time/willingness to do it, I would like to see my original code compiled in a different avr-gcc version to see if it is a compiler problem or my problem. |
|
|
| |
|
|
|
|
|
Posted: Sep 30, 2011 - 09:08 AM |
|


Joined: Jul 18, 2005
Posts: 62281
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
I would like to see my original code compiled in a different avr-gcc version
To know what's "different" I think first you are going to have to tell us which one you used. |
_________________
|
| |
|
|
|
|
|
Posted: Sep 30, 2011 - 08:37 PM |
|

Joined: Oct 07, 2009
Posts: 248
|
|
| I'm using WinAVR-20100110 maybe its a bit outdated... |
|
|
| |
|
|
|
|
|
|
|
|