O Raspberry Pi pode de maneira confiável produzir um serial de 9600 bauds e existe um código de exemplo?

29

Gostaria de saber como é possível usar o bitbanging para conduzir uma série de 9600 bauds através dos pinos GPIO no Raspberry Pi.

Obviamente, o Linux não é uma plataforma muito boa para troca de bits, pois há um grande número de drivers e outras interrupções que podem bloquear a CPU por longos períodos de tempo (1 a 10 ms). No entanto, a situação ficou muito melhor recentemente, e alguma preferência está agora ativada regularmente nos kernels. Eu também suspeito que um kernel corrigido em tempo real possa ser facilmente usado no Raspberry Pi e o hardware e os drivers conectados possam ser selecionados com cuidado.

Meu padrão de confiabilidade é que ele permaneça dentro das tolerâncias seriais normais de 9600 baud na maior parte do tempo. Atualmente, não tenho certeza de quantos erros são toleráveis ​​na prática, mas há retransmissões e reconhecimentos no protocolo, portanto, pelo menos é um pouco tolerante.

Então, minhas perguntas são:

  • O software userland pode bit com velocidade confiável de 9600 bauds?
  • Isso precisa de um kernel corrigido em tempo real?
  • Quantos erros devo esperar em geral?

Além disso, existe algum código de exemplo executando serial via GPIO bit banging? Eu conheço o Arduino SoftSerial, mas é isso.

Apenas para garantir a questão: esta não é uma interface serial padrão e o UART do Raspberry Pi já está sendo usado para outro link serial. Não desejo respostas que sugiram hardware externo (como Gertboard ou Arduino) nem respostas que dependam do UART.

Nakedible
fonte
1
+1 boa pergunta. Eu também estaria interessado na resposta para isso. No entanto, a questão de reliabilitydepender da ação e das expectativas.
Jivings
2
O RS-232 precisa de voltagens negativas, mas esse link serial não usa voltagens RS-232. Não sei, mas acredito que o RPi requer o conversor TTL para RS-232 padrão, como o MAX232 ou qualquer uma das dezenas de opções. No entanto, só para esclarecer, isso não é relevante para a minha pergunta, eu só queria responder.
Nakedible 24/09/12
4
Também existe um segundo UART nos pinos 14 e 15 do GPIO. Não é tão completo como o principal
John La Rooy
1
@ Nakedible, você poderia compartilhar seu trabalho? Eu tenho um problema semelhante e gostaria de ver como você fez uma segunda série via spi. obrigado MSP

Respostas:

15

Finalmente resolvi isso, mas de uma maneira pouco ortodoxa. Abandonei o bit-banging por não ser muito confiável e tentei encontrar outras soluções que me permitiriam a mesma coisa sem adicionar mais hardware. Eu estava pensando em escrever um driver de kernel, o que acionaria uma interrupção no GPIO e, em seguida, reconfiguraria o pino como SPI e o usaria para ler um byte de dados inteiro - mas tive uma idéia melhor.

Eu uso o SPI para provar as linhas em 20x a taxa de transmissão. Ignoro completamente os pinos SCLK e SS, conecto a linha RX ao MISO e a linha TX ao MOSI. Isso me dá uma visão do osciloscópio (1 bit) na linha RX e vê claramente os bits sendo transmitidos na linha serial:

00 00 00 00 00 00 
00 00 00 00 01 FF 
FF FF FF FF 00 00 
01 FF FF FF FF FF 
FF FF E0 00 00 00 
00 00 07 FF FF FF 
FF FF 

A partir disso, é uma simples questão de codificação descobrir as posições corretas a partir das quais é possível amostrar os bits de dados reais. O lado de envio é igualmente trivial, só preciso converter cada byte em um longo fluxo de bits com o bit inicial e o final incluído.

A razão pela qual isso funciona melhor do que o bit-bang é que o SPI tem seu próprio relógio que não congela com o kernel e as linhas de envio e recebimento do SPI têm um FIFO de 16 bytes para transferência, que também é independente do congelamento do kernel. Para 9600 bauds, estou usando um relógio SPI de 250kHz e isso significa que posso dormir por até um milissegundo entre encher e esgotar os FIFOs sem erros de transmissão. No entanto, para errar no lado seguro, estou usando 300 µs dorme. Testei brevemente até que ponto eu poderia levar isso a sério e, pelo menos, um relógio SPI de 2MHz ainda era utilizável; portanto, essa solução também se adapta a taxas de transmissão mais altas.

A parte mais feia dessa solução é que o driver SPI do kernel não suporta essa transferência de bits de streaming. Isso significa que não posso fazer isso escrevendo meu próprio módulo do kernel usando o driver SPI do kernel, e também não posso fazer isso usando /dev/sdidev0.0 da área do usuário. No entanto, no Raspberry Pi, o SPI e outros periféricos são acessíveis diretamente da terra do usuário por mmap (): n / dev / mem, ignorando completamente o controle do kernel. Não estou muito satisfeito com isso, mas funciona perfeitamente e oferece o benefício adicional de que falhas de segmentação na região do usuário não podem travar o kernel (a menos que mexam com os outros periféricos por acidente). Quanto ao uso da CPU, 300 µs dorme parecem me fornecer cerca de 7% de uso constante da CPU, mas meu código é muito ruim. Aumentar a duração do sono obviamente diminui o uso da CPU diretamente.

Edit: Esqueci de mencionar, usei a boa biblioteca bcm2835 para controlar o SPI da userland, estendendo-a quando necessário.

Resumindo: posso transmitir e receber de forma confiável um link serial de 9600 baud inteiramente da terra do usuário, usando diretamente o chip SPI via / dev / mem a 250kHz no Raspberry Pi.

Nakedible
fonte
@ Nakedible - Você pode elaborar um pouco ou fornecer links na parte mmap (). Eu estou trabalhando na mesma coisa.
Jay K
Você está usando o hardware SPI para transmitir também?
Cheetah
Sim, a transmissão também funciona.
Nakedible
3
Você pode, por favor, dar instruções passo a passo sobre como você pode enviar e receber código e compartilhar quaisquer pacotes modificados, se você usar algo que você mesmo corrigiu ... TIA!
valentt
10

Parece que, pelo menos sem as correções em tempo real (CONFIG_PREEMPT_RT), o Raspberry Pi não pode confiavelmente confundir um serial de 9600 bauds.

Eu usei um testador de latência simples que configurou todas as coisas do lado do linux de maneira otimizada (sched_fifo, prioridade 99, cpu_dma_latench 0us, mlockall). Tentei dormir por 100 µsec (aproximadamente 9600 baud) e verificar a latência excedente em um sistema silencioso por 2 minutos. Os resultados foram:

Mín: 12 µsec Média: 24 µsec Máx: 282 µsec

Este parecia ser o resultado comum. O máximo variou em medições mais lentas entre 100 µsec e 300 µsec. Eu também verifiquei a distribuição e parece que a grande maioria está na faixa de 24 µsec. Existem apenas alguns acima de 50 µsec, mas quase sempre existem alguns. Às vezes, também existem latências enormes, como 4000 µsec, mas elas são incomuns o suficiente para serem ignoradas, pelo menos por enquanto.

Eu acho que as latências máximas devem estar abaixo de 50 µsec para 9600 baud para não obter erros e qualquer latência acima de 100 µsec causa um pouco de falta na transmissão ou recepção.

Tudo isso sem nem tocar nos pinos do GPIO ainda. Como eu não consegui executar uma operação limpa, mesmo com apenas 2 segundos, parece seguro dizer que, sem correções em tempo real, o Raspberry Pi não pode atrapalhar um link serial de 9600 bauds sem gerar erros por muito tempo.

Testarei os patches em tempo real mais tarde, se eu tiver tempo.

(Ferramenta usada: http://git.kernel.org/?p=linux/kernel/git/clrkwllms/rt-tests.git;a=summary )

Atualização: O kernel RPi trava na inicialização sem detectar o cartão SD se compilado com o conjunto de patches CONFIG_PREEMPT_RT. Pode ser uma coisa simples de consertar, mas vendo as diferenças bagunçadas na fonte RPi, acho que quero esperar até que mais delas estejam no kernel da linha principal.

Então, testar isso é muito difícil e estou abandonando.

Nakedible
fonte
0

Você não precisa morder. Você pode configurar uma interrupção de terra do usuário no rx gpio para detectar a queda do bit inicial. Em seguida, defina uma interrupção de tempo para amostrar no meio dos bits. Um módulo do kernel fazendo isso é possível.

Lalo UY
fonte
Mas isso ainda está batendo um pouco. Na verdade, é assim que você costuma bater.
Philippos