Tengo problemas para escribir el código para hacer parpadear 6 LED de PORTC en el microcontrolador PIC16F690. Los LED deben parpadear como las luces de una pista.
Con el código que tengo actualmente, los LED están parpadeando en pares y fuera de servicio.
Aquí está el código que tengo actualmente:
list p=16F690
radix hex
include "P16F690.INC"
__config _WDT_OFF & _BOR_OFF & _PWRTE_ON & _INTOSCIO & _MCLRE_OFF
errorlevel -302
org 0
count equ 20 ;we're going to need two variables
count2 equ 21
call initial
call Blink
initial ; initialize registers
bsf STATUS, RP0
movlw B'00000000'
movwf TRISC
bcf STATUS, RP0
bsf STATUS, RP1
clrf ANSEL
clrf ANSELH
bcf STATUS, RP1
return
Blink ;flip the LED on or off
movlw B'00000001'
rlf PORTC,F
call Delay
call Blink
Delay ; waste time
movlw 0xFF
movwf count
movwf count2
Delayloop
decfsz count,f
goto Delayloop
decfsz count2,f
goto Delayloop
return
END
Hay algunos problemas con su código. Los señalaré en el orden en que creo que pueden ser el problema real aquí. Los que se encuentran en la parte inferior son solo consejos de mejores prácticas.
Nunca inicializas PORTC
. PORTC
Se desconoce el valor de en un restablecimiento de encendido. Tendrá que establecerlo en algún valor en su initial
rutina:
movlw 0x01
movwf PORTC
Esto lo configura para tener un LED encendido, que es probablemente lo que desea. Después de eso, con RLF
, comenzará a girar este bit para encender otros LED.
Puede ser que esta sea la idea detrás de estas líneas en blink
:
movlw B'00000001'
rlf PORTC,F
Sin embargo, la primera línea no tiene ningún efecto: se almacena 0b00000001
en W, pero W nunca se usa. RLF
toma el registro PORTC
, lo gira y lo almacena de nuevo en PORTC
- W no se usa. Además, ejecuta movlw B'00000001'
cada vez que recorre blink
. Incluso si esto se corrigiera, el valor de PORTC
nunca cambiaría porque se reinicia todo el tiempo: debe inicializar el puerto en la initial
rutina y cambiarlo en la blink
rutina.
Blink ;flip the LED on or off
movlw B'00000001'
rlf PORTC,F
call Delay
call Blink
Hay tres tipos diferentes de instrucciones para saltar en el código: GOTO
, CALL
y RETURN
.
Con GOTO
, simplemente ingrese el código, es tan fácil como eso.
Con CALL
, implementa GOTO
pero también empuja el contador del programa actual en la pila. Esta pila es un módulo de memoria LIFO (último en entrar, primero en salir) en el que, en este caso, se pueden almacenar ubicaciones de programas.
Con RETURN
, extrae el último elemento de la pila y salta a esa ubicación. Esencialmente, salta a la instrucción después de la última ejecución CALL
. Esto es lo que usa al principio donde llama Initial
y luego regresa y llama a Blink
.
La pila de este chip tiene ocho niveles, como se describe en la sección 2.3.2 de la hoja de datos . Eso significa que puede insertar un máximo de ocho ubicaciones de programas en esta pila. Después de eso, se sobrescribe el primer índice (la pila se implementa como un búfer circular). Básicamente, esto significa que no es posible volver a la primera CALL
instrucción y que el controlador saltará a otra posición. Esto puede causar muchos problemas inexplicables en software más avanzado.
Con el código que cité arriba, llamas continuamente Blink
, pero no hay RETURN
instrucciones. Esto significa que seguirás empujando cosas a la pila sin hacerlas estallar. La pila se sobrescribe todo el tiempo. Como no usas RETURN
allí, no importa tanto. Pero en esta situación, realmente debería usar GOTO
en lugar de CALL
:
Blink ;flip the LED on or off
movlw B'00000001'
rlf PORTC,F
call Delay
goto Blink
Como GOTO
no usa la pila, esto no es problema.
Has puesto un montón de cosas org 0
. Esta no es una buena práctica. Como se puede leer en la sección 2.1 de la hoja de datos , este chip tiene un vector de interrupción en org 4
. Esto significa que cuando ocurre una interrupción, el contador del programa saltará a la ubicación 4, casi directamente después de 0. Es por eso que normalmente implementamos solo una GOTO
instrucción en org 0
. Algo como esto:
org 0
goto start
org 4
retfie ; return from interrupt (alternatively you could have
; an interrupt handler here)
start:
; your main code...
Como ya mencioné en los comentarios: en la parte superior de su programa indicó radix hex
(y este también es el valor predeterminado). Debido a esto, puede usar EQU 20
para seleccionar el registro 0x20
. Sin embargo, es mucho más claro cuando solo usa EQU 0x20
o EQU 20h
. Cuando otros leen su código, no tienen que buscar la especificación radix o el valor predeterminado del ensamblador.
Tal vez ya sepa sobre esto, pero puede encontrarlo interesante: http://www.piclist.com/techref/piclist/codegen/delay.htm
Este es un generador de retraso que generará un retraso de un tiempo específico para usted. Su rutina de retraso es perfecta por lo que puedo ver ahora, pero si alguna vez está buscando algo que lo calcule exactamente, ¡aquí lo tiene!
Hay tanta mala programación en su ejemplo que una respuesta adecuada llevaría demasiado tiempo. Existe el modo absoluto, la configuración manual del banco, la ubicación fija de las variables, las suposiciones ocultas pero implícitas sobre la configuración del banco y el desbordamiento de la pila de llamadas. ¡Que desastre! Tal vez tenga tiempo para leer todo eso mañana.
Sin embargo, el código en BLINK es particularmente confuso y probablemente sea la fuente del problema. Ni siquiera puedo adivinar lo que crees que se supone que debe lograr la carga de 1 en W. La falta de comentarios es francamente irresponsable. En su descripción sobre el código, habla de varios LED, pero el comentario en BLINK habla solo de cambiar un solo LED. El error real parece ser que RLF no funciona como parece pensar, aunque sin comentarios uno no puede saber lo que está pensando. Tenga en cuenta que la rotación se realiza en 9 bits, el registro y el bit C.
Esto debería ser fácil de depurar. Cargue esto en MPLAB y ejecútelo en el simulador. Luego, puede pasar un solo paso por el programa y ver qué está haciendo cada instrucción.
vladimir cravero
vladimir cravero
vladimir cravero
mrplow911