📄 lh79524_adc_driver.c
字号:
/***********************************************************************
* $Workfile: lh79524_adc_driver.c $
* $Revision: 1.0 $
* $Author: ZhangJ $
* $Date: Oct 20 2004 09:48:54 $
*
* Project: LH79524 ADC driver
*
* Description:
* This file contains driver support for the ADC module on the
* LH79524
*
* Notes:
* The number of different configurations supported by the ADC
* is beyond the scope of this driver. This driver provides the
* following basic functions:
* Sequenced analog to digital conversions (polled, interrupt)
* Touchscreen conversions (polled, interrupt)
* Pendown state detect to touchscreen conversions (interrupt)
* Brownout interrupt handling (interrupt only)
* ADC status polling and general configuration
*
* Revision History:
* $Log:: //smaicnt2/pvcs/VM/sharpmcu/archives/sharpmcu/software/csps$
*
* Rev 1.0 Oct 20 2004 09:48:54 ZhangJ
* Initial revision.
*
* Rev 1.1 Jul 20 2004 16:50:32 PattamattaD
* Updated comments.
*
* Rev 1.0 Jun 25 2004 14:26:02 PattamattaD
* Initial revision.
*
*
*
***********************************************************************
*
* Copyright (c) 2004 Sharp Microelectronics of the Americas
*
* All rights reserved
*
* 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.
*
*********************************************************************/
#include "lh79524_adc_driver.h"
#include "lh79524_rcpc.h"
#include "lh79524_iocon.h"
/***********************************************************************
* ADC driver private data and types
**********************************************************************/
/* Size of the ADC receive ring buffer */
#define ADC_RING_BUFSIZE 64
/* Function prototype used for polled and interrupt driven reads */
typedef INT_32 (*ADC_RFUNC_T) (void *, void *, INT_32);
/* ADC device configuration structure type */
typedef struct
{
BOOL_32 init; /* Device initialized flag */
ADC_REGS_T *regptr; /* Pointer to ADC registers */
UNS_16 rx[ADC_RING_BUFSIZE]; /* ADC data ring buffer */
INT_32 rx_head; /* ADC ring buffer head index */
INT_32 rx_tail; /* ADC ring buffer tail index */
UNS_16 conv_count; /* Number of conversions to perform */
PFV bro_cbfunc; /* Brown interrupt callback function */
PFV pd_cbfunc; /* Pendown interrupt completion callback
function */
PFV eos_cbfunc; /* End of sequence interrupt callback
function */
UNS_32 adc_clk_source; /* clock coming to ADC block from RCPC */
} ADC_CFG_T;
/* ADC device configuration structure */
static ADC_CFG_T adccfg;
/***********************************************************************
* ADC driver private functions
**********************************************************************/
/***********************************************************************
*
* Function: adc_get_clock
*
* Purpose:
* Return the ADC's time base in Hz
*
* Processing:
* Compute the time base using the system clock and the divider.
*
* Parameters:
* adccfgptr: Pointer to ADC config structure
*
* Outputs: None.
*
* Returns:
* the actual base frequency in Hz or _ERROR if the frequency is
* out of range for the converter or the system clock.
*
* Notes: None
*
**********************************************************************/
static UNS_32 adc_get_clock(ADC_CFG_T* adccfgptr)
{
UNS_32 freq;
freq = adccfgptr->adc_clk_source /
(1 << ADC_CLKSEL_GET(adccfgptr->regptr->adcpcr));
if (freq < ADC_MIN_CLK || freq > ADC_MAX_CLK)
return 0;
return freq;
}
/***********************************************************************
*
* Function: adc_set_clock
*
* Purpose:
* Set the ADC's time base
*
* Processing:
* Use the system clock and the divider to get as close as possible
* without going outside the min and max operating frequency of the
* ADC.
*
* Parameters:
* freq: the desired base frequency in Hz
* adccfgptr: Pointer to ADC config structure
*
* Outputs: None.
*
* Returns:
* the actual base frequency in Hz or _ERROR if a legal frequency is
* not obtainable using the system clock.
*
* Notes:
* If the requested frequency is too high, the maximum frequency
* is returned. If the requested frequency is too low, the minimum
* frequency is returned. If the requested frequency, even after
* clipping it to be in range, cannot be generated using the system
* clock, then this function returns _ERROR. This implies that the
* system clock is too high or too low to use the ADC.
*
**********************************************************************/
static UNS_32 adc_set_clock(UNS_32 freq, ADC_CFG_T* adccfgptr)
{
/* clk is the freq. of the clock that drives the ADC block, in Hz */
INT_32 clk;
INT_32 divisor_idx; /* the power of 2 of the current divisor */
INT_32 best_divisor_idx; /* the value of divisor_idx for best_delta*/
INT_32 divisor; /* 1 << divisor_idx*/
INT_32 new_freq; /* clk / divisor*/
INT_32 delta; /* abs(new_freq - freq)*/
INT_32 best_delta; /* the smallest value of delta found so far*/
UNS_32 temp; /* intermediate value of the PC register*/
/* max_clk is the highest value of freq the ADC can handle */
INT_32 max_clk = (ADC_MAX_CLK < adccfgptr->adc_clk_source) ?
ADC_MAX_CLK : adccfgptr->adc_clk_source;
if (freq > max_clk)
freq = max_clk;
if (freq < ADC_MIN_CLK)
freq = ADC_MIN_CLK;
clk = adccfgptr->adc_clk_source;
best_divisor_idx = 0;
best_delta = freq - clk;
if (best_delta < 0)
best_delta = -best_delta;
for (divisor_idx = 0;
divisor_idx < (1 << ADC_CLKSEL_WIDTH);
divisor_idx++)
{
divisor = (1 << divisor_idx);
new_freq = clk / divisor;
if (new_freq >= ADC_MIN_CLK && new_freq <= ADC_MAX_CLK)
{
delta = freq - new_freq;
if (delta < 0)
delta = -delta;
if (delta < best_delta)
{
best_delta = delta;
best_divisor_idx = divisor_idx;
}
}
}
/* do logic in temp to prevent multiple writes to ADC->pcr */
temp = (adccfgptr->regptr->adcpcr & ~ADC_CLKSEL_MASK);
temp |= ADC_CLKSEL_SET(best_divisor_idx);
adccfgptr->regptr->adcpcr = temp;
return adc_get_clock(adccfgptr);
}
/***********************************************************************
*
* Function: adc_time_2_clock
*
* Purpose:
* Convert usec delay time to ADC clocks.
*
* Processing:
* Compute the number of ADC clocks equivalent to the time passed.
*
* Parameters:
* adccfgptr: Pointer to ADC config structure
* time: time duration to be converted in to ADC clocks
*
* Outputs:
*
* Returns:
* The number of ADC clocks equivalent to the time passed.
*
* Notes: None
*
**********************************************************************/
static UNS_32 adc_time_2_clock(ADC_CFG_T* adccfgptr, UNS_32 time)
{
UNS_32 adc_clcks = 0;
if(time > 0)
{
/* calculate the settling time clocks */
adc_clcks = adc_get_clock(adccfgptr) /
(1000000 / time);
if(adc_clcks > _BIT(ADC_SETTIME_WIDTH))
adc_clcks = _BITMASK(ADC_SETTIME_WIDTH);
}
return adc_clcks;
}
/***********************************************************************
*
* Function: adc_default
*
* Purpose: Places the ADC interface and controller in a default state
*
* Processing:
* Disable ADC interrupts, set ADC power control to off (idle) with
* maximum clock divider and 1 conversion, set general configuration
* to continuous conversion mode. Set up each MUX input to maximum
* clock with default references. Set up idle high and low control
* words. Clear the ADC FIFO and clear any latched interrupts.
*
* Parameters:
* adcregs: Pointer to an ADC register set
* xtal_in: On board clock osillator frequency. If 0 the ADC clock
* settings are not changed.
* sel_SCOC: Specifies whether to use XTAL_IN as ADC clock source
*
* Outputs:
*
* Returns:
* ADC clock frequency.
*
* Notes: None
*
**********************************************************************/
static UNS_32 adc_default(ADC_REGS_T *adcregs, UNS_32 xtal_in,
BOOL_32 sel_SCOC)
{
INT_32 idx;
UNS_32 temp;
UNS_32 adc_clock_source;
if(xtal_in)
{
/* setup and enable ADC clock in RCPC registers */
RCPC->adcclkprescale = RCPC_PRESCALER_DIV1;
if(sel_SCOC == TRUE)
{
RCPC->periphclksel |= RCPC_PCLKSEL1_ADC_SCOC;
/* since we set ADC predivisor to 1 ADC clock source equal to
System oscilator - XTAL_IN */
adc_clock_source = xtal_in;
}
else
{
/* select HCLK as ADC clock source */
RCPC->periphclksel &= ~RCPC_PCLKSEL1_ADC_SCOC;
/* since we set ADC predivisor to 1 ADC clock source equal to
HCLK*/
adc_clock_source = RCPC_GET_HCLK(xtal_in);
}
/* enable clock to ADC block */
RCPC->periphclkctrl1 &= ~RCPC_CLKCTRL1_ADC_DISABLE;
}
/* enable ADC pins in IOCON registers */
temp = IOCON->mux_ctl_25;
temp &= (IOCON_MUX_MASK(IOCON_RES25_AN2_PJ3) &
IOCON_MUX_MASK(IOCON_RES25_AN3_PJ0) &
IOCON_MUX_MASK(IOCON_RES25_AN4_PJ1) &
IOCON_MUX_MASK(IOCON_RES25_AN5_PJ5_INT5) &
IOCON_MUX_MASK(IOCON_RES25_AN6_PJ7_INT7) &
IOCON_MUX_MASK(IOCON_RES25_AN7_PJ6_INT6) &
IOCON_MUX_MASK(IOCON_RES25_AN8_PJ4) &
IOCON_MUX_MASK(IOCON_RES25_AN9_PJ2));
temp |= IOCON_MUX25_AN2_LL | IOCON_MUX25_AN3_LR |
IOCON_MUX25_AN4_WIPER | IOCON_MUX25_AN5 |
IOCON_MUX25_AN6 | IOCON_MUX25_AN7 | IOCON_MUX25_AN8 |
IOCON_MUX25_AN9;
IOCON->mux_ctl_25 = temp;
/* Setup default state of ADC as idle, with all channels set up
in a safe configuration, and all interrupts masked */
/* Mask all interrupts */
adcregs->adcie = 0;
/* Setup mode as disabled, 1 conversion in a sequence, and
maximum ADC clock divider */
adcregs->adcpcr = (ADC_NOC_SET(1) | ADC_PWM_ALL_OFF |
ADC_CLKSEL_DIV128);
/* Set conversion mode to continuous with a single FIFO entry
watermark level */
adcregs->adcgencfg = (ADC_SSM_SSB | ADC_WM_SET(1));
/* Set up each control bank register to use a linear analog
input mapped array with maximum settling time */
for (idx = 0; idx <= 15; idx++)
{
/* High control word */
adcregs->adchicl[idx] = (ADC_SETTIME(0x1FF) |
ADC_INPSEL(idx) | ADC_INM_GND | ADC_REF_VREFP);
/* Low control word */
adcregs->adclocl[idx] = ADC_REF_VREFN;
}
/* Setup idle mode high and low words using analog input 0
as the default idle input */
adcregs->adcidlehi = (ADC_SETTIME(0x1FF) | ADC_INP_AN0 |
ADC_INM_GND | ADC_REF_VREFP);
adcregs->adcidlelo = ADC_REF_VREFP;
/* Make sure the FIFO is clear */
while ((adcregs->adcfifosts & ADC_FIFO_EMPTY) == 0)
{
idx = (INT_32) adcregs->adcresults;
}
/* Clear pending ADC interrupts */
adcregs->adcic = (ADC_EOS_CLR | ADC_PEN_CLR | ADC_BROWNOUT_CLR);
return adc_clock_source;
}
/***********************************************************************
*
* Function: adc_add_sequence_entry
*
* Purpose: Adds a new sample entry to the sequence list
*
* Processing:
* If the number of existing conversion sequence entries is greater
* than 16, then return _ERROR to the caller. Otherwise, generate
* the high and low sequence control words and place them into the
* the sequence register array indexed by conv_count. Set status to
* conv_count and increment conv_count by one. Return conv_count to
* the caller.
*
* Parameters:
* adc_ch_cfgptr: Pointer to a channel configuration structure
* adccfgptr: Pointer to ADC config structure
*
* Outputs: None
*
* Returns: A key to the new sequence entry (0 to 15)
*
* Notes: None
*
**********************************************************************/
static STATUS adc_add_sequence_entry(ADC_CH_CONV_T *adc_ch_cfgptr,
ADC_CFG_T *adccfgptr)
{
UNS_32 tmphi, tmplo;
STATUS status = _ERROR;
/* Limit conversion count to 16 conversions */
if (adccfgptr->conv_count < 16)
{
/* Build the high and low control words based on the passed
channel configuration values */
tmphi = (ADC_SETTIME(adc_time_2_clock(adccfgptr, adc_ch_cfgptr->settle_time))
| ADC_INPSEL(adc_ch_cfgptr->inpc)
| ADC_INNSEL(adc_ch_cfgptr->innc)
| ADC_REFP_SEL(adc_ch_cfgptr->vrefp));
/* Build and save the control bank low word register */
tmplo = (ADC_REFN_SEL(adc_ch_cfgptr->vrefn) |
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -