tx3904_serial.c

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

C
758
字号
    {
        cyg_count32 i,j,k;

        for( i = 0; i < clock_loop; i++ )
            for( j = 0; j < div_loop; j++ )
                for( k = 0; k < bit_time; k++ )
                    continue;
    }
}

//-------------------------------------------------------------------------

static bool
tx3904_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
{
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
    cyg_uint16 cr = 0;
    cyg_uint16 icr = 0;
    cyg_uint16 baud_divisor = select_baud[new_config->baud];

    if (baud_divisor == 0)
        return false;  // Invalid baud rate selected

    // set up other config values:

    cr |= select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5];
    cr |= select_stop_bits[new_config->stop];
    cr |= select_parity[new_config->parity];

    // Source transfer clock from BRG
    cr |= LCR_BRG;

#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
    // Enable RX interrupts only at present
#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
    if ((chan->out_cbuf.len != 0) || (chan == &tx3904_serial_channel0)) {
#else
    if (chan->out_cbuf.len != 0) {
#endif
        icr |= ICR_RXE;
    }
#endif

    // Avoid any interrupts while we are fiddling with the line parameters.
    cyg_drv_interrupt_mask(tx3904_chan->int_num);

    
    // In theory we should wait here for the transmitter to drain the
    // FIFO so we dont change the line parameters with characters
    // unsent. Unfortunately the TX39 serial devices do not allow us
    // to discover when the FIFO is empty.

    delay_char_time(tx3904_chan, 8);
    
    // Disable device entirely.
//    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, 0);
//    HAL_WRITE_UINT8(tx3904_chan->base+SERIAL_ICR, 0);

    // Reset the FIFOs

    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 7);
    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 0);
    
    // Set up baud rate

    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_BRG, baud_divisor );

    // Write CR into hardware
    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, cr);    

    // Write ICR into hardware
    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_ICR, icr);    

    // Re-enable interrupts.
    cyg_drv_interrupt_unmask(tx3904_chan->int_num);

    // Save current baud rate
    tx3904_chan->cur_baud = new_config->baud;

    if (new_config != &chan->config) {
        chan->config = *new_config;
    }
    return true;
}

//-------------------------------------------------------------------------
// Function to initialize the device.  Called at bootstrap time.

bool tx3904_serial_init(struct cyg_devtab_entry *tab)
{
    serial_channel *chan = (serial_channel *)tab->priv;
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;

    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices

    tx3904_chan->cur_baud = CYGNUM_SERIAL_BAUD_38400;
        
#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
    if (chan->out_cbuf.len != 0) {
        // Install and enable the interrupt
        cyg_drv_interrupt_create(tx3904_chan->int_num,
                                 4,                      // Priority
                                 (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
                                 (cyg_ISR_t *)tx3904_serial_ISR,
                                 tx3904_serial_DSR,
                                 &tx3904_chan->interrupt_handle,
                                 &tx3904_chan->interrupt);
        cyg_drv_interrupt_attach(tx3904_chan->interrupt_handle);
        cyg_drv_interrupt_unmask(tx3904_chan->int_num);
    }
#endif
    
    tx3904_serial_config_port(chan, &chan->config, true);
    
    return true;
}

//-------------------------------------------------------------------------
// This routine is called when the device is "looked" up (i.e. attached)

static Cyg_ErrNo 
tx3904_serial_lookup(struct cyg_devtab_entry **tab, 
                  struct cyg_devtab_entry *sub_tab,
                  const char *name)
{
    serial_channel *chan = (serial_channel *)(*tab)->priv;
    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
    return ENOERR;
}

//-------------------------------------------------------------------------
// Return 'true' if character is sent to device

bool
tx3904_serial_putc(serial_channel *chan, unsigned char c)
{
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
    cyg_uint16 isr;

    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );

    if( isr & ISR_TXRDY )
    {
        HAL_WRITE_UINT8( tx3904_chan->base+SERIAL_TXB, c );

        isr &= ~ISR_TXRDY;
        
        HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );

        return true;
    }
    else return false;
}

//-------------------------------------------------------------------------

unsigned char 
tx3904_serial_getc(serial_channel *chan)
{
    unsigned char c;
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
    cyg_uint16 isr;

    do
    {
        HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );

        // Eliminate any RX errors
        if( isr & ISR_ERROR )
        {
            cyg_uint16 sr = 0;
            
            isr &= ISR_ERROR;

//            HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );

            HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, sr );            
            HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
        }
        
    } while( (isr & ISR_RXRDY) != ISR_RXRDY );
    
    HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, c );

    isr &= ~ISR_RXRDY;
        
    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );

    return c;
}

//-------------------------------------------------------------------------

static Cyg_ErrNo
tx3904_serial_set_config(serial_channel *chan, cyg_uint32 key,
                         const void *xbuf, cyg_uint32 *len)
{
    switch (key) {
    case CYG_IO_SET_CONFIG_SERIAL_INFO:
      {
        cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
        if ( *len < sizeof(cyg_serial_info_t) ) {
            return -EINVAL;
        }
        *len = sizeof(cyg_serial_info_t);
        if ( true != tx3904_serial_config_port(chan, config, false) )
            return -EINVAL;
      }
      break;
    default:
        return -EINVAL;
    }
    return ENOERR;
}

//-------------------------------------------------------------------------
// Enable the transmitter on the device

static void
tx3904_serial_start_xmit(serial_channel *chan)
{
#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
    cyg_uint16 icr;

    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );

    icr |= ICR_TXE;

    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
#endif    
}

//-------------------------------------------------------------------------
// Disable the transmitter on the device

static void 
tx3904_serial_stop_xmit(serial_channel *chan)
{
#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
    cyg_uint16 icr;

    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );

    icr &= ~ICR_TXE;

    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
#endif    
}

//-------------------------------------------------------------------------
// Serial I/O - low level interrupt handlers (ISR)

#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE

static cyg_uint32 
tx3904_serial_ISR(cyg_vector_t vector, cyg_addrword_t data, cyg_addrword_t *regs)
{
    serial_channel *chan = (serial_channel *)data;
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
    cyg_uint8 isr;
    cyg_uint32 result = 0;
    
    cyg_drv_interrupt_mask(tx3904_chan->int_num);
    cyg_drv_interrupt_acknowledge(tx3904_chan->int_num);

    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );

    // Eliminate any RX errors
    if( isr & ISR_ERROR )
    {
        cyg_uint16 sr = 0;
            
        isr &= ~ISR_ERROR;

        HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );
        
        HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, 0 );            
    }

    // Check for a TX interrupt and set the flag if so.
    if( isr & ISR_TXRDY )
    {
        isr &= ~ISR_TXRDY;

        tx3904_chan->output_ready = true;

        result |= CYG_ISR_CALL_DSR;  // Cause DSR to be run
    }
    
    
    // Check here for an RX interrupt and fetch the character. If it
    // is a ^C then call into GDB stub to handle it.
    
    if( isr & ISR_RXRDY )
    {
        cyg_uint8 rxb;
        HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );

        isr &= ~ISR_RXRDY;

        if( cyg_hal_is_break( &rxb , 1 ) )
            cyg_hal_user_break( regs );
        else
        {
            tx3904_chan->input_char = rxb;
            tx3904_chan->input_char_valid = true;
            result |= CYG_ISR_CALL_DSR;  // Cause DSR to be run
        }

    }

    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
    
    return result;
}


#endif

//-------------------------------------------------------------------------
// Serial I/O - high level interrupt handler (DSR)

#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE

static void       
tx3904_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    serial_channel *chan = (serial_channel *)data;
    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
    cyg_uint8 isr;

    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );

    if( tx3904_chan->input_char_valid )
    {
        (chan->callbacks->rcv_char)(chan, tx3904_chan->input_char);

        tx3904_chan->input_char_valid = false;

#if 0        
        // And while we are here, pull any further characters out of the
        // FIFO. This should help to reduce the interrupt rate.

        HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
    
        while( isr & ISR_RXRDY )
        {
            cyg_uint8 rxb;
            HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );

            (chan->callbacks->rcv_char)(chan, rxb);
            
            isr &= ~ISR_RXRDY;
        
            HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
            HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );        
        }
#endif
        
    }

    if( tx3904_chan->output_ready )
    {
        (chan->callbacks->xmt_char)(chan);

        tx3904_chan->output_ready = false;
    }
    
    cyg_drv_interrupt_unmask(tx3904_chan->int_num);
}

#endif
#endif // CYGPKG_IO_SERIAL_TX39_JMR3904

//-------------------------------------------------------------------------
// EOF tx3904_serial.c

⌨️ 快捷键说明

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