Trouble with ADC MCP3204 - Page 1
Forum section: electronics / projects

author: wigman27
author_rank: Regular Contributor
timestamp: June 20, 2014, 09:48:13 am
content: Hi All,I have made up my dummy load as perthisthread and the attached schematic and seems to work quite well! It of course has its newbie errors on pin outs and footprints but out of all of the components I have only stuffed up 2!Everything is working to plan except the ADC! I am using a Microchip MCP3204 to sense 4 voltage and convert to digital through an SPI interface. I am using the circuit from an Arduino Leonardo to drive it. I have written the code myself which works perfectly on a separate chip I have on the breadboard driven by the genuine Leonardo.This is the code im usingCode:byte adcRegister = 0x08; // Sets defult ADC Address register B00001000, This is a default address setting, the fourth LSB sets the ADC to single ended mode, the third LSB is for D2 address bit, for this ADC its a "Don't Care" bit, the second LSB is the D1 Address bit and the LSB id the D0 address bit. The default address sets to single ended channel 0.float readAdc(int channel) {byte registerMask = 0x0F; // b00001111 Isolates the four LSB.byte adcPrimaryByteMask = 0x0F; // b00001111 isolates the 4 LSB for the value returned.byte adcConfig = adcRegister & registerMask; // ensures the adc register is limited to the mask and assembles the configuration byte to send to ADC.switch (channel) {case 0:adcConfig = adcConfig; // Leaves the configuration byte as originally set. This will set the address to ADC 0.break;case 1:adcConfig |= 0x01; // adds a 1 to the configuration byte to set it to B00001001. This will set the address to ADC 1.break;case 2:adcConfig |= (1 << 1); // adds a 10 to the configuration byte to set it to B00001010. This will set the address to ADC 2.break;case 3:adcConfig |= 0x03; // adds a 11 to the configuration byte to set it to B00001011. This will set the address to ADC 3.break;}noInterrupts(); // disable interupts to prepare to send address data to the ADC.digitalWrite(adcChipSelectPin,LOW); // take the Chip Select pin low to select the ADC.SPI.transfer(adcConfig); // send in the Configuration address byte to the ADC.byte adcPrimaryByte = SPI.transfer(0x00); // read the primary byte, also sending 0 as this doesn't matter.byte adcSecondaryByte = SPI.transfer(0x00); // read the secondary byte, also sending 0 as this doesn't matter.digitalWrite(adcChipSelectPin,HIGH); // take the Chip Select pin high to de-select the ADC.interrupts(); // Enable interupts.adcPrimaryByte &= adcPrimaryByteMask; // Limits the value of the primary byte to the 4 LSB:int digitalValue = (adcPrimaryByte << 8) | adcSecondaryByte; // Shifts the 4 LSB of the primary byte to become the 4 MSB of the 12 bit digital value, this is then ORed to the secondary byte value that holds the 8 LSB of the digital value.float value = (float(digitalValue) * 3.3) / 4096.00; // The digital value is converted to an analogue voltage using a VREF of 2.048V.return digitalValue; // Returns the value from the function}The one I have in my dummy load just returns B11000000 00000000 every time and it has me absolutely beat. I have checked the pinouts and connections 100 times back to the processor. The processor must be working as I am programming it through ICSP and the DAC MCP4922 is working perfectly that shares the same MOSI and CLK pins. The only thing the ADC has that the DAC doesn't share is its CS pin and the MISO pin. Again, I have checked them 500 times.. The only thing left is the chip itself but I know its rare..Any Ideas?

author: PA0PBZ
author_rank: Super Contributor
timestamp: June 20, 2014, 10:52:19 am
content: You did not connect the digital ground of the MCP3204?

author: wigman27
author_rank: Regular Contributor
timestamp: June 20, 2014, 10:57:35 am
content: Sorry Mate, That is an old schematic I have accidentally uploaded.I have connected the digital ground to the common ground plane, I remember leaving that while I investigated splitting the grounds and it was decided to keep them all common for one of my first designs and tackle the split grounds on another one.All other connections are as per the schematic.

author: dannyf
author_rank: Super Contributor
timestamp: June 20, 2014, 11:19:19 am
content: I would read the datasheet and compare it to your transmission code. The datasheet describes a transmission scheme that's quite different from what the code implies.

author: wigman27
author_rank: Regular Contributor
timestamp: June 20, 2014, 11:28:46 am
content: Quote from: dannyf on June 20, 2014, 11:19:19 amCan you please elaborate a little more?

author: wigman27
author_rank: Regular Contributor
timestamp: June 20, 2014, 11:39:40 am
content: Also, the code works really well with the same chip on the breadboard. So I will be interested to see what I have stuffed up.

author: dannyf
author_rank: Super Contributor
timestamp: June 20, 2014, 11:52:24 am
content: What does the datasheet say about the transmission?

author: kizzap
author_rank: Unknown
timestamp: June 20, 2014, 11:53:09 am
content: if the code worked on the breadboard, then it is a layout issue.Can you post the layout?

author: wigman27
author_rank: Regular Contributor
timestamp: June 20, 2014, 12:02:02 pm
content: Quote from: dannyf on June 20, 2014, 11:52:24 amI'm sorry Danny, I am just not seeing it. It maybe because I have read it trying to find the answer so many times that I'm missing it.Quote from: kizzap on June 20, 2014, 11:53:09 amCertainly :-)

author: wigman27
author_rank: Regular Contributor
timestamp: June 23, 2014, 08:58:21 am
content: Hi all,I would really appreciate some further help if anyone has any ideas. I would be interested to see what you have seen in the datasheet the I have missed dannyf, is it to do with the SPI mode?ThanksLee

author: dannyf
author_rank: Super Contributor
timestamp: June 23, 2014, 11:25:06 pm
content: The issue and the solution were provided to you earlier:QuoteYou just need to follow through. I am happy to walk you through to a solution but you have to your part too.

author: moffy
author_rank: Super Contributor
timestamp: June 23, 2014, 11:52:28 pm
content: From the datasheet: "The first clock received with CS low and DIN high will constitute a start bit". But you are only sending 0's therefore no start bit.

author: dannyf
author_rank: Super Contributor
timestamp: June 24, 2014, 12:06:44 am
content: QuoteSpot on!That's the preferred solution, particularly if you want to utilize existing (hardware or software) 8-bit spi transmission routines.Otherwise, there is another solution too.

author: wigman27
author_rank: Regular Contributor
timestamp: June 24, 2014, 12:37:35 am
content: Quote from: moffy on June 23, 2014, 11:52:28 pmOh of course don't I feel like an idiot! I'm sorry! Could I send that initial 1 in the start bit? For example B1001000? Would I sent just a 1 then immediately send the address byte?Also I think I'm doing this wrongQuoteSo I need a one clock pulse between the address byte and the first reading byte. Is that correct?If so, what is the best was to delay one single clock pulse?Thank you very much!!Maybe the noise in the breadboard caused an "fake" 1 on the primary address byte!

author: wigman27
author_rank: Regular Contributor
timestamp: June 24, 2014, 10:31:44 pm
content: Hi all,Someone has just pointed out to me that I have been doing a very bad job at reading the datasheet!I'm going to re write the code and start again! I'm really really sorry I have waisted everyone's time with this one.Thanks again for pointing me in the right direction!Lee

author: nctnico
author_rank: Super Contributor
timestamp: June 24, 2014, 10:40:06 pm
content: You are misreading it. In the idle situation CS must be high so make sure CS is high when the chip is not in use. If you want to read an ADC value you need to make CS low and send an 18 bit word to the chip(*). The last 13 bits you read back are the status bit and a result. Once the transaction is done CS must be made high again. If you have a scope make sure the signals look like the timing diagram in the datasheet.(*) The first bit of the 18 bits send to the MXP3204 must be 1. For some odd reason Microchip calls this a start bitIn synchronous transmission with an enable (or framing) signal like SPI a start bit has very little use. The synchronisation is already there.

author: dannyf
author_rank: Super Contributor
timestamp: June 24, 2014, 11:03:39 pm
content: QuoteThat is unfortunately true.To write for any device, you have to read the datasheet extensively to understand how it works.In this case, the chart to look for is Figure 5-1. The transmission essentially has 5 config bits (Start bit, SE/Diff bit, D2, D1, D0), then a sample bit, and a null bit. After that, you have your 12 data bits. For a total of 5 + 2 + 12 = 19 bits.So one way to code it is to bit bang out those 19 bits. This is the easiest, and at the same time, least efficient one.A better approach will rely on the sentence that moffy pointed out earlier. In that approach, you send 3 * 8 = 24 bits, except that the first 5 bits (on MOSI/DIN) is zero. So the chip actually sees 19 pulses - that's the value of that start bit -> it is only odd for a newbie programmer that Microchip implemented that start bit.This approach allows you to use hardware/software spi which typically transmits in packs of 8 bits.Either way, it requires you to read the datasheet with great intensity.

author: wigman27
author_rank: Regular Contributor
timestamp: June 27, 2014, 11:39:44 am
content: Its amazing what happens when you read the datasheet!This is the final code I came up with and guess what.. IT WORKS PERFECTLY!!Code:byte adcPrimaryRegister = 0x06; // Sets default Primary ADC Address register B00000110, This is a default address setting, the third LSB is set to one to start the ADC, the second LSB is to set the ADC to single ended mode, the LSB is for D2 address bit, for this ADC its a "Don't Care" bit.byte adcPrimaryRegisterMask = 0x07; // b00000111 Isolates the three LSB.byte adcPrimaryByteMask = 0x0F; // b00001111 isolates the 4 LSB for the value returned.float readAdc(int channel) {byte adcPrimaryConfig = adcPrimaryRegister & adcPrimaryRegisterMask; // ensures the adc register is limited to the mask and assembles the configuration byte to send to ADC.byte adcSecondaryConfig = channel << 6;noInterrupts(); // disable interupts to prepare to send address data to the ADC.digitalWrite(adcChipSelectPin,LOW); // take the Chip Select pin low to select the ADC.SPI.transfer(adcPrimaryConfig); // send in the primary configuration address byte to the ADC.byte adcPrimaryByte = SPI.transfer(adcSecondaryConfig); // read the primary byte, also sending in the secondary address byte.byte adcSecondaryByte = SPI.transfer(0x00); // read the secondary byte, also sending 0 as this doesn't matter.digitalWrite(adcChipSelectPin,HIGH); // take the Chip Select pin high to de-select the ADC.interrupts(); // Enable interupts.adcPrimaryByte &= adcPrimaryByteMask; // Limits the value of the primary byte to the 4 LSB:int digitalValue = (adcPrimaryByte << 8) | adcSecondaryByte; // Shifts the 4 LSB of the primary byte to become the 4 MSB of the 12 bit digital value, this is then ORed to the secondary byte value that holds the 8 LSB of the digital value.float value = (float(digitalValue) * 2.048) / 4096.000; // The digital value is converted to an analogue voltage using a VREF of 2.048V.return value; // Returns the value from the function}Once again, a big thank you to everyone that helped me with this!

author: dannyf
author_rank: Super Contributor
timestamp: June 27, 2014, 12:09:51 pm
content: -Its amazing what happens when you read the datasheet!-that's why I kept telling you to go back to the datasheet.- is the final code I came up with and guess what.. IT WORKS PERFECTLY!!-the code still has room for improvement.- I would return an integer rather that a float.- I would use just the lowest four bits of the first data byte.