вторник, 7 января 2014 г.

stm32f4discovery & ssd1963 LCD

Сегодня, я хочу рассказать об ещё одном эксперименте: о том как я подключал TFT - дисплей, на базе контроллера SSD1963 к плате STM32F4Discovery. На это действие у меня ушло целых три дня и то благодаря моей невнимательности.

Контроллер

Контроллер   SSD1963 фирмы SOLOMON SYSTECH является универсальным контроллером TFT дисплеев, с разрешением до 864х480 и цветовым разрешением до 24 бит/пиксель. Контроллер снабжён встроенной статической памятью объёмом в 1215 кБ, а также ФАПЧ генераторами развёртки и т. п., что позволяет обходится минимумом внешней обвязки.

Внешний интерфейс контроллера может быть двух видов - 6800 и 8080. Оба этих интерфейса широко расписаны в технической литературе, я же отмечу, что для удобства и по причине того, что интерфейс 8080 поддерживается аппаратно STM32F407, рекомендую использовать именно его.

Ширина данных внешнего интерфейса может быть 8/16/24 бит. Мне попался дисплей у которого выведен внешний интерфейс шириной 8 бит. Более подробную информацию вы можете почерпнуть из документации на контроллер, которую я на всякий случай приложил к репозиторию с примером. От ширины интерфейса не зависит настройка дисплея, поэтому инициализация происходит абсолютно одинаково. Однако в процессе инициализации необходимо настроить ширину данных изображения которые будет принимать контроллер от МК/МП в процессе работы (об этом будет расписано в части настройки дисплея).

FSMC
Flexible static memory controller (FSMC, коммутируемый контроллер статической памяти). Этот контроллер позволяет работать со статической памятью, а также NOR и NAND флеш памятью. Также при определённых навыках можно заставить его работать с любой параллельной шиной используя свойства непосредственного проецирования на адресное пространство, для сокращения кода управления и ускорения режима работы. Собственно этой особенностью контроллера мы и воспользуемся. Для начала определимся с выводами которые мы будем использовать:

  1. /*
  2.  * num func net port pin type
  3.  * 4 RS A16     PD11 58     out
  4.  * 5 WR WE      PD5 86 out
  5.  * 6 RD OE      PD4 85 out
  6.  * 15 CS NE      PD7 88 out
  7.  * 7 D0 D0 PD14 61 I/O
  8.  * 8 D1 D1 PD15 62 I/O
  9.  * 9 D2 D2      PD0 81 I/O
  10.  * 10 D3 D1 PD1 82      I/O
  11.  * 11 D4 D4 PE7 38 I/O
  12.  * 12 D5 D5 PE8 39 I/O
  13.  * 13 D6 D6 PE9 40 I/O
  14.  * 14 D7 D7 PE10 41 I/O
  15.  */

Я описал тут только линии данных, остальные линии (сброса, управления подсветкой и питания) будут описаны далее. Как видно из подключения у нас есть 8 линий данных, 4 линии управления - WR - разрешение записи, RD - разрешение чтения, CS - выбор канала, RS - команда/данные (который висит на линии адреса A16).  Если данными и первыми 3 управляющими сигналами всё более или менее понятно, то в случае с RS не совсем ясно для чего так сделано, если вы не использовали интерфейс памяти ранее.

RS это линия которая говорит контроллеру дисплея, что если лог 0 то пришедшие биты это команда, а если 1 то данные. Так вот, повесив её на шину адреса мы можем обойтись без дополнительного кода управления этой линией: FSMC сам будет управлять ею, для этого необходимо только обращаться по адресу в котором содержится бит с номером 16 и имеющий значение 1 для данных и 0 для команд.

А т.к у нас базовый адрес первого канала памяти равен 0x6000 0000, то существуют два адреса: для команд - 0x6000 0000, и данных - 0x6001 0000. Также используя эту особенность, можно посылать несколько байт в одной цепочке (длина которой в нашем случае может быть до 64 кБ).

Инициализация FSMC сравнительно небольшая в отличие от инициализации SDRAM на МП. Для работы необходимо перевести указанные выше выводы в состояние альтернативной функции - GPIO_AF_FSMC:

  1.  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE, ENABLE);
  2.  
  3.   GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);
  4. ....
  5.   GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);
  6.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 |
  7.   GPIO_Pin_7 | GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15 ;
  8.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
  9.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  10.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  11.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  12.   GPIO_Init(GPIOD, &GPIO_InitStructure);
  13.  
  14.   GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FSMC);
  15. ...
  16.   GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FSMC);
  17.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
  18. ...
  19.   GPIO_Init(GPIOE, &GPIO_InitStructure);
А также настроить сам контроллер:

  1.  RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);
  2.  
  3. /*-- FSMC Configuration ------------------------------------------------------*/
  4. // Parameters SRAM with external TFT plug connections
  5.  
  6.   p.FSMC_AddressSetupTime      = 3;
  7.   p.FSMC_AddressHoldTime       = 0;
  8.   p.FSMC_DataSetupTime         = 4;
  9.   p.FSMC_BusTurnAroundDuration = 0;
  10.   p.FSMC_CLKDivision           = 0;
  11.   p.FSMC_DataLatency           = 0;
  12.   p.FSMC_AccessMode            = FSMC_AccessMode_A;
  13.  
  14.  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;
  15.   FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
  16.   FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM;
  17.   FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;
  18.   FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
  19.   FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
  20.   FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
  21.   FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
  22.   FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
  23.   FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
  24.   FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
  25.   FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
  26.   FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
  27.   FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
  28.   FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;      
  29.   FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); 
  30.  
  31.   FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);
При настройке контроллера памяти главное помнить о задержках, которые задаются в строка 6 - 8, 11. Причина этому то, что у нас параллельная шина работающая с большой скоростью и следовательно на проводниках разной длины время прохода сигнала будет разным. Также если у вас дисплей имеет ширину данных 16 бит, то необходимо подключить в соответствии с документацией на МК, дополнительные выводы GPIO и указать в настройке FSMC (строка 17), ширину не 8 бит а 16. Помимо этого адреса увеличатся на 2,  не включая базовый адрес, который останется прежним, а вот последующие изменятся - например 0x6001 0000, станет 0x6002 0000.

Для проверки правильной инициализации после сброса контроллера дисплея при подключенном питании попробуйте прочитать его ID:

  1.     volatile char * WF43_COMMAND_ptr = ((char *)0x60000000);
  2.     volatile char * WF43_DATA_ptr = ((char *)(0x60000000 + (1 << 16)));
  3.  
  4.     volatile int t;
  5.     int x;
  6.  
  7.     GPIO_SetBits(GPIOA,GPIO_Pin_1);
  8.     for(t=0;t<1000000;t++);
  9.     GPIO_ResetBits(GPIOA,GPIO_Pin_1);
  10.     for(t=0;t<10000;t++);
  11.     GPIO_SetBits(GPIOA,GPIO_Pin_1);
  12.     for(t=0;t<1000000;t++);
  13.     xputs("Reading display id...\r\n");
  14.     *WF43_COMMAND_ptr = 0xa1;
  15.     x = *WF43_DATA_ptr;
  16.     xprintf("Display ID: %x\r\n", x);
  17.     x = *WF43_DATA_ptr;
  18.     xprintf("Display ID: %x\r\n", x);
  19.     x = *WF43_DATA_ptr;
  20.     xprintf("Display ID: %x\r\n", x);
  21.     x = *WF43_DATA_ptr;
  22.     xprintf("Display ID: %x\r\n", x);
  23.     x = *WF43_DATA_ptr;
  24.     xprintf("Display ID: %x\r\n", x);
В случае правильной инициализации вы должны в консоли к которой подключен последовательный порт увидеть следующее:

Если же у вас вместо ID отображаются нули или другие случайные числа, значит перепроверьте подключение дисплея. В случае если отображаются не все числа, можно попробовать увеличить временные задержки в настройке FSMC. Как видно из функции, перед сбросом и после него идет значительная временная задержка - это сделано для того чтобы устоялись электрические процессы в нашей схеме.

Дисплей


Инициализацию дисплея можно посмотреть в документе от Winstar - WINSTAR_TFT Application Note, который я также приложил к другим документам. В этом документе нас интересует раздел настройки дисплея - 5.1  8bit-80 interface mode , 262K color , 3.5_Panel:320xRGBx240. Весь код инициализации который лежит в файле ssd1963_43.c взят целиком оттуда. Также, из этого документа можно почерпнуть реккомендации по подключению дисплея к контроллеру и этой связки в дальнейшем к МК/МП:


Все исходные коды и документация доступны в моём репозитории:

Для тех кому интересно прилагается видео работы демо прошивки:


В процессе разбора использовал документацию, топики в форумах и статьи:

  1. http://forum.easyelectronics.ru/viewtopic.php?f=35&t=8232&start=0
  2. https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DispForm.aspx?ID=28876&Source=/public/STe2ecommunities/mcu/Tags.aspx?tags=FSMC
  3. http://forum.easyelectronics.ru/viewtopic.php?f=35&t=15745&start=0
  4. http://forum.easyelectronics.ru/viewtopic.php?f=35&t=13375&sid=c17e02c9ec831e61dd33fdcaefea179b
  5. http://electronix.ru/forum/index.php?showtopic=109843
Также отдельное спасибо, моему другу Руслану за то, что указал возможный источник проблемы, когда после первого дня мучений я попал в тупиковую ситуацию с шиной данных.

Как видно из этого снимка, дисплей немного пострадал в ходе эксперимента - были оторваны проводники идущие к шлейфу:


На этом всё.

P. S. (может быть, когда-нибудь я соберу графическую библиотеку из того разрозненного кода что у меня есть).