Прошивка клона дисплея 7inch от Waveshare

b19-0

Строил строил своё приложение и … уронил вот Windows IoT, ну да речь не об этом. :) После продолжительного по времени портирования и изучения Open Source прошивки для оригинальных дисплеев под GDT32, я таки получил первый рабочий вариант. Попробую описать процесс портирования в статье ниже…

Напомню, что для этой прошивки требуется доработка платы, описанная в прошлой статье.

Начнем с настройки проекта в STM32CubeMX. Тут настраиваем минимум необходимой периферии и входов\выходов.

B19-1 Pins

В итоге получилось:

  • RCC — тактирование от внешнего кварца;
  • SWD — оставим включенной отладку;
  • USB — Device (FS) + Custom HID Device;
  • I2C — I2C1 Master;
  • PB4 — как выход для управления nRESET GT811;
  • PB5 — как вход для чтения INT GT811;
  • PC13 — как выход для программного управления USB (опционально);
  • PB11 — как вход, использовалось для отладки, подключал кнопку (нет необходимости установки на плате).

Тактирование настраивается крайне просто и практически по умолчанию всё для USB-применения:

B19-2 Clock

Генерим проект для Keil uVision v5 и будем с ним работать.

Далее предстояло портирование драйвера GT811 и его небольшая оптимизация. В целом всё вышло просто и пояснений будет по минимуму. Для удобства я разделил драйвер (по сравнению с исходным открытым проектом) на GT811.cGT811.h и GT811CONF.h, чтобы конфигурационной структурой не засорять код. Вопросов при портировании не возникло, кроме как работать с I2C на HAL — это было мое первое использование I2C именно на HAL, многие вещи были несколько незнакомыми, но после более глубокого изучения HAL (+попалась хорошая статья-пример по работе с I2C памятью на HAL) всё стало понятно и получилось. :)

Из недоделок: тут несколько неправильно реализован доступ к периферии в самом драйвере. К примеру, в GT811.c файле в самом верху есть объявления на конкретные имена структур периферии с внешней компоновкой (extern), которые реально объявлены в main.c и генерируются Кубом:

extern USBD_HandleTypeDef hUsbDeviceFS;
extern I2C_HandleTypeDef hi2c1;

А по хорошему здесь надо делать ссылки без конкретных имен периферии, а назначать используемую периферию при вызове функции GT811_Init(). Пока разбирался, сделал как сделал, заработало, больше вник, посмотрел примеров — понял, что не совсем удобно сделал (немного больше мороки, если понадобится это библиотека в другом проекте), но сейчас лень переписывать… :)

Далее вкратце по основным моментам, что менял и дополнял в проекте, сгенерированным STM32CubeMX для получения рабочей прошивки тачскрина. В main.c подключаем наш драйвер и добавляем функции обслуживания тачскрина:

/* USER CODE BEGIN Includes */
#include "GT811.h"

/ ... /

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();

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

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_USB_DEVICE_Init();
 MX_I2C1_Init();

 /* USER CODE BEGIN 2 */
 GT811_Init();
 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // USB On
 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 for(;;)
 {
 /* USER CODE END WHILE */

 /* USER CODE BEGIN 3 */
 GT811_Poll();
 }
 /* USER CODE END 3 */
}

Примечание: Включение PC13 опционально и это не обязательная доработка платы. У меня это было реализовано для отладки (при прошивке без этого для повторного определения девайса необходимо было переподключать USB для повторной энумерации).

Открываем файл usbd_customhid.h и корректируем размеры посылаемых и отправляемых пакетов, а также указываем размер дескриптора устройства, который мы будем так же (чуть ниже) редактировать:

/** @defgroup USBD_CUSTOM_HID_Exported_Defines
 * @{
 */
#define CUSTOM_HID_EPIN_ADDR 0x81
#define CUSTOM_HID_EPIN_SIZE 64 //0x02

#define CUSTOM_HID_EPOUT_ADDR 0x01
#define CUSTOM_HID_EPOUT_SIZE 64 //0x02

#define USB_CUSTOM_HID_CONFIG_DESC_SIZ 41
#define USB_CUSTOM_HID_DESC_SIZ 9

Теперь переходим в usbd_customhid.c, добавляем структуру-ответ на запрос ПК, сколько же пальцев (касаний) поддерживает тачскрин.

__ALIGN_BEGIN static uint8_t hid_feature_report[2] __ALIGN_END =
{
 0x02, // report id
 0x05 // number of contact points
};

И вставляем ответ с этой структурой по запросу от ПК, иначе тач определится, но ПК будет считать, что он ничего не умеет (0 касаний поддерживает, самый передовой тач :D ):

static uint8_t USBD_CUSTOM_HID_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
 uint16_t len = 0;
 uint8_t *pbuf = NULL;
 USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)pdev->pClassData;

 // обрабатываем запрос кол-ва точек тача
 if(req->bmRequest == 0xA1 && req->bRequest == CUSTOM_HID_REQ_GET_REPORT && req->wValue == 0x0302)
 {
 pbuf = hid_feature_report;
 len = 2;

 USBD_CtlSendData(pdev, pbuf, len);
 return USBD_OK;
 } 

 switch (req->bmRequest & USB_REQ_TYPE_MASK)
 {
 /*
 ......
*/
 }
 return USBD_OK;
}

В этом же файле правим структуру USBD_CUSTOM_HID, должна выглядеть так:

USBD_ClassTypeDef USBD_CUSTOM_HID =
{
USBD_CUSTOM_HID_Init, // Init
USBD_CUSTOM_HID_DeInit, // DeInit
USBD_CUSTOM_HID_Setup, // Setup
NULL, // EP0_TxSent
USBD_CUSTOM_HID_EP0_RxReady, // EP0_RxReady - STATUS STAGE IN
USBD_CUSTOM_HID_DataIn, // DataIn
USBD_CUSTOM_HID_DataOut, // DataOut
NULL, // SOF
NULL, // IsoINIncomplete
NULL, // IsoOUTIncomplete
USBD_CUSTOM_HID_GetCfgDesc, // GetHSConfigDescriptor
USBD_CUSTOM_HID_GetCfgDesc, // GetFSConfigDescriptor
USBD_CUSTOM_HID_GetCfgDesc, // GetOtherSpeedConfigDescriptor
USBD_CUSTOM_HID_GetDeviceQualifierDesc, // GetDeviceQualifierDescriptor
//NULL // GetUsrStrDescriptor
};

Далее дескрипторы устройства. Базовый дескриптор в этом же файле:

/* USB CUSTOM_HID device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, // bLength: Configuration Descriptor size
USB_DESC_TYPE_CONFIGURATION, // bDescriptorType: Configuration
USB_CUSTOM_HID_CONFIG_DESC_SIZ, // wTotalLength: Bytes returned
0x00,
0x01, // bNumInterfaces: 1 interface
0x01, // bConfigurationValue: Configuration value
0x00, // iConfiguration: Index of string descriptor describing the configuration
0xC0, // bmAttributes: bus powered
0xfa, //0x32 // MaxPower 500 mA: this current is used for detecting Vbus
/************** Descriptor of CUSTOM HID interface ****************/
/* 09 */
0x09, // bLength: Interface Descriptor size
USB_DESC_TYPE_INTERFACE, // bDescriptorType: Interface descriptor type
0x00, // bInterfaceNumber: Number of Interface
0x00, // bAlternateSetting: Alternate setting
0x01,//0x02 // bNumEndpoints
0x03, // bInterfaceClass: CUSTOM_HID
0x00, // bInterfaceSubClass : 1=BOOT, 0=no boot
0x00, // nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse
0, // iInterface: Index of string descriptor
/******************** Descriptor of CUSTOM_HID *************************/
/* 18 */
0x09, // bLength: CUSTOM_HID Descriptor size
CUSTOM_HID_DESCRIPTOR_TYPE, // bDescriptorType: CUSTOM_HID
0x11, // bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number
0x01,
0x09,//0x09 // bCountryCode: Hardware target country
0x01, // bNumDescriptors: Number of CUSTOM_HID class descriptors to follow
0x22, // bDescriptorType
USBD_CUSTOM_HID_REPORT_DESC_SIZE, // wItemLength: Total length of Report descriptor
0x00,
/******************** Descriptor of Custom HID endpoints ********************/
/* 27 */
0x07, // bLength: Endpoint Descriptor size
USB_DESC_TYPE_ENDPOINT, // bDescriptorType
CUSTOM_HID_EPIN_ADDR, // bEndpointAddress: Endpoint Address (IN)
0x03, // bmAttributes: Interrupt endpoint
CUSTOM_HID_EPIN_SIZE, // wMaxPacketSize
0x00,
0x01, // bInterval: Polling Interval (1 ms)
/* 34 */
0x07, // bLength: Endpoint Descriptor size
USB_DESC_TYPE_ENDPOINT, // bDescriptorType:
CUSTOM_HID_EPOUT_ADDR, // bEndpointAddress: Endpoint Address (OUT)
0x03, // bmAttributes: Interrupt endpoint
CUSTOM_HID_EPOUT_SIZE, // wMaxPacketSize: 2 Bytes max
0x00,
0x01, // bInterval: Polling Interval (1ms)
/* 41 */
} ;

Тут, в принципе, изменений особо нет, кроме интервала опроса (1 мс) и подключенных констант (CUSTOM_HID_EPOUT_ADDR и подобные), которые мы правили ранее.

Теперь в файле usbd_custom_hid_if.c вставляем дескриптор, описывающий, что умеет наше устройство:

__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
/* USER CODE BEGIN 0 */
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x04, // USAGE (Touch Screen)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (Touch)
0x09, 0x22, // USAGE (Finger)
0xa1, 0x02, // COLLECTION (Logical)
0x09, 0x42, // USAGE (Tip Switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x07, // REPORT_COUNT (7)
0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x51, // USAGE (Contact Identifier)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x30, // USAGE (Tip Pressure)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x01, // REPORT_COUNT (1)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x55, 0x0F, // UNIT EXPONENT (-1)
0x65, 0x11, // UNIT VALUE (SI Linear:Distance)
0x26, 0x20, 0x03, // LOGICAL_MAXIMUM (800)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xFF, 0xFF, // PHYSICAL_MAXIMUM (x)
0x09, 0x30, // USAGE (X)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x46, 0xFF, 0xFF, // PHYSICAL_MAXIMUM (y)
0x26, 0xe0, 0x01, // LOGICAL_MAXIMUM (480)
0x09, 0x31, // USAGE (Y)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x55, 0x0C, // UNIT_EXPONENT (-4 == 100 microseconds)
0x66, 0x01, 0x10, // UNIT (Seconds)
0x47, 0xff, 0xff, 0x00, 0x00, // PHYSICAL_MAXIMUM (65535)
0x27, 0xff, 0xff, 0x00, 0x00, // LOGICAL_MAXIMUM (65535)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x56, // USAGE (Scan Time)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x54, // USAGE (Contact count)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x85, 0x02, // REPORT_ID (Feature)
0x09, 0x55, // USAGE(Contact Count Maximum)
0x95, 0x01, // REPORT_COUNT (1)
0x25, 0x02, // LOGICAL_MAXIMUM (2)
0xb1, 0x02, // FEATURE (Data,Var,Abs)
/* USER CODE END 0 */
0xC0 /* END_COLLECTION */
}

По умолчанию тут пусто (после генерации проекта из Куба) и наше устройство не умеет ни отправлять, ни принимать, как и не описан его тип в принципе.

Вот и все основные моменты. Пробуем скомпилировать, прошиваем и … работает только первое нажатие на тачскрин. :D Вникаем дальше.

Разбираясь с отладкой, я нашел в чем проблема. Выяснилось, что конфигурация и первая отправка пакета данных происходят нормально, а потом состояние USB (hhid->state) становится BUSY. Почему? В вызываемой функции USBD_CUSTOM_HID_SendReport, собственно, это состояние назначается и нигде не сбрасывается. Возможно, я не понял до конца правильность использования Custom HID от куба, я пошел путём «в лоб», закомментировав строку назначения состояния. :) Поэтому …

B19-3

После этого передача заработала:

uint8_t USBD_CUSTOM_HID_SendReport(USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len)
{
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)pdev->pClassData;

if (pdev->dev_state == USBD_STATE_CONFIGURED )
{
if(hhid->state == CUSTOM_HID_IDLE)
{
//hhid->state = CUSTOM_HID_BUSY; // костыль ? и ладно.
USBD_LL_Transmit (pdev, CUSTOM_HID_EPIN_ADDR, report, len);
}
}
return USBD_OK;
}

Также еще один момент, без которого не работает 5 касаний или, возможно, для слабых ПК придется подредактировать эту часть. В драйвере GT811 после опроса микросхемы и парсинга данных с неё есть непосредственная отправка пакета в ПК (вызов отправки USB пакета). Вот конец функции GT811_Poll() из файла GT811.c:

void GT811_Poll(void)
{
/*
.....
*/
scan_time = (uint16_t)(HAL_GetTick() & 0xFFFF);
hid_report->report[8] = scan_time & 0xFF;
hid_report->report[9] = (uint8_t)(scan_time >> 8);

USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, hid_report->report, 11);
HAL_Delay(1); // мы не следим, отправился ли Report, поэтому ждем
sentReports++;
}
}
}

Это следствие того, что мы не следим за состоянием USB-шины (да-да, как раз BUSY state) и просто ждем 1 мс (период опроса устройства) перед отправкой следующего пакета. Без этой задержки будет как повезёт — какой первый прилетел пакет в ПК, тот и будет обработан, а остальные 4  (или менее, сколько было касаний) будут пропущены за эту 1 мс.

Исходники проекта можно скачать ЗДЕСЬ. Для компиляции использовал Keil uVision v5.20, для прошивки — J-Link. Только HEX прошивку можно скачать ЗДЕСЬ.

На этом всё. Есть замечания или идеи как исправить оставшиеся костыли, буду рад прочитать в комментариях! ;)

7 thoughts on “Прошивка клона дисплея 7inch от Waveshare

  1. Михаил 03.07.2017 / 03:25

    простите, у меня небольшая проблема, как вы думаете в чем может быть причина?
    дисплей Waveshare 7 inch Raspberry Pi Display 1024×600 Touchscreen LCD HDMI rev2.1
    вот мое видео https://youtu.be/RLHfTpDKtPc
    На видео видно, что сенсорный экран работает, но как буд-то с низкой чувствительностью, такая штука наблюдается и при использовании Windows 10 Pro. Мультитач работает, прикосновение как буд-то идёт слегка, и невозможно ничего нажать. Waveshare поддержка не отвечает. Написано, драйвера работают из коробки!

    Нравится

    • admin 03.07.2017 / 12:40

      Добрый день!
      А у вас как я понимаю оригинальная прошивка, верно ?

      Попробуйте в win 10 (декстоп) открыть Paint и проверить на нём легко — кистью пробуйте рисовать, если рисуется как одним пальцем, так и несколькими одновременно (до 5), но никакие кнопки управления не работают — то поможет только перепрошивка на рабочий вариант (в обоих статьях про этот дисплей я написал про необходимые доработки железа, так и прошивку для полученного мною варианта с stm32. Какой дисплей у вас — не видно, возможно у вас на GD32F103, тогда вам подойдет прошивка отсюда — https://github.com/pysco68/waveshare-hid ). Судя по ролику — у вас именно проблема кривой прошивки (у меня так же криво работало, что на win 10, что на win 10 IoT), думаю придется перепрошивать, надо только посмотреть на чём собран тач-USB-контроллер.

      Нравится

  2. Денис 20.07.2017 / 13:46

    Спасибо за работу, перекинул дорожки, прошил родной МК и всё заработало. После того, как запорол всё своей прошивкой уже собирался сам этой работой, но случайно наткнулся на сайт. Не против, если опубликую Вашу статью на pccar.ru?

    Нравится

    • admin 20.07.2017 / 15:42

      На здоровье, рад что мои старания помогли не только мне!
      Ссылку что ли ? Без проблем, делитесь. ;)

      Нравится

  3. Евгений 21.07.2017 / 23:46

    Я так понимаю, что 100% совместимости между GD и STM32 нет. Плюс ко всему, схемы подключения к 811 контроллеру у них разные. Например, на GD32 прерывание заводится на PB6, а на STM32 к PB7 кЕтайцы завели.
    У меня дисплей на STM32 но с разрешением 800 на 480. Я так понимаю, что необходимо пойти по Вашему пути- порезать дороги и прошить стм Вашей прошивкой. Ничего корректировать не нужно в связи с другим разрешением?
    Не могли бы Вы еще запихнуть в архив сам файл прошивки? А то ставить кейл для меня сейчас целое дело…

    Нравится

    • admin 22.07.2017 / 01:08

      Да, они не полностью совместимы.
      Если хочется пойти по пути наименьшего сопротивления, то да, проще всего провести доработку как в статье (если остальное аналогично) и залить прошивку — HEX вот тут выложил — https://yadi.sk/d/HK8jWkls3LGur6

      Еще, что забавно, я в прошивке так же не правил разрешение экрана для тача и оно там как раз 800*480 и всё нормально работает и для 1024*600. Такой вот прикол. :)

      Нравится

  4. Евгений 22.07.2017 / 23:13

    Тач неожиданно заработал на raspbian. Пока еще не решил переходить на вынь IoT. Так что пока спешить с перепрошивкой не буду- закажу у кЕтайцев стм и просто поменяю чтоб осталась оригинальная прошивка.

    Нравится

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s