Positron - MCP9600. I2C Thermocouple Amplifier Library and Demo

Started by top204, Apr 06, 2023, 09:25 AM

Previous topic - Next topic

top204

An earlier post on the forum requesting some code to interface with an MCP9600 thermocouple amplifier with an I2C interface got my interest up, and I had a bit of "rare" time on my hands, so I looked at its datasheet and it seemed relatively straightforward to do in the Positron compiler. Also, the fact that the Proteus simulator has an MCP9600 simulating model in it, allowed me to write a simple to use library for it last night and test it in the Isis simulator. In fact I got so involved in it, I added a lot more to the library than I intended too, so I am rather tired this morning. :-)

The library's code listing is below. Name it: "MCP9600.inc", and place the "MCP9600.inc" file in the compiler's "Includes" folder, located here: "C:\Users\User Name\PDS\Includes\", and all program listings will be able to see it with an "Include MCP9600 . inc" directive:

$ifndef _MCP9600_INC_
$define _MCP9600_INC_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A library to interface to an MCP9600 thermocouple amplifier, using an I2C interface.
'
' Written for the Positron8 BASIC compiler by Les Johnson.
'
' Setup the default pins used for the I2C interface to the MCP9600 device
'
$ifndef MCP9600_SCL_Pin
    $define MCP9600_SCL_Pin PORTC.3
    $SendWarning "MCP9600_SCL_Pin missing or incorrectly placed in the main program, so using the default pin of PORTC.3"
$endif
$ifndef MCP9600_SDA_Pin
    $define MCP9600_SDA_Pin PORTC.4
    $SendWarning "MCP9600_SDA_Pin missing or incorrectly placed in the main program, so using the default pin of PORTC.4"
$endif

$ifndef False
    $define False 0
$endif
$ifndef True
    $define True 1
$endif

$define cMCP9600_I2CADDR            $C0             ' The I2C address when the MCP9600's ADDR pin is grounded
'
' MCP9600 registers
'
$define cMCP9600_HOTJUNCTION        $00             ' Hot junction temperature T_H
$define cMCP9600_JUNCTIONDELTA      $01             ' Hot/Cold junction delta
$define cMCP9600_COLDJUNCTION       $02             ' Cold junction temperature T_C
$define cMCP9600_RAWDATAADC         $03             ' The raw ADC reading
$define cMCP9600_STATUS             $04             ' Device status
$define cMCP9600_SENSORCONFIG       $05             ' Configuration for thermocouple type
$define cMCP9600_DEVICECONFIG       $06             ' Device config
$define cMCP9600_ALERTCONFIG_1      $08             ' The alert's config
$define cMCP9600_ALERTHYST_1        $0C             ' The alert's hysteresis
$define cMCP9600_ALERTLIMIT_1       $10             ' The alert's limit

$define cMCP9600_DEVICEID           $20             ' Device ID/Revision
'
' Status register bit flags
'
$define cMCP9600_STATUS_ALERT1      0               ' Bit flag for alert 1 status
$define cMCP9600_STATUS_ALERT2      1               ' Bit flag for alert 2 status
$define cMCP9600_STATUS_ALERT3      2               ' Bit flag for alert 3 status
$define cMCP9600_STATUS_ALERT4      3               ' Bit flag for alert 4 status
$define cMCP9600_STATUS_INPUTRANGE  4               ' Bit flag for input range
$define cMCP9600_STATUS_THUPDATE    6               ' Bit flag for TH update
$define cMCP9600_STATUS_BURST       7               ' Bit flag for burst complete
'
' The possible thermocouple types
'
$define cMCP9600_TYPE_K 0
$define cMCP9600_TYPE_J 1
$define cMCP9600_TYPE_T 2
$define cMCP9600_TYPE_N 3
$define cMCP9600_TYPE_S 4
$define cMCP9600_TYPE_E 5
$define cMCP9600_TYPE_B 6
$define cMCP9600_TYPE_R 7
'
' The possible ADC resolutions
'
$define cMCP9600_ADCRESOLUTION_18 0
$define cMCP9600_ADCRESOLUTION_16 1
$define cMCP9600_ADCRESOLUTION_14 2
$define cMCP9600_ADCRESOLUTION_12 3
'
' Create some global variables for the library procedures
'
    Dim MCP9600_bSensorConfig As Byte = %00000000                   ' Holds the value of the sensor config register
    Dim MCP9600_bDeviceConfig As Byte = %10000000                   ' Holds the value of the device config register
    Dim MCP9600_bAlertConfig  As Byte = %00000000                   ' Holds the value of the alert config register
    Dim MCP9600_wTemporary    As SWord                              ' Used as a temporary variable in a few procedures
    Dim MCP9600_bTemporary0   As MCP9600_wTemporary.Byte0           ' An alias byte sized temporary variable
    Dim MCP9600_fResult       As Float                              ' Holds the result from some temperature reading procedures

'--------------------------------------------------------------------------------------------------------
' Write an 8-bit value to the MCP9600 device
' Input     : pReg holds the register to write to
'           : pValue holds the value to write to the register
' Output    : None
' Notes     : None
'
Proc MCP9600_Write8(pReg As Byte, pValue As Byte)
    I2Cout MCP9600_SDA_Pin, MCP9600_SCL_Pin, cMCP9600_I2CADDR, pReg, [pValue]
EndProc

'--------------------------------------------------------------------------------------------------------
' Write a 16-bit value to the MCP9600 device
' Input     : pReg holds the register to write to
'           : pValue holds the value to write to the register
' Output    : None
' Notes     : Writes Most Significant Byte first
'
Proc MCP9600_Write16(pReg As Byte, pValue As Word)
    I2Cout MCP9600_SDA_Pin, MCP9600_SCL_Pin, cMCP9600_I2CADDR, pReg, [pValue.Byte1, pValue.Byte0]
EndProc

'--------------------------------------------------------------------------------------------------------
' Read an 8-bit value from the MCP9600 device
' Input     : pReg holds the register to read
' Output    : Returns the value read from the register
' Notes     : None
'
Proc MCP9600_Read8(pReg As Byte), Byte
    I2Cin MCP9600_SDA_Pin, MCP9600_SCL_Pin, cMCP9600_I2CADDR, pReg, [Result]
EndProc

'--------------------------------------------------------------------------------------------------------
' Read a 16-bit value from the MCP9600 device
' Input     : pReg holds the register to read
' Output    : Returns the value read from the register
' Notes     : Reads Most Significant Byte first
'
Proc MCP9600_Read16(pReg As Byte), Word
    I2Cin MCP9600_SDA_Pin, MCP9600_SCL_Pin, cMCP9600_I2CADDR, pReg, [Result.Byte1, Result.Byte0]
EndProc

'--------------------------------------------------------------------------------------------------------
' Convert the raw value to degrees Centigrade
' Input     : pValue (MCP9600_wTemporary) holds the signed raw value to convert
' Output    : Returns the converted value, which is also held in global variable "MCP9600_fResult"
' Notes     : 0.06254 is used instead of 0.0625, to slightly round up the integer and 32-bit floating point calculation
'
Proc ConvToDegrees(pValue As MCP9600_wTemporary), MCP9600_fResult
    Result = pValue * 0.06254                                       ' Convert to Centigrade
EndProc

'--------------------------------------------------------------------------------------------------------
' Sets up the default MCP9600 device
' Input     : Global variables "MCP9600_bDeviceConfig" and MCP9600_bSensorConfig hold the values that are written to the device
' Output    : None
' Notes     : None
'
Proc MCP9600_Init()
    MCP9600_Write8(cMCP9600_DEVICECONFIG, MCP9600_bDeviceConfig)
    MCP9600_Write8(cMCP9600_SENSORCONFIG, MCP9600_bSensorConfig)
EndProc

'--------------------------------------------------------------------------------------------------------
' Read the temperature at the Hot end of the thermocouple
' Input     : None
' Output    : Returns the Hot junction temperature in Centigrade
' Notes     : None
'
Proc MCP9600_ReadHot(), MCP9600_fResult
    MCP9600_wTemporary = MCP9600_Read16(cMCP9600_HOTJUNCTION)
    Result = ConvToDegrees(MCP9600_wTemporary)                      ' Convert to degrees Centigrade
EndProc

'--------------------------------------------------------------------------------------------------------
' Read the cold junction temperature at the MCP9600 chip's package
' Input     : None
' Output    : Returns the Cold (Ambient) temperature in Centigrade
' Notes     : None
'
Proc MCP9600_ReadCold(), MCP9600_fResult
    MCP9600_wTemporary = MCP9600_Read16(cMCP9600_COLDJUNCTION)
    Result = ConvToDegrees(MCP9600_wTemporary)                      ' Convert to degrees Centigrade
EndProc

'--------------------------------------------------------------------------------------------------------
' Read the hot/cold delta temperature, which is the actual temperature of the Thermocouple
' Input     : None
' Output    : Returns the Hot/Cold delta temperature in Centigrade
' Notes     : None
'
Proc MCP9600_ReadThermo(), MCP9600_fResult
    MCP9600_wTemporary = MCP9600_Read16(cMCP9600_JUNCTIONDELTA)
    Result = ConvToDegrees(MCP9600_wTemporary)                      ' Convert to degrees Centigrade
EndProc

'--------------------------------------------------------------------------------------------------------
' Enable or disable the MCP9600 device
' Input     : pFlag holds 1 for awake mode, 0 for sleep mode
' Output    : None
' Notes     : Shutdown Mode. Bits 1-0 of the cMCP9600_DEVICECONFIG register
'               00 = Normal operation
'               01 = Shutdown mode
'               10 = Burst mode
'               11 = Unimplemented
'
Proc MCP9600_Enable(pFlag As Bit)
    MCP9600_bDeviceConfig = MCP9600_bDeviceConfig & %11111100       ' Clear the mode bits

    If pFlag = 0 Then                                               ' Sleep mode?
        MCP9600_bDeviceConfig.1 = 1                                 ' Yes. So set bit-1 of the cMCP9600_DEVICECONFIG register
    EndIf
    MCP9600_Write8(cMCP9600_DEVICECONFIG, MCP9600_bDeviceConfig)    ' Write the value to the MCP9600 device
EndProc

'--------------------------------------------------------------------------------------------------------
' Set the ADC resolution that the MCP9600 device will use
' Input     : pRes holds the ADC resolution:
'               cMCP9600_ADCRESOLUTION_18
'               cMCP9600_ADCRESOLUTION_16
'               cMCP9600_ADCRESOLUTION_14
'               cMCP9600_ADCRESOLUTION_12
' Output    : None
' Notes     : Sets the ADC Measurement Resolution. Bits 6-5 of the cMCP9600_DEVICECONFIG register
'               00 = 18-bit Resolution
'               01 = 16-bit Resolution
'               10 = 14-bit Resolution
'               11 = 12-bit Resolution
'
Proc MCP9600_SetADCRes(pRes As Byte)
    If pRes > 3 Then pRes = 3

    MCP9600_bDeviceConfig = MCP9600_bDeviceConfig & %10011111       ' Clear the resolution bits
    pRes = pRes << 5                                                ' Move the value into the correct position
    MCP9600_bDeviceConfig = MCP9600_bDeviceConfig | pRes            ' Or the value into the register
    MCP9600_Write8(cMCP9600_DEVICECONFIG, MCP9600_bDeviceConfig)    ' Write to the device config register

EndProc

'--------------------------------------------------------------------------------------------------------
' Read the ADC resolution used by the MCP9600 device
' Input     : None
' Output    : Returns the resolution that the MCP9600 device is using:
'               0 = 18-bit Resolution
'               1 = 16-bit Resolution
'               2 = 14-bit Resolution
'               3 = 12-bit Resolution
' Notes     : None
'
Proc MCP9600_GetADCRes(), Byte
    Result = MCP9600_Read8(cMCP9600_DEVICECONFIG)                   ' Read the device config register
    Ror Result                                                      ' \
    Ror Result                                                      ' |
    Ror Result                                                      ' | Rotate the device bits into the LSB
    Ror Result                                                      ' |
    Ror Result                                                      ' /
    Result = Result & %00000011                                     ' Mask the required bits and return the value
EndProc

'--------------------------------------------------------------------------------------------------------
' Read the raw ADC value
' Input     : None
' Output    : Returns the 32-bit signed value from the ADC
' Notes     : None
'
Proc MCP9600_ReadADC(), SDword
    Result.Byte3 = 0
    I2Cin MCP9600_SDA_Pin, MCP9600_SCL_Pin, cMCP9600_I2CADDR, cMCP9600_RAWDATAADC, [Result.Byte2,
                                                                                    Result.Byte0,
                                                                                    Result.Byte0]
    If Result.23 = 1 Then                                           ' \
        Result.Byte3 = $FF                                          ' | Extend signed 24 bits to signed 32-bits
    EndIf                                                           ' /
EndProc

'--------------------------------------------------------------------------------------------------------
' Read the type of thermocouple setup in the MCP9600 device
' Input     : None
' Output    : Returns the selected thermocouple type:
'              0 = Type K
'              1 = Type J
'              2 = Type T
'              3 = Type N
'              4 = Type S
'              5 = Type E
'              6 = Type B
'              7 = Type R
' Notes     : None
'
Proc MCP9600_GetThermoType(), Byte
    Result = MCP9600_Read8(cMCP9600_SENSORCONFIG)                   ' Read the sensor config register
    Ror Result                                                      ' \
    Ror Result                                                      ' |
    Ror Result                                                      ' | Rotate the sensor bits into the LSB
    Ror Result                                                      ' /
    Result = Result & %00000111                                     ' Mask the required bits and return the value
EndProc

'--------------------------------------------------------------------------------------------------------
' Set the thermocouple type used by the MCP9600 device
' Input     : pType holds the required type:
'               cMCP9600_TYPE_K
'               cMCP9600_TYPE_J
'               cMCP9600_TYPE_T
'               cMCP9600_TYPE_N
'               cMCP9600_TYPE_S
'               cMCP9600_TYPE_E
'               cMCP9600_TYPE_B
'               cMCP9600_TYPE_R
' Output    : None
' Notes     : Thermocouple Type Select bits 6-4 of the cMCP9600_SENSORCONFIG register
'              000 = Type K
'              001 = Type J
'              010 = Type T
'              011 = Type N
'              100 = Type S
'              101 = Type E
'              110 = Type B
'              111 = Type R
'
Proc MCP9600_SetThermoType(pType As Byte)
    If pType > 7 Then pType = 7

    pType = pType << 4                                              ' Move the type value into the correct position
    MCP9600_bSensorConfig = MCP9600_bSensorConfig & %10001111       ' Clear the type bits in the sensor config register
    MCP9600_bSensorConfig = MCP9600_bSensorConfig | pType           ' Or the value into the correct position
    MCP9600_Write8(cMCP9600_SENSORCONFIG, MCP9600_bSensorConfig)    ' Write the MCP9600_bSensorConfig value into the MCP9600 device
EndProc

'--------------------------------------------------------------------------------------------------------
' Read the filter coefficient the MCP9600 device is using
' Input     : None
' Output    : Returns how many readings we will be averaging (0 to 7)
'              0 = Filter off
'              1 = Minimum filter
'              7 = Maximum filter
' Notes     : None
'
Proc MCP9600_GetFilterCoeff(), Byte
    Result = MCP9600_Read8(cMCP9600_SENSORCONFIG)                   ' Read the sensor config register
    Result = Result & %00000111                                     ' Mask the required bits and return the value
EndProc

'--------------------------------------------------------------------------------------------------------
' Set the required filter coefficient for the MCP9600  device
' Input     : pFiltCount holds how many readings will be averaged (0 to 7)
' Output    : None
' Notes     : Filter Coefficient. Bits 2-0 of the sensor config register
'              0 = Filter off
'              1 = Minimum filter
'              4 = Mid filter
'              7 = Maximum filter
'
Proc MCP9600_SetFilterCoeff(pFiltCount As Byte)
    If pFiltCount > 7 Then ExitProc

    MCP9600_bSensorConfig = MCP9600_bSensorConfig & %11111000       ' Clear the coefficient bits in the sensor config register
    MCP9600_bSensorConfig = MCP9600_bSensorConfig | pFiltCount      ' Or the value into the correct position
    MCP9600_Write8(cMCP9600_SENSORCONFIG, MCP9600_bSensorConfig)    ' Write the MCP9600_bSensorConfig value into the MCP9600 device
EndProc

'--------------------------------------------------------------------------------------------------------
' Read an alert's temperature setting
' Input     : pAlert holds which alert value to read (1 to 4)
' Output    : Returns the temperature in Centigrade
' Notes     : None
'
Proc MCP9600_GetAlertTemp(pAlert As Byte), MCP9600_fResult
    pAlert = pAlert - 1
    If pAlert > 3 Then ExitProc

    MCP9600_wTemporary = MCP9600_Read16(cMCP9600_ALERTLIMIT_1 + pAlert)
    Result = ConvToDegrees(MCP9600_wTemporary)                      ' Convert to degrees Centigrade
EndProc

'--------------------------------------------------------------------------------------------------------
' Set an alert's temperature setting
' Input     : pAlert Which pAlert output we're getting, can be 1 to 4
'           : pTemp  holds the floating point temperature in Centigrade
' Output    : None
' Notes     : None
'
Proc MCP9600_SetAlertTemp(pAlert As Byte, pTemp As Float)
    pAlert = pAlert - 1
    If pAlert > 3 Then ExitProc

    MCP9600_wTemporary = pTemp / 0.06254             ' Convert from degrees Centigrade
    MCP9600_Write16(cMCP9600_ALERTLIMIT_1 + pAlert, MCP9600_wTemporary)
EndProc

'--------------------------------------------------------------------------------------------------------
' Configure temperature alert
' Input     : pAlert holds which alert to setup (1 to 4)
'           : pEnabled holds 1 for the alert to be enabled, 0 for the alert to be disabled
'           : pRising holds 1 if an alert is triggered when the temperature rises above a threshold. 0 for for an alert falling below a threshold
'           : pCold holds 1 if the alert temperature is the internal chip temperature, or 0 for the thermocouple. Default is thermocouple
'           : pActHigh holds 1 for the output pin to go high on alert, or 0 for the pin to go low
'           : pIntMode holds 1 for the output pin to lath on until it is cleared, or 0 for comparator mode.
' Output    : None
' Notes     : None
'
Proc MCP9600_ConfigureAlert(pAlert As Byte, pEnabled As Bit, pRising As Bit, pCold As Bit, pActHigh As Bit, pIntMode As Bit)
    pAlert = pAlert - 1
    If pAlert > 3 Then ExitProc

    MCP9600_bTemporary0 = 0

    If pEnabled = 1 Then
        MCP9600_bTemporary0.0 = 1
    EndIf
    If pIntMode = 1 Then
        MCP9600_bTemporary0.1 = 1
    EndIf
    If pActHigh = 1 Then
        MCP9600_bTemporary0.2 = 1
    EndIf
    If pRising = 1 Then
        MCP9600_bTemporary0.3 = 1
    EndIf
    If pCold = 1 Then
        MCP9600_bTemporary0.4 = 1
    EndIf
    MCP9600_Write8(cMCP9600_ALERTLIMIT_1 + pAlert, MCP9600_bTemporary0)
EndProc

'--------------------------------------------------------------------------------------------------------
' Read the MCP9600 device's Status register
' Input     : None
' Output    : Returns the 8-bit Status value
' Notes     : None
'
Proc MCP9600_GetStatus(), Byte
    Result = MCP9600_Read8(cMCP9600_STATUS)
EndProc

$endif      ' _MCP9600_INC_

As can be seen, the library has procedures for all of the internal registers, and a demonstration program is listed below that reads the temperature and displays it on a serial terminal:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Interface to an MCP9600 Thermocouple amplifier, using an I2C interface.
' Transmit the temperature, in degrees Centigrade, to a serial terminal.
'
' Written for the Positron8 BASIC compiler by Les Johnson.
'
    Device = 18F26K22                                   ' Tell the compiler what device to compile for
    Declare Xtal = 16                                   ' Tell the compiler what frequency the device is operating at (in MHz)
    Declare Float_Display_Type = Fast                   ' Use the compiler's faster and more accurate Floating Point to ASCII converter
'
' Setup USART1
'
    Declare HSerial1_Baud = 9600                        ' The Baud rate
    Declare HSerout1_Pin = PORTC.6                      ' The pin used for USART1 TX
'
' Setup the pins to use for the I2C interface to the MCP9600 device
'
    $define MCP9600_SCL_Pin PORTC.3                     ' Connects to the MCP9600's SCL pin
    $define MCP9600_SDA_Pin PORTC.4                     ' Connects to the MCP9600's SDA pin

    Include "MCP9600.inc"                               ' Load the MCP9600 library into the program's listing
'
' Create a variable for the demo
'
    Dim fTemperature As Float                           ' Holds the temperature of the Thermocouple

'--------------------------------------------------------------------------------------------------------
' The main program starts here
' Read the MCP9600 thermocouple amplifier and display the temperature on a serial terminal
'
Main:
    MCP9600_Init()                                      ' Initialise the MCP9600 device

    MCP9600_SetThermoType(cMCP9600_TYPE_K)              ' Set it for a K type thermocouple
    MCP9600_SetADCRes(cMCP9600_ADCRESOLUTION_18)        ' Set it for an 18-bit ADC resolution
    MCP9600_SetFilterCoeff(3)                           ' Set the filter coefficient to 3 samples

    Do                                                  ' Create a loop
        fTemperature = MCP9600_ReadThermo()             ' Read the thermocouple's temperature
        HRsoutLn "Temperature=", Dec1 fTemperature      ' Transmit the temperature to a serial terminal
        DelayMs 500                                     ' A small delay so the values can be seen changing
    Loop                                                ' Do it forever

Below is a screenshot of the above demo program operating in the Proteus simulator:

MCP9600 Isis Screenshot.jpg