Hello,
I'm using the Servo.h library with 3 servos and I plan to use it with 6 servo.
Is it possible to know which timer is used, in order to do not modify it while servo are used ?
Sev
Hello,
I'm using the Servo.h library with 3 servos and I plan to use it with 6 servo.
Is it possible to know which timer is used, in order to do not modify it while servo are used ?
Sev
Timer1, the sixteen bit timer.
Duane B
You ! again !
Thank you very much
Sev
Maybe you can help me again... I'd like to use a more precise time scale than the miros() command (4µs), do you know how to do without modifying timer1 ?
First question is why do you need better than 4us ?
Duane
To avoid this kind of "leap year" problem :
For example, the channel 3 value should be around 1483, it is mesured almost time as 1484 and some time as 1480.
With a better resolution, the value will be more stable around 1483.
Sev
Hi,
Some interrupts take around 4us to complete, hence the 4us error, it will still be there whatever resolution you use.
In practice this gets smoothed out by the motors and mechanical momentum of the vehicle.
My older equipment has signals allover the place due to the transmission technology but its not noticeable in use -
And
Duane B
DuaneB:
Some interrupts take around 4us to complete, hence the 4us error, it will still be there whatever resolution you use.
I'm trying to use the lightest interrupt code (inspired by your's) : I think this is clearly less than 1µs
DuaneB:
In practice this gets smoothed out by the motors and mechanical momentum of the vehicle.My older equipment has signals allover the place due to the transmission technology but its not noticeable in use -
RCArduino: How To Read an RC Receiver With A Microcontroller - Part 2
And
RCArduino: Reading From An RC Reveiver - Do you need a new Radio ?
Duane B
Thank you for the link, I'm using a 2.4GHz transmission, that seems very stable.
My measures were done in a domestic condition but short range, so I'd like to analyse again with more precision, and wider range.
...But the true reason to no more use the micros() command, is to be a hard-core coder that don't use any library, and can develop in assembly 8)
Hi,
the servo library has an interrupt service routine that takes around 4us, the pin change int library takes a little less both of them could be replaced with lower level, less generic code for a slight gain.
In my view it's not worth doing for 3 or fewer channels but would be essential for more than 4 channels.
Duane b
Since my projet is to input 6ch and output 6 servos, I think we are agree to tell that this is a need to perform interruptions manually.
My action plan is :
But I still don't know how to improve the precision, maybe build my own timer, without modifying timer1 that is used by the servo library ?
Hi,
Thinking about it, I have not made any measurements or tests that would support my statement -
In my view it's not worth doing for 3 or fewer channels but would be essential for more than 4 channels.
If you have equipment that can generate six channels, try a test with an extended version of the 3-channel code, if the variation in the signal does not increase I would suggest that you stick with the existing libraries on the basis that they have had far more testing than any of us could do individually to our own code.
If it gets worse then you know that you are definitely not wasting your time building alternatives from the ground up.
Duane B
Wise position Duane, I'll following this direction.
Here is the Rx part, I modified the interrupt part to use only one interruption function.
Parameters are NbOfCh & FSCh :
NbOfCh : Number of channel (I don't know the maximum, I think 8 is possible)
FSCh : Number of the channel to survey, for "failsafe" detection (just connect a RED LED ont PIN 12 to see it)
The main loop is around 5/6ms with the DEBUG output. everything seems ok (except the 4µs resolution)
#include "PinChangeInt.h"
#define NO_PORTB_PINCHANGES //to go faster
#define NO_PORTC_PINCHANGES //to go faster
// LED pins
#define ledO 13
#define ledR 12
// Rx parameters
#define NbOfCh 6 // Put here the total channel number and plug the FIRST channel on the PIN 3
#define FSCh 1 // Wich channel will be looked for failsafe
// ReadReciever variables
boolean FailSafe;
volatile boolean EndTrame;
volatile unsigned long vChUp[NbOfCh];
volatile unsigned long vChPos[NbOfCh];
// Blink LED variables
int ledState = LOW;
unsigned long MillDeb, MillFin, LoopPer, previousMillis = 0;
// Blink LED function
void BlinkLed(const int BLed,int BDelay)
{
int Bdelay;
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > BDelay) {
previousMillis = currentMillis;
if (ledState == LOW) ledState = HIGH;
else ledState = LOW;
digitalWrite(BLed, ledState);
}
}
void setup()
{
pinMode(ledO, OUTPUT);
pinMode(ledR, OUTPUT);
// DEBUG serial OUTPUT
Serial.begin(57600);
Serial.println(";");
Serial.print("Receiver with : ");
Serial.print(NbOfCh);
Serial.println(" channels");
// Enable Interruption on channel's PIN
for (int i=0; i < NbOfCh; i++){
PCintPort::attachInterrupt(i+3, TSedge,CHANGE);
}
}
void loop()
{
//TimeStamp Loop begining
MillDeb = millis();
// Declaration : Local version of the channel values
static unsigned long ChPos[NbOfCh];
if ((MillDeb*1000)>(vChUp[FSCh-1]+50000)){ //Detect FailSafe on the (FSCh) Channel
Serial.print("FailSafe"); //DEBUG "Failsafe" serial output
digitalWrite(ledR, HIGH);
FailSafe=1;
}
else{
if (FailSafe==1) { //FailSafe Exit
FailSafe=0;
digitalWrite(ledR, LOW);
}
else{
if (EndTrame == 1) { //End of trame detection
noInterrupts(); //Stop IRQ & copie channels values
for (int i=0; i < NbOfCh; i++){
ChPos[i]=vChPos[i];
}
interrupts(); //Restart IRQ & Reset EndTrameflag
EndTrame=0;
}
}
}
// DEBUG : Serial output the channels
for (int i=0; i < NbOfCh; i++){
Serial.print(ChPos[i]);
Serial.print(";");
}
//Blink "normaly" the Orange LED
BlinkLed(ledO,500);
//EndLoop TimeStamp
MillFin = millis();
LoopPer = MillFin - MillDeb;
// DEBUG : Serial output the Loop Duration (ms)
Serial.print(LoopPer);
Serial.println(";");
}
void TSedge() // Same Interrupt code on all channels
{
// If this is a rising edge, record it as (vChUp)
if(PCintPort::pinState == HIGH)
{
vChUp[PCintPort::arduinoPin-3] = micros();
}
else
{
//If this is a falling edge, calculate the channel value
vChPos[PCintPort::arduinoPin-3] = micros()- vChUp[PCintPort::arduinoPin-3];
}
if (PCintPort::pinState == LOW && PCintPort::arduinoPin == NbOfCh + 2) EndTrame=1; // End of trame is set when the last falling edge of the last channel has gone !
}
I'm adding the Servo part, everything seems ok, running the loop without connected servo seems very fast (I do not have yet a power connection for 6 servos,building it...)
Another question : Is there PIN that are recommended to connect servos ?
Hi,
There are no recommended pins for servos, any pins will do.
The servo library uses an interrupt routine that takes 4us to complete so this will definitley increase the frequency of the 4us glitches. As far as I can tell, the glitches happen when an input changes state, but the input ISR cannot run because the Servo ISR is already running and needs to complete before the input ISR can run. This leads to the 4us error reading the input pulse length.
As long as the error stays around the 4us mark I would live with it, let us know what happens.
Duane B
Hello world !
Could you please explain me the ~ (PWM ?) symbol accorded to the 3,5,6,9,10,11 PIN ?
I was thinking, this was "prefered" output for PWM...
I've tested my new sketch with 6ch and 6 servo :
#include "PinChangeInt.h"
#define NO_PORTB_PINCHANGES //to go faster
#define NO_PORTC_PINCHANGES //to go faster
#include <Servo.h>
// LED pins
#define ledO 13
#define ledR 12
// Rx parameters
#define nbOfCh 6 // Put here the total channel number and plug the FIRST channel on the PIN 3
#define nbOfServo 6 // Put here the total channel number and plug the FIRST channel on the PIN 3
#define failsafeCh 1 // Wich channel will be looked for failsafe
// ServoParam
Servo outServ[nbOfServo];
int servPos[nbOfServo];
// ReadReciever variables
boolean failsafe;
volatile boolean endTrame;
volatile unsigned long vChUp[nbOfCh];
volatile unsigned long vchPos[nbOfCh];
// Blink LED variables
int ledState = LOW;
unsigned long previousTS = 0;
// Blink LED function
void BLINK_LED(const int bLed,int bDelay)
{
unsigned long currentTS = millis();
if(currentTS - previousTS > bDelay) {
previousTS = currentTS;
if (ledState == LOW) ledState = HIGH;
else ledState = LOW;
digitalWrite(bLed, ledState);
}
}
void setup()
{
pinMode(ledO, OUTPUT);
pinMode(ledR, OUTPUT);
// Enable Interruption on channel's PIN
for (int i=0; i < nbOfCh; i++){
PCintPort::attachInterrupt(i, TS_EDGE,CHANGE);
}
// Servo attachement
for (int i=0; i < nbOfServo; i++){
outServ[i].attach(i+nbOfCh);
}
}
void loop()
{
// Declaration : Local version of the channel values
static unsigned long chPos[nbOfCh];
if ((millis()*1000)>(vChUp[failsafeCh-1]+50000)){ //Detect failsafe on the (failsafeCh) Channel
digitalWrite(ledR, HIGH);
failsafe=1;
// Set servos on MID-position
for (int i=0; i < nbOfServo; i++){
outServ[i].writeMicroseconds(1500);
}
}
else{
if (failsafe==1) { //failsafe Exit
failsafe=0;
digitalWrite(ledR, LOW);
}
else{
if (endTrame == 1) { //End of trame detection
noInterrupts(); //Stop IRQ & copie channels values
for (int i=0; i < nbOfCh; i++){
chPos[i]=vchPos[i];
}
interrupts(); //Restart IRQ & Reset endTrameflag
endTrame=0;
// Set servos as Channel
for (int i=0; i < nbOfServo; i++){
outServ[i].writeMicroseconds(chPos[i]);
}
}
}
}
//Blink "normaly" the Orange LED
BLINK_LED(ledO,500);
}
void TS_EDGE() // Same Interrupt code on all channels
{
// If this is a rising edge, record it as (vChUp)
if(PCintPort::pinState == HIGH)
{
vChUp[PCintPort::arduinoPin] = micros();
}
else
{
//If this is a falling edge, calculate the channel value
vchPos[PCintPort::arduinoPin] = micros()- vChUp[PCintPort::arduinoPin];
}
if (PCintPort::pinState == LOW && PCintPort::arduinoPin == nbOfCh-1) endTrame=1; // End of trame is set when the last falling edge of the last channel has gone !
}
And there is lot's of glitches, at 2 level :
I think we can say there is interruption conflicts.
Sev
The ~ shows the pins that support hardware pwm. The servo library doesn't use hardware pwm, so can use other pins too.
Thank you dxw00d,
Why don't we just use hardware PWM to generate servo signal ?
Hi,
As you know, there are only three timers, these timers are what drives hardware PWM.
The servo library uses one of the timers to trigger interrupts which it then uses to support upto 12 servos in software instead of just 2 (assumption on my part here) that a single hardware timer without additional software could support.
A little bit about the servo library here -
Are the glitches still 4us or are they bigger in size ?
Duane B
I don't know the values since my debug output has been disabled (6 ch & 6 servo are using the PIN from 0 to 12, even the PIN 1 that is also the serial output)
So in my opinion, I must drive the servo output manually, my first idea is to "schedule" the digitalwrite() command to be triggered at a specific micros() value, but again, I just don't know how to do this...
So in my opinion, I must drive the servo output manually, my first idea is to "schedule" the digitalwrite() command to be triggered at a specific micros() value, but again, I just don't know how to do this...
Why do you thing you need to drive servos manually, are they moving wildly ?
As a quick tip, you can use any of the analog pins as normal digital pins, use the aliases A0 to A5 in any command i.e.
pinMode(A0,OUTPUT);
MyServo.Attach(A0);
digitalRead(A0);
etc, etc
Thats a quick way to get your debug back.
Duane B
Duane,
All about my project is explained in this topic, especially the 2 last posts (17 & 18) :
I don't want to update 2 topic about that, so my arduino questions are in the Arduino forum, but my project is on RCgroup, where other modelists (as you) can see it.
About the servo driving, I'd like to synchronize it to the Rx music.
Sev