This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Tool/software: Code Composer Studio
Hi all,
I am fairly new to embedded development and am working on acquiring the data from an 18-bit ADC over SPI. I understand that the C2000's architecture is designed to handle 16-bit data. In fact, I was able to acquire the 16-bit SPI data using a 16-bit ADC I had haandy. But for this application I'm working on, I definitely need 18 bit ADC's and need to read this 18-bit adc data over SPI. How can I go about doing it?
The following is the initialization code I have set up for simple 16-bit transfers. How do I now tell the processor to handle 18-bit transfers?
#include "Lab.h" // Main include file #include "F2837xD_device.h" //#define SPI_INT_ENABLE 1 //Comment to disable SPI interrupts //Data rate selection #define DataRate_1_7_MBPS 1 //14.28 MHz Serial Clock //#define DataRate_2_0_MBPS 1 //16.66 MHz Serial Clock //#define DataRate_2_5_MBPS 1 //20 MHz Serial Clock void InitSPI(void) { SpiaRegs.SPICCR.bit.SPISWRESET = 0; //Reset the SPI peripheral SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1; //Configure C2000 as an SPI master //SpiaRegs.SPICCR.bit.SPILBK = 1; //Disable Loopback //SpiaRegs.SPICTL.bit.TALK = 1; //Referring to LT2338 Clock Polarity is 0: ie, clock signal is zero when no transfers are taking place //The clock phase refers to which clock edge the data is captured on and which clock edge does the data change. //Refer to your microcontroller’s data sheet for the bit value for the clock phase. //According to this and referring to the data transfer waveform diagram provided in the datasheet //of LT2338 ADC IC it is found that the data has to be stable at the falling edge and can change at the // rising edge. //Referring to Table 18-3 on Pg 2060 of the technical refernce manual of F28379D, the appropriate settings is //Rising edge without delay //i.e. CLKPOLARITY 0 and CLK_PHASE 0 SpiaRegs.SPICCR.bit.CLKPOLARITY = 0; //Based on the previous explanation SpiaRegs.SPICTL.bit.CLK_PHASE = 0; //Based on the previous explanation //Baud rate settings //Core clock at 200MHz //LSPCLK @100MHz EALLOW; ClkCfgRegs.LOSPCP.bit.LSPCLKDIV = 1; //LSPCLK = / 2 (default on reset) EDIS; #if DataRate_2_5_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x04; //SPI Baud Rate = LSPCLK/7 (20 MHz) #endif #if DataRate_2_0_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x05; //SPI Baud Rate = LSPCLK/7 (16.66 MHz) #endif #if DataRate_1_7_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x06; //SPI Baud Rate = LSPCLK/7 (14.28 MHz) #endif SpiaRegs.SPICCR.bit.SPICHAR = 0xF; //16-bit word //Clear the flags SpiaRegs.SPISTS.bit.OVERRUN_FLAG = 1; //clear the SPI Receiver Overrun Flag #if SPI_INT_ENABLE //Interrupt Settings SpiaRegs.SPICTL.bit.OVERRUNINTENA = 0x1; //Enable Receiver Overrun SpiaRegs.SPICTL.bit.SPIINTENA = 0x1; //Enable SPI Interrupt #endif //Reset Release SpiaRegs.SPICCR.bit.SPISWRESET = 1; //Release the SPI peripheral from RESET State } __interrupt void epwm1_isr(void) { static volatile Uint16 GPIO34_count = 0; // Counter for pin toggle PieCtrlRegs.PIEACK.all = PIEACK_GROUP3; // Must acknowledge the PIE group EPwm1Regs.ETCLR.bit.INT = 1; //clear the EPWM interrupt flag #if LTC1864 //Start a SPI data transmit //Pull the pin high; starts the conversion on the chip GpioDataRegs.GPASET.bit.GPIO19 = 1; //GPIO Set DelayUs(3); //wait for 2.75 us for conversion to take place GpioDataRegs.GPACLEAR.bit.GPIO19 = 1; //GPIO Clear //SPI communication begins SpiaRegs.SPITXBUF=0xAAAA; // Send data. This is a dummy byte //The above step automatically starts an SPI data transmit and a subsequent receive // WAIT FOR THE BYTE TO BE SENT AND THE OTHER BYTE TO BE RECEIVED AND PLACED IN THE RXBUF REGISTER while(SpiaRegs.SPISTS.bit.INT_FLAG == 0); if(SpiaRegs.SPISTS.bit.INT_FLAG == 1); { if(GPIO34_count++ > 25000) // Toggle slowly to see the LED blink { GpioDataRegs.GPBTOGGLE.bit.GPIO34 = 1; // Toggle the pin GPIO34_count = 0; // Reset the counter } rxdata1 = SpiaRegs.SPIRXBUF;//extarct the data (automatically clears the interrupt flag) } #endif }
Any help would be greatly appreciated!
Thank you.
Hi Kartikeya,
As you have already noticed, the SPI's maximum character length is 16-bits. However, you can easily generate two characters or more per chip-select cycle by using the CPU to drive the SPISTE pin (rather than have the SPI drive it low only for the duration of the character).
I think the answer really depends on what the ADC does if it receives more than 18 clocks from the master. If it can tolerate something like 32 clocks, you could, in your code, transfer two 16-bit values per sample. Instead of programming the SPI to drive the SPISTE pin, you could drive it directly with a GPIO such that SPISTE stays low for the entire 32 clocks.
Mr. Martinez, thank you for your quick reply.
The 16 bit ADC (LTC1864) I'm using explicitly mentions what happens when we apply further clock cycles beyond the 16 cycles necessary for a full transfer; it ignores the extra cycles. However, no such thing is mentioned in the datasheet of the 18 bit ADC (LTC2338). However looking at the LTC2338's timing diagram for the SPI sequence, it seems that it is a dont care too. I'd like to know your thoughts on that. I have attached the specific timing diagram with this message for your reference.
Thank you.
Hi Kartikeya,
Some corrections to my previous statement:
- The SPI supports a character size of 9-bits (set SPICHAR = 8h)
- If you enable FIFO mode, the SPISTE will stay low until there is no more data in the FIFO. See below post for reference.
https://e2e.ti.com/support/microcontrollers/c2000/f/171/p/688266/2536345
If you set SPICHAR = 8h, enable FIFO mode, and then load two 16-bit dummy values in the SPI TX FIFO, back-to-back, the SPI should generate a total of 18 clocks per SPISTE pulse. You will then receive two 9-bit characters in the SPI RX FIFO. You can then use the CPU to read & combine the two 9-bit values into one 18-bit value.
Mr. Martinez, thank you for the corrections that explaination was really helpful. I took some time to write this code following your suggestions.
So, I was able to alter the initilization sequence and modified the PWM ISR to do the tranceive and data processing steps.
The following is the SPI initilization that configures the peripheral in FIFO mode with RXFFIL bits set to trigger after 2 transmits. Is this correct?
void SPI_INIT(void) { //Setup SPI to do three 9-bit FIFO transfers so that it subsequently receives 9 bits over SPI receive //The transmits are dummy; the receives contain the data SpiaRegs.SPICCR.bit.SPISWRESET = 0; //Reset the SPI peripheral SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1; //Configure C2000 as an SPI master //The clock phase refers to which clock edge the data is captured on and which clock edge does the data change. //Refer to your microcontroller’s data sheet for the bit value for the clock phase. //According to this and referring to the data transfer waveform diagram provided in the datasheet //of LT2338 ADC IC it is found that the data has to be stable at the falling edge and can change at the // rising edge. //Referring to Table 18-3 on Pg 2060 of the technical refernce manual of F28379D, the appropriate settings is //Rising edge without delay //i.e. CLKPOLARITY 0 and CLK_PHASE 0 SpiaRegs.SPICCR.bit.CLKPOLARITY = 0; //Based on the previous explanation SpiaRegs.SPICTL.bit.CLK_PHASE = 0; //Based on the previous explanation //Baud rate settings //Core clock at 200MHz //LSPCLK @100MHz EALLOW; ClkCfgRegs.LOSPCP.bit.LSPCLKDIV = 1; //LSPCLK = / 2 (default on reset) EDIS; #if DataRate_2_5_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x04; //SPI Baud Rate = LSPCLK/7 (20 MHz) #endif #if DataRate_2_0_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x05; //SPI Baud Rate = LSPCLK/7 (16.66 MHz) #endif #if DataRate_1_7_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x06; //SPI Baud Rate = LSPCLK/7 (14.28 MHz) #endif SpiaRegs.SPICCR.bit.SPICHAR = 0xF; //16-bit word //Clear the flags SpiaRegs.SPISTS.bit.OVERRUN_FLAG = 1; //clear the SPI Receiver Overrun Flag #if SPI_INT_ENABLE //Interrupt Settings SpiaRegs.SPICTL.bit.OVERRUNINTENA = 0x1; //Enable Receiver Overrun SpiaRegs.SPICTL.bit.SPIINTENA = 0x1; //Enable SPI Interrupt #endif //SPI FIFO Settings SpiaRegs.SPIFFTX.all = 0xE000; //SPIFFTX = 1110 0000 0000 0000 //SPIFFTX register Settings //Bit6: TX FIFO Interrupt Enable; Disable 0 //Bit 7: TXFIFO Interrupt Clear; 1h (R/W) = Write 1 to clear SPIFFTX[TXFFINT] flag //Bit 13:TX FIFO Reset; Release transmit FIFO from reset //Bit 14: SPI FIFO Enhancements Enable; SPI FIFO enhancements are enabled. //Bit 15: SPI Reset ;SPI FIFO can resume transmit or receive. No effect to the SPI registers bits SpiaRegs.SPIFFRX.all = 0x6042; //SPIFFRX = 0110 0000 0100 0011 //SPIFFRX register Settings //Bit 0-4: RXFFIL: Trigger interrupt after two receives //Bit 6: Receive FIFO Interrupt Clear; Write 1 to clear SPIFFRX[RXFFINT] flag //Bit 13: Receive FIFO Reset; Re-enable receive FIFO operation. //Bit 14: , RXFFOVFCLR; reset the overflow flag SpiaRegs.SPIFFCT.all = 0x0; //SPIFFCT = 0000 0000 0000 0000 SpiaRegs.SPICCR.bit.SPICHAR = 0x8; //9-bit word //Reset Release SpiaRegs.SPICCR.bit.SPISWRESET = 1; //Release the SPI peripheral from RESET State }
The next phase is initiating the two FIFO SPI transmits and waiting for the interrupt flag to be raised when two receives are in the buffer. Is this correct?
__interrupt void epwm1_isr(void) { static volatile Uint16 GPIO34_count = 0; // Counter for pin toggle PieCtrlRegs.PIEACK.all = PIEACK_GROUP3; // Must acknowledge the PIE group EPwm1Regs.ETCLR.bit.INT = 1; //clear the EPWM interrupt flag //Start a SPI data transmit //Pull the pin high; starts the conversion on the chip GpioDataRegs.GPASET.bit.GPIO19 = 1; //GPIO Set DelayUs(1); //wait for 500 ns for conversion to take place GpioDataRegs.GPACLEAR.bit.GPIO19 = 1; //GPIO Clear //SPI communication begins SpiaRegs.SPITXBUF=0x00; // Send data. First dummy byte in FIFO SpiaRegs.SPITXBUF=0x00; // Send data. Second dummy byte in FIFO //The above step automatically starts an SPI data transmit and a subsequent receive // WAIT FOR THE BYTE TO BE SENT AND THE OTHER BYTE TO BE RECEIVED AND PLACED IN THE RXBUF REGISTER while(SpiaRegs.SPIFFRX.bit.RXFFST !=1); //Bit 13: Receive FIFO Reset; Re-enable receive FIFO operation. SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1; //Clear the interrupt flag //Read the RXFIFO for (int i=0;i<2;i++) { rxfifobuf[i] = SpiaRegs.SPIRXBUF; } SpiaRegs.SPIFFRX.bit.RXFIFORESET = 1; //Re-enable FIFO Receives //Reconstruct the ADC word by concatenating the ADC words temp = rxfifobuf[0]; ADC_18bitval |= (temp<<9); temp = rxfifobuf[1]; ADC_18bitval |= (temp<<0); }
Could you kindly comment on the above code? I am very new to this FIFO approach of handling things on SPI.
Thank you very much!
Kartikeya Veeramraju
Hi Kartikeya,
I think your approach is headed in the right direction. A couple of comments though. First, it doesn't look like you are actually using the SPI interrupts. In your PWM ISR code you transmit dummy data and then poll the level of the RX FIFO. Since you are polling, it doesn't make much sense to enable the RX interrupt. Second, you are resetting the RX FIFO after you receive two SPI characters. This is not necessary. The RX FIFO status bits (RXFFST) should automatically update after you read data from SPIRXBUF.
Kartikeya Veeramraju said:The following is the SPI initilization that configures the peripheral in FIFO mode with RXFFIL bits set to trigger after 2 transmits. Is this correct?
Technically this is a don't care since you don'ta actually use interrupts. But, this is correct to get the SPI to generate an interrupt after it receive 2 characters.
Kartikeya Veeramraju said:The next phase is initiating the two FIFO SPI transmits and waiting for the interrupt flag to be raised when two receives are in the buffer. Is this correct?
Same comment as above.
Kartikeya Veeramraju said:Could you kindly comment on the above code? I am very new to this FIFO approach of handling things on SPI.
One thing you may want to do is to refer to the driverlib example spi_ex3_external_loopback_fifo_interrupts in c2000Ware.
<c2000ware_install_folder>\<c2000ware_revision>\driverlib\f2837xd\examples\cpu1\spi
You don't have to adapt your code to use driverlib, but you can get a general idea of the configuration and flow when using FIFOs.
Mr Martinez, good day.
I took at look a the code you asked me to and was unsuccessful in deciphering what the subroutines were doing. I believe that my approach is atleast correct.
I suppose my initialiation is probably improper. I am unable to trigger an SPI interrup after the RX FIFO has two bytes in its FIFO pipeline. Here's my initialiation sequene and the two interrupts I have setup. The TXFIFO interrupt is disabled and RXFIFO interrupt is set up to trigger after two receives. The PWM interrupt loads two bytes to the TXFIFO, which initiates the SPI tranmit sequence and does two 9-bit receives and reads. Once the RXFIFO has two words, the interrupt is fired and the words are recovered and processed to reconstruct the 18 bit word. I am a lttle doubtful about holding the TXFIFO, RXFIFO and SPI peripheral under reset. I have commented the code reasonably for your reference.
However, the interrupt doesnot seem to be firing for some reason. Could you kindly take a look?
SPI_INIT() { SpiaRegs.SPICCR.bit.SPISWRESET = 0; //Reset the SPI peripheral SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1; //Configure C2000 as an SPI master //The clock phase refers to which clock edge the data is captured on and which clock edge does the data change. //Refer to your microcontroller’s data sheet for the bit value for the clock phase. //According to this and referring to the data transfer waveform diagram provided in the datasheet //of LT2338 ADC IC it is found that the data has to be stable at the falling edge and can change at the // rising edge. //Referring to Table 18-3 on Pg 2060 of the technical refernce manual of F28379D, the appropriate settings is //Rising edge without delay //i.e. CLKPOLARITY 0 and CLK_PHASE 0 SpiaRegs.SPICCR.bit.CLKPOLARITY = 0; //Based on the previous explanation SpiaRegs.SPICTL.bit.CLK_PHASE = 0; //Based on the previous explanation //Baud rate settings //Core clock at 200MHz //LSPCLK @100MHz EALLOW; ClkCfgRegs.LOSPCP.bit.LSPCLKDIV = 1; //LSPCLK = / 2 (default on reset) EDIS; #if DataRate_2_5_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x04; //SPI Baud Rate = LSPCLK/7 (20 MHz) #endif #if DataRate_2_0_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x05; //SPI Baud Rate = LSPCLK/7 (16.66 MHz) #endif #if DataRate_1_7_MBPS SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x06; //SPI Baud Rate = LSPCLK/7 (14.28 MHz) #endif //Clear the flags SpiaRegs.SPISTS.bit.OVERRUN_FLAG = 1; //clear the SPI Receiver Overrun Flag #if SPI_INT_ENABLE //Interrupt Settings SpiaRegs.SPICTL.bit.OVERRUNINTENA = 0x1; //Enable Receiver Overrun SpiaRegs.SPICTL.bit.SPIINTENA = 0x1; //Enable SPI Interrupt EALLOW; PieVectTable.SPIA_RX_INT = &spiarx_isr; IER |= 0x0010; //Enable INT6 PieCtrlRegs.PIEIER6.bit.INTx1 = 1; //Enable PIE Group 3's Channel 1 (Which happens to be the ePWM1 ) //PieCtrlRegs.PIEIFR3.bit.INTx1 = 0; //Interrupt flag is cleared. Ready to be set by the interrupt peripheral #endif //-------------------------------------------------------------- //SPI FIFO Settings //--------------------------- //SPIFFTX register Settings //--------------------------- //Reset the SPI transmit and receive channels. The SPI FIFO register configuration bits will be left as is. SpiaRegs.SPIFFTX.bit.SPIRST = 0; //reset the FIFO pointer to zero, and hold in reset SpiaRegs.SPIFFTX.bit.TXFIFO = 0; //TXFIFO Interrupt Clear; 1h (R/W) = Write 1 to clear SPIFFTX[TXFFINT] flag SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 1; //Disable TX interrupt SpiaRegs.SPIFFTX.bit.TXFFIENA = 0; //SPI FIFO Enhancements Enable; SPI FIFO enhancements are enabled. SpiaRegs.SPIFFTX.bit.SPIFFENA = 1; //TX FIFO Reset; Release transmit FIFO from reset SpiaRegs.SPIFFTX.bit.TXFIFO = 1; //SPI FIFO can resume transmit or receive. No effect to the SPI registers bits SpiaRegs.SPIFFTX.bit.SPIRST = 0; //--------------------------- //SPIFFRX register Settings //--------------------------- //reset the FIFO pointer to zero, and hold in reset. SpiaRegs.SPIFFRX.bit.RXFIFORESET = 0; //Receive FIFO Interrupt Clear; Write 1 to clear SPIFFRX[RXFFINT] flag SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1; //RX FIFO interrupt based on RXFFIL match (greater than or equal to) will be enabled SpiaRegs.SPIFFRX.bit.RXFFIENA = 1; //Bit 14: , RXFFOVFCLR; reset the overflow flag SpiaRegs.SPIFFRX.bit.RXFFOVFCLR = 1; //Bit 5: Enable RX FIFO Interrupts SpiaRegs.SPIFFRX.bit.RXFFIENA = 1; //Bit 0-4: RXFFIL: Trigger interrupt after two receives SpiaRegs.SPIFFRX.bit.RXFFIL = 2; //Receive FIFO Reset; Re-enable receive FIFO operation. SpiaRegs.SPIFFRX.bit.RXFIFORESET = 1; //----------------------------------------------------------- SpiaRegs.SPIFFCT.all = 0x0; //SPIFFCT = 0000 0000 0000 0000 SpiaRegs.SPICCR.bit.SPICHAR = 0x8; //9-bit word //Reset Release SpiaRegs.SPICCR.bit.SPISWRESET = 1; //Release the SPI peripheral from RESET State } __interrupt void epwm1_isr(void) { GpioDataRegs.GPASET.bit.GPIO19 = 1; //GPIO Set DelayUs(3); //wait for 2.75 us for conversion to take place GpioDataRegs.GPACLEAR.bit.GPIO19 = 1; //GPIO Clear //SPI communication begins SpiaRegs.SPITXBUF=0x0000; // Send data. First dummy byte in FIFO SpiaRegs.SPITXBUF=0x0000; // Send data. Second dummy byte in FIFO //SpiaRegs.SPITXBUF=0x00; // Send data. Third dummy byte in FIFO } __interrupt void spiarx_isr(void) { static volatile Uint16 GPIO31_count = 0; // Counter for pin toggle PieCtrlRegs.PIEACK.all = PIEACK_GROUP6; // Must acknowledge the PIE group SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1; //Clear the interrupt flag //Read the RXFIFO for (i=0;i<WORDS;i++) { rxfifobuf[i] = SpiaRegs.SPIRXBUF; } temp = rxfifobuf[0]; ADC_18bitval |= (temp<<9); temp = rxfifobuf[1]; ADC_18bitval |= (temp<<0); if(GPIO31_count++ > 25000) // Toggle slowly to see the LED blink { GpioDataRegs.GPATOGGLE.bit.GPIO31 = 1; // Toggle the pin GPIO31_count = 0; // Reset the counter } }
Any help would be greatly appreciated!
Thank you.
Kartikeya
Hi Kartikeya,
Kartikeya Veeramraju said:I took at look a the code you asked me to and was unsuccessful in deciphering what the subroutines were doing. I believe that my approach is atleast correct.
Were you not able to find the source for the subroutines? You can easily import the project into CCS and find the source code for all subroutines. The driverliver lib functions are not all that difficult to understand.
If you want a non-driverlib example, you can also refer to the Example_2837xDSpi_FFDLB_int.c under <c2000ware_install>/device_support\f2837xd\examples\cpu1\spi_loopback_interrupts\cpu01.
Regardless, I'm copying the subroutines here for your reference. Note that the FIFO init doesn't have to be so complicated.
In this code, the SPIB is setup as the master.
// // Function to configure SPI B as master with FIFO enabled. // void initSPIBMaster(void) { // // Must put SPI into reset before configuring it // SPI_disableModule(SPIB_BASE); // // SPI configuration. Use a 500kHz SPICLK and 16-bit word size. // SPI_setConfig(SPIB_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA0, SPI_MODE_MASTER, 500000, 16); SPI_disableLoopback(SPIB_BASE); SPI_setEmulationMode(SPIB_BASE, SPI_EMULATION_FREE_RUN); // // FIFO and interrupt configuration // SPI_enableFIFO(SPIB_BASE); SPI_clearInterruptStatus(SPIB_BASE, SPI_INT_TXFF); SPI_setFIFOInterruptLevel(SPIB_BASE, SPI_FIFO_TX2, SPI_FIFO_RX2); SPI_enableInterrupt(SPIB_BASE, SPI_INT_TXFF); // // Configuration complete. Enable the module. // SPI_enableModule(SPIB_BASE); }
Here is the SPI_enableFIFO() code. It literally sets three bits. The FIFO trigger levels are set in the routine above.
//***************************************************************************** // //! Enables the transmit and receive FIFOs. //! //! \param base is the base address of the SPI port. //! //! This functions enables the transmit and receive FIFOs in the SPI. //! //! \return None. // //***************************************************************************** static inline void SPI_enableFIFO(uint32_t base) { // // Check the arguments. // ASSERT(SPI_isBaseValid(base)); // // Enable the FIFO. // HWREGH(base + SPI_O_FFTX) |= SPI_FFTX_SPIFFENA | SPI_FFTX_TXFIFO; HWREGH(base + SPI_O_FFRX) |= SPI_FFRX_RXFIFORESET; }
Can you monitor the SPI pins on your board? Do you have activity on the SPI MOSI and SPI CLK pins when you start the transmit? If you do see activity, then we can focus on the interrupt configuration as that may be the source of the problem.