ili9341 и STM32F4Discovery

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Sergey_78r
Родился
Сообщения: 6
Зарегистрирован: Ср ноя 22, 2017 11:34:47

ili9341 и STM32F4Discovery

Сообщение Sergey_78r »

Добрый день. Пытаюсь подключить экран с контроллером ili9341 к stm32f4-discovery.
У модуля с экраном 9 ног для подключения по SPI (VCC, GND, CS, RESET, D/C, MOSI, SCK, LED, MISO)
К STM подключил так:

Код: Выделить всё

TFT					STM32
-------------------
VCC   			        5v
GND					GND
CS					PB11
RESET				PB12
DC					PB10
MOSI				PA7
SCK					PA5
LED					3v
MISO				PA6
При включении подсветка включается, экран равномерно белый.

Для проверки работы, пытаюсь прочитать из экрана его ID. Согласно datasheet, надо отправить команду 0x4, и 4 раза прочитать из экрана. В последних трех ответах будет ID (стр. 91 даташита). Команду шлю и читаю ответ в бесконечном цикле.
Для отладки, полученные значения шлю в UART. В ответ приходит 0, 0x3F и дальше все ответы 0xFF.
Для проверки работы SPI я слал с SPI1 на плате на SPI2 и обратно. Все работает, отправляется и принимает.
Буду очень благодарен, если подскажете, что делаю не правильно. Во вложении проект из Keil5.
Весь код специально написал прямо в main(), чтоб не искать по функциям. Убрал только работу с uart.
Пробовал устанавливать LSBFIRST (порядок бит) и менять скорость baute rate, но результат тот же.

Код: Выделить всё

#include "main.h"

/*
Подключение экрана:
TFT					STM32
-------------------
VCC   			5v
GND					GND
CS					PB11
RESET				PB12
DC					PB10
MOSI				PA7
SCK					PA5
LED					3v
MISO				PA6
*/



int main(void)
{
	//Инициализация UART2, используется для вывода отладки
	uart_init();

	//Включение тактирования порта B (для DC, RST, CS)
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;   
	//Режим output для пинов 10, 11, 12
	GPIOB->MODER |= GPIO_MODER_MODE10_0|GPIO_MODER_MODE11_0|GPIO_MODER_MODE12_0;
	//Скорость порта Very High speed для 10, 11, 12
	GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED10_0|GPIO_OSPEEDR_OSPEED10_1;
	GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED11_0|GPIO_OSPEEDR_OSPEED11_1;
	GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED12_0|GPIO_OSPEEDR_OSPEED12_1;
	//Установка подтяжки:
	//Обнуление на всякий случай 
	GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPD10_0|GPIO_PUPDR_PUPD10_1);
	GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPD11_0|GPIO_PUPDR_PUPD11_1);
	GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPD12_0|GPIO_PUPDR_PUPD12_1);
	//Установка подтяжки к GND (Pull-down)
	GPIOB->PUPDR |= GPIO_PUPDR_PUPD10_1|GPIO_PUPDR_PUPD11_1|GPIO_PUPDR_PUPD12_1;
	
	//Перед инициализацией SPI, устанавливаю CS в HIGH, т.е. отключаю выбор (активное состояние LOW)
	GPIOB->BSRR |= GPIO_BSRR_BS11;

	//Инициализация SPI
	//Включить тактирование SPI1
	RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
	//Включить тактирование порта А
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;   
	//Включение AF5 на пинах 5,6,7
	GPIOA->AFR[0] |= GPIO_AFRL_AFSEL5_0|GPIO_AFRL_AFSEL5_2;
	GPIOA->AFR[0] |= GPIO_AFRL_AFSEL6_0|GPIO_AFRL_AFSEL6_2;
	GPIOA->AFR[0] |= GPIO_AFRL_AFSEL7_0|GPIO_AFRL_AFSEL7_2;
	//Режим альтернативной функция для PA5,PA6,PA7
	GPIOA->MODER |= GPIO_MODER_MODE5_1|GPIO_MODER_MODE6_1|GPIO_MODER_MODE7_1;
  //Скорость High speed
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED5_0|GPIO_OSPEEDR_OSPEED5_1;
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED6_0|GPIO_OSPEEDR_OSPEED6_1;
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED7_0|GPIO_OSPEEDR_OSPEED7_1;
	//Режим push-pull
	GPIOA->OTYPER &= ~GPIO_OTYPER_OT5;
	GPIOA->OTYPER &= ~GPIO_OTYPER_OT6;
	GPIOA->OTYPER &= ~GPIO_OTYPER_OT7;
	//Отключение подтяжки на PA5, PA6, PA7
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD5_0|GPIO_PUPDR_PUPD5_1);
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD6_0|GPIO_PUPDR_PUPD6_1);
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD7_0|GPIO_PUPDR_PUPD7_1);
	//Режим работы по двум линиям
	SPI1->CR1 &= ~SPI_CR1_BIDIMODE;
	//Отключение CRC
	SPI1->CR1 &= ~SPI_CR1_CRCEN;
	//8-bit передача
	SPI1->CR1 &= ~SPI_CR1_DFF;
	//Програмное управление SS
	SPI1->CR1 |= SPI_CR1_SSI|SPI_CR1_SSM;
	//Режим работы SPI 0
	SPI1->CR1 &= ~SPI_CR1_CPHA;
	SPI1->CR1 &= ~SPI_CR1_CPOL;
	//Выбор скорости /8
	SPI1->CR1 &= ~SPI_CR1_BR;
  SPI1->CR1 |= (SPI_CR1_BR_1);
	//Установка флага Master
	SPI1->CR1 |= SPI_CR1_MSTR;
	//Выбор формата фрейма, какой бит передавать первым
	//SPI1->CR1 |= SPI_CR1_LSBFIRST;
	//Включение SPI1
	SPI1->CR1 |= SPI_CR1_SPE;
  
	//Установка CS в LOW, для выбора slave
	GPIOB->BSRR |= GPIO_BSRR_BR11;

	//Судя по описанию ili9341, RST активен кошда LOW. 
	//Т.е. для работы, нужно установить в HIGH
	GPIOB->BSRR |= GPIO_BSRR_BS12;

	//Экрану буду слать только команду на чтение, поэтому DC в LOW
	GPIOB->BSRR |= GPIO_BSRR_BR10;
	
	
	//Перед началом работы надо отправить команду SOFWARE RESET (0x1)
	//Пока бит TXE не будет установлен, просто ждать
	while(!(SPI1->SR & SPI_SR_TXE)){};
	//Команда для чтения из экрана 
	SPI1->DR=0x1;
	//Дисплею надо 5ms, чтобы выполнить reset, тупая пауза с запасом
	for(int i=0; i < 10000000; i++);
	
while(1)
	{
			uint8_t d,f,g=0; //переменные, чтобы складывать ответы экрана
			
			//Пока бит TXE не будет установлен, просто ждать
			while(!(SPI1->SR & SPI_SR_TXE)){};
			//Команда для чтения из экрана 0x4
			//В ответ вернет 4 байта. В первом мусор, остальные 3 с ID
			SPI1->DR=0x4;
			//Сразу после отправки команды, в ответ сначала придет мусор
			d=SPI1->DR;
			
			//Чтобы получить ответ на команду, надо продолжить тактировать
			//передавая 0 (NOP)
			while(!(SPI1->SR & SPI_SR_TXE)){};
			SPI1->DR=0;
			// 1 из 3 байт ответа
			d=SPI1->DR;
				
			//Чтобы получить ответ на команду, надо продолжить тактировать
			//передавая 0
			while(!(SPI1->SR & SPI_SR_TXE)){};
			SPI1->DR=0;
			// 2 из 3 байт ответа
			f=SPI1->DR;

			//Чтобы получить ответ на команду, надо продолжить тактировать
			//передавая 0
			while(!(SPI1->SR & SPI_SR_TXE)){};
			SPI1->DR=0;
			// 3 из 3 байт ответа
			g=SPI1->DR;

		        sprintf(str_buffer, "d: %02X f: %02X g: %02X \r\n",d,f,g);
			uart_send_string(str_buffer);
			
			
		};
};
Добавлено after 19 minutes 33 seconds:
Извиняюсь, случайно создал тему не в том разделе.
Можно перенести в ARM?
Вложения
TFT_22_ili9341.rar
Проект в Keil5
(736.14 КБ) 304 скачивания
Реклама
jcxz
Мудрый кот
Сообщения: 1726
Зарегистрирован: Вт авг 15, 2017 10:51:13

Re: ili9341 и STM32F4Discovery

Сообщение jcxz »

[uquote="Sergey_78r",url="/forum/viewtopic.php?p=3239618#p3239618"]Добрый день. Пытаюсь подключить экран с контроллером ili9341 к stm32f4-discovery.
Для проверки работы, пытаюсь прочитать из экрана его ID. Согласно datasheet, надо отправить команду 0x4, и 4 раза прочитать из экрана. В последних трех ответах будет ID (стр. 91 даташита). Команду шлю и читаю ответ в бесконечном цикле.[/uquote]
Те LCD на ILI934x, что у меня есть, ни один не отвечает на команды чтения. И сколько я читал в форумах - и у других людей аналогично. Так что - забейте на чтение.
Инициализируйте и не будет белого экрана. Примеров инициализации в инете можно найти массу.
И на моей последней плате, на которой использую ILI934x, он подключен по совмещённому MOSI/MISO. И режим SPI тогда нужно программировать соответствующий. Хотя и на обычный SPI можно подключить.
В качестве примера подключения поищите даташит на отладочную плату STM32F429-DISCOVERY - на ней как раз стоит этот самый ILI9341 на SPI.

[uquote="Sergey_78r",url="/forum/viewtopic.php?p=3239618#p3239618"]Можно перенести в ARM?[/uquote]
А какое отношение чип контроллера LCD имеет к ARM? Если его подключить к другому МК, его работа разве как-то изменится?
Реклама
Аватара пользователя
Fusion
Грызет канифоль
Сообщения: 272
Зарегистрирован: Пт ноя 13, 2009 10:39:32
Откуда: Москва
Контактная информация:

Re: ili9341 и STM32F4Discovery

Сообщение Fusion »

После инициализации надо что то записать в экран - иначе ничего не происходит. Например очистить.
У меня на ili9341 отлично читает из дисплея. Вот код для F103:
Спойлер

Код: Выделить всё



#define Orient_Book			0xE8
#define Orient_Land			0x88

#define ILI9341_RESET					0x01
#define ILI9341_SLEEP_OUT			0x11
#define ILI9341_GAMMA					0x26
#define ILI9341_DISPLAY_OFF		0x28
#define ILI9341_DISPLAY_ON		0x29
#define ILI9341_COLUMN_ADDR		0x2A
#define ILI9341_PAGE_ADDR			0x2B
#define ILI9341_GRAM					0x2C
#define ILI9341_MAC			      0x36
#define ILI9341_PIXEL_FORMAT  0x3A
#define ILI9341_WDB			    	0x51
#define ILI9341_WCD				    0x53
#define ILI9341_RGB_INTERFACE 0xB0
#define ILI9341_FRC				    0xB1
#define ILI9341_BPC				    0xB5
#define ILI9341_DFC				    0xB6
#define ILI9341_POWER1				0xC0
#define ILI9341_POWER2				0xC1
#define ILI9341_VCOM1					0xC5
#define ILI9341_VCOM2					0xC7
#define ILI9341_POWERA				0xCB
#define ILI9341_POWERB				0xCF
#define ILI9341_PGAMMA				0xE0
#define ILI9341_NGAMMA				0xE1
#define ILI9341_DTCA					0xE8
#define ILI9341_DTCB					0xEA
#define ILI9341_POWER_SEQ			0xED
#define ILI9341_3GAMMA_EN			0xF2
#define ILI9341_INTERFACE			0xF6
#define ILI9341_PRC				    0xF7

#define WHITE			0xFFFF
#define BLACK			0x0000	  
#define BLUE			0x001F  
#define BRED			0XF81F
#define GRED			0XFFE0
#define GBLUE			0X07FF
#define RED				0xF800
#define MAGENTA		0xF81F
#define GREEN			0x07E0
#define CYAN			0x7FFF
#define YELLOW		0xFFE0
#define BROWN			0XBC40
#define BRRED			0XFC07
#define GRAY			0X8430
#define DGRAY			0X2104
#define DARKBLUE	0X01CF
#define LIGHTBLUE	0X7D7C 
#define GRAYBLUE	0X5458
#define LIGHTGREEN	0X841F
#define LGRAY			0XC618
#define LGRAYBLUE	0XA651
#define LBBLUE		0X2B12


void LCDinit(void)
{
	GPIO_ResetBits(GPIOB, LCD_RESET);
	Delay(10000);
	GPIO_SetBits(GPIOB, LCD_RESET);

  SendCMD(ILI9341_RESET);
  Delay(10000);
   
  SendCMD(ILI9341_POWERA);
  SendDAT(0x39);
  SendDAT(0x2C);
  SendDAT(0x00);
  SendDAT(0x34);
  SendDAT(0x02);
  SendCMD(ILI9341_POWERB);
  SendDAT(0x00);
  SendDAT(0xC1);
  SendDAT(0x30);
  SendCMD(ILI9341_DTCA);
  SendDAT(0x85);
  SendDAT(0x00);
  SendDAT(0x78);
  SendCMD(ILI9341_DTCB);
  SendDAT(0x00);
  SendDAT(0x00);
  SendCMD(ILI9341_POWER_SEQ);
  SendDAT(0x64);
  SendDAT(0x03);
  SendDAT(0x12);
  SendDAT(0x81);
  SendCMD(ILI9341_PRC);
  SendDAT(0x20);
  SendCMD(ILI9341_POWER1);
  SendDAT(0x23);
  SendCMD(ILI9341_POWER2);
  SendDAT(0x10);
  SendCMD(ILI9341_VCOM1);
  SendDAT(0x3E);
  SendDAT(0x28);
  SendCMD(ILI9341_VCOM2);
  SendDAT(0x86);
  SendCMD(ILI9341_MAC);
  SendDAT(Orient_Book);								//  SendDAT(0x48);
  SendCMD(ILI9341_PIXEL_FORMAT);
  SendDAT(0x55);
  SendCMD(ILI9341_FRC);
  SendDAT(0x00);
  SendDAT(0x18);
  SendCMD(ILI9341_DFC);
  SendDAT(0x08);
  SendDAT(0x82);
  SendDAT(0x27);
  SendCMD(ILI9341_3GAMMA_EN);
  SendDAT(0x00);
  SendCMD(ILI9341_COLUMN_ADDR);
  SendDAT(0x00);
  SendDAT(0x00);
  SendDAT(0x01);			//  SendDAT(0x00);
  SendDAT(0x3F);			//  SendDAT(0xEF);
  SendCMD(ILI9341_PAGE_ADDR);
  SendDAT(0x00);
  SendDAT(0x00);
  SendDAT(0x00);			//  SendDAT(0x01);
  SendDAT(0xEF);			//	SendDAT(0x3F);
  SendCMD(ILI9341_GAMMA);
  SendDAT(0x01);
  SendCMD(ILI9341_PGAMMA);
  SendDAT(0x0F);
  SendDAT(0x31);
  SendDAT(0x2B);
  SendDAT(0x0C);
  SendDAT(0x0E);
  SendDAT(0x08);
  SendDAT(0x4E);
  SendDAT(0xF1);
  SendDAT(0x37);
  SendDAT(0x07);
  SendDAT(0x10);
  SendDAT(0x03);
  SendDAT(0x0E);
  SendDAT(0x09);
  SendDAT(0x00);
  SendCMD(ILI9341_NGAMMA);
  SendDAT(0x00);
  SendDAT(0x0E);
  SendDAT(0x14);
  SendDAT(0x03);
  SendDAT(0x11);
  SendDAT(0x07);
  SendDAT(0x31);
  SendDAT(0xC1);
  SendDAT(0x48);
  SendDAT(0x08);
  SendDAT(0x0F);
  SendDAT(0x0C);
  SendDAT(0x31);
  SendDAT(0x36);
  SendDAT(0x0F);
  SendCMD(ILI9341_SLEEP_OUT);
   
  Delay(100000);
  SendCMD(ILI9341_DISPLAY_ON);
}


void SendDAT(uint8_t byteToSend)
{
		GPIO_SetBits(GPIOB, LCD_RS);		// data
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) {}
SPI_I2S_SendData(SPI1, byteToSend);
		while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) {}
}
void SendCMD(uint8_t byteToSend)
{
		GPIO_ResetBits(GPIOB, LCD_RS);		// comand
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) {}
SPI_I2S_SendData(SPI1, byteToSend);
		while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) {}
}

void LCDclr(uint16_t X, uint8_t Y, uint16_t L, uint8_t H, uint16_t color)
{
  SendCMD(ILI9341_MAC);
  SendDAT(Orient_Book);
  SendCMD(ILI9341_COLUMN_ADDR);
  SendDAT(X>>8);
  SendDAT(X);
  SendDAT((X+L-1)>>8);
  SendDAT(X+L-1);
  SendCMD(ILI9341_PAGE_ADDR);
  SendDAT(0);
  SendDAT(Y);
  SendDAT(0);
  SendDAT(Y+H-1);
	
	SendCMD(ILI9341_GRAM);
	GPIO_SetBits(GPIOB, LCD_RS);		// data			
	for (uint32_t i = 0; i < L*H; i++) {

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) {}
SPI_I2S_SendData(SPI1, color>>8);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) {}
SPI_I2S_SendData(SPI1, color);

	}
}




uint8_t ReadSPI(void)
{
	while (!(SPI1->SR & SPI_SR_TXE)) {}
	*((__IO uint8_t *)&SPI1->DR) = 0x00;
	while (!(SPI1->SR & SPI_SR_RXNE)) {}
	return (uint8_t)SPI1->DR;
}



Для F4 чуть подправить работу с SPI. Я запускал с дискавери, но код не сохранился.
Sergey_78r
Родился
Сообщения: 6
Зарегистрирован: Ср ноя 22, 2017 11:34:47

Re: ili9341 и STM32F4Discovery

Сообщение Sergey_78r »

Вроде удалось победить дисплей, вывод работает. Подключил DMA, получилось примерно так: заливка экрана с DMA - 6 раз в секунду, заливка экрана просто по SPI 3 раза в секунду. Использую SPI1, BR стоит 0, т.е. /2
Делал без использования STL и HAL, вдруг кому будет пример интересен, прикладываю проект в keil5.

Ну и немного кода с комментариями, буду благодарен за критику, как можно улучшить и ускорить.

Инициализация SPI1 (у SPI1 частота шины больше, чем у SPI2, соответственно будет побыстрее)

Код: Выделить всё

	//Инициализация SPI1 на пинах PA5, PA6, PA7
	//Включить тактирование SPI1
	RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
	//Включить тактирование порта А
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;   
	//Включение AF5 на пинах 5,6,7
	GPIOA->AFR[0] |= GPIO_AFRL_AFSEL5_0|GPIO_AFRL_AFSEL5_2;
	GPIOA->AFR[0] |= GPIO_AFRL_AFSEL6_0|GPIO_AFRL_AFSEL6_2;
	GPIOA->AFR[0] |= GPIO_AFRL_AFSEL7_0|GPIO_AFRL_AFSEL7_2;
	//Режим альтернативной функция для PA5,PA6,PA7
	GPIOA->MODER |= GPIO_MODER_MODE5_1|GPIO_MODER_MODE6_1|GPIO_MODER_MODE7_1;
       //Скорость High speed
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED5_0|GPIO_OSPEEDR_OSPEED5_1;
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED6_0|GPIO_OSPEEDR_OSPEED6_1;
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED7_0|GPIO_OSPEEDR_OSPEED7_1;
	//Режим push-pull
	GPIOA->OTYPER &= ~GPIO_OTYPER_OT5;
	GPIOA->OTYPER &= ~GPIO_OTYPER_OT6;
	GPIOA->OTYPER &= ~GPIO_OTYPER_OT7;
	//Отключение подтяжки на PA5, PA6, PA7
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD5_0|GPIO_PUPDR_PUPD5_1);
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD6_0|GPIO_PUPDR_PUPD6_1);
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD7_0|GPIO_PUPDR_PUPD7_1);
	//Режим работы по двум линиям
	SPI1->CR1 &= ~SPI_CR1_BIDIMODE;
	//Отключение CRC
	SPI1->CR1 &= ~SPI_CR1_CRCEN;
	//8-bit передача
	SPI1->CR1 &= ~SPI_CR1_DFF;
	//Программное управление SS
	SPI1->CR1 |= SPI_CR1_SSI|SPI_CR1_SSM;
	//Режим работы SPI 0
	SPI1->CR1 &= ~SPI_CR1_CPHA;
	SPI1->CR1 &= ~SPI_CR1_CPOL;
	//Выбор скорости /2
	SPI1->CR1 &= ~SPI_CR1_BR;
	//Установка флага Master
	SPI1->CR1 |= SPI_CR1_MSTR;
        //DMA на TX
	SPI1->CR2 |= SPI_CR2_TXDMAEN;
	//Выбор формата фрейма, какой бит передавать первым
	//SPI1->CR1 |= SPI_CR1_LSBFIRST;
	//Включение SPI1
	SPI1->CR1 |= SPI_CR1_SPE;
Для управления линией DS функция:

Код: Выделить всё

void LCD_DC_set(uint8_t data){
	while((SPI1->SR & SPI_SR_BSY) == 1){}; //Дождаться окончания передачи, перед изменением режима
	if(data == 0){
		GPIOA->BSRR |= GPIO_BSRR_BR3;
	}else{
		GPIOA->BSRR |= GPIO_BSRR_BS3;
	};
}
Основной момент тут в том, что перед тем как сменить режим работы с Data на Command надо дождаться окончания текущей передачи.
Функции отправки данных в SPI:

Код: Выделить всё

void LCD_SendCommand(uint8_t data) {
  LCD_DC_set(0);
  spi1_send(data);
}


void LCD_SendFastData(uint8_t data) {
  	 while((SPI1->SR & SPI_SR_TXE) == 0){};
		 SPI1->DR = data;
}

void LCD_SendData(uint8_t data) {
  LCD_DC_set(1);
  spi1_send(data);
}


void spi1_send(uint8_t data){
  	 while((SPI1->SR & SPI_SR_TXE) == 0){};
	 SPI1->DR = data;
};
В spi_send надо дождаться только флага TXE, что означает, что буфер свободен. BSY тут ждать не зачем, этот флаг проверяется только когда DC меняется, чтобы не порушить обмен.
Для отправки две функции, LCD_SendData и LCD_SendFastData. Вторая появилась, когда экспериментировал со скоростью заливки всего экрана. Она вызывается 76800 раз, поэтому даже просто
убрав лишний вызов LCD_DC_set(1) и перенеся запись в SPI-DR (без вызова spi1_send) удалось ускорить на заметную глазу величину. Соответственно, если перенести запись в SPI-DR прямо в функцию заливки экрана, можно еще чуть ускорить.

Заливка экрана с помощью DMA:

Код: Выделить всё

void LCD_Fill_dma(uint16_t color) {
	LCD_SetCursorPosition(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);
	LCD_SendCommand(ILI9341_GRAM);
	
	LCD_DC_set(1);
        //Цвета у экрана 16 бит и передавать их удобнее тоже по 16 бит.
        //Для этого надо переключить SPI1 в режим 16 бит
        //Сначала надо выключить передачу. Я не жду тут флага BSY, поскольку перед этим устанавливал DC и там этот
        //флаг проверяется.
	SPI1->CR1 &= ~SPI_CR1_SPE;
        //16бит
	SPI1->CR1 |= SPI_CR1_DFF;	
	//Включаем SPI
        SPI1->CR1 |= SPI_CR1_SPE;
		
        //NDTR 16 битный регистр и значение 76800 в него не влезет. Поэтому передаем двумя кусками по 38400
	DMA2_Stream3 -> NDTR = LCD_PIXEL_COUNT/2; //Количество байт для передачи

	DMA2_Stream3 -> PAR = (uint32_t)&(SPI1 -> DR); //Адрес переферии
	DMA2_Stream3 -> M0AR = (uint32_t)&color; //Адрес в памяти
	DMA2_Stream3 -> CR |= DMA_SxCR_CHSEL_0|DMA_SxCR_CHSEL_1;  //Канал 3
	DMA2_Stream3 -> CR &= ~DMA_SxCR_MINC; //Инкремент в памяти не нужен, заливка будет одним цветом
	DMA2_Stream3 -> CR |=DMA_SxCR_DIR_0; //Направление, из памяти в переферию
	DMA2_Stream3 -> CR |=DMA_SxCR_MSIZE_0;  //16 бит
	DMA2_Stream3 -> CR |=DMA_SxCR_PSIZE_0;  //16 бит

	DMA2_Stream3 -> CR |= DMA_SxCR_EN;	//Запустить копирование

	while((DMA2->LISR & DMA_LISR_TCIF3) == 0) {}; //подождать окончания копирования
	DMA2->LIFCR |= DMA_LIFCR_CTCIF3;    //Сбросить флаг окончания

        //Копирование 2 половины данных
	DMA2_Stream3 -> NDTR = LCD_PIXEL_COUNT/2; //Количество байт для передачи
	DMA2_Stream3 -> CR |= DMA_SxCR_EN;	//Запустить копирование
	
	while((DMA2->LISR & DMA_LISR_TCIF3) == 0) {}; //Подождать окончания копирования
	DMA2->LIFCR |= DMA_LIFCR_CTCIF3;    //Сбросить флаг
	
        //После заливки экрана надо вернуть режим работы SPI в 8 бит
        //Ждем пока закончится передача
	while((SPI1->SR & SPI_SR_BSY) == 1){};
        //Выключаю SPI, устанавливаю 8 бит, включаю SPI
	SPI1->CR1 &= ~SPI_CR1_SPE;
	SPI1->CR1 &= ~SPI_CR1_DFF;	
	SPI1->CR1 |= SPI_CR1_SPE;
	
}
Ну и собственно как проверял скорость обновления:

Код: Выделить всё

cur_time=get_ms();
while(get_ms() - cur_time < 20000){
	color=(color == 0x00F8 ? 0xF800 : 0x00F8);		
   	LCD_Fill_dma(color);
	j++;
};
	
cur_time=get_ms();
while(get_ms() - cur_time < 20000){
	color=(color == 0x00F8 ? 0xF800 : 0x00F8);		
   	LCD_Fill(color);
	l++;
};
	
j=j/20;
l=l/20;
get_ms возвращает нарастающее время в ms. Посчитал количество обновлений за 20 секунд с DMA и просто spi_send.

С DMA видел другой метод работы - цикличный режим и отслеживание количества запусков по прерыванию. Не думаю, что это сильно скажется на скорости, а кода больше :) Я решил просто запускать копирование еще раз.

Код инициализации дисплея я взял из чьего-то проекта (в IAR был). Оттуда же схема работы с экраном, но переделал работу с флагами SPI, поскольку она была реализована не правильно и работало медленно.
Вообщем сейчас на глаз работает довольно шустро, хотя заливка экрана глазом видна.

Если есть идеи как еще ускорить вывод, или есть явные ошибки в реализации, буду рад услышать.

Проект во вложении, надеюсь кому-нибудь будет полезен. В сети много примеров на SPL и HAL, а на регистрах я не видел.
Вложения
TFT_22_ili9341.rar
Проект в Keil5
(990.6 КБ) 345 скачиваний
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
Fusion
Грызет канифоль
Сообщения: 272
Зарегистрирован: Пт ноя 13, 2009 10:39:32
Откуда: Москва
Контактная информация:

Re: ili9341 и STM32F4Discovery

Сообщение Fusion »

По DMA не проверял, но если просто цветом заливать и больше ничего не делать то получается примерно 29 fps (заливок экрана в секунду).
Скорость SPI 36 мГц.
36 000 000 / 320 / 240 / 16 ~ 29.3 Герц
Реклама
jcxz
Мудрый кот
Сообщения: 1726
Зарегистрирован: Вт авг 15, 2017 10:51:13

Re: ili9341 и STM32F4Discovery

Сообщение jcxz »

[uquote="Sergey_78r",url="/forum/viewtopic.php?p=3241607#p3241607"]Вроде удалось победить дисплей, вывод работает. Подключил DMA, получилось примерно так: заливка экрана с DMA - 6 раз в секунду[/uquote]
Очень медленно. У меня без проблем работает на SCLK=45МГц.

Добавлено after 5 minutes 7 seconds:
[uquote="Fusion",url="/forum/viewtopic.php?p=3242115#p3242115"]Скорость SPI 36 мГц[/uquote]
У Вас видимо CLK периферийной шины ==72МГц, как и частота ядра? И видимо STM32? И видимо SPI в режиме 16бит/слово?
И FIFO на SPI у STM32 нету.
При 2 тактах шины на 1 бит SPI на диаграмме SCLK нет пауз?
Реклама
Sergey_78r
Родился
Сообщения: 6
Зарегистрирован: Ср ноя 22, 2017 11:34:47

Re: ili9341 и STM32F4Discovery

Сообщение Sergey_78r »

Да, действительно, работало медленно, потому что тактирование было оставлено по умолчанию.
Вышеприведенный пример удалось "разогнать" до 34 в режиме DMA и 32 в SPI.
Так что вроде экран не такой и тормоз :)
Ответить

Вернуться в «ARM»