This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Problems with UART on a Philips P8xCL883

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

Parents
  • 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 );
    }

Reply
  • 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 );
    }

Children
  • 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?