|
|
View previous topic :: View next topic |
Author |
Message |
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Sep 13, 2007 6:12 am |
|
|
First program, working: Code: | setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_16); |
Second Program, failing: Code: | setup_spi(spi_master|spi_l_to_h|spi_clk_div_16); | Spot the difference...
In SPI there are 4 possible combinations for how the timing of the clock and data signals relates to each other. Motorola has given these combinations the names mode 0 to mode 3. Sometimes the names are given with binary numbers, for example mode 2 is then written as mode 1,0.
A graphical explanation of all four combinations can be found at http://elm-chan.org/docs/spi_e.html
The MCP41xxx devices clock the data in at the upgoing clock edge, this means you have to use either mode 0 or 3.
The other mode parameter defines if the clock has a high or low level in idle mode. Often this is not very critical but from the timing diagram in the MCP41xxx dataheet it looks like a high level is preferred, i.e. SPI mode 3.
Below is a SPI-mode to CCS-notation (also posted at other threads in this forum). Code: | // MOTOROLA | MICROCHIP | CCS
//------------------------------------------------------------------------------
// SPI Mode 0,0 | CKP = 0, CKE = 1 | SPI_L_TO_H | SPI_XMIT_L_TO_H
// SPI Mode 0,1 | CKP = 0, CKE = 0 | SPI_L_TO_H
// SPI Mode 1,0 | CKP = 1, CKE = 1 | SPI_H_TO_L
// SPI Mode 1,1 | CKP = 1, CKE = 0 | SPI_H_TO_L | SPI_XMIT_L_TO_H
#define SPI_MODE_0_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_0_1 (SPI_L_TO_H)
#define SPI_MODE_1_0 (SPI_H_TO_L)
#define SPI_MODE_1_1 (SPI_H_TO_L | SPI_XMIT_L_TO_H)
Example: setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_4 );
|
Last edited by ckielstra on Thu Sep 13, 2007 7:02 am; edited 2 times in total |
|
|
jfk1965
Joined: 21 Oct 2003 Posts: 58
|
|
Posted: Thu Sep 13, 2007 6:46 am |
|
|
Thanks
program works now surprise surprise.
I'm now on the way to the opticians to get my eye's tested.
Thanks again
JFK |
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Thu Sep 13, 2007 8:57 am |
|
|
Re: 2. point
sorry, i was not clear. You can add two elements to the Temp[] array, a 0 in the beginning and 0xFFFF to the end and the corresponding digipot values to that array also. So you will not need check with two extra instructions for the out of range values. You will do it implicitly in the loop. this will result a more compact, clear code.
4. therm < Temp && therm > Temp[a+1] these tests ignore the rare case if the therm happens to be equal with some of the Temp values. (it causes not much trouble here, but you should get to the habit of looking at all possible code paths) Change the > to >= |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Sep 13, 2007 2:11 pm |
|
|
Maybe you missed my updated post, probably the best setting for SPI is: Code: | setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_XMIT_L_TO_H | SPI_CLK_DIV_16); |
|
|
|
jfk1965
Joined: 21 Oct 2003 Posts: 58
|
|
Posted: Tue Sep 18, 2007 4:47 am |
|
|
Thanks can't see any difference in operation using the original one to the one you suggested later.
Noticed a problem and I don't know why it does this as it simulated out ok.
When I first switch on the digipot is set at midpoint as it should be, adjusting the temperature measured by heating and cooling the thermistor is fine as long as it doesn't send the digipot below 38K. But if it needs to go below 38K it does but then it won't exceed 38K again at all it stops at a maximum of 38K until you reset by switching off the power.
I modified my program to start the digipot at it's lowest value and cycle up it's scale one step at a time but even using this method it still stops at 38K.
I have changed the digipot and it does exactly the same with the new one.
I'm getting a bit stumped now any suggestions please.
JFK |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Sep 18, 2007 5:54 am |
|
|
Quote: | Thanks can't see any difference in operation using the original one to the one you suggested later. | According to chapter 5.8 of the datasheet you can run the devices in SPI mode 1,1 or 0,0. Your original configuration was using SPI-mode 1,0 and only working by chance (bus speed, line capacitance, temperature, etc.). Better to play it safe and use one of the specified bus modes.
Regarding the 38k problem you need to provide more information, best is to post a small program (15 lines) demonstrating your problem.
What is the nominal resitance of your potentiometer? |
|
|
jfk1965
Joined: 21 Oct 2003 Posts: 58
|
|
Posted: Tue Sep 18, 2007 6:53 am |
|
|
OK thanks
The program i'm using is this one, the pot is 100K and automatically goes to midpoint at powerup unless changed by the data sent. The program i'm using is this.
Code: |
#include <16f690.h>
#DEVICE *=16 ADC=10
#use delay(clock=4000000)
#fuses XT,NOWDT,NOBROWNOUT
#include <stdlib.h>
//Redbox 27VDC @ 20A Charger
//Revision 1.0
//written on compiler version 3.245
//tested on compiler version 3.245
#define ledred (Pin_C0)
#define CS (Pin_B7)
#define vmin 517
#define imin 50
#define onoff (Pin_C2)
//Arrays
long Temp[42] = {784,775,765,755,745,735,725,714,704,693,682,671,660,649,637,626,615,603,592,581,569,558,546,535,524,512,501,490,479,468,457,446,436,425,415,405,395,385,375,365,356,346};
int digipot[41] = {255,250,244,238,232,226,220,214,208,202,196,190,184,178,172,166,154,148,142,136,130,124,118,112,106,100,94,88,82,76,70,64,58,52,46,40,34,28,22,16,0};
//Variables
long vbatt,therm,ibatt;
int a,pot;
void main()
{
setup_adc_ports(sAN0|sAN1|sAN2);
setup_adc(ADC_CLOCK_DIV_2);
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_XMIT_L_TO_H | SPI_CLK_DIV_16);
start:
//setup
output_high CS;
therm=0,pot=0;
do{
set_adc_channel(2);
delay_us(75);
therm = read_adc();
For (a = 0; a <= 40; ++a)
{
if(therm <= Temp[a]&& therm >= Temp[a+1])
{
pot = digipot[a];
}
if(therm > Temp[0])
{
pot = digipot[0];
}
if (therm < Temp[39])
{
pot = digipot[39];
}
}
Output_low CS;
spi_write(0x11); //command byte
spi_write(pot); //data byte
output_high CS;
delay_ms(500);
}
while(1);
}
|
At the moment at switch on the pot goes to midpoint (50K) and then from the first data sent drops to 44K and settles at this because it is room temerature setting.
If I spray the thermistor with a freezer spray the resistance changes to 100K and then slowly drops down to 44K as it warms back up to room temperature. If I warm the thermistor up by breathing on it the resistance of the digipot drops to 12K and then slowly rises as the thermistor cools. When it rises to 38K it stops and doesn't go any higher even if I freeze spray the thermistor again, it will go lower if I warm the thermistor again though but will stop at 38K again.
If I turn off the power for a few swconds and turn it back on a gain it will then return to 44K and will work ok until it drops below the 38K point.
For some reason once it drops below the 38K it won't go any higher until reset.
Any ideas?
Thanks
JFK
PS Just checked the data coming out of the PIC and when the digipot is stuck at the 38K point the data sent to the digipot is still changing (correctly) so it looks like a problem with the digipot rather than my program. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Sep 18, 2007 7:35 am |
|
|
How are you meassuring the 38k? Is it possible for some kind of load in your circuit to switch on or off at the higher temperatures causing your meassurements to be influenced? Are you getting the same readings directly of the potentiometer pins when no other circuit is connected?
A small error unrelated I can spot quickly is: Code: | if (therm < Temp[39])
{
pot = digipot[39];
} | Change the first 39 to 41 and the second to 40.
And you didn't implement Libor's suggestion for optimizing the for-next loop. Restructuring the loop makes your code easier to read, faster to execute and less error prone.
Change to: Code: | if (therm > Temp[0])
{
pot = digipot[0];
}
else if (therm <= Temp[41]) // Note: changed 39 to 41 and added '='
{
pot = digipot[40]; // Changed 39 to 40
}
else for (a = 0; a <= 40; ++a)
{
if ( (therm <= Temp[a]) && (therm > Temp[a+1]) ) // Note: removed '=' and added extra '(..)' to avoid compiler priority problems.
{
pot = digipot[a];
}
} |
|
|
|
jfk1965
Joined: 21 Oct 2003 Posts: 58
|
|
Posted: Tue Sep 18, 2007 7:58 am |
|
|
I'm measuring the 38K with a digital multimeter across the digipot terminals there is no other circuitry connected to the digipot terminals, so no influence from elsewhere.
Thanks for the code amendments I'll test them out.
JFK |
|
|
jfk1965
Joined: 21 Oct 2003 Posts: 58
|
|
Posted: Wed Sep 19, 2007 4:06 am |
|
|
Anyone any other ideas why once it has passed 38K it won't go passed it again?
I've looked and looked I just can't see a problem.
Thanks
JFK |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Sep 19, 2007 6:42 am |
|
|
The setup of the ADC clock is outside the specifications. With your clock speed of 4Mhz set it to either DIV_8 or DIV_16. Now the ADC is delivering unreliable results.
Here is a cleaned up version of your code with all unused variables and declarations removed: Code: | #include <16f690.h>
#fuses XT,NOWDT,NOBROWNOUT
#DEVICE *=16 ADC=10
#use delay(clock=4000000)
//Redbox 27VDC @ 20A Charger
//Revision 1.0
//written on compiler version 3.245
//tested on compiler version 3.245
#define CS PIN_B7
//Arrays
const int16 Temp[42] =
{784, 775, 765, 755, 745, 735, 725, 714, 704, 693,
682, 671, 660, 649, 637, 626, 615, 603, 592, 581,
569, 558, 546, 535, 524, 512, 501, 490, 479, 468,
457, 446, 436, 425, 415, 405, 395, 385, 375, 365,
356, 346};
const int8 digipot[41] =
{255, 250, 244, 238, 232, 226, 220, 214, 208, 202,
196, 190, 184, 178, 172, 166, 154, 148, 142, 136,
130, 124, 118, 112, 106, 100, 94, 88, 82, 76,
70, 64, 58, 52, 46, 40, 34, 28, 22, 16,
0};
void main()
{
int8 a, pot;
int16 therm;
setup_adc_ports(sAN0|sAN1|sAN2);
setup_adc(ADC_CLOCK_DIV_8); // Changed DIV_2 to DIV_8 for clock speed compliance.
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_XMIT_L_TO_H | SPI_CLK_DIV_16);
//setup
output_high(CS);
therm=0;
pot=0;
do
{
set_adc_channel(2);
delay_us(75);
therm = read_adc();
if (therm > Temp[0])
{
pot = digipot[0];
}
else if (therm <= Temp[41])
{
pot = digipot[40];
}
else for (a = 0; a <= 40; a++)
{
if ( (therm <= Temp[a]) && (therm > Temp[a+1]) )
{
pot = digipot[a];
break; // Optimization: exit loop when found.
}
}
output_low(CS);
spi_write(0x11); //command byte
spi_write(pot); //data byte
output_high(CS);
delay_ms(500);
} while(1);
} |
Also note how the arrays are declared as const. This places the data in ROM and saves about 120 bytes of RAM.
If the above code is still not working then try to split the problem in smaller parts. Is it the ADC that's failing or is it the potentiometer?
A real time saver in debugging is to implement an rs232 output for debug messages on the PC. Then you can print the values read from the ADC and the value you are sending to the potentiometer. |
|
|
Ttelmah Guest
|
|
Posted: Wed Sep 19, 2007 7:33 am |
|
|
How are you actually 'testing' this?.
You say that you only have the test meter attached to the pot. This may be the problem.
Digital potentiometers, behave significantly differently from 'real' ones. If you apply a DVM, the result can be wildly 'skewed', if the test voltage takes the wiper outside the supply range. You need to ground the bottom terminal of the digipot, or attach it to a voltage inside the working range, and then ensure that the DVM, is applying a +ve test voltage to the wiper. If you look at the data sheet test setup, this is done exactly this way. The pot will behave abnormally, if this is not the case.
Best Wishes |
|
|
jfk1965
Joined: 21 Oct 2003 Posts: 58
|
|
Posted: Wed Sep 19, 2007 7:44 am |
|
|
Thankyou for all the help you are giving ckielstra.
I will test the code you have modified for me. One thing that makes me think that there is nothing wrong with the code as such is that when the pot gets stuck at 38K and I freeze spray the thermistor the data coming out of the PIC does change but the potentiometer doesn't change value.
I have sat and watched (with a scope)what the data should be when the pot is at 100K. When the pot is stuck I freeze spray the thermistor and the data comming out of the PIC changes to the 100K data but the digipot doesn't respond.
I have tried this on 3 different digipot chips now and they all do the same, I really don't now where the problem is.
OK now update I have tested the code you sent me and there is no change, it still does the same
I monitored the data coming out of the chip again and it definetly changes to set the pot to 100K but the pot just sits there at 38K and won't budge. If I reset the power whilst the thermistor is still cold it immediately jumps to 100K when power is restored.
Can I assume the code works now and that the problem lies with the digipot?
JFK |
|
|
jfk1965
Joined: 21 Oct 2003 Posts: 58
|
|
Posted: Wed Sep 19, 2007 8:01 am |
|
|
Ttelmah wrote: | How are you actually 'testing' this?.
You say that you only have the test meter attached to the pot. This may be the problem.
Digital potentiometers, behave significantly differently from 'real' ones. If you apply a DVM, the result can be wildly 'skewed', if the test voltage takes the wiper outside the supply range. You need to ground the bottom terminal of the digipot, or attach it to a voltage inside the working range, and then ensure that the DVM, is applying a +ve test voltage to the wiper. If you look at the data sheet test setup, this is done exactly this way. The pot will behave abnormally, if this is not the case.
Best Wishes |
Hi I have the digipot setup as per the test parameter diagriam in the datasheet. I'm measuring it by connecting the DVM between Ground and the wiper. I'll check the DVM for a positive test voltage.
JFK |
|
|
|
|
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
|