STC12C5A60S2マイコンとSC09Aタッチチップによる増減ボタン実装

概要

本記事では、STC12C5A60S2シリーズ1T 8051マイクロコントローラーとXiamen icman(晶尊微)製のタッチチップSC09Aを用いて、タッチボタンの実装方法を解説します。実装する機能は、一方のボタンは短押し・長押しの両方で値を増加させ、もう一方のボタンは短押し・長押しの両方で値を減少させるというものです。

ハードウェア構成

システムはSTC12C5A60S2マイクロコントローラーを中心に構成され、SC09AタッチチップはI2Cインターフェースを通じて接続されます。値の表示には7セグメントディスプレイを使用します。

ソフトウェア実装

メインプログラム (main.c)

#include "STC12C5A60S2.h"  
#include "TouchSC09A.h"
#include "ExternalInt.h"
#include "ButtonHandler.h"
#include "Display7Seg.h"

#define UCHAR unsigned char
#define UINT unsigned int

// ポートモード設定関数
void PortModeSet() {
  P0M1 = 0x00;
  P0M0 = 0x00;
  P1M1 = 0x00;
  P1M0 = 0x00;
  P2M1 = 0x00;
  P2M0 = 0x00;
  P3M1 = 0x00;
  P3M0 = 0x00;
  P4M1 = 0x00;
  P4M0 = 0x00;
}

// メイン関数
void main() {
  PortModeSet();           // ポートモード初期化
  ExternalIntInit();       // 外部割り込み初期化
  DisplayInit();           // ディスプレイ初期化
  
  while(1) {               // メインループ
    ButtonProcess();        // ボタン処理
    UpdateDisplay();        // ディスプレイ更新
  }
}

SC09AタッチチップI2Cインターフェース (TouchSC09A.c)

#include "TouchSC09A.h"
#include "STC12C5A60S2.h"

#define UCHAR unsigned char
#define UINT unsigned int
#define TOUCH_CHIP_ADDR 0x81

// グローバル変数
UINT touchStatus = 0;
sbit I2C_SDA = P1^0;
sbit I2C_SCL = P1^1;
sbit ERROR_LED = P1^2;

// SC09Aからタッチ状態を読み取る関数
UINT ReadTouchSensor() {
  UCHAR bitCount, temp, address;
  bit bitTemp;
  
  address = TOUCH_CHIP_ADDR;
  touchStatus = 0xFFFF;
  EA = 0;  // 割り込み禁止
  
  // I2Cスタート条件
  I2C_SDA = 0;
  for(temp = 0; temp < 4; temp++) { }
  
  // アドレス送信 (8ビット)
  for(bitCount = 0; bitCount < 8; bitCount++) {
    I2C_SCL = 0;
    temp = address & 0x80;
    I2C_SDA = (temp == 0x80) ? 1 : 0;
    address <<= 1;
    
    for(temp = 0; temp < 4; temp++) { }
    I2C_SCL = 1;
    
    for(temp = 0; temp < 4; temp++) { }
  }
  
  // データ読み取り準備
  I2C_SDA = 1;
  I2C_SCL = 0;
  for(temp = 0; temp < 4; temp++) { }
  
  I2C_SCL = 1;
  for(temp = 0; temp < 4; temp++) { }
  
  bitTemp = I2C_SDA;
  if(bitTemp) ERROR_LED = 0;
  
  // タッチデータ読み取り (16ビット)
  for(bitCount = 0; bitCount < 16; bitCount++) {
    I2C_SCL = 0;
    for(temp = 0; temp < 4; temp++) { }
    
    I2C_SCL = 1;
    for(temp = 0; temp < 4; temp++) { }
    
    bitTemp = I2C_SDA;
    if(bitTemp) {
      touchStatus = (touchStatus << 1) | 0x01;
    } else {
      touchStatus <<= 1;
    }
  }
  
  // ストップ条件
  I2C_SCL = 0;
  I2C_SDA = 1;
  for(temp = 0; temp < 4; temp++) { }
  
  I2C_SCL = 1;
  for(temp = 0; temp < 4; { }
  
  I2C_SCL = 0;
  I2C_SDA = 0;
  for(temp = 0; temp < 4; temp++) { }
  
  I2C_SCL = 1;
  for(temp = 0; temp < 4; temp++) { }
  
  I2C_SDA = 1;
  touchStatus ^= 0xFFFF;
  EA = 1;  // 割り込み許可
  
  return touchStatus;
}

ボタン処理ロジック (ButtonHandler.c)

#include "ButtonHandler.h"
#include "TouchSC09A.h"
#include "ExternalInt.h"

#define UCHAR unsigned char
#define UINT unsigned int
#define DEBOUNCE_TIME 15    // チャタリング除去時間(ms)
#define LONG_PRESS_TIME 100 // 長押し判定時間(ms)

// ボタン状態フラグ
UCHAR incButtonFlag = 0;
UCHAR incButtonLock = 0;
UCHAR incShortPressLock = 0;
UCHAR incLongPressLock = 0;
UCHAR decButtonFlag = 0;
UCHAR decButtonLock = 0;
UCHAR decShortPressLock = 0;
UCHAR decLongPressLock = 0;

// タイミング変数
UINT pressTimer = 0;
UINT releaseTimer = 0;
UINT currentValue = 0;
UINT buttonType = 0;

// ボタン処理関数
void ButtonProcess() {
  UINT touchData = ReadTouchSensor();
  
  // 外部割り込みによるタッチ検出
  if(IntFlag == 1) {
    IntFlag = 0;
    
    // 増加ボタン検出
    if((touchData == 0x1000) && (incButtonFlag == 0)) {
      incButtonFlag = 1;
    }
    // 減少ボタン検出
    else if((touchData == 0x0010) && (decButtonFlag == 0)) {
      decButtonFlag = 1;
    }
  }
  
  // 増加ボタン処理
  if((incButtonFlag == 1) && (incButtonLock == 0)) {
    LED_INC = 0;  // 増加LED点灯
    incButtonFlag = 0;
    decButtonLock = 1;  // ボタン間の干渉防止
    
    pressTimer++;
    
    // 短押し/長押し判定
    if((incShortPressLock == 0) && (incLongPressLock == 0)) {
      if(pressTimer > DEBOUNCE_TIME) {
        pressTimer = 0;
        incShortPressLock = 1;
        buttonType = 1;  // 短押し
      }
    }
    
    if(pressTimer > LONG_PRESS_TIME) {
      pressTimer = 0;
      incShortPressLock = 0;
      incLongPressLock = 1;
      buttonType = 3;  // 長押し
    }
  }
  // 減少ボタン処理
  else if((decButtonFlag == 1) && (decButtonLock == 0)) {
    LED_DEC = 0;  // 減少LED点灯
    decButtonFlag = 0;
    incButtonLock = 1;  // ボタン間の干渉防止
    
    pressTimer++;
    
    // 短押し/長押し判定
    if((decShortPressLock == 0) && (decLongPressLock == 0)) {
      if(pressTimer > DEBOUNCE_TIME) {
        pressTimer = 0;
        decShortPressLock = 1;
        buttonType = 2;  // 短押し
      }
    }
    
    if(pressTimer > LONG_PRESS_TIME) {
      pressTimer = 0;
      decShortPressLock = 0;
      decLongPressLock = 1;
      buttonType = 4;  // 長押し
    }
  }
  // ボタン解放処理
  else {
    // 増加ボタン解放
    if(incShortPressLock == 1 || incLongPressLock == 1) {
      LED_INC = 1;  // LED消灯
      pressTimer = 0;
      releaseTimer++;
      
      if(releaseTimer > DEBOUNCE_TIME) {
        releaseTimer = 0;
        incShortPressLock = 0;
        incLongPressLock = 0;
        decButtonLock = 0;
        
        // 短押しの場合は値を更新
        if(buttonType == 1) {
          currentValue++;
          if(currentValue > 9999) currentValue = 0;
        }
      }
    }
    
    // 減少ボタン解放
    if(decShortPressLock == 1 || decLongPressLock == 1) {
      LED_DEC = 1;  // LED消灯
      pressTimer = 0;
      releaseTimer++;
      
      if(releaseTimer > DEBOUNCE_TIME) {
        releaseTimer = 0;
        decShortPressLock = 0;
        decLongPressLock = 0;
        incButtonLock = 0;
        
        // 短押しの場合は値を更新
        if(buttonType == 2) {
          if(currentValue == 0) currentValue = 0;
          else currentValue--;
        }
      }
    }
  }
}

// ディスプレイ更新関数
void UpdateDisplay() {
  static digitPosition = 0;
  static UCHAR digitBuffer[4];
  
  // 値を桁に分解
  digitBuffer[0] = currentValue / 1000;
  digitBuffer[1] = (currentValue / 100) % 10;
  digitBuffer[2] = (currentValue / 10) % 10;
  digitBuffer[3] = currentValue % 10;
  
  // 0埋め処理
  if(currentValue < 1000) digitBuffer[0] = 0x0F;
  if(currentValue < 100) digitBuffer[1] = 0x0F;
  if(currentValue < 10) digitBuffer[2] = 0x0F;
  
  // 7セグメントディスプレイへの出力
  SEGMENT_PORT = 0xFF;  // 消灯
  
  switch(digitPosition) {
    case 0:  // 千の位
      SEGMENT_PORT = digitCode[digitBuffer[0]];
      DIGIT_PORT = digitSelect[0];
      break;
    case 1:  // 百の位
      SEGMENT_PORT = digitCode[digitBuffer[1]];
      DIGIT_PORT = digitSelect[1];
      break;
    case 2:  // 十の位
      SEGMENT_PORT = digitCode[digitBuffer[2]];
      DIGIT_PORT = digitSelect[2];
      break;
    case 3:  // 一の位
      SEGMENT_PORT = digitCode[digitBuffer[3]];
      DIGIT_PORT = digitSelect[3];
      break;
  }
  
  digitPosition = (digitPosition + 1) % 4;
}

外部割り込み処理 (ExternalInt.c)

#include "ExternalInt.h"

#define UCHAR unsigned char

UCHAR IntFlag = 0;

// 外部割り込み0初期化
void ExternalIntInit() {
  IT0 = 0;    // レベルトリガー
  EX0 = 1;    // 割り込み許可
}

// 外部割り込み0サービスルーチン
void ExternalInt() interrupt 0 {
  IntFlag = 1;  // 割り込みフラグ設定
}

動作仕様

本システムでは、以下の動作を実装しています:

  • タッチボタンA:短押し・長押しの両方で値を1増加
  • タッチボタンB:短押し・長押しの両方で値を1減少
  • 値は0〜9999の範囲で表示
  • LEDで各ボタンの操作状態を表示
  • 7セグメントディスプレイで現在値を表示

回路接続

SC09AタッチチップはI2Cインターフェース(SDA: P1.0, SCL: P1.1)で接続します。外部割り込みはP3.2を使用します。7セグメントディスプレイはP0(セグメント)とP2(桁選択)に接続します。

タグ: STC12C5A60S2 8051 SC09A タッチセンサ I2C

7月9日 21:01 投稿