UMBC | CMSC 391 -- Programming Microcontrollers |
When the 8051 is powered on or rest, the following code (relevant to to serial I/O) gets called:
;initialize the serial port, auto baud detect if necessary acall autobaud ;set up the serial port ;mov a, th1 ;lcall phexSo in turn, we get sent down to:
;to do automatic baud rate detection, we assume the user will ;press the carriage return, which will cause this bit pattern ;to appear on port 3 pin 0 (CR = ascii code 13, assume 8N1 format) ; ; 0 1 0 1 1 0 0 0 0 1 ; | | | | ; start bit----+ +--lsb msb--+ +----stop bit ; ;we'll start timer #1 in 16 bit mode at the transition between the ;start bit and the LSB and stop it between the MBS and stop bit. ;That will give approx the number of cpu cycles for 8 bits. Divide ;by 8 for one bit and by 16 since the built-in UART takes 16 timer ;overflows for each bit. We need to be careful about roundoff during ;division and the result has to be inverted since timer #1 counts up. Of ;course, timer #1 gets used in 8-bit auto reload mode for generating the ;built-in UART's baud rate once we know what the reload value should be. autobaud: mov a, #baud_const ;skip if user supplied baud rate constant jnz autoend_jmp mov a, baud_save+3 ;is there a value from a previous boot? xrl baud_save+2, #01010101b xrl baud_save+1, #11001100b xrl baud_save+0, #00011101b cjne a, baud_save+2, autob1 cjne a, baud_save+1, autob1 cjne a, baud_save+0, autob1 autoend_jmp: ajmp autoend autob1: ;wait for inactivity
mov pcon, #0x80 ;configure uart, fast baud
Bit | Symbol |
---|---|
7 | SM0 |
6 | SM1 |
5 | SM2 |
4 | REN |
3 | TB8 |
2 | RB8 |
1 | TI |
0 | RI |
SM0 | SM1 | Mode | Description |
---|---|---|---|
0 | 0 | 0 | Serial data enters and exits through RXD. TXD outputs the shift clock. 8 bits are transmitted/received (LSB first). The baud rate is fixed at 1/12 the oscillator frequency. |
0 | 1 | 1 | 10 bits are transmitted through TXD or rceived through RXD: a start bit (0), 8 data bits (LSB first) and a stop bit (1). On receive the stop bit goes into RB8 (SCON.2). The baud rate is variable. |
1 | 0 | 2 | 11 bits are transmitted trough TXD or eceived through RXD: start bit(0), 8 data bits (LSB first), a programmable 9th data bit, and a stop bit(1). On Transmit, the 9th data bit (TB8) in SCON) can be assigned the value of 0 or 1. Or, for example, the parity bit (P, in the PSW) could be moved into TB8. On receive, the 9th data bit goes into RB8 in SCON, while the stop bit is ignored. The baud rate is programmable to either 1/32 or 1/64 the oscillator frequency. |
1 | 1 | 3 | 11 bits are transmitted through TXD or recieved through TRXD: a start bit (0), 8 data bits (LSB first), a programmable 9th data bit and a stop bit (1). In fact, Mode 3 is the same as Mode 2 in all respects except baud rate. The baud rate in Mode 3 is variable. |
Only the bit SM2 has not been describred. It is the multiprocessor communications bit.
mov scon, #0x42 ;configure uart, but receive disabled mov tmod, #0x11 ;get timers ready for action (16 bit mode) clr a mov tcon, a mov tl0, a mov th0, a mov tl1, a mov th1, a ;make sure there is no activity on the line ;before we actually begin looking for the carriage return mov r0, #200 autob1b:mov r1, #30 autob1c:jnb p3.0, autob1 djnz r1, autob1c djnz r0, autob1b autob2: ;look for the bits of the carriage return jb p3.0, autob2 ;wait for start bit jb p3.0, autob2 jb p3.0, autob2 ; check it a few more times to make jb p3.0, autob2 ; sure we don't trigger on some noise jb p3.0, autob2 autob2b:jnb p3.0, autob2b ;wait for bit #0 to begin setb tr1 ;and now we're timing it autob2c:jb tf1, autob1 ;check for timeout while waiting jb p3.0, autob2c ;wait for bit #1 to begin autob2d:jb tf1, autob1 ;check for timeout while waiting jnb p3.0, autob2d ;wait for bit #2 to begin autob2e:jb tf1, autob1 ;check for timeout while waiting jb p3.0, autob2e ;wait for bit #4 to begin setb tr0 ;start timing last 4 bits autob2f:jb tf1, autob1 ;check for timeout while waiting jnb p3.0, autob2f ;wait for stop bit to begin clr tr1 ;stop timing (both timers) clr tr0 jb tf1, autob1 ;check for timeout one last time
Baud = Crystal / ( 12 * 16 * N )
where "N" is an integer from 1 to 255. This code will select the available baud rate which is the closest to what was received. Usually, an error of about 2.5% will still manage to communicate without trouble, but much beyond that will be problematic, if it works at all. Many crystals are available which are exact multiples of the standard baud rates. Usually these crystals provide faster baud rates than simply switching to a faster crystal. For example, a 4 MHz crystal provides 1200 baud. Switching to 8 MHz only increases the maximum to 2400 baud, but switching to a 3.6864 MHz crystal will allow 19200 baud!
Unquote
For a crystal of 22.1184Mhz, the maximum buad rate is 115,200 with an error of zero percent.
;compute the baud rate based on timer1 mov a, tl1 rlc a mov b, a mov a, th1 rlc a jc autob1 ;error if timer0 > 32767 mov c, b.7 addc a, #0 cpl a inc a ;now a has the value to load into th1 jz autob1 ;error if baud rate too fast ;after we get the carriage return, we need to make sure there ;isn't any "crap" on the serial line, as there is in the case ;were we get the letter E (and conclude the wrong baud rate). ;unfortunately the simple approach of just looking at the line ;for silence doesn't work, because we have to accept the case ;where the user's terminal emulation is configured to send a ;line feed after the carriage return. The best thing to do is ;use the uart and look see if it receives anything autob3: mov th1, a ;config timer1 mov tl1, #255 ;start asap! mov tmod, #0x21 ;autoreload mode setb ren ;turn on the uart setb tr1 ;turn on timer1 for its clock mov a, th1 cpl a inc a mov r1, a autob3b:mov r0, #255 autob3c:djnz r0, autob3c djnz r1, autob3b jnb ri, autob4 ;if we got here, there was some stuff after the carriage ;return, so we'll read it and see if it was the line feed clr ri mov a, sbuf anl a, #01111111b add a, #246 jz autob4 ;ok if 0A, the line feed character add a, #5 jz autob4 ;of if 05, since we may have missed start bit autob1_jmp: ljmp autob1 autob4: ;compute the baud rate based on timer0, check against timer1 value mov a, tl0 rlc a mov r0, a mov a, th0 rlc a mov r1, a jc autob1_jmp ;error if timer0 > 32767 mov a, r0 rlc a mov b, a mov a, r1 rlc a mov c, b.7 addc a, #0 jz autob1_jmp ;error if baud too fast! cpl a inc a cjne a, th1, autob1_jmp ;acc has th1 value at this point autoend:mov baud_save+3, a mov baud_save+2, a ;store the baud rate for next warm boot. mov baud_save+1, a mov baud_save+0, a xrl baud_save+2, #01010101b xrl baud_save+1, #11001100b xrl baud_save+0, #00011101b mov th1, a mov tl1, a mov tmod, #0x21 ;set timer #1 for 8 bit auto-reload mov pcon, #0x80 ;configure built-in uart mov scon, #0x52 setb tr1 ;start the baud rate timer ret