Ir ao conteúdo
  • Cadastre-se

Tutorial de PIC - Programação em Assembly


Posts recomendados

Lipextreme, você não está incomodando, pelo contrário.

Sua dúvida me mostrou que não expliquei bem as operações com os registradores.

No sistema decimal, cada dezena vale dez unidades e, por isso, quando vamos fazer, por exemplo, a operação 23 - 18, nós decrementamos a dezena do número 23 e somamos dez à sua unidade transformando-a em 13. Daí fazemos 13 - 8, o que resulta em 5. Depois subtraímos as dezenas (1 -1) resultando em 0. O resultado da subtração é portanto 5.

No sistema decimal cada algarismo varia de 0 a 9.

Repare que se decrementarmos o algarismo 3 oito vezes vamos chegar ao seguinte:

3 - 1 = 2

2 - 1 = 1

1 - 1 = 0

0 - 1 = 9

9 - 1 = 8

8 - 1 = 7

7 - 1 = 6

6 - 1 = 5

No caso dos registradores do programa, cada "unidade" do REG2B vale 256 "unidades" do REG1B.

Então se o REG1A for maior do que o REG1B, nós deveríamos decrementar REG2B e somar 256 ao REG1B para fazer a subtração.

Mas, nós não precisamos somar 256 ao REG1B, pois, no microcontrolador, a subtração ocorre pelo método do complemento de 2.

O resultado obtido é o mesmo que se o REG1B fosse decrementado um número de vezes igual ao valor do REG1A, como no exemplo que dei para o sistema decimal.

Vamos supor que o valor de REG1B fosse 128 e o de REG1A, 230.

Quando fazemos REG1B = REG1B - REG1A, através da instrução SUBWF, é como se o REG1B fosse decrementado 230 vezes. Após 128 vezes o seu valor seria igual a 0. Na 129ª vez, o seu valor seria igual a 255. Apos 230 vezes, o seu valor seria de 154.

Mas, repare que 154 é igual a (128 + 256) - 230

Só lembrando que o microcontrolador não decrementa o REG1B 230 vezes. Ele calcula o complemento de 2 do REG1A e depois soma (isso mesmo: soma) ao REG1B para efetuar a operação de subtração.

É por isso que não precisamos somar 256 ao REG1B antes. Foi isso que faltou explicar no tutorial e que gerou a sua dúvida. Espero que tenha sido claro.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Lipextreme, você não está incomodando, pelo contrário.

Sua dúvida me mostrou que não expliquei bem as operações com os registradores.

No sistema decimal, cada dezena vale dez unidades e, por isso, quando vamos fazer, por exemplo, a operação 23 - 18, nós decrementamos a dezena do número 23 e somamos dez à sua unidade transformando-a em 13. Daí fazemos 13 - 8, o que resulta em 5. Depois subtraímos as dezenas (1 -1) resultando em 0. O resultado da subtração é portanto 5.

No sistema decimal cada algarismo varia de 0 a 9.

Repare que se decrementarmos o algarismo 3 oito vezes vamos chegar ao seguinte:

3 - 1 = 2

2 - 1 = 1

1 - 1 = 0

0 - 1 = 9

9 - 1 = 8

8 - 1 = 7

7 - 1 = 6

6 - 1 = 5

No caso dos registradores do programa, cada "unidade" do REG2B vale 256 "unidades" do REG1B.

Então se o REG1A for maior do que o REG1B, nós deveríamos decrementar REG2B e somar 256 ao REG1B para fazer a subtração.

Mas, nós não precisamos somar 256 ao REG1B, pois, no microcontrolador, a subtração ocorre pelo método do complemento de 2.

O resultado obtido é o mesmo que se o REG1B fosse decrementado um número de vezes igual ao valor do REG1A, como no exemplo que dei para o sistema decimal.

Vamos supor que o valor de REG1B fosse 128 e o de REG1A, 230.

Quando fazemos REG1B = REG1B - REG1A, através da instrução SUBWF, é como se o REG1B fosse decrementado 230 vezes. Após 128 vezes o seu valor seria igual a 0. Na 129ª vez, o seu valor seria igual a 255. Apos 230 vezes, o seu valor seria de 154.

Mas, repare que 154 é igual a (128 + 256) - 230

Só lembrando que o microcontrolador não decrementa o REG1B 230 vezes. Ele calcula o complemento de 2 do REG1A e depois soma (isso mesmo: soma) ao REG1B para efetuar a operação de subtração.

É por isso que não precisamos somar 256 ao REG1B antes. Foi isso que faltou explicar no tutorial e que gerou a sua dúvida. Espero que tenha sido claro.

Sensacional, muito obrigado mais uma vez pela ajuda Mulder_Fox, ficou claríssima e de fácil entendimento a sua explicação^_^!

Link para o comentário
Compartilhar em outros sites

Bom dia Mulder_Fox.

Sinto-me muito grato por poder ter acesso ao seu tutorial.

No Mplab 8.92 estou simulando o seu código do Contador Crescente e Decrescente e funcionou corretamente enquanto ativo na opção do RUN do Mplab Sim, incrementando e decrementando perfeitamente. A única modificação feita foi nas saídas do PORTB onde alterei a ordem dos segmentos do display 7seg.

A dificuldade está no momento de simular utilizando a opção F7-Step Into que tanto o incremento quanto o decremento não funcionam. Tenho feito da seguinte forma:

-Inicio a simulação ativando Step into até o simulador apontar para "CALL TRATA_BOTAO_1";

-No Stimulus ponho RA0 e RA1 em nível High e então chamo Step into até voltar para a posição "CALL TRATA_BOTAO_1";

-Neste instante altero para Low o estado de pino RA0 e chamo Step into correndo o programa por duas vezes. A seta do simulador corre por todas as linhas e o incremento de UNIDADE não ocorre.

Mesmo com RA0 em Low e RA1 em High, o simulador não sente a mudança de estado em RA0. O mesmo ocorre para o decremento.

;-----------------------------------------------------------------------

Identifiquei o problema e ele estava entre o computador e a cadeira!!!

Acontece que para ocorrer um incremento em “UNIDADE' é necessário que se conclua com sucesso a verificação do debounce das teclas.

Fazendo isto na simulação em modo Run, o tempo consumido por cada instrução é quase idêntico ao do microcontrolador, mas quando se procura simular vendo o passo a passo em cada instrução, ainda que se vejas na máxima velocidade, ela será cerca de 1.000 a 1.000.000 de vezes mais lenta que em tempo real. Explicando mais claramente, a variável “UNIDADE” não se incrementa apenas teclando-se a tecla instalada na porta A, mas sim, depois de várias e várias repetições e, se cada instrução equivale a 1 us ou 2 us, imagine o quanto de linhas seriam necessárias a se executar Step into até então haver o tal incremento em “UNIDADE”! Imaginem 53460 ciclos ou 53,46 ms??

Então foi aí que comecei a aprender mesmo a depurar um código de forma decente, vendo cada linha de código atuando e conferindo para que serve cada uma das funções no Assembly (Assembler é outra coisa) conjuntamente com a janelinha Watch alterando manualmente os valores dos bits para acelerar a depuração.

Com tudo isso, aprendi a depurar decentemente um programa na própria IDE. Também aproveitei para comentar cada linha de código enquanto depurava. Muito didático ver o momento em que uma instrução do tipo “decfsz” pula a linha, essa instrução não espera a variável chegar a '0', mas sim a '1'!! Então faz o pulo e, aí sim, seta a variável para o '0'.

Link para o comentário
Compartilhar em outros sites

Olá, ricardospimentel. O prazer é meu.

O que ocorre é que o incremento somente irá ocorrer quando terminar o "debounce" do botão 1.

Para o "debounce" são usadas as variáveis DB1_BTA e DB1_BTB. A primeira inicia com o valor 255 e é decrementada toda vez que a subrotina "TRATA_BOTAO_1" for executada, estando o botão 1 pressionado. Quando ela chega a 0, é reiniciada com o valor 255 e a variável DB1_BTB é decrementada. Essa última é inciada com o valor 20. Quando DB1_BTB houver chegado a 0, o debounce acabou e, então o incremento ocorre.

Dessa forma, rotina "TRATA_BOTAO_1" deve ser executada 5.100 vezes (255 x 20) para que ocorra o incremento.

O mesmo vale para o botão 2 que faz o decremento. Para o botão 2 são usadas as variáveis DB2_BTA e DB2_BTB.

Você pode colocar "breakpoints" para acompanhar passo a passo usando o botão "Run".

Coloque um "breakpoint" na seguinte linha para acompanhar a partir do fim do "debounce":

INCF			UNIDADE,F				;INCREMENTA UNIDADE

Edit:

Opa! Estava escrevendo ao mesmo tempo que você!

Vejo que você já entendeu. De fato, cada vez que você clica no botão "Step Into" o simulador executa uma instrução.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Oi Mulder.

Acho que estavas escrevendo a resposta a mim enquanto eu também editava a minha própria resposta (para evitar o tal do flood).

Aprendi e ainda estou aprendendo muita coisa com o seu tutorial. Te agradeço ainda mais uma vez.

Coloquei ali na edição da minha resposta tudo o que se passou e acho que será útil para visitas futuras.

Muito obrigado à atenção!

Link para o comentário
Compartilhar em outros sites

Verdade, caro Mulder_Fox.

No momento sou um autodidata, só que pretendo utilizar tudo isso profissionalmente, claro irei atrás de cursos e estudar a sério, mas pelo o que eu ando vendo que numa faculdade o cidadão sai sem saber nada e, como vi ainda que tem gente saindo 'Dilma' faculdade sem saber operar um circuito prático por que só praticou no Proteus, prefiro entrar na faculdade já sabendo e então pegar meu certificado.

Vamos lá, tenho uma dúvida quanto ao funcionamento do TMR0, mas que pode se equivaler aos TMR1 e TMR2 também. Eu carrego o código no microcontrolador (acredito que todos eles trabalhem de forma similar, Atmel, Motorola, NXP, etc) e após o vetor de reset, ao menos no simulador, o cursor não atravessa o vetor de interrupções, mas tem algumas linhas ali até chegar no retfie. A minha dúvida é como funcionam as interrupções e qual o limite do código carregado por ali?

Sei que é possível fazer o código inteiro entre o vetor 4H (PIC16f628a) até retfie e não utilizar uma linha sequer dentro do loop principal até o end.

Parece que não há como simular o código operando ali, a não ser que se tivessem dois cursores no simulador, pois acredito que ali dentro do microcontrolador um código possa rodar independentemente do que estiver ocorrendo dentro do loop principal. Se é assim, creio que o mesmo possa ocorrer também com TMR1, TMR2, etc e poderia-se então elaborar 4 códigos diferentes dentro de apenas um microcontrolador, mas aí vem a seguinte pergunta:

Sabendo-se como funciona o pipeline do uC que com 4 ciclos de máquina consegue-se carregar, executar uma instrução e ainda incrementar o PC e ainda por cima se preparar para a próxima instrução. Com um código rodando nos timers, como seria isso?

Para desconfucionar minha dúvida: Acredito que o tmr0 fique executando um código segundo o que foi definido no prescaler em OPTION_REG e o loop principal pode consultar o que então está havendo por lá através de registradores como o STATUS Z ou os FILEs e até enviar comandos a ele através de registradores (File é um deles).

Desculpe se me embaralhei na pergunta, he-hê.

Link para o comentário
Compartilhar em outros sites

Bom dia, ricardospimentel.

O que eu entendo sobre o funcionamento do PIC16F628A é o seguinte:

Há a chamada Memória de Programa, onde são gravadas as instruções que compõe o programa. Essa memória começa no endereço 0000 e vai até o endereço 07FF, totalizando 2048 endereços.

Há o chamado Contador de Programa onde está escrito qual endereço da memória de programa será acessado para que seja executada a instrução ali contida. Após a execução de uma instrução, o Contador de Programa é incrementado automaticamente.

Após um reset, inclusive o power-on reset que ocorre quando o microcontrolador recebe alimentação, o valor do Contador de Programa é igual a 0000 e, portanto, a instrução a ser executada é a gravada no endereço 0000 da Memória de Programa. Após isso, o contador de programa é incrementado e, então, será executada a instrução contida no endereço 0001 da Memória de Programa, depois o contido no endereço 0002 e assim por diante.

As interrupções são provocadas pelos periféricos do microcontrolador, que são circuitos "independentes". Elas fazem com que o microcontrolador execute a instrução gravada no endereço 0004 da Memória de Programa, pois, escrevem esse valor no Contador de Programa (ao mesmo tempo que salvam o endereço da Memória de Programa da instrução que seria executada antes da interrupção para que ela possa ser executada após a saída da rotina de interrupção).

Então, se o nosso programa não fará uso de nenhuma interrupção, podemos gravá-lo a partir do endereço 0000, passando pelo 0004 sem problemas.

Se vamos usar alguma interrupção, devemos gravar a rotina de interrupção a partir do endereço 0004, já que esse é o endereço que será acessado na interrupção. O limite para o tamanho da rotina de interrupção é o tamanho da Memória de Programa.

Como as instruções do programa que referem-se à rotina de interrupção devem ser escritas a partir do endereço 0004 da Memória de Programa, o restante do programa é gravado depois do fim da rotina de interrupção, já que não caberia nos quatro endereços que existem antes (0000, 0001, 0002 e 0003). No endereço 0000 da Memória de Programa é gravada uma instrução GOTO que desvia o microcontrolador para depois do fim da rotina de interrupção.

O microcontrolador executa apenas uma instrução por vez. Ele não roda dois códigos ao mesmo tempo e não há diferença entre a execução de instruções no programa "principal" ou na rotina de interrupção. É só uma questão do local da Memória de Programa onde as instruções estão gravadas.

Você pode simular a execução do programa com o MPlab dentro da rotina de interrupção, colocando um breakpoint onde ela começa.

Os Timers não rodam código, eles são circuitos digitais (contadores) que funcionam independentemente da CPU do microcontrolador. Eles podem ser sincronizados com o mesmo sinal de clock da CPU ou com um externo.

Posso estar enganado sobre o que escrevi, mas é assim que creio que a "coisa" toda funciona.

Segue o link para o manual de referência da família 16 da Microchip:

http://ww1.microchip.com/downloads/en/DeviceDoc/33023a.pdf

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Como as instruções do programa que referem-se à rotina de interrupção devem ser escritas a partir do endereço 0004 da Memória de Programa, o restante do programa é gravado depois do fim da rotina de interrupção, já que não caberia nos quatro endereços que existem antes (0000, 0001, 0002 e 0003).

Olá Mulder e Ricardo (Desculpa atrapalhar o raciocínio de vocês), mas como grande apreciador da linguagem Assembly só queria dizer que finalmente eu consegui entender, graças a essa explicação, o porque de não colocar o programa entre os endereços 0000 e 0004, mais só para sanar a minha dúvida de vez, quer dizer que teoricamente não é realizado nada nesses endereços 0001, 0002, 0003, pois no próprio endereço 0000 o programa é desviado?

Link para o comentário
Compartilhar em outros sites

Sim, Lipextreme. Esses endereços da memória de programa não são usados quando fazemos o desvio no endereço 0000 para depois do fim da rotina de interrupção que começa no endereço 0004.

No MPlab, no menu "View", em "Disassembly listing" podemos ver o que será gravado em cada endereço da memória de programa:

Disassemblylist.png

Na imagem anterior, eu desenhei retângulos vermelhos ao redor dos endereços 0000 e 0004 da memória de programa. Repare que não será gravado nada nos endereços 0001, 0002 e 0003.

Comecei a escrever a parte 9 do tutorial.

Nela, vou mostrar como produzir três sinais PWM por software cujos ciclos ativos mudam regularmente, porém são diferentes entre si. Esses sinais PWM são usados para acionar um LED RGB. Como a cor aparente do LED é resultante da soma das três cores dele que são: vermelho, verde e azul, ela muda a cada intervalo de tempo, gerando um efeito visual muito interessante e que pode ser usado em decoração.

Edit:

vtrx, quando comecei a escrever esta resposta, você ainda não havia postado.

Sempre pensei que no PIC16F628A, a instrução GOTO ocupasse apenas um endereço de memória e que o destino do desvio constasse na própria instrução.

Repare na imagem a seguir que os dois últimos caracteres da instrução correspondem ao endereço de destino e que a instrução GOTO que será gravada no endereço 0009 só ocupa aquele endereço pois, no próximo 000A será gravada outra instrução:

Disassemblylist4.png

Link para o comentário
Compartilhar em outros sites

Sim, Lipextreme. Esses endereços da memória de programa não são usados quando fazemos o desvio no endereço 0000 para depois do fim da rotina de interrupção que começa no endereço 0004.

No MPlab, no menu "View", em "Disassembly listing" podemos ver o que será gravado em cada endereço da memória de programa:

Disassemblylist.png

Na imagem anterior, eu desenhei retângulos vermelhos ao redor dos endereços 0000 e 0004 da memória de programa. Repare que não será gravado nada nos endereços 0001, 0002 e 0003.

Mulder_Fox novamente agradeço o auxilio, realmente ficou mais claro ainda a questão da utilização desses 3 primeiros endereços utilizando a ferramenta "Disassembly List", ferramenta essa muito boa por sinal, eu havia lido um pouco sobre ela no seu tutorial, e agora ficou mais clara ainda a utilização da mesma, muito obrigado!

Comecei a escrever a parte 9 do tutorial.

Nela, vou mostrar como produzir três sinais PWM por software cujos ciclos ativos mudam regularmente, porém são diferentes entre si. Esses sinais PWM são usados para acionar um LED RGB. Como a cor aparente do LED é resultante da soma das três cores dele que são: vermelho, verde e azul, ela muda a cada intervalo de tempo, gerando um efeito visual muito interessante e que pode ser usado em decoração.

Sensacional, esse projeto vai ficar show, eu e a galera estamos contando os minutos para aprender um pouco mais da utilização e os truques dessa incrível linguagem!

A instrução GOTO ocupa os 3 endereços.

1 Para GOTO e mais 2 para formar o endereço de salto.

vtrx, muito obrigado , ficou nítida as informações!

Link para o comentário
Compartilhar em outros sites

Ola Mulder_Fox

Antes de mais nada eu queria agradecer. Pois sua apostila que me ensinou tudo que eu sei sobre assembly e mplab (conhecimento que eu não me imagino sem hoje em dia) ótima didática linguagem simples. Melhor que muita apostila paga que eu comprei.

Bom, vim aqui te perguntar se você ainda vai lançar aquela nona parte hehehe :);)

É por ansiedade e curiosidade também hehehehehe estou ansioso para ver aquele LED rgb em ação

Flowww :)

Link para o comentário
Compartilhar em outros sites

  • mês depois...
  • 2 meses depois...
  • 6 meses depois...

Uma curiosidade...

 

Quando se vai definir algumas coisas no pic, tipo qual oscilador vai usar, se tera proteção contra copia.. etc....  No datasheet do pic16F628A por exemplo diz que essa configuração tem que estar na linha 2007h da memoria de programa..

 

NO MPLAB você da o comando e ele ja faz sozinho, mas quando não se tem pc, quando tinha que programar com aqueles cartões perfurados, como se escolheria a posição 2007h para escrever a palavra de 14bits que contem essas definições?

 

também não entendi se esses 2007 é em hexa ou decimal... acho que eles colocariam 0x2007h se fosse hexa né? até porque 2007 em hexa equivale a posição 8199 da memoria, mas o PIC16F628A só tem 2K kkkkkkk

 

é so uma curiosidade!  :D  :D  :D  :D

Link para o comentário
Compartilhar em outros sites

Este texto foi copiado do "datasheet" do PIC16F628A:

 

"The user will note that address 2007h is beyond the

user program memory space. In fact, it belongs to the
special configuration memory space (2000h-3FFFh),
which can be accessed only during programming. See
“PIC16F627A/628A/648A EEPROM Memory
Programming Specification” (DS41196) for additional
information."
Link para o comentário
Compartilhar em outros sites

  • 3 semanas depois...

@Mulder_Fox

 

estou com um problema bem estranho, desde que baixei e instalei a ultima versão do MPLAB, a v8.92, o compilador não gera mais as configurações, eu coloco pra não setar pelo código, e sim pelos checkbox que tem em um menu, como sempre fiz, mas o compilador não gera mais as configurações no hexadecimal.... você sabe algo a respeito, como corrigir isto e tal.... já desinstalei e re-instalei, mas continua com o mesmo bug

 

vlewwwww

Link para o comentário
Compartilhar em outros sites

  • 3 semanas depois...

Pessoal!

Recebi uma Mensagem Privada do colega Ivan (eletronic).

Na mensagem ele solicita meu trabalho em assembly, sobre a gravação e leitura de uma memória externa com o protocolo I2C.

O trabalho é um pouco grande, pois é um tutorial que mostra como fazer e entender.

Junto com o trabalho, há rotinas para um mostrador LCD, que criei. O ponto positivo destas rotinas é que permitem que sejam escolhidos cada pino do LCD com cada pino do PIC. Não é necessário que os pinos ocupem a mesma porta.

Outro detalhe muito interessante é que criei também um arquivo com Macros, que facilita muito o trabalho. Tem quase tudo que se espera de Macros. Vale a pena dar uma olhada, pois serve para simplificar a programação em assembly.

 

http://www.4shared.com/zip/3nr0FxLSba/I2C.html

 

Divirtam-se

MOR_AL

Link para o comentário
Compartilhar em outros sites

  • 5 meses depois...

Aproveita que ta no assunto alguem poderia me informa qual melhor opção de escolha entre usar linguagem c ccs ou assembly pois não pretendo fica só no microcontroladores embarcado quero evolui para criação de sistema, estou perguntado porque sou iniciante alguem pode da está dica valeu

Link para o comentário
Compartilhar em outros sites

Visitante
Este tópico está impedido de receber novas respostas.

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

 

GRÁTIS: ebook Redes Wi-Fi – 2ª Edição

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!