Carbonera
Posts: 11
Joined: Wed Sep 05, 2012 5:21 pm

Fast ADC with MCP3208

Sun Dec 30, 2012 10:53 pm

Hi everybody,
I am using the ADC MCP3208 with raspberry pi.
Until now I can comunicate raspberry with mcp3208 and get the samples.
The problem is that in the datasheet says that with 2.7V I can get 50Ksps and i get only 6480sps. I am using the spidev to use the SPI.
I follow the exemple from here:
http://www.brianhensley.net/2012/07/get ... ry-pi.html
and did some modifies.

I wanna know if someone is working on it to or in other ADC for fast sample in raspberry.
he is the code that i am working on (any dots ask me):

Code: Select all

/*
 * SPI testing utility (using spidev driver)
 *
 * Copyright (c) 2007  MontaVista Software, Inc.
 * Copyright (c) 2007  Anton Vorontsov <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
 */

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <termios.h>
#include <time.h>



#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static void pabort(const char *s)
{
	perror(s);
	abort();
}

static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 2000000;
static uint16_t delay;
static int ADCOUNT;
static long int n=0;

static void transfer(int fd){


}

static void print_usage(const char *prog)
{
	printf("Usage: %s [-DsbdlHOLC3]\n", prog);
	puts("  -D --device   device to use (default /dev/spidev1.1)\n"
	     "  -s --speed    max speed (Hz)\n"
	     "  -d --delay    delay (usec)\n"
	     "  -b --bpw      bits per word \n"
	     "  -l --loop     loopback\n"
	     "  -H --cpha     clock phase\n"
	     "  -O --cpol     clock polarity\n"
	     "  -L --lsb      least significant bit first\n"
	     "  -C --cs-high  chip select active high\n"
	     "  -3 --3wire    SI/SO signals shared\n");
	exit(1);
}

static void parse_opts(int argc, char *argv[])
{
	while (1) {
		static const struct option lopts[] = {
			{ "device",  1, 0, 'D' },
			{ "speed",   1, 0, 's' },
			{ "delay",   1, 0, 'd' },
			{ "bpw",     1, 0, 'b' },
			{ "loop",    0, 0, 'l' },
			{ "cpha",    0, 0, 'H' },
			{ "cpol",    0, 0, 'O' },
			{ "lsb",     0, 0, 'L' },
			{ "cs-high", 0, 0, 'C' },
			{ "3wire",   0, 0, '3' },
			{ "no-cs",   0, 0, 'N' },
			{ "ready",   0, 0, 'R' },
			{ NULL, 0, 0, 0 },
		};
		int c;

		c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);

		if (c == -1)
			break;

		switch (c) {
		case 'D':
			device = optarg;
			break;
		case 's':
			speed = atoi(optarg);
			break;
		case 'd':
			delay = atoi(optarg);
			break;
		case 'b':
			bits = atoi(optarg);
			break;
		case 'l':
			mode |= SPI_LOOP;
			break;
		case 'H':
			mode |= SPI_CPHA;
			break;
		case 'O':
			mode |= SPI_CPOL;
			break;
		case 'L':
			mode |= SPI_LSB_FIRST;
			break;
		case 'C':
			mode |= SPI_CS_HIGH;
			break;
		case '3':
			mode |= SPI_3WIRE;
			break;
		case 'N':
			mode |= SPI_NO_CS;
			break;
		case 'R':
			mode |= SPI_READY;
			break;
		default:
			print_usage(argv[0]);
			break;
		}
	}
}

int main(int argc, char *argv[]){

	int ret = 0;
	int fd;
    int x=0;
	int n_anterior=0;
	time_t now;
	struct tm *tm;
	int flag=0;
	int k;
		int ret;
	uint8_t tx[] = {
		0x0, 0x6, 0x0, 0x0, 0x0, 0x0
	
	};
	uint8_t rx[ARRAY_SIZE(tx)] = {0, };
	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = ARRAY_SIZE(tx),
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};

	
	
	now = time(0);
	tm = localtime (&now);
	flag = tm-> tm_min;
	flag ++;
	if(flag==60) flag=0;
	
	parse_opts(argc, argv);
	
	

	
	//ret=0;
	
    
	fd = open(device, O_RDWR);
	if (fd < 0)
		pabort("can't open device");

	/*
	 * spi mode
	 */
	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
	if (ret == -1)
		pabort("can't set spi mode");

	ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
	if (ret == -1)
		pabort("can't get spi mode");

	/*
	 * bits per word
	 */
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't set bits per word");

	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't get bits per word");

	/*
	 * max speed hz
	 */
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't set max speed hz");

	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't get max speed hz");

	//printf("spi mode: %d\n", mode);
	//printf("bits per word: %d\n", bits);
	//printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

	now = time(0);
	tm = localtime(&now);
	

	
	
	while(1){
		ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

	ADCOUNT = ((rx[4]&15<<8) + rx[5]);
	n++;
	
	now = time(0);
	tm = localtime (&now);
	
	if((tm->tm_min) == flag){
	n=(n)/(60*60*6); // to see samples per channel per cycle (60hz)
	printf("Conversao %d amostras %d",ADCOUNT,n);
	puts("");
	flag++;
	if(flag>60) flag = 0;
	}
    
	
	n=0;
	
	}

	close(fd);

return ret;
}

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Mon Dec 31, 2012 8:16 am

Carbonera wrote:Hi everybody,
I am using the ADC MCP3208 with raspberry pi.
Until now I can comunicate raspberry with mcp3208 and get the samples.
The problem is that in the datasheet says that with 2.7V I can get 50Ksps and i get only 6480sps. I am using the spidev to use the SPI.
I follow the exemple from here:
http://www.brianhensley.net/2012/07/get ... ry-pi.html
and did some modifies.

I wanna know if someone is working on it to or in other ADC for fast sample in raspberry.
he is the code that i am working on (any dots ask me):
What you're up against is the latency of the SPI driver in the kernel. It works well, but there is more oberheard than there should be. Patches have been developed, but they're not yet merged into the kernel yet.

The code you posted was the standard linux test code, not your ADC code.. I've written code for the MCP3002 2-channel ADC on the Gertboard and when I looked at the 8-channel variant, it seemed like a minor tweak was all that was needed - however I'm not able to sample that very fast, and the fastest SPI clock is 1MHz for that chip at 3.3v too.

If you want to see my code grab a copy of wiringPi and look in wiringPi/gertboard.c and examples/gertboard.c too, however with the SPI driver the way it is, I feel it's unlikely you're going to see much speed improvement for some time. See this thread for more details: http://www.raspberrypi.org/phpBB3/viewt ... 44&t=19489

-Gordon
--
Gordons projects: https://projects.drogon.net/

Carbonera
Posts: 11
Joined: Wed Sep 05, 2012 5:21 pm

Re: Fast ADC with MCP3208

Mon Dec 31, 2012 12:44 pm

The code you posted was the standard linux test code, not your ADC code.. I've written code for the MCP3002 2-channel ADC on the Gertboard and when I looked at the 8-channel variant, it seemed like a minor tweak was all that was needed - however I'm not able to sample that very fast, and the fastest SPI clock is 1MHz for that chip at 3.3v too.
Gordon the code I show was modified and it is working fine with the mcp3208.
Here I got the conversion for AD:
ADCOUNT = ((rx[4]&15<<8) + rx[5]);
I use 2 Mhz into mcp3208 with 3.3V and work fine.
Thanks for the information about SPI driver.

So now I will try another chip to do that more fast. I am seeing the AD7656 anyone work with this? or maybe with other ADC?

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Mon Dec 31, 2012 1:29 pm

Carbonera wrote:
The code you posted was the standard linux test code, not your ADC code.. I've written code for the MCP3002 2-channel ADC on the Gertboard and when I looked at the 8-channel variant, it seemed like a minor tweak was all that was needed - however I'm not able to sample that very fast, and the fastest SPI clock is 1MHz for that chip at 3.3v too.
Gordon the code I show was modified and it is working fine with the mcp3208.
Ah! Woops - yes, I just saw the start of it, recognised it and thought you'd posted it instead of your own one!
Carbonera wrote: Here I got the conversion for AD:
ADCOUNT = ((rx[4]&15<<8) + rx[5]);
I use 2 Mhz into mcp3208 with 3.3V and work fine.
Thanks for the information about SPI driver.

So now I will try another chip to do that more fast. I am seeing the AD7656 anyone work with this? or maybe with other ADC?
Speed it limited by several factors - one is the SPI clock speed, and that will be up to the max. speed the chip can go to - my (limited) experience is that the A2D's use the incoming SPI clock as their own sequencer clock rather than have their own oscillator, so the speed of the SPI clock will determine the sampling speed - so if the max. clock is 1MHz and it takes 16 clocks to sample 1 channel, then the fastest it will go is 16uS per sample, or 62.5K/second - for one channel. If you have to sample 8 channels, then the max. you can sample a single channel is going to be just under 8K samples/sec.

Just doing simple maths will tell you that even quadrupling the clock speed to 4MHz is only going to take the sample rate to ~32K samples/sec - barely enough to sample low-grade audio... (unless you use less channels!)

The other thing that limits speed is the Linux kernel driver - and that's not going to change soon - see the link I posted earlier...

I wish it were better myself, but the Pi - Jack of all trades, master of none, as it were! ie. a great little general purpose device, but not excelling in anything in particular (other than maybe what the SoC was designed for - playing video!)

The AD7656 is faster - and can sample all channels concurrenty too - but its SPI clock is a max. of 18MHz - that translates to 16MHz on the Pi. A serial read also needs 16 bits clocked over the SPI, so 16 bits at 16MHz or 1 sample per uS, or 1M samples per second. For one channel. Divide by the number of channels your sampling - down to 6 channels for that chip. Although, the chip itself is only going to do a max. of 250K samples/sec, so that means in theory, you can read 4 channels per uS... And then add on the kernel latency...

Good luck!

-Gordon
--
Gordons projects: https://projects.drogon.net/

Wisar
Posts: 60
Joined: Tue Sep 25, 2012 6:33 am
Location: Temple near Marlow, England
Contact: Website

Re: Fast ADC with MCP3208

Sun Jun 02, 2013 6:29 am

Gordon:

I read the above post with interest as I am playing with an ADC Pi and am wondering how many samples I can get per second. I was hoping for way more than the 4! per second that I am getting using the below sample code. Based on the above it seems like I should be able to do better but have no idea what to check/change to do so...or if that really is the case?

Thanks,
Will

Code: Select all

#!/usr/bin/env python3
# read abelectronics ADC Pi V2 board inputs with repeating reading from each channel.
# # Requries Python 2.7
# Requires SMBus 
# I2C API depends on I2C support in the kernel

# Version 1.0  - 06/02/2013
# Version History:
# 1.0 - Initial Release

#
# Usage: changechannel(address, hexvalue) to change to new channel on adc chips
# Usage: getadcreading(address, hexvalue) to return value in volts from selected channel.
#
# address = adc_address1 or adc_address2 - Hex address of I2C chips as configured by board header pins.

from smbus import SMBus
import re
import datetime as dt

adc_address1 = 0x68
adc_address2 = 0x69

# create byte array and fill with initial values to define size
adcreading = bytearray()

adcreading.append(0x00)
adcreading.append(0x00)
adcreading.append(0x00)
adcreading.append(0x00)

varDivisior = 64 # from pdf sheet on adc addresses and config
varMultiplier = (2.4705882/varDivisior)/1000

# detect i2C port number and assign to i2c_bus
for line in open('/proc/cpuinfo').readlines():
    m = re.match('(.*?)\s*:\s*(.*)', line)
    if m:
        (name, value) = (m.group(1), m.group(2))
        if name == "Revision":
            if value [-4:] in ('0002', '0003'):
                i2c_bus = 0
            else:
                i2c_bus = 1
            break
               

bus = SMBus(i2c_bus) 
 
def changechannel(address, adcConfig):
	tmp= bus.write_byte(address, adcConfig)

def getadcreading(address, adcConfig):
	adcreading = bus.read_i2c_block_data(address,adcConfig)
	h = adcreading[0]
	m = adcreading[1]
	l = adcreading[2]
	s = adcreading[3]
	# wait for new data
	while (s & 128):
		adcreading = bus.read_i2c_block_data(address,adcConfig)
		h = adcreading[0]
		m = adcreading[1]
		l = adcreading[2]
		s = adcreading[3]
	
	# shift bits to product result
	t = ((h & 0b00000001) << 16) | (m << 8) | l
	# check if positive or negative number and invert if needed
	if (h > 128):
		t = ~(0x020000 - t)
	return t * varMultiplier
	
changechannel(adc_address1, 0x9C)
i = 0
samples = 100
n1=dt.datetime.now()
while i < samples:
	r = getadcreading(adc_address1, 0x9C)
	i += 1
n2=dt.datetime.now()
et = (n2-n1).seconds
sps = samples / float(et)
print "Total elapsed time was " + str(et) + " seconds with " + str(sps) + " samples taken per second"

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Sun Jun 02, 2013 9:31 am

Wisar wrote:Gordon:

I read the above post with interest as I am playing with an ADC Pi and am wondering how many samples I can get per second. I was hoping for way more than the 4! per second that I am getting using the below sample code. Based on the above it seems like I should be able to do better but have no idea what to check/change to do so...or if that really is the case?

Thanks,
Will

Code: Select all

#!/usr/bin/env python3
# read abelectronics ADC Pi V2 board inputs with repeating reading from each channel.
# # Requries Python 2.7
# Requires SMBus 
# I2C API depends on I2C support in the kernel

# Version 1.0  - 06/02/2013
# Version History:
# 1.0 - Initial Release

#
# Usage: changechannel(address, hexvalue) to change to new channel on adc chips
# Usage: getadcreading(address, hexvalue) to return value in volts from selected channel.
#
# address = adc_address1 or adc_address2 - Hex address of I2C chips as configured by board header pins.

from smbus import SMBus
import re
import datetime as dt

adc_address1 = 0x68
adc_address2 = 0x69

# create byte array and fill with initial values to define size
adcreading = bytearray()

adcreading.append(0x00)
adcreading.append(0x00)
adcreading.append(0x00)
adcreading.append(0x00)

varDivisior = 64 # from pdf sheet on adc addresses and config
varMultiplier = (2.4705882/varDivisior)/1000

# detect i2C port number and assign to i2c_bus
for line in open('/proc/cpuinfo').readlines():
    m = re.match('(.*?)\s*:\s*(.*)', line)
    if m:
        (name, value) = (m.group(1), m.group(2))
        if name == "Revision":
            if value [-4:] in ('0002', '0003'):
                i2c_bus = 0
            else:
                i2c_bus = 1
            break
               

bus = SMBus(i2c_bus) 
 
def changechannel(address, adcConfig):
	tmp= bus.write_byte(address, adcConfig)

def getadcreading(address, adcConfig):
	adcreading = bus.read_i2c_block_data(address,adcConfig)
	h = adcreading[0]
	m = adcreading[1]
	l = adcreading[2]
	s = adcreading[3]
	# wait for new data
	while (s & 128):
		adcreading = bus.read_i2c_block_data(address,adcConfig)
		h = adcreading[0]
		m = adcreading[1]
		l = adcreading[2]
		s = adcreading[3]
	
	# shift bits to product result
	t = ((h & 0b00000001) << 16) | (m << 8) | l
	# check if positive or negative number and invert if needed
	if (h > 128):
		t = ~(0x020000 - t)
	return t * varMultiplier
	
changechannel(adc_address1, 0x9C)
i = 0
samples = 100
n1=dt.datetime.now()
while i < samples:
	r = getadcreading(adc_address1, 0x9C)
	i += 1
n2=dt.datetime.now()
et = (n2-n1).seconds
sps = samples / float(et)
print "Total elapsed time was " + str(et) + " seconds with " + str(sps) + " samples taken per second"
You're using I2C and not SPI (Which is what this thread was originally about), also I don't program in PYthon, so I've no idea if your code is correct, or not..

However... The Ablelectronics ADC board has the MCP3424 chip on it, and I've written a module for that in wiringPi, so I'm a little familiar with it... To read an 18-bit sample from a single channel is a 6-byte transaction over the I2C bus - lets err on the side of approximation and assume that's 60 bits/clocks per sample.

So at the default speed of 100KHz, you can achieve 1667 samples/sec in theory...

I don't actually have one of these chips - I did the code for someone else and haven't had any feedback, so I've no idea if my code is actually working OK, but I really would expect to see more than 4 samples/sec..

-Gordon
--
Gordons projects: https://projects.drogon.net/

Wisar
Posts: 60
Joined: Tue Sep 25, 2012 6:33 am
Location: Temple near Marlow, England
Contact: Website

Re: Fast ADC with MCP3208

Sun Jun 02, 2013 10:24 am

Gordon:

Thanks, even given the differing communication protocols I think you are confirming what I suspected...namely that something is badly wrong in dodgeville with the sample rates that I am getting! The code is straight from the examples so I am assuming it is ok. Your original thread was the closest that I could find to the topic but I will keep looking.

Thanks for the quick response,
Will

User avatar
BAStumm
Posts: 134
Joined: Fri Aug 23, 2013 3:37 pm
Location: Loon Lake, WA USA
Contact: Website

Re: Fast ADC with MCP3208

Tue Dec 10, 2013 7:55 pm

Gordon,

Where is the code for the ADC Pi board within wiringpi? By this I mean the one in this link which I believe you referred to in this post.

http://www.abelectronics.co.uk/products ... -converter

I just ordered 2 of these and it would be great if I could stick to wiringpi libs for coding.

I did look at the quick2wire product but I like how this product stacks on the pi and brings out the gpio for further stacking or connecting to. I'll be using this product to read from 6 different current transformers to control heater wattage (closed loop, zero cross fire) as well as reading an airflow sensor.

Did you ever get any feedback from users? Or is this still untested code? I'd be happy to provide feedback if I can get it working.

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Tue Dec 10, 2013 10:32 pm

BAStumm wrote:Gordon,

Where is the code for the ADC Pi board within wiringpi? By this I mean the one in this link which I believe you referred to in this post.

http://www.abelectronics.co.uk/products ... -converter

I just ordered 2 of these and it would be great if I could stick to wiringpi libs for coding.

I did look at the quick2wire product but I like how this product stacks on the pi and brings out the gpio for further stacking or connecting to. I'll be using this product to read from 6 different current transformers to control heater wattage (closed loop, zero cross fire) as well as reading an airflow sensor.

Did you ever get any feedback from users? Or is this still untested code? I'd be happy to provide feedback if I can get it working.
It's the mcp3422 code. It works with the 3224 too.

Use like:

Code: Select all

  wiringPiSetup () ;
  mcp3422Setup (400, 0x32, rate, gain) ;
  v = analogRead (400) ;
  z = analogRead (403) ;
you'll need to set the rate and gain from the data sheets to the values you want. The 400 is an arbitrary number you want the base pin of the chip to be and 0x32 is the I2C address.

-Gordon
--
Gordons projects: https://projects.drogon.net/

User avatar
BAStumm
Posts: 134
Joined: Fri Aug 23, 2013 3:37 pm
Location: Loon Lake, WA USA
Contact: Website

Re: Fast ADC with MCP3208

Tue Dec 10, 2013 11:08 pm

Thanks... I had a feeling it was the same (except i2c vs spi).

User avatar
BAStumm
Posts: 134
Joined: Fri Aug 23, 2013 3:37 pm
Location: Loon Lake, WA USA
Contact: Website

Re: Fast ADC with MCP3208

Mon Jan 20, 2014 7:10 pm

Here is what I came up with as a quick test. Not sure if this is working or not, have the adc-pi connected to the RPi but nothing connected to it just now. Question, where does the 400 value come from? Can I assume that 400 - 403 are the 4 channels for this i2c address? This board has 2 i2c's each with 4 channels.

One thing I notice, when I call ./adc-pi nothing is returned (printed) until I hit return. I have to hit return for each of the printf's in this example.

Code: Select all

#include <stdio.h>
#include <wiringPi.h>
#include <mcp3422.h>

int main (void)
{
  int val;
  printf ("ADC-Pi analogRead Program\n") ;

  wiringPiSetup() ;

  //Setup rate and gain for the ADC-Pi mcp3424
  //mcp3422Setup(pin, i2c address, rate, gain);
  // MCP3422_SR_3_75 = 18bit, MCP3422_SR_15 = 16bit, MCP_SR_60 = 14bit, MCP3422_SR_240
  mcp3422Setup(400, 0x68, MCP3422_SR_15, MCP3422_GAIN_1);

  val = analogRead (400);
  printf("val=%i\n", val);

  val = analogRead (401);
  printf("val=%i\n", val);

  return 0 ;
}
With nothing connected I get this output.

Code: Select all

[email protected]:~/wiringPi/examples$ make adc-pi
gcc -O3 -Wall -I/usr/local/include -Winline -pipe  -L/usr/local/lib  adc-pi.c  -lwiringPi -lwiringPiDev -lpthread -lm -o adc-pi
[email protected]:~/wiringPi/examples$ sudo ./adc-pi
ADC-Pi analogRead Program

val=2560

val=2694
[email protected]:~/wiringPi/examples$ 

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Mon Jan 20, 2014 7:15 pm

Check the value being returned from mcp3422Setup ().

I'm guessing that the open is failing (run as root? i2c module loaded?) and when you then call analogRead() it reads from fd=0 which is stdin and is returning garbage.

You must check the return value.

-Gordon
--
Gordons projects: https://projects.drogon.net/

User avatar
BAStumm
Posts: 134
Joined: Fri Aug 23, 2013 3:37 pm
Location: Loon Lake, WA USA
Contact: Website

Re: Fast ADC with MCP3208

Mon Jan 20, 2014 7:29 pm

i2c is setup and working. I have a mod-tc-mk2-31855 thermocouple uext olimex deal hooked and working too. It is hooked to the top of the adc-pi. What should it return? It's returning zero.

Code: Select all

[email protected]:~/wiringPi/examples$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 69 -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --             

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Mon Jan 20, 2014 7:37 pm

Actually.... It looks like you may have discovered a bug in the mcp3422Setup() code )-:

If you care to try this, then I'm sure you'll be sorted:

in wiringPi/wiringPi, edit the file mcp3422.c and go to the bottom of the file.

Look for a line:

Code: Select all

  node->data0      = sampleRate ;
above that line, add this new line:

Code: Select all

  node->fd = fd ;
and then do

Code: Select all

make ; sudo make install
-Gordon
--
Gordons projects: https://projects.drogon.net/

User avatar
BAStumm
Posts: 134
Joined: Fri Aug 23, 2013 3:37 pm
Location: Loon Lake, WA USA
Contact: Website

Re: Fast ADC with MCP3208

Mon Jan 20, 2014 7:43 pm

Now it returns immediately with values of zero.

Code: Select all

[email protected]:~/wiringPi/examples$ sudo ./adc-pi 
ADC-Pi analogRead Program
mcp3422Setup=0
val=0
val=0
[email protected]:~/wiringPi/examples$ 

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Mon Jan 20, 2014 7:46 pm

Well it's a start.

Debugging is going to be somewhat interesting though. Can you drop me an email?

-Gordon
--
Gordons projects: https://projects.drogon.net/

User avatar
BAStumm
Posts: 134
Joined: Fri Aug 23, 2013 3:37 pm
Location: Loon Lake, WA USA
Contact: Website

Re: Fast ADC with MCP3208

Mon Jan 20, 2014 8:36 pm

I believe this change fixed the bug in mcp3422/wiringPi. With nothing connected it returns 0 as value. With channel 1 shorted to 3v3 I get a value of 4095. This is using my example code above.

billw
Posts: 389
Joined: Tue Sep 18, 2012 8:23 pm

Re: Fast ADC with MCP3208

Sun Feb 23, 2014 7:47 pm

I just built a board with a MCP3422 on it and wanted to get some code going with wiringPi but ended up having to fix some things before I could get it working:
1) Not only is node->fd not set in mcp3422Setup(), but node->pinBase isn't set.
2) Values were returned from analogRead() before the conversion finished.
3) The sample rate values defined in mcp3422.h are reversed from what is needed when setting the configuration register.

Anyway, I've got some patches if anyone is interested:
mcp3422.c patch:

Code: Select all

--- mcp3422.c.old   2014-02-23 13:16:11.699073812 -0600
+++ mcp3422.c   2014-02-23 13:17:19.187463461 -0600
@@ -59,26 +59,42 @@
   {
     case MCP3422_SR_3_75:          // 18 bits
       delay (270) ;
-      read (node->fd, buffer, 4) ;
-      value = ((buffer [0] & 3) << 16) | (buffer [1] << 8) | buffer [0] ;
+      do
+          {
+          read (node->fd, buffer, 4) ;
+         }
+      while (buffer[3] & 0x80);
+      value = ((buffer [0] & 3) << 16) | (buffer [1] << 8) | buffer [2] ;
       break ;
 
     case MCP3422_SR_15:                // 16 bits
       delay ( 70) ;
-      read (node->fd, buffer, 3) ;
+      do
+          {
+          read (node->fd, buffer, 3) ;
+         }
+      while (buffer[2] & 0x80);
       value = (buffer [0] << 8) | buffer [1] ;
       break ;
 
     case MCP3422_SR_60:                // 14 bits
       delay ( 17) ;
-      read (node->fd, buffer, 3) ;
+      do
+          {
+          read (node->fd, buffer, 3) ;
+         }
+      while (buffer[2] & 0x80);
       value = ((buffer [0] & 0x3F) << 8) | buffer [1] ;
       break ;
 
     case MCP3422_SR_240:           // 12 bits
       delay (  5) ;
-      read (node->fd, buffer, 3) ;
-      value = ((buffer [0] & 0x0F) << 8) | buffer [0] ;
+      do
+          {
+          read (node->fd, buffer, 3) ;
+         }
+      while (buffer[2] & 0x80);
+      value = ((buffer [0] & 0x0F) << 8) | buffer [1] ;
       break ;
   }
 
@@ -102,6 +118,8 @@
 
   node = wiringPiNewNode (pinBase, 4) ;
 
+  node->fd         = fd;
+  node->pinBase    = pinBase;
   node->data0      = sampleRate ;
   node->data1      = gain ;
   node->analogRead = myAnalogRead ;

and a patch for mcp3422.h:

Code: Select all

--- mcp3422.h.old   2014-02-23 13:16:11.699073812 -0600
+++ mcp3422.h   2014-02-23 13:17:19.194463087 -0600
@@ -21,10 +21,10 @@
  ***********************************************************************
  */
 
-#define    MCP3422_SR_3_75 0
-#define    MCP3422_SR_15   1
-#define    MCP3422_SR_60   2
-#define    MCP3422_SR_240  3
+#define    MCP3422_SR_3_75 3
+#define    MCP3422_SR_15   2
+#define    MCP3422_SR_60   1
+#define    MCP3422_SR_240  0
 
 #define    MCP3422_GAIN_1  0
 #define    MCP3422_GAIN_2  1
And here is my test program which now seems to work fine:

Code: Select all

/* Test of a MCP3422 using wiringPi
|  compile with:
|   cc -o adc adc.c -lwiringPi
*/

#include <stdio.h>
#include <wiringPi.h>
#include <mcp3422.h>

#define ADC_I2C_ADDRESS 0x68
#define ADC_PIN_BASE    400
#define MAX_CHANNELS    2       /* 4 for a MCP3424 */
int
main(int argc, char **argv)
    {
    int     channel,
            val,
            sign,
            sample_rate,
            sign_bit_mask, max_conversion_value;
    float   scale_factor, voltage;

    if (argc > 1)
        sample_rate = atoi(argv[1]);
    if (sample_rate < 0 || sample_rate > 3)
        sample_rate = 0;

    wiringPiSetup();

    /* Set a voltage scale factor.  I have a 10K/8.2K voltage dividor
    |  feeding the ADC + input pin because my readings are 4.5 Volts
    |  max.  It can be a 10K/6.8K dividor to scale readings for 5.0 volts.
    */
    scale_factor = 2.2195;      /* 2.4706 for 10K/6.8K resitor dividor */

    switch (sample_rate)
        {
        case 3:
            sign_bit_mask = 0x20000;
            max_conversion_value = 0x1FFFF;
            sample_rate = MCP3422_SR_3_75;
            printf("MCP342x sample rate: 3.75\n");
            break;

        case 2:
            sign_bit_mask = 0x8000;
            max_conversion_value = 0x7FFF;
            sample_rate = MCP3422_SR_15;
            printf("MCP342x sample rate: 15\n");
            break;

        case 1:
            sign_bit_mask = 0x2000;
            max_conversion_value = 0x1FFF;
            sample_rate = MCP3422_SR_60;
            printf("MCP342x sample rate: 60\n");
            break;

        case 0:
            sign_bit_mask = 0x800;
            max_conversion_value = 0x7FF;
            sample_rate = MCP3422_SR_240;
            printf("MCP342x sample rate: 240\n");
            break;
        }
    mcp3422Setup(ADC_PIN_BASE, ADC_I2C_ADDRESS, sample_rate, MCP3422_GAIN_1);
    for (channel = 0; channel < MAX_CHANNELS; ++channel)
        {
        val = analogRead(ADC_PIN_BASE + channel);
        if (val & sign_bit_mask)
            val |= ~max_conversion_value;       /* Form 2's complement */
        voltage = (float) val / max_conversion_value * 2.048;
        voltage *= scale_factor;
        printf("  channel %d: %2.5f Volts\n", channel, voltage);
        }
    return 0;
    }

billw
Posts: 389
Joined: Tue Sep 18, 2012 8:23 pm

Re: Fast ADC with MCP3208

Sun Feb 23, 2014 8:28 pm

Oh, I now see node->pinBase is set elsewhere, so that's not needed in my patch to mcp3422.c.

Also, it's possible the conversions weren't completing in time because of the wrong sample rate defines in mcp3422.h, but I anyway think it's a good idea to do the check.

itsisme
Posts: 20
Joined: Sat Mar 09, 2013 2:16 am

Re: Fast ADC with MCP3208

Mon Jul 21, 2014 3:20 pm

I am strugling with the MCP3208 together with my RPi and was hoping someone could help me out.
My setup is described at:

http://pi.gids.nl:81/adc

I get readouts.

However the Python scripts and C code provide different values so I am lost.... also the voltage is a bit of using the Python code.
With the C code I guess there is an error as its way of..

Hoping for some hints in the right direction. Or if someone sees the error in the code.

Chrs..

User avatar
joan
Posts: 14006
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Fast ADC with MCP3208

Mon Jul 21, 2014 3:57 pm

Post the code you think isn't working.

itsisme
Posts: 20
Joined: Sat Mar 09, 2013 2:16 am

Re: Fast ADC with MCP3208

Mon Jul 21, 2014 5:07 pm

It is on the link as in the post..... but for the lazy ones hi....

C code

Code: Select all

/*
 * Save as spi-test.c
 * Compile with: gcc -o spi-test spi-test.c -lwiringPi
 *
 * If no value: 
 * rmmod spi_bcm2708
 * modprobe spi_bcm2708
 * 
 * http://www.icbanq.com/pbloger/board_View.aspx?number=269
 * http://www.raspberrypi.org/forums/viewtopic.php?f=93&t=78551 (baart)
 * http://www.mikroe.com/add-on-boards/measurement/adc-proto/ (boards)
 * 
 */ 

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#include <wiringPi.h>
#include <wiringPiSPI.h>

#define CS_MCP3208  6        // BCM_GPIO 25

#define SPI_CHANNEL 0
#define SPI_SPEED   1000000  // 1MHz


int read_mcp3208_adc(unsigned char adcChannel)
{
  unsigned char buff[3];
  int adcValue = 0;

  buff[0] = 0x06 | ((adcChannel & 0x07) >> 7);
  buff[1] = ((adcChannel & 0x07) << 6);
  buff[2] = 0x00;

  digitalWrite(CS_MCP3208, 0);  // Low : CS Active

  wiringPiSPIDataRW(SPI_CHANNEL, buff, 3);

  buff[1] = 0x0F & buff[1];
  adcValue = ( buff[1] << 8) | buff[2];

  digitalWrite(CS_MCP3208, 1);  // High : CS Inactive

  return adcValue;
}


int main (void)
{
  int adc1Channel = 0;
  int adc1Value   = 0;
  int adc2Channel = 1;
  int adc2Value   = 0;
  int adc3Channel = 2;
  int adc3Value   = 0;
  int adc4Channel = 3;
  int adc4Value   = 0;
  int adc5Channel = 4;
  int adc5Value   = 0;
  int adc6Channel = 5;
  int adc6Value   = 0;
  int adc7Channel = 6;
  int adc7Value   = 0;
  int adc8Channel = 7;
  int adc8Value   = 0;
   
  if(wiringPiSetup() == -1)
  {
    fprintf (stdout, "Unable to start wiringPi: %s\n", strerror(errno));
    return 1 ;
  }

  if(wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1)
  {
    fprintf (stdout, "wiringPiSPISetup Failed: %s\n", strerror(errno));
    return 1 ;
  }

  pinMode(CS_MCP3208, OUTPUT);

  while(1)
  {
    system("clear");
    printf("\n\nMCP3208 channel output.\n\n");
    adc1Value = read_mcp3208_adc(adc1Channel);
    printf("adc0 Value = %04u", adc1Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc1Value));
    adc2Value = read_mcp3208_adc(adc2Channel);
    printf("adc1 Value = %04u", adc2Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc2Value));
    adc3Value = read_mcp3208_adc(adc3Channel);
    printf("adc2 Value = %04u", adc3Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc3Value));
    adc4Value = read_mcp3208_adc(adc4Channel);
    printf("adc3 Value = %04u", adc4Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc4Value));
    adc5Value = read_mcp3208_adc(adc5Channel);
    printf("adc4 Value = %04u", adc5Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc5Value));
    adc6Value = read_mcp3208_adc(adc6Channel);
    printf("adc5 Value = %04u", adc6Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc6Value));
    adc7Value = read_mcp3208_adc(adc7Channel);
    printf("adc6 Value = %04u", adc7Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc7Value));
    adc8Value = read_mcp3208_adc(adc8Channel);
    printf("adc7 Value = %04u", adc8Value);
    printf("\tVoltage = %.3f\n", ((3.23/4096) * adc8Value));
    usleep(1000000);
  }
  return 0;
}
Python code

Code: Select all

#!/usr/bin/env python
#
#
import time
import os
import sys
import RPi.GPIO as GPIO
from decimal import *
import atexit
import signal

CONTROL_C = False

def program_exit():
    # You may do some clean-up here, but you don't have to.
    print "Exiting application... Thnxs                                             "
    os.system('setterm -cursor on')
    print " "
       
def ctrlCHandler(*whatever):
    # Just sets the value of CONTROL_C
    global CONTROL_C
    CONTROL_C = True
                 
THREEPLACES = Decimal(10) ** -3

GPIO.setwarnings(False)

GPIO.setmode(GPIO.BCM)

# read SPI data from MCP3208 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
        if ((adcnum > 7) or (adcnum < 0)):
                return -1
        GPIO.output(cspin   , True)

        GPIO.output(clockpin, False)  # start clock low
        GPIO.output(cspin   , False)  # bring CS low

        commandout = adcnum
        commandout |= 0x18            # start bit + single-ended bit
        commandout <<= 3              # we only need to send 5 bits here
        for i in range(5):
                if (commandout & 0x80):
                        GPIO.output(mosipin, True )
                else:
                        GPIO.output(mosipin, False)
                commandout <<= 1
                GPIO.output(clockpin, True )
                GPIO.output(clockpin, False)

        adcout = 0

        # read in one empty bit, one null bit and 12 ADC bits
        # pro desetibitovy prevodnik tu bylo puvodne cislo 12
        for i in range(14):
                GPIO.output(clockpin, True )
                GPIO.output(clockpin, False)
                adcout <<= 1
                if (GPIO.input(misopin)):
                        adcout |= 0x1

        GPIO.output(cspin, True)
        
        adcout >>= 1 # first bit is 'null' so drop it
        return adcout

# change these as desired - they're the pins connected from the SPI port on the ADC to the RPi
SPICLK  = 11
SPIMISO = 9
SPIMOSI = 10
SPICS   = 8

# set up the SPI interface pins

GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN )
GPIO.setup(SPICLK,  GPIO.OUT)
GPIO.setup(SPICS,   GPIO.OUT)

# 10k trim pot connected to adc #0
potentiometer_adc = 0;

os.system('clear')
os.system('setterm -cursor off')

# You must check CONTROL_C in your program

# call this procedure, if control-c is pressed.
signal.signal(signal.SIGINT, ctrlCHandler)

# program_exit is called, when sys.exit is executed.
atexit.register(program_exit)

print " "
print "CTRL-C to exit"
print " "

while True:
        trim_pot = readadc(potentiometer_adc, SPICLK, SPIMOSI, SPIMISO, SPICS)
        voltage = 3.3 * trim_pot / 4096 
        print "Digital:", trim_pot , "\t Voltage:", Decimal(voltage).quantize(THREEPLACES), "V                 ",
        time.sleep(0.001)
        print "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r",
        if CONTROL_C: sys.exit(0)
exit (0)
Now I have two issues:

1:
Using the C++ code I get a lower value than using the Python code.
Why ?

2:
Using the C++ code I often need to remove the spi_bcm2708 driver and reload it again oterwise values will stay 0:
* rmmod spi_bcm2708
* modprobe spi_bcm2708
Why or what can I do to have it working without the reload ?

Python result:
Digital: 1464 Voltage: 1.154 V

C code:
adc0 Value = 1238 Voltage = 0.976

Real voltage (multimeter test) 1.17v

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Mon Jul 21, 2014 5:14 pm

You don't need to change pin modes or explicitly drive the CS line when using the SPI interface. Setting the CS line explicitly to output maybe the reason you continually need to re-load the driver. Remove all references to it - the kernel driver should drive the CS line at the right times.

-Gordon
--
Gordons projects: https://projects.drogon.net/

itsisme
Posts: 20
Joined: Sat Mar 09, 2013 2:16 am

Re: Fast ADC with MCP3208

Mon Jul 21, 2014 5:28 pm

Hai Gordon,

So you mean rem out :
// pinMode(CS_MCP3208, OUTPUT);

Tried it but after running the Python code it reads 0 again (reload spi_bcm2708) and it worked again (However also value to low compared to the Python output)

Chrs

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Fast ADC with MCP3208

Mon Jul 21, 2014 5:29 pm

OK. Just checked the data sheets and your code:

Code: Select all

  wiringPiSPIDataRW(SPI_CHANNEL, buff, 3);

  buff[1] = 0x0F & buff[1];
  adcValue = ( buff[1] << 8) | buff[2];
The device needs 18 bits of data, so

first 6 bits: start bit (1), SGL/DIFF (1), chan (3 bits), fill (0)

then the 12-bit result is in all 3 bytes - bottom 2 bits of buff [0], 8 bits of buff [1] and top 2 bits of buff [2]

So (I think) then result is:

Code: Select all

 adcValue = (buf [0] & 0x03) << 10 | buf [1] << 2 | (buf [2] >> 6) & 0x03 ;
I think you're losing precision in the bottom 2 bits.

Also I think you're setting the channel incorrectly too - all the channel, etc. bits go into buf [0]

-Gordon
--
Gordons projects: https://projects.drogon.net/

Return to “Automation, sensing and robotics”