Author Topic: PIC16F1946 - Hardware SPI timing issues?  (Read 4927 times)

0 Members and 1 Guest are viewing this topic.

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
PIC16F1946 - Hardware SPI timing issues?
« on: November 06, 2022, 09:35:42 pm »
Hi all,

I am working on a board I designed using a PIC16F1946 in the QFN-64 package as my main mcu. This is not my first PIC16 project, but it is the first where I am implementing SPI with PIC.
I have been reading and studying the datasheet and online examples regarding hardware SPI using the MSSP peripheral.
I would like to use typical 4-wire SPI to interface with a MAX 31865 RTD temp sensor interface IC. Please see attached schematic for pinout and connection info.

I began with writing some code to initialize and read/write using hardware SPI on MSSP1. I am developing in MPLAB IDE X v6.05 using the free XC8 v2.40 compiler.

As far as I can tell, the setup code I wrote follows the SPI configuration as per the datasheet. However, I am seeing this strange behavior on my hardware: the clock (CLK) and serial data out (SDI) are transitioning at the same time, on the same edge. This is not how SPI should operate, as data is shifted in on the clock transition... i.e. the clock should transition while the data remains constant. For some reason, they are transitioning on the same edge which will not allow SPI to work.

My main.c is as follows:
Code: [Select]
/*
 * File:   max_test_1.c
 * Author: kev
 *
 * Created on November 6, 2022, 1:05 PM
 */


#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include "configs.h"

void spi_init(){
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
   
    TRISC = (0<<3) | (1<<4) | (0<<5) | (0<<6); //clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4 (input: SDI)
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
    SSP1CON1 = (0<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP), 3-0:(SSPM = 0000; Master mode , Fosc/4)
    SSP1STAT = (0<<7) | (0<<6); //6: (CKE) transmit on clock transition to active
    SSP1ADD = 0;
   
    //LATCbits.LATC6 = 1;  //CS high
    //LATCbits.LATC6 = 0;  //CS low
   
    SSP1CON1 = (1<<5); //set bit 5, enable serial port
}

uint8_t spi_read(uint8_t buf)
{
    SSP1BUF = buf;
    while(!PIR1bits.SSP1IF);
    PIR1bits.SSP1IF = 0;
    return SSP1BUF;
}

uint8_t spi_read2(uint8_t buf)
{
    SSP1BUF = 0xAA; // Writes dummy byte
while(!PIR1bits.SSP1IF); // Wait for transfer to complete
return SSP1BUF; // Returns received byte
}

void spi_write(uint8_t *buf)
{
    SSP1BUF = *buf;
    while(!PIR1bits.SSP1IF);
    PIR1bits.SSP1IF = 0;
}

void spi_write2(uint8_t buf)
{
uint8_t rxtmp;
SSP1BUF = buf; // Write to SPI
while(!PIR1bits.SSP1IF); // Wait for transfer to complete
rxtmp = SSP1BUF; // Dummy read
}

void main(void) {
    spi_init();
   
    unsigned char i;
   
    while(1){
        for( i = 0; i < 255; i++ ) {
            spi_write2(i);
        }
    }
}

Any idea or explanations as to if I am doing something incorrectly in software to initialize and write to the SPI buffer?

Attached a screenshot from my scope showing what happens when this code runs. The labels (CS, CLK, SDI, SDO) are as viewed from the MAX chip, so SDI here would be the same net as SDO on the PIC.
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #1 on: November 06, 2022, 09:38:48 pm »
My code is a bit sloppy as of now as I have been scatterbrained trying to figure this out as of now, and I am by no means a pro software engineer.
Attaching datasheet for PIC16F1946 for convenience.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #2 on: November 06, 2022, 09:55:10 pm »
Your problem is CKE. You must set it to 1.
But your code isn't doing that:
Code: [Select]
SSP1STAT = (0<<7) | (0<<6); //6: (CKE) transmit on clock transition to active

Fix:
Code: [Select]
SSP1STAT = (0<<7) | (1<<6); //6: (CKE) transmit on clock transition to active

No need to upload the datasheet (Wasting forum storage), just link it from microchip: datasheet

Edit:
For dummy read:
Code: [Select]
void spi_write2(uint8_t buf)
{
uint8_t rxtmp;
SSP1BUF = buf;            // Write to SPI
while(!PIR1bits.SSP1IF);  // Wait for transfer to complete
rxtmp = SSP1BUF;          // Dummy read
}

This is enough, and faster:
Code: [Select]
void spi_write2(uint8_t buf)
{
SSP1BUF = buf;            // Write to SPI
while(!PIR1bits.SSP1IF);  // Wait for transfer to complete
SSP1BUF;           // Dummy read
}
« Last Edit: November 06, 2022, 10:35:20 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #3 on: November 06, 2022, 09:57:02 pm »
I use similar chips (e.g., 16F18xx and 16F17xx).  I also code in Assembly, so my code probably won't help you.  The two variables I worry about are mode and system oscillator frequency.  Usually mode is pretty clear, but I wrote a routine for all four modes.  If I have a problem, I drop the fosc to something very reasonable, like 4 MHz or 8Mhz, then try each mode.  Once that is working, I go to 16 MHz or 32 MHz to be sure they also work.  In my present project, for example, with two peripherals on the same SPI, only one mode worked with both, and I needed to keep fosc at 16 MHz or below. 

Have you tried different modes at a low frequency?
 

Online Benta

  • Super Contributor
  • ***
  • Posts: 5870
  • Country: de
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #4 on: November 06, 2022, 10:10:27 pm »
There are four modes of SPI transfers depending on clock phase and clock polarity.
It looks to me like you have your clock polarity wrong.
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #5 on: November 06, 2022, 10:49:32 pm »
So I set the CKE bit as suggested:

Code: [Select]
void spi_init(){
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
   
    TRISC = (0<<3) | (1<<4) | (0<<5) | (0<<6); //clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4 (input: SDI)
    SSP1CON1 = (0<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP), 3-0:(SSPM = 0000; Master mode , Fosc/4)
    SSP1STAT = (0<<7) | (1<<6); //6: (CKE) transmit on clock transition to active
    SSP1ADD = 0;
   
    //LATCbits.LATC6 = 1;  //CS high
    //LATCbits.LATC6 = 0;  //CS low
   
    SSP1CON1 = (1<<5); //set bit 5, enable serial port
}

It still looks like the data and clock transition are falling on the same edge. I took a few more screenshots from the scope showing what the lines look like; the data being generated for SPI is random as per the for loop, but all captures appear to have this property.

Indeed, the timing diagram picture you shared does show clock transition occurring at half the data bit "length" in terms of time. This doesn't appear to be the case on my waveforms; I can't imagine that the hardware SPI perihperal has such as issue though? There is nothing in the errata for F1946 regarding this.


Your problem is CKE. You must set it to 1.
But your code isn't doing that:
Code: [Select]
SSP1STAT = (0<<7) | (0<<6); //6: (CKE) transmit on clock transition to active

Fix:
Code: [Select]
SSP1STAT = (0<<7) | (1<<6); //6: (CKE) transmit on clock transition to active

No need to upload the datasheet (Wasting forum storage), just link it from microchip: datasheet

« Last Edit: November 06, 2022, 10:52:52 pm by nexus »
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #6 on: November 06, 2022, 10:51:35 pm »
I use similar chips (e.g., 16F18xx and 16F17xx).  I also code in Assembly, so my code probably won't help you.  The two variables I worry about are mode and system oscillator frequency.  Usually mode is pretty clear, but I wrote a routine for all four modes.  If I have a problem, I drop the fosc to something very reasonable, like 4 MHz or 8Mhz, then try each mode.  Once that is working, I go to 16 MHz or 32 MHz to be sure they also work.  In my present project, for example, with two peripherals on the same SPI, only one mode worked with both, and I needed to keep fosc at 16 MHz or below. 

Have you tried different modes at a low frequency?

I am running at a pretty low speed; I have the interal system clock set for 1 MHz and I'm using Fosc/4 for SPI timing, so the clock is running at 250 KHz.
I believe the SPI mode is also set with the CKP/CKE bits that I have also set in the SSP1STAT register.
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #7 on: November 06, 2022, 11:09:58 pm »
Microchip's definition of the 4 modes uses different terms than most peripheral producers use.  (See: Wikipedia)  Sure, it is usually obvious what will work, but it takes very little simulator time to check the other modes when my first choice doesn't work.  Your choice of Fosc should not be a problem.  SPI is spec'd to almost DC, as it is simply an exchange of bits.

EDIT:  I checked the errata for that chip and found this:
Quote
6.1 SPI Master mode
When the MSSP is used in SPI Master mode
and the CKE bit is clear (CKE = 0), the Buffer
Full (BF) bit and the MSSP Interrupt Flag
(SSPIF) bit becomes set half an SCK cycle
early. If the user software immediately reacts to
either of the bits being set, a write collision may
occur as indicated by the WCOL bit being set.
I don't know if that is relevant to your situation, as I cannot read C reliably.  But it would be a problem if one was polling the flag.
« Last Edit: November 06, 2022, 11:18:31 pm by jpanhalt »
 

Online Benta

  • Super Contributor
  • ***
  • Posts: 5870
  • Country: de
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #8 on: November 06, 2022, 11:19:26 pm »
I looked at the Maxim datasheet.
They might call it "SPI", but it's nothing of the sort. Just the signal names prove that.
Your MCU side has to provide 16 consecutive positive-edge clock cycles to the socalled "SPI" Maxim device.
The first eight send an adress, the last eight read or write data.
A standard SPI peripheral interface will not support this.
I think you may need to write your own "bit-banger". Perhaps using the SPI peripheral interface, but I can't say.


 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #9 on: November 06, 2022, 11:34:12 pm »
It still looks like the data and clock transition are falling on the same edge.

And? Where's the problem?
All you need to care of is the sampling (active) edge, your problem was that both data and clock were changing in the active edge, not the case anymore.

The MAX31865 samples the data on the rising edges of the clock, when it goes high!
In that very specific moment the data is sampled, and nothing else happens until the next edge.
What happens to the data between the rising edges simply doesn't matter.

This is reflected on the clock waveforms, data changing inmediately after the clock goes idle is a great feature, leaves more time for the signal to settle down before the next clock pulse happens.


Edit: However I didn't notice Maxim idle clock level is high, so you need a diffferent clock polarity:
Code: [Select]
SSP1CON1 = (1<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP=1)
SSP1STAT = (0<<7) | (0<<6); //6: (CKE=0)
« Last Edit: November 06, 2022, 11:54:42 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #10 on: November 07, 2022, 12:00:10 am »
And should look like this:
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #11 on: November 07, 2022, 01:03:46 am »
It still looks like the data and clock transition are falling on the same edge.

Clock has two edges - rising and falling. There are two possibilities:

- The transmitter changes the data on the rising edge of the clock, the receiver samples the data on the falling edge of the clock (because the data is stable at this point)

or

- The transmitter changes the data on the falling edge of the clock, the receiver samples the data on the rising edge of the clock

If you're a transmitter you choose one or the other based on what the receiver does.

With higher speeds (or with longer transmission delays), it gets a little bit more complicated.
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #12 on: November 07, 2022, 01:41:58 am »
I looked at the Maxim datasheet.
They might call it "SPI", but it's nothing of the sort. Just the signal names prove that.
Your MCU side has to provide 16 consecutive positive-edge clock cycles to the socalled "SPI" Maxim device.
The first eight send an adress, the last eight read or write data.
A standard SPI peripheral interface will not support this.
I think you may need to write your own "bit-banger". Perhaps using the SPI peripheral interface, but I can't say.




I see... i think this is what im primarily missing.

Looking at the timing diagrams in the maxim datasheet, it shows that no serial data is sent from the Maxim during the first 8 clock cycles during a read or write. My SPI is currently only sending a byte at a time (8 clock cycles); no wonder the Maxim chip is not sending any data on SDO.

I will have to modify the SPI write function to run for two bytes consecutively for both read and write. I will try to do this with still using the MSSP hardware with SPI. Worst case I will have to abandon the hardware peripheral and write a bitbang function.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #13 on: November 07, 2022, 01:48:50 am »
I :o for a moment, one section showed one polarity type, other the opposite.
Well, it can take any of those polarities automatically:
Quote
The MAX31865 automatically accommodates either clock polarity by sampling SCLK when CS becomes active to determine the polarity of the inactive clock

So any of these will work:

CKP=1, CKE=0,
CKP=0, CKE=1

But NOT other combinations!
« Last Edit: November 07, 2022, 01:55:44 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #14 on: November 07, 2022, 02:21:59 am »
I will have to modify the SPI write function to run for two bytes consecutively for both read and write. I will try to do this with still using the MSSP hardware with SPI. Worst case I will have to abandon the hardware peripheral and write a bitbang function.

You don't need to abandon anything.

1. drive cs low
2. send the byte with the address
3. wait for the byte to be transmitted
4. read and discard the returned byte
5. send a zero byte
6. wait for the byte to be transmitted
7. read the returned data
8. drive cs high

If you need to read more data, you most likely can repeat steps 5 to 7 several times before step 8.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #15 on: November 07, 2022, 03:38:08 am »
Simple as this. Nothing else!
Code: [Select]
uint8_t spi_read(uint8_t addr)
{
    LATCbits.LATC6 = 0;       // CS low
    Nop();                    // Small delay
    SSP1BUF = addr;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0; 
    SSP1BUF = 0;              // Send dummy byte
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
    return SSP1BUF;           // Return received data
}
Code: [Select]
void spi_write(uint8_t addr, uint8_t data)
{
    LATCbits.LATC6 = 0;       // CS low
    Nop();                    // Small delay
    SSP1BUF = addr;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0; 
    SSP1BUF = data;           // Send data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
}

Or combined:
Code: [Select]
uint8_t max_rw(uint8_t addr, uint8_t data)
{
    LATCbits.LATC6 = 0;       // CS low
    Nop();                    // Small delay
    SSP1BUF = addr;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0; 
    SSP1BUF = data;           // Send dummy byte / data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
    return SSP1BUF;           // Return received data
}
Code: [Select]
    max_rw(addr, data);       // write
    data = max_rw(addr, 0);   // read
« Last Edit: November 07, 2022, 03:51:10 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #16 on: November 07, 2022, 06:23:46 am »
I must have been asleep yesterday.  I use the MAX31856 regularly with a PIC16F1789.  The MAX31865 has the same interface.  I need to use mode 3 as the GLCD display doesn't work with mode 1.  If DavidAlfa's code doesn't work, you may find these settings helpful.
Here is the setup for a PIC16F1789.  You may need to change the register names as the 16F1946 has two MSSP modules; whereas, the 16F1789 only has one.
Code: [Select]
;*******************************************************************************
;Setup SPI serial port
;*******************************************************************************
;;Mode 0                      ;CKP=0, CKE=1
;     banksel   SSPSTAT       ;16F1783 DS refers to these registers as shown  |B4
;     clrf      SSPSTAT       ;but the text omits the "1's", works either way |b4
;     bsf       SSPSTAT,6     ;CKE=1 =active to idle                          |B4
;     movlw     b'00100001'    ;SPI clk=Fosc/4,bit<0>=0,clk=Fosc/16,bit<0>=1  |B4
;     movwf     SSPCON1        ;bit<4>,CKP=0 = idle low, bit<5> enables SPI   |B4
     
;;Mode 1                      ;CKP=0,CKE=0
;     banksel   SSPSTAT       ;all SPI controls in Bank4                    |B4
;     clrf      SSPSTAT       ;CKE=0, SMP=0 (sample at middle)              |B4
;     movlw     b'00100001'    ;enable SPEN, set SPI CLK to Fosc/16         |B4
;     movwf     SSPCON1       ;CKP=0,master clk =Fosc/x (see below)         |B4

;;Mode 2                      ;CKP=1,CKE=1                     
;     banksel   SSPSTAT       ;all SPI controls in Bank4                    |B4
;     clrf      SSPSTAT       ;CKE=1, SMP=1 (sample at middle)              |B4
;     bsf       SSPSTAT,6     ;                                             |B4   
;     movlw     b'00110001'    ;enable SPEN, set SPI CLK to Fosc/16         |B4
;     movwf     SSPCON1       ;CKP=1,master clk =Fosc/x (see below)         |B4

;;Mode3                       ;CKP=1,CKE=0
     banksel   SSPSTAT       ;                                             |B4
     clrf      SSPSTAT       ;                                             |B4
     movlw     b'00110001'   ;enable serial port, clk = Fosc/16            |B4
     movwf     SSPCON1       ;                                             |B4
;*******************************************************************************
     bsf       SSPSTAT,7      ;set SMP, doess not affect mode              |B4
;NB: Modes 0,2,3 work with display.  Mode 1 does not.  Only Modes 1&3 work
;with MAX31856. Tested at Fosc/16 = 2 MHz.  Modes defined as per Wikipedia.
;******************************************************************************* 
And here are the 3 routines I use to communicate to the MAX31856
Code: [Select]
;*******************************************************************************
PutAddr                       ;call from any bank, returns |B0
     movlb     2              ;                                                 |B2                                         
     bcf       TC_CS          ;                                                 |B2
     movlb     4              ;                                                 |B4
     movwf     SSPBUF         ;                                                 |B4
     btfss     SSPSTAT,BF     ;                                                 |B4
     bra       $-1            ;                                                 |B4
     return

;NB: Can combine Putreg and GetReg as what is written to SSPBUF doesn't matter
;when reading.  Factor off "ExitR_W" as a separate call.  Saves about 7 LOC
PutReg                        ;enter w/ value to be written in WREG
     movwf     SSPBUF         ;                                                 |B4
     btfss     SSPSTAT,BF     ;                                                 |B4
     bra       $-1            ;                                                 |B4
     movlb     2              ;                                                 |B2
     bsf       TC_CS          ;                                                 |B2
     movlb     0              ;                                                 |B0
     return
GetReg
     clrf      SSPBUF         ;if read not necessary, then just clrf            |B4
     btfss     SSPSTAT,BF     ;                                                 |B4
     bra       $-1            ;                                                 |B4
     movf      SSPBUF,w       ;move first read byte to WREG                     |B4
     return                   ;NB:returns in|B4                                 |B4

EDIT: Since this uses polling, you may need to add a NOP or two before returning to take care of the errata issue.  The SSPIF flag as in DavidAlfa's routine has the same problem.  It might be worth checking the C-code disassembly to see if there is a delay.
« Last Edit: November 07, 2022, 10:10:49 am by jpanhalt »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #17 on: November 07, 2022, 12:49:17 pm »
Just use CKE=1, CKP=0 and errata will be gone.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #18 on: November 07, 2022, 02:15:34 pm »
The Maxim MAX31865 only supports modes 1 & 3
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #19 on: November 10, 2022, 09:03:18 pm »
@Nexus
Did you get it to work?
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #20 on: November 11, 2022, 06:31:51 pm »
Sorry for the delay, busy week.

I implemented DavidAlfa's code, which makes perfect sense to me when I read it line by line. When I run my test routine:

Code: [Select]
/*
 * File:   max_test_1.c
 * Author: kev
 *
 * Created on November 6, 2022, 1:05 PM
 */


#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include "configs.h"

void spi_init(){
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
   
    TRISC = (0<<3) | (1<<4) | (0<<5) | (0<<6); //clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4 (input: SDI)
    SSP1CON1 = (1<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP), 3-0:(SSPM = 0000; Master mode , Fosc/4)
    SSP1STAT = (0<<7) | (1<<6); //6: (CKE) transmit on clock transition to active
    SSP1ADD = 0;
   
    SSP1CON1 = (1<<5); //set bit 5, enable serial port
   
    //LATCbits.LATC6 = 1;  //CS high
    //LATCbits.LATC6 = 0;  //CS low
}

uint8_t spi_read(uint8_t addr)
{
    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = addr;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = 0;              // Send dummy byte
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
    return SSP1BUF;           // Return received data
}

void spi_write(uint8_t addr, uint8_t data)
{
    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = addr;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = data;           // Send data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
}

void main(void) {
    spi_init();
   
    unsigned char i;
   
    while(1){
        for( i = 0; i < 255; i++ ) {
            spi_write(0x80,i);
        }
    }
}

It seems to be working; I get 16 clock cycles, CS goes low which enables comm on the max device. There is data being written on SDI.
I don't expect any data on SDO from the max since this is just writing random data.



I tried moving on to using the library I had selected for this project. It is a C library for the MAX 31865 which seems pretty well structured and comprehensive. https://github.com/hepingood/max31865

I added the library to my project and I am trying to implement the "shot" reading mode.

I imported the code into the "driver_max31865_interface.c" file which is the blank interface template provided by the library to be tailored to the hardware.

This is my "driver_max31865_interface.c" code:
Code: [Select]
#include "driver_max31865_interface.h"
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#define _XTAL_FREQ 1000000

/**
 * @brief  interface spi bus init
 * @return status code
 *         - 0 success
 *         - 1 spi init failed
 * @note   none
 */
uint8_t max31865_interface_spi_init(void)
{
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
   
    TRISC = (0<<3) | (1<<4) | (0<<5) | (0<<6); //clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4 (input: SDI)
    SSP1CON1 = (0<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP), 3-0:(SSPM = 0000; Master mode , Fosc/4)
    SSP1STAT = (0<<7) | (1<<6); //7: (SMP) 1-end, 0-middle, 6: (CKE) transmit on clock transition to active
    SSP1ADD = 0;
   
    SSP1CON1 = (1<<5); //set bit 5, enable serial port
   
return 0;
}

/**
 * @brief  interface spi bus deinit
 * @return status code
 *         - 0 success
 *         - 1 spi deinit failed
 * @note   none
 */
uint8_t max31865_interface_spi_deinit(void)
{
    SSP1CON1 = (0<<5); //clear bit 5 of SSP1CON1 to disable serial port
    return 0;
}

/**
 * @brief      interface spi bus read
 * @param[in]  reg is the register address
 * @param[out] *buf points to a data buffer
 * @param[in]  len is the length of data buffer
 * @return     status code
 *             - 0 success
 *             - 1 read failed
 * @note       none
 */
uint8_t max31865_interface_spi_read(uint8_t reg, uint8_t *buf, uint16_t len)
{

    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = reg;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = 0;              // Send dummy byte
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
    return SSP1BUF;           // Return received data
       
return 0;   

}

/**
 * @brief     interface spi bus write
 * @param[in] reg is the register address
 * @param[in] *buf points to a data buffer
 * @param[in] len is the length of data buffer
 * @return    status code
 *            - 0 success
 *            - 1 write failed
 * @note      none
 */
uint8_t max31865_interface_spi_write(uint8_t reg, uint8_t *buf, uint16_t len)
{
    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = reg;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = *buf;           // Send data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
   
    return 0;
}

/**
 * @brief     interface delay ms
 * @param[in] ms
 * @note      none
 */
int delay_ms(unsigned int ms)
  {
     while(ms > 0)
      {
       __delay_ms(1);
      ms--;
      }
  }
void max31865_interface_delay_ms(uint32_t ms)
{
delay_ms(ms);
}

/**
 * @brief     interface print format data
 * @param[in] fmt is the format data
 * @note      none
 */
void max31865_interface_debug_print(const char *const fmt, ...)
{
   
}

And my main.c:

Code: [Select]
#include "configs.h" // configuration bits
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include "driver_max31865_shot.h"

uint8_t res;
uint8_t i;
float temp = 0;

#define _XTAL_FREQ 1000000 // needed for delay functions

 
void main(void) {
    OSCCONbits.IRCF = 0x0B; //set internal osc to 1mhz
    OSCCONbits.SCS = 0x00; //system clock setup
   
    max31865_shot_init(MAX31865_WIRE_2, MAX31865_RESISTOR_100PT, 400.f);
    while(1)    {
        max31865_shot_read((float *)&temp);;
        max31865_interface_delay_ms(1000);
    }
    return;
}

I do get the correct waveforms on the CS and CLK lines when "max31865_interface_spi_read" is being polled every second. However, no data being written on SDI and subsequently nothing on SDO.



Perhaps I'm not understanding how the library works completely?

For example, the "max31865_interface_spi_read" function has 3 arguments: (uint8_t reg, uint8_t *buf, uint16_t len)

"reg" should pass the correct register for the MAX31865 to the SPI routine, correct? (as defined in library file "driver_MAX31856.c")
Code: [Select]
/**
 * @brief chip register definition
 */
#define MAX31856_REG_CONFIG                0x00        /**< config register */
#define MAX31856_REG_RTD_MSB               0x01        /**< rtd msb register */
#define MAX31856_REG_RTD_LSB               0x02        /**< rtd lsb register */
#define MAX31856_REG_HIGH_FAULT_MSB        0x03        /**< high fault msb register */
#define MAX31856_REG_HIGH_FAULT_LSB        0x04        /**< high fault lsb register */
#define MAX31856_REG_LOW_FAULT_MSB         0x05        /**< low fault msb register */
#define MAX31856_REG_LOW_FAULT_LSB         0x06        /**< low fault lsb register */
#define MAX31856_REG_FAULT_STATUS          0x07        /**< fault status register */

This is where I begin scratching me head as a self-taught programmer. I have no formal computer science education, so I am just picking up lessons as I attempt to do things and they do not work.
« Last Edit: November 11, 2022, 06:34:02 pm by nexus »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #21 on: November 11, 2022, 08:14:25 pm »
"reg" should pass the correct register for the MAX31865 to the SPI routine, correct? (as defined in library file "driver_MAX31856.c")

Which of the registers are you trying to read?
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #22 on: November 11, 2022, 10:04:30 pm »
"reg" should pass the correct register for the MAX31865 to the SPI routine, correct? (as defined in library file "driver_MAX31856.c")

Which of the registers are you trying to read?

From the library (driver_max31865.c), the "max31865_single_read" function first reads and writes the config register (which is specified as 0x00) and then it reads the rtd msb register (0x01). So those would be the two registers I am trying to access during a call to "max31865_shot_read". I don't see any problems between the specified registers in the lib and the datasheet. Not sure why this isn't working assuming my SPI functions are also fine.
 

Online Benta

  • Super Contributor
  • ***
  • Posts: 5870
  • Country: de
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #23 on: November 11, 2022, 10:53:25 pm »
From the first 'scope plot, your clock has the wrong polarity/phase. You should have positive going pulse when transferring data.
« Last Edit: November 11, 2022, 10:55:15 pm by Benta »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #24 on: November 12, 2022, 12:23:17 am »
From the library (driver_max31865.c), the "max31865_single_read" function first reads and writes the config register (which is specified as 0x00) and then it reads the rtd msb register (0x01). So those would be the two registers I am trying to access during a call to "max31865_shot_read". I don't see any problems between the specified registers in the lib and the datasheet. Not sure why this isn't working assuming my SPI functions are also fine.

If it reads the config register and the value of the config register is zero, then what we see on your scope picture is what you would expect - both SDI and SDO low all the time. To observe non-trivial SDO you need to read a register which has an address other than zero. Similarly, to see something on SDI, the register you read must contain a value which is not zero.
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #25 on: November 12, 2022, 05:45:11 am »
From the first 'scope plot, your clock has the wrong polarity/phase. You should have positive going pulse when transferring data.


I see. I reversed the clock polarity by setting bit 4 in SSP1CON1. This is my initialization code now:

Code: [Select]
uint8_t max31865_interface_spi_init(void)
{
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
   
    TRISC = (0<<3) | (1<<4) | (0<<5) | (0<<6); //clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4 (input: SDI)
    SSP1CON1 = (1<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP), 3-0:(SSPM = 0000; Master mode , Fosc/4)
    SSP1STAT = (0<<7) | (0<<6); //7: (SMP) 1-end, 0-middle, 6: (CKE) transmit on clock transition to active
    SSP1ADD = 0;
   
    SSP1CON1 = (1<<5); //set bit 5, enable serial port
   
return 0;
}

CKE (SSP1STAT bit 6) is still cleared (0 - Transmit occurs on transition from Idle to active clock state)

This is what happens when the code first initializes and runs the "max31865_shot_init" function.



Interestingly, SDO is appears to go high after the last clock cycle and then decays, almost capacatively.

The polling "max31865_shot_read" function in the while loops looks like this:



The same decay happens on SDO after the 16th clock cycle. It actually goes high when the last CLK pulse occurs, the decays.
Perhaps the "gap" between the first 8 and last 8 clock cycles is too large, and the max is beginning to write serial data after the last 8 clock cycles?
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #26 on: November 12, 2022, 06:10:38 am »
SDO (also called MOSI) is driven by the PIC. SDI (MISO) is driven by Max. Check your connections. Do you have anything else connected to these pins.

Also, whewn you write this:

Code: [Select]
SSP1CON1 = (1<<5); //set bit 5, enable serial port
you overwrite all the bits of the register. It's the same as if you wrote

Code: [Select]
SSP1CON1 = 0x20; //set bit 5, enable serial port
It does set the 5-th bit to 1, but alse reset all the other bits to 0. What you need is this:

Code: [Select]
SSP1CON1 |= (1<<5); //set bit 5, enable serial port
or this:

Code: [Select]
SSP1CON1bits.SSPEN = 1; //set bit 5, enable serial port


 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #27 on: November 12, 2022, 07:27:15 am »
Your code is overcomplicated.
The MAX31865 only reads/write one byte for each register, so there's little sense on using a pointer to the buffer and data length.
Also the two sepparate read/write functions are 99% redundant, that's why I showed how to unify them, the result will be the same.
Why are you setting CKE low? You've been warned about the errata, also CKP=0,CKE=1 is the most common SPI mode.
Why so many functions always returning 0 when there're no checks of any kind, or anything else to return? Just make them void.
The delay_ms function is redundant, simply use __delay_ms();
Remember for the delay to work correctly, _XTAL_FREQ must be defined somewhere, matching the crystal frequency in Hz.
You're defining it as 1MHz, is that correct? Seems too low?

Writing SSPBUF before clearing SSP1IF will make it a bit faster, as the transmission starts earlier.
Code: [Select]
void max31865_interface_spi_init(void)
{
    TRISC    = 1<<4;          // Clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4 (input: SDI)
    SSP1CON1 = 0;
    SSP1STAT = 0;
    SSP1ADD  = 0;
    CKE1     = 1;             // Transmit from idle to active transition
    SSPEN1   = 1;             // Enable serial port
}

void max31865_interface_spi_deinit(void)
{
    SSPEN1=0;                 // Disable serial port
}

uint8_t max31865_interface_spi_readwrite(uint8_t reg, uint8_t data)
{
    LATC6 = 0;                // CS low
    NOP();                    // Small delay
    SSP1BUF = reg;            // Send address
    while(!SSP1IF);           // Wait
    SSP1BUF = data;           // Send data (Or dummy byte in read mode)
    SSP1IF = 0;               // Clear flag
    while(!SSP1IF);SSP1IF=0;  // Wait, clear flag
    LATC6 = 1;                // CS high
    return SSP1BUF;           // Return received data if any (Ignore in write mode)
}

Image test:
Code: [Select]
max31865_interface_spi_readwrite(0x99,0xAA);
« Last Edit: November 12, 2022, 09:32:33 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #28 on: November 20, 2022, 02:58:41 am »
Coming back to this after another exhausting week.

SDO (also called MOSI) is driven by the PIC. SDI (MISO) is driven by Max. Check your connections. Do you have anything else connected to these pins.

Also, whewn you write this:

Code: [Select]
SSP1CON1 = (1<<5); //set bit 5, enable serial port
you overwrite all the bits of the register. It's the same as if you wrote


I see, thank you for pointing this out. I changed the code such that I am not clearing any bits that were previously set accidently.

Your code is overcomplicated.
The MAX31865 only reads/write one byte for each register, so there's little sense on using a pointer to the buffer and data length.
Also the two sepparate read/write functions are 99% redundant, that's why I showed how to unify them, the result will be the same.
Why are you setting CKE low? You've been warned about the errata, also CKP=0,CKE=1 is the most common SPI mode.
Why so many functions always returning 0 when there're no checks of any kind, or anything else to return? Just make them void.
The delay_ms function is redundant, simply use __delay_ms();
Remember for the delay to work correctly, _XTAL_FREQ must be defined somewhere, matching the crystal frequency in Hz.
You're defining it as 1MHz, is that correct? Seems too low?

I did change it to CKP = 0 and CKE = 1 to avoid the errata; thank you for pointing this out (again). I also consolidated everything to the single read/write duplex function as you suggested. The "return 0" was a part of the max31865 library I was attempting to use. I discarded the library as you will see below and I'm trying to make my code as simple as possible so I can first understand what is (or isn't) working.

I am running the Fosc at 1MHz, so CLK is 250 kHz (Fosc/4). I don't think running on the slower side should somehow "break" SPI?

Keeping things as simple as possible, here is what I am trying:

Code: [Select]
/*
 * File:   max_test_1.c
 * Author: kev
 *
 * Created on November 6, 2022, 1:05 PM
 */


#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include "configs.h"
#define _XTAL_FREQ 1000000

void spi_init(){
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
   
    TRISC = (0<<3) | (1<<4) | (0<<5) | (0<<6); //clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4 (input: SDI)
    SSP1CON1 = (0<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP), 3-0:(SSPM = 0000; Master mode , Fosc/4)
    SSP1STAT = (0<<7) | (1<<6); //6: (CKE) transmit on clock transition to active
    SSP1ADD = 0;
   
    SSP1CON1bits.SSPEN = 1; //set bit 5, enable serial port
   
}

uint8_t max31865_interface_spi_readwrite(uint8_t reg, uint8_t data)
{
    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = reg;            // Send address
    while(!SSP1IF);           // Wait
    SSP1BUF = data;           // Send data (Or dummy byte in read mode)
    PIR1bits.SSP1IF = 0;      // Clear flag
    while(!SSP1IF);SSP1IF=0;  // Wait, clear flag
    LATCbits.LATC6 = 1;       // CS low
    return SSP1BUF;           // Return received data if any (Ignore in write mode)
}


void main(void) {
    spi_init();
    OSCCONbits.IRCF = 0x0B; //set internal osc to 1mhz
    OSCCONbits.SCS = 0x00; //system clock setup
   
    max31865_interface_spi_readwrite(0x00,0x62);
    __delay_ms(100);
   
    while (1){
        max31865_interface_spi_readwrite(0x01,0x00); //read RTD MSBs
        max31865_interface_spi_readwrite(0x02,0x00); //read RTD LSBs
//        max31865_interface_spi_readwrite(0x03,0x00); //read high fault threshold status
//        max31865_interface_spi_readwrite(0x04,0x00); //read low fault threshold status
//        max31865_interface_spi_readwrite(0x05,0x00); //read low fault threshold MSB
//        max31865_interface_spi_readwrite(0x06,0x00); //read low fault threshold LSB
//        max31865_interface_spi_readwrite(0x07,0x00); //read fault status
        __delay_ms(1000);
    }
}

The first command is to set the configuration register:  max31865_interface_spi_readwrite(0x00,0x62). Here's what that looks like on the scope:



The second two commands which run once a second in the while loop are to read the RTD MSB and LSB registers. Here's what that looks like on scope:



I have a tested PT100 hooked up across the RTD. My board is using a 400 Ohm reference resistor. Also attached full schematic project in case something here is obviously wrong, which I don't believe it is since I am using the MAX31865 reference design and I'm sure I have the SPI pins mapped correctly between the PIC and this chip.



What I'm not sure of is the data that I'm reading from the MAX31865 on SDO. The RTD MSB register reads empty. The RTD LSB register is all high, and there is this strange decay during the end of each transmission. It looks to be capacitive, which I don't understand.

Also, when I plug and unplug my RTD, nothing changes in the RTD MSB/LSB data from what I can see.

Room temperature (20C) for the PT100 with a 400 Ohm Rreference should yield the following data from the RTD registers:



Do I need to set the fault threshold registers as well? I don't see anything else that could be wrong here.
I also replaced the MAX31865 chip. Digikey was out of stock when I first made the board, so I bought a breakout on eBay to get the part. Now that the QFN20 package is in stock again I ordered 10 brand new ones, so I'm sure the chip itself is genuine and functional.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #29 on: November 20, 2022, 05:37:25 am »
The decay is normal, SDO becomes hi-z when chip is not active, PIC input has high impedance too, so it takes a bit to discharge.
If you really need to get rid of it, add a 10K pulldown resistor to SDO.
« Last Edit: November 20, 2022, 05:38:57 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #30 on: November 20, 2022, 02:50:38 pm »
Coming back to this after another exhausting week.

I looked at the datasheet.

In your communications, everything appears to be shifted by 1 bit. You're reading register 0x04 instead of 0x02, hence you get all ones. The response is shifted right by 1 bit too - 0x7f instead of 0xff. So, your SPI mode is wrong. Looking at their pictures in the datasheet, you need CKE = 0. The datasheet claims CKP doesn't matter (see Fig. 5). You have CKP = 0, CKE = 1.

Also, it will not do any conversions on its own. You can do either automatic conversions, or one shot conversion. Either way, you need to write to the configuration register to get anything out of the chip. Read the datasheet for details.

 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #31 on: November 20, 2022, 04:27:16 pm »
In your communications, everything appears to be shifted by 1 bit. You're reading register 0x04 instead of 0x02, hence you get all ones. The response is shifted right by 1 bit too - 0x7f instead of 0xff. So, your SPI mode is wrong. Looking at their pictures in the datasheet, you need CKE = 0. The datasheet claims CKP doesn't matter (see Fig. 5). You have CKP = 0, CKE = 1.

Ah, deja vu post #17 & 19.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #32 on: November 20, 2022, 06:49:52 pm »
Doesn't look shifted to me, I clearly see how SDO changes on the previous falling edge, sending 1 & 2.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #33 on: November 21, 2022, 05:16:42 am »
Doesn't look shifted to me, I clearly see how SDO changes on the previous falling edge, sending 1 & 2.

Sure. But the MAX device expects CKE = 0, so it sees it shifted.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #34 on: November 21, 2022, 05:54:57 am »
Nope:

The MAX31865 automatically accommodates either clock polarity by sampling SCLK when CS becomes active to determine the polarity of the inactive clock
« Last Edit: November 21, 2022, 05:56:41 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #35 on: November 21, 2022, 11:29:59 am »
The MAX31865 (and its related chips for thermocouples) only works SPI modes 1 and 3.  I documented that fact experimentally with the MAX31856.

From MAX31865 datasheet page 16 (attachment):
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #36 on: November 21, 2022, 12:07:52 pm »
Damn, I got the spi modes backwards, you're right  :-X
So yeah:
CKE=0,
CKP=0 (Mode 1) or CKP=1(Mode 3)
« Last Edit: November 21, 2022, 12:10:02 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3472
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #37 on: November 21, 2022, 12:20:20 pm »
Remember (post #8) that when CKE=0, the errata for the PIC16F1946 BF comes into play.  That errata does not apply to the 16F18xx or 16F17xx chips so far as I know.  That is based on my experience with a few examples in those latter series. I have not checked every chip in those latter series, however.
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #38 on: November 24, 2022, 04:19:07 am »
Good news, I believe the SPI is somewhat working now.

One mistake I found was that I kept trying to write to the config read register (0x00) instead of the actual write address (0x80). Doh!

Another thing I realized is that I need to have 24 clock cycles to do a complete read of the RTD registers; 8 to write the register address, and 16 for the full 16 bits of data in the MSB temperature measurement register.

Here is the code that works as far as setting the config and doing a 1 shot measurement by setting and clearing the 1-shot bit (D5):

Code: [Select]
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include "configs.h"
#define _XTAL_FREQ 1000000

void spi_init(){
    SSP1CON1 = (0<<5); //clear bit 5, disable serial port
   
    TRISC = (0<<3) | (1<<4) | (0<<5) | (0<<6)| (0<<6); //clear RC3, RC5, RC6 (output: SCK1, SDO, CS) and set RC4, RC7 (input: SDI, MAX RDY)
    SSP1CON1 = (1<<4) | (0<<3) | (0<<2) | (0<<1) | (0<<0); // 4: (CKP), 3-0:(SSPM = 0000; Master mode , Fosc/4)
    SSP1STAT = (0<<7) | (0<<6); //7 (SPI Master mode), 0 = Input data sampled at middle of data output time, 6: (CKE) transmit on clock transition to active
    SSP1ADD = 0;
   
    SSP1CON1bits.SSPEN = 1; //set bit 5, enable serial port
   
}

uint8_t max31865_interface_spi_readwrite(uint8_t reg, uint8_t data)
{
    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = reg;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = data;           // Send dummy byte / data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = data;           // Send dummy byte / data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
    return SSP1BUF;           // Return received data
}

void main(void) {
    spi_init();
    OSCCONbits.IRCF = 0x0B; //set internal osc to 1mhz
    OSCCONbits.SCS = 0x00; //system clock setup
   
    max31865_interface_spi_readwrite(0x80,0xA0); //config register write addr: 0x80. set config register bits 1000000 = 0x80
    __delay_ms(100);
   
    while (1){
        max31865_interface_spi_readwrite(0x80,0xA8); //set 1-shot bit to trigger conversion (D5)
        max31865_interface_spi_readwrite(0x01,0x00); //read RTD MSBs
//        max31865_interface_spi_readwrite(0x02,0x00); //read RTD LSBs
        max31865_interface_spi_readwrite(0x80,0xA0); //clear 1-shot bit (D5)
//        max31865_interface_spi_readwrite(0x03,0x00); //read high fault threshold status
//        max31865_interface_spi_readwrite(0x04,0x00); //read low fault threshold status
//        max31865_interface_spi_readwrite(0x05,0x00); //read low fault threshold MSB
//        max31865_interface_spi_readwrite(0x06,0x00); //read low fault threshold LSB
//        max31865_interface_spi_readwrite(0x07,0x00); //read fault status
        __delay_ms(1000);
    }
}



I have serial decode set up on the scope so you can clearly see what data is being transacted between the PIC and the MAX.

Now one challenge I am having to read the data is to create a buffer for SSP1BUF. Since I need to read all 16 bits, and SSP1BUF is only 8 bits and cleared in the middle of the read, I need to capture all 16 bits of the two SSP1BUF reads into a single variable as the code executes..
« Last Edit: November 24, 2022, 04:31:37 am by nexus »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #39 on: November 24, 2022, 04:54:01 am »
Why sending/receiving 2 data bytes?
Code: [Select]
uint8_t max31865_interface_spi_readwrite(uint8_t reg, uint8_t data)
{
    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = reg;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = data;           // Send dummy byte / data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = data;           // Send dummy byte / data  <<--------- ?????
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
    return SSP1BUF;           // Return received data
}

Just make two readings and pack the data into a 16-bit variable:
Code: [Select]
uint8_t max31865_interface_spi_readwrite(uint8_t reg, uint8_t data)
{
    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay
    SSP1BUF = reg;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    SSP1BUF = data;           // Send dummy byte / data
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    LATCbits.LATC6 = 1;       // CS high
    return SSP1BUF;           // Return received data
}

uint16_t read_rtd(void)
{
    uint16_t rtd = max31865_interface_spi_readwrite(0x01,0x00); //read RTD MSBs
    rtd = rtd<<8 | max31865_interface_spi_readwrite(0x02,0x00); //read RTD LSBs
    return rtd;
}
« Last Edit: November 24, 2022, 05:07:45 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #40 on: November 24, 2022, 04:28:31 pm »
Another thing I realized is that I need to have 24 clock cycles to do a complete read of the RTD registers; 8 to write the register address, and 16 for the full 16 bits of data in the MSB temperature measurement register.

Not necessarily, you can read each half separately.

But you certainly can read two bytes at once, a la:

Code: [Select]
uint16_t max31865_interface_spi_read16(uint8_t reg)
{
    uint8_t temp;

    LATCbits.LATC6 = 0;       // CS low
    NOP();                    // Small delay

    SSP1BUF = reg;           // Send address
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;

    SSP1BUF = 0;           // Send dummy byte
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;
    temp = SSP1BUF;

    SSP1BUF = 0;           // Send dummy byte
    while(!PIR1bits.SSP1IF);  // Wait
    PIR1bits.SSP1IF = 0;

    LATCbits.LATC6 = 1;       // CS high

    return (SSP1BUF << 8) | temp;           // Return received data
}
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #41 on: November 27, 2022, 05:19:49 am »
Finally, I think I've reached the finish line on this project. Thank you for all the tips!

The SPI was the biggest software hurdle for me since I had never used the hardware peripheral on a PIC, never used the MAX 31865 before, and in general am still a beginner.

The program is right up against the memory limit for the PIC16F1946 when compiled using XC8 free v2.40. Attached main.c. It's not the prettiest code (I'm still a mechanical engineer on paper), but it works.

Code: [Select]
Memory Summary:
    Program space        used  1FC4h (  8132) of  2000h words   ( 99.3%)
    Data space           used   1D3h (   467) of   200h bytes   ( 91.2%)
    EEPROM space         used     0h (     0) of   100h bytes   (  0.0%)
    Configuration bits   used     2h (     2) of     2h words   (100.0%)
    ID Location space    used     4h (     4) of     4h bytes   (100.0%)

I would have used the 16F1947 with twice the internal storage and RAM but none were in stock on digikey and mouser. I could always upgrade with a simple swap as I believe they are identical outside of the memory size difference.

I will do a proper writeup on my project site https://neonkev.com/.

The metalwork was also completely custom. I designed the box in CAD and had it made by PCBway along with the boards. 316 stainless sheet with press fit PEM inserts. All M3 fasteners.

For now, a quick demo of what this whole thing was about: a power supply for the Vision Engineering C073 14 point LED illuminator. It drives two separate strings of 7 high power LEDs, the 12V cooling fan, and reads the internal PT100 RTD in the LED heatsink to monitor temp. Brightness is controlled by PWM through the PIC and the rotary encoder. Push button for the encoder turns the main power on and off via relay to the LED driver ICs (MPM6010).

I upgraded the fans in the light head and power supply to PWM controlled ones as well.
They are autonomous by use of independent temperature controlled drivers: https://www.ebay.com/itm/284526780525

LEDs on the original C073 ring light were also upgraded to the latest Luxeon star rebel LEDs, 235lm @ 700mA: https://www.luxeonstar.com/cool-white-5650K-sinkpad-ii-20mm-star-led-module-235lm

Quick demo vid:
« Last Edit: November 27, 2022, 05:34:58 am by nexus »
 
The following users thanked this post: DavidAlfa

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #42 on: November 27, 2022, 05:50:13 am »
Ensure to enable optimizations, O2 is free and will reduce the memory usage a lot.

So you're "only” a mechanical enginneer but did that? That's pretty good!
« Last Edit: November 27, 2022, 05:53:00 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: nexus

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #43 on: November 27, 2022, 06:08:20 am »
Ensure to enable optimizations, O2 is free and will reduce the memory usage a lot.

Appreciate this tip! I had no idea that there were different levels of compiler optimization with the free version of XC8.

I see that there are different options that have different tradeoffs in size vs speed.
Can optimization actually increase speed/efficiency of code? Also, is it possible to "break" code with optimization that might otherwise work without it?

Thank you! Yup, I am doing my masters in electrical now, but practically all of my electrical/computer science knowledge is self taught.
It all started with nixie tubes; from there I dabbled with embedded design, power electronics, and down the rabbit hole I went...
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5898
  • Country: es
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #44 on: November 27, 2022, 07:08:25 am »
It could break some things if not properly programmed, like interrupts writing variables not declared as volatile or for/while loops doing nothing aka simple delays (Inserting a nop in the loop would fix it, but also need to calibrate the delay).
Overall optimizations are great, both for speed and memory usage.
Try it, if you have any issues, I could review your code searching for non optimization-friendly stuff, pics are really simple and It would be easy to spot.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #45 on: November 27, 2022, 07:32:00 am »
Understood. I did select optimization level 3, which is described in MPLAB as "Optimize yet more favoring speed (superset of O2)." This one appealed to me the most especially since I have a big timer interrupt that needs to run quickly to handle the rotary encoder and display.

Code: [Select]
Memory Summary:
    Program space        used  1D7Ch (  7548) of  2000h words   ( 92.1%)
    Data space           used   1D3h (   467) of   200h bytes   ( 91.2%)
    EEPROM space         used     0h (     0) of   100h bytes   (  0.0%)
    Configuration bits   used     2h (     2) of     2h words   (100.0%)
    ID Location space    used     4h (     4) of     4h bytes   (100.0%)

Shrunk the program and data space by around 8% and seems to run on the hardware just fine.

Think I might have put more effort into this unit than the OEM did in their production power supply.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #46 on: November 27, 2022, 04:31:17 pm »
Finally, I think I've reached the finish line on this project.

Congratulations! Great project!
 
The following users thanked this post: nexus

Offline nexusTopic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #47 on: November 27, 2022, 06:03:29 pm »
Thank you!

It will be open source soon, so it could be a useful point of reference for a generic LED driver module for others.
 

Offline Proto

  • Contributor
  • Posts: 33
  • Country: us
Re: PIC16F1946 - Hardware SPI timing issues?
« Reply #48 on: November 29, 2022, 10:11:43 pm »
I did not see anyone mention Microchip Code Configurator (MCC). I started a PIC project a few years ago after a long hiatus and grumbled that Microchip did not appear to have many code examples compared to the TI MSP430 I had used more recently. This all changed when I fired up MCC. The PIC16F15324 part I used allows on chip peripherals to be mapped to almost any port pin through a well-designed GUI. The GUI extends to picking peripherals and lets you set them up without necessarily looking at the datasheet.  MCC also reminds you to set up dependent peripherals like the TIMER1 for the Compar/Cpture subsystem. Now you press generate and you get initialization, general usage API's and interrupt functions created for you in C code, all neatly organized by peripheral name with source and hex file extensions. Its like having another programmer on your team writing your driver API's for you. Then you can modify them for your task while you also expand main.c for program flow. Is anybody using MCC?  A mix of code examples can have conflicting setup requirements when put together while MCC does a very good job of keeping all the peripherals happy.
« Last Edit: November 30, 2022, 04:24:26 am by Proto »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf