📄 lh7a404_ssp_driver.c
字号:
/***********************************************************************
* $Workfile: lh7a404_ssp_driver.c $
* $Revision: 1.2 $
* $Author: KovitzP $
* $Date: Apr 07 2004 09:59:54 $
*
* Project: LH7A404 SSP driver
*
* Description:
* This file contains driver support for the SSP module on the
* LH7A404
*
* Revision History:
* $Log: //smaicnt2/pvcs/VM/sharpmcu/archives/sharpmcu/software/csps/lh7a404/source/lh7a404_ssp_driver.c-arc $
*
* Rev 1.2 Apr 07 2004 09:59:54 KovitzP
* Fixed bug in SSP_SET_DATA_BITS IOCTL.
*
* Rev 1.1 Mar 15 2004 08:35:54 WellsK
* Corrected default clock speed when device is opened.
*
* Rev 1.0 Jul 01 2003 11:06:02 WellsK
* 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 "lh7a404_csc_driver.h"
#include "lh7a404_ssp_driver.h"
/***********************************************************************
* SSP driver private data and types
**********************************************************************/
/* Speed of SSP peripheral clock */
#define SSP_CLOCK (CLOCK_MAINOSC / 2)
/* Size of SSP transmit and receive queues (ring buffers) */
#ifndef SSP_R_SIZE
#define SSP_R_SIZE 64
#endif
/* SSP device configuration structure type */
typedef struct
{
BOOL_32 init; /* Device initialized flag */
UNS_16 tx[SSP_R_SIZE]; /* SSP TX data */
UNS_16 rx[SSP_R_SIZE]; /* SSP RX data */
INT_32 tx_head; /* SSP TX 'put' entry index */
INT_32 tx_tail; /* SSP TX 'get' entry index */
INT_32 rx_head; /* SSP RX 'put' entry index */
INT_32 rx_tail; /* SSP RX 'get' entry index */
SSP_REGS_T *regptr; /* Pointer to SSP registers */
} SSP_CFG_T;
/* SSP device configuration structure */
STATIC SSP_CFG_T sspcfg;
/* SSP default clock rate when initialized */
#define DEFAULT_SSP_CLOCK (CLOCK_MAINOSC / 16)
/***********************************************************************
* SSP driver private functions
**********************************************************************/
/***********************************************************************
*
* Function: ssp_set_clock
*
* Purpose: Sets or resets the serial clock rate of the SSP interface
* (in Hz)
*
* Processing:
* Set the default divider and prescaler divider values to 1. Read
* the existing control 0 word and mask off the clock rate bits.
* Compute the optimum divider valye with a call to
* csc_compute_divider with the target_clock frequency and the
* default SSP peripheral clock. If the divider is 1, update the
* SSP control 0 register and SSP prescaler register with values
* to generate the fastest SSP clock available and exit. If the
* computed main divider is over 254, then limit the computed
* main divider to 254 and compute the prescaler divider by
* increment the prescaler divider and testing it against the
* computed frequency with the main SSP divider. Before exiting,
* write the computed main clock divider and prescaler divider
* values to the SSP control 0 and prescaler registers.
*
* Parameters:
* ssp_ptr : Pointer to SSP registers
* target_clock : The value in Hz for the new SSP serial clock
*
* Outputs: None
*
* Returns: Nothing
*
* Notes: None
*
**********************************************************************/
void ssp_set_clock(SSP_REGS_T *ssp_ptr, UNS_32 target_clock) /// CLOCK_MAINOSC / 16
{
UNS_32 control, prescale, ssp_div;
/* Always a minimum divider and prescaler value of 1 */
ssp_div = 1;
prescale = 1;
/* Get existing control word with clock data and clear prescale */
control = ssp_ptr->cr0 &= ~(SSP_CR0_SCR(255));
/* The SSP clock is derived from the (main system oscillator / 2),
so compute the best divider from that clock */
///ssp_div = csc_compute_divider(SSP_CLOCK, target_clock);
ssp_div = SSP_CLOCK / target_clock; ///8
/* If divider is above 254, then the prescaler is needed also */
if (ssp_div >= 255)
{
/* Limit SSP main divider to 254 (which corresponds to a 255
divider) */
ssp_div = 254;
/* Find the closest target clock frequency with the prescaler */
while ((prescale < 254) &&
(target_clock < (SSP_CLOCK / (prescale + ssp_div + 1))))
{
/* Try next prescale value */
prescale++;
}
}
/* Write computed prescaler and divider back to register */
ssp_ptr->cr0 = control | SSP_CR0_SCR(ssp_div - 1);
ssp_ptr->cpsr = prescale;
}
/***********************************************************************
*
* Function: ssp_get_free_tx_count
*
* Purpose: Returns the number of free spaces left in the TX queue
*
* Processing:
* Compute count by subtracting the tx_tail value from the tx_head
* value for the transmit queue. If the value is less than 0, then
* compute count by subtracting tx_tail from the queue size and
* adding in the tx_head index. Computed the 'used' value by
* subtracting the (count value + 1) from the queue size and return
* this value to the caller.
*
* Parameters:
* sspcfgptr : Pointer to an SSP config structure
*
* Outputs: None
*
* Returns: The number of free data entries in the TX ring buffer
*
* Notes: None
*
**********************************************************************/
INT_32 ssp_get_free_tx_count(SSP_CFG_T *sspcfgptr)
{
INT_32 count;
count = sspcfgptr->tx_head - sspcfgptr->tx_tail;
if (count < 0)
{
/* Head pointer has flipped to start of ring */
count = (SSP_R_SIZE - sspcfgptr->tx_tail) +
sspcfgptr->tx_head;
}
return (SSP_R_SIZE - count - 1);
}
/***********************************************************************
*
* Function: ssp_standard_receive
*
* Purpose: SSP data receive function
*
* Processing:
* While the receive FIFO is not empty, read the data from the FIFO
* and place it into the receive queue indexed by the rx_head index.
* Increment the rx_head index. If the rx_head_index goes past the
* size of the queue end index (SSP_R_SIZE), reset the rx_head_index
* to 0. Continue reading until the FIFO is empty.
*
* Parameters:
* sspcfgptr : Pointer to an SSP config structure
*
* Outputs: None
*
* Returns: Nothing
*
* Notes: None
*
**********************************************************************/
void ssp_standard_receive(SSP_CFG_T *sspcfgptr)
{
SSP_REGS_T *sspregs = sspcfgptr->regptr;
/* Is there data in the RX FIFO? */
while ((sspregs->sr & SSP_SR_RNE) != 0)
{
/* Read data from SSP and put in queue */
sspcfgptr->rx[sspcfgptr->rx_head] = (UNS_16) sspregs->dr;
/* Increment and limit rx queue head pointer */
sspcfgptr->rx_head++;
/* Reindex queue to start if needed */
if (sspcfgptr->rx_head >= SSP_R_SIZE)
{
sspcfgptr->rx_head = 0;
}
}
}
/***********************************************************************
*
* Function: ssp_standard_transmit
*
* Purpose: SSP data transmit function
*
* Processing:
* While the transmit queue is not empty and the SSP transmit FIFO
* has space in it, place the data value from the transmit queue
* indexed by tx_tail into the SSP transmit FIFO. Increment tx_tail.
* If tx_tail goes past the size of the queue end index
* (SSP_R_SIZE), reset the rx_head_index to 0. If the transmit FIFO
* is empty, disable the transmit FIFO service interrupt, otherwise
* enable it.
*
* Parameters:
* sspcfgptr : Pointer to an SSP config structure
*
* Outputs: None
*
* Returns: Nothing
*
* Notes: None
*
**********************************************************************/
void ssp_standard_transmit(SSP_CFG_T *sspcfgptr)
{
SSP_REGS_T *sspregs = sspcfgptr->regptr;
/* Continue until all data is sent or until TX FIFO is full */
while ((sspcfgptr->tx_head != sspcfgptr->tx_tail) &&
((sspregs->sr & SSP_SR_TNF) != 0))
{
/* Put entry in FIFO */
sspregs->dr = (UNS_32) sspcfgptr->tx[sspcfgptr->tx_tail];
/* Index to next transmit FIFO entry */
sspcfgptr->tx_tail++;
/* Re-index queue to start if needed */
if (sspcfgptr->tx_tail >= SSP_R_SIZE)
{
sspcfgptr->tx_tail = 0;
}
}
/* If the transmit queue is empty, disable the transmit interrupt,
otherwise enable it */
if (sspcfgptr->tx_head == sspcfgptr->tx_tail)
{
/* Empty queue, no more transmit interrupts */
sspregs->cr1 &= ~SSP_CR1_TIE;
}
else
{
/* Enable or keep transmit interrupts enabled */
sspregs->cr1 |= SSP_CR1_TIE;
}
}
/***********************************************************************
*
* Function: ssp_standard_interrupt
*
* Purpose: SSP standard interrupt function
*
* Processing:
* If the receive FIFO interrupt is pending, call the
* ssp_standard_receive function. If the transmit FIFO empty
* interrupt is pending, call the ssp_standard_transmit function.
* If the receive FIFO overrun interrupt is pending, disable and
* clear the receive overrun interrupt. If the transmitter idle
* interrupt is pending, disable the transmitter idle interrupt.
*
* Parameters:
* sspcfgptr : Pointer to an SSP config structure
*
* Outputs: None
*
* Returns: Nothing
*
* Notes: None
*
**********************************************************************/
void ssp_standard_interrupt(SSP_CFG_T *sspcfgptr)
{
SSP_REGS_T *sspregs = sspcfgptr->regptr;
/* Interrupt was due to a receive data FIFO service request */
if ((sspregs->iir_icr & SSP_IIR_RIS) != 0)
{
/* Receive interrupt */
ssp_standard_receive(sspcfgptr);
}
/* Interrupt was due to a transmit data FIFO service request */
if ((sspregs->iir_icr & SSP_IIR_TIS) != 0)
{
/* Transmit interrupt */
ssp_standard_transmit(sspcfgptr);
}
/* Interrupt was due to a receive FIFO overrun service request */
if ((sspregs->iir_icr & SSP_IIR_RORIS) != 0)
{
/* RX FIFO overrun, just clear interrupt and disable it */
sspregs->iir_icr = 0;
sspregs->cr1 &= ~SSP_CR1_RORIE;
}
/* Interrupt was due to a transmitter idle service request */
if ((sspregs->iir_icr & SSP_IIR_TXIDLE) != 0)
{
/* Disable this interrupt, not used */
sspregs->cr1 &= ~SSP_CR1_TXIDLE;
}
}
/***********************************************************************
* SSP driver public functions
**********************************************************************/
/***********************************************************************
*
* Function: ssp_open
*
* Purpose: Open the SSP
*
* Processing:
* If init is not FALSE, return NULL to the caller. Otherwise,
* set init to TRUE, save the SSP peripheral register set address,
* set the default states of the SSP device (SSP disabled, SSP clock
* is divide by 8, SPI mode, FIFOs enabled), and 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;
if ((sspcfg.init == FALSE) && ((SSP_REGS_T *) ipbase == SSP))
{
/* Device is valid and not previously initialized */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -