「MSP430F149 - LCD」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

 
577行目: 577行目:
  #define LCD_D7    0x80  // ビット7: データビット7
  #define LCD_D7    0x80  // ビット7: データビット7
   
   
  // 遅延マクロ (1MHz動作時)  
  // 遅延マクロ (1[MHz]動作時)
  #define DELAY_MS(x) __delay_cycles((x) * 1000)
  #define DELAY_MS(x) __delay_cycles((x) * 1000)
  #define DELAY_US(x) __delay_cycles((x))
  #define DELAY_US(x) __delay_cycles((x))
   
   
  // グローバル変数
  // グローバル変数
  uint8_t lcd_backlight = LCD_BL;  // バックライト状態 (デフォルトON)  
  uint8_t lcd_backlight = LCD_BL;  // バックライト状態 (デフォルトON)
   
   
  // 関数プロトタイプ宣言
  // 関数プロトタイプ宣言
604行目: 604行目:
     // ウォッチドッグタイマを停止
     // ウォッチドッグタイマを停止
     WDTCTL = WDTPW | WDTHOLD;
     WDTCTL = WDTPW | WDTHOLD;
   
     // システムクロックの設定
     // システムクロックの設定
     // DCOを約1MHzに設定 (MSP430F149のキャリブレーション値を使用)  
     // DCOを約1[MHz]に設定 (MSP430F149のキャリブレーション値を使用)
     BCSCTL1 = CALBC1_1MHZ;
     BCSCTL1 = CALBC1_1MHZ;
     DCOCTL = CALDCO_1MHZ;
     DCOCTL = CALDCO_1MHZ;
   
     // SMCLKの設定 (ペリフェラル用クロック)  
     // SMCLKの設定 (ペリフェラル用クロック)
     BCSCTL2 &= ~(DIVS_3);  // SMCLK分周なし (1MHz)  
     BCSCTL2 &= ~(DIVS_3);  // SMCLK分周なし (1[MHz])
   
     // I2Cの初期化
     // I2Cの初期化
     i2c_init();
     i2c_init();
   
     // LCDの初期化
     // LCDの初期化
     lcd_init();
     lcd_init();
   
     // テキストの表示
     // テキストの表示
     lcd_clear();
     lcd_clear();
     lcd_set_cursor(0, 0);
     lcd_set_cursor(0, 0);
     lcd_print("MSP430F149");
     lcd_print("MSP430F149");
   
     lcd_set_cursor(1, 0);
     lcd_set_cursor(1, 0);
     lcd_print("I2C LCD Demo");
     lcd_print("I2C LCD Demo");
   
     lcd_set_cursor(2, 0);
     lcd_set_cursor(2, 0);
     lcd_print("USART0 Mode");
     lcd_print("USART0 Mode");
   
     lcd_set_cursor(3, 0);
     lcd_set_cursor(3, 0);
     lcd_print("20x4 Display");
     lcd_print("20x4 Display");
   
     // メインループ
     // メインループ
     while(1) {
     while(1) {
        // 低電力モードに入る (割り込みで復帰)  
      // 低電力モードに入る (割り込みで復帰)
        __bis_SR_register(LPM0_bits + GIE);
      __bis_SR_register(LPM0_bits + GIE);
        __no_operation();
      __no_operation();
     }
     }
  }
  }
648行目: 648行目:
  {
  {
     // ポート3の機能選択レジスタを設定
     // ポート3の機能選択レジスタを設定
     // P3.1: SDA (USART0 STE/SIMO)  
     // P3.1: SDA (USART0 STE/SIMO)
     // P3.3: SCL (USART0 SCLK)  
     // P3.3: SCL (USART0 SCLK)
     P3SEL |= BIT1 | BIT3;
     P3SEL |= BIT1 | BIT3;
   
     // USART0制御レジスタの設定
     // USART0制御レジスタの設定
     // SWRSTビットをセットしてUSARTをリセット状態にする
     // SWRSTビットをセットしてUSARTをリセット状態にする
     U0CTL = SWRST;
     U0CTL = SWRST;
   
     // I2Cモードの設定
     // I2Cモードの設定
     // I2C = I2Cモード有効
     // I2C = I2Cモード有効
661行目: 661行目:
     // MST = マスターモード
     // MST = マスターモード
     U0CTL |= I2C | SYNC;
     U0CTL |= I2C | SYNC;
   
     // I2C制御レジスタの設定
     // I2C制御レジスタの設定
     // I2CRM = リピートモード無効
     // I2CRM = リピートモード無効
     // I2CSSEL1 = SMCLK選択 (ビット1)  
     // I2CSSEL1 = SMCLK選択 (ビット1)
     I2CTCTL = I2CSSEL1;
     I2CTCTL = I2CSSEL1;
   
     // I2C自身アドレス設定 (マスターモードでは使用しないが設定は必要)  
     // I2C自身アドレス設定 (マスターモードでは使用しないが設定は必要)
     I2COA = 0x01;
     I2COA = 0x01;
   
     // I2Cスレーブアドレス設定
     // I2Cスレーブアドレス設定
     I2CSA = LCD_ADDR;
     I2CSA = LCD_ADDR;
   
     // I2Cクロック分周器の設定
     // I2Cクロック分周器の設定
     // SMCLKが1MHzの場合、I2Cクロックを100kHzに設定
     // SMCLKが1[MHz]の場合、I2Cクロックを100[kHz]に設定
     // 分周比 = 1MHz / 100kHz = 10
     // 分周比 = 1[MHz] / 100[kHz] = 10
     I2CPSC = 0;      // プリスケーラなし
     I2CPSC = 0;      // プリスケーラなし
     I2CSCLH = 5;    // SCL High時間
     I2CSCLH = 5;    // SCL High時間
     I2CSCLL = 5;    // SCL Low時間
     I2CSCLL = 5;    // SCL Low時間
   
     // USARTをリセット状態から解除
     // USARTをリセット状態から解除
     U0CTL &= ~SWRST;
     U0CTL &= ~SWRST;
   
     // I2C割り込みを有効化 (必要に応じて)  
     // I2C割り込みを有効化 (必要に応じて)
     // I2CIE |= TXRDYIE | RXRDYIE | NACKIE;
     // I2CIE |= TXRDYIE | RXRDYIE | NACKIE;
  }
  }
694行目: 694行目:
     // マスターモードを確認
     // マスターモードを確認
     U0CTL |= MST;
     U0CTL |= MST;
   
     // スタートコンディションを生成
     // スタートコンディションを生成
     // STTビットをセットするとハードウェアがスタートコンディションを生成
     // STTビットをセットするとハードウェアがスタートコンディションを生成
     I2CTCTL |= I2CSTT;
     I2CTCTL |= I2CSTT;
   
     // スタートコンディション生成完了を待機
     // スタートコンディション生成完了を待機
     // STTビットは自動的にクリアされる
     // STTビットは自動的にクリアされる
     while(I2CTCTL & I2CSTT);
     while(I2CTCTL & I2CSTT);
   
     // スレーブアドレスと書き込みビット (0) を送信
     // スレーブアドレスと書き込みビット (0) を送信
     I2CDRB = (LCD_ADDR << 1) | 0;  // 書き込みモード
     I2CDRB = (LCD_ADDR << 1) | 0;  // 書き込みモード
   
     // 送信準備完了を待機
     // 送信準備完了を待機
     while((I2CIFG & TXRDYIFG) == 0);
     while((I2CIFG & TXRDYIFG) == 0);
718行目: 718行目:
     // STPビットをセットするとハードウェアがストップコンディションを生成
     // STPビットをセットするとハードウェアがストップコンディションを生成
     I2CTCTL |= I2CSTP;
     I2CTCTL |= I2CSTP;
   
     // ストップコンディション生成完了を待機
     // ストップコンディション生成完了を待機
     // STPビットは自動的にクリアされる
     // STPビットは自動的にクリアされる
     while(I2CTCTL & I2CSTP);
     while(I2CTCTL & I2CSTP);
   
     // 短い遅延を入れてバスを安定させる
     // 短い遅延を入れてバスを安定させる
     DELAY_US(10);
     DELAY_US(10);
735行目: 735行目:
     // データをI2Cデータレジスタに書き込む
     // データをI2Cデータレジスタに書き込む
     I2CDRB = data;
     I2CDRB = data;
   
     // 送信完了を待機
     // 送信完了を待機
     // TXRDYIFGフラグが立つまで待つ
     // TXRDYIFGフラグが立つまで待つ
     while((I2CIFG & TXRDYIFG) == 0);
     while((I2CIFG & TXRDYIFG) == 0);
   
     // NACKを受信していないか確認
     // NACKを受信していないか確認
     if(I2CIFG & NACKIE) {
     if(I2CIFG & NACKIE) {
        // NACK受信時の処理
      // NACK受信時の処理
        I2CIFG &= ~NACKIE;  // フラグをクリア
      I2CIFG &= ~NACKIE;  // フラグをクリア
        // エラーハンドリング (必要に応じて実装)  
      // エラーハンドリング (必要に応じて実装)
      // ...略
     }
     }
  }
  }
755行目: 757行目:
  {
  {
     // 電源投入後の安定化待ち
     // 電源投入後の安定化待ち
     // LCDコントローラの電源が安定するまで最低15ms必要
     // LCDコントローラの電源が安定するまで最低15[ms]必要
     DELAY_MS(50);
     DELAY_MS(50);
   
     // 初期化シーケンスの開始
     // 初期化シーケンスの開始
     // I2C-LCDモジュールを4ビットモードで初期化
     // I2C-LCDモジュールを4ビットモードで初期化
   
     // 最初の初期化コマンド (8ビットモード)  
     // 最初の初期化コマンド (8ビットモード)
     // これはLCDを既知の状態にリセットするための手順
     // これはLCDを既知の状態にリセットするための手順
     lcd_write_nibble(0x03, 0);
     lcd_write_nibble(0x03, 0);
     DELAY_MS(5);
     DELAY_MS(5);
   
     // 2回目の初期化コマンド
     // 2回目の初期化コマンド
     lcd_write_nibble(0x03, 0);
     lcd_write_nibble(0x03, 0);
     DELAY_US(150);
     DELAY_US(150);
   
     // 3回目の初期化コマンド
     // 3回目の初期化コマンド
     lcd_write_nibble(0x03, 0);
     lcd_write_nibble(0x03, 0);
     DELAY_US(150);
     DELAY_US(150);
   
     // 4ビットモードに切り替え
     // 4ビットモードに切り替え
     lcd_write_nibble(0x02, 0);
     lcd_write_nibble(0x02, 0);
     DELAY_US(150);
     DELAY_US(150);
   
     // 以降、通常のコマンド送信関数を使用
     // 以降、通常のコマンド送信関数を使用
   
     // Function Set: 4ビットモード、2行表示、5x8ドット
     // Function Set: 4ビットモード、2行表示、5x8ドット
     lcd_cmd(0x28);
     lcd_cmd(0x28);
   
     // Display Control: ディスプレイON、カーソルOFF、点滅OFF
     // Display Control: ディスプレイON、カーソルOFF、点滅OFF
     lcd_cmd(0x0C);
     lcd_cmd(0x0C);
   
     // Entry Mode Set: カーソル自動右移動、表示シフトなし
     // Entry Mode Set: カーソル自動右移動、表示シフトなし
     lcd_cmd(0x06);
     lcd_cmd(0x06);
   
     // Clear Display: 画面クリア
     // Clear Display: 画面クリア
     lcd_clear();
     lcd_clear();
795行目: 797行目:
  /**
  /**
   * LCD 4ビット書き込み関数
   * LCD 4ビット書き込み関数
   * @param nibble 送信する4ビットデータ (下位4ビットを使用)  
   * @param nibble 送信する4ビットデータ (下位4ビットを使用)
   * @param mode 送信モード (0=コマンド、1=データ)  
   * @param mode 送信モード (0=コマンド、1=データ)
   */
   */
  void lcd_write_nibble(uint8_t nibble, uint8_t mode)
  void lcd_write_nibble(uint8_t nibble, uint8_t mode)
  {
  {
     uint8_t data;
     uint8_t data;
   
     // 上位4ビットにニブルデータを配置
     // 上位4ビットにニブルデータを配置
     data = (nibble << 4) & 0xF0;
     data = (nibble << 4) & 0xF0;
   
     // モードビット (RS) とバックライトを設定
     // モードビット (RS) とバックライトを設定
     if(mode) {
     if(mode) {
        data |= LCD_RS;  // データモード
      data |= LCD_RS;  // データモード
     }
     }
     data |= lcd_backlight;  // バックライト状態を保持
     data |= lcd_backlight;  // バックライト状態を保持
   
     // Enableパルスを生成してデータを転送
     // Enableパルスを生成してデータを転送
     i2c_start();
     i2c_start();
827行目: 829行目:
  {
  {
     uint8_t data_h, data_l;
     uint8_t data_h, data_l;
   
     // 上位4ビットと下位4ビットに分割
     // 上位4ビットと下位4ビットに分割
     data_h = (cmd & 0xF0) | lcd_backlight;
     data_h = (cmd & 0xF0) | lcd_backlight;
     data_l = ((cmd << 4) & 0xF0) | lcd_backlight;
     data_l = ((cmd << 4) & 0xF0) | lcd_backlight;
   
     i2c_start();
     i2c_start();
   
     // 上位4ビットを送信
     // 上位4ビットを送信
     i2c_write(data_h | LCD_EN);  // Enable HIGH
     i2c_write(data_h | LCD_EN);  // Enable HIGH
839行目: 841行目:
     i2c_write(data_h);          // Enable LOW
     i2c_write(data_h);          // Enable LOW
     DELAY_US(1);
     DELAY_US(1);
   
     // 下位4ビットを送信
     // 下位4ビットを送信
     i2c_write(data_l | LCD_EN);  // Enable HIGH
     i2c_write(data_l | LCD_EN);  // Enable HIGH
     DELAY_US(1);
     DELAY_US(1);
     i2c_write(data_l);          // Enable LOW
     i2c_write(data_l);          // Enable LOW
   
     i2c_stop();
     i2c_stop();
   
     // コマンド実行完了待ち
     // コマンド実行完了待ち
     DELAY_MS(2);
     DELAY_MS(2);
858行目: 860行目:
  {
  {
     uint8_t data_h, data_l;
     uint8_t data_h, data_l;
   
     // 上位4ビットと下位4ビットに分割
     // 上位4ビットと下位4ビットに分割
     // RSビットをセット (データモード)  
     // RSビットをセット (データモード)
     data_h = (data & 0xF0) | LCD_RS | lcd_backlight;
     data_h = (data & 0xF0) | LCD_RS | lcd_backlight;
     data_l = ((data << 4) & 0xF0) | LCD_RS | lcd_backlight;
     data_l = ((data << 4) & 0xF0) | LCD_RS | lcd_backlight;
   
     i2c_start();
     i2c_start();
   
     // 上位4ビットを送信
     // 上位4ビットを送信
     i2c_write(data_h | LCD_EN);  // Enable HIGH
     i2c_write(data_h | LCD_EN);  // Enable HIGH
871行目: 873行目:
     i2c_write(data_h);          // Enable LOW
     i2c_write(data_h);          // Enable LOW
     DELAY_US(1);
     DELAY_US(1);
   
     // 下位4ビットを送信
     // 下位4ビットを送信
     i2c_write(data_l | LCD_EN);  // Enable HIGH
     i2c_write(data_l | LCD_EN);  // Enable HIGH
     DELAY_US(1);
     DELAY_US(1);
     i2c_write(data_l);          // Enable LOW
     i2c_write(data_l);          // Enable LOW
   
     i2c_stop();
     i2c_stop();
   
     // データ書き込み完了待ち
     // データ書き込み完了待ち
     DELAY_US(50);
     DELAY_US(50);
885行目: 887行目:
  /**
  /**
   * 文字列表示関数
   * 文字列表示関数
   * @param str 表示する文字列 (null終端)  
   * @param str 表示する文字列 (null終端)
   */
   */
  void lcd_print(const char *str)
  void lcd_print(const char *str)
891行目: 893行目:
     // 文字列の各文字を順次送信
     // 文字列の各文字を順次送信
     while(*str) {
     while(*str) {
        lcd_data(*str++);
      lcd_data(*str++);
     }
     }
  }
  }
897行目: 899行目:
  /**
  /**
   * カーソル位置設定関数
   * カーソル位置設定関数
   * @param row 行番号 (0-3)  
   * @param row 行番号 (0-3)
   * @param col 列番号 (0-19)  
   * @param col 列番号 (0-19)
   */
   */
  void lcd_set_cursor(uint8_t row, uint8_t col)
  void lcd_set_cursor(uint8_t row, uint8_t col)
  {
  {
     uint8_t address;
     uint8_t address;
   
     // DDRAMアドレスを行番号から計算
     // DDRAMアドレスを行番号から計算
     // 20x4 LCDのDDRAMアドレスマッピング:
     // 20x4 LCDのDDRAMアドレスマッピング:
911行目: 913行目:
     // 4行目: 0x54-0x67
     // 4行目: 0x54-0x67
     switch(row) {
     switch(row) {
        case 0:
      case 0:
            address = 0x00 + col;
          address = 0x00 + col;
            break;
          break;
        case 1:
      case 1:
            address = 0x40 + col;
          address = 0x40 + col;
            break;
          break;
        case 2:
      case 2:
            address = 0x14 + col;
          address = 0x14 + col;
            break;
          break;
        case 3:
      case 3:
            address = 0x54 + col;
          address = 0x54 + col;
            break;
          break;
        default:
      default:
            address = 0x00;
          address = 0x00;
     }
     }
   
     // Set DDRAM Address コマンド (最上位ビットを1に設定)  
     // Set DDRAM Address コマンド (最上位ビットを1に設定)
     lcd_cmd(0x80 | address);
     lcd_cmd(0x80 | address);
  }
  }
938行目: 940行目:
     // Clear Display コマンド
     // Clear Display コマンド
     lcd_cmd(0x01);
     lcd_cmd(0x01);
   
     // クリアコマンドは実行に約1.52ms必要
     // クリアコマンドは実行に約1.52[ms]必要
     DELAY_MS(2);
     DELAY_MS(2);
  }
  }
949行目: 951行目:
  {
  {
     lcd_backlight = LCD_BL;
     lcd_backlight = LCD_BL;
   
     // バックライト状態を更新
     // バックライト状態を更新
     i2c_start();
     i2c_start();
962行目: 964行目:
  {
  {
     lcd_backlight = 0;
     lcd_backlight = 0;
   
     // バックライト状態を更新
     // バックライト状態を更新
     i2c_start();
     i2c_start();

2025年12月19日 (金) 00:25時点における最新版

概要

SC1604 / SC2004は、I2C通信を使用したキャラクタLCDモジュールである。
これらのLCDモジュールは、16文字×4行 (SC1604) または 20文字×4行 (SC2004) のテキストを表示することができる。

MSP430F149マイコンは、60[KB]のフラッシュメモリと2[KB]のSRAMを搭載した16ビットRISCアーキテクチャのマイコンである。
64ピンのパッケージで提供され、豊富なペリフェラル機能を持つ。
特に、2つのUSART (Universal Synchronous/Asynchronous Receiver/Transmitter) モジュールを搭載しており、これらをI2CやSPIモードで使用することができる。

SC1604 / SC2004は、シンプルな構造と使いやすいインターフェースが特徴である。
MSP430F149と接続することで、簡単に文字を表示することができる。

  • I2C通信
    SC1604 / SC2004は、I2C通信プロトコルを使用することができる。
    I2Cは、2本の信号線 (SCLとSDA) を使用して、マスターデバイスとスレーブデバイス間でデータを転送する同期式のシリアル通信プロトコルである。
    MSP430F149では、USARTモジュールをI2Cモードで動作させることにより、LCDモジュールを制御することができる。

  • アドレッシング
    各LCDモジュールには、固有のI2Cアドレスが割り当てられている。
    一般的に、SC1604 / SC2004のI2Cアドレスは 0x27 または 0x3f である。
    マスターデバイスは、このアドレスを使用してLCDモジュールを特定して通信を行う。

  • コマンドとデータ
    LCDモジュールは、コマンドとデータの2種類の情報を受信する。
    コマンドは、LCDの動作を制御するための指示 (例 : 画面のクリア、カーソル位置の設定等) である。
    データは、LCDに表示される実際の文字情報である。

    マスターデバイスは、コマンドとデータを適切に送信することにより、LCDの表示を制御する。

  • 初期化シーケンス
    LCDモジュールを使用する前に、一連の初期化コマンドを送信する必要がある。
    これらのコマンドは、LCDの動作モード、表示設定、文字フォント等を設定する。
    初期化シーケンスは、LCDモジュールのデータシートに記載されている。

  • 文字の表示
    初期化後、マスターデバイスは文字データをLCDモジュールに送信することにより、文字を表示することができる。
    各文字は、ASCIIコード、または、カスタム文字コードで表される。
    LCDモジュールは、受信した文字データを表示用のRAMに格納して、画面に表示する。

  • カーソル制御
    LCDモジュールには、カーソル位置を制御するためのコマンドがある。
    これにより、文字の表示位置を指定したり、カーソルを移動させたりすることができる。

  • バックライト制御
    多くのSC1604 / SC2004モジュールは、バックライト機能を備えている。
    バックライトの制御は、別の信号線や専用のコマンドを使用して行う。



SC1602 / SC2004の文字コード

HD44780コントローラを搭載したLCDモジュールは、以下に示す文字コードセットをサポートしている。
また、内蔵キャラクタジェネレータ (CGROM) の他にも、CGRAMによりユーザが任意のパターンを定義することができる。

  • 基本ラテン文字 (英数字および一般的な記号)
    ASCIIコードの0x20〜0x7F (スペース、アルファベット、数字、一般的な記号)
  • 日本語カタカナ
    ASCII拡張セットの0xA0〜0xDF (半角カタカナ)
  • ユーザ定義文字
    8つのカスタムキャラクタ (CGRAM) を作成可能
    アドレスは0x00〜0x07



SC2004 / GPIOピンで直接制御

MSP430F149とSC2004の接続

MSP430F149は64ピンのパッケージで提供されるため、十分な数のGPIOピンが利用可能である。

以下に示すように、MSP430F149とSC2004を接続する。

MSP430F149のポート1 (P1) を使用した接続例を示す。ポート1は8ビット幅のデジタルI/Oポートであり、各ピンは個別に入力または出力として構成できる。

MSP430F149とSC2004の接続
MSP430F149 SC2004 備考
P1.0 RS (Register Select)
P1.1 E (Enable)
P1.4 DB4 データピン
P1.5 DB5 データピン
P1.6 DB6 データピン
P1.7 DB7 データピン
GND Vss
GND RW
GND K (Backlight Cathode) バックライト陰極
+5[V] Vdd
+5[V] A (Backlight Anode) バックライト陽極
GND Vo (Contrast Adjust) 10[kΩ]可変抵抗経由


MSP430F149は3.3[V]動作のマイコンであるが、5V tolerant (5[V]耐性) のピンを持つ。
ただし、LCDモジュールが5[V]動作の場合は、レベルシフタの使用を検討する必要がある。

一部のLCDモジュールは3.3[V]動作に対応しているため、使用するモジュールの仕様を確認すること。

  • LCDのRSピンを 0 にする場合 (RS = 0)
    コマンドレジスタが選択されて、送信されるデータはコマンドとして解釈される。
  • LCDのRSピンを 1 にする場合 (RS = 1)
    データレジスタが選択されて、送信されるデータは表示データとして解釈される


以下の例では、SC2004を制御する関数を実装して、SC2004の初期化 および 表示する文字列をlcd_puts関数で送信している。
4ビットモード (8ビットモードと比較してデータの転送速度が遅くなる) でLCDと通信しており、データバスとして使用しているのはDB4〜DB7 (P1.4〜P1.7) の4本のみである。

  • void lcd_init(void)
    SC2004の初期化
  • void lcd_cmd(uint8_t cmd)
    コマンドの送信
  • void lcd_data(uint8_t data)
    文字データの送信
  • void lcd_puts(const char *s)
    文字列の表示


 #include <msp430x14x.h>
 #include <stdint.h>
 
 // ピン定義:MSP430F149のポート1を使用
 #define RS_PIN  BIT0    // P1.0 - Register Select
 #define E_PIN   BIT1    // P1.1 - Enable
 #define DB4_PIN BIT4    // P1.4 - Data bit 4
 #define DB5_PIN BIT5    // P1.5 - Data bit 5
 #define DB6_PIN BIT6    // P1.6 - Data bit 6
 #define DB7_PIN BIT7    // P1.7 - Data bit 7
 
 // データバス全体のマスク (DB4-DB7)
 #define DATA_MASK (DB4_PIN | DB5_PIN | DB6_PIN | DB7_PIN)
 
 // 遅延マクロ (1[MHz]動作時)
 #define DELAY_MS(x) __delay_cycles((x) * 1000)
 #define DELAY_US(x) __delay_cycles((x))
 
 // 関数プロトタイプ
 void lcd_cmd(uint8_t cmd);
 void lcd_data(uint8_t data);
 void lcd_init(void);
 void lcd_puts(const char *s);
 void lcd_set_cursor(uint8_t row, uint8_t col);
 
 int main(void)
 {
    // ウォッチドッグタイマを停止
    // WDTCTLレジスタはパスワード保護されているため、上位バイトに0x5Aを書き込む必要がある
    WDTCTL = WDTPW | WDTHOLD;
 
    // システムクロックの設定 (DCO = 約1MHz)
    // MSP430F149のデフォルトDCO周波数を使用
    BCSCTL1 = CALBC1_1MHZ;    // DCOを1[MHz]に設定
    DCOCTL = CALDCO_1MHZ;
 
    // LCDの初期化
    lcd_init();
 
    // 1行目に文字列を表示
    lcd_set_cursor(0, 0);
    lcd_puts("MSP430F149");
 
    // 2行目に文字列を表示
    lcd_set_cursor(1, 0);
    lcd_puts("LCD Controller");
 
    // メインループ
    while(1) {
       // 何もしない (低電力モードに入ることも可能)
       __no_operation();
    }
 }
 
 /**
  * LCD初期化関数
  * HD44780互換コントローラの初期化シーケンスを実行
  */
 void lcd_init(void)
 {
    // P1ポートの方向レジスタを設定 (出力に設定) 
    P1DIR |= RS_PIN | E_PIN | DATA_MASK;
 
    // すべての制御ピンとデータピンをLOWに初期化
    P1OUT &= ~(RS_PIN | E_PIN | DATA_MASK);
 
    // LCDの電源が安定するまで40[ms]以上待機
    // これはHD44780データシートで推奨されている待機時間
    DELAY_MS(40);
 
    // 特殊な初期化シーケンス
    // HD44780の仕様により、最初の数回のコマンドは特定のタイミングで送信する必要がある
    // この時点ではBusy Flag (BF) をチェックしてはいけない
 
    // 最初の初期化コマンド (8ビットモード設定)
    P1OUT = (P1OUT & ~DATA_MASK) | (0x30 >> 0);  // 0x03を4ビットで送信
    P1OUT |= E_PIN;   // Enableピンを立ち上げ
    DELAY_US(1);      // 短い遅延
    P1OUT &= ~E_PIN;  // Enableピンを下げ
    DELAY_MS(5);      // 4.1[ms]以上待機
 
    // 2回目の初期化コマンド
    P1OUT = (P1OUT & ~DATA_MASK) | (0x30 >> 0);
    P1OUT |= E_PIN;
    DELAY_US(1);
    P1OUT &= ~E_PIN;
    DELAY_US(100);    // 100[us]以上待機
 
    // 3回目の初期化コマンド
    P1OUT = (P1OUT & ~DATA_MASK) | (0x30 >> 0);
    P1OUT |= E_PIN;
    DELAY_US(1);
    P1OUT &= ~E_PIN;
    DELAY_US(100);
 
    // 4ビットモードに設定
    P1OUT = (P1OUT & ~DATA_MASK) | (0x20 >> 0);
    P1OUT |= E_PIN;
    DELAY_US(1);
    P1OUT &= ~E_PIN;
    DELAY_US(100);
 
    // 以降は通常のコマンド送信を使用
 
    // Function Set: 4ビットモード、2行表示、5x8ドットフォント
    // コマンド: 0x28 = 0b00101000
    lcd_cmd(0x28);
 
    // Display Control: ディスプレイON、カーソルOFF、点滅OFF
    // コマンド: 0x0C = 0b00001100
    lcd_cmd(0x0C);
 
    // Entry Mode Set: カーソル自動インクリメント、表示シフトなし
    // コマンド: 0x06 = 0b00000110
    lcd_cmd(0x06);
 
    // Clear Display: 画面クリアとカーソルをホームポジションに移動
    // コマンド: 0x01 = 0b00000001
    lcd_cmd(0x01);
 
    // クリアコマンドは実行に時間が掛かるため、追加の待機時間が必要
    DELAY_MS(2);
 }
 
 /**
  * LCDコマンド送信関数
  * @param cmd 送信するコマンドバイト
  */
 void lcd_cmd(uint8_t cmd)
 {
    // RSピンをLOWに設定 (コマンドモード)
    P1OUT &= ~RS_PIN;
 
    // 上位4ビットを送信
    // cmdの上位4ビットを取り出して、P1.4〜P1.7に配置
    P1OUT = (P1OUT & ~DATA_MASK) | ((cmd & 0xF0) >> 0);
 
    // Enableパルスを生成 (立ち上がりエッジでLCDがデータを読み取る)
    P1OUT |= E_PIN;
    DELAY_US(1);      // Enableパルス幅 (最小450[ns])
    P1OUT &= ~E_PIN;
    DELAY_US(1);      // セットアップ時間
 
    // 下位4ビットを送信
    // cmdの下位4ビットを取り出して、P1.4〜P1.7に配置
    P1OUT = (P1OUT & ~DATA_MASK) | ((cmd & 0x0F) << 4);
 
    // Enableパルスを生成
    P1OUT |= E_PIN;
    DELAY_US(1);
    P1OUT &= ~E_PIN;
 
    // コマンド実行完了まで待機
    // 大半のコマンドは37[us]以内に完了するが、安全のため2[ms]待機
    DELAY_MS(2);
 }
 
 /**
  * LCDデータ送信関数
  * @param data 送信する文字データ (ASCII)
  */
 void lcd_data(uint8_t data)
 {
    // RSピンをHIGHに設定 (データモード)
    P1OUT |= RS_PIN;
 
    // 上位4ビットを送信
    P1OUT = (P1OUT & ~DATA_MASK) | ((data & 0xF0) >> 0);
 
    // Enableパルスを生成
    P1OUT |= E_PIN;
    DELAY_US(1);
    P1OUT &= ~E_PIN;
    DELAY_US(1);
 
    // 下位4ビットを送信
    P1OUT = (P1OUT & ~DATA_MASK) | ((data & 0x0F) << 4);
 
    // Enableパルスを生成
    P1OUT |= E_PIN;
    DELAY_US(1);
    P1OUT &= ~E_PIN;
 
    // データ書き込み完了まで待機
    DELAY_MS(2);
 }
 
 /**
  * 文字列表示関数
  * @param s 表示する文字列 (null終端)
  */
 void lcd_puts(const char *s)
 {
    // 文字列の各文字を順次送信
    while(*s) {
       lcd_data(*s++);
    }
 }
 
 /**
  * カーソル位置設定関数
  * @param row 行番号 (0-3)
  * @param col 列番号 (0-19)
  */
 void lcd_set_cursor(uint8_t row, uint8_t col)
 {
    uint8_t address;
 
    // DDRAMアドレスを行番号から計算
    // HD44780の2行LCDのDDRAMアドレスマッピング:
    // 1行目: 0x00-0x27
    // 2行目: 0x40-0x67
    // 4行LCDの場合:
    // 1行目: 0x00-0x13
    // 2行目: 0x40-0x53
    // 3行目: 0x14-0x27
    // 4行目: 0x54-0x67
    switch(row) {
       case 0:
          address = 0x00 + col;
          break;
       case 1:
          address = 0x40 + col;
          break;
       case 2:
          address = 0x14 + col;
          break;
       case 3:
          address = 0x54 + col;
          break;
       default:
          address = 0x00;
    }
 
    // Set DDRAM Address コマンド (最上位ビットを1に設定)
    lcd_cmd(0x80 | address);
 }



SC2004 / I2C通信

MSP430F149とI2C

MSP430F149は2つのUSARTモジュール (USART0とUSART1) を搭載している。
これらのUSARTモジュールは、非同期シリアル通信 (UART)、同期シリアル通信 (SPI)、I2Cモードで動作することができる。

MSP430F149において、I2C通信で一般的に使用されるピンは以下の通りである。
I2Cモードでは、USARTモジュールのSPIピンがI2C通信に使用される。

USART0をI2Cモードで使用する場合のピン配置:

  • SCL (Serial Clock Line)
    USART0のSCLK機能に対応
    P3.3 (USART0のUCLKピン)
  • SDA (Serial Data Line)
    USART0のSIMO/SOMI機能に対応
    P3.1 (USART0のSIMOピン) または P3.2 (USART0のSOMIピン)


USART1をI2Cモードで使用する場合のピン配置:

  • SCL (Serial Clock Line)
    P5.3 (USART1のUCLKピン)
  • SDA (Serial Data Line)
    P5.1 (USART1のSIMOピン) または P5.2 (USART1のSOMIピン)


MSP430F149には、Port1からPort6まで合計48本のデジタルI/Oピンが用意されている。
I2C通信に使用するピンを選択する場合は、他の機能との競合を避ける必要がある。

使用を避けるべきピン、または注意が必要なピン:

  • P1.0、P1.1
    これらのピンは外部クリスタル発振器 (XT2IN、XT2OUT) に使用される可能性がある。
    クリスタルを使用する場合は、I2C通信には使用できない。
  • P2.0、P2.1
    これらのピンも外部クリスタル発振器 (XIN、XOUT) に接続される可能性がある。
  • P5.6、P5.7
    これらのピンはJTAGデバッグインターフェースに使用される。
    デバッグ機能を使用する場合は、これらのピンをI2C通信に使用することはできない。


※注意
I2C通信を使用する場合は、SCLとSDAラインにプルアップ抵抗を接続する必要がある。
プルアップ抵抗の標準的な値は4.7[kΩ]〜10[kΩ]である。
多くのI2Cモジュールには内蔵プルアップ抵抗が搭載されているが、バスの長さやデバイス数に応じて外部プルアップ抵抗の追加が必要になる場合がある。

ピンの割り当ては、プロジェクトの要件やPCBレイアウトに応じて柔軟に変更できるが、使用するピンが他の機能と競合しないように十分注意すること。

MSP430F149のI2C設定

MSP430F149でI2C通信を正常に動作させるためには、以下の設定を行う必要がある。

  • クロック設定
    MSP430F149では、複数のクロックソースが利用可能である。
    ACLK (Auxiliary Clock) は、32.768[kHz]の低速クロックで、低電力動作に使用される。
  • " MCLK (Master Clock) は、CPUのメインクロックで、1[MHz]〜8[MHz]で動作する。
    SMCLK (Sub-Main Clock) は、ペリフェラル用のクロックで、I2C通信ではこのSMCLKを使用することが一般的である。

  • USARTモジュールをI2Cモードで使用
    1. まず、U0CTLレジスタ (USART0の場合) のSWRSTビットをセットして、モジュールをリセット状態にする。
    2. 次に、I2Cモードを有効にして、マスターまたはスレーブモードを選択する。
      MSP430F149がI2Cマスターとして動作する場合は、I2CENビットとMSTビットをセットする。

  • クロック分周の設定
    U0BRレジスタを使用してI2Cのクロック周波数を設定する。
    例えば、SMCLKが1[MHz]で動作している場合、100[kHz]のI2C通信を実現するには、分周比を10に設定する必要がある。

  • I2Cアドレッシング
    MSP430F149は7ビットアドレスと10ビットアドレスの両方をサポートしているが、SC2004のような一般的なI2Cデバイスは7ビットアドレスを使用する。
    スレーブデバイスのアドレスは、I2CSAレジスタに設定する。

  • 割り込みの使用
    I2CIEレジスタで適切な割り込みを有効にして、割り込みサービスルーチン (ISR) を使用する必要がある。
    I2C通信では、送信完了割り込み、受信完了割り込み、NACK受信割り込み等が利用できる。

  • エラーハンドリング
    I2C通信中にエラーが発生した場合、I2CSTATレジスタのエラーフラグを確認して、適切にエラー処理を行う。
    アービトレーション損失 (Arbitration Lost)、NACK受信 (No Acknowledge)、バスビジー (Bus Busy)等のエラー状態を検出できる。

  • 低電力モードとの連携
    MSP430F149は複数の低電力モード (LPM0〜LPM4) をサポートしている。
    I2C通信を使用する場合、通信中はアクティブモードまたはLPM0で動作して、通信が完了したら低電力モードに移行することでバッテリ駆動時間を延ばすことができる。


SC2004の設定

SC2004は、I2Cスレーブデバイスとして動作する。I2C-LCD変換モジュール (一般的に、PCF8574またはPCF8574Aチップを使用) を介してHD44780互換のLCDコントローラと通信を行う。

SC2004の初期化では、まず、LCDコントローラをリセットし、動作モードを設定する必要がある。

  • ファンクションセット (Function Set) コマンド
    データ長 (8ビットまたは4ビット)、表示行数 (1行または2行)、文字フォント (5x8ドットまたは5x10ドット) を設定する。

  • ディスプレイコントロール (Display Control) コマンド
    ディスプレイのON/OFF、カーソルの表示/非表示、カーソルの点滅を制御する。

  • エントリーモード (Entry Mode Set) コマンド
    文字書き込み時のカーソルの移動方向とディスプレイ全体のシフトを設定する。

  • クリアディスプレイ (Clear Display) コマンド
    表示されている全ての文字を消去して、カーソルをホームポジション (0,0) に戻す。
    このコマンドは実行に約1.52[ms]掛かるため、コマンド送信後に適切な待機時間を設ける必要がある。


I2Cを介したデータ送信

MSP430F149からSC2004にデータを送信する手順を以下に示す。

  1. まず、I2C通信のスタートコンディションを生成する。
    スタートコンディションは、SCLがHIGHの状態でSDAをHIGHからLOWに遷移させることで生成される。
    MSP430F149では、制御レジスタの適切なビットをセットすることにより、ハードウェアが自動的にスタートコンディションを生成する。

  2. 次に、スレーブアドレスと読み書きビットを送信する。
    7ビットアドレスの場合、アドレスの後に読み書きビット (Write=0、Read=1) を付加して8ビットのデータとして送信する。
    スレーブデバイスは、自分のアドレスと一致した場合にACK (Acknowledge) を返す。

  3. ACKを受信したら、コマンドまたはデータバイトを送信する。
    I2C-LCD変換モジュールでは、送信する各バイトの上位4ビットがLCDコントロール信号とバックライト制御に使用され、下位4ビットがLCDデータに使用される。
    具体的には、ビット0〜3がD4〜D7データライン、ビット4がE (Enable)、ビット5がRW (Read/Write)、ビット6がRS (Register Select)、ビット7がバックライト制御に対応する。

  4. 全てのデータ送信が完了した後、ストップコンディションを生成する。
    ストップコンディションは、SCLがHIGHの状態でSDAをLOWからHIGHに遷移させることで生成される。


注意点

電源電圧の互換性

SC2004モジュールの多くは5[V]動作を前提としている。
MSP430F149は3.3[V]動作のマイコンであるため、電圧レベルの不一致に注意が必要である。

I2C通信では、プルアップ抵抗が3.3[V]側に接続されている場合、5[V]デバイスのLOWレベル出力が3.3[V]システムで正しく認識されるかを確認する必要がある。
必要に応じて、双方向レベルシフタ (例 : TXS0108E) の使用を検討すること。

プルアップ抵抗の選択

プルアップ抵抗は、I2C通信の信頼性に大きく影響する。
プルアップ抵抗の値が小さすぎると電流消費が増加し、大きすぎると立ち上がり時間が遅くなる。

標準的な100[kHz]通信では、4.7[kΩ]〜10[kΩ]が推奨される。
高速モード (400[kHz]) を使用する場合は、2.2[kΩ]〜4.7[kΩ]程度の小さい値が適している。

タイミング要件

HD44780互換コントローラには厳密なタイミング要件がある。
各コマンドの実行には最小実行時間があり、この時間内に次のコマンドを送信してはならない。

クリアディスプレイコマンドやホームコマンドは特に実行時間が長く、約1.52[ms]掛かる。

その他のコマンドは、通常37[μs]程度で実行される。

初期化シーケンス

電源投入後、LCDコントローラが安定するまで最低15[ms]待機する必要がある。 初期化コマンドは特定の順序で送信し、各コマンド間に適切な待機時間を設ける必要がある。

通信速度の設定では、I2C標準モード (100[kHz]) または 高速モード (400[kHz]) を選択できる。
SC2004の応答速度に合わせて適切なクロック周波数を設定すること。

クロック周波数が高すぎると、LCDコントローラが正しく応答しない可能性がある。

エラーハンドリング

I2C通信では、NACKの受信、アービトレーション損失、タイムアウト等のエラーが発生する可能性がある。
これらのエラーを適切に検出し、必要に応じて通信の再試行や初期化処理を行うこと。

スタートコンディション / ストップコンディション

I2C通信プロトコルにおいて、スタートコンディションとストップコンディションは通信の開始と終了を明確に定義する重要な信号である。

スタートコンディション

スタートコンディションは、マスターデバイスがバスの制御を開始することを示す信号である。
この信号が生成されると、バス上の全てのスレーブデバイスがアドレス受信モードに入る。

物理的には、SCLがHIGH状態を維持している間にSDAがHIGHからLOWに遷移することでスタートコンディションが生成される。
この遷移は、データ転送中の通常のLOW状態とは明確に区別される。

MSP430F149では、I2C制御レジスタのSTTビット (Start Bit) をセットすることにより、ハードウェアが自動的にスタートコンディションを生成する。

スタートコンディションの後、マスターはスレーブアドレスバイトを送信する。
このバイトは7ビットのスレーブアドレスと1ビットの読み書き方向ビット (R/W) で構成される。

スレーブデバイスは、送信されたアドレスと自分のアドレスを比較して、一致した場合はACK信号を返して通信を継続する。

リピーテッドスタート (Repeated Start) は、ストップコンディションを生成せずに新しいスタートコンディションを生成する特殊な操作である。
これは、複数のスレーブデバイスと連続して通信する場合や、書き込み操作の後に読み込み操作を行う場合に使用される。
リピーテッドスタートを使用することで、バスの制御を失うことなく通信方向を変更できる。

ストップコンディション

ストップコンディションは、データ転送の完了とバスの解放を示す信号である。
物理的には、SCLがHIGH状態の間にSDAがLOWからHIGHに遷移することでストップコンディションが生成される。

MSP430F149では、制御レジスタのSTPビット (Stop Bit) をセットすることで、ハードウェアが自動的にストップコンディションを生成する。

ストップコンディションが生成されると、バスはアイドル状態に戻る。
アイドル状態では、SCLとSDAの両方がプルアップ抵抗によってHIGH状態に保持される。
この状態で、他のマスターデバイスが新しい通信を開始できる。

マルチマスター環境では、複数のマスターデバイスが同時にスタートコンディションを生成しようとする可能性がある。
この場合、アービトレーション (調停) メカニズムによって、どのマスターがバスの制御を獲得するかが決定される。
アービトレーションに失敗したマスターは、バスがアイドル状態に戻るまで待機する必要がある。

MSP430F149のI2Cハードウェアモジュールは、これらのコンディション生成を自動的に処理する。
プログラマは、適切な制御ビットをセットするだけで、タイミングや信号の生成をハードウェアに任せることができる。
これにより、ソフトウェアの複雑さが軽減され、タイミングエラーのリスクが減少する。

サンプルコード

以下のサンプルコードは、MSP430F149のUSART0モジュールをI2Cモードで使用してSC2004を制御する完全な実装例である。

プログラムの主要な構成要素を説明する。まず、I2Cの初期化関数 (i2c_init) では、ピンの機能選択、USARTモジュールのI2Cモード設定、クロック周波数の設定を行う。次に、LCDの初期化関数 (lcd_init) では、HD44780互換コントローラの初期化シーケンスを実行する。文字列の表示関数 (lcd_print) では、lcd_data関数を使用して各文字をLCDに送信する。

I2C通信では、i2c_start関数でスタートコンディション、i2c_stop関数でストップコンディションを発生させ、i2c_write関数でデータを送信している。LCDへのコマンド送信はlcd_cmd関数、データ送信はlcd_data関数を使用している。

このコードは、I2C-LCD変換モジュール (PCF8574ベース) を使用していることを前提としている。このモジュールは、8ビットのI2Cデータを4ビットのパラレルLCDインターフェースに変換する。

 #include <msp430x14x.h>
 #include <stdint.h>
 
 // I2Cアドレスの定義
 // 一般的なI2C-LCDモジュールのアドレス
 #define LCD_ADDR 0x27
 // 別のアドレスを使用する場合
 // #define LCD_ADDR 0x3F
 
 // I2C-LCD変換モジュール (PCF8574) のビット定義
 #define LCD_RS    0x01  // ビット0: Register Select (0=コマンド、1=データ)
 #define LCD_RW    0x02  // ビット1: Read/Write (0=書き込み、1=読み込み)
 #define LCD_EN    0x04  // ビット2: Enable信号
 #define LCD_BL    0x08  // ビット3: バックライト制御 (1=ON、0=OFF)
 #define LCD_D4    0x10  // ビット4: データビット4
 #define LCD_D5    0x20  // ビット5: データビット5
 #define LCD_D6    0x40  // ビット6: データビット6
 #define LCD_D7    0x80  // ビット7: データビット7
 
 // 遅延マクロ (1[MHz]動作時)
 #define DELAY_MS(x) __delay_cycles((x) * 1000)
 #define DELAY_US(x) __delay_cycles((x))
 
 // グローバル変数
 uint8_t lcd_backlight = LCD_BL;  // バックライト状態 (デフォルトON)
 
 // 関数プロトタイプ宣言
 void i2c_init(void);
 void i2c_start(void);
 void i2c_stop(void);
 void i2c_write(uint8_t data);
 void i2c_write_addr(uint8_t addr);
 void lcd_init(void);
 void lcd_cmd(uint8_t cmd);
 void lcd_data(uint8_t data);
 void lcd_print(const char *str);
 void lcd_set_cursor(uint8_t row, uint8_t col);
 void lcd_clear(void);
 void lcd_backlight_on(void);
 void lcd_backlight_off(void);
 void lcd_write_nibble(uint8_t nibble, uint8_t mode);
 
 int main(void)
 {
    // ウォッチドッグタイマを停止
    WDTCTL = WDTPW | WDTHOLD;
 
    // システムクロックの設定
    // DCOを約1[MHz]に設定 (MSP430F149のキャリブレーション値を使用)
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
 
    // SMCLKの設定 (ペリフェラル用クロック)
    BCSCTL2 &= ~(DIVS_3);  // SMCLK分周なし (1[MHz])
 
    // I2Cの初期化
    i2c_init();
 
    // LCDの初期化
    lcd_init();
 
    // テキストの表示
    lcd_clear();
    lcd_set_cursor(0, 0);
    lcd_print("MSP430F149");
 
    lcd_set_cursor(1, 0);
    lcd_print("I2C LCD Demo");
 
    lcd_set_cursor(2, 0);
    lcd_print("USART0 Mode");
 
    lcd_set_cursor(3, 0);
    lcd_print("20x4 Display");
 
    // メインループ
    while(1) {
       // 低電力モードに入る (割り込みで復帰)
       __bis_SR_register(LPM0_bits + GIE);
       __no_operation();
    }
 }
 
 /**
  * I2C初期化関数
  * USART0をI2Cモードで設定
  */
 void i2c_init(void)
 {
    // ポート3の機能選択レジスタを設定
    // P3.1: SDA (USART0 STE/SIMO)
    // P3.3: SCL (USART0 SCLK)
    P3SEL |= BIT1 | BIT3;
 
    // USART0制御レジスタの設定
    // SWRSTビットをセットしてUSARTをリセット状態にする
    U0CTL = SWRST;
 
    // I2Cモードの設定
    // I2C = I2Cモード有効
    // SYNC = 同期モード
    // MST = マスターモード
    U0CTL |= I2C | SYNC;
 
    // I2C制御レジスタの設定
    // I2CRM = リピートモード無効
    // I2CSSEL1 = SMCLK選択 (ビット1)
    I2CTCTL = I2CSSEL1;
 
    // I2C自身アドレス設定 (マスターモードでは使用しないが設定は必要)
    I2COA = 0x01;
 
    // I2Cスレーブアドレス設定
    I2CSA = LCD_ADDR;
 
    // I2Cクロック分周器の設定
    // SMCLKが1[MHz]の場合、I2Cクロックを100[kHz]に設定
    // 分周比 = 1[MHz] / 100[kHz] = 10
    I2CPSC = 0;      // プリスケーラなし
    I2CSCLH = 5;     // SCL High時間
    I2CSCLL = 5;     // SCL Low時間
 
    // USARTをリセット状態から解除
    U0CTL &= ~SWRST;
 
    // I2C割り込みを有効化 (必要に応じて)
    // I2CIE |= TXRDYIE | RXRDYIE | NACKIE;
 }
 
 /**
  * I2Cスタートコンディション生成関数
  */
 void i2c_start(void)
 {
    // マスターモードを確認
    U0CTL |= MST;
 
    // スタートコンディションを生成
    // STTビットをセットするとハードウェアがスタートコンディションを生成
    I2CTCTL |= I2CSTT;
 
    // スタートコンディション生成完了を待機
    // STTビットは自動的にクリアされる
    while(I2CTCTL & I2CSTT);
 
    // スレーブアドレスと書き込みビット (0) を送信
    I2CDRB = (LCD_ADDR << 1) | 0;  // 書き込みモード
 
    // 送信準備完了を待機
    while((I2CIFG & TXRDYIFG) == 0);
 }
 
 /**
  * I2Cストップコンディション生成関数
  */
 void i2c_stop(void)
 {
    // ストップコンディションを生成
    // STPビットをセットするとハードウェアがストップコンディションを生成
    I2CTCTL |= I2CSTP;
 
    // ストップコンディション生成完了を待機
    // STPビットは自動的にクリアされる
    while(I2CTCTL & I2CSTP);
 
    // 短い遅延を入れてバスを安定させる
    DELAY_US(10);
 }
 
 /**
  * I2Cデータ書き込み関数
  * @param data 送信するデータバイト
  */
 void i2c_write(uint8_t data)
 {
    // データをI2Cデータレジスタに書き込む
    I2CDRB = data;
 
    // 送信完了を待機
    // TXRDYIFGフラグが立つまで待つ
    while((I2CIFG & TXRDYIFG) == 0);
 
    // NACKを受信していないか確認
    if(I2CIFG & NACKIE) {
       // NACK受信時の処理
       I2CIFG &= ~NACKIE;  // フラグをクリア
 
       // エラーハンドリング (必要に応じて実装)
       // ...略
    }
 }
 
 /**
  * LCD初期化関数
  * HD44780互換コントローラの初期化シーケンスを実行
  */
 void lcd_init(void)
 {
    // 電源投入後の安定化待ち
    // LCDコントローラの電源が安定するまで最低15[ms]必要
    DELAY_MS(50);
 
    // 初期化シーケンスの開始
    // I2C-LCDモジュールを4ビットモードで初期化
 
    // 最初の初期化コマンド (8ビットモード)
    // これはLCDを既知の状態にリセットするための手順
    lcd_write_nibble(0x03, 0);
    DELAY_MS(5);
 
    // 2回目の初期化コマンド
    lcd_write_nibble(0x03, 0);
    DELAY_US(150);
 
    // 3回目の初期化コマンド
    lcd_write_nibble(0x03, 0);
    DELAY_US(150);
 
    // 4ビットモードに切り替え
    lcd_write_nibble(0x02, 0);
    DELAY_US(150);
 
    // 以降、通常のコマンド送信関数を使用
 
    // Function Set: 4ビットモード、2行表示、5x8ドット
    lcd_cmd(0x28);
 
    // Display Control: ディスプレイON、カーソルOFF、点滅OFF
    lcd_cmd(0x0C);
 
    // Entry Mode Set: カーソル自動右移動、表示シフトなし
    lcd_cmd(0x06);
 
    // Clear Display: 画面クリア
    lcd_clear();
 }
 
 /**
  * LCD 4ビット書き込み関数
  * @param nibble 送信する4ビットデータ (下位4ビットを使用)
  * @param mode 送信モード (0=コマンド、1=データ)
  */
 void lcd_write_nibble(uint8_t nibble, uint8_t mode)
 {
    uint8_t data;
 
    // 上位4ビットにニブルデータを配置
    data = (nibble << 4) & 0xF0;
 
    // モードビット (RS) とバックライトを設定
    if(mode) {
       data |= LCD_RS;  // データモード
    }
    data |= lcd_backlight;  // バックライト状態を保持
 
    // Enableパルスを生成してデータを転送
    i2c_start();
    i2c_write(data | LCD_EN);  // Enable HIGH
    DELAY_US(1);
    i2c_write(data);           // Enable LOW
    DELAY_US(50);
    i2c_stop();
 }
 
 /**
  * LCDコマンド送信関数
  * @param cmd 送信するコマンドバイト
  */
 void lcd_cmd(uint8_t cmd)
 {
    uint8_t data_h, data_l;
 
    // 上位4ビットと下位4ビットに分割
    data_h = (cmd & 0xF0) | lcd_backlight;
    data_l = ((cmd << 4) & 0xF0) | lcd_backlight;
 
    i2c_start();
 
    // 上位4ビットを送信
    i2c_write(data_h | LCD_EN);  // Enable HIGH
    DELAY_US(1);
    i2c_write(data_h);           // Enable LOW
    DELAY_US(1);
 
    // 下位4ビットを送信
    i2c_write(data_l | LCD_EN);  // Enable HIGH
    DELAY_US(1);
    i2c_write(data_l);           // Enable LOW
 
    i2c_stop();
 
    // コマンド実行完了待ち
    DELAY_MS(2);
 }
 
 /**
  * LCDデータ送信関数
  * @param data 送信する文字データ (ASCII) 
  */
 void lcd_data(uint8_t data)
 {
    uint8_t data_h, data_l;
 
    // 上位4ビットと下位4ビットに分割
    // RSビットをセット (データモード)
    data_h = (data & 0xF0) | LCD_RS | lcd_backlight;
    data_l = ((data << 4) & 0xF0) | LCD_RS | lcd_backlight;
 
    i2c_start();
 
    // 上位4ビットを送信
    i2c_write(data_h | LCD_EN);  // Enable HIGH
    DELAY_US(1);
    i2c_write(data_h);           // Enable LOW
    DELAY_US(1);
 
    // 下位4ビットを送信
    i2c_write(data_l | LCD_EN);  // Enable HIGH
    DELAY_US(1);
    i2c_write(data_l);           // Enable LOW
 
    i2c_stop();
 
    // データ書き込み完了待ち
    DELAY_US(50);
 }
 
 /**
  * 文字列表示関数
  * @param str 表示する文字列 (null終端)
  */
 void lcd_print(const char *str)
 {
    // 文字列の各文字を順次送信
    while(*str) {
       lcd_data(*str++);
    }
 }
 
 /**
  * カーソル位置設定関数
  * @param row 行番号 (0-3)
  * @param col 列番号 (0-19)
  */
 void lcd_set_cursor(uint8_t row, uint8_t col)
 {
    uint8_t address;
 
    // DDRAMアドレスを行番号から計算
    // 20x4 LCDのDDRAMアドレスマッピング:
    // 1行目: 0x00-0x13
    // 2行目: 0x40-0x53
    // 3行目: 0x14-0x27
    // 4行目: 0x54-0x67
    switch(row) {
       case 0:
          address = 0x00 + col;
          break;
       case 1:
          address = 0x40 + col;
          break;
       case 2:
          address = 0x14 + col;
          break;
       case 3:
          address = 0x54 + col;
          break;
       default:
          address = 0x00;
    }
 
    // Set DDRAM Address コマンド (最上位ビットを1に設定)
    lcd_cmd(0x80 | address);
 }
 
 /**
  * 画面クリア関数
  */
 void lcd_clear(void)
 {
    // Clear Display コマンド
    lcd_cmd(0x01);
 
    // クリアコマンドは実行に約1.52[ms]必要
    DELAY_MS(2);
 }
 
 /**
  * バックライトON関数
  */
 void lcd_backlight_on(void)
 {
    lcd_backlight = LCD_BL;
 
    // バックライト状態を更新
    i2c_start();
    i2c_write(lcd_backlight);
    i2c_stop();
 }
 
 /**
  * バックライトOFF関数
  */
 void lcd_backlight_off(void)
 {
    lcd_backlight = 0;
 
    // バックライト状態を更新
    i2c_start();
    i2c_write(lcd_backlight);
    i2c_stop();
 }



ST7735 / ST7789 (TFT LCDコントローラ)

MSP430F149とSPI通信

MSP430F149は、2つのUSARTモジュールを搭載しており、これらをSPIモードで使用することができる。
SPIは、マスター・スレーブ方式の同期シリアル通信プロトコルで、TFT LCDディスプレイの制御に広く使用されている。

USART0をSPIモードで使用する場合のピン配置を以下に示す。
USART0は、ポート3のピンに配置されており、SPIマスターモードで使用する場合は、以下に示す接続となる。

  • MOSI (Slave In, Master Out)
    P3.1
    マスターからスレーブへのデータ送信ライン
  • MISO (Slave Out, Master In)
    P3.2
    スレーブからマスターへのデータ受信ライン
  • SCLK (Serial Clock)
    P3.3
    SPIクロック信号


USART1をSPIモードで使用する場合は、ポート5のピンを使用する。

  • MOSI
    P5.1
  • MISO
    P5.2
  • SCLK
    P5.3


チップセレクト (CS) 、データ/コマンド選択 (DC) 、リセット (RESET) 等の制御信号は、任意のGPIOピンに接続できる。
これらの信号は、SPIデータ転送とは独立して制御される。

ST7735 と ST7789の違い

ST7735 と ST7789は、どちらもTFT LCDコントローラICであるが、いくつかの重要な違いがある。

  • 解像度とディスプレイサイズ
    ST7735は、128x160ピクセルの解像度をサポートし、1.8インチのディスプレイで使用されることが多い。
    ST7789は、240x320ピクセルや240x240ピクセル等、より高解像度のディスプレイをサポートする。

  • 初期化コマンド
    基本的なコマンドセットは類似しているが、ST7789では追加の設定オプションや改良された機能が提供されている。
    例えば、ST7789ではガンマ補正の設定がより柔軟になっている。

  • カラーモードの設定
    両方とも16ビット (RGB565)、18ビット (RGB666) の色深度をサポートしているが、設定コマンドのパラメータが若干異なる場合がある。

  • メモリアクセス制御 (MADCTL) コマンド
    このコマンドは、画面の向きや色順序を設定するために使用される。
    ST7789では、このコマンドのビット配置が一部変更されており、より多くの表示オプションが利用可能となっている。

  • 表示反転制御
    ST7789では反転表示の制御がデフォルトで有効になっていることがある。
    そのため、正常な色表示を得るためにINVON (反転表示ON) コマンドを送信する必要がある場合が多い。


ハードウェア接続

MSP430F149とST7735 / ST7789の接続方法を以下に示す。

電源接続では、ST7735 / ST7789のVCCピンとGNDピンをMSP430F149の電源に接続する。
多くのTFT LCDモジュールは3.3[V]動作であるため、MSP430F149の3.3[V]電源と直接接続できる。
ただし、一部のモジュールは5[V]動作のため、使用するモジュールの仕様を確認すること。

SPI通信ピンの接続は、USART0を使用する場合、以下のように接続する。

  • ST7735 / ST7789のSCL (クロック)
    MSP430F149のP3.3 (SCLK)
  • ST7735 / ST7789のSDA (データ)
    MSP430F149のP3.1 (MOSI)


ST7735 / ST7789は、MISOラインを使用しないため、P3.2 (MISO) の接続は不要である。
ただし、ディスプレイから読み取り機能を使用する場合は、MISO接続が必要となる。

制御ピンの接続について、以下の3つの制御信号が必要である。
これらは任意のGPIOピンに接続できるが、グループ化して連続したピンに配置すると配線が整理しやすい。
推奨される接続例を以下に示す。

  • RESET (リセット)
    P2.0
    ディスプレイのハードウェアリセット
  • DC (データ/コマンド選択)
    P2.1
    0=コマンド、1=データ
  • CS (チップセレクト)
    P2.2
    0=選択、1=非選択


Port2を使用する理由は、これらのピンがデジタルI/O専用であり、他の機能との競合が少ないためである。
また、P2.0〜P2.2は連続しているため、必要に応じてポート操作を効率化できる。

バックライト制御について、多くのTFT LCDモジュールにはLEDバックライトが搭載されている。
バックライトのLED (LED+) ピンは、抵抗を介して3.3[V]に接続する、あるいは、PWM制御可能なGPIOピンに接続して輝度制御を実装できる。
バックライトのLED- (カソード) ピンはGNDに接続する。

SPIの設定

MSP430F149のUSARTモジュールをSPIモードで設定する。

  1. まず、使用するポートの機能選択レジスタを設定する必要がある。
    USART0を使用する場合、P3SELレジスタの該当ビットをセットすることにより、P3.1、P3.2、P3.3がSPI機能として動作する。
  2. 次に、USARTモジュールをリセット状態にする。
    U0CTLレジスタのSWRSTビットをセットすることにより、モジュールが初期化される。
    リセット状態では、すべての設定レジスタを安全に変更できる。


SPIモードの基本設定では、U0CTLレジスタを使用して以下に示すパラメータを設定する。

  • CHAR = 1
    8ビットデータ
  • SYNC = 1
    同期モード (SPI)
  • MM = 1
    マスターモード


  • SPIクロックの極性と位相の設定
    ST7735 / ST7789は、SPIモード0 (CPOL=0、CPHA=0) または モード3 (CPOL=1、CPHA=1) で動作する。
    U0TCTLレジスタのCKPHとCKPLビットでこれらを設定する。

  • クロックソースと分周比の設定
    U0TCTLレジスタのSSELx ビットでクロックソースを選択する。
    通常は、SMCLK (Sub-Main Clock) を使用する。

  • SPIクロック周波数
    U0BR0とU0BR1レジスタで設定する。
    例えば、SMCLKが1[MHz]、SPIクロックを500[kHz]にする場合、分周比を2に設定する。

    ST7735 / ST7789は数[MHz]以上の高速SPIクロックに対応しているが、配線長や信号品質を考慮して適切な周波数を選択する必要がある。
    実験的には、1[MHz]程度から始めて、問題がなければ徐々に周波数を上げることが推奨される。


最後に、U0CTLレジスタのSWRSTビットをクリアしてUSARTモジュールを動作状態にする。
これにより、SPIデータの送受信が可能になる。

GPIOの設定

制御信号用のGPIOピンを出力モードに設定する必要がある。
P2DIRレジスタの該当ビットをセットすることにより、RESET、DC、CSピンが出力として構成される。

初期状態では、以下のように設定することが推奨される。

  • RESET
    HIGH (通常動作状態)
  • DC
    LOW (初期はコマンドモード)
  • CS
    HIGH (非選択状態)


これらの初期値を P2OUT レジスタに設定する。
適切な初期化により、ディスプレイが予期しない動作をすることを防ぐことができる。

初期化シーケンス

ST7735/ST7789の初期化は、正確な順序で実行する必要がある。

詳細な初期化手順を以下に示す。

  1. まず、ハードウェアリセットを実行する。
    1. RESETピンをLOWにして少なくとも10[μs]保持し、その後HIGHに戻す。
    2. リセット後、ディスプレイコントローラが安定するまで約120[ms]待機する必要がある。

    ソフトウェアリセット (SWRESET、コマンド0x01) を送信することもできるが、ハードウェアリセットの方が確実である。
    ソフトウェアリセット後は、120[ms]以上の待機が必要となる。

  2. 次に、スリープモード解除コマンド (SLPOUT、0x11) を送信する。
    このコマンドにより、ディスプレイが低電力スリープモードから通常動作モードに移行する。
  3. スリープモード解除後、内部の電源回路が安定するまで120[ms]以上待機する必要がある。

  4. カラーモード設定コマンド (COLMOD、0x3A) では、ピクセルフォーマットを指定する。
    16ビット色 (RGB565) を使用する場合、パラメータとして0x55 (ST7735) または 0x55 (ST7789) を送信する。
    RGB565フォーマットでは、赤5ビット、緑6ビット、青5ビットで色を表現する。

  5. メモリアクセス制御コマンド (MADCTL、0x36) では、画面の向き、ミラーリング、色順序を設定する。
    このコマンドのパラメータは、使用するディスプレイモジュールによって異なる場合がある。
    一般的な設定は、0x00 (通常の向き、RGBカラー順序) である。

  6. ST7789では、反転表示制御が重要である。
    多くのST7789モジュールでは、色の反転がデフォルトで必要な場合がある。
    この場合、INVONコマンド (0x21) を送信する。
    反転が不要な場合は、INVOFFコマンド (0x20) を送信する。

  7. 最後に、ディスプレイONコマンド (DISPON、0x29) を送信してディスプレイを有効にする。
  8. このコマンド送信後、約100[ms]待機してディスプレイが完全に動作状態になるのを待つ。


描画関数の実装

TFT LCDへの描画は、ピクセルデータをディスプレイのRAMに書き込むことで実現される。

描画領域設定関数

描画領域設定関数 (Set Address Window) は、描画を行う矩形領域を指定する。
この関数は、列アドレス設定コマンド (CASET、0x2A) と 行アドレス設定コマンド (RASET、0x2B) を使用する。

各コマンドの送信

各コマンドには、開始座標と終了座標を16ビット値 (上位8ビットと下位8ビットに分けて) で指定する。
アドレスウィンドウを設定した後、メモリ書き込みコマンド (RAMWR、0x2C) を送信すると、以降送信されるデータがそのウインドウ内に順次書き込まれる。

単一ピクセル描画関数

単一ピクセル描画関数は、1x1のアドレスウィンドウを設定してから色データを送信する。
色データは16ビット値 (RGB565) で、上位バイトと下位バイトの2回に分けて送信する。

矩形塗りつぶし関数

矩形塗りつぶし関数は、指定されたサイズのアドレスウィンドウを設定して、そのウインドウ内の全てのピクセルに同じ色を書き込む。
ピクセル数は幅×高さで計算され、そのピクセル数だけ色データを送信する。
この操作は、背景の塗りつぶしや単色の矩形描画に使用される。

線描画関数

線描画関数は、ブレゼンハムのアルゴリズム等を使用して、2点間の直線を構成するピクセルを計算して、各ピクセルを描画する。

文字描画関数

文字描画関数は、ビットマップフォントを使用して文字を表示する。
各文字は、通常 5x7 または 8x8 ピクセルのビットマップとして定義される。

文字コードに対応するビットマップデータを参照して、各ピクセルについてフォントカラーまたは背景カラーを描画する。

画像表示

画像表示機能を実装する場合、画像データを配列として定義して、ピクセルごとに色データを送信する。
大きな画像の場合、データサイズがメモリを圧迫する可能性があるため、圧縮や外部メモリの使用を検討する必要がある。

タイミングと待機時間

TFT LCDコントローラとの通信では、適切な待機時間が重要である。
例えば、ハードウェアリセット後は、コントローラの内部回路が初期化されるまで最低120[ms]待機する必要がある。
この待機時間が不足すると、以降のコマンドが正しく処理されない可能性がある。

スリープモード解除 (SLPOUT) コマンド後も、電源レギュレータが安定するまで120[ms]以上の待機が必要である。
データシートでは最大120[ms]と規定されているが、安全のため150[ms]程度待機することが推奨される。

ディスプレイON (DISPON) コマンド後は、表示が完全に有効になるまで約100[ms]待機する。
この待機時間中に描画を開始すると、表示の乱れが発生する可能性がある。

一般的なコマンド (CASET、RASET、RAMWR等) は、実行に数マイクロ秒しか掛からない。
そのため、これらのコマンド後に明示的な待機時間を設ける必要はない。
ただし、SPI送信完了を確認してから次のコマンドを送信することは重要である。

反転表示制御コマンド (INVON/INVOFF) も、処理時間が非常に短いため、通常は待機時間不要である。
しかし、コマンド送信後に表示の乱れが観察される場合は、数マイクロ秒から数ミリ秒の待機時間を追加することで改善される場合がある。

サンプルコード

以下の例では、ST7735コントローラを搭載した128x160ピクセルのTFT LCDを制御している。
SPIモジュールの初期化、GPIO制御、ST7735の初期化シーケンス、基本的な描画関数を含んでいる。

実務では、高度な機能として、ビットマップ画像の表示、フォント描画ライブラリの統合、タッチスクリーン入力の処理等を追加することができる。
また、描画処理の最適化として、DMA転送の使用やピクセルバッファリングの実装も検討できる。


ST7789を使用する場合は、初期化シーケンスのカラーモード設定パラメータと反転表示設定を変更する必要がある。

 #include <msp430x14x.h>
 #include <stdint.h>
 
 // GPIOピンの定義
 // ST7735の制御ピンをポート2に配置
 #define LCD_CS_PORT     P2OUT
 #define LCD_CS_PIN      BIT2    // P2.2: チップセレクト
 #define LCD_DC_PORT     P2OUT
 #define LCD_DC_PIN      BIT1    // P2.1: データ/コマンド選択
 #define LCD_RESET_PORT  P2OUT
 #define LCD_RESET_PIN   BIT0    // P2.0: リセット
 
 // ST7735コマンド定義
 #define ST7735_NOP      0x00  // No Operation
 #define ST7735_SWRESET  0x01  // ソフトウェアリセット
 #define ST7735_SLPIN    0x10  // スリープモードに入る
 #define ST7735_SLPOUT   0x11  // スリープモードを解除
 #define ST7735_PTLON    0x12  // 部分モードON
 #define ST7735_NORON    0x13  // 通常表示モードON
 #define ST7735_INVOFF   0x20  // 表示反転OFF
 #define ST7735_INVON    0x21  // 表示反転ON
 #define ST7735_DISPOFF  0x28  // ディスプレイOFF
 #define ST7735_DISPON   0x29  // ディスプレイON
 #define ST7735_CASET    0x2A  // 列アドレス設定
 #define ST7735_RASET    0x2B  // 行アドレス設定
 #define ST7735_RAMWR    0x2C  // メモリ書き込み
 #define ST7735_RAMRD    0x2E  // メモリ読み込み
 #define ST7735_PTLAR    0x30  // 部分エリア定義
 #define ST7735_COLMOD   0x3A  // カラーモード設定
 #define ST7735_MADCTL   0x36  // メモリアクセス制御
 #define ST7735_FRMCTR1  0xB1  // フレームレート制御 (ノーマルモード)
 #define ST7735_FRMCTR2  0xB2  // フレームレート制御 (アイドルモード) 
 #define ST7735_FRMCTR3  0xB3  // フレームレート制御 (部分モード)
 #define ST7735_INVCTR   0xB4  // 表示反転制御
 #define ST7735_PWCTR1   0xC0  // 電源制御1
 #define ST7735_PWCTR2   0xC1  // 電源制御2
 #define ST7735_PWCTR3   0xC2  // 電源制御3
 #define ST7735_PWCTR4   0xC3  // 電源制御4
 #define ST7735_PWCTR5   0xC4  // 電源制御5
 #define ST7735_VMCTR1   0xC5  // VCOMコントロール1
 #define ST7735_GMCTRP1  0xE0  // ガンマ (正) 補正
 #define ST7735_GMCTRN1  0xE1  // ガンマ (負) 補正
 
 // カラー定義 (RGB565フォーマット)
 // RGB565: 16ビット色 (赤5ビット、緑6ビット、青5ビット)
 #define COLOR_BLACK     0x0000  // 黒
 #define COLOR_WHITE     0xFFFF  // 白
 #define COLOR_RED       0xF800  // 赤
 #define COLOR_GREEN     0x07E0  // 緑
 #define COLOR_BLUE      0x001F  // 青
 #define COLOR_YELLOW    0xFFE0  // 黄色
 #define COLOR_CYAN      0x07FF  // シアン
 #define COLOR_MAGENTA   0xF81F  // マゼンタ
 
 // ディスプレイサイズ
 #define ST7735_WIDTH    128
 #define ST7735_HEIGHT   160
 
 // 遅延マクロ (1[MHz]動作時)
 #define DELAY_MS(x)     __delay_cycles((x) * 1000)
 #define DELAY_US(x)     __delay_cycles((x))
 
 // 関数プロトタイプ
 void spi_init(void);
 void spi_write(uint8_t data);
 void gpio_init(void);
 void st7735_init(void);
 void st7735_write_command(uint8_t cmd);
 void st7735_write_data(uint8_t data);
 void st7735_set_addr_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
 void st7735_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
 void st7735_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);
 void st7735_fill_screen(uint16_t color);
 void st7735_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color);
 
 int main(void)
 {
    // ウォッチドッグタイマを停止
    WDTCTL = WDTPW | WDTHOLD;
 
    // システムクロックの設定
    // DCOを1[MHz]に設定
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
 
    // SMCLKの設定 (分周なし)
    BCSCTL2 &= ~(DIVS_3);
 
    // GPIOの初期化
    gpio_init();
 
    // SPIの初期化
    spi_init();
 
    // ST7735の初期化
    st7735_init();
 
    // 描画テスト
    // 背景を青で塗りつぶし
    st7735_fill_screen(COLOR_BLUE);
 
    // 赤い矩形を描画
    st7735_fill_rect(10, 10, 108, 140, COLOR_RED);
 
    // 緑の線を描画
    st7735_draw_line(0, 0, 127, 159, COLOR_GREEN);
 
    // メインループ
    while(1) {
       // 低電力モード
       __bis_SR_register(LPM0_bits + GIE);
       __no_operation();
    }
 }
 
 /**
  * GPIO初期化関数
  */
 void gpio_init(void)
 {
    // ポート2のピンを出力に設定
    P2DIR |= LCD_CS_PIN | LCD_DC_PIN | LCD_RESET_PIN;
 
    // 初期状態を設定
    LCD_CS_PORT |= LCD_CS_PIN;        // CS: HIGH (非選択)
    LCD_DC_PORT &= ~LCD_DC_PIN;       // DC: LOW (コマンドモード)
    LCD_RESET_PORT |= LCD_RESET_PIN;  // RESET: HIGH (通常動作)
 }
 
 /**
  * SPI初期化関数
  * USART0をSPIモードで設定
  */
 void spi_init(void)
 {
    // ポート3の機能選択
    // P3.1: SIMO (Master Out)
    // P3.2: SOMI (Master In) - ST7735では未使用
    // P3.3: SCLK (Clock)
    P3SEL |= BIT1 | BIT2 | BIT3;
 
    // USART0をリセット状態に
    U0CTL = SWRST;
 
    // USART0をSPIモードで設定
    // CHAR = 8ビットデータ
    // SYNC = 同期モード (SPI)
    // MM = マスターモード
    U0CTL |= CHAR | SYNC | MM;
 
    // SPI設定 (モード0: CPOL=0, CPHA=0)
    // CKPL = 0: クロック極性 (アイドル時LOW)
    // CKPH = 1: クロック位相 (最初のエッジでキャプチャ)
    // SSEL1 = 1: SMCLK選択
    // STC = 1: 3ピンSPIモード
    U0TCTL = CKPH | SSEL1 | STC;
 
    // SPIクロック分周器の設定
    // SMCLK=1[MHz]の場合、分周比2でSPICLK=500[kHz]
    U0BR0 = 0x02;
    U0BR1 = 0x00;
    U0MCTL = 0x00;
 
    // USART0をアクティブに
    U0CTL &= ~SWRST;
 
    // 送信割り込みを無効化 (ポーリングで制御)
    IE1 &= ~UTXIE0;
 }
 
 /**
  * SPI書き込み関数
  * @param data 送信するデータバイト
  */
 void spi_write(uint8_t data)
 {
    // 送信バッファが空になるまで待機
    while(!(IFG1 & UTXIFG0));
 
    // データを送信バッファに書き込み
    U0TXBUF = data;
 
    // 送信完了を待機
    while(!(IFG1 & UTXIFG0));
 }
 
 /**
  * ST7735初期化関数
  */
 void st7735_init(void)
 {
    // ハードウェアリセット
    LCD_RESET_PORT &= ~LCD_RESET_PIN;  // RESET: LOW
    DELAY_MS(10);
    LCD_RESET_PORT |= LCD_RESET_PIN;   // RESET: HIGH
    DELAY_MS(120);                     // リセット後の安定化待ち
 
    // ソフトウェアリセット (オプション) 
    st7735_write_command(ST7735_SWRESET);
    DELAY_MS(150);
 
    // スリープモード解除
    st7735_write_command(ST7735_SLPOUT);
    DELAY_MS(120);  // 電源安定化待ち
 
    // フレームレート制御 (ノーマルモード)
    st7735_write_command(ST7735_FRMCTR1);
    st7735_write_data(0x01);  // RTNA: Frame rate
    st7735_write_data(0x2C);  // FPA: Front porch
    st7735_write_data(0x2D);  // BPA: Back porch
 
    // フレームレート制御 (アイドルモード)
    st7735_write_command(ST7735_FRMCTR2);
    st7735_write_data(0x01);
    st7735_write_data(0x2C);
    st7735_write_data(0x2D);
 
    // フレームレート制御 (部分モード)
    st7735_write_command(ST7735_FRMCTR3);
    st7735_write_data(0x01);  // Dot inversion
    st7735_write_data(0x2C);
    st7735_write_data(0x2D);
    st7735_write_data(0x01);  // Line inversion
    st7735_write_data(0x2C);
    st7735_write_data(0x2D);
 
    // 表示反転制御
    st7735_write_command(ST7735_INVCTR);
    st7735_write_data(0x07);  // Line inversion
 
    // 電源制御1
    st7735_write_command(ST7735_PWCTR1);
    st7735_write_data(0xA2);
    st7735_write_data(0x02);
    st7735_write_data(0x84);
 
    // 電源制御2
    st7735_write_command(ST7735_PWCTR2);
    st7735_write_data(0xC5);
 
    // 電源制御3 (ノーマルモード)
    st7735_write_command(ST7735_PWCTR3);
    st7735_write_data(0x0A);
    st7735_write_data(0x00);
 
    // 電源制御4 (アイドルモード)
    st7735_write_command(ST7735_PWCTR4);
    st7735_write_data(0x8A);
    st7735_write_data(0x2A);
 
    // 電源制御5 (部分モード) 
    st7735_write_command(ST7735_PWCTR5);
    st7735_write_data(0x8A);
    st7735_write_data(0xEE);
 
    // VCOMコントロール1
    st7735_write_command(ST7735_VMCTR1);
    st7735_write_data(0x0E);
 
    // 表示反転OFF (必要に応じてINVONに変更)
    st7735_write_command(ST7735_INVOFF);
 
    // メモリアクセス制御
    // MY=0, MX=0, MV=0, ML=0, RGB=1, MH=0
    st7735_write_command(ST7735_MADCTL);
    st7735_write_data(0x00);  // 通常の向き、RGBカラー順序
 
    // カラーモード: 16ビット (RGB565)
    st7735_write_command(ST7735_COLMOD);
    st7735_write_data(0x05);  // 16-bit color
 
    // 列アドレス範囲設定 (0-127)
    st7735_write_command(ST7735_CASET);
    st7735_write_data(0x00);
    st7735_write_data(0x00);
    st7735_write_data(0x00);
    st7735_write_data(0x7F);  // 127
 
    // 行アドレス範囲設定 (0-159)
    st7735_write_command(ST7735_RASET);
    st7735_write_data(0x00);
    st7735_write_data(0x00);
    st7735_write_data(0x00);
    st7735_write_data(0x9F);  // 159
 
    // 通常表示モードON
    st7735_write_command(ST7735_NORON);
    DELAY_MS(10);
 
    // ディスプレイON
    st7735_write_command(ST7735_DISPON);
    DELAY_MS(100);
 }
 
 /**
  * コマンド送信関数
  * @param cmd 送信するコマンドバイト
  */
 void st7735_write_command(uint8_t cmd)
 {
    LCD_DC_PORT &= ~LCD_DC_PIN;  // DC: LOW (コマンドモード) 
    LCD_CS_PORT &= ~LCD_CS_PIN;  // CS: LOW (選択) 
 
    spi_write(cmd);
 
    LCD_CS_PORT |= LCD_CS_PIN;   // CS: HIGH (非選択) 
 }
 
 /**
  * データ送信関数
  * @param data 送信するデータバイト
  */
 void st7735_write_data(uint8_t data)
 {
    LCD_DC_PORT |= LCD_DC_PIN;   // DC: HIGH (データモード)
    LCD_CS_PORT &= ~LCD_CS_PIN;  // CS: LOW (選択)
 
    spi_write(data);
 
    LCD_CS_PORT |= LCD_CS_PIN;   // CS: HIGH (非選択)
 }
 
 /**
  * アドレスウィンドウ設定関数
  * @param x0 開始列
  * @param y0 開始行
  * @param x1 終了列
  * @param y1 終了行
  */
 void st7735_set_addr_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
 {
    // 列アドレス設定
    st7735_write_command(ST7735_CASET);
    st7735_write_data(x0 >> 8);      // 開始列の上位バイト
    st7735_write_data(x0 & 0xFF);    // 開始列の下位バイト
    st7735_write_data(x1 >> 8);      // 終了列の上位バイト
    st7735_write_data(x1 & 0xFF);    // 終了列の下位バイト
 
    // 行アドレス設定
    st7735_write_command(ST7735_RASET);
    st7735_write_data(y0 >> 8);      // 開始行の上位バイト
    st7735_write_data(y0 & 0xFF);    // 開始行の下位バイト
    st7735_write_data(y1 >> 8);      // 終了行の上位バイト
    st7735_write_data(y1 & 0xFF);    // 終了行の下位バイト
 
    // メモリ書き込みモード
    st7735_write_command(ST7735_RAMWR);
 }
 
 /**
  * ピクセル描画関数
  * @param x X座標
  * @param y Y座標
  * @param color 色 (RGB565)
  */
 void st7735_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
 {
    if((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT)) {
       return;  // 範囲外
    }
 
    st7735_set_addr_window(x, y, x, y);
    st7735_write_data(color >> 8);    // 上位バイト
    st7735_write_data(color & 0xFF);  // 下位バイト
 }
 
 /**
  * 矩形塗りつぶし関数
  * @param x X座標
  * @param y Y座標
  * @param w 幅
  * @param h 高さ
  * @param color 色 (RGB565)
  */
 void st7735_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color)
 {
    if((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT)) {
       return;
    }
 
    if((x + w - 1) >= ST7735_WIDTH) {
       w = ST7735_WIDTH - x;
    }
 
    if((y + h - 1) >= ST7735_HEIGHT) {
       h = ST7735_HEIGHT - y;
    }
 
    st7735_set_addr_window(x, y, x + w - 1, y + h - 1);
 
    uint8_t hi = color >> 8;
    uint8_t lo = color & 0xFF;
 
    LCD_DC_PORT |= LCD_DC_PIN;   // データモード
    LCD_CS_PORT &= ~LCD_CS_PIN;  // CS選択
 
    uint32_t pixels = (uint32_t)w * (uint32_t)h;
    while(pixels--) {
       spi_write(hi);
       spi_write(lo);
    }
 
    LCD_CS_PORT |= LCD_CS_PIN;   // CS非選択
 }
 
 /**
  * 全画面塗りつぶし関数
  * @param color 色 (RGB565)
  */
 void st7735_fill_screen(uint16_t color)
 {
    st7735_fill_rect(0, 0, ST7735_WIDTH, ST7735_HEIGHT, color);
 }
 
 /**
  * 線描画関数 (ブレゼンハムのアルゴリズム)
  * @param x0 開始点X座標
  * @param y0 開始点Y座標
  * @param x1 終了点X座標
  * @param y1 終了点Y座標
  * @param color 色 (RGB565)
  */
 void st7735_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color)
 {
    int16_t dx = (x1 > x0) ? (x1 - x0) : (x0 - x1);
    int16_t dy = (y1 > y0) ? (y1 - y0) : (y0 - y1);
    int16_t sx = (x0 < x1) ? 1 : -1;
    int16_t sy = (y0 < y1) ? 1 : -1;
    int16_t err = dx - dy;
 
    while(1) {
       st7735_draw_pixel(x0, y0, color);
 
       if((x0 == x1) && (y0 == y1)) {
          break;
       }
 
       int16_t e2 = err * 2;
       if(e2 > -dy) {
          err -= dy;
          x0 += sx;
       }
 
       if(e2 < dx) {
          err += dx;
          y0 += sy;
       }
    }
 }