Email: Password: Remember Me | Create Account (Free)

Back to Subject List

Old thread has been locked -- no new posts accepted in this thread
???
02/28/06 12:18
Read: times


 
#110913 - comments
Responding to: ???'s previous message
Jon,

I promised to comment on your interrupt-driven serial program and I will also comment on mine later if time permits.

My comments in red.

Of course, I am nothing of perfect etc. so these are only my ideas. Arguable, of course, all of them.

Jan Waclawek

;******************************************************************** 
;******************************************************************** 
CR            EQU    0DH 
LF            EQU    0AH 
              ; 
TXBUFSZ      EQU    2             ;Define buffer sizes 
2 bytes long Tx buffer... Not really worth, is 
it? See also comment on TXBUFSZ in the interrupt routine itself.
RXBUFSZ      EQU    8 
              ;                    ;Indexes 
RX_IN         EQU    08H           ;RX next to go in 
RX_OUT       EQU    09H           ;RX next to go out 
TX_IN         EQU    0AH           ;TX next to go in 
TX_OUT       EQU    0BH           ;TX next to go out 
RXBUF         EQU    0CH           ;Allow RXBUFSZ to next variable 
TXBUF         EQU    14H           ;Allow TXBUFSZ to next variable 
the definition of variables using EQU is not 
the best idea; if you need to squeeze in some other variable or 
change the sizes of buffers, it would require a lot of work. It 
is better to use DB or similar pseudoinstructions intended to 
reserve space for variables; for buffers (with already defined 
size) use DB TXBUFSZ .
              ; 
CHR           EQU    16H 
End of variables in RAM -> the initial value of
 stack should come here (see MOV SP,#17h below; should be like 
MOV SP,#SPINIT and SPINIT definition should come here). Again, 
the idea is to have as much job done by the assembler itself 
automatically, when juggling with the variables. 

              ; 
NEEDTI       BIT    01H           ;Clear it if TI=1 is needed 
              ;                    ;Set if TI=1 is not needed 
;******************************************************************** 
       ORG    0h 
       LJMP   MAIN                 ;Reset vector 
       ; 
;****************************************************************** 
       ORG    23h  
       LJMP   SER_ISR             ;Serial port interrupt vector 
       ; 
;******************************************************************** 
       ORG    40h 
       ; 
MAIN:  MOV    SP,#17h             ;Initialize stack pointer 
see comment on initial value of stack ponter above
       LCALL  SERINIT             ;Initialize serial port 
       SETB   EA                   ;Enable all interrupts 
       MOV    DPTR,#SINIT         ;Serial Port Initialized! 
       LCALL  TEXT_OUT            ;Send the message out 
; 
LOOP:                              ;Repeating loop 
       LCALL  GETCHR 
       MOV    A,R1 this
       CPL    A    and this
       JZ     LOOP and this
can be written as one instruction: CJNE R1,#0FFh,LOOP
Anyway; IMHO it is not the best idea to indicate a valid 
received character in this way - see comment below in GETCHR routine
       LCALL  PUTCHR 
       AJMP   LOOP                 ;Loop back 
; 
It would be nice to have a comment here, like: "the program here waits
for any received character and echoes it back". But, is this a good example for 
interrupt-driven serial communication? It can be done in the same way
also using polling... Some other, more complicated, example would be fine.

;******************************************************************** 
A comment should come here, saying something like: 
"this is a string output routine, something like PRINT in BASIC or 
"printf" in C or "write" in Pascal. Sends out the string pointed
to by DPTR, terminated by zero.
TEXT_OUT: 
       CLR    A                    ;dptr has message 
       MOVC   A,@A+DPTR           ;get the next character 
       INC    DPTR                 ;move index to next position 
       JZ     WT2                  ;if zero then end of message 
       MOV    R1,A                 ;else put the chr in the tx buffer 
       LCALL  PUTCHR 
;############################### 
Now THIS (a delay loop) has nothing to do here! 
It indicates some FATAL problem with interrupt-driven communication. The point 
of using interrupts was not to delay the "main" program by the serial routines, 
but this is contradicting it.
Correctly, the  transmitting routine should check for empty space in the 
transmit buffer and if available, put the character otherwise wait. There is a 
buffer empty space check in PUTCHR, but it only fails to put the character and 
THIS routine is suposed to check whether it failed and retry.
In this way, if the string is shorter than the transmit buffer, it would be
placed into the buffer immediately without any delay, and the main program could
move on while the buffer would be transmitted "on the background" by the interrupt
routine - and that's the whole point of interrupt driven communication.
       mov    r3,#2                ;Temporary delay loop 
LP1:   mov    r2,#225             ;to prevent buffer overrun 
       djnz   r2,$ 
       djnz   r3,LP1 
;############################### 
       AJMP   TEXT_OUT 
A note on the preferencial use of particular form of JMP: 
if the jump is explicitly given (not derived by assembler from the genericv JMP),
in short self-contained routines such as this the SJMP is preferred rather than
AJMP (if the length of jump permits it of course). The reason is, if this routine
gets shifted somewhere on the 2k page boundary, AJMP would fail 
while SJMP would not.
WT2:   RET 
; 
;******************************************************************** 
;                                  ;R1 has character 
PUTCHR: 
       SETB   C                    ;using C to store state of EA 
       JBC    EA,PCSKP            ;if interrupts enabled, then disable 
       CLR    C                    ;EA was already disabled 
Ehm... what about... MOV C,EA ?
       ; 
PCSKP:       PUSH   PSW           ;store current status, including EA 
... but not MOV C,EA is needed; why not PUSH IE ?
       MOV    CHR,R1              ;store the character 
there is NO NEED to store the character in CHR - R1 does not
get corrupted until the character is really stored in the buffer!
       CLR    C 
       MOV    A,TX_IN 
       SUBB   A,TX_OUT 
INCORRECT! This code attempts to get the occupied space in buffer
and compare it against the buffer size (below) to get the empty space.
However, it completely misses the fact that in circular buffer the "head" pointer
(TX_IN) may get BELOW the "tail" pointer (TX_OUT)! Although this implementation 
of circular buffer is - ehm - nonstandard (the pointers are NOT the actual
pointers to the buffer - see below) and the described problem will occur less
frequently than in the "normal" implementation (it takes 256 bytes to transmit for 
the "wraparound" of the TX_IN/TX_OUT to occur), it is still not a proper
way to go.
       CLR    C 
       SUBB   A,#TXBUFSZ          ;TXBUFSZ = 2 
       JC     TBUFOK              ; 
       MOV    R1,#0FFh            ;else error RETURN VALUE -1 
       AJMP   PCXIT                ;buffer full  return 
        ; 
TBUFOK: 
       MOV    A,TX_IN 
       ANL    A,#TXBUFSZ-1       ;TXBUFSZ =2-1=1 0 through 1 = 2 
Now this is a method which I object.
The pointers don't properly wrap around at the end of buffer; rather, 
their topmost bits are masked out when calculating the real address.
It assumes that the buffer sizes are in power of two (should be added as a note
at the buffer length declaration).
It also prevents to use the real addresses as pointers, so they the address
has to be calculated each time.
Maybe it works, but I don't like it. I could imagine working with something 
similar, but not in this form - I would keep the PROPER address as pointer,
and I would mask it after incrementing - it requires exact positioning
of the buffer but I think this is more appropriate than this approach.
       ADD    A,#TXBUF            ;# of characters in buffer+buffer address 
       MOV    R0,A                 ;location in buffer 
       MOV    @R0,CHR             ;put character in buffer 
       INC    TX_IN                ;move index 
       JNB    NEEDTI,SKPTI       ;exit if no more characters in buffer 
       CLR    NEEDTI              ;else more to send 
       SETB   TI                   ;interrupt and send them 
SKPTI: 
       MOV    R1,#00h             ;done - return value 0 
PCXIT: 
       POP    PSW                  ;restore and return 
       MOV    EA,C 
       RET 
; 
;******************************************************************** 
GETCHR: 
       SETB   C                    ;use C to store the state of EA 
       JBC    EA,GCSKP            ;if all enabled then disable 
       CLR    C                    ;all were disabled already 
GCSKP: 
       PUSH   PSW                  ;store the current state 
The same applies for storing EA as above...
       ; 
       CLR    C 
       MOV    A,RX_IN             ;how many characters in buffer? 
       SUBB   A,RX_OUT 
       JNZ    RBUFNZ              ;jump if buffer not empty 
the four above lines merge into 
MOV A,RX_OUT
CJNE A,RX_IN,RXBUFNZ
       MOV    R1,#0FFh            ;else buffer is empty return value -1 
       AJMP   GCXIT                ;exit with buffer empty code 
       ; 
RBUFNZ: 
       MOV    R1,RX_OUT           ;next to get out 
       INC    RX_OUT              ;move index 
       MOV    A,R1 
There is no need to move RX_OUT into R1 first, 
then move it from R1 into A
       ANL    A,#RXBUFSZ-1       ;RXBUFSZ = 8-1 =7 0 through 7 = 8 
       ADD    A,#RXBUF            ;# of characters in buffer+buffer address 
       MOV    R0,A                 ;R0 has buffer location 
       MOV    A,@R0                ;get chr from buffer 
       MOV    R1,A                 ;put chr in R1 
GCXIT: 
       POP    PSW                  ;restore previous state 
       MOV    EA,C                 ;restore previous interrupt state 
       RET                         ;return 
; 
;******************************************************************** 
SERINIT:                          ;Initialize serial port 
       CLR    A 
       MOV    TX_IN,A             ;initialize indexes 
       MOV    TX_OUT,A 
       MOV    RX_IN,A 
       MOV    RX_OUT,A 
       CLR    TR1 
       CLR    TI 
       CLR    RI 
       MOV    SCON,#01011010B    ;TI SET INDICATES TRANSMITTER READY. 
                                   ;MODE 1 REN 
       MOV    TMOD,#00100001B    ;TIMER 1 MODE 8 BIT AUTO RELOAD 
        MOV    TH1,#0FDh           ;TIMER RELOAD VALUE 
       SETB   TR1                  ;START TIMER 
       SETB   NEEDTI 
       SETB   ES 
       RET 
; 
SINIT: 
       DB     CR,LF 
       DB     'Serial Port Initialized! ' 
CRLF:  DB     CR,LF,0 
;******************************************************************** 
SER_ISR: 
       PUSH   ACC                  ;store current state 
       PUSH   PSW 
       MOV    PSW,#00h 
       PUSH   00h 
I think this deserves a comment - this is storing R0 from 
register bank 0, and MOV PSW,#0 ensures, that the isr will really work with
register bank 0...

       JNB    RI,TXISR            ;if not receive then check xmit 
       CLR    RI                   ;else clear RI and process 
       CLR    C 
       MOV    A,RX_IN 
       SUBB   A,RX_OUT            ;how many characters in buffer? 
       ANL    A,#0-RXBUFSZ       ; 
Argh... Why this mask??? No need even for that twisted pointer
scheme...
       JNZ    TXISR                ;jump if no characters in buffer 
FATAL! The receiver refuses to receive further characters if 
there is already one character in buffer - they got lost!!!
       MOV    A,RX_IN             ;else  
       ANL    A,#RXBUFSZ-1       ;number of characters in buffer 
       ADD    A,#RXBUF            ;plus buffer address = 
       MOV    R0,A                 ;buffer position 
       MOV    @R0,SBUF            ;put received character in buffer 
       INC    RX_IN                ;increment buffer position 
; 
TXISR: 
       JNB    TI,ISRXIT           ;if TI=0 then exit 
       CLR    TI                   ;else clear TI and process 
       MOV    A,TX_IN             ;how many characters in buffer? 
       XRL    A,TX_OUT 
       JZ     TXBUFZ              ;jump if buffer empty 
MOV A,TX_OUT; CJNE A,TX_IN,TXBUFZ and the following line 
can be omitted
       MOV    A,TX_OUT            ;else 
       ANL    A,#TXBUFSZ-1       ;number of characters in buffer 
       ADD    A,#TXBUF            ;plus buffer address = 
       MOV    R0,A                 ;buffer position 
       MOV    A,@R0                ;get character from buffer 
       MOV    SBUF,A              ;send character out 
       INC    TX_OUT              ;increment buffer position 
       CLR    NEEDTI              ;need to set TI 
       AJMP   ISRXIT              ;exit ISR 
TXBUFZ: 
       SETB   NEEDTI              ;buffer was empty dont need to set TI 
ISRXIT: 
       POP    00h                  ;restore and return 
       POP    PSW 
       POP    ACC 
       RETI 
;******************************************************************** 
;******************************************************************** 
 


List of 46 messages in thread
TopicAuthorDate
Feedback needed            01/01/70 00:00      
   Couple of ideas            01/01/70 00:00      
   Missing            01/01/70 00:00      
      the source...            01/01/70 00:00      
         Stupid EIA            01/01/70 00:00      
      Maybe a Name change?            01/01/70 00:00      
         minor but annoying ...            01/01/70 00:00      
   USB            01/01/70 00:00      
      Limited Experience            01/01/70 00:00      
      known bad USB/serial            01/01/70 00:00      
      FYI - Targus PA088            01/01/70 00:00      
   thoughts            01/01/70 00:00      
      I am so stupid            01/01/70 00:00      
         Kickstart            01/01/70 00:00      
   attribution            01/01/70 00:00      
   The ONE thing I always have to look up a            01/01/70 00:00      
   Ok - Second revision, but still working,            01/01/70 00:00      
      Nice            01/01/70 00:00      
         OK            01/01/70 00:00      
         MAX202 vs. MAX232A            01/01/70 00:00      
         or the 232A            01/01/70 00:00      
      Polled; Interrupt            01/01/70 00:00      
         polled tx            01/01/70 00:00      
            Assorted small ideas            01/01/70 00:00      
               Not a good idea            01/01/70 00:00      
                  Serial speeds?            01/01/70 00:00      
                     Enhanced specifications            01/01/70 00:00      
                        That's a strange spec            01/01/70 00:00      
      formal stuff            01/01/70 00:00      
         formal            01/01/70 00:00      
            who's the intended audience            01/01/70 00:00      
   3rd Revision            01/01/70 00:00      
      Busy?            01/01/70 00:00      
         more than a couple of comments...            01/01/70 00:00      
            So.....            01/01/70 00:00      
               what I like or not...            01/01/70 00:00      
                  comments            01/01/70 00:00      
                     Comments on Comments            01/01/70 00:00      
                        bah...            01/01/70 00:00      
                           more problems            01/01/70 00:00      
                              RE: Problems            01/01/70 00:00      
                              Something Strange            01/01/70 00:00      
                                 Nope....            01/01/70 00:00      
                                    interrupt            01/01/70 00:00      
   All members will enjoy            01/01/70 00:00      
   Intel serial intro app note            01/01/70 00:00      

Back to Subject List