ラベル Android の投稿を表示しています。 すべての投稿を表示
ラベル Android の投稿を表示しています。 すべての投稿を表示

2024年8月26日月曜日

デバイス変更中 Arduino→STM32F401→STM32H723

ただいま絶賛デバイス変更中です.
開発projectで「デバイス変更」ってけっこう重い決断だと思います.マジか、みたいな.

今日は朝からアートワークばかりしていました.
51x51mm 4層です.4層基板を作るのは久しぶりです.JLCPCBは51x51mmまでなら4層でも$2で作ってくれるみたいです.送料込みで$3ぐらい.
シルクのとおり、STM32H723を載せる子基板です.STM32シリーズではけっこうhigh endなCPUです.

今開発中の回路は、最初はArduinoで作りました.
依頼されたわけじゃないのですが、「こんなの出来ます」と波形をAD変換してPCへ送信する機能を追加したら好評でした.PC上で波形表示アプリを作ってデモとかしてるみたいです.
しかしArduinoはRAMが2kBしかないので、work RAMをせいぜい1kBしか確保できませんから表示分解能が低いです.

サクッとArduinoを捨てました.
CPUをSTM32F401に交換して、波形表示を10倍高精細にしました.
F401はRAMが64kBとか96kBとか載っているのでらくちんです.
とりあえず満足のゆく機能にはなったのですが、、、4ch表示したいなぁと考えると、96kBだとなんかギリギリな予感がするんですよね.

Aliexpressで入手しやすいCPUを探してSTM32F407なんかどうかなぁと考えました.同CPUは192kB RAMを搭載しています.お値段も安くて¥200ぐらいです.10個ぐらい買ってみました.
ただ、なんかハンパな性能です.どーせあとになれば信号処理の要求がインフレするんだからもっと速いCPUにした方が良いのではないか?

CPUの速さイメージ:
 Arduino   8bit  16MHz  2KB  ママチャリ
 STM32F103 32bit 72MHz  96kB  原付バイク
 STM32F401 32bit 84MHz  96kB  125ccバイク
 STM32F407 32bit 168MHz 192kB  400ccバイク
 STM32H723 32bit 550MHz 564kB  軽自動車
 スマホSOC              GT-R
 coreなんちゃら            船舶エンジン
 GPU                 ジェットエンジン

以前、STM32H723を使ってFIR filterを動かしてみたことがあります.
DSPぐらいの処理能力があるので、かなりやれるCPUです.
STM32H723のNucleoは¥5000ぐらいするけど、AliexpressでCPU単品だと¥700ぐらいで買えるのでそんなに高価じゃないです.

現状の開発フェーズにはover specなCPUですが、STM32H723でお気楽極楽になろう.

かしこ

2023年2月28日火曜日

java/Kotlinが苦手です @JvmStatic(34)

皆さんこんにちは.毎日何かしら作っているヒラサカです.

USBオシロを作る取り組みの第33回でUSB2.0転送レートが3MByte/Secしか出ないと実測され、やる気がしなしなになってしまいました.しかし不屈の闘志で立ち上がったわたしは、AndroidスマホをUSB hostとして運用してみることにしました.USB BULK転送をすりゃいいのでなんとか自力でできるんじゃないかと思います.

開発環境はAndroidStudioで、言語はjava/Kotlinを使っています.

でも、つくづく思うのは、java/Kotlinはわたしのsoftware能力の上を行ってますね.

Cみたく関数を書くための仕組みだけが提供されている平易な言語がわたしの対応能力の上限です.ところがjava/Kotlinときたらobjectやthreadのための仕組みがどっちゃりと上乗せされていてややこしくて困る.interface, abstract, this, it, final, synchronized, throw, super, companion, inner,,,,,


それで、netで拾ったUSB COMのデバドラ(java)を参考にしているのですが、sourceにjavaとKotlinが混ざってると読みづらいのでKotlinに書き換えているところ.

それで、う~んと思ってしまったのが@JvmStaticっていうマイナーな制御句でした.

とあるjavaのcodeにstatic関数がありました.
 class Clock {
     public static long nsec() { return System.nanoTime(); }
 }
これをKotlinに書き換えるのですが、Kotlinにはstaticが無いのでcompanionという宣言?で囲みますとstaticっぽく使える事になっています.javaよりもKotlinが好きなわたしですが、これはjavaの方が良いと思いますがね.
 class Clock {
     companion object {
          fun nsec(): Long { return System.nanoTime() }
     }
 }

なのですが、このKotlin codeをビルドできません.
static関数nsec()を呼び出すjava codeで「staticじゃないから呼べない」とエラーが出ます.

元々嫌な感じはするんです.AndroidStudioの処理系はKotlin/javaのmix codeに対応しているのか?という疑問です.
 関数定義:Kotlin
 関数呼出:java
これまではKotlin/java混在のビルドは円満に通っていましたから疑いは無用のはずでした.でも上の通りになってしまい、昨夜から悩んでいました.

そんな折、これを読んだら @JvmStatic @JvmFieldという気になるwordがありました.

さっそく試します.エラーが消えました.う~む、これって言語仕様なのかな? どっちかというとコンパイラへの指示じゃなかろうか? 混在ダメですか....
 class Clock {
     companion object {
          @JvmStatic fun nsec(): Long { return System.nanoTime() }
     }
 }

教訓: Kotlin/java混在には気をつけよう

33へ    35へ

かしこ

2023年2月25日土曜日

【Android USB oscilloscope】(33) USB2.0 BULK転送 3MByte/Sec

USB2.0で、STM32をUSB hostとし、USB deviceであるスマホを接続する.こういう図になっている.
これで転送レートは3MByte/SecのBULK転送が限界という結果を得ました.スマホのUSB受信はUIとは別のスレッドでやってます.もっと速くなってほしいんですけど、残念です.
ちなみに、こちらで書いたようにUSB2.0で16MByte/Secを出した実績はありますが、EZ-USBとLinuxの組み合わせであって、スマホが16MByte/Secを出すのは難しいみたいです.

今回の実験の諸条件です.
 USB    2.0 BULK transfer 512Byte packet
 スマホ   2016年製 HUAEWI P9-Lite Android7
 CPU    STM32F205
 開発環境1 STM32CubeIDE 1.5.1
 開発環境2 Android Studio Bumblebee 2021.1.1 Patch2

接続状況の写真.
白い変換コネクタはOTG cableではありますが、OTGを目的とした使い方ではなく、ただのマイクロ-タイプA変換として使っています.

動作を詩的に表現するとこんなことをやっています.
 0)USB cableが挿されたらAOA接続する ※
 1)hostであるSTM32は、10mSec割込みでBULK転送を試みる
  1a)USBがNAKだったら何もしない
  1b)USBがACKなら511BYTEをBULK転送する(512だとスマホがhungる)
  1c)NAK比率をCOM portに出力表示する
 2)スマホは、MainActivity内のRunnableで1000回USB read loopを記述する
  2a)スマホのボタンが押されるとRunnnableが起動される
  2b)Runnable内で転送レートを計算しUIに表示する

(※)スマホのUSB接続は特殊です.USB hostにスマホ(USB device)を挿すと、descriptorのやり取りなどを経て、一旦USBを切断し、再度接続するという凝ったことをします.AOAという作法です.AOAについてはこちらに書きました.

スマホアプリ画面.
READボタンを押すと数値が更新されます.4番が転送レートです.

STM32とスマホの接続.
スマホアプリを起動しておいてからUSB cableを接続する感じ.スマホから接続許可を求められます.ダメだったらSTM32のresetをするとかいろいろ試す.

STM32とPCの接続.
STM32の内部状態を表示するためのものです.FT232のようなUSB COM変換が必要です.

sourceと回路図の詰め合わせzipをupしておきます.テストコード満載の糞コードですのでゲロです、反吐です、要注意です.読者に損害を与えましても賠償とかしません.

ーーーー
STM32ソースの簡単な説明.

まずAOA接続について.
AOA接続の手続きは、main loopから繰り返しcallされています.
  while (1)  {
    MX_USB_HOST_Process(); // invoke AOA procerss
  略
  }
MX_USB_HOST_Process()の先ではUSBH_Process()をcallしてるのでそっちへ行きます.
この関数ではUSB接続時の手続きがいろいろ記述されていて、USB classを分析とかしていますが、知らないUSB classに悩んでabortする間際に藁を掴んでaoa_setupAccessory()をcallします.そこからAOA接続が始まります.
Linux hostのAOA接続についてはこちらを参照.これをSTM32に移植して動かしています.
AOAが接続成功した結果、OUT方向の設定は「EP2 max packet size 512Byte」になりました.いままでpacketを2000ByteとかにできないのはSTM32のバグだと何度も書きましたが、スマホにも512で制限かかってます.ちっ、なんだかなー

次にBULK転送について.
TIM7で10mSec割込みをかけています.割り込みルーチンでBULK転送をcallします.
void TIM7_IRQHandler(void){
if(aoa_ok==2) aoa_bulkTransfer(&hUsbHostHS);
}
aoa_bulkTransfer()を見ると、USBH_BulkSendData()をcallしています.511ByteでDMAです.そんな頻繁にBULK転送しようとしてもUSBが追い付きゃしませんが、構わずcallする理由は、USB statusを採取するためです.BUSYだったら無視されます.
void aoa_bulkTransfer( USBH_HandleTypeDef *phost ) {
USBH_BulkSendData(phost, buffer, 511, phost->Control.pipe_out, 1);
}

ーーーー
スマホアプリの簡単な説明.

一番最初、スマホにUSB cableが挿された時に、このアプリにeventが飛んできてもらわなくちゃ困りますよね.このアプリが「USB挿入で声かけてくれ」とOSに依頼する仕組みをintent-filterと呼ぶそうです.projectの中のAndroidManifest.xmlを開くと、USB device attachedという文字があります.細かくは知りませんがこんなもんだっつうことで.
<intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />

次のUSB接続過程はAOAです.accessory_filter.xmlを開くと見える文字があります.
 manufacturer="bangflat" model="aoatest"
この2つは、STM32 hostのAOAルーチンにも記述されており、AOAの先頭で軽く認証するのに使われます.hostとdeviceで一致してなくちゃいけません.あとはスマホがやってくれます.

USB BULK受信について.
MainActivity.ktの中で別スレッドを記述します.USBの受信を1000回やってます.usbDriverが肝です.
    val runnable = object : Runnable {
        override fun run() {
            for (i in 0..999) {
                try {
                    val rmsg: String = usbDriver!!.receive()
                    USBparams.sum += rmsg.length
                } catch (e: IOException) {
                    errorDialog(e.message)
                }
MainActivity.ktのonCreateの中で、READボタンが押されたらRunnableを起動するようにしてます.別スレッドでUSBの1000回readが始まります.
  b.IDreadBTN.setOnClickListener {
  handle.post(runnable)
 }

ーーーー
3MByte/Secでは不満なので、以上で投了です.

次はスマホをUSB hostにするやり方でトライしてみます.出来るかどうかは知りません.

32へ    34へ

かしこ

2023年2月23日木曜日

【Android USB oscilloscope】(32) 再起動

あれ以来、すっかりやる気を失ってしまいました.

その件について最後に記したのは2022年5月13日.ずいぶん古い出来事だったように感じていましたが、あの敗北からまだ1年と経ってなかったか....

余ったスマホをオシロの表示デバイスにするという構想でした.外付けのプリント基板でADCして、波形データをUSB経由でスマホに送る.画面表示はなんとかなるさ、と.

ところが、スマホのUSB2.0転送レートが1MB/Secにも届かず、ダメだこりゃと匙を投げてしまいました.

首尾よく出来たら夏コミケで売ろうとも思ってたんだったっけ....

やる気を失ったとはいえ、また復活させるつもりは少しありました.やり残した事があるからです.

ここでUSB2.0の転送レートの期待値を考えてみます.

チャネルレート: 480Mbit/Sec≒60MByte/Sec
60MB/Secはずいぶんと高速ですが、飽くまでも理論値なのでこんなには出ません.

packetレート: packetは125uSec毎に送信される
ということは、1 packetに7500Byte詰め込める計算ですが、実際はヘッダ情報などの無駄があるのでそんなに詰め込めません.

マルチpacket転送:
例えば2000Byteを送信したいとすると、USB controller ICが適当にぶつ切りしてくれて、自動的に複数のpacketに分けて送受信してくれます.記憶がウロですが、512Byteまでなら1packetに載るけどそれ以上なら複数packetになるよな感じかと思います.
ヘッダ情報などによる損失を避けるためには、500Byteを4回送信するよりも、2000Byteを1回送信する方が高速化できると想像されますが、実際にその通りになります.

実測16MB/Sec:
USB2.0ではどのくらいの転送レートが出るものなのかを実験したことがあります.EZ-USBを使いました.実験条件は、EZ-USB(device)→Linux(host)、2048Byte送信 です.
最善で16MB/Secが出ました.これくらいがUSB2.0の限界なのでしょう.

USB2.0の転送レートの期待値についてはこんなところです.

ーーーー
Androidスマホで900kByte/Secしか出ないのはあまりにもショボイので改善するには?
律速はスマホだと判っています.スマホ側の改善プランとは、、、

1)マルチpacketにする
残念ながら、STM32のsample codeはバグっていて、マルチpacketが動かないらしい.これはかなりfuckなバグです.しかも未解決だとか.なので900kByte/Secを出したのは511Byte転送のsingle packetでした.

2)packet受信を別スレッドでやる
これは当然そうするべきでしょう.
900kByte/Secの時はメインスレッドで受信していました.
しかしKotlinでスレッドで受信するのがよくわかりません.お勉強中なう.

3)スマホをUSB hostにする
↓900kByte/Secの時は、STM32がUSB host、スマホはUSB deviceでした.スマホはSTM32から押し付けられるpacketを漏れなく処理しなければなりません.しかしそれはAndroid OSにとっては酷なのかもしれません.いろいろと多忙ざんしょ?
↓そこで、データ転送の主導権をスマホに返上すべく、スマホ=USB hostに昇格させるのがいろいろと円満なのではないかと思っています.これならOSが忙しい時にオシロ処理を急かされる心配は減りますから.
最初からhostにすればよかったのですが、問題がありました.
↓OTG cableが必要になってしまう.
↓すべてのスマホがhostに成れるわけではない.少し古いスマホだとダメだったりします.OTG checkerというアプリでhost可能かどうかをチェックできます.上の写真の2016年HUAWEI製スマホはダメだったりします.

というわけで、ヨタヨタと再起動です.あまり期待しないでください.

31へ     33へ

かしこ

2022年10月17日月曜日

3Dプリンタ フィラメント接合器開発(4)V6 hotendヒーター制御

皆さんこんにちは.
「パルフェ」を買ったんだけど18禁バージョンでなくて落胆しているヒラサカです.付録のタペストリーもらったからまぁいいか・・・・

さて、フィラメントを接合しよう!

ArduinoでV6 hotendの温度制御をしました.作業風景はこんな不安定です.ご安全に!

↓target 100℃で走らせてこんな出来栄えです.ださっ! 5℃ぐらい振動してる.
MarlinのPID制御はもっと収束性がGOODですから、負け組確定でぇす.
フィラメントを接合するだけだからこれでもいいいや.
いや~、温度制御って初めてやったんですけど、思ったよりもエグいんですね.
 理由1) 加熱だけしか制御できない(冷却は自然冷却)
 理由2)loop応答が10秒ぐらいある(遅いloopはダルい)
こんなloopは嫌いだなぁ.やっぱloop応答はuSecオーダーがいいよね.

ヒーター開発はこれでオシマイなので、次はメカニカルな開発へと移行します.うまくできるかは不明です.

以下にArduino sourceを記します.

3へ   5へ

かしこ

ーーーー
#include <TimerOne.h>  タイマライブラリ

ヒーター目標温度
#define TEMP_TARGET 100 // heater target temp. [degree]
ヒーターduty初期設定
#define HEATER_DUTY_START 50 // initial heater duty [%]

サーミスタ抵抗→温度変換テーブル (長いので省略.連載2回目を参照)
volatile const float t[] PROGMEM ={-30,-29,-28,-27,-26,-25,-24,-23,...
volatile const float r[] PROGMEM ={1733.2,1630.408,1534.477,....

int duty = HEATER_DUTY_START; // initial duty %

void setup() {
  Serial.begin(9600); COMシリアル9600bps
  pinMode(LED_BUILTIN, OUTPUT); LEDチカチカポート
  Timer1.initialize(200000); // uSec 0.2秒周期で12Vをチョッパする
タイマ1duty設定のおまじない
  Timer1.pwm(9, duty / 100.0 * 1023.0); // pin9 or pin10, duty=N/1024
タイマ1のコールバック関数設定
  Timer1.attachInterrupt(cb_tim1);
  Serial.print("heater control initial duty ");
  Serial.println(duty);
}

// call back function of timer1
int p=0;
void cb_tim1() {
  if(p==0) p=1; LEDチカチカしてるだけなので空っぽでもOK
  else     p=0; 
  digitalWrite(LED_BUILTIN, p);
}

void loop() { メインループ
  int sensorValue = analogRead(A0); サーミスタポートをADCする(12bit)
  float voltage = sensorValue * (5.0 / 1023.0); 電圧に変換する、5V full scale
  Serial.print("ADC : ");
  Serial.print(sensorValue);
  Serial.print(" voltage: ");
  Serial.print(voltage);
  // voltage = 5.0 * rth / ( rth + 4700 )
  // voltage rth + voltage 4700 = 5 rth
  // 4700 voltage = ( 5.0 - voltage) rth
  // 4700 voltage / ( 5.0 - voltage ) = rth
  float rth; // thermistor kohm 電圧をサーミスタ抵抗kΩに変換
  rth = 4700.0 * voltage / ( 5.0 - voltage ) / 1000.0 ; // kohm
  Serial.print(" [V] termistor: ");
  Serial.print(rth);
  Serial.print(" [kohm]");

  // R vs T table
  // convert rth to temperature
  int i;
  for(i=0;i<320;i++){ kΩを温度に変換する
    float rtable = pgm_read_float_near(r+i);
    if(rtable<rth) break;
    else  i++;
  }
  float ttable = pgm_read_float_near(t+i); // temp.
  Serial.print(" temp: ");
  Serial.print((int)ttable);
  Serial.print(" [deg]");

  // heater control ヒーター制御
やってることは、かなり不真面目です.
 ・目標温度に達したらヒーターOFF
 ・目標温度までの乖離が10℃より大きいならヒーターON
 ・目標温度までの乖離が10℃以下ならヒーター50%ON
  if(ttable < TEMP_TARGET) {
    if(TEMP_TARGET - ttable > 10) duty = 100;
    else duty = 50;
  }
  else duty = 0; // heater cut off

ヒーター更新
  Timer1.pwm(9, duty / 100.0 * 1023.0); // update heater duty

  Serial.print(" heater duty: ");
  Serial.print(duty);
  Serial.println(" [%]");

loop応答が10秒もあるのでヒーター更新は1秒毎で十分かなと
  delay(1000);    // wait for N mSec
}

2022年10月15日土曜日

3Dプリンタ フィラメント接合器開発(3)ヒーター動かした

フィラメント接合器をつくろう!

FETでチョッパーしてヒーターを温めることが出来ました.

↓現物はこれです.コードの先端のアルミブロックにヒーターとサーミスタがついてます.制御にはArduino Nanoを使っています.
↓回路はこれです.FETは秋月電子で買いました.EKI04036というN-MOS.50Aぐらい流せるみたいよ.3.3Vでgate駆動するのでVthが2.5VぐらいのFETです.ヒーターはON時に3Aぐらい流れます.12V3Aなので36Wのヒーターなのでしょう.
ヒーター電源は12V.
ArduinoでFETをON/OFFして12Vをチョッパします.
ArduinoのFET制御ピンはD9です.タイマー1のPWMがD9に出てきます.
(サーミスタはArduino ADCのA0へ接続しますが、今回は使いません)

タイマーのPWMとは、タイマーのカウンタがある値以上ならHIGHになる仕組みです.「ある値」を上下させるとdutyを変えられ、ヒーターを制御できます.CPUはdutyをセットするだけで、ON/OFFはタイマのハードウエアに自走させときます.らくちんです.

↓Arduinoソースはこれです.まだLOOP制御してません.

#include <TimerOne.h>   ←タイマライブラリ

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
タイマの設定が3つ.
タイマ周期0.2秒.ヒーターON/OFF周期が0.2秒.
  Timer1.initialize(200000); // uSec
PWM出力ポートD9、duty=100/1024  この100がヒーター強度になる
  Timer1.pwm(9, 100); // pin9 or pin10, duty=N/1024
タイマ割り込みのコールバック関数名cb_tim1
  Timer1.attachInterrupt(cb_tim1);
}

タイマのコールバック関数.LEDチカチカさせてるだけなので空っぽでもOK.
int p=0;
void cb_tim1() {
  if(p==0) p=1;
  else     p=0; 
  digitalWrite(LED_BUILTIN, p);
}

PWMはタイマーに自走させてるため、メインループは何も仕事しなくてOK.
あとでLOOPを組み込むのはここだが今は空っぽ.
void loop() { }

2へ   4へ

かしこ

2022年10月14日金曜日

3Dプリンタ フィラメント接合器開発(2)Adruino PROGMEMなんだかなー

フィラメント接合器をつくろう!
↓フィラメントの切れ端がもったいない.

V6 hotendを流用してフィラメントを溶かすヒーターを作ります.温度制御はarduinoでやります.MEGA2560なんつう高価なPCBを使う気はないです.

サーミスタで温度を測定するArduino programが今回のお題です.

回路は、サーミスタを4700Ωで5Vプルアップし、ArduinoのADC A0へ接続するだけ.これはRamps1.4と同じ回路です.
サーミスタは、NTC 3590 100kと通称されるもので、25℃で100kΩです.こちらのpdfに-30~289℃までの温度vs抵抗値テーブルがあるので、そのテーブル全部をsourceに記述して温度表示に使います.

温度vs抵抗値テーブルのsource codeの最初だけコピペするとこんなかんじ.
volatile const float t[] PROGMEM ={-30,-29,-28,-27,-26,-25,-24,-23,...
volatile const float r[] PROGMEM ={1733.2,1630.408,1534.477,1444.903,...

このPROGMEMが曲者でした.

このテーブルの総サイズは5kBぐらいあるかと思うので、ArduinoのATMEGA328とかいうCPUの2kBしかないSRAMには展開できません.それゆえ「FLASHに置け」という意味のコンパイラオプションがPROGMEMです.

ここまでは別に問題は無かったのですが、参照時に問題あり.
r[i]みたく参照しても化けてて動きません.
Arduino IDEの処理系のバグかと思ってVS codeに変えても動きません.

解決しました.
PROGMEMに置いた例えばfloatを参照する場合は、こうしなくちゃいけないんだってさ.
 float rtable = pgm_read_float_near(r+i);
なんだよめんどくさい.
変数を置くセグメントがあっちかこっちかというだけじゃないの?
なのにさ、どうして参照するのにpgm_read_float_near(r+i)ってしなくちゃいけないのかねぇ?
しかも、r+iみたくポインタ引数じゃないとダメで、r[i]のような配列指定だとダメ.
まるでEEPROMの取扱いじゃんかといいたい.

そんなかったるい壁がありましたが、温度測定は無事出来ました.ライターで炙ったら温度上昇しましたし.

次はヒーターの制御へ.
これからFET買いに秋月電子へいきまーす.(手持ちでいいFETが無かった)

1へ    3へ

ーーーー
参考までに、温度測定のソースコードを貼っておくね.VS codeのsourceだけど、Arduino IDEでも動くと思うけど、知らんけど...

#include <Arduino.h>

volatile const float t[] PROGMEM ={-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,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,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289};

volatile const float r[] PROGMEM ={1733.2,1630.408,1534.477,1444.903,1361.22,1283,1209.327,1140.424,1075.949,1015.588,959.05,906.0117,856.2883,809.6506,765.8865,724.8,685.6507,648.8929,614.3649,581.9169,551.41,522.6908,495.6674,470.2286,446.2714,423.7,402.056,381.6658,362.4488,344.3301,327.24,311.0397,295.7506,281.3157,267.682,254.8,242.5827,231.0321,220.108,209.7724,199.99,190.5578,181.6319,173.1822,165.1804,157.6,150.4253,143.6234,137.1726,131.0528,125.245,119.6582,114.3559,109.3221,104.5415,100,95.8191,91.8392,88.0494,84.4395,81,77.6238,74.4091,71.3472,68.4301,65.65,62.9838,60.442,58.0181,55.706,53.5,51.3708,49.3391,47.3998,45.5483,43.78,42.0555,40.4092,38.8369,37.335,35.8999,34.616,33.3855,32.2059,31.0748,29.99,28.9053,27.866,26.87,25.9153,25,24.1099,23.2565,22.4381,21.6531,20.9,20.1741,19.4774,18.8087,18.1666,17.55,16.9459,16.3659,15.8089,15.2739,14.76,14.2813,13.8206,13.3774,12.9507,12.54,12.1347,11.7447,11.3694,11.008,10.66,10.3243,10.001,9.6895,9.3893,9.1,8.8171,8.5444,8.2816,8.0283,7.784,7.5538,7.3316,7.1172,6.91,6.71,6.5265,6.349,6.1772,6.0109,5.85,5.6832,5.5221,5.3663,5.2156,5.07,4.9291,4.7928,4.661,4.5334,4.41,4.2906,4.1751,4.0632,3.9549,3.85,3.741,3.6357,3.5338,3.4353,3.34,3.255,3.1726,3.0927,3.0152,2.94,2.8634,2.7893,2.7173,2.6476,2.58,2.5144,2.4507,2.389,2.3291,2.271,2.2135,2.1577,2.1035,2.051,2,1.9513,1.904,1.858,1.8134,1.77,1.7319,1.6947,1.6586,1.6233,1.589,1.552,1.516,1.4811,1.4471,1.414,1.3812,1.3494,1.3184,1.2883,1.259,1.2301,1.2019,1.1745,1.1479,1.122,1.0956,1.0699,1.0449,1.0206,0.997,0.9757,0.955,0.9348,0.9151,0.896,0.875,0.8547,0.8349,0.8157,0.797,0.7806,0.7646,0.749,0.7338,0.719,0.7029,0.6873,0.6721,0.6574,0.643,0.6302,0.6177,0.6055,0.5936,0.582,0.5717,0.5617,0.5519,0.5423,0.533,0.5225,0.5122,0.5022,0.4925,0.483,0.4733,0.4639,0.4547,0.4457,0.437,0.4284,0.42,0.4118,0.4038,0.396,0.3884,0.381,0.3739,0.3668,0.36,0.3533,0.3467,0.3403,0.3341,0.328,0.322,0.3161,0.3104,0.3048,0.2993,0.294,0.2888,0.2836,0.2786,0.2737,0.2689,0.2642,0.2596,0.2551,0.2507,0.2464,0.2422,0.238,0.234,0.23,0.2262,0.2223,0.2186,0.215,0.2114,0.2079,0.2045,0.2011,0.1978,0.1946,0.1914,0.1883,0.1853,0.1823,0.1794,0.1765,0.1737,0.171,0.1683,0.1656,0.163,0.1604,0.1579,0.1555,0.1531,0.1507,0.1484,0.1461,0.1439,0.1417,0.1396,0.1375,0.1354,0.1334,0.1314,0.1295,0.1275,0.1257,0.1238};

void setup() {
  Serial.begin(9600); // display to COM port
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  int sensorValue = analogRead(A0); // ADC port is A0
  float voltage = sensorValue * (5.0 / 1023.0); // 10bit ADC, max 5V
  Serial.println(voltage);
  Serial.println(sensorValue);
  // voltage = 5 * rth / ( rth + 4700)
  // voltage rth + voltage 4700 = 5 rth
  // 4700 voltage = (5-voltage) rth
  // 4700 voltage / ( 5 - voltage ) = rth
  float rth; // thermistor kohm
  rth = 4700 * voltage / ( 5 - voltage ) / 1000 ; // kohm
  Serial.println(rth);
  int i;
  for(i=0;i<320;i++){
    float rtable = pgm_read_float_near(r+i);
    if(rtable<rth) break;
    else  i++;
  }
  float ttable = pgm_read_float_near(t+i);
  Serial.println(ttable); // display temperature

  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on
  delay(100);         // wait for mSec
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off 
  delay(500);         // wait for mSec
}

2022年5月13日金曜日

【Android USB oscilloscope】(31) STM32-スマホのUSB転送レートが上がらない

余っているAndroidスマホをオシロにしよう!

performance tuningって好きなんです.STM32-スマホとのUSB2.0接続で転送レート向上に取り組み中.

なのですが、滅亡しかかっています.

STM32 hostの送信条件「DMA、511Byte転送、single packet」で900kBytes/Sec しか出ない.5MBytes/Secぐらい出て欲しいのだけど.(今はメインスレッドでUSB readしてます.サブスレッドでやったら高速化できるかもしれない)

STM32をDMA→非DMAに変更すると500kBytes/Secに落ちる.まぁこれは仕方ない.

律速はスマホだと判っています.スマホの内部処理が遅いのはさもありなん.
ちなみにスマホのUSB送受信はIO stream方式です.

一方、STM32はこの10倍で送出できるとの感触は得ています.

こうゆう状況でさらにレートを上げようとしたらやれることは、multi packet転送にすることです.すなわち、1つのUSBヘッダに連なるdataを増やすことです.「DMA、2000Byte転送、multi packet」に条件を変えてみる.

ところがバグってこれがいて動きません.multi packetが動かない.multi packetだとエラーIRQが立ちまくる.この現象はSTM32シリーズに偏在するバグらしく、海外サイトで多く取り沙汰されています.「STM32はmulti packetに対応してますか?」「STM32をmulti packetにするには?」みたいなスレが立っている有様です.なんという絶望の香り.
hardwareにこんなプアなバグがあるとは考えにくいです.かといってsample programにいつまでもこんなバグが残ってるっていうのもどうかと思う.なにが起きてるんだかねー

DMAでワード境界が悪さしてたりしないかと非DMAも試してみました.
 「DMA、511Byte転送、1packet」  これよりByte数を増やすと死ぬ
 「非DMA、768Byte転送、2packet」 これよりByte数を増やすと死ぬ
なんだか微妙.

先へ進む目途が全然たたないっす.滅亡.

30へ     32へ

かしこ

2022年5月12日木曜日

【Android USB oscilloscope】(30) Kotlinで大域変数を使う Companion object

余っているAndroidスマホをオシロにしよう!

USB BULK転送で転送レートがどこまで出るのかを試そうとしています.
転送レートを制限する要因は、STM32 host、USB2.0、スマホ があるわけですが、USB2.0の理論限界480Mbpsまでは絶対にいけません.経験的に16MByte/Secが出たらネ申ってとこ.10MByte/Secでも褒めてあげたいレベル.
STM32 firmwareは内部でDMA使ってると思うのでtune upは難なく完了すると予想しますが、スマホはどうなっちゃうんですかね? バックグランド処理が邪魔するのでpacket落ちしない程度までレートを下げるとかなり遅くなってしまうというオチが予想され背筋が寒いです.
(現在800kByte/Secというショボさで泣く....)

レートはさておき、今回の投稿はKotlinの大域変数についてです.

かつてこんな投稿をしたことがあります.Kotlinで大域変数を使いたいのだけれどどうやったらいいのかな?という疑問でした.
この投稿の時点での知見では大域変数的な運用するのはやればできる.大域変数ばかり集めたobjectを1つだけ作り、そいつを他classのobjectに引数で渡してやればいい.実際にそれで大域変数的運用をやっています.

なのですが、、、そんなめんどくさいことをしなくてもモロに大域変数みたく使う仕組みがKotlinに在ると知りました.それが Companion object です.

実例を挙げます.こんなクラスを作ります.classの中にcompanion object{...}を書くだけです.中身には大域運用したい変数を書きます.
class USBparams {
    companion object {
        var model : String? = ""
        var manufacturer : String? = ""
        var serial : String? = ""
        lateinit var timeStart : LocalTime
        lateinit var timeNow : LocalTime
    }
}
そして、これをどうやって呼び出すのかがキモなわけですが、こんな特徴があります.こんな魔法のような仕様があっていいんか(笑).
・companion objectを複数個生成しても、参照先は1つのobjectに絞られる ←魔法
・それどころかobject生成しなくていい  ←なんたる掟破り
・companion objectのメンバ変数を呼び出すには、他のclassからでもいきなり、USBparams.model=”TS-1” のように呼び出せる  ←なんたるアバウト

Kotlinには大域変数は無いと云われますが、これなら事実上大域変数みたいなもんじゃないかな? 抜け道ですかぁ?

Kotlinはマイナー仕様が後から湧いて出てくるので困るじゃん.

29へ    31へ

かしこ

2022年5月8日日曜日

【Android USB oscilloscope】(29) STM32とスマホでBULK転送うまく動いたらしい(重箱の隅)

余っているAndroidスマホをオシロにしよう!

BULK転送がなかなかうまく動きませんでしたがようやく動いたみたいです.
STM32CubeIDEのproject詰め合わせは後ほどupします.下の方を見てください.

今回も重箱の隅なのでご注意ください.

STM32CubeIDE→MiddeWare→virtual COMが吐き出すsample codeをひな形にしています.しかし単純なBULK転送をどうやったら良いのだ?という素朴な疑問には答えてくれません.困ってしまってかれこれ1weekが経ちました.

USBプロトコルアナライザ(ハード)なんつう高級機材は持ってないので、内部状態をdumpさせたりして問題抽出に苦労しました.

【問題】
BULK送信がpacket落ちする.
大雑把にいえばこんなトラブルが起きている.
 hostは毎秒送信したい
  →deviceが速やかに受信するとは限らない
   →やがてbuffer overflowで死亡
    →死んでるのがhostなのかdeviceなのかは不明
期待としては、deviceがもたついているなら再送信してもよいのでpacket落ちだけは回避したい.

【問題の詳細】
不活性なdeviceの状態をhostがどうやって察知するかが焦点でした.

deviceが活性なとき:
STM32 hostがBULK転送すると、STM32内部にはHC_XFRCフラグが立つ

deviceが不活性なとき:
STM32 hostがBULK転送すると、STM32内部にはHC_NAKフラグが立つ

フラグの挙動はこんなもんでしょうが運用にコツが要るかんじ.

HC_NAKだったらどう対処するか?
deviceがACKになるまでdeviceのstatusをpollingし続ければいいとフツーは考えます.ところが、USB_status()みたいな関数はあれど、STM32内部のwork areaのオウム返しなのでdeviceの最新情報ではないため役に立たないんです.つかえねぇー

sample codeのどこを見てもdevice statusをpollする関数なんかありません.そんなバカなと思ってさんざ探しましたけど無いんです.ならば割り込みかと探してもそんなのは無い.どうしたらいいんだ?

【解決の作法】
「device statusをpollする関数が無い」のですから、別の手段でpollするしかありません.
別の手段とは、、、再度BULK送信を行う というのがUSBの作法であるようです.
すなわちこういうこと.
 前回の結果を見る
  →deviceが不活性(NAK)だったなら旧いdataを再送する
  →deviceが活性(ACK)だったなら新dataを送る
   →新しい結果がSTM32のメモリ上に残される
ゆえにdeviceが受信バッファを消化してくれない限りdata再送で無限loopします.

肝の部分のcodeはこうなります.
①で判るのは、前回の送信の終了時のstatusです.最新のstatusではありません
②HC_IDLE:前回の送信後にUSBがIDLEの意 →新dataを送信する  ※再送すべきかもしれませんが確信持てません
 HC_XFRC:前回が送信完了だった意 →新dataを送信する
 HC_NAK:前回の送信時にdeviceが不活性だった意 →前回dataを再送する
③④dataを準備する.IDLEかXFRCなら新data.NAKなら前回dataのまま
⑤BULK転送関数
HCD_HCStateTypeDef r;
r = HAL_HCD_HC_GetState(phost->pData, phost->Control.pipe_out); ①
if(r == HC_IDLE || r == HC_XFRC || r==HC_NAK) { ②
   if(r!=HC_NAK) N++; ③
   sprintf((char*)buffer,"bulk transfer test %d\n",N); ④
  USBH_BulkSendData(phost, ⑤
                    buffer,
                    strlen((char*)buffer),
                    phost->Control.pipe_out,
                    1);
}
上位レイヤーでもっとエレガントにやれるように仕組まれている可能性はあります.しかしそこまでcodeを見切れてない.

【試験環境】
諸条件:
CPU STM32F205
スマホ Huawei P9 lite
STM32自作プリント基板  (ミスを含む回路図)
接続はUSB2.0
スマホ接続はAOA

code:
project folder詰め合わせをupしときます.
開発環境は、スマホアプリはAndroid Studio、STM32はSTM32CubeIDE.
スマホのアプリ →使い方はこちら.ボタンを押すとUSBを読んで表示するだけのもの
STM32 project →STM32CubeIDEが出力するVirtuelCOM(CDC) sampleをひな型に改造を加えました.test用codeを多く含むのですげー汚いです.test codeはUARTに内部状態を出力しています.hira_senduart_...()がUART出力関数です.


以下はSTM32のcodeの説明です.STM32のCDC sampleをひな型にしています.

【AOA接続処理】
スマホがSTM32に接続されるとdescriptorを読んだりいろいろなUSB接続処理が行われます.sample codeの本来の意図はCDC deviceをSTM32へ接続することです.しかしスマホはCDCではありませんのでCDC設定へ進めずに途中で断念します.断念したところへAOA処理を突っ込んでいます.

AOAへ至る流れはmain()のwhile loopから繰り返し呼び出されます.
MX_USB_HOST_Process()
 →USBH_Process()
  →aoa_setupAccessory()   これがAOA処理の本体

AOAの重要部分だけ抜粋します.
int aoa_setupAccessory( USBH_HandleTypeDef *phost ) {
↓VID/PIDを取得する.これが呼び出されるまでにdescriptorは取得済なのでVID/PIDなどはメモリに在る.
 uint16_t vid = phost->device.DevDesc.idVendor;
 uint16_t pid = phost->device.DevDesc.idProduct;
↓既にAOA接続してる場合はおしまい.VID/PID=18D1/2D01か18D1/2D00ならAOA.
 if( vid == ACCESSORY_VID && pid == ACCESSORY_PID ) {
   aoa_ok = 1;
   return 0;
 }
 else if( vid == ACCESSORY_VID && pid == ACCESSORY_PID_ALT ) {
   aoa_ok = 1;
   return 0;
 }
↓違うVID/PIDだったらAOA接続処理を始める
 else {
↓control転送でスマホのAOA versionを採取する.ゼロだったらAOAをサポートしてないのでexitする.なぜかいつもver2みたいよ.
   aoa_usb_ctrlreq( phost, 0xC0, 51, 0, 0, buffer, 2, 0);
   devVersion = buffer[1] << 8 | buffer[0];
   if(devVersion==0) return -1;
↓謎のdelay 50mSec.長くしすぎないこと.→その理由
   USBH_Delay(50);
↓スマホに"bangflat","aoatest"の文字が登録されているかどうかをチェックする.この文字はスマホアプリに記述してあるもの.アプリがスマホにインストされていればOK
   strcpy((char*)buffer,MANUFACTURER); ←bangflat
   aoa_usb_ctrlreq(phost,0x40,52,0,0,buffer,
                strlen((char*)buffer),0);
   strcpy((char*)buffer,MODEL);  ←aoatest
   aoa_usb_ctrlreq(phost,0x40,52,0,1,buffer,
                strlen((char*)buffer),0);
↓続けて謎のコマンドを送るとスマホの接続が勝手に切れて、AOAのVID/PIDで再接続してくる
   strcpy((char*)buffer," ");
   aoa_usb_ctrlreq(phost,0x40,52,0,2,buffer,1,0);
   aoa_usb_ctrlreq(phost,0x40,52,0,3,buffer,1,0);
   aoa_usb_ctrlreq(phost,0x40,52,0,4,buffer,1,0);
   aoa_usb_ctrlreq(phost,0x40,52,0,5,buffer,1,0);
   aoa_usb_ctrlreq(phost,0x40,53,0,0,buffer,0,0);
   USBH_Delay(50);
↓上と同じようにVID/PIDチェック
   if( vid == ACCESSORY_VID && pid == ACCESSORY_PID ) {
      aoa_ok = 1;
      return 0;
   }
   else if( vid == ACCESSORY_VID && pid == ACCESSORY_PID_ALT ) {
      aoa_ok = 1;
      return 0;
   }
   else {
      aoa_ok = 0;
      return -1;
   }
 }
}
AOA処理ルーチンは以上です.謎のコントロールコマンドはそうゆうもんだと思うしかないですね.

上で出てくるaoa_usb_ctrlreq()は何をやっているのか?
↓まず引数はUSBコントロールコマンドの羅列です.
int aoa_usb_ctrlreq(
  USBH_HandleTypeDef *phost,
  uint8_t bmRequestType,
  uint8_t bRequest,
  uint16_t wValue,
  uint16_t wIndex,
  uint8_t *pbuf,
  uint16_t wLength,
  uint16_t timeout){
  do{
↓構造体へコントロールコマンド情報を記入.CMD_SENDは「送り終わった」という意味だったかな
   if (phost->RequestState == CMD_SEND) {
     phost->Control.setup.b.bmRequestType = bmRequestType;
     phost->Control.setup.b.bRequest = bRequest;
     phost->Control.setup.b.wValue.w = wValue;
     phost->Control.setup.b.wIndex.w = wIndex;
     phost->Control.setup.b.wLength.w = wLength;
   }
  }
↓OKフラグになるまで何度もloopするというなんだかなーな構造になっている.裏でシーケンサが上手くやってくれてます
  while(USBH_CtlReq(phost, pbuf, wLength)!=USBH_OK);
  return USBH_OK;
}

【AOA後のOPEN処理】
AOA接続ができたとして、次はBULK転送するpipeを作ってやらなくちゃいけません.
main()のwhile loopで行います.
①は上で述べたAOA接続をやってくれます.AOA接続が完了したらaoa_ok=1になる
②はAOA接続できたら、、、、
③はOPEN処理
④はopen完了の意味のフラグを立てとく
  while (1)  {
    MX_USB_HOST_Process(); ①
    if(aoa_ok==1) { ②
    USBH_SelectInterface(&hUsbHostHS, 0); ③
    if(aoa_getPipe(&hUsbHostHS)!=0) break; ③
     aoa_openPipe(&hUsbHostHS); ③
     aoa_ok=2; ④
    }
  }

aoa_getPipe()は未割当のPipe Numberを取得します.コントロールパイプは0と1なのでここでは2と3になるのかな? それだけでなく、Pipe NumberとEndPoint Numberとの対応付けもします.構造体の深いところに欲しいデータがあります.
ぶっちゃけこんな感じになるんです.
 パイプ2 IN  EP83
 パイプ3 OUT EP02
int aoa_getPipe( USBH_HandleTypeDef *phost ){
 uint8_t NumEndpoints =
   phost->device.CfgDesc.Itf_Desc[0].bNumEndpoints;
 if(NumEndpoints!=2) return -1;

 uint8_t ep1 = 
  phost->device.CfgDesc.Itf_Desc[0].Ep_Desc[0].bEndpointAddress;
 uint8_t ep2 = 
  phost->device.CfgDesc.Itf_Desc[0].Ep_Desc[1].bEndpointAddress;
 uint8_t ep_in,ep_out;
 if( (ep1 & 0x80) == 0x80 ) {
   ep_in = ep1;
   ep_out = ep2;
 }
 else {
   ep_in = ep2;
   ep_out = ep1;
 }
 phost->Control.pipe_in  = USBH_AllocPipe(phost, ep_in);
 phost->Control.pipe_out = USBH_AllocPipe(phost, ep_out);
 return 0;
}

【OPEN後のBULK送信】
ようやくBULKです.現在は試験的に動かすだけなので、TIM7の1秒割込みでBULK送信しています.aoa_ok=2になっていたらOPEN完了してますのでBULK送信します.
void TIM7_IRQHandler(void) {
   if(aoa_ok==2) aoa_bulkTransfer(&hUsbHostHS);
}

aoa_bulkTransfer()は【解決の作法】で説明済です.
void aoa_bulkTransfer( USBH_HandleTypeDef *phost ) {
 HCD_HCStateTypeDef r;
 r = HAL_HCD_HC_GetState(phost->pData, phost->Control.pipe_out);
 if(r == HC_IDLE || r == HC_XFRC || r==HC_NAK) {
   if(r!=HC_NAK) N++;
   sprintf((char*)buffer,"bulk transfer test %d\n",N);
   USBH_BulkSendData(phost, 
                 buffer, 
                 strlen((char*)buffer), 
                 phost->Control.pipe_out, 
                 1);
 }
}

今宵はこれまでにしとうございます.これで先へ進めます.

28へ    30へ

かしこ

2022年5月5日木曜日

【Android USB oscilloscope】(28) STM32でBULK転送うまく動かず

余っているAndroidスマホをオシロにしよう!

通常人は海に山にと忙しいGWですが、わたしは北千住で飲んだあの日以外は引きこもってSTM32のBULK転送を動かそうとしています.がっ、うまく動きません.もう10日間ぐらい悩んでいるように思うけど、せいぜい5日間ぐらいでした.

STM32→スマホのBULK転送は一応動いたんです.しかしバリバリとpacket落ちしてる.

Net検索すると「STM32でBULK転送するやり方を教えて」的な海外のスレがちょくちょく在るけれどモロ参考になる投稿にはお目にかかってません.単純明快にBULKのpacket送信するやり方を知りたいだけなのですがね、そうゆうのが無いんだよ、くそぅ.

そんなわけで、STM32CubeIDEが吐き出すMass Storage Class(MSC)をひな形にして改造しているのだけれど、MSCは複雑すぎてcodeを追うのがつらいです.単純にBULK転送する場面は何処にもなくて、MSC標準のFATアクセスする構造体をガツガツ形成して見通しが悪くてかなわん.
現状でかろうじて動いているBULK転送は、MSCのclass用codeを無視して、low level関数を組み合わせて自己流で組んだもの.BULK送信はできていると思うんだが、スマホから打ち返される完了ステータスの処理が死んでいてbuffer overflowで死亡という有様と思われます.

ちなみに、連載16回目でAOA及びBULK転送の動作確認をしたときのhostはLinuxのlibusbでした.libusbではスマホの受信完了packetを待ってから次のBULK packetを送信していました.ところが、STM32の現状ではスマホのステータスなんかお構いなしに撃ちまくって死んでる模様.

どうもMSCをひな型にしたのは失敗だったみたいです.CDCの方がcodeが簡単なのでCDCに乗り換えようと思っているところ.作っては捨てを延々と繰り返す他にアセンションへの道など無い.

ーーーーーーー
ぎょえーっと思っちゃったネタを2つ挙げとく.

1)Android StudioのLogcatでなぜかLog.d()が表示されない
これねぇ、Huawei端末だけの特殊事情だそうです.アプリの動作速度低下防止のため、HuaweiのOSがLog.d()を抑圧している.解除するには、、、 →こちら

*#*#2846579#*#* にダイアルすると謎の設定画面が出るんです.グラディウスかこのヤローと端末をぶん投げたくなりました.上上下下左右......

2)Androidスマホのデバッガwifi接続
wifiデバッグといえばAndroid11.
この投稿でAndroid11を入手したのですが、作業服のポケットに入れたまま洗濯してしまってお陀仏にしちゃいました.仕方なくAndroid7のHuaweiスマホを使ってデバッグしています.

デバッガwifi接続はAndroid11じゃないとダメというのは間違いで、11より古いOSでも面倒くさいけどwifiデバッグできます.まぁwifiデバッグはいつも調子が悪く、すぐに接続が切れるので使いづらいのですが、今回はOS7ということでOS11よりもトラブルが多発しました.

トラブル回避するためのwifiデバッグ接続手順(OS10以前)
0)adbのpathを通しておく
わたしのwindowsではこんな辺鄙な場所がAndroid SDKのインスト場所です.
    C:\Users\hira\AppData\Local\Android\Sdk

1)スマホの設定画面→スリープ無しにできればする.できなければ10分とか長くする

2)スマホの開発者向けオプションにて
    ・USBデバッグ
    ・スリープモードにしない
    ・充電専用モードでADBデバッグを許可

3)USBケーブルでhostマシンと接続する、デバッグダイアログが出るかもしんない

4)hostマシンとスマホを同じWIFI APへ接続する
スマホのwifi設定画面でIPアドレスをメモっておく

5)windowsなら管理者モードでpowershellを起動

6)shellでコマンドを打つ
    ping 192.168.10.104   ←スマホのIP
    adb kill-server
    adb start-server
    adb emu kill    ←へんなemulatorが残ってることがあるため
    adb usb
    adb tcpip 5555    ①
    adb connect 192.168.10.104  ②
    adb devices

7)USBケーブルを抜いてOK

8)せっかく接続できたとしても、スマホがスリープに入ると切れてしまうので、USBケーブルを再度接続し、6からやり直す

①でこうゆう表示が出ればOK
restarting in TCP mode port: 5555

②でこうゆう表示が出たらOK
connected to 192.168.10.104:5555

失敗すると②でこんな表示が出ます.うぜー
cannot connect to 192.168.10.104:5555: 接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませ んでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。
その時は①と②をやり直すと接続できるケースが多いです.(100%とは言えない)

27へ    29へ

かしこ

2022年5月2日月曜日

【Android USB oscilloscope】(27) STM32でAOA 重箱の隅

余っているAndroidスマホをオシロにしよう!

STM32をUSB hostにする取り組みで悩んでいましたが、少し前進しました.
なお、今回は重箱の隅ですので重箱の隅を覗きたい人だけ下へ進みましょう.

【いきさつ】
こんなブロックでスマホの画面をオシロ画面として活用したいというのが最終目標.
開発要素はこの3つ.
・ハードウエア(STM32)  ←入手難で絶賛死亡中
・STM32のfirmware(STM32CubeIDE C言語)
・スマホアプリ(Kotlin)
今はSTM32 firmwareをやってるところ.
firmwareのひな型としてこの画面のMiddleWareを利用しています.
スマホに外部からUSB dataを流し込むには、AOAという聞きなれないUSBプロトコルを使います.AOAについては過去投稿を参照してください.ひな形firmwareにAOAは実装されてません.
【Android USB oscilloscope】(16) AOA --- Android~Linux hostとのUSB BULK通信 (化物)

AOAは曲者です.AOAこんな手順でUSB接続します.
・スマホにUSBを挿すと、、、
・最初はMass Storage接続だったりするのは誰もが見る光景なのだが、、、 ①
・裏でUSB hostが特殊コマンドを送信すると、、、
・スマホがUSB接続を一旦切断し、別のVID/PIDで再接続してくる
・再接続したスマホはAOA接続になっている   ②

①はUSB classなので型にはまっていて使いづらい.②はclassではないのでユーザーが好き勝手出来るのでわたしにとってはAOA接続の方が便利.それがAOAを使う理由です.

本シリーズの過去投稿で、PC(Linux)でAOA接続の動作確認はできています.
なので現在取り組んでいるのは、同じものをSTM32 USB hostに移植すること.

だけどなかなか上手く動かなかったんだよね.数日間悩んでた.

【動かないってどこが?】
・Linux hostなら動くけれど、STM32 hostだと動きません
・AOAの特徴である、接続断→再接続のときに死にます
・codeを眺めていてもおかしな点はない
・USBプロトコルアナライザ(wireshark)でおかしなpacketは飛んでない
・接続断処理が正常に完了してないので再接続が滞っているのではないか?
・接続断処理に要する時間をオシロで観測したら1秒ぐらいかかっている  ③
・接続断→再接続の間隔を実測したら250mSecしかない  ④
・③>④なので死んでいる

【USBの原則とAOA】
一般にCPUが外界の事象にコミットするには割り込みを使わないと上手くいきません.USBでも同様で、USBの様々なイベントに即応するべく割り込み(callback関数)がたくさん用意されています.

ただし「callback多数」はUSB deviceの場合に限ります.USB hostはほとんど割り込みを使いません.それはSTM32CubeIDEが吐き出すsample codeを眺めると分かります.USB hostではcallback関数が3つしかないんです.
 1)USB disconnect callback
 2)USB connect callback
 3)SOF callback
これはUSB deviceに比べたら鬼のように少ないです.てか割り込み処理しようという気が基本的に無い設計思想といえます.

後日訂正:setupに割込みは少ないですが、data転送にはIRQが盛んに発生しているようです.調査中.

どうしてこんな仕組みになっているのか?
USBではデータの流れを全てhostが握っています.hostはdeviceに「しゃべっていいぞ」と命じ、deviceは返答します.
deviceが勝手にイベントを発生させる場面は「基本的に」ありません.

「基本的に」というからには例外があるものでして、hostのcallback関数にも見られる通りこの2場面では「deviceがイベント発生」させてもOKOK.人力でケーブル引っこ抜くのに時間制限なんかないですからね.
 1)deviceが落ちるとき
 2)deviceが接続するとき

なので、AOAが接続断→再接続するのはスマホが好き勝手にやります.わたしがdebugに使っているHuweiのスマホでは接続断→再接続の間隔はたったの250mSecです.1秒程度の余裕があってもいいんじゃね? これだから接続断→再接続ってあまり関わりたくないです.

【余談】
USB機器には、USBメモリのような高速応答の物もあれば、プリンタのような応答速度が数秒かかる物もあります.

LinuxのUSBの挙動をプロトコルアナライザを見ているとこんな場面が多々あります.
 プリンタへ要求
  ↓
 USBメモリへ要求
  ↓
 USBメモリが応答
  ↓
 プリンタが応答   ←順序おかしい
つまり、hostは要求を乱れ撃ちします.結果としてdeviceからの応答も因果関係無視の乱れ撃ちになります.driverがキューを参照して対応関係を並び替えしてるんでしょう.

ところが、STM32のsample programは乱れ撃ち非対応です.ゆえにやたら応答が遅いdeviceが在ったらhostの処理が滞ります.(RTOSを実装したら違うかもだけど)

【余談2】
STM32 sample programにおいては、hostはdevice応答をいつまでも待ち続けると書きました.また、hostは割り込みをほとんど使わないと書きました.

では.hostはどこで待っているんでしょうか?
大雑把にいえばmain loopです.ここでpollingしまくってます.なんか懐かしい感じw
main() {
    while(1) {
        polling()  ←ここでフラグをチェックしつつ待つ
    }
}


以下はcodeの説明などをするつもりですがそれは後ほど追記します.project folder詰め合わせもDLできるようにします.

追記: BULK転送まで一気に実装してsource codeを公開しようと思ったのですが、BULK転送が全然動かないわSTM32がhung-upするわで挫折.今宵はここまでにしとう御座います.

26へ    28へ

かしこ

2022年4月27日水曜日

【Android USB oscilloscope】(26) STM32 hostはMass-Storageを挿されて何をするのか

余っているAndroidスマホをオシロにしよう!

STM32をUSB hostにする取り組みの本日は3日目.

STM32CubeIDEが自動生成してくれるUSB Mass Storage Class(MSC) sample prog.を焼いてみました.IDE画面ではMiddleWareの項目で生成します.

MSCのcodeを読んでいるのですがいつもの通りよくわかりません.
Mass Storage Class(MSC)は最終的な目標ではなく、知りたいのはBULK転送関数の運用手順です.
BULK転送手順を知るために、USBメモリにアクセスする関数の中を調べたのですがBULK転送なんか書かれてないんだよこれが.なんでかというと、MSCに「FATにセクタ読み書きするUSBコマンド」が実装されているのでそれを使っているためです.便利すぎて役に立たん.

というわけで、DDCの初期の頃にやってたみたいにcontrol転送をdumpさせて何やってるのかを逐一確認するという地道な作業の始まりとなりました.control転送は、
usbh_ctlreq.c→USBH_CtlReq()で行われています.

下記のdumpは、USBメモリが挿され、STM32 hostがUSB接続完了するまでのlogです.
USBメモリのderscriptorをこちらにupしておきます.
80 06 0100 0000 0008のような8bytesの数字がUSB control転送です.USB規格書と首っ引きで比較すると意味がわかります.

丸数字はcontrol転送と表示結果の対応を示しています.MSC classが開始した後で生じたcontrol転送⑧はMSC classのものです.①~⑦は通常のUSB2.0のものです.まぁ想定内のことしかやってないね.

USB Device Connected
USB Device Reset Completed
80 06 0100 0000 0008 GET DEVICE DESC. 8bytes
80 06 0100 0000 0012 GET DEVICE DESC. 18bytes ①
PID: 6544h ①
VID: 30deh ①
00 05 0001 0000 0000 SET ADDRESS 1 ②
Address (#1) assigned. ②
80 06 0200 0000 0009 GET CONFIG DESC. 9bytes
80 06 0200 0000 0020 GET CONFIG+INTERFACE DESC. 32bytes ③
80 06 0301 0409 00ff GET STRING1 DESC. 256bytes ④
Manufacturer : KIOXIA ④
80 06 0302 0409 00ff GET STRING2 DESC. 256bytes ⑤
Product : TransMemory ⑤
80 06 0303 0409 00ff GET STRING3 DESC. 256bytes ⑥
Serial Number : 0022CFF6B8A4C471A01239BE ⑥
Enumeration done.
This device has only 1 configuration.
00 09 0001 0000 0000 SET CONFIG 1 ⑦
Default configuration set. ⑦
Switching to Interface (#0) ③
Class    : 8h ③ Mass Storage class
SubClass : 6h ③ SCSI command support
Protocol : 50h ③ BULK transfer only
MSC class started.
a1 fe 0000 0000 0001 ⑧ GET logical unit number
Number of supported LUN: 1 ⑧
LUN #0:
Inquiry Vendor  : KIOXIA
Inquiry Product : TransMemory
Inquiry Version :
MSC Device ready
MSC Device capacity : 2615672320 Bytes
Block number : 30274559
Block Size   : 512

25へ    27へ

かしこ

2022年4月26日火曜日

【Android USB oscilloscope】(25) storage class動いてるみたい

余っているAndroidスマホをオシロにしよう!

STM32をUSB hostにする取り組みの本日は2日目.

STM32CubeIDEが自動生成してくれるMass Storage Classを焼いてみました.

実験環境の写真.コードが4つありまして、上から、
・5V電源
・USB deviceとしてUSBメモリを挿す
・STM32 writer
・COM port
すでに実験基板に致命的な馬脚が露になっています.
 ・USB hostの仕様を失念していたため、、、
 ・micro USBはhostじゃなかった →OTG cableでUSB-Aに変換する
 ・5V電源はACアダプタから供給しなくちゃいけない →つけた
基板を作ってみて気付く、そうゆう事ってよくあるよね.JLCPCBでこの失敗基板を作るのに要した費用は$5.9だからまぁいいや.全然ショックでかくないんだからねっ.

hardwareについては以上.
以下はsoftwareについて.

STM32のUSB host sample programのフレームワークを真似さしてもらいます.

まず意外だったのが、main()のloopで繰り返し呼んでる関数が何をやってるのかというと、USB deviceの脱着処理なのでした.そんなの割り込みでやってるんだろうと想像してたのですが違いました.pollingでやってるの.
  while (1)
  {
    MX_USB_HOST_Process();
  }

ちなみに、STM32のUSB device sample programでは割込み処理でした.こんなイメージ.
USBに挿すとhostから各種要求が来る
 →各種要求とはUSB packetに他ならない
 →USB hardwareがpacketを解析し
 →適するIRQが発生する
 →userは割込みハンドラに処理を記述すればよい

USB hostのdevice脱着処理のstate machineはザックリとこうなっています.そこはかとなく意味が伝わってくるものがあります.
USBH_Process()
{
  switch (phost->gState)
  {
    case HOST_IDLE :
    case HOST_DEV_WAIT_FOR_ATTACHMENT:
    case HOST_DEV_ATTACHED :
    case HOST_ENUMERATION:
    case HOST_INPUT:
    case HOST_SET_CONFIGURATION:
    case HOST_SET_WAKEUP_FEATURE:
    case HOST_CHECK_CLASS:
    case HOST_CLASS_REQUEST:
    case HOST_CLASS:
    case HOST_DEV_DISCONNECTED :
    case HOST_ABORT_STATE:
  }
}

上の各caseにdebuggerのlog messageが仕込まれています.
 USBH_UsrLog("USB Device Connected");
しかしdebuggerの使い方がよくわからんのでCOM portに文字列を出力させました.
マクロ定義ってよく知らないのですがこんな無茶でも通用するんですね.
 #define  USBH_UsrLog(...)   do { \
char x[100]; \
sprintf(x,__VA_ARGS__); \
hira_senduart_str(x); \
hira_senduart_str("\n"); \
 } while (0)

STM32のUSB host sample programが正常動作していればUSBメモリを挿抜したときにstate遷移に伴う表示がCOM portに流れるはずです.それをやってみたのがこれ.connectedから始まりdisconnectedで終わっています.どうやらhard/soft共に正常に動いているようです.これで開発環境が整いました.
KIOXIAのUSBメモリです.
USB Device Connected
USB Device Reset Completed
PID: 6544h
VID: 30deh
Address (#1) assigned.
Manufacturer : KIOXIA
Product : TransMemory
Serial Number : 0022CFF6B8A4C471A01239BE
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Switching to Interface (#0)
Class    : 8h  <--mass storage class
SubClass : 6h
Protocol : 50h
MSC class started.
Number of supported LUN: 1
LUN #0:
Inquiry Vendor  : KIOXIA
Inquiry Product : TransMemory
Inquiry Version :
MSC Device ready
MSC Device capacity : 2615672320 Bytes
Block number : 30274559
Block Size   : 512
USB Device disconnected

24へ    26へ

かしこ