MCP3008
Multi-Channel
10-bit ADC
Library

MCP3008 Multi-Channel, 10-bit ADC Library

If you need to add more analogue inputs to a PIC device that only has a few, the MCP3008 chip will add 8 channels of 10-bit analogue inputs to your microcontroller project, and is very easy to use with the Positron8 compiler. The MCP3008 uses an SPI interface, so it only requires 4 pins to operate.

Because of the Positron8's compiler's ease of use and the small, fast, assembler code it produces, the library for the MCP3008 device is easy to use and simple to understand. The code within the library file "MCP3008.inc" is listed, in full, below:

$ifndef _MCP3008_INC_
$define _MCP3008_INC_
'
' /\\\\\\\\\
' /\\\///////\\\
' \/\\\ \/\\\ /\\\ /\\\
' \/\\\\\\\\\\\/ /\\\\\ /\\\\\\\\\\ /\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\
' \/\\\//////\\\ /\\\///\\\ \/\\\////// /\\\/////\\\ \////\\\//// \////\\\//// \////////\\\
' \/\\\ \//\\\ /\\\ \//\\\ \/\\\\\\\\\\ /\\\\\\\\\\\ \/\\\ \/\\\ /\\\\\\\\\\
' \/\\\ \//\\\ \//\\\ /\\\ \////////\\\ \//\\/////// \/\\\ /\\ \/\\\ /\\ /\\\/////\\\
' \/\\\ \//\\\ \///\\\\\/ /\\\\\\\\\\ \//\\\\\\\\\\ \//\\\\\ \//\\\\\ \//\\\\\\\\/\\
' \/// \/// \///// \////////// \////////// \///// \///// \////////\//
' Let's find out together what makes a PIC Tick!
'
' Interface library for an MCP3008 multi-channel 10-bit ADC.
' Uses a, fast, software SPI interface so it will work on all suitable PIC devices.
'
' Written by Les Johnson for the Positron8 BASIC compiler.
'
https://sites.google.com/view/rosetta-tech/home
'
' Setup the default pins to use for the MCP3008 interface
' Also create warning messages if using the default pins
'

$ifndef MCP3008_CLK_Pin
$define MCP3008_CLK_Pin PORTB.0
$SendWarning "MCP3008_CLK_Pin missing from the main program, so using the default pin of PORTB.0"
$endif
$ifndef MCP3008_DOUT_Pin
$define MCP3008_DOUT_Pin PORTB.1
$SendWarning "MCP3008_DOUT_Pin missing from the main program, so using the default pin of PORTB.1"
$endif
$ifndef MCP3008_DIN_Pin

$define MCP3008_DIN_Pin PORTB.2
$SendWarning "MCP3008_DIN_Pin missing from the main program, so using the default pin of PORTB.2"
$endif
$ifndef MCP3008_CS_Pin

$define MCP3008_CS_Pin PORTB.3
$SendWarning "MCP3008_CS_Pin missing from the main program, so using the default pin of PORTB.3"
$endif

'-----------------------------------------------------------------
' Initialise the software SPI interface pins
' Input : None
' Output : None
' Notes : None
'

Proc MCP3008_Init()
High MCP3008_CS_Pin ' Make the CS pin a high so the SPI is inactive
Output MCP3008_CLK_Pin ' Make the CLK pin an output
Output MCP3008_DIN_Pin ' Make the DIN pin an output (data written to the MCP3008)
Input MCP3008_DOUT_Pin ' Make the DOUT pin an input (data read from the MCP3008)
EndProc
'-----------------------------------------------------------------
' Read Single-Ended data from an MCP3008 ADC device
' Input : pChan holds the ADC channel to read from the MCP3008 (0 to 7)
' Output : Returns the 10-bit value from the channel being read. Returns 0 if an incorrect channel is chosen
' Notes : The code is a software SPI interface
'

Proc MCP3008_Read(pChan As Byte), Word
Dim bCmdOut As pChan ' Holds the command to send to the MCP3008
Dim bIndex As Byte ' Used for bit loop counting

If pChan > 7 Then ' Is a correct channel chosen?
Result = 0 ' No. So return a value of 0
ExitProc ' Exit the procedure
EndIf
PinClear MCP3008_CLK_Pin ' Start the Clock pin low
PinClear MCP3008_CS_Pin ' Enable the SPI interface
bCmdOut = pChan | %00011000
' Or in the Start bit and the Single-Ended bit
For bIndex = 4 To 0 Step -1 ' Create a loop for the 5 command bits
PinClear MCP3008_DIN_Pin ' \
If bCmdOut.4 = 1 Then ' | Send a bit to the MCP3008
PinSet MCP3008_DIN_Pin ' |
EndIf ' /
Rol bCmdOut ' Rotate the bits for MSB first
PinSet MCP3008_CLK_Pin ' \
DelayCS 1 ' | Toggle the Clock pin
PinClear MCP3008_CLK_Pin ' /
Next
'
' Read in one null bit, and 10 ADC bits
'

Result = 0 ' Before entering the loop, clear Result
For bIndex = 10 To 0 Step -1 ' Create a loop for the 11 bits to read
PinSet MCP3008_CLK_Pin ' \
DelayCS 1 ' | Toggle the Clock pin
PinClear MCP3008_CLK_Pin ' /
Result = Result << 1 ' Rotate the bits of Result
If MCP3008_DOUT_Pin = 1 Then ' \
Result.0 = 1 ' | Load bit-0 of Result with the bit read from the MCP3008 device
EndIf ' /
Next
PinSet MCP3008_CS_Pin ' Disable the SPI interface
EndProc
$endif ' _MCP3008_INC_

To show the above library working, the program below can be used, which reads all the channels from the MCP3008 ADC device and transmits the 10-bit values, in ASCII, to a serial terminal set for 9600 Baud:

' Interface with an MCP3008 multi-channel 10-bit ADC and display the results on a serial terminal

'

' Written by Les Johnson for the Positron8 BASIC compiler.

' https://sites.google.com/view/rosetta-tech/home

'

Device = 18F25K20 ' Choose a PIC microcontroller type

Declare Xtal = 64 ' Tell the compiler the device is going to operate at 64MHz (16 MIPS)

'

' Setup the compiler's HRSOut configurations

'

Declare Hserial_Baud = 9600 ' Set the Baud rate from USART1 for HRSOut

Declare HRSOut_Pin = PORTB.6 ' Set the pin to use for the HRSOut command

'

' Setup the pins to use for the SPI interface to the MCP3008 chip

' These can be any I/O pins, and must be placed in the program before the library is included, so it can see them

'

$define MCP3008_CLK_Pin PORTB.0 ' Connects to the MCP3008 CLK pin

$define MCP3008_DOUT_Pin PORTB.1 ' Connects to the MCP3008 DOUT pin

$define MCP3008_DIN_Pin PORTB.2 ' Connects to the MCP3008 DIN pin

$define MCP3008_CS_Pin PORTB.3 ' Connects to the MCP3008 CS pin

Include "MCP3008.inc" ' Load the MCP3008 library into the program


Dim bChannel As Byte ' Holds the channel to read from the MCP3008 chip

Dim wResult As Word ' Holds the 10-bit result read from the MCP3008 chip

'----------------------------------------------------------------------------------

' Read multiple channels from an MCP3008 ADC and display the results on a serial terminal

'

Main:

MCP3008_Init() ' Initialise the SPI pins for the MCP3008 interface

Do ' Create a loop

For bChannel = 0 To 7 ' Create a loop for all the channels to read

wResult = MCP3008_Read(bChannel) ' Read a channel from the MCP3008

HRSOutLn "CH:", Dec bChannel, " = ", Dec wResult ' Display the ADC value on a serial terminal

Next ' Close the channel loop

DelayMS 300 ' A delay so the results can be seen between reads

Loop ' Do it forever

'-------------------------------------------------------------

' Setup the config fuses for 4xPLL with an external crystal

' This will allow the device to run at 64MHz with a 16MHz crystal

'

Config_Start

FOSC = HSPLL ' HS oscillator, PLL enabled and under software control

Debug = Off ' Background debugger disabled. RB6 and RB7 configured as general purpose I/O pins

XINST = Off ' Instruction set extension and Indexed Addressing mode disabled

STVREN = Off ' Reset on stack overflow/underflow disabled

WDTEN = Off ' WDT disabled (control is placed on SWDTEN bit)

FCMEN = Off ' Fail-Safe Clock Monitor disabled

IESO = Off ' Two-Speed Start-up disabled

WDTPS = 128 ' Watchdog is 1:128

BOREN = Off ' Brown-out Reset disabled in hardware and software

BORV = 18 ' VBOR set to 1.8 V nominal

MCLRE = On ' MCLR pin enabled. RE3 input pin disabled

HFOFST = Off ' The system clock is held Off until the HF-INTOSC is stable

LPT1OSC = Off ' Timer1 operates in standard power mode

PBADEN = Off ' PORTB<4:0> pins are configured as digital I/O on Reset

CCP2MX = PORTC ' CCP2 input/output is multiplexed with RC1

LVP = Off ' Single-Supply ICSP disabled

Cp0 = Off ' Block 0 (000800-001FFFh) not code-protected

CP1 = Off ' Block 1 (002000-003FFFh) not code-protected

CPB = Off ' Boot block (000000-0007FFh) not code-protected

CPD = Off ' Data eeprom not code-protected

WRT0 = Off ' Block 0 (000800-001FFFh) not write-protected

WRT1 = Off ' Block 1 (002000-003FFFh) not write-protected

WRTB = Off ' Boot block (000000-0007FFh) not write-protected

WRTC = Off ' Configuration registers (300000-3000FFh) not write-protected

WRTD = Off ' Data eeprom not write-protected

EBTR0 = Off ' Block 0 (000800-001FFFh) not protected from table reads executed in other blocks

EBTR1 = Off ' Block 1 (002000-003FFFh) not protected from table reads executed in other blocks

EBTRB = Off ' Boot block (000000-0007FFh) not protected from table reads executed in other blocks

Config_End

Positron8 makes it look so simple, yet it is extremely powerful, and the assembler code it produces is both small and fast. For those of you who wish to learn Microchip assembler code, pressing the F2 button in the IDE will display the full assembler code produced in a seperate window. It has always been my purpose with the compiler to make the assembler code very readable and easy to follow and understand.

Below is an image of the demo program and library working within the Proteus Isis simulator:

The Positron8 MCP3008 library and demonstration program can be downloaded from here:
Positron8 MCP3008 Library