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

Software SPI
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
SvenBraun



Joined: 19 Mar 2016
Posts: 29

View user's profile Send private message

Software SPI
PostPosted: Wed Apr 06, 2016 8:11 pm     Reply with quote

I will preface this question by saying I've turned to my colleagues, countless searching online, and reading my datasheets and the CCS manual, but I haven't found what I'm looking for.

----

System information:
uC: PIC18F4455
DDS: AD9102


I am still new to SPI, and unfortunately, most of the resources I'm seeing are for hardware SPI. While the PIC18F series has hardware SPI, I can't use it (or it's very tricky to use it) in conjunction with UART (because there is a shared pin between UART and SPI), which is a requirement in my system.

So, because CCS supports software SPI, I have turned to that to solve my issue.

Unfortunately, a majority of people I work with do not use external DDS's, and even if they have, nobody has used software SPI in their system. So I'm more or less going in blind.


I have my Trigger, Reset, Chip Select, DI, DO, and SPI clock hooked up.

Here are my declarations for UART and SPI capabilities:
Code:
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)
#use SPI(MASTER, MODE=0, BAUD=115200, CLK=PIN_E0, DI=PIN_E1, DO=PIN_E2, BITS=16, NOINIT)


I am initiating transfer of SPI through the following method:
Code:

      if(!isEmpty(&DDS_instructions))
      {
         delay_ms(5);
         
         // If there are elements in the queue, remove them.
         while(!isEmpty(&DDS_instructions))
         {
            delay_ms(20);
            a = pop(&DDS_instructions);   // First pop should always be the address
           
            delay_ms(20);
            e = pop(&DDS_instructions);   // Second pop should always be the data
            sendSPI(a,e);
            delay_ms(20);
         }

         // When all instructions are sent, update the SPI settings on the DDS
         sendSPI(RAMUPDATE, UPDATESPI);

         turnOnTRIGGER;

      }



Here are my main functions for sending SPI:
ADDRESS and ELEMENT are both int16_t type.

Code:

void sendSPI(ADDRESS addrX, ELEMENT dataX)
{
   // Configure for transfer
   turnOnCS; // Start transfer (Active low)

   // Transferring
   spi_send(addrX);        // Send address first
   spi_send(dataX);        // Send data (see note above)
   
   // Reconfigure to end transfer
   //-- RAMUPDATE bit can be placed here to finish the transfer to shadow registers
   //      but the transfers happen automatically when the pattern generator is off.

   turnOffCS;   // End transfer

}

void spi_send(ELEMENT data)
{
   int i = 15;
   
   while(i+1 > 0)
   {
      output_high(PIN_E0); // High during transfer
   
      spi_xfer( ((data>>i)&0x1) );
      i--;
     
      output_low(PIN_E0);  // Low after sending
   }

}



My output so far is producing a sine wave through the DDS's differential current output at 60 Hz. I'm assuming that since I've been changing the desired frequency in my code, yet it's maintained 60 Hz, that I'm not programming it correctly.


I've looked at the example codes, but the one provided in PICC/Examples is for Hardware implementation of SPI.


Last edited by SvenBraun on Wed Apr 06, 2016 10:01 pm; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Apr 06, 2016 9:31 pm     Reply with quote

What are you doing here ?
Quote:
void spi_send(ELEMENT data)
{
int i = 15;

while(i+1 > 0)
{
output_high(PIN_E0); // High during transfer

spi_xfer( ((data>>i)&0x1) );
i--;

output_low(PIN_E0); // Low after sending
}

}


In your statement below, you specify 16 bit transfers.
Quote:
#use SPI(MASTER, MODE=0, BAUD=115200, CLK=PIN_E0, DI=PIN_E1, DO=PIN_E2, BITS=16, NOINIT)

This means spi_xfer() will automatically handle the transmission of 16 bits
of data. All you need is one line of code to transmit all 16 bits.
Code:

spi_xfer(data);
SvenBraun



Joined: 19 Mar 2016
Posts: 29

View user's profile Send private message

PostPosted: Wed Apr 06, 2016 9:58 pm     Reply with quote

Good point.

My main issue is not understanding how SCK interacts with the software SPI. In the datasheets, they illustrate the writing process with sending one bit per clock cycle.

In my original version, I sent it all at once, and that second SPI function wasn't involved. When I realized my clock wasn't changing, I added that function in, so I could send one bit at a time, while manually changing the clock line.

I have modified that BITS=16 to BITS=1 in my program.

EDIT: I accidentally omitted the calling function from the method and sendSPI. I updated the original post to include where that was.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Apr 06, 2016 11:25 pm     Reply with quote

Quote:

When I realized my clock wasn't changing, I added that function in, so I
could send one bit at a time, while manually changing the clock line.

I have modified that BITS=16 to BITS=1 in my program.

If the SPI clock is not changing, then something is very wrong.
You should not do a work-around. You should trouble-shoot and
fix the missing clock problem.

Do that by removing all higher level code and reducing the
program down so it sends 16-bits with the spi_xfer() function,
and that's all it does. Don't connect an SPI slave device to
the SCK pin. Just watch it with an oscilloscope.
Ttelmah



Joined: 11 Mar 2010
Posts: 19382

View user's profile Send private message

PostPosted: Thu Apr 07, 2016 2:05 am     Reply with quote

Some comments:

In the CCS examples, the SPI _slave_ uses hardware, the SPI _master_ uses hardware or software, according to the pins you use. This is documented in the comments.

NOINIT, only applies to a hardware port.

Then have you disabled the SPP port (PSP)?. Old 'rule' with the PIC, always make sure every other peripheral on the pins you are trying to use is disabled. Your pins are all used for SPP functions. Quite a few peripherals do default to disabled, but many others default to enabled.

Why on earth are you trying to use 115200bps?. Binary serial values have no advantage for this. Suggest something simple that relates to your CPU clock rate like 200000bps.

You don't show what values you are actually sending, but since the pattern generator is off, nothing will be output. Then also nothing will be output, till you change the start and end address used by the pattern generator.

For a while just forget the chip you are trying to talk to. Do a simple piece of code initialising the chip, and with the #USE SPI, and have this loop sending words out the SPI port. So something like:
Code:

//Your chip setup and clock
:   
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)
#use SPI(MASTER, MODE=0, BAUD=200000, CLK=PIN_E0, DI=PIN_E1, DO=PIN_E2, BITS=16)

void main(void)
{
   int16 val;
   output_high(CS);
   setup_ccp1(CCP_OFF);
   setup_ccp2(CCP_OFF);
   setup_psp(PSP_DISABLED);
   setup_comparator(NC_NC_NC_NC);
   setup_adc(ADC_OFF);
   setup_adc_ports(NO_ANALOGS);
   while(TRUE)
   {
      for (val=0;val<256;val++)
      {
         output_low(CS); //whatever pin you want
         spi_xfer(val);
         output_high(CS);
         delay_us(50);
      }
   }
}

Sync your scope on CS, and look at the clock and data.
You should see the CS drop, then 8 clocks with the data line held low, then 8 clocks with the value 'counting' from 0 to 255, then CS going high, then a short pause and the cycle starting again.

Key thing with debugging is to always step back and attack _just_ the core problem. Forget about your chip and everything, till you know how to drive the SPI. Only then start to attack the bigger problems....
SvenBraun



Joined: 19 Mar 2016
Posts: 29

View user's profile Send private message

PostPosted: Thu Apr 07, 2016 11:52 am     Reply with quote

Thank you to both of you!

I noticed myself getting flustered with the issue, and knew I had to step back to look at the problem again, but I really should have stepped back to the core of SPI, which I should've done in the first place.

I've played around with the code that Ttelmah posted, and saw exactly what I was looking for: with the oscilloscope, I noticed my clock going for 16 bits, and then going low. My data line also had data being transferred. (I wish this was an example in the PICC examples library, for how simple it is.)

I modified it to 8-bit transfer, and the oscilloscope output matched what I expected.


I do have one more question:

Ttelmah, you mentioned that the general rule is to disable unused peripherals, because the pins are all used for SSP functions. (Something I didn't know, but am now writing down to never forget.)

To either of you:
If I was going to add, for example, an ADC component to my system, can there be potential issues with overlapping data from the SSP? Or are peripherals that are set-up to specific pins become dedicated to that peripheral?
Ttelmah



Joined: 11 Mar 2010
Posts: 19382

View user's profile Send private message

PostPosted: Thu Apr 07, 2016 11:59 am     Reply with quote

You need to make sure that in the setup_adc_ports command, the analog channels corresponding to these bits are not enabled. Some peripherals use specific pins, others (like the ADC), have them selectable.

If you use a stream name for the #use spi, you can change the transfer length 'on the fly'. If you look at the spi_xfer command, when a stream name is used, you can use the command like:

spi_xfer(STREAM_NAME, data_to_send, number_of_bits_to_send);
SvenBraun



Joined: 19 Mar 2016
Posts: 29

View user's profile Send private message

PostPosted: Thu Apr 07, 2016 12:05 pm     Reply with quote

So using peripherals that have more than one channel, as long as you do something like:

Code:
setup_adc_ports(AN0);


Only the first analog channel will be enabled, with the other channels remaining disabled like in:

Code:
setup_adc_ports(NO_ANALOGS);
Ttelmah



Joined: 11 Mar 2010
Posts: 19382

View user's profile Send private message

PostPosted: Thu Apr 07, 2016 2:14 pm     Reply with quote

Exactly.
SvenBraun



Joined: 19 Mar 2016
Posts: 29

View user's profile Send private message

PostPosted: Fri Apr 08, 2016 12:33 pm     Reply with quote

Perfect, thank you! Now I can focus on programming the DDS.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Sat Apr 09, 2016 7:16 pm     Reply with quote

After reading the data sheet for the AD9102
i think you are going to have much education and entertainment
ahead of you in programming a driver using
code like this example.

spi_xfer( ((data>>i)&0x1) );

have a closer look at the number of
bits transmitted for various different commands
while /CS is low.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Apr 09, 2016 7:38 pm     Reply with quote

That's what we were pointing out to him. But he never said for certain
if he had fixed it.
SvenBraun



Joined: 19 Mar 2016
Posts: 29

View user's profile Send private message

PostPosted: Sat Apr 09, 2016 8:30 pm     Reply with quote

asmboy wrote:
After reading the data sheet for the AD9102
i think you are going to have much education and entertainment
ahead of you in programming a driver using
code like this example.

spi_xfer( ((data>>i)&0x1) );

have a closer look at the number of
bits transmitted for various different commands
while /CS is low.



I don't quite understand what you mean.

Following PCM and Ttelmah's advice, I stepped back to a simpler program, and reverted a lot of those changes.

After modification, my code changed back to 16-bit spi_xfers; one for address, and one for data.

The datasheet for AD9102 mentions that the addresses and the data themselves are 16-bits each, for a total of 32 bits.

I have tried modifying my spi_xfers to 32 bits (and sending the command + data together), but I didn't see much change.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Sat Apr 09, 2016 10:04 pm     Reply with quote

The data sheet issue i refer to is the single or double command possibility.

Without knowing what problem you are still having
the whatever that "has not changed"
and
without seeing your best shot at the code.
i don't know what can be done to help.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Apr 09, 2016 10:32 pm     Reply with quote

I'm pretty sure he has gotten rid of his faulty spi_send() routine and
replaced it with a simple spi_xfer() line, but he doesn't know how to tell you this.
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, 3  Next
Page 1 of 3

 
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