「あー!!!PWMポートもっとあるのを書き忘れてたの思い出した!!!!(✽ ゚д゚ ✽)!!!!」
「3つだけじゃなくて他のポートも前回のコアアップデートで動くようになったの忘れてたw」
![]() |
| ピン配置図(2025年9月版) |
![]() |
| ピン配置図(2025年9月版) |
//------------------------------------------------
// LED + ボタン + ブザー実験用スケッチ
//
// 目的:
// ・74HC595を2個使って、LEDを最大11個制御する
// ・74HC165を2個使って、ボタンを最大11個読み取る
// ・ボタン0〜9は、対応するLEDを点灯し、音階を鳴らす
// ・ボタン10は、LED10を点灯し、自励式ブザーを鳴らす
//
// 接続概要:
// ・LED出力側:74HC595 x2
// ・ボタン入力側:74HC165 x2
// ・音出力:他励式ブザー / スピーカー
// ・警告音:自励式ブザー
//------------------------------------------------
//------------------------------------------------
// 74HC595 側の接続
//
// 74HC595は「少ないマイコンピンでたくさんのLEDを制御する」IC。
// 今回は2個カスケード接続して、LEDを11個まで扱う。
//
// PIN_SER : 74HC595のSER / DS
// PIN_LATCH : 74HC595のRCLK / ST_CP
// PIN_CLK : 74HC595のSRCLK / SH_CP
//------------------------------------------------
const int PIN_SER = 11;
const int PIN_LATCH = 8;
const int PIN_CLK = 12;
//------------------------------------------------
// 74HC165 側の接続
//
// 74HC165は「たくさんのボタン入力を少ないマイコンピンで読む」IC。
// 今回は2個カスケード接続して、16bit分の入力を読む。
// 実際に使っているのは、そのうち11個。
//
// PIN_165_DATA : 74HC165のQH
// PIN_165_LOAD : 74HC165のSH/LD
// PIN_165_CLK : 74HC165のCLK
//
// ボタン入力はプルアップ前提。
// つまり、押していない時はHIGH、押すとLOWになる。
//------------------------------------------------
const int PIN_165_DATA = 7;
const int PIN_165_LOAD = 5;
const int PIN_165_CLK = 10;
//------------------------------------------------
// LED表示用の仮想画面
//
// vram[0]〜vram[10] が LED0〜LED10 に対応。
// 直接74HC595へ書き込まず、いったんvramに状態を作ってから
// drawVRAM()でまとめてLEDへ反映する。
//------------------------------------------------
const int SCREEN_WIDTH = 11;
bool vram[SCREEN_WIDTH];
//------------------------------------------------
// ブザー接続
//
// PIN_SPEAKER : 他励式ブザー、または小型スピーカー
// tone()で周波数を指定して鳴らす。
// PIN_ALARM : 自励式ブザー
// HIGHにするだけで鳴るタイプ。
//------------------------------------------------
const int PIN_SPEAKER = 6; // 他励式ブザー / スピーカー
const int PIN_ALARM = 0; // 自励式ブザー
//------------------------------------------------
// ボタン0〜9に対応する音階
//------------------------------------------------
const int noteTable[10] =
{
262, // Button0 : ド(C4)
294, // Button1 : レ
330, // Button2 : ミ
349, // Button3 : ファ
392, // Button4 : ソ
440, // Button5 : ラ
494, // Button6 : シ
523, // Button7 : ド(C5)
587, // Button8 : レ
659 // Button9 : ミ
};
//------------------------------------------------
// VRAM操作
//------------------------------------------------
void clearVRAM()
{
for (int i = 0; i < SCREEN_WIDTH; i++)
{
vram[i] = false;
}
}
void setPixel(int x, bool on)
{
if (x < 0 || x >= SCREEN_WIDTH) return;
vram[x] = on;
}
//------------------------------------------------
// 74HC595へLED状態を出力
//
// vram[0]〜vram[7] → 1個目の74HC595
// vram[8]〜vram[10] → 2個目の74HC595
//
// shiftOutは、先に2個目ぶんのデータを送り、
// 次に1個目ぶんのデータを送る。
// その後、LATCHをHIGHにしてLEDへ反映する。
//------------------------------------------------
void drawVRAM()
{
byte b1 = 0;
byte b2 = 0;
for (int i = 0; i < SCREEN_WIDTH; i++)
{
if (vram[i])
{
if (i < 8)
{
b1 |= (1 << i);
}
else
{
b2 |= (1 << (i - 8));
}
}
}
digitalWrite(PIN_LATCH, LOW);
shiftOut(PIN_SER, PIN_CLK, MSBFIRST, b2);
shiftOut(PIN_SER, PIN_CLK, MSBFIRST, b1);
digitalWrite(PIN_LATCH, HIGH);
}
//------------------------------------------------
// 74HC165 x2 読み取り
//
// 2個の74HC165から16bitぶんのボタン状態を読む。
// LOADをLOWにすると、各入力ピンの状態がIC内部に取り込まれる。
// その後、CLKを動かしながらDATAピンから1bitずつ読む。
//
// プルアップ接続なので、
// ・押していない → 1
// ・押している → 0
// になる。
//------------------------------------------------
unsigned int read165x2()
{
unsigned int data = 0;
digitalWrite(PIN_165_CLK, LOW);
delayMicroseconds(20);
digitalWrite(PIN_165_LOAD, LOW);
delayMicroseconds(20);
digitalWrite(PIN_165_LOAD, HIGH);
delayMicroseconds(20);
data = digitalRead(PIN_165_DATA) ? 1 : 0;
for (int i = 1; i < 16; i++)
{
digitalWrite(PIN_165_CLK, HIGH);
delayMicroseconds(20);
digitalWrite(PIN_165_CLK, LOW);
delayMicroseconds(20);
data <<= 1;
if (digitalRead(PIN_165_DATA))
{
data |= 1;
}
}
return data;
}
//------------------------------------------------
// ボタン割り当て
//
// read165x2()で読んだ16bitのうち、
// どのbitをどのボタンとして扱うかを定義する。
//
// Button0〜Button7 : 1個目の74HC165
// Button8〜Button10 : 2個目の74HC165
//
// H, G, F... の順番で読んでいるため、
// Button0 = #1 H になっている。
//------------------------------------------------
const unsigned int buttonMask[11] =
{
0x8000, // Button0 = #1 H
0x4000, // Button1 = #1 G
0x2000, // Button2 = #1 F
0x1000, // Button3 = #1 E
0x0800, // Button4 = #1 D
0x0400, // Button5 = #1 C
0x0200, // Button6 = #1 B
0x0100, // Button7 = #1 A
0x0080, // Button8 = #2 H
0x0040, // Button9 = #2 G
0x0020 // Button10 = #2 F
};
//------------------------------------------------
// setup
//------------------------------------------------
void setup()
{
// UIAPduino / CH32V003用の初期化処理。
// 書き込みやデバッグピンの都合で必要になる場合がある。
if (FLASH->STATR & (1<<14)) NVIC_SystemReset();
SystemReset_StartMode(Start_Mode_BOOT);
pinMode(PD4, OUTPUT);
pinV32_DisconnectDebug(PD_1);
pinMode(PIN_SPEAKER, OUTPUT);
pinMode(PIN_ALARM, OUTPUT);
digitalWrite(PIN_ALARM, LOW);
pinMode(PIN_SER, OUTPUT);
pinMode(PIN_LATCH, OUTPUT);
pinMode(PIN_CLK, OUTPUT);
pinMode(PIN_165_DATA, INPUT);
pinMode(PIN_165_LOAD, OUTPUT);
pinMode(PIN_165_CLK, OUTPUT);
digitalWrite(PIN_LATCH, HIGH);
digitalWrite(PIN_CLK, LOW);
digitalWrite(PIN_165_LOAD, HIGH);
digitalWrite(PIN_165_CLK, LOW);
clearVRAM();
drawVRAM();
}
//------------------------------------------------
// loop
//
// 1. 74HC165からボタン状態を読む
// 2. 押されたボタンに対応するLEDを点灯する
// 3. Button0〜Button9なら音階を鳴らす
// 4. Button10なら自励式ブザーを鳴らす
// 5. 74HC595へLED状態を反映する
//------------------------------------------------
void loop()
{
unsigned int sw = read165x2();
clearVRAM();
int note = -1;
// Button0〜Button9
// 押されているボタンに対応するLEDを点灯し、
// 最初に見つかったボタンの音を鳴らす。
for (int i = 0; i < 10; i++)
{
if (!(sw & buttonMask[i]))
{
setPixel(i, true);
if (note < 0)
{
note = i;
}
}
}
// Button10
// LED10を点灯し、自励式ブザーを鳴らす。
if (!(sw & buttonMask[10]))
{
setPixel(10, true);
digitalWrite(PIN_ALARM, HIGH);
}
else
{
digitalWrite(PIN_ALARM, LOW);
}
drawVRAM();
if (note >= 0)
{
tone(PIN_SPEAKER, noteTable[note]);
}
else
{
noTone(PIN_SPEAKER);
}
delay(10);
}
シフトレジスタ[74HC595]を使って11個のLEDを点灯させることができたので、次は入力だ! ということでシフトレジスタ[74HC165]を使った11個のキー入力に挑戦したのだけれど、これが苦戦の連続だった。
出力側につかうシフトレジスタ[74HC595]の参考資料やサンプルプログラムは、公式サイトやあちこちのブログに書かれている。でも入力側につかう[74HC165]に関しては、(自分が理解できる)ちょうど良いものを見つけることができなかったのだ。
もちろん資料がゼロというわけではない。データシートや海外の解説記事も見つかった。でも初心者向けに「こう繋げば動くよ」と説明してくれる記事は少なく、自分には少しハードルが高かった。
それから苦戦した原因のもう一つ。これは完全に自分が悪い。すぐに動くだろうと考えて、単体テストを行わずに、いきなり制作中の基板に[74HC165]を2つも繋げてしまった。
そのせいで追加した回路が悪いのか? ICが壊れているのか? 元々動いているLED表示側回路が実は間違っていたのか? テストプログラムが間違っているのか? 原因を特定することができなくなってしまった……。
さんざん試行錯誤をしたあとに冷静になって、入力側のシフトレジスタ[74HC165]を1つだけでテストする別の基板をつくってみた。ChatGPTに回路図やテストプログラムを何度も見てもらいながら、なんとか動かすことができるようになり、その後で[74HC165]を更に追加した。
本当はブレッドボード2枚に全部の回路を納めようと思って詰め込んでいたけれど、また修正する可能性が高い。3枚構成にすることにした。一番下はユーザーインタフェース専用基板、2枚目はLED出力管理専用基板、3枚目はキー入力管理+UIAPduinoを設置する基板となった。
ひとまず11個のLEDを、対応する11個のタクトスイッチを押すと光らせる所までは、つくることができた。次はブザーをつけて、音を鳴らすつもり。
今度は絶対に単体テストをしてからやるぞー!
| ブレッドボード3枚になってしまった! |