一文搞定msp430


最近准备电赛,学习了一下msp430这块芯片,分享一些库函数代码。

时钟管理

将时钟倍频到24.9MHZ, 其中三个时钟源:Aclk为32.768K、smclk和mclk为24.96M。

//倍频25M
//最终时钟输出:Aclk 32.768K   smclk mclk  24.96M
void get_clk(void)
{
    
    
    //配置引脚复用功能
    P1DIR |= BIT0;
    P1SEL |= BIT0;
    P2DIR |= BIT2;
    P2SEL |= BIT2;//SMCLK
    P7DIR |= BIT7;
    P7SEL |= BIT7;//MCLK
    P5SEL |= BIT4 | BIT5;//外部晶振     输入为32.768K
    UCSCTL6 |= XCAP_3;
    UCSCTL6 &= ~XT1OFF;

    //提高核心电压
    PMMCTL0_H = 0XA5;
    SVSMLCTL |= SVSMLRRL_1 + SVMLE;
    PMMCTL0 = PMMPW + PMMCOREV_3;
    while ((PMMIFG & SVSMLDLYIFG) == 0);
    PMMIFG &= ~(SVMLVLRIFG + SVMLVLRIFG + SVSMLDLYIFG);
    if ((PMMIFG & SVMLIFG) == 1)
    {
    
    
        while ((PMMIFG & SVMLVLRIFG) == 0);
    }
    SVSMLCTL &= ~SVMLE;
    PMMCTL0_H = 0x00;

    __bis_SR_register(SCG0);
    UCSCTL0 = 0;
    UCSCTL1 = DCORSEL_6;
    UCSCTL2 = FLLD_1 | 380;
    __bic_SR_register(SCG0);
    __delay_cycles(782000);

    while (SFRIFG1 & OFIFG)
    {
    
                                   // Check OFIFG fault flag
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);         // Clear OSC flaut Flags
        SFRIFG1 &= ~OFIFG;                                  // Clear OFIFG fault flag
    }

    UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_3|SELM_3;

    UCSCTL4 = UCSCTL4 & (~(SELS_7 | SELM_7)) | SELS_3 | SELM_3;

}

效果查看

可以将P2_2连到示波器上,输出应该为正弦波,频率为24.9M,会有波动,下面为效果图:
在这里插入图片描述

gpio

枚举

typedef enum //枚举端口
{
    
    
    //在设置IO时请自行根据硬件确认当前芯片是否具有此IO
    P01_0 = 0*16,  P01_1, P01_2,  P01_3,  P01_4,  P01_5,  P01_6,  P01_7,

    P02_0       ,  P02_1, P02_2,  P02_3,  P02_4,  P02_5,  P02_6,  P02_7,

    P03_0 = 1*16, P03_1, P03_2,  P03_3,  P03_4,  P03_5,  P03_6,  P03_7,

    P04_0       , P04_1, P04_2,  P04_3,  P04_4,  P04_5,  P04_6,  P04_7,

    P05_0 = 2*16, P05_1, P05_2,  P05_3,  P05_4,  P05_5,  P05_6,  P05_7,

    P06_0       , P06_1, P06_2,  P06_3,  P06_4,  P06_5,  P06_6,  P06_7,

    P07_0 = 3*16, P07_1, P07_2,  P07_3,  P07_4,  P07_5,  P07_6,  P07_7,

    P08_0       , P08_1, P08_2,  P08_3,  P08_4,  P08_5,  P08_6,  P08_7,

}PIN_enum;

头文件

#ifndef LIB_MY_GPIO_H_
#define LIB_MY_GPIO_H_

#include "headfile.h"

typedef struct {
    
    
    volatile    uint16 P_IN;
    volatile    uint16 P_OUT;
    volatile    uint16 P_DIR;
    volatile    uint16 P_EN;
    volatile    uint16 P_DS;
    volatile    uint16 P_SEL;
    volatile    uint16 P_IES;
    volatile    uint16 P_IE;
    volatile    uint16 P_IFG;
} GPIO_Type;

#define PA_BASE_PTR ((GPIO_Type *) PA_BASE)
#define PB_BASE_PTR ((GPIO_Type *) PB_BASE)
#define PC_BASE_PTR ((GPIO_Type *) PC_BASE)
#define PD_BASE_PTR ((GPIO_Type *) PD_BASE)

typedef enum //枚举端口方向
{
    
    
    GPI = 0, //定义管脚输入方向
    GPO = 1, //定义管脚输出方向
}GPIODIR_enum;

typedef enum //枚举端口电平
{
    
    
    GPIO_LOW = 0,   //定义低电平
    GPIO_HIGH = 1,  //定义高电平
}GPIOLEVEL_enum;

typedef enum    // 枚举上下拉选项
{
    
    
    NO_PULL,    //无输入上下拉
    PULLUP,     //输入上拉
    PULLDOWN,   //输入下拉
}GPIOMODE_enum;

#define PORT_BASE_PTRS {
      
      PA_BASE_PTR,PB_BASE_PTR,PC_BASE_PTR,PD_BASE_PTR}
void gpio_init(PIN_enum pin, GPIODIR_enum dir, GPIOLEVEL_enum level, GPIOMODE_enum pinmode);
uint8 gpio_get(PIN_enum pin);
void gpio_set(PIN_enum pin, uint8 level);

#endif /* LIB_MY_GPIO_H_ */

源文件

#include <my_gpio.h>
#include "headfile.h"

static GPIO_Type* const GPIOX[4] = PORT_BASE_PTRS;

void gpio_init(PIN_enum pin, GPIODIR_enum dir, GPIOLEVEL_enum level, GPIOMODE_enum pinmode)
{
    
    
    uint8 pin_x, pin_n;

    pin_x = pin >> 4;
    pin_n = pin & 0x0f;
    //清楚复用功能
    GPIOX[pin_x]->P_SEL &= ~(uint16)(1<<pin_n);

    if (dir == GPO)
    {
    
    
        GPIOX[pin_x]->P_DIR |= (uint16)(1<<pin_n);
        if (level == GPIO_HIGH)
            GPIOX[pin_x]->P_OUT |= (uint16)(1<<pin_n);
        else if (level == GPIO_LOW)
            GPIOX[pin_x]->P_OUT &= ~(uint16)(1<<pin_n);

        //配置上下拉
        if (pinmode == PULLUP)
        {
    
    
            GPIOX[pin_x]->P_EN  |= (uint16)(1<<pin_n);
        }
        else if (pinmode == PULLDOWN)
        {
    
    
            GPIOX[pin_x]->P_EN  |= (uint16)(1<<pin_n);
        }

    }
    else if (dir == GPI)
    {
    
    
        GPIOX[pin_x]->P_DIR &= ~(uint16)(1<<pin_n);
        GPIOX[pin_x]->P_EN  &= ~(uint16)(1<<pin_n);
        //配置上下拉
        if (pinmode == PULLUP)
        {
    
    
            GPIOX[pin_x]->P_EN  |= (uint16)(1<<pin_n);
            GPIOX[pin_x]->P_OUT |= (uint16)(1<<pin_n);
        }
        else if (pinmode == PULLDOWN)
        {
    
    
            GPIOX[pin_x]->P_EN  |= (uint16)(1<<pin_n);
            GPIOX[pin_x]->P_OUT &= ~(uint16)(1<<pin_n);
        }
        else
        {
    
    
            GPIOX[pin_x]->P_EN &= ~(uint16)(1<<pin_n);
        }

    }

    return ;
}


uint8 gpio_get(PIN_enum pin)
{
    
    
    uint8 pin_x, pin_n;

    pin_x = pin >> 4;
    pin_n = pin & 0x0f;

    return ((GPIOX[pin_x]->P_IN & (uint16)(1<<pin_n)) ? 1 : 0);
}

void gpio_set(PIN_enum pin, uint8 level)
{
    
    
    uint8 pin_x, pin_n;
    pin_x = pin >> 4;
    pin_n = pin & 0x0f;

    if (level)
        GPIOX[pin_x]->P_OUT |= (uint16)(1<<pin_n);
    else
        GPIOX[pin_x]->P_OUT &= ~(uint16)(1<<pin_n);

    return ;
}

定时器相关

选择将A1用作定时器中断,A0作为pwm输出。

定时器中断

初始化及其他

//将定时器A作为pit
void pit_init(uint16 ms)
{
    
    
    Timer_A_initUpModeParam param = {
    
    0};
    //结构体配置
    param.clockSource = TIMER_A_CLOCKSOURCE_ACLK;//32.768K
    param.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_32;
    param.timerPeriod = ms;
    param.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;//输入捕捉
    param.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE;//定时
    param.timerClear = TIMER_A_DO_CLEAR;
    param.startTimer = true;
    Timer_A_initUpMode(TIMER_A1_BASE, &param);//仅配置了一个通道    按照需求进行修改

}

//关闭所有pit
void pit_stop(void)
{
    
    
    Timer_A_disableInterrupt(TIMER_A1_BASE);
}

//重新开启所有中断
void pit_restart(void)
{
    
    
   Timer_A_enableInterrupt(TIMER_A1_BASE);
}

中断处理函数

#pragma vector = TIMER1_A1_VECTOR
__interrupt void timer_A1_ch1_2(void)
{
    
    

}

可以用switch选择中断信号产生的通道,进行多定时器中断(只需修改初始化代码)

pwm输出

头文件

#ifndef LIB_MY_PWM_H_
#define LIB_MY_PWM_H_



#include "headfile.h"
#include "timer_a.h"

#define TIMER_PERIOD 10000

typedef enum
{
    
    
    TIME_A0,
    TIME_A1,
    TIME_A2
}TIMER_A_ENUM;

typedef enum
{
    
    
    TIME_A_ch0 = 0x02,
    TIME_A_ch1 = 0x04,
    TIME_A_ch2 = 0x06,
    TIME_A_ch3 = 0x08,
    TIME_A_ch4 = 0x0A,
    TIME_A_ch5 = 0x0C,
    TIME_A_ch6 = 0x0E
}TIMER_A_ch;

void pwm_init(TI
MER_A_ENUM timeA_num, TIMER_A_ch timeA_ch, uint16 output_duty);
void pwm_duty_adjust(TIMER_A_ENUM timeA_num, TIMER_A_ch timeA_ch, uint16 output_duty);
void pwm_init_moto(TIMER_A_ENUM timeA_num, TIMER_A_ch timeA_ch, uint16 output_duty);

#endif /* LIB_MY_PWM_H_ */

源文件

/*
 * pwm.c
 *
 *  Created on: 2020年9月4日
 *      Author: 11845
 */

#include "my_pwm.h"

static uint16 const TIMER_A[3] = {
    
    TIMER_A0_BASE, TIMER_A1_BASE, TIMER_A2_BASE};

//自行添加
void pwm_pin_init(TIMER_A_ENUM timeA_num, TIMER_A_ch timeA_ch)
{
    
    
    if (timeA_num == TIME_A0)
    {
    
    
        if (timeA_ch == TIME_A_ch1)
        {
    
    
            P1DIR |= BIT2;
            P1SEL |= BIT2;
        }
        else if (timeA_ch == TIME_A_ch2)
        {
    
    
            P1DIR |= BIT3;
            P1SEL |= BIT3;
        }
        else if (timeA_ch == TIME_A_ch3)
        {
    
    
            P1DIR |= BIT4;
            P1SEL |= BIT4;
        }
        else if (timeA_ch == TIME_A_ch4)
        {
    
    
            P1DIR |= BIT5;
            P1SEL |= BIT5;
        }
    }
//    timeA1作为定时器
//    else if (timeA_num == TIME_A1)
//    {
    
    
//        if (timeA_ch == TIME_A_ch1)
//        {
    
    
//            P2DIR |= BIT0;
//            P2SEL |= BIT0;
//        }
//        else if (timeA_ch == TIME_A_ch2)
//        {
    
    
//            P2DIR |= BIT1;
//            P2SEL |= BIT1;
//        }
//    }
    else if (timeA_num == TIME_A2)
    {
    
    
        if (timeA_ch == TIME_A_ch0)
        {
    
    
            P2DIR |= BIT3;
            P2SEL |= BIT3;
        }
        else if (timeA_ch == TIME_A_ch1)
        {
    
    
            P2DIR |= BIT4;
            P2SEL |= BIT4;
        }
        else if (timeA_ch == TIME_A_ch2)
        {
    
    
            P2DIR |= BIT5;
            P2SEL |= BIT5;
        }
    }
}

//电机12000hz 舵机50hz  注意引脚选择
void pwm_init(TIMER_A_ENUM timeA_num, TIMER_A_ch timeA_ch, uint16 output_duty)
{
    
    

    Timer_A_outputPWMParam param_timerA = {
    
    0};
    //配置
    param_timerA.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    param_timerA.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_48;
    param_timerA.timerPeriod = TIMER_PERIOD;
    param_timerA.compareRegister = timeA_ch;
    param_timerA.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
    param_timerA.dutyCycle = output_duty;

    pwm_pin_init(timeA_num, timeA_ch);
    Timer_A_outputPWM(TIMER_A[timeA_num], &param_timerA);
}

void pwm_duty_adjust(TIMER_A_ENUM timeA_num, TIMER_A_ch timeA_ch, uint16 output_duty)
{
    
    
    HWREG16(TIMER_A[timeA_num] + timeA_ch + OFS_TAxR) = output_duty;
}

//频率太低      电机配置为12000hz 占空比0~1000
void pwm_init_moto(TIMER_A_ENUM timeA_num, TIMER_A_ch timeA_ch, uint16 output_duty)
{
    
    
    Timer_A_outputPWMParam param_timerA = {
    
    0};
    //配置
    param_timerA.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    param_timerA.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_2;
    param_timerA.timerPeriod = 1000;
    param_timerA.compareRegister = timeA_ch;
    param_timerA.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
    param_timerA.dutyCycle = output_duty;

    pwm_pin_init(timeA_num, timeA_ch);
    Timer_A_outputPWM(TIMER_A[timeA_num], &param_timerA);
}


硬件iic

这里是参考别的博主的,文章链接,自己写的时候我tx标志位总是不会置1。

源文件

void iic_init(void)
{
    
    
    P3SEL &= ~BIT1;
    P3DIR |= BIT1;
    P3OUT |= BIT1;
    // 输出9个时钟以恢复I2C总线状态
    uint8 i= 0;
    for(i= 0; i <9 ; i++)
    {
    
    
        P3OUT |= BIT1;
        __delay_cycles(8000);
        P3OUT &= ~BIT1;
        __delay_cycles(8000);
    }

    P3SEL |= (BIT1 + BIT0);

    UCB0CTL1 |= UCSWRST;
    UCB0CTL0 = UCMST+ UCMODE_3 + UCSYNC;   // I2C主机模式
    UCB0CTL1 |= UCSSEL_2;                  // 选择SMCLK
    UCB0BR0 = 40;
    UCB0BR1 = 0;
    UCB0CTL0 &= ~UCSLA10;                  // 7位地址模式
    UCB0I2CSA = 0x1E;
    UCB0CTL1 &= ~UCSWRST;

}


uint8 iic_write_reg(uint8 salve, uint8 reg, uint8 data)
{
    
    
    UCB0I2CSA = salve;
    while( UCB0CTL1& UCTXSTP );
    UCB0CTL1 |= UCTR;                // 写模式
    UCB0CTL1 |= UCTXSTT;             // 发送启动位

    UCB0TXBUF = reg;           // 发送字节地址
    // 等待UCTXIFG=1与UCTXSTT=0 同时变化等待一个标志位即可
    while(!(UCB0IFG& UCTXIFG))
    {
    
    
        if( UCB0IFG& UCNACKIFG )      // 若无应答 UCNACKIFG=1
        {
    
    
          return 1;
        }
    }

    UCB0TXBUF = data;          // 发送字节内容
    while(!(UCB0IFG& UCTXIFG));     // 等待UCTXIFG=1

    UCB0CTL1 |= UCTXSTP;
    while(UCB0CTL1& UCTXSTP);       // 等待发送完成

    return 0;

}

uint8 iic_read_reg(uint8 salve, uint8 reg, uint8* data, uint8 len)
{
    
    
    UCB0I2CSA = salve;
    while( UCB0CTL1& UCTXSTP );
    UCB0CTL1 |= UCTR;                // 写模式
    UCB0CTL1 |= UCTXSTT;             // 发送启动位和写控制字节

    UCB0TXBUF = reg;           // 发送字节地址
    // 等待UCTXIFG=1与UCTXSTT=0 同时变化等待一个标志位即可
    while(!(UCB0IFG& UCTXIFG))
    {
    
    
        if( UCB0IFG& UCNACKIFG )      // 若无应答 UCNACKIFG=1
        {
    
    
          return 1;
        }
    }

    UCB0CTL1 &= ~UCTR;               // 读模式
    UCB0CTL1 |= UCTXSTT;             // 发送启动位和读控制字节

    while(UCB0CTL1& UCTXSTT);       // 等待UCTXSTT=0
    // 若无应答 UCNACKIFG = 1
    uint8_t i= 0;
    for( i= 0; i< len -1 ; i++)
    {
    
    
        while(!(UCB0IFG& UCRXIFG));   // 读取字节内容,不包括最后一个字节内容
        *data++= UCB0RXBUF;
    }

    UCB0CTL1 |= UCTXSTP;             // 在接收最后一个字节之前发送停止位

    while(!(UCB0IFG& UCRXIFG));     // 读取最后一个字节内容
    *data = UCB0RXBUF;

    while( UCB0CTL1& UCTXSTP );

    return 0;
}

效果查看

#include "fx_data_read.h"

float K1 = 0.01;
float dtt = 0.01;
int16_t angleyy = 0;
int16_t angleylast = 0;
SRAWDATA FXA_data,FXO_data;

void FXA_init(void)
{
    
    
    iic_write_reg(FXA_SLAVE_ADDR, FXA_CTRL_REG1, 0x02);
}

void FXA_read(SRAWDATA* pAccelData)
{
    
    
    uint8 Buffer[7];

    iic_read_reg(FXA_SLAVE_ADDR, FXA_STATUS, Buffer, 7);
    pAccelData->x = (int16_t)(((Buffer[1] << 8) | Buffer[2]));// /16.4
    pAccelData->y = (int16_t)(((Buffer[3] << 8) | Buffer[4]));
    pAccelData->z = (int16_t)(((Buffer[5] << 8) | Buffer[6]));
}

void FXO_init(void)
{
    
    
    iic_init();
    iic_write_reg(FXO_SLAVE_ADDR, FXO_XYZ_DATA_CFG, 0x00);//range 2g,  sensitivity 0.244mg/lsb
//    i2c_write_reg(FXO_SLAVE_ADDR, HP_FILTER_CUTOFF, 0x10);
    iic_write_reg(FXO_SLAVE_ADDR, FXO_CTRL_REG1, 0x01);

}

void FXO_read(SRAWDATA* pAccelData)
{
    
    
    uint8 Buffer[7] = {
    
    0};

    iic_read_reg(FXO_SLAVE_ADDR, FXO_STATUS, Buffer, 7);
    pAccelData->x = ((int16_t)(((Buffer[1] << 8) | Buffer[2]))>> 2);
    pAccelData->y = ((int16_t)(((Buffer[3] << 8) | Buffer[4]))>> 2);
    pAccelData->z = ((int16_t)(((Buffer[5] << 8) | Buffer[6]))>> 2);

}

下面为效果图:
在这里插入图片描述
这里为读出来陀螺仪和加速度计三个轴的数据。

串口

#include "my_uart.h"

//引脚 P03_3 P03_4    模块UCA0
void uart_init(uint32 baud)
{
    
    
    UCA0CTL1 |= UCSWRST;
    P3SEL |= (BIT3 + BIT4);
    //时钟分频
    UCA0CTL1 |= UCSSEL__SMCLK;//smclk时钟     24.9M
    uint16 prescaler = (uint16)(CPU_F / baud);//分频系数
    UCA0BR0 = (uint8)(prescaler & 0x00ff);
    UCA0BR1 = (uint8)(prescaler >> 8);
    UCA0MCTL |= UCBRS_2 + UCBRF_0;

    UCA0CTL1 &= ~UCSWRST;

    UCA0IE |= UCRXIE;//使能接受中断
}

void uart_put_char(uint8 data)
{
    
    
    UCA0TXBUF = data;
    while(!(UCA0IFG & UCTXIFG));

}

void uart_put_buf(uint8* data, uint8 len)
{
    
    
    while (len--)
    {
    
    
        uart_put_char(*(data++));
    }
}

串口有一点不同的是,分频时不光要配置整数部分(也就是UCA0BR0 和UCA0BR1寄存器),还有小数部分的寄存器(UCA0MCTL ),可以对照手册里面的表格进行配置。

小结

计划做的控制类题目,这些功能应该够用了(不够用了再补充)。

猜你喜欢

转载自blog.csdn.net/qq_43550173/article/details/108522271