Mudanças entre as edições de "Motor de Passo MIC29004-2014-2"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
 
(175 revisões intermediárias por 8 usuários não estão sendo mostradas)
Linha 1: Linha 1:
 
==Descrição do projeto==
 
==Descrição do projeto==
  
 
+
O objetivo da primeira versão do projeto é gerar um conjunto básico de mensagens para controlar um motor de passo através de um kit didático da família de microcontroladores 8051 da ATMEL. Os comandos de acionamento do motor de passo são enviados por um computador através da interface RS-232 para o microcontrolador 8051.
Desenvolver um controlador para motor de passo usando o microcontrolador 8051. Os comandos de acionamento do motor de passo são gerados por de um software em C sob um computador enviando comandos via RS-232 para o microcontrolador 8051. Este último decodifica as mensagens e habilita as portas que controlam o motor de passo. As principais etapas são a codificação das mensagens de comando e implementação via hardware do projeto
+
O kit está programado para decodificar as mensagens e habilitar imediatamente as portas que controlam um motor de passo unipolar. Este documento descreve a definição do modo de codificação e decodificação das mensagens de comando, processo implementação via hardware e software para comunicação serial. Adicionamos descritivos sobre características de circuitos com motores de passo, esquemas elétricos, procedimento de gravação do microcontrolador 8051 no kit para que se possa incrementar novas atualizações ao projeto.
  
 
===Etapas===
 
===Etapas===
 
====Especificação do projeto====
 
====Especificação do projeto====
Consiste a documentação postada em Wiki descrevendo as características, dispositivos utilizados, forma de implementação, descrição da implementação em hardware.
+
Consiste na documentação postada em Wiki descrevendo as características, dispositivos utilizados, implementação e testes do projeto.
 +
A codificação das mensagens tende a ser o mais transparente possível entre o PC e o motor de passo. Fazendo com que o código no microcontrolador seja mais simplificado. A desvantagem desta implementação é grande quantidade de números "0" que são transmitidos, fazendo que se houver transmissão continua de um mesmo dado, haverá a possibilidade de perda do sincronismo. A codificação está definida na tabela abaixo. Uma palavra na interface serial é composto por 8 bits entre PC e Kit, respeitando o padrão UART. O objetivo final é decodificar para acionar o motor de passo de 4 fios rotacionando conforme o ângulo definido na tabela abaixo.
  
As mensagens entre PC<->Microcontrolador seguem o padrão UART, 9600 bps, fullduplex, sem paridade. A codificação para acionamento do motor segue a tabela abaixo. O código é composto por 8 bits entre PC e Kit. A mensagem é decodificada para acionar o motor de passo de 4 fios conectado na porta P2 do microcontrolador.
 
 
<table border="1" cellpadding="2">
 
<table border="1" cellpadding="2">
 
<caption>Tabela de codificação do motor de passo</caption>
 
<caption>Tabela de codificação do motor de passo</caption>
<tr><th>Valor</th><th>Código UART</th><th>Código P2 8051</th><th>Movimento</th></tr>
+
<tr><th>Valor</th><th>Códificação UART</th><th>decodificação porta P2 8051</th><th>Movimento</th></tr>
 
<tr><th>1</th><td>00000001</td><td>0001</td><td>0°</td></tr>
 
<tr><th>1</th><td>00000001</td><td>0001</td><td>0°</td></tr>
 
<tr><th>2</th><td>00000011</td><td>0011</td><td>45°</td></tr>
 
<tr><th>2</th><td>00000011</td><td>0011</td><td>45°</td></tr>
<tr><th>3</th><td>00000001</td><td>0010</td><td>90°</td></tr>
+
<tr><th>3</th><td>00000010</td><td>0010</td><td>90°</td></tr>
 
<tr><th>4</th><td>00000110</td><td>0110</td><td>135°</td></tr>
 
<tr><th>4</th><td>00000110</td><td>0110</td><td>135°</td></tr>
 
<tr><th>5</th><td>00000100</td><td>0100</td><td>180°</td></tr>
 
<tr><th>5</th><td>00000100</td><td>0100</td><td>180°</td></tr>
Linha 23: Linha 23:
  
 
Se a tabela acima fosse implementada na ordem sugerida o motor de passo completaria uma volta.  
 
Se a tabela acima fosse implementada na ordem sugerida o motor de passo completaria uma volta.  
 
+
*Demais especificações:
 
+
** As mensagens serão sentido PC (mestre)-> microcontrolador (escravo), ou seja, o microcontrolador apenas decodifica as mensagens e aciona o motor de passo.
 
+
** Não haverá verificação de erro da palavra recebida no SBUFF.
Previsão de conclusão: 24/11/2014
+
** Não haverá implementação de reenvio de palavra.
 +
** Ângulos de movimento pré-fixados em tabela.
 +
** Sem controle de desvio de rotação.
 +
** Controle somente de um motor de passo.
 +
** Rotação do motor é sentindo horário e anti-horário.
 +
** atraso entre os comandos de 1 segundo.
 +
** mensagens recebidas são tratadas como um interrupção.
 +
** Motor ficará em repouso enquanto não houve comando.
 +
** Configuração da interface serial: um bit de parada, nenhum bit de paridade, velocidade 9600bps.
  
 
====Pesquisa====
 
====Pesquisa====
Consiste avaliar/obter as ferramentas de hardware para desenvolvimento do projeto. Os principais recursos utilizados são: kit diático AT89Sxx, computador com interface RS232, paralela, Aplicativos: Proteus, MCU8051IDE, Gterm, AEC ISP(programador8051).
+
Consiste em avaliar e obter as ferramentas de hardware e aplicativos para desenvolvimento do projeto. Os principais recursos utilizados são: kit diático AT89Sxx,motor de passo 5V, driver ULN2003, computador com uma distribuição Linux, com porta RS232 e paralela. Aplicativos: Proteus, MCU8051IDE, Gtkterm, AEC ISP.
 +
=====Descritivo dos aplicativos=====
 +
* MCU8051IDE: simulador completo para circuito da arquitetura C51. Acesso aos registradores especiais e memória interna, externa. O Simulador permite compilar e gerar um código .hex para gravação em um respectivo microcontrolador.
 +
* Gtkterm: aplicativo que lê e escreve dados na porta RS-232.
 +
* AEC_ISP: Gravador dedicado para gravar microcontroladores da família C51 pela porta paralela do computador.
 +
* Proteus: Simulador de circuito eletrônico com grande biblioteca de componentes.
  
 
Previsão de conclusão: 01/12/2014
 
Previsão de conclusão: 01/12/2014
  
====Implementação====
+
=====Descritivo dos dispositivos=====
Consiste na montagem do protótipo e avaliação do projeto com testes e relatório dos resultados.
+
*;Motor de Passo
 
 
===Descrição dos dispositivos===
 
====Motor de Passo====
 
 
Motores de passos são dispositivos mecânicos eletro-magnéticos que podem ser controlados digitalmente através de um hardware específico ou através de softwares. Esses motores são usados em aparelhos onde a precisão é um fator importante. Existem vários modelos de motores de passos disponíveis no mercado que podem ser utilizados para propósitos como impressoras, robôs, câmeras de vigilância, máquinas industriais, brinquedos.
 
Motores de passos são dispositivos mecânicos eletro-magnéticos que podem ser controlados digitalmente através de um hardware específico ou através de softwares. Esses motores são usados em aparelhos onde a precisão é um fator importante. Existem vários modelos de motores de passos disponíveis no mercado que podem ser utilizados para propósitos como impressoras, robôs, câmeras de vigilância, máquinas industriais, brinquedos.
 
Como os demais motores, o movimento dos motores de passo é gerado pela interação entre imãs permanentes e eletroímãs. Tipicamente os eletroímãs estão fixos e os imãs permanentes estão na parte móvel do motor.
 
Como os demais motores, o movimento dos motores de passo é gerado pela interação entre imãs permanentes e eletroímãs. Tipicamente os eletroímãs estão fixos e os imãs permanentes estão na parte móvel do motor.
 
O que diferencia o motor de passo é que o movimento é controlado por pulsos, ativando e desativando os eletroímãs em uma determinada ordem. Dependendo de quais eletroímãs estão ativos, os imãs permanentes serão atraídos, colocando o motor em uma determinada posição. A ordem e a velocidade dos pulsos determina o movimento do motor.
 
O que diferencia o motor de passo é que o movimento é controlado por pulsos, ativando e desativando os eletroímãs em uma determinada ordem. Dependendo de quais eletroímãs estão ativos, os imãs permanentes serão atraídos, colocando o motor em uma determinada posição. A ordem e a velocidade dos pulsos determina o movimento do motor.
  
 +
*;Funcionamento Motor de passo
 +
 +
* [http://youtu.be/Yt96gdpxV2g Funcionamento Motor de passo]  {{#ev:youtube|Yt96gdpxV2g#!}}
 +
 +
 +
As portas de I/O do microntrolador 8051 usam tecnologia CMOS e tensão até 5.5V e 60uA de corrente. Para controlar o motor de passo é necessário um arranjo tipo drive para fornecer maior corrente ao motor. O esquema abaixo demostra a conexão da saída do microntrolador a um transistor de Potência. A base do microcontrolador colocará o transistor de potência saturado fazendo o motor ser alimentado. Se A base do transistor de potência ser 0V o transistor entrará na região de corte e o motor será desligado. Se a base do transistor de potência ser 5V, o transistor entrará na região de saturação, permitindo que o motor seja alimentado.
 +
 +
[[Arquivo:Drive.jpg|500px]]
 +
 +
*;Circuito Motor de passo
 +
Para agilizar o desenvolvimento optamos por utilizar o driver comercial ULN2003 e o motor de passo 28BYJ-48. O ULN2003 suporta até 500mA de corrente em cada porta. Este componente possui diodo catodo comum em cada porta para proteção contra picos de corrente provenientes do "start do motor.
 +
 +
Motor de passo 28BYJ-48.
 +
 +
[[Arquivo:motor1.png|350px]]
 +
 +
 +
 +
 +
Conforme a derivada abaixo, descreve o pico de corrente gerada quando o motor é acionado. O pico de corrente ocorre devido ao período do pulso tender a zero e gerando uma alta frequência na bobina, no qual surgirá uma reatância indutiva e um tensão aumentando o fluxo de corrente para o driver. Para controlar este efeito é usado diodos em cada porta do driver ULN2003.
 +
 +
 +
<math>V = L.(di/dt)</math>
 +
 +
L= indutância.
 +
 +
t= tempo.
 +
 +
V=tensão.
 +
 +
[http://img.filipeflop.com/files/download/Datasheet_28BYJ-48.pdf Especificação motor de passo 28BYJ-48 ]
 +
 +
De acordo com o datasheet sem o redutor (considerando somente o eixo principal), o motor precisa de 64 steps para completar uma volta. Neste caso cada step completo moveria 5,625° do eixo principal.
 +
 +
Conforme imagem abaixo o motor possui o eixo principal(1) e um sistema redutor(2), no qual, reduz em 1/64 o movimento de cada step. Portanto, o eixo principal(1) gera em cada step 5,625° de movimento e o sistema redutor(2) divide este angulo de passo em 1/64.
 +
 +
[[Arquivo:mt1.png|350px]]
 +
 +
Desta forma em nosso projeto, cada passo representa 0.08° e uma volta completa necessitará de 2048 steps completos e 4090 meio steps.
  
 +
*;Diagrama driver ULN2003.
  
 +
[[Arquivo:ULN2003.png|350px]]
  
[[Arquivo:Motor_de_passo.jpg|350px]]
+
[http://www.ti.com/lit/ds/symlink/uln2003a.pdf Especificação Driver ULN2003 ]
  
[[Arquivo:MotorPasso2.jpg|350px]]
+
O esquema elétrico do circuito entre o microcontrolador e o motor de passo :
  
 +
[[Arquivo:mpsetup.png|350px]]
  
 +
*;RS-232
 +
Os comandos para acionar o motor são enviados pelo computador para o kit através de um cabo RS-232. É uma tecnologia de comunicação ponto a ponto e a mensagem é formada por palavras de 8 bits. Sua facilidade, custo e baixa complexidade de implementação permite seu uso em grande escala em projetos que não exigem alta taxa de transferência. Uma de suas limitações é a distância, não devendo ser superior a 10 metros. O microcontrolador oferece 4 modos de operação. Porém iremos forcar no modo de operação 2, no qual configura a interface para operar em modo full-duplex, sem bit de paridade. A  velocidade da interface depende da frequência de oscilação do cristal montado no Kit. As equações para obter a taxa de bauds, são apresentadas na apostila do curso.
 +
O pinos obrigatórios para haver comunicação são o 2(TX), 3(RX) e 5(GND). A conexão na outra extremidade deve ter os sinais RX e TX invertidos na própria interface do computador ou senão invertido no conector do cabo.
  
As portas de I/O do microntrolador 8051 usam tecnologia CMOS e tensão até 5.5V e 60uA de corrente. Para controlar o motor de passo é necessário um arranjo tipo drive para fornecer maior corrente ao motor. O esquema abaixo demostra a conexão da saída do microntrolador a um transistor de Potência. A base do microcontrolador colocará o transistor de potência saturado fazendo o motor ser alimentado. Se A base do transistor de potência ser 0V o transistor entrará na região de corte e o motor será desligado. Se a base do transistor de potência ser 5V, o transistor entrará na região de saturação, permitindo que o motor seja alimentado.
 
  
[[Arquivo:Drive.jpg|500px]]
+
[[Arquivo:rs232_1.jpg|300px|center]]
 +
 
 +
[[Arquivo:rs232_3.jpg|350px|center]]
 +
 
 +
[[Arquivo:rs232_2.jpg|300px|center]]
 +
 
  
http://multilogica-shop.com/
 
  
====RS-232====
+
<br>''Disposição dos pinos e sinais no conector DB-9''
  
====Microcontrolador====
+
*;Microcontrolador
 
Para o desenvolvimento deste projeto, utilizamos o Kit didático micro AT89Sxx disponível no almoxarifado do IFCS-SJ.  
 
Para o desenvolvimento deste projeto, utilizamos o Kit didático micro AT89Sxx disponível no almoxarifado do IFCS-SJ.  
 
O kit é composto por:
 
O kit é composto por:
* Microcontrolador ATMEL AT89S8252
+
** Microcontrolador ATMEL AT89S8252
* 1 Interface UART
+
** 1 Interface UART
* 6 botões push-button conectados na porta P3.
+
** 6 botões push-button conectados na porta P3.
* 8 botões push-button conectados na porta P2.
+
** 8 botões push-button conectados na porta P2.
* 8 Leds conectados na porta P1.
+
** 8 Leds conectados na porta P1.
* 4 conjuntos de barra pinos conectados nas portas P0, P1, P2 e P3.
+
** 4 conjuntos de barra pinos conectados nas portas P0, P1, P2 e P3.
* Interface de gravação do microcontrolador.
+
** Interface de gravação do microcontrolador.
* Interface para display 2x16.
+
** Interface para display 2x16.
  
=====Interface de gravação=====
+
as interfaces do kit são apresentados na imagem abaixo:
A interface de gravação utiliza um cabo RS-232/paralela. A interface RS-232 do cabo é conectado na porta paralela do computador. O esquema do cabo é representado na imagem abaixo:
 
  
[[Arquivo:Grav.jpg|400px]]
+
[[Arquivo:kit.png|600px]]
  
=====Programa gravador=====
+
*;Conector para gravaçãoo
O programa de gravação para Windows XP está disponível no link abaixo.
+
A interface de gravação utiliza um cabo DB9/DB25. O conector DB9 do cabo é conectado na porta Gravação do kit (conector DB-9 Fêmea) e conector DB-25 do cabo conectado na porta paralela do computador. O esquema do cabo é representado na imagem abaixo:
 +
 
 +
[[Arquivo:Grav.jpg|400px|center]]
 +
*;Programa gravador
 +
O programa de gravação do microcontrolador para Windows XP está disponível no link abaixo.
  
 
* [http://www.aec-electronics.co.nz/aec_isp.pdf#especificação especificação do aplicativo]
 
* [http://www.aec-electronics.co.nz/aec_isp.pdf#especificação especificação do aplicativo]
Linha 83: Linha 144:
 
* [ftp://pti.kpi.ua/pub/electric/CONTROLER/atmel/aec_isp.zip#AEC_ISP Gravador do kit Link 2]
 
* [ftp://pti.kpi.ua/pub/electric/CONTROLER/atmel/aec_isp.zip#AEC_ISP Gravador do kit Link 2]
  
=====Gravação=====
+
*;Gravação no Kit didático AT89Sxx
 
Passos para gravar um arquivo (.HEX) compilado no Kit.
 
Passos para gravar um arquivo (.HEX) compilado no Kit.
 
*1- Conecte o cabo de gravação na porta "CABO PROG" e na porta paralela do computador.
 
*1- Conecte o cabo de gravação na porta "CABO PROG" e na porta paralela do computador.
 
*2- Com o kit desligado, abra o jumper J1.
 
*2- Com o kit desligado, abra o jumper J1.
*3- Mude a chave para Prog.
+
*3- Mude a chave EXEC/PROG para Prog.
*4- Ligue o kit.
+
*4- Ligue o kit. (O LED PROG acenderá)
 
*5- Abra o programa AEC_ISP.
 
*5- Abra o programa AEC_ISP.
 
*6- Selecione a opção "Load to buffer flash the .hex data.
 
*6- Selecione a opção "Load to buffer flash the .hex data.
 +
 +
[[Arquivo:prog1.png|400px|center]]
 +
 
*7- Em "input file:" Digite o caminho do arquivo .hex.
 
*7- Em "input file:" Digite o caminho do arquivo .hex.
*8- Pressione "ENTER".
+
 
*9- Pressione "ESC".
+
[[Arquivo:prog2.png|400px|center]]
*10- Selecione a opção "Load display buffer flash".
+
 
*11- Pressione "ESC".
+
*8- Pressione "ENTER" para confirmar.
*12- Selecione a opção "program".
+
 
*13- Desligue o kit.
+
[[Arquivo:prog3.png|400px|center]]
*14- Mude a Chave de "PROG" para "EXEC".
+
 
*15- Feche o jumper J1.
+
*9- Pressione "ESC" para retornar.
*16- Retire o cabo prog.
+
*10- Selecione a opção "Load display buffer flash". verifique se o arquivo foi carregado.
*17- Ligue o kit. O programa gravado entrará em execução.
+
 
 +
[[Arquivo:prog4.png|400px|center]]
 +
 
 +
*11- Pressione "ESC" para voltar.
 +
 
 +
[[Arquivo:prog5.png|400px|center]]
 +
 
 +
*12- Selecione a opção "program" e observe as mensagens durante de gravação.
 +
 
 +
[[Arquivo:prog6.png|400px|center]]
 +
 
 +
*13-Tela de confirmação que a gravação foi realizada com sucesso.
 +
 
 +
[[Arquivo:prog7.png|400px|center]]
 +
 
 +
*14- Desligue o kit.
 +
*15- Mude a Chave EXEC/PROG de "PROG" para "EXEC".
 +
*16- Feche o jumper J1.
 +
*17- Retire o cabo prog.
 +
*18- Ligue o kit(O LED prog ficará apagado). O programa gravado entrará em execução.
  
 
==Fluxogramas==
 
==Fluxogramas==
Linha 117: Linha 200:
 
[[Arquivo:Fluxograma_passo_motor_passos_MIC2014-2.jpg|center|200px]]
 
[[Arquivo:Fluxograma_passo_motor_passos_MIC2014-2.jpg|center|200px]]
  
===Desenvolvimento===
+
==Desenvolvimento==
 +
===Algorítmo 8051 (Escravo)===
  
O Esquema é apresentado no link abaixo.
+
O programa no microcontrolador atua como escravo. Recebe as mensagens do PC(Mestre) e decodifica através de sinais na porta P2.
 +
====configuração dos registradores e temporizações====
  
[[Arquivo:Esquema1.jpg|300px]]
+
O código do microcontrolador é feito em Assembly. Inicia configurando o modo de operação do timer 1, serial e interrupções. É implementado a rotina de acionamento da porta P2, no qual esta conectado o motor de passo no endereço de interrupção 0x0023H. Em um eventual dado recebido pela interface  RS 232, o byte em SBUF é enviado para o acumulador. O byte então, é decodificado e enviado o sinal correspondente para a porta P2.
 
 
O código Assembly inicia configura o modo de operação do timer 1, serial e interrupções. É implementado a rotina de acionamento da porta P2, no qual esta conectado o motor de passo. No eventual dado recebido pela interface  RS 232, o byte em SBUF é enviado para o acumulador. O byte então, é decodificado e enviado o sinal correspondente para a porta P2.
 
  
 
* TMOD, TH1, TH0, TL0
 
* TMOD, TH1, TH0, TL0
Linha 144: Linha 227:
 
Loop: JNB RI, Loop
 
Loop: JNB RI, Loop
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
*Rotina 10ms
 +
Rotina de atraso entre os comandos de step gerados pelo microcontrolador para o motor. Se o intervalo entre cada step por menor que 10ms, o motor poderá não responder corretamente.
 +
 +
<code>
 +
DELAY_10ms:
 +
        MOV TH0,  #0D8H;
 +
        MOV TL0,  #0F0H;
 +
        MOV TCON, #50H;
 +
LOOP:  JNB TF0,  LOOP;
 +
        CLR TF0;
 +
        CLR TR0;
 +
        RET;
 +
</syntaxhighlight>
 +
 +
* Rotina 50 ms
 +
O Timer 0 é utilizado para contabilizar um bloco elementar de 50ms. Demais temporizações são referenciadas a partir deste bloco.
 +
<code>
 +
DELAY_50ms:
 +
      MOV TH0,#3CH;
 +
      MOV TL0,#0B0H;
 +
      MOV TCON,#50H;
 +
LOOP:  JNB TF0, LOOP;
 +
      CLR TF0;
 +
      CLR TR0;
 +
      RET;
 +
</syntaxhighlight>
 +
 +
* Rotina de 1 segundo usa a rotina de delay_50ms como estrutura básica para gerar delay de 1 segundo:
 +
<code>
 +
MAIN:LCALL DELAY_1SEG;
 +
DELAY_1SEG:
 +
MOV B,#19D;
 +
AQUI: LCALL DELAY_50MS;
 +
DJNZ B, AQUI;
 +
RET;
 +
 +
DELAY_50ms:
 +
        MOV TH0,#3CH;
 +
        MOV TL0,#0B0H;
 +
        MOV TCON,#50H;
 +
LOOP:  JNB TF0, LOOP;
 +
        CLR TF0;
 +
        CLR TR0;
 +
        RET;
 +
</syntaxhighlight>
 +
 +
====Tratamento das interrupções e comandos para o motor de passo====
 +
 +
Quando habilitado a interrrupção na porta serial e houver o SBUFF recebido um byte na RS-232, o registrador PC irá assumir o endereço para a posição 0023H e Stack Pointer será incrementado em dois onde será armazenado o endereço de programa da próxima instrução antes do desvio para a posição 0023H.
 +
 +
A partir da posiçã 0023H é zerado a flag de interrupção IR e o byte recebido em SBUF é movido para o acumulador A e então testado com os ângulos pré-determinados. Em caso de igualdade jogará na saída de P2 (P2.[0-3]) o respectivos sinais de acionamento.
 +
 +
<code>
 +
org    0023H
 +
mov    a,sbuf;
 +
clr    ri
 +
SJMP  TEST_ANG_45
 +
 +
TEST_ANG_45:
 +
  CJNE  A, #ANG_45,TEST_ANG_90 ;ANG_45,TEST_ANG_90
 +
  MOV    B, #63d;
 +
ROT_45: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_45;
 +
  RETI
 +
 +
TEST_ANG_90:
 +
  CJNE  A,#ANG_90,TEST_ANG_135
 +
 +
rot_90_1:
 +
  MOV    B,#127d;
 +
ROT_90: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_90;
 +
  RETI
 +
 +
TEST_ANG_135:
 +
  CJNE  A,#ANG_135,TEST_ANG_180
 +
  MOV    B,#192d;
 +
ROT_135: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_135;
 +
  RETI
 +
 +
TEST_ANG_180:
 +
  CJNE  A,#ANG_180,TEST_ANG_225
 +
  MOV    B,#255d;
 +
ROT_180: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_180;
 +
  lcall  passo;
 +
  RETI     
 +
 +
TEST_ANG_225:
 +
  CJNE  A,#ANG_225,TEST_ANG_270
 +
  MOV    R7,#5d;
 +
ROT_225_1:
 +
  MOV    B,#63d;
 +
ROT_225: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_225;
 +
  DJNZ  R7,ROT_225_1
 +
  RETI   
 +
;
 +
TEST_ANG_270:
 +
  CJNE  A,#ANG_270,TEST_ANG_315
 +
  MOV R7,#3d;
 +
ROT_270_1:
 +
  MOV    B,#127d;
 +
ROT_270: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_270;
 +
  DJNZ  R7,ROT_270_1
 +
  RETI 
 +
 +
TEST_ANG_315:
 +
  CJNE  A,#ANG_315,TEST_ANG_360;
 +
  MOV    R7,#7d;
 +
ROT_315_1:
 +
  MOV    B,#63d;
 +
ROT_315: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_315;
 +
  DJNZ  R7,ROT_315_1
 +
  RETI
 +
 +
TEST_ANG_360:
 +
  CJNE  A,#ANG_360,ERRO;
 +
  MOV    R7,#8d;
 +
ROT_360_1:
 +
  MOV    B,#63d;
 +
ROT_360: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_360;
 +
  DJNZ  R7,ROT_360_1
 +
  RETI
 +
 +
erro:
 +
MOV    P1, #00H;
 +
lcall  delay_1SEG;
 +
  MOV    P1,#0ffh;
 +
  reti;
 +
 +
</syntaxhighlight>
 +
 +
Quando o SBUF é preenchido com um byte, uma interrupção é gerada, o PC é apontado para 0023H e o SP que inicialmente era 07 é incrementado em dois, assumindo 09. SP na posição 08 e 09 assume o endereço de programa da instrução main(ver código completo abaixo)jmp. Se SBUFF corresponder a algum dos testes de ângulo, haverá um novo incremento de SP devido a rotina de atraso ser acionada via LCALL. Desta forma notamos que interrupção e LCALL incrementam o SP, indicando o endereço da próxima instrução antes de entrar no função correspondente ao LCALL ou interrupção. Podemos observar que o SP é decrementado em 2 para RETI ou RET e o valor de PC assume o conteúdo SP antes de ser decrementado.
 +
 +
 +
* Rotina de um passo no motor.
 +
Conforme datasheet um passo no motor tem 5,625°/64. Desta forma, se quisermos movimentar 45° são necessário 512 steps no total. Criamos uma subrotina chamada "passo" no qual gera 8 passos para movimentar 5,625°.Durante a simulação notamos um ganho de velocidade de 0,5 quando enviamos para a porta P2 apenas o passo inteiro (0001,0010,0100 e 1000) com o mesmo ângulo de 5,625° de passo.
 +
 +
<code>
 +
passo:
 +
  MOV    P2,#00000001b;
 +
  lcall  delay_10ms;
 +
  MOV    P2,#00000010b;
 +
  lcall  delay_10ms;
 +
  MOV    P2,#00000100b;
 +
  lcall  delay_10ms;
 +
  MOV    P2,#00001000b;
 +
  lcall  delay_10ms;
 +
  RET;
 +
</syntaxhighlight>
 +
 +
====Código completo Controle motor de passo====
 +
 +
{{Collapse top |codigo Assembly do Projeto Motor de passo ATMEL 8051 (escravo)}}
 +
<code>
 +
org 0000;
 +
 +
ANG_45  EQU  01H;
 +
ANG_90  EQU  02H;
 +
ANG_135 EQU  03H;
 +
ANG_180 EQU  04H;
 +
ANG_225 EQU  05H;
 +
ANG_270 EQU  06H;
 +
ANG_315 EQU  07H;
 +
ANG_360 EQU  08h;
 +
 +
MOV TMOD,  #21H
 +
MOV TH1,    #0FDH
 +
MOV SCON,  #50H
 +
mov ie,    #10010000b
 +
 +
ljmp main;
 +
 +
org    0023H
 +
mov    a,sbuf;
 +
clr    ri
 +
SJMP  TEST_ANG_45
 +
 +
TEST_ANG_45:
 +
  CJNE  A, #ANG_45,TEST_ANG_90 ;ANG_45,TEST_ANG_90
 +
  MOV    B, #63d;
 +
ROT_45: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_45;
 +
  RETI
 +
 +
TEST_ANG_90:
 +
  CJNE  A,#ANG_90,TEST_ANG_135
 +
 +
rot_90_1:
 +
  MOV    B,#127d;
 +
ROT_90: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_90;
 +
  RETI
 +
 +
TEST_ANG_135:
 +
  CJNE  A,#ANG_135,TEST_ANG_180
 +
  MOV    B,#192d;
 +
ROT_135: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_135;
 +
  RETI
 +
 +
TEST_ANG_180:
 +
  CJNE  A,#ANG_180,TEST_ANG_225
 +
  MOV    B,#255d;
 +
ROT_180: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_180;
 +
  lcall  passo;
 +
  RETI     
 +
 +
TEST_ANG_225:
 +
  CJNE  A,#ANG_225,TEST_ANG_270
 +
  MOV    R7,#5d;
 +
ROT_225_1:
 +
  MOV    B,#63d;
 +
ROT_225: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_225;
 +
  DJNZ  R7,ROT_225_1
 +
  RETI   
 +
;
 +
TEST_ANG_270:
 +
  CJNE  A,#ANG_270,TEST_ANG_315
 +
  MOV R7,#3d;
 +
ROT_270_1:
 +
  MOV    B,#127d;
 +
ROT_270: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_270;
 +
  DJNZ  R7,ROT_270_1
 +
  RETI 
 +
 +
TEST_ANG_315:
 +
  CJNE  A,#ANG_315,TEST_ANG_360;
 +
  MOV    R7,#7d;
 +
ROT_315_1:
 +
  MOV    B,#63d;
 +
ROT_315: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_315;
 +
  DJNZ  R7,ROT_315_1
 +
  RETI
 +
 +
TEST_ANG_360:
 +
  CJNE  A,#ANG_360,ERRO;
 +
  MOV    R7,#8d;
 +
ROT_360_1:
 +
  MOV    B,#63d;
 +
ROT_360: 
 +
  lcall  passo;
 +
  DJNZ  B,ROT_360;
 +
  DJNZ  R7,ROT_360_1
 +
  RETI
 +
 +
erro:
 +
MOV    P1, #00H;
 +
lcall  delay_1SEG;
 +
  MOV    P1,#0ffh;
 +
  reti;
 +
 +
;-----------------------------------
 +
DELAY_1SEG:
 +
MOV    r0,#02h;
 +
AQUI: LCALL  DELAY_50MS;
 +
DJNZ  r0, AQUI;
 +
RET;
 +
 +
DELAY_50ms:
 +
        MOV TH0,  #3CH;
 +
        MOV TL0,  #0B0H;
 +
        MOV TCON, #50H;
 +
LOOP:  JNB TF0,  LOOP;
 +
        CLR TF0;
 +
        CLR TR0;
 +
        RET;
 +
 +
DELAY_10ms:
 +
        MOV TH0,  #0D8H;
 +
        MOV TL0,  #0F0H;
 +
        MOV TCON, #50H;
 +
LOOP2:  JNB TF0,  LOOP2;
 +
        CLR TF0;
 +
        CLR TR0;
 +
        RET;
 +
;------------------------------------
 +
passo:
 +
  MOV    P2,#00000001b;
 +
  lcall  delay_10ms;
 +
  MOV    P2,#00000010b;
 +
  lcall  delay_10ms;
 +
  MOV    P2,#00000100b;
 +
  lcall  delay_10ms;
 +
  MOV    P2,#00001000b;
 +
  lcall  delay_10ms;
 +
 +
  RET;
 +
;-------------------------------------
 +
main:
 +
MOV TH1,    #0FDH
 +
SETB TR1
 +
  Loop1: jnb ri, Loop1
 +
          jmp main;
 +
</syntaxhighlight>
 +
{{Collapse bottom}}
 +
 +
===Algorítmo  computador (Mestre)===
 +
O código de envio dos comandos é construído em C e utiliza bibliotecas do código fonte do kernel para controlar o interface RS 232.
 +
 +
o arquivo 8051.h provê especifica alguns apelidos para as palavras em binário dos comandos do motor, importa as bibliotecas do linux e define as funções utilizadas:
 +
 +
<syntaxhighlight lang=c>
 +
 +
#include <stdio.h> // standard input / output functions
 +
#include <string.h> // string function definitions
 +
#include <unistd.h> // UNIX standard function definitions
 +
#include <fcntl.h> // File control definitions
 +
#include <errno.h> // Error number definitions
 +
#include <termios.h> // POSIX terminal control definitionss
 +
#include <time.h>  // time calls
 +
#include <sys/time.h>
 +
 +
enum commands
 +
{
 +
    CM_START_MOTOR = 0x0,
 +
    CM_STOP_MOTOR  = 0x1,
 +
    CM_STATE_MOTOR = 0X2
 +
};
 +
 +
enum events
 +
{
 +
    EV_MOTOR_IS_STOPED  = 0x0,
 +
    EV_MOTOR_IS_RUNNING = 0x1,
 +
    EV_START_MOTOR_FAIL = 0x2
 +
};
 +
 +
//apelidos para os comandos de movimento para o motor conforme o respectivo ângulo
 +
enum angles
 +
{
 +
    angle_0  = 0x1, // 0001
 +
    angle_45  = 0x3, // 0011
 +
    angle_90  = 0x2, // 0010
 +
    angle_135 = 0x6, // 0110
 +
    angle_180 = 0x4, // 0100
 +
    angle_225 = 0xC, // 1100
 +
    angle_270 = 0x8, // 1000
 +
    angle_315 = 0x9  // 1001
 +
};
 +
 +
// definição das funções
 +
int open_serial_port(char * serial_port); // le o dispositivo /dev/ttyS0 no qual esta a porta RS-232 do computador
 +
int configure_port(int fd); // função para definir o modo de operação porta RS-232. definimos a /dev/ttyS0 para operar com taxa de  9600bps, 8 bits por palavra, Sem bit de paridade e com 1 bit de parada.
 +
 +
</syntaxhighlight>
 +
 +
O arquivo 8051.c é o arquivo principal do programa. É por ele que a configuração da porta e envio dos comandos pela RS-232.
 +
 +
código de acesso à porta serial
 +
<syntaxhighlight lang=c>
 +
int open_serial_port(char * serial_port) //recebe o caminho do dispositivo. Normalmente é chamado de ttyS0 ou ttyusbS0, localizado no /dev
 +
{
 +
    int fd; // Arquivo de descrição da porta serial
 +
    //O_RDWR - define a porta como escrita e leitura.
 +
    //O_NOCTTY -
 +
    //O_NDELAY - Irá atender a qualquer dado recebido na RS-232.
 +
    fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
 +
 +
    if (fd_serial_8051  > -1)
 +
    {
 +
        fcntl(fd_serial_8051, F_SETFL, 0);
 +
        configure_port();
 +
    }
 +
    return(fd_serial_8051);
 +
}
 +
</syntaxhighlight>
 +
 +
Função de configuração da porta RS 232 do computador.
 +
 +
<syntaxhighlight lang=c>
 +
 +
int configure_port(int fd)
 +
{
 +
    if (fd_serial_8051 < 0)
 +
        return fd_serial_8051;
 +
 +
    // Save current settings.
 +
    tcgetattr(fd_serial_8051, &port_settings_backup);
 +
 +
    // set baud rates.
 +
    cfsetispeed(&port_settings, B9600); //define a velocidade da porta
 +
    cfsetospeed(&port_settings, B9600);
 +
 +
    // Clear all settings.
 +
    port_settings.c_cflag = 0x0;
 +
    port_settings.c_oflag = 0x0;
 +
    port_settings.c_iflag = 0x0;
 +
    port_settings.c_lflag = 0x0;
 +
    int i = 0;
 +
    for (i = 0; i < NCCS; i++)
 +
        port_settings.c_cc[i] = 0x0;
 +
 +
    // Set control mode flags.
 +
    port_settings.c_cflag |= CSIZE;
 +
    port_settings.c_cflag |= CS6;
 +
    port_settings.c_cflag |= CS7;
 +
    port_settings.c_cflag |= CS8;
 +
    port_settings.c_cflag |= CREAD;
 +
    port_settings.c_cflag |= CLOCAL;
 +
 +
    // Set input mode flags.
 +
    port_settings.c_iflag |= IGNBRK;
 +
    port_settings.c_iflag |= IGNPAR;
 +
 +
    // Set control characters.
 +
    port_settings.c_cc[VKILL]    = 0x58;
 +
    port_settings.c_cc[VEOF]    = 0xAE;
 +
    port_settings.c_cc[VMIN]    = 0x01;
 +
    port_settings.c_cc[VSWTC]    = 0xBF;
 +
    port_settings.c_cc[VEOL]    = 0x83;
 +
    port_settings.c_cc[VREPRINT] = 0x28;
 +
    port_settings.c_cc[VDISCARD] = 0x63;
 +
    port_settings.c_cc[VWERASE]  = 0xB7;
 +
    port_settings.c_cc[VLNEXT]  = 0x68;
 +
    port_settings.c_cc[VEOL2]    = 0x2B;
 +
 +
    // apply the settings to the port
 +
    tcsetattr(fd_serial_8051, TCSANOW, &port_settings);
 +
 +
    return(fd_serial_8051);
 +
}
 +
</syntaxhighlight c>
 +
 +
função para testes de envio de comando pela RS232
 +
<code>
 +
int send_command(int8 command)
 +
{
 +
    return write(fd_serial_8051, &command, sizeof(int8));
 +
}
 +
 +
</syntaxhighlight>
 +
O arquivo example.c é a parte do programa que interage com o usuário, requisita acesso a porta RS232 e envia os comandos ao kit passando paramentros para funções do arquivo serial_8051.c
 +
 +
<syntaxhighlight lang=c>
 +
int main(int argc, char ** argv)
 +
{
 +
 +
    int angle;
 +
    char angle_s[5];
 +
 +
    printf("angles:\n");
 +
    printf("\t1 - 45\n\t2 - 90\n\t3 - 135\n\t4 - 180\n\t5 - 225\n\t6 - 270\n\t7 - 315\n\t8 - 360\n");
 +
    printf("angle: ");
 +
    fgets(angle_s, sizeof(angle_s), stdin);
 +
 +
    if (sscanf(angle_s, "%d", &angle) != 1)
 +
    {
 +
        printf("invalid option: %s\n", angle_s);
 +
        return 1;
 +
    }
 +
    if (!(angle >= 1 && angle <= 8))
 +
    {
 +
        printf("invalid option: %d\n\n", angle);
 +
        return 1;
 +
    }
 +
 +
    if (open_serial_port("/dev/ttyS0") < 0)
 +
    {
 +
        perror("open_serial_port");
 +
        return 1;
 +
    }
 +
    else   
 +
    {
 +
        if (send_command(angle) < 0)
 +
            perror("send_command");
 +
 +
        close_serial_port();
 +
    }
 +
 +
    return 0;
 +
}
 +
</syntaxhighlight>
 +
 +
====Código completo em C do mestre(PC)====
 +
{{Collapse top |codigo C do mestre(PC)}}
 +
 +
Arquivo serial_8051.h
 +
 +
<syntaxhighlight lang=c>
 +
#ifndef SERIAL_8051_H
 +
#define SERIAL_8051_H
 +
 +
#include <unistd.h>  // UNIX standard function definitions
 +
#include <fcntl.h>    // File control definitions
 +
#include <errno.h>    // Error number definitions
 +
#include <termios.h>  // POSIX terminal control definitions
 +
#include <time.h>    // Time calls
 +
#include <sys/time.h>
 +
 +
typedef unsigned char int8;
 +
 +
/*
 +
* Commands.
 +
*/
 +
enum commands
 +
{
 +
    CM_START_MOTOR = 0x00,
 +
    CM_STOP_MOTOR  = 0x01,
 +
    CM_STATE_MOTOR = 0X02
 +
};
 +
 +
/*
 +
* Events.
 +
*/
 +
enum events
 +
{
 +
    EV_MOTOR_IS_STOPED    = 0x00,
 +
    EV_MOTOR_IS_RUNNING  = 0x01,
 +
    EV_START_MOTOR_FAIL  = 0x02,
 +
    EV_UNKNOWN_COMMAND    = 0x03,
 +
    EV_RECV_EVENT_TIMEOUT = 0xFE,
 +
    EV_RECV_EVENT_FAIL    = 0xFF // errno is set with failure of cause.
 +
};
 +
 +
/*
 +
* Step motor angles.
 +
*/
 +
enum angles
 +
{
 +
    angle_45  = 0x03, // 0011
 +
    angle_90  = 0x02, // 0010
 +
    angle_135 = 0x06, // 0110
 +
    angle_180 = 0x04, // 0100
 +
    angle_225 = 0x0C, // 1100
 +
    angle_270 = 0x08, // 1000
 +
    angle_315 = 0x09  // 1001
 +
};
 +
 +
/*
 +
* Open and configure serial port (9600, 8N1).
 +
* return the new file descriptor,
 +
* or -1 if an error occurred (in which case, errno is set appropriately).
 +
*/
 +
int open_serial_port(char * serial_port);
 +
 +
/*
 +
* Close serial port.
 +
* returns zero on success.
 +
* On error, -1 is returned, and errno is set appropriately.
 +
*/
 +
int close_serial_port();
 +
 +
/*
 +
* Send a command to serial port (see enum commads).
 +
* On success, the number of bytes written is returned
 +
* (zero indicates nothing was written).
 +
* On error, -1 is returned, and errno is set appropriately.
 +
*/
 +
int send_command(int8 command);
 +
 +
/*
 +
* Receive a event from serial port. This function is blocking.
 +
* On success, the event is returned (see enum events).
 +
* On error, EV_READ_EVENT_FAIL is returned and errno is set appropriately.
 +
*/
 +
int8 recv_event();
 +
 +
/*
 +
* Same recv_event function, but this function received two params
 +
* timeout_sec = seconds and timeout_usec = micro seconds.
 +
* The timeout argument specifies the interval that read_event_timeout()
 +
* should block waiting for a serial port to become ready.
 +
* If timeout period has expired, the EV_READ_EVENT_TIMEOUT is returned.
 +
*/
 +
int8 recv_event_timeout(time_t timeout_sec, suseconds_t timeout_usec);
 +
 +
/*
 +
* Convert a command to string.
 +
* Return command in string format (see enum commands).
 +
* If the commands is unknown, "UNKNOWN" is returned.
 +
*/
 +
char * command2string(int8 event);
 +
 +
/*
 +
* Convert an event to string.
 +
* Return event in string format (see enum events).
 +
* If the event is unknown, "UNKNOWN" is returned.
 +
*/
 +
char * event2string(int8 event);
 +
 +
#endif
 +
 +
</syntaxhighlight>
 +
 +
arquivo serial_8051.c:
 +
 +
<syntaxhighlight lang=c>
 +
#include "serial_8051.h"
 +
 +
// File descriptor of the serial port.
 +
static int fd_serial_8051 = -1;
 +
 +
// Structure to store the port settings in.
 +
static struct termios port_settings;
 +
 +
// backup of the current settings.
 +
static struct termios port_settings_backup;
 +
 +
int open_serial_port(char * serial_port)
 +
{
 +
    fd_serial_8051 = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
 +
 +
    if (fd_serial_8051  > -1)
 +
    {
 +
        fcntl(fd_serial_8051, F_SETFL, 0);
 +
        configure_port();
 +
    }
 +
 +
    return(fd_serial_8051);
 +
}
 +
 +
int close_serial_port()
 +
{
 +
    tcsetattr(fd_serial_8051, TCSANOW, &port_settings_backup);
 +
    return close(fd_serial_8051);
 +
}
 +
 +
int configure_port()
 +
{
 +
    if (fd_serial_8051 < 0)
 +
        return fd_serial_8051;
 +
 +
    // Save current settings.
 +
    tcgetattr(fd_serial_8051, &port_settings_backup);
 +
 +
    // set baud rates.
 +
    cfsetispeed(&port_settings, B9600);
 +
    cfsetospeed(&port_settings, B9600);
 +
 +
    // Clear all settings.
 +
    port_settings.c_cflag = 0x0;
 +
    port_settings.c_oflag = 0x0;
 +
    port_settings.c_iflag = 0x0;
 +
    port_settings.c_lflag = 0x0;
 +
    int i = 0;
 +
    for (i = 0; i < NCCS; i++)
 +
        port_settings.c_cc[i] = 0x0;
 +
 +
    // Set control mode flags.
 +
    port_settings.c_cflag |= CSIZE;
 +
    port_settings.c_cflag |= CS6;
 +
    port_settings.c_cflag |= CS7;
 +
    port_settings.c_cflag |= CS8;
 +
    port_settings.c_cflag |= CREAD;
 +
    port_settings.c_cflag |= CLOCAL;
 +
 +
    // Set input mode flags.
 +
    port_settings.c_iflag |= IGNBRK;
 +
    port_settings.c_iflag |= IGNPAR;
 +
 +
    // Set control characters.
 +
    port_settings.c_cc[VKILL]    = 0x58;
 +
    port_settings.c_cc[VEOF]    = 0xAE;
 +
    port_settings.c_cc[VMIN]    = 0x01;
 +
    port_settings.c_cc[VSWTC]    = 0xBF;
 +
    port_settings.c_cc[VEOL]    = 0x83;
 +
    port_settings.c_cc[VREPRINT] = 0x28;
 +
    port_settings.c_cc[VDISCARD] = 0x63;
 +
    port_settings.c_cc[VWERASE]  = 0xB7;
 +
    port_settings.c_cc[VLNEXT]  = 0x68;
 +
    port_settings.c_cc[VEOL2]    = 0x2B;
 +
 +
    // apply the settings to the port
 +
    tcsetattr(fd_serial_8051, TCSANOW, &port_settings);
 +
 +
    return(fd_serial_8051);
 +
}
 +
 +
int send_command(int8 command)
 +
{
 +
    return write(fd_serial_8051, &command, sizeof(int8));
 +
}
 +
 +
int8 recv_event()
 +
{
 +
    int8 buf = 0;
 +
    read(fd_serial_8051, &buf, sizeof(int8));
 +
    if (read < 0)
 +
        return EV_RECV_EVENT_FAIL;
 +
 +
    return buf;
 +
}
 +
 +
int8 recv_event_timeout(time_t timeout_sec, suseconds_t timeout_usec)
 +
{
 +
    int8 n;
 +
    fd_set rdfs;
 +
    struct timeval timeout;
 +
 +
    FD_ZERO(&rdfs);
 +
    FD_SET(fd_serial_8051, &rdfs);
 +
 +
    timeout.tv_sec  = timeout_sec;
 +
    timeout.tv_usec = timeout_usec;
 +
 +
    n = select(fd_serial_8051 + 1, &rdfs, NULL, NULL, &timeout);
 +
 +
    // check if an error has occured.
 +
    if(n < 0)
 +
    {
 +
        return EV_RECV_EVENT_FAIL;
 +
    }
 +
    else if (n == 0)
 +
    {
 +
        return EV_RECV_EVENT_TIMEOUT;
 +
    }
 +
    else
 +
    {
 +
        read(fd_serial_8051, &n, sizeof(int8));
 +
        if (n < 0)
 +
            return EV_RECV_EVENT_FAIL;
 +
    }
 +
    return n;
 +
}
 +
 +
char * command2string(int8 command)
 +
{
 +
    switch (command)
 +
    {
 +
 +
        case CM_START_MOTOR:
 +
            return "CM_START_MOTOR";
 +
        case CM_STOP_MOTOR:
 +
            return "CM_STOP_MOTOR";
 +
        case CM_STATE_MOTOR:
 +
            return "CM_STATE_MOTOR";
 +
        default:
 +
            return "UNKNOWN";
 +
    }
 +
}
 +
 +
char * event2string(int8 event)
 +
{
 +
    switch (event)
 +
    {
 +
        case EV_MOTOR_IS_STOPED:
 +
            return "EV_MOTOR_IS_STOPED";
 +
        case EV_MOTOR_IS_RUNNING:
 +
            return "EV_MOTOR_IS_RUNNING";
 +
        case EV_START_MOTOR_FAIL:
 +
            return "EV_START_MOTOR_FAIL";
 +
        case EV_UNKNOWN_COMMAND:
 +
            return "EV_UNKNOWN_COMMAND";
 +
        case EV_RECV_EVENT_TIMEOUT:
 +
            return "EV_RECV_EVENT_TIMEOUT";
 +
        case EV_RECV_EVENT_FAIL:
 +
            return "EV_RECV_EVENT_FAIL";
 +
        default:
 +
            return "UNKNOWN";
 +
    }
 +
}
 +
 +
</syntaxhighlight>
 +
 +
Arquivo example.c
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include "serial_8051.h"
 +
 +
int main(int argc, char ** argv)
 +
{
 +
 +
    int angle;
 +
    char angle_s[5];
 +
 +
    printf("angles:\n");
 +
    printf("\t1 - 45\n\t2 - 90\n\t3 - 135\n\t4 - 180\n\t5 - 225\n\t6 - 270\n\t7 - 315\n\n");
 +
    printf("angle: ");
 +
    fgets(angle_s, sizeof(angle_s), stdin);
 +
 +
    if (sscanf(angle_s, "%d", &angle) != 1)
 +
    {
 +
        printf("invalid option: %s\n", angle_s);
 +
        return 1;
 +
    }
 +
    if (!(angle >= 1 && angle <= 7))
 +
    {
 +
        printf("invalid option: %d\n\n", angle);
 +
        return 1;
 +
    }
 +
 +
    if (open_serial_port("/dev/ttyS0") < 0)
 +
    {
 +
        perror("open_serial_port");
 +
        return 1;
 +
    }
 +
    else   
 +
    {
 +
        if (send_command(angle) < 0)
 +
            perror("send_command");
 +
 +
        close_serial_port();
 +
    }
 +
 +
    return 0;
 +
}
 +
</syntaxhighlight>
 +
 +
arquivo Makefile
 +
 +
<code>
 +
OBJS=serial_8051.o example.o
 +
 +
all: serial_8051
 +
 +
serial_8051: $(OBJS)
 +
gcc -g -o example $(OBJS)
 +
 +
@echo " +----------------------------+"
 +
@echo " |  API serial8051 compilado  |"
 +
@echo " +----------------------------+"
 +
 +
clean:
 +
rm -f example $(OBJS)
 +
 +
.c.o:
 +
gcc -c -g $<
 +
 +
</syntaxhighlight>
 +
 +
Baixe os 4 arquivos com os respectivos nomes serial_8051.c, serial_8051.h, example.c e Makefile. digite make e execute o arquivo gerado pelo compilador ./example
 +
 +
{{Collapse bottom}}
 +
 +
==Testes==
 +
* [http://youtu.be/UrHmRznOsSA demonstração simplificada]  {{#ev:youtube|UrHmRznOsSA#!}}
 +
 +
==Conclusão==
 +
 +
Objetivo desta primeira versão do projeto é decodificar as mensagens do motor de passo, e ter um controle básico enviando sinais para movimentar o motor em pré-determinados ângulos. Fica como sugestão em próximas versões do projeto, o controle de rotação, tal como intensidade, menor ângulo de passo ou  proteção contra desvio de rotação. Desta forma é necessário um motor de passo mais complexo, com saída para sensor de corrente e um driver com esta opção. Demais propostas são de criar maior robustez na comunicação entre PC e kit, contra erros de Tx, para isto é necessário implementar mensagens de tx no kit e padronizar tais mensagens de erro. Devido a quantidade de "0" é recomendado codificar as mensagens de modo manter o sincronismo durante a transmissão.

Edição atual tal como às 07h34min de 4 de dezembro de 2014

Descrição do projeto

O objetivo da primeira versão do projeto é gerar um conjunto básico de mensagens para controlar um motor de passo através de um kit didático da família de microcontroladores 8051 da ATMEL. Os comandos de acionamento do motor de passo são enviados por um computador através da interface RS-232 para o microcontrolador 8051. O kit está programado para decodificar as mensagens e habilitar imediatamente as portas que controlam um motor de passo unipolar. Este documento descreve a definição do modo de codificação e decodificação das mensagens de comando, processo implementação via hardware e software para comunicação serial. Adicionamos descritivos sobre características de circuitos com motores de passo, esquemas elétricos, procedimento de gravação do microcontrolador 8051 no kit para que se possa incrementar novas atualizações ao projeto.

Etapas

Especificação do projeto

Consiste na documentação postada em Wiki descrevendo as características, dispositivos utilizados, implementação e testes do projeto. A codificação das mensagens tende a ser o mais transparente possível entre o PC e o motor de passo. Fazendo com que o código no microcontrolador seja mais simplificado. A desvantagem desta implementação é grande quantidade de números "0" que são transmitidos, fazendo que se houver transmissão continua de um mesmo dado, haverá a possibilidade de perda do sincronismo. A codificação está definida na tabela abaixo. Uma palavra na interface serial é composto por 8 bits entre PC e Kit, respeitando o padrão UART. O objetivo final é decodificar para acionar o motor de passo de 4 fios rotacionando conforme o ângulo definido na tabela abaixo.

Tabela de codificação do motor de passo
ValorCódificação UARTdecodificação porta P2 8051Movimento
1000000010001
200000011001145°
300000010001090°
4000001100110135°
5000001000100180°
6000011001100225°
7000010001000270°
8000010011001315°

Se a tabela acima fosse implementada na ordem sugerida o motor de passo completaria uma volta.

  • Demais especificações:
    • As mensagens serão sentido PC (mestre)-> microcontrolador (escravo), ou seja, o microcontrolador apenas decodifica as mensagens e aciona o motor de passo.
    • Não haverá verificação de erro da palavra recebida no SBUFF.
    • Não haverá implementação de reenvio de palavra.
    • Ângulos de movimento pré-fixados em tabela.
    • Sem controle de desvio de rotação.
    • Controle somente de um motor de passo.
    • Rotação do motor é sentindo horário e anti-horário.
    • atraso entre os comandos de 1 segundo.
    • mensagens recebidas são tratadas como um interrupção.
    • Motor ficará em repouso enquanto não houve comando.
    • Configuração da interface serial: um bit de parada, nenhum bit de paridade, velocidade 9600bps.

Pesquisa

Consiste em avaliar e obter as ferramentas de hardware e aplicativos para desenvolvimento do projeto. Os principais recursos utilizados são: kit diático AT89Sxx,motor de passo 5V, driver ULN2003, computador com uma distribuição Linux, com porta RS232 e paralela. Aplicativos: Proteus, MCU8051IDE, Gtkterm, AEC ISP.

Descritivo dos aplicativos
  • MCU8051IDE: simulador completo para circuito da arquitetura C51. Acesso aos registradores especiais e memória interna, externa. O Simulador permite compilar e gerar um código .hex para gravação em um respectivo microcontrolador.
  • Gtkterm: aplicativo que lê e escreve dados na porta RS-232.
  • AEC_ISP: Gravador dedicado para gravar microcontroladores da família C51 pela porta paralela do computador.
  • Proteus: Simulador de circuito eletrônico com grande biblioteca de componentes.

Previsão de conclusão: 01/12/2014

Descritivo dos dispositivos
  • Motor de Passo

Motores de passos são dispositivos mecânicos eletro-magnéticos que podem ser controlados digitalmente através de um hardware específico ou através de softwares. Esses motores são usados em aparelhos onde a precisão é um fator importante. Existem vários modelos de motores de passos disponíveis no mercado que podem ser utilizados para propósitos como impressoras, robôs, câmeras de vigilância, máquinas industriais, brinquedos. Como os demais motores, o movimento dos motores de passo é gerado pela interação entre imãs permanentes e eletroímãs. Tipicamente os eletroímãs estão fixos e os imãs permanentes estão na parte móvel do motor. O que diferencia o motor de passo é que o movimento é controlado por pulsos, ativando e desativando os eletroímãs em uma determinada ordem. Dependendo de quais eletroímãs estão ativos, os imãs permanentes serão atraídos, colocando o motor em uma determinada posição. A ordem e a velocidade dos pulsos determina o movimento do motor.

  • Funcionamento Motor de passo


As portas de I/O do microntrolador 8051 usam tecnologia CMOS e tensão até 5.5V e 60uA de corrente. Para controlar o motor de passo é necessário um arranjo tipo drive para fornecer maior corrente ao motor. O esquema abaixo demostra a conexão da saída do microntrolador a um transistor de Potência. A base do microcontrolador colocará o transistor de potência saturado fazendo o motor ser alimentado. Se A base do transistor de potência ser 0V o transistor entrará na região de corte e o motor será desligado. Se a base do transistor de potência ser 5V, o transistor entrará na região de saturação, permitindo que o motor seja alimentado.

Drive.jpg

  • Circuito Motor de passo

Para agilizar o desenvolvimento optamos por utilizar o driver comercial ULN2003 e o motor de passo 28BYJ-48. O ULN2003 suporta até 500mA de corrente em cada porta. Este componente possui diodo catodo comum em cada porta para proteção contra picos de corrente provenientes do "start do motor.

Motor de passo 28BYJ-48.

Motor1.png



Conforme a derivada abaixo, descreve o pico de corrente gerada quando o motor é acionado. O pico de corrente ocorre devido ao período do pulso tender a zero e gerando uma alta frequência na bobina, no qual surgirá uma reatância indutiva e um tensão aumentando o fluxo de corrente para o driver. Para controlar este efeito é usado diodos em cada porta do driver ULN2003.


L= indutância.

t= tempo.

V=tensão.

Especificação motor de passo 28BYJ-48

De acordo com o datasheet sem o redutor (considerando somente o eixo principal), o motor precisa de 64 steps para completar uma volta. Neste caso cada step completo moveria 5,625° do eixo principal.

Conforme imagem abaixo o motor possui o eixo principal(1) e um sistema redutor(2), no qual, reduz em 1/64 o movimento de cada step. Portanto, o eixo principal(1) gera em cada step 5,625° de movimento e o sistema redutor(2) divide este angulo de passo em 1/64.

Mt1.png

Desta forma em nosso projeto, cada passo representa 0.08° e uma volta completa necessitará de 2048 steps completos e 4090 meio steps.

  • Diagrama driver ULN2003.

ULN2003.png

Especificação Driver ULN2003

O esquema elétrico do circuito entre o microcontrolador e o motor de passo :

Mpsetup.png

  • RS-232

Os comandos para acionar o motor são enviados pelo computador para o kit através de um cabo RS-232. É uma tecnologia de comunicação ponto a ponto e a mensagem é formada por palavras de 8 bits. Sua facilidade, custo e baixa complexidade de implementação permite seu uso em grande escala em projetos que não exigem alta taxa de transferência. Uma de suas limitações é a distância, não devendo ser superior a 10 metros. O microcontrolador oferece 4 modos de operação. Porém iremos forcar no modo de operação 2, no qual configura a interface para operar em modo full-duplex, sem bit de paridade. A velocidade da interface depende da frequência de oscilação do cristal montado no Kit. As equações para obter a taxa de bauds, são apresentadas na apostila do curso. O pinos obrigatórios para haver comunicação são o 2(TX), 3(RX) e 5(GND). A conexão na outra extremidade deve ter os sinais RX e TX invertidos na própria interface do computador ou senão invertido no conector do cabo.


Rs232 1.jpg
Rs232 3.jpg
Rs232 2.jpg



Disposição dos pinos e sinais no conector DB-9

  • Microcontrolador

Para o desenvolvimento deste projeto, utilizamos o Kit didático micro AT89Sxx disponível no almoxarifado do IFCS-SJ. O kit é composto por:

    • Microcontrolador ATMEL AT89S8252
    • 1 Interface UART
    • 6 botões push-button conectados na porta P3.
    • 8 botões push-button conectados na porta P2.
    • 8 Leds conectados na porta P1.
    • 4 conjuntos de barra pinos conectados nas portas P0, P1, P2 e P3.
    • Interface de gravação do microcontrolador.
    • Interface para display 2x16.

as interfaces do kit são apresentados na imagem abaixo:

Kit.png

  • Conector para gravaçãoo

A interface de gravação utiliza um cabo DB9/DB25. O conector DB9 do cabo é conectado na porta Gravação do kit (conector DB-9 Fêmea) e conector DB-25 do cabo conectado na porta paralela do computador. O esquema do cabo é representado na imagem abaixo:

Grav.jpg
  • Programa gravador

O programa de gravação do microcontrolador para Windows XP está disponível no link abaixo.

  • Gravação no Kit didático AT89Sxx

Passos para gravar um arquivo (.HEX) compilado no Kit.

  • 1- Conecte o cabo de gravação na porta "CABO PROG" e na porta paralela do computador.
  • 2- Com o kit desligado, abra o jumper J1.
  • 3- Mude a chave EXEC/PROG para Prog.
  • 4- Ligue o kit. (O LED PROG acenderá)
  • 5- Abra o programa AEC_ISP.
  • 6- Selecione a opção "Load to buffer flash the .hex data.
Prog1.png
  • 7- Em "input file:" Digite o caminho do arquivo .hex.
Prog2.png
  • 8- Pressione "ENTER" para confirmar.
Prog3.png
  • 9- Pressione "ESC" para retornar.
  • 10- Selecione a opção "Load display buffer flash". verifique se o arquivo foi carregado.
Prog4.png
  • 11- Pressione "ESC" para voltar.
Prog5.png
  • 12- Selecione a opção "program" e observe as mensagens durante de gravação.
Prog6.png
  • 13-Tela de confirmação que a gravação foi realizada com sucesso.
Prog7.png
  • 14- Desligue o kit.
  • 15- Mude a Chave EXEC/PROG de "PROG" para "EXEC".
  • 16- Feche o jumper J1.
  • 17- Retire o cabo prog.
  • 18- Ligue o kit(O LED prog ficará apagado). O programa gravado entrará em execução.

Fluxogramas

Fluxograma do laço principal

Fluxograma principal motor passos MIC2014-2.jpg

Fluxograma da sub-rotina interrupção serial

Fluxograma interrupcao serial motor passos MIC2014-2.jpg

Fluxograma da sub-rotina interrupção timer0

Fluxograma passo motor passos MIC2014-2.jpg

Desenvolvimento

Algorítmo 8051 (Escravo)

O programa no microcontrolador atua como escravo. Recebe as mensagens do PC(Mestre) e decodifica através de sinais na porta P2.

configuração dos registradores e temporizações

O código do microcontrolador é feito em Assembly. Inicia configurando o modo de operação do timer 1, serial e interrupções. É implementado a rotina de acionamento da porta P2, no qual esta conectado o motor de passo no endereço de interrupção 0x0023H. Em um eventual dado recebido pela interface RS 232, o byte em SBUF é enviado para o acumulador. O byte então, é decodificado e enviado o sinal correspondente para a porta P2.

  • TMOD, TH1, TH0, TL0

O Timer 1 é definido para operar em 8 bits, sendo utilizado para gerar taxa de TX na RS232 de 9600 bauds. ​​org 0000h MOV TMOD, #21H ;Configuração do Timer 1 em modo 2 (8 bits) e Timer 0 em modo 1 (16 bits). MOV TH1 , #0FDH ;Considerando um clock 11,052MHz e uma taxa de 9600 Baud rate. </syntaxhighlight>

  • SCON

MOV SCON, #50H ;configura o modo de operação da interface serial, e habilita o modo de recepção

mov ie, #10010000b ;habilita a interrupção do modo serial. Endereço da interrupção
0x0023H

</syntaxhighlight>

  • SBUF

o registrador IR atua como uma flag, sendo setado toda vez que recebe uma janela de bits validos no Registrador SBUF. Loop: JNB RI, Loop </syntaxhighlight>

  • Rotina 10ms

Rotina de atraso entre os comandos de step gerados pelo microcontrolador para o motor. Se o intervalo entre cada step por menor que 10ms, o motor poderá não responder corretamente.

DELAY_10ms:

       MOV TH0,  #0D8H;
       MOV TL0,  #0F0H;
       MOV TCON, #50H;

LOOP: JNB TF0, LOOP;

       CLR TF0;
       CLR TR0;
       RET;

</syntaxhighlight>

  • Rotina 50 ms

O Timer 0 é utilizado para contabilizar um bloco elementar de 50ms. Demais temporizações são referenciadas a partir deste bloco. DELAY_50ms:

      MOV TH0,#3CH;
      MOV TL0,#0B0H;
      MOV TCON,#50H;

LOOP: JNB TF0, LOOP;

      CLR TF0;
      CLR TR0;
      RET;

</syntaxhighlight>

  • Rotina de 1 segundo usa a rotina de delay_50ms como estrutura básica para gerar delay de 1 segundo:

MAIN:LCALL DELAY_1SEG; DELAY_1SEG: MOV B,#19D; AQUI: LCALL DELAY_50MS; DJNZ B, AQUI; RET;

DELAY_50ms:

       MOV TH0,#3CH;
       MOV TL0,#0B0H;
       MOV TCON,#50H;

LOOP: JNB TF0, LOOP;

       CLR TF0;
       CLR TR0;
       RET;

</syntaxhighlight>

Tratamento das interrupções e comandos para o motor de passo

Quando habilitado a interrrupção na porta serial e houver o SBUFF recebido um byte na RS-232, o registrador PC irá assumir o endereço para a posição 0023H e Stack Pointer será incrementado em dois onde será armazenado o endereço de programa da próxima instrução antes do desvio para a posição 0023H.

A partir da posiçã 0023H é zerado a flag de interrupção IR e o byte recebido em SBUF é movido para o acumulador A e então testado com os ângulos pré-determinados. Em caso de igualdade jogará na saída de P2 (P2.[0-3]) o respectivos sinais de acionamento.

org 0023H mov a,sbuf; clr ri SJMP TEST_ANG_45

TEST_ANG_45:

  CJNE   A, #ANG_45,TEST_ANG_90 ;ANG_45,TEST_ANG_90
  MOV    B, #63d;

ROT_45:

  lcall  passo;
  DJNZ   B,ROT_45;
  RETI

TEST_ANG_90:

  CJNE   A,#ANG_90,TEST_ANG_135

rot_90_1:

  MOV    B,#127d;

ROT_90:

  lcall  passo;
  DJNZ   B,ROT_90;
  RETI

TEST_ANG_135:

  CJNE   A,#ANG_135,TEST_ANG_180 
  MOV    B,#192d;

ROT_135:

  lcall  passo;
  DJNZ   B,ROT_135;
  RETI

TEST_ANG_180:

  CJNE   A,#ANG_180,TEST_ANG_225
  MOV    B,#255d;

ROT_180:

  lcall  passo;
  DJNZ   B,ROT_180;
  lcall  passo;
  RETI      

TEST_ANG_225:

  CJNE   A,#ANG_225,TEST_ANG_270
  MOV    R7,#5d;

ROT_225_1:

  MOV    B,#63d;

ROT_225:

  lcall  passo;
  DJNZ   B,ROT_225;
  DJNZ   R7,ROT_225_1
  RETI    

TEST_ANG_270:

  CJNE   A,#ANG_270,TEST_ANG_315
  MOV R7,#3d;

ROT_270_1:

  MOV    B,#127d;

ROT_270:

  lcall  passo;
  DJNZ   B,ROT_270;
  DJNZ   R7,ROT_270_1
  RETI   

TEST_ANG_315:

  CJNE   A,#ANG_315,TEST_ANG_360;
  MOV    R7,#7d;

ROT_315_1:

  MOV    B,#63d;

ROT_315:

  lcall  passo;
  DJNZ   B,ROT_315;
  DJNZ   R7,ROT_315_1
  RETI 

TEST_ANG_360:

  CJNE   A,#ANG_360,ERRO;
  MOV    R7,#8d;

ROT_360_1:

  MOV    B,#63d;

ROT_360:

 lcall  passo;
  DJNZ   B,ROT_360;
  DJNZ   R7,ROT_360_1
  RETI

erro:

MOV    P1, #00H;
lcall  delay_1SEG;
 MOV    P1,#0ffh;
  reti;

</syntaxhighlight>

Quando o SBUF é preenchido com um byte, uma interrupção é gerada, o PC é apontado para 0023H e o SP que inicialmente era 07 é incrementado em dois, assumindo 09. SP na posição 08 e 09 assume o endereço de programa da instrução main(ver código completo abaixo)jmp. Se SBUFF corresponder a algum dos testes de ângulo, haverá um novo incremento de SP devido a rotina de atraso ser acionada via LCALL. Desta forma notamos que interrupção e LCALL incrementam o SP, indicando o endereço da próxima instrução antes de entrar no função correspondente ao LCALL ou interrupção. Podemos observar que o SP é decrementado em 2 para RETI ou RET e o valor de PC assume o conteúdo SP antes de ser decrementado.


  • Rotina de um passo no motor.

Conforme datasheet um passo no motor tem 5,625°/64. Desta forma, se quisermos movimentar 45° são necessário 512 steps no total. Criamos uma subrotina chamada "passo" no qual gera 8 passos para movimentar 5,625°.Durante a simulação notamos um ganho de velocidade de 0,5 quando enviamos para a porta P2 apenas o passo inteiro (0001,0010,0100 e 1000) com o mesmo ângulo de 5,625° de passo.

passo:

  MOV    P2,#00000001b;
  lcall  delay_10ms;
  MOV    P2,#00000010b;
  lcall  delay_10ms;
  MOV    P2,#00000100b;
  lcall  delay_10ms;
  MOV    P2,#00001000b;
  lcall  delay_10ms;
  RET;

</syntaxhighlight>

Código completo Controle motor de passo

codigo Assembly do Projeto Motor de passo ATMEL 8051 (escravo)

org 0000;

ANG_45 EQU 01H; ANG_90 EQU 02H; ANG_135 EQU 03H; ANG_180 EQU 04H; ANG_225 EQU 05H; ANG_270 EQU 06H; ANG_315 EQU 07H; ANG_360 EQU 08h;

MOV TMOD, #21H MOV TH1, #0FDH MOV SCON, #50H mov ie, #10010000b

ljmp main;

org 0023H mov a,sbuf; clr ri SJMP TEST_ANG_45

TEST_ANG_45:

  CJNE   A, #ANG_45,TEST_ANG_90 ;ANG_45,TEST_ANG_90
  MOV    B, #63d;

ROT_45:

  lcall  passo;
  DJNZ   B,ROT_45;
  RETI

TEST_ANG_90:

  CJNE   A,#ANG_90,TEST_ANG_135

rot_90_1:

  MOV    B,#127d;

ROT_90:

  lcall  passo;
  DJNZ   B,ROT_90;
  RETI

TEST_ANG_135:

  CJNE   A,#ANG_135,TEST_ANG_180 
  MOV    B,#192d;

ROT_135:

  lcall  passo;
  DJNZ   B,ROT_135;
  RETI

TEST_ANG_180:

  CJNE   A,#ANG_180,TEST_ANG_225
  MOV    B,#255d;

ROT_180:

  lcall  passo;
  DJNZ   B,ROT_180;
  lcall  passo;
  RETI      

TEST_ANG_225:

  CJNE   A,#ANG_225,TEST_ANG_270
  MOV    R7,#5d;

ROT_225_1:

  MOV    B,#63d;

ROT_225:

  lcall  passo;
  DJNZ   B,ROT_225;
  DJNZ   R7,ROT_225_1
  RETI    

TEST_ANG_270:

  CJNE   A,#ANG_270,TEST_ANG_315
  MOV R7,#3d;

ROT_270_1:

  MOV    B,#127d;

ROT_270:

  lcall  passo;
  DJNZ   B,ROT_270;
  DJNZ   R7,ROT_270_1
  RETI   

TEST_ANG_315:

  CJNE   A,#ANG_315,TEST_ANG_360;
  MOV    R7,#7d;

ROT_315_1:

  MOV    B,#63d;

ROT_315:

  lcall  passo;
  DJNZ   B,ROT_315;
  DJNZ   R7,ROT_315_1
  RETI 

TEST_ANG_360:

  CJNE   A,#ANG_360,ERRO;
  MOV    R7,#8d;

ROT_360_1:

  MOV    B,#63d;

ROT_360:

 lcall  passo;
  DJNZ   B,ROT_360;
  DJNZ   R7,ROT_360_1
  RETI

erro:

MOV    P1, #00H;
lcall  delay_1SEG;
 MOV    P1,#0ffh;
  reti;

-----------------------------------

DELAY_1SEG: MOV r0,#02h; AQUI: LCALL DELAY_50MS; DJNZ r0, AQUI; RET;

DELAY_50ms:

       MOV TH0,  #3CH;
       MOV TL0,  #0B0H;
       MOV TCON, #50H;

LOOP: JNB TF0, LOOP;

       CLR TF0;
       CLR TR0;
       RET;

DELAY_10ms:

       MOV TH0,  #0D8H;
       MOV TL0,  #0F0H;
       MOV TCON, #50H;

LOOP2: JNB TF0, LOOP2;

       CLR TF0;
       CLR TR0;
       RET;
------------------------------------

passo:

  MOV    P2,#00000001b;
  lcall  delay_10ms;
  MOV    P2,#00000010b;
  lcall  delay_10ms;
  MOV    P2,#00000100b;
  lcall  delay_10ms;
  MOV    P2,#00001000b;
  lcall  delay_10ms;
  RET;
-------------------------------------

main: MOV TH1, #0FDH SETB TR1

  Loop1: jnb ri, Loop1
         jmp main;

</syntaxhighlight>

Algorítmo computador (Mestre)

O código de envio dos comandos é construído em C e utiliza bibliotecas do código fonte do kernel para controlar o interface RS 232.

o arquivo 8051.h provê especifica alguns apelidos para as palavras em binário dos comandos do motor, importa as bibliotecas do linux e define as funções utilizadas:

#include <stdio.h> // standard input / output functions
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitionss
#include <time.h>   // time calls
#include <sys/time.h>

enum commands
{
    CM_START_MOTOR = 0x0,
    CM_STOP_MOTOR  = 0x1,
    CM_STATE_MOTOR = 0X2
};

enum events
{
    EV_MOTOR_IS_STOPED  = 0x0,
    EV_MOTOR_IS_RUNNING = 0x1,
    EV_START_MOTOR_FAIL = 0x2
};

//apelidos para os comandos de movimento para o motor conforme o respectivo ângulo
enum angles
{
    angle_0   = 0x1, // 0001
    angle_45  = 0x3, // 0011
    angle_90  = 0x2, // 0010
    angle_135 = 0x6, // 0110
    angle_180 = 0x4, // 0100
    angle_225 = 0xC, // 1100
    angle_270 = 0x8, // 1000
    angle_315 = 0x9  // 1001
};

// definição das funções
int open_serial_port(char * serial_port); // le o dispositivo /dev/ttyS0 no qual esta a porta RS-232 do computador
int configure_port(int fd); // função para definir o modo de operação porta RS-232. definimos a /dev/ttyS0 para operar com taxa de  9600bps, 8 bits por palavra, Sem bit de paridade e com 1 bit de parada.

O arquivo 8051.c é o arquivo principal do programa. É por ele que a configuração da porta e envio dos comandos pela RS-232.

código de acesso à porta serial

int open_serial_port(char * serial_port) //recebe o caminho do dispositivo. Normalmente é chamado de ttyS0 ou ttyusbS0, localizado no /dev
{
    int fd; // Arquivo de descrição da porta serial
    //O_RDWR - define a porta como escrita e leitura.
    //O_NOCTTY -
    //O_NDELAY - Irá atender a qualquer dado recebido na RS-232.
    fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);

    if (fd_serial_8051  > -1)
    {
        fcntl(fd_serial_8051, F_SETFL, 0);
        configure_port();
    }
    return(fd_serial_8051);
}

Função de configuração da porta RS 232 do computador.

int configure_port(int fd)
{
     if (fd_serial_8051 < 0)
        return fd_serial_8051;

    // Save current settings.
    tcgetattr(fd_serial_8051, &port_settings_backup);

    // set baud rates.
    cfsetispeed(&port_settings, B9600); //define a velocidade da porta
    cfsetospeed(&port_settings, B9600);

    // Clear all settings.
    port_settings.c_cflag = 0x0;
    port_settings.c_oflag = 0x0; 
    port_settings.c_iflag = 0x0; 
    port_settings.c_lflag = 0x0;
    int i = 0;
    for (i = 0; i < NCCS; i++)
        port_settings.c_cc[i] = 0x0;

    // Set control mode flags.
    port_settings.c_cflag |= CSIZE;
    port_settings.c_cflag |= CS6;
    port_settings.c_cflag |= CS7;
    port_settings.c_cflag |= CS8;
    port_settings.c_cflag |= CREAD;
    port_settings.c_cflag |= CLOCAL;

    // Set input mode flags.
    port_settings.c_iflag |= IGNBRK;
    port_settings.c_iflag |= IGNPAR;

    // Set control characters.
    port_settings.c_cc[VKILL]    = 0x58;
    port_settings.c_cc[VEOF]     = 0xAE;
    port_settings.c_cc[VMIN]     = 0x01;
    port_settings.c_cc[VSWTC]    = 0xBF;
    port_settings.c_cc[VEOL]     = 0x83;
    port_settings.c_cc[VREPRINT] = 0x28;
    port_settings.c_cc[VDISCARD] = 0x63;
    port_settings.c_cc[VWERASE]  = 0xB7;
    port_settings.c_cc[VLNEXT]   = 0x68;
    port_settings.c_cc[VEOL2]    = 0x2B;

    // apply the settings to the port
    tcsetattr(fd_serial_8051, TCSANOW, &port_settings);

    return(fd_serial_8051);
}
</syntaxhighlight c>

função para testes de envio de comando pela RS232
<code>
int send_command(int8 command)
{
    return write(fd_serial_8051, &command, sizeof(int8));
}

O arquivo example.c é a parte do programa que interage com o usuário, requisita acesso a porta RS232 e envia os comandos ao kit passando paramentros para funções do arquivo serial_8051.c

int main(int argc, char ** argv)
{

    int angle;
    char angle_s[5];

    printf("angles:\n");
    printf("\t1 - 45\n\t2 - 90\n\t3 - 135\n\t4 - 180\n\t5 - 225\n\t6 - 270\n\t7 - 315\n\t8 - 360\n");
    printf("angle: ");
    fgets(angle_s, sizeof(angle_s), stdin);

    if (sscanf(angle_s, "%d", &angle) != 1)
    {
        printf("invalid option: %s\n", angle_s);
        return 1;
    }
    if (!(angle >= 1 && angle <= 8))
    {
        printf("invalid option: %d\n\n", angle);
        return 1;
    }

    if (open_serial_port("/dev/ttyS0") < 0)
    {
        perror("open_serial_port");
        return 1;
    }
    else    
    {
        if (send_command(angle) < 0)
            perror("send_command");

        close_serial_port();
    }

    return 0;
}

Código completo em C do mestre(PC)

codigo C do mestre(PC)

Arquivo serial_8051.h

#ifndef SERIAL_8051_H
#define SERIAL_8051_H

#include <unistd.h>   // UNIX standard function definitions
#include <fcntl.h>    // File control definitions
#include <errno.h>    // Error number definitions
#include <termios.h>  // POSIX terminal control definitions
#include <time.h>     // Time calls
#include <sys/time.h>

typedef unsigned char int8;

/*
 * Commands.
 */
enum commands
{
    CM_START_MOTOR = 0x00,
    CM_STOP_MOTOR  = 0x01,
    CM_STATE_MOTOR = 0X02
};

/*
 * Events.
 */
enum events
{
    EV_MOTOR_IS_STOPED    = 0x00,
    EV_MOTOR_IS_RUNNING   = 0x01,
    EV_START_MOTOR_FAIL   = 0x02,
    EV_UNKNOWN_COMMAND    = 0x03,
    EV_RECV_EVENT_TIMEOUT = 0xFE,
    EV_RECV_EVENT_FAIL    = 0xFF // errno is set with failure of cause.
};

/*
 * Step motor angles.
 */
enum angles
{
    angle_45  = 0x03, // 0011
    angle_90  = 0x02, // 0010
    angle_135 = 0x06, // 0110
    angle_180 = 0x04, // 0100
    angle_225 = 0x0C, // 1100
    angle_270 = 0x08, // 1000
    angle_315 = 0x09  // 1001
};

/*
 * Open and configure serial port (9600, 8N1).
 * return the new file descriptor,
 * or -1 if an error occurred (in which case, errno is set appropriately).
 */
int open_serial_port(char * serial_port);

/*
 * Close serial port.
 * returns zero on success.
 * On error, -1 is returned, and errno is set appropriately.
 */
int close_serial_port();

/*
 * Send a command to serial port (see enum commads).
 * On success, the number of bytes written is returned
 * (zero indicates nothing was written).
 * On error, -1 is returned, and errno is set appropriately.
 */
int send_command(int8 command);

/*
 * Receive a event from serial port. This function is blocking.
 * On success, the event is returned (see enum events).
 * On error, EV_READ_EVENT_FAIL is returned and errno is set appropriately.
 */
int8 recv_event();

/*
 * Same recv_event function, but this function received two params
 * timeout_sec = seconds and timeout_usec = micro seconds.
 * The timeout argument specifies the interval that read_event_timeout()
 * should block waiting for a serial port to become ready.
 * If timeout period has expired, the EV_READ_EVENT_TIMEOUT is returned.
 */
int8 recv_event_timeout(time_t timeout_sec, suseconds_t timeout_usec);

/*
 * Convert a command to string.
 * Return command in string format (see enum commands).
 * If the commands is unknown, "UNKNOWN" is returned.
 */
char * command2string(int8 event);

/*
 * Convert an event to string.
 * Return event in string format (see enum events).
 * If the event is unknown, "UNKNOWN" is returned.
 */
char * event2string(int8 event);

#endif

arquivo serial_8051.c:

#include "serial_8051.h"

// File descriptor of the serial port.
static int fd_serial_8051 = -1;

// Structure to store the port settings in.
static struct termios port_settings;

// backup of the current settings.
static struct termios port_settings_backup;

int open_serial_port(char * serial_port)
{
    fd_serial_8051 = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);

    if (fd_serial_8051  > -1)
    {
        fcntl(fd_serial_8051, F_SETFL, 0);
        configure_port();
    }

    return(fd_serial_8051);
}

int close_serial_port()
{
    tcsetattr(fd_serial_8051, TCSANOW, &port_settings_backup);
    return close(fd_serial_8051);
}

int configure_port()
{
    if (fd_serial_8051 < 0)
        return fd_serial_8051;

    // Save current settings.
    tcgetattr(fd_serial_8051, &port_settings_backup);

    // set baud rates.
    cfsetispeed(&port_settings, B9600);
    cfsetospeed(&port_settings, B9600);

    // Clear all settings.
    port_settings.c_cflag = 0x0;
    port_settings.c_oflag = 0x0; 
    port_settings.c_iflag = 0x0; 
    port_settings.c_lflag = 0x0;
    int i = 0;
    for (i = 0; i < NCCS; i++)
        port_settings.c_cc[i] = 0x0;

    // Set control mode flags.
    port_settings.c_cflag |= CSIZE;
    port_settings.c_cflag |= CS6;
    port_settings.c_cflag |= CS7;
    port_settings.c_cflag |= CS8;
    port_settings.c_cflag |= CREAD;
    port_settings.c_cflag |= CLOCAL;

    // Set input mode flags.
    port_settings.c_iflag |= IGNBRK;
    port_settings.c_iflag |= IGNPAR;

    // Set control characters.
    port_settings.c_cc[VKILL]    = 0x58;
    port_settings.c_cc[VEOF]     = 0xAE;
    port_settings.c_cc[VMIN]     = 0x01;
    port_settings.c_cc[VSWTC]    = 0xBF;
    port_settings.c_cc[VEOL]     = 0x83;
    port_settings.c_cc[VREPRINT] = 0x28;
    port_settings.c_cc[VDISCARD] = 0x63;
    port_settings.c_cc[VWERASE]  = 0xB7;
    port_settings.c_cc[VLNEXT]   = 0x68;
    port_settings.c_cc[VEOL2]    = 0x2B;

    // apply the settings to the port
    tcsetattr(fd_serial_8051, TCSANOW, &port_settings);

    return(fd_serial_8051);
}

int send_command(int8 command)
{
    return write(fd_serial_8051, &command, sizeof(int8));
}

int8 recv_event()
{
    int8 buf = 0;
    read(fd_serial_8051, &buf, sizeof(int8));
    if (read < 0)
        return EV_RECV_EVENT_FAIL;

    return buf;
}

int8 recv_event_timeout(time_t timeout_sec, suseconds_t timeout_usec)
{
    int8 n;
    fd_set rdfs;
    struct timeval timeout;

    FD_ZERO(&rdfs);
    FD_SET(fd_serial_8051, &rdfs);

    timeout.tv_sec  = timeout_sec;
    timeout.tv_usec = timeout_usec;

    n = select(fd_serial_8051 + 1, &rdfs, NULL, NULL, &timeout);

    // check if an error has occured.
    if(n < 0)
    {
        return EV_RECV_EVENT_FAIL;
    }
    else if (n == 0)
    {
        return EV_RECV_EVENT_TIMEOUT;
    }
    else
    {
        read(fd_serial_8051, &n, sizeof(int8));
        if (n < 0)
            return EV_RECV_EVENT_FAIL;
    }
    return n;
}

char * command2string(int8 command)
{
    switch (command)
    {

        case CM_START_MOTOR:
            return "CM_START_MOTOR";
        case CM_STOP_MOTOR:
            return "CM_STOP_MOTOR";
        case CM_STATE_MOTOR:
            return "CM_STATE_MOTOR";
        default:
            return "UNKNOWN";
    }
}

char * event2string(int8 event)
{
    switch (event)
    {
        case EV_MOTOR_IS_STOPED:
            return "EV_MOTOR_IS_STOPED";
        case EV_MOTOR_IS_RUNNING:
            return "EV_MOTOR_IS_RUNNING";
        case EV_START_MOTOR_FAIL:
            return "EV_START_MOTOR_FAIL";
        case EV_UNKNOWN_COMMAND:
            return "EV_UNKNOWN_COMMAND";
        case EV_RECV_EVENT_TIMEOUT:
            return "EV_RECV_EVENT_TIMEOUT";
        case EV_RECV_EVENT_FAIL:
            return "EV_RECV_EVENT_FAIL";
        default:
            return "UNKNOWN";
    }
}

Arquivo example.c

#include <stdio.h>
#include "serial_8051.h"

int main(int argc, char ** argv)
{

    int angle;
    char angle_s[5];

    printf("angles:\n");
    printf("\t1 - 45\n\t2 - 90\n\t3 - 135\n\t4 - 180\n\t5 - 225\n\t6 - 270\n\t7 - 315\n\n");
    printf("angle: ");
    fgets(angle_s, sizeof(angle_s), stdin);

    if (sscanf(angle_s, "%d", &angle) != 1)
    {
        printf("invalid option: %s\n", angle_s);
        return 1;
    }
    if (!(angle >= 1 && angle <= 7))
    {
        printf("invalid option: %d\n\n", angle);
        return 1;
    }

    if (open_serial_port("/dev/ttyS0") < 0)
    {
        perror("open_serial_port");
        return 1;
    }
    else    
    {
        if (send_command(angle) < 0)
            perror("send_command");

        close_serial_port();
    }

    return 0;
}

arquivo Makefile

OBJS=serial_8051.o example.o

all: serial_8051

serial_8051: $(OBJS) gcc -g -o example $(OBJS)

@echo " +----------------------------+" @echo " | API serial8051 compilado |" @echo " +----------------------------+"

clean: rm -f example $(OBJS)

.c.o: gcc -c -g $<

</syntaxhighlight>

Baixe os 4 arquivos com os respectivos nomes serial_8051.c, serial_8051.h, example.c e Makefile. digite make e execute o arquivo gerado pelo compilador ./example

Testes

Conclusão

Objetivo desta primeira versão do projeto é decodificar as mensagens do motor de passo, e ter um controle básico enviando sinais para movimentar o motor em pré-determinados ângulos. Fica como sugestão em próximas versões do projeto, o controle de rotação, tal como intensidade, menor ângulo de passo ou proteção contra desvio de rotação. Desta forma é necessário um motor de passo mais complexo, com saída para sensor de corrente e um driver com esta opção. Demais propostas são de criar maior robustez na comunicação entre PC e kit, contra erros de Tx, para isto é necessário implementar mensagens de tx no kit e padronizar tais mensagens de erro. Devido a quantidade de "0" é recomendado codificar as mensagens de modo manter o sincronismo durante a transmissão.