Hi, I'm a Dutch student busy with my in internship where I have to make an application with a Philips P8xCL883 microcontroller. I would like to use UART to send and receive strings from a PC, but I can not get this to work properly. I searched the web for some interrupt driver routines and found some very nice ones, but implementing these caused some weird problems. I set the baudrate to 9600, but I can only receive chars when (on the PC) I send them with 28800 baud. If I search the datasheet of the P8xCL883 I did set the prescaler to the right value and I am using the right oscillator. The chars I then receive have a problem as well. For instance, if I send a "b" (binairy 01100010) I receive this as a 11000010. Hereby the 4th bit from the left should not be there and bits 1-3 should be shifted to the right 1 position. This happens with every char I receive. My mentor here can not help me with this and after a week of trying, I do not know where to look anymore. I hope anyone can give me some clues so that I can get this communication to work. Thanks in advance, Terry. The P8xCL883 runs on a 3.58 MHz crystal, which is different from a standard 8051. Using the prescaler I should be able to set the frequency for the baudrate, but this does not work for me. A datasheet is available from: http://www-us.semiconductors.philips.com/acrobat/datasheets/P8XCL883_CL884_2.pdf
The sourcecode: // --------------------------------------------------------------------------- // Includes. // --------------------------------------------------------------------------- #include <reg.h> // --------------------------------------------------------------------------- // Defines. // --------------------------------------------------------------------------- #define BUFFER_SIZE 16 // Transmit and Receive buffer size // --------------------------------------------------------------------------- // Function prototypes. // --------------------------------------------------------------------------- void init_ser( void ); void ser_write_byte( unsigned char buf ); char ser_byte_avail( void ); unsigned char ser_read_byte( void ); // --------------------------------------------------------------------------- // Global variable definitions. // --------------------------------------------------------------------------- const char acTestString[] = "Hello World...\n\rThis is a test string.\n\r"; char *pcStr = acTestString; // Pointer to splash text. unsigned char data tx_tail; // Transmit interrupt index. unsigned char data rx_head; // Receive interrupt index. unsigned char data rx_tail; // Receive read index. unsigned char data tx_head; // Transmit write index. static bit is_txing; // True when transmitting a character. static unsigned char data rx_buf[ BUFFER_SIZE ]; // Receive queue. static unsigned char data tx_buf[ BUFFER_SIZE ]; // Transmit queue. //------------------------------------------------------------------ // Main routine here //------------------------------------------------------------------ void main( void ) { init_ser(); // Initialize serial ops. EA = 1; // Enable interrupts. // while( *pcStr ) // Display splash, the Hello World string. // ser_write_byte( *pcStr++ ); while( 1 ) // forever. { if( ser_byte_avail()) // if data coming in... ser_write_byte(ser_read_byte()); // get char and send it back out. } } // --------------------------------------------------------------------------- // Initialize serial operations. // --------------------------------------------------------------------------- void init_ser( void ) { rx_head = 0; // Default head/tail pointers. rx_tail = 0; tx_tail = 0; tx_head = 0; is_txing = 0; // Not transmitting. P3CFGA = 0xFF; // Mode 1 for port 3. P3CFGB = 0x00; P3 = 0x03; // Set Txd & Rxd to high. PRESC = 0x28; // Set prescaler for timer 1 to 9600 baud. S0CON = 0x50; // Mode 1, REN = 1. TMOD = (TMOD & 0x0F) | 0x20; // Timer 1, mode 2, 8-bit reload. // By basic 8051 documentation. PCON = 0x00; // Do not double baud rate. // SMOD bit, PCON.7. Done because P8xCL883 PRESC register is set to 9600 baud. TH1 = 0xFD; // 9600 baud. // (256 - (XTAL / (16L * 12L * baudrate))) , taking XTAL as 11.059Mhz. TR1 = 1; // Timer 1 run. IEN0 = 0x10; // UART receive interrupt. IEN1 = 0x00; // Disable external interrupts. IEN2 = 0x04; // UART transmit interrupt. ALTP = 0x00; // No alternative port functions. TH2 = 0xFD; // set timer2 overflow value // not tested T2CON = 0x04; // Enable timer2. // not tested OISYS = 0x00; // Disable OTP programming. } // --------------------------------------------------------------------------- // Serial interrupt handler. (Receive) // --------------------------------------------------------------------------- void SerIntRec( void ) interrupt 4 using 2 { if( RI ) // Receive character? { RI = 0; // clear receive flag rx_buf[ rx_head++ ] = S0BUF; // Get character from serial port and put into fifo. if( rx_head >= BUFFER_SIZE) // Wrap pointer to beginning of buffer if at end. rx_head = 0; } } // --------------------------------------------------------------------------- // Serial interrupt handler. (Transmit) // --------------------------------------------------------------------------- void SerIntTrans( void ) interrupt 15 using 2 { if( TI ) // Transmit character? { TI = 0; // Clear transmitter flag. if( tx_head == tx_tail ) // Check to see if anymore characters to send? is_txing = 0; // No, indicate to ser_write_byte to set TI next time. else { is_txing = 1; // TI interrupt will occur at end of this character. S0BUF = tx_buf[ tx_tail++ ]; // Transmit character out serial port. if( tx_tail >= BUFFER_SIZE) // Wrap pointer to beginning of buffer if at end. tx_tail = 0; } } } // --------------------------------------------------------------------------- // Transmits the character in buf out the serial port. // --------------------------------------------------------------------------- void ser_write_byte( unsigned char buf ) { unsigned char next_head; tx_buf[ tx_head ] = buf; next_head = tx_head + 1; if( next_head >= BUFFER_SIZE) next_head = 0; // Wait until we can stick the next character into the queue. // prevent buffer over write while( next_head == tx_tail ); tx_head = next_head; if( is_txing == 0 ) TI = 1; // TI kan niet softwarematig gezet worden!! } // --------------------------------------------------------------------------- // Checks to see if any characters are available to be read from the // receive queue. // --------------------------------------------------------------------------- char ser_byte_avail( void ) { if( rx_head == rx_tail ) // return(rx_head != rx_tail); return( 0 ); else return( 1 ); } // --------------------------------------------------------------------------- // Gets one character from the receive queue. // if we get here we know we have a character available. // --------------------------------------------------------------------------- unsigned char ser_read_byte( void ) { unsigned char buf; buf = rx_buf[ rx_tail++ ]; if( rx_tail >= BUFFER_SIZE) // rx_tail %= BUFFER_SIZE; rx_tail = 0; return( buf ); }
And the header file, I took the headerfile for a 8051 from the Keil include directory and adjusted it according the datasheet from the P8xCL883: /* BYTE Register */ sfr P0 = 0x80; sfr SP = 0x81; sfr DPL = 0x82; sfr DPH = 0x83; sfr PCON = 0x87; sfr TCON = 0x88; sfr TMOD = 0x89; sfr TL0 = 0x8A; sfr TL1 = 0x8B; sfr TH0 = 0x8C; sfr TH1 = 0x8D; sfr P1 = 0x90; sfr S0CON = 0x98; sfr S0BUF = 0x99; sfr P3 = 0xB0; sfr PSW = 0xD0; sfr ACC = 0xE0; sfr B = 0xF0; /* SFR's added from the datasheet */ sfr P0CFGA = 0x8E; sfr P0CFGB = 0x8F; sfr P1CFGA = 0x9E; sfr P1CFGB = 0x9F; sfr LGF = 0xA1; sfr HGF = 0xA2; sfr ALTP = 0xA3; sfr WDCON = 0xA5; sfr IEN0 = 0xA8; sfr COMP2L = 0xAA; sfr COMP2H = 0xAB; sfr IP0 = 0xB8; sfr P3CFGA = 0xBE; sfr P3CFGB = 0xBF; sfr IRQ1 = 0xC0; sfr P4 = 0xC1; sfr T2CON = 0xC8; sfr RCAP2L = 0xCA; sfr RCAP2H = 0xCB; sfr TL2 = 0xCC; sfr TH2 = 0xCD; sfr MBUF = 0xD1; sfr MSTAT = 0xD2; sfr MCON = 0xD3; sfr OAL = 0xD4; sfr OAH = 0xD5; sfr ODATA = 0xD6; sfr OTEST = 0xD7; sfr S1CON = 0xD8; sfr S1STA = 0xD9; sfr S1DAT = 0xDA; sfr S1ADR = 0xDB; sfr OISYS = 0xDC; sfr ISE1 = 0xE1; sfr RSTAT = 0xE6; sfr IEN1 = 0xE8; sfr IX1 = 0xE9; sfr IEN2 = 0xF1; sfr LVDCON = 0xF2; sfr IP1 = 0xF8; sfr IP2 = 0xF9; sfr EECON = 0xFB; sfr WDTIM = 0xFF; sfr PRESC = 0xF3; /* BIT Register */ /* PSW */ sbit CY = 0xD7; sbit AC = 0xD6; sbit F0 = 0xD5; sbit RS1 = 0xD4; sbit RS0 = 0xD3; sbit OV = 0xD2; sbit P = 0xD0; /* TCON */ sbit TF1 = 0x8F; sbit TR1 = 0x8E; sbit TF0 = 0x8D; sbit TR0 = 0x8C; sbit IE1 = 0x8B; sbit IT1 = 0x8A; sbit IE0 = 0x89; sbit IT0 = 0x88; /* IEN0 */ // Changed from IE sbit EA = 0xAF; sbit ET2 = 0xAE; sbit ES = 0xAD; sbit ES0R = 0xAC; // Enable interrupt UART receive sbit ET1 = 0xAB; sbit EX1 = 0xAA; sbit ET0 = 0xA9; sbit EX0 = 0xA8; /* IEN2 */ sbit ES0T = 0xF3; // Enable interrupt UART transmit /* IP0 */ // Changed from IP sbit PS = 0xBC; sbit PT1 = 0xBB; sbit PX1 = 0xBA; sbit PT0 = 0xB9; sbit PX0 = 0xB8; /* P3 */ sbit RD = 0xB7; sbit WR = 0xB6; sbit T1 = 0xB5; sbit T0 = 0xB4; sbit INT1 = 0xB3; sbit INT0 = 0xB2; sbit TXD = 0xB1; sbit RXD = 0xB0; /* S0CON */ sbit SM0 = 0x9F; sbit SM1 = 0x9E; sbit SM2 = 0x9D; sbit REN = 0x9C; sbit TB8 = 0x9B; sbit RB8 = 0x9A; sbit TI = 0x99; sbit RI = 0x98; Sorry for messing up the layout, but the messages are of a limited length
After some calculations and some testing I found that setting the PRESC register to 34h seems to solve the receive problem. Reception works for 9600 baud. The formula: Baud Rate = ((2^SMOD) / 32) * Fpsc * (1 / ((2^PTWD) * (3^P3)) is used, I take SMOD = 0, Fpsc = 1:5 Fosc = 715909, PTWD = 6 and P3 = 0. This gives a baud rate of 10240 baud, and perfect reception with 9600 baud sending from the PC. Setting Fpsc to 1:3 = 1193181.667, PTWD = 5 and P3 = 1 should give a baud rate of 9216, but does not work, reception of 9600 baud chars is not working properly. Very weird, but reception is working, I am happy as I can be today.
After reception works ok, I started to work on transmitting chars. It seems that I can not set the TI bit manual (or in software) to enter the interrupt routine. Not by setting the TI = 1; nor by S0CON |= 0x02;. Is there a way around this, so that I can enter the interrupt routine without setting the TI bit?
After reception works ok, I started to work on transmitting chars. It seems that I can not set the TI bit manual (or in software) to enter the interrupt routine. Not by setting the TI = 1; nor by S0CON |= 0x02; (S0CON = SM0, SM1, SM2, REN, TB8, RB8, TI, RI). Is there a way around this, so that I can enter the interrupt routine without setting the TI bit?