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 . 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.
With the BX24, the +5 VDC supply voltage is filtered with a series 100 Ohm, shunt capacitor which is then connected to the PORTA supply which powers both the PORTA IO and the internal A/D converters. Thus, if one uses a few of the bits associated with PORTA (pins 13 - 20) to drive LEDs at 10 mA, the PORTA supply will drop substantially and the operation of the A/D converters will produce erratic results. This problem may easily be remedied by using PORTA terminals which are not used as A/D inputs in a manner that minimizes output source current. For example, as inputs, or driving LEDs against +5 VDC with a ground.
With the BX35, the user has direct access to the AVCC terminal at pin 30 and thus may opt to provide the filtering or not. If filtering is provided as with the BX24, the user must continue to observe the precaution in not sourcing appreciable current on any of the PORTA pins not used as A/D inputs.
Use of an external MCP3208 provides the user with the ability to provide heavy filtering on the Vref input. It also provides a separate analog ground which may be separately routed from the digital ground to the lowest impedance point possible, which is usually right at the +5 VDC supply.
General Operation,
The data sheet for the MCP3208 may be downloaded from the . 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