MCF52235 UART Problems

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MCF52235 UART Problems

5,992 Views
JeffB
Contributor I
Has anyone sucessfully used the UARTs in the 52235?
 
I thought I had them working but now some strange things are happening.
 
I know I am initializing correctly, my code basically matches the code generated by CFINIT.
 
Here is a brief explaination of what I am trying to do.
 
When I receive a character via the UART I put the data into a que.
The mainline code will check to que to see if any data is available.  When checking the que I disable the receive interrupt, the re-enable when the routine is finished.  When I disable the interrupt I first set the SR interrupt priority up to 7, disable the RX interrupt, then set the SR interrupt priority back to 0.
 
Now for what is happening.  When I disable the RX interrupt, a pending interrupt (INT14 for UART1) is getting set in the IPRL register.  As soon and I set the SR priority back to 0 the interrupt gets serviced. During the interrupt service routine I check the transmit que an if it is empty I disable the TX interrupt.  When I do that, the pending interrupt bit gets cleared.
 
I have include the relevant code and would appreciate if anyone could take a look at it.  Any pointers would be helpful.  I have also made sure the no interrupts share the same level and priority.
 
Code:uint16 UART0_BAUDTABLE[10] =
{
 (SYS_CLOCK/32)/300, 
 (SYS_CLOCK/32)/600, 
 (SYS_CLOCK/32)/1200, 
 (SYS_CLOCK/32)/2400, 
 (SYS_CLOCK/32)/4800, 
 (SYS_CLOCK/32)/9600, 
 (SYS_CLOCK/32)/19200, 
 (SYS_CLOCK/32)/38400, 
 (SYS_CLOCK/32)/57600, 
 (SYS_CLOCK/32)/115200, 
};
//*****************************************************************************//*//*  InitUART1 //* //* Initialize UART1.//* Default Settings:  9600,8,N,1//*              //*****************************************************************************void InitUART1(uint8 BaudRate){ // assign port pins to UART functions MCF_GPIO_PUBPAR = 0     | MCF_GPIO_PUBPAR_TXD1_TXD1     | MCF_GPIO_PUBPAR_RXD1_RXD1;        // Reset Receiver and Transmitter (UCR) MCF_UART1_UCR = MCF_UART_UCR_RESET_TX; MCF_UART1_UCR = MCF_UART_UCR_RESET_RX;     // Reset Mode Pointer MCF_UART1_UCR = MCF_UART_UCR_RESET_MR;  // Enable Interrupt Source (UIMR) // Transmit Interrupt will be enabled when que is not empty MCF_UART1_UIMR = 0     | MCF_UART_UIMR_RXRDY_FU;       MCF_INTC0_IMRL &= ~(0    | MCF_INTC_IMRL_MASK14    | MCF_INTC_IMRL_MASKALL); MCF_INTC0_ICR14 = 0b00110110; // level 6, priority 6     // Initialize Input Enable Control (UACR) MCF_UART1_UACR = 0;     // Select Receiver and Transmitter Clock (UCSR)   MCF_UART1_UBG2 = (uint8)UART1_BAUDTABLE[5];  MCF_UART1_UBG1 = (uint8)(UART1_BAUDTABLE[5] >> 8);   MCF_UART1_UCSR = 0 //Set Rx and Tx clocks to system clock     | MCF_UART_UCSR_TCS(MCF_UART_UCSR_TCS_SYS_CLK)     | MCF_UART_UCSR_RCS(MCF_UART_UCSR_TCS_SYS_CLK);       // Set Mode Register 1 (UMR1) MCF_UART1_UMR = 0 // RX Ready will generate interrupt     | MCF_UART_UMR_BC_8  // 8 bits/char     | MCF_UART_UMR_PM_NONE;  // no parity      // Set Mode Register 2 (UMR2) MCF_UART1_UMR = 0 // normal mode     | MCF_UART_UMR_SB_STOP_BITS_15; // stop bit width  // Enable Transmitter and Receiver (UCR) MCF_UART1_UCR = 0     | MCF_UART_UCR_TX_ENABLED     | MCF_UART_UCR_RX_ENABLED;}

 
Code:
//*****************************************************************************//*//*  UART1 Interrupt Routine //* //*              //*****************************************************************************__interrupt__void UART1Interrupt(void){uint8 temp; // Determine interrupt source if ((MCF_UART1_UISR & 4)) {  temp = MCF_UART1_URB;  MCF_UART1_UCR = MCF_UART_UCR_RESET_ERROR;  MCF_UART1_UCR = MCF_UART_UCR_BKCHGINT;  }  if ((MCF_UART1_UISR & MCF_UART_UISR_TXRDY) == MCF_UART_UISR_TXRDY) {  // Transmit Empty  if (UART1_q_TX_size() == 0)  {   // nothing more to send, shutoff TX interrupt   MCF_UART1_UIMR &= ~MCF_UART_UIMR_TXRDY;  }  else  {   // send the next byte    MCF_UART1_UTB = pull_UART1_TX_q();  } } if((MCF_UART1_UISR & MCF_UART_UISR_RXRDY_FU) == MCF_UART_UISR_RXRDY_FU) {  // Receive interrupt  // put the received byte on the que  // check error bit here  if (MCF_UART1_USR & (0xF0))  {   temp = MCF_UART1_URB;   MCF_UART1_UCR  = MCF_UART_UCR_RESET_ERROR;   MCF_UART1_UCR = MCF_UART_UCR_BKCHGINT;    }  else  {   add_UART1_RX_q(MCF_UART1_URB);   } }}

 
Code:
//*****************************************************************************//*//*  UART1_getchar //* //*              //*****************************************************************************uint16 UART1_getchar(void){uint16 RetVal; // set interrupt level to 7 mcf5xxx_wr_sr(0x2700);  // disable RX interrupt MCF_UART1_UIMR &= ~MCF_UART_UIMR_RXRDY_FU;  mcf5xxx_wr_sr(0x2000);  if (UART1_q_RX_size() == 0)  RetVal = QUE_EMPTY; // que is empty else {    RetVal = UART1_RX_Q[UART1_RX_Q_TAIL];  if (++UART1_RX_Q_TAIL == UART1_Q_MAX)  {   UART1_RX_Q_TAIL = 0;  } }  mcf5xxx_wr_sr(0x2700); // Re-enable RX interrupt MCF_UART1_UIMR |= MCF_UART_UIMR_RXRDY_FU;  mcf5xxx_wr_sr(0x2000);  return(RetVal); }

 

 
Labels (1)
0 Kudos
6 Replies

636 Views
JeffB
Contributor I

Mark,

Thanks for the tips I will give it a try.

You mentioned keeping a copy of the mask register.  Does the coldfire do a read-modify-write on this register?

Here is another strange thing that happens.

If I turn on both the transmitter and receiver in the initialization routine and write a 0 into the UIMR register, it still fires the interrupt because of the transmit ready bit.  I don't understand why I still get the interrupt when all interrupt sources have been masked off.

 

 

0 Kudos

636 Views
mjbcswitzerland
Specialist V

Hi Jeff

The Mask register (UART block + 0x14) is writable to set and clear the mask bits.

When this address is read it doesn't return the mask bits which have been set but instead returns the Status of the interrupt bits. This means that a read and write are at the same location but the actual register accessed is different. This means that instructions like x |= y or x &+ ~y; will not work correctly.

I don't know why you are seeing an initial interrupt. I didn't hav ethis effect. Below I have copied the initialisation routine which I use - you should get the idea and maybe see something which could help explain the difference.

Regards

Mark

==========================================================

// User configuration
QUEUE_HANDLE SerialPortID;
TTYTABLE tInterfaceParameters;                                       // table for passing information to driver

tInterfaceParameters.ucChannel = 1;                                  // use serial channel 1 on evaluation board / target
tInterfaceParameters.ucSpeed = temp_pars->temp_parameters.ucSerialSpeed; // baud rate
tInterfaceParameters.Rx_tx_sizes.RxQueueSize = RX_BUFFER_SIZE;       // input buffer size
tInterfaceParameters.Rx_tx_sizes.TxQueueSize = TX_BUFFER_SIZE;       // output buffer size
tInterfaceParameters.cTask_to_wake = OWN_TASK;                       // wake self when messages have been received
tInterfaceParameters.ucFlowHighWater = temp_pars->temp_parameters.ucFlowHigh;// set the flow control high and low water levels in %
tInterfaceParameters.ucFlowLowWater = temp_pars->temp_parameters.ucFlowLow;
tInterfaceParameters.usConfig = temp_pars->temp_parameters.usSerialMode;
SerialPortID = fnOpen( TYPE_TTY, FOR_I_O, &tInterfaceParameters );   // open or change the channel with defined configurations

... the following is called somewhere down the line


// configure the SCI hardware
//
void fnConfigSCI(unsigned char ucChannel, TTYTABLE *pars)
{
    unsigned char *ucReg = fnSelectChannel(ucChannel);                   // set register pointer to corresponding block (command register)
    unsigned short usSpeed;
    unsigned char ucBits = UART_8_BIT;

    switch (ucChannel) {                                                 // First configure the GPIO pins for UART use
    case 0:
        PUAPAR |= 0x05;                                                  // Set TX/RX on UA
        IC_ICR_0_13 = (INTERRUPT_LEVEL_5 | INTERRUPT_PRIORITY_1);        // define interrupts level and priority
        IC_IMRL_0 &= ~(UART0_PIF_INT_L | MASK_ALL_INT);                  // unmask interrupt source
        break;

    case 1:
        PUBPAR |= 0x05;                                                  // Set TX/RX on UB
        IC_ICR_0_14 = (INTERRUPT_LEVEL_5 | INTERRUPT_PRIORITY_2);        // define interrupts level and priority
        IC_IMRL_0 &= ~(UART1_PIF_INT_L | MASK_ALL_INT);                  // unmask interrupt source
        break;

    case 2:
        PUCPAR |= 0x03;                                                  // Set TX/RX on UB
        IC_ICR_0_15 = (INTERRUPT_LEVEL_5 | INTERRUPT_PRIORITY_3);        // define interrupts level and priority
        IC_IMRL_0 &= ~(UART2_PIF_INT_L | MASK_ALL_INT);                  // unmask interrupt source
        break;

    default:
        return;                                                          // Invalid UART channel
    }

    *ucReg = UART_RESET_RX;                                              // reset rx and tx and ensure internal command pointer at start
    *ucReg = UART_RESET_TX;
    *ucReg = UART_RESET_CMD_PTR;

    ucReg -= (UCR_0_OFFSET - USR_UCSR_0_OFFSET);                         // set to clock select register
    *ucReg = (UART_RX_BUS_CLK | UART_TX_BUS_CLK);                        // Use internal bus clock
    ucReg += (UBG1_0_OFFSET - USR_UCSR_0_OFFSET);                        // Move to BRG register

    switch (pars->ucSpeed) {
    case SERIAL_BAUD_300:
        usSpeed = BUS_CLOCK/300/32;                                      // set 300
        break;
    case SERIAL_BAUD_600:
        usSpeed = BUS_CLOCK/600/32;                                      // set 600
        break;
    case SERIAL_BAUD_1200:
        usSpeed = BUS_CLOCK/1200/32;                                     // set 1200
        break;
    case SERIAL_BAUD_2400:
        usSpeed = BUS_CLOCK/2400/32;                                     // set 2400
        break;
    case SERIAL_BAUD_4800:
        usSpeed = BUS_CLOCK/4800/32;                                     // set 4800
        break;
    case SERIAL_BAUD_9600:
        usSpeed = BUS_CLOCK/9600/32;                                     // set 9600
        break;
    case SERIAL_BAUD_14400:
        usSpeed = BUS_CLOCK/14400/32;                                    // set 14400
        break;
    default:                                                             // if not valid value set this one
    case SERIAL_BAUD_19200:
        usSpeed = BUS_CLOCK/19200/32;                                    // set 19200
        break;
    case SERIAL_BAUD_38400:
        usSpeed = BUS_CLOCK/38400/32;                                    // set 38400
        break;
    case SERIAL_BAUD_57600:
        usSpeed = BUS_CLOCK/57600/32;                                    // set 57600
        break;
    case SERIAL_BAUD_115200:
        usSpeed = BUS_CLOCK/115200/32;                                   // set 115200
        break;
    case SERIAL_BAUD_230400:
        usSpeed = BUS_CLOCK/230400/32;                                   // set 230400
        break;
    }
    *ucReg = (unsigned char)(usSpeed >> 8);
    ucReg += (UBG2_0_OFFSET - UBG1_0_OFFSET);
    *ucReg = (unsigned char)(usSpeed);                                   // Baud rate set
    ucReg -= (UBG2_0_OFFSET - UMR1_2_0_OFFSET);                          // set to Mode register

    if (pars->usConfig & CHAR_7) {
      ucBits = UART_7_BIT;
    }

    if (pars->usConfig & (RS232_ODD_PARITY | RS232_EVEN_PARITY)) {
        if (pars->usConfig & RS232_ODD_PARITY) {
          *ucReg = (UART_WITH_PARITY | ucBits | ODD_PARITY);             // 7/8 bits - odd parity
        }
        else {
          *ucReg = (UART_NO_PARITY | ucBits);                            // 7/8 bits - even parity
        }
    }
    else {
        *ucReg = (ucBits);                                               // 7/8 bits - no parity
    }

    if (pars->usConfig & TWO_STOPS) {                                    // The mode register 2 has been selected by previous write
        *ucReg = UART_TWO_STOP;
    }
    else if (pars->usConfig & ONE_HALF_STOPS) {
        *ucReg = UART_ONE_HALF_STOP;
    }
    else {
        *ucReg = UART_ONE_STOP;
    }

    // Enable receiver (tx enabled when used)
    ucReg = fnSelectChannel(ucChannel);                                  // set register pointer to corresponding block (command register)
    *ucReg = UART_RX_ENABLE;                                             // enable RX interrupt and Rx
    ucReg += (UIMR_UISR_0_OFFSET - UCR_0_OFFSET);
    ucEnabledState[ucChannel] |= UART_RXRDY_MASK;                        // we must make a backup since the register can not be read
    *ucReg = ucEnabledState[ucChannel];
}

=====================================================================

0 Kudos

636 Views
JeffB
Contributor I

Mark,

Thanks for taking time to look at my problem.

Could you explain more what you mean in item 1?

Thanks,

Jeff

0 Kudos

636 Views
mjbcswitzerland
Specialist V
Hi Jeff
 
Here's the interrupt routine that I use which handles only rx and tx interrupts. It exists three times, once for each UART with the values 0 changed to 1 or 2 accordingly.
__interrupt void SCI0_Interrupt(void)
{
    unsigned char ucState;
    while (ucState = (UIMR_UISR_0 & (UART_RXRDY_MASK | UART_TXRDY_MASK))) { // while interrupts present
        if (ucState & UART_RXRDY_MASK) {
            fnSciRxByte(UTB_RB_0, 0);                // receive data interrupt - read the byte
        }
        if (ucState & UART_TXRDY_MASK) {
            fnSciTxByte(0);                          // transmit data empty interrupt - write next byte
        }
    }
}
fnSciRxByte() is hardware independent and puts the received byte into a circular buffer although it can also do flow control when the buffer is getting full) and wakes receive tasks if required.
fnSciTxByte() gets and sends the next queued byte or disables the transmitter when there are no more to be sent (otherwise it signals an empty tx buffer all the time).

Since the interrupt mask register is write only I found it necessary to keep a backup of it on each channel so that it was possible to control the interrupt masks without the possibility of inadvertently corrupting other settings. I believe that this could also be a problem in your code since you are using the following construct:MCF_UART1_UIMR &= ~MCF_UART_UIMR_RXRDY_FU;
When UIMR is written it sets the mask bits. When it is read it returns the present state and not the mask bit settings. This means that the if a flag is set it will be set as a mask which is not the idea of the instruction (assuming that I have understood the register defines correctly).
Regards and good luck
 
Mark Butcher
www.mjbc.ch
0 Kudos

636 Views
mjbcswitzerland
Specialist V

Hi Jeff

I have implemented a UART driver for the three UARTs in the M5223X for the uTasker operating system with integrated TCP/IP stack - see the following posing:

http://forums.freescale.com/freescale/board/message?board.id=CFCOMM&message.id=274

The uTasker also simulates the UARTs on a PC and is available free for non-commercial work.

It is too late to check your code in detail tonight but a couple of first thoughts:

1. It is generally best to put the interrupts in a while loop to avoid quitting when a new interrupt arrives.
2. I found it best (easiest) to always disable the transmitter when it is not in use since it otherwise causes an empty interrupt always to be signalled and this is difficult to distinguish in the general rx/tx interrupt routine. I think that you have also done this
3. It is probably best to read MCF_UART1_UISR only once during a while loop in the interrupt routine. Store it temporarily in a variable and check the flags from the variable and not each time from the register since this may be resetting the opposite interrupt flag (which is probably happening in your case).

I have a driver with RTS/CTS, XON-XOFF/escape sequening/scan sequence support. All code is available with a simple educational license agreement (or for evaluation purposes). If it could help please contact me directly. I think that point 3 will in fact solve your present problem but tell me if it doesn't and I will look in more detail.

Regards

Mark Butcher
www.mjbc.ch


 

0 Kudos

636 Views
JeffB
Contributor I

I forgot to mention that if I disable the transmitter none of this happens.

 

 

0 Kudos