MSP432驱动舵机串口输出角度
备注:我用的TI官方launchpad的MSP432P401R开发板
1、舵机需要50Hz基准的PWM,占空比是0.025~0.125.如何产生PWM,当然是定时器了。查看MSP432P401R数据手册,有4个定时器。
2、要算出详细的具体PWM频率,就需要知道系统的时钟,定时器的时钟。MSP432时钟来源比较复杂。具体可以看手册。(上TI官网下载)
5个时钟源,这里我选择HFXTCLK外部高速时钟(48MHz)
3、定时器的时钟来源可以有4种选择
这里我选择SMCLK时钟,定时器和串口都是SMCLK时钟源。
4、定时器具体配置代码
<1>、定义PWM结构并初始化
Timer_A_PWMConfig TIM0_PwmConfig =
{
TIMER_A_CLOCKSOURCE_SMCLK,
TIMER_A_CLOCKSOURCE_DIVIDER_4,//12MHZ / 4 = 3MHz
60000,//3000 000 / 50 = 60000
TIMER_A_CAPTURECOMPARE_REGISTER_1,
TIMER_A_OUTPUTMODE_RESET_SET,
1500//舵机初始化角度
};
<2>、定时器初始化
void TimerA0_Init(void)
{
/* Configuring GPIO2.4 as peripheral output for PWM */
MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P2,
GPIO_PIN4,
GPIO_PRIMARY_MODULE_FUNCTION);
/* Configuring Timer_A to have a period of approximately 500ms and
* an initial duty cycle of 10% of that (3200 ticks) */
MAP_Timer_A_generatePWM(TIMER_A0_BASE, &TIM0_PwmConfig);
}
<3>、舵机角度控制
/**
*************************************************************************
* @brief: Servo_TurnAngle
* @param: uch_angle -- 舵机旋转角度
* @return: void
* @function: 舵机旋转角度
* @author:
* @version: V1.0
* @note: 舵机需要50hz 3000000 / 60000= 50hz 60000
0度 0.5/20=0.025 60000*0.025= 1500
180度 2.5/20=0.125 60000*0.125= 7500
*************************************************************************
**/
void Servo_TurnAngle(INT8U uch_angle)
{
FP32 f_cycle;
f_cycle = (7500.0f - 1500.0f) / 180.0f * uch_angle + 1500.0f;//定时器计数与角度
TIM0_PwmConfig.dutyCycle = (INT16U)f_cycle;//新的占空比
TIM2_PwmConfig.dutyCycle = (INT16U)f_cycle;//新的占空比
MAP_Timer_A_generatePWM(TIMER_A0_BASE, &TIM0_PwmConfig);
}
5、串口配置代码实现
<1>、串口初始化
/**
*************************************************************************
* @brief: Uart0_Init
* @param: void
* @return: void
* @function: 串口0初始化 波特率9600
* @author:
* @version: V1.0
* @note:http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
*************************************************************************
**/
void Uart0_Init(void)
{
eUSCI_UART_Config uartConfig =
{
EUSCI_A_UART_CLOCKSOURCE_SMCLK, // SMCLK Clock Source
78, // BRDIV = 78
2, // UCxBRF = 2
0, // UCxBRS = 0
EUSCI_A_UART_NO_PARITY, // No Parity
EUSCI_A_UART_LSB_FIRST, // LSB First
EUSCI_A_UART_ONE_STOP_BIT, // One stop bit
EUSCI_A_UART_MODE, // UART mode
EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION // Oversampling
};
/* Selecting P1.2 and P1.3 in UART mode */
MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
GPIO_PIN2 | GPIO_PIN3,
GPIO_PRIMARY_MODULE_FUNCTION);
/* Configuring UART Module */
MAP_UART_initModule(EUSCI_A0_BASE, &uartConfig);
/* Enable UART module */
MAP_UART_enableModule(EUSCI_A0_BASE);
/* Enabling interrupts */
MAP_UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT);
MAP_Interrupt_enableInterrupt(INT_EUSCIA0);
MAP_Interrupt_enableSleepOnIsrExit();
MAP_Interrupt_enableMaster();
}
这里,串口时钟来源也是有4种,这里我还是选SMCLK.串口配置结构体内的参数,根据note网址计算。也可根据手册自己计算,过程比较复杂,还是用官方出的工具吧。
<2>、发送数据
/**
*************************************************************************
* @brief: Uart0_SendByte
* @param: uch_byte -- 字节
* @return: void
* @function:
* @author:
* @version: V1.0
* @note:
*************************************************************************
**/
void Uart0_SendByte(INT8U uch_byte)
{
MAP_UART_transmitData(EUSCI_A0_BASE, uch_byte);
}
/**
*************************************************************************
* @brief: Uart0_SendDtring
* @param: puch_buf -- 缓存指针 uin_len --数据长度
* @return: void
* @function: 串口发送字符串
* @author:
* @version: V1.0
* @note:
*************************************************************************
**/
void Uart0_SendDtring(INT8U *puch_buf, INT16U uin_len)
{
INT16U i;
for(i = 0; i < uin_len; i++)
{
Uart0_SendByte(*(puch_buf + i));
}
}
<3>、串口接收中断服务函数
/**
*************************************************************************
* @brief: EUSCIA0_IRQHandler
* @param: void
* @return: void
* @function: 串口中断服务函数
* @author:
* @version: V1.0
* @note:
*************************************************************************
**/
void EUSCIA0_IRQHandler(void)
{
uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_BASE);
MAP_UART_clearInterruptFlag(EUSCI_A0_BASE, status);
if(status & EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG)
{
MAP_UART_transmitData(EUSCI_A0_BASE, MAP_UART_receiveData(EUSCI_A0_BASE));
}
}
接收到什么,重新发回。这里需要接收数据,增加接收缓存即可。
5、主函数代码
<1>、初始化定时器、串口
MAP_WDT_A_holdTimer();//挂起看门狗
SystemClockInit(HFXT);//初始化时钟到48MHz
/* Enabling the FPU for floating point operation */
MAP_FPU_enableModule();
MAP_FPU_enableLazyStacking();//打开FPU,硬件运算浮点数
CS_initClockSignal(CS_SMCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_4);//48MHz / 4 = 12MHz
TimerA0_Init();//定时器A0初始化
KEY_Init();//按键初始化
Uart0_Init();//串口0初始化 9600
GlobalValue_Init();//全局变量初始化
Interrupt_enableMaster();//使能全局中断
<2>、循环执行
while (1)
{
Key_Scanf();//按键扫描
if(t++ / 100)
{
t = 0;
LED_Control(GREEN_LED);//绿灯
}
delay_us(1);//短暂延时
sprintf((char*)buff, "舵机角度:%d\r\n", guch_ServoAngle);
Uart0_SendDtring(buff, strlen((char*)buff));//发送数据
}
<3>系统时钟代码
/**********************************
时钟: 默认时钟源 默认频率 描述
MCLK DCO 3MHZ 主时钟,向CPU和外设提供时钟
HSMCLK DCO 3MHZ 子系统主时钟,向外设提供时钟源
SMCLK DCO 3MHZ 低速系统主时钟,向外设提供时钟源
ACLK LFXT(或REFO没有晶振时) 32.768kHz 辅助时钟,向外设提供时钟
BCLK LFXT(或REFO没有晶振时) 32.768kHz 低速后配域时钟,提供LPM外设
********************************/
void SystemClockInit(u8 ClockSource)
{
/* Halting the Watchdog */
MAP_WDT_A_holdTimer();
if(ClockSource==LFXT ){
///
/* 配置外部低速时钟引脚*/
MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_PJ,
GPIO_PIN0 | GPIO_PIN1, GPIO_PRIMARY_MODULE_FUNCTION);
/* Setting the external clock frequency. This API is optional, but will
* come in handy if the user ever wants to use the getMCLK/getACLK/etc
* functions
*/
CS_setExternalClockSourceFrequency(32768,48000000);
/* Starting LFXT in non-bypass mode without a timeout. */
CS_startLFXT(false);
/* Initializing MCLK to LFXT (effectively 32khz) */ //主时钟
MAP_CS_initClockSignal(CS_ACLK, CS_LFXTCLK_SELECT, CS_CLOCK_DIVIDER_1);
/* Since we are operating at 32khz, we can operating in LF mode */
// MAP_PCM_setPowerMode(PCM_LF_MODE);
}
else if (ClockSource==HFXT){
/* 配置外部高速时钟引脚 */
GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_PJ,
GPIO_PIN3 | GPIO_PIN2, GPIO_PRIMARY_MODULE_FUNCTION);
/* Just in case the user wants to use the getACLK, getMCLK, etc. functions,
* let's set the clock frequency in the code.
*/
CS_setExternalClockSourceFrequency(32000,48000000);
/* Starting HFXT in non-bypass mode without a timeout. Before we start
* we have to change VCORE to 1 to support the 48MHz frequency */
PCM_setCoreVoltageLevel(PCM_VCORE1);
FlashCtl_setWaitState(FLASH_BANK0, 2);
FlashCtl_setWaitState(FLASH_BANK1, 2);
CS_startHFXT(false);//false
/* Initializing MCLK to HFXT (effectively 48MHz) */
MAP_CS_initClockSignal(CS_MCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_1);
}
else if (ClockSource==DCO){
/*DCO时钟源配置,内部数控振荡器*/
/* Enabling FPU for DCO Frequency calculation */
FPU_enableModule();
/* Setting the DCO Frequency to a non-standard 12MHz */
CS_setDCOFrequency(CS_12MHZ);
CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_12);//8~16MHZ
// CS_setDCOFrequency(CS_48MHZ);
// CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_48);//8~16MHZ
CS_initClockSignal(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);
CS_initClockSignal(CS_HSMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);//告诉子系统时钟
}
else {
//
/*内部低功耗低频时钟*/
/*设置频率*/
CS_setReferenceOscillatorFrequency(CS_REFO_32KHZ);
/* Initializing MCLK to REFO */
MAP_CS_initClockSignal(CS_BCLK, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
/* Since we are operating at 32khz, we can operating in LF mode */
MAP_PCM_setPowerMode(PCM_LF_MODE);
}
}
6、按键代码
两个按键控制舵机角度的加减
<1>、按键初始化·
/**
*************************************************************************
* @brief: KEY_Init
* @param: void
* @return: void
* @function: 按键初始化
* @author:
* @version: V1.0
* @note:
*************************************************************************
**/
void KEY_Init(void)
{
MAP_GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN1);
MAP_GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN4);
}
<2>、按键扫描
/**
*************************************************************************
* @brief: Key_Scanf
* @param: void
* @return: void
* @function: 按键扫描
* @author:
* @version: V1.0
* @note:
*************************************************************************
**/
void Key_Scanf(void)
{
if(!MAP_GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN4))//S2按下
{
while(!MAP_GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN4));//等待按键释放
if(guch_ServoAngle <= 0)
guch_ServoAngle = 0;
else
guch_ServoAngle -= 5;
Servo_TurnAngle(guch_ServoAngle);
}
if(!MAP_GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1))//S1按下
{
while(!MAP_GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1));//等待按键释放
if(guch_ServoAngle >= 180)
guch_ServoAngle = 180;
else
guch_ServoAngle += 5;
Servo_TurnAngle(guch_ServoAngle);
}
}