BX35, BX24 Interface with a Microchip MCP3208, Eight 12-bit A/D

copyright, Peter H. Anderson, Nov, '02, Baltimore, MD


Introduction.

This discussion deals with interfacing with the Microchip MCP3208 which provides eight 12-bit A/D converters. It is relatively inexpensive; about $5.00 from Digikey. I interfaced it with a BX35 using both a bit bang implementation of the SPI protocol and the BX series SPI command capability. However, porting these programs to the BX24 may be perfromed by redefining the terminals.

Advantages.

Both the BX24 and BX35 provide eight 10-bit A/D converters. However, there are a number of reasons one may opt to use an outboard A/D such as the MCP3208.

General Operation,

The data sheet for the MCP3208 may be downloaded from the Microchip site. I found Table 5-2, "Configuration Bits" and Figure 6-1, "SPI Communication" to be particularly helpful.

Performing an A/D conversion involves bring CS low, a three byte transfer and bringing the CS lead high.

The first byte output consists of a logic one start bit in the bit two position. Bit 1 is used to identify whether the A/D reading is to be single ended (relative to ground) or differential (relative to an adjacent channel). Bit 0 is the most significant bit of the three bit channel address. The data returned from the 3208 during the transfer is garbage.

The second byte output consists of the two lowest bits of the channel in the bit 7 and 6 positions. The low four bits returned by the 3208 is the high nibble of the A/D reading.

The third byte output is a "don't care". The low eight bits of the A/D result are returned by the MCP3208.

Sample Routines.

Program MCP3208_1.Bas.

Program MCP3208_1.Bas illustrates an interface where the SPI interface is implemented in a bit-bang fashion using four general purpose IO bits. This may seem inefficient, but when working the new SPI devices, I always fall back on this approach as a development tool. If I can't get it working with the bit bang approach, I know I will never get it working with the added complexity and magic of the ATMEL hardware. This extends beyond the BX24 to also include my experience with Microchip PICs.

The bit bang implementation simply models a shift register.

Function SPI_IO(ByVal X as Byte) as Byte

   Dim N as Byte

   For N = 1 to 8
      If ((X AND bx1000_0000) <> 0) Then	' start with most sig bit
         Call PutPin(MOSI, 1)				' set MOSI output to appropriate state
      Else
         Call PutPin(MOSI, 0)
      End If

      Call PutPin(SCK, 1)				' bring clock high
      X = X * 2 + GetPin(MISO)			' read from slave
      Call PutPin(SCK, 0)				' clock low
   Next

   SPI_IO = X

End Function
The MOSI output is set to the state of the most significant bit of the byte to be output. SCK is brought high. The shift register is shifted to the left and the bit read from input MISO is placed in the least significant bit position and SCK is brought low. After eight such operations, the byte has been completely shifted out and the shift register (X in the above code) now contains the byte received from the SPI slave.



' MCP3208_1.Bas
'
' Illustrates an interface with Microchip MCP3208 8-channel 12-bit A/D.
'
' Uses a bit bang implementation of the SPI interface.  Each bit of a byte is output,
' begriming with the most significant bit on the MOSI output, SCK is brought high, MISO
' is read and SCK is brought low.  The byte is then shifted to the left such that the next bit
' is now in the most significant bit position.  This is repeated for each of the eight bits.
'
' Each measurement requires a three byte transfer.
'
' The first byte output consists of a start bit in bit 2, whether the measurement is single ended or
' differential in bit 1 and the most  significant bit of the desired channel in bit 0.  No useful
' information is returned by the MCP3208.
'
' The second byte output consists of the lower two bits of the channel in the bit 7 and bit 6
' positions.  The high four bits of the A/D result and returned in the lower nibble.
'
' The third byte output is a "don't care".  The lower byte of the A/D result is returned by the
' MCP3208.
'
' Note that Sub MCP3208ADMeas provides for selecting whether the measurement is to be single ended or
' differential and the specific channel.
'
' This routine was tested on a BX35.  It may also be run on a BX24 by modifying the term assignments.
'
' BX35                             MCP3208
'
'
'  Term 40 (MISO) <------------- DOut (term 12)
'  Term 39 (MOSI) -------------> Din (term 11)
'  Term 38 (SCK) --------------> Clk (term 13)
'  Term 37 (CS) ---------------> /CS (term 10)
'
'                       +5VDC -- Vdd (term 16)
'                       GRD ---- DGND (term 9)
'
'                       +5VDC -- VRef (term 15)
'                       GRD ---- AGND (term 15)
'
'               Variable
'               Source	    ---- CH0 (term 1)
'               (0 - 5 VDC)
'
'
' Note that MCP3208_2.Bas illustrates an interface using the SPICmd feature of the BX series.
'
' copyright, Peter H Anderson, Baltimore, MD, Nov, '02


' for BX35
const MCP3208CS as Byte = 37
const SCK as Byte = 38
const MOSI as Byte = 39
const MISO as Byte = 40

' for BX24
'const MCP3208CS as Byte = 13
'const SCK as Byte = 14
'const MOSI as Byte = 15
'const MISO as Byte = 16

Sub Main()

   Dim ADVal as Integer

   Call MCP3208SetUp()			' be sure CS is high and SCK is low

   Do
      ADval = MCP3208ADMeas(1, 0)	' performs a single ended measurment on Ch 0
      Debug.Print CStr(ADVal)
      Call Sleep(1.0)
   Loop

End Sub


Sub MCP3208SetUp()

   Call PutPin(MCP3208CS, 1)
   Call PutPin(SCK, 0)
   Call PutPin(MISO, 2)			' probably not necessary, but to be safe

End Sub

Function MCP3208ADMeas(ByVal SingleEnd as Byte, ByVal Channel as Byte) as Integer

   Dim DontCare as Byte, X as Byte, H as Byte, L as Byte
   Dim ADVal as Integer

   Call PutPin(MCP3208CS, 0)
   X = bx0000_0100 + SingleEnd * 2 + GetBit(Channel, 2)
   ' start bit in bit 2, single ended in bit 1, most sign bit of channel in bit 0
   DontCare = SPI_IO(X)
   X = Channel * 128	' two least significant bits of channel in two high bits of X
   H = SPI_IO(X) AND bx0000_1111	' take only the four least significant bits
   L = SPI_IO(DontCare)

   Call PutPin(MCP3208CS, 1)		' end of session

   ADVal = 256 * CInt(H) + CInt(L)      ' put the high and low values together into an integer
   MCP3208ADMeas = ADVal

End Function


Function SPI_IO(ByVal X as Byte) as Byte
   Dim N as Byte

   For N = 1 to 8
      If ((X AND bx1000_0000) <> 0) Then	' start with most sig bit
         Call PutPin(MOSI, 1)			' set MOSI output to appropriate state
      Else
         Call PutPin(MOSI, 0)
      End If

      Call PutPin(SCK, 1)			' bring clock high
      X = X * 2 + GetPin(MISO)			' read from slave
      Call PutPin(SCK, 0)			' clock low
   Next

   SPI_IO = X

End Function


Program MCP3208_2.Bas.

This program is fuctionally the same as MCP3208_1.Bas except that the BX SPI capability is used. Thus, only one general purpose IO is used; the CS to select the MCP3208.

Note that with the BX35, SCK, MOSI and MISO are available at terminals 8, 6 and 7, respectively. For the BX24, the signals are available in the line of seven holes across the side of the device closest to terminal 1.

When using the BX SPI capability it is important to recall that the SCK and MOSI outputs and the MISO input are shared with the AT25256 EEPROM that stores the program. If another device other than the EEPROM is enabled, the MISO from this other device will conflict with the MISO from the EEPROM. The BX35 (or BX24) will then be unable to fetch the tokens from the EEPROM. In other words, the BX will halt.

Thus, it is important that the CS lead or leads associated with external SPI outputs remain high at all times other than when executing the SPICmd. The SPICmd command will bring the CS low, transfer the bytes and again bring the CS high allowing the BX24 to then continue fetching tokens from the EEPROM.

One situation I have encountered was to have the BX24 boot with all IO in a high impedance mode. However, the slave device then saw a floating open on the CS lead and periodically turned on, interferring with the operation of the processor reading from the EEPROM. The simple solution is to add a 10K pullup resistor to +5 VDC on each of the CS leads associated with the external slave devices. This assures that when CS is in a high impedance state, the slave device sees a good logic one and it is inactive.



' MCP3208_2.Bas
'
' Illustrates an interface with Microchip MCP3208 8-channel 12-bit A/D.
'
' This program uses the BX series SPI bus and thus aside from the chip select which specically
' selects the MCP3208, this approach uses no additional IO terms on the BX-35 (BX-24).
'
' Uses the OpenSPI amd SPICommand commands.  The SPI channel is configured for most sig bit first
' clock mostly low, read in middle of clock, freq/4.  Thus the setup byte is &H00.
'
' Each measurment requires a three byte transfer.
'
' The first byte output consists of a start bit in bit 2, whether the measurment is single ended or
' differential in bit 1 and the most  significant bit of the desired channel in bit 0.  No useful
' information is returned by the MCP3208.
'
' The second byte output consists of the lower two bits of the channel in the bit 7 and bit 6
' positions.  The high four bits of the A/D result and returned in the lower nibble.
'
' The third byte output is a "don't care".  The lower byte of the A/D result is returned by the
' MCP3208.
'
' Note that in using the SPICmd;
'
'   Call SPICmd(1, 1, A(1), 2, A(2))
'
' A(1) is sent and the value returned by the MCP3208 is discarded,  A(2) and A(3) are sent, with the
' returned data stored in the same elements.
'
' Note that Sub MCP3208ADMeas provides for selecting whether the measurment is to be single ended or
' differential and the specific channel.
'
' This routine was tested on a BX35.  It may also be run on a BX24 by modifying the term assignments.
'
' BX35				MCP3208
'
'
'  Term 7 (MISO) <------------- DOut (term 12)
'  Term 6 (MOSI) -------------> Din (term 11)
'  Term 8 (SCK) --------------> Clk (term 13)
'  Term 40 (CS) ---------------> /CS (term 10)
'
'                       +5VDC -- Vdd (term 16)
'                       GRD ---- DGND (term 9)
'
'                       +5VDC -- VRef (term 15)
'                       GRD ---- AGND (term 15)
'
'               Variable
'               Source	    ---- CH0 (term 1)
'               (0 - 5 VDC)
'
'
' copyright, Peter H Anderson, Baltimore, MD, Nov, '02


' SPI setup byte
Const SPI_LSB as Byte = &H20 'lsb transmitted first
Const SPI_CPOL as Byte = &H08 'sck is mostly high
Const SPI_CPHA as Byte = &H04 'clock phase, see Atmel docs for timing
Const SPI_SCK4 as Byte = &H00 'clock speed f/4
Const SPI_SCK16 as Byte = &H01 'clock speed f/16
Const SPI_SCK64 as Byte = &H02 'clock speed f/64
Const SPI_SCK128 as Byte = &H03 'clock speed f/128

Sub Main()

   Dim ADVal as Integer

   Call MCP3208SetUp()

   Do
      ADval = MCP3208ADMeas(1, 0)	' single ended on Ch 0
      Debug.Print CStr(ADVal)
      Call Sleep(1.0)
   Loop

End Sub


Sub MCP3208SetUp()

   Call OpenSPI(1, H&00, 40)	' open SPI channel 1, with setup byte of &H00, CS on terminal 40

End Sub


Function MCP3208ADMeas(ByVal SingleEnd as Byte, ByVal Channel as Byte) as Integer

   Dim A(1 to 3) as Byte
   Dim DontCare as Byte, H as Byte, L as Byte
   Dim ADVal as Integer


   A(1) = bx0000_0100 + SingleEnd * 2 + GetBit(Channel, 2)
   ' start bit in bit 2, single ended in bit 1, most sign bit of channel in bit 0
   A(2) = Channel * 128	' two least significant bits of channel in two high bits of A(2)
   A(3) = DontCare

   Call SPICmd(1, 1, A(1), 2, A(2))
   H = A(2) AND bx0000_1111	' take only the four least significant bits
   L = A(3)

   ADVal = 256 * CInt(H) + CInt(L)
   MCP3208ADMeas = ADVal

End Function