-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCameraProbe.cpp
More file actions
174 lines (151 loc) · 6.24 KB
/
Copy pathCameraProbe.cpp
File metadata and controls
174 lines (151 loc) · 6.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include "CameraProbe.h"
#include <M5Unified.h>
#include "Config.h"
namespace {
constexpr uint32_t kFreq = 100000; // 走査は遅めにします。取りこぼしを避けるためです
// カメラのレジスタ番地は16ビットです。M5Unifiedの readRegister8() は8ビットの
// 番地しか扱えないので、ここは自分で組み立てます。
//
// 手順は「書き込みで始める → 番地を上位・下位の順に送る → 読み込みへ切り替える
// → 1バイト受け取る → 終わる」です。
bool readReg16(uint8_t address, uint16_t reg, uint8_t *out) {
auto &i2c = M5.In_I2C;
if (!i2c.start(address, false, kFreq)) return false;
uint8_t regBytes[2] = {uint8_t(reg >> 8), uint8_t(reg & 0xFF)};
if (!i2c.write(regBytes, 2)) {
i2c.stop();
return false;
}
if (!i2c.restart(address, true, kFreq)) {
i2c.stop();
return false;
}
bool ok = i2c.read(out, 1, true);
i2c.stop();
return ok;
}
// 機種を表す番号を読みます。SC2356(型番SC202CS)は0x3107と0x3108の2バイトです。
bool readSensorId(uint8_t address, uint16_t *outId) {
uint8_t hi = 0, lo = 0;
if (!readReg16(address, 0x3107, &hi)) return false;
if (!readReg16(address, 0x3108, &lo)) return false;
*outId = uint16_t(hi) << 8 | lo;
// 全部0や全部1は、応答していないのと同じです。読めたことにしません。
return *outId != 0x0000 && *outId != 0xFFFF;
}
// 本体内部のI2Cにぶら下がっている機器のアドレスを並べます。
String scanBus() {
String found;
int count = 0;
for (uint8_t addr = 0x08; addr <= 0x77; addr++) {
if (!M5.In_I2C.scanID(addr, kFreq)) continue;
char buf[8];
snprintf(buf, sizeof(buf), "%02X ", addr);
found += buf;
count++;
}
return String(count) + "台: " + found;
}
// カメラがいそうなアドレスを順に試します。
//
// SCCBのアドレスは機種ごとに決まっていますが、実機で確かめていないので
// 候補を複数当たります。走査で見つかったアドレスに対して総当たりするより、
// 意味のある候補に絞るほうが、他の機器に妙な読み出しを送らずに済みます。
const uint8_t kCandidates[] = {0x36, 0x30, 0x3C, 0x21, 0x60};
bool findCamera(uint8_t *outAddr, uint16_t *outId) {
for (uint8_t addr : kCandidates) {
if (!M5.In_I2C.scanID(addr, kFreq)) continue;
if (!readSensorId(addr, outId)) continue;
*outAddr = addr;
return true;
}
return false;
}
// リセットを解除します。
//
// カメラのリセットはIOエクスパンダ(PI4IOE5V6408)の6番に繋がっています。
// どちらのエクスパンダかが分かっていないので、両方を順に試します。
//
// 他のビットには触れません。同じエクスパンダには画面のリセットや電源の制御も
// ぶら下がっているので、まとめて書き換えると画面が消えたり電源が切れたりします。
void releaseReset(int expanderIndex) {
auto &ioexp = M5.getIOExpander(expanderIndex);
ioexp.setDirection(CAMERA_RESET_IO_PIN, true); // 出力にする
ioexp.setHighImpedance(CAMERA_RESET_IO_PIN, false); // 実際に駆動する
ioexp.digitalWrite(CAMERA_RESET_IO_PIN, false); // いったんリセットをかける
delay(10);
ioexp.digitalWrite(CAMERA_RESET_IO_PIN, true); // 解除
delay(20); // 立ち上がりを待つ
}
} // namespace
namespace CameraProbe {
String run() {
String report;
report += "内部I2Cの走査\n " + scanBus() + "\n";
uint8_t addr = 0;
uint16_t id = 0;
if (findCamera(&addr, &id)) {
char buf[80];
snprintf(buf, sizeof(buf), "カメラを検出: アドレス 0x%02X / 番号 0x%04X\n", addr, id);
report += buf;
report += "リセットは既に解除されています";
Serial.printf("[CAM] %s", report.c_str());
return report;
}
report += "カメラの応答なし。リセットの解除を試します\n";
for (int i = 0; i < 2; i++) {
releaseReset(i);
if (findCamera(&addr, &id)) {
char buf[100];
snprintf(buf, sizeof(buf), "カメラを検出: アドレス 0x%02X / 番号 0x%04X\n", addr, id);
report += buf;
snprintf(buf, sizeof(buf), "リセットはIOエクスパンダ%d番(0x%02X)のピン%dでした", i,
0x43 + i, CAMERA_RESET_IO_PIN);
report += buf;
Serial.printf("[CAM] %s\n", report.c_str());
return report;
}
}
report += "どちらのエクスパンダでも検出できませんでした\n";
report += "供給クロックが必要か、リセットのピンが違う可能性があります";
Serial.printf("[CAM] %s\n", report.c_str());
return report;
}
String dumpExposure() {
uint16_t id = 0;
if (!readSensorId(CAMERA_SCCB_ADDR, &id)) {
return "カメラが応答しません";
}
// 読むレジスタと、その意味です。番地はEspressifのSC202CS用ドライバの
// 定義に合わせています(実機のチップIDが同ドライバの値と一致することを
// 確認済みです)。
struct Entry {
uint16_t reg;
const char *label;
};
static const Entry kEntries[] = {
{0x0100, "動作中"}, {0x3e00, "露出 上位"}, {0x3e01, "露出 中位"},
{0x3e02, "露出 下位"}, {0x3e06, "ゲイン 微"}, {0x3e07, "ゲイン 粗"},
{0x3e09, "ゲイン アナログ"},
};
String report;
uint32_t exposure = 0;
for (const Entry &e : kEntries) {
uint8_t v = 0;
bool ok = readReg16(CAMERA_SCCB_ADDR, e.reg, &v);
char buf[48];
snprintf(buf, sizeof(buf), "%s(%04X)=%s%02X ", e.label, e.reg, ok ? "" : "??", ok ? v : 0);
report += buf;
if (e.reg == 0x3e00) exposure = uint32_t(v) << 16;
if (e.reg == 0x3e01) exposure |= uint32_t(v) << 8;
if (e.reg == 0x3e02) exposure |= v;
}
// 露出は3つのレジスタに分かれているので、繋げた値も出します。
// 明るさとの対応を見るときは、こちらのほうが分かりやすいためです。
char buf[48];
snprintf(buf, sizeof(buf), "\n露出をつなげた値: %lu", (unsigned long)exposure);
report += buf;
Serial.printf("[CAM] %s\n", report.c_str());
return report;
}
} // namespace CameraProbe