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 support@ccsinfo.com

Help a newbie to spi?
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
jfk1965



Joined: 21 Oct 2003
Posts: 58

View user's profile Send private message

Help a newbie to spi?
PostPosted: Mon Sep 10, 2007 7:49 am     Reply with quote

Hi

I'm attempting to use spi for the first time.

I'm using a 16F690 and a MCP41100 digital potentionmeter.

I read a post a while ago and I have a small test program to get the spi working first before I attempt to add any other functionality to it. The program is as follows.

compiler version 3.245.

Code:
#include <16f690.h>
#fuses XT,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT
#use delay(clock=4000000)
#define DIGIPOT PIN_B7

void main()
{
  int8 a;

  setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_16);
  while (1)
  {
    for (a='0'; a<='9'; a++)
    {
      output_low(DIGIPOT);             
      spi_write( a );
      output_high(DIGIPOT);             
      delay_ms(1000);
    }
  }
}


The digipot sets itself to midpoint at power up (which I have measured and it does this) I would have expected to see the value of resistance change when the program runs but it's stationary.

I assume that there is something wrong with my test program but I can't see what, can anyone assist please?

JFK
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Mon Sep 10, 2007 8:01 am     Reply with quote

RTFM

The MCP41100 expects two bytes: a command byte and a data byte. You are sending only one byte at a time and it is not even a valid command byte.
jfk1965



Joined: 21 Oct 2003
Posts: 58

View user's profile Send private message

PostPosted: Thu Sep 13, 2007 3:01 am     Reply with quote

OK thanks for the pointer to the command byte have to admit I was guity of not reading that far on the data sheet.

I now have this program which works ok

Code:
#include <16f690.h>
#fuses XT,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT
#use delay(clock=4000000)
#define DIGIPOT PIN_B7

void main()
{
  int8 i;

  setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_16);
  while (1)
  {
    for (i='0'; i<='9'; i++)
    {
      output_low(DIGIPOT);             
      spi_write(0x11);                                  //command byte
      spi_write( i );                                      //data byte
      output_high(DIGIPOT);
      delay_ms(3000);
    }
  }
}



so this proved that my pcb etc was all working.

I then wrote the basis for my application which 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[41] = {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};
 int digipot[41] = {256,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};
//Variables
long vbatt,therm;
int a,pot;

void main()
 {
   setup_adc_ports(sAN0|sAN1);
    setup_adc(ADC_CLOCK_DIV_2);
    setup_spi(spi_master|spi_l_to_h|spi_clk_div_16);

start:
//setup
output_high CS;
vbatt=0,therm=0,pot=0;


//continous loop
do{
//measure what the temperature is
//and adjust output via a
//digi pot on SPI
   
//measure temperature
   set_adc_channel(3);
   delay_us(75);
   therm = read_adc();

//convert temperature to the equivalent
//digipot setting
   For (a = 1; a < 41; ++a)
   {

if(therm < Temp[a]&& therm > Temp[a+1])
      {
      pot = digipot[a];
      }
   if(therm > Temp[1])
      {
      pot = digipot[1];
      }
   if (therm < Temp[39])
      {
        pot = digipot[39];
      }
   }

//write data to digipot
   Output_low CS;
   spi_write(0x11); //command byte
   spi_write(pot);  //data byte
   output_high CS;

   delay_ms(1000);

  }
while(1);   
}


Which I can't get to work Mad
Can someone give me a push in the right direction?

Thanks in advance

JFK
libor



Joined: 14 Dec 2004
Posts: 288
Location: Hungary

View user's profile Send private message

PostPosted: Thu Sep 13, 2007 3:26 am     Reply with quote

1. set_adc_channel(3); You dont have channel 3. only AN0 and AN1 are setup.

2. You should move the 'out of range' tests out of the loop.
3. arrays begin with a 0 index. you begin testing from 1.
jfk1965



Joined: 21 Oct 2003
Posts: 58

View user's profile Send private message

PostPosted: Thu Sep 13, 2007 4:54 am     Reply with quote

Thanks for pointing out those errors , could you clarify what you mean on point 2?

The problem I have now is that the data coming out of the PIC is not changing the resistance value of the digipot.

To give a brief operational description the variable therm is measuring the voltage derived from a NTC thermister when the value of therm varies then the value of the digipot should be changed via the spi.

It would appear on a scope that the data coming out of the pic is changing when the thermistor is heated or cooled but the value of the digipot stays the same so I can only assume the data sent is incorrect.

If I use the short test program then the value of the pot varys so I know the PIC and Digipot etc are ok.

Any suggestions?

JFK
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Sep 13, 2007 6:12 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2007 6:46 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2007 8:57 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2007 2:11 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 18, 2007 4:47 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 18, 2007 5:54 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 18, 2007 6:53 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 18, 2007 7:35 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 18, 2007 7:58 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 19, 2007 4:06 am     Reply with quote

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
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