DsPIC33 SPI Tx as Master Woes

picspi

I'm using a dsPIC33 and want to just send basic SPI data out. No Rx, only Tx for now to control a digipot. Hardware does include slave at other end, and I'm sniffing the output with a logic analyzer. I've just seen literally 10different deprecated ways of doing it, but none of them seem to be working.

Problem: Random data coming out. Every once in a while it makes sense like a 3 or 7, but otherwise garbage.

I'm also not sure if it's OK that the MOSI line does not always return to a default high or low. It usually starts high, writes data, ends low, then ends high on the next round which seems odd.

Other: Short wires, no (potentially damaged) IC at other end, 10k pull-up resistor on chip-select (CS) line, slowest possible baud rate (default spi prescaler and postscaler are largest), tried 8 and 16-bit modes, tried all settings on logic analyzer (MSb first and LSb first, etc), RPn pins can't also be analog by default, RPn pins are not RPn input,…

Is it not just this simple? What might I be missing?

uint16_t main(void) {
  UC_ConfigureOscillator();
  TMRS_Init();
  SPI_Init(38, 39);

  // prime any timers that require it
  TMRS_Start(TX_TIMER, TX_PERIOD);
  volatile uint16_t val = 0;

  // watch the data being transmitted with a logic analyzer
  while (1) {
    // periodically transmit a message
    if (TMRS_Expired(TX_TIMER)) {
      TMRS_Start(TX_TIMER, TX_PERIOD);
      SPI_SendWord(val);
      if (++val > 10) val = 0;
      }
   }

   return 0;
}

void SPI_Init(uint8_t sdo_pin, uint8_t sck_pin) {
  // Configure and initialize the chip-select pin
  _TRISB8 = 0;
  _LATB8 = 1;

  // Configure the SPI clock and MOSI pins
  __builtin_write_OSCCONL(OSCCON & 0xBF);
  _RP39R = 0b001001;
  _RP38R = 0b001000;

  // Configure the interrupt
  _SPI2IE = 0;              // Disable the interrupt
  _SPI2IF = 0;              // Clear the interrupt flag

  _SPI2IE = 1;              // Enable interrupts for this spi module
  _SPI2IP = 2;              // Assign the interrupt priority

  SPI2CON1bits.MODE16 = 1;  // Use in 16-bit mode
  SPI2CON1bits.MSTEN = 1;   // Configure this device as master
  SPI2STATbits.SPIROV = 0;  // Ensure the receive overflow flag begins cleared
  SPI2STATbits.SPIEN = 1;   // Enable the SPI module
  SPI2CON1bits.SMP = 1;     // Sample input data at END of data output time
}

void SPI_SendWord(uint16_t word) {
  uint16_t garbage;

  // lower CS
  _LATB8 = 0;

  // write the given word to the Tx/Rx buffer
  SPI2BUF = word;

  // wait for the transmit buffer to clear
  while (SPI2STATbits.SPITBF == 0);
  // wait for the transmit to begin
  while (SPI2STATbits.SPITBF == 1);

  // Dummy-read the SPIxBUF register to clear flag???
  garbage = SPI2BUF;
}

// Called on 16bits finished shifting in OR out of SPI2 buffer
void __attribute__((__interrupt__, auto_psv)) _SPI2Interrupt(void) {
  // Return CS high
  _LATB8 = 1;

  // Clear the source of the interrupt
  _SPI2IF = 0;
}

Best Answer

Wow, new tool was not configured correctly. I love these guys http://www.saleae.com/logic/, but didn't realize my settings should have been "Data is valid on clock trailing edge" and not clock leading edge. Settings saved now.