单片机
返回首页

MSP430G2553 PWM控制速度,并自我检测和简单调节速度

2022-09-23 来源:csdn

MSP430G2553 测量小车速度,并简单调速

需要了解MSP430系列的GPIO,TIMERA,UART相关的知识

涉及内容:PWM输出配置,TIMER A 的捕捉/比较寄存器的输入捕捉配置,串口通信的配置,系统时钟的配置


STEP 1 测量小车速度的方法

公式:v = s/t;

s,就是轮子周长;t就是中断周期


PREPARE 1 小车橡胶轮子

参数:


周长:C=2*pi*R=pi*D=20.41975cm;总共20个洞。


1.0209875cm/洞


每穿过一个洞,产生一个中断,得到计算的周期T


v = s/t


PREPARE 2 测速模块

未被遮挡,指示灯亮,输出高电平

被遮挡,指示灯灭,输出低电平

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

STEP 2 MSP430 输入捕捉中断计算速度

开发板:MSP430G2ET(MSP430G2553)


设计思路

使用定时器A的计数器1的CCR2作为输入捕捉的功能。

TACCR0寄存器的值,就是UP MODE 的峰值

总时间 = (时间戳时间间隔)* TACCR0周期 + 中断捕捉时间间隔

速度 = S / 总时间

代码部分

1. 输入捕捉配置

TA1CTL,

TA1CCTL

P2SEL,

总中断使能打开

void TA1_CCR2_PIN1_5()

{

    /*设置定时间隔*/

//    TA1CCR0 = 49999;//50ms

    /*开启TAIFG中断*/

    TA1CTL |= TAIE;

    /*TA1,CCR2用于捕捉功能*/

    TA1CCTL2 |= CAP;

    /*上升沿捕捉*/

    TA1CCTL2 |= CM0;

    /*P2.5作为捕捉输入(CCI2B)*/

    TA1CCTL2 |= CCIS0;

    P2SEL |= BIT5;

    /*允许捕捉比较中断*/

    TA1CCTL2 |= CCIE;

    __bis_SR_register(GIE);

}


2. PWM波输出配置

P2SEL(Px端口第二功能),

P2DIR(端口输出方向),

TA1CTL(定时器A控制寄存器),

TA1CCTL1(CCR1控制寄存器),

TA1CCR0(UP MODE的周期),

TA1CCR1(PWM输出)

void TA11_P21_PWM_OUT(void)

{

    P2SEL |= BIT1;

    P2DIR |= BIT1;//P2.1设为输出TA1.1

    TA1CTL |= TASSEL_2 + MC_1;  //选SMCLK,UP MODE

    TA1CCTL1 = OUTMOD_7;

    TA1CCR0 = 2000;

    TA1CCR1 = PWM; //占空比:TACCR1/TACCR0

}


3. 中断服务函数

捕捉/比较1,捕捉/比较2,定时器中断溢出共用一个中断向量

在这里插入图片描述

#pragma vector = TIMER1_A1_VECTOR

__interrupt void Time_Tick(void)

{

    static uint8_t cnt = 0;

    __bis_SR_register(GIE);//允许中断嵌套

    switch(TA1IV)

    {

    case 0x02://捕捉比较中断1

        break;

    case 0x04://捕捉比较中断2

        if(cnt == 0)

        {

            capvalue_1 = TA1CCR2;//保存第一次捕捉值

            timestamp_1 = timestamp;//保存第一次时间戳

            cnt ++;

        }

        else

        {

            capvalue_2 = TA1CCR2;//保存第二次捕捉值

            timestamp_2 = timestamp;//保存第二次时间戳

            cnt = 0;

            totaltime = (timestamp_2 - timestamp_1) * 2000 + capvalue_2 - capvalue_1;//计算总时间

        }

        break;

    case 0x0A://溢出中断

        timestamp ++;

        break;

    default:

        break;

    }

}


总代码及演示效果

#include

#include 'stdint.h'

#include

//#define PWM    170 // max for RCCX UNDER 2000

uint32_t timestamp = 0;//时间戳

uint16_t capvalue_1 = 0;//第一次捕捉值

uint16_t capvalue_2 = 0;//第二次捕捉值

uint32_t timestamp_1 = 0;//第一次时间戳

uint32_t timestamp_2 = 0;//第二次时间戳

uint32_t totaltime = 0;

float freq = 0;

#define P1_IE_ON        P1IE |= BIT3

#define P1_IE_OFF        P1IE &=~ BIT3


int num = 0;


int temp = 0;

unsigned int i,j;

/*

 *  @FN:    void GPIO_INIT()

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF:配置三个 TCRT5000 模块

 */

//void GPIO_INIT()

//{

//    P1DIR &=~(BIT3+BIT4+BIT5);

//    P1REN |= (BIT3+BIT4+BIT5);

//    P1OUT &=~(BIT3+BIT4+BIT5);

//}


/*

 *  @FN:    void InitSystemClock(void)

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF: 配置系统时钟1MHZ

 */


void InitSystemClock(void)

{

    WDTCTL = WDTPW | WDTHOLD;

    /*配置DCO为1MHz*/

   DCOCTL = CALDCO_1MHZ;

   BCSCTL1 = CALBC1_1MHZ;

   /*配置SMCLK的时钟源为DCO*/

   BCSCTL2 &= ~SELS;

   /*SMCLK的分频系数置为1*/

   BCSCTL2 &= ~(DIVS0 | DIVS1);

}

/*

 * @FN:  void TA11_P21_PWM_OUT(void)

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF: 配置定时器A1,产生PWM波

 *          输出引脚    P21

 *          周期T=TA1CCR0S

 *          占空比TA1CCR1/TA1CCR0(2000),

 *          修改值:TACCR1

 */

void TA11_P21_PWM_OUT(void)

{

    P2SEL |= BIT1;

    P2DIR |= BIT1;//P2.1设为输出TA1.1

    TA1CTL |= TASSEL_2 + MC_1;  //选SMCLK,UP MODE

    TA1CCTL1 = OUTMOD_7;

    TA1CCR0 = 2000;

    TA1CCR1 = 1000; //占空比:TACCR1/TACCR0

}

/*

 * @FN:  void TA01_P12_PWM_OUT(void)

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF: 配置定时器A0产生PWM波

 *          输出引脚P12

 */

void TA01_P12_PWM_OUT()

{

    P1SEL |= BIT2;

    P1DIR |= BIT2;


    TA0CTL |= TASSEL_2 + MC_1;

    TA0CCTL1 = OUTMOD_7;

    TA0CCR0 = 2000;

    TA0CCR1 = 100; //Capture/Compare ACTION

}



//void CCR3_CAL_TIME()

//{

//    TA0CTL |=

//}

void speed_test()

{

    TA0CCR1 = 50;

    __delay_cycles(500000);

    TA0CCR1 = 1000;

    __delay_cycles(500000);

    TA0CCR1 = 1200;

    __delay_cycles(500000);

}

/*

 * @fn:     void InitUART(void)

 * @brief:  初始化串口,包括波特率,数据位,停止位,校验位

 * @para:   none

 * @comment:初始化串口

 */

void InitUART(void)

{

    /*复位USCI_Ax*/

   UCA0CTL1 |= UCSWRST;


   /*选择USCI_Ax为UART模式*/


   UCA0CTL0 &= ~UCSYNC;


   /*配置UART时钟源为SMCLK*/

   UCA0CTL1 |= UCSSEL1;


   /*配置波特率为9600@1MHz*/

   UCA0BR0 = 104;

   UCA0BR1 = 0;

   UCA0MCTL = 1 <<1;

   /*使能端口复用*/

   P1SEL |= BIT1 + BIT2;

   P1SEL2 |= BIT1 + BIT2;

   /*清除复位位,使能UART*/

   UCA0CTL1 &= ~UCSWRST;

}


/*

 * @fn:     void UARTSendString(uint8_t *pbuff, uint8_t num)

 * @brief:  通过串口发送字符串

 * @para:   pbuff  :指向要发送字符串的指针

 *          num    :要发送的字符串的个数

 * @comment:通过串口发送字符串

 */

void UARTSendString(unsigned char  *pbuff, unsigned char  num)

{

    unsigned char cnt = 0;

    for(cnt = 0; cnt < num; cnt++)

    {

        while(UCA0STAT &  UCBUSY);

        UCA0TXBUF = *(pbuff + cnt);

    }

}


void TA1_CCR2_PIN1_5()

{

    /*设置定时间隔*/

//    TA1CCR0 = 49999;//50ms

    /*开启TAIFG中断*/

    TA1CTL |= TAIE;

    /*TA1,CCR2用于捕捉功能*/

    TA1CCTL2 |= CAP;

    /*上升沿捕捉*/

    TA1CCTL2 |= CM0;

    /*P2.5作为捕捉输入(CCI2B)*/

    TA1CCTL2 |= CCIS0;

    P2SEL |= BIT5;

    /*允许捕捉比较中断*/

    TA1CCTL2 |= CCIE;

    __bis_SR_register(GIE);

}

uint32_t GET_V_FOR_WHEEL(uint32_t time)

{

    uint32_t v = 0;

    v = 1020987.5 / time;

    return v;

}

void PrintV(uint32_t num)

{

    uint8_t buff[8] = {0,0,0,0,0,0,0,' '};

    uint8_t cnt = 0;

    for(cnt = 0;cnt < 7;cnt ++)

    {

        buff[6 - cnt] = (uint8_t)(num % 10 + '0');

        num /= 10;

    }

    UARTSendString(buff,8);

    UARTSendString('cm/s',4);

}



void main(void)

{


    InitSystemClock();

    TA1_CCR2_PIN1_5();

    InitUART();

//    Interrupt_GPIO_Init();

    TA01_P12_PWM_OUT();

    TA11_P21_PWM_OUT();


    while(1)

    {


        temp++;

        if(temp > 20)

        {

            UARTSendString('当前小车速度为:',16);

            PrintV(GET_V_FOR_WHEEL(totaltime));

            temp = 0;

        }


        if(GET_V_FOR_WHEEL(totaltime) < 88)

        {

            TA1CCR1 += 4;

        }

        if(GET_V_FOR_WHEEL(totaltime) > 100 )

        {

            TA1CCR1-= 4;

        }

        __delay_cycles(20000);

//        totaltime = 0;

    }

}

#pragma vector = TIMER1_A1_VECTOR

__interrupt void Time_Tick(void)

{

    static uint8_t cnt = 0;

    __bis_SR_register(GIE);//允许中断嵌套

    switch(TA1IV)

    {

    case 0x02://捕捉比较中断1

        break;

    case 0x04://捕捉比较中断2

        if(cnt == 0)

        {

            capvalue_1 = TA1CCR2;//保存第一次捕捉值

            timestamp_1 = timestamp;//保存第一次时间戳

            cnt ++;

        }

        else

        {

            capvalue_2 = TA1CCR2;//保存第二次捕捉值

            timestamp_2 = timestamp;//保存第二次时间戳

            cnt = 0;

            totaltime = (timestamp_2 - timestamp_1) * 2000 + capvalue_2 - capvalue_1;//计算总时间

        }

        break;

    case 0x0A://溢出中断

        timestamp ++;

        break;

    default:

        break;

    }

}


STEP 3 简单调速

    if(GET_V_FOR_WHEEL(totaltime) < 80)

        {

            TA1CCR1 += 2;

        }

        if(GET_V_FOR_WHEEL(totaltime) > 85 )

        {

            TA1CCR1-= 2;

        }


附录 整体代码(环境:CCS) 演示说明

硬件环境

在这里插入图片描述

软件环境测试

初始时,PWM占比75%,速度初始为128cm/s,后经过简单的自我反馈(伪PID)原理,进行自我速度调节,大致稳定在88~100cm/s之间,下一步朝着真PID算法的方向前进!

以下,附上全部代码,型号MSP430G2553,

在这里插入图片描述

#if 1

#include

#include 'stdint.h'

#include

//#define PWM    170 // max for RCCX UNDER 2000

uint32_t timestamp = 0;//时间戳

uint16_t capvalue_1 = 0;//第一次捕捉值

uint16_t capvalue_2 = 0;//第二次捕捉值

uint32_t timestamp_1 = 0;//第一次时间戳

uint32_t timestamp_2 = 0;//第二次时间戳

uint32_t totaltime = 0;

float freq = 0;

#define P1_IE_ON        P1IE |= BIT3

#define P1_IE_OFF        P1IE &=~ BIT3


int num = 0;

int left = 0;

int right = 0;

int temp = 0;

unsigned int i,j;

/*

 *  @FN:    void GPIO_INIT()

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF:配置三个 TCRT5000 模块

 */

void GPIO_INIT()

{

    P1DIR &=~(BIT3+BIT4+BIT5);

    P1REN |= (BIT3+BIT4+BIT5);

    P1OUT &=~(BIT3+BIT4+BIT5);

}


/*

 *  @FN:    void InitSystemClock(void)

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF: 配置系统时钟1MHZ

 */


void InitSystemClock(void)

{

    WDTCTL = WDTPW | WDTHOLD;

    /*配置DCO为1MHz*/

   DCOCTL = CALDCO_1MHZ;

   BCSCTL1 = CALBC1_1MHZ;

   /*配置SMCLK的时钟源为DCO*/

   BCSCTL2 &= ~SELS;

   /*SMCLK的分频系数置为1*/

   BCSCTL2 &= ~(DIVS0 | DIVS1);

}

/*

 * @FN:  void TA11_P21_PWM_OUT(void)

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF: 配置定时器A1,产生PWM波

 *          输出引脚    P21

 *          周期T=TA1CCR0S

 *          占空比TA1CCR1/TA1CCR0(2000),

 *          修改值:TACCR1

 */

void TA11_P21_PWM_OUT(void)

{

    P2SEL |= BIT1;

    P2DIR |= BIT1;//P2.1设为输出TA1.1

    TA1CTL |= TASSEL_2 + MC_1;  //选SMCLK,UP MODE

    TA1CCTL1 = OUTMOD_7;

    TA1CCR0 = 2000;

    TA1CCR1 = 1000; //占空比:TACCR1/TACCR0

}

/*

 * @FN:  void TA01_P12_PWM_OUT(void)

 *  @PAR:   NONE

 *  @RETURN:NONE

 *  @BRIEF: 配置定时器A0产生PWM波

 *          输出引脚P12

 */

void TA01_P12_PWM_OUT()

{

    P1SEL |= BIT2;

    P1DIR |= BIT2;


    TA0CTL |= TASSEL_2 + MC_1;

    TA0CCTL1 = OUTMOD_7;

    TA0CCR0 = 2000;

    TA0CCR1 = 100; //Capture/Compare ACTION

}



//void CCR3_CAL_TIME()

//{

//    TA0CTL |=

//}

void speed_test()

{

    TA0CCR1 = 50;

    __delay_cycles(500000);

    TA0CCR1 = 1000;

    __delay_cycles(500000);

    TA0CCR1 = 1200;

    __delay_cycles(500000);

}

/*

 * @fn:     void InitUART(void)

 * @brief:  初始化串口,包括波特率,数据位,停止位,校验位

 * @para:   none

 * @comment:初始化串口

 */

void InitUART(void)

{

    /*复位USCI_Ax*/

   UCA0CTL1 |= UCSWRST;


   /*选择USCI_Ax为UART模式*/


   UCA0CTL0 &= ~UCSYNC;


   /*配置UART时钟源为SMCLK*/

   UCA0CTL1 |= UCSSEL1;


   /*配置波特率为9600@1MHz*/

   UCA0BR0 = 104;

   UCA0BR1 = 0;

   UCA0MCTL = 1 <<1;

   /*使能端口复用*/

   P1SEL |= BIT1 + BIT2;

   P1SEL2 |= BIT1 + BIT2;

   /*清除复位位,使能UART*/

   UCA0CTL1 &= ~UCSWRST;

}


/*

 * @fn:     void UARTSendString(uint8_t *pbuff, uint8_t num)

 * @brief:  通过串口发送字符串

 * @para:   pbuff  :指向要发送字符串的指针

 *          num    :要发送的字符串的个数

 * @comment:通过串口发送字符串

 */

void UARTSendString(unsigned char  *pbuff, unsigned char  num)

{

    unsigned char cnt = 0;

    for(cnt = 0; cnt < num; cnt++)

    {

        while(UCA0STAT &  UCBUSY);

        UCA0TXBUF = *(pbuff + cnt);

    }

}


void TA1_CCR2_PIN1_5()

{

    /*设置定时间隔*/

//    TA1CCR0 = 49999;//50ms

    /*开启TAIFG中断*/

    TA1CTL |= TAIE;

    /*TA1,CCR2用于捕捉功能*/

    TA1CCTL2 |= CAP;

    /*上升沿捕捉*/

    TA1CCTL2 |= CM0;

    /*P2.5作为捕捉输入(CCI2B)*/

    TA1CCTL2 |= CCIS0;

    P2SEL |= BIT5;

    /*允许捕捉比较中断*/

    TA1CCTL2 |= CCIE;

    __bis_SR_register(GIE);

}

uint32_t GET_V_FOR_WHEEL(uint32_t time)

{

    uint32_t v = 0;

    v = 1020987.5 / time;

    return v;

}

void PrintV(uint32_t num)

{

    uint8_t buff[8] = {0,0,0,0,0,0,0,' '};

    uint8_t cnt = 0;

    for(cnt = 0;cnt < 7;cnt ++)

    {

        buff[6 - cnt] = (uint8_t)(num % 10 + '0');

        num /= 10;

    }

    UARTSendString(buff,8);

    UARTSendString('cm/s',4);

}



void main(void)

{


    InitSystemClock();

    TA1_CCR2_PIN1_5();

    InitUART();

//    Interrupt_GPIO_Init();

    TA01_P12_PWM_OUT();

    TA11_P21_PWM_OUT();


    while(1)

    {


        temp++;

        if(temp > 20)

        {

            UARTSendString('当前小车速度为:',16);

            PrintV(GET_V_FOR_WHEEL(totaltime));

            temp = 0;

        }


        if(GET_V_FOR_WHEEL(totaltime) < 80)

        {

            TA1CCR1 += 2;

        }

        if(GET_V_FOR_WHEEL(totaltime) > 85 )

        {

            TA1CCR1-= 2;

        }

        __delay_cycles(20000);

//        totaltime = 0;

    }

}

#pragma vector = TIMER1_A1_VECTOR

__interrupt void Time_Tick(void)

{

    static uint8_t cnt = 0;

    __bis_SR_register(GIE);//允许中断嵌套

    switch(TA1IV)

    {

    case 0x02://捕捉比较中断1

        break;

    case 0x04://捕捉比较中断2

        if(cnt == 0)

        {

            capvalue_1 = TA1CCR2;//保存第一次捕捉值

            timestamp_1 = timestamp;//保存第一次时间戳

            cnt ++;

        }

        else

        {

            capvalue_2 = TA1CCR2;//保存第二次捕捉值

            timestamp_2 = timestamp;//保存第二次时间戳

            cnt = 0;

            totaltime = (timestamp_2 - timestamp_1) * 2000 + capvalue_2 - capvalue_1;//计算总时间

        }

        break;

    case 0x0A://溢出中断

        timestamp ++;

        break;

    default:

        break;

    }

}

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 红外线探测报警器

  • 短波AM发射器电路设计图

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 用NE555制作定时器

  • 带有短路保护系统的5V直流稳压电源电路图

  • 基于TDA2003的简单低功耗汽车立体声放大器电路

    相关电子头条文章