Я пытался реализовать программный UART на PIC18F452 , используя прерывания TIMER0, и я не могу настроить синхронизацию.
Я использую MPLAB ASM для компиляции и PICkit2 для программирования.
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
PIC отправляет данные на PORTD0, как и предполагалось, но это не те данные, которые он должен отсылать. Когда я выбираю то, что передается (с Realterm), отправляемый байт равен 11011111 или 10111111, а иногда и 11111111 вместо 01111111.
Кроме того, поскольку я начал использовать «переменные» в коде, я заметил, что мне приходилось копировать младшую часть временных смещений с HIGH и старшие байты с LOW в регистры TMR0H:L - кто-нибудь знает, почему это работает таким образом? Возможно, я путаю значение этих регистров: я использовал их раньше, как если бы регистр TMR0H содержал более значимые 8 бит 2-байтового счетчика - это правильно?
ПОЛУЧЕНИЕ
Обычный подход к реализации программного асинхронного приемника состоит в том, чтобы иметь отметку таймера, которая непрерывно работает со скоростью, в 3 или 5 раз превышающей скорость передачи данных (примечание: нечетные числа лучше, чем четные). Следите за тем, чтобы вход был низким на двух последовательных тиках. Как только это будет замечено, начинайте сэмплировать ввод на каждом третьем тике, пока вы не сэмплируете его еще девять раз. Если ввод высокий в девятый раз, вы получили правильно оформленный символ. Если в девятый раз он низкий, у вас ошибка кадрирования.
* * * S - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - S
-----______000000111111222222333333444444555555666666777777----
--______000000111111222222333333444444555555666666777777-------
Приведенная выше временная диаграмма показывает, как все должно работать. Верхняя строка показывает, что происходит на каждом такте таймера (каждое непустое значение — это такт таймера; * — это тик, ожидающий стартового бита, S — проверка, чтобы убедиться, что линия все еще находится на низком уровне после получения того, что выглядело как стартовый бит.Числа 0-7 представляют собой выборку битов 0-7, а S - стоповый бит.Нижние две строки показывают входящие данные в тех случаях, когда едва удается уловить начало стартового бита (на третьей звездочка), или когда он едва перехватил его на одном прерывании (таким образом, к моменту обнаружения стартового бита линия находилась в состоянии низкого уровня в течение трети бита времени). Обратите внимание, что даже при таком уровне неопределенности источник гарантированно для отправки бита 0 при его выборке, а также для остальных битов.
Один программный подход состоит в том, чтобы сэмплировать линию на указанных прерываниях и игнорировать ее на тех, которые отмечены тире. Альтернативой является слепая выборка строки при каждом прерывании в 32-битный регистр сдвига, используя что-то вроде:
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
Последние четыре бита, полученные от порта, будут битами заголовка 3..0. 24 бита перед ними будут в buff0..buff2, где каждый будет содержать каждый третий бит. Биты перед этим будут в битах заголовка 7..4. Если это сделать, можно проверить, содержит ли заголовок битовый шаблон 00xxxx1x. Если это так, скопируйте buff1 туда, куда вы хотите входящие данные, скопируйте buff1 в то место, где вы хотите получить данные, ИЛИ headtail с 11000011 и full buff0..buff2 с FF. В противном случае ничего не делайте. Этот подход может быть немного медленнее, чем выборочная загрузка или игнорирование входных данных, но может лучше восстанавливаться после ошибок кадрирования.
ПЕРЕДАЧА ИНФЕКЦИИ
Передача проще, чем прием. Устройте так, чтобы фрагмент кода запускался один раз в бит (если ваш тик в 3 раза превышает скорость передачи данных, запускайте код каждый третий тик). Самый простой способ настроить код — использовать пару байтов для хранения данных, которые должны быть отправлены через порт, включая стартовые и стоповые биты. Есть множество подходов, которые можно использовать. Довольно универсальным будет:
; 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:
Можно подготовить байт для передачи, когда установлен бит 7 TransmitTicks. В этом случае поместите битовую последовательность, которую необходимо установить, в TransmitBuffH и TransmitBuffL, установите TransmitTicks на длину первого бита (обычно 3), а TransmitBitsLeft на общую длину слова, включая начальный и стоповый биты. Что-то вроде:
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
Этот подход может отправлять обычные кадры данных, используя указанный стиль. Если нужно больше стоповых битов, можно убедиться, что в TransmitBuffH установлено достаточное количество битов, и соответствующим образом увеличить TransmitBitsLeft. Если кто-то хочет отправить BREAK, можно установить TransmitBuffL на 2 и TransmitBitsLeft на 1, установить TransmitTicks на желаемую длину сигнала прерывания (в тиках). Чтобы линия простаивала в течение определенного периода времени, установите TransmitBuffL в 1, TransmitBitsLeft в 0 и установите TransmitTicks в желаемое время бездействия (в тиках).
пьяница
суперкот
пьяница
пьяница
суперкот
суперкот