基于 MSP-EXP430F5529LP 开发板和扩展板上的外设实现菜单功能
使用 EXP430F5529LP 开发板连接到扩展板上并读取扩展板上的按键和旋转编码器状态,根据按键和编码器的状态控制 LCD 上的菜单切换,支持进入子界面和退出子界面等功能。
标签
嵌入式系统
2023寒假在家练
topgear
更新2023-03-28
396

2023寒假一起练 - 基于 MSP-EXP430F5529LP 开发板和扩展板上的外设实现菜单功能

 

一、项目描述

MSP-EXP430F5529LP 开发板是一款针对 MSP430F5529 USB 微控制器的廉价而简单的开发套件,它为 MSP430 MCU 提供了一种简单的使用方法,具有用于编程和调试的板载仿真器,以及用于简单用户界面的按钮。本次活动,硬禾提供了 MSP-EXP430F5529LP 开发板和 IO 扩展板,其中 IO 扩展板上包含了下列外设:

  • 按键和旋转编码器 (模拟信号)
  • 双电位计控制 (数字信号)
  • RGB 三色 LED
  • 1.44 寸 128*128 LCD (SPI 接口,st7735 芯片)
  • MMA7660 三轴姿态传感器
  • 电阻加热
  • NST112 温度传感器

在本项目中,我使用了下列硬件:

  • MSP-EXP430F5529LP 开发板
  • 按键和旋转编码器 (模拟信号)
  • RGB 三色 LED
  • 1.44 寸 128*128 LCD (SPI 接口,st7735 芯片)

备注:

因为我另外一个参与的项目 ESP32-S2 也是做同样的菜单展示功能,所以文章中部分图片和描述是一样的。

二、设计思路

首先使用 MSP-EXP430F5529LP 实现控制使用 st7735 芯片的 LCD (我使用软件控制 GPIO 实现 SPI 功能),在屏幕的主界面展示主菜单,主菜单包含五个条目,模块上电后主菜单默认选中第一个条目,此时条目的背景色为黑色,条目的字体为白色。通过旋转编码器可以实现条目切换,向左旋转则条目向下切换,向右旋转则条目向上切换。此时单击旋转按钮,进入当前条目并展示该条目下的内容,五个条目的内容如下:

  • 条目一 - Blue LED,进入该条目,在 LCD 上会周期性地展示蓝色圆圈,同时蓝色 LED 也同步闪烁;
  • 条目二 - Green LED,进入该条目,在 LCD 上会周期性地展示绿色正方形,同时绿色 LED 也同步闪烁;
  • 条目三 - RED LED,进入该条目,在 LCD 上会周期性地展示红色圆圈,同时红色 LED 也同步闪烁;
  • 条目四 - Color Test,进入该条目,在 LCD 上会周期性地全屏展示红绿蓝三条竖行色块;
  • 条目五 - Triangle Test,进入该条目,在 LCD 上会显示一个白色的三角形,此时旋转编码器,三角形可以同步地按照顺时针和逆时针方向转动,如果单击旋转编码器,则三角形会连续转动360度之后静止。

在子菜单中单击 S1 按键,会退出当前子界面。

三、硬件连接

  • 旋转编码器和按键共用一个模拟输出 Aout,连接到 MSP-EXP430F5529LP 的 P6.0 (Analog In - A0);
  • LCD 与 MSP-EXP430F5529LP 的管脚连接如下:

128*128 LCD (ST7735) MSP-EXP430F5529LP
LCD_SDA P3.0
LCD_SCL  P3.2
LCD_CSn P2.6
LCD_DCx P2.7
LCD_RESn P3.7
  • RGB LED 与 MSP-EXP430F5529LP 的管脚连接如下:
RGB LED MSP-EXP430F5529LP
BLUE_LED (LED1) P2.5
GREEN_LED (LED2) P2.4
RED_LED (LED3) P1.5
  • 为了避免 IO 板上的电阻烫手,需要把下列管脚输出低电平:
SI2302 MSP-EXP430F5529LP
V_HEAT P1.4

四、软件流程

FjfYawtFpxd4gaZ3vi4HL7iFmAIq

五、开发环境的搭建

TI 为 MSP430 主推的是 Code Composer Studio 集成式开发环境 (IDE),IDE 分别提供了Windows、Linux和macOS平台的安装程序,在本次开发中我使用的是 Windows 开发环境,从https://www.ti.com.cn/tool/cn/CCSTUDIO 下载安装了  CCS12.2.0.00009_win64.zip 安装包。

具体的安装过程和使用方法见:https://software-dl.ti.com/ccs/esd/documents/users_guide_zh/index.html

工程配置方法如下,本次项目我在官方示例程序 adc12_a_ex5_repeatedSingle  基础上进行修改:

  • 选择官方提供的示例项目:
    # 在 CCS 中,选择 View->Getting Started,然后选择 Browser and Import Examples
    
    # 在 Select board or device 栏位,输入 MSP-EXP430F5529LP,下方会列出 Examples
  • 选择 adc12_a_ex5_repeatedSingle 并导入到工作区:

FqCOedhZd1OotjNQ61yUK6fhTrHC

  • 编译工程:
    在 CCS 界面,选择 Project->Build Project
  • 工程调试:
    在 CCS 界面,选择 Run->Debug

六、代码和功能展示

  1.  LCD st7735 驱动代码
    • 代码如下(这个屏幕有蓝边的问题,设置offset可以消除)
      #define CONFIG_WIDTH        128 // Width - 128px
      #define CONFIG_HEIGHT       128 // Height - 128px
      // 设置offset可以消除屏幕的蓝边
      #define CONFIG_OFFSETX      2   // to fix display issue
      #define CONFIG_OFFSETY      3   // to fix display issue
      
      #define LCD_SDA         P3DIR |= BIT0
      #define LCD_RS          P2DIR |= BIT7
      #define LCD_CS          P2DIR |= BIT6
      #define LCD_RST         P3DIR |= BIT7
      #define LCD_SCL         P3DIR |= BIT2
      #define LCD_LED         P6DIR |= BIT5
      
      #define LCD_CS_SET      P2OUT |= BIT6
      #define LCD_RS_SET      P2OUT |= BIT7
      #define LCD_SDA_SET     P3OUT |= BIT0
      #define LCD_SCL_SET     P3OUT |= BIT2
      #define LCD_RST_SET     P3OUT |= BIT7
      #define LCD_LED_SET     P6OUT |= BIT5
      
      #define LCD_CS_CLR      P2OUT &= ~BIT6
      #define LCD_RS_CLR      P2OUT &= ~BIT7
      #define LCD_SDA_CLR     P3OUT &= ~BIT0
      #define LCD_SCL_CLR     P3OUT &= ~BIT2
      #define LCD_RST_CLR     P3OUT &= ~BIT7
      #define LCD_LED_CLR     P6OUT &= ~BIT5
      
      void SPI_WriteData(uint8_t Data)
      {
          uint8_t i = 0;
      
          for (i = 8;i > 0; i--) {
              if (Data & 0x80)
                  LCD_SDA_SET;
              else
                  LCD_SDA_CLR;
      
              LCD_SCL_CLR;
              LCD_SCL_SET;
      
              Data <<= 1;
          }
      }
      
      void Lcd_WriteIndex(uint8_t Index)
      {
          LCD_CS_CLR;
          LCD_RS_CLR;
      
          SPI_WriteData(Index);
      
          LCD_CS_SET;
      }
      
      void Lcd_WriteData(uint8_t Data)
      {
          LCD_CS_CLR;
          LCD_RS_SET;
      
          SPI_WriteData(Data);
      
          LCD_CS_SET;
      }
      
      void LCD_WriteData_16Bit(uint16_t Data)
      {
          LCD_CS_CLR;
          LCD_RS_SET;
      
          SPI_WriteData(Data >> 8);
          SPI_WriteData(Data);
      
          LCD_CS_SET;
      }
      
      void Lcd_WriteReg(uint8_t Index,uint8_t Data)
      {
          Lcd_WriteIndex(Index);
          Lcd_WriteData(Data);
      }
      
      void LCD_GPIO_Init(void)
      {
          LCD_SDA;
          LCD_RS;
          LCD_CS;
          LCD_RST;
          LCD_SCL;
      }
      
      // LCD Init For 1.44Inch LCD Panel with ST7735R.
      void Lcd_Init(void)
      {
          LCD_GPIO_Init();
          Lcd_Reset(); //Reset before LCD Init.
      }
      
      void spi_master_write_byte(const uint8_t *Data, uint16_t DataLength)
      {
          if (DataLength > 0)
          {
              LCD_CS_CLR;
              SPI_WriteData(*Data);
              LCD_CS_SET;
          }
      }
      
      void spi_master_write_comm_byte(TFT_t *dev, uint8_t cmd)
      {
          static uint8_t Byte = 0;
          Byte = cmd;
          LCD_RS_CLR;
          spi_master_write_byte(&Byte, 1);
      }
      
      void spi_master_write_comm_word(TFT_t *dev, uint16_t cmd)
      {
          static uint8_t Byte[2];
          Byte[0] = (cmd >> 8) & 0xFF;
          Byte[1] = cmd & 0xFF;
          LCD_RS_CLR;
          spi_master_write_byte(&Byte[0], 1);
          spi_master_write_byte(&Byte[1], 1);
      }
      
      void spi_master_write_data_byte(TFT_t *dev, uint8_t data)
      {
          static uint8_t Byte = 0;
          Byte = data;
          LCD_RS_SET;
          spi_master_write_byte(&Byte, 1);
      }
      
      void spi_master_write_data_word(TFT_t *dev, uint16_t data)
      {
          static uint8_t Byte[2];
          Byte[0] = (data >> 8) & 0xFF;
          Byte[1] = data & 0xFF;
          LCD_RS_SET;
          spi_master_write_byte(&Byte[0], 1);
          spi_master_write_byte(&Byte[1], 1);
      }
      
      void spi_master_write_addr(TFT_t *dev, uint16_t addr1, uint16_t addr2)
      {
          static uint8_t Byte[4];
          Byte[0] = (addr1 >> 8) & 0xFF;
          Byte[1] = addr1 & 0xFF;
          Byte[2] = (addr2 >> 8) & 0xFF;
          Byte[3] = addr2 & 0xFF;
          LCD_RS_SET;
          spi_master_write_byte(&Byte[0], 1);
          spi_master_write_byte(&Byte[1], 1);
          spi_master_write_byte(&Byte[2], 1);
          spi_master_write_byte(&Byte[3], 1);
      }
      
      oid lcdInit(TFT_t *dev, uint16_t model, int width, int height, int offsetx, int offsety)
      {
          dev->_model = model;
          dev->_width = width;
          dev->_height = height;
          dev->_offsetx = offsetx;
          dev->_offsety = offsety;
          dev->_font_direction = DIRECTION0;
          dev->_font_fill = false;
          dev->_font_underline = false;
      
          // printf("Your TFT is ST7735");
          // printf("Screen width:%d", width);
          // printf("Screen height:%d", height);
          spi_master_write_comm_byte(dev, 0xC0); // Power Control 1
          spi_master_write_data_byte(dev, 0x23);
      
          spi_master_write_comm_byte(dev, 0xC1); // Power Control 2
          spi_master_write_data_byte(dev, 0x10);
      
          spi_master_write_comm_byte(dev, 0xC5); // VCOM Control 1
          spi_master_write_data_byte(dev, 0x3E);
          spi_master_write_data_byte(dev, 0x28);
      
          spi_master_write_comm_byte(dev, 0xC7); // VCOM Control 2
          spi_master_write_data_byte(dev, 0x86);
      
          spi_master_write_comm_byte(dev, 0x36); // Memory Access Control
          spi_master_write_data_byte(dev, 0xC8); // Right top start, BGR color filter panel  C8/68/A8/08
      
          spi_master_write_comm_byte(dev, 0x3A); // Pixel Format Set
          spi_master_write_data_byte(dev, 0x55); // 65K color: 16-bit/pixel
      
          spi_master_write_comm_byte(dev, 0x20); // Display Inversion OFF
      
          spi_master_write_comm_byte(dev, 0xB1); // Frame Rate Control
          spi_master_write_data_byte(dev, 0x00);
          spi_master_write_data_byte(dev, 0x18);
      
          spi_master_write_comm_byte(dev, 0xB6); // Display Function Control
          spi_master_write_data_byte(dev, 0x08);
          spi_master_write_data_byte(dev, 0xA2); // REV:1 GS:0 SS:0 SM:0
          spi_master_write_data_byte(dev, 0x27);
          spi_master_write_data_byte(dev, 0x00);
      
          spi_master_write_comm_byte(dev, 0x26); // Gamma Set
          spi_master_write_data_byte(dev, 0x01);
      
          spi_master_write_comm_byte(dev, 0xE0); // Positive Gamma Correction
          spi_master_write_data_byte(dev, 0x0F);
          spi_master_write_data_byte(dev, 0x31);
          spi_master_write_data_byte(dev, 0x2B);
          spi_master_write_data_byte(dev, 0x0C);
          spi_master_write_data_byte(dev, 0x0E);
          spi_master_write_data_byte(dev, 0x08);
          spi_master_write_data_byte(dev, 0x4E);
          spi_master_write_data_byte(dev, 0xF1);
          spi_master_write_data_byte(dev, 0x37);
          spi_master_write_data_byte(dev, 0x07);
          spi_master_write_data_byte(dev, 0x10);
          spi_master_write_data_byte(dev, 0x03);
          spi_master_write_data_byte(dev, 0x0E);
          spi_master_write_data_byte(dev, 0x09);
          spi_master_write_data_byte(dev, 0x00);
      
          spi_master_write_comm_byte(dev, 0xE1); // Negative Gamma Correction
          spi_master_write_data_byte(dev, 0x00);
          spi_master_write_data_byte(dev, 0x0E);
          spi_master_write_data_byte(dev, 0x14);
          spi_master_write_data_byte(dev, 0x03);
          spi_master_write_data_byte(dev, 0x11);
          spi_master_write_data_byte(dev, 0x07);
          spi_master_write_data_byte(dev, 0x31);
          spi_master_write_data_byte(dev, 0xC1);
          spi_master_write_data_byte(dev, 0x48);
          spi_master_write_data_byte(dev, 0x08);
          spi_master_write_data_byte(dev, 0x0F);
          spi_master_write_data_byte(dev, 0x0C);
          spi_master_write_data_byte(dev, 0x31);
          spi_master_write_data_byte(dev, 0x36);
          spi_master_write_data_byte(dev, 0x0F);
      
          spi_master_write_comm_byte(dev, 0x11); // Sleep Out
          delayMS(120);
      
          spi_master_write_comm_byte(dev, 0x29); // Display ON
      
          if (dev->_bl >= 0)
          {
              // back light is always on
          }
      }
  2. ADC 采样和按键消抖代码
    • 代码如下:
      #define ADC_DATA_LEN        20
      volatile uint16_t results[ADC_DATA_LEN];
      
      void adc_init(void)
      {
          //Enable A/D channel A0
          GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P6, GPIO_PIN0);
      
          //Initialize the ADC12_A Module
          /*
           * Base address of ADC12_A Module
           * Use internal ADC12_A bit as sample/hold signal to start conversion
           * USE MODOSC 5MHZ Digital Oscillator as clock source
           * Use default clock divider of 1
           */
          ADC12_A_init(ADC12_A_BASE,
              ADC12_A_SAMPLEHOLDSOURCE_SC,
              ADC12_A_CLOCKSOURCE_ADC12OSC,
              ADC12_A_CLOCKDIVIDER_1);
      
          ADC12_A_enable(ADC12_A_BASE);
      
          /*
           * Base address of ADC12_A Module
           * For memory buffers 0-7 sample/hold for 256 clock cycles
           * For memory buffers 8-15 sample/hold for 4 clock cycles (default)
           * Enable Multiple Sampling
           */
          ADC12_A_setupSamplingTimer(ADC12_A_BASE,
              ADC12_A_CYCLEHOLD_256_CYCLES,
              ADC12_A_CYCLEHOLD_4_CYCLES,
              ADC12_A_MULTIPLESAMPLESENABLE);
      
          //Configure Memory Buffer
          /*
           * Base address of the ADC12_A Module
           * Configure memory buffer 0
           * Map input A0 to memory buffer 0
           * Vref+ = AVcc
           * Vref- = AVss
           * Memory buffer 0 is not the end of a sequence
           */
          ADC12_A_configureMemoryParam param = {0};
          param.memoryBufferControlIndex = ADC12_A_MEMORY_0;
          param.inputSourceSelect = ADC12_A_INPUT_A0;
          param.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
          param.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
          param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
          ADC12_A_configureMemory(ADC12_A_BASE ,&param);
      
          //Enable memory buffer 0 interrupt
          ADC12_A_clearInterrupt(ADC12_A_BASE,
              ADC12IFG0);
          ADC12_A_enableInterrupt(ADC12_A_BASE,
              ADC12IE0);
      
          //Enable/Start first sampling and conversion cycle
          /*
           * Base address of ADC12_A Module
           * Start the conversion into memory buffer 0
           * Use the repeated single-channel
           */
          ADC12_A_startConversion(ADC12_A_BASE,
              ADC12_A_MEMORY_0,
              ADC12_A_REPEATED_SINGLECHANNEL);
      
          //Enable interrupts
          __bis_SR_register(GIE);
      }
      
      #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
      #pragma vector=ADC12_VECTOR
      __interrupt
      #elif defined(__GNUC__)
      __attribute__((interrupt(ADC12_VECTOR)))
      #endif
      void ADC12ISR(void)
      {
          static uint8_t index = 0;
      
          switch (__even_in_range(ADC12IV,34)){
              case  0: break;   //Vector  0:  No interrupt
              case  2: break;   //Vector  2:  ADC overflow
              case  4: break;   //Vector  4:  ADC timing overflow
              case  6:          //Vector  6:  ADC12IFG0
                  //Move results
                  results[index] = ADC12_A_getResults(ADC12_A_BASE, ADC12_A_MEMORY_0);
      
                  //Increment results index, modulo;
                  //Set Breakpoint1 here and watch results[]
                  index++;
      
                  if (index == ADC_DATA_LEN){
                      cur_raw_adc = get_raw_adc();
                      index = 0;
                  }
              case  8: break;   //Vector  8:  ADC12IFG1
              case 10: break;   //Vector 10:  ADC12IFG2
              case 12: break;   //Vector 12:  ADC12IFG3
              case 14: break;   //Vector 14:  ADC12IFG4
              case 16: break;   //Vector 16:  ADC12IFG5
              case 18: break;   //Vector 18:  ADC12IFG6
              case 20: break;   //Vector 20:  ADC12IFG7
              case 22: break;   //Vector 22:  ADC12IFG8
              case 24: break;   //Vector 24:  ADC12IFG9
              case 26: break;   //Vector 26:  ADC12IFG10
              case 28: break;   //Vector 28:  ADC12IFG11
              case 30: break;   //Vector 30:  ADC12IFG12
              case 32: break;   //Vector 32:  ADC12IFG13
              case 34: break;   //Vector 34:  ADC12IFG14
              default: break;
          }
      }
      
      volatile int cur_raw_adc = 0;
      int get_raw_adc(void)
      {
          int32_t i, j;
          uint16_t tmp;
          for (i = 0; i <= ADC_DATA_LEN / 2; i++) {
              for (j = 0; j < ADC_DATA_LEN - i - 1; j++) {
                  if (results[j + 1] < results[j]) {
                      tmp = results[j + 1];
                      results[j + 1] = results[j];
                      results[j] = tmp;
                  }
              }
          }
      
          if (ADC_DATA_LEN % 2 == 0) {
              return (((results[ADC_DATA_LEN / 2 - 1] + results[ADC_DATA_LEN / 2]) / 2));
          } else {
              return  (results[ADC_DATA_LEN / 2]);
          }
      }
      
      KEY_STATUS get_key_status(void)
      {
          KEY_STATUS key_status;
      
          if (cur_raw_adc > 1900 && cur_raw_adc < 1930) {
              polling_adc_flag = 1;
              key_status = KEY_1_PRESSED;
          } else if (cur_raw_adc > 2915 && cur_raw_adc < 2945) {
              polling_adc_flag = 0;
              key_status = KEY_2_PRESSED;
          } else if (cur_raw_adc > 3425 && cur_raw_adc < 3455) {
              key_status = KEY_C_PRESSED;
          } else if (cur_raw_adc > 3690 && cur_raw_adc < 3720) {
              key_status = KEY_C_LEFT;
          } else if (cur_raw_adc > 3815 && cur_raw_adc < 3845) {
              key_status = KEY_C_RIGHT;
          } else {
              key_status = KEY_UNKNOWN;
          }
      
          if (menu_type == 0 && polling_adc_flag == 1) {
              char adc_value[10];
              memset(adc_value, 0, sizeof(adc_value));
              snprintf(adc_value, 10, "%d", cur_raw_adc);
              lcdDrawFillRect(&dev, 64, 96, CONFIG_WIDTH, 96 + 16, RED);
              LCD_ShowString(&dev, 64, 96, (const uint8_t *)adc_value, WHITE, BLACK, 16, 1);
              //lcdDrawFillRect(&dev, 0, 96, CONFIG_WIDTH, 96 + 16, RED);
              //LCD_ShowString(&dev, 10, 96, (const uint8_t *)adc_value, WHITE, BLACK, 16, 1);
          }
      #if 0
          if (key_status != KEY_UNKNOWN) {
              if ((xTaskGetTickCount() - startTick) > pdMS_TO_TICKS(300)) {
                  startTick = xTaskGetTickCount();
      
                  return key_status;
              }
          }
      #endif
      
          //return KEY_UNKNOWN;
          return key_status;
      }

七、功能展示

  • 主菜单

Fh0qt024G0dQFPruFREKYTS75r8b

  • 三角形测试子界面

FmGuK6wK0AxXeV_9rV7IYu4sqRpN

  • 三色块测试子界面

FpVy5ZeuY1aLq8Pvn7tSLWx_sMYu

  • 红色LED测试子界面

FlsDVWGTICxl9zFVnI2W6iBuNvIp

八、未来计划

未来计划能够把IO扩展板上的测温芯片和电位计给利用起来。

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