MCP3021

From Steak Wiki
Jump to navigationJump to search

This chip uses I2C. It is not the most common I2C you will find online. Instead of sending a requested register or command to execute, you simply send the address of the ADC on the bus. The datasheet explains the expected comms in more detail, but it boils down to:

  • Host sends start bit, 7 bit address, and 1 bit at the end (1 for conversion, a 0 for getting only an announcement from the MCP).
  • MCP responds back with two bytes

This is slightly confusing, because Wire only sends a 7 bit address (not 8 bits) and there is no way to manually send bits (only bytes), that I found. It turns out that using requestFrom instead of beginTransmission / endTransmission will allow the 8th bit to be set high, and start a conversion.

Default address is A5, which is 101 (5 binary) added to a base address of 1001. So 0b1001101. Different addresses can be obtained. See data sheet for more details. 0b1001101 is 77 in decimal.


Arduino Uno w/Wire

This can be handled with the following Arduino Wire code:

#include <Wire.h>
int addr = 0b1001101; //(77 in dec)(however, technically
// an 8 bit i2c address, as it adds one bit to the end to 
// designate read/write or in this case, 
// read == check adc is accessible
// write == do adc conversion and respond with 2 bytes immediately
// after)

int U8_data = 0;
int L8_data = 0;

void setup(){
Serial.begin(9600);

Wire.begin();
Wire.setClock(10); //slow down clock, for debugging ease
}

void loop(){
delay(1000);

//Wire.beginTransmission(0b10011011);  
//Wire.beginTransmission(0b1001101);   //arduino library cuts off this
//only takes 7 bits, even if you want 8
//https://forum.arduino.cc/index.php?topic=482619.0 EDIT: this url was not helpful.
//Wire.endTransmission();
//i2c usually
//can either do beginTransmission w/writes and reads, then endTransmission
//or just requestFrom, without begin/end 

//For this adc, you want to request two bits, not do begintransmission
//or endtransmission


Wire.requestFrom(0b1001101, 2); //this appears to include the 8th
U8_data = Wire.read();
L8_data = Wire.read();

Serial.print("U8_data:");
Serial.println(U8_data);

Serial.print("L8_data:");
Serial.println(L8_data);
}

Beaglebone

Now that there is a working reference, we can read this adc via the beaglebone. Adapted from https://elinux.org/Interfacing_with_I2C_Devices

changes made: removed references to glib.h to avoid dependencies. removed writes at end. fixed comments, added main.

//#include <glib.h>
//#include <glib/gprintf.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define ADCRANGE 1024
#define ADCVCC 3.3

void sensors_ADC_init(void) {
    int file;
    char filename[40];
    const char *buffer;
    int addr = 0x4d;        // The I2C address of the ADC

    sprintf(filename,"/dev/i2c-2");

    if ((file = open(filename,O_RDWR)) < 0) {
        printf("Failed to open the bus.");
        // ERROR HANDLING; you can check errno to see what went wrong
        exit(1);
    }
//! this section is req'd to assign the address
    if (ioctl(file,I2C_SLAVE,addr) < 0) {
        printf("Failed to acquire bus access and/or talk to slave.\n");
        // ERROR HANDLING; you can check errno to see what went wrong
        exit(1);
    }

    char buf[10] = {0};
    float data;
    char channel;
 


    for(int i = 0; i<4; i++) { //try to run 4 times (for whatever reason they decided upon this)
        // Using I2C Read
        if (read(file,buf,2) != 2) {
            /* ERROR HANDLING: i2c transaction failed */
            printf("Failed to read from the i2c bus.\n");
            buffer = strerror(errno);
            printf(buffer);
            printf("\n\n");
        } else {
            data = (float)((buf[0] & 0b00001111)<<8)+buf[1];
            data = data/(ADCRANGE*ADCVCC);
            channel = ((buf[0] & 0b00110000)>>4);
            printf("Channel %02d Data:  %04f\n",channel,data);
        }//this data formatting needs work. Probably wrong.
    }

    //unsigned char reg = 0x10; // Device register to access
    //buf[0] = reg;
    ////buf[0] = 0b11110000;

    /*if (write(file,buf,1) != 1) {
        // ERROR HANDLING: i2c transaction failed 
        printf("Failed to write to the i2c bus.\n");
        buffer = g_strerror(errno);
        printf(buffer);
        printf("\n\n");
    }*/

}


void main(){

	sensors_ADC_init();
}

Misc Notes on I2C

- speed
- different ways to start transmission (request, and begintransniss/endtransmi)
- unusual i2c devices such as mcp
- timing inconsistencies on waveform
- the need for logic analyzer w/support for i2c
- i2c data only changes when clk is low (assuming only one mode for i2c, unlike spi)
- example waveforms would help (different examples on a wiki page)
- use logic analyzer OR bus pirate for testing i2c


References