|
|
View previous topic :: View next topic |
Author |
Message |
Charlie U
Joined: 09 Sep 2003 Posts: 183 Location: Somewhere under water in the Great Lakes
|
|
Posted: Tue Apr 11, 2006 4:40 pm |
|
|
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
|
Gift Horse |
Posted: Tue Apr 11, 2006 8:50 pm |
|
|
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
|
y need to driver for the multiples mcp3208 can you help-me? |
Posted: Sat Sep 25, 2010 4:55 am |
|
|
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
|
Suggested Changes |
Posted: Tue Jul 19, 2011 10:19 am |
|
|
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
|
|
Posted: Tue Jul 19, 2011 5:28 pm |
|
|
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
|
|
Posted: Tue Jul 19, 2011 8:53 pm |
|
|
Thank you again! |
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
3V7 operation |
Posted: Sun Mar 28, 2021 12:45 pm |
|
|
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
|
|
Posted: Sun Mar 28, 2021 3:05 pm |
|
|
To display long ints with printf in CCS, you need to use "%lu". |
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
|
Posted: Sun Mar 28, 2021 3:31 pm |
|
|
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
|
|
Posted: Sun Mar 28, 2021 3:40 pm |
|
|
OK, well what happens if you use the CCS driver, mcp3208.c ? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Sun Mar 28, 2021 6:59 pm |
|
|
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
|
|
Posted: Mon Mar 29, 2021 3:11 am |
|
|
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
|
|
Posted: Mon Mar 29, 2021 6:12 am |
|
|
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
|
|
Posted: Mon Mar 29, 2021 6:44 am |
|
|
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
|
|
Posted: Mon Mar 29, 2021 7:55 am |
|
|
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. |
|
|
|
|
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
|