Skip to main content

Messages

This section allows you to view all Messages made by this member. Note that you can only see Messages made in areas you currently have access to.

Messages - jbeale

46
General discussion / Re: another (cheap?) PCB fab: Hackvana
The scanner isn't good if you've got big caps, or heatsinks, but the tallest protrusion on that board was about 5 mm so focus was still pretty good.  I don't think my scanner was unique that way; depth of field is much better than, say a microscope.

Note: re, Hackvana, for some reason their description of PCB capabilities is in a Google doc, but not linked in any obvious way from their web page. Here it is: http://tinyurl.com/hvpcbfaq
47
General discussion / Re: another (cheap?) PCB fab: Hackvana
Thanks for the reports and the photo, very helpful!  By the way, if you want a good image of a bare board, I have found a desktop scanner often does a better job than even a very good camera.

For example, here is an image of a "Zipit Z2" (pocket ARM-Linux system) showing a lot of detail, using a mid-range desktop scanner.  Image size is 3181 x 2692 pixels and that is actually 50% of the original. I doubt I could ever have done as well with my DSLR camera.  (Note: this pcb was not produced by Hackvana) http://bealecorner.org/best/measure/z2/z2a-main.jpg
48
General discussion / another (cheap?) PCB fab: Hackvana
Has anyone used "Hackvana" for PCB fab? I just came across them from a posting on the KiCad Yahoo user group.  They don't have prices online up front, but I gather that order totals under $50 are possible, and they mention capabilities the cheapest places don't provide (eg. non-plated through holes, interior cutouts, and V-scoring).  Below from http://http://www.hackvana.com/store/

Quote
Hello from Mitch and Tully! We're two hackers from Australia and the USA who have moved to Shenzhen China. Why Shenzhen? Because Shenzhen has the world's largest electronics market! Here's what we can do:

    Make PCBs to your specifications.
    Give you a quote on the parts for your latest project.

Parts that we quote on are added to the store, so you can order them there if you like the quote.
49
Project logs / Re: Electrical Model of a Human Body?
direct contact to a human body is probably somewhere around 150 pF, it varies depending on how closely coupled the circuit and the body are to a common ground.

I think you can solder to zinc but watch out if it gets too hot, there is a potential for toxic fumes.

Just today I was testing the capacitive sensing on a Teensy 3, which supposedly gives you 1 count = 0.05 pF resolution. Open was about 400 counts and touched was about 3000, so 2600*.05 = 130 pF. Not too far off.  It varied depending on how hard I pressed against the PCB, it wasn't a simple make/break contact. (Water inside the body is conductive, but dry skin is more like an insulator). It is also proportional to your own surface area, other things being equal, so a really big heavy person should show a larger capacitance.
51
General discussion / Re: sample code for LTC2440 24-bit ADC
Just an observation, it looks like the last two bits of the 32-bit output word from the LTC2440 are always fixed, despite Table 2 in the data sheet saying otherwise.  I sent the below note to Linear Tech support, we'll see what they say. I can't really complain, since the 200 nV noise level of the part matches what they claim.

Quote
I am evaluating the LTC2440 with SDI held high, for the 6.9 Hz output data rate. I do see the specified 200 nV (rms) noise performance which the datasheet claims, so the part appears to be working as designed. I notice that b4, b3,and b2 fluctuate, but two least significant bits in the 32-bit output word are always fixed. Bit 1 is always low, and bit 0 is always high.  This is true both with internal SCK (pin 10 held high), and with external SCK. Is this expected? Table 2 on p. 12 of the datasheet shows bit 0 can be high or low depending on the input, and also says the "sub LSBs" below bit 5 can be included in averaging, and in "ultrahigh resolution modes... sub LSBs... represent useful information".
52
General discussion / Another try using LTC2440 24-bit ADC
Success! I finally got the datasheet-specified noise performance from the LTC2440 ADC. I made a new circuit with more attention to very low-impedance grounds, and I built it SIP-style with the digital side having pins, and the analog portion on its own ground plane without directly contacting the breadboard.  My signal is as before, two 1k resistors forming a divider from +Vref to -Vref (GND) and a 10 ohm resistor between +IN and -IN to give me a signal of about (5V/2.01k)*10 ohms =  25 mV with a common-mode voltage of 2.5 V. This circuit shows 198 nV (rms) noise, right at the spec level (200 nV) for the 6.9 Hz output rate.  You can see after turning on the circuit, my 24.948 mV signal drifts down by less than one microvolt (!) and then levels off, over the course of 45 minutes. That is < 40 ppm stability. 

What would I expect the drift to be? I assume the dominant error is thermal drift of the 1k resistors. Taking a wild guess, say a 1/4 watt resistor is 400 C/W, and at 2.5 mA current each 1k resistor dissipates I^2*R = 6.25 mW, so dT = 2.5 C temperature rise.  I think these parts are spec'd to be 100 ppm/C, so looks like they're at least 4x better than their spec limit- not too bad.

The earlier prototype (first few posts in this thread) used the adaptor board DIP-style with the analog connections on the solderless breadboard, and it never got below 250 nV (rms) with semi-periodic sudden signal jumps of about 1 uV. I believe that was related to the mechanical pressure contacts making the wire connections inside the breadboard; they are not suited to clean signals at the microvolt level.

[attachment=1]
Photo of new circuit on adaptor board: [attachment=0]
chip on adaptor board: https://picasaweb.google.com/1099282360 ... 0911690210
adaptor board on breadboard: https://picasaweb.google.com/1099282360 ... 7608211970

The code is slightly different, this time I used the BUSY;READY/ signal from the chip to read out the ADC the moment it is done converting, rather than hard-coding the delay using the Arduino's clock.

Example Code for LTC2440:
Code: [Select]
/*
  Interface between Arduino DM board and Linear Tech LTC2440 24-bit ADC
  Oct. 24 2012 John Beale

  LTC2440  <---------->  Arduino
  10: /EXT : ground (use external serial clock)
  11: /CS <- to digital pin 10  (SS pin)
  12: MISO -> to digital pin 12 (MISO pin)
  13: SCLK <- to digital pin 13 (SCK pin)
  15: BUSY -> to digital pin 9 (low when result ready)

  1,8,9,16: GND :  all grounds must be connected
  2: Vcc :  +5V supply
  3: REF+ :  +5V reference
  4: REF- :  GND
  5: IN+  :  Input+
  6: IN-  :  Input-
  7: SDI  : +5V  (select 6.9 Hz output rate, or GND for 880 Hz rate)
  14: Fo : GND  (select internal 9 MHz oscillator)

*/

#include <SPI.h>  // include the SPI library
#include <digitalWriteFast.h> // from http://code.google.com/p/digitalwritefast/downloads/list
// I had to update the WriteFast library for Arduino 1.0.1: replace "#include wiring.h" and "#include WProgram.h"
//  with "#include <Arduino.h>"  in libraries/digitalwritefast/digitalWriteFast.h

#define VREF (5.0)    // ADC voltage reference
#define PWAIT (198)  // milliseconds delay between readings
#define SLAVESELECT 10  // digital pin 10 for CS/
#define BUSYPIN 9    // digital pin 9 for BUSY or READY/


const int nsamples = 50;  // how many ADC readings to average together

// SPI_CLOCK_DIV16 gives me a 1.0 MHz SPI clock, with 16 MHz crystal on Arduino

void setup() {
 
 Serial.begin(115200);  // set up serial comm to PC at this baud rate
 
 pinMode (SLAVESELECT, OUTPUT);
 pinMode (BUSYPIN, INPUT);
 digitalWriteFast(SLAVESELECT,LOW);  // take the SS pin low to select the chip
 delayMicroseconds(1);
 digitalWriteFast(SLAVESELECT,HIGH);  // take the SS pin high to start new ADC conversion

 SPI.begin(); // initialize SPI, covering MOSI,MISO,SCK signals
 SPI.setBitOrder(MSBFIRST);  // data is clocked in MSB first
 SPI.setDataMode(SPI_MODE0);  // SCLK idle low (CPOL=0), MOSI read on rising edge (CPHI=0)
 SPI.setClockDivider(SPI_CLOCK_DIV16);  // set SPI clock at 1 MHz. Arduino xtal = 16 MHz, LTC2440 max = 20 MHz
 Serial.println("# LTC2440 v1.1 Oct.25 2012  jpb"); 
 Serial.println("min,uVolts,pkpk,stdev");
 for (int i=0;i<2;i++) {  // throw away the first few readings, which seem to be way off
  SpiRead();
 }
} // end setup()

// =============================================================================
// Main Loop:
// acquire 'nsamples' readings, convert to units of volts, and send out on serial port

void loop() {

int i;
long secs;
float mins;
long in; // incoming serial 32-bit word
float uVolts;  // average reading in microvolts

float datSum;  // accumulated sum of input values
float sMax;
float sMin;
long n;            // count of how many readings so far
float x,mean,delta,sumsq,m2,variance,stdev,pp;  // to calculate standard deviation 

  sMax = -VREF;  // set max to minimum possible reading
  sMin = VREF;  // set min to max possible reading
  sumsq = 0; // initialize running squared sum of differences
  n = 0;    // have not made any ADC readings yet
  datSum = 0;  // accumulated sum of readings starts at zero
  mean = 0; // start off with running mean at zero
  m2 = 0;
 
  for (i=0; i<nsamples; i++) {
    x = SpiRead();
    datSum += x;
    if (x > sMax) sMax = x;
    if (x < sMin) sMin = x;
                      // from http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
    n++;
    delta = x - mean;
    mean += delta/n;
    m2 += (delta * (x - mean));

  } // end for (i..)

  variance = m2/(n-1);  // (n-1):Sample Variance  (n): Population Variance
  stdev = 1E6*sqrt(variance);  // Calculate standard deviation in microvolts
  pp = 1E6 * (sMax - sMin);  // peak-to-peak difference of readings, in uV
 
  uVolts = datSum * (1E6) / n;

mins = (float) millis() / 60000;  // elapsed time in minutes
Serial.print(mins,3);
Serial.print(", ");
Serial.print(uVolts,3);
Serial.print(", ");
Serial.print(pp,3);
Serial.print(", ");
Serial.print(stdev,3);
Serial.println();

} // end main loop


// =================================================================
// SpiRead() -- read 4 bytes from LTC2440 chip via SPI, return Volts
// =================================================================

float SpiRead(void) {

  long result = 0;
  byte sig = 0;    // sign bit
  byte b;
  float v;
 
  while (digitalReadFast(BUSYPIN)==HIGH) {}  // wait until ADC result is ready
 
  digitalWriteFast(SLAVESELECT,LOW);  // take the SS pin low to select the chip
  delayMicroseconds(1);  // probably not needed, only need 25 nsec delay
 
  b = SPI.transfer(0xff);  // B3
  if ((b & 0x20) ==0) sig=1;  // is input negative ?
  b &=0x1f;    // discard bits 25..31
  result = b;
  result <<= 8; 
  b = SPI.transfer(0xff);  // B2
  result |= b;
  result = result<<8;
  b = SPI.transfer(0xff);  // B1
  result |= b;
  result = result<<8;
  b = SPI.transfer(0xff);  // B0
  result |= b;
 
  digitalWriteFast(SLAVESELECT,HIGH);  // take the SS pin high to bring MISO to hi-Z and start new conversion
 
  if (sig) result |= 0xf0000000;    // if input is negative, insert sign bit (0xf0.. or 0xe0... ?)       
  v = result;
  v = v / 16.0;                            // scale result down , last 4 bits are "sub-LSBs"
  v = v * VREF / (2 * 16777216); // +Vfullscale = +Vref/2, max scale (2^24 = 16777216)
  return(v); 
}
53
General discussion / Re: Teensy 3.0 arrived
The new touch sensing function is pretty neat. I get a very noticeable capacitance change when I bring my finger within 1 cm from a length of 22-gauge hookup wire, and then move it away. This data is from the below program (which averages many readings together, for a 500 msec update rate).

[attachment=0]
moving finger to 1 cm from wire, and then away
Code: [Select]
val,n,p-p,stdev
#Teensy3.0 Touchprogram Oct.232012
688.03,4315,7,1.465
688.07,4318,7,1.461
687.99,4317,8,1.477
687.95,4318,7,1.454
687.81,4319,7,1.468
689.23,4315,9,1.786
691.18,4307,6,1.307
690.99,4309,6,1.302
690.90,4310,7,1.275
690.84,4308,7,1.281
690.81,4309,8,1.287
690.82,4309,7,1.290
690.58,4309,7,1.291
690.69,4309,7,1.284
688.76,4312,10,1.865
687.82,4318,7,1.458
687.78,4318,7,1.457
687.77,4317,8,1.465
687.85,4318,7,1.469
687.93,4318,7,1.466
687.94,4317,7,1.465
687.81,4319,7,1.459
687.77,4317,7,1.451
687.87,4318,7,1.460
687.77,4317,7,1.477
688.02,4318,8,1.478
687.93,4318,7,1.472

Grabbing the insulated portion of the wire, then the bare end of the wire- a big signal.  The pk-pk reading change and standard deviation also goes through the roof when touching the wire, I assume that is 60 Hz pickup.  The T3 is connected to my computer via its USB cable during these tests.
Code: [Select]
val,n,p-p,stdev
# Teensy 3.0 Touch program Oct. 23 2012
908.20,3713,18,2.854
910.23,3708,22,2.910
912.22,3704,23,3.007
831.56,3901,233,102.050
689.36,4312,8,1.458
689.57,4313,9,1.502
1230.76,3084,2859,877.954
3574.54,1376,981,155.670
3795.12,1308,941,170.153
3923.82,1272,1118,186.593
4035.86,1242,1058,194.914
2393.54,1911,3912,1677.471
688.00,4315,8,1.540
687.82,4318,7,1.438
687.85,4317,7,1.445

Touch sensing example code:
Code: [Select]
/*  Test capacitive touch sensor function on Teensy 3.0
    using Teensy 3.0 beta 6 (Windows) release    J.Beale Oct. 23 2012
 */
 
#define SAMPLES (5000)  // maximum number of separate readings to take
#define TIMEOUT (500)    // maximum sample integration time in milliseconds

int led = 13;

void setup() {               
  pinMode(led, OUTPUT);   
  digitalWrite(led, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(3000);              // wait for user to start up logging program
  Serial.begin(115200);      // baud rate is ignored with Teensy USB ACM i/o
  delay(2000);
  Serial.println("val,n,p-p,stdev");
  Serial.println("# Teensy 3.0 Touch program Oct. 23 2012");
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
}

void loop() {
int t1;
long tStart;  // starting time
long datSum;  // reset our accumulated sum of input values to zero
int sMax;
int sMin;
long n;            // count of how many readings so far
double x,mean,delta,sumsq,m2,variance,stdev;  // to calculate standard deviation
 
  datSum = 0;
  sumsq = 0; // initialize running squared sum of differences
  n = 0;    // have not made any ADC readings yet
  mean = 0; // start off with running mean at zero
  m2 = 0;
  sMax = 0;
  sMin = 65535;
       
  tStart = millis();
  for (int i=0;i<SAMPLES && ((millis()-tStart)<TIMEOUT);i++) {
            x = touchRead(A1);
            datSum += x;
            if (x > sMax) sMax = x;
            if (x < sMin) sMin = x;
                  // from http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
            n++;
            delta = x - mean;
            mean += delta/n;
            m2 += (delta * (x - mean));
          }
  variance = m2/(n-1);  // (n-1):Sample Variance  (n): Population Variance
  stdev = sqrt(variance);  // Calculate standard deviation

  Serial.print(mean);
  Serial.print(","); 
  Serial.print(n);
  Serial.print(","); 
  Serial.print(sMax-sMin);
  Serial.print(","); 
  Serial.println(stdev,3);
}
54
General discussion / Another 24-bit ADC: LTC2410
Below is a slightly modified version of Martin Nawrath's LTC2400 ADC code, which I am using with a LTC2410 (a fully-differential version of the 2400).  This code should work as-is with a LTC2400 as well, except the output will be half the correct value, due to the difference in full-scale value and output normalization between the two chips.

The differential input is taken from a 4x10k full bridge using simple 1/4 watt resistors. My circuit has both Vcc and +Vref connected to +5V from a LM7805.  The 2410 has an input range of -(Vref/2) to +(Vref/2) or in my case, -2.5 to +2.5V for a full-scale span of 5V.

I have Fo (pin 14) connected to GND for 60 Hz noise rejection, giving me a 134 msec output rate. Obtaining 100 samples takes about 134 seconds. The datasheet claims 0.8 uV(rms) noise for the LTC2410, and in my case I am seeing 1.3 uV(rms).  With the full scale span being 5V, that means the RMS noise is 2.6E-7 of full scale, giving me 21.9 bits (rms) of resolution.  The datasheet claim is 22.6 bits (rms) so I'm not too far off. 

The circuit was in a plastic box to shield from drafts, but a metal shielding box might reduce noise further.  I mounted the SSOP-16 chip on an ebay adaptor I had handy, which turned out to have pins 1-8 some non-100-mil-multiple spacing from pins 9-16, so I had to use it SIP-style on the breadboard, as you see below. I used copper foil under the chip to try for a solid ground (pins 1,7,8,9,10,15,16 are all ground pins on this device).

Photos: LTC2410 on adaptor and adaptor assy in breadboard.

Note: I saw 3 uV(rms) noise when Vcc came from a noisy 5V USB rail, even though +Vref was still using the cleaner LM7805 output, so there is some noise coupling from the Vcc supply. 

Sample output from program (last column is the standard deviation of 100 readings, in microvolts).
Code: [Select]
count,sec,value,stdev
# LTC2410 v0.1 Oct.23 2012  jpb
0,13.59,0.0063365,1.29
1,26.98,0.0063367,1.38
2,40.38,0.0063368,1.35
3,53.77,0.0063368,1.30
4,67.16,0.0063368,1.46
5,80.55,0.0063369,1.36
6,93.94,0.0063368,1.25
7,107.33,0.0063368,1.28
8,120.72,0.0063372,1.32
9,134.12,0.0063372,1.18
10,147.51,0.0063371,1.14
11,160.90,0.0063369,1.24
12,174.29,0.0063371,1.30
Arduino code which generated above output:
Code: [Select]
/* *********** LTC2410 24 Bit ADC Test  *************
 * Connect an LTC2410 24 Bit ADC to the Arduino Board in SPI Mode
 * by John Beale, Oct. 23 2012
 * based on code for LTC2400 by KHM 2009 /  Martin Nawrath
 * from http://interface.khm.de/index.php/lab/experiments/
 */

// define clear-bit and set-bit macros
#ifndef cbi
#define cbi(sfr, bit)    (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit)    (_SFR_BYTE(sfr) |= _BV(bit))
#endif

// Setup SPI pins at standard Arduino locations (specific to ATmega328p chip)
#define LTC_CS 2        // LTC2410 Chip Select Pin on PortB.2 (pin16: D10, CS)
#define LTC_MISO  4      // LTC2410 SDO Select Pin  on PortB.4 (pin18: D12, MISO)
#define LTC_SCK  5      // LTC2410 SCK Select Pin  on PortB.5 (pin19: D13, SCK)

// ADC-related constants
#define VREF (5.0)      // ADC Reference, Volts
#define SAMPLES (100)    // how many readings to combine for average and standard deviation

void setup() {

 cbi(PORTB,LTC_SCK);      // LTC2410 SCK low
 sbi (DDRB,LTC_CS);      // LTC2410 CS HIGH

 cbi (DDRB,LTC_MISO);
 sbi (DDRB,LTC_SCK);

 Serial.begin(115200);  // serial port baud rate
 // init SPI Hardware
 sbi(SPCR,MSTR) ; // SPI master mode
 sbi(SPCR,SPR0) ; // SPI speed
 sbi(SPCR,SPR1);  // SPI speed
 sbi(SPCR,SPE);  //SPI enable

 Serial.println("count,sec,value,stdev");
 Serial.println("# LTC2410 v0.1 Oct.23 2012  jpb");
 cbi(PORTB,LTC_CS);          // LTC2410 CS Low
 delay(200);                  // acquire first reading
 SPI_read();                  // throw it away (1st one has big offset)
 sbi(PORTB,LTC_CS);          // LTC2410 CS High
 delay(1);                 
 cbi(PORTB,LTC_CS);          // LTC2410 CS Low
} // end setup()

/********************************************************************/

int cnt;                  // ADC reading counter

void loop() {  // **** main loop

float datSum = 0;  // reset our accumulated sum of input values to zero
float sMax;
float sMin;
long n;            // count of how many readings so far
float x,mean,delta,sumsq,m2,variance,stdev;  // to calculate standard deviation 
               
  sMax = -VREF;  // set max to minimum possible reading
  sMin = VREF;  // set min to max possible reading
  sumsq = 0; // initialize running squared sum of differences
  n = 0;    // have not made any ADC readings yet
  mean = 0; // start off with running mean at zero
  m2 = 0;
           
  for (int i=0;i<SAMPLES;i++) {
    while (PINB & (1 << PORTB4)) {}  // busy loop waiting for ADC to be ready   
    x = readADC();    // get analog reading in units of volts
//    Serial.println(x,6);
    datSum += x;
    if (x > sMax) sMax = x;
    if (x < sMin) sMin = x;
                      // from http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
    n++;
    delta = x - mean;
    mean += delta/n;
    m2 += (delta * (x - mean));
  }
  variance = m2/(n-1);  // (n-1):Sample Variance  (n): Population Variance
  stdev = 1E6*sqrt(variance);  // Calculate standard deviation in microvolts
 
  float tsec = millis()/1000.0;  // elapsed time in seconds
  Serial.print(cnt++);
  Serial.print(",");
  Serial.print(tsec,2);      // elapsed time, seconds
  Serial.print(",");
  Serial.print(mean,7);      // average reading, volts
  Serial.print(",");
  Serial.print(stdev,2);    // standard deviation, microvolts
  Serial.println();

} // end loop()


// ******************************************************************************
// readADC():  read 32-bit word from Linear Tech LTC2410 and return volts
// currently ignoring the "overrange" and "underrange" condition flags

float readADC(void) {

float volt;              // ADC reading in real-world units (volts)
long int ltw = 0;        // full 32-bit ADC data word
byte b0;                  // one byte in ADC data word
byte sig;                // sign bit flag

  ltw=0;  // 32-bit data word
  sig=0;  // sign bit

  b0 = SPI_read();            // read 4 bytes adc raw data with SPI
  if ((b0 & 0x20) ==0) sig=1;  // is input negative ?
  b0 &=0x1F;                  // discard bit 25..31
  ltw |= b0;
  ltw <<= 8;
  b0 = SPI_read();
  ltw |= b0;
  ltw <<= 8;
  b0 = SPI_read();
  ltw |= b0;
  ltw <<= 8;
  b0 = SPI_read();
  ltw |= b0;

  sbi(PORTB,LTC_CS);          // LTC2410 CS hi: start of new ADC conversion
  delayMicroseconds(5);
  cbi(PORTB,LTC_CS);          // LTC2410 CS Low: allow detection of "conversion done" bit on MISO pin
 
  if (sig) ltw |= 0xf0000000;    // if input is negative, insert sign bit
  ltw=ltw/16;                    // scale result down , last 4 bits have no information
  volt = ltw * VREF / (2 * 16777216); // +Vfullscale = +Vref/2, max scale (2^24 = 16777216)
  return(volt);
 
} // end readADC() 


/********************************************************************/
byte SPI_read()
{
 SPDR = 0;
 while (!(SPSR & (1 << SPIF))) ; /* Wait for SPI shift out done */
 return SPDR;
}
55
General discussion / SPI is working (with default clock rate)
I was happy to see that I can get data via SPI from an 18-bit ADC and send it out one of the three serial ports on the Teensy 3. The below code is the same as the Arduino version I used earlier with the MAX11209 chip, with one exception: at the moment (Teensy 3 beta 5) the SPI.setClockDivider() command is not working, so I have to settle for the default SPI clock rate (4 MHz).

Code: [Select]
/* -----------------------------------------------------------------
  Interface between Teensy 3.0 and MAX11209 18-bit ADC
  compiled with Arduino 1.0.1 Teensy 3 beta 5
  October 12 2012 John Beale
 
  Note: this ADC requires 3.3V logic levels.

  MAX11209  <---------->  Arduino Device
  11: /CS  <- to digital pin 10  (SS pin)
  12: MOSI <- to digital pin 11 (MOSI pin)
  13: MISO -> to digital pin 12 (MISO pin)
  14: SCLK <- to digital pin 13 (CLK pin)
    4: GND  - ground
    9: AVDD - 3.3V supply
  10: DVDD - 3.3V supply
------------------------------------------------------------------ */

/*  MAX11209 8-bit Command Byte, and Status & Control Registers
 ------------------------------------------------------------------------
 #  name    b7    b6    b5    b4    b3    b2    b1    b0      dir
 ------------------------------------------------------------------------
 - COMMAND  START  MODE  CAL1  CAL0  IMPD  RATE2  RATE1  RATE0    W
 0 STAT1    SYSOR  RATE2  RATE1  RATE0  OR    UR    MSTAT  RDY      R
 1 CTRL1    LINEF  U/B    EXTCLK REFBUF SIGBUF FORMAT SCYCLE UNUSED  R/W
 2 CTRL2    DIR4  DIR3  DIR2  DIR1  DIO4  DIO3  DIO2  DIO1    R/W
 3 CTRL3    DGAIN2 DGAIN1 DGAIN0 NOSYSG NOSYSO NOSCG  NOSCO  RESERVED R/W
 ----------------------------------------------------------------------- */
 
// NOTE: MAX11209 SlaveSelect pin must be toggled high & low between each command
// (despite data sheet claim to the contrary)
 
#include <SPI.h>  // include the SPI library for talking with chip

#define UC unsigned char    // 8 bits unsigned char = 1 byte
#define STAT1 0            // addresses of ADC status/control registers
#define CTRL1 1
#define CTRL2 2
#define CTRL3 3

// set I/O pins used in addition to clock, data in, data out
const byte slaveSelectPin = 10;  // digital pin 10 for /CS
const byte SPICLK = 13;  // digital pin for SPI Clock
const byte SPIMISO = 12;  // digital pin for MISO
const byte SPIMOSI = 11;  // digital pin for MOSI

// analog calibration numbers
#define ADCMID 131072  // 2^17 is 0 with 18-bit offset binary
#define VMINUS 2492000.0    // negative analog Vinput (microvolts)
#define TWOE18 262144  // 2^18
#define VFS (1.236*2)        // ADC Volts full-scale = 2(VrefP-VrefN)
#define LSBSIZE (VFS/TWOE18)
#define AVERAGES 48    // for approx 100 millisecond output period

void setup() {
 
    Serial1.begin(115200);      // set up serial port #1 output at this baud rate
    delay(3000);
    SPI.begin(); // initialize SPI, configures SPIMOSI,SPIMISO,SPICLK signals
    pinMode (slaveSelectPin, OUTPUT);  // make CS/ pin an output
    digitalWrite(slaveSelectPin,HIGH);  // CS/ chip select is active low 
    SPI.setBitOrder(MSBFIRST);  // data is clocked in MSB first
   
    // MAX11209 max SPI clock is 5 MHz (assuming no problem with long wires...)
    // SPI.setClockDivider(SPI_CLOCK_DIV16);  // this cmd is not currently supported on Teensy 3.0 beta 5

    Serial1.println("sec,degC");
    Serial1.println("# MAX11209 ADC Test Oct 12 2012");

    initADC();

    // ==== ADC SAMPLING RATE set by this SendCmd() command
    // Possible sampling rates: 1-120 Hz in single shot; 60-480 Hz in continuous
   
    SendCmd(0b10000111); // start converting at Max Rate 120/480 sps
//    SendCmd(0b10000100); // start converting at Rate 15/60 sps
    digitalWrite(slaveSelectPin,LOW);  // enable chip, to monitor RDY/DATA for "conversion ready"
    for (int i=0; i<16; i++) {
      readADC();  // First few readings in continuous mode are invalid (settling time)
    }
}

void loop() { //            ================ MAIN LOOP ===========================
long adcSum;
 
  adcSum = 0;
  for (int i=0;i<AVERAGES;i++) {
    adcSum += readADC();  // get 18 bit ADC reading
  }
 
  float Vin = ((float)(adcSum - (ADCMID*AVERAGES)) * (LSBSIZE/AVERAGES));
  Serial1.println(Vin*100.0,4);          // send ADC value out serial port
     
} //  =================== END of main loop() =================================

// ======================== set up ADC chip registers ==============
void initADC(void) {

// NOTE: if Signal or Ref buffer ON, Sig or Ref must be 100 mV above GND and 100 mV below AVdd
// with buffers OFF, Sig and Ref can swing over full range (actually, 30 mV beyond rails)

/* SCYCLE: The single-cycle bit, SCYCLE, determines if the device runs in “no-latency”
single-conversion mode (SCYCLE = 1) or continuously (SCYCLE = 0). When in single-cycle
conversion mode, the device completes one no-latency conversion and then powers down
into a leakage-only state. When in continuous mode, the part is continuously converting
and the first three data from the part are incorrect due to the SINC4 filter latency.  */

//  WriteReg8(CTRL1, 0b00011110); // 60 Hz,bipolar,intClk,RefBuf,SigBuf,Offset,SingleCycle
//  WriteReg8(CTRL1, 0b00000110); // 60 Hz,bipolar,intClk,NoRefBuf,NoSigBuf,Offset,SingleCycle
    WriteReg8(CTRL1, 0b00000100); // 60 Hz,bipolar,intClk,NoRefBuf,NoSigBuf,Offset,Continuous
    WriteReg8(CTRL2, 0b11110010); // DIO1-4 all outputs, set 4 output pins to 0010
    WriteReg8(CTRL3, 0b00011000); // Gain=1,no system gain/offset, use self-cal gain/offset
   
    SendCmd(0b10010000); // start self-calibration cycle
    delay(220);    // self-calibration takes 200 msec
   
} // end initADC()

// =======================================================
//  Get ADC data from 24-bit register 4.
long readADC(void) {
 
  do {} while (digitalRead(SPIMISO) == HIGH);  // wait for next ADC conversion result to be ready

  digitalWrite(slaveSelectPin,LOW);  // take the SS pin low to select the chip
  long result = SPI.transfer(0b11001001); // send command to read register 4
  result |= SPI.transfer(0x00);  // obtain high-order byte
  result = result << 8;          // shift left one byte
  result |= SPI.transfer(0x00);  // obtain 2nd byte
  result = result << 8;          // shift left one byte
  result |= SPI.transfer(0x00);  // obtain low-order byte
 
  digitalWrite(slaveSelectPin,HIGH);  // take the SS pin high, ending SPI transaction
  digitalWrite(slaveSelectPin,LOW);  // enable chip, to monitor RDY/DATA for "conversion ready" 
  result = result >> 6;        // 24-bit word: 18 data bits, and low-order 6 bits = 0
  return(result);
}

// ===================  read an 8-bit register (Registers 0,1,2,3)
UC ReadReg8(UC addr) {
// mode 1 command byte
// b7 b6 b5 b4  b3  b2  b1  b0
// 1  1  0  RS3 RS2 RS1 RS0 1  (b0 = W/R)

 digitalWrite(slaveSelectPin,LOW);  // take the SS pin low to select the chip
 UC a = (addr << 1) | 0b11000001;  // set b7,b6,b0 = 1
 a &= 0b11011111;    // set b5 = 0
 UC result = SPI.transfer(a); // send command to read register 'addr'
 result = SPI.transfer(0x00);  // obtain 1-byte register value
 digitalWrite(slaveSelectPin,HIGH);  // take the SS pin high to deselect chip
 return(result);
} // end ReadReg8()

// =======================================================
// write an 8-bit register (Registers 0,1,2,3)

void WriteReg8(UC addr, UC data) {
// mode 1 command byte
// b7 b6 b5 b4  b3  b2  b1  b0
// 1  1  0  RS3 RS2 RS1 RS0 0  (b0 = W/R)

 digitalWrite(slaveSelectPin,LOW);  // DEBUG
 delay(4);  // DEBUG
 digitalWrite(slaveSelectPin,HIGH);  // DEBUG
 delay(4); // DEBUG

 digitalWrite(slaveSelectPin,LOW);  // take the SS pin low to select the chip
 UC a = (addr << 1) | 0b11000000;  // set b7,b6 = 1
 a &= 0b11011110;    // set b5,b0 = 0
 SPI.transfer(a); // send command to read register 'addr'
 SPI.transfer(data);  // obtain 1-byte register value
 digitalWrite(slaveSelectPin,HIGH);  // take the SS pin high to deselect chip

} // end WriteReg8()


// =======================================================
// send Command Byte (MODE = 0)

void SendCmd(UC data) {
// === mode 0 command byte format =======
// b7 b6 b5  b4  b3  b2    b1    b0
// 1  0  CAL1 CAL0 IMPD RATE2 RATE1 RATE0
 digitalWrite(slaveSelectPin,LOW);  // take the SS pin low to select the chip
 UC a = data | 0b10000000;  // set b7 = 1
 a &= 0b10111111;    // set b6 = 0
 SPI.transfer(a); // send command byte
 digitalWrite(slaveSelectPin,HIGH);  // take the SS pin high to deselect chip
} // end SendCmd()

// =======================================================
// read a 24-bit register (Registers 4,5,6,7,8)

long ReadReg24(UC addr) {
// mode 1 command byte
// b7 b6 b5 b4  b3  b2  b1  b0
// 1  1  0  RS3 RS2 RS1 RS0 W/R

 digitalWrite(slaveSelectPin,LOW);  // take the SS pin low to select the chip
 UC a = (addr << 1) | 0b11000001;  // set b7,b6,b0 = 1
 a &= 0b11011111;    // set b5 = 0
 long result = SPI.transfer(a); // send command to read register 'addr'
 result |= SPI.transfer(0x00);  // obtain high-order byte
 result = result << 8;          // shift left one byte
 result |= SPI.transfer(0x00);  // obtain 2nd byte
 result = result << 8;          // shift left one byte
 result |= SPI.transfer(0x00);  // obtain low-order byte
 digitalWrite(slaveSelectPin,HIGH);  // take the SS pin high to deselect chip

 return(result);  // return 24-bit value
 
} // end ReadReg24()
56
General discussion / Teensy3 maximum pin toggle rate is 24 MHz
Using the program below and looking at the LED pin on my scope, I measure 12.0 MHz out @24M clock, 24.0 MHz @48M, but also 24.0 MHz @96M with either 20% or 80% duty cycle. So from the standpoint of the maximum GPIO pin toggle rate, overclocking beyond the "stock" 48 MHz does not make it faster.

Code: [Select]
#define T GPIOC_PTOR = 1<<5; // toggle state of PortC,bit5 = LED pin
void setup() {pinMode(13, OUTPUT);} // make LED pin an output
void loop() { T T T T T T T T T T T T T T T T T T T } // toggle pin 19x as unrolled loop
But if I instead use a single "T" as the body of loop() then the overclock is indeed 2x faster (2.4MHz @48M and 4.8MHz @96M) so the overclock is meaningful if you're not trying to do I/O at that frequency. Also, running the ARM at 96 MHz you can generate an isolated 10 ns wide pulse, of either polarity.  (Well, approximately 10 ns, but with the rise time at about 6 ns, it's hard to say.)

The equivalent program on a standard 16 MHz ATmega328p Arduino generates a 8 MHz square wave:
Code: [Select]
#define H PORTB = 1<<5; // Arduino set D13 (LED pin) high
#define L PORTB = 0; // make PortB all zeros
void setup() {pinMode(13, OUTPUT);}  // make LED pin an output
void loop() { H L H L H L H L H L H L H L H L H L H L }
57
General discussion / Teensy 3 internal temp sensor
Just FYI, another feature of the Teensy 3.0 ARM chip is an internal temperature sensor. analogRead(38) gets you a reading proportional to the temperature of the chip. On my device, in 16 bit mode I get 14400 counts at 25 C, and -35.7 counts per deg. C above that (so 40 C is 13868). The internal sensor is a transistor Vbe drop. My temperature reference was a thermocouple taped to the top of the ARM chip.  With my rather limited, casual test, it looks linear through the 25 C to 45 C range at least. 

The scatter from the linear fit that you see in the chart may have more to do with the rate of change of temperature causing a difference between inside & outside of the chip package.  I did not use a thermal chamber for this; this was mostly just waving a heat gun around.

[attachment=0]
58
General discussion / Re: Teensy 3.0 arrived
Yes, the ground is shared, but right now the ADC is just looking at a resistive divider from the chip's Vdd, nothing external- so with only one shared connection point there is no circuit (except for stray capacitive coupling, which is probably the culprit). 

Yes, USB isolation would be one useful test. I was thinking of just running the Teensy 3 standalone (no USB at all) off a clean power supply, and blinking out the RMS ADC readings in morse code on the built-in LED. Which I guess might also be considered a one-way, low-bandwidth opto-isolator.  The Teensy also has a normal UART and I already have a serial optoisolator, somewhere around here...
59
General discussion / Teensy 3.0 working with Linux now (was my fault...)
I have both my laptop and the Raspberry Pi working now with Teensy3.  What works is using the command
'screen /dev/ttyACM0 115200'  (and also, presumably, any normal terminal emulator; minicom also works ok on the R-Pi, although not on my laptop for some reason. Paul, the developer of the board uses seyon on his Ubuntu system).

What did not work was just using the command 'cat /dev/ttyACM0'.  I didn't realize 'cat' was not well-defined for opening serial ports, I guess it just happened to work with Teensy2. 

Update: 'cat' does work, as long as you use 'stty' to set the serial parameters first.  This two-line script works for me:
Code: [Select]
stty -F /dev/ttyACM0 1:0:80001cb1:0:3:1c:7f:15:4:5:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
cat /dev/ttyACM0 > /home/pi/teensy3.log

Curious note: for whatever reason, the measured RMS noise of the ADC is slightly lower when connected to the R-Pi (typically 0.85 LSB's, vs 1.20 on my laptop, and near 1.0 for the desktop at work.)  This is true even using a hacked USB splitter cable so the +5V power comes from a separate supply from the USB host.
60
General discussion / Re: Teensy 3.0 arrived
UPDATE Oct. 8 2012: The below overlong post is only left here for historical interest. The problem I was debugging here was simply me not setting up the host serial port properly. Feel free to skip this one, and continue with the next post if you're still interested.  -John
[hr:][/hr:]
This is mostly a note to myself, but any USB protocol experts are certainly welcome to comment! This really should go to a "Teensy 3" forum on the vendor's website (PJRC.com), but a forum there is still a work in progress.

Summary: the Teensy 3.0 is a USB ACM device. A program running on it which sends serial output via USB works just fine on 3 different WinXP computers. It does not talk properly to a Raspberry Pi (Raspbian Linux 3.2.27+), or a 6-year-old Dell Inspiron 9300 laptop running Debian Linux 2.6.32-5-686. The R-Pi has known USB issues, but the Laptop USB is normally reliable. The symptom is the first line of text (actually 68 bytes in two lines, but one USB packet) sent by Teensy 3.0 is received by the host (eg. by cat /dev/ttyACM0) but no further communication is received.  Interestingly, the Teensy 2.0 is also a USB ACM device, and the *same* program running on that (smaller, slower) CPU works just fine sending data to these two Linux boxes (and also on Windows).

I'm a complete novice at low-level USB, but I set up Wireshark 1.2.11 on my laptop, and did 'modprobe usbmon' and 'mount -t debugfs / /sys/kernel/debug' so I could monitor USB packets.  I plugged the Teensy 2 set up with my program into my laptop USB port, did 'cat /dev/ttyACM0' to capture the output, and logged the USB packets. Then I stopped it with control-C, and unplugged the device.  Then I did the same with the Teensy 3, running the same program (which is nearly the same one as in my first post on this thread).  A hand-transcribed excerpt of the USB packet trace is below.  In each case, you see the initial line of 68 bytes of text (starting 'min,sps,avg,pkpk,') come in as one packet, an "in progress" reply, and then each byte is forwarded in a separate packet to a different port/USB endpoint (that being my shell 'cat' command? I don't know how USB works...) The difference seems to be that actually fewer of the separate bytes in the Teensy 2 case get forwarded in this way, and later acknowledged (but I don't even know if this trace is relevant to the actual problem.) You see the second line of text from Teensy 2 (starting '0.07') and also subsequent data, but with Teensy 3, this and subsequent data lines never show up.  The action stops with confirmation of the one-byte 'k' packet, URB id 0xf6f90b80 and nothing further seen until about 14 seconds later when I killed the cat command and unplugged the device. 

 The actual dumps (readable in Wireshark, at least on my machine) are in the attached zip file.

In the listing below, the first line shows a USB packet containing a line with 68 bytes of text coming from source (USB endpoint 5.4 or 4.4) which is the Teensy 2 or 3 respectively, to the destination which is the laptop Linux box. The second line, about 20 microseconds later, is a reply from the Linux host to the Teensy with a status update saying that USB Request Block (URB) is 'in progress'. 

Code: [Select]
time(sec)	src	dest	type	data or status		bytes	URB id:
----------------------------------------------------------------------------------
Teensy 2.0 USB-ACM packet trace Linux debian 2.6.32-5-686  Wireshark 1.2.11
----------------------------------------------------------------------------------
14.627059 5.4 host C min,sps,avg,pkpk,... 68 0x00000000f61b5800
14.627076 host 5.4 S -EINPROGRESS (-115) 0 0x00000000f61b5800
14.628010 host 5.3 S m (-EINPROGRESS) 1 0x00000000f61b5900
14.628019 host 5.3 S i (-EINPROGRESS) 1 0x00000000f61b5600
14.628023 host 5.3 S n (-EINPROGRESS) 1 0x00000000f61b5380
...
14.628081 host 5.3 S k (-EINPROGRESS) 1
14.629057 5.3 host C Success 0 0x00000000f61b5900
14.629064 5.3 host C Success 0 0x00000000f61b5600

16.324065 5.4 host 0.07,5899.70,175...  33 0x00000000f61b5180
16.324084 host 5.4 -EINPROGRESS (-115) 0 0x00000000f61b5180
...
22.258057 5.4 host C -ENOENT 0 0x00000000f61b5180
22.259057 5.4 host C -ENOENT 0 0x00000000f61b5800
----------------------------------------------------------------------------------
Teensy 3.0 USB-ACM packet trace Linux debian 2.6.32-5-686  Wireshark 1.2.11
----------------------------------------------------------------------------------
22.004169 4.4 host C min,sps,avg,pkpk,... 68 0x00000000f61e9e80
22.004189 host 4.4 S -EINPROGRESS (-115) 0 0x00000000f61e9e80
22.008006 host 4.3 S m (-EINPROGRESS) 1 0x00000000f61e9400
22.008016 host 4.3 S i (-EINPROGRESS) 1 0x00000000f61a3880
22.008020 host 4.3 S n (-EINPROGRESS) 1 0x00000000f6f90400
...
22.008077 host 4.3 S k (-EINPROGRESS) 1 0x00000000f6f90b80
22.008155 4.3 host C Success 0 0x00000000f61e9400
22.008162 4.3 host C Success 0 0x00000000f61a3880
22.009165 4.3 host C Success 0 0x00000000f6f90400
...
22.009206 4.3 host C Success 0 0x00000000f6f90b80

36.196116 host 4.0 S (-EINPROGRESS) 0 0x00000000f6286100
------------------------------------------------------------------------------------------

Text output of program run on Teensy 2.0 (despite "Teensy 3.0" text) until ^C terminated cat command. (the original was actually double-spaced as it was sending out DOS-style CR-LF line endings.)
Code: [Select]
root@debian:/home/john# cat /dev/ttyACM0
min,sps,avg,pkpk,stdev
# Teensy 3.0 ADC test v1.1 2012.10.6 JPB
0.07,5899.70,175.05,905,214.296
0.10,5934.72,70.29,98,11.796
0.13,5941.77,72.79,98,11.616
0.16,5927.68,71.95,105,14.226

Text output of program run on Teensy 3.0, until ^C terminated cat command.  (the original was actually double-spaced as it was sending out DOS-style CR-LF line endings.)
Code: [Select]
root@debian:/home/john# cat /dev/ttyACM0
min,sps,avg,pkpk,stdev
# Teensy 3.0 ADC test v1.1 2012.10.6 JPB


Here is the exact code running on both Teensy 2 and Teensy 3
Code: [Select]
    // Analog input test for Teensy 3.0    Oct 4 2012 J.Beale
    // Setup: https://picasaweb.google.com/109928236040342205185/Electronics#5795546092126071650

        #define VREF (3.266)        // ADC reference voltage (= power supply)
        #define VINPUT (2.171)      // ADC input voltage from resistive divider to VREF
        #define ADCMAX (65535)      // maximum possible reading from ADC
        #define EXPECTED (ADCMAX*(VINPUT/VREF))    // expected ADC reading
        #define SAMPLES (10000)      // how many samples to combine for pp, std.dev statistics

        const int analogInPin = A0;  // Analog input is AIN0 (Teensy3 pin 14, next to LED)
        const int LED1 = 13;        // output LED connected on Arduino digital pin 13

        int sensorValue = 0;        // value read from the ADC input
        long oldT;

        void setup() {    // ==============================================================
          pinMode(LED1,OUTPUT);      // enable digital output for turning on LED indicator
          analogReference(INTERNAL);  // set analog reference to internal ref
          //analogReadRes(16);          // Teensy 3.0: set ADC resolution to this many bits
       
          Serial.begin(115200);      // baud rate is ignored with Teensy USB ACM i/o
          digitalWrite(LED1,HIGH);  delay(1000);  // LED on for 1 second
          digitalWrite(LED1,LOW);    delay(3000);  // wait for slow human to get serial capture running
       
          Serial.println("min,sps,avg,pkpk,stdev ");
          Serial.println("# Teensy 3.0 ADC test v1.1 2012.10.6 JPB ");
        } // ==== end setup() ===========

        void loop() {  // ================================================================
       
          long datSum = 0;  // reset our accumulated sum of input values to zero
          int sMax = 0;
          int sMin = 65535;
          long n;            // count of how many readings so far
          double x,mean,delta,sumsq,m2,variance,stdev;  // to calculate standard deviation
       
          oldT = millis();  // record start time in milliseconds

          sumsq = 0; // initialize running squared sum of differences
          n = 0;    // have not made any ADC readings yet
          mean = 0; // start off with running mean at zero
          m2 = 0;
       
          for (int i=0;i<SAMPLES;i++) {
            x = analogRead(analogInPin);
    //        Serial.println(x,0);
            datSum += x;
            if (x > sMax) sMax = x;
            if (x < sMin) sMin = x;
                  // from http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
            n++;
            delta = x - mean;
            mean += delta/n;
            m2 += (delta * (x - mean));
          }
          variance = m2/(n-1);  // (n-1):Sample Variance  (n): Population Variance
          stdev = sqrt(variance);  // Calculate standard deviation

        // Serial.print("# Samples/sec: ");
          long durT = millis() - oldT;
          float datAvg = (1.0*datSum)/n;
          Serial.print(oldT/60000.0,2);
          Serial.print(",");
          Serial.print((1000.0*n/durT),2);
          Serial.print(",");
         
          Serial.print(datAvg,2);
//          Serial.print(" Offset: "); 
//          Serial.print(datAvg - EXPECTED,2);
          Serial.print(","); 
          Serial.print(sMax-sMin);
          Serial.print(","); 
          Serial.println(stdev,3);
       
    //    while (true) {}
 } // end main()  =====================================================
 
       

Additional note: if I wait for a long time after connecting the Teensy 3, before typing 'cat /dev/ttyACM0' I can get several more lines of data, just over half a minute's worth, which appears all at once, as shown below. But it does not continue beyond this initial burst.
Code: [Select]
root@debian:/home/john# cat /dev/ttyACM0
min,sps,avg,pkpk,stdev
# Teensy 3.0 ADC test v1.1 2012.10.6 JPB
0.07,2238.14,32788.21,8,0.886
0.14,2238.14,32788.15,7,0.902
0.22,2238.14,32788.16,7,0.923
0.29,2238.14,32788.14,6,0.926
0.37,2238.14,32788.13,7,0.907
0.44,2238.14,32788.14,6,0.894
0.52,2237.64,32788.24,6,0.885
0.59,2238.14,32788.21,6,0.871

( ! ) Fatal error: Uncaught exception 'Elk_Exception' with message 'Please try again. If you come back to this error screen, report the error to an administrator.' in /var/www/dangerousprototypes/forum/sources/database/Db-mysql.class.php on line 696
( ! ) Elk_Exception: Please try again. If you come back to this error screen, report the error to an administrator. in /var/www/dangerousprototypes/forum/sources/database/Db-mysql.class.php on line 696
Call Stack
#TimeMemoryFunctionLocation
10.01792558168session_write_close ( )...(null):0
20.01822689784ElkArte\sources\subs\SessionHandler\DatabaseHandler->write( )...(null):0
30.01822690560Database_MySQL->query( ).../DatabaseHandler.php:119
40.06312829320Database_MySQL->error( ).../Db-mysql.class.php:273