✖
アナログ回路への理解を深めたい。まだ全然、やりたいことをすぐに実現できるレベルにならない。どうすればできるのかわからない、ということが多すぎる。
アナログ回路への理解を深めたい。まだ全然、やりたいことをすぐに実現できるレベルにならない。どうすればできるのかわからない、ということが多すぎる。
2015年もたくさんコードかきました。他人に承認されないことはせめて自分で承認しましょう。つらい
[tech] リファレンスマニュアルをインクリメンタル検索するやつを Electron で実装した | Wed, Oct 14. 2015 - 氾濫原
ドキュメントビューワ。Mac AppStore まで出してみたが全く売れておりません。自分では便利に使ってる
[tech] Arduino の digitalWrite / pinMode / digitalRead をコンパイル時に展開する | Wed, Dec 16. 2015 - 氾濫原
digitalWrite とかを静的に解決するやつ。
[tech] もう僕らは OpenGL ライブラリにリンクするビルドに悩むことはない | Thu, Oct 8. 2015 - 氾濫原
KX3(無線機)用のパナダプター(Panoramic Adapter = FFTウォーターフォール) WebGL に書きなおしたりした。便利
[tech] CNC フライス Grbl 制御編 | Sun, Aug 16. 2015 - 氾濫原
CNCフライス制御用のアプリケーション。最近あんまり切削してない。
[tech] スクリーンキャスト用のキーストローク表示アプリ | Sat, Feb 14. 2015 - 氾濫原
入力したキーを画面に表示するやつ。
[tech] CopyHook というペーストボードの中身をいじるツールを作った | Wed, Mar 18. 2015 - 氾濫原
クリップボードコピーしたときにフックでJS実行するやつ。なんかうまく動いてない気がするがデバッグの時間がとれない。
[tech] JavaScript で書かれたリードソロモン符号のエンコーダ・デコーダ | Tue, Mar 31. 2015 - 氾濫原
任意のリードソロモン(誤り訂正)符号のエンコーダデコーダ。Zxing (Java) の一部ライブラリの移植
[tech] WebAudio の BiquadFilterNode の周波数特性をグラフにするやつ | Fri, Mar 20. 2015 - 氾濫原
WebAudio のフィルタの周波数特性を出すやつ。
[tech] デカい文字をA4で分割して印刷するツールをJSで書いた | Sat, Mar 7. 2015 - 氾濫原
ー https://cho45.stfuawsc.com/dekaimoji-a4/
A4プリンタでデカい文字が印刷できるやつ。
C/C++にはほとんど使われてないがビットフィールドという機能がある。
union {
uint8_t raw;
struct {
unsigned FAULT_QUEUE : 2;
unsigned CT_PIN_POLARITY : 1;
unsigned INT_PIN_POLARITY : 1;
unsigned INT_CT_MODE : 1;
unsigned OPERATION_MODE : 2;
unsigned RESOLUTION : 1;
};
} config; このように書ける。struct 内で名前の後ろについているのが、そのフィールドで消費するビット数で、この場合合計で8bitになり、それを uint8_t と共用している。
こうすると config.OPERATION_MODE = 2; などと、マスクやシフトを伴わずに直接書けて、結果をconfig.rawでとれる。
めっちゃ便利なので使わない手はなさそうだと思いきや、実際のところ実用するのは不安がある。というのも、この struct 内のビット配置の順序は実装依存となっていて、uint8_t として評価したとき、どのような結果が返ってくるか確かなことがいえない。
コンパイラ依存
そこで、上記のようなビットフィールドを以下のように書きなおす
template <class T, uint8_t s, uint8_t e = s>
struct bits {
T ref;
static constexpr T mask = (T)(~( (T)(~0) << (e - s + 1))) << s;
void operator=(const T val) { ref = (ref & ~mask) | ((val & (mask >> s)) << s); }
operator T() const { return (ref & mask) >> s; }
};
template <uint8_t s, uint8_t e = s>
using bits8 = bits<uint8_t, s, e>;
union {
uint8_t raw = 0;
bits8<0, 1> FAULT_QUEUE ;
bits8<2> CT_PIN_POLARITY ;
bits8<3> INT_PIN_POLARITY ;
bits8<4> INT_CT_MODE ;
bits8<5, 6> OPERATION_MODE ;
bits8<7> RESOLUTION ;
} config; uint8_t 全体を明確に共用する複数のstructという形にし、明示的にビットシフトやマスクを行っている。それぞれ、テンプレートの第一引数〜第二引数のビットを扱うクラスになっている。
組み込みで他のデジタルICとやりとりをする場合、だいたいデータシートには [0:1] foobar みたいな形でビット範囲と値の説明が書いてあるので、それをその通り書きうつして union を作れば間違いなくビット操作できる状態になる。
これで安心してビットフィールドっぽいものが使える。
試した限りだと完全にインライン化される。また、1bitだけ書く場合andかorだけにまで最適化される。
int main(void) {
asm volatile ("nop");
config.OPERATION_MODE = 0b11;
asm volatile ("nop");
config.RESOLUTION = 1;
asm volatile ("nop");
config.FAULT_QUEUE = 1;
asm volatile ("nop");
for (;;) {
}
return 0;
}
こういうコードは
000000a0 <main>: a0: 00 00 nop a2: 00 00 nop a4: 00 00 nop a6: 80 91 00 01 lds r24, 0x0100 aa: 8c 71 andi r24, 0x1C ; 28 ac: 81 6e ori r24, 0xE1 ; 225 ae: 80 93 00 01 sts 0x0100, r24 b2: 00 00 nop b4: ff cf rjmp .-2 ; 0xb4 <main+0x14>
こうなる
| #include <cstdio> | |
| #include <stdint.h> | |
| #include <iostream> | |
| template <class T, class U> | |
| void is(T got, U expected) { | |
| if (got == expected) { | |
| std::cout << "ok" << std::endl; | |
| } else { | |
| std::cout << "not ok " << got << " != " << expected << std::endl; | |
| } | |
| } | |
| template <class T, uint8_t s, uint8_t e = s> | |
| struct bits { | |
| T ref; | |
| static constexpr T mask = (T)(~( (T)(~0) << (e - s + 1))) << s; | |
| void operator=(const T val) { ref = (ref & ~mask) | ((val & (mask >> s)) << s); } | |
| operator T() const { return (ref & mask) >> s; } | |
| }; | |
| template <uint8_t s, uint8_t e = s> | |
| using bits8 = bits<uint8_t, s, e>; | |
| int main () { | |
| union { | |
| uint8_t raw = 0; | |
| bits8<0, 1> FAULT_QUEUE ; | |
| bits8<2> CT_PIN_POLARITY ; | |
| bits8<3> INT_PIN_POLARITY ; | |
| bits8<4> INT_CT_MODE ; | |
| bits8<5, 6> OPERATION_MODE ; | |
| bits8<7> RESOLUTION ; | |
| } config; | |
| config.OPERATION_MODE = 0b11; | |
| is((uint)config.raw, 0b01100000); | |
| config.FAULT_QUEUE = 0b10; | |
| is((uint)config.raw, 0b01100010); | |
| config.RESOLUTION = 1; | |
| is((uint)config.raw, 0b11100010); | |
| config.OPERATION_MODE = 0; | |
| is((uint)config.raw, 0b10000010); | |
| config.raw = 0; | |
| is((uint)config.OPERATION_MODE, 0b00); | |
| config.raw = 0b01000000; | |
| is((uint)config.OPERATION_MODE, 0b10); | |
| config.FAULT_QUEUE = 0b111; | |
| is((uint)config.raw, 0b01000011); | |
| return 0; | |
| } |
I2Cセンサーとかを扱うと固定小数点表現によく出会う。が、固定小数点のままだと計算がめんどうなので、とりあえず浮動小数点に変換しときたいというケースがまぁまぁある。
そういうときに雑に使えるスニペットがほしかったので書いた。
#include <type_traits>
template <uint8_t int_bits, uint8_t fractional_bits, class T>
inline float fixed_point_to_float(const T fixed) {
static_assert(std::is_unsigned<T>::value, "argument must be unsigned");
constexpr uint8_t msb = int_bits + fractional_bits - 1;
constexpr T mask = static_cast<T>(~(( static_cast<T>(~0)) << msb));
constexpr float deno = 1<<fractional_bits;
if (fixed & (1<<msb)) {
// negative
return -( ( (~fixed & mask) + 1) / deno);
} else {
// positive
return fixed / deno;
}
} type_traits がない環境の場合、include と static_assert を消すだけで動く。これはエラーチェックにしか使ってなくて、もし消したとしても、負の signed を渡すと左シフトが不正になるのでエラーになる。
センサ出力とかの場合、8bit単位のビット数ではないことが多いので、渡された型のサイズに関わらずに処理できるようにマスクを作っている。
// usage
int main (int argc, char* argv[]) {
// from ADT7410 datasheet
is(fixed_point_to_float<9, 4>((uint16_t)0b0000000000001), 0.0625);
is(fixed_point_to_float<9, 4>((uint16_t)0b0100101100000), 150.0);
is(fixed_point_to_float<9, 4>((uint16_t)0b0000000000000), 0);
is(fixed_point_to_float<9, 4>((uint16_t)0b1110010010000), -55.0);
is(fixed_point_to_float<9, 4>((uint16_t)0b1111111111111), -0.0625);
is(fixed_point_to_float<9, 4>((uint32_t)0b1111111111111), -0.0625 );
/** compile error : argument must be unsigned
printf("%f\n", fixed_point_to_float<9, 4>((int16_t)0b1111111111111));
*/
// from MCP3425 datasheet
is(fixed_point_to_float<1, 11>((uint16_t)0x001) * (2.048), 1e-3);
is(fixed_point_to_float<1, 13>((uint16_t)0x001) * (2.048), 250e-6);
is(fixed_point_to_float<1, 15>((uint16_t)0x001) * (2.048), 62.5e-6);
// from MPL115A2 datasheet
// a0 coefficient
is(fixed_point_to_float<13, 3>((uint16_t)0x3ECE), 2009.75);
// b1 coefficient
is(fixed_point_to_float<3, 13>((uint16_t)0xB3F9), -2.37585);
// b2 coefficient
is(fixed_point_to_float<2, 14>((uint16_t)0xC517), -0.92047);
// c12 coefficient
is(fixed_point_to_float<1, 15>((uint16_t)0x33C8)/(1<<9), 0.000790);
// test dynamic variable
volatile uint16_t x = 0x001;
is(fixed_point_to_float<1, 11>(x) * (2.048), 1e-3);
} テンプレートの第1引数は整数部(符号込み)のビット数・第2引数は小数点分のビット数
これはQ表記に対応する。
Q表記だと Q1.15 だと符号分1・整数部なし・15ビットの小数点桁。Q9.4 だと符号付き整数部8bit、小数部4bit。
固定小数点数用のクラス作って可能な限りは固定小数点で演算したほうがいい気はする。ヘッダ1ファイルとかで使えるの、当然もうありそうだけど見つけられてない。
モバイバッテリーは低電流時、充電完了と判断してパワーオフする(出力回路の動作をやめる)が、これをやらせたくない場合どうすればいいか。現時点でのメモ
Arduino に適当なプログラムを書きこんで、ポートに抵抗を繋ぎ(複数ポートにわけて) パワーオフするかどうかを調べた。測定時は電源供給経路途中に1Ωの抵抗をいれ、オシロでこの抵抗の両端電圧を測ることで間接的に実測の電流値を求めている。
Arduino のベースの消費電力は40mA程度。なので以下でさらに大きな電流を流しているが、40mA との切り替えということになる。
Anker は製品説明書に最低限充電電流が書いてある (50mA)
しかし連続で流し続けなければならないのか、パルスでいいのかはわからない。
A1208
検知できないと30秒ぐらいでパワーオフする
すくなくとも短いパルスではリセットできないっぽい
一定時間内の平均消費をみている?
PB-T1
説明書には特にオートパワーオフの閾値の記載なし。
約3分後にパワーオフ。電流を検知している間はバッテリーランプが点灯するっぽい?
ランプが5秒ぐらいで消灯するが、その前にパルスを検知すればいいっぽい… 謎
100mA ぐらい常時流しといたらいいんちゃう?
→ 5V 100mA (0.5W)
3.3V 10000mAh のやつは26Whぐらいなので、とても厳しいという状態でなければそれでもいいかもしれない。
Quick Charge バッテリの場合、電流値で検出しているのか電力値で検出しているのかで大きくわかれる。12V 100mA 流すことになったら常時 1.2W 消費ということになりつらい。
Quick Charge 2.0 電源から 9V をとる (任意の電圧をとる) | tech - 氾濫原 で、だいたいこれで良さそうと思ったので ATTiny13A 使ってユニバーサル基板にまとめた。
3端子レギュレータの S-812C33AY は安い (12.5円/個) から以外の意味はとくにない。ピン配置が GND IN OUT と 78* 系と違うので注意
Tiny13A は8pinなのでIOが最大で6pinしかない。そしてRESETをIOにするとISP書きこみができなくなるので、ISPを使うなら、実質5ピンのIOになる。
レポジトリ https://github.com/cho45/QCdirect
コードは Arduino 版とほぼ同じだが、AVR ネイティブで書きなおしてある。
main.cpp https://github.com/cho45/QCdirect/blob/master/firmware/main.cpp
ISP ピンヘッダの位置でいつも困る。AVRISP mkII の直で繋げられるようにしようとすると、意外と干渉するので、ピンヘッダまわりに十分スペースが必要
esptool が以下で死ぬ。
warning: espcomm_send_command: didn't receive command response warning: espcomm_send_command(FLASH_DOWNLOAD_DATA) failed warning: espcomm_send_command: wrong direction/command: 0x01 0x03, expected 0x01 0x04
シリアルダウンロードのモードにはなっているがうまくいかなかった。
書きこみに使う USB シリアル変換を別のもの(FT234X使用のもの)にしたらうまくいった。
使えなかったのはFT4232Hを使ったもので、このチップは4chを1つのUSBポートで通信できるものだが、どのチャンネルを使ってもダメだった。
ドライババージョンは現時点で最新
$ kextstat | grep FTDI 86 0 0xffffff7f80f6c000 0x7000 0x7000 com.FTDI.driver.FTDIUSBSerialDriver (2.3) ECC3AF36-431D-370D-86F2-5237785E9CF8 <85 39 5 4 3 1>
いまいち原因がわからない。
各チャンネルをループバックして screen を使って手動で通信テストする限りはうまく動いているようにみえる。壊れているわけではないっぽい。なぜこんなことになるのかわからない。
Over The Air で (すなわち Wifi 経由で)、ファームウェア書きかえをするやつ。
https://github.com/esp8266/Arduino/blob/master/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino
これの通りにすればコード的にはとりあえずうまくいく。
board は esp12e にしとかないとだめ。generic と書いてあるからといって、esp01 とか esp01_1m とかは flash のサイズが小さいので OTA_BEGIN_ERROR と言われる。
OTA の場合使える容量は半分以下になる。というのも、既存領域の余った部分にとりあえず書くからっぽい。
まずアドレスを mDNS からひいてくる (シリアルに表示させてもいいけど)
$ dns-sd -B _arduino._tcp Browsing for _arduino._tcp DATE: ---Wed 23 Dec 2015--- 17:49:14.912 ...STARTING... Timestamp A/R Flags if Domain Service Type Instance Name 17:49:14.913 Add 2 4 local. _arduino._tcp. esp8266-ee8488 $ dns-sd -G v4 esp8266-ee8488.local DATE: ---Wed 23 Dec 2015--- 17:48:48.955 ...STARTING... Timestamp A/R Flags if Hostname Address TTL 17:48:48.958 Add 2 4 esp8266-ee8488.local. 192.168.0.11 120
platform.ini の port に、この ip address を書いておく。これだけで自動的に OTA 経由と判別して書きこもうとする。
222K のbinファイル書きこみに4秒ぐらい。
$ ls -altrh .pioenvs/esp12e/firmware.bin
-rw-r--r--@ 1 cho45 staff 222K 12 23 21:05 .pioenvs/esp12e/firmware.bin
$ time "/Users/cho45/.platformio/packages/framework-arduinoespressif/tools/espota.py" --debug --progress -i 192.168.0.11 -f .pioenvs/esp12e/firmware.bin
18:55:58 [DEBUG]: Options: {'esp_ip': '192.168.0.11', 'image': '.pioenvs/esp12e/firmware.bin', 'auth': '', 'esp_port': 8266, 'spiffs': False, 'debug': True, 'progress': True}
18:55:58 [INFO]: Starting on 0.0.0.0:24984
18:55:58 [INFO]: Upload size: 227280
18:55:58 [INFO]: Sending invitation to: 192.168.0.11
18:55:58 [INFO]: Waiting for device...
Uploading: [============================================================] 100% Done...
18:56:02 [INFO]: Waiting for result...
18:56:02 [INFO]: Result: OK
"/Users/cho45/.platformio/packages/framework-arduinoespressif/tools/espota.py 0.06s user 0.05s system 2% cpu 4.417 total UART Download で書きこんだ直後の起動中に OTA 書きこみをした場合、ESP.restart() が失敗するっぽい。UART Download したあとは、一旦 reset してから OTA すればいいっぽい。
だいぶ忘れてたが、しばらくぶりにとりくんだらうまくいった。
こんな回路で
入出力DCスイープ
1石で非反転にしたいのでベース接地とした。ヘッドフォン出力を入力にすることを想定しているので入力インピーダンスはそれほど高くなくても良いだろう。この回路の入力インピーダンスは約100Ω(100Ωと20kΩの並列)
そして出力インピーダンスは100kとかなり大きいがデジタル入力なのでたぶん大丈夫だろう……
バイアスは0Vから飽和になるように選び、負の出力のときだけLOWに。
赤が入力(ヘッドフォン出力)、黄がUARTへの出力
9600baud 拡大
受信側はよくあるFTDI チップのUSBシリアル変換のもので、screen でデバイスファイルを指定して見ている。どのボーレートでも問題なく受信できた。
まだ実験的なページしか作っていない。これでだいたいうまくいきそうなので汎用的に使えるようにしたい。
これ以上簡単な回路にはならない気がする。
最初オーディオ出力がハイになるときにスパイクが出てしまう。これは出力に100pFぐらいつけたらだいぶ良くなるが、別段つけなくても問題はなさそう。
なお ASUS Zenfone2 で実験を行なった。他のデバイスだとうまくいかないケースがあるかもしれない。
3.3V 版もつくる。たぶん↓でよさそう