Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 14 of 14

Thread: Teensy + ADS129x ADC + SD card

  1. #1

    Teensy + ADS129x ADC + SD card

    I'm try to interface the three components in the title to make a data-logger capable of sampling physiological signals directly. To minimise the number of connections, the ADC and the SD card are to share the SPI bus. The problem I've encountered is that if I initialise and setup the files on the SD card first, then access the ADC and then write the data to the card I get an error during the sd.card()->writeData(pCache) stage (see snippets below). When I then plug the card into my computer and check file contents, it's full of garbage, i.e. the file is filled with ˇ characters (exactly 512 of these). I tested the ADC+Teensy and SD+Teensy (with sdfatlib) separately and they work as expected. I tried tracing the error and it seems like the card doesn't respond with the correct code after the data is sent, m_status in Sd2Card::writeData stays at 0xFF. I also checked the activity on the MOSI pin and I can see that something is being sent to the SD card.

    From what I've found so far one cannot access SPI bus during multiple block write (http://forum.arduino.cc/index.php?topic=50010.0). My assumption is that what's meant is that when the data is actually being sent to the SD card one cannot access SPI bus, which is fine, but then another interpretation is that one cannot access SPI bus between sd.card()->writeStart() and sd.card()->writeStop() commands. So which one is correct? If the latter is correct is software SPI for ADC the only way forward?

    Components used:
    Teensy 3.1
    ADS1294
    Swissbit 1GB microSD card (http://www.mouser.com/ds/2/615/S-200...110-263222.pdf)

    The connections between the components are as follows:

    ADC connections to Teensy
    ADC MOSI -> pin 11 Teensy
    ADC MISO -> pin 12 Teensy
    ADC SCK -> pin 13 Teensy
    ADC SS -> pin 10 Teensy

    ADC PWDN -> pin 2 Teensy
    ADC RESET -> pin 3 Teensy
    ADC START -> pin 4 Teensy
    ADC DRDY -> pin 5 Teensy
    ADC CLKSEL -> pin 6 Teensy

    SD card connections to Teensy
    SD MOSI -> pin 11 Teensy
    SD MISO -> pin 12 Teensy
    SD SCK -> pin 13 Teensy
    SD SS -> pin 9 Teensy


    The exact sequence of steps I'm following is below:

    1. Initialise standard SPI.
    2. Initialise ADC by first sending SDATAC command to stop continuous data output, then writing to the configuration registers and finally getting the ID of the ADC indicating number of channels.
    3. Initialise SD card, create contiguous file, get its range, erase, setup buffer and write start.
    4. Change SPI mode to MODE1, since SD card uses MODE0 while ADC uses MODE1.
    5. Repeat ADC initialisation.
    6. Send 512 byte block of dummy data to the card and write stop.


    Basically if I do steps 3 and 6 one after another, then everything works as expected and I don't get any errors, but as soon as I try to access ADC in between those steps I get the write error.
    I tried running this procedure on a setup without the ADC, i.e. just an SD card connected to Teensy and it still throws the same error (I had to modify the adcSetup() function to not read ID register at the end). So running the SDSetup(), then sending some data through the SPI (albeit to a non-existent, not connected module) and then finally writing dummy data to the SD card fails.


    Here's the setup() function and all the relevant code snippets:

    Code:
    void setup() 
    {
        using namespace ADS1298;
        
        // Setup I/O pins
        pinMode(IPIN_DRDY,      INPUT);
        pinMode(IPIN_CS,        OUTPUT);
        pinMode(chipSelect,     OUTPUT);
        pinMode(PIN_START,      OUTPUT);
        pinMode(PIN_CLKSEL,     OUTPUT);
        pinMode(IPIN_RESET,     OUTPUT);
        pinMode(IPIN_PWDN,      OUTPUT);
    
        // Set output pins to safe defaults
        digitalWrite(PIN_START,     LOW);
        digitalWrite(IPIN_CS,       HIGH);
        digitalWrite(chipSelect,    HIGH);
        
        //  Start serial port
        Serial.begin(115200);
        while (Serial.read() >= 0) {} //http://forum.arduino.cc/index.php?topic=134847.0
        
        // Start Arduino SPI to interface with ADC
        SPI.begin();
        SPI.setBitOrder(MSBFIRST);
        SPI.setClockDivider(SPI_CLOCK_DIV2); //http://forum.pjrc.com/threads/1156-Teensy-3-SPI-Basic-Clock-Questions
        SPI.setDataMode(SPI_MODE1);
        
        // Setup ADC
        uint8_t gMaxChan = adcSetup();
        
        // If ADC is not detected print "ADC Error" every 1 s to the serial terminal
        if (gMaxChan == 0) {
            while(1) {
                Serial.println("ADC Error");
                delay(1000);
            }
        }
        
        Serial.print("ADC detected has ");
        Serial.print(gMaxChan);
        Serial.print(" channels\n");
        
        // Setup SD card and print error # if setup fails
        SDSetup();
        Serial.print("error is ");
        Serial.print(error);
        Serial.print("\n");
    
        // Change SPI mode to MODE1 since that's the mode used by ADC
        SPI.setDataMode(SPI_MODE1);
        
        // Setup ADC
        gMaxChan = adcSetup();
        
        // If ADC is not detected print "ADC Error" every 1 s to the serial terminal
        if (gMaxChan == 0) {
            while(1) {
                Serial.println("ADC Error");
                delay(1000);
            }
        }
        
        Serial.print("ADC detected has ");
        Serial.print(gMaxChan);
        Serial.print(" channels\n");
        
        // Send dummy data to the SD card and print error # if it fails
        sendSDdata(true);
        Serial.print("error is ");
        Serial.print(error);
        Serial.print("\n");
    }

    Code for adcSetup() function which runs successfully:

    Code:
    uint8_t adcSetup()
    {
        using namespace ADS1298;
        
        //  Power-up sequence for ADS129x
        digitalWrite(IPIN_PWDN, LOW);
        digitalWrite(PIN_CLKSEL, HIGH);
        digitalWrite(IPIN_PWDN, HIGH);
        digitalWrite(IPIN_RESET, HIGH);
        delay(1000);
        digitalWrite(IPIN_RESET, LOW);
        delayMicroseconds(1);
        digitalWrite(IPIN_RESET, HIGH);
        delayMicroseconds(9);
        adc_send_command(SDATAC);
        delayMicroseconds(5);
        
        // FOR RLD: Power up the internal reference
        adc_wreg(CONFIG3, RLDREF_INT | PD_RLD | PD_REFBUF | CONFIG3_const);
    	// Only use channels IN1P and IN1N for the RLD Measurement
        adc_wreg(RLD_SENSP, 0x01);
    	adc_wreg(RLD_SENSN, 0x01);
        
        // All GPIO set to output 0x0000: (floating CMOS inputs can flicker on and off, creating noise)
    	adc_wreg(GPIO, 0x00);
        
        // Set ADC to work at high resolution 1 KS/s sampling rate
        adc_wreg(CONFIG1, HIGH_RES_1k_SPS);
        
        // Generate internal test signal
        adc_wreg(CONFIG2, INT_TEST);
        
        // Set the first channel as differential input with x12 gain
        adc_wreg(CH1SET, ELECTRODE_INPUT | GAIN_12X);
        //    adc_wreg(CH1SET, TEST_SIGNAL | GAIN_12X); //create square wave
        
        // Power down and short all the rest of the channels since they aren't used
    	for (int i = 2; i <= 4; i++) {
            adc_wreg(CHnSET + i, PDn | SHORTED);
    	}
        
        // Get ADC ID
        int IDval = adc_rreg(ID) ;
        switch (IDval & B00011111 ) { //5 least significant bits report channels
            case  B10000:
                return 4; //ADS1294
                break;
            case B10001:
                return 6; //ADS1296
                break;
            case B10010:
                return 8; //ADS1298
                break;
            case B11110:
                return 8; //ADS1299
                break;
            default:
                return 0;
        }
    }

    Code for SDsetup() function:

    Code:
    void SDSetup()
    {
        if (!sd.begin(chipSelect,  SPI_FULL_SPEED)) {
            error = 1;
            sd.initErrorHalt();
        }
    
        // Delete possible existing file
        sd.remove("RAW.TXT");
        
        // Create a contiguous file
        if (!file.createContiguous(sd.vwd(), "RAW.TXT", CACHE_SIZE*BLOCK_COUNT)) error = 2;
    
        // Set the location of the file's blocks
        if (!file.contiguousRange(&bgnBlock, &endBlock)) error = 3;
        file.close();
        
    //    memset(pCache, '-', CACHE_SIZE);
    
        // Tell card to setup for multiple block write with pre-erase
        if (!sd.card()->erase(bgnBlock, endBlock)) error = 4;
        
        pCache = (uint8_t*)sd.vol()->cacheClear();
    
        if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) error = 5;
    }

    Code for sendSDdata (bool dummy) function which throws error 6:

    Code:
    void sendSDdata(bool dummy)
    {
        if (dummy) {
            // Populate cache with dummy data
            uint32_t adc_dat = 4276803;
            
            for (int iii = 1; iii < 511; iii += 6) {
                pCache[iii - 1]     =   adc_dat;
                pCache[iii]         =   adc_dat >> 8;
                pCache[iii + 1]     =   adc_dat >> 16;
                pCache[iii + 2]     =   'X';
                pCache[iii + 3]     =   '\r';
                pCache[iii + 4]     =   '\n';
            }
            
            for (int iii = 1; iii < 511; iii += 6) {
                Serial.print(pCache[iii - 1], DEC);
                Serial.print(",");
                Serial.print(pCache[iii], DEC);
                Serial.print(",");
                Serial.print(pCache[iii + 1], DEC);
                Serial.print(",");
                Serial.print(pCache[iii + 2], DEC);
                Serial.print(",");
                Serial.print(pCache[iii + 3], DEC);
                Serial.print(",");
                Serial.print(pCache[iii + 4], DEC);
                Serial.print("\n");
            }
        }
        
        // Send data to the SD card and measure latency
        uint32_t tw = micros();
        if (!sd.card()->writeData(pCache)) error = 6;
        tw = micros() - tw;
        
        delay(1000);
        if (!sd.card()->writeStop()) error = 7;
    //    file.close();
        
        latency[block_count-1] = tw;
    }
    I don't fully understand the SD card operation and that's the most likely reason my setup doesn't work, so I'd really appreciate if someone could point out any problems with this setup and the sequence of steps I'm following.
    Last edited by tingo; 07-17-2014 at 03:15 PM.

  2. #2
    Senior Member
    Join Date
    Jan 2013
    Location
    San Francisco Bay Area
    Posts
    641
    I'm assuming you are using sd and not sdfat. sdfat could do it differently. I'm pretty fuzzy on this, so just trying to point you possibly in a useful direction.

    I believe this is the inherent difficulty with sharing these types of resources (SPI, DMA, etc). Paul has been working on a more generalized solution to get things to play nice together.
    You might find it useful to look at the code Paul did for the audio adapter, cause he basically had to do this. I hope I have that right.

    Check out this thread: http://forum.pjrc.com/threads/25582-...d-main-program

    I've used the interval timer for sampling from the t3 ADC but not an external ADC like yours, using block writes to the uSD card.

    Also, I have one of these ads129x chips. Was there a board that you found easily enough to solder it onto, or a certain technique?

  3. #3
    Quote Originally Posted by linuxgeek View Post
    I'm assuming you are using sd and not sdfat. sdfat could do it differently. I'm pretty fuzzy on this, so just trying to point you possibly in a useful direction.
    I'm using the sdfatlib from here: https://code.google.com/p/sdfatlib/downloads/list. Based on what I've read it provides the fastest access to the card. Based on this post (http://forum.pjrc.com/threads/25582-...ll=1#post45745) Paul is considering to replace the old SD code with the latest from Bill Greiman (author of sdfatlib) when Arduino jumps to 1.5.x.

    Quote Originally Posted by linuxgeek View Post
    I believe this is the inherent difficulty with sharing these types of resources (SPI, DMA, etc). Paul has been working on a more generalized solution to get things to play nice together.
    You might find it useful to look at the code Paul did for the audio adapter, cause he basically had to do this. I hope I have that right.

    Check out this thread: http://forum.pjrc.com/threads/25582-...d-main-program

    I've used the interval timer for sampling from the t3 ADC but not an external ADC like yours, using block writes to the uSD card.
    Thanks for the link to the thread, yes it's definitely relevant for the overall project, because I was planning to use interrupts to get data form the ADC and that is bound to create contention for the SPI bus between the ADC and the SD card.

    On the other hand what I'm doing right now is purely sequential access to the SPI bus. As far as I understand each one of these functions accessing the SD card are logically complete:

    Code:
    if (!sd.begin(chipSelect, SPI_SIXTEENTH_SPEED)) {
            error = 1;
            sd.initErrorHalt();
        }
    
        // Delete possible existing file
        sd.remove("RAW.TXT");
        
        // Create a contiguous file
        if (!file.createContiguous(sd.vwd(), "RAW.TXT", CACHE_SIZE*BLOCK_COUNT)) error = 2;
    
        // Set the location of the file's blocks
        if (!file.contiguousRange(&bgnBlock, &endBlock)) error = 3;
        file.close();
        
    //    memset(pCache, '-', CACHE_SIZE);
    
        // Tell card to setup for multiple block write with pre-erase
        if (!sd.card()->erase(bgnBlock, endBlock)) error = 4;
        
        pCache = (uint8_t*)sd.vol()->cacheClear();
    
        if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) error = 5;
    
        if (!sd.card()->writeData(pCache)) error = 6;
    
        if (!sd.card()->writeStop()) error = 7;
    so accessing the SPI bus between them (to communicate with another device) shouldn't create any contention issues, or am I wrong?

    EDIT: I have tried the SDFat library fix suggested by Paul here: http://forum.pjrc.com/threads/25582-...ll=1#post47263 but the error is still there.

    Quote Originally Posted by linuxgeek View Post
    Also, I have one of these ads129x chips. Was there a board that you found easily enough to solder it onto, or a certain technique?
    I got this LQFP-64 to DIP adapter from Proto Advantage: http://www.proto-advantage.com/store...cts_id=2200113.
    You can just use the "drag" soldering techniques to solder ADC to it, as described here: https://www.youtube.com/watch?v=wUyetZ5RtPs
    Then I put everything on the breadboard, here's the pic (sorry that it's upside down, but I can't figure out how to rotate it since both my mac and windows inside parallels see it the correct way round)):
    Click image for larger version. 

Name:	photo.JPG 
Views:	272 
Size:	49.3 KB 
ID:	2397.

    Obviously this setup is not ideal in terms of noise, but the idea is to make the different components work together and then create a PCB (with ADC+passives and SD card socket on it) which would attach to Teensy. Even with the breadboard I was able to obtain fairly clean ECG and EEG signals.
    Last edited by tingo; 07-18-2014 at 10:08 AM. Reason: Tried the bug fix in SDFat library suggested by Paul

  4. #4
    Senior Member
    Join Date
    Jan 2013
    Location
    San Francisco Bay Area
    Posts
    641
    You might want to follow this thread:
    http://forum.pjrc.com/threads/26148-...and-Teensy-3-1

  5. #5
    Cool, thanks!

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,082
    I received the ADS1294 chip today.

    Any word on code to reproduce this problem?

  7. #7
    Great! I hope to have it done tomorrow, if not - Monday.

  8. #8
    Paul, so I've written the test for my setup over the weekend and it seems to work no problem so far it seems like my original issues were the result of the SD cards behaving in a very strange way (as described here: http://forum.pjrc.com/threads/26242-...d-test-request). I will be doing quite a bit more testing in the next few days and will keep you updated of any issues I run into.

  9. #9
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,082
    Ok, thanks and good to know things are looking better.

    I'm curious to hear how the ADS1294 works out. Occasionally I hear requests for a library or sample code for this chip or the 8 channel version. If you get it working, I hope you'll consider sharing the code. I'm sure other people will find it really useful.

  10. #10
    Yes I'll share the code as soon as I have it fully tested.

  11. #11
    Got extremely busy with another project, thus couldn't dedicate much time to this, but here's the very preliminary ADS129x library if anyone's interested.

    Hopefully I'll get back to it in the next few weeks.

    ADC129xADC.zip

  12. #12
    Senior Member Constantin's Avatar
    Join Date
    Nov 2012
    Location
    In the yard with a 17' Dia. Ferris Wheel
    Posts
    1,408
    Hey that's awesome and thanks. I have no plans to use that ADC for now but it's great of you to make the code available for the day that any of us in the community might need it!

  13. #13
    Junior Member
    Join Date
    Feb 2016
    Posts
    1
    hello

    i am facing an error , i am using ads1298 with teensy3.2 it is detecting id and channels but when i am using matlab to decode this through GUI everything thing looking good but there is an error occurring about timerfcn please help me out...

    error is

    Channels available: 8 Device ID register value: 146
    ads Data: Recording 5 channels at 2000 Hz attached to port "COM3"
    Error while evaluating TimerFcn for timer 'timer-3'

    Double inputs must have integer values in the range of ASSUMEDTYPE.

  14. #14
    Quote Originally Posted by Sidharth View Post
    hello

    i am facing an error , i am using ads1298 with teensy3.2 it is detecting id and channels but when i am using matlab to decode this through GUI everything thing looking good but there is an error occurring about timerfcn please help me out...

    error is

    Channels available: 8 Device ID register value: 146
    ads Data: Recording 5 channels at 2000 Hz attached to port "COM3"
    Error while evaluating TimerFcn for timer 'timer-3'

    Double inputs must have integer values in the range of ASSUMEDTYPE.
    It's hard to tell what the problem is without further details... Which GUI are you using? What firmware do you have running on Teensy? Is is matlab that's decoding the sample values or the Teensy?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •