cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407G-DISC1 code fails to draw a rectangle on ILI9341 screen

cockatoo
Associate II

I want to use ILID9341 screen on STM32F407G-DISC1 without any libraries, just spi protocol. However, while the screen turns on, I can't draw anything on it. Why is this the case?
This is SPI configuration.
I have checked the physical wiring, and it seems to be correct.

cockatoo_0-1764236463924.png

This is the code that is relevant for the LCD screen (there is also a code for functions for reading values from accelometer, which is probably not relevant).

#define RST_GPIO_PORT GPIOD
#define RST_GPIO_PIN GPIO_PIN_9
#define DC_GPIO_PORT GPIOB
#define DC_GPIO_PIN GPIO_PIN_14
#define SET_ROW_CMD 0x2B
#define SET_COL_CMD 0x2A
#define SET_COLOR_CMD 0x2C

void Reset_Screen() {
	HAL_GPIO_WritePin(RST_GPIO_PORT, RST_GPIO_PIN, GPIO_PIN_RESET); // pull low
	HAL_Delay(10);
	HAL_GPIO_WritePin(RST_GPIO_PORT, RST_GPIO_PIN, GPIO_PIN_SET);   // release reset
	HAL_Delay(120);
}

void Write_Rectangle(uint16_t row_lower, uint16_t row_higher, uint16_t col_lower, uint16_t col_higher, uint16_t color) {
	//16 bit/pixel color order (R:5-bit, G:6-bit, B:5-bit)

	//1.Set column
	uint8_t col_params[4] = {(uint8_t)(col_lower >> 8), (uint8_t)(col_lower & 0x00FF),
								(uint8_t)(col_higher >> 8), (uint8_t)(col_higher & 0x00FF)};
	HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_RESET); //send command
	uint8_t set_col_cmd[] = {SET_COL_CMD};
	HAL_SPI_Transmit(&hspi2,  set_col_cmd, 1, 100);
	HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_SET); //send data for command
	HAL_SPI_Transmit(&hspi2, col_params, 4, 100);

	//2.Set row
	uint8_t row_params[4] = {(uint8_t)(row_lower >> 8), (uint8_t)(row_lower & 0x00FF),
							(uint8_t)(row_higher >> 8), (uint8_t)(row_higher & 0x00FF)};
	uint8_t set_row_cmd[] = {SET_ROW_CMD};
	HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_RESET); //send command
	HAL_SPI_Transmit(&hspi2,  set_row_cmd, 1, 100);
	HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_SET); //send data for command
	HAL_SPI_Transmit(&hspi2, row_params, 4, 100);

	//3.Set color
	HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_RESET); //send command
	uint8_t set_color_cmd[] = {SET_COLOR_CMD};
	HAL_SPI_Transmit(&hspi2, set_color_cmd, 1, 100);
	HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_SET); //send data for command

	// Compute rectangle size
	uint32_t width  = col_higher - col_lower + 1;
	uint32_t height = row_higher - row_lower + 1;
	uint32_t pixels = width * height;
	// Send color repeatedly
	//uint8_t *pix_array = malloc(pixels);
	//for (int i = 0; i < ; i++) {
	//	pix_array[i] =
	//}
	uint8_t pix[2] = {(uint8_t)(color >> 8), (uint8_t)(color & 0xFF)};
	//uint32_t total_bytes = pixels * 2;
	//HAL_SPI_Transmit(&hspi2, pix_array, total_bytes, 1000);
	//free(pix_array);
	for (uint32_t i = 0; i < pixels; i++) {
	        HAL_SPI_Transmit(&hspi2, pix, 2, 100);
	}
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */


  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2S3_Init();
  MX_SPI1_Init();
  MX_USB_HOST_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
  //Reset the screen
  Reset_Screen();

  //screen experiment, delete later (black rectangle)
  Write_Rectangle(0, 10, 0, 10, 0);

  //Set everything for the accelometer
  uint8_t OUT_X_L = 0x28;
  uint8_t OUT_X_H = 0x29;
  uint8_t OUT_Y_L = 0x2A;
  uint8_t OUT_Y_H = 0x2B;
  uint8_t OUT_Z_L = 0x2C;
  uint8_t OUT_Z_H = 0x2D;
  uint8_t CTRL_REG5 = 0x24; //for sensitivity (change from default 2 to 4/8) (bits 5-4)
  //receivers and transmitters
  //xyz_addr[6] = {OUT_X_L, OUT_X_H, OUT_Y_L, OUT_Y_H, OUT_Z_L, OUT_Z_H};
  xyz_addr[0] = OUT_X_L; xyz_addr[1] = OUT_X_H; xyz_addr[2] = OUT_Y_L; xyz_addr[3] = OUT_Y_H; xyz_addr[4] = OUT_Z_L; xyz_addr[5] = OUT_Z_H;
  uint8_t ctrl_val;

  //fill buffer with 0s
  for (int i = 0; i < ACC_BUF_SIZE; i++) {
	  acc_values_buf[i] = 0;
  }

  //receive and set accelerator sensitivity
  HAL_SPI_TransmitReceive(&hspi1, &CTRL_REG5, &ctrl_val, 1, 100);
  ctrl_val =  ctrl_val | 0x08; //set FSCALE0
  uint8_t tx_buf[2];
  tx_buf[0] = CTRL_REG5 & 0x7F; //register address
  tx_buf[1] = ctrl_val; //data to write into it
  HAL_SPI_Transmit(&hspi1, tx_buf, 1, 100);

  //initial dma call
  HAL_SPI_TransmitReceive_DMA(&hspi1, xyz_addr, xyz_data, 6);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    /* USER CODE END WHILE */
    MX_USB_HOST_Process();

    /* USER CODE BEGIN 3 */
    //During each SPI clock cycle, full-duplex transmission of a single bit occurs.
    //1 clock cycle is 168/32MHz = 5.25MHz
    //meaning there are 5.25 * 10^6 bit, or 5.25 * 10^6/8 bytes = 656250 bytes
    //or, all coordinates are received 109375 times.

  }
  /* USER CODE END 3 */
}

I think that my initialization function may be not complete, but I don't know how to complete it.

18 REPLIES 18

I don't have either oscilloscope, or logic analyzer.

Then you are running blind !

Microcontroller programming at this level is not just about software; you are also dealing directly with the hardware - so you need the tools for that.

There are other discovery boards which include a display - perhaps you'd be better off with one of them?

eg, 32F469IDISCOVERY:

 

AndrewNeil_1-1764676985002.png

 

PS:

You can get a simple logic analyser for under $10 on the likes of Amazon, ebay, et al ...

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

I think I know what is wrong. I have skimmed through HAL_Transmit, and it does not do anything to NSS. But the device expects an edge in NSS every time it starts receiving data. So, I need to toggle it manually.

> You can get a simple logic analyser for under $10 on the likes of Amazon, ebay, et al ...

Like these here : https://sigrok.org/wiki/MCU123_Saleae_Logic_clone
Cheaper than most STM32 boards.
I have two of them, and they work fine - even with the logic analyser software downloaded from the Salae website.
Although I prefer PulseView.
And both PulseView and the Salae software come with inbuilt protocol interpreters. You just define the proper SPI lines, and it shows the transfered data values in the display.

> I think that my initialization function may be not complete, but I don't know how to complete it.
Anyway, I think you need to bite the bullet, go through the LCD driver datasheet in detail, and check if your code correctly implements the required initialisation sequence.

And of course you need to validate all electrical connections (power supply, SPI pins, GPIO signals) before that.
But this can be done statically, with an ohmmeter. Next step would be to check the voltages on the display side while power is on.

> I think I know what is wrong. I have skimmed through HAL_Transmit, and it does not do anything to NSS. But the device expects an edge in NSS every time it starts receiving data. So, I need to toggle it manually.

Check the SPI init code.
If it's configured for hardware control, it will be done automatically.
It "software NSS management" is set, you have to do it yourself.
This is usually reflected in SPIx->CR1 register bits, you can check in the debugger.

It is set as hardware output, so it is probably alright.

static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_HARD_OUTPUT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

 

The datasheet doesn't contain the initialization sequence, I am afraid. However, I modified my init function based on the one from the driver (https://github.com/STMicroelectronics/stm32-ili9341/blob/main/ili9341.c.). I have shared the new code earlier today.

> It is set as hardware output, so it is probably alright.

I'm afraid not.
Again, check with the LCD driver datasheet. Most SPI transfers happen as combined transfer, e.g. SPI slave internal register first, value to write next. To finalize such a transmission, a L->H transition of /NSS is usually required.
Here the respective section from the STM32F303 RM as example:

Ozone_0-1764681639578.png

SPI is not as "standardized" as e.g. I2C, you will need specific settings and sequences for different slave devices.

And a scope - or logic analyser - is really helpful the validate your code.


@cockatoo wrote:

I think I know what is wrong. I have skimmed through HAL_Transmit, and it does not do anything to NSS. But the device expects an edge in NSS every time it starts receiving data. So, I need to toggle it manually.


You would have seen this immediately with a logic analyser (or a scope) !

Note that the behaviour of the STM32F4's hardware NSS control is (probably) not what you require - see the RM extract posted by @Ozone.

So you need to set it to software control, and then manually assert & release NSS as required.

 

 

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.