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.
Hi all,
I am trying to send multiple bytes at once to an MSP430FR5969 slave using FTDI SPI USB to Serial cable (C232HM). I already was able to create a working loop-back test where I send and receive one byte at a time. However, if I try to send multiple bytes at once, only every other byte is ever received. For example, If I were to send the buffer [5,3,6], the microcontroller will only have [5,6] in its buffer. I checked using an oscilloscope and the entire byte stream is being sent rather than parts of it (i.e. [5,3,6] is visible on MOSI). As such, I suspect there is something incorrect with my initialization of the SPI connection on the slave device or with my interrupt service routine rather than there being something wrong with the master device. I have attached the relevant code sections below.
uint8_t ReceiveIndex = 0; uint8_t ReceiveBuffer[128] = {0}; /* Initialize SPI Interface for 3-wire SPI Interface. * Chip-select is active low (using GPIO) * Used only for communicating to the computer */ void SpiDriver_InitCom(void) { /* Set SPI Chip Select as input */ GPIO_setAsPeripheralModuleFunctionInputPin( SPI_CS_N_PORT_COM, SPI_CS_N_PIN_COM, GPIO_SECONDARY_MODULE_FUNCTION ); /* Set SPI Clock as input */ GPIO_setAsPeripheralModuleFunctionInputPin( SPI_CLK_PORT_COM, SPI_CLK_PIN_COM, GPIO_SECONDARY_MODULE_FUNCTION ); /* Set SPI MOSI as input */ GPIO_setAsPeripheralModuleFunctionInputPin( SPI_MOSI_PORT_COM, SPI_MOSI_PIN_COM, GPIO_SECONDARY_MODULE_FUNCTION ); /* Set SPI MISO as output */ GPIO_setAsPeripheralModuleFunctionOutputPin( SPI_MISO_PORT_COM, SPI_MISO_PIN_COM, GPIO_SECONDARY_MODULE_FUNCTION ); /* Set SPI CS_n as input (active low) */ GPIO_setAsInputPin(SPI_CS_N_PORT_COM, SPI_CS_N_PIN_COM); /* Initialize Slave */ EUSCI_B_SPI_initSlaveParam param = {0}; param.msbFirst = EUSCI_B_SPI_MSB_FIRST; param.clockPhase = EUSCI_B_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT;
param.clockPolarity = EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_HIGH; param.spiMode = EUSCI_B_SPI_3PIN; EUSCI_B_SPI_initSlave(EUSCI_B0_BASE, ¶m); /* Enable SPI module */ EUSCI_B_SPI_enable(EUSCI_B0_BASE); /* Clear receive/transmit interrupt */ EUSCI_B_SPI_clearInterrupt(EUSCI_B0_BASE, EUSCI_B_SPI_RECEIVE_INTERRUPT); /* Enable receive/transmit interrupt */ EUSCI_B_SPI_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_SPI_RECEIVE_INTERRUPT); __bis_SR_register(LPM0_bits + GIE); // Enter LPM0, enable interrupts } #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USCI_B0_VECTOR __interrupt void USCI_B0_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USCIB0_VECTOR))) USCI_B0_ISR(void) #else #error Compiler not supported! #endif { uint8_t rx_data = 0; switch(__even_in_range(UCB0IV, USCI_SPI_UCTXIFG)) { case USCI_NONE: break; case USCI_SPI_UCRXIFG: rx_data = EUSCI_B_SPI_receiveData(EUSCI_B0_BASE); UCB0IFG &= ~UCRXIFG; if (!(SPI_CS_N_PORT & SPI_CS_N_PIN)) { if (rx_data != 0 || rx_data != -1) { ReceiveBuffer[ReceiveIndex++] = rx_data; } } case USCI_SPI_UCTXIFG: break; default: break; } }
Main.c:
int main(void) { int ii; PMM_unlockLPM5(); ClockDriver_Init(); WatchdogDriver_Init(); // Will start Watchdog WatchdogDriver_SetEnable(false); SpiDriver_InitCom(); SpiControl_Init(); UartControl_Init(); __enable_interrupt(); //EUSCI_B_SPI_transmitData(EUSCI_B0_BASE, (uint8_t)0xA5); int testflag = 0; while(1) { printOutBuff(); }
SPI_CONFIG_OPTION_MODE0 = 0x00000000
SPI_CONFIG_OPTION_CS_DBUS3 = 0x00000000 # 000 00 #
SPI_CONFIG_OPTION_CS_ACTIVELOW = 0x00000020
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES = 0x00000000
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE = 0x00000002
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE = 0x00000004
def main(): libMPSSE = load_spi_library() device = FT_DEVICE_LIST_INFO_NODE() config = ChannelConfig() numChannels, status = getNumChannels(libMPSSE) indx = ctypes.c_uint32(0) device, status = getChannelInfo(libMPSSE, indx, device) handle, status = openChannel(libMPSSE, indx) config.ClockRate = ctypes.c_uint32(1000000) config.LatencyTimer = ctypes.c_byte(1) config.configOptions = ctypes.c_uint32(mask.SPI_CONFIG_OPTION_MODE0 | mask.SPI_CONFIG_OPTION_CS_DBUS3 | mask.SPI_CONFIG_OPTION_CS_ACTIVELOW) config, status = initChannel(libMPSSE, handle, config) options = ctypes.c_uint32(mask.SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES | mask.SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE | mask.SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE) tx_buffer = [0,0,0,0,0,0] rx_buffer = [0,0,0,0,0,0] tx = (ctypes.c_uint8*len(tx_buffer))(*tx_buffer) rx = (ctypes.c_uint8*len(rx_buffer))(*rx_buffer) transferCount = ctypes.c_uint32(0) tx[0] = 0x0F tx[1] = 0x06 tx[2] = 0x0E status = libMPSSE.SPI_Write(handle, ctypes.byref(tx), ctypes.c_uint32(3), ctypes.byref(transferCount), options)
The code in the master device is based of the technical documentation for FTDI the SPI library: www.ftdichip.com/.../AN_178_User Guide for LibMPSSE-SPI.pdf
At this point I am pretty stumped and any help would be greatly appreciated!!
Hi Bruce,
Thank you for the advice! It seems the clock was the issue. I dropped my master clock to 10 kHz and I started seeing all the values in my buffer. However, I am surprised that I was overrunning the clock, as I was running the slave device's clock at 16MHz. Do you have any suggestions as to why this would be the case? Ideally I would need the clock to run as fast as I can, which is why I started at such a high value in the first place.
On a side note:
I only put in the line: UCB0IFG &= ~UCRXIFG in because I saw it in an example and I was getting pretty desperate. Now that I know this isn't best practice, I will avoid doing this in the future.
The second line was mostly a debugging statement. It was to make sure that I only got my true data (in this case 3, 5,6) and not -1 which is my no data here condition or 0 which sometimes appears in my buffer but is not desired.
Hi Bruce,
I changed my ISR to this:
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USCI_B0_VECTOR __interrupt void USCI_B0_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USCIB0_VECTOR))) USCI_B0_ISR(void) #else #error Compiler not supported! #endif { int8_t rx_data = 0; int8_t tx_data = 0; switch(__even_in_range(UCB0IV, USCI_SPI_UCTXIFG)) { case USCI_NONE: break; case USCI_SPI_UCRXIFG: rx_data = EUSCI_B_SPI_receiveData(EUSCI_B0_BASE); SpiControl_PutIntoRxBuffer(rx_data); /* If there's something in the Ring Buffer, transmit it * If not, then disable TX interrupts until new data gets written. */ tx_data = SpiControl_GetFromTxBuffer(); if (tx_data != -1) { EUSCI_B_SPI_transmitData(EUSCI_B0_BASE, (uint8_t)tx_data); } case USCI_SPI_UCTXIFG: break; default: break; } }
and my main to this:
int main(void) { int ii; PMM_unlockLPM5(); ClockDriver_Init(); WatchdogDriver_Init(); // Will start Watchdog WatchdogDriver_SetEnable(false); SpiDriver_InitCom(); SpiControl_Init(); __enable_interrupt(); while(1) { ii = SpiControl_GetFromRxBuffer(); if (ii != -1) { SpiControl_PutIntoTxBufferChar(((uint8_t)ii)); } } }
and tried your suggestion. My results turned out to be pretty weird. If I send only 2 bytes, I found that I am able to send the bytes (buffer of [5,6]) up to a clock of 2 MHz and still see it in my slave device. However, If I send out 3 bytes at once (buffer of [3, 5, 7]) I am only able to send at a clock cycle of between 400 and 500 KHz (at 500 KHz, 5 disappears and only 3 and 7 make it to the buffer). This make me wonder if it is something else rather than my clock that is causing the issue. I am also having issues sending data back from my slave device as I need to ask for additional bytes before my data is returned. For example:
At 10 KHz:
master sends: |data| 0 | 0 |
master reads: | 0 |data| 0 |
At 50 KHz:
master sends: |data| 0 | 0 |
master reads: | 0 | 0 |data|
At 100 KHz:
master sends: |data| 0 | 0 |
master reads: | 0 | 0 |data|
At 500 KHz:
master sends: | data | 0 | 0 |
master reads: |prev data| 0 | 0 |
Any suggestions as what could be going on would be greatly appreciated!
Hi Bruce and Ling,
Sorry for the late follow up. I just checked the clock of my microcontroller using an oscilloscope and it is 16MHz, which is what I expected. This makes me think that the clock is not the issue... or at least not the clock of the slave. Could there be another reason why it is not working properly?
Thanks in advance!
Gadiel
Hi Bruce,
Thank you for the suggestion! I will try that and see how long the ISR actually takes. I am relatively new to this process so it might take me a while though.
Best regards,
Gadiel
Hi Gadiel,
Have you considered using DMA? It essentially works as a buffer. It can take each byte received by the SPI peripheral and move it to an array in memory, in the background, without interrupts. Another DMA channel can be configured to do transmission as well. You put an array of data into memory and DMA will feed it to the SPI peripheral one byte at a time for transmission.
Since this is exactly the task your interrupt is attempting to accomplish and nothing more, using DMA would replace it very nicely. Your code can then process the data whenever there is time (non-realtime task.. you can process the data in main without an interrupt). DMA is very fast. But keep in mind that there are only 3 DMA channels available on your device, so use it sparingly.
I also noticed that you do not really use the chip-select pin as effectively as you could. At first you initialize it as a SPI pin, then you reinitialize it as a GPIO input and read it in code. Ideally, you should leave it as a SPI pin and configure the SPI peripheral to use it. (By setting param.spiMode = EUSCI_B_SPI_4PIN_UCxSTE_ACTIVE_HIGH or EUSCI_B_SPI_4PIN_UCxSTE_ACTIVE_LOW if I'm not mistaken. Sorry, I don't have any experience with the library. I do direct register manipulation.) If you configure it this way, then the SPI peripheral will automatically ignore data while the chip-select pin is inactive. But if you have only one master and one slave, then you really don't need to use a chip-select at all.
I hope this helps.
Hi Gadiel,
Have you tried the code example?
//****************************************************************************** // MSP430FR59xx Demo - eUSCI_A0, SPI 3-Wire Master multiple byte RX/TX // // Description: SPI master communicates to SPI slave sending and receiving // 3 different messages of different length. SPI master will enter LPM0 mode // while waiting for the messages to be sent/receiving using SPI interrupt. // SPI Master will initially wait for a port interrupt in LPM0 mode before // starting the SPI communication. // ACLK = NA, MCLK = SMCLK = DCO 16MHz. // // // MSP430FR5969 // ----------------- // /|\ | P1.3|-> Slave Chip Select (GPIO) // | | | // ---|RST P1.4|-> Slave Reset (GPIO) // | | // | P2.0|-> Data Out (UCA0SIMO) // | | // Button ->|P1.1 P2.1|<- Data In (UCA0SOMI) // | | // | P1.5|-> Serial Clock Out (UCA0CLK) // // Nima Eskandari // Texas Instruments Inc. // April 2017 // Built with CCS V7.0 //****************************************************************************** #include <msp430.h> #include <stdint.h> //****************************************************************************** // Example Commands ************************************************************ //****************************************************************************** #define DUMMY 0xFF #define SLAVE_CS_OUT P1OUT #define SLAVE_CS_DIR P1DIR #define SLAVE_CS_PIN BIT3 /* CMD_TYPE_X_SLAVE are example commands the master sends to the slave. * The slave will send example SlaveTypeX buffers in response. * * CMD_TYPE_X_MASTER are example commands the master sends to the slave. * The slave will initialize itself to receive MasterTypeX example buffers. * */ #define CMD_TYPE_0_SLAVE 0 #define CMD_TYPE_1_SLAVE 1 #define CMD_TYPE_2_SLAVE 2 #define CMD_TYPE_0_MASTER 3 #define CMD_TYPE_1_MASTER 4 #define CMD_TYPE_2_MASTER 5 #define TYPE_0_LENGTH 1 #define TYPE_1_LENGTH 2 #define TYPE_2_LENGTH 6 #define MAX_BUFFER_SIZE 20 /* MasterTypeX are example buffers initialized in the master, they will be * sent by the master to the slave. * SlaveTypeX are example buffers initialized in the slave, they will be * sent by the slave to the master. * */ uint8_t MasterType0 [TYPE_0_LENGTH] = {0x11}; uint8_t MasterType1 [TYPE_1_LENGTH] = {8, 9}; uint8_t MasterType2 [TYPE_2_LENGTH] = {'F', '4' , '1' , '9', '2', 'B'}; uint8_t SlaveType2 [TYPE_2_LENGTH] = {0}; uint8_t SlaveType1 [TYPE_1_LENGTH] = {0}; uint8_t SlaveType0 [TYPE_0_LENGTH] = {0}; //****************************************************************************** // General SPI State Machine *************************************************** //****************************************************************************** typedef enum SPI_ModeEnum{ IDLE_MODE, TX_REG_ADDRESS_MODE, RX_REG_ADDRESS_MODE, TX_DATA_MODE, RX_DATA_MODE, TIMEOUT_MODE } SPI_Mode; /* Used to track the state of the software state machine*/ SPI_Mode MasterMode = IDLE_MODE; /* The Register Address/Command to use*/ uint8_t TransmitRegAddr = 0; /* ReceiveBuffer: Buffer used to receive data in the ISR * RXByteCtr: Number of bytes left to receive * ReceiveIndex: The index of the next byte to be received in ReceiveBuffer * TransmitBuffer: Buffer used to transmit data in the ISR * TXByteCtr: Number of bytes left to transfer * TransmitIndex: The index of the next byte to be transmitted in TransmitBuffer * */ uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0}; uint8_t RXByteCtr = 0; uint8_t ReceiveIndex = 0; uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0}; uint8_t TXByteCtr = 0; uint8_t TransmitIndex = 0; /* SPI Write and Read Functions */ /* For slave device, writes the data specified in *reg_data * * reg_addr: The register or command to send to the slave. * Example: CMD_TYPE_0_MASTER * *reg_data: The buffer to write * Example: MasterType0 * count: The length of *reg_data * Example: TYPE_0_LENGTH * */ SPI_Mode SPI_Master_WriteReg(uint8_t reg_addr, uint8_t *reg_data, uint8_t count); /* For slave device, read the data specified in slaves reg_addr. * The received data is available in ReceiveBuffer * * reg_addr: The register or command to send to the slave. * Example: CMD_TYPE_0_SLAVE * count: The length of data to read * Example: TYPE_0_LENGTH * */ SPI_Mode SPI_Master_ReadReg(uint8_t reg_addr, uint8_t count); void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count); void SendUCA0Data(uint8_t val); void SendUCA0Data(uint8_t val) { while (!(UCA0IFG & UCTXIFG)); // USCI_A0 TX buffer ready? UCA0TXBUF = val; } void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count) { uint8_t copyIndex = 0; for (copyIndex = 0; copyIndex < count; copyIndex++) { dest[copyIndex] = source[copyIndex]; } } SPI_Mode SPI_Master_WriteReg(uint8_t reg_addr, uint8_t *reg_data, uint8_t count) { MasterMode = TX_REG_ADDRESS_MODE; TransmitRegAddr = reg_addr; //Copy register data to TransmitBuffer CopyArray(reg_data, TransmitBuffer, count); TXByteCtr = count; RXByteCtr = 0; ReceiveIndex = 0; TransmitIndex = 0; SLAVE_CS_OUT &= ~(SLAVE_CS_PIN); SendUCA0Data(TransmitRegAddr); __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts SLAVE_CS_OUT |= SLAVE_CS_PIN; return MasterMode; } SPI_Mode SPI_Master_ReadReg(uint8_t reg_addr, uint8_t count) { MasterMode = TX_REG_ADDRESS_MODE; TransmitRegAddr = reg_addr; RXByteCtr = count; TXByteCtr = 0; ReceiveIndex = 0; TransmitIndex = 0; SLAVE_CS_OUT &= ~(SLAVE_CS_PIN); SendUCA0Data(TransmitRegAddr); __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts SLAVE_CS_OUT |= SLAVE_CS_PIN; return MasterMode; } //****************************************************************************** // Device Initialization ******************************************************* //****************************************************************************** void initSPI() { //Clock Polarity: The inactive state is high //MSB First, 8-bit, Master, 3-pin mode, Synchronous UCA0CTLW0 = UCSWRST; // **Put state machine in reset** UCA0CTLW0 |= UCCKPL + UCMSB + UCSYNC + UCMST + UCSSEL__SMCLK; // 3-pin, 8-bit SPI Slave UCA0BRW = 0x20; UCA0MCTLW = 0; UCA0CTLW0 &= ~UCSWRST; // **Initialize USCI state machine** UCA0IE |= UCRXIE; // Enable USCI0 RX interrupt } void initGPIO() { //LEDs P1OUT = 0x00; // P1 setup for LED & reset output P1DIR |= BIT0 + BIT4; P4DIR |= BIT6; P4OUT &= ~(BIT6); // Configure GPIO P1SEL1 |= BIT5; // Configure SPI pins P2SEL1 |= BIT0 | BIT1; SLAVE_CS_DIR |= SLAVE_CS_PIN; SLAVE_CS_OUT |= SLAVE_CS_PIN; //Button to initiate transfer P1DIR &= ~(BIT1); P1OUT |= BIT1; // P1.1 pull up P1REN |= BIT1; // P1.1 pull up/down resistor enable P1IES |= BIT1; // P1.1 Hi/lo edge P1IE |= BIT1; // P1.1 interrupt enabled // Disable the GPIO power-on default high-impedance mode to activate // previously configured port settings PM5CTL0 &= ~LOCKLPM5; P1IFG &= ~BIT1; // P1.1 IFG cleared } void initClockTo16MHz() { // Configure one FRAM waitstate as required by the device datasheet for MCLK // operation beyond 8MHz _before_ configuring the clock system. FRCTL0 = FRCTLPW | NWAITS_1; // Clock System Setup CSCTL0_H = CSKEY >> 8; // Unlock CS registers CSCTL1 = DCORSEL | DCOFSEL_4; // Set DCO to 16MHz CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK; CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1; // Set all dividers CSCTL0_H = 0; // Lock CS registerss } //****************************************************************************** // Main ************************************************************************ // Send and receive three messages containing the example commands ************* //****************************************************************************** int main(void) { WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer initClockTo16MHz(); initGPIO(); initSPI(); P1OUT &= ~BIT4; // Now with SPI signals initialized, __delay_cycles(100000); P1OUT |= BIT4; // reset slave __delay_cycles(100000); // Wait for slave to initialize P1OUT |= BIT0; __bis_SR_register(LPM0_bits + GIE); // CPU off, enable interrupts SPI_Master_ReadReg(CMD_TYPE_2_SLAVE, TYPE_2_LENGTH); CopyArray(ReceiveBuffer, SlaveType2, TYPE_2_LENGTH); SPI_Master_ReadReg(CMD_TYPE_1_SLAVE, TYPE_1_LENGTH); CopyArray(ReceiveBuffer, SlaveType1, TYPE_1_LENGTH); SPI_Master_ReadReg(CMD_TYPE_0_SLAVE, TYPE_0_LENGTH); CopyArray(ReceiveBuffer, SlaveType0, TYPE_0_LENGTH); SPI_Master_WriteReg(CMD_TYPE_2_MASTER, MasterType2, TYPE_2_LENGTH); SPI_Master_WriteReg(CMD_TYPE_1_MASTER, MasterType1, TYPE_1_LENGTH); SPI_Master_WriteReg(CMD_TYPE_0_MASTER, MasterType0, TYPE_0_LENGTH); __bis_SR_register(LPM0_bits + GIE); __no_operation(); return 0; } //****************************************************************************** // SPI Interrupt *************************************************************** //****************************************************************************** #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USCI_A0_VECTOR __interrupt void USCI_A0_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void) #else #error Compiler not supported! #endif { uint8_t uca0_rx_val = 0; switch(__even_in_range(UCA0IV, USCI_SPI_UCTXIFG)) { case USCI_NONE: break; case USCI_SPI_UCRXIFG: uca0_rx_val = UCA0RXBUF; UCA0IFG &= ~UCRXIFG; switch (MasterMode) { case TX_REG_ADDRESS_MODE: if (RXByteCtr) { MasterMode = RX_DATA_MODE; // Need to start receiving now //Send Dummy To Start __delay_cycles(2000000); SendUCA0Data(DUMMY); } else { MasterMode = TX_DATA_MODE; // Continue to transmision with the data in Transmit Buffer //Send First SendUCA0Data(TransmitBuffer[TransmitIndex++]); TXByteCtr--; } break; case TX_DATA_MODE: if (TXByteCtr) { SendUCA0Data(TransmitBuffer[TransmitIndex++]); TXByteCtr--; } else { //Done with transmission MasterMode = IDLE_MODE; __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } break; case RX_DATA_MODE: if (RXByteCtr) { ReceiveBuffer[ReceiveIndex++] = uca0_rx_val; //Transmit a dummy RXByteCtr--; } if (RXByteCtr == 0) { MasterMode = IDLE_MODE; __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } else { SendUCA0Data(DUMMY); } break; default: __no_operation(); break; } __delay_cycles(1000); break; case USCI_SPI_UCTXIFG: break; default: break; } } //****************************************************************************** // PORT1 Interrupt ************************************************************* // Interrupt occurs on button press and initiates the SPI data transfer ******** //****************************************************************************** #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(PORT1_VECTOR))) Port_1 (void) #else #error Compiler not supported! #endif { P4OUT |= BIT6; P1IFG &= ~BIT1; // P1.1 IFG cleared P1IE &= ~BIT1; //Initiate __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0 }
//****************************************************************************** // MSP430FR59xx Demo - eUSCI_A0, SPI 3-Wire Slave multiple byte RX/TX // // Description: SPI master communicates to SPI slave sending and receiving // 3 different messages of different length. SPI slave will enter LPM0 // while waiting for the messages to be sent/receiving using SPI interrupt. // ACLK = NA, MCLK = SMCLK = DCO 16MHz. // // // MSP430FR5969 // ----------------- // /|\ | P1.3|<- Master's GPIO (Chip Select) // | | | // ---|RST RST |<- Master's GPIO (To reset slave) // | | // | P2.0|<- Data In (UCA0SIMO) // | | // | P2.1|-> Data Out (UCA0SOMI) // | | // | P1.5|<- Serial Clock In (UCA0CLK) // // Nima Eskandari // Texas Instruments Inc. // April 2017 // Built with CCS V7.0 //****************************************************************************** #include <msp430.h> #include <stdint.h> #include <stdbool.h> //****************************************************************************** // Example Commands ************************************************************ //****************************************************************************** #define DUMMY 0xFF #define SLAVE_CS_IN P1IN #define SLAVE_CS_DIR P1DIR #define SLAVE_CS_PIN BIT3 /* CMD_TYPE_X_SLAVE are example commands the master sends to the slave. * The slave will send example SlaveTypeX buffers in response. * * CMD_TYPE_X_MASTER are example commands the master sends to the slave. * The slave will initialize itself to receive MasterTypeX example buffers. * */ #define CMD_TYPE_0_SLAVE 0 #define CMD_TYPE_1_SLAVE 1 #define CMD_TYPE_2_SLAVE 2 #define CMD_TYPE_0_MASTER 3 #define CMD_TYPE_1_MASTER 4 #define CMD_TYPE_2_MASTER 5 #define TYPE_0_LENGTH 1 #define TYPE_1_LENGTH 2 #define TYPE_2_LENGTH 6 #define MAX_BUFFER_SIZE 20 /* MasterTypeX are example buffers initialized in the master, they will be * sent by the master to the slave. * SlaveTypeX are example buffers initialized in the slave, they will be * sent by the slave to the master. * */ uint8_t MasterType2 [TYPE_2_LENGTH] = {0}; uint8_t MasterType1 [TYPE_1_LENGTH] = { 0, 0}; uint8_t MasterType0 [TYPE_0_LENGTH] = { 0}; uint8_t SlaveType2 [TYPE_2_LENGTH] = {'A', 'B', 'C', 'D', '1', '2'}; uint8_t SlaveType1 [TYPE_1_LENGTH] = {0x15, 0x16}; uint8_t SlaveType0 [TYPE_0_LENGTH] = {12}; //****************************************************************************** // General SPI State Machine *************************************************** //****************************************************************************** typedef enum SPI_ModeEnum{ IDLE_MODE, TX_REG_ADDRESS_MODE, RX_REG_ADDRESS_MODE, TX_DATA_MODE, RX_DATA_MODE, TIMEOUT_MODE } SPI_Mode; /* Used to track the state of the software state machine*/ SPI_Mode SlaveMode = RX_REG_ADDRESS_MODE; /* The Register Address/Command to use*/ uint8_t ReceiveRegAddr = 0; /* ReceiveBuffer: Buffer used to receive data in the ISR * RXByteCtr: Number of bytes left to receive * ReceiveIndex: The index of the next byte to be received in ReceiveBuffer * TransmitBuffer: Buffer used to transmit data in the ISR * TXByteCtr: Number of bytes left to transfer * TransmitIndex: The index of the next byte to be transmitted in TransmitBuffer * */ uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0}; uint8_t RXByteCtr = 0; uint8_t ReceiveIndex = 0; uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0}; uint8_t TXByteCtr = 0; uint8_t TransmitIndex = 0; /* Initialized the software state machine according to the received cmd * * cmd: The command/register address received * */ void SPI_Slave_ProcessCMD(uint8_t cmd); /* The transaction between the slave and master is completed. Uses cmd * to do post transaction operations. (Place data from ReceiveBuffer * to the corresponding buffer based in the last received cmd) * * cmd: The command/register address corresponding to the completed * transaction */ void SPI_Slave_TransactionDone(uint8_t cmd); void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count); void SendUCA0Data(uint8_t val); void SendUCA0Data(uint8_t val) { while (!(UCA0IFG & UCTXIFG)); // USCI_A0 TX buffer ready? UCA0TXBUF = val; } void SPI_Slave_ProcessCMD(uint8_t cmd) { ReceiveIndex = 0; TransmitIndex = 0; RXByteCtr = 0; TXByteCtr = 0; switch (cmd) { case (CMD_TYPE_0_SLAVE): //Send slave device id (This device's id) SlaveMode = TX_DATA_MODE; TXByteCtr = TYPE_0_LENGTH; //Fill out the TransmitBuffer CopyArray(SlaveType0, TransmitBuffer, TYPE_0_LENGTH); //Send First Byte SendUCA0Data(TransmitBuffer[TransmitIndex++]); TXByteCtr--; break; case (CMD_TYPE_1_SLAVE): //Send slave device time (This device's time) SlaveMode = TX_DATA_MODE; TXByteCtr = TYPE_1_LENGTH; //Fill out the TransmitBuffer CopyArray(SlaveType1, TransmitBuffer, TYPE_1_LENGTH); //Send First Byte SendUCA0Data(TransmitBuffer[TransmitIndex++]); TXByteCtr--; break; case (CMD_TYPE_2_SLAVE): //Send slave device location (This device's location) SlaveMode = TX_DATA_MODE; TXByteCtr = TYPE_2_LENGTH; //Fill out the TransmitBuffer CopyArray(SlaveType2, TransmitBuffer, TYPE_2_LENGTH); //Send First Byte SendUCA0Data(TransmitBuffer[TransmitIndex++]); TXByteCtr--; break; case (CMD_TYPE_0_MASTER): SlaveMode = RX_DATA_MODE; RXByteCtr = TYPE_0_LENGTH; break; case (CMD_TYPE_1_MASTER): SlaveMode = RX_DATA_MODE; RXByteCtr = TYPE_1_LENGTH; break; case (CMD_TYPE_2_MASTER): SlaveMode = RX_DATA_MODE; RXByteCtr = TYPE_2_LENGTH; break; default: //while(1); __no_operation(); break; } } void SPI_Slave_TransactionDone(uint8_t cmd) { switch (cmd) { case (CMD_TYPE_0_SLAVE): //Slave device id was sent(This device's id) break; case (CMD_TYPE_1_SLAVE): //Slave device time was sent(This device's time) break; case (CMD_TYPE_2_SLAVE): //Send slave device location (This device's location) break; case (CMD_TYPE_0_MASTER): CopyArray(ReceiveBuffer, MasterType0, TYPE_0_LENGTH); break; case (CMD_TYPE_1_MASTER): CopyArray(ReceiveBuffer, MasterType1, TYPE_1_LENGTH); break; case (CMD_TYPE_2_MASTER): CopyArray(ReceiveBuffer, MasterType2, TYPE_2_LENGTH); break; default: __no_operation(); break; } } void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count) { uint8_t copyIndex = 0; for (copyIndex = 0; copyIndex < count; copyIndex++) { dest[copyIndex] = source[copyIndex]; } } //****************************************************************************** // Device Initialization ******************************************************* //****************************************************************************** void initSPI() { //Clock Polarity: The inactive state is high //MSB First, 8-bit, Master, 3-pin mode, Synchronous UCA0CTLW0 = UCSWRST; // **Put state machine in reset** UCA0CTLW0 |= UCCKPL + UCMSB + UCSYNC; // 3-pin, 8-bit SPI Slave UCA0CTLW0 &= ~UCSWRST; // **Initialize USCI state machine** UCA0IE |= UCRXIE; // Enable USCI0 RX interrupt } void initGPIO() { //LEDs P1OUT = 0x00; // P1 setup for LED & reset output P1DIR |= BIT0; // Configure GPIO P1SEL1 |= BIT5; // Configure SPI pins P2SEL1 |= BIT0 | BIT1; SLAVE_CS_DIR &= ~(SLAVE_CS_PIN); // Disable the GPIO power-on default high-impedance mode to activate // previously configured port settings PM5CTL0 &= ~LOCKLPM5; } void initClockTo16MHz() { // Configure one FRAM waitstate as required by the device datasheet for MCLK // operation beyond 8MHz _before_ configuring the clock system. FRCTL0 = FRCTLPW | NWAITS_1; // Clock System Setup CSCTL0_H = CSKEY >> 8; // Unlock CS registers CSCTL1 = DCORSEL | DCOFSEL_4; // Set DCO to 16MHz CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK; CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1; // Set all dividers CSCTL0_H = 0; // Lock CS registerss } //****************************************************************************** // Main ************************************************************************ // Enters LPM0 and waits for SPI interrupts. The data sent from the master is * // then interpreted and the device will respond accordingly * //****************************************************************************** int main(void) { WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer initClockTo16MHz(); initGPIO(); initSPI(); __bis_SR_register(LPM0_bits + GIE); // Enter LPM0, enable interrupts __no_operation(); return 0; } //****************************************************************************** // SPI Interrupt *************************************************************** //****************************************************************************** #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USCI_A0_VECTOR __interrupt void USCI_A0_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void) #else #error Compiler not supported! #endif { uint8_t uca0_rx_val = 0; switch(__even_in_range(UCA0IV, USCI_SPI_UCTXIFG)) { case USCI_NONE: break; case USCI_SPI_UCRXIFG: uca0_rx_val = UCA0RXBUF; UCA0IFG &= ~UCRXIFG; if (!(SLAVE_CS_IN & SLAVE_CS_PIN)) { switch (SlaveMode) { case (RX_REG_ADDRESS_MODE): ReceiveRegAddr = uca0_rx_val; SPI_Slave_ProcessCMD(ReceiveRegAddr); break; case (RX_DATA_MODE): ReceiveBuffer[ReceiveIndex++] = uca0_rx_val; RXByteCtr--; if (RXByteCtr == 0) { //Done Receiving MSG SlaveMode = RX_REG_ADDRESS_MODE; SPI_Slave_TransactionDone(ReceiveRegAddr); } break; case (TX_DATA_MODE): if (TXByteCtr > 0) { SendUCA0Data(TransmitBuffer[TransmitIndex++]); TXByteCtr--; } if (TXByteCtr == 0) { //Done Transmitting MSG SlaveMode = RX_REG_ADDRESS_MODE; SPI_Slave_TransactionDone(ReceiveRegAddr); } break; default: __no_operation(); break; } } break; case USCI_SPI_UCTXIFG: break; default: break; } }
Ling
**Attention** This is a public forum