/* SPI.c * Jonathan Valvano * December 27, 2025 * Derived from uart_rw_multibyte_fifo_poll_LP_MSPM0G3507_nortos_ticlang */ // hardware connections // **********ST7735 TFT and SDC******************* // ST7735 // Backlight (pin 10) to +3.3 V // MISO (pin 9) from SPI1 POCI PB7 // SCK (pin 8) to SPI1 SCLK: PB9 // MOSI (pin 7) to SPI1 PICO: PB8 // TFT_CS (pin 6) to LCD, GPIO: PB6 // CARD_CS (pin 5) to SDC, GPIO PB0 // Data/Command (pin 4) to PB16 (GPIO), high for data, low for command // RESET (pin 3) to PB15 (GPIO) // VCC (pin 2) to +3.3 V // Gnd (pin 1) to ground // **********HiLetgo ST7735 TFT and SDC ******************* // ST7735 // LED- (pin 16) TFT, to ground // LED+ (pin 15) TFT, to +3.3 V // SD_CS (pin 14) SDC, to chip select, GPIO PB0 // MOSI (pin 13) SDC, to MOSI PB8 MOSI SPI1 PICO // MISO (pin 12) SDC, to MISO from SPI1 POCI PB7 // SCK (pin 11) SDC, to serial clock // CS (pin 10) TFT, // TFT_CS to LCD, GPIO: PB6 // SCL (pin 9) TFT, to PB9 SPI1 SCLK // SDA (pin 8) TFT, to PB8 MOSI SPI1 PICO // A0 (pin 7) TFT, to PB16 Data/Command, high for data, low for command // RESET (pin 6) TFT, to PB15 reset (GPIO), low to reset // NC (pins 3,4,5) // VCC (pin 2) to +3.3 V // GND (pin 1) to ground #include #include "../RTOS_Labs_common/SPI.h" #include "../inc/Clock.h" #include "../inc/Timer.h" #include "../inc/LaunchPad.h" /* // calls Clock_Freq to get bus clock // initialize SPI for 8 MHz baud clock // busy-wait synchronization // SPI0,SPI1 in power domain PD1 SysClk equals bus CPU clock void SPI_Init(void){uint32_t busfreq = Clock_Freq(); // assumes GPIOA and GPIOB are reset and powered previously // RSTCLR to SPI1 peripherals // bits 31-24 unlock key 0xB1 // bit 1 is Clear reset sticky bit // bit 0 is reset gpio port SPI1->GPRCM.RSTCTL = 0xB1000003; // Enable power to SPI1 peripherals // PWREN // bits 31-24 unlock key 0x26 // bit 0 is Enable Power SPI1->GPRCM.PWREN = 0x26000001; // configure PB9 PB6 PB8 as alternate SPI1 function IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000083; // SPI1 SCLK IOMUX->SECCFG.PINCM[PB6INDEX] = 0x00000083; // SPI1 CS0 IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000083; // SPI1 PICO IOMUX->SECCFG.PINCM[PB15INDEX] = 0x00000081; // GPIO output, LCD !RST IOMUX->SECCFG.PINCM[PB16INDEX] = 0x00000081; // GPIO output, LCD RS // ** IOMUX->SECCFG.PINCM[PA13INDEX] = 0x00000081; // GPIO output, LCD RS Clock_Delay(24); // time for gpio to power up // ** GPIOA->DOE31_0 |= 1<<13; // PA13 is LCD RS GPIOB->DOE31_0 |= 1<<16; // PA13 is LCD RS GPIOB->DOE31_0 |= 1<<15; // PB15 is LCD !RST GPIOB->DOUTSET31_0 = 1<<16; // RS=1 // ** GPIOA->DOUTSET31_0 = 1<<13; // RS=1 GPIOB->DOUTSET31_0 = 1<<15; // !RST = 1 SPI1->CLKSEL = 8; // SYSCLK // bit 3 SYSCLK // bit 2 MFCLK // bit 1 LFCLK SPI1->CLKDIV = 0; // divide by 1 // bits 2-0 n (0 to 7), divide by n+1 //Set the bit rate clock divider to generate the serial output clock // outputBitRate = (spiInputClock) / ((1 + SCR) * 2) // 8,000,000 = (16,000,000)/((0 + 1) * 2) // 8,000,000 = (32,000,000)/((1 + 1) * 2) // 6,666,667 = (40,000,000)/((2 + 1) * 2) // 10,000,000 = (40,000,000)/((1 + 1) * 2) // 8,000,000 = (80,000,000)/((4 + 1) * 2) // 8,000,000 = (Clock_Freq)/((m + 1) * 2) // m = (Clock_Freq/16000000) - 1 if(busfreq <= 16000000){ SPI1->CLKCTL = 0; // frequency= busfreq/2 }else if(busfreq == 40000000){ SPI1->CLKCTL = 1; // frequency= 10MHz // SPI1->CLKCTL = 2; // frequency= 6.66MHz }else{ SPI1->CLKCTL = busfreq/16000000 -1; // 8 MHz } SPI1->CTL0 = 0x0027; // bit 14 CSCLR=0 not cleared // bits 13-12 CSSEL=0 CS0 // bit 9 SPH = 0 // bit 8 SPO = 0 // bits 6-5 FRF = 01 (4 wire) // bits 4-0 n=7, data size is n+1 (8bit data) SPI1->CTL1 = 0x0015; // bits 29-24 RXTIMEOUT=0 // bits 23-16 REPEATX=0 disabled // bits 15-12 CDMODE=0 manual // bit 11 CDENABLE=0 CS3 // bit 7-5 =0 no parity // bit 4=1 MSB first // bit 3=0 POD (not used, not peripheral) // bit 2=1 CP controller mode // bit 1=0 LBM disable loop back // bit 0=1 enable SPI SPI_Reset(); } //---------SPI_OutData------------ // Output 8-bit data to SPI port // Input: data is an 8-bit data to be transferred // Output: none void SPI_OutData(char data){ while((SPI1->STAT&0x02) == 0x00){}; // spin if TxFifo full GPIOA->DOUTSET31_0 = 1<<13; // RS=PA13=1 for data SPI1->TXDATA = data; } //---------SPI_OutCommand------------ // Output 8-bit command to SPI port // Input: data is an 8-bit data to be transferred // Output: none void SPI_OutCommand(char command){ while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy GPIOA->DOUTCLR31_0 = 1<<13; // RS=PA13=0 for command SPI1->TXDATA = command; while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy } */ // SDC CS initialization void CS_Init(void){ IOMUX->SECCFG.PINCM[SDC_CS_INDEX] = (uint32_t) 0x00000081; SDC_CS->DOE31_0 |= SDC_CS_PIN; SDC_CS_HIGH(); // PB0=1 } //---------SPI1_Reset------------ // Reset LCD // Input: none // Output: none void SPI1_Reset(void){ TFT_RST_HIGH(); // PB15=!RST=1 Clock_Delay1ms(500); // 500ms (calibrated with logic analyzer) TFT_RST_LOW(); // PB15=!RST=0 Clock_Delay1ms(500); // 500ms TFT_RST_HIGH(); // PB15=!RST=1 Clock_Delay1ms(500); // 500ms } //********SPI1_Init***************** // Initialize SPI1 interface to SDC and TFT // inputs: useSDC, 1 for enabling SDC features // outputs: none // outputBitRate = (spiInputClock) / ((1 + SCR) * 2) // 99 for 400,000 bps slow mode, used during initialization // 4 for 8,000,000 bps fast mode, used during disk I/O // Version for both SDC and TFT int SPI1_init_Flag=0; void SPI1_Init(void){ if(SPI1_init_Flag) return; CS_Init(); uint32_t busfreq = Clock_Freq(); // assumes GPIOA and GPIOB are reset and powered previously // RSTCLR to SPI1 peripherals // bits 31-24 unlock key 0xB1 // bit 1 is Clear reset sticky bit // bit 0 is reset gpio port SPI1->GPRCM.RSTCTL = 0xB1000003; // Enable power to SPI1 peripherals // PWREN // bits 31-24 unlock key 0x26 // bit 0 is Enable Power SPI1->GPRCM.PWREN = 0x26000001; // configure PB9 PB6 PB8 as alternate SPI1 function IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000083; // SPI1 SCLK IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000083; // SPI1 PICO IOMUX->SECCFG.PINCM[TFT_RST_INDEX] = 0x00000081; // GPIO output, LCD !RST IOMUX->SECCFG.PINCM[TFT_DC_INDEX] = 0x00000081; // GPIO output, LCD D/C RS IOMUX->SECCFG.PINCM[PB7INDEX] = 0x00040083; // SPI1 POCI IOMUX->SECCFG.PINCM[TFT_CS_INDEX] = 0x00000081; // PB6 is regular GPIO Clock_Delay(24); // time for gpio to power up TFT_DC->DOE31_0 |= TFT_DC_PIN; // PB16 is LCD RS TFT_DC_HIGH(); TFT_RST->DOE31_0 |= TFT_RST_PIN; TFT_RST_HIGH(); TFT_CS->DOE31_0 |= TFT_CS_PIN; // PB6 is regular GPIO TFT_CS_HIGH(); // disable LCD SPI1->CLKSEL = 8; // SYSCLK // bit 3 SYSCLK // bit 2 MFCLK // bit 1 LFCLK SPI1->CLKDIV = 0; // divide by 1 // bits 2-0 n (0 to 7), divide by n+1 //Set the bit rate clock divider to generate the serial output clock // outputBitRate = (spiInputClock) / ((1 + SCR) * 2) // 8,000,000 = (16,000,000)/((0 + 1) * 2) // 8,000,000 = (32,000,000)/((1 + 1) * 2) // 6,666,667 = (40,000,000)/((2 + 1) * 2) // 10,000,000 = (40,000,000)/((1 + 1) * 2) // 8,000,000 = (80,000,000)/((4 + 1) * 2) // 8,000,000 = (Clock_Freq)/((m + 1) * 2) // m = (Clock_Freq/16000000) - 1 if(busfreq <= 16000000){ SPI1->CLKCTL = 0; // frequency= busfreq/2 }else if(busfreq == 40000000){ SPI1->CLKCTL = 1; // frequency= 10MHz // SPI1->CLKCTL = 2; // frequency= 6.66MHz }else{ SPI1->CLKCTL = busfreq/16000000 -1; // 8 MHz } SPI1->CTL0 = 0x0007; // bit 14 CSCLR=0 not cleared // bits 13-12 CSSEL=0 CS0 // bit 9 SPH = 0 // bit 8 SPO = 0 // bits 6-5 FRF = 00 (3 wire) // bits 4-0 n=7, data size is n+1 (8bit data) SPI1->CTL1 = 0x0015; // bits 29-24 RXTIMEOUT=0 // bits 23-16 REPEATX=0 disabled // bits 15-12 CDMODE=0 manual // bit 11 CDENABLE=0 CS3 // bit 7-5 =0 no parity // bit 4=1 MSB first // bit 3=0 POD (not used, not peripheral) // bit 2=1 CP controller mode // bit 1=0 LBM disable loop back // bit 0=1 enable SPI SPI1_init_Flag = 1; SPI1_Reset(); } /* STAT register * 4 BUSY 0h = SPI is in idle mode. 1h = SPI is currently transmitting and/or receiving data, or transmit FIFO is not empty. 3 RNF Receive FIFO not full 0h = Receive FIFO is full. 1h = Receive FIFO is not full. 2 RFE Receive FIFO empty. 0h = Receive FIFO is not empty. 1h = Receive FIFO is empty. 1 TNF Transmit FIFO not full 0h = Transmit FIFO is full. 1h = Transmit FIFO is not full. 0 TFE Transmit FIFO empty. 0h = Transmit FIFO is not empty. 1h = Transmit FIFO is empty.*/ //---------TFT_OutData------------ // Output 8-bit data to SPI port // Input: data is an 8-bit data to be transferred // Output: none void TFT_OutData(char data){char response; while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy SDC_CS_HIGH(); // PB0 high, disable SDC TFT_CS_LOW(); // PB6 low, enable TFT TFT_DC_HIGH(); // RS=PB16=1 for data SPI1->TXDATA = data; while((SPI1->STAT&0x04) == 0x04){}; // spin SPI RxFifo empty response = SPI1->RXDATA; // has no meaning, flush TFT_CS_HIGH(); // PB6 high } //---------TFT_OutCommand------------ // Output 8-bit command to SPI port // Input: data is an 8-bit data to be transferred // Output: none void TFT_OutCommand(char command){char response; while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy SDC_CS_HIGH(); // PB0 high, disable SDC TFT_CS_LOW(); // PB6 low, enable TFT TFT_DC_LOW(); // RS=PB16=0 for command SPI1->TXDATA = command; while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy response = SPI1->RXDATA; // has no meaning, flush TFT_CS_HIGH(); // PB6 high }