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).
Related
It is a homework and I have completely NO idea, my teacher says you need just while, analogWrite and a counter. I have a DC motor, a transistor and a 9V battery.
I know my code does NOTHING, but just as example.
int analogPin = 3;
int count = 0;
void setup()
{
pinMode(analogPin, OUTPUT);
}
void loop() {
while(count<30){
analogWrite(analogPin , 255);
delay(20000);
count++;
}
}
You need to use counter value as your analogue output value:
void loop()
{
if( count < 256 )
{
analogWrite( analogPin, count ) ;
delay( 20000 );
count++ ;
}
}
Note that you do not need a while loop; the Arduino framework already calls loop() iteratively (the clue is in the name). However if your teacher thinks you need one and is expecting one, you may need to use one just for the marks. Alternatively discuss it with your teacher, and explain why it is unnecessary
In fact the delay too is arguably bad practice - it is unhelpful in applications where the loop() must do other things while controlling the motor. The following allows other code to run whilst controlling the motor:
unsigned long delay_start = 0 ;
void loop()
{
if( count < 256 &&
millis() - delay_start >= 20000ul )
{
analogWrite( analogPin, count ) ;
count++ ;
delay_start = millis() ;
}
// Do other stuff here
}
Because the loop() now never blocks on the delay() function, you can have code that does other things such as read switch inputs and it can react to them instantly, whereas as in your solution, such inputs could be ignored for up-to 20 seconds!
A typical DC motor will not start moving at very low values - you may want to start count somewhat higher than zero to account for the "dead-band". Analogue signals are also generally a poor way to drive a DC motor and varying speed; a PWM is generally a more efficient method, and will allow the motor to run at lower speeds. With an analogue signal at low levels (lower than for PWM), your motor will not move and will just get warm and drain your battery.
For test purposes, reduce the delay time; you don't want to sit there for an hour and 25 minutes just to find the code does not work! Set it to say 500ms, then start it, time how long it takes before the motor starts to move. If that is say 30 seconds, then yu know the motor starts to move when count is about 60; in which case that is a better starting value that zero. Then you can increase your delay back to 20 seconds if you wish - though a DC power supply might be better than a battery - I'm not sure it will last that long.
Can someone explain how to modify this program to "ReadAnalogVoltage" once every 60 seconds, forever.
Right now it reads the voltage every millisecond (I think), forever.
Too much information is being generated.
Any suggestions would be appreciated.
Rob.
> /*
ReadAnalogVoltage
Reads an analog input on pin 0, converts it to voltage, and prints
the result to the serial monitor.
Attach the center pin of a potentiometer to pin A0, and the outside
pins to +5V and ground.
This example code is in the public domain.
*/
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
}
There are several ways to lower the sampling rate. Place a counter inside your loop and only print when the counter reaches a certain value. Be sure to reset the value after printing.
int counter = 0;
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
if(counter >= 1000000){
Serial.println(voltage);
counter = 0;
}
}
The best way to solve this is with an time interrupt. The actual mechanics of setting the interrupt will depend on what hardware you are using.
unsigned long t;
boolean isHigh;
#define BUZZER_PIN 3
void setup() {
// put your setup code here, to run once:
pinMode(BUZZER_PIN, OUTPUT);
isHigh = false;
t = micros();
}
void loop() {
playNote('c');
}
void playNote(char note) {
unsigned long timeToWait;
unsigned long timeToPlayTheNote = millis();
while (timeToPlayTheNote - millis() < 1000) {
if (note == 'c') {
timeToWait = 1911;
}
if (micros() - t > timeToWait) {
if (!isHigh) {
digitalWrite(BUZZER_PIN, HIGH);
isHigh = true;
} else {
digitalWrite(BUZZER_PIN, LOW);
isHigh = false;
}
t = micros();
}
}
}
I don't know why this won't work. I used to play a frequency every 1,000 microseconds but is there any way to make this simpler as well? Also, with this method I have to do (1/f)/2 and then convert that value from seconds to microseconds and use that as the value for timeToWait.
Initialization of ˋtimeToWait` should obviously be outside of the loop.
An array could be used for timing data.
ˋt` should probably be initialized inside ˋplayNoteˋ
Alternatively, you might use an enum for delay associated to a note.
enum class notes
{
C = 1911
};
Well, all suggestion assume that you don't want to compensate for drifting offsets.
Buzzers have a fixed frequency. They don't work like speakers at all. You will get better results with a real speaker. Don't forget to put a capacitor in series with it so the speaker sees an AC signal, you can fry a speaker quite easily if you feed it a DC signal..
For best results, you should use 2 x 47uF to 100uF electrolytic capacitors back to back, with the negative poles joined together, one positive to the 'duino and the other positive pole connected to the speaker. With higher capacitance, you'll get more bass.
Why don't you use a PWM at 50% (128) and change the PWM frequency to generate the sound? You could use the Timer1 or Timer3 library for that. Letting the hardware do the work would be more presise and would free your application for other tasks, such as reading a keyboard.
https://playground.arduino.cc/Code/Timer1
Setting the PWM at 0% with an analogWrite() would cut the sound.
Can someone please help me with code? I have a 24 teeth trigger wheel. Every tooth is registered by hall sensor and I need that Arduino simulate 36 pulse output of that corresponding 24 pulse input.
Here is my code with delayMicroseconds, but I can`t use delayMicroseconds, because Arduino doesn't understand bigger than 16k micros delay.
const int hall = 2; // hall sensor
const int ledPin = 13; // the pin that the LED is attached to
// Variables will change:
int teethCounter = 0;
int hallState = 0;
int lasthallState = 0;
long cycles=0;
boolean cycle = false;
unsigned long microsStart = 0;
unsigned long microsStop = 0;
unsigned long usElapsed = 0;
unsigned long usElapsedUp = 0;
unsigned long usInterval;
void setup() {
// initialize the button pin as a input:
pinMode(hall, INPUT);
// initialize the LED as an output:
pinMode(ledPin, OUTPUT);
// initialize serial communication:
Serial.begin(9600);
}
void loop() {
hallState = digitalRead(hall);
if(cycle==true){
microsStart=micros();
}
if(cycle==true){
usInterval = usElapsedUp/72;
for (int i=0; i <= 36; i++){
digitalWrite(13,HIGH);
delayMicroseconds(usInterval);
digitalWrite(13,LOW);
delayMicroseconds(usInterval);
cycle = false;
}
}
// compare the hallState to its previous state
if (hallState != lasthallState) {
// if the state has changed, increment the counter
if (hallState == HIGH) {
teethCounter++;
if(teethCounter==24){
cycle = true;
cycles++;
teethCounter=0;
usElapsedUp = usElapsed;
}
Serial.print("Tooth count: ");
Serial.print(teethCounter);
Serial.print(" Cycles: ");
Serial.print(cycles);
Serial.print(" Time: ");
Serial.print(usElapsedUp);
Serial.print(" Interval: ");
Serial.println(usInterval);
}
microsStop=micros();
usElapsed=microsStop-microsStart;
}
// save the current state as the last state,
//for next time through the loop
lasthallState = hallState;
}
How can I calculate and from where can I take trigger points?
If(event happens==true){
digitalWrite(13,HIGH);
}
If(event happens==false){
digitalWrite(13,LOW);
}
If it helps to understand here is a block diagram
As long as you understand that you will never be able get 36 pulses per turn accuracy with 24 pulse/turn, you can do this, which is a common trick derived from the Bresenham algorithm. This solution assumes you are concerned about the position.
Now, this will generate pulses in real-time, as opposed to your code, which generates pulses in a blocking manner, I don't think losing pulses was your original intent.
This code will not generate pulses uniformly, 1 out of 3 readings will generate 2 pulses.
Another way would be to calculate the average speed and program a hardware timer to simulate the 36 pulses per turn, using interrupts, but going that route would likely (invariably, in my experience) end up in total loss of sync between the actual position of the wheel and what your corrected tick count reports. There are also strict speed ranges that you have to respect if going that route, also this will introduce severe latency issues to your application.
Change the increment value to 36, and the whole turn count to 24/36.
Change the step detection to a threshold of 24.
I'm trying to understand why you want to do this 36/24 thing, and can't.
So, your mileage may vary.
// compare the hall State to its previous state
// to declared outside of loop()
// int smallCounter;
// PULSE_WIDTH as small positive pulse with in us
//
if (hallState != lasthallState) {
// if the state has changed, increment the counter
smallCounter += ((hallState == HIGH) ? 36 : 0);
// ... I'm assuming that the serial prints you had here were just here for debugging.
lasthallState = hallState;
}
//
// reporting for each step below
//
if (smallCounter >= 24)
{
smallCounter -= 24;
if (++teethCounter >= 36) {
cycle = true;
cycles++;
teethCounter=0;
usElapsedUp = usElapsed;
}
digitalWrite(13,HIGH);
delayMicroseconds(PULSE_WIDTH);
digitalWrite(13,LOW);
delayMicroseconds(PULSE_WIDTH); // this is probably not needed.
}
I'm dealing with arduino mega based quadcopter and trying to make PWM frequency for 4 motors - 400hz each. I've found an interesting solution where 4 ATmega2560 16bit timers are used to control 4 ESCs with PWM so it could reach 400hz frequency. 700 to 2000µs are normal pulse widths ESC are dealing with.
1sec/REFRESH_INTERVAL = 1/0.0025 = 400hz.
this is servo.h lib:
#define MIN_PULSE_WIDTH 700 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2000 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1000 // default pulse width when servo is attached
#define REFRESH_INTERVAL 2500 // minimum time to refresh servos in microseconds
#define SERVOS_PER_TIMER 1 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
The problem is to make it work each PWM should be controlled with 1 16bit timer. Otherwize, say, 2 escs on 1 timer would give 200hz. So all of 16bit timers are busy controlling 4 ESC but I still need to read input PPM from receiver. To do so I need at least one more 16bit timer which I don't have anymore. It's still one 8bit timer free bit it can only read 0..255 numbers while normal number escs operate with are 1000..2000 and stuff.
So what would happen if I'll use same 16bit timer for both pwm and ppm reading? Would it work? Would it decrease speed drastically? I have arduino working in pair with Raspberry Pi which controls data filtering, debugging, and stuff, is it better to move ppm reading to Raspberry?
To answer one of your questions:
So what would happen if I'll use same 16bit timer for both pwm and ppm
reading? Would it work?
Yes. When your pin change interrupt fires you may just read the current TCNT value to find out how long it has been since the last one. This will not in any way interfere with the timer's hardware PWM operation.
Would it decrease speed drastically?
No. PWM is done by dedicated hardware, software operations running at the same time will not affect its speed and neither will any ISRs you may have activated for the corresponding timer. Hence, you can let the timer generate the PWM as desired and still use it to a) read the current counter value from it and b) have an output compare and/or overflow ISR hooked to it to create a software-extended timer.
Edit in response to your comment:
Note that the actual value in the TCNT register is the current timer (tick) count at any moment, irrespective of whether PWM is active or not. Also, the Timer OVerflow interrupt (TOV) can be used in any mode. These two properties allow to make a software-extended timer for arbitrary other time measurement tasks via the following steps:
Install and activate a timer overflow interrupt for the timer/counter you want to use. In the ISR you basically just increment a (volatile!) global variable (timer1OvfCount for example), which effectively counts timer overflows and thus extends the actual timer range. The current absolute tick count can then be calculated as timer1OvfCount * topTimerValue + TCNTx.
When an event occurs, e.g a rising edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount and store these values in another global variable (e.g. startTimestamp), effectively starting your time measurement.
When the second event occurs, e.g. a falling edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount. Now you have the timestamp of the start of the signal in startTimestamp and the timestamp of the end of the signal in another variable. The difference between these two timestamps is exactly the duration of the pulse you're after.
Two points to consider though:
When using phase-correct PWM modes the timer will alternate between counting up and down successively. This makes finding the actual number of ticks passed since the last TOV interrupt a little more complicated.
There may be a race condition between one piece of code first reading TCNT and then reading timer1OvfCount, and the TOV ISR. This can be countered by disabling interrupts, then reading TCNT, then reading timer1OvfCount, and then checking the TOV interrupt flag; if the flag is set, there's a pending, un-handled overflow interrupt -> enable interrupts and repeat.
However, I'm pretty sure there are a couple of library functions around to maintain software-extended timer/counters that do all the timer-handling for you.
what is unit of 700 and 2000?I guess usec.You have not exaplained much in your question but i identified that you need pulses of 25msec duration in which 700 usec on time may be 0 degree and 2000 may be for 180 degree now pulse input of each servo may be attached with any GPIO of AVR.and this GPIOs provide PWM signal to Servo.so i guess you can even control this all motors with only one timer.With this kind of code:
suppose you have a timer that genrate inturrupt at every 50 usec.
now if you want 700 usec for motor1,800 usec for motor 2,900 usec for motor 3 & 1000 usec for motor 4 then just do this:
#define CYCLE_PERIOD 500 // for 25 msec = 50 usec * 500
unsigned short motor1=14; // 700usec = 50x14
unsigned short motor2=16; // 800usec
unsigned short motor3=18; // 900usec
unsigned short motor4=20; // 1000usec
unsigned char motor1_high_flag=1;
unsigned char motor2_high_flag=1;
unsigned char motor3_high_flag=1;
unsigned char motor4_high_flag=1;
PA.0 = 1; // IO for motor1
PA.1 = 1; // IO for motor2
PA.2 = 1; // IO for motor3
PA.3 = 1; // IO for motor4
void timer_inturrupt_at_50usec()
{
motor1--;motor2--;motor3--;motor4--;
if(!motor1)
{
if(motor1_high_flag)
{
motor1_high_flag = 0;
PA.0 = 0;
motor1 = CYCLE_PERIOD - motor1;
}
if(!motor1_high_flag)
{
motor1_high_flag = 1;
PA.0 = 1;
motor1 = 14; // this one is dummy;if you want to change duty time update this in main
}
}
if(!motor2)
{
if(motor2_high_flag)
{
motor2_high_flag = 0;
PA.1 = 0;
motor2 = CYCLE_PERIOD - motor2;
}
if(!motor2_high_flag)
{
motor2_high_flag = 1;
PA.1 = 1;
motor2 = 16;
}
}
if(!motor3)
{
if(motor3_high_flag)
{
motor3_high_flag = 0;
PA.2 = 0;
motor3 = CYCLE_PERIOD - motor3;
}
if(!motor3_high_flag)
{
motor3_high_flag = 1;
PA.2 = 1;
motor3 = 18;
}
}
if(!motor4)
{
if(motor4_high_flag)
{
motor4_high_flag = 0;
PA.3 = 0;
motor4 = CYCLE_PERIOD - motor4;
}
if(!motor4_high_flag)
{
motor4_high_flag = 1;
PA.3 = 1;
motor4 = 19;
}
}
}
& tell me what is ESC?