The TX line is normally low on a the Serial port, so use this to power the PIC16F84.
Now the serial line does not drive the Serial Port on the PC,
so use a simple bootstrap to raise the level to drive the Serial
Port's RX pin.
When the PC Serial Port TX is sending stops, Tx is negative. D2 and R1 charge C4 to provide the power for the chip. D2 prevents the power being sucked out when the Serial ports sends characters to the Chip.
I have used a number of chips powered with any thing from 2 AA cells to 5 volts. It amazes me that the serial port on the 2 two or 3 PCs that I have used have always managed to pick up the character being sent to it. However, not in this case. I need to level shift the pin output. When the PIC is outputing stops, D1 charges C1, Whiles bits are being sent, then C1 level shifts the serial output enough for the PC.
The code to drive this is found in code.zip
I have clocked the PIC at 32kHz, as that normally provides enough cycles for what I wanted.
Its useful to have a serial output for debugging and output. When clocked at 32Khz, the PIC is running 8192 instructions per second, so 6.8266 instructions per bit. This is now an intellectual challenge akin to those simple wooden puzzles, relatively pointless, but enjoyable.
Its useful to have a serial output for debugging and output. When clocked at 32Khz, the PIC only has 6.83 instructions per bit.
You need 7 instructions to do bit tests and then to get the timing right you need quite a lot of code. It works for inline code, but looped code needs too many instructions.
This routine uses XOR. Test to see if the bit needs testing, This is done by xoring the current output with the new bit. This leaves W with 1 or 0. Now xor this with the output pin, and it toggles if required.
movfw SRtxTemp xorwf PORTA,W andlw 0x1 xorwf PORTA,f ;t+6,19 RRF SRtxTemp,f
movfw SRtxTemp xorwf PORTA,W andlw 0x1 xorwf PORTA,f ;t+6,19 RRF SRtxTemp,f
If inline this routine uses 5 cycles per bit, It also uses a very small amount of code.
;SRtxChar SRtxCharLp cblock SRtxTemp SRtxCnt endc BTFSC SRinEvents,0 ; If serial input, then crash through output RETURN ; ;Output Start Bit ; movwf SRtxTemp comf SRtxTemp,f BCF PORTA,0 MOVLW ~( 1 << 0 ) ; TRIS PORTA BSF STATUS, RP0 MOVWF TRISA ; Set output high to start Measurement BCF STATUS, RP0 ; 1200 baud output ; move the char into SRtxtemp, it is destroyed. ;Start Bit BSF PORTA,0 movlw 4 movwf SRtxCnt GOTO $+1 SRtxCharLp1 ; This outputs two bits at a time ; this takes 6.5 cycles each ; After start bit, which is 1, then test each bit ; I need to set PA:0 to same as LSB SRtxTemp ; this compares PA:0 with SRtxTemp so see if it needs toggling ; The xorwf PORTA,f causes the bit to be toggled if required. movfw SRtxTemp xorwf PORTA,W andlw 0x1 xorwf PORTA,f ;t+6,19 RRF SRtxTemp,f movfw SRtxTemp xorwf PORTA,W andlw 0x1 RRF SRtxTemp,f xorwf PORTA,f ;t+11,24 decfsz SRtxCnt goto SRtxCharLp1 GOTO $+1 GOTO $+1 BCF PORTA,0 GOTO $+1 GOTO $+1 RETURN
SRwrtHexNibble ; currently only displays 0..9, A..F ANDLW 0x0F ADDLW 0x06 ; is it A..F, if so trigger a digit overflow SKPNDC ADDLW 7; subtract 10, then add 'A'-'0' ADDLW 0x30-6 ; Subtract extra 6 added to cause DC MOVWF SRout GOTO SRtxChar
The timings for these routines can be compared to the required.
With only 6.83 instructions per bit, this is tight. Use the RB0 interrupts to crash into the routine.
Leave SRin set, and SRinEvents bit 0 is set to indicate that a character has been received.
If you are outputing a character, you cannont let is finish. From the raising edge of the Start bit to the sampling of the first bit, you have 1.5 bits or 10.24 instructions.
;-------------------------------------------------------------------------- ; Sec 3. Program Code ;-------------------------------------------------------------------------- ORG 0 GOTO Start ORG 4 GOTO Intrtn Start MOVLW 8 MOVWF SRcnt
; Change Prescaler CLRWDT ; Configure Tmr 0 BSF STATUS,RP0 ; Set up INT edge and prescaller for 8192 / 16 = 512 ticks perseconds. MOVLW 1 << INTEDG | 0x0 + 2 ; For Tmr0 0=/2, 1=/4, 2=/8,3=/16 MOVWF OPTION_REG BCF STATUS,RP0
; Change Prescaler CLRWDT ; Configure Tmr 0 BSF STATUS,RP0 ; Set up prescaller for 8192 / 16 = 512 ticks perseconds. MOVLW 1 << INTEDG | 0x0 + 2 ; For Tmr0 0=/2, 1=/4, 2=/8,3=/16 MOVWF OPTION_REG BCF STATUS,RP0 maincode ; your code GOTO maincode
;-------------------------------------------------------------------------- ; Sec 5.0 Interrupt Routines ;-------------------------------------------------------------------------- Intrtn movwf w_temp ; save off current W register contents movf STATUS,w ; move status register into W register movwf status_temp ; save off contents of STATUS register ; Which Interupt ? ; No Context Save in these routines. ; RS232 1200 baud serial code nop TurboSRinA ; for the 1200 baud serial input, each bit takes 6.83 cycles(8192/1200) ; We need to sample 1.5 bits ( 10.245 cycles in ) ;RS232 input pin must be RB0 ( or RB7 with mods ) TurboSRinA1 RRF PORTB,W ; shift LSB into Carry RRF SRin,f goto $+1 ; wants 2 cycle delay DECFSZ SRcnt,f GOTO TurboSRinA1 ; now set up the counter for next time MOVLW 8 MOVWF SRcnt ; The SRin needs to be inverted COMF SRin,f IntrtnEnding BSF SRinEvents,0 ; Set this bit to indicate a serial byte. movf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ; restore pre-isr W register contents BCF INTCON,INTF retfie ; return from interrupt ; return BSF SRinEvents,0 ; Set this bit to indicate a serial byte. CodeEnd goto CodeEnd
The timings for these routines can be compared to the required.
With only 6.83 instructions per bit, this is tight. These routines need testing. The timing diagrams were produces using AWK and DeltaCad.
The full code is in the diectory Serial Test in code.zip.
The following AWK script extracts the information from the MPLAB trace saved tracein.txt.
#################################################### # # T.awk - A simple awk script to plot test timing # #################################################### BEGIN { FS = "," lastX = 0 lastY = 0 scale = 0.5 of[ "00" ] = 3 of[ "01" ] = 5 # Start the DXF file F_header() } /\|/{ split($0,lr,"|") split(lr[2],reg,":") ts= substr(lr[1],length(lr[1])-20) split(ts,tss," " ) if ( tss[1] != ";" ) { T = tss[2] # print tss[2] "," reg[1] "," reg[2] > "reg.txt" if ( reg[1] =="05" ) { v = substr( reg[2],1,2 ) V = of[ v ] #plot line line( lastT * scale ,lastV,T * scale, lastV , 1 ) line( T * scale,3,T * scale, 5 , 1 ) text( T * scale,6, 90, T ) lastT = T lastV = V print T "," V > "reg.txt" } } } /RRF PORTB,W/ { split($0,lr,"|") split(lr[2],reg,":") ts= substr(lr[1],length(lr[1])-20) split(ts,tss," " ) if ( tss[1] != ";" ) { # print tss[2] "," reg[1] "," reg[2] > "reg.txt" #if ( reg[1] =="05" ) { v = substr( reg[2],1,2 ) T = tss[2] V = of[ v ] lastV = 3 #plot line #line( lastT * scale ,lastV,T * scale, lastV , 2 ) line( T * scale,2,T * scale, 6 , 1 ) text( T * scale,6, 90, T ) lastT = T lastV = V print T "," V > "reg.txt" #} } } /BSF PORTA,0/ { # found start of start bit dt = 0 line( (T+dt) * scale,1,(T+dt) * scale, 7 , 0 ) text( (T+dt+1) * scale, 4 , 0, "start" ) dt = dt+1.5*8192/1200 for ( cnt = 0 ; cnt < 8 ; cnt ++ ) { line( (T+dt) * scale, 2,(T+dt) * scale, 6 , 4 ) text( (T+dt) * scale, 6 , 90, " " T+dt ) text( (T+dt+0.25 ) * scale, 4 , 0, cnt ) dt = dt+8192/1200 } line( (T) * scale,2,(T+dt) * scale, 2 , 4 ) line( (T) * scale,6,(T+dt) * scale, 6 , 4 ) } /call Intrtn;/ { line( (T-4) * scale,3,(T-4+0.25) * scale, 5 , 4 ) line( (T-3.25) * scale,3,(T-3.25+0.25) * scale, 5 , 4 ) line( (T-3.5) * scale,3,(T-3.5+0.25) * scale, 5 , 4 ) line( (T-3.75) * scale,3,(T-3.75+0.25) * scale, 5 , 4 ) text( (T-4) * scale, 1 , 0, " INT pin 0->1" ) #text( (T-3) * scale, 3 , 0, " START" ) # found start of start bit dt = -3 line( (T+dt) * scale,3,(T+dt+0.25) * scale, 5 , 4 ) line( (T+dt+0.25) * scale,3,(T+dt) * scale, 5 , 4 ) text( (T+dt+1) * scale,4, 0, "start" ) dt = dt+1*8192/1200 for ( cnt = 0 ; cnt < 8 ; cnt ++ ) { line( (T+dt) * scale,3,(T+dt+0.25) * scale, 5 , 4 ) line( (T+dt+0.25) * scale,3,(T+dt) * scale, 5 , 4 ) text( (T+dt) * scale, 6 , 90, " " T+dt ) text( (T+dt+1) * scale, 4 , 0, " bit " cnt ) dt = dt+8192/1200 } line( (T-4+0.25) * scale,3,(T+dt) * scale, 3 , 4 ) line( (T-4) * scale,5,(T+dt) * scale, 5 , 4 ) } /BCF PORTA,0/ { # found start of start bit dt = 0 line( (T+dt) * scale,2,(T+dt) * scale, 6 , 5 ) text( (T+dt) * scale,6, 90, T+dt ) text( (T+dt) * scale, 1 , 0, "stop" ) } END { # And finally tidy up the DXF file F_end() }
If you use these functions and find them useful, please email me or sign my guestbook. This is postcard ware.
Doug Rice,
Copyright, 22nd December 2001