📄 lh79524_ssp_driver.c
字号:
/***********************************************************************
* $Workfile: lh79524_ssp_driver.c $
* $Revision: 1.0 $
* $Author: ZhangJ $
* $Date: Oct 20 2004 09:48:56 $
*
* Project: LH79520 SSP driver
*
* Description:
* This file contains driver support for the SSP module on the
* LH79520
*
* Revision History:
* $Log: //smaicnt2/pvcs/VM/sharpmcu/archives/sharpmcu/software/csps/lh79524/source/lh79524_ssp_driver.c-arc $
*
* Rev 1.0 Oct 20 2004 09:48:56 ZhangJ
* Initial revision.
*
* Rev 1.0 Jul 07 2003 16:40:00 LiJ
* Initial revision.
*
* SHARP MICROELECTRONICS OF THE AMERICAS MAKES NO REPRESENTATION
* OR WARRANTIES WITH RESPECT TO THE PERFORMANCE OF THIS SOFTWARE,
* AND SPECIFICALLY DISCLAIMS ANY RESPONSIBILITY FOR ANY DAMAGES,
* SPECIAL OR CONSEQUENTIAL, CONNECTED WITH THE USE OF THIS SOFTWARE.
*
* SHARP MICROELECTRONICS OF THE AMERICAS PROVIDES THIS SOFTWARE SOLELY
* FOR THE PURPOSE OF SOFTWARE DEVELOPMENT INCORPORATING THE USE OF A
* SHARP MICROCONTROLLER OR SYSTEM-ON-CHIP PRODUCT. USE OF THIS SOURCE
* FILE IMPLIES ACCEPTANCE OF THESE CONDITIONS.
*
* COPYRIGHT (C) 2001 SHARP MICROELECTRONICS OF THE AMERICAS, INC.
* CAMAS, WA
*
***********************************************************************/
#include "lh79524_rcpc.h"
#include "lh79524_iocon.h"
#include "lh79524_dmac.h"
#include "lh79524_vic.h"
#include "lh79524_gpio.h"
#include "lh79524_ssp_driver.h"
#include "sdk79524_board.h"
/***********************************************************************
* SSP driver private data
**********************************************************************/
STATIC INT_32 ssp_set_speed(UNS_32 requested_bits_per_second);
STATIC INT_32 ssp_get_speed(void);
STATIC INT_32 hclk_freq;
/* SSP device configuration structure */
STATIC SSP_CFG_T sspcfg;
#define SSPFRM_GPIO_BIT _BIT(2)
/**********************************************************************
*
* Function: ssp_set_speed
*
* Purpose:
* return the SSP receive timeout in microseconds
*
* Processing:
* If the requested_bits_per_second is too fast, set the bit rate
* as fast as possible.
* If the requested_bits_per_second is too slow, set the bit rate as
* slow as possible.
* If the requested_bits_per_second is in range, set the RCPC
* SSP clock prescaler register, SSP prescaler, and SSP divider
* to obtain the clock as close as possible.
*
* Parameters:
* requested_bits_per_second: The desired bits per second
*
* Outputs: None
*
* Returns:
* the actual bits per second obtained or 0 if the requested bits
* per second is not obtainable.
*
* Notes:
* The ssp_init_spi_mode, ssp_init_uwire_mode, or ssp_init_ti_mode
* must be called first in order for this function to work.
* RCPC write lock bit need to be cleared using
* rcpc_reg_write_unlock() function call before using this function
*
* set serial ssp serial clock rate. The bit rate is
* fSSPCLK + (CPSDVSR * (1+SCR)), where CPSDVSR is an
* even value from 2 to 254 and SCR is a value from 0-255)
*
**********************************************************************/
STATIC INT_32 ssp_set_speed(UNS_32 requested_bits_per_second)
{
INT_32 ssp_prescale;
INT_32 ssp_divider;
INT_32 rcpc_prescale;
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;
/* 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;
rcpc_prescale = 1;
}
else
{
/* compute the required divider as close as possible */
quotient = hclk_freq / requested_bits_per_second;
//assert(quotient > 0);
/* round the quotient */
delta1 = requested_bits_per_second - (hclk_freq / quotient );
if (delta1 < 0)
delta1 = - delta1;
delta2 = requested_bits_per_second
- (hclk_freq / (quotient + 1) );
if (delta2 < 0)
delta2 = - delta2;
if (delta1 > delta2)
quotient++;
if (quotient >= (SSP_PRESCALE_MAX
* RCPC_SSP_PRESCALE_MAX
* SSP_DIVIDER_MAX) )
{
/*
Then requested clock frequency is <= minimum possible.
Make it as slow as possible.
*/
rcpc_prescale = RCPC_SSP_PRESCALE_MAX;
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
*/
rcpc_prescale = 1;
/*
try to reduce power by using RCPC prescaler
Note that the ssp prescaler minimum is two
so can only prescale and maintain accuracy
if quotient is divisble by 4.
*/
while ( ((quotient & 0x3) == 0)
&& (rcpc_prescale < RCPC_SSP_PRESCALE_MAX) )
{
quotient >>= 1;
rcpc_prescale <<= 1;
}
/*
Make sure the requested frequency is within range
of the SPP's prescaler and divider.
Hopefully, this loop never executes. If it does,
accuracy suffers.
*/
while (quotient > (SSP_PRESCALE_MAX * SSP_DIVIDER_MAX) )
{
rcpc_prescale <<= 1;
quotient >>= 1;
}
/*
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 */
/* set up registers */
RCPC->sspclkprescale = rcpc_prescale >> 1;
SSP->cpsr = ssp_prescale;
SSP->ctrl0 &= 0xff; /* clear old divider value */
SSP->ctrl0 |= SSP_CR0_SCR(ssp_divider - 1);
return ssp_get_speed();
}
/**********************************************************************
*
* Function: ssp_get_speed
*
* Purpose:
* return the SSP speed in bits per second.
*
* Processing:
*
*
* Parameters:
* time_in_us: The timeout interval in microseconds.
*
* Outputs: None
*
* Returns:
* Return the SSP bits per second
*
* Notes: None
*
**********************************************************************/
STATIC INT_32 ssp_get_speed(void)
{
INT_32 rcpc_prescale;
INT_32 ssp_prescale=1;
INT_32 ssp_divider;
if (ssp_prescale == 0)
return 0;
rcpc_prescale = RCPC->sspclkprescale;
if (rcpc_prescale == 0)
rcpc_prescale = 1;
else
rcpc_prescale <<= 1;
ssp_prescale = SSP->cpsr;
ssp_divider = (SSP->ctrl0 & _SBF(8,_BITMASK(8) ) ) >> 8;
return hclk_freq / (rcpc_prescale
* (ssp_prescale)
* (ssp_divider + 1) );
}
/***********************************************************************
* SSP driver public functions
**********************************************************************/
/***********************************************************************
*
* Function: ssp_open
*
* Purpose: Open the SSP controller
*
* Processing:
* If init is not FALSE, return 0x00000000 to the caller. Otherwise,
* return a pointer to the SSP config structure to the caller.
*
* Parameters:
* ipbase: SSP descriptor device address
*
* Outputs: None
*
* Returns: The pointer to a SSP config structure or 0
*
* Notes: None
*
**********************************************************************/
INT_32 ssp_open(void *ipbase, INT_32 arg)
{
INT_32 status = 0;
INT_32 temp1;
if ((sspcfg.init == FALSE) && ((SSP_REGS_T *) ipbase == SSP))
{
/* Device is valid and not previously initialized */
sspcfg.init = TRUE;
/* Save and return address of peripheral block */
sspcfg.regptr = (SSP_REGS_T *) ipbase;
/* Return pointer to SSP configuration structure */
status = (INT_32) &sspcfg;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -