项目介绍:
本项目使用Arduino开发框架进行开发,通过程序获取三轴传感器的信息,来判断MSP-EXP430F5529LP开发板+扩展板倾斜的方向,从而控制扩展板上的RGB灯显示不同的颜色,并驱动扩展板上的ST7735显示当前的各项数据。当静置开发板和扩展板一段时间后,RGB灯将会周期性的闪烁交替显示。
设计思路:
要实现本项目的功能,需要完成以下几项工作:
- 三轴传感器信息的获取
- ST7735显示屏的控制
- RGB灯的控制
因为习惯用Arduino开发,所以计划用Arduino框架进行开发。但Arduino IDE本身不支持MSP430,而430官方的Energia IDE又年久失修无法使用。
经过了解,PlatformIO能够提供对MSP430的Arduino开发框架的支持,相关的文档地址:https://docs.platformio.org/en/latest/platforms/timsp430.html
所以在VSCode+PlatformIO上,配置好了开发环境,并且找到了以上几项工作的实现方法。
但MSP430的整个工具链环境不是较新的,所以很多现有的第三方库都不能使用,需要好好寻找可以使用的库。
板载的三轴传感器芯片为MMA7660,对应的第三方库为:https://github.com/mcauser/Grove-3Axis-Digital-Accelerometer-1.5g-MMA7660FC,这个库在platformio里面可以直接安装。
板载的ST7735,也只能使用较老的第三方库,可用的为:https://github.com/olikraus/Ucglib_Arduino ,这个库在platformio里面可以直接安装,也可以从git下载了安装使用。不过该库支持板载的ST7735有点小问题,会在右边有一点点的白边。感兴趣的同学,可以调整位移来消除。
RGB灯的控制就比较简单了,通过数字IO端口控制即可。
当通过三轴传感器读取到信息后,克将对应各轴的信息,显示到ST7735屏幕上。
然后,通过XY方向的数据,判断倾斜的情况,从而控制RGB(LED1、LED2、LED3)的显示。
硬件介绍:
本项目使用的硬件,有硬禾提供:
通过开发板的实用手册、原理图来获取对应的GPIO控制信息:
据此得到对照关系图:
从上可以得知:
- 板载三轴传感器为MMA7660FC,使用I2C接口通信,对应的GPIO口为:
- SCL:P4.2
- SDA:P4.1
- 因为使用的是硬件I2C,所以在代码中不需要设置改信息,直接使用即可
 
- 板载ST7735对应的GPIO为:
- LCD_SCL:P3.2
- LCD_SDA:P3.0
- LCD_DCx:P2.7
- LCD_CSn:P2.6
- LCD_RSTn:P3.7
- 该ST7735使用硬件SPI通信,所以SCL、SDA不用在代码中特殊设置即可使用
 
- 板载RGB三色LED对应的控制GPIO:
- LED1:P2.5
- LED2:P2.4
- LED3:P1.5
 
因为扩展板提供了上述外设完整的连接和控制线路,所以无需其他辅助,仅使用MSP核心板+扩展板就可以完成全部工作。
实现功能:
基于Arduino开发框架,本项目最终视线了如下的功能:
- 获取三轴传感器的信息,并将其对应的信息分别显示到屏幕和输出到串口
- 通过获取的信息,判断开发板左右倾斜和前后倾斜的情况,通过该情况,来控制LED1、LED2、LED3的实际先许昌
- 当静置开发板30秒以后,将会依次闪烁LED1、LED2、LED3,每个灯每次闪烁三次,然后切换到下一个灯。
- 闪烁的初始间隔时间为100毫秒,如果持续静置,则每个循环中增加5毫秒,直到最大1秒为止。如果检测到三轴传感器达到倾斜位置,则自动恢复根据倾斜方向控制LED的状态。
代码说明:
本项目最终的代码如下:
#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>
#include <driverlib.h>
#include "MMA7660.h"
#include "Ucglib.h"
// LED设置
#define LED1 P2_5
#define LED2 P2_4
#define LED3 P1_5
// 最大静置时间
#define REST_TIME_MAX 30
// ST7735定义
Ucglib_ST7735_18x128x160_HWSPI ucg(/*cd=*/P2_7, /*cs=*/P2_6, /*reset=*/P3_7);
// 三轴传感器定义
MMA7660 acc;
// 三轴传感器状态数据
float ax, ay, az;
int8_t x, y, z;
// 三周传感器检测中间变量
int8_t step = 0;
int8_t ix = 0;
int8_t iy = 0;
// 静置时间变量
unsigned long rest_time_count = 0;
unsigned long rest_time_prev = 0;
bool rest_time_status = 0;
// 开发板自身LED状态
bool red_led_status = 0;
// RGB灯索引
uint8_t led_idx = 1;
uint8_t leds_status = 0;
// 延时时间
uint32_t delay_time = 100;
void setup()
{
    // disableWatchDog();
    // 三轴传感器初始化
    acc.init();
    // 串口初始化
    Serial.begin(115200);
    // V-HEAD,关闭加热
    pinMode(P1_4, OUTPUT);
    digitalWrite(P1_4, LOW);
    // 初始化数字IO
    pinMode(RED_LED, OUTPUT);
    pinMode(LED1, OUTPUT);
    pinMode(LED2, OUTPUT);
    pinMode(LED3, OUTPUT);
    // 初始化屏幕
    ucg.begin(UCG_FONT_MODE_TRANSPARENT);
    ucg.clearScreen();
    // 设置屏幕字体
    ucg.setFont(ucg_font_ncenR12_tr);
    // 在屏幕显示坐标名称
    ucg.setColor(0, 255, 255, 255);
    ucg.setPrintPos(10, 25);
    ucg.print("ax:");
    ucg.setPrintPos(10, 50);
    ucg.print("ay:");
    ucg.setPrintPos(10, 75);
    ucg.print("az:");
}
void loop()
{
    // 获取三周传感器数据
    acc.getXYZ(&x, &y, &z);
#if 0
    Serial.print("x = ");
    Serial.print(x);
    Serial.print(", y = ");
    Serial.print(y);
    Serial.print(", z = ");
    Serial.print(z);
#endif
    // 获取三周传感加速度器数据
    if (acc.getAcceleration(&ax, &ay, &az))
    {
        Serial.print("get data ok: ");
    }
    else
    {
        Serial.print("tiem out: ");
    }
    Serial.print("accleration of X/Y/Z: ");
    Serial.print(ax);
    Serial.print(" g, ");
    Serial.print(ay);
    Serial.print(" g, ");
    Serial.print(az);
    Serial.print(" g;    ");
    // Serial.println();
    // 依次显示三轴信息
    ucg.setColor(0, 0, 0, 0);
    ucg.drawBox(50, 0, 75, 25);
    ucg.setColor(0, 255, 255, 255);
    ucg.setPrintPos(50, 25);
    ucg.print((double)ax, 2);
    ucg.setColor(0, 0, 0, 0);
    ucg.drawBox(50, 25, 75, 25);
    ucg.setColor(0, 255, 255, 255);
    ucg.setPrintPos(50, 50);
    ucg.print((double)ay, 2);
    ucg.setColor(0, 0, 0, 0);
    ucg.drawBox(50, 50, 75, 25);
    ucg.setColor(0, 255, 255, 255);
    ucg.setPrintPos(50, 75);
    ucg.print((double)az, 2);
    // 控制开发板自身LED闪烁
    digitalWrite(RED_LED, red_led_status ? HIGH : LOW);
    red_led_status = !red_led_status;
    // 根据一定阈值检测xy方向的倾斜情况
    if (ax > 0.5 && ax < 1.0)
    {
        ix = 0;
    }
    else if (ax < -0.5 && ax > -1.0)
    {
        ix = 2;
    }
    else
    {
        ix = 1;
    }
    if (ay > 0.5 && ay < 1.0)
    {
        iy = 0;
    }
    else if (ay < -0.5 && ay > -1.0)
    {
        iy = 2;
    }
    else
    {
        iy = 1;
    }
    Serial.print("( ");
    Serial.print(ix);
    Serial.print(", ");
    Serial.print(iy);
    Serial.print(" )");
    if (ix == 1 && iy == 1)
    {
        if (rest_time_prev > 0)
        {
            rest_time_count += millis() - rest_time_prev;
        }
        rest_time_prev = millis();
        // 判断是否达到静置时间
        if (rest_time_count > REST_TIME_MAX * 1000)
        {
            rest_time_status = 1;
            // 闪烁时间增加
            if (delay_time <= 1000)
            {
                delay_time += 5;
            }
        }
        else
        {
            rest_time_status = 0;
        }
    }
    else
    {
        // 未静置,全部清零
        rest_time_prev = 0;
        rest_time_count = 0;
        rest_time_status = 0;
        delay_time = 100;
    }
    Serial.print(" rest_time_count:");
    Serial.print(rest_time_count);
    Serial.print(" rest_time_status:");
    Serial.println(rest_time_status);
    // 检查是否静置状态
    if (rest_time_status and ix == 1 and iy == 1)
    {
        // 处于静置状态
        if (led_idx == 1 || led_idx == 3 || led_idx == 5)
        {
            leds_status = 0b100;
        }
        else if (led_idx == 7 || led_idx == 9 || led_idx == 11)
        {
            leds_status = 0b010;
        }
        else if (led_idx == 13 || led_idx == 15 || led_idx == 17)
        {
            leds_status = 0b001;
        }
        else
        {
            leds_status = 0b000;
        }
        led_idx++;
        if (led_idx > 18)
        {
            led_idx = 1;
        }
    }
    else
    {
        if (ix == 0 and iy == 0)
        {
            // 斜角触发
            leds_status = 0b001;
        }
        else if (ix == 0 and iy == 1)
        {
            // X-左
            leds_status = 0b100;
        }
        else if (ix == 0 and iy == 2)
        {
            // 斜角触发
            leds_status = 0b001;
        }
        else if (ix == 1 and iy == 0)
        {
            // Y-前
            leds_status = 0b010;
        }
        else if (ix == 1 and iy == 1)
        {
            // 全亮
            leds_status = 0b111;
        }
        else if (ix == 1 and iy == 2)
        {
            // Y-后
            leds_status = 0b110;
        }
        else if (ix == 2 and iy == 0)
        {
            // 斜角触发
            leds_status = 0b001;
        }
        else if (ix == 2 and iy == 1)
        {
            // X-右
            leds_status = 0b101;
        }
        else if (ix == 2 and iy == 2)
        {
            // 斜角触发
            leds_status = 0b001;
        }
    }
    // 控制LED
    digitalWrite(LED1, leds_status & 0b100 ? !HIGH : !LOW);
    digitalWrite(LED2, leds_status & 0b010 ? !HIGH : !LOW);
    digitalWrite(LED3, leds_status & 0b001 ? !HIGH : !LOW);
    // 延时
    delay(delay_time);
    // enableWatchDog();
}
上述代码中的注释较为详细,有几个关键点进行说明:
- 各设备的初始化:
- 三轴传感器:MMA7660 acc;
- ST7735:Ucglib_ST7735_18x128x160_HWSPI ucg(/*cd=*/P2_7, /*cs=*/P2_6, /*reset=*/P3_7);
- LED:pinMode(LED1, OUTPUT);
 
如之前所说,使用的I2C、SPI都是硬件提供,不是软件实现的,所以不用设置其对应的GPIO口。
不过ST7735还有DC、CS、RST接口所以需要进行设置。
ST7735库的主要调用如下:
- 初始化:ucg.begin(UCG_FONT_MODE_TRANSPARENT);
- 清屏:ucg.clearScreen();
- 字体:ucg.setFont(ucg_font_ncenR12_tr);
- 颜色:ucg.setColor(0, 255, 255, 255); // 后三位设置RGB颜色值
- 坐标:ucg.setPrintPos(10, 50); //前者为x,后者为y
- 显示字符:ucg.print("ax:"); // 直接输出字符串
- 显示数字:ucg.print((double)ax, 2); // 输出双精度数据,第二个参数为小数位数
- 画框:ucg.drawBox(50, 0, 75, 25); //前两者为xy坐标,后两者为宽高
MMA7660库主要有3个调用:
- 初始化:acc.init();
- 获取寄存器信息:acc.getXYZ(&x, &y, &z);
- 获取加速度信息:acc.getAcceleration(&ax, &ay, &az)
实际使用主要使用后者的数据进行进一步的处理。
通过获取到的加速度信息,根据ax、ay的情况进行判断,如果达到一定的阈值,则认为处于倾斜状态,并设置对应的LED状态。
代码中,控制LED1、LED2、LED3,使用了一个 leds_status = 0b101; 的方式来设置其值,实际显示的时候,则使用如下的代码:
    // 控制LED
    digitalWrite(LED1, leds_status & 0b100 ? !HIGH : !LOW);
    digitalWrite(LED2, leds_status & 0b010 ? !HIGH : !LOW);
    digitalWrite(LED3, leds_status & 0b001 ? !HIGH : !LOW);这样可以避免在中途写较多的digitalWrite()调用。
另外需要注意的是,需要设置低电平,LED才会点亮,所以以上都是用!取反。
在上述的代码中,当处于持续静置状态的时候,delay_time += 5; 是的闪烁间隔时间边长,最长为1000ms,也就是1秒。
操作步骤:
- 在VSCode的PlatformIO面板中,新建项目,使用MSP430的arduino-blink例子:
- 安装ucglib库支持ST7735:
- 安装MMA7660支持库:
- 不过需要注意的是,该支持库中,有一个结构体属性的定义,与MSP430的寄存器定义冲突了,需要做如下的修改:
 
- 然后编写代码:
- 编写完成后,编译代码验证:
- 最后下载编译后的固件并监听串口:
- 需要注意的是,烧录完成后,到打开串口监听,可能需要间隔较长的时间,请耐心等待。
 
- 操作开发板+扩展板,观察ST7735显示屏上的信息及对应的状态:
- 倾斜开发板+扩展板,查看RGB灯的状态;
- 静置,然后查看RGB灯的状态
 


