概要
本記事では、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(桁選択)に接続します。