📄 exercise-9.c
字号:
{
integrated_rtc_correction += CRYSTAL_BASE_CYCLES_PER_SECOND;
/* We need to drop a second from the RTC */
return 0;
}
bump_rtc();
return 1;
#endif
}
void update_pulse_stuffer(void)
{
uint16_t step;
/* This is called each time TACCR0 matches.
The CPU is running at about 32*32768Hz == 1048576Hz. The counter is free running,
so if we left TACCR0 alone 16 interrupts would occur in 1048576 clock cycles.
However, our estimate will be that a second consists of a little less or a little
more than 1048576 cycles. By stepping TACCR0 by 1% less than the full range of TAR
at each interrupt we arrange that near the end of a second an interrupt will occur
in which we can use an adjusted step size to make the following interrupt occur
precisely at the end of the second. We arrange that an external pulse is generate
during this interrupt, so that pulse will occur at precisely one second
intervals. */
step = 65536U - 655U;
cycles_left_this_second -= step;
if (cycles_left_this_second <= 655)
{
step += cycles_left_this_second;
TACCTL0 &= ~OUTMOD2;
cycles_left_this_second = current_estimated_cycles_per_second;
}
else
{
TACCTL0 |= OUTMOD2;
}
TACCR0 += step;
}
void init_pulse_stuffer(void)
{
TAR = 0;
TACCR0 = 0;
TACTL = TASSEL_2 | MC_2 | ID_2;
P1SEL |= BIT0;
P1DIR |= BIT0;
TACCTL0 = OUTMOD0 | CCIE;
/* Initialise the cycles per second with an approximate value, until we
have measured the temperature and know a more accurate value. */
current_estimated_cycles_per_second = CRYSTAL_BASE_CYCLES_PER_SECOND + crystal_base_error;
cycles_left_this_second = current_estimated_cycles_per_second;
}
void estimate_current_cycles_per_second(int16_t temperature)
{
int32_t temp;
/* Work out what 32 x the current crystal frequency is, allowing for the manufacturing
tolerance of the crystal in use, and the current temperature. 32 x the crystal frequency
give a frequency of about 1MHz. The error in the estimated frequency is, therefore, in
approx. 1ppm steps. This is a little better than we will really achieve in our error
estimation, so it is a suitable precision to use. Also, we will use this rate for
generating an accurate one pulse per second clock, when it is required.*/
/* The readings from the temperature diode can be a little noisy. Use
a simple IIR filter to smooth the readings a little. */
raw_temperature_from_adc = raw_temperature_from_adc - (raw_temperature_from_adc >> 3) + (temperature >> 3);
/* Now work out the temperature, based on the values for the slope and intercept of the
diode characteristic found at calibration time. */
temp = raw_temperature_from_adc - temperature_diode_intercept;
temp *= temperature_diode_slope;
temp >>= 16;
/* We now have the temperature in degrees C. */
temperature_in_celsius = temp;
/* Now we need to calculate the ppm of clock error due to the current temperature. */
/* The crystal frequency varies in a parabolic manner with temperature. The exact
curve for the crystal is defined by the manufacturers, so we use a centre point
temperature and quadratic coefficient from the crystal's data sheet. */
/* Subtract the centre point of the crystal curve. */
temp -= CRYSTAL_QUADRATIC_CENTRE_TEMPERATURE;
/* Do the parabolic curve calculation, to find the current ppm of
error due to temperature. */
temp = temp*temp;
temp = (temp*CRYSTAL_QUADRATIC_COEFF) >> 16;
/* In a real application we would just use option 3 in the following case statement.
However, for demonstration purposes it is useful to be able to select between no
compensation, compensating only for temperature, only for manufacturing error,
or the combination of the two. */
switch (correction_components)
{
case RTC_CORRECTION_NONE:
current_rtc_correction = 0;
break;
case RTC_CORRECTION_CRYSTAL_ERROR:
current_rtc_correction = crystal_base_error;
break;
case RTC_CORRECTION_TEMPERATURE:
current_rtc_correction = -temp;
break;
case RTC_CORRECTION_CRYSTAL_ERROR_AND_TEMPERATURE:
current_rtc_correction = crystal_base_error - temp;
break;
}
current_estimated_cycles_per_second = CRYSTAL_BASE_CYCLES_PER_SECOND + current_rtc_correction;
}
int32_t assess_rtc_speed(void)
{
int32_t period;
uint16_t this_capture;
uint16_t last_capture;
uint16_t step;
int32_t counter;
int limit;
/* The fast clock should be an exact multiple of the crystal clock, once the FLL has
settled. If we capture many cycles of an accurate external 32768Hz clock, using
timer A (or B), we can measure the speed difference between the MSP430's crystal
and the external clock in a reasonable time. */
/* The SM clock should be running at 128*32768Hz at this time. */
_DINT();
/* Change timer A to running fast, and sample the external 32768Hz reference. */
P2SEL |= BIT0;
TACCR0 = 0xFFFF;
TACCTL0 = CAP | CCIS_0 | CM_1;
TACCTL2 = CAP | CCIS_0 | CM_1 | SCS;
TACTL = TACLR | MC_2 | TASSEL_2; /* start TIMER_A up mode, SMCLK as input clock */
period = 0;
last_capture = TACCR2;
limit = -1;
TACCTL2 &= ~CCIFG;
for (counter = 0; counter < 32768*5 + 1; counter++)
{
limit = 1000;
while (!(TACCTL2 & CCIFG))
{
if (--limit <= 0)
break;
}
if (limit <= 0)
break;
TACCTL2 &= ~CCIFG;
this_capture = TACCR2;
step = this_capture - last_capture;
last_capture = this_capture;
/* Skip the first sample, as it will be meaningless */
if (counter)
period += step;
}
if (limit <= 0)
period = limit;
/* Change timer A back to its normal use. */
P2SEL &= ~BIT0;
init_pulse_stuffer();
_EINT();
return period;
}
void init_lcd(void)
{
int j;
for (j = 0; j < 20; j++)
LCDMEM[j] = 0;
/* Turn on the COM0-COM3 and R03-R33 pins */
P5SEL |= (BIT4 | BIT3 | BIT2);
P5DIR |= (BIT4 | BIT3 | BIT2);
LCDACTL = LCDFREQ_128 | LCD4MUX | LCDSON | LCDON;
LCDAPCTL0 = LCDS0 | LCDS4 | LCDS8 | LCDS12;
LCDAPCTL1 = 0;
LCDAVCTL0 = 0;
LCDAVCTL1 = 0;
}
void LCDchar(int ch, int pos)
{
/* Put a segment pattern at a specified position on the LCD display */
LCDMEM[7 - pos] = ch;
}
void LCDdec(uint16_t val, int pos)
{
LCDchar(lcd_digit_table[val/10], pos);
LCDchar(lcd_digit_table[val%10], pos + 1);
}
/* Display the HH:MM:SS section of the current RTC setting on the LCD display */
void update_rtc_time_display(void)
{
LCDchar(CHAR_SPACE, 1);
LCDdec(rtc.hour, 2);
LCDdec(rtc.minute, 4);
LCDdec(rtc.second, 6);
}
void update_rtc_date_display(void)
{
LCDchar(CHAR_SPACE, 1);
LCDdec(rtc.year, 2);
LCDdec(rtc.month, 4);
LCDdec(rtc.day, 6);
}
void update_lcd(void)
{
update_rtc_time_display();
}
#pragma vector=TIMERA0_VECTOR
__interrupt void timera0_interrupt(void)
{
if (one_pps_active)
{
update_pulse_stuffer();
}
else
{
}
TACCTL0 &= ~CCIFG;
}
#pragma vector=BASICTIMER_VECTOR
__interrupt void basictimer_interrupt(void)
{
if (update_rtc())
{
update_lcd();
/* Kick off a sampling of the temperature */
SD16CCTL0 |= SD16SC;
}
}
void init_basic_timer(void)
{
/* Basic timer setup */
#if defined(AVOID_VISIBLE_RTC_HOPS)
/* Set the basic timer interrupts to occur four times per second */
BTCTL = BT_fLCD_DIV64 | BT_ADLY_250;
#else
/* Set the basic timer interrupts to occur once per second */
BTCTL = BT_fLCD_DIV64 | BT_ADLY_1000;
#endif
/* Enable the basic timer interrupt */
IE2 |= BTIE;
}
#pragma vector=SD16_VECTOR
__interrupt void adc_interrupt(void)
{
estimate_current_cycles_per_second(SD16MEM0);
SD16CCTL0 &= ~SD16IFG;
}
void init_adc(void)
{
/* Initialize and enable the SD16_1 */
SD16CTL = SD16DIV_2 | SD16SSEL_1 | SD16REFON | SD16VMIDON;
SD16IV = 0;
/* Select the temperature diode for measurement */
SD16INCTL0 = SD16GAIN_1 | SD16INCH_6;
SD16CCTL0 = SD16BUF_0 | SD16OSR_128 | SD16DF | SD16IE;
SD16CCTL0 |= SD16SC;
raw_temperature_from_adc = 22000;
P1DIR |= BIT5;
}
void init_fll(void)
{
/* Set the FLL to 2MHz. The frequency is fast enough for the work we need to do
but slow enough to rely on the brown out detector. */
int i;
/* Set the load capacitance for the 32k xtal */
FLL_CTL0 = XCAP18PF;
SCFI0 = FN_3 | FLLD_2;
SCFQCTL = 64 - 1;
FLL_CTL0 |= DCOPLUS;
FLL_CTL1 = 0;
/* Wait a little for things to settle. */
for (i = 0; i < 10000; i++)
_NOP();
}
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
init_fll();
init_lcd();
init_basic_timer();
set_rtc_sumcheck();
if (one_pps_active)
init_pulse_stuffer();
init_adc();
_EINT();
/* Now just sleep, and let the interrupt routines do the work */
if (one_pps_active)
LPM0;
else
LPM3;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -