// SPI Stuff here
#include "SPI.h"
const uint8_t MCP3911_CS = 9; // Teensy SPI CS1 = MCP3911
const uint8_t SD_Card_CS = 10; // Teensy CS0 = SD Card
const uint8_t MCP3911_DR = 2; // Input for Data Ready Signal from MCP3911
const uint8_t MCP3911_CLK = 6; // Teensy CLK Signal output for MCP3911 OSC1 input
int16_t Volts,Current;
volatile uint8_t MCP3911_Data_Ready=0;
void setup() {
//SPI Bus setup
digitalWrite(MCP3911_CS,HIGH); //
pinMode (MCP3911_CS, OUTPUT); // MCP3911
pinMode (SD_Card_CS, OUTPUT); // SD Card
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV2); //i.e. 8MHz on a Teensy running at 48MHz.
SPI.begin();
pinMode (MCP3911_DR, INPUT_PULLUP); //Data Ready Pin on MCP3911 for later use in interrupt
attachInterrupt(MCP3911_DR,MCP3911_Data_Ready_ISR,FALLING);
//setup CLK function on Pin 6 to put CLK signal into MCP3911 OSC1 input
pinMode (MCP3911_CLK, OUTPUT); // MCP3911 OSC1 input
//analogWriteResolution(1);//make it single-bit only (need a SQW)
//analogWrite(MCP3911_CLK, 1); //now make it run
//analogWriteFrequency(MCP3911_CLK, 1000000); //for 1MHz SQW operation
//Setup Serial Comms
Serial.begin(115200);
delay(1000); //give me 1s to get serial monitor set up
//Now setup the ADC - produces diagnostics
Initial_ADC_Setup();
}
void loop()
{
//in beginning, ensure 12MHz Master Clock
analogWriteResolution(1);//make it single-bit only (need a SQW)
analogWrite(MCP3911_CLK, 1); //now make it run
analogWriteFrequency(MCP3911_CLK, 12000000); //for 12MHz SQW operation
int i=0;
uint32_t nextsecond=millis()+1000UL;
while (millis()<nextsecond) {
if (MCP3911_Data_Ready>0)
{
read_VI_from_MCP3911();
Serial.print("Volts: ");
Serial.print(Volts);
Serial.print(" - Current: ");
Serial.println(Current);
i++;
MCP3911_Data_Ready=0;
}
}
Serial.print("Samples/s: ");
Serial.println(i);
}
void MCP3911_Data_Ready_ISR ()
{
MCP3911_Data_Ready++;
}
//SPI function to see if data is ready, alternative to intrrupt approach using MCP3911_DR pin
uint8_t Is_MCP3911_Data_Ready ()
{
/* R/W Status and Communication Register (Read / Write)
Address: 0x0A - 16 bits
bit 15:14 MODOUT<1:0>: Modulator Output Setting for MDAT pins
11 = Both CH0 and CH1 modulator outputs are present on MDAT1 and MDAT0 pins, both SINC fil- ters are off, no data ready pulse is present
10 = CH1 ADC Modulator output present on MDAT1 pin, SINC filter on channel 1 is off, data ready pulse from channel 1 is not present on DR pin
01 = CH0 ADC Modulator output present on MDAT0 pin, SINC filter on channel 0 is off, data ready pulse from channel 0 is not present on DR pin
*00 = No Modulator output is enabled, SINC filters are on, data readys are present on DR pin for both channels (DEFAULT)
bit 13 Unimplemented, read as 0
bit 12 DR_HIZ: Data Ready Pin Inactive State Control
1 = The DR pin state is a logic high when data is NOT ready
*0 = The DR pin state is high impedance when data is NOT ready(DEFAULT)
bit 11:10 DRMODE<1:0>: Data Ready Pin (DR) mode configuration bits
11 = Both Data Ready pulses from CH0 and CH1 are output on DR pin
10 = Data Ready pulses from CH1 ADC are output on DR pin. Data ready pulses from CH0 are not present on the DR pin.
01 = Data Ready pulses from CH0 ADC are output on DR pin. Data ready pulses from CH1 are not present on the DR pin.
*00 = Data Ready pulses from the lagging ADC between the two are output on DR pin. The lagging ADC depends on the PHASE register and on the OSR. (DEFAULT)
bit 9:8 DRSTATUS<1:0>: Data Ready Status
*11 = ADC Channel 1 and Channel 0 data not ready (DEFAULT)
10 = ADC Channel 1 data not ready, ADC Channel 0 data ready
01 = ADC Channel 0 data not ready, ADC Channel 1 data ready
00 = ADC Channel 1 and Channel 0 data ready
*/
uint8_t i = Read_MCP3911_Register(0x0A); // send address with read command to MCP3911
i = i & B00000011; //strip out stuff not needed
return i;
}
//read Current and Voltage results from MCP3911
void read_VI_from_MCP3911()
{
digitalWrite(MCP3911_CS, LOW); // now take CS low to enable SPI device
SPI.transfer(0x01); // send 0x00 address with read command to MCP3911
//now ask for data
//Volts = SPI.transfer(0x00)<<24;
Volts = SPI.transfer(0x00)<<8;
Volts |= SPI.transfer(0x00);
//Current = SPI.transfer(0x00)<<24;
Current = SPI.transfer(0x00)<<8;
Current |= SPI.transfer(0x00);
digitalWrite(MCP3911_CS, HIGH); // deselect the CS pin.
//Volts >>= 8; // now get the sign right ===> Convert from 24bit number to signed 32bit int.
//Current >>= 8; // now get the sign right ===> Convert from 24bit number to signed 32bit int.
}
void Initial_ADC_Setup()
{
// First Put ADC into Reset Mode
/*
bit 7:6 RESET<1:0>: Reset mode setting for ADCs
11 = Both CH0 and CH1 ADC are in reset mode
10 = CH1 ADC in reset mode
01 = CH0 ADC in reset mode
*00 = Neither ADC in reset mode(default)
bit5:4 SHUTDOWN<1:0>: Shutdown mode setting for ADCs
11 = Both CH0 and CH1 ADC in Shutdown
10 = CH1 ADC in Shutdown
01 = CH0 ADC in Shutdown
*00 = Neither Channel in Shutdown(default)
bit 3: Not implemented, read as 0
bit 2: VREFEXT Internal Voltage Reference Shutdown Control
1 = Internal Voltage Reference Disabled
*0 = Internal Voltage Reference Enabled (Default)
bit 1: CLKEXT Internal Clock selection bits
*1 = External clock drive by MCU on OSC1 pin (crystal oscillator disabled, no internal power consumption) (Default)
0 = Crystal oscillator is enabled. A crystal must be placed between OSC1 and OSC2 pins.
bit 0: Not implemented, read as 0
*/
Write_MCP3911_Register (0x0D, B11000010);
/* Phase Register (Read / Write)
Address: 0x07 - 16 bits
bit 15:12 Unimplemented, read as ‘0’
bit 11:0 CH0 relative to CH1 phase delay
Delay = PHASE Register’s two’s complement code/DMCLK (Default PHASE = 0).
Write_MCP3911_Register (0x07, B00000000);
Write_MCP3911_Register (0x08, B00000000);
since no phase delay is programmed at present, leave out
*/
/* R/W Gain and Boost Configuration Register (Read / Write)
Address: 0x09 - 8 bits
bit 7:6 BOOST<1:0> Bias Current Selection
*11 = Both channels have current x 2
10 = Both channels have current x 1(DEFAULT)
01 = Both channels have current x 0.66
00 = Both channels have current x 0.5
bit 5:3 PGA_CH1<2:0>: PGA Setting for Channel 1
111 = Reserved (Gain = 1)
110 = Reserved (Gain = 1)
101 = Gain is 32
100 = Gain is 16
011 = Gain is 8
010 = Gain is 4
001 = Gain is 2
*000 = Gain is 1 (DEFAULT)
bit 2:0 PGA_CH0<2:0>: PGA Setting for Channel 0
111 = Reserved (Gain = 1)
110 = Reserved (Gain = 1)
101 = Gain is 32
100 = Gain is 16
011 = Gain is 8
010 = Gain is 4
001 = Gain is 2
*000 = Gain is 1 (DEFAULT)
*/
Write_MCP3911_Register (0x09, B11000000);
/* R/W Status and Communication Register (Read / Write)
Address: 0x0A - 16 bits
bit 15:14 MODOUT<1:0>: Modulator Output Setting for MDAT pins
11 = Both CH0 and CH1 modulator outputs are present on MDAT1 and MDAT0 pins, both SINC fil- ters are off, no data ready pulse is present
10 = CH1 ADC Modulator output present on MDAT1 pin, SINC filter on channel 1 is off, data ready pulse from channel 1 is not present on DR pin
01 = CH0 ADC Modulator output present on MDAT0 pin, SINC filter on channel 0 is off, data ready pulse from channel 0 is not present on DR pin
*00 = No Modulator output is enabled, SINC filters are on, data readys are present on DR pin for both channels (DEFAULT)
bit 13 Unimplemented, read as 0
bit 12 DR_HIZ: Data Ready Pin Inactive State Control
*1 = The DR pin state is a logic high when data is NOT ready
0 = The DR pin state is high impedance when data is NOT ready(DEFAULT)
bit 11:10 DRMODE<1:0>: Data Ready Pin (DR) mode configuration bits
11 = Both Data Ready pulses from CH0 and CH1 are output on DR pin
10 = Data Ready pulses from CH1 ADC are output on DR pin. Data ready pulses from CH0 are not present on the DR pin.
01 = Data Ready pulses from CH0 ADC are output on DR pin. Data ready pulses from CH1 are not present on the DR pin.
*00 = Data Ready pulses from the lagging ADC between the two are output on DR pin. The lagging ADC depends on the PHASE register and on the OSR. (DEFAULT)
bit 9:8 DRSTATUS<1:0>: Data Ready Status
*11 = ADC Channel 1 and Channel 0 data not ready (DEFAULT)
10 = ADC Channel 1 data not ready, ADC Channel 0 data ready
01 = ADC Channel 0 data not ready, ADC Channel 1 data ready
00 = ADC Channel 1 and Channel 0 data ready
*/
Write_MCP3911_Register (0x0A, B00010011); // data byte #1;
/*
bit 7:6 READ<1:0>: Address Loop Setting
*11 = Address counter incremented, cycle through entire register set
10 = Address counter loops on register types (DEFAULT)
01 = Address counter loops on register groups
00 = Address not incremented, continually read single register
bit 5: WRITE: Address Loop Setting for Write mode
*1 = Address counter loops on entire register map (DEFAULT)
0 = Address not incremented, continually write same single register
bit 4:3 WIDTH <1:0> ADC Channel output data word width
*11 = Both channels are in 24-bit mode(DEFAULT)
10 = Channel1 in 24-bit mode, Channel0 in 16-bit mode
01 = Channel1 in 16-bit mode, Channel0 in 24-bit mode
00 = Both channels are in 16-bit mode
bit 2: EN_OFFCAL Enables or disables the 24-bit digital offset calibration on both channels
1 = Enabled; this mode does not add any group delay
*0 = Disabled (DEFAULT)
bit 1: EN_GAINCAL Enables or disables the 24-bit digital offset calibration on both channels
1 = Enabled; this mode adds a group delay on both channels of 24 DMCLK periods. All data ready pulses are delayed by 24 clock periods compared to the mode with EN_GAINCAL=0
*0 = Disabled(DEFAULT)
bit 0: Unimplemented, read as 0
*/
Write_MCP3911_Register (0x0B, B11100000);
/* Offset Correction Register - Channel 0 i.e. Voltage (Read / Write)
Address: 0x0E - 24 bits
bit 23:0 Digital Offset calibration value for the corresponding channel CHn.
This register simply is added to the output code of the channel bit-by-bit. This register is 24-bit two's complement MSB first coding.
CHn Output Code = OFFCAL_CHn + ADC CHn Output Code.
This register is a Don't Care if EN_OFFCAL=0 (Offset calibration disabled) but its value is not cleared by the EN_OFFCAL bit.
Write_MCP3911_Register (0x0E, B00000000);
Write_MCP3911_Register (0x0F, B00000000);
Write_MCP3911_Register (0x10, B00000000);
not implemented now, so disabled
*/
/* Gain Correction Register - Channel 0 i.e. Voltage (Read / Write)
Address: 0x11 - 24 bits
bit 23:0 Digital gain error calibration value for the corresponding channel CHn.
This register is 24-bit signed MSB first coding with a range of -1x to +0.9999999x (from 0x80000 to 0x7FFFFF).
The gain calibration adds 1x to this register and multiplies it to the output code of the channel bit by bit, after offset calibration.
The range of the gain calibration is thus from 0x to 1.9999999x (from 0x80000 to 0x7FFFFF). The LSB corresponds to a 2-23 increment in the multiplier.
CHn Output Code = (GAINCAL_CHn+1)*ADC CHn Output Code.
This register is a Don't Care if EN_GAINCAL=0 (Offset calibration disabled) but its value is not cleared by the EN_GAINCAL bit.
Write_MCP3911_Register (0x11, B00000000);
Write_MCP3911_Register (0x12, B00000000);
Write_MCP3911_Register (0x13, B00000000);
not implemented now, so disabled
*/
/* Offset Correction Register - Channel 1 i.e. Current (Read / Write)
Address: 0x14 - 24 bits
This register simply is added to the output code of the channel bit-by-bit. This register is 24-bit two's complement MSB first coding.
CHn Output Code = OFFCAL_CHn + ADC CHn Output Code.
This register is a Don't Care if EN_OFFCAL=0 (Offset calibration disabled) but its value is not cleared by the EN_OFFCAL bit.
Write_MCP3911_Register (0x14, B00000000);
Write_MCP3911_Register (0x15, B00000000);
Write_MCP3911_Register (0x16, B00000000);
not implemented now, so disabled
*/
/* Gain Correction Register - Channel 1 i.e. Current (Read / Write)
Address: 0x17 - 24 bits
bit 23:0 Digital gain error calibration value for the corresponding channel CHn.
This register is 24-bit signed MSB first coding with a range of -1x to +0.9999999x (from 0x80000 to 0x7FFFFF).
The gain calibration adds 1x to this register and multiplies it to the output code of the channel bit by bit, after offset calibration.
The range of the gain calibration is thus from 0x to 1.9999999x (from 0x80000 to 0x7FFFFF). The LSB corresponds to a 2-23 increment in the multiplier.
CHn Output Code = (GAINCAL_CHn+1)*ADC CHn Output Code.
This register is a Don't Care if EN_GAINCAL=0 (Offset calibration disabled) but its value is not cleared by the EN_GAINCAL bit.
Write_MCP3911_Register (0x17, B00000000);
Write_MCP3911_Register (0x18, B00000000);
Write_MCP3911_Register (0x19, B00000000);
not implemented now, so disabled
*/
/*Internal Voltage reference Temperature Coefficient Adjustment (Read / Write)
Address: 0x1A - 8 bits
bit 7:0 Internal Voltage Temperature coefficient register value. (See compensation (VREFCAL register)” for complete description). Section 5.7.3
Write_MCP3911_Register (0x1A, 0x42);
not implemented now, so disabled
*/
/* R/W Configuration Register (Read / Write)
Address: 0x0C - 16 bits
bit 15:14 PRE<1:0> Analog Master Clock (AMCLK) Prescaler Value
11 = AMCLK = MCLK / 8
10 = AMCLK = MCLK / 4
01 = AMCLK = MCLK / 2
*00 = AMCLK = MCLK (DEFAULT)
bit 13:11 OSR<2:0> Oversampling Ratio for Delta-Sigma A/D Conversion (ALL CHANNELS, fd / fS)
111=4096(fd=244spsforMCLK=4MHz, fs=AMCLK=1MHz)
*110=2048(fd=488spsforMCLK=4MHz, fs=AMCLK=1MHz)
101=1024(fd=976spsforMCLK=4MHz, fs=AMCLK=1MHz)
100=512(fd=1.953kspsforMCLK=4MHz, fs=AMCLK=1MHz)
011 = 256 (fd = 3.90625 ksps for MCLK = 4 MHz, fs = AMCLK = 1 MHz) (DEFAULT)
010=128(fd=7.8125kspsforMCLK=4MHz, fs=AMCLK=1MHz)
001=64(fd=15.625kspsforMCLK=4MHz, fs=AMCLK=1MHz)
000=32(fd=31.25kspsforMCLK=4MHz, fs=AMCLK=1MHz)
bit 10:9 DITHER<1:0> Control for dithering circuit for idle tones cancellation and improved THD
11 =DitheringON,bothchannels,Strength=Maximum(MCP3901Equivalent)-(DEFAULT)
10 = Dithering ON, both channels, Strength = Medium
*01 =DitheringON,bothchannels,Strength=Minimum
00 = Dithering turned OFF
bit 8 AZ_FREQ Auto-zero frequency setting
1 = Auto-zeroing algorithm running at higher speed
*0 = Auto-zeroing algorithm running at lower speed (Default)
*/
Write_MCP3911_Register (0x0C, B00110010);
/*
bit 7:6 RESET<1:0>: Reset mode setting for ADCs
11 = Both CH0 and CH1 ADC are in reset mode
10 = CH1 ADC in reset mode
01 = CH0 ADC in reset mode
*00 = Neither ADC in reset mode(default)
bit5:4 SHUTDOWN<1:0>: Shutdown mode setting for ADCs
11 = Both CH0 and CH1 ADC in Shutdown
10 = CH1 ADC in Shutdown
01 = CH0 ADC in Shutdown
*00 = Neither Channel in Shutdown(default)
bit 3: Not implemented, read as 0
bit 2: VREFEXT Internal Voltage Reference Shutdown Control
1 = Internal Voltage Reference Disabled
*0 = Internal Voltage Reference Enabled (Default)
bit 1: CLKEXT Internal Clock selection bits
*1 = External clock drive by MCU on OSC1 pin (crystal oscillator disabled, no internal power consumption) (Default)
0 = Crystal oscillator is enabled. A crystal must be placed between OSC1 and OSC2 pins.
bit 0: Not implemented, read as 0
*/
Write_MCP3911_Register (0x0D, B00000010);
Serial.println("MCP3911 Init complete...");
}
uint8_t Write_MCP3911_Register (uint8_t MCP3911_Register_Address, uint8_t Command) {
Serial.print("Command Register Received: ");
Serial.print(MCP3911_Register_Address,BIN);
Serial.print(" - Command Received: ");
Serial.println(Command,BIN);
MCP3911_Register_Address <<= 1; //left shift address one digit
digitalWrite(MCP3911_CS, LOW); // now take CS low to enable SPI device
SPI.transfer(MCP3911_Register_Address); // send address with write command to MCP3911
SPI.transfer(Command); //now send payload
digitalWrite(MCP3911_CS, HIGH); // deselect the CS pin.
Serial.print(" Write Command Byte Sent: ");
Serial.println(MCP3911_Register_Address,BIN); // verify what command was sent (i.e. address and write bit = 0)
//Now Verify all went well. If so, have the function return value of one,
//otherwise, alert the user that something is amiss.
uint8_t Response = Read_MCP3911_Register (MCP3911_Register_Address>>1);
if (Response == Command) return 1;
else
{
Serial.println("");
Serial.print("Error for register: ");
Serial.print(MCP3911_Register_Address>>1,BIN);
Serial.print(" - Command Sent: ");
Serial.print(Command,BIN);
Serial.print(" - Response Received: ");
Serial.println(Response,BIN);
Serial.println("");
return 0;
}
}
uint8_t Read_MCP3911_Register (uint8_t MCP3911_Register_Address) {
MCP3911_Register_Address <<=1; //left shift address one bit for command byte
MCP3911_Register_Address |=1; // Ensure read bit is set
//Serial.print(" Read Byte Command Sent: ");
//Serial.print(MCP3911_Register_Address,BIN);
digitalWrite(MCP3911_CS, LOW);
SPI.transfer(MCP3911_Register_Address); // send address with read command to MCP3911
uint8_t Response = SPI.transfer(0x0);
digitalWrite(MCP3911_CS, HIGH);
//Serial.print(" - Response Received: ");
//Serial.println(Response,BIN);
return Response;
}