2020年07月18日

lwipを用いたFTP Client

イーサネットIFを搭載したSTM32マイコンではCubeMXを用いてイーサネット通信が容易に行うことができます。NucleoボードやDiscoveryボードでは基板上にETHPHYのコントローラICを搭載しているものも多くあり、そのままイーサネット通信の実験を行うことが可能です。

今回はSTM32マイコンを用いてFTPサーバーにデータを格納する方法について紹介します。STM32マイコンがFTPクライアント、サーバーがIIS FTPサーバー@Windows10の環境で実験しました。また、STM32マイコンのイーサネット通信ライブラリとしてlwipを使用し、FTPクライアントはlwftpライブラリを使用しました。lwftpのサンプルコードではそのままでは動かない点や分かりづらい点があったのでそこを踏まえて紹介します。なお、lwftpライブラリはパッシブ通信のFTPクライアントのみサポートしています。

グローバル変数としてセッションを宣言します。
static lwftp_session_t s;


main関数内のメイン処理は下記の通りです。
必要に応じてFTPサーバーのIP、ログインID、ログインパスワードを変更してください。

 // Initialize session data
 memset(&s, 0, sizeof(s));
 IP4_ADDR(&s.server_ip, 192,168,6,3);
 s.server_port = 21;
 s.done_fn = ftp_connect_callback;
 s.user = "ftpuser";
 s.pass = "test";
 s.handle = &s;
 // Start the connection state machine
 error = lwftp_connect(&s);
 if ( error != LWFTP_RESULT_INPROGRESS ) {
  printf("lwftp_connect failed (%d)", error);
 }
 else{
  printf("FTP END \n\r");
 }
 while(1);



main関数の外のグローバル関数として下記のコールバック関数を宣言します。

//FTP通信完了に呼び出される
static void ftp_retr_callback(void *arg, int result)
{
 lwftp_session_t *s = (lwftp_session_t*)arg;
 if ( result != LWFTP_RESULT_OK ){
  LOG_ERROR("retr failed (%d)", result);
  return lwftp_close(s);
 }
 lwftp_close(s);
}

//未使用、FTPサーバからデータ受信で使用
static uint data_sink(void *arg, const char* ptr, uint len)
{
 static const uint mylen = 10;
 static char * const myconfig = (char*)0x20000000;
 static uint offset = 0;
 if (ptr) {
 len = min( len, mylen-offset );
 memcpy( myconfig+offset, ptr, len );
 offset += len;
 }
 return len;
}


static void ftp_stor_callback(void *arg, int result)
{
 lwftp_session_t *s = (lwftp_session_t*)arg;
 err_t error;
 if(s->control_state==LWFTP_DATAEND && result == LWFTP_RESULT_OK ){
  s->control_state=LWFTP_DATAEND;
  return lwftp_close(s);
 }
 if ( result != LWFTP_RESULT_OK && result != LWFTP_RESULT_INPROGRESS && result != LWFTP_RESULT_LOGGED ) {
  LOG_ERROR("stor failed (%d)", result);
  return lwftp_close(s);
 }
 if(s->control_state==LWFTP_CLOSED && result == LWFTP_RESULT_OK)return;
 if(result == LWFTP_RESULT_INPROGRESS)return;
 s->data_sink = data_sink;
 s->done_fn = ftp_retr_callback;
 s->remote_path = "new";
 error = lwftp_retrieve(s);
 if ( result != LWFTP_RESULT_OK && error != LWFTP_RESULT_INPROGRESS && result != LWFTP_RESULT_LOGGED ) {
 LOG_ERROR("lwftp_retrieve failed (%d)", error);
 }
}

//データ元の関数、今回はTEST_DATA:abcdefgの文字列を格納する
char test_data[]={"TEST_DATA:abcdefg"};
static uint data_source(void *arg, const char** pptr, uint maxlen)
{
 static const uint mylen = sizeof(test_data);;
 static const char * const mydata = (char*)&test_data;
 static uint offset = 0;
 uint len = 0;
 if (pptr) {
  len = mylen - offset;
  if ( len > maxlen ) len = maxlen;
  *pptr = mydata + offset;
 }
else {
 offset += maxlen;
 if ( offset > mylen ) offset = mylen;
 }
 return len;
}

//FTP通信接続成功で呼び出される
//logfile.txtというファイル名でFTPサーバーに格納する
static void ftp_connect_callback(void *arg, int result)
{
 lwftp_session_t *s = (lwftp_session_t*)arg;
 err_t error;
 if( result == LWFTP_RESULT_INPROGRESS ){
  return;
 }
 if ( result != LWFTP_RESULT_LOGGED ) {
  LOG_ERROR("login failed (%d)", result);
  return lwftp_close(s);
 }
 s->data_source = data_source;
 s->done_fn = ftp_stor_callback;
 s->remote_path = "logfile.txt";
 error = lwftp_store(s);
 if ( error != LWFTP_RESULT_INPROGRESS ) {
  LOG_ERROR("lwftp_store failed (%d)", error);
 }
}


lwipはコールバック関数で接続や受信などのイベント発生毎に関数が呼び出される構造となっており、lwftpについても同様にイベント毎に関数が実行されます。サンプルコードでは各コールバック関数内のcontrol_state状態に応じた分岐処理がうまくいかず、FTPサーバーに格納できませんでした。Wiresharkで動作を確認しながら、control_stateの分岐条件を追加することでFTPサーバーにデータを格納できるようになりました。lwftpを用いてちょっとしたデータロガーとして応用できそうです。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2020年07月11日

iCE40HX8K RISC-V導入

今更な感じもしますが、命令セットアーキテクチャ (ISA)がオープンとなっている RISC-Vを試食してみました。ターゲットはiCE40-HX8Kを用いて、こちらのサイトの手順を参考に試食してみました。

手順についてはサイトに詳細に書かれているため省略しますが、hx8kdemoを実装した際のビルド結果を下記に記します。

=== hx8kdemo ===
Number of wires: 4536
Number of wire bits: 9050
Number of public wires: 4536
Number of public wire bits: 9050
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 7053
SB_CARRY 961
SB_DFF 175
SB_DFFE 651
SB_DFFESR 538
SB_DFFESS 58
SB_DFFNSR 4
SB_DFFSR 216
SB_DFFSS 6
SB_IO 4
SB_LUT4 4434
SB_RAM40_4K 6

サンプルコードでは約7000セル、LUTは約4500個の使用率でした。


RISCV.jpg

iCE40-HX8KのUart機能を使用してTeratermから動作確認ができました。picorv32/picosoc内のfirmware.cがデモのソースコードとなっています。

ファームのみのビルドする場合は下記のコマンドでビルドできます。
make hx8kdemo_fw.elf

ファームのみの書き込みする場合は下記のコマンドで書き込みできます。
sudo make hx8kprog_fw

iCE40-HX8Kで試食する前にMACHXO3でRISC-Vの実装を検討しましたが、そのままではRAM容量やクロックが確保できず諦めていました。海外等では工夫してMACHXO3でRISC-Vを実装した例がありましたが、コードが公開されていませんでした。最近、MACHXO3の新シリーズのMACHXO3DでLatticeが公式にRISC-VのIP Coreを公開されました。3.3Vで駆動可能なMACHXO3はiCE40-HX8Kよりも魅力が大きいため、今後、MACHXO3Dの評価ボードで試食してみたいと思います。

posted by Crescent at 00:00| Comment(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2020年07月04日

ライブラリ追加方法

今回はEclipseベースの開発環境(SW4STM32、STM32CubeIDEなど)でリンカライブラリ(*.lib)ファイルや静的ライブラリ(*.a)ファイル等のライブラリを追加する方法について紹介します。

すべてのコードが公開されている場合はリンカライブラリ(*.lib)ファイルや静的ライブラリ(*.a)ファイル等を読み込む必要はありませんが、コードが公開されていない場合にメーカ等からリンカライブラリ(*.lib)ファイルや静的ライブラリ(*.a)ファイル等のみ提供される場合があります。このような場合にライブラリを追加してリンカ生成時にユーザー側のコード内の関数と結合させてバイナリファイルを生成します。


@ライブラリ配置
 今回はプロジェクトフォルダの直下にライブラリファイルを置きます。

Aライブラリ追加設定
 プロジェクトファイルのプロパティを開き(Alt+Enter)、「C/C++Build」の「Settings」、「Tool Settings」「MCU GCC Linker」内のLibrariesから追加します。

img1.jpg


 まず、「Library search path」の設定を下記のように設定し、プロジェクトフォルダ直下を見に行く設定にします。

"${workspace_loc:/${ProjName}}"

 また、追加したライブラリを「Libraries」に追加します。ここでポイントは*.aファイルの場合はライブラリ名の前に「:」を追加する点して拡張子を合わせたフルネームで追加します。一方、*.libファイルの場合は「:」を除いて、ファイル名のみを追加します。

:***.a  ←*.aファイルの場合
***          ←*.libファイルの場合

lib.jpg



Bヘッダファイル追加
 ライブラリファイル内の関数を定義しているヘッダファイルを必要に応じて追加します。プロジェクトフォルダ内のIncフォルダに追加します。

Cその他
 ライブラリファイルが特定のArmコアに依存している場合、下記のようなビルドエラーが発生する場合があります。下記のエラーの場合はライブラリファイルがFPUなしのMPUをターゲットにしているため、FPU有のMPUでビルドしようとして発生するエラーです。このような場合はライブラリをFPU対応に書き換えることは難しいため、FPUなしとしてビルドします。

/bin/ld.exe: error: *.elf uses VFP register arguments *.o does not
/bin/ld.exe: failed to merge target specific data of file *.o

 プロジェクトファイルのプロパティを開き(Alt+Enter)、「C/C++Build」の「Settings」、「MPU Settings」内の「Floating-point ABI」を「hard」から「soft」に切り替えます。

img3.jpg


メーカからすべてのコードや情報が公開されている状態が理想的ですが、様々な制約で公開されていない場合はこのような方法で組み込むことができます。

posted by Crescent at 00:00| Comment(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2020年06月27日

Nucleo144 Memory Shield

大容量のデータをマイコンで扱う場合、一時的なデータ保存に揮発性のSRAMやSDRAM、バックアップデータに不揮発性のNAND FlashメモリやNOR Flashメモリを使うことが多いと思います。不揮発性のデータの場合、仕様によってはSDカードを使うこともあるかもしれません。また、昨今はSPI接続の大容量PSRAMも増えています。そのような中でメモリを選択する場合、容量拡張性や部品供給安定性を考慮するとパラレル接続のメモリを選択することが多くなります。一方でSTM32マイコン等でもパラレルメモリを搭載した評価ボードは一部のEvalボードにSDRAMが搭載されるくらいでSRAM、NOR Flash、2つのSDRAM、2つのSRAMといった評価まで対応したボードがありませんでした。

そこでNucleo144のメモリシールドを設計してみました。一般的なパラレル接続のメモリに対応しており、SDRAM x2、SRAM x2、NAND Flash x1、NOR Flash x1を搭載しています。STM32H743Zをターゲットに設計しましたが、他のSTM32F743といったボードでも利用できると思います。なお、Nucleo144の外側のCN11とCN12のみ使用しています。内側のCN7、CN8、CN9、CN10はボードのレビジョンによってピンアサインが異なるため使用していません。

ターゲットとしているフットプリントとメモリは下記の通りです。

SDRAMx2TSSOP-U 54ピンMT48LC16M16A2P
SRAMx2TSSOP-U 44ピンCY62157EV30LL
NOR Flashx1TSSOP-T 56ピンMT28EW512A
NAND Flashx1TSSOP-T 48ピンMT29F8G08A

SDRAMやNAND Flashはピンアサインやフットプリントがほぼ統一されているため、他のメモリでも動作すると思いますが、SRAMやNOR Flashはメーカによってパッケージが様々でピンアサインも異なる場合があり注意が必要です。ただ、多くはデータやアドレスピンの順番が互い違いになっているだけのことが多く、フットプリントが同じで一部の順番が互い違いあれば、異なるメモリの場所に互い違いに保存されるだけで動作上は問題ありません。


ns.jpg

実際に基板に実装してSDRAM x2とNAND Flash x1の動作確認ができました。

mem-shield.JPG

必要な配線はNucleo144の外側のCN11とCN12のみ使用しています。内側のCN7、CN8、CN9、CN10にピンを立てる必要はありません。また、CN11、CN12の片側のGNDはピンがデフォルトで立っているため、当該箇所のソケットを除いて実装しました。

今後、SRAM x2 NOR Flashの動作確認を行い、問題なければこちらで基板のデータの他にCubeMXのプロジェクトファイル、コードを合わせて公開したいと思います。
posted by Crescent at 00:00| Comment(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2020年06月20日

RMD Servo Motor

今回は海外で話題になっているRMD Servo Motorについて紹介します。

RMD Servo Motorはブラシレスモータ本体、磁気エンコーダ(12bit以上、4096/回転)、モータ制御ドライバ、RS485orCAN通信ドライバが一体になったモータで電源と通信線だけでモータを制御することができます。ここまでは普通のプロポサーボモータ等と同じような仕様ですが、位置制御、速度制御に加えてトルク制御(電流制御)が可能です。トルク制御に対応したドライバ一体のブラシレス小型モータはほとんどありません。一部のプロポサーボモータはトルク制限(トルク制御でない)に対応していますが、トルク制御とは違います。ちょっと残念なのはトルク制御はオープンループな点ですが...電流センサを内蔵してフィードバックしてほしい気もします。

なお、各制御系の制御周期はオープンループトルク制御32kHz、クローズド速度制御4kHz、クローズド位置制御2kHzとなっており、32bitマイコンを搭載したメリットを存分に活かす仕様です。専用のソフトウェアで制御ゲインの他、モータIDや動作テストをすることができます。モータIDを書き換えることでRS485ラインorCAN通信ラインに最大32台のモータを同時に接続することができます。



今回はRMD Servo Motorの中でも最も小さいRMD-S-24を試食してみました。購入はAliexpressで行いました。

IMG_0089.JPG

設定ソフトウェアRMDconfigを用いて設定や動作確認を行いました。PCとの接続はUSB-RS485変換ケーブルが手元になかったため、LTC485AE-FT2232を用いて接続しました。配線は下記の通りです。ポイントはTXDEN出力があるシリアルICを選択する点とRMDへモータ電源のVCCとは別に制御系と通信系の電源となる5Vも外部から供給が必要な点です。

rmd_con.jpg

モータ電源とは別に5V電源の供給が必要なことに気づかず、通信できない問題に時間を要してしまいました。5V電源を供給するとRMDモータ内部の基板の緑LEDが点灯し、無事通信とモータ制御できることが確認できました。


RMD Servo Motorは様々な出力の種類が用意されており、最も小さいRMD-S-24では4000円前後(送料別)となっています。プロポサーボモータに比べると高いものの、12bit以上の高精度な磁気エンコーダを搭載して制御系も一折実装されており、安価な産業機器のサーボモータレベルの機能でこの価格は驚きです。時間を見つけて次はArduinoやSTM32マイコン等から制御してみたいと思います。

posted by Crescent at 00:00| Comment(0) | 電子部品 | このブログの読者になる | 更新情報をチェックする