Yuano0o / alpaca

0 stars 0 forks source link

How to use run-time libs of MSP430 for further measurements and tests. #1

Open ghost opened 1 year ago

ghost commented 1 year ago

Boards

The project supports three boards, MSP430FR5994, MSP430FR5969 and MSP430FR2433. Switch to your board from CCS.Properties_for_project.General.Project.Device.Variant.

Clock

The function clock_sys_init() in $/driver/target.c is used to do initialization for main clock MCLK, second main clock SMCLK and auxiliary clock ACLK. By default, MCLK and SMCLK is driven by internal DCO with variable frequencies and ACLK is driven by internal REFO with frequency 32768Hz.

Usually, setting DCO as 1, 2, 4, 8 MHz is preferred. Do not adjust other parameters of the clock unless you fully understand what you are doing. See the following code.

#if WORKING_FREQUENCY_IN_MHZ == 1
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_0);                              // DCO in 1MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // MCLK = DCO = 1MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // SMCLK = DCO = 1MHz
#elif WORKING_FREQUENCY_IN_MHZ == 2
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_3);                              // DCO in 4MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_2);  // MCLK = DCO/2 = 2MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_2);  // SMCLK = DCO/2 = 2MHz
#elif WORKING_FREQUENCY_IN_MHZ == 4
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_3);                              // DCO in 4MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // MCLK = DCO = 4MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // SMCLK = DCO = 4MHz
#elif WORKING_FREQUENCY_IN_MHZ == 8
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_6);                              // DCO in 8MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // MCLK = DCO = 8MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // SMCLK = DCO = 8MHz
#else
#error "WORKING FREQUENCY ILLEGAL!"
#endif

UART

The UART is used for data transmission (interaction) between the MSP430 development board and the host (Windows).

The UART is usually driven with MCLK (don't modify it), so the settings for the UART are relative to the current MCLK frequency. Change code in $/driver/uart2target.h to match the configuration of the UART to the MCLK frequency. See code below.

#if WORKING_FREQUENCY_IN_MHZ == 1
#define CLK_PRESCALAR_USED          8
#define FIRST_MODE_REG_USED         0
#define SECOND_MODE_REG_USED        214
#define OVER_SAMP_USED              0
#elif WORKING_FREQUENCY_IN_MHZ == 2
#define CLK_PRESCALAR_USED          17
#define FIRST_MODE_REG_USED         0
#define SECOND_MODE_REG_USED        74
#define OVER_SAMP_USED              0
#elif WORKING_FREQUENCY_IN_MHZ == 4
#define CLK_PRESCALAR_USED          2
#define FIRST_MODE_REG_USED         2
#define SECOND_MODE_REG_USED        187
#define OVER_SAMP_USED              1
#elif WORKING_FREQUENCY_IN_MHZ == 8
#define CLK_PRESCALAR_USED          4
#define FIRST_MODE_REG_USED         5
#define SECOND_MODE_REG_USED        85
#define OVER_SAMP_USED              1
#else
#error "WORKING FREQUENCY ILLEGAL!"
#endif

Download a serial debugging assistant (see [HERE](https://blog.csdn.net/m0_46563076/article/details/105318326)), set the baud rate to 115200, and keep other configurations as default. Connect the MSP430 development board to the host, and select the corresponding channel (serial port number).

Do not edit fputc() and fputs() functions in $/dirver/uart2target.c unless you fully understand what you are doing. With these two functions, you can simply call printf() to send the required data from the MSP430 development board to the host, and see the data in the serial debugging assistant.

Pay attention

For some version of CCS, printf may not work well.

Find Level of printf/scanf support required in CCS.Properties_for_project.C/C++ Build/Settings/Tool Settings/Advanced Options/Language Options and set the value to full to get fully support for printf function.

TimerA and time measurement

Add these two files to $dirver/.

/* time_meas.h */

#ifndef IMC_ANALYSIS_TIME_MEAS_H_
#define IMC_ANALYSIS_TIME_MEAS_H_

#include <stdint.h>

#define TA2_CLOCKDIV        TIMER_A_CLOCKSOURCE_DIVIDER_64
//!                         TIMER_A_CLOCKSOURCE_DIVIDER_1
//!                         TIMER_A_CLOCKSOURCE_DIVIDER_10
//!                         TIMER_A_CLOCKSOURCE_DIVIDER_64

void timer_init();

void timer_start_count();

void timer_end_count();

uint32_t timer_get_cycles();

#endif /* IMC_ANALYSIS_TIME_MEAS_H_ */

and

/* time_meas.h */

#include "time_meas.h"  // Modify if needed.
#include <stdbool.h>

void timer_init()
{
    Timer_A_initContinuousModeParam timerAInitParam = {0};

    timerAInitParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    timerAInitParam.clockSourceDivider = TA2_CLOCKDIV;
    timerAInitParam.startTimer = false;
    timerAInitParam.timerClear = TIMER_A_DO_CLEAR;
    timerAInitParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;

    Timer_A_initContinuousMode(TIMER_A2_BASE, &timerAInitParam);
}

void timer_start_count(uint16_t *timer_counter_start)
{
    HWREG16(TIMER_A2_BASE + OFS_TAxCTL) &= ~MC_3;
    HWREG16(TIMER_A2_BASE + OFS_TAxCTL) |= TIMER_A_CONTINUOUS_MODE;
    *timer_counter_start = HWREG16(TIMER_A2_BASE + OFS_TAxR);
}

void timer_end_count(uint16_t *timer_counter_finish)
{
    *timer_counter_finish = HWREG16(TIMER_A2_BASE + OFS_TAxR);
    HWREG16(TIMER_A2_BASE + OFS_TAxCTL) &= ~MC_3;
    HWREG16(TIMER_A2_BASE + OFS_TAxCTL) |= TACLR;
}

uint32_t timer_get_cycles(uint16_t cstart, uint16_t cfinish)
{
    uint32_t timer_ticks = (uint32_t)cfinish - (uint32_t)cstart;
    uint32_t cycles;
    switch(TA2_CLOCKDIV)
    {
    case TIMER_A_CLOCKSOURCE_DIVIDER_1:
        cycles = timer_ticks;
        break;
    case TIMER_A_CLOCKSOURCE_DIVIDER_10:
        cycles = timer_ticks * 10;
        break;
    case TIMER_A_CLOCKSOURCE_DIVIDER_64:
        cycles = timer_ticks * 64;
        break;
    }
    return cycles;
}

Here is a simple usage example.

timer_init();

// ...

uint16_t s, f;
timer_start_count(&s);
// Do someting.
timer_end_count(&f);
printf("Cycles %ld.", timer_get_cycles(s, f));  // print out.

The TA2 timer is driven by SMCLK. TA2_CLOCKDIV determines when the clock passes through how many cycles, the value of the counter of the TA2 timer will increase by 1 (i.e. frequency division).

The frequency division reflects the trade-off between timing accuracy and maximum timing. It should be noted that the timer of TA2 is only 16 bits long, that is, the maximum tick can be 65536.

For example, when MCLK is 1MHz, if the frequency division ratio is set to 1, then the precision is 1/1M, that is, 1 microsecond, but it can only time less than 0.066 seconds at most; if the frequency division ratio is set to 64, then the precision There is only 64/1M, which is 64 microseconds, but it can time more than 4 seconds at this time.

Run function timer_get_cycles() to get the actual period elapsed by the clock (32 bits long).

ghost commented 1 year ago

Correct time_meas.h as below.

/* time_meas.h */

#ifndef IMC_ANALYSIS_TIME_MEAS_H_
#define IMC_ANALYSIS_TIME_MEAS_H_

#include <stdint.h>

#define TA2_CLOCKDIV        TIMER_A_CLOCKSOURCE_DIVIDER_64
//!                         TIMER_A_CLOCKSOURCE_DIVIDER_1
//!                         TIMER_A_CLOCKSOURCE_DIVIDER_10
//!                         TIMER_A_CLOCKSOURCE_DIVIDER_64

void timer_init();

void timer_start_count(uint16_t *timer_counter_start);
void timer_end_count(uint16_t *timer_counter_finish);
uint32_t timer_get_cycles(uint16_t cstart, uint16_t cfinish);

#endif /* IMC_ANALYSIS_TIME_MEAS_H_ */
Yuano0o commented 1 year ago

这里的时钟参数设置是不是有问题?

#if WORKING_FREQUENCY_IN_MHZ == 1
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_0);                              // DCO in 1MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // MCLK = DCO = 1MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // SMCLK = DCO = 1MHz
#elif WORKING_FREQUENCY_IN_MHZ == 2
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_3);                              // DCO in 4MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_2);  // MCLK = DCO/2 = 2MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_2);  // SMCLK = DCO/2 = 2MHz
#elif WORKING_FREQUENCY_IN_MHZ == 4
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_3);                              // DCO in 4MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // MCLK = DCO = 4MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // SMCLK = DCO = 4MHz
#elif WORKING_FREQUENCY_IN_MHZ == 8
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_6);                              // DCO in 8MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // MCLK = DCO = 8MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // SMCLK = DCO = 8MHz
#else
#error "WORKING FREQUENCY ILLEGAL!"
#endif

与下面的设置不一致

void clock_sys_init()
{
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_6);                              // DCO in 8MHz
    CS_initClockSignal(CS_MCLK,   CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_1);  // MCLK = DCO = 8MHz
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLK_SELECT,   CS_CLOCK_DIVIDER_8);  // SMCLK = DCO/8 = 1MHz, x8 divided DCO
    CS_initClockSignal(CS_ACLK,   CS_VLOCLK_SELECT,   CS_CLOCK_DIVIDER_1);
    CS_turnOffLFXT();
}