997 lines
31 KiB
C
997 lines
31 KiB
C
/* --COPYRIGHT--,BSD
|
|
* Copyright (c) 2017, Texas Instruments Incorporated
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* --/COPYRIGHT--*/
|
|
//*****************************************************************************
|
|
//
|
|
// cs.c - Driver for the cs Module.
|
|
//
|
|
//*****************************************************************************
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! \addtogroup cs_api cs
|
|
//! @{
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#include "inc/hw_memmap.h"
|
|
|
|
#if defined(__MSP430_HAS_CS__) || defined(__MSP430_HAS_SFR__)
|
|
#include "cs.h"
|
|
|
|
#include <assert.h>
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// The following value is used by CS_getACLK, CS_getSMCLK, CS_getMCLK to
|
|
// determine the operating frequency based on the available DCO frequencies.
|
|
//
|
|
//*****************************************************************************
|
|
#define CS_DCO_FREQ_1 1000000
|
|
#define CS_DCO_FREQ_2 2670000
|
|
#define CS_DCO_FREQ_3 3330000
|
|
#define CS_DCO_FREQ_4 4000000
|
|
#define CS_DCO_FREQ_5 5330000
|
|
#define CS_DCO_FREQ_6 6670000
|
|
#define CS_DCO_FREQ_7 8000000
|
|
#define CS_DCO_FREQ_8 16000000
|
|
#define CS_DCO_FREQ_9 20000000
|
|
#define CS_DCO_FREQ_10 24000000
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Internal very low power VLOCLK, low frequency oscillator with 10kHz typical
|
|
// frequency, internal low-power oscillator MODCLK with 5 MHz typical
|
|
// frequency and LFMODCLK is MODCLK divided by 128.
|
|
//
|
|
//*****************************************************************************
|
|
#define CS_VLOCLK_FREQUENCY 10000
|
|
#define CS_MODCLK_FREQUENCY 5000000
|
|
#define CS_LFMODCLK_FREQUENCY 39062
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// The following value is used by CS_XT1Start, CS_bypassXT1,
|
|
// CS_XT1StartWithTimeout, CS_bypassXT1WithTimeout to properly set
|
|
// the XTS bit. This frequency threshold is specified in the User's Guide.
|
|
//
|
|
//*****************************************************************************
|
|
#define LFXT_FREQUENCY_THRESHOLD 50000
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// LFXT crystal frequency. Should be set with
|
|
// CS_externalClockSourceInit if LFXT is used and user intends to invoke
|
|
// CS_getSMCLK, CS_getMCLK, CS_getACLK and
|
|
// CS_turnOnLFXT, CS_LFXTByPass, CS_turnOnLFXTWithTimeout,
|
|
// CS_LFXTByPassWithTimeout.
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t privateLFXTClockFrequency = 0;
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// The HFXT crystal frequency. Should be set with
|
|
// CS_externalClockSourceInit if HFXT is used and user intends to invoke
|
|
// CS_getSMCLK, CS_getMCLK, CS_getACLK,
|
|
// CS_turnOnLFXT, CS_LFXTByPass, CS_turnOnLFXTWithTimeout,
|
|
// CS_LFXTByPassWithTimeout.
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t privateHFXTClockFrequency = 0;
|
|
|
|
static uint32_t privateCSASourceClockFromDCO (uint8_t clockdivider)
|
|
{
|
|
uint32_t CLKFrequency = 0;
|
|
|
|
if (HWREG16(CS_BASE + OFS_CSCTL1)& DCORSEL) {
|
|
switch(HWREG16(CS_BASE + OFS_CSCTL1) & DCOFSEL_7) {
|
|
case DCOFSEL_0:
|
|
CLKFrequency=CS_DCO_FREQ_1/clockdivider;
|
|
break;
|
|
case DCOFSEL_1:
|
|
CLKFrequency=CS_DCO_FREQ_5/clockdivider;
|
|
break;
|
|
case DCOFSEL_2:
|
|
CLKFrequency=CS_DCO_FREQ_6/clockdivider;
|
|
break;
|
|
case DCOFSEL_3:
|
|
CLKFrequency=CS_DCO_FREQ_7/clockdivider;
|
|
break;
|
|
case DCOFSEL_4:
|
|
CLKFrequency=CS_DCO_FREQ_8/clockdivider;
|
|
break;
|
|
case DCOFSEL_5:
|
|
CLKFrequency=CS_DCO_FREQ_9/clockdivider;
|
|
break;
|
|
case DCOFSEL_6:
|
|
case DCOFSEL_7:
|
|
CLKFrequency=CS_DCO_FREQ_10/clockdivider;
|
|
break;
|
|
default:
|
|
CLKFrequency=0;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch(HWREG16(CS_BASE + OFS_CSCTL1) & DCOFSEL_7) {
|
|
case DCOFSEL_0:
|
|
CLKFrequency=CS_DCO_FREQ_1/clockdivider;
|
|
break;
|
|
case DCOFSEL_1:
|
|
CLKFrequency=CS_DCO_FREQ_2/clockdivider;
|
|
break;
|
|
case DCOFSEL_2:
|
|
CLKFrequency=CS_DCO_FREQ_3/clockdivider;
|
|
break;
|
|
case DCOFSEL_3:
|
|
CLKFrequency=CS_DCO_FREQ_4/clockdivider;
|
|
break;
|
|
case DCOFSEL_4:
|
|
CLKFrequency=CS_DCO_FREQ_5/clockdivider;
|
|
break;
|
|
case DCOFSEL_5:
|
|
CLKFrequency=CS_DCO_FREQ_6/clockdivider;
|
|
break;
|
|
case DCOFSEL_6:
|
|
case DCOFSEL_7:
|
|
CLKFrequency=CS_DCO_FREQ_7/clockdivider;
|
|
break;
|
|
default:
|
|
CLKFrequency=0;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return (CLKFrequency);
|
|
}
|
|
|
|
static uint32_t privateCSAComputeCLKFrequency (uint16_t CLKSource,
|
|
uint16_t CLKSourceDivider
|
|
)
|
|
{
|
|
uint32_t CLKFrequency = 0;
|
|
uint8_t CLKSourceFrequencyDivider = 1;
|
|
uint8_t i = 0;
|
|
|
|
//Determine Frequency divider
|
|
for ( i = 0; i < CLKSourceDivider; i++) {
|
|
CLKSourceFrequencyDivider *= 2;
|
|
}
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Determine clock source based on CLKSource
|
|
switch (CLKSource) {
|
|
|
|
//If LFXT is selected as clock source
|
|
case SELM__LFXTCLK:
|
|
CLKFrequency = (privateLFXTClockFrequency /
|
|
CLKSourceFrequencyDivider);
|
|
|
|
//Check if LFXTOFFG is not set. If fault flag is set
|
|
//VLO is used as source clock
|
|
if (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) {
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);
|
|
//Clear OFIFG fault flag
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
|
|
if (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) {
|
|
CLKFrequency = CS_LFMODCLK_FREQUENCY;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SELM__VLOCLK:
|
|
CLKFrequency =
|
|
(CS_VLOCLK_FREQUENCY / CLKSourceFrequencyDivider);
|
|
break;
|
|
|
|
case SELM__LFMODOSC:
|
|
CLKFrequency =
|
|
(CS_LFMODCLK_FREQUENCY / CLKSourceFrequencyDivider);
|
|
|
|
break;
|
|
|
|
case SELM__DCOCLK:
|
|
CLKFrequency =
|
|
privateCSASourceClockFromDCO( CLKSourceFrequencyDivider);
|
|
|
|
break;
|
|
|
|
case SELM__MODOSC:
|
|
CLKFrequency =
|
|
(CS_MODCLK_FREQUENCY / CLKSourceFrequencyDivider);
|
|
|
|
break;
|
|
|
|
case SELM__HFXTCLK:
|
|
CLKFrequency =
|
|
(privateHFXTClockFrequency / CLKSourceFrequencyDivider);
|
|
|
|
if (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {
|
|
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~HFXTOFFG;
|
|
//Clear OFIFG fault flag
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
}
|
|
|
|
if (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {
|
|
CLKFrequency = CS_MODCLK_FREQUENCY;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
|
|
return (CLKFrequency);
|
|
}
|
|
|
|
void CS_setExternalClockSource (uint32_t LFXTCLK_frequency,
|
|
uint32_t HFXTCLK_frequency
|
|
)
|
|
{
|
|
privateLFXTClockFrequency = LFXTCLK_frequency;
|
|
privateHFXTClockFrequency = HFXTCLK_frequency;
|
|
}
|
|
|
|
void CS_initClockSignal (uint8_t selectedClockSignal,
|
|
uint16_t clockSource,
|
|
uint16_t clockSourceDivider
|
|
)
|
|
{
|
|
|
|
//Verify User has selected a valid Frequency divider
|
|
assert(
|
|
(CS_CLOCK_DIVIDER_1 == clockSourceDivider) ||
|
|
(CS_CLOCK_DIVIDER_2 == clockSourceDivider) ||
|
|
(CS_CLOCK_DIVIDER_4 == clockSourceDivider) ||
|
|
(CS_CLOCK_DIVIDER_8 == clockSourceDivider) ||
|
|
(CS_CLOCK_DIVIDER_16 == clockSourceDivider) ||
|
|
(CS_CLOCK_DIVIDER_32 == clockSourceDivider)
|
|
);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
uint16_t temp = HWREG16(CS_BASE + OFS_CSCTL3);
|
|
switch (selectedClockSignal) {
|
|
case CS_ACLK:
|
|
assert(
|
|
(CS_LFXTCLK_SELECT == clockSource) ||
|
|
(CS_VLOCLK_SELECT == clockSource) ||
|
|
(CS_LFMODOSC_SELECT == clockSource)
|
|
);
|
|
|
|
clockSourceDivider = clockSourceDivider << 8;
|
|
clockSource = clockSource << 8;
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL2) &= ~(SELA_7);
|
|
HWREG16(CS_BASE + OFS_CSCTL2) |= (clockSource);
|
|
HWREG16(CS_BASE + OFS_CSCTL3) = temp & ~(DIVA0 + DIVA1 + DIVA2) |
|
|
clockSourceDivider;
|
|
break;
|
|
case CS_SMCLK:
|
|
assert(
|
|
(CS_LFXTCLK_SELECT == clockSource) ||
|
|
(CS_VLOCLK_SELECT == clockSource) ||
|
|
(CS_DCOCLK_SELECT == clockSource) ||
|
|
(CS_HFXTCLK_SELECT == clockSource) ||
|
|
(CS_LFMODOSC_SELECT== clockSource) ||
|
|
(CS_MODOSC_SELECT == clockSource)
|
|
);
|
|
|
|
clockSource = clockSource << 4;
|
|
clockSourceDivider = clockSourceDivider << 4;
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL2) &= ~(SELS_7);
|
|
HWREG16(CS_BASE + OFS_CSCTL2) |= clockSource;
|
|
HWREG16(CS_BASE + OFS_CSCTL3) = temp & ~(DIVS0 + DIVS1 + DIVS2) |
|
|
clockSourceDivider;
|
|
break;
|
|
case CS_MCLK:
|
|
assert(
|
|
(CS_LFXTCLK_SELECT == clockSource) ||
|
|
(CS_VLOCLK_SELECT == clockSource) ||
|
|
(CS_DCOCLK_SELECT == clockSource) ||
|
|
(CS_HFXTCLK_SELECT == clockSource) ||
|
|
(CS_LFMODOSC_SELECT== clockSource) ||
|
|
(CS_MODOSC_SELECT == clockSource)
|
|
);
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL2) &= ~(SELM_7);
|
|
HWREG16(CS_BASE + OFS_CSCTL2) |= clockSource;
|
|
HWREG16(CS_BASE + OFS_CSCTL3) = temp & ~(DIVM0 + DIVM1 + DIVM2) |
|
|
clockSourceDivider;
|
|
break;
|
|
}
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
void CS_turnOnLFXT (uint16_t lfxtdrive
|
|
)
|
|
{
|
|
assert(privateLFXTClockFrequency != 0);
|
|
|
|
assert((lfxtdrive == CS_LFXT_DRIVE_0 ) ||
|
|
(lfxtdrive == CS_LFXT_DRIVE_1 ) ||
|
|
(lfxtdrive == CS_LFXT_DRIVE_2 ) ||
|
|
(lfxtdrive == CS_LFXT_DRIVE_3 ));
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Switch ON LFXT oscillator
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTOFF;
|
|
|
|
//Highest drive setting for LFXTstartup
|
|
HWREG16(CS_BASE + OFS_CSCTL4_L) |= LFXTDRIVE1_L + LFXTDRIVE0_L;
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTBYPASS;
|
|
|
|
//Wait for Crystal to stabilize
|
|
while (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG)
|
|
{
|
|
//Clear OSC flaut Flags fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);
|
|
|
|
//Clear OFIFG fault flag
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
}
|
|
|
|
//Set requested Drive mode
|
|
HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
|
|
~(LFXTDRIVE_3)
|
|
) |
|
|
(lfxtdrive);
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
void CS_turnOffSMCLK(void)
|
|
{
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= SMCLKOFF;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
void CS_turnOnSMCLK(void)
|
|
{
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~SMCLKOFF;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
void CS_bypassLFXT (void)
|
|
{
|
|
//Verify user has set frequency of LFXT with SetExternalClockSource
|
|
assert(privateLFXTClockFrequency != 0);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
assert(privateLFXTClockFrequency < LFXT_FREQUENCY_THRESHOLD);
|
|
|
|
//Set LFXT in LF mode Switch off LFXT oscillator and enable BYPASS mode
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= (LFXTBYPASS + LFXTOFF);
|
|
|
|
//Wait until LFXT stabilizes
|
|
while (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG)
|
|
{
|
|
//Clear OSC flaut Flags fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);
|
|
|
|
//Clear the global fault flag. In case the LFXT caused the global fault
|
|
//flag to get set this will clear the global error condition. If any
|
|
//error condition persists, global flag will get again.
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
}
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
bool CS_turnOnLFXTWithTimeout (uint16_t lfxtdrive,
|
|
uint32_t timeout
|
|
)
|
|
{
|
|
assert(privateLFXTClockFrequency != 0);
|
|
|
|
assert((lfxtdrive == CS_LFXT_DRIVE_0 ) ||
|
|
(lfxtdrive == CS_LFXT_DRIVE_1 ) ||
|
|
(lfxtdrive == CS_LFXT_DRIVE_2 ) ||
|
|
(lfxtdrive == CS_LFXT_DRIVE_3 ));
|
|
|
|
assert(timeout > 0);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Switch ON LFXT oscillator
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTOFF;
|
|
|
|
//Highest drive setting for LFXTstartup
|
|
HWREG16(CS_BASE + OFS_CSCTL4_L) |= LFXTDRIVE1_L + LFXTDRIVE0_L;
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTBYPASS;
|
|
|
|
while ((HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) && --timeout)
|
|
{
|
|
//Clear OSC fault Flags fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);
|
|
|
|
//Clear the global fault flag. In case the LFXT caused the global fault
|
|
//flag to get set this will clear the global error condition. If any
|
|
//error condition persists, global flag will get again.
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
}
|
|
|
|
if(timeout) {
|
|
//Set requested Drive mode
|
|
HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
|
|
~(LFXTDRIVE_3)
|
|
) |
|
|
(lfxtdrive);
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
else {
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
return (STATUS_FAIL);
|
|
}
|
|
}
|
|
|
|
bool CS_bypassLFXTWithTimeout (uint32_t timeout
|
|
)
|
|
{
|
|
assert(privateLFXTClockFrequency != 0);
|
|
|
|
assert(privateLFXTClockFrequency < LFXT_FREQUENCY_THRESHOLD);
|
|
|
|
assert(timeout > 0);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Set LFXT in LF mode Switch off LFXT oscillator and enable BYPASS mode
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= (LFXTBYPASS + LFXTOFF);
|
|
|
|
while ((HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) && --timeout)
|
|
{
|
|
//Clear OSC fault Flags fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);
|
|
|
|
//Clear the global fault flag. In case the LFXT caused the global fault
|
|
//flag to get set this will clear the global error condition. If any
|
|
//error condition persists, global flag will get again.
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
|
|
}
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
|
|
if (timeout) {
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
else {
|
|
return (STATUS_FAIL);
|
|
}
|
|
}
|
|
|
|
void CS_turnOffLFXT (void)
|
|
{
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Switch off LFXT oscillator
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= LFXTOFF;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
void CS_turnOnHFXT (uint16_t hfxtdrive
|
|
)
|
|
{
|
|
assert(privateHFXTClockFrequency != 0);
|
|
|
|
assert((hfxtdrive == CS_HFXT_DRIVE_4MHZ_8MHZ )||
|
|
(hfxtdrive == CS_HFXT_DRIVE_8MHZ_16MHZ )||
|
|
(hfxtdrive == CS_HFXT_DRIVE_16MHZ_24MHZ )||
|
|
(hfxtdrive == CS_HFXT_DRIVE_24MHZ_32MHZ ));
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Switch ON HFXT oscillator
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTOFF;
|
|
|
|
//Disable HFXTBYPASS mode and Switch on HFXT oscillator
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTBYPASS;
|
|
|
|
//If HFFrequency is (16, 24] MHz
|
|
if (privateHFXTClockFrequency > 16000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
|
|
}
|
|
//If HFFrequency is (8, 16] MHz
|
|
else if (privateHFXTClockFrequency > 8000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
|
|
}
|
|
//If HFFrequency is (4, 8] MHz
|
|
else if (privateHFXTClockFrequency > 4000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
|
|
}
|
|
//If HFFrequency is [0, 4] MHz
|
|
else {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
}
|
|
|
|
while (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {
|
|
//Clear OSC fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);
|
|
|
|
//Clear OFIFG fault flag
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
}
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
|
|
~(CS_HFXT_DRIVE_24MHZ_32MHZ)
|
|
) |
|
|
(hfxtdrive);
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
void CS_bypassHFXT (void)
|
|
{
|
|
//Verify user has initialized value of HFXTClock
|
|
assert(privateHFXTClockFrequency != 0);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Switch off HFXT oscillator and set it to BYPASS mode
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= ( HFXTBYPASS + HFXTOFF );
|
|
|
|
//If HFFrequency is (16, 24] MHz
|
|
if (privateHFXTClockFrequency > 16000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
|
|
}
|
|
//If HFFrequency is (8, 16] MHz
|
|
else if (privateHFXTClockFrequency > 8000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
|
|
}
|
|
//If HFFrequency is (4, 8] MHz
|
|
else if (privateHFXTClockFrequency > 4000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
|
|
}
|
|
//If HFFrequency is [0, 4] MHz
|
|
else {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
}
|
|
|
|
while (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {
|
|
//Clear OSC fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);
|
|
|
|
//Clear OFIFG fault flag
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
}
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
bool CS_turnOnHFXTWithTimeout (uint16_t hfxtdrive,
|
|
uint32_t timeout
|
|
)
|
|
{
|
|
//Verify user has initialized value of HFXTClock
|
|
assert(privateHFXTClockFrequency != 0);
|
|
|
|
assert(timeout > 0);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Switch on HFXT oscillator
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTOFF;
|
|
|
|
//Disable HFXTBYPASS mode
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTBYPASS;
|
|
|
|
//If HFFrequency is (16, 24] MHz
|
|
if (privateHFXTClockFrequency > 16000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
|
|
}
|
|
//If HFFrequency is (8, 16] MHz
|
|
else if (privateHFXTClockFrequency > 8000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
|
|
}
|
|
//If HFFrequency is (4, 8] MHz
|
|
else if (privateHFXTClockFrequency > 4000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
|
|
}
|
|
//If HFFrequency is [0, 4] MHz
|
|
else {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
}
|
|
|
|
while ((HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) && --timeout)
|
|
{
|
|
//Clear OSC fault Flags fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);
|
|
|
|
// Clear the global fault flag. In case the LFXT caused the global fault
|
|
// flag to get set this will clear the global error condition. If any
|
|
// error condition persists, global flag will get again.
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
|
|
}
|
|
|
|
if (timeout) {
|
|
//Set drive strength for HFXT
|
|
HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
|
|
~(CS_HFXT_DRIVE_24MHZ_32MHZ)
|
|
) |
|
|
(hfxtdrive);
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
else {
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
return (STATUS_FAIL);
|
|
}
|
|
}
|
|
|
|
bool CS_bypassHFXTWithTimeout (uint32_t timeout
|
|
)
|
|
{
|
|
//Verify user has initialized value of HFXTClock
|
|
assert(privateHFXTClockFrequency != 0);
|
|
|
|
assert(timeout > 0);
|
|
|
|
// Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//If HFFrequency is (16, 24] MHz
|
|
if (privateHFXTClockFrequency > 16000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
|
|
}
|
|
//If HFFrequency is (8, 16] MHz
|
|
else if (privateHFXTClockFrequency > 8000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
|
|
}
|
|
//If HFFrequency is (4, 8] MHz
|
|
else if (privateHFXTClockFrequency > 4000000) {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
|
|
}
|
|
//If HFFrequency is [0, 4] MHz
|
|
else {
|
|
HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
|
|
}
|
|
|
|
//Switch off HFXT oscillator and enable BYPASS mode
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= (HFXTBYPASS + HFXTOFF);
|
|
|
|
while ((HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) && --timeout)
|
|
{
|
|
//Clear OSC fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);
|
|
|
|
// Clear the global fault flag. In case the LFXT caused the global fault
|
|
// flag to get set this will clear the global error condition. If any
|
|
// error condition persists, global flag will get again.
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
}
|
|
|
|
// Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
|
|
if (timeout) {
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
else {
|
|
return (STATUS_FAIL);
|
|
}
|
|
}
|
|
|
|
void CS_turnOffHFXT (void)
|
|
{
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Switch off HFXT oscillator
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= HFXTOFF;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
void CS_enableClockRequest (uint8_t selectClock
|
|
)
|
|
{
|
|
assert(
|
|
(CS_ACLK == selectClock )||
|
|
(CS_SMCLK == selectClock )||
|
|
(CS_MCLK == selectClock )||
|
|
(CS_MODOSC== selectClock ));
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
HWREG8(CS_BASE + OFS_CSCTL6) |= selectClock;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
void CS_disableClockRequest (uint8_t selectClock
|
|
)
|
|
{
|
|
assert(
|
|
(CS_ACLK == selectClock )||
|
|
(CS_SMCLK == selectClock )||
|
|
(CS_MCLK == selectClock )||
|
|
(CS_MODOSC== selectClock ));
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
HWREG8(CS_BASE + OFS_CSCTL6) &= ~selectClock;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
uint8_t CS_getFaultFlagStatus (uint8_t mask
|
|
)
|
|
{
|
|
assert(
|
|
(CS_HFXTOFFG == mask )||
|
|
(CS_LFXTOFFG == mask )
|
|
);
|
|
return (HWREG8(CS_BASE + OFS_CSCTL5) & mask);
|
|
}
|
|
|
|
void CS_clearFaultFlag (uint8_t mask
|
|
)
|
|
{
|
|
assert(
|
|
(CS_HFXTOFFG == mask )||
|
|
(CS_LFXTOFFG == mask )
|
|
);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~mask;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
uint32_t CS_getACLK (void)
|
|
{
|
|
|
|
//Find ACLK source
|
|
uint16_t ACLKSource = (HWREG16(CS_BASE + OFS_CSCTL2) & SELA_7);
|
|
ACLKSource = ACLKSource >> 8;
|
|
|
|
//Find ACLK frequency divider
|
|
uint16_t ACLKSourceDivider = HWREG16(CS_BASE + OFS_CSCTL3) & SELA_7;
|
|
ACLKSourceDivider = ACLKSourceDivider >> 8;
|
|
|
|
return (privateCSAComputeCLKFrequency(
|
|
ACLKSource,
|
|
ACLKSourceDivider));
|
|
|
|
}
|
|
|
|
uint32_t CS_getSMCLK (void)
|
|
{
|
|
//Find SMCLK source
|
|
uint16_t SMCLKSource = HWREG8(CS_BASE + OFS_CSCTL2) & SELS_7;
|
|
|
|
SMCLKSource = SMCLKSource >> 4;
|
|
|
|
//Find SMCLK frequency divider
|
|
uint16_t SMCLKSourceDivider = HWREG16(CS_BASE + OFS_CSCTL3) & SELS_7;
|
|
SMCLKSourceDivider = SMCLKSourceDivider >> 4;
|
|
|
|
return (privateCSAComputeCLKFrequency(
|
|
SMCLKSource,
|
|
SMCLKSourceDivider )
|
|
);
|
|
}
|
|
|
|
uint32_t CS_getMCLK (void)
|
|
{
|
|
//Find MCLK source
|
|
uint16_t MCLKSource = (HWREG16(CS_BASE + OFS_CSCTL2) & SELM_7);
|
|
//Find MCLK frequency divider
|
|
uint16_t MCLKSourceDivider = HWREG16(CS_BASE + OFS_CSCTL3) & SELM_7;
|
|
|
|
return (privateCSAComputeCLKFrequency(
|
|
MCLKSource,
|
|
MCLKSourceDivider )
|
|
);
|
|
}
|
|
|
|
void CS_turnOffVLO(void)
|
|
{
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
HWREG16(CS_BASE + OFS_CSCTL4) |= VLOOFF;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
uint16_t CS_clearAllOscFlagsWithTimeout(uint32_t timeout)
|
|
{
|
|
assert(timeout > 0);
|
|
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
do {
|
|
//Clear all osc fault flags
|
|
HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG + HFXTOFFG);
|
|
|
|
//Clear the global osc fault flag
|
|
HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
|
|
|
|
//Check LFXT fault flags
|
|
} while ((HWREG8(SFR_BASE + OFS_SFRIFG1) & OFIFG) && --timeout);
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
|
|
return (HWREG8(CS_BASE + OFS_CSCTL5) & (LFXTOFFG + HFXTOFFG));
|
|
}
|
|
|
|
void CS_setDCOFreq(uint16_t dcorsel,
|
|
uint16_t dcofsel)
|
|
{
|
|
assert(
|
|
(dcofsel==CS_DCOFSEL_0)||
|
|
(dcofsel==CS_DCOFSEL_1)||
|
|
(dcofsel==CS_DCOFSEL_2)||
|
|
(dcofsel==CS_DCOFSEL_3)||
|
|
(dcofsel==CS_DCOFSEL_4)||
|
|
(dcofsel==CS_DCOFSEL_5)||
|
|
(dcofsel==CS_DCOFSEL_6)
|
|
);
|
|
|
|
//Verify user has selected a valid DCO Frequency Range option
|
|
assert(
|
|
(dcorsel==CS_DCORSEL_0)||
|
|
(dcorsel==CS_DCORSEL_1));
|
|
|
|
uint16_t tempCSCTL3 = 0;
|
|
//Unlock CS control register
|
|
HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;
|
|
|
|
//Assuming SMCLK and MCLK are sourced from DCO
|
|
//Store CSCTL3 settings to recover later
|
|
tempCSCTL3 = HWREG16(CS_BASE + OFS_CSCTL3);
|
|
|
|
//Keep overshoot transient within specification by setting clk
|
|
//sources to divide by 4
|
|
//Clear the DIVS & DIVM masks (~0x77) and set both fields to 4 divider
|
|
HWREG16(CS_BASE + OFS_CSCTL3) = HWREG16(CS_BASE + OFS_CSCTL3) &
|
|
(~(0x77)) | DIVS1 | DIVM1;
|
|
|
|
//Set user's frequency selection for DCO
|
|
HWREG16(CS_BASE + OFS_CSCTL1) = (dcorsel + dcofsel);
|
|
|
|
//Delay by ~10us to let DCO settle. cycles to wait = 20 cycles buffer +
|
|
//(10us * (x MHz/4))
|
|
switch(dcofsel)
|
|
{
|
|
case(CS_DCOFSEL_0):
|
|
//1 MHz or 1 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(23) : __delay_cycles(23);
|
|
break;
|
|
case(CS_DCOFSEL_1):
|
|
//2.67 MHz or 5.33 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(27) : __delay_cycles(34);
|
|
break;
|
|
case(CS_DCOFSEL_2):
|
|
//3.33 MHz or 6.67 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(29) : __delay_cycles(37);
|
|
break;
|
|
case(CS_DCOFSEL_3):
|
|
//4 MHz or 8 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(30) : __delay_cycles(40);
|
|
break;
|
|
case(CS_DCOFSEL_4):
|
|
//5.33 MHz or 16 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(34) : __delay_cycles(60);
|
|
break;
|
|
case(CS_DCOFSEL_5):
|
|
//6.67 MHz or 21 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(37) : __delay_cycles(73);
|
|
break;
|
|
case(CS_DCOFSEL_6):
|
|
//8 MHz or 24 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(40) : __delay_cycles(80);
|
|
break;
|
|
default:
|
|
//Should not be used, but default is 8 MHz or 24 MHz
|
|
(dcorsel == CS_DCORSEL_0) ? __delay_cycles(40) : __delay_cycles(80);
|
|
break;
|
|
}
|
|
|
|
//Set all dividers
|
|
HWREG16(CS_BASE + OFS_CSCTL3) = tempCSCTL3;
|
|
|
|
//Lock CS control register
|
|
HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
|
|
}
|
|
|
|
#endif
|
|
//*****************************************************************************
|
|
//
|
|
//! Close the doxygen group for cs_api
|
|
//! @}
|
|
//
|
|
//*****************************************************************************
|