; An electronic postcard, for when paper and a pen are too easy, ; or too cheap! ; This isn't a pretty program, it's just a brute force way ; to display everyones name and department on an LCD. ; The display is a Hitachi 2x40 LCD using the 44780 chip. ; The power is supplied by a 9V battery regulated by a Maxim chip ; with a shutdown input. CMOS parts were used, and a design goal ; was to use as little power as possible by using the power down mode ; of the 80C31 when not DOING something. ; A time int would wake up the CPU, the display would be updated, and ; then go back to sleep. When all 232 names were displayed, shut down ; and power off the CPU/Display. ; All functions are controlled by the data being read. My office had ; a longer display time, and my name was last-just before the display dies. ; I don't have the logics and parts I used, but it could be reversed ; engineered from the code. Parts included were 80C31, EPROM, latch, ; xtal, Maxim regulator, LCD, and switch to start things going. ; Note: An office symbol is the department name. ; Hardware reset was designed for the LCD, but the software reset ; was also used. Control pins were wired to p3, data p1. ; What can be learned? Clock subroutine, interrupts, 44780 programming, ; "state machine" programming-kinda... ; Spaghetti coding? $MOD51 $DEBUG ENABLE EQU P3.2 ; ENABLE IS WIRED TO INT0 PIN REG_SEL EQU P3.4 ; RS TO T0 RD_WR EQU P3.5 ; R/W TO T1 ; P3.3 I/O PIN WHEN LOW-TURNS OFF POWER WAIT EQU 1 ; NEXT BYTE=ms TO WAIT UNLESS BIT 7=1 THEN SECONDS STOS EQU 2 ; START OF OFFICE SYMBOL (POS DISPLAY TO 40H) EOS EQU 3 ; END OF OFFICE SYMBOL (SPACE TILL 53H) STN EQU 4 ; START OF NAME (POS DISPLAY TO 54H+1) EON EQU 5 ; END OF NAME (SPACE TILL 67H+1) POS EQU 6 ; POS CURSOR ACC=POS EOT EQU 7 ; END OF TEXT (NAMES) POS_C EQU 8 ; POS CURSOR, NEXT BYTE = HEX ADDR D_STN EQU 75 ; BEGINNING OF NAME ON LINE 2 FLAGS equ 2FH ; FLAG BITS NOBUSY EQU 2FH.1 ; DON'T CHECK BUSY FLAG (SOFTWARE RESET) NEWNAM EQU 2FH.2 ; POS FLAG FOR SYMBOL AND NAME SUBS PASS_ONE EQU 2FH.3 ; FLAG FOR 1ST PASS DONE tim equ 2fh.4 SECONDS EQU 18H ; SECONDS COUNT QSEC EQU 17H ; QUARTER SECONDS MSECONDS EQU 16H ; ms COUNT DPSAVH EQU 15H ; DPTR HIGH SAVE DPSAVL EQU 14H ; LOW SAVE ORG 0 ; AJMP INIT ; INITIALIZE org 0bh MOV TCON,#0 ; TIMER OFF, IDLE PICKS UP WHERE IT LEFT OFF RETI ; TIMER INTR INIT: MOV SP,#30H ;STACK AFTER REGS MOV R0,#7FH ; clear 127 bytes of ram CLR A RESET: MOV @R0,A DJNZ R0,RESET MOV TMOD,#1 ; mode 1-timer 0 (16bit timer) ; SET TR0-TCON.4 TO RUN SETB IE.7 ; enable ints SETB IE.1 ; enable timer 0 int BEGIN: ; CLEAR EVERYTHING-ASSUME POWERON RESET DIDN'T WORK (LCD) SETB NOBUSY ; DON'T CHECK FOR BUSY DURING POWERUP MOV A,#40 ACALL TWAIT MOV A,#38H ; FUNCTION SET,8 BIT,2 LINES, 5 X 7 FONT ACALL SET_IF MOV A,#40 ACALL TWAIT MOV A,#38H ; FUNCTION SET,8 BIT,2 LINES, 5 X 7 FONT ACALL SET_IF MOV A,#40 ACALL TWAIT MOV A,#38H ; FUNCTION SET,8 BIT,2 LINES, 5 X 7 FONT ACALL SET_IF MOV A,#40 ACALL TWAIT CLR NOBUSY MOV A,#0CH ACALL SET_IF MOV A,#01H ACALL SET_IF MOV A,#6 ACALL SET_IF RESTART: MOV A,#01H ACALL SET_IF ; NOW SET UP TO READ THE DATA BLOCK. WHILE READING IF A CHARACTER IS READ ; THAT IS LESS THAN 31, IT IS AN INTERNAL FUNCTION, AND WE SHOULD CALL ; THE JUMP_TABLE TO GOTO THE CORRECT FUNCTION. BEGN: MOV DPTR,#HEDR ; START OF THE LCD DATA NEXT: CLR A MOVC A,@A+DPTR ; GET A CHAR INC DPTR ; POINT TO NEXT CHAR CJNE A,#31,C_CMD ; IS IT A CMD C_CMD: JC CMD ; IF COMMAND, CMD ACALL WR_LCD ; WRITE DATA AJMP NEXT ; NEXT CHAR CMD: ; COMMAND CHAR IN DATA STREAM ACALL SAVDP MOV DPTR,#JUMP_TABLE ; DEC A ; BIAS OF 1-NO ZERO COMMAND RL A ; TIMES 2 (ADDRESS TABLE=2 BYTE ENTRIES) JMP @A+DPTR ; DOIT CMD_RET: ; RETURN HERE AFTER COMMANDS ACALL RESTDP ; RESTORE DPTR AJMP NEXT JUMP_TABLE: AJMP C_WAIT ; WAIT 1 AJMP C_STOS ; START OF OFFICE SYMBOL 2 AJMP C_EOS ; END OF SAME 3 AJMP C_STN ; START OF NAME 4 AJMP C_EON ; END OF SAME 5 AJMP POSC ; POSITION CURSOR 6 AJMP C_EOT ; END OF TEXT TABLE (DONE) 7 AJMP C_POS ; DATA DRIVEN POSITION 8 C_POS: ACALL RESTDP ; GET THE DATA POINTER BACK CLR A MOVC A,@A+DPTR INC DPTR ACALL SAVDP acall posc ajmp cmd_ret C_EOT: JNB PASS_ONE,FIRST_PASS ; FIRST PASS THROUGH? CLR P3.3 MOV PCON,#2 ; POWERDOWN MODE jmp $ FIRST_PASS: SETB PASS_ONE AJMP RESTART C_EON: MOV A,#3+128 ; SECOND FLAG AND 1 SECOND WAIT ACALL TWAIT ; WAIT AJMP CMD_RET C_STN: ; JB NEWNAM,STNCLR ; IF NAME FIELD CLEARED, SKIP CLEAR MOV A,#D_STN ; CHAR POS OF NAME FIELD ACALL POSC ; START OF NAME FIELD MOV A,#29 ; LENGTH ACALL SPACE ; CLEAR IT MOV A,#D_STN ACALL POSC STNCLR: AJMP CMD_RET C_EOS: MOV A,#D_STN ; START OF NAMES ACALL POSC MOV A,#3+128 ; 1.5 SEC ACALL TWAIT AJMP CMD_RET C_STOS: MOV A,#40H ; LINE 2 ACALL POSC ; BEGINNING OF LINE MOV A,#40 ; 40 SPACES ACALL SPACE ; CLEAR LINE MOV A,#42H ; START OF SYMBOL IN LINE acall posc MOV A,#3+128 AJMP CMD_RET ; DONE FOR NOW C_WAIT: ACALL RESTDP CLR A MOVC A,@A+DPTR INC DPTR ; NEXT CHAR ACALL SAVDP ACALL TWAIT AJMP CMD_RET SAVDP: MOV DPSAVH,DPH ; SAVE DPTR HIGH BYTE MOV DPSAVL,DPL ; AND LOW, COMMAND MODIFIES IT RET RESTDP: MOV DPH,DPSAVH MOV DPL,DPSAVL RET SET_IF: SETB ENABLE MOV P1,A CLR REG_SEL CLR RD_WR NOP NOP CLR ENABLE NOP NOP JNB NOBUSY,SET_CK ; IF INIT, CAN'T CHECK BUSY FLAG RET SET_CK: ACALL CK_BUSY RET TWAIT: ; TIMED WAIT ENTER WITH ACC=TIME IN ms. IF ACC.7=1 THEN 0-6=SEC COUNT JB ACC.7,TWAITSEC ; SECONDS? MOV MSECONDS,A ; SAVE COUNT TWAITMS: ; ACC=ms TO WAIT ACALL WAITMS ; WAIT 1 ms DJNZ MSECONDS,TWAITMS ; DO UNTIL ms COUNT = 0 RET WAITMS: MOV TL0,#LOW(65237) ; LOW BYTE MOV TH0,#HIGH(65237) ; 1ms COUNT HIGH BYTE MOV TCON,#10H MOV PCON,#1 ; IDLE MODE NOP RET ; 1ms DONE TWAITSEC: ; WAIT HALF SECONDS, ACC=BIT 7 AND COUNT ANL A,#7FH ; GET RID OF ACC FLAG BIT JZ WSECDONE ; IF 0-DONE! MOV SECONDS,A ; SAVE COUNT (POST-DEC) WAITS: MOV QSEC,#2 ;TEST ; 2 QUARTER SECONDS (POST-DEC) WAITQ: MOV MSECONDS,#250 ; 250 ms (4th QUARTER SECOND) ACALL TWAITMS ; WAIT 250ms DJNZ QSEC,WAITQ ; ANOTHER QSEC DJNZ SECONDS,WAITS ; ANOTHER WSECDONE: ; DONE RET WR_LCD: ; DATA WRITE-DATA IS IN ACC SETB ENABLE ; TRANSITION TO LOW WILL XFER P1 CLR RD_WR ; WRITE FUNCTION SETB REG_SEL ; DATA WRITE MOV P1,A ; DATA TO OUPUT PORT CLR ENABLE ; TRANSFER DATA TO LCD CLR REG_SEL ; ACALL CK_BUSY ; CHECK BUSY RET SPACE: push acc MOV A,#20H ; SPACE ACALL WR_LCD ; WRITE IT pop acc DJNZ ACC,SPACE ; TILL DONE RET POSC: ; POSITION CURSOR TO (ACC) SETB ENABLE CLR RD_WR CLR REG_SEL ; COMMAND REGISTER ORL A,#128 ; ADD IN SET DD RAM ADDR MOV P1,A ; DATA TO OUTPUT PORT CLR ENABLE ; TRANSFER COMMAND TO LCD ACALL CK_BUSY ; CHECK FOR BUSY RET CK_BUSY: CLR REG_SEL MOV P1,#0FFH ; INPUT SETB RD_WR SETB ENABLE MOV A,P1 CLR ENABLE CLR RD_WR JB ACC.7,CK_BUSY ANL A,#07FH ; ACC=CURSOR POS RET ; ; DATA Block. Format is Function[,data]... HEDR: DB 8,0 DB 'SCCC/II COL. EDWARD W. BRASS 7/90-6/93' DB 1,6+128 ; WAIT 6 SECONDS DB 8,40H ; SECOND LINE DB 'BEST WISHES IN THE FUTURE! FROM.....' DB 8,40h DB 1,6+128 ; WAIT 3 SECONDS DB STOS,'II',EOS DB STN,'ORR ELLIOTT R',EON DB STN,'ANDERSON PAT A',EON DB STN,'ILLUZZI ROBERT W',EON DB STN,'BAYSE JOHN M',EON DB STN,'ANDERSON ARLINE A',EON DB STN,'SUTHERLAND ROSE A',EON DB STOS,'IIOSN',EOS,1,130 DB STN,'FLUD TROY',EON,1,130 DB STN,'VAUGHN ROY L',EON,1,130 DB STN,'HAYES JAMES M',EON,1,130 DB STN,'GARY S. DRUMMOND',EON,1,130,EOT end