unsigned int serinp[8]; //servo positionsvoid setup_timer1(){//disable all interuptsTIMSK1 &= ~( _BV(TOIE1) | _BV(ICIE1) | _BV(OCIE1A) | _BV(OCIE1B));//set timer modeTCCR1A &= ~( _BV(WGM11) | _BV(WGM10) );TCCR1B &= ~( _BV(WGM12) | _BV(WGM13) | _BV(ICNC1));//capture raising edgeTCCR1B |= _BV(ICES1); //capture raising edge//prescaler 1/8TCCR1B |= _BV(CS11);TCCR1B &= ~( _BV(CS12) | _BV(CS10) );//disable outputsTCCR1A &= ~( _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1));//enable capture interuptTIMSK1 |= (1<<ICIE1);}ISR(TIMER1_CAPT_vect){static unsigned int lasticr; //icr at last caputrestatic unsigned char cserinp; //current input servounsigned int licr;//TCCR1B ^= _BV(ICES1);licr=ICR1-lasticr;lasticr=ICR1;if(licr>5000){ //pulse too long, means start of new framecserinp=0;}else if(licr>1000){ //pulse good, take reading, go to next channelserinp[cserinp]=licr;if(cserinp<8){cserinp++;}}else{//too short, nothing to see here}}void setup()pinMode( 8, INPUT ); //sumsetup_timer1();}void loop(){//use serinp!!!}it'll take a sum signal on pin 8. you can also connect the signal for a single servo, then just serinp[0] will be populated. the values in serinp are 2 times the microseconds, so between 2000-4000 (instead of 1000-2000), yes, there's extra accuracy in there!the timer-capture only works on digital pin 8!if you don't have access to the sum signal, a possible way to read multiple servos would be to connect them all to pin 8. since the receivers usually output the pulses one after the other, this should work (you'll have to adjust the code in the capture-interupt).
You need to be a member of diydrones to add comments!
Comments
Now I use the APM_radio code as suggested by Chris. I connect the PPM signal to the input pin 49 of the arduino Mega board and it works fine.
I want to read the 8 channel PPM Signal from an ACT Rx receiver using an arduinoMega. Can I try the code above?
Anyone have a link to a simple interrupt driven sketch that reads one channel and prints out the value?
I'm trying to avoid using pulsein on my code for my stepper based antenna tracker. Here is the link to where I am at if you are curious... http://www.rcgroups.com/forums/showthread.php?t=1529524#post19715075
Anyhow I am trying the code above with my atmega 328p pro mini and am unable to get a reading using pin 8 (always 0).
The code is below and I have tried another ISR(TIMER1_CAPT_vect) routine that I found on RCgroups but it doesn't work either. Can anyone point me to some update code? I just want to look at one channel from the RX and have it interrupt based.
thanks,
Brendin
#define enablePin 4
// CODE FOR HARDWARE INTERRUPT
unsigned int serinp[8]; //servo positions
void setup_timer1(){
//disable all interupts
TIMSK1 &= ~( _BV(TOIE1) | _BV(ICIE1) | _BV(OCIE1A) | _BV(OCIE1B));
//set timer mode
TCCR1A &= ~( _BV(WGM11) | _BV(WGM10) );
TCCR1B &= ~( _BV(WGM12) | _BV(WGM13) | _BV(ICNC1));
//capture raising edge
TCCR1B |= _BV(ICES1); //capture raising edge
//prescaler 1/8
TCCR1B |= _BV(CS11);
TCCR1B &= ~( _BV(CS12) | _BV(CS10) );
//disable outputs
TCCR1A &= ~( _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1));
//enable capture interupt
TIMSK1 |= (1<<ICIE1);
}
/*
ISR(TIMER1_CAPT_vect){
static unsigned int lasticr; //icr at last caputre
static unsigned char cserinp; //current input servo
unsigned int licr;
//TCCR1B ^= _BV(ICES1);
licr=ICR1-lasticr;
lasticr=ICR1;
if(licr>5000){ //pulse too long, means start of new frame
cserinp=0;
}else if(licr>1000){ //pulse good, take reading, go to next channel
serinp[cserinp]=licr;
if(cserinp<8){
cserinp++;
}
}else{
//too short, nothing to see here
}
}
*/
ISR(TIMER1_CAPT_vect){
static unsigned int lasticr; //icr at last caputre
static unsigned char cserinp; //current input servo
unsigned int licr;
//TCCR1B ^= _BV(ICES1);
licr=ICR1-lasticr;
lasticr=ICR1;
if (licr<10000) { //pulse not super long - so Ok, otherwise totally ignore
if(licr>5000){ //pulse too long, means start of new frame
cserinp=0;
serinp[cserinp]=licr;
cserinp++;
}else if(licr>500){ //pulse good, take reading, go to next channel
serinp[cserinp]=licr;
if(cserinp<8){
cserinp++;
}
}else{
//too short, nothing to see here
}
//updateTXVals(); // So at the end lets update our calibrate vals
}
}
// END HARDWARE INTERRUPT CODE
void setup()
{
Serial.begin(9600);
Serial.println("Setting up timer.");
pinMode( 8, INPUT ); //sum
setup_timer1();
Serial.println("done setting up timer.");
}
void loop()
{
channel=serinp[0];
if ((serinp[0] >0) or (serinp[0] <0) ){
Serial.print("Value = " );
Serial.println(serinp[0]);
}
}
Where can i find that pwm code??
10x!
I'd use the PWM code from ArduPilot, which has advanced this technique quite a bit.
I was trying to use your code, but i have some problems or mistakes...
Im reading the ppm channel, only for the first 2 channels... and got something like this:
1419 1506
1419 1506
1418 1503
1418 1503
1421 1503
1420 1502
1420 1502
1422 1502
1422 1502
1424 1502
1424 1501
1426 1501
Then, i try to send that value to the servo...
And the servo goes very high! i a position that doesnt correspond to +-1500 values, what can be wrong??
This is my code:
#include
#define ser1out 2//Servo Exterior
#define ser2out 3//Servo Interior
unsigned int serinp[2]; //servo positions
ServoTimer2 ciLat;
ServoTimer2 ciLon;
void setup(){
Serial.begin(9600);
pinMode( 8, INPUT ); //sum
ciLat.attach(ser1out);
ciLon.attach(ser2out);
setup_timer1();
}
void loop()
{
Serial.print(serinp[0]/2);
Serial.print(" ");
Serial.println(serinp[1]/2);
ciLat.write(serinp[0]/2);
ciLon.write(1500);
}
The rest is your functions...
With one servo i write to it 1500, and it works fine, but the othe servo, is just crazy... what im doing wrong??
Thanks!!
Cheers!!
Nice code, quix-fz, many thanks! I tried in on my Arduino and: it works! My setup is:
Spektrum DX6i + AR6100e (plus PWM to PPM Converter from rc-cam.com)
Arduino Nano 3.0 (ATMega 328)
LCD Display (it is very convinient to see all channel values)
ADXL330 (I want to implement a simple wing leveler for my Easy Glider)
I had to use ServoTimer2 library for servo control since Servo uses Timer1. LiquidCrystal had to be modified as well to avoid using delay() functions.
Thanks for the ICR PPM capture code. I am attempting to use it but have hit a snag. I am also using the analogWrite() procedure to drive ESC's @ 488Hz and apparently timer1 which your code uses is also responsible for pins 9,10 which I need for the PWM drivers. Is there anyway that you can think of to modify your code so as to still allow pins 9 and 10 to be used to driver hardware PWM or should I just ditch the idea?
Thanks!
here's some understandable descrition of interupts:
http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/
the code first setups the timer1. this is the only 16bit timer on the atmega168/arduino. this means it counts from 0 to 65536 and then again from zero. the number is increased every 1/8 clock (arduino is 16mhz, so the number is increased every 8/16000000 = 1/2000000 second!!). the input capture is also setup, it "records" the time (the time (16bit counter) from timer1) when the signal goes high on the input capture pin (thats digital 8 on the arduino) to the internal register ICR1. this is completely done in hardware, no matter what else is going on!!! even if your software is crashed this will probably continue to work (including the following part). when signal goes high, an interupt (basicly a function) is called too, which reads ICR1 and subtracts the old ICR1 the get the pulse-length and decodes the ppm.
-
1
-
2
of 2 Next