Páginas: [1]   Ir para o fundo
  Imprimir  
Autor Tópico: Explicação do codigo do Bettencourt  (Lida 376 vezes)
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« em: Agosto 15, 2008, 22:11:25 »

Vamos lá então tentar explicar o código, que tem a ver com este tópico:

http://www.electronicapt.com/forum/index.php?topic=1430.0

Para começar vamos então mesmo para o fim do código...
vamos então para o PIC_CONFIG...


;************************************************
PIC_CONFIG ;*** Rotina de configuração do PIC ***
;************************************************

Em cima temos o endereço para o inicio desta rotina, que tem como função configurar o PIC

   clrf PORTA
   clrf PORTB
   clrf flag
   clrf tempo
   clrf STATUS

A instrução "clrf" tem como função limpar registos, neste caso vamos garantir que quando o programa começar todas as variáveis que nos interessam fiquem limpas

   bsf STATUS, RP0

"bsf" esta instrução põe a nível "1" um bit determinado por nós, de uma variável pré  definida também por nós. Neste caso vamos activar o bit "RP0" (bit 5) do registo STATUS, que tem como função activar o banco 1, da RAM.

   movlw b'11111111'

"movlw", esta instrução cria um determinado valor e coloca-o no registo W, neste caso vamos gerar o valor 255 em decimal, ou se preferirem FF em hexadecimal, ou 11111111 em binário, porque preferi em binário? Bem iram saber mais há frente...

   movwf TRISB

esta instrução "movwf", move determinado valor que se encontra no acumulador (registo W), para um registo há nossa escolha. Voltando a "11111111", preferi em binário "b", pois o que vamos definir é o Que vai se passar com o PORTO B, ou seja vamos definir quais os do PORTO B que são saída e os que são entrada... como vamos precisar de seis entradas para este circuito e como o PORTO B do chip até tem oito pinos, então é mais que natural usar este porto para as entradas, levando em conta que para a saída iremos precisar de quatro pinos e o PORTO A até tem cinco pinos, então a escolha é mais que acertada. Mas, voltando ao PORTO B... temos que levar em conta que para determinado pino de determinado PORTO ser entrada o seu bit correspondente, que se encontra no registo "TRIS" (registo do sistema que tem como função definir quais pinos são entrada e quais são saída) tem que estar a nível "1", se quisesse-mos que esse pino fosse saída ele teria que estar a nivel "0", assim defeniu-se que tobos os pinos do PORTO B são entradas, é preferível por-se pinos que não são usados como entradas, pois assim não iram provocar danos nenhuns ao circuito.

   movlw b'11110000'
   movwf TRISA

Neste caso definimos que os pinos de menor peso do PORTO A (RA0, RA1, RA2, RA3, RA4 e RA5) são saídas, agora podem perguntar o porque do registo que eu defini em "movlw b'11111111'", ser a oito bits, quando na realidade o PORTO A ter só cinco bits... bem o chip elimina neste caso o que está a mais, sem provocar danos.

   movlw b'11000111'
   movwf OPTION_REG

Aqui fomos configurar o registo OPTION REGISTER, e este ficou com as seguintes características começando do bit de maior peso para o de menor peso:
- bit7: pull-up off
- bit6: intrupt activo no flanco ascendente
- bit5: fonte de clock interna para o TMR0
- bit4: TMR0 activa no flanco ascendente do clock
- bit3: prescaler activado para o TMR0
- bit2 a bit0: "111" factor de divisão e 255 para o TMR0
Sobre os últimos três bits há que explicar o que se passa... o TMR0 (TIMER) é um registo do sistema de oito bits que tem como única função, contar. Sempre que o registo atinge o valor maximo (255) ele activa uma flag (T0IF) do INTCON (registo dos interrupts), indicando que o registo encheu e começou a contagem de novo, ora, como este registo soma sempre um bit ao seu registo quando recebe um impulso de clock, teríamos então 255us para este encher. Para se poder alterar esses valores usa-se o prescaler, que não passa de um divisor de clock, seja ele interno ou externo, no nosso caso usamos o interno como sabemos que o clock do chip é de 1us, e como coloca-mos  o prescaler a 255, teremos então assim que esperar 255us para que o TMR0 some um bit ao seu registo.

   bcf STATUS, RP0

"bcf", esta instrução é o inverso da "bsf", ora se a primeira activava um bit de um registo esta por sua vez coloca-o a nível "0"

   call TEMPO

"call" tal como o "goto" são instruções de salto, a diferença é que a primeira é sempre acompanhada por um "return", ou seja neste caso preciso... saltamos para a sub-rotina TEMPO o código vai lendo e executando até achar a instrução "return" que faz o programa saltar para a instrução logo a seguir ao "call", neste caso até é um return.

   return

Com este "return" saltamos para fora da rotina PIC_CONFIG
     
;***************************
END ;*** Fim do programa ***                 
;***************************
« Última modificação: Agosto 15, 2008, 23:44:39 por resinba » Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #1 em: Agosto 16, 2008, 12:56:30 »

Vamos aqui fazer um pequeno parêntesis, e falar sobre as instruções Assembler usadas nos PIC antes da família 18.
Na realidade o código Assembler é muito parecido com o usado no 8085, a diferença básica é que (no caso dos PICs) o código Assembler é composto por apenas 35 instruções...
Que são as seguintes:

Antes de mais há que levar em conta que existem dois tipos de registos (posições de memoria, variáveis) distintos, que são os registos gerais (tanto do sistema "SFR" e os "GPR"), e existe um registo especial  que se encontra por baixo da "ALU" que é o registo w, é nesne registo que todos os resultados de todas as operações caem, tendo depois quem programa definir para onde quer por este valor.
- OPERAÇÕES ORIENTADAS A REGISTOS (BYTES)

- ADDWF: soma o valor que se encontra no registo w, a um registo há nossa escolha e coloca-o ou no registo w ou no outro.
              exemplo:
                          addwf teste,f  ao usar o "f" ou "1", estou a dizer que quero o resultado no registo teste
                                              se tivesse usado o "w" ou o "0"  o resultado seria guardado no registo w
                          reg w=20
                          reg teste=10
                          resultado
                          reg w=20
                          reg teste =30

- ANDWF: faz a operação lógica "and" entre o registo w e um registo há nossa escolha, colocando o valor num local há nossa escolha.
              exemplo:
                         andwf teste, f
             
                         reg w=10111
                         reg teste=11000010
                         resultado
                         reg teste=10

- CLRF: limpa determinado registo.
               exemplo:
                          clrf meu
                     
                          reg meu=120
                          resultado
                          reg meu=0

-CLRW: limpa exclusivamente o registo w.
               exemplo:
                          clrw
                          reg w=55
                          resultado
                          reg w=0

- COMF: complemento de "f", ou seja com esta instrução vamos inverter os dados de determinado registo.
               exemplo:
                          comf ai,f
                          reg ai=1101
                          resultado
                          reg ai=11110010

- DECF: decrementa o registo "f", há nossa escolha, e coloca o resultado onde nós queremos.
               exemplo:
                          decf teste,f
                          reg teste=20
                          resultado
                          reg teste=19

-DECFSZ: decrementa o registo "f" e salta se for zero... problemas?Hein
              ok, não é facil há primeira... esta instrução é de decisão tal como umas quantas outras...
              vamos lá ver como ela trabalha...
              1º decrementa o registo (por exemplo registo menos).
              2º analisa o resultado:
                                            a) ser diferente de zero executa a instrução a seguir.
                                            b) se o resultado for zero salta a instrução  logo a seguir a "decfsz"
                                                e executa a instrução seguinte
               exemplo:
                          decfsz tempo, 1
                                goto LOOP_DELAY_1
                                bsf flag,6

Neste caso vamos decrementar o registo tempo (tempo=tempo-1), se o valor de tempo for maior que zero a instrução a ser executada será "goto LOOP_DELAY_1", mas se o resultado for zero a instrução a ser executada será então "bsf flag,6".

- INCF: incrementa o registo "f", neste caso vamos incrementar um bit ao registo (reg teste=reg teste+1).
               exemplo:
                          incf teste,f
                          reg teste=10
                          resultado
                         reg teste=11

-INCFSZ: incrementa o registo "f" salta se o valor for zero, esta instrução é exactamente o oposto
             há "defsz", pois se "decfsz" faz (registo=registo-1), a instrução "infsz" faz (registo=registo+1)
                exemplo:
                           incfsz tempo, 1
                                  goto LOOP_DELAY_1
                                  bsf flag,6

Neste caso o registo vai enchendo graças a (reg=reg+1), não esquecer que isto só acontece uma vez por turno e sempre que a instrução de decisão é executada, pelo menos uma das outras duas tem que ser, isto porque neste caso se saltarmos para "goto LOO_DELAY_1" nunca iremos executar a "bsf flag,6", agora se esta parte do código estive-se de outra maneira...
                           
                            incfsz tempo, 1
                                     bsf flag,6
                                     goto LOOP_DELAY_1
                           
Neste caso se o valor de "tempo" for diferente de zero, a próxima instrução a ser executada é "bsf flag,6" indo logo a seguir a instrução "goto LOOP_DELAY_1".

- IORWF: OR inclusivo entre o registo W e o registo f.
               exemplo:
                          iorwf teste,w
                          reg w=11111001
                          reg teste=10101010
                          resultado
                          teste=10101010
                          w=11111011
hehehe... desta vez para os fintar mandei o resultado para o registo w

- MOVF: move o registo "f", para dentro dele mesmo ("f" ou"1"),ou para o registo w ("w" ou "0"), quando se
            a primeira possibilidade é para ver se o registo está a zero ou tem algum valor no seu interior...
            pois se este estiver  a zero ele irá afectar  a flag "zero" do registo STATUS. No segundo caso
            vamos apenas movimentar o seu valor para o registo w.

               exemplo:
                          movf teste,f
                          reg STATUS *****0**
                          reg teste=0
                          resultado
                          reg teste=0
                          reg STATUS *****1**

                          ou ainda
                          movf teste,w
                          reg w=0
                          reg teste=21
                          resultado
                          reg w=21
                          reg teste=21

- MOVWF: move o valor que se encontra no registo w para um registo há nossa escolha.
               exemplo:
                             movwf teste
                             reg w=12
                             reg teste=45  (atenção que poderia ser outro valor qualquer)
                             resultado
                             reg w=12
                             reg teste=12

- NOP: sem operação, esta instrução faz exactamente isso "nada", tem como função base "queimar"
           um ciclo maquina.
               exemplo:
                             nop
                             resultado
                             um ciclo maquina foi gasto sem fazer nada.

-RLF: roda pela esquerda através do bit carry. A ideia é simples imaginem um registo de oito bits, e
        pretendem movimentar o registo, assim com esta instrução podem no faze-lo sem perder
        informação, pois o sistema vai ao registo STATUS pedir "emprestado" a flag carry para não se 
        perder informação.
        exemplo:
                      rlf teste,w
                      reg w=0
                      reg teste=1110 0110
                      carry=0
                      resultado
                      reg teste=1110 0110
                      carry=1
                      reg w=1100 1100

- RRF:  roda pela direita através do bit carry. A ideia é simples imaginem um registo de oito bits, e
        pretendem movimentar o registo, assim com esta instrução podem no faze-lo sem perder
        informação, pois o sistema vai ao registo STATUS pedir "emprestado" a flag carry para não se 
        perder informação.
        exemplo:
                      rrf teste,w
                      reg w=0
                      reg teste=1110 0110
                      carry=0
                      resultado
                      reg teste=1110 0110
                      carry=0
                      reg w=01110011

- SUBWF: subtrai o registo w ao registo f, e coloca o resultado ou no registo w ou no registo f,
             (reg f-reg w=resultado).
               exemplo:
                          subwf teste,f
                          reg w=10
                          ref teste=30
                          resultado
                          reg teste=20
                          reg w=10

- SWAMPF: troca os nibles (conjuntos de 4bits), nos registos.
               exemplo:
                          swampf teste,1
                          reg teste=11110000
                          resultado
                          reg teste=00001111

- XORWF: OR esclusivo, entre o registo w e outro há nossa escolha.
               exemplo:
                          xorwf teste,1
                          reg teste=10101111
                          reg w=10110101
                          resultado
                          reg w=10110101
                          reg teste=11010
« Última modificação: Agosto 17, 2008, 19:05:22 por resinba » Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
bettencourt
Moderador Global
Chip
*****
Offline Offline

Mensagens: 608



« Responder #2 em: Agosto 16, 2008, 21:17:33 »

um pouco confuso a assim a primeira.... Mas com o tempo vai lá  Grin Grin
Registado
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #3 em: Agosto 16, 2008, 23:08:40 »

Bem ainda não comecei a explicar o código, mas olha conta lá os problemas com código para se poder alterar.
Outra coisa, vou continuar a actualizar o campo que se encontra em cima sobre as instruções, e quando tiver concluído avanço então para a explicação do código.
Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #4 em: Agosto 18, 2008, 13:34:49 »

OPERAÇÃO ORIENTADA A REGISTOS (BITS)

Nesta secção vamos saber como analisar e alterar bits específicos dos registos.

- BCF: Limpa determinado bit de determinado registo.
               exemplo:
                             bcf teste,5
                             reg teste=11111111
                             resultado
                             reg teste=11011111

- BSF: Neste caso activa determinado bit de determinado registo
               exemplo:
                            bsf teste,6
                            reg teste=10010111
                            resultado
                            reg teste=11010111

Agora vêem duas instruções parecidas com "decfsz" e "incfsz", pois são instruções de decisão.

- BTFSC: testa determinado bit de determinado registo e salta se for zero.
               exemplo:
                            btfsc teste,5
                                    goto SALTO_A
                                    goto SALTO_B
neste caso se o bit(5) do registo teste for "1", o programa executa a instrução "goto SALTO_A". Agora se o mesmo bit estiver a "0", então o programa salta a instrução "goto SALTO_A" e executa então a instrução "goto SALTO_B".

- BTFSs: testa determinado bit de determinado registo e salta se for um.
               exemplo:
                            btfss teste,5
                                    goto SALTO_A
                                    goto SALTO_B
neste caso se o bit(5) do registo teste for "0", o programa executa a instrução "goto SALTO_A". Agora se o mesmo bit estiver a "1", então o programa salta a instrução "goto SALTO_A" e executa então a instrução "goto SALTO_B".

Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #5 em: Agosto 18, 2008, 14:12:55 »

OPERAÇÕES DE CONTROLO E DE LITERAIS

- ADDLW: soma o literal ao registo w.
               exemplo:
                            addlw d'10'
                            adiciona o literal com valor "10" em decimal ao registo w
                            reg w=20
                            resultado
                            reg w=30

- ANDLW: faz a operação logica "AND" entre o literal e o registo w .
               exemplo:
                            andlw b'11001101'
                            reg w=111111111'
                            resultado
                            reg w=11001101

-CALL: com esta instrução podemos chamar uma sub-rotina, executamos esse sub-rotina, e ao
          encontrar a instrução "return" o programa salta para a linha logo a segir há instrução "call".
               exemplo:
                            ...
                            call LOOP
                            bcf teste,4
                            ...
Quando o programa chega à instrução "call LOOP", o program counter memoriza a posição do programa, e salta para a sub-rotina LOOP, que poderia ser a seguinte:

LOOP
       btfss teste,4
               return
               bsf PORTB,1
      return
assim, se o bit(4) do registo teste fosse "0", a sub-rotina saltava logo para... (já vou explicar). Agora, se o bit(4) fosse "1" o programa iria por o bit(1) do porto b a nivel "1" e iria saltar então...
Bem, aqui entra então o return, assim sempre que na sub-rotina LOOP apanhar a instrução return, o program counter vai buscar a posição memorizada anteriormente e soma-lhe "1", que irá então saltar para a posição "bcf teste,4".

NOTA: a instrução "call" não pode ser executada mais de oito vezes dentro de sub-rotinas, ou seja não podem "chamar" mais de oito sub-rotinas com a instrução "call", pois o stack pointer só tem oito posições de memoria para guardar a informação, indo assim a nona cair sobre a primeira posição e por ai fora.

- CLRWDT: limpa o watchdog timer, e limpa também o prescaler.
               clrwdt
               reg WDT=?
               resultado
               reg WDT=0
               WDT prescaler=0 (encontra-se no OPTION_REG)
               TO=1                    (encontra-se no STATUS)
               PD=1                    (encontra-se no STATUS)

-GOTO: esta instrução tem parecenças com o "call", sem a agravante de não se poder usar mais de oito vezes interligadas. Mas, com a agravante que teremos de usar outra vez a quando de uma sub rotina estar terminada, pois ela não faz o salto de retorno.
               exemplo:
                            ...
                            goto LOOP
                   LOOP_2
                            ...
quando o programa chegar a "goto LOOP" ele salta para ai e executa o que vem a seguir, que poderia ser algo assim...

                    LOOP
                             btfss teste,0
                                      goto LOOP_2
                                      ...
o porgrama ao chegar a LOOP vai testar o bit(0) do registo teste, se este for zero salta então para LOOP_2, continuando o programa a partir desse ponto, caso contrario o programa salta a instrução "goto LLOP_2" e continua a executar as instruções que se seguem em LOOP.

- IORLW:  faz a operação logica "OR" entre o literal e o registo w .
               exemplo:
                            andlw b'11001101'
                            reg w=11110111
                            resultado
                            reg w=11110111

- MOVLW: move determinado valor para o registo w.
               exemplo:
                             movlw d'21'
                            reg w=?
                            resultado
                            reg w=21

- RETFIE: "return" de um interrupt. Quando o nosso programa está preparado para interrupts, esta
               instrução deve ser usada, pois quando existe um interrupt, o sistema salta automaticamente
               para a posição 0x0004, e convem, depois de ele executar a sub-rotina de interrupt, apanhar
               um "retfie" para voltar há rotina principal.
               exemplo:
                             org 0x0004
                                             movf SATUS,0
                                             movwf var_status,1
                                             ...
                                             retfie
Eis uma possível sub-rotina de interrup, quando a instrução chegar a "retfie", o programa salta para a rotina principal, e activa novamente o "GIE" que se encontra no registo INTCON.

- RETLW: return com literal, quando a sub-rotina chegar a "retlw", ele volta para a rotina principal, e
              carrega no registo w o literal.
               exemplo:
                             ...
                             retlw d'123'
                             reg w=?
                             resultado
                             reg w=123

- RETURN: esta já sabemos, é usada para as sub-rotinas normais em conjunto com a instrução "call".

- SLEEP: faz com que o chip entre em modo de standby, para poupança de energia.

- SUBLW: faz a subtracção de um literal com o registo w.
               exemplo:
                             sublw d'23'
                             reg w=12
                             resultado
                             reg w=11

- XORLW:  faz a operação lógica "XOR" entre o literal e o registo w .
               exemplo:
                            andlw b'11001101'
                            reg w=11110111
                            resultado
                            reg w=111010

Assim, termino a explicação sobre as instruções de assembler para este tipo de chip.
no próximo topico começo então a explicar o código.
« Última modificação: Agosto 18, 2008, 22:57:01 por resinba » Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #6 em: Agosto 19, 2008, 10:13:27 »

A EXPLICAÇÃO DO CÓDIGO

bem, quando peguei no projecto, levei certos dados em consideração, dados esses que me iriam ajudar a desenvolver o código, em que consistiam no seguinte:

- 6 entradas formadas por interruptores.
- 4 saidas.

Com estes dados iniciais, e levando em conta o tipo de chip, associei logo as entradas ao PORTO B (pois é formado por oito entradas/saídas), e as saídas ao PORTO A (tem cinco entradas/saídas), não o fiz por ser obrigatório ser assim, pois um PORTO pode ter pinos como entradas e pinos como saídas, mas, para facilitar o código, optei por fazer dessa maneira.

Quanto há velocidade, como esta não é crucial optei pelos 4MHz, e isso, porque depois nos cálculos de temporização, o trabalho  ser facilitado, pois o ciclo maquina é nos dado da seguinte maneira (1/(fclk/4)), assim como a frequência de clock é de 4MHz e como esta internamente é dividida por quatro, iremos ter então 1MHz, que o seu inverso dá então 1us, que belo numero para fazer contas, pois como cada instrução leva 1us a ser feita, há excepção dos "call" e dos "goto" que usam dois ciclo maquina, os cálculos tornam-se mais fáceis.

Pronto, com estes dados pode então partir para o passo seguinte... esse passo é muito importante para quem usa entradas do tipo ligado/desligado, mas por partes mecânicas (interruptores, botões de pressão e relés). O Problema em sí chama-se "DEBAUCING", e consiste basicamente num sem números de impulsos, que dois contactos metálicos fazem por "selfs" (faiscas), durante o período que estes se estão ou a aproximar ou a afastar um do outro, isso para um micro controlador é atrofiante, pois onde ele devia de estar há espera de um impulso, leva é com "carradas" deles.
Para se resolver esse problema há varias técnicas, desde as formadas por hardware (uso de condensadores, ou mesmo circuitos lógicos), ou pelas formadas por software (uso de rotinas de delay), que é o caso desta.
Levei em linha de conta que os botões eram acionados por uma pessoa, e assim sendo sabia que o período mínimo de debaucing é de 8ms, período mínimo de tempo que levamos desde que começamos a premir no botão e até ele estar completamente premido. Para facilitar usei como valor de delay 50ms, que para um Humano continua a ser extremamente rápido, e como tinha que usar outro tipo de delay (5s), que mais tarde foi reduzido para 1,5s, os 50ms caiam que nem "gingas".

No caso desta montagem o programa analisa as entradas, uma a uma "btfss PORTB,x", em que o valor "x" varia entre o valor "0" e o valor "5" (correspondentes aos PORTOS B (0 a 5), sempre que uma entrada é activada e depois de confirmada o programa irá saber qual foi a entrada activada e envia para a saída o valor correspondente em código BCD.
« Última modificação: Agosto 19, 2008, 10:31:38 por resinba » Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #7 em: Agosto 19, 2008, 11:17:30 »

O COMEÇO E A CONFIGURAÇÂO

 list      p=16F84A            ; Aqui listamos a directiva que define o micro-controlador
   #include <p16F84A.inc>        ; Aqui importamos as definições das variáveis internas do chip

   __CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC
;    protecção do codigo (CP) OFF, watchdog timer (WDT) OFF, Power-up Timer PWRT) ON, oscilador do tipo HS





;***** VARIABLE DEFINITIONS
tempo        EQU     0x0C        ;definição da posição de memoria (RAM) para a variável tempo
flag   EQU     0x0D        ; definição da posição de memoria (RAM)para a variável flag








;**********************************************************************
      ORG     0x000             ;Posição da memoria flash para o inicio do programa
        call PIC_CONFIG        ; chamamos a sub-rotina PIC_CONFIG, onde vamos definir certos parâmetros para o chip
      goto    MAIN              ; salta para a rotina principal

Bem a parte que se segue foi um copy/paste do que já tinha dito.

;************************************************
PIC_CONFIG ;*** Rotina de configuração do PIC ***
;************************************************

Em cima temos o endereço para o inicio desta rotina, que tem como função configurar o PIC

   clrf PORTA
   clrf PORTB
   clrf flag
   clrf tempo
   clrf STATUS

A instrução "clrf" tem como função limpar registos, neste caso vamos garantir que quando o programa começar todas as variáveis que nos interessam fiquem limpas

   bsf STATUS, RP0

"bsf" esta instrução põe a nível "1" um bit determinado por nós, de uma variável pré  definida também por nós. Neste caso vamos activar o bit "RP0" (bit 5) do registo STATUS, que tem como função activar o banco 1, da RAM.

   movlw b'11111111'

"movlw", esta instrução cria um determinado valor e coloca-o no registo W, neste caso vamos gerar o valor 255 em decimal, ou se preferirem FF em hexadecimal, ou 11111111 em binário, porque preferi em binário? Bem iram saber mais há frente...

   movwf TRISB

esta instrução "movwf", move determinado valor que se encontra no acumulador (registo W), para um registo há nossa escolha. Voltando a "11111111", preferi em binário "b", pois o que vamos definir é o Que vai se passar com o PORTO B, ou seja vamos definir quais os do PORTO B que são saída e os que são entrada... como vamos precisar de seis entradas para este circuito e como o PORTO B do chip até tem oito pinos, então é mais que natural usar este porto para as entradas, levando em conta que para a saída iremos precisar de quatro pinos e o PORTO A até tem cinco pinos, então a escolha é mais que acertada. Mas, voltando ao PORTO B... temos que levar em conta que para determinado pino de determinado PORTO ser entrada o seu bit correspondente, que se encontra no registo "TRIS" (registo do sistema que tem como função definir quais pinos são entrada e quais são saída) tem que estar a nível "1", se quisesse-mos que esse pino fosse saída ele teria que estar a nivel "0", assim defeniu-se que tobos os pinos do PORTO B são entradas, é preferível por-se pinos que não são usados como entradas, pois assim não iram provocar danos nenhuns ao circuito.

   movlw b'11110000'
   movwf TRISA

Neste caso definimos que os pinos de menor peso do PORTO A (RA0, RA1, RA2, RA3, RA4 e RA5) são saídas, agora podem perguntar o porque do registo que eu defini em "movlw b'11111111'", ser a oito bits, quando na realidade o PORTO A ter só cinco bits... bem o chip elimina neste caso o que está a mais, sem provocar danos.

   movlw b'11000111'
   movwf OPTION_REG

Aqui fomos configurar o registo OPTION REGISTER, e este ficou com as seguintes características começando do bit de maior peso para o de menor peso:
- bit7: pull-up off
- bit6: intrupt activo no flanco ascendente
- bit5: fonte de clock interna para o TMR0
- bit4: TMR0 activa no flanco ascendente do clock
- bit3: prescaler activado para o TMR0
- bit2 a bit0: "111" factor de divisão e 255 para o TMR0
Sobre os últimos três bits há que explicar o que se passa... o TMR0 (TIMER) é um registo do sistema de oito bits que tem como única função, contar. Sempre que o registo atinge o valor maximo (255) ele activa uma flag (T0IF) do INTCON (registo dos interrupts), indicando que o registo encheu e começou a contagem de novo, ora, como este registo soma sempre um bit ao seu registo quando recebe um impulso de clock, teríamos então 255us para este encher. Para se poder alterar esses valores usa-se o prescaler, que não passa de um divisor de clock, seja ele interno ou externo, no nosso caso usamos o interno como sabemos que o clock do chip é de 1us, e como coloca-mos  o prescaler a 255, teremos então assim que esperar 255us para que o TMR0 some um bit ao seu registo.

   bcf STATUS, RP0

"bcf", esta instrução é o inverso da "bsf", ora se a primeira activava um bit de um registo esta por sua vez coloca-o a nível "0"

   call TEMPO

"call" tal como o "goto" são instruções de salto, a diferença é que a primeira é sempre acompanhada por um "return", ou seja neste caso preciso... saltamos para a sub-rotina TEMPO o código vai lendo e executando até achar a instrução "return" que faz o programa saltar para a instrução logo a seguir ao "call", neste caso até é um return.

   return

Com este "return" saltamos para fora da rotina PIC_CONFIG
     
;***************************
END ;*** Fim do programa ***                 
;***************************

A sub-rotina TEMPO tem como função básica carregar um determinado valor no registo tempo, tal como podem ver:

;********************************
TEMPO ;*** Rotina que gera 5s ***
;********************************
   movlw d'100'   ; carrega no registo w o valor 100 (em decimal)
   movwf tempo  ; carrega no registo tempo o valor que se encontra no registo w (100 em decimal)
   return             ; sai da sub-rotina TEMPO, e volta para a linha a seguir há linha "call TEMPO".
Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #8 em: Agosto 20, 2008, 23:27:59 »

O CENTRO DAS DECISÕES

Bem o nome diz tudo, é neste ponto que tudo é decidido, é aqui que se vai descobrir qual botão foi premido, se ele foi validado ou não, se passaram mais de 1,5s sem nada acontecer.
Tem que se levar em conta que depois do PIC_CONFIG, a rotina a seguir é o MAIN.
Bem, vou deixar-me de tretas e vou apresentar então esse parte.

;**********************************************   
TESTA_TECLA ;*** Rotina que testa as teclas ***
;********************************************** se o registo flag(0) estava a "0" então saltamos para este ponto que vai testar qual do botões foi premido

      btfss PORTB,0                   ;testa se o botão um foi premido
         goto UM                        ;se foi salta para a rotina UM
         btfss PORTB,1                ;testa se o botão dois foi premido
            goto DOIS                   ;se foi salta para a rotina DOIS
            btfss PORTB,2             ;testa se o botão tres foi premido
               goto TRES                ;se foi salta para a rotina TRES
               btfss PORTB,3          ;testa se o botão quatro foi premido
                  goto QUATRO       ;se foi salta para a rotina QUATRO
                  btfss PORTB,4       ;testa se o botão cinco foi premido
                     goto CINCO       ;se foi salta para a rotina CINCO
                     btfss PORTB,5    ;testa se o botão seis foi premido
                        goto SEIS       ;se foi salta para a rotina SEIS
                        bsf flag, 0       ;se nada acontecer activa a flag(0)
      goto MAIN                        ;salta para a rotina MAIN

;************************     
MAIN ;*** Rotina MAIN ***
;************************
   btfss flag, 0                ;a flag(0) vai indicar se alguma tecla foi premida e validada ou não
      goto TESTA_TECLA  ;se não foi salta para a rotina TESTA_TECLA
      bcf flag, 0                ; aconteça o que acontecer a flag(0) é limpa
   btfss flag,6                 ;a flag(6) indica se já passaram os 5s ou não
      call DELAY               ;se não executa a sub-rotina DELAY
      btfss flag,6              ;volta a testar a flag(6), não vá o diabo tece-las 
         goto MAIN            ;se o tempo (5s) ainda não passou salta de novo para o inicio da rotina MAIN
         call TEMPO           ;se o tempo (5s) já passou, então chama-mos a sub-rotina TEMPO
   clrf flag                       ;limpa todos os bits do registo flag
   clrf PORTA                   ;limpa a informação BCD que havia antes, fazendo com que no display fique a "0"
   goto MAIN                  ;salta novamente para o inicio da rotina MAIN
« Última modificação: Agosto 21, 2008, 11:55:04 por resinba » Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
bettencourt
Moderador Global
Chip
*****
Offline Offline

Mensagens: 608



« Responder #9 em: Agosto 21, 2008, 20:30:04 »

Tas a ter um gande tabalho a fazer isso... mas ta a ficar 5* Wink
Para mim ta a ser mais facil perceber o assembly do pic, visto tar a "mexer" na pratica com ele

Parabéns e Obrigado  Wink Grin Contente
Registado
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #10 em: Agosto 22, 2008, 10:27:57 »

Sabes isto é daquelas coisas que me dá gozo fazer...
E sempre que posso tento ajudar, quem precisa, pois também ade haver o dia que eu preciso, como se costuma dizer uma mão lava a outra. Grin Grin Grin
Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #11 em: Agosto 22, 2008, 11:02:09 »

CONFIRMAÇÃO DA TECLA PREMIDA E GERAÇÃO DE CÓDIGO BCD

Anteriormente, no "O CENTRO DAS DECISÕES", tinha se visto como se detectava se uma tecla tinha sido premida, agora vamos ver como se confirma como esse tecla foi premida, para alem disso também se irá ver como se gera o código BCD.

Mais um pequeno parêntesis... o código BCD (para quem não sabe), é um código do tipo binário, formado por quatro linhas, mas, com uma grande diferença... é que o código binário normal com quatro linhas conseguimos fazer contagens de 0 a 15 (B'0000' a B'1111'), mas o código BCD só conta de 0 a 9 (B'0000' a B'1001').

Vou só fazer a descrição da primeira rotina, pois todas as seis são exactamente iguais umas, às outras, alterando só os nomes das rotinas, dos LOOP's e do código BCD.
Bem, aqui vai o código:

;********************************** imaginemos, que a rotina anterior detectou que a tecla um foi premida, então salta
UM ;*** Rotina que gera o BCD 1 *** para este ponto via "goto UM", e começa a executar o código...
;**********************************
   call DELAY               ; primeiro "chama" a sub-rotina DELAY para fazer uma pausa de 50ms
   btfsc PORTB, 0        ;de pois dos 50ms terem acabado vamos ver se o botão um continua premido
      goto LOOP_UM_1  ;se não saltamos para LOOP_UM_1
      movlw d'1'            ; se sim carregamos o valor "1" no registo w
   movwf PORTA           ;agarramos no valor "1" e enviamos para o PORTO A, e já temos o "1" BCD na saida do PIC
LOOP_UM                   
   btfss PORTB,0          ;aqui testamos se o botão continua premido
      goto LOOP_UM      ;se sim continuamos neste LOOP interminavelmente
      call TEMPO            ;se não chamamos a sub-rotina TEMPO para azararmos o contador de segundos
   goto MAIN                ;saltamos para a rotina MAIN
LOOP_UM_1
   bsf flag, 0                ;activamos a flag(0), porque a tecla não foi validada, e assim fazemos uma contagem do
                                   ;período a OFF.
    goto MAIN               ;salta para MAIN

;************************************
DOIS ;*** Rotina que gera o BCD 2 ***
;************************************
   call DELAY
   btfsc PORTB, 1
      goto LOOP_DOIS_1
      movlw d'2'
   movwf PORTA
LOOP_DOIS
   btfss PORTB,1
      goto LOOP_DOIS
      call TEMPO
   goto MAIN
LOOP_DOIS_1
   bsf flag, 0
   goto MAIN

;************************************
TRES ;*** Rotina que gera o BCD 3 ***
;************************************
   call DELAY
   btfsc PORTB, 2
      goto LOOP_TRES_1
      movlw d'3'
   movwf PORTA
LOOP_TRES
   btfss PORTB,2
      goto LOOP_TRES
      call TEMPO
   goto MAIN
LOOP_TRES_1
   bsf flag, 0
   goto MAIN

;**************************************
QUATRO ;*** Rotina que gera o BCD 4 ***
;**************************************
   call DELAY
   btfsc PORTB, 3
      goto LOOP_QUATRO_1
      movlw d'4'
   movwf PORTA
LOOP_QUATRO
   btfss PORTB,3
      goto LOOP_QUATRO
      call TEMPO
   goto MAIN
LOOP_QUATRO_1
   bsf flag, 0
   goto MAIN

;*************************************
CINCO ;*** Rotina que gera o BCD 5 ***
;*************************************
   call DELAY
   btfsc PORTB, 4
      goto LOOP_CINCO_1
      movlw d'5'
   movwf PORTA
LOOP_CINCO
   btfss PORTB,4
      goto LOOP_UM
      call TEMPO
   goto MAIN
LOOP_CINCO_1
   bsf flag, 0
   goto MAIN

;************************************
SEIS ;*** Rotina que gera o BCD 6 ***
;************************************
   call DELAY
   btfsc PORTB, 5
      goto LOOP_SEIS_1
      movlw d'6'
   movwf PORTA
LOOP_SEIS
   btfss PORTB,5
      goto LOOP_SEIS
      call TEMPO
   goto MAIN
LOOP_SEIS_1
   bsf flag, 0
   goto MAIN

Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #12 em: Agosto 22, 2008, 11:42:54 »

SUB-ROTINAS TEMPO E DELAY

Estas duas sub-rotinas estão interligadas, e complementam-se uma há outra, uma porque quera os 50ms, e os 1,5s que são dados pela variável tempo.

Aqui está o codigo:

;********************************
TEMPO ;*** Rotina que gera 5s ***
;********************************
   movlw d'100'  ;move o valor d'100' para o registo w, com o valor 100, temos 5s, e com o valor 30, temos 1,5s
   movwf tempo  ;move o valor que se encontra no registo w para o registo tempo
   return             ;sai da sub-rotina

;**************************************************
DELAY ;*** Rotina que provoca um atraso de 50ms ***
;**************************************************
   clrf TMR0                           ;limpa o registo do temporizador (TMR0) 
   clrf INTCON                        ;limpa o registo INTECON, por causa da flagT0IF
   movlw d'60'                       ;move o valor d'60' para o registo w
   movwf TMR0                      ;move esse valor para o registo TMR0
LOOP_DELAY
   btfss INTCON, T0IF             ;testa o a flag T0IF do registo INTCON, que fica activa quando o TMR0 der OVERFLOW
      goto LOOP_DELAY           ;se não estiver activa salta para LOOP_DELAY
      decfsz tempo, 1              ;se estiver activa, decrementa a variavel tempo e "vê" se esta já chegou a zero
         goto LOOP_DELAY_1     ;se não chegou salta para LOOP_DELAY_1
   bsf flag,6                           ;se tempo=0, então activa a flag(6). O tempo passou dos 1,5s ou dos 5s!
LOOP_DELAY_1
   clrf INTCON                         ;limpa o INTCON
   return                                ;sai da sub rotina


Pronto o código está feito explicado, o tempo de concepção foi de alguns dias, mas, na realidade traduziu-se em cerca de 2 a 3 horas por dia durante um período de mais ou menos cinco dias.

Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
bettencourt
Moderador Global
Chip
*****
Offline Offline

Mensagens: 608



« Responder #13 em: Agosto 23, 2008, 21:13:51 »

começando por esta parte  pequena parte
   
goto LOOP_DELAY_1     ;se não chegou salta para LOOP_DELAY_1
   bsf flag,6                           ;se tempo=0, então activa a flag(6). O tempo passou dos 1,5s ou dos 5s!
LOOP_DELAY_1
   clrf INTCON                         ;limpa o INTCON
   return                                ;sai da sub rotina

quando faz o return ele vai para onde?
LOOP_DELAY
ou
goto LOOP_DELAY_1
Registado
resinba
Moderador Global
Cristal
*****
Offline Offline

Mensagens: 449


« Responder #14 em: Agosto 23, 2008, 21:38:28 »

Nop, sabes ele pode saltar para muitos sítios...

Tens que levar em linha de conta qual parte do programa invocou a instrução "call DELAY", imagina o "C", ele é composto por funções, e as funções para alem de outras coisas tem o nome, agoara imagina que estas dentro de uma função e invocas outra, então o programa salta para essa função, executa-a, e depois de terminada volta para a instrução seguinte que se encontra na 1ªfunção.

void multiplica()
{
...
} //quando terminada salta para a linha a seguir de onde foi invocada

Void main
{
...
multiplica (23); //neste ponto saltamos para a função multiplica
...
}

No nosso caso pode ser...

;********************************** imaginemos, que a rotina anterior detectou que a tecla um foi premida, então salta
UM ;*** Rotina que gera o BCD 1 *** para este ponto via "goto UM", e começa a executar o código...
;**********************************
   call DELAY               ; primeiro "chama" a sub-rotina DELAY para fazer uma pausa de 50ms
   btfsc PORTB, 0        ;Salta para este ponto

;************************************
DOIS ;*** Rotina que gera o BCD 2 ***
;************************************
   call DELAY
   btfsc PORTB, 1          ;ou para este

;************************************
TRES ;*** Rotina que gera o BCD 3 ***
;************************************
   call DELAY
   btfsc PORTB, 2          ;ou para este

;**************************************
QUATRO ;*** Rotina que gera o BCD 4 ***
;**************************************
   call DELAY
   btfsc PORTB, 3          ;ou para este

;*************************************
CINCO ;*** Rotina que gera o BCD 5 ***
;*************************************
   call DELAY
   btfsc PORTB, 4          ;ou para este

;************************************
SEIS ;*** Rotina que gera o BCD 6 ***
;************************************
   call DELAY
   btfsc PORTB, 5          ;ou para este

;************************     
MAIN ;*** Rotina MAIN ***
;************************
   btfss flag, 0                ;a flag(0) vai indicar se alguma tecla foi premida e validada ou não
      goto TESTA_TECLA  ;se não foi salta para a rotina TESTA_TECLA
      bcf flag, 0                ; aconteça o que acontecer a flag(0) é limpa
   btfss flag,6                 ;a flag(6) indica se já passaram os 5s ou não
      call DELAY               ;se não executa a sub-rotina DELAY
      btfss flag,6              ;ou ainda salta para esta

Como podes ver, sempre que existe um "call DELAY", o programa salta para a sub-rotina DELAY e quando esta estiver terminada, salta para a instrução logo a seguir há instrução "call" que chamou a sub-rotina.

Não sei se me consegui fazer entender...
« Última modificação: Agosto 23, 2008, 21:59:29 por resinba » Registado

Por favor, alterem o meu email no msn pois tenho outro email dedicado aos foruns, e vou bloquear todos os que se encontram no antigo email.
Páginas: [1]   Ir para o topo
  Imprimir  
 
Ir para: