Arduino Measuring PWM with attachInterrupt() - c++

Can someone explain the flow of this Arduino program shown below?
volatile int pwm_value = 0;
volatile int prev_time = 0;
void setup() {
Serial.begin(115200);
// when pin D2 goes high, call the rising function
attachInterrupt(0, rising, RISING);
}
void loop() { }
void rising() {
attachInterrupt(0, falling, FALLING);
prev_time = micros();
}
void falling() {
attachInterrupt(0, rising, RISING);
pwm_value = micros()-prev_time;
Serial.println(pwm_value);
I understand that PWM means looking for the length of time the signal remains high for each cycle.
In void setup(), the first rising edge of the signal will trigger the void rising(). So during void rising() the signal is at HIGH and prev_time = micros() is measuring the time of the signal at high (pulse width) right?
Then once the falling edge of the signal comes in, the attachInterrupt() function in void rising() will trigger void falling(). At this point the signal is at LOW, so micros() in void falling() is measuring the time of the signal at low? Then that will make no sense to take pwm_value = micros()-prev_time.
This will only make sense if prev_time is the measurement of the signal at LOW and micros() is the measurement of the period of the signal. Then pwm_value = micros()-prev_time is correct.
Based on my explanation, please explain to me what I am not getting.

This code will wait for a rising edge. Once the signal goes high it will store the current time in prev_time and start waiting for the signal to drop low. Once a falling edge is detected it will print the difference between prev_time and the current time which is your on-time in microseconds.
pwm_value is a misleading name. This is just a time measurement which is not related to PWM per se. PWM values are usually duty cycles. The on-time alone does not give you any information in terms of PWM. You further need the off-time or the total time to know the duty cycle.
As cleblanc mentioned in his comment using serial prints in an ISR is not very good.

Related

Can't update data through the Loop

I'm currently working on an EEG system using the Arduino. The purpose of the system is to vibrate a vibrator for .1 seconds, wait .5 seconds, and then vibrate again for .1 seconds. However from the start I want it to read the EEG (Serial.println(brain.readCSV()); Serial.println(brain.readErrors());) every .1 seconds from the start.
The problem is that it only takes one sample and just repeats it throughout the process until it cycles, instead of continuously updating through the loop.
How can I get it to continuously read the new data while the entire system is operating.
#include "Brain.h"
#include <SPI.h>
#include <Wire.h>
int n=0;
int m=0;
// Set up the brain parser, pass it the hardware serial object you want to listen on.
Brain brain(Serial);
int vib = 5;
void setup() {
// Start the hardware serial.
Serial.begin(9600);
pinMode(vib, OUTPUT);
}
void loop() {
// Expect packets about once per second.
// The .readCSV() function returns a string (well, char*) listing the most recent brain data, in the following format:
// "signal strength, attention, meditation, delta, theta, low alpha, high alpha, low beta, high beta, low gamma, high gamma"
if (brain.update()) {
Serial.println(brain.readCSV());
Serial.println(brain.readErrors());
if(brain.readSignalQuality() == 0) {
// Vibrate
digitalWrite(vib,HIGH);
Serial.println(brain.readCSV());
Serial.println(brain.readErrors());
delay(100);
while (n<500){
n=n+100;
digitalWrite(vib,LOW);
Serial.println(brain.readCSV());
Serial.println(brain.readErrors());
Serial.println(n);
delay(100);
}
digitalWrite(vib,HIGH);
Serial.println(brain.readCSV());
Serial.println(brain.readErrors());
delay(100);
while (m<10000){
m=m+100;
digitalWrite(vib,LOW);
Serial.println(brain.readCSV());
Serial.println(brain.readErrors());
Serial.println(m);
delay(100);
}
n=0;
m=0;
}
}
}
As Delta_G says in the comments, you need to be calling brain.update() each time you want new data. Looking at the source code for Brain.cpp, notice that "readErrors" just returns a variable that only changes after you run update() (and readCSV runs sprintf on a bunch of variables that also get updated in update()). This implies that readErrors and readCSV don't actually pull new data.

Measuring frequency with Arduino

I am working on a small project for measuring input square wave frequency using Arduino, I did it using both timer and hardware interrupts and here is my code:
#include <TimerOne.h>
long count1=0,freq1=0;
void setup(){
attachInterrupt(5,count,RISING);
Timer1.initialize(1000000); //increments count for 1 sec and prints out count
Timer1.attachInterrupt( freq ); // attach the service routine here
Serial.begin(9600);
}
void loop(){
while(freq1)
Serial.println(freq1);
}
void count()
{
count1++;
}
void freq(){
freq1 = count1;
count1=0;
}
The problem is that output value is almost double the input frequency,
I don't know what's wrong with it. I learned about interrupts after an intensive Google search. Seems it didn't work.
There is a working example here.
It uses native ISR functions (faster) instead of arduino interrupt handlers.
Uses Timer1 for best resolution and precision.
The code is big but the part that interests you is:
The "// Setup Timer1 for precise timing" portion of void setup()
ISR(TIMER1_OVF_vect) -> for timer1 overflow counter
ISR(INT0_vect) // External Interrupt pin 2 [D2]
void PulseCaptureScheduler_callback() // Starts a capture and flags a timeout
void PrintInfoScheduler_callback() // Processes the results

Arduino interrupt frequency

I am using an Arduino uno to measure the speed of a dc motor.
I have a opto sensor that gives a pulse when the motor has made a full turn.
The problem I've got starts when the motor has a speed > 90Hz.
As soon as I reach 90Hz, the Arduino doesn't enter the interrupt function.
My code:
int pin = 13;
volatile int state = LOW;
volatile unsigned long startTijd = 0;
volatile unsigned long eindTijd = 0;
unsigned int frequentie = 0;
volatile int count = 0;
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, blink, FALLING); //LOW, HIGH, FALLING, RISING, CHANGE
Serial.begin(19200);
}
void loop()
{
noInterrupts();
digitalWrite(pin, state);
interrupts();
}
void blink()
{
if (count == 0) {
startTijd = micros();
}
count++;
if (count == 31) {
count = 0;
eindTijd = micros();
eindTijd -= startTijd;
Serial.print(eindTijd);
Serial.print(" ms. - ");
frequentie = 30 * 1000000 / eindTijd;
Serial.print(frequentie);
Serial.println(" Hz.");
}
state = !state;
}
My question is : When the Arduino receives interrupts at 90Hz, it doesn't execute the code in the interrupt. When the motor goes below 90Hz after that, the code works again. What am I doing wrong ?
It looks as though blink is your ISR. If that's the case, you shouldn't be doing debug I/O within that routine for 2 reasons. The first is that you are calling a process that could block. The second is that ISRs should do their thing and finish (should be highly efficient). My guess is that if you remove the debug I/O from your ISR and pass info back to the interrupted task instead (safely, of course) you will be able to service interrupts at greater than 90 hz.
Just to add to #Bruce answer. You are using serial IO in the interrupt. Each time you are printing around 15-20 characters (depending on the values calculated). Each character is encoded by 8 bit data + 1 start bit + 1 stop bit = 10 bits. So, say 20*10=200 bits. The baud rate is 19200bps, so time required to transmit 200 bits is 200/19200 sec, or in terms of frequency 19200/200=96Hz. So this is the maximum frequency achievable for transmission of 20 characters, which is close to your measured 90Hz (take in account that I am not considering any time spacing overhead between the transmits).

Calling constant complexity O(1) 5 line function significantly impacts performance, why?

I have the following code:
#include <ros.h>
#include <ros/time.h>
#include <sensor_msgs/Range.h>
#define sensNumber 3
#define firstTrigPin 3
#define firstEchoPin 9
ros::NodeHandle nh;
sensor_msgs::Range range_msg;
ros::Publisher pub_range( "/ultrasound", &range_msg);
char frameid[] = "/ultrasound";
int trigPin=firstTrigPin;
int echoPin=firstEchoPin;
void increasepins(){
trigPin++;
echoPin++;
if(trigPin>firstTrigPin+sensNumber){
trigPin=firstTrigPin;
}
if(echoPin>firstEchoPin+sensNumber){
echoPin=firstEchoPin;
}
}
void setup(){
nh.initNode();
nh.advertise(pub_range);
for(int i=0;i<sensNumber;i++){
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
increasepins();
}
range_msg.radiation_type = sensor_msgs::Range::ULTRASOUND;
range_msg.header.frame_id = frameid;
range_msg.field_of_view = 0.1;
range_msg.min_range = 0.0;
range_msg.max_range = 6.47;
}
long range_time;
void loop()
{
increasepins();
//publish the adc value every 50 milliseconds
//since it takes that long for the sensor to stablize
long duration, distance;
digitalWrite(trigPin, LOW); // should be high?
delayMicroseconds(2); // make this 20
digitalWrite(trigPin, HIGH);
delayMicroseconds(10); // reset value?
digitalWrite(trigPin, LOW);
distance = pulseIn(echoPin, HIGH)/ 58,2; //sensor constant is 3.4
range_msg.range = distance;
range_msg.header.stamp = nh.now();
range_time = millis() + 50;
range_msg.field_of_view = trigPin;
range_msg.min_range = echoPin;
pub_range.publish(&range_msg);
nh.spinOnce();
}
This above code runs at my funduino at aprox 2 times a second for the whole loop. If I however remove the increasePins method it runs at around 100 times per second. Why the big chance? It seems like such a trivial piece of code (just increase 2 variables and then compare them) make such a large difference?
For reference we are talking about this function:
void increasepins(){
trigPin++;
echoPin++;
if(trigPin>firstTrigPin+sensNumber){
trigPin=firstTrigPin;
}
if(echoPin>firstEchoPin+sensNumber){
echoPin=firstEchoPin;
}
}
Which to me looks really simple when compared to having to wait for the echo to come back and getting the time and that sort of things required for the rest of the code.
I believe that one of the problem is that if you comment the increasePins function in the loop, the compiler will decide to take some of the logic outside the loop. For that you need to look # the assembly code and see what's being done inside the loop.
Another problem may be related to the pin behavior. For example, I read that "pulseIn" function sends a pulse to the pin and measures it's duration. You use it to pulse the "echoPin". I suspect that if you don't change the echoPin you will have a rather constant duration. However, changing the echoPin may lead to a situation where another pin's pulse duration would be longer. To test this, you can try and use pulsePin(echoPin, HIGH, timeout) with a timeout parameter for the case when you did not comment the increasePins function. I expect the loop to run faster.
So i think that the increasePins is not a bottleneck. I recommend making it inline however (could add some speedup if the compiler hasn't done this already).

Ultrasonic Sensor HC-SR04 + Arduino Calculation?

I'm using this code :
int trigPin = 7;
int echoPin = 8;
void setup() {
Serial.begin(9600);
pinMode(trigPin,OUTPUT);
pinMode(echoPin,INPUT);
}
void loop() {
int duration;
int distance;
digitalWrite(trigPin,HIGH);
delayMicroseconds(1000);
digitalWrite(trigPin,LOW);
duration = pulseIn (echoPin,HIGH);
distance = (duration/2)/29.1;
Serial.print("distance = ");
Serial.println (distance);
delay(500);
}
I understand the concept that we send a pulse of 1000 µsec in this case and we wait for the reflected signal. But I don't understand how in this code, only the pulse width of the reflected signal is being used for the calculation.
I searched around and they say the reflected pulse width is proportional to the distance traveled. Can someone please explain how this happens (the physics behind it) and also where does the 29.1 comes from?
I read this documentation but I still don't understand the pulse width concept.
Many thanks in advance!
Like Chris touched on in the comments, the input pin goes HIGH for the time between it being send and received. I'm not exactly sure if it goes HIGH for a certain amount of time after or during the transmission, but you might be able to find that on a datasheet.
where does the 29.1 come from?
That's the speed of sound through air... you can use that to convert the time to centimeters. You'll have to divide it by two because it goes two ways.
Recap:
Arduino sends pulse to SR04 (1000 microseconds):
digitalWrite(trigPin,HIGH);, delayMicroseconds(1000);, &digitalWrite(trigPin,LOW);.
SR04 emits a ping and changes the signal pin to HIGH
The ping hits an object, bounces back, and goes back to the SR04
The SR04 sets the pin back to LOW
The Arduino measures the length the signal pin is HIGH with this:
duration = pulseIn (echoPin,HIGH);