24 bit, 16 channel ADC using SPI

The LTC2498 is a 24 bit, 16 channel ADC with an SPI interface. I’ve used it in a number of industrial products, and I thought it might be a useful exercise to attach one to the IMP. It turned out to be much easier than I was expecting. The spi.writeread() function makes things really simple.

Basic code is attached, although it could be a bit tighter - for example, I’m not polling for EOC, I’m just waiting. Similarly, I’m not testing for over-range data, and I’m assuming all inputs are positive. But it functions, and should provide a basic framework if anyone else is interested in this particular device.

`
// SPI example for interfacing to LTC2498, 24-bit, 16 channel ADC with temperature
// sjm 20130320

// Pin 2 = SPI MISO
// Pin 5 = SPI SCLK
// Pin 7 = SPI MOSI
// Pin 8 = Chip select

// ADC configuration bits
// 1 0 EN SGL ODD A2 A1 A0 EN2 IM FA FB SPD …

// 101 10 000 1 0 00 0 = B080 - enable, single, Ch0, enable, non-temp, 50/60Hz, x1
// 101 10 001 1 0 00 0 = B180 - enable, single, Ch2, enable, non-temp, 50/60Hz, x1
// 101 10 010 1 0 00 0 = B280 - enable, single, Ch4, enable, non-temp, 50/60Hz, x1
// 101 10 011 1 0 00 0 = B380 - enable, single, Ch6, enable, non-temp, 50/60Hz, x1
// 101 10 100 1 0 00 0 = B480 - enable, single, Ch8, enable, non-temp, 50/60Hz, x1
// 101 10 101 1 0 00 0 = B580 - enable, single, Ch10, enable, non-temp, 50/60Hz, x1
// 101 10 110 1 0 00 0 = B680 - enable, single, Ch12, enable, non-temp, 50/60Hz, x1
// 101 10 111 1 0 00 0 = B780 - enable, single, Ch14, enable, non-temp, 50/60Hz, x1
// 101 11 000 1 0 00 0 = B880 - enable, single, Ch1, enable, non-temp, 50/60Hz, x1
// 101 11 001 1 0 00 0 = B980 - enable, single, Ch3, enable, non-temp, 50/60Hz, x1
// 101 11 010 1 0 00 0 = BA80 - enable, single, Ch5, enable, non-temp, 50/60Hz, x1
// 101 11 011 1 0 00 0 = BB80 - enable, single, Ch7, enable, non-temp, 50/60Hz, x1
// 101 11 100 1 0 00 0 = BC80 - enable, single, Ch9, enable, non-temp, 50/60Hz, x1
// 101 11 101 1 0 00 0 = BD80 - enable, single, Ch11, enable, non-temp, 50/60Hz, x1
// 101 11 110 1 0 00 0 = BE80 - enable, single, Ch13, enable, non-temp, 50/60Hz, x1
// 101 11 111 1 0 00 0 = BF80 - enable, single, Ch15, enable, non-temp, 50/60Hz, x1
// 101 00 000 1 1 00 0 = A0C0 - enable, single, internal temperature, 50/60Hz, x1

WriteData <- blob(4); // Configuration data written to ADC
ReadData <- blob(4); // Sample data read back from ADC

// This function is bi-directional. It writes out the channel config for the next measurement
// cycle, and reads back data from the previous measurement cycle.
function ReadADC(data)
{
WriteData.seek(0);
WriteData.writen(swap4(data),‘i’); // We pass 32-bit big-endian data, but blob is little endian
hardware.pin8.write(0); // chip select = low
ReadData = hardware.spi.writeread(WriteData);
hardware.pin8.write(1); // chip select = high
imp.sleep(0.2); // 160ms conversion time in x1 mode, ideally, should poll EOC here
}

function PrintValue(Channel)
{
ReadData.seek(0);
local val = ReadData.readn(‘i’); // convert blob to a 32 bit, little endian integer
val = (swap4(val) >> 5) & 0x00FFFFFF; // make it big endian,and extract 24 data bits

if (Channel == 99)      // internal temperature
{
    local temp = (val * 3.3)/1570.0 - 273.0;
    server.log(format("Temperature = %3.1f",temp));
}
else
{
    local volt = 3.3 * (val/16777216.0);
    server.log(format("Ch %d, voltage = %1.3f",Channel,volt));
}

}

imp.configure(“SPI-ADC”, [], []);
hardware.spi257.configure(MSB_FIRST | CLOCK_IDLE_LOW, 100);
hardware.pin8.configure(DIGITAL_OUT); // chip select
hardware.pin8.write(1);

// Prime the ADC by writing something, so the first read will be legitimate
ReadADC(0xB0800000); // Write Ch0, read unknown

ReadADC(0xB8800000); // Write Ch1, read previous channel (which was 0)
PrintValue(0);

ReadADC(0xBB800000); // Write Ch7, read Ch1
PrintValue(1);

ReadADC(0xBC800000); // Write Ch9, read Ch7
PrintValue(7);

ReadADC(0xA0C00000); // Write temperature, read Ch9
PrintValue(9);

ReadADC(0xB0800000); // Write Ch0, read temperature
PrintValue(99);
`

03/20/2013 05:47:02 PM: Device configured to be "SPI-ADC"
03/20/2013 05:47:02 PM: Ch 0, voltage = 1.892
03/20/2013 05:47:02 PM: Ch 1, voltage = 1.350
03/20/2013 05:47:03 PM: Ch 7, voltage = 0.651
03/20/2013 05:47:03 PM: Ch 9, voltage = 0.226
03/20/2013 05:47:03 PM: Temperature = 23.6

It’s people like you that post projects and scripts of what they’re working on that really make this forum a powerful thing for all Imp users. Even though as you mentioned, it’s not totally tweaked, this can give others “aha” ideas that trigger other projects and device integration exploits.

Hugo …
I think a section on “projects”, “projects in the works” would be a great place to store all of these types of things. Maybe not in a ‘post’ type of venue, but a place where each member can store and display their projects … like a web page within this website.

Same idea for server-side scripting like PHP/MySQLi, Perl, etc. A place to display snippets of code and scripts for others to use.

Sjm … thanks for posting your project and keep posting any changes or ideas.
You can’t imagine how much benefit stuff like this will be for others.

thanks for posting your project and keep posting any changes or ideas. You can't imagine how much benefit stuff like this will be for others.
Actually, I can. One of the most useful things I've found when learning a new language is looking at other people's code. Squirrel is very poorly documented in this respect, and finding useful examples is difficult, hence my post.

@sjm Would you mind if we put your code up on the example code snippets section of the devwiki?

That’s fine, however there are lots of interesting code snippets scattered throughout the forums here. I tend to flick between the wiki and the forums when searching for something.

Hugo, we need a better way.

The editor (with the code highlighting) is a bad deal. When I post PHP scripts, it cuts off long lines, and there is an issue with underscores that appear after a $. Example $_POST … the underscore does not show up with highlighted code.

It’s almost as though this forum text editor was never meant for coders who wish to post scripts. Maybe Vanilla2 has a different set of editor functions?

I plan on doing a lot of imp <-> PHP projects, but I’m sure others might be using Perl, servers, etc. When new agents are released, things will really get cooking with the imp. I’m learning new tricks every day … especially by looking at projects others are working on.

@mlseim I agree we need a better way to post code; possibly this is via a freely editable section of devwiki. Vanilla forums is a bit… vanilla when it comes to being used as a technical bbs.

@sjm thanks, posted!

I would be very grateful for your tips for my present problem definition:

I have a microcontroller ATmega128RFA1 with integrated Zigbee with UART-USB(TTL Converter) attached to the PC.

From the ATmega128RFA1 I have connected via SPI a LTC2498 Temperature Sensor Chip.

I need to read The Temperature from LTC2498 on The Terminal Program in my PC. The scheme is as follows:

LTC2498–SPI–uC(Programmed with code Read_ltc2498)–UART_USB–PC.

The code with which I am working is as follows:

#include <avr/io.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <util/delay.h>

#define DD_MISO 3
#define DD_MOSI 2
#define DD_SCK 1
#define DD_SS 0

void uart_init()
{
uint16_t temp = ((8000000)/(9600.0*16)-1); //Calculation transmission rate

UBRR0L = temp; //Transmitting transmission rate
UBRR0H = temp>>8;

UCSR0B |= (1<<TXEN0)|(1<<RXEN0)|(1<<RXCIE0); //Sending and receiving Free


UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01);  //Assignment format: 8 bit

}

int sendestring(char *s)
{
int i=0;

for(i=0; s[i] != '\\0'; i++)
{
    while bit_is_clear(UCSR0A, UDRE0); //wait, until UDRE0 = 1 (Send free)

    UDR0 = s[i]; //send sing

}

return 0;

}

void SPI_MasterInit(void)
{
//Outputs: MOSI, SCK, SS
DDRB = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);

//Enable SPI, Master-Mode, set clock rate fck/16
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);

}

int SPI_MasterTransmit(char MOSI)
{
//start Transmission
SPDR = MOSI;

//wait for transmission complete
while(!(SPSR & (1<<SPIF)));

//returned data from slave to master
return SPDR;

}

int main()
{

char str[20];

// Thermoelement an CH0 und CH1
char masterOut1 = 0b10100000;

// interne Temperaturmessung:
char masterOut2 = 0b10000000;

char result;
uint32_t masterIn = 0;
uint32_t Temp = 0;

uart_init();    

SPI_MasterInit();


for(;;)
{

    _delay_ms(500);

    //CS des Thermochips to low
    PORTB &= !(1<<DD_SS);


    //Byte1
    result = SPI_MasterTransmit(masterOut1);        
    masterIn = (result & 0b00011111);  /*last 3 bits lost/*
    sendestring(str);

    //Byte2
    result = SPI_MasterTransmit(masterOut2);
    masterIn = (masterIn<<8) + result;


    //Byte3
    result = SPI_MasterTransmit(0);
    masterIn = (masterIn<<8) + result;


    //Byte4
    result = SPI_MasterTransmit(0);
    masterIn = (masterIn<<8) + result;


    //CS des Thermochips to high
    PORTB |= (1<<DD_SS);


    masterIn = (masterIn>>5) & 0b00000000111111111111111111111111; /*Last 5 bits lost/*

    sendestring(" masterIn: ");
    sprintf(str, "%ld", masterIn);
    sendestring(str);

    //bei interner Temperaturmessung: Temperatur in °C berechnen laut Datenblatt Seite21
    Temp = (masterIn*4/1570)-273;

    sendestring(" Temp: ");
    sprintf(str, "%ld", Temp);
    sendestring(str);

    _delay_ms(2000);
}


return 0;

}

I have tried change in SPI function the 4 modes for CPOL and CPHA bits and DORD in the SPCR Register but doesn´t work.

With the next chars I have LTC2498 configured as follows:

32 bits: Byte1= 10100000; Byte2= 10000000; Byte3= 00000000; Byte4= 00000000;

The results are around this:
result1: 11111111 masterIn: 16777215 Temp: 42471

masterIn = DATAOUT according to the LTC2498 DataSheet has to be around 80000, so is too high.

Could anyone help me with this problem please? I have tried all, but is impossible.

Thanks in advance,
Hector.

Have you tried posting this on the Adafruit, or maybe the Sparkfun forums? Those forums have a lot of information regarding your question.

For some reason, this doesn’t sound like an imp application?