Sistema de mídia para Toyota Prius (Restayl)



Este é o primeiro artigo (introdutório) da série sobre como vou refinar o sistema de mídia do carro. O projeto em si está em andamento, não há tempo, como todo mundo, portanto, queridos leitores, por favor, seja paciente, porque muitas vezes não prometo rebitar artigos.

Tudo começou com o fato de eu ter um Prius.

E a primeira coisa que chamou minha atenção foram os problemas com a atualização da navegação. A seguir, são muito escassos, mas em alguns lugares os recursos necessários de um dispositivo chamado "Monitor Multifuncional" (em pessoas comuns - a cabeça). E isso acontece no contexto de um grande número de rádios chinesas com Android a bordo e de muitas comodidades. Mas sua instalação em um local regular implica a privação de tais "pães" como um diagrama da distribuição de energia e controle climático.

A idéia nasceu para, de alguma forma, conectar o rádio Android ao carro com mais força do que os irmãos chineses sugerem. Sobre isso e o artigo.

Situação inicial


Então A bordo, há uma tela de 7 polegadas com uma tela de toque resistiva, conectada a outros eletrônicos com as linhas TX + e TX-. E já existem 3 pares desse tipo na cabeça.No circuito, esse milagre é chamado AVC-LAN, e se parece com isso:



Parte 1: Olhando em volta


Como você pode ver, a cabeça está na brecha da rede, entre o roteador e a cadeia posterior do rádio, o amplificador (eu o tenho separadamente) e uma conexão com a unidade de navegação segue um canal separado. Em outro lugar, uma unidade de estacionamento está suspensa, não mencionada de nenhuma maneira nos meus esquemas. Bem, bem ... eu decidi adiar a proximidade com ela até tempos melhores. Além disso, o estacionamento é mais uma função do jogo do que uma necessidade real.

Depois de remover todos os desnecessários, obtemos aproximadamente o seguinte diagrama de blocos dos dispositivos:



Reflexões


Havia uma idéia de simplesmente substituir a unidade de navegação por algo android, mas desapareceu quando eu entendi mais profundamente como eles se comunicavam com a cabeça. Além da AVC-LAN, esses módulos também são conectados pela linha GVIF (Gigabit Video InterFace), e essa mesma face dos fabricantes de conversores pode quebrar acidentalmente se eu também comprar um conversor de sinal de vídeo na GVIF por mais de US $ 100. “Viver sem rosto é ser pode ser difícil, mas .. "- soou na minha cabeça o motivo de uma música famosa, e eu não gostei da decisão.

Havia soluções na rede com a instalação de um rádio chinês em vez de um receptor de rádio. Isso não me agradou, porque os dois monitores são redundância irracional. IMHO.

Solução


Nasceu a seguinte solução: substituir a cabeça inteira e finalizar o rádio Android, fazendo amizade com o Prius, pelo qual:

  1. Conversor de hardware USB <-> AVC-LAN de design
  2. Desenvolva firmware para ele, para que ele se conecte como um USB-HID.
  3. Torne-o composto para que uma das funções seja detectada como um teclado de hardware normal (para usá-lo como um controle nativo nos botões no painel)
  4. Desenvolva um aplicativo Android com funcionalidade semelhante ao (ou superior) ao nativo Priusovsky
  5. Alinhar a câmera traseira
  6. Resolver problemas na parte mecânica (instalação em local regular)

No processo, será necessário desenvolver outro aplicativo para Android - um sniffer comum, para que seja mais conveniente reverter pacotes no AVC-LAN. Ao mesmo tempo e prática.

Deve ficar assim:



Como base de hardware, foi decidido usar uma placa de treinamento no SM32F103:



Encomendado no AliExpress por US $ 2,05.

Ou pesquise - spoiler
Talvez o lote já tenha sido excluído pelo vendedor, por isso dou a sequência mágica para pesquisar no Ali:
STM32F103C8T6 Módulo mínimo da placa de desenvolvimento do sistema do BRAÇO STM32

O que eu gosto nela:

  • Módulo de hardware USB (dispositivo) a bordo do processador
  • Pilha USB adequada do fabricante (diferente da Freescale-ovsky, não é lembrada à noite).
  • Portas GPIO gratuitas que podem ser usadas para conectar botões regulares nas laterais do monitor. Talvez isso oculte os botões de hardware do rádio abaixo do painel. Não sei o que ela vai ser
  • E nele você pode pendurar o conversor AVC-LAN em níveis lógicos

Descreverei mais adiante na ordem de implementação, que é devida, antes de tudo, ao meu conhecimento pessoal. I.e. Tentei perceber os lugares em que eles não estavam no começo, deixando o que certamente deveria acontecer.

De qualquer forma, vários artigos são planejados em diferentes hubs. O projeto acaba sendo muito FullStack - da conexão de hardware ao aplicativo Android.

Parte 2: USB, HID, descritores e tudo para obter um protótipo piloto


O primeiro passo era obter um monte de dispositivos e telefones, para que o dispositivo pudesse transferir o pacote para o telefone, e isso - para exibi-lo no aplicativo.
Como Gagarin disse: Vamos!

Dispositivo composto USB HID no STM32


O que eu decidi fazer foi adaptar o exemplo ST de minhas tarefas e obter um dispositivo USB que seja reconhecido pelo host como parte do teclado e "outra coisa" - o Dispositivo RAW HID. O primeiro, como eu já disse, destina-se ao controle nativo do Android, o segundo - à troca direta de pacotes AVC-LAN com o programa no dispositivo.

Com base no CubeMX da STM, e depois de ler muitos artigos sobre como implementar o HID personalizado, descobri uma coisa desagradável na rede: a questão da criação de dispositivos compostos é praticamente ausente ou muito escassa.

Os códigos-fonte serão mais tarde
Ainda não carreguei os códigos-fonte, devido ao fato de o projeto estar agora sendo implementado no modo de treinamento experimental. Se o projeto for concluído com êxito, arraste-os para o Github e edite o artigo com um link para eles.
Na forma em que estão, não faz sentido fazer o upload - há bastante bagunça na Internet sem mim.

USB, Composto, HID


Apenas algumas palavras sobre este assunto. Supõe-se que você esteja mais ou menos familiarizado com o padrão USB. Caso contrário, é melhor primeiro se familiarizar e experimentar exemplos do CubeMX.

Então nós temos:

Exemplo de implementação de pilha e mouse USB do STM. Lá, configuramos alguns descritores e um ponto de extremidade funcional. Isso além de um par de 0x00 e 0x80 para controlar o dispositivo inteiro.

Para implementar meu projeto, preciso que o endpoint do teclado seja bidirecional (não sei por que - é útil) e mais alguns endpoints que serão usados ​​para trocar dados com a segunda função - RAW. Adicione-os.

Tornamos o ponto bidirecional adicionando o ponto OUT ao descritor:

Descritor de configuração.
Ao editar um descritor, fique de olho nos índices e tamanhos.
(2c5cf968121f0d8fa43a6755c09e15ef3a317791):

0x07, /*bLength: Endpoint Descriptor size*/ USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/ HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/ 0x03, /*bmAttributes: Interrupt endpoint*/ HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */ 0x00, HID_FS_BINTERVAL, 


E adicione mais alguns pontos:

Descritor de configuração
(bc2bd583c98715e106fcb3ab07b266bc9221be36):

  /* 59 */ 0x07, /*bLength: Endpoint Descriptor size*/ USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/ HID_EPIN_ADDR2, /*bEndpointAddress: Endpoint Address (IN)*/ 0x03, /*bmAttributes: Interrupt endpoint*/ HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */ 0x00, HID_FS_BINTERVAL, /*bInterval: Polling Interval (10 ms)*/ /* 66 */ 0x07, /*bLength: Endpoint Descriptor size*/ USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/ HID_EPOUT_ADDR2, /*bEndpointAddress: Endpoint Address (IN)*/ 0x03, /*bmAttributes: Interrupt endpoint*/ HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */ 0x00, HID_FS_BINTERVAL, /*bInterval: Polling Interval (10 ms)*/ 


Era um descritor de configuração. Agora, o host terá certeza de que temos algum tipo de dispositivo HID composto e você poderá enviar dados para todos esses pontos. Mas isso não é verdade.
Para tornar isso verdade:

1. Nosso controlador possui uma memória especialmente alocada que é sincronizada com os módulos CAN e USB. Dado que o módulo USB está envolvido de maneira independente no processo de recebimento / transmissão de um pacote de dados, é necessário definir buffers nesta parte da memória para cada terminal individual:

USBD_LL_Init no arquivo usbd_conf.c
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18); HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58); HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPOUT_ADDR , PCD_SNG_BUF, 0x100); HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPIN_ADDR , PCD_SNG_BUF, 0x140); HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPOUT_ADDR2 , PCD_SNG_BUF, 0x180); HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPIN_ADDR2 , PCD_SNG_BUF, 0x1B0); 


Os endereços de buffer são arbitrários, se eles não se sobrepuserem.

Por alguma razão, a pilha ST é gravada com a expectativa de que o dispositivo não tenha mais que um ponto de extremidade bidirecional, portanto, modificaremos a pilha um pouco:

Transferir


Renomeie o procedimento USBD_HID_SendReport para USBD_HID_SendReportEP, adicionando mais um parâmetro - o número do terminal. Deixamos o procedimento com o nome antigo para compatibilidade com versões anteriores, mas no corpo chamamos USBD_HID_SendReportEP com uma constante na forma de um terminal. A solução ainda não é a mais estética, mas funcionará para o experimento e, mesmo que permaneça, não interferirá em um projeto específico.

usbd_hid.c
 uint8_t USBD_HID_SendReportEP (USBD_HandleTypeDef *pdev, uint8_t ep, uint8_t *report, uint16_t len) { ... ,   USBD_HID_SendReport } uint8_t USBD_HID_SendReport (USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len) { return USBD_HID_SendReportEP(pdev,HID_EPIN_ADDR,report,len); } 


Agora tudo está pronto para enviar dados, resta apenas no momento certo para chamar esta função.

Finalização


Por uma questão de ordem, procuramos o projeto e chamamos USBD_LL_CloseEP novamente, mas os pontos de extremidade recém-criados.

Recepção


Para que os pontos de extremidade sejam ajustados moralmente para funcionarem, você precisa chamar USBD_LL_PrepareReceive para eles. Eu recomendo que o leitor repasse a pesquisa do projeto para esta linha e adapte essas chamadas às suas necessidades.

Eu tenho esse choco feio no meu código:

usbd_core.c
 USBD_LL_PrepareReceive(pdev, HID_EPOUT_ADDR+(epnum&0x7F)-1 , hhid->Report_buf, USBD_HID_OUTREPORT_BUF_SIZE); 


I.e. Eu procedi do fato de que o número de pontos de extremidade é seguido. Isso é ruim, IMHO. Não faça isso. No entanto, também não gosta do ST.

Tudo o que resta é acessar o arquivo usbd_hid.c e, especificamente, a função USBD_HID_DataOut, e certifique-se de que a chamada para o manipulador de dados recebido corresponda às suas idéias pessoais sobre o belo. Como também não tive sucesso, o código e a descrição serão longos e incompreensíveis. Mais fácil de fazer você mesmo.

Relatório


Tudo, neste local, temos um dispositivo composto capaz de trocar dados através de dois pontos bidirecionais. No último golpe, "calamos a boca" a curiosidade do motorista da HID, descrevendo um descritor de relatório:

 __ALIGN_BEGIN static uint8_t HID_ReportDesc2[33] __ALIGN_END = { 0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined Page 1) 0x09, 0x01, // USAGE (Vendor Usage 1) 0xa1, 0x01, // COLLECTION (Application) 0x85, 0x01, // REPORT_ID (1) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x1f, // REPORT_COUNT (31) 0x09, 0x00, // USAGE (Undefined) 0x81, 0x00, // INPUT (Data,Ary,Abs) 0x85, 0x02, // REPORT_ID (2) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x1f, // REPORT_COUNT (31) 0x91, 0x00, // OUTPUT (Data,Ary,Abs) 0xc0 // END_COLLECTION }; 

Este relatório informa ao driver HID: haverá cerca de 31 bytes de dados. Não há necessidade de descobrir o que são - basta fornecê-los ao programa que abriu este dispositivo. Em um relatório físico, o byte zero será igual ao índice do relatório (REPORT_ID (2)). Assim, um total de 32 bytes virá.

E insira os dados sobre isso em usbd-hid.c, função USBD_HID_Setup.

usbd-hid.c
  switch (req->bRequest) { case USB_REQ_GET_DESCRIPTOR: if( req->wValue >> 8 == HID_REPORT_DESC) { // TODO: !!!   ,     req->wIndex !!! THIDDescPtrLen * rep = (req->wIndex==1)?&HID_ReportDesc:&HID_ReportDesc2; len = MIN(rep->len , req->wLength); pbuf = rep->ptr; } 

Mais adiante no programa:


  1. Montagem do conversor de nível lógico AVC-LAN e conexão à placa. Análise da camada física do AVC-LAN, formas de onda reais.
  2. Processando a interface no nível do controlador e enviando pacotes com relatórios
  3. Interface ponta a ponta e engenharia reversa Prius. Sniffer de pacote (ou meu primeiro aplicativo Android)

PS


  • Decidi escrever um artigo porque fui forçado (quase), convencendo que isso deveria ser compartilhado. Mesmo se eu não concluir o projeto, algumas das informações mais recentes podem ajudar alguém, mesmo que de forma "bruta".
  • As críticas ao projeto são bem-vindas, pois Eu mesmo ainda não imagino completamente que vai dar certo.
  • Críticas ao artigo, design, apresentação - principalmente porque Este é o primeiro artigo para o recurso. Com a continuação do trabalho, gostaria de expressar meus pensamentos da forma usual e digerível para os leitores

Source: https://habr.com/ru/post/pt411617/


All Articles