Show Posts
|
|
Pages: 1 [2] 3 4 5
|
|
16
|
Forum 2005-2010 (read only) / Syntax & Programs / Re: Interrupts...
|
on: May 27, 2009, 03:56:42 am
|
Ideally, I will need to disable all external interrupts and all timers except the one I'll be using... If I'm understanding your goals right, you won't need to disable the other timers. While in an interrupt, no other interrupts can run, so nothing can interfere with your 1000Hz timer. As far as generating a 1000Hz square wave, though, you could probably get that just by manually configuring the PWM frequency. That way, it would be set and forget, and you would have 100% of your processor speed left over to do other things. Also, beware of trying to do an analogRead() inside an interrupt-- it takes a long time to run, and so you risk locking up your program.
|
|
|
|
|
17
|
Forum 2005-2010 (read only) / Syntax & Programs / Re: using the Timer1 library for PCM audio output
|
on: May 26, 2009, 03:22:39 pm
|
Okay, I did get it working by manually configuring timer1. Changes in capacitance are reflected as a change in frequency in the speaker output. It's definitely quick and dirty, but fwiw here's the code (I'm using Paul Badger's capacitance sensing code here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1171076259): int i; unsigned int x, y; float accum, fval = .20; // these are variables for a simple low-pass (smoothing) filter - fval of 1 = no filter - .001 = max filter long fout;
int period = 160;
char sineovertones[] = {0,84,106,99,113,127,107,79,71,51,1,-26,7,43,23,-17,-26,-23,-43,-60,-49,-44,-66,-63,0,63,66,44,49,60,43,23,26,17,-23,-43,-8,26,-1,-51,-71,-79,-107,-127,-113,-99,-106,-84};
char sine[] = {0, 22, 44, 64, 82, 98, 111, 120, 126, 128, 126, 120, 111, 98, 82, 64, 44, 22, 0, -22, -44, -64, -82, -98, -111, -120, -126, -128, -126, -120, -111, -98, -82, -64, -44, -22}; //a simple sine wave with 36 samples
int analog0 = 160; //variable used to store analog input pin 0
byte speakerpin = 3; //audio playback on pin 3. This can also be set to pin 11. volatile byte waveindex = 0; //index variable for position in waveform array Sine[] volatile byte currentvalue = 0;
void setup() { Serial.begin(57600); /************************** PWM audio configuration ****************************/ // Configures PWM on pins 3 and 11 to run at maximum speed, rather than the default // 500Hz, which is useless for audio output
pinMode(3,OUTPUT); //Speaker on pin 3
cli(); //disable interrupts while registers are configured
bitSet(TCCR2A, WGM20); bitSet(TCCR2A, WGM21); //set Timer2 to fast PWM mode (doubles PWM frequency)
bitSet(TCCR2B, CS20); bitClear(TCCR2B, CS21); bitClear(TCCR2B, CS22); /* set prescaler to /1 (no prescaling). The timer will overflow every * 62.5nS * 256ticks = 16uS, giving a PWM frequency of 62,500Hz, I think. */
sei(); //enable interrupts now that registers have been set /************************* Timer 1 interrupt configuration *************************/
cli(); //disable interrupts while registers are configured
bitClear(TCCR1A, COM1A1); bitClear(TCCR1A, COM1A1); bitClear(TCCR1A, COM1A1); bitClear(TCCR1A, COM1A1); /* Normal port operation, pins disconnected from timer operation (breaking pwm). * Should be set this way by default, anyway. */
bitClear(TCCR1A, WGM10); bitClear(TCCR1A, WGM11); bitSet(TCCR1B, WGM12); bitClear(TCCR1B, WGM13); /* Mode 4, CTC with TOP set by register OCR1A. Allows us to set variable timing for * the interrupt by writing new values to OCR1A. */
bitClear(TCCR1B, CS10); bitSet(TCCR1B, CS11); bitClear(TCCR1B, CS12); /* set the clock prescaler to /8. Since the processor ticks every 62.5ns, the timer * will increment every .5uS. Timer 1 is a 16-bit timer, so the maximum value is 65536, * Giving us a theoretical range of .5us-32.7mS. There are 48 samples, so the * theoretical frequency range is 41.7KHz - .635Hz, which neatly covers the audio * spectrum of 20KHz-20Hz. Theoretical, because I wouldn't recommend actually calling * the Timer1 interrupt every .5uS :) */
bitClear(TCCR1C, FOC1A); bitClear(TCCR1C, FOC1B); /* Disable Force Output Compare for Channels A and B, whatever that is. * Should be set this way by default anyway. */
OCR1A = 160; /* Initializes Output Compare Register A at 160, so a match will be generated every * 62.5nS * 8 * 160 = 80uS, for a 1/(80uS*48) = 260Hz tone. */
bitClear(TIMSK1, ICIE1); //disable input capture interrupt bitClear(TIMSK1, OCIE1B); //disable Output Compare B Match Interrupt bitSet(TIMSK1, OCIE1A); //enable Output Compare A Match Interrupt bitClear(TIMSK1, TOIE1); //disable Overflow Interrupt Enable
sei(); //enable interrupts now that registers have been set
delay(1000);
}//end setup() ISR(TIMER1_COMPA_vect) { /* timer1 ISR. Every time it is called it sets * speakerpin to the next value in Sine[]. frequency modulation is done by changing * the timing between successive calls of this function, e.g. for a 1KHz tone, * set the timing so that it runs through Sine[] 1000 times a second. */
if (waveindex > 47) { //reset waveindex if it has reached the end of the array waveindex = 0; }
analogWrite(speakerpin, sineovertones[waveindex] + 128); waveindex++;
OCR1A = period;
} //end Timer1 ISR void loop() { //analog0 = analogRead(0) + 4; //Serial.println(analog0); y = 0; // clear out variables x = 0;
for (i=0; i < 4 ; i++ ){ // do it four times to build up an average - not really neccessary but takes out some jitter
// LOW-to-HIGH transition digitalWrite(8, HIGH); // output pin is PortB0 (Arduino 8), sensor pin is PortB1 (Arduinio 9)
while ((PINB & B100) != B100 ) { // while the sense pin is not high // while (digitalRead(9) != 1) // same as above port manipulation above - only 20 times slower! x++; } delay(1);
// HIGH-to-LOW transition digitalWrite(8, LOW); while((PINB & B100) != 0 ){ // while pin is not low -- same as below only 20 times faster // while(digitalRead(9) != 0 ) // same as above port manipulation - only 20 times slower! y++; }
delay(1); }
fout = (fval * (float)x) + ((1-fval) * accum); // Easy smoothing filter "fval" determines amount of new data in fout accum = fout; period = constrain(fout / 3, 50, 500); Serial.println(period);
}//end loop()
Perhaps whoever maintains the timer1 library should take a gander at why you can't change timer frequency dynamically?
|
|
|
|
|
18
|
Forum 2005-2010 (read only) / Syntax & Programs / Re: using the Timer1 library for PCM audio output
|
on: May 25, 2009, 03:09:10 am
|
Sorry, example code here: #include "TimerOne.h"
char sineovertones[] = {0,84,106,99,113,127,107,79,71,51,1,-26,7,43,23,-17,-26,-23,-43,-60,-49,-44,-66,-63,0,63,66,44,49,60,43,23,26,17,-23,-43,-8,26,-1,-51,-71,-79,-107,-127,-113,-99,-106,-84};
char sine[] = {0, 22, 44, 64, 82, 98, 111, 120, 126, 128, 126, 120, 111, 98, 82, 64, 44, 22, 0, -22, -44, -64, -82, -98, -111, -120, -126, -128, -126, -120, -111, -98, -82, -64, -44, -22}; //a simple sine wave with 36 samples
long analog0 = 160; //variable to store input from analog input pin 0
byte speakerpin = 3; //audio playback on pin 3. This can also be set to pin 11. volatile byte waveindex = 0; //index variable for position in waveform array Sine[] volatile byte currentvalue = 0;
void setup() { Serial.begin(57600); /************************** PWM audio configuration ****************************/ // Configures PWM on pins 3 and 11 to run at maximum speed, rather than the default // 500Hz, which is useless for audio output
pinMode(speakerpin,OUTPUT); //Speaker on pin 3
cli(); //disable interrupts while registers are configured
bitSet(TCCR2A, WGM20); bitSet(TCCR2A, WGM21); //set Timer2 to fast PWM mode (doubles PWM frequency)
bitSet(TCCR2B, CS20); bitClear(TCCR2B, CS21); bitClear(TCCR2B, CS22); /* set prescaler to /1 (no prescaling). The timer will overflow every * 62.5nS * 256ticks = 16uS, giving a frequency of 62,500Hz, I think. */
sei(); //enable interrupts now that registers have been set /************************* Timer 1 interrupt configuration *************************/
Timer1.initialize(160); // initialize timer1, and set a 1/2 second period Timer1.attachInterrupt(playtone); // attaches playtone() as a timer overflow interrupt } void playtone() { /* This function is called by the timer1 interrupt. Every time it is called it sets * speakerpin to the next value in Sine[]. frequency modulation is done by changing * the timing between successive calls of this function, e.g. for a 1KHz tone, * set the timing so that it runs through Sine[] 1000 times a second. */ if (waveindex > 47) { waveindex = 0; }
analogWrite(speakerpin, sineovertones[waveindex] + 128); waveindex++; } void loop() {
analog0 = analogRead(0) / 2; Serial.println(analog0); delay(100); Timer1.setPeriod(analog0); }
|
|
|
|
|
19
|
Forum 2005-2010 (read only) / Syntax & Programs / using the Timer1 library for PCM audio output
|
on: May 25, 2009, 03:02:59 am
|
|
Hi All, I'm trying to generate audio from the Arduino, using Pulse Code Modulation.
If you're not familiar with this technique, I take a waveform (let's say a sine wave), divide it up into 48 samples, and encode it's level at that point into a char array. Then I set up an interrupt on timer1 so that every time it fires, it sets one of the timer2 pwm pins the current sample point, then increments the array index. I've changed the prescaler on timer2 so that the pwm frequency is 62,500 Hz, so once I put the output through a low-pass filter it sounds...surprisingly decent, actually.
So this all works as expected with a static frequency. On my oscilloscope, I can see the waveform that I've encoded, and I can hear it on my speaker.
Now, I should be able to vary the frequency by changing how quickly the timer1 interrupt is called, i.e. how quickly it steps through the array of samples. I'm currently using the Timer1 library in the Arduino playground, but when I try to vary the interrupt period, I get squat-- no output at all.
Has anyone used this library with varying interrupt frequencies? Or had success generating sound with this method? I'd like to use the library because manually configuring timers makes me :'(
|
|
|
|
|
20
|
Forum 2005-2010 (read only) / Syntax & Programs / Re: newbie, need critique/suggestions for clock code
|
on: May 25, 2009, 03:05:38 pm
|
|
If you're interested in doing more advanced projects with the Arduino, I think it will repay you many times over to learn how to configure a timer and its associated interrupt(s) manually. It's a real pain to figure out the first time, but get ye to the datasheet, I say.
You could use Timer1 in CTC mode with a /256 prescaler. Then the timer will tick 62,500 times per second, so you set your output compare register to 62500, and write the interrupt to increment your seconds variable every time it fires. Voila, you have a subroutine that will run exactly every 1 second, independent of how much you're loading down your processor with your main program routine.
|
|
|
|
|
23
|
Forum 2005-2010 (read only) / Development / Re: Using own interrupt handlers: Collision of ISRs
|
on: March 03, 2009, 10:10:41 pm
|
|
Hello, I am working on the same project, in a similar plan of attack as you, Zeb.
My serial receive operations are giving me junk at times, and I think it is because of other interrupts disrupting the serial read at critical moments. So I thought I could put my time sensitive code inside the USART_RX_vect ISR, which would not be interrupted by other interrupts while running.
When compiling, I get a similar error message:
C:\Users\Max\AppData\Local\Temp\build3450.tmp\wiring_serial.c.o: In function `__vector_18':
C:\Program Files (x86)\arduino-0013-win\arduino-0013\hardware\cores\arduino/wiring_serial.c:112: multiple definition of `__vector_18'
o:C:\Users\Max\AppData\Local\Temp\build3450.tmp/Temporary_6403_5722.cpp:42: first defined here Couldn't determine program size: C:\Program Files (x86)\arduino-0013-win\arduino-0013\hardware/tools/avr/bin/avr-size: 'C:\Users\Max\AppData\Local\Temp\build3450.tmp\receiver_rev3.hex': No such file
I tried moving the ISR code over to blink.pde as suggested by mem, but got the same error message. Has anyone had any luck setting up a custom serial receive ISR?
Thanks, Max
|
|
|
|
|
24
|
Forum 2005-2010 (read only) / Troubleshooting / Re: Duemilanove Vista Driver Problems
|
on: May 10, 2009, 01:34:02 am
|
|
I'm having problems like this in my Vista Home Premium x64 install. The ports work fine in Ubuntu, so it's not a physical hardware issue.
Does anyone know where I could get a copy of the 2.04.06 drivers? I'd like to try installing them instead of the latest 2.04.16 version, even though it says it's digitally signed.
|
|
|
|
|
25
|
Forum 2005-2010 (read only) / Troubleshooting / Re: arduino pro : usb + external power supply
|
on: June 10, 2009, 08:22:18 pm
|
|
I'm putting together finishing touches on a project with the Arduino pro and an external 9V power supply. I'd like to be able to connect the USB cable after I've soldered the power supply connection in and do software uploads. Will that damage the board (5V + 9V > 12V) or is the power select module smart enough to power off one source, but not both?
It's the 16Mhz/5V version, btw.
|
|
|
|
|
27
|
Forum 2005-2010 (read only) / Interfacing / Receive DMX-512 with an Arduino!
|
on: March 20, 2009, 02:50:17 pm
|
Hi All, I've written a sketch that allows you to receive DMX-512 with an Arduino. If you're not familiar with it, DMX is the control protocol for theatrical/live event lighting. It's been possible to send DMX with an arduino for quite awhile, but this will allow you to receive DMX values and execute your custom code accordingly. The code is quite long, and there's some hardware setup and malarkey with the wiring_serial.c file involved, so I'm just going to give you the link: http://blog.wingedvictorydesign.com/2009/03/20/receive-dmx-512-with-an-arduino/It's my first project with the Arduino, and I worked really hard on it, so please let me know if it works for you and how you like it. It has the following features: - Capable of receiving 8 contiguous addresses from 1 to 505.
- Works with controllers that send less than the full 512 address DMX frame.
- Break detection is done correctly by detecting a Low value of >88[ch956]S per ANSI E1.11-2008, rather than the frame error hack used by many devices.
- Uses interrupt-based algorithms to eliminate processor-load related timing problems. It may be of general interest to those in this forum who are trying to get interrup-based sketches off the ground
- If the DMX data signal is lost, the Arduino will maintain the current state until new values are received.
- The reception and user code run sequentially rather than at the same time, so they won't interfere with each others timing.
And the following limitations, as of now: - Atmega168 based processors only (you will have to rename the registers and interrupt vectors if you want to use it for another processor).
- Currently only tested with a USB-DMX Pro controlled via Lightfactory and MagicQ software, since that's what I could get my hands on. But it handles a variety of frame rates, break lengths, etc. just fine. If you're in the Portland area, and you'd like to let me test it with other controllers, drop me a line. Also, if you use it with other controllers, let me know how it turned out.
- Because I needed access to exact timing, I had to use the Timer2 functionality, so pins 3 and 11 cannot be used for PWM.
- Will not detect bad addressing, such as “dmxaddress=510;” or ”dmxaddress=50;” when only 55 slots are sent by the controller.
I'm still developing and improving the software, so check the link above for the latest version. Max
|
|
|
|
|
29
|
Forum 2005-2010 (read only) / Interfacing / Re: interrupt-driven USART reception: I'm stumped.
|
on: March 11, 2009, 08:21:45 pm
|
Does this means that when i need serial in version 0013 i need to manually to modify wiring_serial.c back again to the original? It depends on whether you need Serial.flush(), which as near as I can tell is the only thing this change to wiring_serial.c would disable. I started this thread in hopes that someone would chime in with a better work-around, but at the moment this is the only one I've got. yes it works Interestingly, this code doesn't work with my controller (an Enttec USB-DMX Pro with Lightfactory software), even when I compile it in the 0012 version of the software, with an unmodified wiring_serial.c What controller(s) have you used it with? I'm currently trying to write some software that will work with my (or any) controller, so I'm interested to know. Thanks, Max
|
|
|
|
|
30
|
Forum 2005-2010 (read only) / Interfacing / Re: interrupt-driven USART reception: I'm stumped.
|
on: March 10, 2009, 10:49:51 pm
|
Well, it compiles fine on my computer, with my altered wiring_serial.c Once I load it onto the Arduino, it doesn't do anything, though -- from poking at it it looks like it's not setting the values in DMX[] to anything, they just stay at their default value of 0. I haven't gone through it in detail, though. Have you managed to receive DMX correctly with this code, under any version of the Arduino library? If you want to compile it, here's what I did to wiring_serial.c (towards the end, there's a part that's commented out): /* wiring_serial.c - serial functions. Part of Arduino - http://www.arduino.cc/
Copyright (c) 2005-2006 David A. Mellis
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
$Id: wiring.c 248 2007-02-03 15:36:30Z mellis $
Modified on March 1, 2009 by Norbert Pozar */
#include "wiring_private.h"
// Define constants and variables for buffering incoming serial data. We're // using a ring buffer (I think), in which rx_buffer_head is the index of the // location to which to write the next incoming character and rx_buffer_tail // is the index of the location from which to read.
// use only powers of 2 for the buffer size to produce optimal code // 8, 16, 32, 64, 128 or 256
// input buffer #define RX_BUFFER_SIZE 128 // output buffer // set to 0 to disable the output buffer altogether (saves space) #define TX_BUFFER_SIZE 32
unsigned char rx_buffer[RX_BUFFER_SIZE];
unsigned char rx_buffer_head = 0; unsigned char rx_buffer_tail = 0;
void beginSerial(long baud) {
#if defined(__AVR_ATmega8__) UBRRH = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8; UBRRL = ((F_CPU / 16 + baud / 2) / baud - 1); // enable rx and tx sbi(UCSRB, RXEN); sbi(UCSRB, TXEN); // enable interrupt on complete reception of a byte sbi(UCSRB, RXCIE); #else UBRR0H = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8; UBRR0L = ((F_CPU / 16 + baud / 2) / baud - 1); // enable rx and tx sbi(UCSR0B, RXEN0); sbi(UCSR0B, TXEN0); // enable interrupt on complete reception of a byte sbi(UCSR0B, RXCIE0); #endif
// defaults to 8-bit, no parity, 1 stop bit }
// advanced function, saves ~200 bytes because it doesn't use the long division // prescaler for the baud rate can be found using the formula // prescaler = fClock / (16 * baud rate) - 1 // see beginSerial void beginSerialWithPrescaler(unsigned int prescaler) {
#if defined(__AVR_ATmega8__) UBRRH = prescaler >> 8; UBRRL = prescaler; // enable rx and tx sbi(UCSRB, RXEN); sbi(UCSRB, TXEN); // enable interrupt on complete reception of a byte sbi(UCSRB, RXCIE); #else UBRR0H = prescaler >> 8; UBRR0L = prescaler; // enable rx and tx sbi(UCSR0B, RXEN0); sbi(UCSR0B, TXEN0); // enable interrupt on complete reception of a byte sbi(UCSR0B, RXCIE0); #endif
// defaults to 8-bit, no parity, 1 stop bit }
int serialAvailable() { unsigned char i = RX_BUFFER_SIZE + rx_buffer_head - rx_buffer_tail; i %= RX_BUFFER_SIZE;
return i; }
int serialRead() { // if the head isn't ahead of the tail, we don't have any characters if (rx_buffer_head == rx_buffer_tail) { return -1; } else { unsigned char c = rx_buffer[rx_buffer_tail]; rx_buffer_tail = rx_buffer_tail + 1; rx_buffer_tail %= RX_BUFFER_SIZE; return c; } }
void serialFlush() { // don't reverse this or there may be problems if the RX interrupt // occurs after reading the value of rx_buffer_head but before writing // the value to rx_buffer_tail; the previous value of rx_buffer_head // may be written to rx_buffer_tail, making it appear as if the buffer // were full, not empty. rx_buffer_head = rx_buffer_tail; }
/* #if defined(__AVR_ATmega8__) *********THIS PART COMMENTED OUT********* SIGNAL(SIG_UART_RECV) #else SIGNAL(USART_RX_vect) #endif { #if defined(__AVR_ATmega8__) unsigned char c = UDR; #else unsigned char c = UDR0; #endif
unsigned char i = rx_buffer_head + 1; i %= RX_BUFFER_SIZE;
// if we should be storing the received character into the location // just before the tail (meaning that the head would advance to the // current location of the tail), we're about to overflow the buffer // and so we don't write the character or advance the head. if (i != rx_buffer_tail) { rx_buffer[rx_buffer_head] = c; rx_buffer_head = i; } } */
// buffered output #if (TX_BUFFER_SIZE > 0 )
// TX is buffered // ************************ // tested only for ATmega168 // ************************
unsigned char tx_buffer[TX_BUFFER_SIZE];
unsigned char tx_buffer_head = 0; volatile unsigned char tx_buffer_tail = 0;
// interrupt called on Data Register Empty #if defined(__AVR_ATmega8__) SIGNAL(SIG_UART_DATA) #else SIGNAL(USART_UDRE_vect) #endif { // temporary tx_buffer_tail // (to optimize for volatile, there are no interrupts inside an interrupt routine) unsigned char tail = tx_buffer_tail;
// get a byte from the buffer unsigned char c = tx_buffer[tail]; // send the byte #if defined(__AVR_ATmega8__) UDR = c; #else UDR0 = c; #endif
// update tail position tail ++; tail %= TX_BUFFER_SIZE;
// if the buffer is empty, disable the interrupt if (tail == tx_buffer_head) { #if defined(__AVR_ATmega8__) UCSRB &= ~(1 << UDRIE); #else UCSR0B &= ~(1 << UDRIE0); #endif
}
tx_buffer_tail = tail;
}
void serialWrite(unsigned char c) {
#if defined(__AVR_ATmega8__) if ((!(UCSRA & (1 << UDRE))) #else if ((!(UCSR0A & (1 << UDRE0))) #endif || (tx_buffer_head != tx_buffer_tail)) { // maybe checking if buffer is empty is not necessary, // not sure if there can be a state when the data register empty flag is set // and read here without the interrupt being executed // well, it shouldn't happen, right?
// data register is not empty, use the buffer unsigned char newhead = tx_buffer_head + 1; newhead %= TX_BUFFER_SIZE;
// wait until there's a space in the buffer while (newhead == tx_buffer_tail) ;
tx_buffer[tx_buffer_head] = c; tx_buffer_head = newhead;
// enable the Data Register Empty Interrupt #if defined(__AVR_ATmega8__) UCSRB |= (1 << UDRIE); #else UCSR0B |= (1 << UDRIE0); #endif
} else { // no need to wait #if defined(__AVR_ATmega8__) UDR = c; #else UDR0 = c; #endif } }
#else
// unbuffered write void serialWrite(unsigned char c) { #if defined(__AVR_ATmega8__) while (!(UCSRA & (1 << UDRE))) ;
UDR = c; #else while (!(UCSR0A & (1 << UDRE0))) ;
UDR0 = c; #endif }
#endif
|
|
|
|
|