mcf5272_serial.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,138 行 · 第 1/3 页

C
1,138
字号
            if (chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX)
                umr1_mask |= MCF5272_UART_UMR1_RXRTS;
            else umr1_mask &= ~MCF5272_UART_UMR1_RXRTS;
          
            if (chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_TX)
                umr2_mask |= MCF5272_UART_UMR2_TXCTS;
            else umr2_mask &= ~MCF5272_UART_UMR2_TXCTS;

            // Reset mode register pointer
            HAL_WRITE_UINT8(&port->base->ucr, MCF5272_UART_UCR_RESET_MR);
          
            // Write mode register 1
            HAL_WRITE_UINT8(&port->base->umr, umr1_mask);

            // Write mode register 2
            HAL_WRITE_UINT8(&port->base->umr, umr2_mask);

            // Unmask UART interrupt
            cyg_drv_interrupt_unmask(port->uart_vector);
        }
        break;
#endif // CYGOPT_IO_SERIAL_FLOW_CONTROL_HW

        default:
            return EINVAL;
    }
    
    return ENOERR;
}


// ***************************************************************************
//  MCF5272_uart_start_xmit() - Enable the transmitter on the device.
//
//  INPUT:
//    chan - pointer to the serial private data.

static void MCF5272_uart_start_xmit(serial_channel *chan)
{
    MCF5272_uart_info_t *port = (MCF5272_uart_info_t *) chan->dev_priv;

    
    // Mask UART interrupt to prevent race conditions
    cyg_drv_interrupt_mask(port->uart_vector);
    
    // Enable the UART transmitter.
    // Eventually, preserve the ongoing autobaud calculation.
#ifdef REQUESTED_AUTOBAUD
    if(port->autobaud_state == AB_BEGIN)
      HAL_WRITE_UINT8(&port->base->ucr, (MCF5272_UART_UCR_TX_ENABLED | 
                                         MCF5272_UART_UCR_ENAB));
    else
#endif
    {
        HAL_WRITE_UINT8(&port->base->ucr, MCF5272_UART_UCR_TX_ENABLED);
    }
    
    // Enable transmitter interrupt
    port->imr_mirror |= MCF5272_UART_UIMR_TXRDY;
    HAL_WRITE_UINT8(&port->base->uisr_uimr, port->imr_mirror);

    // Unmask UART interrupt
    cyg_drv_interrupt_unmask(port->uart_vector);
}


// ***************************************************************************
// MCF5272_uart_stop_xmit() - Disable the transmitter on the device
//
// INPUT:
//    chan - pointer to the serial private data.

static void MCF5272_uart_stop_xmit(serial_channel * chan)
{   
    MCF5272_uart_info_t *port = (MCF5272_uart_info_t *) chan->dev_priv;

    
    // Mask UART interrupt to prevent race conditions
    cyg_drv_interrupt_mask(port->uart_vector);

    // Disable transmitter interrupt
    port->imr_mirror &= ~MCF5272_UART_UIMR_TXRDY;
    HAL_WRITE_UINT8(&port->base->uisr_uimr, port->imr_mirror);

    // Disable the UART transmitter.
    // Eventually, preserve the ongoing autobaud calculation.
    //   !!!!!!!!!!!!!
    //   !!!WARNING!!!
    //   !!!!!!!!!!!!!
    //   If the transmitter is disabled
    //   the diag_printf routines will poll forever to transmit the
    //   a character. Hence, don't ever disable the transmitter if
    //   you want it to work with diag_printf.
#ifdef REQUESTED_AUTOBAUD
    if(port->autobaud_state == AB_BEGIN)
      HAL_WRITE_UINT8(&port->base->ucr, (MCF5272_UART_UCR_TX_DISABLED | 
                                         MCF5272_UART_UCR_ENAB));
    else
#endif
    {
        HAL_WRITE_UINT8(&port->base->ucr, MCF5272_UART_UCR_TX_DISABLED);
    }
    
    // Unmask UART interrupt
    cyg_drv_interrupt_unmask(port->uart_vector);
}


// ***************************************************************************
// MCF5272_uart_ISR() - UART I/O interrupt interrupt service routine (ISR).
//
// INPUT:
//    vector - the interrupt vector number.
//    data   - user parameter.
//
// RETURN:
//     returns CYG_ISR_CALL_DSR to call the DSR.

static cyg_uint32 MCF5272_uart_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
    serial_channel *chan = (serial_channel *) data;
    MCF5272_uart_info_t *port = (MCF5272_uart_info_t *) chan->dev_priv;


    // Write the value in the interrupt status register back
    // to the mask register to disable the interrupt temporarily.
    HAL_WRITE_UINT8(&port->base->uisr_uimr, 0);

    // Cause DSR to run
    return CYG_ISR_CALL_DSR;
}


// ***************************************************************************
// MCF5272_uart_DSR() - Defered Service Routine (DSR) - This routine processes
//                      the interrupt from the device.
//
// INPUT:
//    vector - The interrupt vector number.
//    count  - The nunber of DSR requests.
//    data   - Device specific information.
//
// The autobaud feature is implemented by means of a simple finite state
// machine which can take the following states:
// AB_DISABLED: autobaud is disabled.
// AB_IDLE: no autobaud calculation is in progress. If autobaud calculation
//          has completed, retrieve the new baud rate.
// AB_BEGIN_BREAK: the start of a break character was detected.
// AB_BEGIN: the end of a break character was detected. Start autobaud
//           calculation.
//
// The state diagram is the following:
// AB_IDLE --> AB_BEGIN_BREAK --> AB_BEGIN --> Back to AB_IDLE
// The state AB_DISABLED is isolated and means that the autobaud feature is
// not active for that port.

static void MCF5272_uart_DSR(cyg_vector_t vector, cyg_ucount32 count,
                             cyg_addrword_t data)
{
    serial_channel *chan = (serial_channel *) data;
    MCF5272_uart_info_t *port = (MCF5272_uart_info_t *) chan->dev_priv;
    volatile cyg_uint8 isr;
    cyg_uint8 uisr_uimr;


    while (1)
    {
        // First of all, the exit condition
        
        // Retrieve the interrupt status bits. We use these status bits to
        // figure out what process should we perform: read from the UART or
        // inform of a completion of a data transmission.
        HAL_READ_UINT8(&port->base->uisr_uimr, uisr_uimr);
        isr = uisr_uimr & port->imr_mirror;
        
        // If there are no more events pending, exit the loop
        if (!isr) break;
        
#ifdef REQUESTED_AUTOBAUD
        switch (port->autobaud_state)
        {
            case AB_DISABLED:
                // Nothing to check for
                break;

            case AB_BEGIN_BREAK:
                if (isr & MCF5272_UART_UISR_DB)
                {
                    // Detected the end of a break, set the state to
                    // AB_BEGIN, and setup autobaud detection.
                    port->autobaud_state = AB_BEGIN;

                    // Initialize divider
                    HAL_WRITE_UINT8(&port->base->udu, 0);
                    HAL_WRITE_UINT8(&port->base->udl, 0);

                    // Reset the Delta Break bit in the UISR and
                    //Enable autobaud
                    HAL_WRITE_UINT8(&port->base->ucr, 
                                    MCF5272_UART_UCR_RESET_BKCHGINT);

                    HAL_WRITE_UINT8(&port->base->ucr, MCF5272_UART_UCR_ENAB);

                    // Enable autobaud completion interrupt
                    port->imr_mirror |= MCF5272_UART_UIMR_ABC;

                    // Disable the delta break interrupt so we can't receive
                    // anymore break interrupt.
                    port->imr_mirror &= ~MCF5272_UART_UIMR_DB;

                }
                break;

            case AB_BEGIN:
                if (isr & MCF5272_UART_UISR_ABC)
                {
                    int count;
                    unsigned int threshold;
                    cyg_uint8 uabu, uabl;
               
                    // Retrieve the detected baud rate
                    HAL_READ_UINT8(&port->base->uabu, uabu);
                    HAL_READ_UINT8(&port->base->uabl, uabl);
                    
                    cyg_uint16 divider = (uabu << 8) + uabl;

                    // Search in the list to find a match
                    for (count = sizeof(dividers_table)/
                           sizeof(unsigned long) - 1;
                            count > 1; count--)
                    {
                        if (divider < dividers_table[count - 1]) break;
                    }

                    // Modify baud rate only if it is in range
                    if (count > 1)
                    {
                        // Set the baud rate to the nearest standard rate
                        threshold = (dividers_table[count] + 
                                     dividers_table[count - 1]) / 2;
                        port->config.baud = (divider < threshold) ? count : 
                          count - 1;
                    }

                    divider = dividers_table[port->config.baud];
                   
                    // Program the baud settings to the device
                    HAL_WRITE_UINT8(&port->base->udu, 
                                    (cyg_uint8)((divider & 0xFF00) >> 8));
                    HAL_WRITE_UINT8(&port->base->udl, 
                                    (cyg_uint8)(divider & 0x00FF));

                    // Autobaud completion
                    port->autobaud_state = AB_IDLE;

                    // Disable autobaud
                    HAL_WRITE_UINT8(&port->base->ucr, MCF5272_UART_UCR_NONE);

#if 0
                    // In case patch submitted July 11, 2005 gets committed
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
                    // Inform upper layers of the new baud rate
                    {
                        cyg_serial_line_status_t stat;
                        
                        stat.which = CYGNUM_SERIAL_STATUS_NEWBAUDRATE;
                        stat.value = port->config.baud;
                        
                        (chan->callbacks->indicate_status)(chan, &stat);
                    }
#endif
#endif
                    // Ignore autobaud completion interrupt
                    port->imr_mirror &= ~MCF5272_UART_UIMR_ABC;

                    // Reenable delta break interrupt
                    port->imr_mirror |= MCF5272_UART_UIMR_DB;

                }
                break;

            default:
            case AB_IDLE:
                if (isr & MCF5272_UART_UISR_DB)
                {
                    // Detected the begin of a break, set the state to
                    // AB_BEGIN_BREAK
                    port->autobaud_state = AB_BEGIN_BREAK;

                    // Reset the delta break bit in the UISR
                    HAL_WRITE_UINT8(&port->base->ucr, 
                                    MCF5272_UART_UCR_RESET_BKCHGINT);
                }
                break;
        }
#endif // REQUESTED_AUTOBAUD

        // Receive character interrupt
        if ((isr & MCF5272_UART_UISR_RXRDY))
        {
            char c;
            cyg_uint8 usr_ucsr;
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
            cyg_serial_line_status_t stat;
#endif
            
            // Read all the characters in the fifo
            while (1)
            {
                // First of all, the exit condition

                // If there are no more characters waiting, exit the loop
                HAL_READ_UINT8(&port->base->uisr_uimr, uisr_uimr);
                if (!(uisr_uimr & MCF5272_UART_UISR_RXRDY)) break;

                // Read port status
                HAL_READ_UINT8(&port->base->usr_ucsr, usr_ucsr);
                
                // Received break
                if (usr_ucsr & MCF5272_UART_USR_RB)
                {
                    // Ignore break character
                    HAL_READ_UINT8(&port->base->urb_utb, c);
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
                    stat.which = CYGNUM_SERIAL_STATUS_BREAK;
                    (chan->callbacks->indicate_status)(chan, &stat);
#endif
                    continue;
                }

#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
                // Overrun error
                if (usr_ucsr & MCF5272_UART_USR_OE)
                {
                    stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR;
                    (chan->callbacks->indicate_status)(chan, &stat);
                }
#endif

#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
                // Framing error
                if (usr_ucsr & MCF5272_UART_USR_FE)
                {
                    stat.which = CYGNUM_SERIAL_STATUS_FRAMEERR;
                    (chan->callbacks->indicate_status)(chan, &stat);
                }
#endif

#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
                // Parity error
                if (usr_ucsr & MCF5272_UART_USR_PE)
                {
                    stat.which = CYGNUM_SERIAL_STATUS_PARITYERR;
                    (chan->callbacks->indicate_status)(chan, &stat);
                }
#endif

                // Read the character from the UART
                HAL_READ_UINT8(&port->base->urb_utb, c);

                // Pass the read character to the upper layer
                (chan->callbacks->rcv_char)(chan, c);
            }
        }

        // Transmit complete interrupt
        if ((isr & MCF5272_UART_UISR_TXRDY))
        {
            // Transmit holding register is empty
            (chan->callbacks->xmt_char)(chan);
        }
    }

    // Unmask all the UART interrupts that were masked in the ISR, so
    // that we can receive the next interrupt.
    HAL_WRITE_UINT8(&port->base->uisr_uimr, port->imr_mirror);
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?