I tried today to send characters raw through bash, using stty -F /dev/ttyS0 ispeed 4800 ospeed 4800 cs8. I then echoed: echo 'Hello, world!' > /dev/ttyS0. It sent beautifully. I went back to the Python code and saw that the uspp function write(s) could take a full string. So I sent:
s = SerialPort("/dev/ttyS0", None, 4800)
s.flush()
str = 'Hello, world!' + chr(0b10000000) + chr(0b11000000) + 'From: Tom'
s.write( str )
And it worked perfectly. I just need to remember to put a 1us delay between full string sends. A note on the above. The pic code listens for 0b10000000. This signals that the next character will be an instruction to the LCD ( like clear screen or next line). The 0b11000000 instruction moves the cursor to the second line. The datasheet for your LCD will show the commands. Mine is: GDM1602K
Here is the pic assembly code. It needs clean up. I want to move all the delays into the macro at least.
;******************************************************************************
; *
; Filename: lcd2.asm *
; Date: 2010.03.13 *
; File Version: 1.0.0 *
; *
; Author: Tom Hunt *
; *
;******************************************************************************
; NOTES: Implements lcd serial interface to Hitachi style lcd *
; pic 16f84a running at 20MHz *
; *
;******************************************************************************
; CHANGE LOG: *
; *
; *
;******************************************************************************
list R=DEC, p=16f84a ; list directive to define processor
#include
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC
;******************************************************************************
; CONSTANT & VARIABLE DEFINITIONS *
;******************************************************************************
#define RS232 PORTA
#define TX 3
#define RX 4
#define Cntrl PORTA
#define RS 0
#define RW 1
#define E 2
#define Data PORTB
#define Instruction b'10000000' ; will send to indicate an instruction
#define isInstruction 0
CBLOCK 0x0C
Byte ; holds the byte for rs232 and lcd
Count ; holds the counter for start, stop and data bits ( 10 )
Dlay:2 ; 16 bits for long delays
Flags
w_temp
status_temp
ENDC
;******************************************************************************
; MACROS *
;******************************************************************************
; courtesy of Myke Predko - delays from 4 to 771
Delay Macro Value ; Figure Out Delay Values
variable Cnt, Remainder
Cnt = (Value - 4) / 3
Remainder = Value - ((Cnt * 3) + 4)
movlw Cnt + 1
movwf Dlay
decfsz Dlay, f
goto $ - 1
if (Remainder == 2)
goto $ + 1
endif
if (Remainder == 1)
nop
endif
endm
;******************************************************************************
; RESET VECTOR *
;******************************************************************************
ORG 0x000 ; processor reset vector
goto main ; go to beginning of program
;******************************************************************************
; INTERRUPT VECTOR *
;******************************************************************************
ORG 0x004 ; interrupt vector location
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
; TODO: Place ISR (Interrupt Service Routine) here...
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
retfie ; return from interrupt
;************************************************************************************************
; *
; INITIALIZATION CODE *
; *
;************************************************************************************************
initLcd
bcf Flags,isInstruction
call dlay5ms ; wait 15ms+ to start
call dlay5ms
call dlay5ms
call dlay5ms
; This sequence loads values into the D0-7, RS, RW pins and strobes E
bcf Cntrl,RS
bcf Cntrl,RW
bcf Cntrl,E
;call dlay15us ; don't think I need this...
movlw b'00110000' ; first control command to lcd
movwf Data
bsf Cntrl,E
call dlay15us
bcf Cntrl,E ; strobe it into lcd
call dlay5ms ; a wait period is needed. For this, we need 5ms
bsf Cntrl,E
call dlay15us
bcf Cntrl,E ; strobe lcd again with same function set
call dlay150us ; a wait period of +100us is needed
bsf Cntrl,E
call dlay15us
bcf Cntrl,E ; strobe lcd 3rd time with same function set
call dlay5ms
movlw b'00111000' ; 0x38 function set for 2 lines, small font
movwf Data
bsf Cntrl,E
call dlay15us
bcf Cntrl,E
call dlay5ms
; display off
movlw b'00001000' ; 0x08
movwf Data
bsf Cntrl,E
call dlay15us
bcf Cntrl,E
call dlay5ms
; clear display
movlw b'00000001' ; 0x01
movwf Data
bsf Cntrl,E
call dlay15us
bcf Cntrl,E
call dlay5ms
; entry set incr data and no shift
movlw b'00000110'
movwf Data
bsf Cntrl,E
call dlay15us
bcf Cntrl,E
call checkLcdBusy ; now we can poll the busy flag
; turn on display
movlw b'00001110' ; 0xE - display and cursor on, blink off
movwf Data
bsf Cntrl,E
call dlay15us
bcf Cntrl,E
call checkLcdBusy
return
initInput ; count with return = 5
movlw 0xFF
movwf TRISB ^ 0x080
clrf Data ; clear Data port for clean read
return
initOutput ; count = 10
clrf RS232
clrf Data ; clear the lcd data byte/port bits
bsf STATUS, RP0 ; Select Bank 1
movlw 0x00 ^ ( 1 << RX ) ; make all portA output except RX
movwf TRISA^0x080
movlw 0x00
movwf TRISB^0x080 ; PORTB starts as output for lcd data
bcf STATUS, RP0 ; Select Bank 0
return
;************************************************************************************************
;* *
;* LCD ROUTINES *
;* *
;************************************************************************************************
processByte ; takes ~204 if no busy and writing to lcd
btfss Flags,isInstruction ; this is an instruction
goto notInstr ; not an instruction, continue processing
call writeByte ; process instruction
return
notInstr
movf Byte,W
xorlw Instruction
btfss STATUS,Z ; see if we want an instruction
goto regularByte
bsf Flags,isInstruction ; next will be instruction. set flag and return
return
regularByte
call writeByte ; write the byte to lcd
return ; count with return = 204
writeByte ; count = 192 = 104 + 75 + 13
call checkLcdBusy ; total = 104 + 2 for call
bcf Cntrl,RW ; write operation
bsf Cntrl,RS ; default to writing data
btfsc Flags,isInstruction ; are we expecting an instruction?
bcf Cntrl,RS ; then set to write instruction
movf Byte,W
movwf Data
bsf Cntrl,E ; strobe the byte into the lcd
call dlay15us ; total = 75 with call
bcf Cntrl,E
bcf Flags,isInstruction ; clear in case it was an instruction
return
checkLcdBusy ; total with no busy lcd = 104
call initInput ; takes 2 for call and 5 for method = 7
bsf Cntrl,RW ; set for read ( input = 1 )
bcf Cntrl,RS
bsf Cntrl,E ; strobe on
call dlay15us ; total 75 instrs. with call
movf Data,W ; read the contents of the lcd
bcf Cntrl,E ; stobe off
;goto $ + 1 ; debug goto. delete for app
btfsc Data,7 ; test if D7 is busy
goto $ - 4 ; wait for it!
bcf Cntrl,RW ; reset back to output mode
call initOutput ; 2 for call + 10 for method = 12
return
;************************************************************************************************
;* *
;* RS232 ROUTINES *
;* *
;************************************************************************************************
receiveByte
Delay 521 - 3 ; wait 1/2 bit period and test again
btfsc RS232,RX
return ; false read, return and block again
movlw 0x08
movwf Count ; set up to read 8 bits, LSB first
goto $ + 1
receiveBit
Delay 521 - 7 ; 2 half bit delays put us squarely in the middle of the bit send
Delay 521 ;
bcf STATUS,C ; clear the carry bit
btfsc RS232,RX
bsf STATUS,C ; set carry bit to be shifted into received byte var
rrf Byte,F ; shift the bit into Byte ( we get LSb first )
decfsz Count,F
goto receiveBit
Delay 600 ; should put us in the stop bit with 80 instr for error margin
btfsc RS232,F ; check stop bit, don't write if invalid
return
call processByte ; takes ~204 if no blocking on lcdBusy
return ; as soon as possible and block for next byte
;************************************************************************************************
;* *
;* MAINLINE CODE *
;* *
;************************************************************************************************
main
call initOutput
call initLcd
;movlw 'K'
;movwf Byte
;call processByte
processLoop ; btfss Flags,reading
btfss RS232,RX ; block until start bit received
call receiveByte
goto processLoop
;******************************************************************************
; Debugging Routines *
;******************************************************************************
debugging ; call to blink an led on A4 where needed for debug
bsf PORTA,3
movlw d'100' ; c. half second on
movwf Count
call dlay5ms
decfsz Count,F
goto $ - 2
bcf PORTA,3
movlw d'100' ; c. half second off
movwf Count
call dlay5ms
decfsz Count,F
goto $ - 2
return
;************************************************************************************************
;* *
;* DELAY ROUTINES *
;* *
;************************************************************************************************
dlay5ms
movlw 0x19 ; 25 * 1005 + 2 = 25127 ( .0050254 secs at 20 MHz )
movwf Dlay + 1
outer5ms
movlw 0xFA ; 250 decimal
movwf Dlay
inner5ms ; 250 * 5 instructions = 1000
nop
decfsz Dlay,F
goto inner5ms
nop
decfsz Dlay + 1,F
goto outer5ms
nop
return
dlay150us
movlw 250 ; 250 * 3 instrs = 750. 750 / 5e6 = .0015
movwf Dlay
decfsz Dlay,F
goto $ - 1
return
dlay15us ; 15us ( 6 + 23*3 = 75 ) worked
movlw 23
movwf Dlay
decfsz Dlay,F
goto $ - 1
nop
return
END ; directive 'end of program'
Use at your own risk... Enjoy!
As you can see from the defines, this is for a 16f84a, running at 20 MHz. PortB pins are the 8 data line pins ( makes shifting the byte in easier ). PortA,0 is RS, PortA,1 is RW, and PortA,2 is the E for the LCD. PortA,4 is the RS232 receive pin. TX is not used. I actually used this pin for debugging. If you want to use this at different baud rates or different clock speed, you will have to adjust the timings. With slower crystals, beware! Writing to the LCD takes at least 204 instructions. This might be less at 4 MHz. With 20MHz, I had to add in a 15us delay in the strobe of the E pin. I didn't need this at 4 MHz. Removing the delay will save you 75 instructions. In writeByte, this will save you 150 instructions( called for checkLcdBusy and in the write strobe ).
No comments:
Post a Comment