ADS1256 - Fully working code and demonstration
In this long video, you will see all the tiny bits and details of how to make the ADS1256 work with Arduino/STM32 (using STM32duino). I explain you everything from how to connect the ADS1256 with the microcontrollers, to how to receive data continuously from all the 8 channels. It took some time and effort to develop the code, because I confirmed each function bit-by-bit by checking all the SPI communication with a logic analyzer. Nevertheless, the code can still contain errors or inefficient solutions.
If you download and use the code, please consider subscribing to my channel. You can also support me so I can share similar content in the future.
Also, check this relevant playlist which contains a lot of details about the ADS1256.
You should also take a look at the datasheet of the ADS1256.
Arduino / STM32 source code
/*Information * The code is written by Curious Scientist * https://curiousscientist.tech * * Playlist for more ADS1256-related videos * https://www.youtube.com/playlist?list=PLaeIi4Gbl1T-RpVNM8uKdiV1G_3t5jCIu * * If you use the code, please subscribe to my channel * https://www.youtube.com/c/CuriousScientist?sub_confirmation=1 * * I also accept donations * https://www.paypal.com/donate/?hosted_button_id=53CNKJLRHAYGQ * * The code belongs to the following video * https://youtu.be/rsi9o5PQzwM * */ //-------------------------------------------------------------------------------- /* The green board has a RESET pin and the blue does not. Therefore, for the blue board, the reset function is disabled. BUFEN should be enabled for precise measurement, but in that case, the max input voltage cannot be higher than AVDD-2 V. */ //-------------------------------------------------------------------------------- // Pin configuration - STM32F103 [Arduino]: Pins in square brackets [xx] are for Arduino Uno or Nano. /* SPI default pins: MOSI - PA7[11] // DIN MISO - PA6[12] // DOUT SCK - PA5[13] // SCLK SS - PA4[10] // CS -------------------- -------------------- MOSI: Master OUT Slave IN -> DIN MISO: Master IN Slave OUT -> DOUT -------------------- -------------------- Other pins - You can assign them to any pins RST - PA3 DRDY - PA2 // this is an interrupt pin PDWN - +3.3 V PDWN - PA1 (Alternatively, if you want to switch it) */ //-------------------------------------------------------------------------------- //Clock rate /* f_CLKIN = 7.68 MHz tau = 130.2 ns */ //-------------------------------------------------------------------------------- //REGISTERS /* REG VAL USE 0 54 Status Register, Everything Is Default, Except ACAL and BUFEN 1 1 Multiplexer Register, AIN0 POS, AIN1 NEG 2 0 ADCON, Everything is OFF, PGA = 1 3 99 DataRate = 50 SPS 4 225 GPIO, Everything Is Default */ //-------------------------------------------------------------------------------- #include <SPI.h> //SPI communication //-------------------------------------------------------------------------------- // The setup() function runs once each time the micro-controller starts void setup() { Serial.begin(115200); //We will need high datarate, so it should be a high baud rate delay(1000); Serial.println("*ADS1256 Initialization...."); //Some message initialize_ADS1256(); //run the initialization function delay(1000); Serial.println("*Initialization finished!"); //Confirmation message reset_ADS1256(); //Reset the ADS1256 userDefaultRegisters(); //Set up the default registers printInstructions(); //Print the instructions for the commands used in the code } //-------------------------------------------------------------------------------- //Variables double VREF = 2.50; //Value of V_ref. In case of internal V_ref, it is 2.5 V double voltage = 0; //Converted RAW bits. int CS_Value; //we use this to store the value of the bool, since we don't want to directly modify the CS_pin //Pins const byte CS_pin = PA4; //goes to CS on ADS1256 const byte DRDY_pin = PA2; //goes to DRDY on ADS1256 const byte RESET_pin = PA3; //goes to RST on ADS1256 //const byte PDWN_PIN = PA1; //Goes to the PDWN/SYNC/RESET pin (notice, that some boards have different pins!) //The above pins are described for STM32. For Arduino, you have to use a different pin definition ("PA" is not used in "PA4) //Values for registers uint8_t registerAddress; //address of the register, both for reading and writing - selects the register uint8_t registerValueR; //this is used to READ a register uint8_t registerValueW; //this is used to WRTIE a register int32_t registerData; //this is used to store the data read from the register (for the AD-conversion) uint8_t directCommand; //this is used to store the direct command for sending a command to the ADS1256 String ConversionResults; //Stores the result of the AD conversion String PrintMessage; //this is used to concatenate stuff into before printing it out. //-------------------------------------------------------------------------------- void loop() { if (Serial.available() > 0) { char commandCharacter = Serial.read(); //we use characters (letters) for controlling the switch-case switch (commandCharacter) //based on the command character, we decide what to do { case 'r': //this case is used to READ the value of a register //Relevant info on registers: https://youtu.be/wUEx6pEHi2c //Ask the user to pick a register //Register map: Table 23 in the Datasheet Serial.println("*Which register to read?"); //I put the "*" in front of every text message, so my processing software ignores them registerAddress = Serial.parseInt(); //we parse the entered number as an integer and pass it to the registerAddress variable //Wait for the input; Example command: "r 1". This will read the register 1 which is the MUX register (they start at 0!) while (!Serial.available()); //Text before the print - string concatenation PrintMessage = "*Value of register " + String(registerAddress) + " is " + String(readRegister(registerAddress)); Serial.println(PrintMessage); //printing the confirmation message PrintMessage = ""; //reset the value of the variable break; case 'w': //this case is used to WRITE the value of a register //Ask the user to pick a register Serial.println("*Which Register to write?"); //Wait for the input while (!Serial.available()); registerAddress = Serial.parseInt(); //Store the register in serialData //Ask for the value we want to write Serial.println("*Which Value to write?"); //wait for the input; //Example command: "w1 1". This will write the value 1 in the MUX register. This will set the AIN0(+) + AIN1(-) as the input. while (!Serial.available()); registerValueW = Serial.parseInt(); //Write the serialData register with the recent input value (Serial.parseInt()) writeRegister(registerAddress, registerValueW); //The writeRegister() function expects 2 arguments delay(500); //wait PrintMessage = "*The value of the register now is: " + String(readRegister(registerAddress)); Serial.println(PrintMessage); //printing a confirmation message PrintMessage = ""; break; case 't': //this case is used to print a message to the serial terminal. Just to test the connection. Serial.println("*Test message triggered by serial command"); break; case 'O': //this case is used to read a single value from the AD converter readSingle(); //The readSingle() function returns the result of a single conversion. break; case 'R': //this does a RESET on the ADS1256 reset_ADS1256(); //the reset_ADS1256() function resets all the register values break; case 's': //SDATAC - Stop Read Data Continously SPI.transfer(B00001111); //Sending a direct code for SDATAC // Figure 33 in datasheet break; case 'p': //Printing the instructions printInstructions(); //this function prints the commands used to control the ADS1256 break; case 'C': //Single-ended mode cycling cycleSingleEnded(); //the cycleSingleEnded() function cycles through ALL the 8 single ended channels //single ended channels: A0+COM, A1+COM, A2+COM...A7+COM break; case 'D': //differential mode cycling cycleDifferential(); //the cycleDifferential() function cycles through ALL the 4 differential channels //differential channels: A0+A1, A2+A3, A4+A5, A6+A7 break; case 'H': //"high-speed" differential mode cycling cycleDifferential_HS(); //the cycleDifferential_HS() function cycles through ALL the 4 differential channels //differential channels: A0+A1, A2+A3, A4+A5, A6+A7 break; case 'd': //direct command - See Table 24 in datasheet while (!Serial.available()); directCommand = Serial.parseInt(); sendDirectCommand(directCommand); //This function sends a standalone command Serial.println("*Direct command performed!"); break; case 'A': //Single channel continous reading - MUX is manual, can be single and differential too readSingleContinuous(); //the readSingleContinuous() continuously returns a single-ended channel's conversion break; case 'U'://Set everything back to default userDefaultRegisters(); //the userDefaultRegisters() function writes the default values //The default values are determined by the user, therefore it has to be done in this code //This is more like a shortcut for quickly resetting the values to those that you usually use break; } } } //-------------------------------------------------------------------------------- //Functions //Reading registers //DIN should receive 2 command bytes //1st command byte: address of the register (for example MUX: 1 / 01h / 0000 0001) //2nd command byte: number of bytes to read (0000 0001 - 1 byte) //delay between the end of the command and the beginning of the shifting out of the data on DOUT is t6 = 50 * tau_CLKIN // 50 * 130.2 ns = 6510 ns = 6.51 us //Seems like, according to the SPI analyzer, 5 us delay is sufficient. //When writing the register, the time between the last clock for RREG command an the first clock of the answer is t = 6.5 us. unsigned long readRegister(uint8_t registerAddress) //Function for READING a selected register { //Relevant video: https://youtu.be/KQ0nWjM-MtI while (digitalRead(DRDY_pin)) {} //we "stuck" here until the DRDY changes its state SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); //SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0, clock phase: 1. //CS must stay LOW during the entire sequence [Ref: P34, T24] digitalWrite(CS_pin, LOW); //CS_pin goes LOW SPI.transfer(0x10 | registerAddress); //0x10 = RREG SPI.transfer(0x00); delayMicroseconds(5); //see t6 in the datasheet registerValueR = SPI.transfer(0xFF); //0xFF is sent to the ADS1256 which returns us the register value digitalWrite(CS_pin, HIGH); //CS_pin goes HIGH SPI.endTransaction(); return registerValueR; //return the registers value } void writeRegister(uint8_t registerAddress, uint8_t registerValueW) { //Relevant video: https://youtu.be/KQ0nWjM-MtI while (digitalRead(DRDY_pin)) {} //we "stuck" here until the DRDY changes its state SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); //SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0, clock phase: 1. //CS must stay LOW during the entire sequence [Ref: P34, T24] digitalWrite(CS_pin, LOW); //CS_pin goes LOW delayMicroseconds(5); //see t6 in the datasheet SPI.transfer(0x50 | registerAddress); // 0x50 = WREG SPI.transfer(0x00); SPI.transfer(registerValueW); //we write the value to the above selected register digitalWrite(CS_pin, HIGH); //CS_pin goes HIGH SPI.endTransaction(); } void reset_ADS1256() { SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); // initialize SPI with clock, MSB first, SPI Mode1 digitalWrite(CS_pin, LOW); //CS_pin goes LOW delayMicroseconds(10); //wait SPI.transfer(0xFE); //Reset delay(2); //Minimum 0.6 ms required for Reset to finish. SPI.transfer(0x0F); //Issue SDATAC delayMicroseconds(100); digitalWrite(CS_pin, HIGH); //CS_pin goes HIGH SPI.endTransaction(); Serial.println("*Reset DONE!"); //confirmation message } void initialize_ADS1256() //starting up the chip by making the necessary steps. This is in the setup() of the Arduino code. { //Setting up the pins first //Chip select pinMode(CS_pin, OUTPUT); //Chip select is an output digitalWrite(CS_pin, LOW); //Chip select LOW SPI.begin(); //start SPI (Arduino/STM32 - ADS1256 communication protocol) //The STM32-ADS1256 development board uses a different SPI channel (SPI_2) //For more info: https://youtu.be/3Rlr0FCffr0 CS_Value = CS_pin; //We store the value of the CS_pin in a variable //DRDY pinMode(DRDY_pin, INPUT); //DRDY is an input pinMode(RESET_pin, OUTPUT); //RESET pin is an output digitalWrite(RESET_pin, LOW); //RESET is set to low delay(500); // Wait digitalWrite(RESET_pin, HIGH); //RESET is set to high delay(500); // Wait } void readSingle() { //Relevant video: https://youtu.be/4-A8aJ5BzIs //Reading a single value registerData = 0; // every time we call this function, this should be 0 in the beginning! //Wait for DRDY to go LOW while (digitalRead(DRDY_pin)) {} SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); digitalWrite(CS_pin, LOW); //REF: P34: "CS must stay low during the entire command sequence" //Issue RDATA (0000 0001) command SPI.transfer(B00000001); //Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(10); //according to the SPI analyser, the time between DIN and MSB = ~6.833 us //SPI.transfer(0x0F) probably also takes some time, therefore less delay is enough. //step out the data: MSB | mid-byte | LSB, //registerData is ZERO registerData |= SPI.transfer(0x0F); //MSB comes in, first 8 bit is updated // '|=' compound bitwise OR operator registerData <<= 8; //MSB gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //MSB | Mid-byte registerData <<= 8; //MSB | Mid-byte gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //(MSB | Mid-byte) | LSB - final result //After this, DRDY should go HIGH automatically Serial.println(registerData); //printing the RAW data convertToVoltage(registerData); // converting and printing the raw data as well (unit is Volts) digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it back to HIGH SPI.endTransaction(); } void readSingleContinuous() //Continuously reading 1 single-ended channel (i.e. A0+COM) { //Relevant video: https://youtu.be/4-A8aJ5BzIs //Some commands should only be initiated in the beginning of this type of acquisition (RDATAC) //Therefore, we run them outside the while() loop. registerData = 0; // every time we call this function, this should be 0 in the beginning! SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); digitalWrite(CS_pin, LOW); //REF: P34: "CS must stay low during the entire command sequence" //Issue RDATAC (0000 0011) command SPI.transfer(B00000011); //Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(5); while (Serial.read() != 's') //while the reading is not interrupted by a command from the serial terminal { //Reading a single input continuously using the RDATAC while (digitalRead(DRDY_pin)) {} //Wait for DRDY to go LOW delayMicroseconds(5); //step out the data: MSB | mid-byte | LSB, //registerData is ZERO //Previous, we used 0x0F, here we use 0 for the SPI.transfer() argument; registerData |= SPI.transfer(0); //MSB comes in, first 8 bit is updated // '|=' compound bitwise OR operator registerData <<= 8; //MSB gets shifted LEFT by 8 bits registerData |= SPI.transfer(0); //MSB | Mid-byte registerData <<= 8; //MSB | Mid-byte gets shifted LEFT by 8 bits registerData |= SPI.transfer(0); //(MSB | Mid-byte) | LSB - final result //After this, DRDY should go HIGH automatically Serial.println(registerData); //RAW data //Temporary //convertToVoltage(registerData); //Converted data (units is Volts) registerData = 0; // every time we call this function, this should be 0 in the beginning! } digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it back to HIGH SPI.endTransaction(); } void cycleSingleEnded() //Cycling through all (8) single ended channels { //Relevant video: https://youtu.be/GBWJdyjRIdM int cycle = 1; registerData = 0; SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); while (Serial.read() != 's') { for (cycle = 1; cycle < 9; cycle++) { //we cycle through all the 8 single-ended channels with the RDATAC //INFO: //RDATAC = B00000011 //SYNC = B11111100 //WAKEUP = B11111111 //--------------------------------------------------------------------------------------------- /*Some comments regarding the cycling: When we start the ADS1256, the preconfiguration already sets the MUX to [AIN0+AINCOM]. When we start the RDATAC (this function), the default MUX ([AIN0+AINCOM]) will be included in the cycling which means that the first readout will be the [AIN0+AINCOM]. But, before we read the data from the [AIN0+AINCOM], we have to switch to the next register already, then start RDATA. This is demonstrated in Figure 19 on Page 21 of the datasheet. Therefore, in order to get the 8 channels nicely read and formatted, we have to start the cycle with the 2nd input of the ADS1256 ([AIN1+AINCOM]) and finish with the first ([AIN0+AINCOM]). \ CH1 | CH2 CH3 CH4 CH5 CH6 CH7 CH8 \ CH1 | CH2 CH3 ... The switch-case is between the two '|' characters The output (one line of values) is between the two '\' characters. *///------------------------------------------------------------------------------------------- //Steps are on Page21 //Step 1. - Updating MUX while (digitalRead(DRDY_pin)) {} //waiting for DRDY switch (cycle) { //Channels are written manually, so we save time on switching the SPI.beginTransaction on and off. case 1: //Channel 2 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00011000); //AIN1+AINCOM break; case 2: //Channel 3 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00101000); //AIN2+AINCOM break; case 3: //Channel 4 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00111000); //AIN3+AINCOM break; case 4: //Channel 5 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01001000); //AIN4+AINCOM break; case 5: //Channel 6 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01011000); //AIN5+AINCOM break; case 6: //Channel 7 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01101000); //AIN6+AINCOM break; case 7: //Channel 8 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01111000); //AIN7+AINCOM break; case 8: //Channel 1 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00001000); //AIN0+AINCOM break; } //Step 2. //Issue RDATA (0000 0001) command SPI.transfer(B11111100); //SYNC delayMicroseconds(4); //t11 delay 24*tau = 3.125 us //delay should be larger, so we delay by 4 us SPI.transfer(B11111111); //WAKEUP //Step 3. //Issue RDATA (0000 0001) command SPI.transfer(B00000001); //Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(5); //step out the data: MSB | mid-byte | LSB, //registerData is ZERO registerData |= SPI.transfer(0x0F); //MSB comes in, first 8 bit is updated // '|=' compound bitwise OR operator registerData <<= 8; //MSB gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //MSB | Mid-byte registerData <<= 8; //MSB | Mid-byte gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //(MSB | Mid-byte) | LSB - final result //After this, DRDY should go HIGH automatically //Constructing an output ConversionResults = ConversionResults + registerData; ConversionResults = ConversionResults + "\t"; //--------------------- registerData = 0; digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it back to HIGH //Expected output when using a resistor ladder of 1k resistors and the ~+5V output of the Arduino: //Formatting Channel 1 Channel 2 Channel 3 Channel 4 Channel 5 Channel 6 Channel 7 Channel 8 /* 12:41:40.280 -> 4.78714609 4.16558074 3.55143761 2.96154289 2.37305951 1.78396224 1.19539093 0.60204453 12:41:40.450 -> 4.78708410 4.16603088 3.55298733 2.96177434 2.37242603 1.78440055 1.19551980 0.60218434 12:41:40.620 -> 4.78826045 4.16563510 3.55332374 2.96192693 2.37245225 1.78419756 1.19552350 0.60213699 */ } Serial.println(ConversionResults); ConversionResults=""; } SPI.endTransaction(); } void cycleDifferential() { //Relevant viodeo: https://youtu.be/GBWJdyjRIdM int cycle = 1; //outside while() loop, we have to switch to the first differential channel ([AIN0+AIN1]) //writeRegister(1, 1); //B00000001 = 1; [AIN0+AIN1] registerData = 0; SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); //We start this SPI.beginTransaction once. digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00000001); //AIN0+AIN1 digitalWrite(CS_pin, HIGH); SPI.endTransaction(); while (Serial.read() != 's') { for (cycle = 1; cycle < 5; cycle++) { //we cycle through all the 4 differential channels with the RDATA //RDATA = B00000001 //SYNC = B11111100 //WAKEUP = B11111111 //Steps are on Page21 //Step 1. - Updating MUX while (digitalRead(DRDY_pin)) {} switch (cycle) { case 1: //Channel 2 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00100011); //AIN2+AIN3 break; case 2: //Channel 3 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01000101); //AIN4+AIN5 break; case 3: //Channel 4 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01100111); //AIN6+AIN7 break; case 4: //Channel 1 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00000001); //AIN0+AIN1 break; } SPI.transfer(B11111100); //SYNC delayMicroseconds(4); //t11 delay 24*tau = 3.125 us //delay should be larger, so we delay by 4 us SPI.transfer(B11111111); //WAKEUP //Step 3. //Issue RDATA (0000 0001) command SPI.transfer(B00000001); //Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(5); //step out the data: MSB | mid-byte | LSB, //registerData is ZERO registerData |= SPI.transfer(0x0F); //MSB comes in, first 8 bit is updated // '|=' compound bitwise OR operator registerData <<= 8; //MSB gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //MSB | Mid-byte registerData <<= 8; //MSB | Mid-byte gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //(MSB | Mid-byte) | LSB - final result //After this, DRDY should go HIGH automatically //Constructing an output ConversionResults = ConversionResults + registerData; ConversionResults = ConversionResults + "\t"; //--------------------- registerData = 0; digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it back to HIGH //Expected output when using a resistor ladder of 1k resistors and the ~+5V output of the Arduino: //Formatting Channel 1 Channel 2 Channel 3 Channel 4 /* 16:14:23.066 -> 4.79074764 4.16625738 3.55839943 2.96235866 16:14:23.136 -> 4.79277801 4.16681241 3.55990862 2.96264190 16:14:23.238 -> 4.79327344 4.16698741 3.55968427 2.96277694 */ } Serial.println(ConversionResults); ConversionResults = ""; } SPI.endTransaction(); } void cycleDifferential_HS() //"High-speed" version of the cycleDifferential() function { //Relevant viodeo: https://youtu.be/GBWJdyjRIdM int cycle = 1; registerData = 0; SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); //We start this SPI.beginTransaction once. //Setting up the input channel digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00000001); //AIN0+AIN1 digitalWrite(CS_pin, HIGH); SPI.endTransaction(); while (Serial.read() != 's') { for(int package = 0; package < 10; package++) //We will collect 10 "packages" { for (cycle = 1; cycle < 5; cycle++) { //we cycle through all the 4 differential channels with the RDATA //RDATA = B00000001 //SYNC = B11111100 //WAKEUP = B11111111 //Steps are on Page21 //Step 1. - Updating MUX while (digitalRead(DRDY_pin)) {} //waiting for DRDY switch (cycle) { case 1: //Channel 2 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00100011); //AIN2+AIN3 break; case 2: //Channel 3 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01000101); //AIN4+AIN5 break; case 3: //Channel 4 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B01100111); //AIN6+AIN7 break; case 4: //Channel 1 digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref: P34, T24] SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX SPI.transfer(0x00); SPI.transfer(B00000001); //AIN0+AIN1 break; } SPI.transfer(B11111100); //SYNC delayMicroseconds(4); //t11 delay 24*tau = 3.125 us //delay should be larger, so we delay by 4 us SPI.transfer(B11111111); //WAKEUP //Step 3. //Issue RDATA (0000 0001) command SPI.transfer(B00000001); //Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(5); //step out the data: MSB | mid-byte | LSB, //registerData is ZERO registerData |= SPI.transfer(0x0F); //MSB comes in, first 8 bit is updated // '|=' compound bitwise OR operator registerData <<= 8; //MSB gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //MSB | Mid-byte registerData <<= 8; //MSB | Mid-byte gets shifted LEFT by 8 bits registerData |= SPI.transfer(0x0F); //(MSB | Mid-byte) | LSB - final result //After this, DRDY should go HIGH automatically //Constructing an output ConversionResults = ConversionResults + registerData; if(cycle < 4) { ConversionResults = ConversionResults + "\t"; } else { ConversionResults = ConversionResults; } //--------------------- registerData = 0; digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it back to HIGH //Expected output when using a resistor ladder of 1k resistors and the ~+5V output of the Arduino: //Formatting Channel 1 Channel 2 Channel 3 Channel 4 /* 16:14:23.066 -> 4.79074764 4.16625738 3.55839943 2.96235866 16:14:23.136 -> 4.79277801 4.16681241 3.55990862 2.96264190 16:14:23.238 -> 4.79327344 4.16698741 3.55968427 2.96277694 */ } ConversionResults = ConversionResults + '\n'; //Add a linebreak after a line of data (4 columns) } Serial.print(ConversionResults); //print everything after 10 packages ConversionResults = ""; } SPI.endTransaction(); } void sendDirectCommand(uint8_t directCommand) { //Direct commands can be found in the datasheet Page 34, Table 24. //Use binary, hex or dec format. //Here, we want to use everything EXCEPT: RDATA, RDATAC, SDATAC, RREG, WREG //We don't want to involve DRDY here. We just write, but don't read anything. //Start SPI SPI.beginTransaction(SPISettings(1700000, MSBFIRST, SPI_MODE1)); digitalWrite(CS_pin, LOW); //REF: P34: "CS must stay low during the entire command sequence" delayMicroseconds(5); //t6 - maybe not necessary SPI.transfer(directCommand); //Send Command delayMicroseconds(5); //t6 - maybe not necessary digitalWrite(CS_pin, HIGH); //REF: P34: "CS must stay low during the entire command sequence" SPI.endTransaction(); } void userDefaultRegisters() { // This function is "manually" updating the values of the registers then reads them back. // This function should be used in the setup() after performing an initialization-reset process // I use the below listed settings for my "startup configuration" /* REG VAL USE 0 54 Status Register, Everything Is Default, Except ACAL and BUFEN 1 1 Multiplexer Register, AIN0 POS, AIN1 POS 2 0 ADCON, Everything is OFF, PGA = 1 3 99 DataRate = 50 SPS */ //We update the 4 registers that we are going to use delay(500); writeRegister(0x00, B00110110); //STATUS delay(200); writeRegister(0x01, B00000001); //MUX AIN0+AIN1 delay(200); writeRegister(0x02, B00000000); //ADCON delay(200); writeRegister(0x03, B01100011); //DRATE - DEC[99] - 50 SPS delay(500); sendDirectCommand(B11110000); // SELFCAL Serial.println("*Register defaults updated!"); } void printInstructions() { //This function should be in the setup() and it shows the commands PrintMessage = "*Use the following letters to send a command to the device:" + String("\n") + "*r - Read a register. Example: 'r1' - reads the register 1" + String("\n") + "*w - Write a register. Example: 'w1 8' - changes the value of the 1st register to 8." + String("\n") + "*O - Single readout. Example: 'O' - Returns a single value from the ADS1256." + String("\n") + "*A - Single, continuous reading with manual MUX setting." + String("\n") + "*C - Cycling the ADS1256 Input multiplexer in single-ended mode (8 channels). " + String("\n") + "*D - Cycling the ADS1256 Input multiplexer in differential mode (4 channels). " + String("\n") + "*H - Cycling the ADS1256 Input multiplexer in differential mode (4 channels) at high-speed. " + String("\n") + "*R - Reset ADS1256. Example: 'R' - Resets the device, everything is set to default." + String("\n") + "*s - SDATAC: Stop Read Data Continuously." + String("\n") + "*U - User Default Registers." + String("\n") + "*t - triggers a test message." + String("\n") + "*d - Send direct command."; Serial.println(PrintMessage); PrintMessage = ""; //Reset (empty) variable. } void convertToVoltage(int32_t registerData) { if (long minus = registerData >> 23 == 1) //if the 24th bit (sign) is 1, the number is negative { registerData = registerData - 16777216; //conversion for the negative sign //"mirroring" around zero } voltage = ((2*VREF) / 8388608)*registerData; //2.5 = Vref; 8388608 = 2^{23} - 1 //Basically, dividing the positive range with the resolution and multiplying with the bits Serial.println(voltage, 8); //print it on serial, 8 decimals voltage = 0; //reset voltage } //-------------------------------------------------------------------------------- //end of code //-------------------------------------------------------------------------------- //Good to know things /* TAU = 1/f = 1/7.68 MHz = 130.2 ns For a 7.68 MHz ADC clock, the SCLK may not exceed 1.92 MHz. Binary to decimal is read from right to left 8 bit = 1 byte 0000 0001 = 1 (2^0) 0000 0011 = 3 (2^0 + 2^1) 1000 0001 = 129 (2^0 + 2^7) PAGE 34 of the datasheet, Table 24: Command definitions RREG and WREG require a second command byte plus data. The ORDER bit in the STATUS register sets the order of the bits within the output data. CS must stay LOW during the entire sequence. The operator '|' is the bitwise OR operator. Example: 0 0 1 1 operand1 0 1 0 1 operand2 ---------- 0 1 1 1 (operand1 | operand2) - returned result //--------------------------------------------------- DRDY goes LOW when new data is available. - When all the 24 bits have been read back, it goes back to HIGH. The operator '|=' is the compound bitwise operator. OUTPUT CODES //REF Page23 Table16 1: Vin (AIN_P - AIN_N) > (2Vref)/PGA 7FFFFFh = 8388607 = 0111 1111 1111 1111 1111 1111 2: Vin (AIN_P - AIN_N) < (-2Vref)/PGA *((2^23)/(2^23 -1)) 800000h = 8388608 = 1000 0000 0000 0000 0000 0000 */