📄 lh7a400_ssp_driver.c
字号:
/**********************************************************************
* $Workfile: lh7a400_ssp_driver.c $
* $Revision: 1.5 $
* $Author: KovitzP $
* $Date: Nov 21 2001 09:39:34 $
*
* Project: LH79520 and eval board test
*
* Description:
* This file contains driver support for the Synchronous
* Serial Port (SSP) on the LH7A400. Functions are described below
* ssp_init_spi_mode()--Put the SSP into SPI mode with FIFOs off,
* interrupts off, default word length, default
* divider, SSP enabled, SPH==SPO==0
* ssp_init_uwire_mode()--Put the SSP into MICROWIRE mode with FIFOs
* off, interrupts off, default word length,
* default divider, SSP enabled
* ssp_init_ti_mode()--Put the SSP into TI mode with FIFOs off,
* interrupts off, default word length, default
* divider, SSP enabled, SPH==SPO==0
* ssp_disable()--Put the SSP into its default state with receiver
* overrun interrupts disabled.
* ssp_enable()--Gate on the SSP clock to enable to SSP.
* ssp_set_bits_per_word()--Set the bits per SSP word (5-16)
* ssp_get_bits_per_word()--return the number of bits per SSP word
* ssp_loopback_on()--put the SSP in loopback mode
* ssp_loopback_off()--take the SSP out of loopback mode.
* ssp_set_speed()--set the SSPCLK frequency as close to the requested
* bits per second as possible and return the actual
* bits per second.
* ssp_get_speed()--return the SSPCLK speed in bits per second.
* ssp_spi_set_spo()--For SPI mode, SSPCLK is normally high
* ssp_spi_clr_spo()--For SPI mode, SSPCLK is normally low
* ssp_spi_set_sph()--For SPI mode, data are sampled 1/2 SSPCLK cycle
* after the first SSPCLK transition in a frame
* ssp_spi_clr_sph()--For SPI mode, data are sampled on the first
* SSPCLK transition in a frame
* ssp_int_enable_receive_overflow()--enable SSP receiver overrun
* interrupts
* ssp_int_disable_receive_overflow()--disable SSP receiver overrun
* interrupts
* ssp_int_enable_receive()--enable SSP receiver interrupt
* ssp_int_disable_receive()--disable SSP receiver interrupt
* ssp_int_enable_transmit()--enable SSP transmitter interrupt
* ssp_int_disable_transmit()--disable SSP transmitter interrupt
* ssp_int_enable_transmit_idle()--enable SSP transmit idle interrupt
* ssp_int_disable_transmit_idle()--disable SSP transmit idle interrupt
* ssp_int_disable_all()--disable all SSP interrupts
* ssp_disable_fifos()--disable SSP transmitter and receiver FIFOs
* ssp_enable_fifos()--enable SSP transmitter and receiver FIFOs
* ssp_flush_fifos()--empty SSP transmitter and receiver FIFOs
* ssp_transmit()--write a single word to the transmitter FIFO (if
* FIFOs are enabled) or to the transmit register (if
* FIFOs are disabled). If the FIFO or transmit
* register are full, wait until the FIFO or
* register is available.
* ssp_transmit_block()--transmit an array of words by repeated calls
* to ssp_transmit()
* ssp_receive()--wait for a word to become available in the SSP
* receiver. Read the word and return it.
* ssp_receive_block()--Fill an array with all the words in the receive
* FIFO
* ssp_transceive()--Send a word and wait for the data return. Return
* the received data.
* ssp_transceive_block()--Send an array of data and record an array
* of returned data by multiple calls to
* ssp_transceive()
* ssp_fifos_enabled()--Return 1 if FIFOs are enabled.
* ssp_busy()--Return 1 if the SSP is transmitting a word
* ssp_receive_fifo_full()--Return 1 if the SSP receive FIFO is full
* ssp_receive_fifo_not_empty()--Return 1 if the SSP receive FIFO
* contains data
* ssp_transmit_fifo_not_full()--Return 1 if the SSP transmit FIFO is
* not full.
* ssp_transmit_fifo_empty()--Return 1 if the SSP transmit FIFO is empty
* ssp_loopback_is_on()--Return 1 if the SSP is in loopback mode.
* ssp_int_transmit_idle_enabled()--Return 1 if the SSP transmit idle
* interrupt is enabled
* ssp_int_receive_overflow_enabled()--Return 1 if the SSP receive
* overflow is enabled.
* ssp_int_receive_enabled()--Return 1 if the SSP receiver interrupt is
* enabled
* ssp_int_transmit_enabled()--Return 1 if the SSP transmitter interrupt
* is enabled
* ssp_spi_spo_is_set()--Return 1 if the SSP SPO bit is set
* ssp_spi_sph_is_set()--Return 1 if the SSP SPH bit is set
* ssp_clr_receive_overflow()--Clear any pending SSP receiver overflow
* interrupts
* ssp_int_transmit_idle()--Return 1 if the SSP transmitter idle
* interrupt is pending.
* ssp_int_transmit()--Return 1 if the SSP transmitter interrupt is
* pending.
* ssp_int_receive()--Return 1 if the SSP receiver interrupt is pending
* ssp_int_receive_overflow()--Return 1 if the SSP receiver overflow
* interrupt is pending.
*
* Revision History:
* $Log: P:/PVCS6_6/archives/LH7A400 (Aruba)/SSP/lh7a400_ssp_driver.c-arc $
*
* Rev 1.5 Nov 21 2001 09:39:34 KovitzP
* All block comments added.
*
* Rev 1.4 Nov 20 2001 18:37:14 KovitzP
* Added block comment headers to all functions. All fields
* of block headers are not filled in yet.
*
* Rev 1.3 Nov 19 2001 10:25:22 KovitzP
* Corrected bugs in interrupt enable/disable functions. Removed
* some dead code for installing interrupt handlers.
*
* Rev 1.2 Nov 14 2001 09:01:46 KovitzP
* Modified include hierarchy
*
* Rev 1.1 Nov 12 2001 17:47:52 KovitzP
* Used derived clock symbol from clock and state controller include file instead of a hard number.
*
* Rev 1.0 Nov 12 2001 13:32:10 KovitzP
* Initial revision.
*
*
* COPYRIGHT (C) 2001 SHARP MICROELECTRONICS OF THE AMERICAS, INC.
* CAMAS, WA
*********************************************************************/
#include "LH7A400_ssp_driver.h"
/* sspclk is the ssp state machine clock */
static const UNS_32 sspclk=CLKSC_SSP_CLK;
/**********************************************************************
*
* Function: init_ssp
*
* Purpose:
* Put the SSP in the default state with the clock gated on.
*
* Processing:
* Clear the control registers with the enable bit set. This disables
* the FIFOs and the interrupts
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
* This function is called by the three initialize functions
*
**********************************************************************/
static void init_ssp(void)
{
/* gate on the SSP clock */
SSP->cr0 = SSP_CR0_SSE;
/* flush the FIFOs. disable all interrupts */
SSP->cr1 = 0;
}
/**********************************************************************
*
* Function: ssp_init_spi_mode
*
* Purpose:
* Put the SSP into SPI mode with FIFOs off, interrupts off,
* default word length, default divider, SSP enabled, SPH==SPO==0
*
* Processing:
* Call init_ssp() and then change to SPI mode.
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
*
**********************************************************************/
void ssp_init_spi_mode(void)
{
init_ssp();
SSP->cr0 |= SSP_CR0_FRF_MOT;
}
/**********************************************************************
*
* Function: ssp_init_uwire_mode
*
* Purpose:
* Put the SSP into MICROWIRE mode with FIFOs off, interrupts off,
* default word length, default divider, SSP enabled
*
* Processing:
* Call init_ssp() and then change to MICROWIRE mode.
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
*
**********************************************************************/
void ssp_init_uwire_mode(void)
{
init_ssp();
SSP->cr0 |= SSP_CR0_FRF_NS;
}
/**********************************************************************
*
* Function: ssp_init_ti_mode
*
* Purpose:
* Put the SSP into TI mode with FIFOs off, interrupts off,
* default word length, default divider, SSP enabled
*
* Processing:
* Call init_ssp() and then change to TI mode.
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
*
**********************************************************************/
void ssp_init_ti_mode(void)
{
init_ssp();
SSP->cr0 |= SSP_CR0_FRF_TI;
}
/**********************************************************************
*
* Function: ssp_enable
*
* Purpose:
* Gate the SSP clock on
*
* Processing:
* set the enable bit
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
*
**********************************************************************/
void ssp_enable(void)
{
SSP->cr0 |= SSP_CR0_SSE;
}
/**********************************************************************
*
* Function: ssp_disable
*
* Purpose:
* Gate the SSP clock off; disable interrupts and clear any pending
* receiver overrun interrupts.
*
* Processing:
* Clear the control registers and write to the interrupt control
* register.
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
*
**********************************************************************/
void ssp_disable(void)
{
/* set control registers to their reset defaults */
SSP->cr1 = 0;
SSP->cr0 = 0;
/* clear any receive overruns */
SSP->u.icr = SSP_IIR_RORIS;
}
/* control functions */
/**********************************************************************
*
* Function: ssp_set_bits_per_word
*
* Purpose:
* Set the number of bits per word to nbits. Return
* the previous bits per word session.
*
* Processing:
* Read the current bits per word.
*
* Parameters:
* nbits: number of bits per word (4-16)
*
* Outputs: None
*
* Returns:
* previously set number of bits per word.
*
* Notes:
* If the requested bits per word is out of range, the function
* returns the curent setting and has no effect on the word length.
*
**********************************************************************/
INT_32 ssp_set_bits_per_word(INT_32 nbits)
{
INT_32 saved_nbits = ssp_get_bits_per_word();
if (nbits > 16 || nbits < 4)
return saved_nbits;
SSP->cr0 &= ~ SSP_CR0_DSS(16); /* clear all bits */
SSP->cr0 |= SSP_CR0_DSS(nbits);
return saved_nbits;
}
/**********************************************************************
*
* Function: ssp_get_bits_per_word
*
* Purpose:
* return the number of bits per SSP word
*
* Processing:
* AND off all non-word size bits in Control Register 0. Shift
* the result right to the bit position of the the LSB of the
* word size bit field, and add 1. Return the result.
*
* Parameters: None
*
* Outputs: None
*
* Returns:
* The number of bits per word.
*
* Notes:
* If the number of bits per word set is less than 4, the results
* of this function are not predictable.
*
**********************************************************************/
/* Return the number of bits per word */
INT_32 ssp_get_bits_per_word(void)
{
return ( (SSP->cr0 & SSP_CR0_DSS(16)) / SSP_CR0_DSS(2) ) + 1;
}
/**********************************************************************
*
* Function: ssp_loopback_on
*
* Purpose:
* put the SSP in loopback mode
*
* Processing:
* Set the LBM bit in control register 1
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
*
**********************************************************************/
/* turn SSP loopback on */
void ssp_loopback_on(void)
{
SSP->cr1 |= SSP_CR1_LBM;
}
/**********************************************************************
*
* Function: ssp_loopback_off
*
* Purpose:
* take the SSP out of loopback mode.
*
* Processing:
* Clear the LBM bit in control register 1
*
* Parameters: None
*
* Outputs: None
*
* Returns: Nothing
*
* Notes:
*
**********************************************************************/
/* turn SSP loopback off */
void ssp_loopback_off(void)
{
SSP->cr1 &= ~ SSP_CR1_LBM;
}
/**********************************************************************
*
* Function: ssp_set_speed
*
* Purpose:
* set the SSPCLK frequency as close to the requested bits per second
* as possible and return the actual bits per second.
*
* Processing:
* If the requested bits per second is greater than or equal to
* the maximum possible SSPCLK frequency, set the divider and
* the prescaler to the minimum values.
*
* If not too fast, compute the closest integer result of dividing the
* base sspclk frequency (approximately 7.4 MHz) by the requested
* bits per second. If this quotient is larger than the result
* of multiplying the maximum possible prescaler by the maximum
* possible divider, then the requested clock is too slow. Use
* the maximum possible divider and prescaler to get the slowest
* SSPCLK speed.
*
* If the clock frequency is neither too fast nor too slow, then
* step through all possible prescaler values to determine which
* combination of prescaler and divider values give the SSPCLK
* value closest to the requested bits per second.
*
* Set the SSP clock prescaler register to the prescaler value
* determined above. Clear out the old divider value from SSP
* control register 0 and OR in the new divider value - 1. Return
* the value returned by ssp_get_speed().
*
* Parameters:
* requested_bits_per_second: the desired frequency of the SSPCLK
* pin in bits per second.
*
* Outputs: None
*
* Returns:
* 0 if the requested frequency is illegal. Otherwise, return
* the actual clock speed.
*
* Notes:
*
**********************************************************************/
INT_32 ssp_set_speed(INT_32 requested_bits_per_second)
{
INT_32 ssp_prescale;
INT_32 ssp_divider;
INT_32 new_prescale;
INT_32 new_divider;
INT_32 quotient;
INT_32 delta1;
INT_32 delta2;
INT_32 min_error;
INT_32 new_error;
if (requested_bits_per_second <= 0)
/* requested frequency is illegal. Return 0 */
return 0;
/* get the dividers as close to the requested freq as possible */
if (requested_bits_per_second >= MAX_SSP_FREQ)
{
/* requested clock frequency is too fast. Set to max freq */
ssp_prescale = SSP_PRESCALE_MIN;
ssp_divider = 1;
}
else
{
/* compute the required divider as close as possible */
quotient = sspclk / requested_bits_per_second;
/* round the quotient */
delta1 = requested_bits_per_second - (sspclk / quotient );
if (delta1 < 0)
delta1 = - delta1;
delta2 = requested_bits_per_second - (sspclk / (quotient + 1) );
if (delta2 < 0)
delta2 = - delta2;
if (delta1 > delta2)
quotient++;
if (quotient >= (SSP_PRESCALE_MAX * SSP_DIVIDER_MAX) )
{
/*
Then requested clock frequency is <= minimum possible.
Make it as slow as possible.
*/
ssp_prescale = SSP_PRESCALE_MAX;
ssp_divider = SSP_DIVIDER_MAX;
}
else
{
/*
The computed quotient is in range.
quotient is the target clock divide frequency;
get as close as possible
*/
/*
factor the quotient into the divider and prescaler combo
that minimizes the error in the quotient by exhaustively
searching all legal ssp prescaler values.
*/
ssp_prescale = SSP_PRESCALE_MIN;
ssp_divider = (quotient / ssp_prescale);
ssp_divider = (ssp_divider > SSP_DIVIDER_MAX) ?
SSP_DIVIDER_MAX :
ssp_divider;
min_error = quotient - (ssp_divider * ssp_prescale);
min_error = (min_error < 0) ? -min_error : min_error;
for (new_prescale = SSP_PRESCALE_MIN + 2;
new_prescale < SSP_PRESCALE_MAX;
new_prescale += 2)
{
new_divider = (quotient / new_prescale);
new_divider = (new_divider > SSP_DIVIDER_MAX) ?
SSP_DIVIDER_MAX :
new_divider;
new_error = quotient - (new_divider * new_prescale);
new_error = (new_error < 0) ? -new_error : new_error;
if (new_error < min_error)
{
min_error = new_error;
ssp_prescale = new_prescale;
ssp_divider = new_divider;
}
}
} /* end if quotient is greater than max */
} /* end if requested frequency is too large */
SSP->cpsr = ssp_prescale;
SSP->cr0 &= ~SSP_CR0_SCR(SSP_DIVIDER_MAX - 1); /* clear old divider value */
SSP->cr0 |= SSP_CR0_SCR(ssp_divider - 1);
return ssp_get_speed();
}
/**********************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -