SPI config - Config for SCK incorrect - PIC24F - Page 1
Forum section: electronics / general

author: aurmer
author_rank: Regular Contributor
timestamp: October 17, 2016, 04:42:48 pm
content: Context:SPI MasterPIC24FJ128GA204SPI SlaveAT25SF041I have been struggling. Many examples and tutorials use Microchip libraries, which I am not using. I am trying to learn how to operate the module. Learn all its features and configurations. I have almost got it, but how do I get the SCK pin to keep pulsing while I wait for a response from the Slave. As of now, it only pulses because the module is actively transmitting. Then it stops pulsing, which stops the Slave's ability to respond.Code:// PIC24FJ128GA204 Configuration Bit Settings// 'C' source line config statements// CONFIG4#pragma config DSWDTPS = DSWDTPS1F // Deep Sleep Watchdog Timer Postscale Select bits (1:68719476736 (25.7 Days))#pragma config DSWDTOSC = LPRC // DSWDT Reference Clock Select (DSWDT uses LPRC as reference clock)#pragma config DSBOREN = OFF // Deep Sleep BOR Enable bit (DSBOR Disabled)#pragma config DSWDTEN = OFF // Deep Sleep Watchdog Timer Enable (DSWDT Disabled)#pragma config DSSWEN = OFF // DSEN Bit Enable (Deep Sleep operation is always disabled)#pragma config PLLDIV = PLL8X // USB 96 MHz PLL Prescaler Select bits (8x PLL selected)#pragma config I2C1SEL = DISABLE // Alternate I2C1 enable bit (I2C1 uses SCL1 and SDA1 pins)#pragma config IOL1WAY = OFF // PPS IOLOCK Set Only Once Enable bit (The IOLOCK bit can be set and cleared using the unlock sequence)// CONFIG3#pragma config WPFP = WPFP127 // Write Protection Flash Page Segment Boundary (Page 127 (0x1FC00))#pragma config SOSCSEL = OFF // SOSC Selection bits (Digital (SCLKI) mode)#pragma config WDTWIN = PS25_0 // Window Mode Watchdog Timer Window Width Select (Watch Dog Timer Window Width is 25 percent)#pragma config PLLSS = PLL_FRC // PLL Secondary Selection Configuration bit (PLL is fed by the on-chip Fast RC (FRC) oscillator)#pragma config BOREN = OFF // Brown-out Reset Enable (Brown-out Reset Disabled)#pragma config WPDIS = WPDIS // Segment Write Protection Disable (Disabled)#pragma config WPCFG = WPCFGDIS // Write Protect Configuration Page Select (Disabled)#pragma config WPEND = WPENDMEM // Segment Write Protection End Page Select (Write Protect from WPFP to the last page of memory)// CONFIG2#pragma config POSCMD = NONE // Primary Oscillator Select (Primary Oscillator Disabled)#pragma config WDTCLK = LPRC // WDT Clock Source Select bits (WDT uses LPRC)#pragma config OSCIOFCN = ON // OSCO Pin Configuration (OSCO/CLKO/RA3 functions as port I/O (RA3))#pragma config FCKSM = CSDCMD // Clock Switching and Fail-Safe Clock Monitor Configuration bits (Clock switching and Fail-Safe Clock Monitor are disabled)#pragma config FNOSC = FRCPLL // Initial Oscillator Select (Fast RC Oscillator with PLL module (FRCPLL))#pragma config ALTCMPI = CxINC_RX // Alternate Comparator Input bit (C1INC, C2INC and C3INC are on RB9)#pragma config WDTCMX = WDTCLK // WDT Clock Source Select bits (WDT clock source is determined by the WDTCLK Configuration bits)#pragma config IESO = ON // Internal External Switchover (Enabled)// CONFIG1#pragma config WDTPS = PS32768 // Watchdog Timer Postscaler Select (1:32,768)#pragma config FWPSA = PR128 // WDT Prescaler Ratio Select (1:128)#pragma config WINDIS = OFF // Windowed WDT Disable (Standard Watchdog Timer)#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT disabled in hardware; SWDTEN bit disabled)#pragma config ICS = PGx1 // Emulator Pin Placement Select bits (Emulator functions are shared with PGEC1/PGED1)#pragma config LPCFG = OFF // Low power regulator control (Disabled - regardless of RETEN)#pragma config GWRP = OFF // General Segment Write Protect (Write to program memory allowed)#pragma config GCP = OFF // General Segment Code Protect (Code protection is disabled)#pragma config JTAGEN = OFF // JTAG Port Enable (Disabled)#include <xc.h> // include processor files - each processor file is guarded.int spi_send_read();int main(void) {//Make sure pins are set correctly so that PPS worksANSA = 0x0000;ANSB = 0x0000;ANSC = 0x0000;TRISA = 0xFFFF;TRISB = 0xF87F;TRISC = 0xFFFF;//These two pins connect to the Flash IC "/HOLD" and "/Write Protect".//Not using these features.LATB = 0x0600;//Outs to output, ins to inputTRISC = 0xFE3F;LATC = 0x0100;//***************************************************************************************//SPI ?//***************************************************************************************SPI1CON1Lbits.SPIEN = 0; //Module off for configRPOR11bits.RP22R = 8; //SCK1 outRPOR11bits.RP23R = 7; //SDO1RPOR12bits.RP24R = 9; //SS1 outRPINR20bits.SDI1R = 25; //SDI1//Clear Interrupt Flags_SPI1RXIF = 0;_SPI1TXIF = 0;_SPI1IF = 0;//Top priority interrupt_SPI1RXIP = 0b111;_SPI1TXIP = 0b111;_SPI1IP = 0b111;//Disable Interrupts (for now)_SPI1RXIE = 0;_SPI1TXIE = 0;_SPI1IE = 0;SPI1CON1L = 0; //Start with all 0SPI1CON1Lbits.MSTEN = 1; //Master modeSPI1CON1Lbits.CKE = 1; //Aligns middle of MOSI with the slave read timingSPI1CON1Lbits.MODE = 0x00; //8b serial word lengthSPI1CON1H = 0; //Start with all 0SPI1CON1Hbits.FRMEN = 0; //Framed SPI disabledSPI1CON1Hbits.MSSEN = 0; //this is it TODOSPI1CON2Lbits.WLENGTH = 0b00111; //8-bit wordsSPI1IMSKL = 0;SPI1IMSKH = 0;SPI1BRGL = 31; //Input Clock Divider. Sets speed of comm.SPI1STATLbits.SPIROV = 0; //clear Receive OverflowSPI1CON1Lbits.SPIEN = 1; //Turn on.spi_send_read();while(1){}return 0;}int spi_send_read(){int temp = SPI1BUFL; //Just in case the SPI thinks it has data in the buffer.LATC &= 0xFEFF; //Select chip, pulling it out of idleSPI1BUFL = 0x9F; //This OP code => the slave responds with 3Bytes 0x1F8401//I know that I am not reading the response here. I'll worry about coding//the read once the scope shows me that my config is right.while(1);}WHAT MY SCOPE SHOWS: (see first attachment)Yup, looks like I am sending the command byte fine, but then, the SCK stops and the slave cannot respond.WHAT IT SHOULD SHOW: (second attachment)

author: hans
author_rank: Super Contributor
timestamp: October 17, 2016, 05:14:00 pm
content: The SPI peripheral is a dumb peripheral. It's just a shift register with clock and data in 2 directions.You should send the opcode 0x9F (8clks) and then generate the clocks for the slave to send it's data. This doesn't happen by magic. Often you just send a couple of 0x00's and then read the content in the SPI BUF register. A most generic spi transfer implemention actually something like:uint8_t SpiTxRx(uint8_t tx_data){if (check for overrun etc. in SPI1STAT){// handle error; reset SPI, etc.}SPI1BUF = tx_data;while(transfer not completed) // (sorry don't know that register on top of my head)return SPI1BUF;}I use such a function for all tx and rx bytes in my driver code.For reading you just call this function and send 0x00, and process the result.For writing you just send the variable and don't care about the return. Don't omit the return to think it's faster.. every SPI transfer should read the SPI1BUF in order to clear data. Otherwise the SPI peripheral will generate an overrun flag and seize operatoin.

author: aurmer
author_rank: Regular Contributor
timestamp: October 17, 2016, 05:40:31 pm
content: So I plan to execute a continuous read from the flash chip. The flash datasheet just says all I have to do is1. SS -> select chip2. Send appropriate read command with starting address3. Just keep the SS asserted and the SCK pulse running and it will send me byte by byte forever (looping from END_ADDRESS to START_ADDRESS when it gets to the end)I would think there should be the option to tying the SCK pin activation to whenever the SS is asserted, not just when it is transmitting (transmitting dummy bytes to keep the clock running seems like a hack).So in this scenario, what would you do? Just use one of the Timer modules as the SCK?

author: bktemp
author_rank: Super Contributor
timestamp: October 17, 2016, 05:47:58 pm
content: Quote from: zetharx on October 17, 2016, 05:40:31 pmNo, you do it the way hans described:Send 0x00 (or 0xFF, it doesn't care). This will generate the required SCK pulses for reading the data.

author: aurmer
author_rank: Regular Contributor
timestamp: October 17, 2016, 06:33:53 pm
content: Well that was certainly a shift in my paradigm. It works, and I see now how nice this code looks, and how well I can control it.The first call is to send.The next three are to receive the expected 3 bytes.I can build a function for each Operation Code I need from the Slave's OPcode-set.Code:LATC &= 0xFEFF; //SelectSpiTxRx(0x9F);received = SpiTxRx(0x00); //3 Bytes expected in response, so 3 calls to the TxRx funcreceived = SpiTxRx(0x00);received = SpiTxRx(0x00);LATC |= 0x0100; //DeselectResult as seen in attachment