Você está na página 1de 57

PESQUISA PID E COMUNICAÇÃO

SERIAL

Matheus Dimas
Nicholas Santana Santos
INTRODUÇÃO
Esta pesquisa foi realizada pelos membros Matheus Dimas e Nicholas Santana Santos,
ambos da eletrônica da equipe Uairrior, e está dividido em dois grandes tópicos, o primeiro
sendo uma pesquisa sobre PID, realizada pelo Matheus, e o segundo tópico uma pesquisa
sobre comunicação serial realizada pelo Nicholas.

Esperamos que este documento seja útil para você.

1
SUMÁRIO

INTRODUÇÃO 1

SUMÁRIO 2

PID 4
introdução 4
Começando de uma forma simples 5
Começando pelo controlador proporcional(P) 8
Como devemos definir o valor da potência do motor ao fazer uma curva? 12
Adicione " I " ao controlador e temos o controlador PI 14
Adicione " D " ao controlador e temos o controlador PI 17
Ajuste o controlador PID sem usar métodos matemáticos complicados 19
Indicações de leitura 21

COMUNICAÇÃO SERIAL 22
COMUNICAÇÃO SERIAL SÍNCRONA 23
SPI (SERIAL PERIPHERAL INTERFACE) 23
DISPOSITIVO 25
FORMA DE ONDA 25
COMUNICAÇÃO HALF-DUPLEX E FULL-DUPLEX 27
SELETOR DO ESCRAVO (SLAVE SELECT) 32
SPI BAUD RATE REGISTER 32
CRC (CYCLIC REDUNDANCY CHECK) 32
COMUNICAÇÃO SPI NO ARDUINO UNO 33
EXEMPLO EM CÓDIGO 34
COMUNICAÇÃO SPI NO STM 35
CONFIGURANDO O STM CUBE MX 35
USANDO A SPI NO SYSTEM WORKBENCH 38
I2C (INTER INTEGRATED CIRCUIT) 41
CONEXÃO UTILIZANDO I2C 42
ENDEREÇAMENTO DOS DISPOSITIVOS 42
FORMA DE ONDA 43
FUNCIONAMENTO DO I2C 43
COMUNICAÇÃO I2C NO ARDUINO UNO 44
EXEMPLO EM CÓDIGO 45
COMUNICAÇÃO I2C NO STM 46
CONFIGURANDO O STM CUBE MX 46
USANDO I2C NO SYSTEM WORKBENCH 48
COMUNICAÇÃO SERIAL ASSÍNCRONA 48
UART (UNIVERSAL ASYNCHRONOUS RECEIVER TRANSMITTER) 49

2
FORMA DE ONDA 50
BAUD RATE 50
FUNCIONAMENTO DO PROTOCOLO UART 51
COMUNICAÇÃO UART NO ARDUINO UNO 53
COMUNICAÇÃO UART NO STM 54
VANTAGENS E DESVANTAGENS DOS PROTOCOLOS 55
VANTAGENS DO SPI 55
DESVANTAGENS DO SPI 55
VANTAGENS DO I2C 55
DESVANTAGENS DO I2C 55
VANTAGENS DA UART 56
DESVANTAGENS DA UART 56
CONCLUSÃO 56

3
PID

introdução

essa pesquisa e uma tradção do seguinte artigo


https://bbs.cmnxt.com/thread-5688-1-1.html​, nesse artigo o autor tenta explicar o sistema de
controle PID de uma maneira extremamente simples, para pessoas que não tenham o
conhecimento de cálculo possam entender. Nessa tradução eu cortei alguns pontos que
não achei tão necessário e adaptei algumas coisas. Isso não é um guia completo sobre
controle PID, apenas uma pequena introdução, no final do texto eu indico algumas leituras
que possam te auxiliar num melhor entendimento do PID e de suas variações P, PI e PD.

4
Começando de uma forma simples

O controlador PID é uma tecnologia de controle, geralmente usada em uma variedade de


dispositivos mecânicos (como veículos, robôs, foguetes). É muito complicado descrever
matematicamente o controlador PID. Este artigo descreve como criar um controlador PID
em um robô LEGO programado com NXT-G.

Este artigo usará exemplos para ilustrar como criar um PID para concluir a tarefa de um
robô seguidor de linha.

As pessoas que estudaram cálculo podem entender facilmente a descrição típica do PID.
Este artigo foi escrito para leitores que não têm quase nenhum conceito de PID, como
crianças de 3 a 8 ano. Considerando que você pode não entender cálculo, tento não usá-lo
e começo a construir todo o conceito a partir de ideias mais simples.

Vamos dar uma olhada na estrutura de um robô seguidor de linha. Olhando para a figura
abaixo, este robô é acionado por dois motores, conectados às rodas A e C. A extremidade
frontal é equipada com um sensor fotoelétrico apontado verticalmente para baixo. A parte
marcada pelo círculo vermelho é a parte que o sensor fotoelétrico pode "ver". O retângulo
grande com uma seta indica o restante do robô e a seta indica a direção do movimento do
robô.

A tarefa de seguir uma linha é uma tecnologia básica de robôs, e também é a primeira coisa
que todos devem fazer ao aprender sobre robôs. Um dispositivo automático capaz de seguir
uma linha tem todas as características de um robô: usa sensores para coletar informações
sobre o ambiente circundante e ajusta o estado de movimento do robô de acordo.

O robô de rastreamento de linha pode usar 1 sensor fotoelétrico, 2 sensores fotoelétricos ou


uma dúzia de sensores fotoelétricos. De fato, quanto mais sensores fotoelétricos você usar,
melhor o efeito de seguir a linha. O uso de apenas um sensor fotoelétrico também pode
permitir que o robô rastreie a linha com precisão (mesmo que a linha seja curvada), mas é
fácil escapar da trajetória quando o robô se move muito rápido. De um modo geral, quanto

5
mais sensores forem usados, mais rápida será a velocidade que você poderá atingir ao
seguir a linha.

Agora vamos experimentar o primeiro método (método não PID). A patrulha de linha é, na
verdade, permitir que o robô ande ao longo da borda da linha, porque se você caminhar ao
longo da própria linha preta, quando o robô se desvia da linha preta e o sensor "vê branco",
não sabemos de que lado da linha o robô está. Se você caminhar ao longo da borda da
linha, quando o sensor fotoelétrico "vê branco", sabemos que o robô está no lado esquerdo
da borda da linha e quando o sensor fotoelétrico "vê preto", sabemos que o robô está no
lado direito da borda da linha. Como o robô segue o lado esquerdo da linha, esse método é
chamado de "regra da esquerda".

Precisamos saber o valor de leitura retornado quando o sensor fotoelétrico "vê branco" e "vê
preto". Um sensor não calibrado típico (valores de 0 a 100) retornará 50 quando vir branco e
40 quando vir preto. Podemos marcar a leitura do sensor fotoelétrico em um segmento de
linha de dados para nos ajudar a entender como alterar a leitura do sensor fotoelétrico no
movimento do robô. A seguir, a leitura do sensor fotoelétrico de "branco" para "preto" que
desenhamos.

Dividimos esse segmento de linha numérica em duas partes: quando o valor do sensor
fotoelétrico for menor que 45, deixe o robô virar à esquerda; quando o valor do sensor
fotoelétrico for maior que 45, deixe o robô virar à direita. Aqui, não consideramos a precisão
da ação de direção do robô. Em uma linha relativamente reta, a ação de direção do robô
pode ser relativamente pequena; em uma linha com muitas curvas, o robô geralmente tem
uma ação de direção significativa. Ao fazer pequenos movimentos, você pode definir a
potência da roda rápida em 50% e a potência da roda lenta em 20%. Quando existem
muitas curvas, você pode definir um valor de 30% de potência nas rodas rápidas e zerar o
valor de potência da roda lenta. Independentemente do valor de potência definido, essa
configuração deve ser a mesma para a direção à esquerda e à direita, ou seja, define um
valor de potência maior em um lado da roda e defina um valor menor no outro lado da roda.

Esse método usado para seguir uma linha pode funcionar, mas o efeito não é muito bom.
Com esse método o robô apenas "sabe" duas coisas: vire à esquerda e vire à direita. Com
esse método de rastreamento de linha, a velocidade do robô geralmente não é muito rápida
e podemos notar muitas trepidações enquanto se tenta seguir a linha.

Mesmo que a linha seja reta, esse método não pode fazer o robô seguir uma linha reta nem
alinhar completamente com a borda da linha. Então Como tornar a patrulha de linha mais
eficiente?

6
Vamos tentar ajustá-lo. Divida o segmento de linha de leitura do sensor fotoelétrico em três
partes.

Quando o valor do sensor fotoelétrico é menor que 43, deixamos o robô virar à esquerda.
Quando o valor do sensor fotoelétrico está entre 44 e 47, deixamos o robô seguir em frente.
Quando o valor do sensor fotoelétrico é maior que 47, deixamos o robô girar para a direita.
Isso é alcançado no programa NXT-G, selecionando sim / não no módulo de julgamento. Na
verdade, você só precisa fazer dois julgamentos em vez de três.

O segundo método de rastreamento de linha é muito melhor que o primeiro. Pelo menos o
robô às vezes segue em frente. Assim como no primeiro método de rastreamento de linha,
você ainda precisa decidir qual método de direção (quanto de potência irá usar na roda
rápida e na roda lenta) com base no quão reto e a sua linha. O robô ainda vai trepidar uma
quantidade considerável para frente e para trás.

7
Começando pelo controlador proporcional(P)

E se dividirmos o segmento de linha do sensor fotoelétrico em mais segmentos? A primeira


coisa que queremos resolver é como determinar o valor de "turn" (quanto de potência irá
usar na roda rápida e na roda lenta) para cada novo segmento. Em nosso primeiro método
de rastreamento de linha, o robô faz apenas duas coisas: vire à esquerda ou à direita; o
valor de "turn" é o mesmo, mas a direção é diferente. No segundo método de rastreamento
de linha, adicionamos "se mantenha reto" com base na leitura obtida dos sensores. Mas e
quando excedemos 3 segmentos, precisamos de mais "tipos" de "turn".

Para ajudar a entender, redesenhamos o segmento de linha de dados da leitura do sensor


fotoelétrico e o convertemos em um gráfico. O eixo X (linha horizontal) é o valor de leitura
do sensor fotoelétrico, que é o mesmo que o segmento de linha de dados do valor de leitura
do sensor fotoelétrico acima. O eixo Y (linha vertical) é o eixo "turn".

O gráfico à esquerda mostra nosso primeiro método de deslocamento de linha - a situação


em que a leitura do sensor fotoelétrico é dividida em dois segmentos.O robô só pode fazer
duas coisas (indicadas pela linha azul), virar à esquerda ou à direita. Fora isso, o valor da
direção é o mesmo. O gráfico no meio é o segundo método de rastreamento de linha - o
caso em que a leitura do sensor fotoelétrico é dividida em três seções. A seção adicionada
no meio é a parte onde o robô se mantém “constante”. A figura à direita é um robô de
rastreamento de linha controlado proporcionalmente.A mudança de direção entre os dois
pontos extremos é muito suave. Se o valor da luz lido pelo sensor fotoelétrico indicar que o
robô está muito perto da borda da linha, o robô faz uma pequena curva; se o valor da luz de
leitura indica que o robô está muito longe da borda da linha, o robô faz uma curva grande.
Proporção é um conceito importante. Proporção significa que existe uma relação linear
entre duas variáveis.Em resumo, a relação entre variáveis ​aparece como uma linha reta
(conforme mostrado na figura à direita).

A expressão da linha reta é:

y = (m * x) + b

8
Aqui, x, y se refere ao valor da coordenada (x, y) de qualquer ponto da linha, m é a
inclinação da linha, b é a interceptação da linha no eixo Y quando x = 0. A inclinação de
uma linha reta é definida como a alteração no valor y dividida pela alteração no valor x em
quaisquer dois pontos da linha.
Simplificando os gráficos e expressões. Primeiro, definimos o ponto central do segmento de
linha de leitura do sensor (eixo X) para 0, como nossa faixa de leitura do sensor fotoelétrico
é de 40 a 50, subtraímos todas as leituras do sensor fotoelétrico por 45 (40 e 50 O valor
médio será (40 + 50) / 2), o resultado será chamado de " erro ". Quando o sensor
fotoelétrico lê 47, você pode obter um erro = 47-45 = 2. Este erro indica a que distância o
sensor fotoelétrico do robô está fora do limite. Quando o sensor fotoelétrico estiver na borda
da linha, o " erro " será 0 (porque a leitura do sensor fotoelétrico é 45 neste momento, e
queremos subtrair 45 da leitura do sensor fotoelétrico). Se os sensores fotoelétricos
estiverem todos em branco, o " erro " será +5, se os sensores fotoelétricos estiverem todos
em preto, o " erro " será -5.

No gráfico acima, usei " erro " para indicar o eixo das coordenadas X. Como essa linha reta
passa pelo eixo Y na origem, o valor de b é 0, o que simplifica a expressão:

y = mx

Ou use nosso método:

P = m * erro

Ainda não definimos o eixo Y, então agora determinamos que o alcance da direção é de -1
(curva máxima à esquerda) a +1 (curva máxima à direita), 0 significa apenas siga em frente.
A inclinação da linha reta no gráfico acima pode ser calculada usando os dois pontos
marcados em vermelho (na verdade, quaisquer dois pontos na linha reta podem ser
usados).

9
Inclinação = m = (alteração no valor de y) / (alteração no valor de x) = (1- (-1)) / (-5-5) =
-2/10 = -0,2 a

inclinação é uma constante proporcional , multiplicando por (valor x) para obter "(valor de
curva) " (valor y).

Em várias publicações sobre PID, a inclinação (também chamada constante proporcional ou


m em uma expressão de linha reta) é chamada " K ". Vários Ks aparecem na literatura do
PID. Você pode pensar em K (ou m, ou inclinação ou constante proporcional) como um fator
de conversão e usar K para converter um número (leitura ou erro do sensor fotoelétrico no
nosso exemplo ) em outro número (como o valor de curva). Este é o papel de K , muito
simples e muito poderoso.

Em seguida, use esses novos nomes de variáveis ​em nossa expressão linear:

P (valor de curva) = K * (erro)

A faixa de valores de " erro " é determinada pelas configurações do sensor fotoelétrico, pela
cor do papel de teste de linha e por outros fatores. Você deve ter notado que, no último
gráfico, a linha não se estende além do valor de erro de -5 a +5. Fora do intervalo de -5 a
+5, não podemos julgar até que ponto o sensor fotoelétrico está fora da linha. Quando a
borda do sensor fotoelétrico está muito distante, o valor da luz lido pelo sensor fotoelétrico
se torna um valor constante, o que significa que a leitura do sensor fotoelétrico e o erro não
estão mais em um relacionamento proporcional. Em uma faixa de valores muito pequena, a
leitura do sensor fotoelétrico é proporcional a essa distância. Portanto o valor deve ser
definido dentro de um intervalo limitado que forneça um relacionamento proporcional. Além
desse intervalo leitura ou erro do sensor fotoelétrico será menor que a situação real, de
modo que, quando o erro for corrigido, não terá um bom efeito.

Na literatura do PID, o intervalo no qual o sensor pode dar uma resposta proporcional é
chamado de "intervalo proporcional". No controle PID, a faixa proporcional é outro conceito
muito importante. Na aplicação do nosso robô de rastreamento de linha, a proporção do
valor de leitura do sensor fotoelétrico é de 40 a 50, a proporção do erro é de -5 a +5 e a
proporção do motor é de -100 (potência total de volta) a +100 (potência total) continue).

Queremos que o intervalo da proporção seja o mais amplo possível. A faixa de escala do
sensor fotoelétrico é bem pequena, ou seja, o sensor fotoelétrico deve estar muito próximo
da borda da linha para obter as informações da escala. A largura da faixa proporcional
depende principalmente da altura do sensor fotoelétrico do papel de teste de rastreamento
de linha. Se o sensor fotoelétrico estiver muito próximo do papel de teste de linha, como
0,16 polegadas (cerca de 0,16 cm), o sensor fotoelétrico verá um pequeno círculo no papel
de teste de linha. Um pequeno movimento do sensor fotográfico produzirá um erro (erro) no
intervalo de -5 a +5 , que é o intervalo proporcional. Você pode dizer que o campo de visão
do sensor fotoelétrico é estreito e apenas uma pequena parte do papel de teste de linha
pode ser vista. Contudo existem duas desvantagens em aumentar a posição do sensor
fotoelétrico, é mais fácil dar uma resposta incorreta à luz ambiente e quando a altura do

10
sensor fotoelétrico do papel de teste de linha é grande o suficiente, os valores lidos para
preto e branco são os mesmos.

11
Como devemos definir o valor da potência do motor ao fazer uma
curva?

Uma maneira de fazer uma curva é definir uma "potência alvo", que eu chamo de " TP ". Tp
é o valor de potência de dois motores quando o robô segue em frente quando erro = 0.
Quando o erro não é 0, usamos a expressão Turn = K * ( erro ) para calcular como alterar a
potência de dois motores, a potência de um motor é Tp + Turn e a potência do outro motor é
Tp -Turn . Observe que, como nosso intervalo de erros é de -5 a +5, o valor de Turn
também terá valores positivos e negativos, o que equivale a virar em direções diferentes. É
exatamente isso que precisamos: ele pode definir automaticamente o valor da potência do
motor corretamente, determinar qual velocidade do motor é rápida e qual velocidade do
motor é lenta. Assumimos que o motor esquerdo está conectado à porta A e seu valor de
potência é Tp + Turn ; o motor direito está conectado à porta C e seu valor de potência é Tp
- Turn . Quando o erro é positivo, o valor da curva é positivo, Tp + Turn é maior que Tp , o
motor à esquerda acelera e o motor à direita diminui a velocidade. Quando o erro é alterado
para um sinal negativo temos desta vez Tp + Turn valendo menos que Tp, a velocidade
esquerda do motor diminui, Tp - Turn é maior que Tp e o motor à direita acelera.

Para começar, temos que medir a leitura do sensor fotoelétrico quando o sensor fotoelétrico
lê em preto e branco. Com base nesses dois valores, podemos calcular o deslocamento
(valor de compensação) e subtrair esse valor da leitura do sensor fotoelétrico para
convertê-lo em um valor de erro . O deslocamento (valor da compensação) é a média dos
valores do sensor fotoelétrico branco e preto. Para simplificar, presuma que o deslocamento
(valor da compensação) tenha sido medido e armazenado em uma variável chamada
deslocamento. A constante K é chamada Kp (constante K no controlador proporcional ).
Para definir um valor de um chute inicial para Kp e, em seguida, corrija-o por tentativa e
erro. Podemos estimar um valor com base nas características do robô e do sensor: defina
Tp (potência alvo) para 50 e, quando o erro for 0, ambos os motores giram no valor de
potência 50; o intervalo de erro é de -5 a +5. Esperamos que, quando o erro mude de 0
para -5, o valor da potência do motor mude de 50 para 0.

Kp = ( 0-50 ) / (-5-0) = 10

Usamos Kp = 10 para converter erro (O valor do erro é convertido em um valor de curva)


Para cada alteração de 1 unidade, aumentaremos o valor de potência de um motor em 10 e
a potência do outro motor em -10. O código virtual é o seguinte:
● Kp = 10 #Inicializar variáveis
● deslocamento = 45
● Tp = 50
● Loop infinito
● LightValue = ler o sensor de luz #O valor atual de leitura do sensor fotoelétrico
● error = LightValue-offset #Subtrai o offset (compensação) para calcular o erro
● Turn = Kp * error #Controle proporcional
● powerA = Tp + Turn #Valor de potência do motor A

12
● powerC = Tp -Turn #Valor de potência do motor C
● Finalize o Loop infinito
Se o robô estiver fugindo da linha, em vez de segui-la, você precisará alterar a direção da
curva. Mude o valor de Kp para -10 e veja o que acontece. Se isso puder corrigir a direção
do robô, altere o valor de Kp de volta para +10 e altere as duas linhas de código que
definem a potência do motor da seguinte maneira:

powerA = Tp -Turn
powerC = Tp + Turn

O Kp determina a rapidez com que o robô retorna a linha quando o robô sai gradualmente
da linha; Tp determina a velocidade com que o robô avança ao longo da linha.

Se o percurso for relativamente reta, você poderá definir um valor de Tp maior para
aumentar a velocidade de execução do robô e definir o valor de Kp menor para diminuir o
movimento de correção do robô. Já se o percurso tiver muitas curvas Tp terá que ser menor
e Kp maior. Especulamos que o Kp tenha um valor inicial de Kp = 10. Para Tp (valor alvo da
potência), você pode começar com um valor relativamente baixo, como 15 (o robô se
moverá muito lentamente). Experimente e veja como funciona. Quando o robô girar muito
lentamente e acabar saindo da linha aumente Kp e continue tentando. Se o robô girar para
lado e para o outro diversas vezes reduza Kp . Se o robô consegue seguir a linha de uma
maneira boa, aumente Tp e observe o robô a uma velocidade mais rápida. Embora Kp
geralmente não mude muito, para cada novo valor de Tp , você irá sim precisa determinar
um novo valor de Kp .

13
Adicione " I " ao controlador e temos o controlador PI

Para melhorar a velocidade de resposta do controlador P, adicionamos uma nova peça à


expressão a integração, "I" no PID. Integral é um conteúdo muito importante em matemática
avançada, aqui só precisamos usá-lo diretamente. A integral é usada para calcular a soma
dinâmica de erros. Sempre que lemos o valor do sensor fotoelétrico e calculamos o erro ,
adicionamos o erro a uma variável que chamamos de integral .

integral = integral + erro

Esta expressão não é uma expressão matemática comum e usa um método para acumular
uma série de valores, que é frequentemente usada em programação. Nos programas de
computador, essa expressão tem um significado diferente da matemática. Isso "=" significa
atribuição, o que significa atribuir o resultado do cálculo à direita à esquerda. Em seguida,
como na parte P, multiplicamos a integral por uma constante proporcional, que é outro K.
Como essa constante de proporcionalidade está relacionada à parte integral, chamamos de
Ki . Como na seção de controle proporcional P, multiplicamos a integral por uma constante
para obter um valor de correção. Precisamos adicionar esse valor de correção à variável
Turn .

Turn = Kp * ( erro ) + Ki * ( integral )

A descrição acima é a expressão básica do controlador PI. Turn é a correção do motor, Kp *


( erro ) é a parte do controle proporcional, Ki * ( integral ) é a parte do controle integral.

O que a parte de controle integral faz? Se o erro permanecer o mesmo por vários ciclos, a
parte integral se tornará cada vez maior. Por exemplo, se lermos o valor do sensor
fotoelétrico e calcularmos o erro como 1, após um curto período de tempo, leremos
novamente o valor do sensor fotoelétrico; dessa vez, o erro será 2, o terceiro erro ainda
será 2, a integral será 1 + 2 + 2 = 5. Integral é 5, mas o erro nesta etapa é apenas 2. Na
quantia de correção, a parte integral pode ter um grande efeito, mas, em geral, leva um
tempo relativamente longo para afetar o sistema.

Outra função do controle integral é remover pequenos erros. Durante a inspeção da linha,
se o sensor fotoelétrico estiver muito próximo da linha, mas não exatamente na linha, o erro
será pequeno e apenas uma pequena quantidade de correção será aplicada. Você pode
corrigir esse pequeno erro alterando o Kp no controle proporcional , mas geralmente o robô
irá oscilar. A parte de controle integral pode perfeitamente corrigir pequenos erros, porque a
integrante é o acúmulo de erros, vários pequenos erros consecutivos podem tornar a
integral (integral) grande o suficiente para entrar em vigor.

Podemos entender o controle integral como a "memória" do controlador. Algumas perguntas


sutis sobre um pequeno problema (na verdade, não é um problema pequeno, mas temos
que transformá-lo em um pequeno problema) o tempo. O cálculo integral é na verdade a

14
soma do erro × ( tempo unitário) com um espaçamento de tempo unitário ( dT ) que é o
intervalo de tempo desde a última leitura do valor do sensor fotoelétrico até a leitura do valor
do sensor fotoelétrico neste momento.

integral = integral + erro * (dT)

Portanto, toda vez que adicionamos à integral deve haver erro × dT . Medir o dT de um robô
é bastante fácil. Sempre que lemos o valor do sensor fotoelétrico, podemos ler o valor do
temporizador. Se subtrairmos a última vez da hora atual, obtemos o dT da última leitura .
Não seria melhor se não medíssemos esse dT e não fizéssemos cálculos de multiplicação?
E se esse dT tiver sempre o mesmo valor? Se toda vez que adicionarmos à parte
integrante, o valor dT for o mesmo, podemos apenas fazer a operação de soma ser
executada. integral = integral + erro. Na verdade, só precisamos multiplicar por dT quando
precisamos usar a integral para fazer outros cálculos. Portanto, desde que todos os valores
do intervalo de tempo - dT sejam configurados para o mesmo (ou aproximadamente o
mesmo) valor, podemos remover o fator de tempo da parte de controle integral. No
controlador PI o Ki é um fator que precisa ser ajustado (como fizemos com o Kp).

Se o robô estiver no lado esquerdo da linha e o erro acumulado da parte integral também
estiver no lado esquerdo da linha, a parte de controle integral deve não apenas enviar o
robô de volta para a linha, mas também fazer o robô cruzar a linha para o lado direito da
linha. Se houver um grande erro com duração de um período de tempo, a integral tenderá
ao infinito. Isso causará alguns problemas nos controladores que incluem controle integral.
Quando o erro que a parte integral tenta corrigir for grande integral teremos uma “super
reação” do robô, portanto precisamos fazer alguns ajustes no procedimento para evitar
problemas. Para resolver o problema que a integral tende ao infinito, existem duas soluções
comuns:
(1) defina a integral como zero - sempre que o erro for 0 ou sempre que o erro altere de
sinal.
(2) ao calcular um novo valor para a integral, o fator integral cumulativo é multiplicado por
uma constante supressora:

integral = erro + ((2/3)* integral)

Assim, cada ciclo Reduzirá o valor integral em 1/3. Se você acha que a parte de controle
integral é a "memória" (memória) de um controlador, essa supressão forçará o
esquecimento após um período "mais longo".

Para adicionar a parte de controle integral ao controlador, precisamos adicionar novas


variáveis Ki e integral.

● Kp = 10 #Inicializar variáveis
● Ki = 1
● deslocamento = 45
● Tp = 50
● integral = 0

15
● Loop infinito
● LightValue = ler o sensor de luz #O valor atual de leitura do sensor fotoelétrico
● error = LightValue-offset #Subtrai o offset (compensação) para calcular o erro
● integral = integral + error
● Turn = Kp * error + Ki * integral #Controle proporcional
● powerA = Tp + Turn #Valor de potência do motor A
● powerC = Tp -Turn #Valor de potência do motor C
● Finalize o Loop infinito

16
Adicione " D " ao controlador e temos o controlador PI

Nosso controlador possui um controle proporcional para corrigir o erro atual e um controle
integral para corrigir o erro passado, mas existe uma maneira de “prever o futuro” e corrigir
erros que ainda não ocorreram?

Isso requer o uso de outro conceito em matemática avançada - a derivada, que é o D no


PID. Como as integrais, derivadas também são métodos matemáticos muito importantes em
na matemática do ensino superior.

O valor esperado do próximo erro é: o erro atual - o erro anterior de amostragem do sensor.
A alteração no erro entre dois pontos consecutivos é chamada de derivada. A derivada é a
inclinação de uma linha reta. Usar dados como exemplo pode nos ajudar a ilustrar esse
problema. Vamos supor que o erro atual seja 2 e o erro anterior seja 5, então qual é o
próximo erro que prevemos? A mudança no erro, que é a derivada, é:

Derivada = (Erro atual) - (erro anterior)

De acordo com o nosso valor, 2-5 = -3. Portanto, a derivada atual é -3. Usamos a derivada
para prever o próximo erro.

(Próximo erro) = (Erro atual) + (Derivada atual)

De acordo com nossa suposição, o valor é 2 + (-3) = -1. Portanto, prevemos que o próximo
erro será -1. Na prática, na verdade, não queremos prever exatamente o próximo erro. Em
vez disso, apenas usamos a derivada diretamente na fórmula do controlador.

A parte de controle de derivativos, como a parte de controle integral, na verdade contém o


fator de tempo.A parte formal de controle de derivativos é:

Kd ( derivado ) / ( dT )

Assim como no controle proporcional e no controle diferencial, precisamos multiplicar a


derivada por uma constante. Como essa constante está relacionada à derivada, é chamada
Kd . Observe que na parte de controle derivativo, estamos dividindo por dT , enquanto na
parte de controle integral, estamos multiplicando por dT . Usaremos a mesma técnica para
remover esse dT da seção de controle derivativo como na seção de controle integral . Se o
valor de dT for o mesmo em cada ciclo , a pontuação Kd / dT é uma constante. Podemos
substituir Kd / dT por outro Kd . Como nos anteriores, esse valor de Kd é desconhecido e
precisa ser determinado por tentativa e erro, portanto, não importa se é Kd / dT ou um novo
Kd .

Agora podemos escrever a fórmula completa do controlador PID:

17
Turn = Kp * erro + Ki * Integral + Kd * Derivada
Se o erro atual for pior que o erro anterior, a seção de controle derivativo corrigirá esse erro.
Se o erro atual for melhor que o erro anterior, a parte de controle derivativo interromperá o
controlador para corrigir esse erro. O segundo efeito muito útil é que, quanto mais próximo o
erro é de 0, mais próximos estamos do ponto em que queremos parar corretamente. Mas o
sistema pode demorar um pouco para responder às mudanças na potência do motor,
portanto, devemos começar a reduzir a potência do motor antes que o erro se aproxime de
zero para evitar o excesso. Não se preocupe com as equações complicadas na parte de
controle derivativo, tudo o que você precisa fazer é fazer a subtração na ordem correta. A
chamada ordem correta é subtrair "anterior" de "atual". Portanto, ao calcular a derivada,
temos que subtrair "erro anterior" de "erro atual".Código virtual do controlador PID

● Kp = 10 #Inicializar variáveis
● Ki = 1
● Kd = 100
● deslocamento = 45
● Tp = 50
● integral = 0
● derivada = 0
● lastError = 0
● Loop infinito
● LightValue = ler o sensor de luz #O valor atual de leitura do sensor fotoelétrico
● error = LightValue-offset #Subtrai o offset (compensação) para calcular o erro
● integral = integral + error
● derivado = error-lastError
● Turn = Kp * erro + Ki * integral + Kd * derivado #Controle PID
● powerA = Tp + Turn #Valor de potência do motor A
● powerC = Tp -Turn #Valor de potência do motor C
● lastError = error
● Finalize o Loop infinito

O código acima é o código completo do controlador PID usado para o robô. A parte mais
difícil é como ajustar Kp, Ki e Kd.

18
Ajuste o controlador PID sem usar métodos matemáticos
complicados

A medição de alguns parâmetros do sistema permite calcular muito bem os valores de Kp ,


Ki e Kd . Existem várias técnicas para calcular Ks, uma das quais é chamada " método de
Ziegler - Nichols ". Você pode encontrar muitas páginas da web sobre essa tecnologia
através da pesquisa no Google. A versão que estou usando está quase diretamente usando
o conteúdo do controlador PID da página Wiki (o mesmo conteúdo também pode ser
encontrado em muitos outros lugares).

Siga estas etapas para ajustar o controlador PID:

● Defina os valores de Ki e Kd como 0


● Defina Tp (valor da potência alvo) pequeno.
● O Kp é definido como um valor "razoável", o que é razoável?

1) Divida o valor máximo (100) que queremos que a potência do motor atinja pelo
valor máximo de erro que pode ser usado. Para o nosso robô assumimos que o erro
máximo é 5, portanto especulamos que o valor de Kp seja 100/5 = 20. Quando o
erro é +5, a potência do motor alcançará 100. Quando o erro é 0, a potência do
motor estará em Tp (valor alvo da potência).

2) Ou defina o valor Kp como 1 (ou 100) e veja o que acontece.

3) Se você deseja multiplicar o valor de K por 100, aqui 1 deve ser registrado como
100, 20 como 2000 e 100 como 10000.

● Execute o robô e observe como ele se comporta. Se não conseguir seguir a linha e
acabar saindo, aumente o valor de Kp ; se balançar violentamente, diminua o valor
de Kp. Ajuste o valor de Kp até que o robô possa seguir a linha e não haja um
balanço tão grande de um lado para o outro. Nós chamamos o valor de Kp nesse
momento de "Kc" (na literatura PID, é chamado de valor crítico)
● Usando o valor Kc como Kp , execute o robô e tente descobrir qual é o "período de
oscilação" quando o robô está em execução. Este teste não precisa ser muito
preciso. O período de oscilação ( Pc ) refere-se ao período de tempo em que o robô
inicia de um lado da linha, oscila para o outro lado e depois retorna ao ponto de
partida. Para um robô Lego típico, o PC é de 0,5 a 1 ou 2 segundos.
● Você também precisa saber qual é o ciclo do sistema de controle do robô o dT.
● Use a tabela a seguir para calcular os valores de Kp , Ki e Kc . Se você deseja
apenas um controlador P, use a linha marcada P na tabela para calcular Kp ( Ki e Kd
são ambos 0). Se você deseja um controlador PI, use a segunda linha para calcular.
Se você deseja um controlador PID completo, use a última linha para calcular.

19
● Na operação real, esses valores K devem ser multiplicados por 100 aos valores
reais, mas você não precisa considerar esse problema no cálculo. Esse fator de 100
é levado em consideração ao determinar o valor crítico de Kp = Kc.
● Execute o robô e veja como ele funciona.
● Você pode ajustar os valores de Kp , Ki e Kd até obter o melhor desempenho. Você
pode começar com ajustes consideráveis, como 30%, e tentar ajustes menores para
obter os melhores (ou pelo menos aceitáveis) resultados.
● Depois de determinar um bom conjunto de valores K, aumente o valor TP e a
velocidade linear do robô.
● Para o novo valor TP , você precisa reajustar os valores dos K , talvez até voltar à
etapa 1 e repetir todo o processo.
● Repita várias vezes até que o desempenho do robô seja aceitável.

Valor de Ziegler - K ' dado pelo método de Nichols (o tempo do ciclo é constante e igual a
dT)

Tipo de controlo Kp Ki ' Kd '

P 0,50 Kc 00 00

PI 0,45 Kc 1,2 Kp dT / Pc 00

PID 0,60 Kc 2 Kp dT / Pc KpPc / (8dT)

Os símbolos em Ki ' e Kd ' são apenas para lembrá-lo - Ki ' e Kd' consideraram o fator de
tempo, a saber, ki '= ki * dt , kd' = kd / dt ( assumindo que dT é um valor constante).

20
Indicações de leitura
https://bbs.cmnxt.com/thread-5688-1-1.html
https://en.wikipedia.org/wiki/PID_controller
https://www.wescottdesign.com/articles/pid/pidWithoutAPhd.pdf
http://vivekbose.com/introduction-to-pid-controller-with-detailed-ppipd-pd-control
https://garberas.com/archives/2436
https://en.wikipedia.org/wiki/Integral_windup
https://www.scilab.org/pid-anti-windup-schemes
https://pdfs.semanticscholar.org/a049/c5c346a2210a3bdb781f5f24dd60329d9269.pdf
http://www.ece.ufrgs.br/~jmgomes/pid/Apostila/apostila/node31.html
https://www.mathworks.com/help/simulink/slref/anti-windup-control-using-a-pid-controller.ht
ml
https://pdfs.semanticscholar.org/a049/c5c346a2210a3bdb781f5f24dd60329d9269.pdf

21
COMUNICAÇÃO SERIAL
Inicialmente, porque usamos comunicações seriais? Basicamente devido a evolução dos
equipamentos eletrônicos e a utilização de vários circuitos integrados, devemos ter uma
forma de que cada circuito interaja com o outro. Então caso usássemos barramentos de
comunicação, acabaríamos tendo circuitos impressos caros e muito grandes, então para
que isso não ocorra fazemos uso da comunicação serial.
Ao longo do tempo várias formas de comunicação serial foram desenvolvidas, sendo
divididas em dois grupos, a comunicação síncrona e a assíncrona. Dentre os métodos de
comunicação vale ressaltar 3, o protocolo I2C, UART e SPI.

Figura 2.1: Esquema com os tipos de comunicação

Além disso, há grandes diferenças mesmo entre os protocolos síncronos. Apesar do padrão
de cada protocolo definir limites máximos de taxas, cada fabricante possui a liberdade de
desenvolver dispositivos com suas velocidades. Abaixo temos um comparativo entre
diversos padrões dispositivos seriais:

Tecnologia Barramento de Comunicação Taxa Fluxo de Dados


Máxima

UART 2 (sem controle de fluxo) 115.200 bps Half ou Full Duplex

SPI 3 + nº de Slaves 2 Mbps Full Duplex

I2C 2 (até 127 dispositivos) 400 Kbps Half Duplex

Tabela 2.1: Comparação entre os 3 tipos de comunicações seriais mais famosos

Vale ressaltar que os valores de taxa máxima podem variar, então é importante sempre
verificar a taxa máxima do micro que você vai usar, para isso olhe no datasheet do seu
micro.

22
COMUNICAÇÃO SERIAL SÍNCRONA
Para a comunicação síncrona definimos o modelo de Master-Slave ou Mestre-Escravo.
Normalmente o gerador do sinal de sincronismo é chamado de Mestre (Master) de
comunicação, e os que recebem esse sinal são os Escravos (Slaves). A ligação mais
comum é termos um Mestre conectado a vários Escravos. Como pode-se ver na imagem
abaixo.

Imagem 2.1.1: Diagrama representando uma comunicação Mestre-Escravo

Em algumas aplicações o protocolo Mestre-Escravo pode ter o nome de protocolo


Cliente-Servidor.

Neste caso temos um sinal de sincronismo, normalmente um sinal de clock, para manter
nossa comunicação. Isso será mostrado mais para a frente desta pesquisa. Neste momento
o importante é saber que a comunicação serial síncrona, utiliza o protocolo Master Slave, e
o envio dos dados é regido por um sinal de sincronismo, para efeitos práticos chamado de
sinal de clock.

SPI (SERIAL PERIPHERAL INTERFACE)


A comunicação SPI, tem algumas características básicas. Primeiramente os sinais de
comunicação tem uma direção fixa e definida, isso significa que sempre existem dois
transistores definindo o estado de um pino (Push-Pull).

Essa simples característica já diferencia a comunicação SPI de outros protocolos síncronos,


como o I2C e o One Wire, que possuem o mesmo barramento de dados para sinais de
entrada e saída através do esquema de dreno aberto (Pull-Up).

No protocolo SPI, temos que usar dois sinais para a comunicação, mas isso permite que
tenhamos velocidades maiores, devido à pouca deformação do sinal, como pode ser visto
na imagem abaixo:

23
Imagem 2.2.1: Comparação entre a forma de onda na saída do sistema Push-Pull e Pull-Up

Outra característica básica do protocolo SPI é que toda a troca de dados sempre acontece
em ambas direções. Ou seja, cada bit trocado entre o Master e um Slave trás um bit do
Slave para o Master. Dessa forma definimos este protocolo como sendo Full-Duplex.

Segue a lista dos pinos básicos da comunicação SPI e seu esquema padrão de ligação.

Pino Nome Padrão Significado Nomes Alternativos

Do Master para o Slave MOSI Master Output Slave Input SDO, DO, SO

Do Slave para o Master MISO Master Input Slave Output SDI, DI, SI

Clock SCLK Serial Clock SCK, CLK

Seleção de Slaves SS Slave Selector CS, nSS, nCS

Tabela 2.2.1: Lista de pinos básicos da SPI

O sinal de SS da SPI funciona como Seleção de Escravo (Slave Select). É um sinal ativo
em nível baixo, o que significa que o dispositivo é selecionado quando este pino se encontra
em nível baixo. No entanto, muitos dispositivos utilizam este sinal como sincronismo de
frame. Dessa forma, é um sinal importante que deve ser respeitado.

24
Imagem 2.2.2: Esquema de conexão da SPI

DISPOSITIVO
O princípio básico do protocolo SPI é o Shift-Register, este dispositivo é responsável de
fazer a conversão de um registrador paralelo para um sinal serial de acordo com o clock. Ou
seja, cada borda recebida no terminal de clock do Shift-Register significa que um bit foi
transferido. Da mesma forma esse tipo de dispositivo é capaz de receber dados vindos de
maneira serial e convertê-los para um valor em um registrador paralelo.
A grosso modo o shift-register basicamente converte um sinal paralelo em um sinal serial,
sendo assim o dispositivo essencial na maioria dos protocolos seriais.

Imagem 2.3.1: Representação de um Shift-Register

FORMA DE ONDA
O protocolo SPI, permite a configuração das bordas de ativação do sinal de clock, isso é
feito pela polaridade e fase do sinal. Para configurarmos a polaridade temos que alterar o
CPOL (Clock Polarity), e para configurarmos a fase devemos acessar o CPHA (Clock
Phase). Como esses canais respondem de forma digital, ao termos 2 entradas, isso nos
permite 4 estados, que estão listados abaixo.

25
Modo CPOL CPHA Borda de Troca Transição Nível em IDLE

0 0 0 Subida Meio bit 1

1 0 1 Descida Começo do bit 0

2 1 0 Descida Meio bit 0

3 1 1 Subida Começo do bit 1

Tabela 2.3.1: Tabela com os diferentes estados da comunicação SPI

Além de termos que considerar os valores lógicos do CPHA e do CPOL, outra entrada que
devemos analisar é o DORD. Isso se deve à que o protocolo SPI, pode alterar a posição do
bit mais significativo, MSB (More Significant Bit). Logo se PORTD assumir valor lógico baixo
(PORTD=0) o MSB é o primeiro bit, agora caso o PORTD assumir valor lógico alto
(PORTD=1) o MSB será o último bit. Vale ressaltar a importância do PORTD, pois se for
feita a má configuração deste valor, a sequência de bits que será enviada do Master para o
Slave, será errada e teremos falhas na comunicação.

Imagem 2.3.2: Forma de onda da comunicação SPI, com CPHA em nível lógico baixo

26
Imagem 2.3.3: Forma de onda da comunicação SPI, com CPHA em nível lógico alto

COMUNICAÇÃO HALF-DUPLEX E FULL-DUPLEX


Quando falamos sobre comunicação, intuitivamente pensamos em dados sendo enviados e
recebidos. Neste caso, isto pode ser feito de duas maneiras, uma onde enviamos e
recebemos dados o tempo todo (Full-Duplex), e a segunda forma onde temos que finalizar o
envio para iniciar a recepção de dados ou vice-versa (Half-Duplex).

Imagem 2.4.1: Representação da comunicação Half-Duplex e Full-Duplex

Comunicações com RS-485 utilizam o mesmo canal para fazer a transmissão e a recepção
de dados. Dessa forma, ou se transmite, ou se recebe, mas nunca simultaneamente. Essa
comunicação é half-duplex. A mesma coisa acontece nos protocolos de comunicação I2C e
OneWire.

Em uma comunicação serial RS-232 padrão, há um canal para transmitir (TXD) e um para
receber (RXD). Dessa forma, o envio e o recebimento de informação podem ser feitas

27
simultaneamente, e não há a necessidade de esperar o fim de um para o início do outro.
Essa comunicação é full-duplex.

O SPI não apenas permite que os dados sejam transmitidos e recebidos simultaneamente,
como é uma exigência de hardware. Cada bit de dado enviado do Master para o Slave,
transfere também um bit de dado do Slave para o Master. Isso porque o fundamento do SPI
é um circuito shift-register, como visto no artigo passado.

Imagem 2.4.2: Representação da comunicação entre o Master e o Slave no protocolo SPI

Dessa forma sempre que o Master transmitir um bit para um Slave, o Master irá receber um
bit desse Slave como resposta. Logo, não há como transmitir um dado sem receber algo em
troca, configurando o protocolo SPI como um Full-Duplex.
Sei que essa parte de transferência de dados pode ser um pouco confusa então iremos
fazer uma simulação para mostrar como esse processo é realizado pela SPI.

Imagem 2.4.3: Estado inicial da simulação

Neste caso queremos enviar o dado contido no buffer do Master para o buffer do Slave e
nesse processo o Master irá receber a informação contida no buffer do Slave. No nosso
caso a SPI envia os dados na borda de descida do sinal de clock.

28
Imagem 2.4.4: Estado da SPI após o primeiro pulso de Clock

Imagem 2.4.5: Estado da SPI após o segundo pulso de Clock

Imagem: 2.4.6: Estado da SPI após o terceiro pulso de Clock

29
Imagem 2.4.7: Estado da SPI após o quarto pulso de Clock

Imagem 2.4.8: Estado da SPI após o quinto pulso de Clock

Imagem 2.4.9: Estado da SPI após o sexto pulso de Clock

30
Imagem 2.4.10: Estado da SPI após o sétimo pulso de Clock

Imagem 2.4.11: Estado da SPI após o oitavo pulso de Clock

Assim temos a representação da comunicação da SPI, transferindo 8 bits. Ou seja após 8


pulsos de clock a informação contida no buffer do Master agora está no buffer do Slave, e
por sua vez a informação contida no buffer do Slave agora está no buffer do Master.

Como visto na simulação não é possível enviar dados sem receber algo, logo quando
pensamos no software de uma SPI, não faz sentido termos uma função para enviar e outra
para receber. Logo, faz mais sentido termos uma única função para transferir, ou seja nessa
função iremos tanto receber dados quanto enviar dados.

uint8_t​ SPI_transfere(​uint8_t​ data);

A chamada de função apresentada, tem como entrada a informação no buffer do Master


que será enviada, uma vez que nós sejamos o Master da comunicação. E tem como retorno
a informação contida no buffer do Slave.

31
Porém caso seja desejado apenas enviar dados, recebemos uma informação inútil que
normalmente não será analisada. E caso desejemos receber dados colocamos no buffer do
Master um valor nulo, normalmente 00h. Mas deve-se ressaltar que a comunicação sempre
será do tipo Full-Duplex, mesmo que a informação enviada ou recebida seja desnecessária.

SELETOR DO ESCRAVO (SLAVE SELECT)


Outro fator importante do protocolo SPI, é o seletor do Escravo, ou SS. Este fator é
responsável por selecionar qual escravo estaremos realizando a comunicação. O SS, é
muito similar a um Chip Select, muito comum em memórias e outros semicondutores. Visto
que o SS é sempre barrado, quando estiver em nível lógico baixo, temos a habilitação do
Slave que vamos nos comunicar. Neste caso podemos ter a ativação de múltiplos Slaves.
Agora caso o SS estiver em nível lógico alto, teremos no pino MISO (Master Input Slave
Output), uma alta impedância e não irá interferir na comunicação.

Logo este pino só faz sentido quando temos um Slave, já que o Master não será afetado por
ele. Por isso quando trabalhamos com um dispositivo na configuração Master, podemos
usar o pino SS, com um GPIO normal, ou seja uma entrada digital.

Vale ressaltar que ligar o SS do Slave diretamente ao GND, pode atrapalhar e muito o
sincronismo da comunicação. ​Pois, muitos Slaves utilizam o sinal de SS como "sincronismo
de frame". Um início de um novo frame será sempre marcado quando o sinal passa de 1
para 0, e ligá-lo diretamente ao GND pode fazer com o Slave perca o sincronismo do frame
após algumas trocas de bytes.

SPI BAUD RATE REGISTER


É uma configuração relativamente importante pois configura a taxa de bits por clock que irá
do Master para os Slaves. Por esse motivo essa configuração é única do Master, mudar o
Baud Rate de um Slave é desnecessária.

CRC (CYCLIC REDUNDANCY CHECK)


O CRC, é um método para verificar se tivemos um erro durante a comunicação. Esse
método é muito usado em comunicações dados digitais, tal como Wi-Fi e Ethernet.

Basicamente o CRC faz algumas operações matemáticas com os dados que vamos
transferir e gera uma saída única para o nosso dado e esse valor do CRC, que para efeitos
práticos vamos chamar de CRC_check, será enviado junto com os nossos dados, então
quando o nosso Slave receber os dados e verificá-los por meio da lógica do CRC, vamos
obter um novo CRC_check, que se for diferente ao que enviamos, significa que por algum
motivo os nossos dados foram corrompidos. Em outras palavras é um teste de mão dupla,
que deve ser feito antes de enviar os dados e assim que recebermos os dados.

Esse processo pode ser implementado por software, porém os microcontroladores da STM,
da família STM32, já tem esse processo feito por hardware. E a principal vantagem está na
velocidade, caso o método CRC seja implementado em software teremos um tempo de

32
execução superior a 78.000 ciclos de clock, por sua vez como a STM32, tem a lógica
implementada por hardware, teremos um tempo de execução inferior a 1.300 ciclos de
clock. Por esse motivo caso formos usar um sistema CRC, altamente recomendo fazer uso
dos microcontroladores da família STM32.

COMUNICAÇÃO SPI NO ARDUINO UNO


Agora irei explicar como implementar uma comunicação SPI usando o Arduino Uno.

Imagem 2.5.1: Pinagem do Arduino Uno, para usarmos o protocolo SPI

Nosso primeiro exemplo de configuração será com o Arduino UNO. Seu microcontrolador, o
ATmega328P possui um periférico de SPI integrado. Também já existe uma biblioteca
pronta para trabalhar com ele, o ​SPI.H.​ Com ela, uma série de métodos ficam disponíveis
para o uso.

- Begin()

Este método inicializa o objeto SPI com a configuração padrão da biblioteca do Arduino.
Após essa instrução, o periférico já está ativo e pronto para ser utilizada.

- setBitOrder()

33
Define qual será o primeiro bit da comunicação, se será o mais significativo (MSB) ou o
menos significativo (LSB). Seus parâmetros são, LSBFIRST ou MSBFIRST.

- setClockDivide()

Configura o divisor do clock principal. O Arduino trabalha com 16MHz e o valor padrão para
a configuração é SPI_CLOCK_DIV4, o que significa que, por padrão, o SCK irá comunicar a
4MHz. No entanto, os valores possíveis vão de 2 a 128, permitindo clocks respectivos de
8MHz a 125KHz. Os parâmetros que podem ser aplicados são; SPI_CLOCK_DIV2
(comunicação à 8MHZ), SPI_CLOCK_DIV4 (comunicação à 4MHZ), SPI_CLOCK_DIV8
(comunicação à 2MHZ), SPI_CLOCK_DIV16 (comunicação à 1MHZ), SPI_CLOCK_DIV32
(comunicação à 500KHZ), SPI_CLOCK_DIV64 (comunicação à 250KHZ) ou
SPI_CLOCK_DIV128 (comunicação à 125KHZ).

- setDataMode()

Este método define o modo de comunicação. Cada modo possui uma borda diferente e uma
fase e uma polaridade diferente. Pode-se trabalhar com os seguintes parâmetros;
SPI_MODE0, SPI_MODE1, SPI_MODE2 ou SPI_MODE3. Os modos estão listados na
Tabela 2.3.1.

- transfer()

Finalmente, este é o método responsável pela transferência. Irá transmitir qualquer valor
passado como parâmetro e, conforme o padrão da SPI, retornará o valor recebido de volta.

EXEMPLO EM CÓDIGO

Deve-se ressaltar que para adicionar a biblioteca


SPI.h,​ devemos ir em ​Sketch,​ na aba de ferramentas,
selecionar a opção ​Adicionar Biblioteca,​ e então
escolher a opção ​SPI.​ Como mostrado na imagem ao
lado. Normalmente a biblioteca ​SPI​, já vem
adicionada na IDE do Arduino.

Ao fazer isso você tem acesso as funções descritas


anteriormente, e pode fazer uso do protocolo SPI de
forma simples e rápida. Uma ótima opção para testar
códigos que envolvem o recebimento e envio de
dados pelo protocolo SPI, é justamente usar um
arduino Uno para testes.

Imagem 2.6.1: Configuração da SPI.h

34
Segue um exemplo de código, que pode ser usado para o Arduino Uno. Para a nossa
exemplificação iremos enviar os seguintes dados para nosso Slave; 00h (0000 0000), 3Ch
(0011 1011) e 5Ah (0101 1010).

Imagem 2.6.2: Código de exemplo para o protocolo SPI usando o Arduino Uno

Neste exemplo de comunicação, o sistema baixou o SS controlando o IO 3 e transferiu os


três bytes esperados. Para cada byte houve um retorno, que foi carregado em uma
respectiva posição da matriz retorno[ ].

COMUNICAÇÃO SPI NO STM


Como dentro da equipe iniciamos uma transição para os microcontroladores da STM, vou
dar bastante atenção para este fabricante.

CONFIGURANDO O STM CUBE MX


A principal vantagem dos microcontroladores da STM, é a facilidade de
programar e configurar estes componentes. Então a primeira parte
desta pesquisa é justamente em como configurar o método SPI no STM
Cube.

Após você criar um novo projeto no Cube, e selecionar o


microcontrolador que você está usando. Vamos configurar a
comunicação SPI. Inicialmente vamos na aba Connectivity, e
procuramos pela opção SPI.

35
Nessa mesma aba podemos encontrar opções que serão abordadas futuramente, tal como
a comunicação I2C e USART.

Continuando a comunicação SPI, assim que abrirmos a aba connectivity e escolhermos a


opção SPI, termos uma nova aba, onde iremos configurar todas as características do
protocolo SPI.

Imagem 2.7.1: Aba para liberar o uso da SPI

Agora vamos setar a nossa SPI, para isso clicamos na opção Mode, presente na imagem
anterior, e selecionamos qual modo vamos usar. Para os micros da STM, temos 9 opções,
que são: Disable, Full-Duplex Master, Full-Duplex Slave, Half-Duplex Master, Half-Duplex
Slave, Receive Only Master, Receive Only Slave, Transmit Only Master, Transmit Only
Slave. Então você escolha o modo que melhor se encaixe na necessidade do seu projeto,
para recursos práticos vamos mostrar a configuração de um SPI Master Full-Duplex.

Imagem 2.7.2: Selecionando o modo da SPI

Agora devemos configurar o SS, como eu disse vou mostrar a configuração para uma SPI
Master Full-Duplex. Então teremos duas opções: Hardware NSS Input Signal, esta opção é
usada para uma configuração multi-master; e Hardware NSS Output Signal, que é a opção
quando temos apenas um Master. Caso você esteja configurando um Slave, você terá
acesso a apenas uma única opção que é a Hardware NSS Input Signal.

Imagem 2.7.3: Configurando a forma de interpretar o Slave Select

A configuração básica do protocolo SPI, já está feito. Podemos avançar na configuração


para ter um protocolo mais adaptado, e preciso para a sua aplicação. Então para configurar
mais a fundo a SPI, temos vários parâmetros, então vamos começar pelos parâmetros
básicos. Nesta opção temos 3 parâmetros: Frame Format, que usamos para configurar a
forma que a SPI interpreta o CPOL e CPHA, o mais usual é usar o padrão Motorola, mas

36
podemos usar a opção TI; Data Size, que define o tamanho de bits que serão transmitidos;
e First Bit, que define se o primeiro bit é o mais significativo (MSB) ou o menos significativo
(LSB).

​Imagem 2.7.4: Configuração dos parâmetros básicos

Outra opção de configuração avançada são os parâmetros do clock. Neste caso temos 4
parâmetros que podem ser configurados: Prescale (for Baud Rate), quando alterarmos este
parâmetro, temos uma alteração automática do Baud Rate, que já mostra quantos bits
serão transmitidos por segundo; Clock Polarity, neste caso vamos configurar qual será a
polaridade de ativação do Clock ou como foi apresentado, configuramos o CPOL (podendo
assumir dois valores High e Low); Clock Phase, neste caso configuramos a fase do Clock
ou seja, o CPHA (podendo assumir dois valores 1 edge e 2 edges). Os dois últimos
parâmetros foram explicados anteriormente, então recomendo consultar a ​Tabela 2.3.1​.

Imagem 2.7.5: Configuração dos parâmetros de clock

Por fim, podemos configurar parâmetros mais avançados. Entre eles podemos configurar os
seguintes parâmetros: CRC Calculation, que irá habilitar o modo CRC, sendo que nos STM
da família STM32, este processo já está embutido no hardware e NSSP Mode, isso força os
pinos SS para nível alto entre as transmissões desabilitando todos os Slaves enquanto não
estamos transmitindo. Existe um terceiro parâmetro que é o NSS Signal Type, que é setado
automaticamente, dependendo da seleção do parâmetro Hardware NSS Signal.

37
Imagem 2.7.6: Configuração dos parâmetros avançados

Antes mesmo de realizar todas essas configurações, o


Cube mostra quais pinos do nosso micro serão usados
para que possamos utilizar a comunicação SPI. Então
recomendo que na etapa de projeto, caso você esteja
usando um micro da STM, utilizar o Cube para planejar
os pinos que serão usados e qual função cada um vai
ter. No nosso exemplo esse foi o Pin Planner que o
Cube gerou.

Imagem 2.7.7: Pin Planner para


o protocolo SPI

USANDO A SPI NO SYSTEM WORKBENCH


Antes de gerarmos o nosso código devemos fazer uma última configuração, que é para ao
gerar o código, gerar também os “.c” e os “.h” dos periféricos, nesse nosso caso, os códigos
do protocolo SPI. Para isso vamos na aba “Project Mannager”, e selecionamos a opção
“Generate Code”, e habilitamos a opção “Generate peripheral initialization pair of ‘.c \ .h’ files
per peripheral”. Como está mostrado na imagem abaixo.

38
Imagem 2.8.1: Configuração para gerarmos os códigos dos nossos periféricos

Após todas as configurações feitas, é só clicar em “GENERATE CODE”, que fica no canto
superior direito. Assim que isso for feito, o Cube automaticamente vai gerar os códigos para
que você possa começar a programar o seu micro.

Assim que o Cube gerar o código, teremos a seguinte estrutura.

Explicando as pastas geradas, temos que a


pasta Src, é onde teremos os códigos ‘.c’; a
pasta Inc, é onde teremos os códigos ‘.h’.

Para os fins desta pesquisa, vamos analisar


apenas os códigos ‘spi.h’ e ‘spi.c’.

Imagem 2.8.2: Estrutura de pastas do Eclipse

Quando vamos trabalhar com o protocolo SPI, temos que inicializar algumas variáveis para
conseguirmos trabalhar de forma eficiente. Então antes de qualquer coisa recomendo que
seja criada na função main duas variáveis, uma que será um buffer de dados para serem
enviados e outro buffer para dados que serão recebidos. Segue o código para inicializar tais
variáveis.

39
Imagem 2.8.3: Inicializando os buffers de dados

Após inicializamos os buffers, podemos usar os métodos da biblioteca spi. Existem muitos
métodos para o uso da SPI, a maioria sendo bem específicos e simples de entender. Para
encontrar todos os métodos da SPI, basta digitar “HAL_SPI” e então usar o atalho (ctr
space), para conseguir ver todos os métodos da SPI. Quais métodos serão usados depende
muito de qual aplicação você fará, porém o método mais geral da SPI é o
HAL_SPI_TransmitRecieve(). Neste caso iremos passar para a SPI os seguintes
parâmetros, qual spi estamos usando, os dados a serem enviados, onde iremos armazenar
os dados que serão recebidos, quantos dados serão enviados, e quanto o tempo máxima
para a comunicação.

Imagem 2.8.4: Métodos importantes da HAL_SPI

Caso você queira usar o protocolo CRC, deve-se criar uma rotina para enviar o valor do
CRC, e calcular o valor do CRC, e comparar com o valor recebido. Para isso devemos
deixar um espaço no nosso buffer para armazenar o valor do CRC, e criar uma variável que
salve esse valor. O uso de um CRC, pode ser feito de diversas formas, a que segue como
exemplo é a mais simples.

Imagem 2.8.5: SPI com método CRC

40
Como dito anteriormente existem várias funções que nossa SPI pode usar. Então
recomendo que quando você for usar esse método de forma mais elaborada dê uma olhada
nas funções da HAL_SPI, para encontrar a função que melhor se encaixe na sua
necessidade. Para dúvidas mais específicas recomendo o canal da própria STM, além dos
fóruns da STM.

Além disso existe uma facilidade imensa ao programar os micros da STM, então se você
tiver a possibilidade de fazer uso desse componente, eu altamente recomendo.

I2C (INTER INTEGRATED CIRCUIT)


Como foi explicado no capítulo sobre SPI, o protocolo I2C, não consegue enviar e receber
dados ao mesmo tempo, caracterizando assim uma comunicação do tipo Half-Duplex, por
isso temos apenas dois barramentos o SCL, responsável pelo clock e o SDA, responsável
pelos dados.

De forma similar ao protocolo SPI, o I2C, também funciona com a lógica Master e Slave,
onde o Master envia o clock e os dados e o Slave recebe essas informações. Porém o
protocolo I2C, é utilizado para comunicar dispositivos lentos, como as EEPROMs, os
conversores analógicos digitais, e as I/O’s).

Uma característica importante deste protocolo é a possibilidade de utilizar componentes de


tecnologia construtiva diferentes, sem termos falhas na comunicação, visto que este
protocolo foi criado para comunicar de forma eficiente entre circuitos, facilitando assim sua
integração.

Os dispositivos conectados pelo protocolo I2C, recebem um endereço fixo, visto que não
temos um barramento para selecionar com qual Slave queremos nos comunicar. Vale
ressaltar que o barramento I2C é do tipo multi-master, ou seja podemos ter mais de um
componente de controle conectado ao barramento, mas durante a comunicação apenas um
dos masters podem estar transmitindo.

Os barramentos SDA e SCL são


bidirecionais e devem ser ligadas
ao pólo positivo da alimentação,
por meio de um resistor pull-up,
assim garantindo que ambos
barramentos estejam em nível
lógico alto quando não tivermos
dados fluindo por eles.

Imagem 3.1.1: Interfaceamento I2C

41
Uma vantagem quando usamos o protocolo I2C, é que
não fixamos a frequência da comunicação, visto que
este fator será determinada unicamente pelo master
que está transmitindo. A comunicação do Master com
os Slaves, no protocolo I2C, é iniciado por uma
condição inicial, e encerrado por uma condição final,
assim conseguimos controlar o fluxo nos barramentos.

Imagem 3.1.2: Condições de início


e encerramento do protocolo I2C

CONEXÃO UTILIZANDO I2C


O I2C é um protocolo que usa um bus, ou seja com o mesmo cabo ou a mesma trilha
conectamos todos os dispositivos, por isso usamos apenas dois pinos independentemente
de quantos dispositivos estiverem conectados ao bus I2C. Isso não só facilita o
planejamento do nosso projeto como também auxilia a reduzir a quantidade de pinos que
usamos para efetuar nossa comunicação, por exemplo que usarmos um protocolo como o
SPI para comunicar nosso micro com 10 dispositivos, iremos precisar de 13 pinos (MISO,
MOSI, CLK, e 10 SS), já o protocolo I2C usaremos apenas dois pinos (SDA e SCL), assim
para projetos mais simples e de baixo custo o protocolo I2C pode ser uma ótima escolha.
Como o protocolo I2C, foi concebido para ter a capacidade de conectar dispositivos, temos
que esse barramento pode ter um tamanho físico de 1 metro, normalmente. Caso esse
barramento seja maior, teremos problemas de impedância dos cabos ou das trilhas, criando
assim ruídos na comunicação.

ENDEREÇAMENTO DOS DISPOSITIVOS


Dispositivos I2C, pela falta de um pino para selecionar os Slaves, faz tal seleção por meio
de um endereçamento dos dispositivos. Normalmente temos um endereço de 8 bits, onde 7
são reservados para localizar os dispositivos, logo podemos ter 127 dispositivos conectados
a um barramento I2C, isso na teoria. O oitavo bit é um marcador para mostrar se estamos
realizando uma operação de leitura ou escrita, o bit responsável por essa seleção é o
menos significativo (LSB).

Imagem 3.3.1: Estrutura do endereçamento dos dispositivos

42
No protocolo I2C, temos um domínio de endereços válidos que é: 0x08 até 0x77, sabendo
que esta faixa está em hexadecimal, ao convertermos para decimal, temos uma faixa com
112 valores, logo a quantidade real de dispositivos que podemos conectar ao barramento
I2C é na realidade 112 e não 127, como a teoria apontava.
Logo para realizarmos nossa comunicação usamos 16 bits, onde 8 bits serão nossos dados,
7 bits serão nossos endereços de escravos e 1 bit será nossa operação read ou write.

FORMA DE ONDA
Quando temos o protocolo I2C a comunicação é iniciada quando o master envia o sinal de
Start​, tal sinal já foi mostrado, então o endereço dos slaves é enviado, logo em seguida o bit
que define se estamos enviando dados (write) ou recebendo dados (read), e o escravo
envia um sinal de feedback, confirmando que a comunicação foi estabelecida, então
enviamos nossos dados, e por último o master envia o sinal ​Stop​, também já apresentado.

Imagem 3.4.1: Funcionamento do protocolo I2C

Logo, usamos menos pinos que o protocolo SPI, porém temos que enviar um número maior
de bits, para conseguirmos estabelecer nossa comunicação.

FUNCIONAMENTO DO I2C
O dado pode ser alterado apenas quando o sinal SCL estiver em nível lógico baixo, visto
que quando este estiver em nível alto, o dado será lido pelo Slave, logo não podemos
alterá-lo.

Caso o Master for enviar uma sequência de bytes para o mesmo Slave, não é necessário
enviar o endereço novamente, mas a cada byte enviado o Slave deve enviar o bit
assegurando a comunicação, tal bit iremos chamar de ACK; a mesma coisa ocorre se o
Master for receber vários bytes do mesmo Slave.

43
COMUNICAÇÃO I2C NO ARDUINO UNO

Imagem 3.6.1: Pinagem do Arduino Uno para o protocolo I2C

Diferentemente do protocolo SPI, onde tínhamos uma biblioteca do próprio arduino, para
implementar tal protocolo, no caso da comunicação I2C não temos uma biblioteca do
arduino para implementação. Porém existe uma biblioteca chamada “​wire.h​” que foi criada
por usuários e que facilita a implementação do protocolo I2C.

Na biblioteca ​wire​ temos alguns métodos que vamos dar atenção:

- Begin():​ Este método inicia a nossa biblioteca, e deve ser usada na setup()

- BeginTransmittion(slaveAdress): ​Este método seleciona com qual escravo vamos


nos comunicar, tipicamente quando criamos esse link entre o mestre e o escravo
dizemos que iniciamos nossa comunicação, porém nenhum dado está sendo
enviado.

- Write(data): ​Este método envia os dados para o dispositivo linkado.

44
- Read(): ​Este método retorna os dados que estamos recebendo do dispositivo
linkado.

- OnRecieve(function): ​Este método detecta quando recebemos algum dado, e


tipicamente isso aciona uma função que irá analisar qual dado foi recebido e como
prosseguir.

- EndTransmittion(): ​Este método encerra a comunicação.

EXEMPLO EM CÓDIGO
Inicialmente devemos adicionar a biblioteca “​wire.h​”, neste caso devemos, escrever
#include <Wire.h>​, no inicio do nosso código.

Neste caso devemos ter dois códigos um para o mestre e outro para o escravo, neste caso
iremos implementar uma comunicação entre dois Arduinos Uno, um sendo nosso mestre e
outro sendo nosso escravo.

Imagem 3.7.1: Implementação do Master

Como foi explicado anteriormente temos que implementar agora o nosso Slave.

45
Imagem 3.7.2: Implementação do Slave

Neste nosso exemplos estávamos controlando um LED, do Arduino Slave, usando nosso
Master. Este código pode ser mudado para conseguir controlar motores, por meio de
sensores, ou qualquer outra aplicação.

Existem diversos módulos específicos para o protocolo I2C, então caso você queira
implementar esse protocolo, recomendo ver se existe algum módulo que facilite seu projeto.

COMUNICAÇÃO I2C NO STM


Como dentro da equipe iniciamos uma transição para os microcontroladores da STM, vou
dar bastante atenção para este fabricante.

CONFIGURANDO O STM CUBE MX


Como no capítulo de SPI, explicamos como acessar a aba “​Conectivity”​ não irei mostrar
novamente, então caso esteja com dúvida, vá para o capítulo de SPI. Agora iremos
unicamente configurar o protocolo I2C.

Inicialmente temos que habilitar o protocolo, assim que clicarmos em I2C, uma aba irá abrir
então você deve clicar na opção I2C, e mudar de “​Disable​” para “​I2C”​ .

Imagem 3.8.1: Habilitando o protocolo I2C

46
Assim que habilitarmos essa opção, uma nova aba será aberta, onde iremos configurar o
tempo do nosso protocolo. Temos sete parâmetros, nomeando e explicando cada uma: I2C
Speed Mode, temos 4 opções nesse parâmetro (Standart Mode, que tem um Data Rate de
até 100KBps; Fast Mode, que tem um Data Rate de até 400KBps; High Speed Mode, que
tem um Data Rate de até 3.4MBps; essas 3 opções são suportadas por todos os micros da
família STM32, porém temos que para alguns micros da família STM32, podemos usar o
Fast Mode+, que tem um Data Rate de até 1Mbps); I2C Speed Frequency, que é auto
populado dependendo da opção que colocamos no Speed Mode, esse valor é o limite
superior, então podemos mudar manualmente, mas o STM Cube nunca deixa que a gente
coloque um valor maior que o limite; Rise Time, que determina o tempo de subida do sinal;
Fall Time, que determina o tempo que o sinal vai de 1 para 0; Coefficient of Digital Filter;
Analog Filter.

Imagem: 3.8.2: Configurando o tempo do nosso I2C

As outras configurações são relacionadas ao escravo. Neste caso temos 4 parâmetros, que
são: Clock No Stretch Mode, este parâmetro é um facilitador, pois caso tenha algum erro de
temporização, este parâmetro irá flexibilizar nosso processo, por isso recomendo em deixar
este parâmetro em disable, visto que este parâmetro quando ativo ignora a flexibilização
explicada; General Call Adress Detection, este parâmetro faz com que todos os nosso
slaves sejam acionados ao mesmo tempo, por isso dependendo da aplicação, eu
recomendo que deixe essa opção desativada; Primary Adress Length Location, define a
quantidade de bits no nosso seletor de escravos, este valor pode ser 7 bits ou 10 bits; Dual
Adress Aknowledged, esta é um parâmetro único para os dispositivos slaves, pois isso
permite que nossos escravos tenham 2 endereços, novamente dependendo da aplicação
pode ser algo útil; e o Primary Slave Adress.

Imagem 3.8.3: Configurando nossos Slaves

47
Por fim, temos a imagem da pinagem do nosso micro.

Antes mesmo de realizar todas essas configurações, o Cube


mostra quais pinos do nosso micro serão usados para que
possamos utilizar a comunicação SPI. Então recomendo que na
etapa de projeto, caso você esteja usando um micro da STM,
utilizar o Cube para planejar os pinos que serão usados e qual
função cada um vai ter. No nosso exemplo esse foi o Pin
Planner que o Cube gerou.

Imagem 3.8.4: Pin Planner

USANDO I2C NO SYSTEM WORKBENCH

COMUNICAÇÃO SERIAL ASSÍNCRONA


Para a comunicação assíncrona definimos o modelo de Master-Slave ou Mestre-Escravo.
Normalmente o gerador da flag de início e finalização é chamado de Mestre (Master) de
comunicação, e os que recebem essa flag são os Escravos (Slaves). A ligação mais comum
é termos um Mestre conectado a vários Escravos. Como pode-se ver na imagem abaixo.

Imagem 4.1.1: Diagrama representando uma comunicação Mestre-Escravo

Em algumas aplicações o protocolo Mestre-Escravo pode ter o nome de protocolo


Cliente-Servidor.

Neste caso temos uma flag, que demarca o início do nosso bloco de dados, para iniciar a
nossa comunicação. Isso será mostrado mais para a frente desta pesquisa. Neste caso
essa flag pode parecer muito com a comunicação I2C, que também envia um sinal de início
e finalização, porém o protocolo I2C temos além dessas flags devemos usar o sinal de
sincronismo para manter nossa comunicação. Logo para a comunicação assíncrona não
vamos precisar do sinal de clock para manter a comunicação.

48
UART (UNIVERSAL ASYNCHRONOUS RECEIVER TRANSMITTER)
Inicialmente o protocolo Uart tem algumas características importantes, tais como a
existência de dois canais independentes, um para envio de dados (TX), e outro para
receber dados (RX). Esta característica diferencia o protocolo Uart dos protocolos I2C e
SPI, pois no I2C, temos um único canal tanto para enviar quanto receber dados, por esse
motivo não podemos enviar e receber dados ao mesmo tempo. Já no caso do protocolo
SPI, temos dois canais para estabelecer a comunicação, um por onde enviamos dados e
outro por onde recebemos dados, esta característica se assemelha muito aos pinos TX e
RX do protocolo Uart, porém a diferença está que no caso da SPI os canais não são
independentes, logo toda vez que enviamos um dado, acabamos recebendo algo do nosso
Slave.
Vale ressaltar que para o protocolo UART, podemos ter apenas um dispositivo receptor e
um emissor.

Imagem 4.2.1: Conexão entre dispositivos UART

No caso da comunicação UART, esse simples fato de ter dois canais independentes,
permite que este protocolo tenha uma versatilidade enorme, pois permite que tenhamos um
protocolo Full-Duplex (envia e recebe dados ao mesmo tempo), Half-Duplex (envia e recebe
dados, porém não pode fazer isso ao mesmo tempo) e Simplex (apenas envia os dados).

Outra característica importante é que quando estabelecemos nossa comunicação, devemos


ter o que chamamos de bits de paridade, que são basicamente bits responsáveis por
mostrar quando iniciamos o envio de um bloco de dados e quando encerramos esse envio.
Isso se deve devido ao assincronismo do protocolo.

49
Imagem 4.2.2: Bits enviados entre os dispositivos de uma UART

Vale ressaltar que existe o protocolo USART, que introduz o sincronismo ao protocolo
UART, assim podemos usar este como um protocolo base para todos os outros. Ou seja,
podemos dizer que o USART é o coração dos protocolos de comunicação.

FORMA DE ONDA
Mas como que realmente a UART consegue estabelecer a comunicação. Inicialmente
devemos saber que quando não temos dados para serem enviados, ou seja o nosso Bus
Idle estiver vazio, as saídas TX e RX vão para nível lógico alto, quando a comunicação vai
ser iniciada, os canais vão para nível lógico baixo, essa variação representa o bit de
paridade que já mencionamos, marcando o início da comunicação, então os bits de dados
são enviados, normalmente enviamos 8 bits, mas podemos enviar entre 5 e 9 bits, por fim
as saídas são forçadas para nível lógico alto, marcando assim o fim da comunicação e se
mantendo até que uma nova comunicação se inicie.

Imagem 4.3.1: Forma de onda da comunicação UART

BAUD RATE
Já falamos sobre Baud Rate quando explicamos SPI, porém esta característica para o
protocolo UART, é algo extremamente importante. Pois é este fator que permite a
comunicação ser estabelecida. O Baud Rate do emissor e do receptor podem diferir no
máximo 10%, caso esse valor seja superior iremos ter falhas na nossa comunicação.

Esta característica é definida como sendo a frequência de transmissão, ou seja quantos bits
são enviados e recebidos por segundo, esta informação está no datasheet dos
componentes, então sempre verificar o datasheet para estabelecer o protocolo sem maiores
erros.

50
FUNCIONAMENTO DO PROTOCOLO UART
Para explicar de forma simples como a comunicação UART é estabelecida, vou dividir em 5
etapas.

Inicialmente a UART recebe informações na forma paralela, pelo Data Bus.

Imagem 4.5.1: Primeira etapa da comunicação

Depois a UART posiciona os bits de paridade, e converte a informação paralela em serial.

Imagem 4.5.2: Segunda etapa da comunicação

Logo os dados são enviados para nosso receptor, que interpreta os dados usando o Baud
Rate, para localizar cada bit dos dados.

51
Imagem 4.5.3: Terceira etapa da comunicação

Assim que o receptor, recebe os dados, ela deve descartar os bits de paridade.

Imagem 4.5.4: Quarta etapa da comunicação

Por fim, o receptor converte a informação serial em paralela.

Imagem 4.5.5: Quinta etapa da comunicação

52
COMUNICAÇÃO UART NO ARDUINO UNO

Imagem 4.6.1: Pinagem do Arduino Uno

De forma similar ao protocolo SPI, a comunicação UART, já tem uma biblioteca padrão para
o Arduino. Dentro dessa biblioteca devemos ressaltar alguns métodos importantes.

- Serial.begin():
É a primeira função a ser utilizada quando vai trabalhar com a comunicação serial. Ela
configura a taxa de comunicação em bits por segundo (baud rate). Possui um segundo
parâmetro opcional para a definição da quantidade de bits, paridade e stop bits. Se for
omitido esse parâmetro o padrão será 8 bits, sem paridade e 1 stop bit. Com a seguinte
sintaxe: Serial.begin(speed) ou Serial.begin(speed, config), explicando o que são os dois
parâmetros, speed define a velocidade em bit por segundo (baud rate) e é do tipo long, e o
config: configura a quantidade de bits, paridade e stop bits. Os valores válidos são
SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1 (padrão), SERIAL_5N2,
SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, SERIAL_5E1, SERIAL_6E1, SERIAL_7E1,
SERIAL_8E1, SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, SERIAL_5O1,
SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, SERIAL_5O2, SERIAL_6O2, SERIAL_7O2,
SERIAL_8O2.

53
- Serial.available():

Retorna a quantidades de bytes disponíveis para leitura no buffer de leitura. Essa função
auxilia em loops onde a leitura dos dados só e realizada quando há dados disponível. A
quantidade máxima de bytes no buffer é 64. Sua sintaxe é Serial.available(). E tem como
retorno a quantidades de bytes disponíveis para leitura, esse valor é do tipo int

- Serial.read():
Lê o byte mais recente apontado no buffer de entrada da serial. Sua sintaxe é Serial.read().
E temos como retorno o primeiro byte disponível no buffer da serial.

- Serial.print():
Escreve na serial texto em formato ASCII. Essa função tem muitas possibilidades. Números
inteiros são escritos usando um caractere ASCII para cada dígito. O mesmo ocorre para
números flutuante e, por padrão, são escrito duas casas decimais. Bytes são enviados
como caracteres únicos e strings e caracteres são enviados como escritos. Sua sintaxe é
Serial.print(val) ou Serial.print(val, format), explicando o que cada parâmetro é val recebe
valor para ser escrito na serial - qualquer tipo de dado e format é a base numérica para
tipos inteiros ou a quantidade de casas decimais para números flutuantes. A função retorna
size_t que é a quantidade de bytes escritos sendo do tipo long.

- Serial.println()
Funciona praticamente igual a função Serial.print(), a única diferença é que esta função
acrescenta ao fim da mensagem o caractere de retorno de carro (ASCII 13 ou ‘\r’) e o
caractere de nova linha(ASCII 10 ou ‘\n’). A sintaxe, os parâmetros e o retorno são os
mesmos da função Serial.print().

- Serial.write()
Escreve um byte na porta serial. Sua sintaxe é Serial.write(val) ou Serial.write(str) ou
Serial.write(buf, len), explicando cada parâmetro val recebe um valor para ser enviado como
um único byte, str recebe uma string para ser enviada como uma sequência de bytes, buf
recebe um array para ser enviado como uma série de bytes e len é o tamanho do buffer a
ser enviado. Esta função tem como retorno a quantidade de bytes escritos na serial. A
leitura deste número é opcional.

COMUNICAÇÃO UART NO STM

54
VANTAGENS E DESVANTAGENS DOS PROTOCOLOS
As principais vantagens das comunicações apresentadas, são mostradas na tabela abaixo.

PROTOCOLO BAUD SENTIDO DE MÉTODO Nº DE TENSÃO MÁXIMO DE


RATE TRANSMISSÃO FIOS RECEPTORES

1200 Full-Duplex, 1
UART 115200 Half-Duplex ou Assíncrono 2 0 a 5V
Simplex

I2C 100k Half-Duplex Síncrono 2 0 a 5V 127 ou 1024


400k

SPI 0 Full-Duplex Síncrono 3+ 0 a 5V Não tem


10M Slave
Tabela 3.1: Diferenças entre os protocolos

VANTAGENS DO SPI
As principais vantagens do protocolo SPI são que este protocolo é extremamente flexível
sobre a quantidade de bits a serem transferidos, a interface de hardware é extremamente
simples, não temos limitações no quesito de velocidade, é um protocolo mais rápido entre
as comunicações assíncronas, temos o suporte à múltiplos escravos, podemos usar uma
comunicação do tipo full-duplex, temos apenas 2 pinos comuns os demais são de via única.

DESVANTAGENS DO SPI
Porém as desvantagens do protocolo SPI são a necessidade de usarmos muitos pinos em
comparação com o protocolo I2C, não podemos adotar uma comunicação dinâmica, temos
apenas um dispositivo mestre, não temos um protocolo de erro (esta desvantagem já foi
corrigida nos STM da família 32F04), ao trabalhar com múltiplos escravos o protocolo
começa a ficar complexo devido a quantidade de SS.

VANTAGENS DO I2C
As vantagens do protocolo I2C são o mantenimento de poucos pinos independentemente
da quantidade de dispositivos conectados, se adaptar às necessidades de vários escravos,
consegue suportar múltiplos mestres, consegue corrigir falhas na comunicação.

DESVANTAGENS DO I2C
Mas temos algumas desvantagens no protocolo I2C que são o aumento na complexidade
do firmware ou do hardware de baixo nível, temos a necessidades de resistores de pull-up
que acabam limitando a velocidade do clock, consome espaço na PCB (este problema pode
ser um fator decisivo em sistemas pequenos), além de aumentar a dissipação de potência
aquecendo a placa.

55
VANTAGENS DA UART
As principais vantagens do protocolo UART são que este protocolo usa apenas dois pinos
ou 2 wires, não necessitamos de um sinal de sincronismo, graças ao bit de paridade temos
uma estrutura para verificar se ocorreu algum erro na comunicação, podemos alterar a
informação no pacote de dados desde que o dispositivos permitirem tal, e este método é
bem simples e com uma vasta documentação.

DESVANTAGENS DA UART
Por outro lado as desvantagens do protocolo UART são quantidade de dados a ser enviada
tem no máximo 9 bits, não temos a possibilidade de manter a comunicação com mais de um
dispositivo ou mais de um slave, e o baud rate tem que ser com uma diferença de no
máximo 10% entre os dispositivos.

CONCLUSÃO
Para finalizar esta pesquisa, cada protocolo tem suas vantagens e desvantagens, isso deve
se à como esse protocolo foi concebido. Logo você deve analisar as necessidades do seu
sistema, para verificar qual protocolo é o ideal para você, porém a implementação da
maioria dos protocolos mostrados é bem simples, tanto a nível de software quanto a nível
de hardware.

Para finalizar minha principal recomendação é usar um microcontrolador que facilite a


implementação de tais protocolos, para isso eu recomendo os micros da STM,
principalmente os da família STM32F04.

56

Você também pode gostar