Other Parts Discussed in Thread: ADS1248
Dear All,
since I got some requests for help about using ADS1248 with PT1000, and we have used successfully, here I am posting:
- schematic example
- how to connect with STM32
- software example
This all for the benefit of people who would like to start projects using the ADS1248 for PT1000 (PT100) interface.
Regards
#include "def.h"
#include "adc.h"
static void setSpi1Clock(void)
{
GPIOB->BSRR = 1 << 13; // PB13
}
static void clrSpi1Clock(void)
{
GPIOB->BRR = 1 << 13; // PB13
}
static void setSpi1Mosi(void)
{
GPIOB->BSRR = 1 << 15;
//PB15
}
static void clrSpi1Mosi(void)
{
GPIOB->BRR = 1 << 15;
//PB15
}
static bool getSpi1Miso(u8 chan)
{
if (chan) {
return (GPIOB->IDR & (1 << 11)) != 0;
} else {
return (GPIOB->IDR & (1 << 14)) != 0;
}
}
// chip select
static void assertCs(u8 chan)
{
if (chan) {
GPIOB->BRR = 1 << 10;
//PB10
} else {
GPIOB->BRR = 1 << 12;
//PB12
}
}
// chip select per compensazione temperatura
static void releaseCs(u8 chan)
{
if (chan) {
GPIOB->BSRR = 1 << 10;
//PB10
} else {
GPIOB->BSRR = 1 << 12;
//PB12
}
}
static void assertReset(void)
{
GPIOC->BRR = 1 << 3; // PC3
}
static void releaseReset(void)
{
GPIOC->BSRR = 1 << 3;
}
static void assertStart(u8 chan)
{
if (chan) {
GPIOC->BSRR = 1 << 13;
//PC13
} else {
GPIOC->BSRR = 1 << 12;
//PC12
}
}
static void releaseStart(u8 chan)
{
if (chan) {
GPIOC->BRR = 1 << 13;
//PC13
} else {
GPIOC->BRR = 1 << 12;
//PC12
}
}
// nota : shiftare 0xff = nop durante fase in cui si vuole lettura !!
static u8 spiAdcSendByte(u8 ch, u8 val)
{
int i;
u8 mask = 0x80;
int ret = 0;
for (i = 0; i < 8; i++) {
delayFix();
if (val & mask) {
setSpi1Mosi();
} else {
clrSpi1Mosi();
}
delayFix();
setSpi1Clock();
delayFix();
ret <<= 1;
ret |= getSpi1Miso(ch);
clrSpi1Clock();
mask >>= 1;
delayFix();
}
return ret;
}
static bool getDRDYConversionComplete(u8 chan)
{
if (chan) {
return (GPIOC->IDR & (1 << 1)) == 0; // PC1
} else {
return (GPIOC->IDR & (1 << 0)) == 0; // PC0
}
}
static void AdcSendSync(u8 chan)
{
assertCs(chan);
delayFix();
spiAdcSendByte(chan, ADS1248_CMD_SYNC);
spiAdcSendByte(chan, ADS1248_CMD_SYNC); // Send two bytes identical
delayFix();
releaseCs(chan);
}
static void AdcSendOneByteCommand(u8 chan, u8 cmd)
{
assertCs(chan);
delayFix();
spiAdcSendByte(chan, cmd);
delayFix();
releaseCs(chan);
}
static void AdcSendSelfOcal(u8 chan)
{
AdcSendOneByteCommand(chan, ADS1248_CMD_SELFOCAL);
}
// 'quanti' � riportato in formato 'umano' 1 = 1 registro, ecc...
// quanti compreso tra 1 e 16 al max
static void AdcWriteReg(u8 chan, u8 add, u8 *data, u8 quanti)
{
assertCs(chan);
delayFix();
spiAdcSendByte(chan, ADS1248_CMD_WREG | add);
spiAdcSendByte(chan, quanti-1);
while (quanti--) {
spiAdcSendByte(chan, *data++);
}
releaseCs(chan);
}
static void AdcReadReg(u8 chan, u8 add, u8 *data, u8 quanti)
{
assertCs(chan);
delayFix();
spiAdcSendByte(chan, ADS1248_CMD_RREG | add);
spiAdcSendByte(chan, quanti-1);
while (quanti--) {
*data++ = spiAdcSendByte(chan, ADS1248_CMD_NOP); // Attention -> NOP (0xff) required!
}
releaseCs(chan);
}
// vale solo se DRDY conversion complete
static u32 AdcReadConversion(u8 chan)
{
u32 value;
assertCs(chan);
delayFix();
spiAdcSendByte(chan, ADS1248_CMD_RDATA);
value = spiAdcSendByte(chan, ADS1248_CMD_NOP); // Attention -> NOP (0xff) required!
value <<= 8;
value |= spiAdcSendByte(chan, ADS1248_CMD_NOP); // Attention -> NOP (0xff) required!
value <<= 8;
value |= spiAdcSendByte(chan, ADS1248_CMD_NOP); // Attention -> NOP (0xff) required!
releaseCs(chan);
return value;
}
static void AdcStopContinuousRead(u8 chan)
{
assertCs(chan);
delayFix();
spiAdcSendByte(chan, ADS1248_CMD_SDATAC);
releaseCs(chan);
}
static void ConfigureTCInputMux(u8 chan, u8 tcIndex)
{
// chan 0..1
// index 0..3
u8 tmp = (tcIndex << 3) | 7; // ain7 neg channel
AdcWriteReg(chan, ADS1248_MUX0_REGISTER, &tmp, 1);
switch (tcIndex) {
case 0 :
tmp = 0xf0 | 0x08; //da IOUT1 (IDAC2 - IDAC1 disconnected)
break;
case 1 :
tmp = 0xf0 | 0x09; //da IOUT2 (IDAC2 - IDAC1 disconnected)
break;
case 2 :
tmp = 0xf0 | 0x04; //da AIN4 (IDAC2 - IDAC1 disconnected)
break;
case 3 :
tmp = 0xf0 | 0x05; //da AIN5 (IDAC2 - IDAC1 disconnected)
break;
}
AdcWriteReg(chan, ADS1248_IDAC1_REGISTER, &tmp, 1);
}
void thermoThread(void)
{
static embtime_t tmCold;
static embtime_t tm;
static u8 state = THERMOTHREAD_STATE_SETUP_START;
static u8 currTcIndex = 0;
static u32 val_ads1 = 0;
static u32 val_ads2 = 0;
u8 tmp;
if (time_fired(tmCold)) {
tmCold = time_ms(100);
// Cold-Junction temperature updated every 100mS, not always
temperatureBase = getTemperatureBase();
}
switch (state) {
//===================================================================================
case THERMOTHREAD_STATE_SETUP_START:
// maybe we will do something here in the future...
state = THERMOTHREAD_STATE_SETUP_HARDWARE_INIT;
break;
//===================================================================================
case THERMOTHREAD_STATE_SETUP_HARDWARE_INIT:
clrSpi1Clock();
assertReset(); //Put Chip in ResetMode (no operations)
releaseReset(); //Release Reset Pin (start chip)
releaseCs(SPI_ADS1248_ONE);
releaseCs(SPI_ADS1248_TWO);
assertStart(SPI_ADS1248_ONE); // start pin must be asserted during ADC configuration!
assertStart(SPI_ADS1248_TWO); // start pin must be asserted during ADC configuration!
tm = time_ms(10); // 4mS minimum
state = THERMOTHREAD_STATE_SETUP_RESET_ADS;
break;
//===================================================================================
case THERMOTHREAD_STATE_SETUP_RESET_ADS :
if (time_fired(tm)) {
spiAdcSendByte(SPI_ADS1248_ONE, ADS1248_CMD_RESET);
spiAdcSendByte(SPI_ADS1248_TWO, ADS1248_CMD_RESET);
tm = time_ms(5); // post reset time (0,6mS minimum)
state = THERMOTHREAD_STATE_SETUP_STOP_CONTINUOUS_MODE;
}
break;
//===================================================================================
case THERMOTHREAD_STATE_SETUP_STOP_CONTINUOUS_MODE :
if ((getDRDYConversionComplete(0) == SPI_ADS1248_ONE) ||
(getDRDYConversionComplete(1) == SPI_ADS1248_TWO)) {
break;
}
AdcStopContinuousRead(SPI_ADS1248_ONE);
AdcStopContinuousRead(SPI_ADS1248_TWO);
tm = time_ms(100);
state = THERMOTHREAD_STATE_SETUP_CONFIG_REGISTERS;
break;
//===================================================================================
case THERMOTHREAD_STATE_SETUP_CONFIG_REGISTERS:
if (time_fired(tm)) {
tmp = 0x28; // internal ref on, ref1 selected, normal operation
AdcWriteReg(SPI_ADS1248_ONE, ADS1248_MUX1_REGISTER, &tmp, 1);
AdcWriteReg(SPI_ADS1248_TWO, ADS1248_MUX1_REGISTER, &tmp, 1);
tmp = 0x03; // GAIN=1 SPS=40
AdcWriteReg(SPI_ADS1248_ONE, ADS1248_SYS0_REGISTER, &tmp, 1);
AdcWriteReg(SPI_ADS1248_TWO, ADS1248_SYS0_REGISTER, &tmp, 1);
tmp = 0x04; //500uA current source DRDY normal mode
AdcWriteReg(SPI_ADS1248_ONE, ADS1248_IDAC0_REGISTER, &tmp, 1);
AdcWriteReg(SPI_ADS1248_TWO, ADS1248_IDAC0_REGISTER, &tmp, 1);
tm = time_ms(500);
state = THERMOTHREAD_STATE_SETUP_OFFSET_CALIBRATION;
}
break;
//===================================================================================
case THERMOTHREAD_STATE_SETUP_OFFSET_CALIBRATION:
if (time_fired(tm)) {
/* WE DO NOT WANT THE CALIBRATION
* If the system is powered without the first TC connected
* the calibration offset will cause wrong readings for
* all the TC's connected.
* We can't do it every reading because it takes
* about 400mS @40SPS ~ 200mS @80SPS
ConfigureTCInputMux(SPI_ADS1248_ONE, 0);
ConfigureTCInputMux(SPI_ADS1248_TWO, 0);
AdcSendSelfOcal(SPI_ADS1248_ONE);
AdcSendSelfOcal(SPI_ADS1248_TWO);
*/
state = THERMOTHREAD_STATE_SETUP_WAIT_END_CALIBRATION;
}
break;
//===================================================================================
case THERMOTHREAD_STATE_SETUP_WAIT_END_CALIBRATION:
// Wait the DRDY for the end calibration before start Read-Loop
if ((getDRDYConversionComplete(SPI_ADS1248_ONE) == 0) ||
(getDRDYConversionComplete(SPI_ADS1248_TWO) == 0)) {
break;
}
releaseStart(SPI_ADS1248_ONE);
releaseStart(SPI_ADS1248_TWO);
state = THERMOTHREAD_STATE_SETUP_FINALIZE;
break;
//===================================================================================
case THERMOTHREAD_STATE_SETUP_FINALIZE:
tm = time_ms(10); // wait 10mS before start loop readings
state = THERMOTHREAD_STATE_LOOP_START;
break;
//===================================================================================
// Continuous Reading-Loop states
case THERMOTHREAD_STATE_LOOP_START:
if (time_fired(tm)) {
ConfigureTCInputMux(SPI_ADS1248_ONE, currTcIndex);
ConfigureTCInputMux(SPI_ADS1248_TWO, currTcIndex);
assertStart(SPI_ADS1248_ONE);
assertStart(SPI_ADS1248_TWO);
state = THERMOTHREAD_STATE_LOOP_WAIT_DATA;
}
break;
//===================================================================================
case THERMOTHREAD_STATE_LOOP_WAIT_DATA :
if ((getDRDYConversionComplete(SPI_ADS1248_ONE) == 0) ||
(getDRDYConversionComplete(SPI_ADS1248_TWO) == 0)) {
break;
}
releaseStart(SPI_ADS1248_ONE);
releaseStart(SPI_ADS1248_TWO);
state = THERMOTHREAD_STATE_LOOP_READ;
break;
//===================================================================================
case THERMOTHREAD_STATE_LOOP_READ:
val_ads1 = AdcReadConversion(SPI_ADS1248_ONE);
val_ads2 = AdcReadConversion(SPI_ADS1248_TWO);
state = THERMOTHREAD_STATE_LOOP_FINALIZE;
break;
//===================================================================================
case THERMOTHREAD_STATE_LOOP_FINALIZE:
tcRawValues[0+currTcIndex] = val_ads1 >> 8;
tcRawValues[4+currTcIndex] = val_ads2 >> 8;
if (++currTcIndex == MAX_NUM_OF_SENSORS) {
Pt1000ReadCounter++; // Increment Read Counter for PLC
currTcIndex = 0;
}
tm = time_ms(1); // wait 1mS before start a new reading
state = THERMOTHREAD_STATE_LOOP_START;
break;
}
}