2015年9月28日月曜日

32bit CPU搭載インバータ改良

iPhone6s plus、カメラの性能すげええ

前回はとりあえず20khzで発振して、ボリュームに応じてデューティー比を変えるだけで32bit CPU Cotrtex-M0をまったく活かしておりませんでした。

今回は、少し仕事をしてもらっています…とはいえ、8bitどころか4bitで十分なレベルなのですが。

■仕様■

出力電圧をボリュームで設定できるように。

■実装■

メインループでは設定電圧と現在の電圧を測定し、その差でパルス幅を設定します。設定電圧以上なら出力をオフにし、設定電圧より低ければその差に応じてパルス幅を決めます。差が大きければ大きいほどパルス幅を広くしてインダクタンスに貯める電力を増やします。

その他、100m秒ごとに電圧設定のためのボリュームを読みに行きます。

■ハード■

前回はMOSFETとSBDの耐圧が限界となって電圧を上げられませんでした。今回は、耐圧500vのMOSFETと1000vのダイオード、それに400vの電解コンデンサを使います。

ただ、一つ問題があって、高耐圧のMOSFETはゲートがオンになる電圧も高く、今回使った2SK3234の場合は約5v以上必要です。そのため3.3vのLPC1114では直接ドライブできないので、小さなMOSFETを一段追加しそれでゲートを駆動します。電源電圧も5v以上必要です。何かすごーい遠回りをしているような気もしますが、気にしないで作ります。

あとどのぐらいの出力かを示すLEDと、出力電圧を測定するための分圧抵抗などを追加しました。

なおMOSFETが1個余計に入ったので、出力の論理が反転しています。つまり、mbedから「1」を出力するとパワーMOSFETがoffになります。そのためにPWMへの出力では 1.0 - width としてデューティー比を反転させています。


■mbedのPWM■

当初はボリュームでPWM周波数を変更できるようにしていました。インダクタンスを使っているので、どのぐらいの周波数でパルスを出力するのが一番効率が良いのかをさぐるためです。

で、100m秒ごとにボリュームからanalog inで読み取った値をもとにpwm.periodの値をセットしたのですが…これを行うとPwmOutの値を0にしていても100mSごとに出力がon / offする、つまり約5hzの太いパルスが出てしまいます。

設定電圧を0にしていても約40vの電圧が出てMOSFETがヤケに熱いので、何だろうと思ったのですが…ともかく回路が壊れなくて何よりです。

もう一つ、LEDをPWMで駆動しているのですが、当初まったく点灯しませんでした。これは、PWMの周波数がデフォルトのまま(約20m秒=50hz)なのにそれよりも早い周期でPWMへの書き込みを実行していたためです。ledに書き込みをしているループは1m秒で1回回りますので、それより十分早い10khzを出力するよう変更したところ、無事点灯しました。

いやはや…。

■動かしてみて■

設定電圧を急速に上げるとLEDが明るく点灯し、電圧が上昇して設定電圧に近づくとすーっと暗くなっていきます。逆に急に下げてやるとLEDはしばらく消灯した後、設定電圧に近づくとすっと点灯します。だいたい狙い通りです。強烈なノイズ発生源の近くにあるのでanalog inの値はそうとうバラついているはずで、安定しなければ移動平均処理を加えようかと思っていたのですが、なしでも安定してます。

時間積分効果とでも言いましょうかw

なお、こういう制御(PD)の場合、設定電圧よりも少し低いところで安定します。今回はボリュームを使っているので気にならないですが、電圧を数値で設定する場合にはそのための補正が必要です。

昔、Skycamという製品のためにモーターぶんまわし系の制御をしていた時にはずいぶん苦労したものです…(遠い目

なお、現在、出力値と電圧差の関係は単純な比例ですが、より安定かつスムーズな制御を行うためには二次関数式で比例させることもできます。つまり電圧差が少ないときには少しだけパルスを出して、差が大きい時はドカッと出すわけです(何その小学生なみの表現)。その先にはPID制御などが…ってキリがないw

電源電圧5vと9vで試しましたが、どちらも200vまできっちり上がりました。一応設定限度を200vにしているんですが、波形を見る限りもうちょっと上げられそうです。消費電流は多いですねぇ…9v電源から100vまで上げた時で70mA前後です。冬の太陽だと手持ちの0.5w太陽電池では無理で、2-5wぐらいのセルでないと安定して動かすのは難しいでしょうねぇ。大きいと風の影響なども考えないといけないし…ああどんどん違う方向へ(笑)。

■ソース■

特に珍しいことはしていません。このソースはフリーで公開します。つまり、何にどう使ってもいいですが、責任は問いませんよライセンスです。


#include "mbed.h"
#include "rtos.h"
AnalogIn vol(dp4);
AnalogIn mea(dp11);
AnalogIn fre(dp13);
PwmOut pwm(dp18);
PwmOut led(dp1);
float prevPwm = 0.0;
void WritePWM(float inWidth) {
led = inWidth * 0.5;
if (prevPwm != inWidth)
pwm = 1.0 - inWidth;
prevPwm = inWidth;
}
float TargetVoltage = 0.0;
void CheckVolumes(void const *val) {
TargetVoltage = vol * 200; // max 200v
}
int main() {
pwm.period(1.0 / 25000); // 周波数25khz
WritePWM(0.0);
led.period(1.0 / 10000); // LED10khz
RtosTimer checkTimer(CheckVolumes, osTimerPeriodic, NULL);
checkTimer.start(100);
while(1) {
float voltage = mea * 330; // 分圧比1/100、3.3vフルスケール
if (voltage > TargetVoltage) {
WritePWM(0.0);
} else {
float d = (TargetVoltage - voltage) * 0.005;
if (d < 0.0) {
d = 0.0;
} else {
if ( d > 0.8) {
d = 0.8;
}
}
WritePWM(d);
}
Thread::wait(1);
}
}
view raw ChopperRtos.cpp hosted with ❤ by GitHub

2015年9月26日土曜日

さっそく「スライドでアップグレード」がorz

まだ希望に満ちていたあのころ…
iPhone6s Plusが届いて小躍りしつつ5sから長い時間をかけてデータの移し替えが終わった喜びも束の間、噂の

「>スライドでアップグレード」

が出たきり何の操作もできなくなる状態がやってきましたorz

ここに対処方法がいろいろ書いてあるんですが、どれもiTunesがデバイスを認識してくれるのが前提。だいたい、パスワード忘れたわけじゃねぇよ、そこの画面までたどり着かないんだこの馬鹿野郎!と罵っていたら、終わりの方に「リカバリモードで復元する」って項目がありました。


「iTunes と同期したことがない場合や、iCloud で「iPhone を探す」を設定していない場合は、リカバリモードを使ってデバイスを復元する必要があります。この手順を実行すると、デバイスのデータとそのパスコードが消去されます。」とのことですが、明日まで待ってAppleStore行くよりマシ。

というわけで指示の通りに押しっぱなしにしていると「iTunesとつなげ画面」が出ました。何とかいけそうなので指示通り進めます。この画面になったまま戻らなくて不安ですが、とにかく待ちます。ピザなんか頼んでしまったので、食べながら待ちます。


上記リンクには「15分でタイムアウトするから、何回も2-3の手順を繰り返せって書いてあるのですが、むやみに繰り返しても無意味です。iTunesの右上にダウンロードアイコンが回っていますので、これをクリックしてみてください。2GB以上をダウンロードしているはずです。なので、これが終わってから(私の場合は4時間でした)もう一度、スリープボタンとホームボタンの両方をじーっと押してアップルマークが出て、そのあと消えて「iTunes→」の画面になるのを待ちます。そして、iTunesの画面で「復元」→「復元&アップデート」を選びます。「iPhoneソフトウェアの抽出中」などがiTunes上に表示され、数分後、空っぽで9.0.1になったiPhone6sがあなたの前に。

あとは、うんざりですけど、もう一度、同期したり設定したり、お好きなように。

帰宅してからここまで、えーと、6時間か…長かった。

2015年9月25日金曜日

ブレッドボードも消耗品、なの?

こんなにあるのに何故すぎ消えるのか…ルパン三世じゃあるまいし

ブレッドボードは実験が終わったら再利用できるので、そんなに買わなくても。

…はい、そう思っていた時期が私にもありました。もう何だかだで30枚ぐらいは買っていると思うんですが…足りないんです。でも、作りかけ収納箱を見ても、ブレッドボードはそんなに多くない。

まるで水が砂に吸い込まれるように消えて行く…。

種島先輩と休眠中のRPi2を従えるBBB


まぁよく見ると、普通に実運用しているブレッドボードが眼の隅に転がっていたりするんですけどね。そういうのはユニバーサルボードに移すかプリント基板発注しなきゃと思うんですが、なかなか…。

ただ、ブレッドボードがないと、あれを試したいこれを作りたい、と思った時に腰が重くなってしまうのは間違いありません。

というわけで、また5枚買いました。ジャンプワイヤも高いヤツを各色ごっそり買いました(でも青は買いません…老眼で黒と区別がつかねえんだよ)。

これでしばらくは大丈夫だろう…と思いたい。

関係ないけどiPhone6s Plus届きました。でかい…早くも後悔気味orz

2015年9月23日水曜日

まさにボトルネック

ADCとD級アンプの間の分圧抵抗をどこに入れよう…w

信号処理もどきを作ったのに、ADCがないので音を出して結果を確かめることができません。部品ぐらい買いに行けばいいんですが、膝を壊していて連休中はあまりでかけないで休むというコンセプトを忠実に守っているため、秋月にいや秋葉原にいけません。

せっかくMSP432P401Rで補聴器の着手できるめどが立ったのに…と思いつつ部品沼を整理していたら、一個だけMCP4725/DCコンバータが出てきました! 在庫リストにも記載されていないので、思いつきで買ってそのまま忘れていたのでしょうw

ということで、出力側の部品が揃いました。

秋月秋月秋月

ということで、まず今まで使ったことのないMCP4725を使ってみます。癖のないI2C仕様で割とすんなりつながった…のですが、何だか動作が不安定です。I2CのSDA線にオシロをつなぐとフリーズしてしまいます。基板裏のプルアップ用ジャンパは問題ないですし、オシロの入力インピーダンスはかなり高いので何か影響を及ぼすとは考えにくいのですが…ともかく、フリーズしていないことをLチカで確認することにして、だましだまし動かしてみます。

そして次に素の状態でMCP4725にどのぐらいの速度でデータを流してみようとして問題発覚。Energia/MSP432はI2Cの速度を切り替えられない。Arduinoだとライブラリのヘッダをちょっと書き換えれば400Khzまでは動くんですが、同じような箇所がMSP432版のドライバライブラリなどには見つかりません。i2c.cなんてソースに

    ASSERT(
            (EUSCI_B_I2C_SET_DATA_RATE_400KBPS == config->dataRate)
            || (EUSCI_B_I2C_SET_DATA_RATE_100KBPS == config->dataRate));

という記述はあるのですが、これを呼び出している側が見つからず、configもdefineも見つからず…。

しかたがないので、100khzで動かしてみましたが、1ワードの送信に3バイト必要+様々なオーバーヘッドで、毎秒約4000回≒2khzがやっと、という有様です。電話を遙かに下回り糸電話レベルの帯域orz

うーん、せめてI2cが400kで動けば16khzで出力できるので帯域は十分なのですが。

Energiaなんかで楽していないで、CMSIS使えってことですかね…ああ面倒くさい…。まぁ電子工作再開したばかりこのにLPC810にI2Cで液晶つないだりしてたので、できないわけじゃないんですけども…API調べたりするのが唯々面倒。


2015年9月22日火曜日

スペクトルアナライザー by MSP432P401R+Energia MT

ピンぼけじゃないもん影だもん(趣味:写真)

■Energia MT=マルチタスク■

EnergiaはArduino互換の開発環境ですが、MSP432P401R版はマルチタスクに対応しています。

マルチタスクというにはあまりにも単純でセマフォとかメールとかまったくありません。でも、今までのArduinoと比べれば格段に作りやすくなりました。

使い方は単純で

  • Energia IDEでタブを開き適当な名前を付ける
  • メイン同様setupとloop関数を用意するが、setupDisplay(), loopDisplay()のようにタスク名が後に続くようにするとsetup+loopのペアを別タスクとして認識する
  • メインに置いたヘッダやメイン上のGlobal変数は別タスクでも認識される

排他などについては厳密な排他制御は無理、暫定的にGlobal上にフラグを置いて自分で制御するしかありません。また、プリエンプションやタスク優先度がどうなっているのかも不明。その辺まったく情報がありません。まぁそのうち何とかなるでしょう。

ということで、とりあえず毎度お馴染みのスペクトルアナライザーをマルチタスクで作ってみました。

■材料

MSP432P401R、アンプ付きマイクAE-MICAMP、超小型グラフィック液晶AQM1284A、ブレッドボード、ジャンパ線…すべて秋月で揃います。合計4000円ぐらい? USBシリアルなども不要なのでArduino uno正規品を買うより安いです。

マイクはアナログ出力なのでドライバ等不要です。液晶についてはAQM1284AのコントローラIC ST7565RのライブラリをAdafruitさんが公開しているので、それを少し改造して使います。

FFTは大浦先生のライブラリそのままですが、M4F対応としてdoubleをfloatに置換して使用します。

以上です。

■配線■

マイクはGND, Vcc(5vが推奨されていますが出力電圧が大きくなりすぎるので3.3vにします)を接続し、OUTをEnergiaのP5.0に接続します。

AQM1284Aは以下の通りです。


AQM1284AEnergia MSP432P401R
VDD3V3
CSP3.0 / 18
RESETP2.5 / 19
RSP5.7 / 17
SCLKP1.5 / 7
SDIP1.6 / 15
GNDGND

■使い方■

各ソースは末尾に掲載します。長くてすみません。

Energiaは最新の16を使用してください。起動する前に、ST7565という名前のフォルダを作り、その中にST7565.cppとST7565.hを入れ、Energiaのlibraryフォルダに入れます。

Enegiaを起動したら、新しいプロジェクトを作り、FFTGraph_MTをコピペします。次に、ウィンドウ右上にある下向き三角をクリックして「新規タブ」を選び、TaskLCDという名前を付けてTaskLCDをコピペします(名前は何でも良いです)。

配線の点検をすませたらMSP432をマイクロUSBで接続し、Energiaでcmd+M、ビルドとプログラムの転送が終わるとグラフィック液晶に表示が出る…はずです。

なお、この作り方は厳密にいえば正しいマルチタスクではありません。TaskLCDが表示している最中にメインスレッドが表示バッファを書き換えてしまうこともありえます。でも、表示中にスペクトルが変わったとしても人間の目で追える変化ではないので、大目に見てやってください。

■毎度お馴染みのハマり■

Adafruitのライブラリの移植は、コンパイルエラーが出る→つぶす→ビルド→エラー→潰す…を淡々と繰り返すだけですが、それで終わらないのがコントラスト設定とpagemapでした。

コントラスト設定は秋月版との液晶の違いかAdafruit版だと真っ黒になってしまうのですが、Voltage Resistor Setを指定して回避できました。

pagemapはコントローラ上のページと液晶の表示位置を対応させるための表ですが、配布されているものと秋月のでは仕様が異なるようで、こんなのが出ました。左上隅から右下隅への直線がちょっと下にずれてます。64ドットで8ページ、たぶん2ページずれているのでしょう。


何度かの途中試行錯誤を経て

逆だ

なんだこれ

最終的にちゃんと表示が出ました。ここまで約1時間。



その次はマイクからの入力が常にゼロという現象が。そんなお行儀のいいアナログ入力はありえないのでしばらく頭を抱えていたのですが、これはanalogRead対応のピンにつないだつもりがanalogWriteのピンだった、というオチ。そりゃ値ゼロだわorz

FFTライブラリはつい最近使ったものなので問題なし。マルチタスク対応に関しては、奇跡的にも一発で動きました。何も難しいことしていないので当たり前ですけども、簡単なところで引っかかるのがこの私orz

でも、「自作補聴器」に今までで一番近づいたなぁ。すごいぞM4F、偉いぞM4F。

/*
$Id:$
ST7565 LCD library!
Copyright (C) 2010 Limor Fried, Adafruit Industries
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// some of this code was written by <cstone@pobox.com> originally; it is in the public domain.
// this source is modified for AQM1284 by kkurahashi
*/
#include <stdlib.h>
#include "ST7565.h"
#define ST7565_STARTBYTES 1
#define _BV(bit) (1 << (bit))
unsigned char is_reversed = 0;
// a handy reference to where the pages are on the screen
const unsigned char pagemap[] = { 5, 4, 3, 2, 1, 0, 7, 6};
// a 5x7 font table
const extern unsigned char font[];
// the memory buffer for the LCD
unsigned char st7565_buffer[1024] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0x7, 0xF, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x7, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x7F, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1F, 0x3F, 0x70, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x0, 0x0, 0x0, 0x3, 0x3,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0xF, 0x7, 0x7,
0x7, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x3F,
0x70, 0x60, 0x60, 0x60, 0x60, 0x30, 0x7F, 0x3F, 0x0, 0x0, 0x1F, 0x3F, 0x70, 0x60, 0x60, 0x60,
0x60, 0x39, 0xFF, 0xFF, 0x0, 0x6, 0x1F, 0x39, 0x60, 0x60, 0x60, 0x60, 0x30, 0x3F, 0x7F, 0x0,
0x0, 0x60, 0xFF, 0xFF, 0x60, 0x60, 0x0, 0x7F, 0x7F, 0x70, 0x60, 0x60, 0x40, 0x0, 0x7F, 0x7F,
0x0, 0x0, 0x0, 0x0, 0x7F, 0x7F, 0x0, 0x0, 0x0, 0x7F, 0x7F, 0x0, 0x0, 0x60, 0xFF, 0xFF,
0x60, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x80, 0xF8, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xE7, 0xE7, 0xE3,
0xF3, 0xF9, 0xFF, 0xFF, 0xFF, 0xF7, 0x7, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x0, 0x0, 0x0, 0xC0,
0xE0, 0x60, 0x20, 0x20, 0x60, 0xE0, 0xE0, 0xE0, 0x0, 0x0, 0x80, 0xC0, 0xE0, 0x60, 0x20, 0x60,
0x60, 0xE0, 0xE0, 0xE0, 0x0, 0x0, 0x80, 0xC0, 0x60, 0x60, 0x20, 0x60, 0x60, 0xE0, 0xE0, 0x0,
0x0, 0x0, 0xE0, 0xE0, 0x0, 0x0, 0x0, 0xE0, 0xE0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xE0,
0x60, 0x60, 0x60, 0x60, 0xE0, 0x80, 0x0, 0x0, 0x0, 0xE0, 0xE0, 0x0, 0x0, 0x0, 0xE0, 0xE0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0x7, 0x1F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF1, 0xE3,
0xE3, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFC, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x7F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF0, 0xE0, 0x80, 0x0, 0x0, 0x0, 0xC,
0x1C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x7F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1C, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xF8,
0xF8, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
0xFF, 0x0, 0x0, 0x0, 0xFF, 0xFF, 0xE0, 0xC0, 0xC0, 0xC0, 0xFF, 0x7F, 0x0, 0x0, 0x1E, 0x7F,
0xE1, 0xC0, 0xC0, 0xC0, 0xC0, 0x61, 0xFF, 0xFF, 0x0, 0x0, 0xFE, 0xFF, 0x1, 0x0, 0x0, 0x0,
0xFF, 0xFF, 0x0, 0x0, 0x21, 0xF9, 0xF8, 0xDC, 0xCC, 0xCF, 0x7, 0x0, 0xC0, 0xFF, 0xFF, 0xC0,
0x80, 0x0, 0xFF, 0xFF, 0xC0, 0xC0, 0x80, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0x1F, 0x7F, 0xF9,
0xC8, 0xC8, 0xC8, 0xC8, 0x79, 0x39, 0x0, 0x0, 0x71, 0xF9, 0xD8, 0xCC, 0xCE, 0x47, 0x3, 0x0,
0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0xC0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0,
0xC0, 0x0, 0x0, 0x0, 0xC0, 0xC0, 0x0, 0x0, 0x0, 0x0, 0xC0, 0xC0, 0x0, 0x0, 0x0, 0x80,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0xC0, 0xC0, 0x0, 0x0, 0x0, 0x80, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0x80, 0x0, 0x0, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x0, 0x0, 0x0, 0xC0, 0xC0, 0x0,
0x0, 0x0, 0xC0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0, 0xC0, 0x0, 0x0, 0x0, 0x80, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x0, 0x0, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,};
// reduces how much is refreshed, which speeds it up!
// originally derived from Steve Evans/JCW's mod but cleaned up and
// optimized
// #define enablePartialUpdate
#ifdef enablePartialUpdate
static unsigned char xUpdateMin, xUpdateMax, yUpdateMin, yUpdateMax;
#endif
static void updateBoundingBox(unsigned char xmin, unsigned char ymin, unsigned char xmax, unsigned char ymax) {
#ifdef enablePartialUpdate
if (xmin < xUpdateMin) xUpdateMin = xmin;
if (xmax > xUpdateMax) xUpdateMax = xmax;
if (ymin < yUpdateMin) yUpdateMin = ymin;
if (ymax > yUpdateMax) yUpdateMax = ymax;
#endif
}
void ST7565::drawbitmap(unsigned char x, unsigned char y,
const unsigned char *bitmap, unsigned char w, unsigned char h,
unsigned char color) {
for (unsigned char j=0; j<h; j++) {
for (unsigned char i=0; i<w; i++ ) {
if (pgm_read_byte(bitmap + i + (j/8)*w) & _BV(j%8)) {
my_setpixel(x+i, y+j, color);
}
}
}
updateBoundingBox(x, y, x+w, y+h);
}
void ST7565::drawstring(unsigned char x, unsigned char line, char *c) {
while (c[0] != 0) {
drawchar(x, line, c[0]);
c++;
x += 6; // 6 pixels wide
if (x + 6 >= LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void ST7565::drawstring_P(unsigned char x, unsigned char line, const char *str) {
while (1) {
char c = pgm_read_byte(str++);
if (! c)
return;
drawchar(x, line, c);
x += 6; // 6 pixels wide
if (x + 6 >= LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void ST7565::drawchar(unsigned char x, unsigned char line, char c) {
for (unsigned char i =0; i<5; i++ ) {
st7565_buffer[x + (line*128) ] = pgm_read_byte(font+(c*5)+i);
x++;
}
updateBoundingBox(x, line*8, x+5, line*8 + 8);
}
// bresenham's algorithm - thx wikpedia
void ST7565::drawline(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1,
unsigned char color) {
unsigned char steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap(x0, y0);
swap(x1, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
// much faster to put the test here, since we've already sorted the points
updateBoundingBox(x0, y0, x1, y1);
unsigned char dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int8_t err = dx / 2;
int8_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;}
for (; x0<=x1; x0++) {
if (steep) {
my_setpixel(y0, x0, color);
} else {
my_setpixel(x0, y0, color);
}
err -= dy;
if (err < 0) {
y0 += ystep;
err += dx;
}
}
}
// filled rectangle
void ST7565::fillrect(unsigned char x, unsigned char y, unsigned char w, unsigned char h,
unsigned char color) {
// stupidest version - just pixels - but fast with internal buffer!
for (unsigned char i=x; i<x+w; i++) {
for (unsigned char j=y; j<y+h; j++) {
my_setpixel(i, j, color);
}
}
updateBoundingBox(x, y, x+w, y+h);
}
// draw a rectangle
void ST7565::drawrect(unsigned char x, unsigned char y, unsigned char w, unsigned char h,
unsigned char color) {
// stupidest version - just pixels - but fast with internal buffer!
for (unsigned char i=x; i<x+w; i++) {
my_setpixel(i, y, color);
my_setpixel(i, y+h-1, color);
}
for (unsigned char i=y; i<y+h; i++) {
my_setpixel(x, i, color);
my_setpixel(x+w-1, i, color);
}
updateBoundingBox(x, y, x+w, y+h);
}
// draw a circle outline
void ST7565::drawcircle(unsigned char x0, unsigned char y0, unsigned char r,
unsigned char color) {
updateBoundingBox(x0-r, y0-r, x0+r, y0+r);
int8_t f = 1 - r;
int8_t ddF_x = 1;
int8_t ddF_y = -2 * r;
int8_t x = 0;
int8_t y = r;
my_setpixel(x0, y0+r, color);
my_setpixel(x0, y0-r, color);
my_setpixel(x0+r, y0, color);
my_setpixel(x0-r, y0, color);
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
my_setpixel(x0 + x, y0 + y, color);
my_setpixel(x0 - x, y0 + y, color);
my_setpixel(x0 + x, y0 - y, color);
my_setpixel(x0 - x, y0 - y, color);
my_setpixel(x0 + y, y0 + x, color);
my_setpixel(x0 - y, y0 + x, color);
my_setpixel(x0 + y, y0 - x, color);
my_setpixel(x0 - y, y0 - x, color);
}
}
void ST7565::fillcircle(unsigned char x0, unsigned char y0, unsigned char r,
unsigned char color) {
updateBoundingBox(x0-r, y0-r, x0+r, y0+r);
int8_t f = 1 - r;
int8_t ddF_x = 1;
int8_t ddF_y = -2 * r;
int8_t x = 0;
int8_t y = r;
for (unsigned char i=y0-r; i<=y0+r; i++) {
my_setpixel(x0, i, color);
}
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
for (unsigned char i=y0-y; i<=y0+y; i++) {
my_setpixel(x0+x, i, color);
my_setpixel(x0-x, i, color);
}
for (unsigned char i=y0-x; i<=y0+x; i++) {
my_setpixel(x0+y, i, color);
my_setpixel(x0-y, i, color);
}
}
}
void ST7565::my_setpixel(unsigned char x, unsigned char y, unsigned char color) {
if ((x >= LCDWIDTH) || (y >= LCDHEIGHT))
return;
// x is which column
if (color)
st7565_buffer[x+ (y/8)*128] |= _BV(7-(y%8));
else
st7565_buffer[x+ (y/8)*128] &= ~_BV(7-(y%8));
}
// the most basic function, set a single pixel
void ST7565::setpixel(unsigned char x, unsigned char y, unsigned char color) {
if ((x >= LCDWIDTH) || (y >= LCDHEIGHT))
return;
// x is which column
if (color)
st7565_buffer[x+ (y/8)*128] |= _BV(7-(y%8));
else
st7565_buffer[x+ (y/8)*128] &= ~_BV(7-(y%8));
updateBoundingBox(x,y,x,y);
}
// the most basic function, get a single pixel
unsigned char ST7565::getpixel(unsigned char x, unsigned char y) {
if ((x >= LCDWIDTH) || (y >= LCDHEIGHT))
return 0;
return (st7565_buffer[x+ (y/8)*128] >> (7-(y%8))) & 0x1;
}
void ST7565::begin(unsigned char contrast) {
st7565_init();
st7565_command(CMD_DISPLAY_ON);
st7565_command(CMD_SET_ALLPTS_NORMAL);
st7565_set_brightness(contrast);
}
void ST7565::st7565_init(void) {
// set pin directions
pinMode(sid, OUTPUT);
pinMode(sclk, OUTPUT);
pinMode(a0, OUTPUT);
pinMode(rst, OUTPUT);
pinMode(cs, OUTPUT);
// toggle RST low to reset; CS low so it'll listen to us
if (cs > 0)
digitalWrite(cs, LOW);
digitalWrite(rst, LOW);
delay(500);
digitalWrite(rst, HIGH);
// LCD bias select
st7565_command(CMD_SET_BIAS_7);
// ADC select
st7565_command(CMD_SET_ADC_NORMAL);
// SHL select
st7565_command(CMD_SET_COM_NORMAL);
// Initial display line
st7565_command(CMD_SET_DISP_START_LINE);
// turn on voltage converter (VC=1, VR=0, VF=0)
st7565_command(CMD_SET_POWER_CONTROL | 0x4);
// wait for 50% rising
delay(50);
// turn on voltage regulator (VC=1, VR=1, VF=0)
st7565_command(CMD_SET_POWER_CONTROL | 0x6);
// wait >=50ms
delay(50);
// turn on voltage follower (VC=1, VR=1, VF=1)
st7565_command(CMD_SET_POWER_CONTROL | 0x7);
// wait
delay(10);
// set lcd operating voltage (regulator resistor, ref voltage resistor)
st7565_command(CMD_SET_RESISTOR_RATIO | 0x6);
// initial display line
// set page address
// set column address
// write display data
// set up a bounding box for screen updates
updateBoundingBox(0, 0, LCDWIDTH-1, LCDHEIGHT-1);
}
inline void ST7565::spiwrite(unsigned char c) {
shiftOut(sid, sclk, MSBFIRST, c);
/*
int8_t i;
for (i=7; i>=0; i--) {
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(i))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
}
*/
/*
// loop unwrapped! too fast doesnt work :(
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(7))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(6))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(5))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(4))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(3))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(2))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(1))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
SCLK_PORT &= ~_BV(SCLK);
if (c & _BV(0))
SID_PORT |= _BV(SID);
else
SID_PORT &= ~_BV(SID);
SCLK_PORT |= _BV(SCLK);
*/
}
void ST7565::st7565_command(unsigned char c) {
digitalWrite(a0, LOW);
spiwrite(c);
}
void ST7565::st7565_data(unsigned char c) {
digitalWrite(a0, HIGH);
spiwrite(c);
}
void ST7565::st7565_set_brightness(unsigned char val) {
st7565_command(0x23); // Vo voltage resistor ratio set
st7565_command(CMD_SET_VOLUME_FIRST);
st7565_command(CMD_SET_VOLUME_SECOND | (val & 0x3f));
}
void ST7565::display(void) {
unsigned char col, maxcol, p;
/*
Serial.print("Refresh ("); Serial.print(xUpdateMin, DEC);
Serial.print(", "); Serial.print(xUpdateMax, DEC);
Serial.print(","); Serial.print(yUpdateMin, DEC);
Serial.print(", "); Serial.print(yUpdateMax, DEC); Serial.println(")");
*/
for(p = 0; p < 8; p++) {
/*
putstring("new page! ");
uart_putw_dec(p);
putstring_nl("");
*/
#ifdef enablePartialUpdate
// check if this page is part of update
if ( yUpdateMin >= ((p+1)*8) ) {
continue; // nope, skip it!
}
if (yUpdateMax < p*8) {
break;
}
#endif
st7565_command(CMD_SET_PAGE | pagemap[p]);
#ifdef enablePartialUpdate
col = xUpdateMin;
maxcol = xUpdateMax;
#else
// start at the beginning of the row
col = 0;
maxcol = LCDWIDTH-1;
#endif
st7565_command(CMD_SET_COLUMN_LOWER | ((col+ST7565_STARTBYTES) & 0xf));
st7565_command(CMD_SET_COLUMN_UPPER | (((col+ST7565_STARTBYTES) >> 4) & 0x0F));
st7565_command(CMD_RMW);
for(; col <= maxcol; col++) {
//uart_putw_dec(col);
//uart_putchar(' ');
st7565_data(st7565_buffer[(128*p)+col]);
}
}
#ifdef enablePartialUpdate
xUpdateMin = LCDWIDTH - 1;
xUpdateMax = 0;
yUpdateMin = LCDHEIGHT-1;
yUpdateMax = 0;
#endif
}
// clear everything
void ST7565::clear(void) {
memset(st7565_buffer, 0, 1024);
updateBoundingBox(0, 0, LCDWIDTH-1, LCDHEIGHT-1);
}
// this doesnt touch the buffer, just clears the display RAM - might be handy
void ST7565::clear_display(void) {
unsigned char p, c;
for(p = 0; p < 8; p++) {
/*
putstring("new page! ");
uart_putw_dec(p);
putstring_nl("");
*/
st7565_command(CMD_SET_PAGE | p);
for(c = 0; c < 129; c++) {
//uart_putw_dec(c);
//uart_putchar(' ');
st7565_command(CMD_SET_COLUMN_LOWER | (c & 0xf));
st7565_command(CMD_SET_COLUMN_UPPER | ((c >> 4) & 0xf));
st7565_data(0x0);
}
}
}
view raw ST7565.cpp hosted with ❤ by GitHub

/*
$Id:$
ST7565 LCD library!
Copyright (C) 2010 Limor Fried, Adafruit Industries
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// some of this code was written by <cstone@pobox.com> originally; it is in the public domain.
// this source is modified for AQM1284 by kkurahahsi
*/
#include "Arduino.h"
#define swap(a, b) { unsigned char t = a; a = b; b = t; }
#define BLACK 1
#define WHITE 0
#define LCDWIDTH 128
#define LCDHEIGHT 48
#define CMD_DISPLAY_OFF 0xAE
#define CMD_DISPLAY_ON 0xAF
#define CMD_SET_DISP_START_LINE 0x40
#define CMD_SET_PAGE 0xB0
#define CMD_SET_COLUMN_UPPER 0x10
#define CMD_SET_COLUMN_LOWER 0x00
#define CMD_SET_ADC_NORMAL 0xA0
#define CMD_SET_ADC_REVERSE 0xA1
#define CMD_SET_DISP_NORMAL 0xA6
#define CMD_SET_DISP_REVERSE 0xA7
#define CMD_SET_ALLPTS_NORMAL 0xA4
#define CMD_SET_ALLPTS_ON 0xA5
#define CMD_SET_BIAS_9 0xA2
#define CMD_SET_BIAS_7 0xA3
#define CMD_RMW 0xE0
#define CMD_RMW_CLEAR 0xEE
#define CMD_INTERNAL_RESET 0xE2
#define CMD_SET_COM_NORMAL 0xC0
#define CMD_SET_COM_REVERSE 0xC8
#define CMD_SET_POWER_CONTROL 0x28
#define CMD_SET_RESISTOR_RATIO 0x20
#define CMD_SET_VOLUME_FIRST 0x81
#define CMD_SET_VOLUME_SECOND 0
#define CMD_SET_STATIC_OFF 0xAC
#define CMD_SET_STATIC_ON 0xAD
#define CMD_SET_STATIC_REG 0x0
#define CMD_SET_BOOSTER_FIRST 0xF8
#define CMD_SET_BOOSTER_234 0
#define CMD_SET_BOOSTER_5 1
#define CMD_SET_BOOSTER_6 3
#define CMD_NOP 0xE3
#define CMD_TEST 0xF0
class ST7565 {
public:
ST7565(int8_t SID, int8_t SCLK, int8_t A0, int8_t RST, int8_t CS) :sid(SID), sclk(SCLK), a0(A0), rst(RST), cs(CS) {}
ST7565(int8_t SID, int8_t SCLK, int8_t A0, int8_t RST) :sid(SID), sclk(SCLK), a0(A0), rst(RST), cs(-1) {}
void st7565_init(void);
void begin(unsigned char contrast);
void st7565_command(unsigned char c);
void st7565_data(unsigned char c);
void st7565_set_brightness(unsigned char val);
void clear_display(void);
void clear();
void display();
void setpixel(unsigned char x, unsigned char y, unsigned char color);
unsigned char getpixel(unsigned char x, unsigned char y);
void fillcircle(unsigned char x0, unsigned char y0, unsigned char r,
unsigned char color);
void drawcircle(unsigned char x0, unsigned char y0, unsigned char r,
unsigned char color);
void drawrect(unsigned char x, unsigned char y, unsigned char w, unsigned char h,
unsigned char color);
void fillrect(unsigned char x, unsigned char y, unsigned char w, unsigned char h,
unsigned char color);
void drawline(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1,
unsigned char color);
void drawchar(unsigned char x, unsigned char line, char c);
void drawstring(unsigned char x, unsigned char line, char *c);
void drawstring_P(unsigned char x, unsigned char line, const char *c);
void drawbitmap(unsigned char x, unsigned char y,
const unsigned char *bitmap, unsigned char w, unsigned char h,
unsigned char color);
private:
int8_t sid, sclk, a0, rst, cs;
void spiwrite(unsigned char c);
void my_setpixel(unsigned char x, unsigned char y, unsigned char color);
//unsigned char buffer[128*64/8];
};
view raw ST7565.h hosted with ❤ by GitHub

#include <ST7565.h>
#define NMAX 256
#define NMAX_HALF (NMAX/2)
#define NMAXSQRT 16
#define MAX(x,y) ((x) > (y) ? (x) : (y))
// fft buffer / workarea
float micBuffer[NMAX+1];
float w[NMAX * 5 / 4+1];
int ip[NMAXSQRT + 2];
void rdft(int n, int isgn, float *a, int *ip, float *w);
#define WAIT_FOR_SAMPLING (1000/32) // 32khz sampling
// Microphone ADC port
#define MIC 13
// LCD
byte LCDBuffer[LCDWIDTH];
int FFTCounter;
// Generate Test data
void putdata(int n, float *a)
{
int j;
float pi2 = 3.14159265*2 / n;
for (j = 0; j <n; j++) {
a[j] = sin(j*pi2*10.0)*10.0 + sin(j*pi2*15.0)*5.0 + sin(j*pi2*20.0)*10.0 + 25.0;
}
}
void setup()
{
Serial.begin(115200);
FFTCounter = 0;
ip[0] = 0;
}
void loop()
{
// sprint buffer
char dispbuf[20];
// time measurement
unsigned long t = millis();
// sampling from microphone
for (int i = 0; i < NMAX; i++) {
int a = analogRead(MIC) - 400;
micBuffer[i] = a;
delayMicroseconds(WAIT_FOR_SAMPLING);
}
// putdata(NMAX, micBuffer);
unsigned long d = millis() - t;
Serial.print("read = ");
Serial.print(d);
// FFT
t = millis();
// ip[0] = 0;
rdft(NMAX, 1, micBuffer, ip, w);
// rdft(n, -1, micBuffer, ip, w);
FFTCounter ++;
if (FFTCounter > 9999) FFTCounter = 0;
d = millis() - t;
Serial.print(", calc = ");
Serial.print(d);
// calc maximum value
t = millis();
float max = 0;
for (int i = 0; i < NMAX_HALF; i++) {
int i2 = i*2;
micBuffer[i] = micBuffer[i2]*micBuffer[i2] + micBuffer[i2+1]*micBuffer[i2+1];
if (i > 0 && micBuffer[i] > max) max = micBuffer[i];
}
// set display data
max = LCDHEIGHT / max;
for (int i = 1; i < NMAX_HALF; i++) {
int y = LCDHEIGHT-max*micBuffer[i];
if (y < 0) y = 0;
LCDBuffer[i] = y;
}
Serial.println();
}
/*
Fast Fourier/Cosine/Sine Transform
dimension :one
data length :power of 2
decimation :frequency
radix :4, 2
data :inplace
table :use
functions
cdft: Complex Discrete Fourier Transform
rdft: Real Discrete Fourier Transform
ddct: Discrete Cosine Transform
ddst: Discrete Sine Transform
dfct: Cosine Transform of RDFT (Real Symmetric DFT)
dfst: Sine Transform of RDFT (Real Anti-symmetric DFT)
function prototypes
void cdft(int, int, float *, int *, float *);
void rdft(int, int, float *, int *, float *);
void ddct(int, int, float *, int *, float *);
void ddst(int, int, float *, int *, float *);
void dfct(int, float *, float *, int *, float *);
void dfst(int, float *, float *, int *, float *);
-------- Complex DFT (Discrete Fourier Transform) --------
[definition]
<case1>
X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k<n
<case2>
X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k<n
(notes: sum_j=0^n-1 is a summation from j=0 to n-1)
[usage]
<case1>
ip[0] = 0; // first time only
cdft(2*n, 1, a, ip, w);
<case2>
ip[0] = 0; // first time only
cdft(2*n, -1, a, ip, w);
[parameters]
2*n :data length (int)
n >= 1, n = power of 2
a[0...2*n-1] :input/output data (float *)
input data
a[2*j] = Re(x[j]),
a[2*j+1] = Im(x[j]), 0<=j<n
output data
a[2*k] = Re(X[k]),
a[2*k+1] = Im(X[k]), 0<=k<n
ip[0...*] :work area for bit reversal (int *)
length of ip >= 2+sqrt(n)
strictly,
length of ip >=
2+(1<<(int)(log(n+0.5)/log(2))/2).
ip[0],ip[1] are pointers of the cos/sin table.
w[0...n/2-1] :cos/sin table (float *)
w[],ip[] are initialized if ip[0] == 0.
[remark]
Inverse of
cdft(2*n, -1, a, ip, w);
is
cdft(2*n, 1, a, ip, w);
for (j = 0; j <= 2 * n - 1; j++) {
a[j] *= 1.0 / n;
}
.
-------- Real DFT / Inverse of Real DFT --------
[definition]
<case1> RDFT
R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2
I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2
<case2> IRDFT (excluding scale)
a[k] = (R[0] + R[n/2]*cos(pi*k))/2 +
sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) +
sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n
[usage]
<case1>
ip[0] = 0; // first time only
rdft(n, 1, a, ip, w);
<case2>
ip[0] = 0; // first time only
rdft(n, -1, a, ip, w);
[parameters]
n :data length (int)
n >= 2, n = power of 2
a[0...n-1] :input/output data (float *)
<case1>
output data
a[2*k] = R[k], 0<=k<n/2
a[2*k+1] = I[k], 0<k<n/2
a[1] = R[n/2]
<case2>
input data
a[2*j] = R[j], 0<=j<n/2
a[2*j+1] = I[j], 0<j<n/2
a[1] = R[n/2]
ip[0...*] :work area for bit reversal (int *)
length of ip >= 2+sqrt(n/2)
strictly,
length of ip >=
2+(1<<(int)(log(n/2+0.5)/log(2))/2).
ip[0],ip[1] are pointers of the cos/sin table.
w[0...n/2-1] :cos/sin table (float *)
w[],ip[] are initialized if ip[0] == 0.
[remark]
Inverse of
rdft(n, 1, a, ip, w);
is
rdft(n, -1, a, ip, w);
for (j = 0; j <= n - 1; j++) {
a[j] *= 2.0 / n;
}
.
-------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
[definition]
<case1> IDCT (excluding scale)
C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k<n
<case2> DCT
C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k<n
[usage]
<case1>
ip[0] = 0; // first time only
ddct(n, 1, a, ip, w);
<case2>
ip[0] = 0; // first time only
ddct(n, -1, a, ip, w);
[parameters]
n :data length (int)
n >= 2, n = power of 2
a[0...n-1] :input/output data (float *)
output data
a[k] = C[k], 0<=k<n
ip[0...*] :work area for bit reversal (int *)
length of ip >= 2+sqrt(n/2)
strictly,
length of ip >=
2+(1<<(int)(log(n/2+0.5)/log(2))/2).
ip[0],ip[1] are pointers of the cos/sin table.
w[0...n*5/4-1] :cos/sin table (float *)
w[],ip[] are initialized if ip[0] == 0.
[remark]
Inverse of
ddct(n, -1, a, ip, w);
is
a[0] *= 0.5;
ddct(n, 1, a, ip, w);
for (j = 0; j <= n - 1; j++) {
a[j] *= 2.0 / n;
}
.
-------- DST (Discrete Sine Transform) / Inverse of DST --------
[definition]
<case1> IDST (excluding scale)
S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k<n
<case2> DST
S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0<k<=n
[usage]
<case1>
ip[0] = 0; // first time only
ddst(n, 1, a, ip, w);
<case2>
ip[0] = 0; // first time only
ddst(n, -1, a, ip, w);
[parameters]
n :data length (int)
n >= 2, n = power of 2
a[0...n-1] :input/output data (float *)
<case1>
input data
a[j] = A[j], 0<j<n
a[0] = A[n]
output data
a[k] = S[k], 0<=k<n
<case2>
output data
a[k] = S[k], 0<k<n
a[0] = S[n]
ip[0...*] :work area for bit reversal (int *)
length of ip >= 2+sqrt(n/2)
strictly,
length of ip >=
2+(1<<(int)(log(n/2+0.5)/log(2))/2).
ip[0],ip[1] are pointers of the cos/sin table.
w[0...n*5/4-1] :cos/sin table (float *)
w[],ip[] are initialized if ip[0] == 0.
[remark]
Inverse of
ddst(n, -1, a, ip, w);
is
a[0] *= 0.5;
ddst(n, 1, a, ip, w);
for (j = 0; j <= n - 1; j++) {
a[j] *= 2.0 / n;
}
.
-------- Cosine Transform of RDFT (Real Symmetric DFT) --------
[definition]
C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n
[usage]
ip[0] = 0; // first time only
dfct(n, a, t, ip, w);
[parameters]
n :data length - 1 (int)
n >= 2, n = power of 2
a[0...n] :input/output data (float *)
output data
a[k] = C[k], 0<=k<=n
t[0...n/2] :work area (float *)
ip[0...*] :work area for bit reversal (int *)
length of ip >= 2+sqrt(n/4)
strictly,
length of ip >=
2+(1<<(int)(log(n/4+0.5)/log(2))/2).
ip[0],ip[1] are pointers of the cos/sin table.
w[0...n*5/8-1] :cos/sin table (float *)
w[],ip[] are initialized if ip[0] == 0.
[remark]
Inverse of
a[0] *= 0.5;
a[n] *= 0.5;
dfct(n, a, t, ip, w);
is
a[0] *= 0.5;
a[n] *= 0.5;
dfct(n, a, t, ip, w);
for (j = 0; j <= n; j++) {
a[j] *= 2.0 / n;
}
.
-------- Sine Transform of RDFT (Real Anti-symmetric DFT) --------
[definition]
S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n
[usage]
ip[0] = 0; // first time only
dfst(n, a, t, ip, w);
[parameters]
n :data length + 1 (int)
n >= 2, n = power of 2
a[0...n-1] :input/output data (float *)
output data
a[k] = S[k], 0<k<n
(a[0] is used for work area)
t[0...n/2-1] :work area (float *)
ip[0...*] :work area for bit reversal (int *)
length of ip >= 2+sqrt(n/4)
strictly,
length of ip >=
2+(1<<(int)(log(n/4+0.5)/log(2))/2).
ip[0],ip[1] are pointers of the cos/sin table.
w[0...n*5/8-1] :cos/sin table (float *)
w[],ip[] are initialized if ip[0] == 0.
[remark]
Inverse of
dfst(n, a, t, ip, w);
is
dfst(n, a, t, ip, w);
for (j = 1; j <= n - 1; j++) {
a[j] *= 2.0 / n;
}
.
Appendix :
The cos/sin table is recalculated when the larger table required.
w[] and ip[] are compatible with all routines.
*/
void cdft(int n, int isgn, float *a, int *ip, float *w)
{
void makewt(int nw, int *ip, float *w);
void bitrv2(int n, int *ip, float *a);
void bitrv2conj(int n, int *ip, float *a);
void cftfsub(int n, float *a, float *w);
void cftbsub(int n, float *a, float *w);
if (n > (ip[0] << 2)) {
makewt(n >> 2, ip, w);
}
if (n > 4) {
if (isgn >= 0) {
bitrv2(n, ip + 2, a);
cftfsub(n, a, w);
}
else {
bitrv2conj(n, ip + 2, a);
cftbsub(n, a, w);
}
}
else if (n == 4) {
cftfsub(n, a, w);
}
}
void rdft(int n, int isgn, float *a, int *ip, float *w)
{
void makewt(int nw, int *ip, float *w);
void makect(int nc, int *ip, float *c);
void bitrv2(int n, int *ip, float *a);
void cftfsub(int n, float *a, float *w);
void cftbsub(int n, float *a, float *w);
void rftfsub(int n, float *a, int nc, float *c);
void rftbsub(int n, float *a, int nc, float *c);
int nw, nc;
float xi;
nw = ip[0];
if (n > (nw << 2)) {
nw = n >> 2;
makewt(nw, ip, w);
}
nc = ip[1];
if (n > (nc << 2)) {
nc = n >> 2;
makect(nc, ip, w + nw);
}
if (isgn >= 0) {
if (n > 4) {
bitrv2(n, ip + 2, a);
cftfsub(n, a, w);
rftfsub(n, a, nc, w + nw);
}
else if (n == 4) {
cftfsub(n, a, w);
}
xi = a[0] - a[1];
a[0] += a[1];
a[1] = xi;
}
else {
a[1] = 0.5 * (a[0] - a[1]);
a[0] -= a[1];
if (n > 4) {
rftbsub(n, a, nc, w + nw);
bitrv2(n, ip + 2, a);
cftbsub(n, a, w);
}
else if (n == 4) {
cftfsub(n, a, w);
}
}
}
void ddct(int n, int isgn, float *a, int *ip, float *w)
{
void makewt(int nw, int *ip, float *w);
void makect(int nc, int *ip, float *c);
void bitrv2(int n, int *ip, float *a);
void cftfsub(int n, float *a, float *w);
void cftbsub(int n, float *a, float *w);
void rftfsub(int n, float *a, int nc, float *c);
void rftbsub(int n, float *a, int nc, float *c);
void dctsub(int n, float *a, int nc, float *c);
int j, nw, nc;
float xr;
nw = ip[0];
if (n > (nw << 2)) {
nw = n >> 2;
makewt(nw, ip, w);
}
nc = ip[1];
if (n > nc) {
nc = n;
makect(nc, ip, w + nw);
}
if (isgn < 0) {
xr = a[n - 1];
for (j = n - 2; j >= 2; j -= 2) {
a[j + 1] = a[j] - a[j - 1];
a[j] += a[j - 1];
}
a[1] = a[0] - xr;
a[0] += xr;
if (n > 4) {
rftbsub(n, a, nc, w + nw);
bitrv2(n, ip + 2, a);
cftbsub(n, a, w);
}
else if (n == 4) {
cftfsub(n, a, w);
}
}
dctsub(n, a, nc, w + nw);
if (isgn >= 0) {
if (n > 4) {
bitrv2(n, ip + 2, a);
cftfsub(n, a, w);
rftfsub(n, a, nc, w + nw);
}
else if (n == 4) {
cftfsub(n, a, w);
}
xr = a[0] - a[1];
a[0] += a[1];
for (j = 2; j < n; j += 2) {
a[j - 1] = a[j] - a[j + 1];
a[j] += a[j + 1];
}
a[n - 1] = xr;
}
}
void ddst(int n, int isgn, float *a, int *ip, float *w)
{
void makewt(int nw, int *ip, float *w);
void makect(int nc, int *ip, float *c);
void bitrv2(int n, int *ip, float *a);
void cftfsub(int n, float *a, float *w);
void cftbsub(int n, float *a, float *w);
void rftfsub(int n, float *a, int nc, float *c);
void rftbsub(int n, float *a, int nc, float *c);
void dstsub(int n, float *a, int nc, float *c);
int j, nw, nc;
float xr;
nw = ip[0];
if (n > (nw << 2)) {
nw = n >> 2;
makewt(nw, ip, w);
}
nc = ip[1];
if (n > nc) {
nc = n;
makect(nc, ip, w + nw);
}
if (isgn < 0) {
xr = a[n - 1];
for (j = n - 2; j >= 2; j -= 2) {
a[j + 1] = -a[j] - a[j - 1];
a[j] -= a[j - 1];
}
a[1] = a[0] + xr;
a[0] -= xr;
if (n > 4) {
rftbsub(n, a, nc, w + nw);
bitrv2(n, ip + 2, a);
cftbsub(n, a, w);
}
else if (n == 4) {
cftfsub(n, a, w);
}
}
dstsub(n, a, nc, w + nw);
if (isgn >= 0) {
if (n > 4) {
bitrv2(n, ip + 2, a);
cftfsub(n, a, w);
rftfsub(n, a, nc, w + nw);
}
else if (n == 4) {
cftfsub(n, a, w);
}
xr = a[0] - a[1];
a[0] += a[1];
for (j = 2; j < n; j += 2) {
a[j - 1] = -a[j] - a[j + 1];
a[j] -= a[j + 1];
}
a[n - 1] = -xr;
}
}
void dfct(int n, float *a, float *t, int *ip, float *w)
{
void makewt(int nw, int *ip, float *w);
void makect(int nc, int *ip, float *c);
void bitrv2(int n, int *ip, float *a);
void cftfsub(int n, float *a, float *w);
void rftfsub(int n, float *a, int nc, float *c);
void dctsub(int n, float *a, int nc, float *c);
int j, k, l, m, mh, nw, nc;
float xr, xi, yr, yi;
nw = ip[0];
if (n > (nw << 3)) {
nw = n >> 3;
makewt(nw, ip, w);
}
nc = ip[1];
if (n > (nc << 1)) {
nc = n >> 1;
makect(nc, ip, w + nw);
}
m = n >> 1;
yi = a[m];
xi = a[0] + a[n];
a[0] -= a[n];
t[0] = xi - yi;
t[m] = xi + yi;
if (n > 2) {
mh = m >> 1;
for (j = 1; j < mh; j++) {
k = m - j;
xr = a[j] - a[n - j];
xi = a[j] + a[n - j];
yr = a[k] - a[n - k];
yi = a[k] + a[n - k];
a[j] = xr;
a[k] = yr;
t[j] = xi - yi;
t[k] = xi + yi;
}
t[mh] = a[mh] + a[n - mh];
a[mh] -= a[n - mh];
dctsub(m, a, nc, w + nw);
if (m > 4) {
bitrv2(m, ip + 2, a);
cftfsub(m, a, w);
rftfsub(m, a, nc, w + nw);
}
else if (m == 4) {
cftfsub(m, a, w);
}
a[n - 1] = a[0] - a[1];
a[1] = a[0] + a[1];
for (j = m - 2; j >= 2; j -= 2) {
a[2 * j + 1] = a[j] + a[j + 1];
a[2 * j - 1] = a[j] - a[j + 1];
}
l = 2;
m = mh;
while (m >= 2) {
dctsub(m, t, nc, w + nw);
if (m > 4) {
bitrv2(m, ip + 2, t);
cftfsub(m, t, w);
rftfsub(m, t, nc, w + nw);
}
else if (m == 4) {
cftfsub(m, t, w);
}
a[n - l] = t[0] - t[1];
a[l] = t[0] + t[1];
k = 0;
for (j = 2; j < m; j += 2) {
k += l << 2;
a[k - l] = t[j] - t[j + 1];
a[k + l] = t[j] + t[j + 1];
}
l <<= 1;
mh = m >> 1;
for (j = 0; j < mh; j++) {
k = m - j;
t[j] = t[m + k] - t[m + j];
t[k] = t[m + k] + t[m + j];
}
t[mh] = t[m + mh];
m = mh;
}
a[l] = t[0];
a[n] = t[2] - t[1];
a[0] = t[2] + t[1];
}
else {
a[1] = a[0];
a[2] = t[0];
a[0] = t[1];
}
}
void dfst(int n, float *a, float *t, int *ip, float *w)
{
void makewt(int nw, int *ip, float *w);
void makect(int nc, int *ip, float *c);
void bitrv2(int n, int *ip, float *a);
void cftfsub(int n, float *a, float *w);
void rftfsub(int n, float *a, int nc, float *c);
void dstsub(int n, float *a, int nc, float *c);
int j, k, l, m, mh, nw, nc;
float xr, xi, yr, yi;
nw = ip[0];
if (n > (nw << 3)) {
nw = n >> 3;
makewt(nw, ip, w);
}
nc = ip[1];
if (n > (nc << 1)) {
nc = n >> 1;
makect(nc, ip, w + nw);
}
if (n > 2) {
m = n >> 1;
mh = m >> 1;
for (j = 1; j < mh; j++) {
k = m - j;
xr = a[j] + a[n - j];
xi = a[j] - a[n - j];
yr = a[k] + a[n - k];
yi = a[k] - a[n - k];
a[j] = xr;
a[k] = yr;
t[j] = xi + yi;
t[k] = xi - yi;
}
t[0] = a[mh] - a[n - mh];
a[mh] += a[n - mh];
a[0] = a[m];
dstsub(m, a, nc, w + nw);
if (m > 4) {
bitrv2(m, ip + 2, a);
cftfsub(m, a, w);
rftfsub(m, a, nc, w + nw);
}
else if (m == 4) {
cftfsub(m, a, w);
}
a[n - 1] = a[1] - a[0];
a[1] = a[0] + a[1];
for (j = m - 2; j >= 2; j -= 2) {
a[2 * j + 1] = a[j] - a[j + 1];
a[2 * j - 1] = -a[j] - a[j + 1];
}
l = 2;
m = mh;
while (m >= 2) {
dstsub(m, t, nc, w + nw);
if (m > 4) {
bitrv2(m, ip + 2, t);
cftfsub(m, t, w);
rftfsub(m, t, nc, w + nw);
}
else if (m == 4) {
cftfsub(m, t, w);
}
a[n - l] = t[1] - t[0];
a[l] = t[0] + t[1];
k = 0;
for (j = 2; j < m; j += 2) {
k += l << 2;
a[k - l] = -t[j] - t[j + 1];
a[k + l] = t[j] - t[j + 1];
}
l <<= 1;
mh = m >> 1;
for (j = 1; j < mh; j++) {
k = m - j;
t[j] = t[m + k] + t[m + j];
t[k] = t[m + k] - t[m + j];
}
t[0] = t[m + mh];
m = mh;
}
a[l] = t[0];
}
a[0] = 0;
}
/* -------- initializing routines -------- */
#include <math.h>
void makewt(int nw, int *ip, float *w)
{
void bitrv2(int n, int *ip, float *a);
int j, nwh;
float delta, x, y;
ip[0] = nw;
ip[1] = 1;
if (nw > 2) {
nwh = nw >> 1;
delta = atan(1.0) / nwh;
w[0] = 1;
w[1] = 0;
w[nwh] = cos(delta * nwh);
w[nwh + 1] = w[nwh];
if (nwh > 2) {
for (j = 2; j < nwh; j += 2) {
x = cos(delta * j);
y = sin(delta * j);
w[j] = x;
w[j + 1] = y;
w[nw - j] = y;
w[nw - j + 1] = x;
}
bitrv2(nw, ip + 2, w);
}
}
}
void makect(int nc, int *ip, float *c)
{
int j, nch;
float delta;
ip[1] = nc;
if (nc > 1) {
nch = nc >> 1;
delta = atan(1.0) / nch;
c[0] = cos(delta * nch);
c[nch] = 0.5 * c[0];
for (j = 1; j < nch; j++) {
c[j] = 0.5 * cos(delta * j);
c[nc - j] = 0.5 * sin(delta * j);
}
}
}
/* -------- child routines -------- */
void bitrv2(int n, int *ip, float *a)
{
int j, j1, k, k1, l, m, m2;
float xr, xi, yr, yi;
ip[0] = 0;
l = n;
m = 1;
while ((m << 3) < l) {
l >>= 1;
for (j = 0; j < m; j++) {
ip[m + j] = ip[j] + l;
}
m <<= 1;
}
m2 = 2 * m;
if ((m << 3) == l) {
for (k = 0; k < m; k++) {
for (j = 0; j < k; j++) {
j1 = 2 * j + ip[k];
k1 = 2 * k + ip[j];
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += 2 * m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 -= m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += 2 * m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
}
j1 = 2 * k + m2 + ip[k];
k1 = j1 + m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
}
}
else {
for (k = 1; k < m; k++) {
for (j = 0; j < k; j++) {
j1 = 2 * j + ip[k];
k1 = 2 * k + ip[j];
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
}
}
}
}
void bitrv2conj(int n, int *ip, float *a)
{
int j, j1, k, k1, l, m, m2;
float xr, xi, yr, yi;
ip[0] = 0;
l = n;
m = 1;
while ((m << 3) < l) {
l >>= 1;
for (j = 0; j < m; j++) {
ip[m + j] = ip[j] + l;
}
m <<= 1;
}
m2 = 2 * m;
if ((m << 3) == l) {
for (k = 0; k < m; k++) {
for (j = 0; j < k; j++) {
j1 = 2 * j + ip[k];
k1 = 2 * k + ip[j];
xr = a[j1];
xi = -a[j1 + 1];
yr = a[k1];
yi = -a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += 2 * m2;
xr = a[j1];
xi = -a[j1 + 1];
yr = a[k1];
yi = -a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 -= m2;
xr = a[j1];
xi = -a[j1 + 1];
yr = a[k1];
yi = -a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += 2 * m2;
xr = a[j1];
xi = -a[j1 + 1];
yr = a[k1];
yi = -a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
}
k1 = 2 * k + ip[k];
a[k1 + 1] = -a[k1 + 1];
j1 = k1 + m2;
k1 = j1 + m2;
xr = a[j1];
xi = -a[j1 + 1];
yr = a[k1];
yi = -a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
k1 += m2;
a[k1 + 1] = -a[k1 + 1];
}
}
else {
a[1] = -a[1];
a[m2 + 1] = -a[m2 + 1];
for (k = 1; k < m; k++) {
for (j = 0; j < k; j++) {
j1 = 2 * j + ip[k];
k1 = 2 * k + ip[j];
xr = a[j1];
xi = -a[j1 + 1];
yr = a[k1];
yi = -a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += m2;
xr = a[j1];
xi = -a[j1 + 1];
yr = a[k1];
yi = -a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
}
k1 = 2 * k + ip[k];
a[k1 + 1] = -a[k1 + 1];
a[k1 + m2 + 1] = -a[k1 + m2 + 1];
}
}
}
void cftfsub(int n, float *a, float *w)
{
void cft1st(int n, float *a, float *w);
void cftmdl(int n, int l, float *a, float *w);
int j, j1, j2, j3, l;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
l = 2;
if (n > 8) {
cft1st(n, a, w);
l = 8;
while ((l << 2) < n) {
cftmdl(n, l, a, w);
l <<= 2;
}
}
if ((l << 2) == n) {
for (j = 0; j < l; j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = a[j + 1] + a[j1 + 1];
x1r = a[j] - a[j1];
x1i = a[j + 1] - a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i + x2i;
a[j2] = x0r - x2r;
a[j2 + 1] = x0i - x2i;
a[j1] = x1r - x3i;
a[j1 + 1] = x1i + x3r;
a[j3] = x1r + x3i;
a[j3 + 1] = x1i - x3r;
}
}
else {
for (j = 0; j < l; j += 2) {
j1 = j + l;
x0r = a[j] - a[j1];
x0i = a[j + 1] - a[j1 + 1];
a[j] += a[j1];
a[j + 1] += a[j1 + 1];
a[j1] = x0r;
a[j1 + 1] = x0i;
}
}
}
void cftbsub(int n, float *a, float *w)
{
void cft1st(int n, float *a, float *w);
void cftmdl(int n, int l, float *a, float *w);
int j, j1, j2, j3, l;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
l = 2;
if (n > 8) {
cft1st(n, a, w);
l = 8;
while ((l << 2) < n) {
cftmdl(n, l, a, w);
l <<= 2;
}
}
if ((l << 2) == n) {
for (j = 0; j < l; j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = -a[j + 1] - a[j1 + 1];
x1r = a[j] - a[j1];
x1i = -a[j + 1] + a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i - x2i;
a[j2] = x0r - x2r;
a[j2 + 1] = x0i + x2i;
a[j1] = x1r - x3i;
a[j1 + 1] = x1i - x3r;
a[j3] = x1r + x3i;
a[j3 + 1] = x1i + x3r;
}
}
else {
for (j = 0; j < l; j += 2) {
j1 = j + l;
x0r = a[j] - a[j1];
x0i = -a[j + 1] + a[j1 + 1];
a[j] += a[j1];
a[j + 1] = -a[j + 1] - a[j1 + 1];
a[j1] = x0r;
a[j1 + 1] = x0i;
}
}
}
void cft1st(int n, float *a, float *w)
{
int j, k1, k2;
float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
x0r = a[0] + a[2];
x0i = a[1] + a[3];
x1r = a[0] - a[2];
x1i = a[1] - a[3];
x2r = a[4] + a[6];
x2i = a[5] + a[7];
x3r = a[4] - a[6];
x3i = a[5] - a[7];
a[0] = x0r + x2r;
a[1] = x0i + x2i;
a[4] = x0r - x2r;
a[5] = x0i - x2i;
a[2] = x1r - x3i;
a[3] = x1i + x3r;
a[6] = x1r + x3i;
a[7] = x1i - x3r;
wk1r = w[2];
x0r = a[8] + a[10];
x0i = a[9] + a[11];
x1r = a[8] - a[10];
x1i = a[9] - a[11];
x2r = a[12] + a[14];
x2i = a[13] + a[15];
x3r = a[12] - a[14];
x3i = a[13] - a[15];
a[8] = x0r + x2r;
a[9] = x0i + x2i;
a[12] = x2i - x0i;
a[13] = x0r - x2r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[10] = wk1r * (x0r - x0i);
a[11] = wk1r * (x0r + x0i);
x0r = x3i + x1r;
x0i = x3r - x1i;
a[14] = wk1r * (x0i - x0r);
a[15] = wk1r * (x0i + x0r);
k1 = 0;
for (j = 16; j < n; j += 16) {
k1 += 2;
k2 = 2 * k1;
wk2r = w[k1];
wk2i = w[k1 + 1];
wk1r = w[k2];
wk1i = w[k2 + 1];
wk3r = wk1r - 2 * wk2i * wk1i;
wk3i = 2 * wk2i * wk1r - wk1i;
x0r = a[j] + a[j + 2];
x0i = a[j + 1] + a[j + 3];
x1r = a[j] - a[j + 2];
x1i = a[j + 1] - a[j + 3];
x2r = a[j + 4] + a[j + 6];
x2i = a[j + 5] + a[j + 7];
x3r = a[j + 4] - a[j + 6];
x3i = a[j + 5] - a[j + 7];
a[j] = x0r + x2r;
a[j + 1] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j + 4] = wk2r * x0r - wk2i * x0i;
a[j + 5] = wk2r * x0i + wk2i * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j + 2] = wk1r * x0r - wk1i * x0i;
a[j + 3] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j + 6] = wk3r * x0r - wk3i * x0i;
a[j + 7] = wk3r * x0i + wk3i * x0r;
wk1r = w[k2 + 2];
wk1i = w[k2 + 3];
wk3r = wk1r - 2 * wk2r * wk1i;
wk3i = 2 * wk2r * wk1r - wk1i;
x0r = a[j + 8] + a[j + 10];
x0i = a[j + 9] + a[j + 11];
x1r = a[j + 8] - a[j + 10];
x1i = a[j + 9] - a[j + 11];
x2r = a[j + 12] + a[j + 14];
x2i = a[j + 13] + a[j + 15];
x3r = a[j + 12] - a[j + 14];
x3i = a[j + 13] - a[j + 15];
a[j + 8] = x0r + x2r;
a[j + 9] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j + 12] = -wk2i * x0r - wk2r * x0i;
a[j + 13] = -wk2i * x0i + wk2r * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j + 10] = wk1r * x0r - wk1i * x0i;
a[j + 11] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j + 14] = wk3r * x0r - wk3i * x0i;
a[j + 15] = wk3r * x0i + wk3i * x0r;
}
}
void cftmdl(int n, int l, float *a, float *w)
{
int j, j1, j2, j3, k, k1, k2, m, m2;
float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
m = l << 2;
for (j = 0; j < l; j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = a[j + 1] + a[j1 + 1];
x1r = a[j] - a[j1];
x1i = a[j + 1] - a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i + x2i;
a[j2] = x0r - x2r;
a[j2 + 1] = x0i - x2i;
a[j1] = x1r - x3i;
a[j1 + 1] = x1i + x3r;
a[j3] = x1r + x3i;
a[j3 + 1] = x1i - x3r;
}
wk1r = w[2];
for (j = m; j < l + m; j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = a[j + 1] + a[j1 + 1];
x1r = a[j] - a[j1];
x1i = a[j + 1] - a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i + x2i;
a[j2] = x2i - x0i;
a[j2 + 1] = x0r - x2r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j1] = wk1r * (x0r - x0i);
a[j1 + 1] = wk1r * (x0r + x0i);
x0r = x3i + x1r;
x0i = x3r - x1i;
a[j3] = wk1r * (x0i - x0r);
a[j3 + 1] = wk1r * (x0i + x0r);
}
k1 = 0;
m2 = 2 * m;
for (k = m2; k < n; k += m2) {
k1 += 2;
k2 = 2 * k1;
wk2r = w[k1];
wk2i = w[k1 + 1];
wk1r = w[k2];
wk1i = w[k2 + 1];
wk3r = wk1r - 2 * wk2i * wk1i;
wk3i = 2 * wk2i * wk1r - wk1i;
for (j = k; j < l + k; j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = a[j + 1] + a[j1 + 1];
x1r = a[j] - a[j1];
x1i = a[j + 1] - a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j2] = wk2r * x0r - wk2i * x0i;
a[j2 + 1] = wk2r * x0i + wk2i * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j1] = wk1r * x0r - wk1i * x0i;
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j3] = wk3r * x0r - wk3i * x0i;
a[j3 + 1] = wk3r * x0i + wk3i * x0r;
}
wk1r = w[k2 + 2];
wk1i = w[k2 + 3];
wk3r = wk1r - 2 * wk2r * wk1i;
wk3i = 2 * wk2r * wk1r - wk1i;
for (j = k + m; j < l + (k + m); j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = a[j + 1] + a[j1 + 1];
x1r = a[j] - a[j1];
x1i = a[j + 1] - a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j2] = -wk2i * x0r - wk2r * x0i;
a[j2 + 1] = -wk2i * x0i + wk2r * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j1] = wk1r * x0r - wk1i * x0i;
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j3] = wk3r * x0r - wk3i * x0i;
a[j3 + 1] = wk3r * x0i + wk3i * x0r;
}
}
}
void rftfsub(int n, float *a, int nc, float *c)
{
int j, k, kk, ks, m;
float wkr, wki, xr, xi, yr, yi;
m = n >> 1;
ks = 2 * nc / m;
kk = 0;
for (j = 2; j < m; j += 2) {
k = n - j;
kk += ks;
wkr = 0.5 - c[nc - kk];
wki = c[kk];
xr = a[j] - a[k];
xi = a[j + 1] + a[k + 1];
yr = wkr * xr - wki * xi;
yi = wkr * xi + wki * xr;
a[j] -= yr;
a[j + 1] -= yi;
a[k] += yr;
a[k + 1] -= yi;
}
}
void rftbsub(int n, float *a, int nc, float *c)
{
int j, k, kk, ks, m;
float wkr, wki, xr, xi, yr, yi;
a[1] = -a[1];
m = n >> 1;
ks = 2 * nc / m;
kk = 0;
for (j = 2; j < m; j += 2) {
k = n - j;
kk += ks;
wkr = 0.5 - c[nc - kk];
wki = c[kk];
xr = a[j] - a[k];
xi = a[j + 1] + a[k + 1];
yr = wkr * xr + wki * xi;
yi = wkr * xi - wki * xr;
a[j] -= yr;
a[j + 1] = yi - a[j + 1];
a[k] += yr;
a[k + 1] = yi - a[k + 1];
}
a[m + 1] = -a[m + 1];
}
void dctsub(int n, float *a, int nc, float *c)
{
int j, k, kk, ks, m;
float wkr, wki, xr;
m = n >> 1;
ks = nc / n;
kk = 0;
for (j = 1; j < m; j++) {
k = n - j;
kk += ks;
wkr = c[kk] - c[nc - kk];
wki = c[kk] + c[nc - kk];
xr = wki * a[j] - wkr * a[k];
a[j] = wkr * a[j] + wki * a[k];
a[k] = xr;
}
a[m] *= c[0];
}
void dstsub(int n, float *a, int nc, float *c)
{
int j, k, kk, ks, m;
float wkr, wki, xr;
m = n >> 1;
ks = nc / n;
kk = 0;
for (j = 1; j < m; j++) {
k = n - j;
kk += ks;
wkr = c[kk] - c[nc - kk];
wki = c[kk] + c[nc - kk];
xr = wki * a[k] - wkr * a[j];
a[k] = wkr * a[k] + wki * a[j];
a[j] = xr;
}
a[m] *= c[0];
}
view raw FFTGraph_MT.c hosted with ❤ by GitHub

// pin P1.6:15 - Serial data out (MOSI)
// pin P1.5: 7 - Serial clock out (SCLK)
// pin P5.7:17 - Data/Command select (RS or A0)
// pin P2.5:19 - LCD reset (RST)
// pin P3.0:18 - LCD chip select (CS)
ST7565 lcd(15, 7, 17, 19, 18);
void setupTaskLCD() {
lcd.begin(0x1c);
lcd.clear();
}
void loopTaskLCD() {
char sbuf[10];
lcd.clear();
for (int i = 1; i < NMAX/2; i++) {
lcd.drawline(i, LCDHEIGHT-1, i, LCDBuffer[i], BLACK);
}
sprintf(sbuf, "%4d", FFTCounter);
lcd.drawstring(10, 0, sbuf);
lcd.display();
delay(100);
}
view raw TaskLCD.c hosted with ❤ by GitHub

2015年9月21日月曜日

Energia16(MacOS X Lion) / Failed: Failed to evaluate GEL_LoadProgramOnly

NMAXSQRT大きすぎるな、今気づいた。

MSP432で遊んでいて、ボードに書き込みをしようとしたら
Failed: Failed to evaluate GEL_LoadProgramOnly
というエラーが出て書き込みができなくなりました。Forumにも同じissueが投稿されていたのですが、最終的には「OSとアプリを再インストールしたら直った」ということですが、それは余りにもリカバリコストが高すぎます。

ので、エラーメッセージにも出ている/var/foldersを消してみたり、Energiaアプリを再インストールしてみたのですが症状は変わらず。当然LaunchPadのUSBケーブルを抜き差ししてもダメ。

ただ、/var/foldersを消そうとしたらFinderがお亡くなりになったので、「これはパーミッションの問題だろうか?」と思い、
  • ディスクユーティリティでアクセス権修正
  • セーフティモードで起動
  • /var/tmp削除
などを行ったのですが変わらず。

ふと思い立ってボード上のリセットボタンを押したら…何事もなく復帰しました。ケーブル抜き差ししてもダメだったのにリセットが効くとは…orz

毎回ではないですが、起動直後にcmd+Mで「ビルド後にシリアルモニタを開く」を選ぶと、再発しやすいような気がします。その場合もリセットで復帰しました。

以下余談。

リカバリーのための諸々の操作の何が悪かったのか、Finderがなんだか不調です。Chromeも起動しなくなってしまいました(再インストールしたら直った)。まぁChromeはタブが増えすぎていたし、メモリをバカ食いするから使わなくても良いかな。

セーフティモードでお掃除したせいか、妙にサクサク動くし。まぁ悪いことばかりではなかった…と…考えましょう。

2015年9月20日日曜日

ゴメン、MSP432P401Rやっぱり速かった

基板裏

えー、前回、MSP432P401Rがイマイチ速くない、という記事を書きました。

32bit 48MhzのMSP432が16bit 16MhzのArduino nanoと比べて約1.7倍速いだけでクロック比やCPU差を考えるとぼろ負け状態でした。

あれ、FPUは仕事してないの?と思ったのですが…よくよく仕様を読んでみれば、M4FのFPUは単精度でした。今回使ったFFTライブラリは倍精度なので、FPU様の恩恵をまったく受けられません。

というわけで、dobuleをfloatにさくっと置換して、実行してみました。

その結果(前回同様64点のFFTを1000回実行)

デバイス結果
MSP432P401R82mSec
Arduino nano1943mSec

前回結果が1142mSecでしたから約14倍スピードアップ! 圧倒的です。一方のArduino nanoはほとんど変わっていません。改めて調べてみるとArduinoはdoubleもfloatも単精度なんですね…知らなかった。Energiaも同じ仕様なのですが、MSP432は特別なのでしょうか。とりあえずfloatが単精度でFPUを使っていることは確認できましたが、もしMSP432だけはdoubleがちゃんと倍精度で実装されているとしたら、この結果は納得がいきます。

前回の結果でも「なんとか補聴器作れるかなぁ…」という程度だったのですが、これなら十分です。あとは私の能力か…そこが一番ネックだな…。

関係ないけど、配列に一定の値を入れて逆FFTかけたらピンクノイズになるんですかね。一度実験したいと思ってたのですが、こないだ「よし、もう買い忘れはないな」とさんざん確認してから発注したにもかかわらず、見事にDACを注文し忘れました。

やはり私の能力が一番のネックだorz

2015年9月19日土曜日

MSP432P401Rがイマイチ速くない?


■追試結果■

イマイチ速くない原因がわかりました。MSP432搭載のFPUは単精度で今回使ったプログラムは倍精度、そのためFPUが寝てる状態でした。ただ、どちらもソフトウェアで演算しているなら、やはりクロック差ほどの性能差が出ていないので疑問は残ります。

追試結果はこちらです(ゴメン、MSP432P401Rやっぱり速かった)

以下は、新しいCPU到着に浮かれるバカっぷりだけをお楽しみくださいorz

■本文■

ついに届いたぜ、M4Fだぜヒャッハー!

…などと浮かれていたのですが、energia上で浮動小数点64ポイントのFFTを1,000回走らせてみたところ、結果は1143ミリ秒でした。

一方、Arduino nanoに同じソースをコピペして実行したら、1992ミリ秒。

クロック比よりも遅い? 浮動小数点プロセッサはどーなった??

…という残念な結果になりました。

ちゃんとオプチマイズしないとダメですね。ARMに最適化されているであろうCMSIS-DSPはどうかなぁ…。

2015年9月16日水曜日

ESP-WROOM-02の電源はかなりシビア?

林さんの髪型を彷彿とさせる配線状況

前回の記事(やっとESP8266(WROOM-02)入手)で、ファームウェアを書き換えるための文字列のスペースが詰まっていたり全角が挟まっていてご迷惑をおかけしました。申し訳ありません。なぜかこのブログを書いているBloggerでは、数日後テキストのスタイルが突然崩れるという珍現象が時々発生します。発表直後には必ず確認しているのですが、後日見ると崩れていて…私も自分でコピペして初めて壊れていることに気づいた次第です。大変申し訳ありませんでした。

さて、某林さんのところでも珍現象が起きていて、どうもESP-WROOM-02のWebサーバが安定しないご様子。数分でフリーズしたり、たまに数時間動いたりする。私のところで正常に動いていたWROOMを送って試したもらったのですが、このときSSIDを林さんちに合わせていなかったので、予備のWiFiを用意してもらいました。するとなんと数時間無事に動作した!…のですが、やっぱり落ちる。林さんの作ったWROOMでも予備WiFiだと比較的安定するものの、やっぱり落ちることは落ちる。

それでここしばらく、林さん、○野さん(伏せ字になってないな)と3人で脳ミソを寄せ集めていたのですが、どうにもならず。

で、ゆうべ、ちょっと確認したいことができたので、私のうちにもう一枚余ってたESP-WROOM-02を組み立てて実験していたのですが…これがまともに動いてくれません。林さんと同様どころか林さんより悪くてsetup()の起動メッセージすらでない。

電源はUSBシリアルの5v出力からDC/DCを使って3.3vに落としているので容量的な問題はないはず。

これも一晩、各種バージョンのファームウェアをアドレス変えながらいろんなパターンで流し込んだりしたのに、状況は変わらず。

…というか変わらなすぎる。ファームが原因なら、もっと毎回違うメッセージが出てくるはず。

というわけで念のため電源電圧を測ってみたら、3.1vしか出ていない? USBシリアルの供給能力が足りないっぽいので、USBシリアルのVcc端子をやめてACアダプタに交換してみました。

すると何事もなかったかのように、Web画面表示が…。

また無駄な苦労をしてしまいました…。

追記:使っている三端子DC/DCについて書きました(リンク)。

今日会社で眠いだろうなぁ…。

寝てないわー3時間寝てないわー(ミサワ
以前Beaglebone blackで作った睡眠・活動計(Play!アプリとBoneScriptの通信 - 1.構成)は今もちゃんと動作していますが、こういう時は便利なのか不便なのか…。寝不足だから眠いのではなく、寝不足だと思うから眠いのだ、と言ったのはどこの漫画家だったか、それとも照井俊光氏の弟子トミ子氏だったか…。

2015年9月15日火曜日

チップコンデンサ2012とジャストサイズ


以前もチップ部品と余ったピンヘッダを使って「ブレッドボードに最適な部品!」とかやっていましたが(「表面実装部品は安いっ」「表面実装部品は安いっ'」)、あれはちょっとハンダ付けが大変でした。あの時使ったのは1608の積層セラミックコンデンサだとピンとチップの間に隙間があるので、ピンをうまく調整しないと落ちてしまうのです。

しかし。本日発見しました。2012のチップコンデンサと、2.5mmピッチのヘッダピンはジャストフィットなのです。指で軽く押し込んでやると、指を離しても落ちません。ハンダ付け楽勝です。ついつい必要以上に量産してしまいましたw

今日使った / 作ったのは4.7μF50Vの積層セラミックですが、このぐらいの容量・耐圧のリードタイプは結構高かったりするので、今後とも活用していきたい…と思ったのですが、品揃えが豊富になると容量がわからないという問題が生じますね。とりあえず一目でわかる4.7と0.1ぐらいにしておこうっと。

2015年9月12日土曜日

32bit CPU搭載インバータw

「マイナスは黒」と書いてあるのはインダクタが赤に届かなかったので

■追記1■

当初この記事では「ジュールシーフ」と表記していましたが、「電子工作部」で「これはジュールシーフではなくてチョッパ型昇圧インバータ」というご指摘をいただきました。ありがとうございます、訂正致しました。

あと、適当に転がっていた0.5wの太陽電池をつないでみたところ、0.2vから動作を開始し0.6vでフル作動の状態になり、この時の電流は約50mAでした。

また、MacのBluetoothキーボードで使用できなくなったマンガン電池では0.2vまでしっかり電池を絞り尽くしました。そういう意味ではジュールシーフと言えなくもw

■追記2■

もうちょっとマシな制御をして指定した電圧でぴたっと止まるようにした続編はこちらです。

■32bit発信器w■

また無駄遣いをしてしまいました。しかも32bit CPUは単に

#include "mbed.h"
AnalogIn vol(dp4);
PwmOut pwm(dp18);
int main() {
    pwm.period(1.0/20000.0);
 
    while(1) {
        pwm = vol;
        wait(0.1);
    }
}

って書いているだけ。要するに単なるデューティー比可変方形波発振回路、CPUのムダにも程があるw

LPC810の方が向いている(こっちも32bitだけど)規模なのですが、LPC810にはADCがないのでLPC1114FN28を使いました。

■昇圧回路1■

回路は「芸が無いにも限度というものが!」と怒られそうですが、電池の1.2vをPFMで3.3vに昇圧してLPC1114と黄色LEDとインバータ回路の電源に。

HOLTEK社 HT77xxA仕様書より引用

■昇圧回路2■

LPC1114で約20khzのPWMを出力して、チョッパ型インバータ回路を構成するMOSFET 2SK2232をスイッチングしています。L1のところにつながっている電源はVccと書いてありますが、実際には電池のプラス極です。逆流防止ダイオードはショットキーバリヤー1S4、パワーインダクタは68μHの表面実装ものにピンヘッダをハンダ付けしたもの、あとは出力用の電解コンデンサ50v22μF、分圧抵抗1kΩと直列の青色LEDです。ボリュームはアナログ入力につながり、PWMのデューティー比を調整しています。

これ書くのに1時間近くかかった…IC1が立派すぎる

PWMをoff(0%)にしてもほんのわずか青色LEDが点灯して電流が流れますが、これは電池の1.2vが素通りになっているためです。Vfが3v近い青色LEDでも1.2vで点灯しちゃうんですね。

前記の通り手持ちの部品の耐圧が最大40v(1S4)しかないので30vまでしか試していませんが、部品(MOSFET, ダイオード, C1)をもっと高耐圧のものにしてLEDとR3を外せば200vぐらいは余裕で出せます。

■LPC1114fn28への書き込み■

いつも説明を省略していますが、LPC1114fn28への書き込み方法はここ(プログラムをフラッシュに書き込む)が詳しいです。

私はmbedのブラウザIDEからダウンロードしたbinをlpc21ispを使ってLPC1114FN28に送っています。

まずターミナルで ls /dev/tty.usb* を実行してUSBシリアルのドライバ名を表示します。私のところでは /dev/tty.usbmodem24131 でした。
USBシリアルのTXDをdp15, RXDをdp16に接続し、あと3.3vとVcc、GNDとGNDを接続します。なお、USBシリアルによってはTXDではなくTXと表記されている場合もあるのでご注意を。で、mbedでのコンパイルが終わるとバイナリがダウンロードされます。そうしたらLPC1114FN28のdp24をGNDに接続し、dp23をちょいとGNDに接触させて離し、dp24も離します。で、ターミナルで以下のコマンドを実行します。なお、このプログラムでは問題ないですが、dp23/24をGPIO特にDigitalOutやPwmOutとして使っている場合、そのままGNDに接続するとチップを破損します。なので、330-1kΩぐらいの抵抗を通じてGNDに接続すると安全です。
lpc21isp -bin <mbedから落ちた.binファイル> /dev/tty.usbmodem24131  115200 48000
メッセージとSector 1:のあとに「.」がととととと…と表示され、書き込みが行われます。もし表示が「Synchronizing (ESC to abort)..」の先に進まないようなら、もう一度dp23とdp24をGndに落としてみてください。
Now launching the brand new code
lpc21isp version 1.97
File Chopper_LPC1114.bin:
loaded...
image size : 11492
Image size : 11492
Synchronizing (ESC to abort)....... OK
Read bootcode version: 1
7
Read part ID: LPC1114.../102, 32 kiB FLASH / 4 kiB SRAM (0x1A40902B)
Will start programming at Sector 1 if possible, and conclude with Sector 0 to ensure that checksum is written last.
Erasing sector 0 first, to invalidate checksum. OK
Sector 1: ...........................|.........................|.........................|.........................
Sector 2: ...........................|.........................|.........................|.........
Sector 0: ..........................|.........................|.........................|.........................
Download Finished... taking 8 seconds

2015年9月3日木曜日

MacとWindowsの色の違い

あ り が ち

ちょっとしたWebアプリをPlay+Angular+Bootstrapで作ったです。ロゴ作る人いないっていうから、私がKeynoteに適当なフォントを乗っけて加工して貼りました。指定色を画像と背景とで同じにして、少しずつグラデーション付けて。

で、Macで見て、確認し社内リリース。特にコメントなし。

が。後日Windowsでそのページを見てみたら…画像部分とその周囲の色が違っているorz あわててMacで見ても同じだし…。なので、


  • pngでなくjpegにしてみる→同じ
  • VMBox上で表示された背景色をスポイトで取って塗る→同じ


背景を透過色にすれば良いんだろうけど、適当なツールも時間もない。ので、結局、「どうせ文字から作ったんだから」ということで、CSSで修飾してほぼ同じモノを作ってリリースしました。当たり前だけど、同じ表示になりました。

私は徹頭徹尾デザインセンスのない人間なので、今までこういう作業に関わってきたことなかったのですが…いやー、大変ですね。最近個人的にはサーバサイドでページを生成するより、クライアントサイドでAngularなどを作って生成する方が合理的だと考えているのでHTML/CSS/JSを弄ることが多いのですが(会社ではくそったれStruts2ですが)…せめて、こういう基本的なことぐらいは勉強しておかないといかんです。

はい。

それはともかく…社内のほとんどはWindowsなんだし、はっきり段差付いちゃってるんだから、クレーム入れてくださいよぉ>社内のヒト まさかそういうデザインだと思ったわけではない、と思いたい…。

2015年9月2日水曜日

大きなセグメントLED


比較的簡単に手に入るモノとしては、秋月とaitendoで売ってる高さ58.8mmの16セグメントアノードコモンLEDがお馴染みだと思います。

どっちも基板も付けて売っているんだけど…秋月のは既存のデジタル時計キットに適合させるために7セグメントとして使っているのとドットが1分しか配線されていない。aitendoのは全セグメント配線されているんだけど、端子が左右に離れているのでコネクタ一個でマイコン取り付けておしまい、ということができない。

このぐらいの基板なら自分で設計するのも簡単なことなんだけど、大きいとEagleのフリー版の制限を超えてしまう。まぁKiCAD使えば良いんだけど…わりと微妙な大きさなのでElecrowなどに注文しても少し高く付いてしまう。いっそクラウドファウンディングで募集したりして。「A-2308SR用基板、ゴールは100枚5万円です」…需要ないな、100枚もw

とか書いているこの瞬間に、ESP-WROOM-02にArduinoシールドと組み合わせて使えるようにする基板を作っている人は世界にどれだけいるのだろうか。もちろん完全コンパチにするにはピン足りないし電圧も違うけど、そこを端折ったり工夫したり。

何がでてくるか楽しみ。