I'm trying to play a tone while changing something on an LCD display. I've searched around and tried protothreads, but it seems that the delay still blocks the program. I've also tried removing the delay altogether, but it skipped everything except the last note. Is there a way to play a tone without using delay? (millis perhaps?)
Sample tone sequence:
//Beats per Minute
#define BPM 250
//Constants, try not to touch, touch anyways.
#define Q 60000/BPM //Quarter note
#define W 4*Q //Whole note
#define H 2*Q //Half note
#define E Q/2 //Eigth note
#define S Q/4 //Sixteenth note
void toneFunction()
{
tone(tonePin,C5,Q);
delay(1+W);
tone(tonePin,C5,Q);
delay(1+W);
tone(tonePin,C5,Q);
delay(1+W);
tone(tonePin,C6,W);
}
You can set up a timer and put note changing logic into an interrupt service routine (ISR).
Each X milliseconds, the timer will reset and interrupt your main loop. The ISR will run and pick the next note and call the tone function. After exiting the ISR, the program continues from the point it was interrupted.
I have attached a code I used in one of my projects. The timer will interrupt the main loop every 50ms (20 Hz), therfore you will have to put your own numbers in OCR1A and the pre-scaler. Please read more about timer interrupt in arduino so that you will understand how to do it (for example here: http://www.instructables.com/id/Arduino-Timer-Interrupts/step2/Structuring-Timer-Interrupts/). You can also see the example at the end of this page (http://playground.arduino.cc/Code/Timer1) for a more user friendly way of doing this.
setup() {
....
/* Set timer1 interrupt to 20Hz */
cli();//stop interrupts
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
OCR1A = 781; // approximately 20Hz
TCCR1B |= (1 << WGM12);// turn on CTC mode
TCCR1B |= (1 << CS12) | (1 << CS10); // 1024 presxaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei();//allow interrupts
}
...
ISR(TIMER1_COMPA_vect){
// pick next note
}
Related
I am using a Remote Control from FlySky. For my robotics project, I want to read PWM from the receiver on an Arduino. I came across 2 options:
pulseIn() arduino function
ISR(PCINTx_vect) (interrupt)
I cant use the first option of pulseIn() because I want my robot to continue with the operation if receiver signal are not coming (Tx not available etc.) So I used ISR.
Most reliable source : Mr. Brookings channel on YouTube.
Here is what I did (Only the required part for 1 axis):
// [R] where R is defined as 0 => [R] == [0]
volatile long CH[4]; //4 pwms to read so array of 4
float IN[3]={0,0,0}; // throttle is directly written
unsigned long timer[4],curr_time;
byte last[4];
void setup(){
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT0);
PCMSK0 |= (1 << PCINT1);
PCMSK0 |= (1 << PCINT2);
PCMSK0 |= (1 << PCINT3);
/* There is some more code here */
Serial.begin(115200);
}
void loop(){
/* There is some more code here */
IN[R] = ((CH[ROLL] - (1500 + R_TRIM))/11.0); // eg.: (1200 - (1500 + 8))/11.0 = -28 (interpreted as setpoint of -28° by the robot)
Serial.println(IN[R]);
}
ISR(PCINT0_vect){
curr_time = micros();
//channel 1 roll
if(PINB & B00000001){
if(last[ROLL] == 0){
last[ROLL] = 1;
timer[ROLL] = curr_time;
}
}
else if(last[ROLL] == 1){
last[ROLL] = 0;
CH[ROLL] = ((curr_time - timer[ROLL]));
}
}
I can read the PWM actually, but the robot keeps showing random twitches in its control at a given set point. I managed to trace the reason and found out that the PWM is insanely ridden by noise. Its not stable like it should be - steady. I have a MATLAB plot I used for analysis:
Signal (IN[R]):
Close up (when Tx stick was in the middle w/o movement) :
There are such spikes coming which is adding up to the control signal eventually making my robot to twitch. I tried some filtering techniques like 'moving average' and '1st and 2nd order exponential filters'. Also checked if it was due to power supplied to it - tried putting a capacitor or an iron core to the power lines but in vain. I can figure out how to remove them as their some constrains :
platform is Arduino Uno (slower in heavy computation)
Control loop shall not go below 100Hz (Currently its at 108Hz exponential filters on 4 axes took it to
~85Hz)
I would appreciate some guidance!
There's no way of telling from this if the input is noisy, or if your code is reading the PWM wrong, of if something else is going on, like external noise on the line, the Arduino's clock jitter, or other interrupts taking time. Also note that micros() on an Arduino Uno only has a resolution of 4µs, not 1µs.
You should check the input for jitter and noise, and try fast code that isn't influenced by other interrupts.
A fairly simple and fast way of getting the PWM pulse width is something like this, preferably without using anything else that uses interrupts:
volatile int pwmPulseWidth = 0;
volatile unsigned long int previousTime = 0;
void setup() {
attachInterrupt(0, rising, RISING);
}
void loop() {
// pwmPulseWidth is available here.
}
void rising() {
attachInterrupt(0, falling, FALLING);
previousTime = micros();
}
void falling() {
attachInterrupt(0, rising, RISING);
pwmPulseWidth = micros() - previousTime;
}
Untested, but it should give you an idea. This will return the width of the PWM pulse.
There are other ways of doing this, of course, like using a timer in capture mode.
Knowing the PWM frequency and the width of the PWM pulse is enough to reconstruct the PWM signal, should you want to.
Trying to understand timing / dimming and interrupts using an Arduino Uno (or any other AVR) is being made very difficult by a serious lack of example code. Having found a sketch that starts from zero and ramps up the brightness, I have tried to adapt the code to prevent the continuous loop which occurs when the 16-bit register overflows.
The attached sketch starts up from zero light output and increases over a period of time - currently using the delay() function.
Attempting to adapt the code to prevent the loop from starting the entire process again and to allow the led to remain at the "top" brightness output for x (variable) number of hours has proved to be most elusive. As one of the contributors have noted this area of coding is one of the most difficult to master.
Any advice or guidance which will put me in the right direction will be most appreciated.
...
//fade over 65535 steps
// 16 bit PWM on any pin
// Example uses built in LED on pin 13 (PORTB bit 5)
// https://forum.arduino.cc/index.php?topic=348170.0
void setup() {
pinMode(13, OUTPUT);
cli(); // Disable all interrupts
TCCR1A = 0; // Clear all flags in control register A
TCCR1B = 0; // Clear all flags in control register B
TCNT1 = 0; // Zero timer 1 count
OCR1A = 32768; // Preload compare match register (50% duty cycle)
// No prescaler
//TCCR1B |= _BV(CS12);
//TCCR1B |= _BV(CS11);
TCCR1B |= _BV(CS10);
TIMSK1 |= _BV(OCIE1A); // Enable timer compare interrupt
TIMSK1 |= _BV(TOIE1); // Enable timer overflow interrupt
sei(); // enable all interrupts
}
void loop() {
for (unsigned int x = 1; x < 65535; x++) {
//cli();
OCR1A = x;
//sei();
delay(20);
}
}
ISR(TIMER1_OVF_vect) { // Timer1 overflow interrupt service routine
PORTB |= _BV(PORTB5); // Turn LED (pin 13) on
}
ISR(TIMER1_COMPA_vect) { // Timer1 compare interrupt service routine
PORTB &= ~_BV(PORTB5); // Turn LED off
}
...
I want to make sure my code looks like working, since I don't have a lot of time with a signal generator tomorrow and I want to know how to set the sample rate.
I want to sample a 2kHz signal with a samplerate of 6kHz with a Arduino MEGA 2560.
It, doesn't have to be in real time, so i'm thinking of filling a buffer and then sending those over the serial connection.
Can anyone say if this code defenitly wouldn't work for this?
And how could i set the samplerate to 6kHz?
void setup() {
Serial.begin(9600);
}
void loop() {
for(int x = 0; x < 1000; x++){
// read the input on analog pin 0:
int sensorValue[x] = analogRead(A0);
}
for( x = 0; x < 1000; x++){
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage[x] = sensorValue[x] * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage[x]);
}
}
Thank you.
Well, as I've mentioned in another thread, you can use auto triggering mode of ADC (for UNO and ATMega328p based Arduinos):
void setup() {
Serial.begin(256000);
// ADC setup is done by arduino framework, but it's possible to change it slightly (for ATMega328) :
ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); // ADTS2..0 = 111, Timer 1 input capture event trigger source
ADCSRA |= _BV(ADATE); // enable auto trigger mode
ADCSRA |= _BV(ADIF); // reset conversion end flag (= interrupt flag)
// timer 1 setting:
TCCR1A = 0; // clear all
ICR1 = F_CPU/6000U; // 1 should be substracted here but result is about 2665.7 and it will be truncated to 2665
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // CTC mode with ICR1 as TOP value, enabled with no prescaling
TIMSK1 = _BV(ICF1); // not working without this... Flag must be cleaned up after the trigger ADC, otherwise it's stucked
analogRead(A0); // dummy read to set correct channel and to start auto trigger mode
pinMode(13, OUTPUT);
}
void loop() {
if (ADCSRA & _BV(ADIF)) {
ADCSRA |= _BV(ADIF); // reset flag by writing logic 1
Serial.println(ADC);
}
}
ISR(TIMER1_CAPT_vect) { // to clear flag
PINB = _BV(PB5); // and toggle d13 so frequency can be measured (it'd be half of real rate)
// it might be enabled on PWM pin too by setting force output compare and some compare register to half of value ICR1
}
This sketch uses baud rate 250000 but it's still too slow. The space character can be used as an separator, this'll save one character (as new line are usually two characters: \r\n). One value can be 1 to 4 characters long so for values:
0-9 - 3B you need baud rate 3*10*6000 = 180000
10-99 - 4B and you need baud rate 240000
and for the rest of cases you're too slow.
So the only way is sending those integers binary and without separator it'd be even better. The 2B per value results into minimal baud rate around 120000 baud/s.
Where do I look to find code that signifies that it uses interrupts? I've gone through wiring.c in the subfolder of Arduino, but it only leads to the function.
The issue is that when I enable CTC mode for Timer/Comp0, the LCD prints out complete jibberish, but when I disable CTC mode, it works perfectly fine.
Here is the timer initialization code:
void timerCompare0_ini(void){ // -Initialization of the Timer Compare 0
TCCR0A = 0; // This regulates the menu navigation arrow to show where user is pointing
TCCR0B = 0;
TCNT0 = 0;
OCR0B = 256;
// TCCR0A |= (1 << COM0B0) | (1 << COM0B1);
TCCR0A |= (1 << WGM01); // -CTC mode
TCCR0B |= ((1 << CS02) | (1 << CS00)); // -1024 prescaler
TIMSK0 |= (1 << OCIE0B); // -Enable timer compare interrupt
}
wiring.c is good place. delay() uses micros() which depends on running timer0 and overflow interrupt. delayMicroseconds() does internal pause based on instructions. BTW But both method has bug because when watchdog is set shorter than requested delay it reboots CPU. I.e. you need implement own functions.
I am using Arduino Mega2560, i have used timer2 as interrupt after 2ms on flag overflow, but somehow it works only once. I have used serial monitoring as you can also see and this tells me that timer interrupt is invoked but then interrupt is not invoked again and program control does not go back in the loop also since it displays "13" and "22" only once. At least it should have displayed "22" continously after timer interrupt was called.Can anybody tell me why timer interrupt does not get invoke again and why it does not return to loop after interrupt.?
#include <avr/io.h>
#include <avr/interrupt.h>
int eraser=0; //0=000
ISR(TIMER2_OVF_vect)
{
TIMSK2=0x00;
TCCR2B=0x00;
TCCR2A=0x00;
TCNT2=0;
TIFR2=0x00;
Serial.println(33);
TCNT2= 131; //reset timer count to 125 out of 255.
TCCR2B=0x6; //using a prescaler of 6 to use divisor 256.
TIMSK2=1; //timer2 Interrupt Mask Register. Set TOIE(Timer Overflow Interrupt Enable).
}
void setup()
{
/*Timer0 would not be used as it is used for other functions.
Timer 5(pin D47) for encoder and interrupt 2(pin 21) for encoder.
Timer2 is 8 bit we'll see if it can be used for time interval between two encoder counts.
This leaves us with Timers 1,3,4,5.
Timer 3(5,3,2) and 4(8,7,6) will be used for motors.*/
Serial.begin(9600);
TCCR2B&=eraser;
TCNT2= 131; //reset timer count to 125 out of 255.
TIFR2= 0x00; //timer2 interrupt flag register: Clear Timer Overflow Flag.
TIMSK2=0x01; //timer2 Interrupt Mask Register. Set TOIE(Timer Overflow Interrupt Enable).
TCCR2B=0x6; //using a prescaler of 6 to use divisor 256.
/*
so it takes 62.5ns per tick in a timer and having set
divisor of 256 so it will take 16usecs per inc and setting
timer2 to 125 would make it count 250 times so
time=62.5nsec * 256 * 125=2msec.
clkTn
TCNTn Timer Counter Register
TCCRnA has LSB of WGM(wave generation mode bits)
TCCRnB Timer Counter Control Register: has LSB of CSn2,CSn1,CSn0.*/
}
void loop()
{
Serial.println(1);
while(1)
{
Serial.println(22);
}
}
Have you tried taking the
ISR(TIMER2_OVF_vect)
{
TIMSK2=0x00;
TCCR2B=0x00;
TCCR2A=0x00;
TCNT2=0;
TIFR2=0x00;
Serial.println(33);
TCNT2= 131; //reset timer count to 125 out of 255.
TCCR2B=0x6; //using a prescaler of 6 to use divisor 256.
TIMSK2=1; //timer2 Interrupt Mask Register. Set TOIE(Timer Overflow Interrupt Enable).
}
block and putting some or all of it into the place where you'd like to start the timer again?
setup only runs once, so my guess is you need to start the timer again.