My balancebot design is ending up with 3 Arduinos for throughput reasons. One for a balance loop (1 KHz) and one for each wheel's motor current/encoder loop (10 KHz). What's an easy way for the balance loop processor to quickly send the commands to the motor processors ?
The three Diecimila's serial ports are tied up going to three instances of the IDE and three instances of the plotting program (and as you might imagine that gets confusing). They currently don't talk to each other.
I2C, SPI, analogWrite -> analogRead, three 1280s ????
I'm really confused, why do you need 3 arduinos again?
This all should be possible with... well 1, MAYBE. Depends on the size of the sketch and everything. If not, you can look into getting the Mega, offers alot more space.. in out pins.. blah blah.
But yes.. I think your approach would get very confusing, very quick!
Have you looked into the Blink without Delay? You can run complete sketches without delaying once, will open up alot of possibilities, enabling you to process multiple processes nearly at the same time.
I2C - max ~800kHz
ADC - max ~1.4MHz (143k samples/s)
Serial - max 2MHz
SPI - max 8MHz
So SPI is by far the fastest alternative . The Mega offers no general speed advantage compared to a Duemilanove (same instructuon set, same clock frequency).
Thanks, this is the information I was looking for. SPI at 8MHz. Why is SPI 10x faster than I2C ?
I tried running on a single '328 but with ISRs firing on 4 edges of two encoders my main loop never gets a chance to run. Give me a 60MHz XMEGA or ARM with the Arduino API and I wouldn't be asking this question.
but with ISRs firing on 4 edges of two encoders my main loop never gets a chance to run
How about "prescaling" those encoders? If you don't need that much precision, a little simple logic (perhaps done using a small PLD) could reduce the number of interrupts. If you're shipping the processed info to another CPU, presumably you could tolerate building an external up/down counter that you could read periodically, and would only interrupt the CPU when it overflowed/underflowed.
I'm after accurate velocity for my motor loop. Not so much position. I've been playing with the code below that tries to filter timestamps on encoder transitions to estimate rate. The up/down counter works OK but not many count ticks when the balance bot is at null. Reading TCLK1 for time doesn't seem to work for unknown reasons.
Could the functionality in the code be converted to Verilog and put in a CPLD ? Maybe then the timestamp and filter could be done at higher rates with an encoder with more lines.
Bruce
//#include <wiring.h>
#define encoder0PinA 2
#define encoder0PinB 3
volatile unsigned int encoder0Pos = 0;
int t;
volatile uint16_t timer_ticks;
// velocity measurement variables
volatile int16_t enc_period_L_prev;
volatile int16_t enc_period_R_prev;
volatile int16_t enc_period_L;
volatile int16_t enc_period_R;
volatile int16_t enc_speed_R;
volatile int16_t enc_speed_L;
void setup() {
pinMode(encoder0PinA, INPUT);
pinMode(encoder0PinB, INPUT);
// encoder pin on interrupt 0 (pin 2)
attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
attachInterrupt(1, doEncoderB, CHANGE);
Serial.begin (9600);
}
void loop(){
t=0;
}
void doEncoderA(){
uint16_t tmr = micros();//get_timer_ticks();
// this calculates a running average to filter alignment noise
enc_period_R = (enc_period_R * 3 + (tmr - enc_period_R_prev)) >> 2;
enc_period_R_prev = tmr;
// look for a low-to-high on channel A
if (digitalRead(encoder0PinA) == HIGH) {
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == LOW) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
else // must be a high-to-low edge on channel A
{
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == HIGH) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
Serial.print (encoder0Pos, DEC);
Serial.print(" ");
Serial.println(enc_period_R,DEC);
// use for debugging - remember to comment out
}
void doEncoderB(){
// look for a low-to-high on channel B
if (digitalRead(encoder0PinB) == HIGH) {
// check channel A to see which way encoder is turning
if (digitalRead(encoder0PinA) == HIGH) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
// Look for a high-to-low on channel B
else {
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinA) == LOW) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
}
uint16_t
get_timer_ticks()
{
return timer_ticks + (TCNT1 >> 5); // add current count to overflow
}