
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:
- Conversor de hardware USB <-> AVC-LAN de design
- Desenvolva firmware para ele, para que ele se conecte como um USB-HID.
- 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)
- Desenvolva um aplicativo Android com funcionalidade semelhante ao (ou superior) ao nativo Priusovsky
- Alinhar a câmera traseira
- 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 - spoilerTalvez 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 tardeAinda 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, USB_DESC_TYPE_ENDPOINT, HID_EPOUT_ADDR, 0x03, HID_EPOUT_SIZE, 0x00, HID_FS_BINTERVAL,
E adicione mais alguns pontos:
Descritor de configuração(bc2bd583c98715e106fcb3ab07b266bc9221be36):
0x07, USB_DESC_TYPE_ENDPOINT, HID_EPIN_ADDR2, 0x03, HID_EPIN_SIZE, 0x00, HID_FS_BINTERVAL, 0x07, USB_DESC_TYPE_ENDPOINT, HID_EPOUT_ADDR2, 0x03, HID_EPOUT_SIZE, 0x00, HID_FS_BINTERVAL,
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,
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) {
Mais adiante no programa:
- 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.
- Processando a interface no nível do controlador e enviando pacotes com relatórios
- 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