CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

MCP3208 driver vs SPI Library functions
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Charlie U



Joined: 09 Sep 2003
Posts: 183
Location: Somewhere under water in the Great Lakes

View user's profile Send private message

PostPosted: Tue Apr 11, 2006 4:40 pm     Reply with quote

Well, time flies when you are having fun????

I got caught up in the real world of production problems.

Here we go:

spi definitions header file:
Code:
// Defines for new spi mode parameters
//

#define SPI_MODE_0_0 0x4000
#define SPI_MODE_0_1 0x0000
#define SPI_MODE_1_0 0x0010
#define SPI_MODE_1_1 0x4010


Version 3 of the compiler sets up the spi in a non-standard mode with the basic parameters. Insert the following line in you initialization section:

Code:
setup_spi(SPI_MASTER|SPI_MODE_0_0|SPI_CLK_DIV_16);

You can change the clock part for your system clock, etc.

Here's my adc_io.h file:
Code:
// Header file for adc_io.c file
// Contains function prototypes for adc_io.c

int adc_address_temp, adc_cs_temp;

long read_ext_adc (int ext_adc_channel);

long const start_ext_adc [8] =
{
   0x0600, 0x0640, 0x0680, 0x06C0,
   0x0700, 0x0740, 0x0780, 0x07C0
};

struct s_long
{
   int l;
   int h;
};

union u_long
{
   long int l_temp;
   struct s_long s_temp;
} long_temp0, long_temp1, long_temp2;


And here's my acd_io.c file:
Code:
// Code file for ADC access functions
// Also add adc_io.h file
/*
   ADC I/O functions:

   read_ext_adc(int ext_adc_channel)
*/

long read_ext_adc (int ext_adc_channel)
{
   adc_address_temp = ext_adc_channel & 0x07;

   // select the proper adc start sequence for our channel
   long_temp0.l_temp = start_ext_adc[adc_address_temp];

   adc1_cs = 0;

   // then start the adc reading by sending the start sequence
   //  and reading the data
   spi_write (long_temp0.s_temp.h);
   long_temp1.s_temp.h = spi_read();
   spi_write (long_temp0.s_temp.l);
   long_temp1.s_temp.h = spi_read();
   spi_write (long_temp0.s_temp.l);
   long_temp1.s_temp.l = spi_read();

   adc1_cs = 1;

   long_temp1.l_temp = 0x0FFF & long_temp1.l_temp;
   return (long_temp1.l_temp);

}


You will need to sort out your chip select and define a pin for it. Just use the read_ext_adc(channel); as you would any other function.

Code:
MyLongInt = read_ext_adc(channel);  // channel is 0-7


Try this out and let us know if it works for you. I had to edit the functions a bit because my system has 2 of these and the chip selects are controlled by external latches.

edited to correct the set up parameters. Thanks PCM!
Barney



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

Gift Horse
PostPosted: Tue Apr 11, 2006 8:50 pm     Reply with quote

DR,

Nope, I got much more than I asked for. Muchos.

"You are looking a gift horse in the mouth when you receive a gift and then you question the value of that gift. Like a person who has been given a horse as a gift (a gift-horse) and you are looking into the horse's mouth to see if it is in good health."
magnoedu



Joined: 29 May 2009
Posts: 11

View user's profile Send private message MSN Messenger

y need to driver for the multiples mcp3208 can you help-me?
PostPosted: Sat Sep 25, 2010 4:55 am     Reply with quote

I need an driver for ccs to work with multiples mcp3208, I search by the all net but not find, I read one post you use multiples mcp3208, I trying to make changes to ex_ad12 by the ccs but not have success.
please help-me
sorry my bad english I live in Brazil
krugger



Joined: 12 Jul 2011
Posts: 18
Location: El Salvador

View user's profile Send private message

Suggested Changes
PostPosted: Tue Jul 19, 2011 10:19 am     Reply with quote

Hello Charlie U,

I've been using your functions and they've been very helpfull to me. However I think I've found a bug in your code. My english is not very good but I'll try to make myself understood.

I was using your library to sequentially measure 6 consecutive ADC channels with MCP3208 Proteus Model. I realized that I obtained correct measures only in some channles, having wrong measures in some others.

Looking at the chronogram in the datasheet we observe that there is a bit that is slightly wider than the others in the address word (as they use 7 leading zeroes before the strat bit the wider bit is the SGL/DIFF bit in this example).

http://imageshack.us/photo/my-images/855/mcp3008driverbug.jpg/

Meanwhile, as you're using 5 leading zeroes in your code, the wider bit is exactly the most significant bit of the Channel selection field (D2). I've found that because of this bit D1 is ¿overlapped? by D2 and thus it always take the same value than D2, making it impossible to select channels 2,3,4 and 5 properly, as they have D2 != D1.

So, my suggestion (it has worked for my application) is to change the contents of start_ext_adc array, i.e.:

Code:
long const start_ext_adc [8] =
{
   0x0600, 0x0640, 0x0680, 0x06C0,
   0x0700, 0x0740, 0x0780, 0x07C0
};


changed as:

Code:
long const start_ext_adc [8] =
{
   0x0180, 0x0190, 0x01A0, 0x01B0,
   0x01C0, 0x01D0, 0x01E0, 0x01F0
};




Maybe I'm wrong but it has worked for me. I'll be glad of hearing comments or corrections if what I've said is wrong.

Thank you again for your work Charlie.

Regards,

Pablo
Charlie U



Joined: 09 Sep 2003
Posts: 183
Location: Somewhere under water in the Great Lakes

View user's profile Send private message

PostPosted: Tue Jul 19, 2011 5:28 pm     Reply with quote

Hello Pablo,

Thanks for the suggestion, but you are using a different ADC chip. Yours is the MCP3004/8 which is a 10 bit part. Your start array will work correctly for it. However, my driver is for an MCP3208 which is a 12 bit part. My original start array is correct for this part. It has been used in many production test systems for many years quite successfully.

Charlie
krugger



Joined: 12 Jul 2011
Posts: 18
Location: El Salvador

View user's profile Send private message

PostPosted: Tue Jul 19, 2011 8:53 pm     Reply with quote

Embarassed

Thank you again!
nuclear__



Joined: 24 Jan 2015
Posts: 63

View user's profile Send private message

3V7 operation
PostPosted: Sun Mar 28, 2021 12:45 pm     Reply with quote

Hi. It's being too long since this thread was created.
I use mcp3208 with 3.7v supply (reference too).

I have an external power supply ( common ground with pic etc) and i test the digital output by adding 0.1v each time.
It looks working up to 1.0V . Then when i go to 1.1V my digital reading goes from around 1000(for 1Volt) to 1600(for 1.1Volt) then it keeps growing but not linear. It stops growing at 2 Volts.

I tried that on more than one channel and i get the same result.

I guess i get corrupted reading . Any idea how to check it more?

Here are variable readings after each command, as shown in file adc_io.c


Code:
spi_write (long_temp0.s_temp.h);
long_temp1.s_temp.h = spi_read();
printf(usb_cdc_putc,"\r\ntemp.h:%u ",long_temp1.s_temp.h);
spi_write (long_temp0.s_temp.l);
long_temp1.s_temp.h = spi_read();
printf(usb_cdc_putc," temp.h:%u ",long_temp1.s_temp.h);
spi_write (long_temp0.s_temp.l);
long_temp1.s_temp.l = spi_read();
printf(usb_cdc_putc," temp.l:%u ",long_temp1.s_temp.l);


Voltage 0.9Volt :
temp.h:0 temp.h:3 temp.l:219 adc1:976

Voltage 1.0Volt :
temp.h:0 temp.h:3 temp.l:251 adc1:1008

Voltage 1.1Volt :
temp.h:0 temp.h:6 temp.l:111 adc1:1648

Voltage 1.2Volt :
temp.h:0 temp.h:6 temp.l:247 adc1:1776
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Mar 28, 2021 3:05 pm     Reply with quote

To display long ints with printf in CCS, you need to use "%lu".
nuclear__



Joined: 24 Jan 2015
Posts: 63

View user's profile Send private message

PostPosted: Sun Mar 28, 2021 3:31 pm     Reply with quote

h and l are int.
It doesn't compile with lu .

actualy you can see adc1 variable which is shown with %lu and is over 255. This is the output .
I just added the rest variables in a destructive way to help me find out the problem . But i cant
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Mar 28, 2021 3:40 pm     Reply with quote

OK, well what happens if you use the CCS driver, mcp3208.c ?
temtronic



Joined: 01 Jul 2010
Posts: 9225
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sun Mar 28, 2021 6:59 pm     Reply with quote

You don't post HOW you convert the 2 ADC bytes into the 'voltage' ! That is important for us to see.
Also, you say that Vref is 3.7 volts, same as VDD of the PIC. Is it the SAME power supply ? If so Vref will vary depending on what the PIC is doing !
The ADc Vref needs to be from a 'voltage reference' chip, well filtered Vin, bypassed, and laidout according to mfr's specs !
As a 10 bit ADC, you need a LOT of GREAt PCB design/layout/ components before you can get ANY reliable, accurate readings
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Mon Mar 29, 2021 3:11 am     Reply with quote

Nuclear__. I'm guessing that perhaps you are on a PIC24/30/33?. Only
thing that would explain your being able to print values above 255, without
using the l format.
If so, this could be the issue, since on these chips the default 'int' is signed
and 16bit. This could very easily be giving issues with how the code
places bits and combines them....
Now as an 'exercise' this morning, I decided to generate a driver that should
be compatible with all the PIC families, using #use spi, and supporting
the MCP 3204 & 8, both in single ended and differential mode.
Now, 'no guarantees', I've just written it from the data sheet.

main3208.c showing it setup, loaded and used
Code:

//Basic setup. Change to suit your PIC
#include <18F4520.h>
#device ADC=10

#FUSES NOWDT                    //No Watch Dog Timer

#use delay(crystal=20000000)

#use rs232(UART1, baud=9600,parity=N,stream=SERIAL, ERRORS)
//Simple UART for demo code

#use SPI(SPI1, baud=1000000, mode=0, BITS=8, stream=MCP32xx)
//setup the SPI. Can be a software or the hardware port as shown
//can go up to 2MHz at 5v, but 1MHz max for whole supply range
//can be mode0, or 3. Stream name must match for driver.

#define SINGLE_ENDED 1
#define DIFFERENTIAL 0
#define CH1NEG
//If in differential mode this will make CH1 the -ve input. REM out this
//definition to make CH1 the +ve input
#define MCP3208 //change to 3204 if this is the chip involved
#define MCP32xx_CS PIN_C0 //define the pin to use for CS - change for your hardware
#define MCP_IP SINGLE_ENDED //run inputs in single ended mode

#include "mcp32xx.h" //load the driver

void main()
{
   signed int16 data[CHANS]; //set up an array for the supported number of channels
   signed int16 single; //and a single value
   int counter;
   
   setup_adc_ports(NO_ANALOGS, VSS_VDD);
   MCP32xx_init(); //INIT the SPI

   while(TRUE)
   {
      //First demonstrate reading a single channel
      single=MCPread_chan(0); //returns chan 0
     
      fprintf(SERIAL,"Chan 0 is %ld\n", single);
     
      //now read all the channels
      MCPread_all(data);
     
      for (counter=0;counter<CHANS;counter++)
         fprintf(SERIAL,"Chan %d is %ld\n", counter, data[counter]);
      delay_ms(1000); //just delay to slow things down
   }
}


Then the mcp32xx.h driver code
Code:

//Driver for MCP3204/3208 using standard SPI functions

//work out how many channels are supported
#ifdef MCP3204
   #if MCP_IP==SINGLE_ENDED
     #define CHANS 4
   #else
     #define CHANS 2
   #endif
#else
   #if MCP_IP==SINGLE_ENDED
      #define CHANS 8
   #else
      #define CHANS 4
   #endif
#endif

void mcp32xx_init(void)
{
   //just ensure CS line is high
   output_high(MCP32XX_CS);
}

signed int16 MCPread_chan(int16 chan)
{
   //routine to read a single channel. Now on this chip, to give the correct
   //12 bit alignment, we have to send the top bit of the channel number
   //together with the SE/DIFF selection and a start as the bottom 3 bits of
   //the
   //first byte sent, then clock out the remaining two bits of the channel
   //two 'dummy' bits, then clock back the 12bit value (phew)...
   struct {
      unsigned int8 bytes[2];
      signed int16 word;
   } combiner, reply;
   if (MCP_IP==SINGLE_ENDED)
      combiner.word=chan*64; //Move the channel to the right location
      //compiler will optimise this to a rotation
   else
   {
      //Now if we are in differential mode, the channel number needs
      //to be doubled and combined with the bit for direction CH1NEG
      combiner.word=chan*128;
      #ifndef CH1NEG
        //if channel one is not negative need to set D0
        bit_set(combiner.byte[0], 6); //D0 bit set
      #endif
   }   
   if (MCP_IP==SINGLE_ENDED)
      bit_set(combiner.bytes[1],1); //set the SGL/DIFF bit
   bit_set(combiner.bytes[1],2); //set the start bit
   output_low(MCP32xx_CS); //start transaction
   //now need to clock the bytes out to the SPI port and read the reply
   //total of three bytes to send, and two reply bytes
   spi_xfer(MCP32xx, combiner.bytes[1], 8);
   reply.bytes[1]=spi_xfer(MCP32xx, combiner.bytes[0], 8);
   reply.bytes[0]=spi_xfer(MCP32xx, 0, 8); //dummy output byte for the reply
   output_high(MCP32xx_CS); //end transaction   
   reply.word &= 0xFFF; //mask just 12 bits
   #if MCP_IP==SINGLE_ENDED
     return reply.word;
   #else 
     //in differential mode, we need to sign extend if the reply is -ve
     if (bit_test(reply.bytes[1],3)
        reply.byte[1] |= 0xF0; //set top 4 bits if so
     return reply.word;
   #endif
}

void MCPread_all(signed int16 * chan_data)
{
   //read all the channels into chan_data array
   int ctr;
   for (ctr=0;ctr<CHANS;ctr++)
      chan_data[ctr]=MCPread_chan(ctr);
}
nuclear__



Joined: 24 Jan 2015
Posts: 63

View user's profile Send private message

PostPosted: Mon Mar 29, 2021 6:12 am     Reply with quote

wow ! nice morning task.
Before i check it i should mention that

I was misunderstood. I use pic18f47j53. I do use lu to print int16 . I mention that the first 3 variables that i printed, was not int16 but only the last one (for which i used lu).

Secondly mcp3208.c didn't work at all. I get 0 on all channel.

About vreference and circuit staff can't be the problem. My voltages are very solid, checked on oscilloscope. I have no load variation on 3.7 V line and the difference i see in adc reading can't be excused in any way unless my Vdd would drop from 3.7V to 2.5V . The pcb for the mcp3208 is really awful, made on pre-holed board, but again it can't be noise problem since i get the very same reading in each adc function calling. This pcb is separate from the main pic pcb which is printed and well designed.

I will come back with testing results!
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Mon Mar 29, 2021 6:44 am     Reply with quote

Are you using the hardware SPI pins?. B4,B5 & C7.
Seriously, there will be ripple on the supply. Not enough to explain this
problem, but when you think a 12bit ADC measures down to 0.9mV, you
will get some error from this. You need to switch the scope to AC
coupling, and set it's range up to perhaps 5mV/div, to actually see the
sort of noise that will affect the signal....

Separate PCB's. You have remembered to connect the grounds together.

If you look, you never actually post any code showing %l being used.
nuclear__



Joined: 24 Jan 2015
Posts: 63

View user's profile Send private message

PostPosted: Mon Mar 29, 2021 7:55 am     Reply with quote

Yes i use hardware spi
Code:
#use SPI (MASTER, DO = PIN_c7, DI = PIN_b5, CLK = PIN_B4, BITS = 8, Mode = 0)


Here is my v supply on main board and then on mcp board. They are ground coupled.

The same waveform i get when i use a loop that i get readings from spi (mcp) all the time.





For 9mv readings is not good but i actually need less than 8 bit resolution, so i will divide my reading with 16.

I will firstly try your code and if i get the same results i will go into pcb correction.

temtronic, the actual conversion is shown on the return value of file adc_io.c shown in Charlie U's post above. I hope i won't need an external reference, if so i will use a zener diode after the rest fail.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group