problema de temporización de UART suave basado en interrupciones PIC

3

He intentado implementar un software UART en un PIC18F452 usando interrupciones TIMER0 y No puedo hacer que el tiempo funcione.

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 están enviando. Cuando hago un pico de lo que se transmite (con Realterm), el byte que se envía es 11011111 o 10111111 y, a veces, 11111111 en lugar de 01111111.

También, desde que comencé a usar 'variables' en el código, noté que tuve que copiar la parte inferior de las compensaciones de temporización con ALTO y 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?

    
pregunta drinker

1 respuesta

2

RECEIVING

El enfoque normal para implementar un receptor asíncrono de software es tener un tic temporizador que se ejecute continuamente a 3x o 5x la velocidad de transmisión (nota: los números impares son mejores que los números pares). Observe que la entrada esté baja en dos tics consecutivos. Una vez que esto se haya observado, comience a muestrear la entrada en cada tercer tick, hasta que haya muestreado nueve veces más. Si la entrada es alta en la novena vez, ha recibido un carácter enmarcado correctamente. Si es baja 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 marca de tiempo (cada espacio que no está en blanco es una marca de tiempo; el * es una marca que espera el bit de inicio, una S está verificando para asegurarse de que la línea sigue siendo baja después de haber recibido lo que parecía bit de inicio. Los números 0-7 representan el muestreo de los bits 0-7, y S es el bit de parada. Las dos líneas inferiores muestran los datos entrantes en los casos en que uno apenas alcanza el comienzo de un bit de inicio (en el tercero asterisco), o donde uno apenas pierde la captura en una interrupción (por lo que la línea ha estado baja durante un tercio de tiempo de 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 para enviar el bit 0 cuando se muestrea, y lo mismo para el resto de los bits.

Un enfoque de software es muestrear la línea en las interrupciones indicadas e ignorarla 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, utilizando 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 de cabecera 3..0. Los 24 bits anteriores estarán en buff0..buff2, y cada uno contendrá cada tercer bit. Los bits antes de eso estarán en bits de cabecera 7..4. Si uno hace eso, uno puede verificar si el headtail contiene el patrón de bits 00xxxx1x. Si es así, copie buff1 en el lugar donde desee los datos entrantes, copie buff1 en el lugar donde desee los datos, O headtail con 11000011, y buff0..buff2 completo con FF. De lo contrario no hagas nada. Este enfoque puede ser un poco más lento que cargar o ignorar de forma selectiva la entrada de datos, pero puede recuperarse mejor de los errores de encuadre.

TRANSMISSION

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 tiempo de bit (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 mantener los datos que deben enviarse al puerto, incluidos los bits de inicio y parada. Hay una variedad de enfoques que uno puede usar. Una más 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:

Uno 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, configure TransmitTicks a la longitud del primer bit (generalmente 3), y TransmitBitsLeft a 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 marcos de datos normales utilizando el estilo indicado. Si necesita más bits de parada, puede asegurarse de que TransmitBuffH tenga suficientes bits establecidos y aumentar TransmitBitsLeft de manera apropiada. Si uno quiere enviar un BREAK, puede configurar TransmitBuffL a 2 y TransmitBitsLeft a 1, configurar TransmitTicks a la longitud deseada de la señal de interrupción (en tics). Para ralentizar la línea durante un cierto período de tiempo, establezca TransmitBuffL en 1, TransmitBitsLeft en 0 y configure TransmitTicks en el tiempo de inactividad deseado (en tics).

    
respondido por el supercat

Lea otras preguntas en las etiquetas