Intenté implementar un UART de software en un PIC18F452 usando interrupciones TIMER0 y no puedo hacer que funcione el tiempo.
Estoy usando MPLAB ASM para la compilación y el PICkit2 para la programación.
LIST P=18F452
include <P18F452.inc>
CONFIG WDT=OFF, LVP=OFF, OSC=HS, CP0=OFF
variable cyclesPerBaud=1 ; 1 cycle(s) per baud
variable cyclesInt=0xFFFF ; counter in 16 bit timer
variable freq = 20000000 ; clock frequency 20Mhz
variable baud = 9600 ; baud rate
variable cyclesMain=33 ; cycles in Main program branch (intr->checkBitCounter->startStopBit->transfer)
variable cyclesIdle=16 ; cycles in Idle branch (intr->Idle)
variable offset = (freq/(4*baud))/(cyclesPerBaud)
variable initOffset = cyclesInt - offset
variable durrOffset = cyclesInt - offset + cyclesMain
variable hurrOffset = cyclesInt - offset + cyclesIdle
cblock 0x20
char ; character to send
bitCounter ; bits left to send
startStopBit ; checks to send start of stop bit
buffer ; buffer where char is rotated
cycle ; the current cycle in cyclesPerBaud starting high
endc
org 0000h
goto main
org 0008h
goto intr
main
bcf OSCCON, SCS ; use primary clock
bsf RCON, IPEN ; enable priority levels
; interrupt config:
bsf INTCON, GIEH ; enable high priority interrupts
bcf INTCON, GIEL ; disable low priority interrupts
bsf INTCON, TMR0IE; enable TMR0 interrupts
bcf INTCON, INT0IE; dis. ext. interrupts
bcf INTCON, RBIE ; dis. rb port change int.
bcf INTCON, TMR0IF; clear the TMR0 intr. flag bit
bcf INTCON, INT0IF;
bcf INTCON, RBIF ;
bsf INTCON2, TMR0IP; set TMR0 high priority
; timer 0 config:
bcf T0CON, TMR0ON ; temp. disable timer
bcf T0CON, T08BIT ; 16 bit counter
bcf T0CON, T0CS ; T0CKI pin input as source
bcf T0CON, T0SE ; switch on falling edge
bsf T0CON, PSA ; disable prescaler
bcf T0CON, T0PS2 ; doesn't really matter
bcf T0CON, T0PS1 ; -||-
bcf T0CON, T0PS0 ; -||-
; init output registers and variables
clrf TRISD
clrf LATD
movlw 0xFF
movwf PORTD ; our output port set to high (serial 0 - but not sure about that)
movlw b'01111111' ; symbol to send
movwf char
movlw cyclesPerBaud
movwf cycle ; unused
clrf bitCounter ; zeros
clrf startStopBit ; zeros
clrf buffer ; zeros
; eof init
movlw LOW initOffset ; dunno why but HIGH has to be reversed with LOW
movwf TMR0H
movlw HIGH initOffset
movwf TMR0L
bsf T0CON, TMR0ON ; turn on timer
loop
goto loop ; waiting for the interrupt
intr ; main branch: 8 cycles
btfss INTCON, TMR0IF
retfie
bcf T0CON, TMR0ON
; had to comment this section out - didn't want to work with it - dunno why
; decf cycle, F
; btfss STATUS, Z
; goto idle
; movlw cyclesPerBaud
; movwf cycle
checkBitCounter ; all branches until 'tmr_ret': 16 cycles
movf bitCounter, F
btfss STATUS, Z
goto rotateBuffer
;startStopBit
movf startStopBit, F
btfss STATUS, Z
goto stopCopy
movlw 0x8
movwf bitCounter
movf char, W
movwf buffer
rlncf buffer, F
movlw 0x00
transfer
movwf PORTD
decf bitCounter, F
btfsc STATUS, Z
incf startStopBit, F
tmr_ret ; 5 cycles (+8 from intr = 13 cycles)
movlw LOW durrOffset ; dunno why but HIGH has to be reversed with LOW
movwf TMR0H
movlw HIGH durrOffset
movwf TMR0L
bsf T0CON, TMR0ON
retfie
stopCopy
clrf startStopBit
movlw 0xFF
nop
goto transfer
rotateBuffer
rrncf buffer, F
movf buffer, W
nop
nop
nop
nop
goto transfer
idle ; 5 cycles (+7 from intr = 12 cycles)
movlw LOW hurrOffset ; dunno why but HIGH has to be reversed with LOW
movwf TMR0H
movlw HIGH hurrOffset
movwf TMR0L
bsf T0CON, TMR0ON
retfie
end
El PIC está enviando cosas en PORTD0, como se supone que debe hacer, pero no son los datos que se supone que debe enviar. Cuando pico lo que se transmite (con Realterm), el byte que se envía es 11011111 o 10111111 y, a veces, 11111111 en lugar de 01111111.
Además, desde que comencé a usar 'variables' en el código, noté que tenía que copiar la parte inferior de las compensaciones de tiempo con ALTO y los bytes altos con BAJO en los registros TMR0H:L. ¿Alguien sabe por qué funciona de esa manera? Tal vez estoy confundiendo el significado de estos registros: los usé antes como si el registro TMR0H pudiera contener los 8 bits más significativos del contador de 2 bytes, ¿es correcto?
RECEPCIÓN
El enfoque normal para implementar un receptor asincrónico de software es tener un tictac del temporizador que se ejecute continuamente a 3x o 5x la velocidad en baudios (nota: los números impares son mejores que los números pares). Observe que la entrada sea baja en dos tics consecutivos. Una vez que se observe eso, comience a muestrear la entrada cada tercer tic, hasta que la haya muestreado nueve veces más. Si la entrada es alta en la novena vez, ha recibido un personaje enmarcado correctamente. Si es bajo en la novena vez, tiene un error de encuadre.
* * * S - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - S
-----______000000111111222222333333444444555555666666777777----
--______000000111111222222333333444444555555666666777777-------
El diagrama de tiempo anterior muestra cómo deberían funcionar las cosas. La línea superior muestra lo que sucede en cada tic del temporizador (cada no en blanco es un tic del temporizador; el * es un tic que espera el bit de inicio, una S verifica que la línea siga siendo baja después de haber recibido lo que parecía un bit de inicio. Los números 0 a 7 representan el muestreo de los bits 0 a 7, y S es el bit de parada. Las dos líneas inferiores muestran los datos entrantes en los casos en los que apenas se detecta el comienzo de un bit de inicio (en la tercera asterisco), o donde uno apenas no lo detecta en una interrupción (por lo que la línea ha estado baja durante un tercio de un bit en el momento en que se nota el bit de inicio). Tenga en cuenta que incluso con ese nivel de incertidumbre, la fuente está garantizada estar enviando el bit 0 cuando se muestrea, y así mismo para el resto de bits.
Un enfoque de software es muestrear la línea en las interrupciones indicadas e ignorarlas en las marcadas con guiones. Una alternativa es muestrear ciegamente la línea en cada interrupción en un registro de desplazamiento de 32 bits, usando algo como:
rrf headtail,w
bcf _tail,3
btfsc _INPUTPORT,_INPUTBIT
bsf _STATUS,_tail,3
rrf buff2,w
movff buff1,buff2
movff buff0,buff1
movwf buff0
rrf headtail,f
Los últimos cuatro bits recibidos del puerto estarán en los bits headtail 3..0. Los 24 bits anteriores estarán en buff0..buff2, y cada uno tendrá uno de cada tres bits. Los bits anteriores estarán en los bits de cabecera 7..4. Si uno hace eso, puede verificar si headtail tiene el patrón de bits 00xxxx1x. Si es así, copia buff1 donde quieras los datos entrantes, copia buff1 donde quieras los datos, O headtail con 11000011, y full buff0..buff2 con FF. De lo contrario, no hagas nada. Este enfoque puede ser un poco más lento que cargar selectivamente o ignorar la entrada de datos, pero puede recuperarse mejor de los errores de trama.
TRANSMISIÓN
La transmisión es más fácil que la recepción. Haga arreglos para que un fragmento de código se ejecute una vez por bit de tiempo (si su marca está a 3 veces la velocidad en baudios, ejecute el código cada tercera marca). La forma más sencilla de configurar el código es usar un par de bytes para contener los datos que deben enviarse por el puerto, incluidos los bits de inicio y parada. Hay una variedad de enfoques que uno puede usar. Uno bastante versátil sería:
; At start of interrupt
btfss TransmitBuffL,0
bcf OUTPUT
btfsc TransmitBuffL,0
bsf OUTPUT
; Later, after having handled reception:
btfss TransmitBitsLeft,7 ; Assume this counts down to -1 (i.e. 255)
decfsz TransmitTicks,f
goto noTransmit ; Transmit if no bits left, or no ticks
movlw 3
movwf TransmitTicks ; Handle bit transmission every third interrupt
decf TransmitBitsLeft,f
rrf TransmitBuffH,f
rrf TransmitBuffL,f
NoTransmit:
Se puede preparar un byte para la transmisión cuando se establece el bit 7 de TransmitTicks. Cuando ese sea el caso, coloque la secuencia de bits que se establecerá en TransmitBuffH y TransmitBuffL, establezca TransmitTicks en la longitud del primer bit (normalmente 3) y TransmitBitsLeft en la longitud total de la palabra, incluidos los bits de inicio y parada. Algo como:
TxByte: ; Sends byte in W. Assumes TransmitBitsLeft has high bit set
movwf TransmitBuffL,f
movlw 3
movwf TransmitTicks
movwf TransmitBuffH,f ; LSB (stop bit) is set. Upper bits don't matter.
movlw 9 ; Total frame length (incl. start and stop) minus one
movwf TransmitBitsLeft
bcf _STATUS,_CARRY ; Start bit should be low
rlf TransmitBuffL,f ; Stick start bit in front of what we're sending
rlf TransmitBuffH,f
Este enfoque puede enviar tramas de datos normales usando el estilo indicado. Si necesita más bits de parada, puede asegurarse de que TransmitBuffH tenga suficientes bits configurados y aumente TransmitBitsLeft de manera adecuada. Si uno quiere enviar un BREAK, puede establecer TransmitBuffL en 2 y TransmitBitsLeft en 1, establecer TransmitTicks en la longitud deseada de la señal de interrupción (en tics). Para dejar la línea inactiva durante un tiempo determinado, establezca TransmitBuffL en 1, TransmitBitsLeft en 0 y establezca TransmitTicks en el tiempo de inactividad deseado (en tics).
bebedor
Super gato
bebedor
bebedor
Super gato
Super gato