基于msp430实现游戏手柄控制LCD上的信息
通过msp430驱动LCD显示屏,并且读取IO扩展板上的手柄输出的PWM值来改变手柄的方向
标签
嵌入式系统
显示
2023寒假在家练
qianju
更新2023-03-28
410

1.项目要求

       本任务需要用MSP430板测量IO扩展板上的PWM信号,在LCD上以图形化的方式显示游戏摇杆的变化,通过游戏摇杆的拨动,能够触及LCD的全屏幕。

2.项目硬件

2.1简介:

MSP-EXP430F5529LP是一款针对MSP430F5529 USB微控 制器的廉价而简单的开发套件。它为MSP430 MCU提供了一种简单的方法,具有用于编程和调试的板载仿真,以及用于简单用户界面的按钮和LED。

2.2特性:

  • 支持 USB 2.0 的 MSP430F5529 16 位微控制器
  • 高达 25 MHz
  • 128KB 闪存和 8KB 内存
  • 12 位 SAR 模数转换器
  • 提供各种 USB 设备类示例和嵌入式软件库(CDC、HID、MSC)
  • eZ-FET lite:带应用程序 UART 的开源板载调试器
  • 通过使用板载 USB 集线器,为仿真器和目标提供 USB 连接
  • USB 作为电源:5V 和 3.3V 通过高效 DC/DC 转换器
  • 40 针 LaunchPad 标准,利用 BoosterPack 生态系统

2.3输入、输出扩展板介绍:

  • 按键、旋转编码器输入 - 以模拟信号的方式
  • 双电位计控制输入 - 以数字信号的方式
  • RGB三色LED显示
  • 1.44寸128*128 LCD,SPI总线访问
  • MMA7660三轴姿态传感器
  • 电阻加热
  • 温度传感器
  • 与MSP430 Launch Pad开发板的接口

3.环境配置

  • CCSTUDIO — Code Composer Studio™ 集成式开发环境 (IDE)

它是美国德州仪器公司(Texas Instrument,TI)出品的代码开发和调试套件。TI公司的产品线中有一大块业务是数字信号处理器(DSP)和微处理器(MCU),CCS便是供用户开发和调试DSP和MCU程序的集成开发软件。

GCC(GNU Compiler Collection,GNU 编译器套装),是一套由 GNU 开发的编程语言编译器。GCC 原名为 GNU C 语言编译器,因为它原本只能处理 C语言。GCC 快速演进,变得可处理 C++、Fortran、Pascal、Objective-C、Java 以及 Ada 等他语言。

4.完成的功能及达到的性能

上电后,便可以看见左上角显示频率,以及占空比,通过移动手柄,实现中间黄色圆圈的移动。

发现向右移动频率提升,向左频率下降,向上占空比下降,向下占空比上升。

5.实现思路

  • 通过定时器的输入捕获捕获其高低电平,再通过计算得出占空比和频率;
  • 通过提高msp430的时钟频率让其能正常使用spi;
  • 通过查询地址,得出如何操控基于st7789的1.44寸LCD屏幕;
  • 最后通过查看占空比和频率得到如何操控整个屏幕最后做出项目。

以下是设计流程图

FqZgtgGm_2QGgrUFcesiEDyRRTzE

6.程序实现

6.1提高定时器的时钟频率

为了让软件spi能正常的运行下去,首先我们得将默认低时钟频率的msp430提高一定的频率让它能正常运行。

提高电压

void SetVcoreUp(unsigned int level)
{
    // Open PMM registers for write
    PMMCTL0_H = PMMPW_H;
    // Set SVS/SVM high side new level
    SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;
    // Set SVM low side to new level
    SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;
    // Wait till SVM is settled
    while ((PMMIFG & SVSMLDLYIFG) == 0)
        ;
    // Clear already set flags
    PMMIFG &= ~(SVMLVLRIFG + SVMLIFG);
    // Set VCore to new level
    PMMCTL0_L = PMMCOREV0 * level;
    // Wait till new level reached
    if ((PMMIFG & SVMLIFG))
        while ((PMMIFG & SVMLVLRIFG) == 0)
            ;
    // Set SVS/SVM low side to new level
    SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
    // Lock PMM registers for write access
    PMMCTL0_H = 0x00;
}

提高频率

void initClock(void)
{
    // Increase Vcore setting to level3 to support fsystem=25MHz
    // NOTE: Change core voltage one level at a time..
    SetVcoreUp(0x01);
    SetVcoreUp(0x02);
    SetVcoreUp(0x03);

    UCSCTL3 = SELREF_2;                       // Set DCO FLL reference = REFO
    UCSCTL4 |= SELA_2;                        // Set ACLK = REFO

    __bis_SR_register(SCG0);                  // Disable the FLL control loop
    UCSCTL0 = 0x0000;                         // Set lowest possible DCOx, MODx
    UCSCTL1 = DCORSEL_7;                     // Select DCO range 50MHz operation
    UCSCTL2 = FLLD_0 + 762;                   // Set DCO Multiplier for 25MHz
                                              // (N + 1) * FLLRef = Fdco
                                              // (762 + 1) * 32768 = 25MHz
                                              // Set FLL Div = fDCOCLK/2
    __bic_SR_register(SCG0);                  // Enable the FLL control loop

    // Worst-case settling time for the DCO when the DCO range bits have been
    // changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
    // UG for optimization.
    // 32 x 32 x 25 MHz / 32,768 Hz ~ 780k MCLK cycles for DCO to settle
    __delay_cycles(782000);

    // Loop until XT1,XT2 & DCO stabilizes - In this case only DCO has to stabilize
    do
    {
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
        // Clear XT2,XT1,DCO fault flags
        SFRIFG1 &= ~OFIFG;                      // Clear fault flags
    } while (SFRIFG1 & OFIFG);                   // Test oscillator fault flag

}

6.2定时器中断输入捕获

为了能获取到拓展板上的PWM波形,我们需要将定时器变换到输入捕获状态,定时器中断就是间隔一定的时间,执行一次中断服务函数。

定时器初始化

void Frequence_Duty_init()
{
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN2);
    //选用P1.2   使用TA0.1 Timer Capture

    Timer_A_initContinuousModeParam Counter = {0};
    Counter.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    Counter.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;
    Counter.startTimer = true;
    Counter.timerClear = TIMER_A_DO_CLEAR;
    Counter.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE;
    Timer_A_initContinuousMode(TIMER_A0_BASE, &Counter);
    //开启一个连续模式的计时

    Timer_A_initCaptureModeParam CaptureParam = {0};
    CaptureParam.captureRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
    CaptureParam.captureMode = TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE;
    CaptureParam.captureInputSelect = TIMER_A_CAPTURE_INPUTSELECT_CCIxA;
    CaptureParam.synchronizeCaptureSource = TIMER_A_CAPTURE_SYNCHRONOUS;
    CaptureParam.captureInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    CaptureParam.captureOutputMode = TIMER_A_OUTPUTMODE_OUTBITVALUE;
    Timer_A_initCaptureMode(TIMER_A0_BASE,&CaptureParam);
    //开启捕获模式,并开启它的中断

}

定时器中断

#pragma vector=TIMER0_A1_VECTOR
__interrupt
void TIMER0_A1_ISR (void)
{
   static uint16_t Overflow_Times = 0;

    switch(TA0IV)
    {
        case TA0IV_TACCR1:

            if(GPIO_getInputPinValue(GPIO_PORT_P1,GPIO_PIN2)==1 && index == 0)  //获取引脚电平,如果为高电平,将当前寄存器值存入Sign_Begin
            {
                Sign_Begin = TA0R;
                index=(index+1)%3;
                Overflow_Times = 0;
            }

            else  if(index==1  &&  GPIO_getInputPinValue(GPIO_PORT_P1,GPIO_PIN2)==0)         //获取引脚电平,如果为低电平,将当前寄存器值存入Sign_End
            {

                Sign_End = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
                index=(index+1)%3;

                if(!Overflow_Times)    //计算高电平时间,如果高电平和低电平都在一个计数周期之内,进入
                    {
                    Sign_Counts = Sign_End - Sign_Begin;   //如果高低电平在同一个计数周期内,那么直接相减

                    }
                else                   //计算高电平时间,如果高电平和低电平不在一个计数周期之内,进入
                {
                    Sign_Counts = (uint32_t)(65536 * (Overflow_Times )+ Sign_End - Sign_Begin);  //如果高低电平不在同一个计数周期,需要先加上一个周期的计数值

                }
            }



            else if (index==2  &&  GPIO_getInputPinValue(GPIO_PORT_P1,GPIO_PIN2)==1)
            {
                Sign_All=Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);

                if(!Overflow_Times)    //计算高电平时间,如果高电平和低电平都在一个计数周期之内,进入
                     {
                     Sign_Counts1 = Sign_All - Sign_Begin;   //如果高低电平在同一个计数周期内,那么直接相减

                     }

                 else                   //计算高电平时间,如果高电平和低电平不在一个计数周期之内,进入
                 {
                     //注意,这里强制类型转换,是因为uint16_t 的最大值为65535,此处的Sign_Counts值会明显大于65535
                     Sign_Counts1 = (uint32_t)(65536 * (Overflow_Times) + Sign_All - Sign_Begin);  //如果高低电平不在同一个计数周期,需要先加上一个周期的计数值
                 }
                index=(index+1)%3;

            }
            Timer_A_clearCaptureCompareInterrupt(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
            break;
        case TA0IV_TAIFG:
                Overflow_Times++;


            Timer_A_clearTimerInterrupt(TIMER_A0_BASE);
            break;
        default:
            break;
    }

}

6.3 1.44寸的LCD屏幕初始化

为了完成任务中控制屏幕上的信息我们可以先上拓展版上的LCD屏幕初始化,再然后通过代码实现画一个小圆圈,从而使得我们可以通过遥杆控制圆圈。

LCD屏幕初始化

void ST7735_display_init( void )
{
  ST7735_command( SW_RESET );
  pause();
  ST7735_command( SLEEP_OUT );
  pause();
  ST7735_command( DISPLAY_ON );
  pause();

  data[0] = 0xC0;
  ST7735_command(0x36); // 屏幕显示方向设置
  ST7735_data(data,1);

}

画圆函数

void Draw_Circle(unsigned short x0,unsigned short y0,unsigned short r,unsigned short color)
{
    int a,b;
    a=0;b=r;

    if(x0<=0)
    {
        x0=0;
    }
    else if(x0>=130)
    {
        x0=130;
    }

    if(y0<=0)
    {
        y0=0;
    }
    else if(y0>=127)
    {
        y0=127;
    }


    while(a<=b)
    {
        LCD_DrawPoint(x0-b,y0-a,color);             //3
        LCD_DrawPoint(x0+b,y0-a,color);             //0
        LCD_DrawPoint(x0-a,y0+b,color);             //1
        LCD_DrawPoint(x0-a,y0-b,color);             //2

        LCD_DrawPoint(x0+a,y0-b,color);             //5
        LCD_DrawPoint(x0+a,y0+b,color);             //6
        LCD_DrawPoint(x0+b,y0+a,color);             //4
        LCD_DrawPoint(x0-b,y0+a,color);             //7
        a++;
        if((a*a+b*b)>(r*r))   //判断要 画的点是否过远
        {
            b--;
        }
    }

}

6.4 完成主要的功能

为了让摇杆能控制圆圈并且达到整个屏幕的范围,下面为操控圆圈的函数

操控圆圈

void title_circle (void)
{

    if(x_centre_circle>Frequency)           //左半部分
    {
        x_circle =(Frequency-x_Fre_L)/0.8125;
        if(Frequency<x_Fre_L) x_circle=0;
    }
    else if(x_centre_circle<Frequency)      //右半部分
    {
        x_circle = 64+(Frequency-x_centre_circle)/1.3593;
    }
    if(y_centre_circle>Duty)                //上半部分
    {
        y_circle = (Duty-y_Duty_U)*3;
        if(Duty<y_Duty_U) y_circle=3;

    }

    else if(y_centre_circle<Duty)
    {
        y_circle = 64+(Duty-y_centre_circle)*3.5;
    }
    if(x_centre_circle == Frequency)
        x_circle = 64;
    if(y_centre_circle == Duty)
        y_circle = 64;

        LCD_ShowNum(0,0,Frequency,5,12);
        LCD_ShowNum(20,0,Duty,5,12);
        Draw_Circle(x_circle,y_circle,3,YELLOW);

}

7.主要的困难

7.1MSP430的学习

这次活动是第一次参加,对于一些MSP430的操作还是不太会,还有定时器的操作还是不会,只能上网上搜索并参考。

7.2 屏幕的操作

对于屏幕的操作,若不对其限幅的话会使得它跳到另一端,最初不知道还在调其他的东西。

7.3 圆圈的操作

通过测量发现若只对上下左右限制会导致四个角落无法接触,所以通过思考,可以先测量出四个角落的频率,再对测量出来的进行限幅。

8.未来的计划建议

该项目已经完成游戏手柄控制LCD上的信息,并达到了预期指标。然而可以通过更改可以将项目变得更好:

  • 将软件spi更改成硬件spi可以让屏幕更快的刷新
  • 将MSP430的时钟频率提升至45M,从而让屏幕更快的刷新以及更快的读取PWM波形。

关于这次项目设计,我花费了比较多的心思,既是对单片机理论内容的一次复习和巩固,还让我们丰富了更多与该专业相关的其他知识,比如软件应用等,在摸索中学习,在摸索中成长,在学习的过程中带着问题去学我发现效率很高,这是我做这次课程设计的又一收获,在真正设计之前我们做了相当丰富的准备,首先巩固一下课程理论,再一遍熟悉课程知识的构架,然后结合加以理论分析、总结,有了一个清晰的思路和一个完整的的软件流程图之后才着手设计。在设计程序时,我们不能妄想一次就将整个程序设计好,反复修改、不断改进是程序设计的必经之路;养成注释程序的好习惯是非常必要的,一个程序的完美与否不仅仅是实现功能,而应该让人一看就能明白你的思路,这样也能为资料的保存和交流提供了方便,我觉得在设计课程过程中遇到问题是很正常,但我们应该将每次遇到的问题记录下来,并分析清楚,以免下次再碰到同样的问题的课程设计又出错了。

附件下载
新建 WinRAR ZIP 压缩文件.zip
源代码
团队介绍
本人
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号