Строил строил своё приложение и … уронил вот Windows IoT, ну да речь не об этом. :) После продолжительного по времени портирования и изучения Open Source прошивки для оригинальных дисплеев под GDT32, я таки получил первый рабочий вариант. Попробую описать процесс портирования в статье ниже…
Напомню, что для этой прошивки требуется доработка платы, описанная в прошлой статье.
Начнем с настройки проекта в STM32CubeMX. Тут настраиваем минимум необходимой периферии и входов\выходов.
В итоге получилось:
- RCC — тактирование от внешнего кварца;
- SWD — оставим включенной отладку;
- USB — Device (FS) + Custom HID Device;
- I2C — I2C1 Master;
- PB4 — как выход для управления nRESET GT811;
- PB5 — как вход для чтения INT GT811;
- PC13 — как выход для программного управления USB (опционально);
- PB11 — как вход, использовалось для отладки, подключал кнопку (нет необходимости установки на плате).
Тактирование настраивается крайне просто и практически по умолчанию всё для USB-применения:
Генерим проект для Keil uVision v5 и будем с ним работать.
Далее предстояло портирование драйвера GT811 и его небольшая оптимизация. В целом всё вышло просто и пояснений будет по минимуму. Для удобства я разделил драйвер (по сравнению с исходным открытым проектом) на GT811.c, GT811.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 от куба, я пошел путём «в лоб», закомментировав строку назначения состояния. :) Поэтому …
После этого передача заработала:
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 мс.
Скачать исходники проекта здесь — WaveshareClone-LCD-Touch-STM32-Firmware (исходники, проект и hex файл). Для компиляции использовал Keil uVision v5.20, для прошивки — J-Link.
На этом всё. Есть замечания или идеи как исправить оставшиеся костыли, буду рад прочитать в комментариях! ;)
UPDATE 2018.06.08:
Вариант прошивки для портретного режима — WaveshareClone-LCD-Touch-STM32-Firmware(Portrait) (исходники, проект и hex файл). Для корректной работы тача обязательно выставлять родное разрешение экрана!
простите, у меня небольшая проблема, как вы думаете в чем может быть причина?
дисплей Waveshare 7 inch Raspberry Pi Display 1024×600 Touchscreen LCD HDMI rev2.1
вот мое видео https://youtu.be/RLHfTpDKtPc
На видео видно, что сенсорный экран работает, но как буд-то с низкой чувствительностью, такая штука наблюдается и при использовании Windows 10 Pro. Мультитач работает, прикосновение как буд-то идёт слегка, и невозможно ничего нажать. Waveshare поддержка не отвечает. Написано, драйвера работают из коробки!
НравитсяНравится
Добрый день!
А у вас как я понимаю оригинальная прошивка, верно ?
Попробуйте в win 10 (декстоп) открыть Paint и проверить на нём легко — кистью пробуйте рисовать, если рисуется как одним пальцем, так и несколькими одновременно (до 5), но никакие кнопки управления не работают — то поможет только перепрошивка на рабочий вариант (в обоих статьях про этот дисплей я написал про необходимые доработки железа, так и прошивку для полученного мною варианта с stm32. Какой дисплей у вас — не видно, возможно у вас на GD32F103, тогда вам подойдет прошивка отсюда — https://github.com/pysco68/waveshare-hid ). Судя по ролику — у вас именно проблема кривой прошивки (у меня так же криво работало, что на win 10, что на win 10 IoT), думаю придется перепрошивать, надо только посмотреть на чём собран тач-USB-контроллер.
НравитсяНравится
Спасибо за работу, перекинул дорожки, прошил родной МК и всё заработало. После того, как запорол всё своей прошивкой уже собирался сам этой работой, но случайно наткнулся на сайт. Не против, если опубликую Вашу статью на pccar.ru?
НравитсяНравится
На здоровье, рад что мои старания помогли не только мне!
Ссылку что ли ? Без проблем, делитесь. ;)
НравитсяНравится
Я так понимаю, что 100% совместимости между GD и STM32 нет. Плюс ко всему, схемы подключения к 811 контроллеру у них разные. Например, на GD32 прерывание заводится на PB6, а на STM32 к PB7 кЕтайцы завели.
У меня дисплей на STM32 но с разрешением 800 на 480. Я так понимаю, что необходимо пойти по Вашему пути- порезать дороги и прошить стм Вашей прошивкой. Ничего корректировать не нужно в связи с другим разрешением?
Не могли бы Вы еще запихнуть в архив сам файл прошивки? А то ставить кейл для меня сейчас целое дело…
НравитсяНравится
Да, они не полностью совместимы.
Если хочется пойти по пути наименьшего сопротивления, то да, проще всего провести доработку как в статье (если остальное аналогично) и залить прошивку — HEX вот тут выложил — https://yadi.sk/d/HK8jWkls3LGur6
Еще, что забавно, я в прошивке так же не правил разрешение экрана для тача и оно там как раз 800*480 и всё нормально работает и для 1024*600. Такой вот прикол. :)
НравитсяНравится
Тач неожиданно заработал на raspbian. Пока еще не решил переходить на вынь IoT. Так что пока спешить с перепрошивкой не буду- закажу у кЕтайцев стм и просто поменяю чтоб осталась оригинальная прошивка.
НравитсяНравится
Спасибо, отличная работа. Расскажите как подключали программатор? Какие выводы j-link к каким разъема платы дисплея? Программирование по SWD?
НравитсяНравится
Разобрался сам. Нужно было питание платы дисплея включить) Прошил. Все работает отлично в Win10. У меня, правда, и китайская прошивка изначально работала в Win10, а нужно было сенсор завести в armbian на orange pi pc. После перепрошивки на Вашу ситуация не изменилась, evtest показывает что проходит первое касание и потом всё- тач молчит.
НравитсяНравится
А Вы дорабатывали плату или она имеет нормальное подключение к аппаратному I2C? В первой статье я описал какие проблемы с этим клоном есть https://adelectronics.ru/2017/02/01/%D0%BA%D0%BB%D0%BE%D0%BD-%D0%B4%D0%B8%D1%81%D0%BF%D0%BB%D0%B5%D1%8F-7inch-%D0%BE%D1%82-waveshare/#more-1960 и там описал, что надо доработать, что бы использовался аппаратный I2C.
Хотя если «Все работает отлично в Win10.» (т.е. 5 касаний в том же Paint корректно работают ?), то даже не знаю, что не так.. видимо драйвер по pid\vid не корректный в armbian для тача.
НравитсяНравится
Плату, конечно, дорабатывал перед прошивкой. Только резисторы не трогал.
В win10 работают все 5 касаний, в ubuntu/xubuntu 16.04 сенсор работает на одно касание, возможно и мультитач работает (судя по evtest), но в приложениях я не проверял.
А вот на orange pi pc plus ни в armbian ни в андроид запустить работу сенсора не получается. В armbian evtest показывает, что проходит только одно единственное первое касание и на этом все замолкает. В андроид в USB HID Terminal данные какие-то летят, но с гуем дисплей не дружит.
НравитсяНравится
Я с линуксом практически не знаком, помочь увы ничем не могу… но по симптомам судя по всему дело именно в драйвере, можно попробовать поменять PID\VID тача на какой-то заведомо распространённый и на существующий в системе стандартный драйвер (хотя это HID и по хорошему должно быть пофиг на PID\VID).
В файле Src\usbd_desc.c есть строки:
#define USBD_VID 0x6666 // 0x0483 — China // 1155
#define USBD_PID_FS 0xBEEF // 0x5750 — China // 22352
Можно попробовать вернуть как в оригинальной прошивке китайцев было.
НравитсяНравится
Ура!
Поставил Keil, поменял PID\VID на значения от ILITEK 222a:0001 и тач заработал и в андроид и в armbian. Почему выбрал ILITEK- потому что под рукой оказался только резистивный POS-монитор, который как раз заработал на апельсине) Его PID\VID и использовал.
После, в качестве эксперимента, пробовал еще около сотни различных пар PID\VID от различных мультитач дисплеев, ни одна пара не подошла. Так что с этим ILITEKом мне просто повезло)
В андроиде Yet Another MultiTouch Test показывает все 5 касаний, рисование и прокрутка плавные. В linux (в т.ч. armbian) gui похуже приспособлен для сенсора, но пользоваться вполне можно.
Заметил такую штуку в armbian: при касании курсор все время подергивается в пределах нескольких пикселов в окрестности точки касания. А в android в тестах при касаниях больше двух наблюдается мерцание точек касания.
НравитсяНравится
Поздравлю! Это радует, что всё получилось и спасибо, что поделились рабочими PID\VID для вашего случая. ;)
А с дерганьем курсора — возможно это связанно с выставленным разрешением 800х600 (якобы тач скрин для такого дисплея), с которым просчитываются касания в прошивке.так сделано в оригинальной прошивке для GD32, я с этим не экспериментировал, возможно в этом причина.
НравитсяНравится
Подскажите а как поменять PID\VID в андроид, для тач скрина?
НравитсяНравится
Несколько не понятен вопрос, для уточнения — у вас такой же дисплей с тачем и вы подключаете его к андроид-устройству ?
Если так, то в файле Src\usbd_desc.c меняете PID\VID на нужные, компилируете и прошиваете STM32, отвечающий за тач скрин.
НравитсяНравится