最近准备电赛,学习了一下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, ¶m);//仅配置了一个通道 按照需求进行修改
}
//关闭所有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], ¶m_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], ¶m_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 ),可以对照手册里面的表格进行配置。
小结
计划做的控制类题目,这些功能应该够用了(不够用了再补充)。