Trouble getting SPI working on dsPIC33EV256GM106 - Page 1
Forum section: electronics / general

author: tech_builder
author_rank: Contributor
timestamp: April 09, 2018, 11:26:14 pm
content: Hi,I am struggling to get the SPI module on a dsPIC33EV256GM106. It is the dsPIC33EV 5V CAN-LIN Starter Kit specifically that I am using. An overview of my task is to read via SPI an ADS1118 ADC and place the reading onto the CAN interface. The demo code that ships with the kit does a great job of interfacing with CAN (so far). However, I cannot seem to get the SPI module functioning. My test code is a pared down version of the demo code in which I inserted SPI functionality. I have tried a few different initialization procedures that were floating around the forums as well as using the MCC configuration tool and copying some of that code into mine.Below is some of the code. In the initSPI() function you will find my SPI initialization code. There lie the trials and tribulations of my efforts in the form of commented out code. The first code commented out is because initially I wanted to use SPI2 because SPI1 is fixed pin. However, I was unclear about the PPS functionality for this chip and didn't want to add to the burden of just getting the peripheral working (it seems from the demo code that there is no unlock procedure for PPS??). The uncommented code is example code from the datasheets, and the last bit of commented out code in initSPI() is code generated from the MCC tool.When I run this code in the debugger it appears as though the SPI registers are being set correctly, but when I run it there is no output on any of the output pins (I have a logic analyzer and scope on them). I have initialized the SPI pins as inputs and outputs as needed and turned off analog functionality. In my initADS1118() function I am writing to the SPI port continuously just to have something to see for functionality. Eventually this will develop into setting up the ADS1118 properly for use.Does anybody have any idea what I am doing incorrectly? Did I forget to dot an i? I did not think that setting up an SPI module would be so difficult, but it seems that this particular version of dsPIC has a lot of 'enhanced' functionality and I'm not too sure how to weed through what I need and don't need.Any help would be greatly appreciated.Thanks!In the below code I am setting defines for the pins being used for the SPI module.Code:/* for SPI2#define TRISSCLK _TRISB8#define ANSEL_SCLK _ANSB8#define TRISMISO _TRISB9#define ANSEL_MISO _ANSB9#define TRISMOSI _TRISB10#define TRISCS _TRISB11*/// for SPI1#define TRISSCLK _TRISC3#define ANSEL_SCLK _ANSC3#define TRISMISO _TRISA9#define ANSEL_MISO _ANSA9#define TRISMOSI _TRISA4#define TRISCS _TRISB0This is the main function, it just calls the initialization functions.Code:int main(void){// Configure Oscillator Clock SourceoscConfig();// Clear Interrupt FlagsclearIntrflags();// Initialize hardware on the boardinit_hw();// Test to see if we are in TRANSMIT or RECIEVE mode for the demo, show LEDsTest_Mode();if (mode == TRANSMIT){LED_Transmit();}else{LED_Receive();while ((SW1 == 0) | (SW2 == 0) | (SW3 == 0)); // wait to release all keysLED1 = 0; // initialize LEDs to all offLED2 = 0;LED3 = 0;}// Initialize the ADC converterADCInit();// Initialize the CAN moduleInitCAN();// initialize the SPI moduleinitSPI();// initialize the ADS1118 ADCinitADS1118();// main loop: every 4 Timer1 ticks (1sec), scan the sensors and transmit the data// or wait for a Receive interrupt from 1 of the 3 interfaces//s_tick = 0;while (1){if (mode == RECEIVE){/* check to see when a message is received and move the messageinto RAM and parse the message */if(canRxMessage.buffer_status == CAN_BUF_FULL){rxECAN(&canRxMessage);/* reset the flag when done */canRxMessage.buffer_status = CAN_BUF_EMPTY;}Receive_Data();s_tick = 0;}else if (mode == TRANSMIT){//// wait for the 250ms timer. User can accumulate s_ticks to get longer delays//while (s_tick <= 1); // wait for 0.5 secondss_tick = 0; // clear flagTransmit_Data();}}}Here is where I set the TRIS values for input/output status of the pins as well as turn off any analog functionality on the pins.Code:void init_hw(void){// set up the LED and switch portsTRISLED1 = 0;TRISLED2 = 0;TRISLED3 = 0;ANSEL_POT = 1;ANSEL_TEMP = 1;TRIS_POT = 1;TRIS_TEMP = 1;ANSELA = 0; // make port A all digital I/OANSELB = 0; // make port B all digital I/OANSELC = 0; // make port C all digital I/OTRISSCLK = 0; // make pin an outputANSEL_SCLK = 0; // make it a digital pinTRISMISO = 1; // make pin an inputANSEL_MISO = 0; // make it a digital pinTRISMOSI = 0; // make pin an outputTRISCS = 0; // make pin an outputANSELC = ANSELC & 0xFC3F; // (re)set the 3 switch bits + CAN due to error in v1.20 header_ANSC3 = 0;s_tick = 0;f_tick = 0; // the timer ticks//// Timer 1 to generate an interrupt every 250ms//T1CONbits.TON = 0; // Disable Timer1T1CONbits.TCS = 0; // Select internal instruction cycle clockT1CONbits.TGATE = 0; // Disable Gated Timer modeT1CONbits.TCKPS = 0x3; // Select 1:256 PrescalerPR1 = 39062; // Load the period value (250ms/(256*25ns))IPC0bits.T1IP = 0x03; // Set Timer 1 Interrupt Priority LevelIFS0bits.T1IF = 0; // Clear Timer 1 Interrupt FlagIEC0bits.T1IE = 1; // Enable Timer1 interrupt//// Timer 2 to generate an interrupt every 10ms//T2CONbits.TON = 0; // Disable Timer2T2CONbits.TCS = 0; // Select internal instruction cycle clockT2CONbits.TGATE = 0; // Disable Gated Timer modeT2CONbits.TCKPS = 0x3; // Select 1:256 PrescalerTMR2 = 0x00; // Clear timer registerPR2 = 1562; // Load the period value (10ms/(256*25ns))IPC1bits.T2IP = 0x02; // Set Timer 2 Interrupt Priority LevelIFS0bits.T2IF = 0; // Clear Timer 2 Interrupt FlagIEC0bits.T2IE = 1; // Enable Timer2 interruptT2CONbits.TON = 1; // Start Timer2T1CONbits.TON = 1; // Start Timer1}This is where the SPI port is initialized.Code:void initSPI(void){// Peripheral pin select for the SPI2 module// see section 11 in datasheet on I/O config//RPOR3 = 0x09; // connect SCK2 to RP40//RPOR4 = 0x0A08; // connect SDO2 to RP42 and CS to RP43//RPINR22 = 0x29; // connect SDI to RP41/* The following code sequence shows SPI register configuration for Master mode */IFS0bits.SPI1IF = 0; // Clear the Interrupt flagIEC0bits.SPI1IE = 0; // Disable the interrupt// SPI1CON1 Register SettingsSPI1CON1bits.DISSCK = 0; // Internal serial clock is enabledSPI1CON1bits.DISSDO = 0; // SDOx pin is controlled by the moduleSPI1CON1bits.MODE16 = 0; // Communication is word-wide (16 bits)SPI1CON1bits.MSTEN = 1; // Master mode enabledSPI1CON1bits.SMP = 0; // Input data is sampled at the middle of data output timeSPI1CON1bits.CKE = 0; // Serial output data changes on transition from// Idle clock state to active clock stateSPI1CON1bits.CKP = 0; // Idle state for clock is a low level;// active state is a high levelSPI1STATbits.SPIEN = 1; // Enable SPI module// Interrupt Controller SettingsIFS0bits.SPI1IF = 0; // Clear the Interrupt flag//IEC0bits.SPI1IE = 1; // Enable the interrupt// MSTEN Master; DISSDO disabled; PPRE 64:1; SPRE 8:1; MODE16 disabled; SMP Middle; DISSCK disabled; CKP Idle:Low, Active:High; CKE Idle to Active; SSEN disabled;//SPI1CON1 = 0x20;// SPIFSD disabled; SPIBEN disabled; FRMPOL disabled; FRMDLY disabled; FRMEN disabled;//SPI1CON2 = 0x0;// SISEL SPI_INT_SPIRBF; SPIROV disabled; SPIEN enabled; SPISIDL disabled;//SPI1STAT = 0x8000;}This is where the magic isn't happening. I feel like this should be straight forward, and several example pieces of code are similar to this that seem to work for other people. What might I be missing here? Or is the problem in the configuration?Code:void initADS1118(void){while(1){while( SPI1STATbits.SPITBF);SPI1BUF = 0x83;while(!SPI1STATbits.SPIRBF);}//SPI2_Exchange8bit(0x83);//SPI2_Exchange8bit(0x8B);}

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 11, 2018, 12:30:51 am
content: Apparently, this part supports PPS (peripheral pin select). But I see no initialization of PPS for your SPI pins.There's an attempt in your initSPI() function, but it's commented out.If I remember well, I/O pins are simple digital I/O's by default, so the right PPS settings are needed for peripheral functions and are not optional if I'm not mistaken.

author: JPortici
author_rank: Super Contributor
timestamp: April 11, 2018, 05:41:00 am
content: Correct, that is exactly what paragraph 11.5.2 of datasheet says: in dsPICs there is no default pin assignment.note that for the clock, as it can be both an input and an output, you have to assign it both on _SCK2R (input) and _RPxR (output)this is one of the parts i use the most, if i have time later i will post the configuration code.I did have weird issues when i first used it with DMA (unexpected waveforms, like seeing a long capacitor discharge at times, as if it changed the output line to high impedance at times..) so in the end i used the enhanced mode, 8 buffers. as it was to read/write a memory, i didn't require particular high speed. firing the interrupt every 8 bytes was acceptable, then i never changed the code because it worked well..same with DACs, required samplerates were never high so i could use interruptsEDIT:SPI1 is'nt PPS-able, in that case this code works for meCode:/* SPI */_TRISA4 = 0; //SDO1_ANSA4 = 0;_TRISA9 = 1; //SDI1_ANSA9 = 0;_TRISC3 = 0; //SCK1_ANSC3 = 0;_CS1 = 1;_TRISB4 = 0; //CSCode:void SPI1_Init() {SPI1STAT = 0;SPI1CON1bits.DISSCK = 0; //Internal SPI Clock EnabledSPI1CON1bits.DISSDO = 0; //SDO EnabledSPI1CON1bits.SSEN = 0; //_SS is not usedSPI1CON1bits.MSTEN = 1; //Master ModeSPI1CON1bits.MODE16 = 0; //8 bit transferSPI1CON1bits.SMP = 0; //Data Sampled at middle of timeSPI1CON1bits.CKE = 1; //Mode 0SPI1CON1bits.CKP = 0;SPI1CON1bits.SPRE = 4; //1:4SPI1CON1bits.PPRE = 1; //1:16 -> 500 kHz SPI clockSPI1CON2 = 1; //Disable Framed mode, Enable enhanced modeSPI1STATbits.SPIEN = 1;_SPI1IF = 0;}same should work with SPI2 if you assign the pins (SDI and SCK PPS input, SDO and SCK PPS output)other notes:-it's not uncommon to have issues or erratas on the SPI regarding the status bits, that's why i tend to use interrupts or clear then check the interrupt flag anyway.-check the errata, but the remaining SPI issues should not affect you-clock prescalers can't be both set at 1:1 at the same time, in that case the peripheral won't work.-after a transaction you must read back the SPIxBUF register, otherwise it will stay in the FIFO buffer and after one or more transactions (depending on enchanced mode status) it will overflow and the peripheral will stop working. relevant status bit should be set.-this is my goto function for spiCode:uint16_t SPI1_Send(uint16_t data) {/* you may or may not want to change the size of data and return depending on the data width and MCU */_SPI1IF = 0;SPI1BUF = data;while(!_SPI1IF);return SPI1BUF;}or, when the status bits do workCode:uint16_t SPI1_Send(uint16_t data) {while(!SPI1STATbits.SRMPT); //Wait for a possible previous transaction to endSPI1BUF = data;while(!SPI1STATbits.SRMPT);return SPI1BUF;}

author: tech_builder
author_rank: Contributor
timestamp: April 14, 2018, 06:42:43 am
content: Thanks so much for the replys. I will take a look and hopefully it will work.One of the things that confused me about the demo code that goes with the dsPIC33EV 5V CAN-LIN Starter Kit was that the PPS for the CAN peripheral didn't use the unlock/lock sequence, and because I couldn't figure out why I chose to use SPI1 on the fixed pins. Do you have any idea how they are getting away without using the unlock/lock sequence? The code below is the relevant part of the CAN initialization function.Code://// drive the CAN STANDBY driver pin low//_TRISG9 = 0;_LATG9 = 0;_TRISF1 = 0;_TRISF0 = 1;//// remap the CAN module to the proper pins on the board//RPINR26 = 0x60; // connect CAN RX to RPI96RPOR9 = 0x000E; // connect CAN TX to RP97C1CTRL1bits.REQOP = 4;while (C1CTRL1bits.OPMODE != 4);C1CTRL1bits.WIN = 0;

author: JPortici
author_rank: Super Contributor
timestamp: April 14, 2018, 07:25:16 am
content: No need to unlock because PPS is unlocked at reset.After the configuration, if you want, you may lock the PPS and then if the configuration bit lets you, you can unlock the PPS at any moment

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 14, 2018, 02:13:18 pm
content: Quote from: JPortici on April 14, 2018, 07:25:16 amIt's recommended to lock the configuration after you set it to make sure there can't be any unwanted change due to software bugs - which could be potentially dangerous since a bad pin config could fry your µC.If you develop in C, use __builtin_write_OSCCONL() and __builtin_write_OSCCONH() to lock/unlock to make sure the proper sequences are generated.(See section 10.4.4.1)