Como as comunicações seriais funcionam no Arduino?


16

Com referência às placas Arduino Uno, Mega2560, Leonardo e similares:

  • Como as comunicações seriais funcionam?
  • Qual a velocidade da série?
  • Como eu me conecto entre um remetente e um destinatário?

Observação: isso é uma pergunta de referência.


Você pode achar isso interessante sobre buffers nos dois lados de um Nano conectado a um sistema Raspian executando um data logger Python, usando apenas um cabo de programação USB comum entre os dois: arduino.stackexchange.com/questions/11710/…
SDsolar

Respostas:


16

As comunicações seriais assíncronas (geralmente chamadas de seriais) são usadas para enviar bytes de um dispositivo para outro. Um dispositivo pode ser um ou mais dos seguintes:

  • Arduino
  • PC
  • GPS
  • Leitor de cartão RFID
  • tela de LCD
  • Modem
  • De outros

Taxa de clock e amostragem de dados

Ao contrário das comunicações seriais SPI / USB / I2C, não há sinal de relógio. O relógio de amostragem é uma taxa de amostragem combinada (conhecida como taxa de transmissão). O remetente e o destinatário devem ser configurados para usar a mesma taxa ou o receptor receberá dados sem sentido (devido ao fato de os bits não terem sido amostrados na mesma taxa em que foram enviados).

A transmissão é assíncrona, o que basicamente significa que os bytes podem ser enviados a qualquer momento, com intervalos diferentes entre eles. Este gráfico ilustra um único byte sendo enviado:

Comunicação serial - enviando um byte

O gráfico acima mostra a letra 'F' sendo transmitida. No ASCII, isso é 0x46 (em hexadecimal) ou 0b01000110 (em binário). O menos (baixa ordem) pouco significativo é transmitido em primeiro lugar, portanto, no gráfico acima, você vê os bits que chegam na ordem: 01100010.

O tempo "ocioso" entre os bytes é transmitido como bits "1" contínuos (efetivamente, a linha de transmissão é mantida alta continuamente).

Para indicar o início de um byte, o bit inicial é sempre indicado puxando a linha para baixo, como mostrado no gráfico. Depois que o receptor vê o bit inicial, ele aguarda 1,5 vezes o tempo de amostragem e, em seguida, realiza a amostragem dos bits de dados. Aguarda 1,5 vezes para que:

  • Ignora o bit inicial
  • Amostras na metade do próximo bit

Se a taxa de transmissão for 9600, por exemplo, a taxa de amostragem será de 1/9600 = 0.00010416segundos (104,16 µs).

Assim, a 9600 bauds, após receber um bit de partida, o receptor aguarda 156,25 µs e depois coleta cada 104,16 µs.

Iniciar sincronização de bits

O objetivo do Stop Bit é garantir que definitivamente haja um bit entre cada byte. Sem o bit de parada, se um byte terminasse em zero, seria impossível para o hardware diferenciar isso e o bit inicial do próximo byte.

Para produzir a saída acima em um Uno, você pode escrever este código:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

Número de bits de dados

Para economizar tempo de transmissão (antigamente, heh), você tinha permissão para especificar diferentes números de bits de dados. O hardware AtMega suporta numeração de bits de dados de 5 a 9. Claramente, quanto menos bits de dados, menos informações você pode enviar, mas mais rápido será.


Bits de paridade

Opcionalmente, você pode ter um bit de paridade. Isso é calculado, se necessário, contando o número de 1s no caractere e, em seguida, certificando-se de que esse número seja ímpar ou uniforme, definindo o bit de paridade como 0 ou 1, conforme necessário.

Por exemplo, para a letra "F" (ou 0x46 ou 0b01000110), você pode ver que existem três no local (em 01000110). Assim, já temos paridade ímpar. Portanto, o bit de paridade seria o seguinte:

  • Sem paridade: omitido
  • Paridade par: a 1 (3 + 1 é par)
  • Paridade ímpar: a 0 (3 + 0 é ímpar)

O bit de paridade, se presente, aparece após o último bit de dados, mas antes do bit de parada.

Se o receptor não receber o bit de paridade correto, isso é chamado de "erro de paridade". Indica que há algum problema. Possivelmente, o remetente e o destinatário estão configurados para usar diferentes taxas de transmissão (bits) ou houve ruído na linha que transformou o zero em um ou vice-versa.

Alguns sistemas antigos também usavam paridade de "marca" (onde o bit de paridade era sempre 1, independentemente dos dados), ou paridade de "espaço" (onde o bit de paridade era sempre 0, independentemente dos dados).


Transmissão de 9 bits

Alguns equipamentos de comunicação usam dados de 9 bits, portanto, nesses casos, o bit de paridade é transformado no 9º bit. Existem técnicas especiais para enviar esse nono bit (os registradores são registradores de 8 bits e, portanto, o nono bit deve ser colocado em outro lugar).


Número de bits de parada

O equipamento inicial tendia a ser um pouco mais lento eletronicamente; portanto, para dar tempo ao receptor para processar o byte recebido, às vezes era especificado que o remetente enviaria dois bits de parada. Isso basicamente adiciona mais tempo em que a linha de dados é mantida alta (mais um bit) antes que o próximo bit inicial possa aparecer. Esse tempo extra fornece ao receptor tempo para processar o último byte recebido.

Se o receptor não obtiver 1 lógico quando o bit de parada for, isso é chamado de "erro de enquadramento". Indica que há algum problema. Muito possivelmente, o remetente e o destinatário estão configurados para usar diferentes taxas de transmissão (bits).


Notação

Geralmente, a comunicação serial é indicada informando a velocidade, número de bits de dados, tipo de paridade e número de bits de parada, assim:

9600/8-N-1

Isso está nos dizendo:

  • 9600 bits por segundo
  • 8 bits de dados
  • Sem paridade (você pode ver: E = par, O = ímpar)
  • 1 bit de parada

É importante que o remetente e o destinatário concordem com o exposto acima; caso contrário, é improvável que a comunicação seja bem-sucedida.


Pin-outs

O Arduino Uno possui os pinos digitais 0 e 1 disponíveis para a série de hardware:

Pinos seriais do Arduino Uno

Para conectar dois Arduinos, você troca Tx e Rx assim:

Conectando dois Arduinos juntos


Rapidez

Uma ampla gama de velocidades é suportada (veja o gráfico abaixo). As velocidades "padrão" são geralmente um múltiplo de 300 baud (por exemplo, 300/600/1200/2400 etc.).

Outras velocidades "não padronizadas" podem ser manipuladas configurando os registros apropriados. A classe HardwareSerial faz isso por você. por exemplo.

Serial.begin (115200);  // set speed to 115200 baud

Como regra geral, supondo que você esteja usando dados de 8 bits, é possível estimar o número de bytes que você pode transmitir por segundo dividindo a taxa de transmissão por 10 (por causa do bit inicial e do bit final).

Assim, a 9600 bauds você pode transmitir 960 bytes ( 9600 / 10 = 960) por segundo.


Erros na taxa de transmissão

A taxa de transmissão no Atmega é gerada dividindo o relógio do sistema e contando até um número predefinido. Esta tabela da folha de dados mostra os valores do registro e as porcentagens de erro de um relógio de 16 MHz (como o do Arduino Uno).

Erros na taxa de transmissão

O bit U2Xn afeta o divisor da taxa de clock (0 = dividir por 16, 1 = dividir por 8). O registro UBRRn contém o número que o processador conta.

Portanto, na tabela acima, vemos que obtemos 9600 baud de um relógio de 16 MHz da seguinte maneira:

16000000 / 16 / 104 = 9615

Dividimos por 104 e não por 103 porque o contador é relativo a zero. Portanto, o erro aqui é o 15 / 9600 = 0.0016que se aproxima do que a tabela acima diz (0,02%).

Você notará que algumas taxas de transmissão têm uma quantidade de erro mais alta que outras.

De acordo com a folha de dados, a porcentagem máxima de erro para 8 bits de dados está na faixa de 1,5% a 2,0% (consulte a folha de dados para obter mais detalhes).


Arduino Leonardo

O Arduino Leonardo e o Micro têm uma abordagem diferente para as comunicações seriais, pois se conectam diretamente via USB ao computador host, não pela porta serial.

Por isso, é necessário aguardar que o Serial fique "pronto" (pois o software estabelece uma conexão USB), com algumas linhas extras, como esta:

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

No entanto, se você realmente deseja se comunicar através dos pinos D0 e D1 (em vez de usar o cabo USB), precisará usar Serial1 em vez de Serial. Você faz assim:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

Níveis de tensão

Observe que o Arduino usa níveis TTL para comunicações seriais. Isso significa que espera:

  • Um bit "zero" é 0V
  • Um bit "um" é + 5V

O equipamento serial mais antigo projetado para conectar à porta serial de um PC provavelmente usa níveis de tensão RS232, a saber:

  • Um bit "zero" é de +3 a +15 volts
  • Um bit "um" é de -3 a -15 volts

Não apenas isso é "invertido" com relação aos níveis de TTL (um "um" é mais negativo que um "zero"), o Arduino não pode lidar com tensões negativas em seus pinos de entrada (nem positivas maiores que 5V).

Portanto, você precisa de um circuito de interface para se comunicar com esses dispositivos. Apenas para entrada (para o Arduino), um transistor simples, diodo e alguns resistores o farão:

Buffer de inversão

Para comunicação bidirecional, você precisa ser capaz de gerar tensões negativas, portanto é necessário um circuito mais complexo. Por exemplo, o chip MAX232 fará isso, em conjunto com quatro capacitores de 1 µF para atuar como circuitos da bomba de carga.


Serial de software

Existe uma biblioteca chamada SoftwareSerial que permite realizar comunicações seriais (até um ponto) no software, e não no hardware. Isso tem a vantagem de poder usar diferentes configurações de pinos para comunicações seriais. A desvantagem é que fazer serial em software é mais intensivo em processador e mais propenso a erros. Consulte Software Serial para mais detalhes.


Mega2560

O Arduino "Mega" possui 3 portas seriais de hardware adicionais. Eles estão marcados no quadro como Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3. Eles devem ser usados ​​em preferência ao SoftwareSerial, se possível. Para abrir essas outras portas, use os nomes Serial1, Serial2, Serial3, assim:

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

Interrompe

O envio e o recebimento, usando a biblioteca HardwareSerial, usam interrupções.

Enviando

Quando você faz um Serial.print, os dados que você está tentando imprimir são colocados em um buffer interno de "transmissão". Se você possui 1024 bytes ou mais de RAM (como no Uno), obtém um buffer de 64 bytes, caso contrário, um buffer de 16 bytes. Se o buffer tiver espaço, ele Serial.printretornará imediatamente, não atrasando seu código. Se não houver espaço, ele "bloqueia" a espera do buffer ser esvaziado o suficiente para que haja espaço.

Então, conforme cada byte é transmitido pelo hardware, uma interrupção é chamada (a interrupção "USART, Data Register Empty") e a rotina de interrupção envia o próximo byte do buffer para fora da porta serial.

Recebendo

Quando os dados recebidos são recebidos, uma rotina de interrupção é chamada (a interrupção "USART Rx Complete") e o byte recebido é colocado em um buffer de "recebimento" (o mesmo tamanho do buffer de transmissão mencionado acima).

Quando você liga, Serial.availabledescobre quantos bytes estão disponíveis no buffer de "recebimento". Quando você chama, Serial.readum byte é removido do buffer de recebimento e retornado ao seu código.

No Arduinos com 1000 bytes ou mais de RAM, não há pressa para remover dados do buffer de recebimento, desde que você não o deixe encher. Se for preenchido, outros dados recebidos serão descartados.

Observe que, devido ao tamanho desse buffer, não há motivo para aguardar a chegada de um número muito grande de bytes, por exemplo:

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

Isso nunca funcionará porque o buffer não pode suportar tanto.


Dicas

  • Antes de ler, verifique sempre se os dados estão disponíveis. Por exemplo, isso está errado:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    O Serial.availableteste garante apenas que você tenha um byte disponível, no entanto, o código tenta ler dois. Pode funcionar, se houver dois bytes no buffer, caso contrário, você receberá -1 retornado, que se parecerá com 'ÿ' se impresso.

  • Esteja ciente de quanto tempo leva para enviar dados. Como mencionado acima, a 9600 bauds você transmite apenas 960 bytes por segundo, portanto, tentar enviar 1000 leituras de uma porta analógica, a 9600 baud, não terá muito sucesso.


Referências


No 1º gráfico: com as setas, parece que o bit de parada é transmitido primeiro. Se você trocasse Rx / Tx e a direção das setas, acho que é menos confuso.
ott--

Ele deveria ser lido da esquerda para a direita (como é esta frase) e, portanto, as coisas à esquerda acontecem primeiro. Coloque assim: em um osciloscópio, é assim que você veria o traço.
Nick Gammon

Ok, com a explicação do osciloscópio, eu compro isso. :-)
ott--

No entanto, tenho pensado que o seu argumento faz muito sentido. O que os outros pensam? Seria mais claro se as setas fossem invertidas e eu trocasse Rx / Tx?
Nick Gammon

1
@ linhartr22 Alterei para ler "dados sem sentido", que provavelmente está mais perto.
Nick Gammon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.