Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
2 views

ArduinoSTM32 source code

The document provides a detailed code implementation for configuring and using the ADS1256 ADC with an STM32F103C8T6 microcontroller via SPI communication. It includes pin configurations, clock rates, setup functions, and various commands for reading and writing to the ADC registers. Additionally, it features interrupt handling and continuous data reading methods, demonstrating the functionality of the ADC in a microcontroller environment.

Uploaded by

thar.m.badri
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

ArduinoSTM32 source code

The document provides a detailed code implementation for configuring and using the ADS1256 ADC with an STM32F103C8T6 microcontroller via SPI communication. It includes pin configurations, clock rates, setup functions, and various commands for reading and writing to the ADC registers. Additionally, it features interrupt handling and continuous data reading methods, demonstrating the functionality of the ADC in a microcontroller environment.

Uploaded by

thar.m.badri
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 11

// Pin configuration - STM32F103C8T6

/*
SPI default pins:
MOSI - PA7 // DIN
MISO - PA6 // DOUT
SCK - PA5 // SCLK
SS - PA4 // CS
--------------------
MOSI: Master OUT Slave IN -> DIN
MISO: Master IN Slave OUT -> DOUT
--------------------
Other pins
RST - PA3
DRDY - PA2 // this is an interrupt pin
PDWN/SYNC - +3.3 V or PA1
*/
//--------------------------------------------------------------------------------
//Clock rate
/*
f_CLKIN = 7.68 MHz
tau = 130.2 ns
*/
//--------------------------------------------------------------------------------

#include <SPI.h> //SPI comunication

void setup()
{
Serial.begin(115200);
delay(1000);
initialize_ADS1256();
delay(1000);
reset_ADS1256();
userDefaultRegisters();
//printInstructions();
attachInterrupt(digitalPinToInterrupt(PA2), checkDReady, FALLING); //FALLING -
DRDY goes low
}
//--------------------------------------------------------------------------------
//Variables

//Booleans
volatile boolean dataReady; //It is very important to have it defined as volatile!
double VREF = 2.50; //VREF for converting the raw data to human-readable voltage

//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 pin - Alternatively can be
permanently tied to 3.3 V

//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 WRITE 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 PrintMessage; //this is used to concatenate stuff into before printing it
out.

byte outputBuffer[3]; //3-byte (24-bit) buffer for the fast acquisition - Single-
channel, continuous
byte differentialBuffer[12]; //4x3-byte buffer for the fast differential-channel
acquisition -
byte singleBuffer[24]; //8x3-byte buffer for the fast single-ended-channel
acquisition
//float StartTime; //This only serves test purposes

//--------------------------------------------------------------------------------
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
while (!Serial.available()); //wait for the serial
registerAddress = Serial.parseInt(); //parse the address of the register
/*
//Wait for the input
while (!Serial.available());
Text before the print
PrintMessage = "*Value of register " + String(registerAddress) + " is " +
String(readRegister(registerAddress));
Serial.println(PrintMessage);
PrintMessage = ""; //resetting value
*/
break;

case 'w': //this case is used to WRITE the value of a register


while (!Serial.available()); //wait for the serial
registerAddress = Serial.parseInt(); //Store the register in
registerAddress
delay(100);
while (!Serial.available());
registerValueW = Serial.parseInt(); //Store the value to be written
delay(100);
writeRegister(registerAddress, registerValueW);
delay(500);
break;

case 't': //this case is used to print a message to the serial terminal. Just
to test the connection...etc.
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();
break;

case 'R': //this does a RESET on the ADS1256


reset_ADS1256();
break;

case 's': //SDATAC - Stop Reading Data Continously


SPI.transfer(B00001111);
break;

case 'A': //Single channel continous reading - MUX is manual, can be single
and differential too
readSingleContinuous();
break;

case 'C': //Single-ended mode cycling


cycleSingleEnded();
break;

case 'D': //differential mode cycling


cycleDifferential();
break;

case 'd': //direct command


while (!Serial.available());
directCommand = Serial.parseInt();
sendDirectCommand(directCommand);
break;

case 'U'://Set everything back to default


userDefaultRegisters();
break;
}
}
}
//--------------------------------------------------------------------------------
//Functions

void checkDReady() //Function for the ISR


{
dataReady = true;
}

unsigned long readRegister(uint8_t registerAddress) //Function for READING a


selected register
{
SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1));
//SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0,
clock phase: 1.

digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref:
P34, T24]

SPI.transfer(0x10 | registerAddress); //0x10 = 0001000 = RREG - OR together the


two numbers (command + address)

SPI.transfer(0x00); //2nd (empty) command byte

delayMicroseconds(5); //see t6 in the datasheet

registerValueR = SPI.transfer(0xFF); //read out the register value


digitalWrite(CS_pin, HIGH);
SPI.endTransaction();

return registerValueR;
}

void writeRegister(uint8_t registerAddress, uint8_t registerValueW)


{
SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1));
//SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0,
clock phase: 1.

digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref:
P34, T24]

delayMicroseconds(5); //see t6 in the datasheet

SPI.transfer(0x50 | registerAddress); // 0x50 = 01010000 = WREG

SPI.transfer(0x00);

SPI.transfer(registerValueW);

digitalWrite(CS_pin, 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);

delayMicroseconds(7);

SPI.transfer(0xFE); //Reset command

delay(2); //Minimum 0.6ms required for Reset to finish.

SPI.transfer(0x0F); //Issue SDATAC (any 8-bit value would complete the RESET
command)

delayMicroseconds(100);

digitalWrite(CS_pin, HIGH);

SPI.endTransaction();
}

void initialize_ADS1256() //starting up the chip by making the necessary steps.


This goes into the setup() later.
{
//Chip select
pinMode(CS_pin, OUTPUT); //Chip select is an output
digitalWrite(CS_pin, LOW);

//DRDY
pinMode(DRDY_pin, INPUT);
//Reset
pinMode(RESET_pin, OUTPUT);
//We do a manual chip reset on the ADS1256 - Datasheet Page 27/ RESET
digitalWrite(RESET_pin, LOW);
delay(100);
digitalWrite(RESET_pin, HIGH); //RESET is set to high
delay(500);

SPI.begin();
}

void readSingle() //Reading a single value ONCE using the RDATA command
{
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"

while (dataReady == false) {} //Wait for DRDY to go LOW


SPI.transfer(B00000001); //Issue RDATA (0000 0001) command
delayMicroseconds(7); //Wait t6 time (~6.51 us) REF: P34, FIG:30.

//step out the data: MSB | mid-byte | LSB,


registerData = SPI.transfer(0x0F); //MSB comes in, first 8 bit is updated
registerData <<= 8; //MSB gets shifted LEFT by 8 bits
registerData |= SPI.transfer(0x0F); //MSB | Mid-byte // '|=' compound bitwise OR
operator
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.print("Raw data: ");


Serial.println(registerData); //prints the raw data (24-bit number)
Serial.print("Voltage: ");
convertToVoltage(registerData); //prints the converted data (PGA should be 0!)

digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it


back to HIGH
SPI.endTransaction();
}

void readSingleContinuous() //Reads the recently selected channel using RDATAC


{
SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1));
digitalWrite(CS_pin, LOW); //REF: P34: "CS must stay low during the entire
command sequence"

//These variables serve only testing purposes!


//uint32_t loopcounter = 0;
//StartTime = micros();
//--------------------------------------------

while (dataReady == false) {} //Wait until DRDY does low, then issue the command
SPI.transfer(B00000011); //Issue RDATAC (0000 0011) command after DRDY goes low
delayMicroseconds(7); //Wait t6 time (~6.51 us) REF: P34, FIG:30.

while (Serial.read() != 's')


{
//while (GPIOA->regs->IDR & 0x0004){} //direct port access to A2 (DRDY) pin -
less reliable polling alternative
while (dataReady == false) {} //waiting for the dataReady ISR
//Reading a single input continuously using the RDATAC
//step out the data: MSB | mid-byte | LSB
outputBuffer[0] = SPI.transfer(0); // MSB comes in
outputBuffer[1] = SPI.transfer(0); // Mid-byte
outputBuffer[2] = SPI.transfer(0); // LSB - final conversion result
//After this, DRDY should go HIGH automatically
Serial.write(outputBuffer, sizeof(outputBuffer)); //this buffer is [3]
dataReady = false; //reset dataReady manually

/*
//These variables only serve test purposes!
loopcounter++;
//if(micros() - StartTime >= 5000000) //5 s
if(loopcounter >= 150000)
{
Serial.print(" Loops: ");
Serial.println(loopcounter++);
Serial.println(micros() - StartTime);
break; //exit the whole thing
}
*/
}
SPI.transfer(B00001111); //SDATAC stops the RDATAC - the received 's' just breaks
the while(), this stops the acquisition
digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it
back to HIGH
SPI.endTransaction();
}

void cycleSingleEnded()
{
int cycle = 0;
SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1));
digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref:
P34, T24]
while (Serial.read() != 's')
{
for (cycle = 0; cycle < 8; 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.

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 Page 21 of the datasheet
while (dataReady == false) {} //direct port access to A2 (DRDY) pin
//Step 1. - Updating MUX
switch (cycle)
{
//Channels are written manually
case 0: //Channel 2
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B00011000); //AIN1+AINCOM
break;

case 1: //Channel 3
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B00101000); //AIN2+AINCOM
break;

case 2: //Channel 4
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B00111000); //AIN3+AINCOM
break;

case 3: //Channel 5
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B01001000); //AIN4+AINCOM
break;

case 4: //Channel 6
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B01011000); //AIN5+AINCOM
break;

case 5: //Channel 7
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B01101000); //AIN6+AINCOM
break;

case 6: //Channel 8
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B01111000); //AIN7+AINCOM
break;

case 7: //Channel 1
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B00001000); //AIN0+AINCOM
break;
}
//Step 2.
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);
delayMicroseconds(7); //Wait t6 time (~6.51 us) REF: P34, FIG:30.

//step out the data: MSB | mid-byte | LSB


singleBuffer[(3 * cycle)] = SPI.transfer(0x0F); //MSB comes in, first 8 bit
is updated
singleBuffer[(3 * cycle) + 1] = SPI.transfer(0x0F); //Mid-byte
singleBuffer[(3 * cycle) + 2] = SPI.transfer(0x0F); //LSB - final result
dataReady = false;
//After this, DRDY should go HIGH automatically
}
//Dump the buffer after all 8 channels are read
Serial.write(singleBuffer, 24);
}
SPI.transfer(B00001111); //SDATAC stops the RDATAC - the received 's' just breaks
the while(), this stops the acquisition
digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it
back to HIGH
SPI.endTransaction();
}

void cycleDifferential() //APPROVED


{
SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1));
//Set the AIN0+AIN1 as inputs manually
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);
delay(50);
digitalWrite(CS_pin, LOW); //CS must stay LOW during the entire sequence [Ref:
P34, T24]

while (Serial.read() != 's')


{
for (int cycle = 0; cycle < 4; cycle++)
{
//Steps are on Page21
//Step 1. - Updating MUX
//DRDY has to go low
while (dataReady == false) {}

switch (cycle)
{
case 0: //Channel 2
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B00100011); //AIN2+AIN3
break;

case 1: //Channel 3
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B01000101); //AIN4+AIN5
break;

case 2: //Channel 4
SPI.transfer(0x50 | 1); // 0x50 = WREG //1 = MUX
SPI.transfer(0x00);
SPI.transfer(B01100111); //AIN6+AIN7
break;

case 3: //Channel 1
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.
SPI.transfer(B00000001); //Issue RDATA (0000 0001) command
delayMicroseconds(7); //Wait t6 time (~6.51 us) REF: P34, FIG:30.

differentialBuffer[(3 * cycle)] = SPI.transfer(0x0F); //MSB comes in, first 8


bit is updated // '|=' compound bitwise OR operator
differentialBuffer[(3 * cycle) + 1] = SPI.transfer(0x0F); //Mid-byte
differentialBuffer[(3 * cycle) + 2] = SPI.transfer(0x0F); //LSB - final
result
dataReady = false;
}
//Dump the buffer after all 4 channels are read
Serial.write(differentialBuffer, 12);
}
SPI.transfer(B00001111); //SDATAC stops the RDATAC - the received 's' just breaks
the while(), this stops the acquisition
digitalWrite(CS_pin, HIGH); //We finished the command sequence, so we switch it
back to HIGH
SPI.endTransaction();
}

void sendDirectCommand(uint8_t directCommand)


{
//Direct commands can be found in the datasheet Page 34, Table 24.
SPI.beginTransaction(SPISettings(1700000, MSBFIRST, SPI_MODE1));
digitalWrite(CS_pin, LOW); //REF: P34: "CS must stay low during the entire
command sequence"
delayMicroseconds(5);
SPI.transfer(directCommand); //Send Command
delayMicroseconds(5);
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 can be used in the setup() after performing an initialization-
reset process
/*
REG VAL USE
0 54 Status Register, Everyting Is Default, Except Auto - Cal
1 1 Multiplexer Register, AIN0 POS, AIN1 POS
2 0 ADCON, Everything is OFF, PGA = 0
3 132 DataRate = 100 SPS
*/
//We update the 4 registers that we are going to use

delay(500);
writeRegister(0x00, B00110110); //STATUS: bit1: bufen=1; bit2: acal=1; rest is
not important or factory default
delay(200);
writeRegister(0x01, B00000001); //MUX AIN0+AIN1
delay(200);
writeRegister(0x02, B00000000); //ADCON - PGA = 0 (+/- 5 V)
delay(200);
writeRegister(0x03, B10000100); //100SPS
delay(500);
sendDirectCommand(B11110000); //Offset and self-gain calibration
}

void printInstructions()
{
//This function should be in the setup() and it shows the commands - not used
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")
+ "*R - Reset ADS1256. Example: 'R' - Resets the device,
everything is set to default." + String("\n")
+ "*s - SDATAC: Stop Read Data Continously." + String("\n")
+ "*U - User Default Registers." + String("\n")
+ "*d - Send direct command.";

Serial.println(PrintMessage);
PrintMessage = ""; //Reset (empty) variable.
}

void convertToVoltage(int32_t registerData)


{
if (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
}
//This is only valid if PGA = 0 (2^0). Otherwise the voltage has to be divided by
2^(PGA)
double voltage = ((2 * VREF) / 8388608) * registerData; //5.0 = Vref; 8388608 =
2^{23} - 1

Serial.println(voltage, 8); //print it on serial


}

You might also like