Jump to content

STM32L1 nucleo + ILI9225


tombalabomba

Recommended Posts

I struggle to get my ILI9225 driven display on a STM32L1 nucleo board running. Essentially.

Below you can find my application code and the board file I use. 

 

main.c :

#include "stm32l1xx.h"
#include "stm32l1xx_nucleo.h"
#include "gfx.h"
#include "stm32l1xx_hal.h"
#include "board_ILI9225.h"

void SystemClock_Config(void);
void Error_Handler(void);

int main(void)
{
	coord_t width, height;
	coord_t i, j;

    HAL_Init();
    SystemClock_Config();

    gfxInit();

	width = gdispGetWidth();
	height = gdispGetHeight();


	gdispDrawBox(10, 10, width/2, height/2, Yellow);
	gdispFillArea(width/2, height/2, width/2-10, height/2-10, Blue);
	gdispDrawLine(5, 30, width-50, height-40, Red);


	while (TRUE) {

		for (i = 5, j = 0; i < width && j < height; i += 7, j += i/20) {
			gdispDrawPixel(i, j, White);
		}

		gfxSleepMilliseconds(500);
		HAL_GPIO_TogglePin(LD2_PORT, LD2_Pin);
	}


}




void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /**Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /**Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  RCC_OscInitStruct.PLL.PLLDIV = RCC_PLL_DIV3;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /**Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}



void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

 

board_ILI9225.h:



#ifndef _GDISP_LLD_BOARD_H
#define _GDISP_LLD_BOARD_H

#include "stm32l1xx_hal.h"
#include "stm32l1xx_hal_spi.h"
#include "gfx.h"

#define LCD_PORT 			GPIOB
#define LCD_DC_Pin 			GPIO_PIN_2
#define LCD_RST_Pin 		GPIO_PIN_11
#define LCD_SPI_SS_Pin 		GPIO_PIN_12
#define LCD_SPI_SCK_Pin 	GPIO_PIN_13
#define LCD_SPI_MOSI_Pin	GPIO_PIN_15

#define LD2_PORT			GPIOA
#define LD2_Pin 			GPIO_PIN_5


SPI_HandleTypeDef hspi2;





//-------------------------------------------------------------------------
static GFXINLINE void init_board(GDisplay *g)
{

	GPIO_InitTypeDef GPIO_InitStruct;

	(void)g;

	// SPI configuration
	__SPI2_CLK_ENABLE();

	/*
	CLKPolarity			CLKPhase			MOSI changes on		SCK value when inactive
	SPI_POLARITY_HIGH	SPI_PHASE_2EDGE		Falling Edge		High
	SPI_POLARITY_LOW	SPI_PHASE_2EDGE		Rising Edge			Low
	SPI_POLARITY_LOW	SPI_PHASE_1EDGE		Falling Edge		Low
	SPI_POLARITY_HIGH	SPI_PHASE_1EDGE		Rising Edge			High
	 */


	hspi2.Instance = SPI2;
	hspi2.Init.Mode = SPI_MODE_MASTER;
	hspi2.Init.Direction = SPI_DIRECTION_1LINE;
	hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
	hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
	hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
	hspi2.Init.NSS = SPI_NSS_SOFT;
	hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
	hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
	hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
	hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	hspi2.Init.CRCPolynomial = 10;

	HAL_SPI_Init(&hspi2);
	__HAL_SPI_ENABLE(&hspi2);



	// GPIO configuration

	// Peripheral clocks
	__HAL_RCC_GPIOA_CLK_ENABLE();

	// Green LED (PA5)
	GPIO_InitStruct.Pin = LD2_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(LD2_PORT, &GPIO_InitStruct);


	__HAL_RCC_GPIOB_CLK_ENABLE();

	//SPI
	GPIO_InitStruct.Pin = LCD_SPI_SCK_Pin | LCD_SPI_MOSI_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
	HAL_GPIO_Init(LCD_PORT, &GPIO_InitStruct);


	// LCD Chip Select (PB12)
	GPIO_InitStruct.Pin = LCD_SPI_SS_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	HAL_GPIO_Init(LCD_PORT, &GPIO_InitStruct);


	// LCD DC (PB10)
	GPIO_InitStruct.Pin = LCD_DC_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	HAL_GPIO_Init(LCD_PORT, &GPIO_InitStruct);

	// LCD Reset (PB11)
	GPIO_InitStruct.Pin = LCD_RST_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	HAL_GPIO_Init(LCD_PORT, &GPIO_InitStruct);


	HAL_GPIO_WritePin(LCD_PORT, LCD_RST_Pin, GPIO_PIN_SET);
	HAL_GPIO_WritePin(LCD_PORT, LCD_SPI_SS_Pin, GPIO_PIN_SET);
}


//-------------------------------------------------------------------------
static GFXINLINE void post_init_board(GDisplay* g)
{
	(void) g;
}


//-------------------------------------------------------------------------
static GFXINLINE void setpin_reset(GDisplay* g, bool_t state)
{
	(void) g;

	if (state) {
		HAL_GPIO_WritePin(LCD_PORT, LCD_RST_Pin, GPIO_PIN_RESET);
	}
	else {
		HAL_GPIO_WritePin(LCD_PORT, LCD_RST_Pin, GPIO_PIN_SET);
	}
}

//-------------------------------------------------------------------------
static GFXINLINE void set_backlight(GDisplay* g, uint8_t percent)
{
	(void) g;
}


//-------------------------------------------------------------------------
static GFXINLINE void acquire_bus(GDisplay* g)
{
	HAL_GPIO_WritePin(LCD_PORT, LCD_SPI_SS_Pin, GPIO_PIN_RESET);
	(void) g;
}


//-------------------------------------------------------------------------
static GFXINLINE void release_bus(GDisplay* g)
{
	HAL_GPIO_WritePin(LCD_PORT, LCD_SPI_SS_Pin, GPIO_PIN_SET);
	(void) g;
}


//-------------------------------------------------------------------------
static GFXINLINE void write_cmd(GDisplay* g, uint16_t index)
{
	(void) g;

	uint8_t tx_data[2];
	tx_data[0] = index >> 8;
	tx_data[1] = 0x00FF & index;


	HAL_GPIO_WritePin(LCD_PORT, LCD_DC_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi2, tx_data, 2, HAL_MAX_DELAY);

}


//-------------------------------------------------------------------------
static GFXINLINE void write_data(GDisplay* g, uint16_t data)
{
	(void) g;

	uint8_t tx_data[2];
	tx_data[0] = data >> 8;
	tx_data[1] = 0x00FF & data;

	HAL_GPIO_WritePin(LCD_PORT, LCD_DC_Pin, GPIO_PIN_SET);
	HAL_SPI_Transmit(&hspi2, tx_data, 2, HAL_MAX_DELAY);

}


//-------------------------------------------------------------------------
static GFXINLINE void setreadmode(GDisplay* g)
{
	(void) g;
}


//-------------------------------------------------------------------------
static GFXINLINE void setwritemode(GDisplay* g)
{
	(void) g;
}


//-------------------------------------------------------------------------
static GFXINLINE uint16_t read_data(GDisplay* g)
{
	(void) g;
	return 0;
}


#endif /* _GDISP_LLD_BOARD_H */

 

Unfortunately, expect of backlight illumination the display does nothing. Hence, I checked the SPI communication which looks as follows (D0 = reset, D1 = chip select, D2 = DC, D3 = SCK, D4 = MOSI)

scope_1.png show the digital lines triggered on the falling edge of the reset and scope_2.png shows the SPI communication triggered on the falling edge of chip select.

 

Since the display I use looks pretty much the same like the one shown in http://www.lcdwiki.com/2.0inch_Arduino_SPI_Module_ILI9225_SKU:MAR2001 . I already checked if it's really am ILI9225 driven display and if it's functional by flashing the library  https://github.com/jorgegarciadev/TFT_22_ILI9225 on an Arduino. Coclusio: The performance is not overwhelming but the display is working. That's basically good news for me since that means I only have a bug in my code.

So the question is what do I wrong? Any hints are highly appreciated.

 

 

 

 

scope_1.png

scope_2.png

Link to comment
Share on other sites

The ILIxxxx display controllers are known to suffer from differences across series and revisions. Also, some (chinese) sources love to sell you an ILI9225 as an ILI9220 and so on. Therefore, once you confirmed that the SPI is working correctly, try to grab the initialization code (sequence) from your supplied and plug it into the init() function of the corresponding µGFX driver.

Another thing you might want to do is trying to read out the display controllers version register / chip ID. This will tell you exactly what display controller you have and will also confirm that the communication is working well.

Link to comment
Share on other sites

Meanwhile, I ended up with a working configuration. Basically, I had to fix two problems. The first was indeed the wrong SPI configuration. In contrast to many other ILI driver  the ILI9225 drivers requires CPOL = 1:

hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;

On the other hand there was also a problem with the chip select signal. Controlling CS in acquire_bus(GDisplay* g) and release_bus(GDisplay* g) didn't do the job and I had to control the chip select signal right before and after data transmission. 

Edited by tombalabomba
Link to comment
Share on other sites

  • 9 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...