Hello. I am starting work with a new project. The LCD with which they chose based on ST7789H2 driver and works through the SPI protocol, but instead of two lines MISO and MOSI, only one is used, which changes from MISO to MOSI. I have not met this before. Are there any configuration examples and driver for the K21F120 MCU with this type of SPI?
Solved! Go to Solution.
Hi Evgeni
Although the PIT may be able to interrupt at a 60MHz rate no Kinetis parts will be able to keep up with it due to the Cortex interrupt overhead, resulting in the much reduced 3MHz that you are seeing (it is only managing to handle about every 20th PIT interrupt!).
If you would like high speed SPI you must use the SPI interface since timer interrupt controlled bit banging has a high overhead that results in much lower speeds, with the processor working as hard as it can. If you do bit banging without timer control you will also find an improvement in performance since the interrupt overhead will not be present.
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Hi
You may be able to fulfill the requirement by connecting the MISO and MISO lines together and configuring the MISO to open drain. It will need a pull-up of maybe 1k (not the internal one) to ensure that a fairly high bus speed is still possible.
Regards
Mark
Hi Mark.
I wrote GPIO driver that simulates this communication the following problems appeared:
// SIM_SCGC6: PIT=1
SIM_SCGC6 |= SIM_SCGC6_PIT_MASK;
// Enable device clock
PIT_MCR = (uint32)0x00UL;
// Clear control register
PIT_TCTRL0 = (uint32)0x00UL;
// Clear timer flag register
PIT_TFLG0 = PIT_TFLG_TIF_MASK;
// Set up load register PIT_LDVAL0: 60MHz
PIT_LDVAL0 = PIT_LDVAL_TSV(PLAY_TIMER_PERIOD);
NVICIP48 = NVIC_IP_PRI48(0x00);
// Set up control register for Timer 0
PIT_TCTRL0 = (PIT_TCTRL_TEN_MASK | PIT_TCTRL_TIE_MASK);
PIT_TCTRL0 = (uint32_t)((PIT_TCTRL0 & (uint32_t)~(uint32_t)(PIT_TCTRL_TEN_MASK | 0xFFFFFFF8U)) |
(uint32_t)(PIT_TCTRL_TIE_MASK));
// Clear interrupt flag
PIT_ClearInterruptFlag(PIT_BASE_PTR, PIT_CHANNEL_0);
// Enable interrupts
Enable_IRQ(PIT0_IRQn);
To prepare command I use a function for every command.
typedef enum _SEND_Type
{
SEND_COMMAND, // Send Command
SEND_DATA_PARAM // Send Data or Parameter
} SEND_Type;
typedef enum _DATA_Type
{
DATA_CONTINUED, // Send continued data
DATA_SIMPLE_COLOR // Send simple color
} DATA_Type;
typedef struct
{
uint8_t Command; // Command for send to LCD
SEND_Type WhatSend; // Send Command or Data
DATA_Type WhatData; // Type of sent data
uint32_t DataLen; // LCD *data len
uint8_t *Data; // Pointer to data that contain LCD parameters
}LCD_SPI_PACKET;
For example,
/******************************************************************************
* Function Name: LCD_TFT_RAMWR_2C (Command 0x2C)
* Description: Memory write
* Parameters: none
* Return: none
* Build_Date: 11/01/2020
* Notes: --
******************************************************************************/
void LCD_TFT_RAMWR_2C(DATA_Type DataType, uint32_t DataLen, uint8_t *Data)
{
LCD_SPI.WhatSend = SEND_COMMAND;
LCD_SPI.Command = TFT_RAMWR;
LCD_SPI.WhatData = DataType;
if(DataType == DATA_SIMPLE_COLOR)
{
LCD_SPI.DataLen = DataLen * 2;
TFT_RAMWR_SimpleData[1] = Data[0];
TFT_RAMWR_SimpleData[0] = Data[1];
LCD_SPI.Data = TFT_RAMWR_SimpleData;
}
else
{
LCD_SPI.DataLen = DataLen;
LCD_SPI.Data = Data;
}
// Start timer for send Command/Data
TIMER_Enable(MAIN_TIMER);
while(MainTimer_Enabled);
}
// Global array
uint8_t TFT_FRCTRL1_Data[] = {0x54, 0x3F};
/******************************************************************************
* Function Name: LCD_TFT_FRCTRL1_B3 (0xB3)
* Description: Frame Rate Control 1
* Parameters: none
* Return: none
* Build_Date: 11/01/2020
* Notes: --
******************************************************************************/
void LCD_TFT_FRCTRL1_B3(void)
{
LCD_SPI.WhatSend = SEND_COMMAND;
LCD_SPI.Command = TFT_FRCTRL1;
LCD_SPI.DataLen = 2;
LCD_SPI.Data = TFT_FRCTRL1_Data;
// Start timer for send Command/Data
TIMER_Enable(MAIN_TIMER);
while(MainTimer_Enabled);
}
On timer event, I change CLK and on CLK low state call the next function:
void LCD_SPI_Send_Command(void)
{
static uint8_t cmd_state = 0;
static uint8_t SendVal = 0;
static uint32_t DataIndx = 0;
static uint8_t bit = 7;
switch(cmd_state)
{
case 0:
// CS='0' - Chip select enable
LCD_SPI_CS_ENABLE;
// 30ns delay
__NOP();
// DC=’0’- command selection pin in parallel interface
LCD_SPI_DC_COMMAND;
// 30ns delay
__NOP();
// Start to send Command value
// Set MSB bit on SDA
if((LCD_SPI.Command&0x80) == 0U)
{
SDA_PIN_SET_LOW;
}
else
{
SDA_PIN_SET_HIGH;
}
// Shift to the next bit at the next rising edge of SCL Shift<<1
SendVal = LCD_SPI.Command<<1;
bit--;
cmd_state++;
break;
case 1:
// Continue to send Command value
// Set MSB bit on SDA
if((SendVal&0x80) == 0U)
{
SDA_PIN_SET_LOW;
}
else
{
SDA_PIN_SET_HIGH;
}
if(bit > 0)
{
bit--;
// Shift to the next bit at the next rising edge of SCL Shift<<1
SendVal <<= 1;
}
else
{
bit = 7;
if(LCD_SPI.DataLen > 0)
{
cmd_state++;
}
else
{
cmd_state = 4;
}
}
break;
case 2:
LCD_SPI_DC_DATA_PARAM;
// 30ns delay
__NOP();
// Start to send Data
SendVal = LCD_SPI.Data[DataIndx];
// Set MSB bit on SDA
if((LCD_SPI.Data[DataIndx]&0x80) == 0U)
{
SDA_PIN_SET_LOW;
}
else
{
SDA_PIN_SET_HIGH;
}
// Shift to the next bit at the next rising edge of SCL Shift<<1
SendVal = LCD_SPI.Data[DataIndx]<<1;
bit--;
cmd_state++;
break;
case 3:
// Continue send Data values
// Set MSB bit on SDA
if((SendVal&0x80) == 0U)
{
SDA_PIN_SET_LOW;
}
else
{
SDA_PIN_SET_HIGH;
}
// Shift to the next bit at the next rising edge of SCL Shift<<1
SendVal <<= 1;
if(bit > 0)
{
bit--;
}
else
{
bit = 7;
if(--LCD_SPI.DataLen > 0)
{
if(LCD_SPI.WhatData == DATA_CONTINUED)
{
SendVal = LCD_SPI.Data[++DataIndx];
}
else if(LCD_SPI.WhatData == DATA_SIMPLE_COLOR)
{
SendVal = LCD_SPI.Data[DataIndx];
DataIndx ^= 1;
}
}
else
{
cmd_state++;
}
}
break;
case 4:
// CS='1' - Chip select disable
LCD_SPI_CS_DISABLE;
cmd_state++;
break;
case 5:
TIMER_Disable(MAIN_TIMER);
cmd_state = 0;
DataIndx = 0;
break;
}
}
As a result, I can config LCD and fill any color, but only once. After this no reaction. I tried different configurations but nothing helps. LCD does not respond next time.
LCD configuration:
Version 1
LCD_TFT_SWRESET_01();
Delay_us(150000);
LCD_TFT_SLPOUT_11();
Delay_us(120000);
LCD_TFT_COLMOD_3A();
LCD_TFT_MADCTL_36();
//LCD_TFT_INVON_21();
LCD_TFT_INVOFF_20();
LCD_TFT_NORON_13();
LCD_TFT_DISPON_29();
Delay_us(150000);
ST7789_FillScreen(BLUE);
Version 2
LCD_TFT_SWRESET_01();
Delay_us(120000);
LCD_TFT_SLPOUT_11();
Delay_us(120000);
LCD_TFT_GAMSET_26(); // Data 0x01
LCD_TFT_TEON_35();
LCD_TFT_MADCTL_36(); // Data 0x08
LCD_TFT_INVON_21();
LCD_TFT_COLMOD_3A(); // Data 0x55
LCD_TFT_FRCTRL1_B3(); // Data 0x54, 0x3F
LCD_TFT_LCMCTRL_C0(); // Data 0x01
LCD_TFT_IDSET_C1(); // Data 0x06
LCD_TFT_VDVS_C4(); // Data 0x25, 0x00
LCD_TFT_VMCTR1_C5(); // Data 0x44, 0x64
LCD_TFT_DISPON_29();
ST7789_FillScreen(BLUE);
Hi Evgeni
Although the PIT may be able to interrupt at a 60MHz rate no Kinetis parts will be able to keep up with it due to the Cortex interrupt overhead, resulting in the much reduced 3MHz that you are seeing (it is only managing to handle about every 20th PIT interrupt!).
If you would like high speed SPI you must use the SPI interface since timer interrupt controlled bit banging has a high overhead that results in much lower speeds, with the processor working as hard as it can. If you do bit banging without timer control you will also find an improvement in performance since the interrupt overhead will not be present.
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]