BX24, Basic Atom and PIC Interface with a Microchip TC3400 16-bit Sigma Delta A/D

copyright, Ulicees Andrews and Peter H. Anderson, Baltimore, MD, May, '03


(Last revision - May 19, '03).

Note

Currently, I have a small stock of the TC3400VPA (8-pin DIP) and TC3403VPE (16-pin DIP). Please see my ordering page.

I will be closing for the summer about June 10, '03 and thus hesitate to order a substantial quantity. However, I will accept orders for these devices through May 26 and will order as necessary to fill orders. Please allow two weeks for me to obtain the devices.


Introduction

(This work was primarily performed by Ulicess Andrews as a part of his undergraduate capstone design project. It was later refined in my undergraduate Microcomputer Applications course).

This discussion focuses on the Microchip TC3400 16-bit sigma-delta A/D converter. In additon to the TC3400, Microchip offers a number of other similar devices in this family which provide multiple A/D coverters and such processor peripherals as a delayed reset and power off indicator. The TC3400VPA provides a single differential input in a single 8-pin DIP.

The processor interface consists simply of SCLK and SDATA leads. Note that the SDATA lead is used strictly for communication between the TC3400 and the processor. The processor does not use it to send commands to the TC3400.

Processor				TC3400

	-------- SCLK --------> Term 7

	<------- SDATA -------- Term 6

The discussion includes sample routines for interfacing a Microchip TC3400 16-bit A/D with a Netmedia BX24, a Basic Atom and with a Microchip PIC16F877.

Detailed Discussion

Normally, the clock is high. A negative going clock pulse may be interpetted by the TC3400 as any of the following;

After a start conversion (short negative going pulse on SCLK), there is a short window of less than 2 ms where each subsequent pulse is interpretted as a data conversion reduction pulse. Thus, a single pulse occuring in this window, reduces the resolution to 15 bits, two pulses to 14 bits, etc to six pulses reducing the resolution to 10 bits.

After the conversion is completed, data is transferred on each subsequent negative going transition of the SCLK. There appears to be no timing limitation of the data transfer. That is, each bit may be clocked in at a bit per every few microseconds, every second or even a bit per minute. The TC3400 does not automatically home to waiting for a start conversion after a period of time.

Note that there are a total of seventeen negative pulses on SCLK associated with each conversion; a start pulse, n data conversion reduction pulses and (16 - n) data transfer pulses.

It is critical to differentiate between the significance of the pulses. The master may issue a pulse which it believes is a start conversion pulse, when in fact the TC3400 inteprets it as a clock for transferring bit 7 of the data. The master processor will then issue sixteen pulses, perhaps some for resolution reduction and the balance for data transfer. However, the TC3400 will interpet what was intended as the "start" pulse as the clock transfer for bit 7 and any resolution reduction pulses as data transfer pulses for bits 6, 5, etc and then interpret addtional pulses as data transfer, a start pulse, resolution reduction pulses and then some data transfer pulses. That is, the master processor and the TC3400 must be synchronized such that when the master processor issues a "start conversion" pulse, it is interpretted by the TC3400 as a start conversion.

Thus, pulses are sent by the master until a logic one is read on the SDATA prior to a negative going clock pulse, after the negative edge of the clock and a logic zero after the positive edge (1 1 0 or six) pattern is observed indicating the TC3400 interpets the negative clock pulse as a start conversion.

(Note that the snippets which are included in this discussion were written for the BX24).


    	Do	' for the BX24
    		' synchronize - send pulses until state on SDAT before, during negative clk and
    		' after negative clock is 1 1 0 (0x06).  Pulses sent every 10 ms
    	    ' Debug.Print CStr(State)		' used for debugging
    	    Call Sleep(0.01)
    	    State = GetPin(SDAT)			' before
    	    Call PutPin(SCLK, 0)
    	    State = State * 2 + GetPin(SDAT)	' during negative clock
    	    Call PutPin(SCLK, 1)
    	    State = State * 2 + GetPin(SDAT)	' after clock
        Loop Until State = 6

(Note that continued 1 1 1 values on the SDAT lead indicates the device is not present and you may wish to revise the code to break out of the loop if the state continues to be 7 to avoid being continually locked in this loop. Note, however, that 1 1 1 is a valid state for data transfer (two consecutive data ones) and thus one must look for multiple consecutive 1 1 1 states, say seventeen prior to interpretting that the device is not present).

The start conversion pulse is quickly followed by resolution reduction pulses. Each pulse, up to six, reduces the resolution by a factor of two and also reduces the conversion time by two. In the following, I reduced the resolution by a factor of two to fourteen bits (sign plus 13 magnitude bits).

        For N = 1 to 2		' resolution reduction to 13 bits plus sign bit
            Call PulseOut(SCLK, 4, 0)  ' might also use PulseIn
        Next
The program then loops until the coversion is complete, which is indicated when SDAT goes high.
        Do			' wait for conversion to complete
        Loop Until GetPin(SDAT) = 1
The sign bit is then read by bringing the clock low and reading the state of SDAT;
        Call PutPin(SCLK, 0)	' fetch the sign bit
        SignBit = GetPin(SDAT)
        Call PutPin(SCLK, 1)
Note that a sign bit of zero indicates the quantity is positive. That is, the voltage at IN+ is positive relative to IN-. A sign bit of one indicates the voltage is negative and in this case, the magnitude bits are in two's compliment format. One may falsely assume that the voltage at IN+ will always be postive relative to IN-. However, I found that for very small voltages, the TC3400 interpretted these as being negative, presummably due to an offset voltage error.

The magnitude bits are then fetched, beginning with the most significant bit;


        ADVal = &H0000

        For N = 1 to 13			' fetch the magnitude bits
           Call PutPin(SCLK, 0)
           ADVal =  ADVal * 2 +  CInt(GetPin(SDAT))
           Call PutPin(SCLK, 1)
        Next

If the sign bit is one, the magnitude is represented in two's complement format. However, in this case, only 13 bits were fetched and thus the high three bits are zeros. Thus, these bit positions must be filled with ones, such that the entire 16-bit quntity is in two's complement format.
        If SignBit = 1 Then 	' it is negative
           For N = 13 to 16	' fill high bits with ones
              Call PutBit(ADVal, N, 1)
           Next
        End If
The voltage is then calculated;
        Voltage = 2.38596 * CSng(ADVal) / 8192.0
        Debug.Print CStr(ADVal); "  "; CStr(Voltage)
Note that the reference voltage, 2.38596 in the above expression, is obtained by applying a precision reference voltage on the REFin input, which is then doubled by the TC3400. For example, a precison 1.25 VDC input might be used, resulting in a reference voltage of 2.5 VDC. Note that the maximum REFin is 1.25 which results in a maximum reference voltage of 2.5 VDC.

The TC3400 provides an on-chip reference of 1.193 VDC at REFout. This may be tied to REFin, resulting in an A/D reference of 2.38596 which was used in the above example.

Miscellaneous Notes.


BX24 Sample

' TC3400_1.Bas (NetMedia BX24)
'
'  Ilustrates an interface with a TC3400 16-bit sigma delta A/D.
'
'                                                    TC3400VPA
'
'           Voltage Divider (0.0 to 2.2 VDC) -------- IN+ (Term 1)
'                                            GRD ---- IN- (Term 2)
' BX24
'
' Term 13 -------------------------------------------> SCLK (term 7)
' Term 14 <------------------------------------------- SDAT (term 6)
'
' Measures voltage on Terminal 1 of TC3400 and displays to the terminal.
'
' Normally SCLK is at logic one.  Pulses are continually output until SDAT states from
' the TC3400 are logic one before the negative clock pulse, logic one during the negative
' clock pulse and logic zero after the clock pulse.   This assures that the negative going
' pulse is interpretted by the TC3400 as a "start conversion".
'
' Two resolution reduction pulses are sent within a ms of the start conversion so as to
' configure the TC3400 for 14-bit operation (sign plus 13 magnitude bits).
'
' SDAT is monitored until the conversion is complete (SDAT at logic one) and the sign bit
' followed by the 13 magnitude bits are then clocked in, most significant bit first.
'
' If the sign bit is one (minus), the high three bits of ADVal are set to one such that ADVal is
' 16-bit twos compliment.
'
' The voltage appearing on IN+ is then calculated and displayed  to the terminal.
'
' copyright, Ulicees Andrews and Peter H Anderson, May, '03, Baltimore, MD

const SCLK as Byte = 13
const SDAT as Byte = 14

Sub Main()

    Dim State as Byte, N as Byte, SignBit as Byte, ADVal as Integer
    Dim Voltage as Single
    Call PutPin(SCLK, 1)	' CLK at output high
    Call PutPin(SDAT, 2)   	' SDAT is an input

    Do    ' continually


    	Do
    		' synchronize - send pulses until state on SDAT before, during negative clk and
    		' after negative clock is 1 1 0 (0x06).  Pulses sent every 10 ms
    	    ' Debug.Print CStr(State)		' used for debugging
    	    Call Sleep(0.01)
    	    State = GetPin(SDAT)			' before
    	    Call PutPin(SCLK, 0)
    	    State = State * 2 + GetPin(SDAT)	' during negative clock
    	    Call PutPin(SCLK, 1)
    	    State = State * 2 + GetPin(SDAT)	' after clock
        Loop Until State = 6

        For N = 1 to 2		' resolution reduction to 13 bits plus sign bit
            Call PulseOut(SCLK, 4, 0)  ' might also use PulseIn
        Next

        Do			' wait for conversion to complete
        Loop Until GetPin(SDAT) = 1
        ' an alternative would be to simply sleep for a period of time greater than T3

        ' Now, fetch the data
        ADVal = &H0000

        Call PutPin(SCLK, 0)	' fetch the sign bit
        SignBit = GetPin(SDAT)
        Call PutPin(SCLK, 1)

        For N = 1 to 13			' fetch the magnitude bits
           Call PutPin(SCLK, 0)
           ADVal =  ADVal * 2 +  CInt(GetPin(SDAT))
           Call PutPin(SCLK, 1)
        Next

        If SignBit = 1 Then 	' it is negative
           For N = 13 to 16	' fill high bits with ones
              Call PutBit(ADVal, N, 1)
           Next
        End If

        Voltage = 2.38596 * CSng(ADVal) / 8192.0
        Debug.Print CStr(ADVal); "  "; CStr(Voltage)
    Loop
End Sub


Basic Atom Sample Routine.

Note that due to the critical timing requirements of the TC3400, the internal ICD associated with the Basic Atom may not be used.

' TC3400_2.Bas  (Basic Atom)
'
'  Ilustrates an interface with a TC3400 16-bit sigma delta A/D.
'
'								TC3400VPA
' 			Voltage Divider (0.0 to 2.2 VDC) -------- IN+ (Term 1)
'							 GRD ---- IN- (Term 2)
' Basic Atom
'
' P0 -----------------------------------------------------------> SCLK (term 7)
' P1 <----------------------------------------------------------- SDAT (term 6)
'
' Measures voltage on IN+ terminal of TC3400 and displays to the terminal.
'
' Normally SCLK is at logic one.  Pulses are continually output until SDAT states from
' the TC3400 are logic one before the negative clock pulse, logic one during the negative
' clock pulse and logic zero after the clock pulse.   This assures that the negative going
' pulse is interpretted by the TC3400 as a "start conversion".
'
' Two resolution reduction pulses are sent within a ms of the start conversion so as to
' configure the TC3400 for 14-bit operation (sign plus 13 magnitude bits).
'
' SDAT is monitored until the conversion is complete (SDAT at logic one) and the sign bit
' followed by the 13 magnitude bits are then clocked in, most significant bit first.
'
' If the sign bit is one (minus), the high three bits of ADVal are set to one such that ADVal is
' 16-bit twos compliment.
'
' The voltage appearing on IN+ is then calculated and displayed to the terminal.
'
' copyright, Peter H Anderson, May, '03, Baltimore, MD

SCLK Var Out0
SDAT Var In1

SCLKDir Var Dir0
SDATDir Var Dir1

N	  Var Byte
State Var Byte
Sign  Var Byte
ADVal Var SWord

Temp1  Var Long	' for floats
Temp2  Var Long
Temp3  Var Long
Volts  Var Long

Main:
   While 1

      SCLK = 1
      SCLKDir = 0		' SCLK is an output
      SDATDir = 1		' SDAT is an input

      Repeat 		' send pulses until it is start conversion
         ' Pause 10
         ' SerOut S_OUT, i9600, [Dec State, 13, 10]
         State = SDAT
         SCLK = 0
         State = State * 2 + SDAT
         SCLK = 1
         State = State * 2 + SDAT
      Until State = 6

      ' Resolution Reduction
      For N = 1 to 2
         SCLK = 0
         SCLK = 1
      Next

      Repeat 			' wait for end of conversion
      Until SDAT = 1

      SCLK = 0			' fetch sign bit
      Sign = SDAT
      SCLK = 1

      ADVal = $0000

      For N = 1 to 13		' fetch the 13 data bits
         SCLK = 0
         ADVal = ADVal * 2 + SDAT
         SCLK = 1
      Next

      If Sign = 1 Then		' its negative
         ADVal = ADVal | $E000	' insert ones in upper three bit positions
      EndIf

      ' compute voltage

      Temp1 = FLOAT ADVal	' Volts = ADVal / 8192 * 2.38596
      Temp2 = 8192.0
      Temp3 = Temp1 FDIV Temp2
      Temp1 = 2.38596
      Volts = Temp3 FMUL Temp1

      SerOut S_OUT, i9600, [SDec ADVal, "  ", Real Volts, 13]

      Pause 1000

   Wend


PIC16F877 Sample Routine

The following routine was implemented using CCS PCM PIC C compiler. It was debugged using an inexpensive Olimex ICD which is compatible with the Microchip ICD 1..

// Program TC3400_1.C
//
//  Ilustrates an interface with a TC3400 16-bit sigma delta A/D.
//
//                                                   TC3400VPA
//          Voltage Divider (0.0 to 2.2 VDC) -------- IN+ (Term 1)
//                                           GRD ---- IN- (Term 2)
// PIC16F877
//
// PORTB0 (term 33) ---------------------------------> SCLK (term 7)
// PORTB1 (term 34) <--------------------------------- SDAT (term 6)
//
// PORTC6/TX (term 25) ---------------> To PC Com Port (9600 baud) via MAX232
//
// Measures voltage on IN+ Terminal of TC3400 and displays to the terminal.
//
// Normally SCLK is at logic one.  Pulses are continually output until SDAT
// states from the TC3400 are logic one before the negative clock pulse, logic
// one during the negative clock pulse and logic zero after the clock pulse.
// This assures that the negative going pulse is interpretted by the TC3400 as
// a "start conversion".
//
// Two resolution reduction pulses are sent within a ms of the start conversion
// so as to configure the TC3400 for 14-bit operation (sign plus 13 magnitude
// bits).
//
// SDAT is monitored until the conversion is complete (SDAT at logic one) and
// the sign bit followed by the 13 magnitude bits are then clocked in, most
// significant bit first.
//
// If the sign bit is one (minus), the high three bits of ADVal are set to one
// such that ADVal is 16-bit twos compliment.
//
// The voltage appearing on IN+ is then calculated and displayed to the terminal.
//
// PIC16F877, 4.0 MHz ceramic resonator, CCS PCM C Compiler
//
// copyright, Peter H. Anderson, Baltimore, MD, May, '03


#case

#device PIC16F877 *=16 ICD=TRUE

#include <defs_877.h>

#define TRUE !0
#define FALSE 0

long tc3400_ad_meas(byte resolution);

void asynch_enable(void);
void asynch_disable(void);
void ser_char(char ch);

void delay_10us(byte t);
void delay_ms(long t);
void delay_secs(long t);

#define SCLK portb0
#define SDAT portb1
#define SCLK_DIR trisb0

main()
{
	signed long adval;
	float volts_dc;

	asynch_enable();
    SCLK_DIR = 0;

	while(1)
	{
		adval = tc3400_ad_meas(14);

		volts_dc = 2.38596 * (float) adval / 8192.0;

		printf(ser_char, "%5.4f\r\n", volts_dc);
		delay_secs(2);
	}
}

long tc3400_ad_meas(byte resolution)
{
	const long a[7] = {0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00};
	byte state, sign, n;
	long adval;
	// start conversion
	do
	{
		delay_ms(1);
		state = SDAT;
		SCLK = 0;	// bring clock low
		state = (state << 1) | SDAT;
		SCLK = 1;	// clk high
		state = (state << 1) | SDAT;
        state = state & 0x07;
	} while(state!=6);

	// resolution reduction
	for (n=16; n>resolution; n--)
	{
		SCLK = 0;
#asm
		NOP
#endasm
		SCLK = 1;
#asm
		NOP
#endasm
	}

    while(SDAT == 0)		// wait for conversion to complete
    {
	}

	SCLK = 0;	// read the sign bit
#asm
	NOP
#endasm
    sign = SDAT;
	SCLK = 1;
#asm
	NOP
#endasm

	adval = 0x0000;
	for (n=0; n<resolution-1; n++)
	{
		SCLK = 0;
#asm
		NOP
#endasm
		adval = (adval << 1) | SDAT;
		SCLK = 1;
#asm
		NOP
#endasm
	}

  	if (sign)	// its minus
  	{

		adval = adval | a[16-resolution];	// fill high bits with ones

	}
    // printf(ser_char, "%ld\n\r", adval);
	return(adval);
}

void asynch_enable(void)
{
   trisc6 = 1; // make tx (term 25 an input)
   trisc7 = 1; // rx (term 26) input

   sync = 0;   // asynchronous
   brgh = 1 ;  // baud rate generator high speed
   SPBRG = 25;  // 9600 with 4.0 MHz clock
		// SPGRG = 129 for 9600 baud with 20.0 MHz clock

   spen = 1;    // serial port enabled

   txen = 1;  // as appropriate
   cren = 1;
}

void asynch_disable(void)
{
   txen = 0;
   cren = 0;
   spen = 0;
}

void ser_char(char ch) // no interrupts
{
   byte n = 250;

   delay_ms(5);		//  required for Serial LCD+
   while(!txif && --n)	/* txif goes to one when buffer is empty */
   {
	   delay_10us(1);
   }
   TXREG = ch;    	//  This clears txif
}

void delay_10us(byte t)
{
#asm
      GOTO DELAY_10US_2
DELAY_10US_1:
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
DELAY_10US_2:
      CLRWDT
      DECFSZ t, F
      GOTO DELAY_10US_1
#endasm
}

void delay_ms(long t)   // delays t millisecs
{
   while(t--)
   {
     delay_10us(99);    // not 100 to compensate for overhead
   }
}

void delay_secs(long t)
{
	while(t--)
	{
		delay_ms(1000);
	}
}