Concerned about drift of micros() value in Arduino program - arduino

I have a program that utilizes the Servo library and an external interrupt routine. From my understanding the Servo library uses a Timer1 interrupt to send pulses to the servo to maintain position. I am wondering what the impact is on the micros() count since it does not increment during an interrupt.
The external interrupt routine in my code is for a tachometer. It determines the time between pulses using micros(). I am concerned that the Servo library will cause drift of the millis() and micros() counters and make the speed inaccurate. The tachometer may have to sense a 10,000 RPM speed so about 167 Hz.
Eventually I will implement PID control using servos and tachometer.
volatile unsigned long period;
unsigned long microseconds;
void setup(){
Serial.begin(9600);
pinMode(tachometerPin, INPUT);
pinMode(led, OUTPUT);
attachInterrupt(0, tachometer, RISING); // set external interrupt
throttle.attach(throttlePin); // attach servo objects to pins
fuel.attach(fuelPin);
throttle.writeMicroseconds(throttle_idle); // set servo positions
fuel.writeMicroseconds(fuel_neutral);
}
void loop(){
Serial.println(calculateSpeed());
}
float calculateSpeed(){
/* Calculate speed of engine in RPM */
float s = 60.0/(period*0.000001);
return(s);
}
void tachometer() {
/* Determine time between rotations of engine (pulses from tachometer) */
period = micros() - microseconds;
microseconds = micros();
}

Even tachometer() and the function that updates millis() are called from an interrupt, so even it would "break" your code.
How much would this ruin your lecture? Not very much. Arduino 2009, with using a crystal for 16 MHz, has a drift of many seconds at day. Arduino Uno, which uses a less precise oscillator, would drift a lot more.
So, unless you are doing a lot of calculation in the interrupt routine, you are fine. Also remember that a micros() call cost you 4 µs (in fact you will see it growing 4 by 4... or this was to have Timer0 to overflow every second? Take a look at the timer prescaler; you can change it if it does not break the Servo lib. It will break millis() and micros(), and you can use Timer1 or Timer2, but Timer0 is the only 16-bit timer, so it depends on you), so this is your real precision killer.
I would suggest to eliminate the function call in the interrupt:
unsigned long tmpTime;
void tachometer() {
/* Determine time between rotations of engine (pulses from tachometer) */
tmpTime = micros();
period = tmpTime - microseconds;
microseconds = tmpTime;
}
And even attachInterrupt call an ISR that call your function. You may implement attachInterrupt by yourself so you implement the ISR directly (one fewer function call, and function calls are relatively expensive as you have to work with the stack and registers).
Also don't use micros(), which does a lot of things (read the Timer0 actual count, and calculate count to µs, and is a function call); just read the timer register directly and do the difference with that value, and do the translation of count to µs into loop(). That way it should be a lot faster.
I'm not writing code as you just have to open Arduino's library and look at what they do, and replicate it by yourself. It is quite easy as you have a working example, an ATmega datasheet and a lot of tutorials on timers, interrupts and even PWM.

Related

Arduino servo without library

As education exercise on sending digital signals, I'm trying to code the pulse train for a servo without using the servo.h library.
The servo is a 9g micro servo. Hardware is correct, as many examples using servo.h work fine.
I have the following code. The problem is that the servo jerks around for 3 seconds rather than moving and holding still.
void loop() {
movePulse_1000();
delay(3000);
}
void movePulse_1000(){
Serial.print("Start movePulse_1000()\t\t");
for (int pulseCounter=0; pulseCounter<=150; pulseCounter++){
digitalWrite(pinServo,LOW);
delay(20); // between pulses
digitalWrite(pinServo,HIGH);
delayMicroseconds(1000);
}
Serial.println("End movePulse_1000()");
}
Using an analog servo, the average pulse width must be 1.5ms apart, and the duty cycle changes based on the desired position. To keep the servo where you want it, you must constantly refresh the servo data. This isn't a super simple task and that servo library is quite optimized. There's few reason not to use it.
It creates hardware timers and uses them to refresh the servos. This allows your regular code to appear to continue regularly even though it's being interrupted by the servo library to service the servos. Duty cycle, pwm frequency, and refresh rates all come into play. You'll have to look at the data sheet of the servo you are using to get the full details. But its not as simple you think and those delay/delaymicros functions you're using aren't always precise enough. That's why you would use times and overflow interrupts. Though most servos aren't too picky and you can get away with a ton of slop.
The library servo.h constantly sends pulses which means servo growling = battery consumption.
I changed your function to only rotate the servo without a timer from 0 to 180 degrees.
del = (7 * x) +500; - for my servo-pulse 500 to 1260us (calculated, not measured)
void movePulse(int x){
int del=(7*x)+500;
for (int pulseCounter=0; pulseCounter<=50; pulseCounter++){
digitalWrite(pinServo,HIGH);
delayMicroseconds(del);
digitalWrite(pinServo,LOW);
delay(20); // between pulses
}
}

TCCR1B breaks micros()

I'm finding that when I'm using Timer 1 that the micros() function stops functioning correctly. I thought that:
Arduino internal functions (except PWM on pins 9. 10 which i'm not using) use timer 0
micros() function doesn't actually rely on a timer (but millis does)
If i call
TCCR1B |= (1 << CS11) | (1 << CS10);
in setup().
The micros function will start reporting sporadic values after about 0.25s. E.g. the micros() will increase at a very low rate after that, and often go backwards.
I'm testing on an Arduino Uno.
I'm also trying to check if the timer interrupt being called might be too heavy. It's not doing anything much except some integer maths and one digitalWrite.
According to the datasheet, "”8-bit Timer/Counter0 with PWM” on page 93 and ”16-bit Timer/Counter1 with PWM” on page 111 share the same prescaler module, but the Timer/Counters can have different prescaler settings. The description below applies to both Timer/Counter1 and Timer/Counter0."
So it's likely not an interaction with clock select, but you have only 64 clocks to perform the interrupt and exit it. There's some overhead in entering and exiting the interrupt. And DigitalWrite isn't as fast as you think. So without counting instruction clock cycles, I think I'd pursue the line of thought from your last statement -- lighten the load in that interrupt by just setting a flag and then exit. Handle the flag in the main loop. When you're talking microseconds, it's especially important to get out of that ISR quickly. Pseudocode:
volatile uint8_t timerflag = 0;
ISR <timer1_whatever>
{
timerflag = 1;
}
back in main loop:
if (timerflag == 1)
{
<do something>
timerflag = 0; // reset for next interrupt
}
Make sure your timeflag is declared as volatile since it's being changed inside an interrupt and checked outside the interrupt.

Arduino: Using analogRead() on Photoresistor to read LED with PWM

I have been working on a project with Arduino and have come across something that I find fascinating/confusing. So, I had to test something before constructing this project. I built a simple circuit that consists of just an LED and photoresistor. What I had to test was whether the photoresistor was capable of determining the brightness of an LED that was being dimmed through PWM. My initial expectation was that this would not work (the photoresistor would either read 1023 or 0 because PWM is achieved digitally). To my surprise, the photoresistor was able to accurately read the brightness of the LED (accurately to an extent -- this is simply based off of comparing the apparent brightness of the PWM LED with an LED placed in series with a certain resistor)! This is exactly what I wanted, but I am just curious as to why this works. I am not sure if my original doubt was due to a misunderstanding of photoresistors or PWM. Any help would be much appreciated. Thank you!
Here is the code I am running (I am not using the analogWrite() function because the project I am working on requires me to have a certain level of control over the PWM):
const int LED_PIN = 9;
const int PHOTO_PIN = 0;
//These values have been altered and tested
const int HIGH_TIME = 250;
const int LOW_TIME = 2750;
void setup()
{
pinMode(LED_PIN, OUTPUT);
pinMode(PHOTO_PIN, INPUT);
Serial.begin(9600);
}
void loop()
{
digitalWrite(LED_PIN, HIGH);
delayMicroseconds(HIGH_TIME);
digitalWrite(LED_PIN, LOW);
delayMicroseconds(LOW_TIME);
Serial.println(analogRead(PHOTO_PIN));
}
A "photoresistor" is a variable resistor. That is the simplest way to say it.
Just imagine your potentiometer, you can control its resistance by turning the little knob and then analogRead it. The photoresistor on the other side, changes it resistance depending on the light intensity. Because of that, the resistance will go up and down depending on your LED.
For "HOW" it actually works, see here.
Now, there are a couple of factors to consider:
1 - The ambient light of your room.
2 - The distance between your LED
So hope I helped you learn a little more about photoresistors!
The response time of the photo resistor is much slower than the PWM frequencies you are using. So it averages the on and off times of the LED and gives a resistance proportional to the average light. If you were using a photodiode with a fast response time, it would be able to "see" the LED go on and off.
I suggest that you don't try to write to the Serial port every time through the loop since it will quickly fall behind at 9600 baud. Perhaps write every 500 times through the loop.

Multiple Arduino Interrupts on same pin

In the following code, why does ISR2 never run?
const int in = 19;
const int LED = 9;
int flag;
void setup() {
pinMode(in, INPUT_PULLUP);
pinMode(LED, OUTPUT);
attachInterrupt(digitalPinToInterrupt(in), ISR2, RISING);
attachInterrupt(digitalPinToInterrupt(in), ISR1, FALLING);
}
void loop() {
if (flag) {delay (100); flag = false;}// debounce
}
void ISR1(){
digitalWrite(LED, LOW);
// Turn Off the motor, since, Limit Switch was engaged
flag = true;
}
void ISR2(){ // Limit Switch was disengaged.
digitalWrite(LED, HIGH);
delay(100); // Debounce, so I do not receive spurious FALLING edges from this.
}
Does the Arduino not allow you to attach two interrupts on the same pin even if the interrupts are programmed for different events?
In my setup, pin 19 gets a signal from a limit switch used in a motion control setup. When the limit switch is engaged, the in pin gets LOW signal. Thus, I first see a FALLING edge followed by RISING edges and FALLING edges due to mechanical bounce. I handle the debouncing correctly in this case.
However, imagine the Limit Switch was sitting in engaged state for a while, and then I reverse the motor causing the Limit Switch to disengage, this will send a RISING edge followed by FALLING and RISING edges. I need to ignore these edges since nothing is in danger. The ISR2 was written with purpose of capturing the first RISING edge when the Limit switch disengages, and then debouncing it so that the following FALLING edges are ignored. But now that ISR2 never gets called, how can I deal with this situation?
P.S. My microcontroller is ATMEGA 2650, It's a Arduino Mega board.
ISR2 never runs because there can be only one interrupt service routine for any interrupt source and you have replaced ISR2 with ISR1 as the sercice routine for this interrupt. If you reordered your code and attached ISR1 before ISR2 then you might see ISR2 run but not ISR1.
A typical microcontroller has an interrupt vector table that associates an interrupt service routine with each interrupt source. There can be only one service routine for each interrupt source. If you assign an new service routine then you're replacing the old service routine. There are not separate service routines for the rising and falling edges. Rising versus falling edge is a configuration setting for the interrupt source to determine when that interrupt should fire. The interrupt source cannot be configured to fire on both edges simultaneously.
However you may be able to reconfigure the interrupt for the other edge after you have received the interrupt for the first edge and debounced the transition. This way your code will ping-pong back and forth configuring for one ISR and then the other.
Why are you saying that it never gets called? I think that it is called, but you don't notice it because the led does not change its state (because there are bounces).
Anyway, you are NOT debouncing it correctly. Let's do an example: you hit the endstop. ISR1 gets called, so flag is true. Ok, at next loop the motor will be stopped. But... Now the switch bounces. ISR2 gets called, and the delay function in it waits for 100ms before exiting the ISR. Result: you delayed the motor stop function by 100ms.
I suggest you to read my answer here, particularly the second case. And I suggest you to use my code instead of yours, since this way you will be able to IMMEDIATELY stop the motor, without bounces nor any other kind of problems.

necessity to give delay to adc

I was trying to send analog signals from arduino to the computer by the serial port.For getting the maximum samples of my input analog signals,I put my baudrate to the maximum limit.The code is as given
void setup()
{
Serial.begin(115200);
}
void loop()
{
int a=analogRead(A0);
Serial.println(a);
delay(1);
}
This program works well for my signals with very low frequency.but at higher frequency signals,there is still arising a problem of aliasing.I tried decreasing the delay.i got more samples When i did this but some of my digital values that I got could not be used,that is,some of the wrong digital values were like 353?12 and so on.Is it manda to give minimum delay of 1 ms to the ADC??Or is there any way that i can Increase my samplestory
You are running into an issue with the default prescaler setting in arduino. The default setup will only get less than 10 samples per millisecond.
You can certainly get faster reads from your ADC if you want to play with some of the underlying settings:
www.microsmart.co.za/technical/2014/03/01/advanced-arduino-adc/
has a good write up on the subject. They got it running at 50 samples per millisecond with some tweaking.
The other option is to get an ADC chip which will run faster for you. Search sparkfun for "mcp3002". No tweaking of your arduino required since it uses spi input.

Resources