STM32のFreeRTOSを使ってみる

Ethernet通信


Arduino UNOやNANOは手軽に使えるマイコンですが、最大の問題はメモリが小さい事です。
特にEthernetを使うアプリケーションを作るとすぐにこのような警告に出くわします。
最大32256バイトのフラッシュメモリのうち、スケッチが24432バイト (75%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が1715バイト(83%)を使っていて、ローカル変数で333バイト使 うことができます。
スケッチが使用できるメモリが少なくなっています。動作が不安定になる可能性があります。

このような警告が出た場合、UNOやNANOをあきらめて、MEGAに移行するしか手は有りません。
その点、STM32はF103でもメモリが最低でも64Kあるので、このような警告に出くわす可能性は低いです。
また、万一、警告が出たとしても64K以上のメモリを持つボードにすぐに切り替えることができます。
この様なF103RCT(256K)やF103RET(512K)を使ったボードを$6程度で入手することができます。




こちらに ENC28J60のチップを使ったSPI Ethernetモジュールのライブラリが公開されています。
ソースを読むと、SPIの初期化処理の中で(__STM32F1__)や(__STM32F3__)の定義を判定しています。
STコアの場合、これらは定義されないので、たまたまの感はありますが、STコアでも動きます。
コードを追うと、SPIの初期化処理では以下のコードだけが実行されます。

SPI.begin();
SPI.setBitOrder(MSBFIRST);

setClockDivider()を実行していないので、どのようなCPU周波数のモデルでも動きます。
但し、ジャンパーケーブルは出来るだけ短い物を使う必要が有ります。
また、ENC28J60のモジュールには5V専用モジュールと、5V/3.3V兼用モジュールが有りますが、
STM32で使うときは、5V/3.3V兼用モジュールを使って、3.3Vで駆動する必要が有ります。
ENC28J60は消費電流が非常に大きい事が知られています。
BluePillやBlackPillに実装されている3.3Vレギュレータ(TX6211BやME6211)は非常に容量が小さいので、
ボードから直接電源を供給すると安定して動きません。
多少面倒でも、AMS1117などの大容量レギュレータを使って、5Vピンか外部電源からレギュレータを介して3.3Vを供給した方が安定して動 きます。

右:5V/3.3V兼用モジュール 左:5V専用モジュール


チップセレクトは「SS」のシンボルで定義されているピンがデフォルトで使われますが、
以下の様にinit()を使って、チップセレクトに特定のピンを指定することもできます。
こちらで紹介しているように、SPIのSSとして定義されているピンはボードごと に違う ので注意が必要です。
  Serial.begin(115200);
  Ethernet.init(PA4); // Specify Chip Select
  if (!Ethernet.begin(mac) ) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
  }



こちらに W5x00シリー ズのチップを使ったSPI Ethernetモジュールのライブラリが公開されています。
このライブラリはArduino-IDE標準のライブラリです。

チップセレクトはEthernet.init()で任意のピンを指定することができます。
例えば、チップセレクトがPB6の場合、以下のコードとなります。
void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // You can use Ethernet.init(pin) to configure the CS pin
  Ethernet.init(PB6);

  // start the Ethernet connection:
  Serial.println("Initialize Ethernet with DHCP:");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    } else if (Ethernet.linkStatus() == LinkOFF) {
      Serial.println("Ethernet cable is not connected.");
    }
    // no point in carrying on, so do nothing forevermore:
    while (true) {
      delay(1);
    }
  }

  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.");
  }
  else if (Ethernet.hardwareStatus() == EthernetW5100) {
    Serial.println("W5100 Ethernet controller detected.");
  }
  else if (Ethernet.hardwareStatus() == EthernetW5200) {
    Serial.println("W5200 Ethernet controller detected.");
  }
  else if (Ethernet.hardwareStatus() == EthernetW5500) {
    Serial.println("W5500 Ethernet controller detected.");
  }
 
  // print your local IP address:
  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());
}

W5x00シリー ズのチップを使ったSPI EthernetモジュールとしてはW5100のモジュールとW5500のモジュールが入手できますが、
W5100のモジュールは5V駆動なのでSTM32では使いにくいです。
STM32のGPIOには5V tolerantのピンと、5V tolerantではないピンが混在しています。
GPIOが5V tolerantかどうかは、McUのデータシートを見ればわかります。
W5100のモジュールを使う場合、MISOは5V tolerantになっている必要が有ります。
SPIのGPIOはモデルにより異なりますが、PillBoardのMISOはPA6で、このGPIOは5V tolerantでは有りません。

W5500のモジュールは3.3V/5Vの両電源となっているので、レベルシフト無しで使うことができますが、
BluePillやBlackPillに実装されている3.3Vレギュレータ(TX6211BやME6211)は非常に容量が小さいので、
ボードから直接電源を供給すると安定して動きません。
多少面倒でも、AMS1117などの大容量レギュレータを使って、5Vピンか外部電源からレギュレータを介して3.3Vを供給した方が安定して動 きます。
PHY側のRESETピンには、ボード側のRESETピンを繋ぎますが、RESETピンの無いボードの場合は、PullUpしておきます。



STM32には標準でEthernetの機能を持っているMPUが有ります。
これらのボードが標準でEthernetをサポートしています。
対応しているインターフェイスは、RMII(Reduced Media Independent Interface)です。
variants\STM32F1xx\F107R(B-C)T\variant_generic.h(180):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F1xx\F107VBT_F107VC(H-T)\variant_generic.h(210):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F2xx\F207I(C-E-F-G)(H-T)_F217I(E-G)(H-T)\variant_generic.h(277):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F2xx\F207V(C-E-F-G)T_F217V(E-G)T\variant_generic.h(219):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F2xx\F207Z(C-E-F-G)T_F217Z(E-G)T\variant_generic.h(251):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F2xx\F207Z(C-E-F-G)T_F217Z(E-G)T\variant_NUCLEO_F207ZG.h(241):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F407I(E-G)(H-T)_F417I(E-G)(H-T)\variant_generic.h(277):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F407V(E-G)T_F417V(E-G)T\variant_generic.h(219):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F407Z(E-G)T_F417Z(E-G)T\variant_generic.h(251):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F427A(G-I)H_F429A(G-I)H_F437AIH_F439AIH\variant_generic.h(267):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F427I(G-I)(H-T)_F429I(E-G-I)(H-T)_F437I(G-I)(H-T)_F439I(G-I)(H-T)\variant_generic.h(277):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F427V(G-I)T_F429V(E-G-I)T_F437V(G-I)T_F439V(G-I)T\variant_generic.h(219):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F427Z(G-I)T_F429ZET_F429Z(G-I)(T-Y)_F437Z(G-I)T_F439Z(G-I)(T-Y)\variant_generic.h(251):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F427Z(G-I)T_F429ZET_F429Z(G-I)(T-Y)_F437Z(G-I)T_F439Z(G-I)(T-Y)\variant_NUCLEO_F429ZI.h(227):   /* #define HAL_ETH_MODULE_ENABLED */
variants\STM32F4xx\F429B(E-G-I)T_F429N(E-G-I)H_F439B(G-I)T_F439N(G-I)H\variant_generic.h(305):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F446Z(C-E)(H-J-T)\variant_NUCLEO_F446ZE.h(237):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F469B(E-G-I)T_F469N(E-G-I)H_F479B(G-I)T_F479N(G-I)H\variant_generic.h(296):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F4xx\F469I(E-G-I)(H-T)_F479I(G-I)(H-T)\variant_generic.h(268):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F722Z(C-E)T_F732ZET\variant_NUCLEO_F722ZE.h(237):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F745I(E-G)(K-T)_F746I(E-G)(K-T)_F756IG(K-T)\variant_generic.h(283):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F745V(E-G)(H-T)_F746V(E-G)(H-T)_F750V8T_F756VG(H-T)\variant_generic.h(225):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F745Z(E-G)T_F746Z(E-G)(T-Y)_F750Z8T_F756ZG(T-Y)\variant_ETHERCAT_DUINO.h(273): //   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F745Z(E-G)T_F746Z(E-G)(T-Y)_F750Z8T_F756ZG(T-Y)\variant_generic.h(257):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F745Z(E-G)T_F746Z(E-G)(T-Y)_F750Z8T_F756ZG(T-Y)\variant_NUCLEO_F7x6ZG.h(238):   /* #define HAL_ETH_MODULE_ENABLED */
variants\STM32F7xx\F746B(E-G)T_F746N(E-G)H_F750N8H_F756BGT_F756NGH\variant_DISCO_F746NG.h(287):   /* #define HAL_ETH_MODULE_ENABLED */
variants\STM32F7xx\F746B(E-G)T_F746N(E-G)H_F750N8H_F756BGT_F756NGH\variant_generic.h(311):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F765B(G-I)T_F765N(G-I)H_F767B(G-I)T_F767N(G-I)H_F777BIT_F777NIH\variant_generic.h(318):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F765I(G-I)(K-T)_F767I(G-I)(K-T)_F777II(K-T)\variant_generic.h(290):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F765V(G-I)(H-T)_F767V(G-I)(H-T)_F777VI(H-T)\variant_generic.h(232):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F765Z(G-I)T_F767Z(G-I)T_F777ZIT\variant_generic.h(264):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F765Z(G-I)T_F767Z(G-I)T_F777ZIT\variant_NUCLEO_F767ZI.h(239):   /* #define HAL_ETH_MODULE_ENABLED */
variants\STM32F7xx\F769B(G-I)T_F769N(G-I)H_F779BIT_F779NIH\variant_generic.h(309):   #define HAL_ETH_MODULE_ENABLED
variants\STM32F7xx\F769I(G-I)T_F779IIT\variant_generic.h(281):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563A(G-I)I\variant_generic.h(293):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563AIIxQ\variant_generic.h(292):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563I(G-I)(K-T)\variant_generic.h(299):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563IIKxQ\variant_generic.h(298):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563IITxQ\variant_generic.h(294):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563MIYxQ\variant_generic.h(203):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563R(G-I)T\variant_generic.h(195):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563R(G-I)V\variant_generic.h(197):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563V(G-I)T\variant_generic.h(224):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563Z(G-I)T\variant_generic.h(259):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H563Z(G-I)T\variant_NUCLEO_H563ZI.h(248):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573AII\variant_generic.h(293):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573AIIxQ\variant_generic.h(292):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573II(K-T)\variant_generic.h(299):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573IIKxQ\variant_generic.h(298):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573IIKxQ\variant_STM32H573I_DK.h(306):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573IITxQ\variant_generic.h(294):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573MIYxQ\variant_generic.h(203):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573RIT\variant_generic.h(195):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573RIV\variant_generic.h(197):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573VIT\variant_generic.h(224):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H5xx\H573ZIT\variant_generic.h(259):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H723V(E-G)(H-T)_H730VB(H-T)_H733VG(H-T)\variant_generic.h(228):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H723Z(E-G)I_H730ZBI_H733ZGI\variant_generic.h(271):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H723Z(E-G)T_H730ZBT_H733ZGT\variant_generic.h(269):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H723Z(E-G)T_H730ZBT_H733ZGT\variant_NUCLEO_H723ZG.h(263):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H725A(E-G)I_H730ABIxQ_H735AGI\variant_generic.h(286):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H725I(E-G)K_H730IBKxQ_H735IGK\variant_generic.h(293):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H725I(E-G)T_H730IBTxQ_H735IGT\variant_generic.h(282):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H725V(E-G)H_H735VGH\variant_generic.h(223):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H725V(E-G)T_H735VGT\variant_generic.h(215):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H725VGY_H735VGY\variant_generic.h(212):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H725Z(E-G)T_H735ZGT\variant_generic.h(252):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742A(G-I)I_H743A(G-I)I_H753AII\variant_generic.h(279):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742B(G-I)T_H743B(G-I)T_H753BIT\variant_generic.h(321):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742I(G-I)(K-T)_H743I(G-I)(K-T)_H750IB(K-T)_H753II(K-T)\variant_DAISY_PATCH_SM.h(251):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742I(G-I)(K-T)_H743I(G-I)(K-T)_H750IB(K-T)_H753II(K-T)\variant_generic.h(287):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742V(G-I)(H-T)_H743V(G-I)(H-T)_H750VBT_H753VI(H-T)\variant_DevEBoxH7xx.h(237): //   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742V(G-I)(H-T)_H743V(G-I)(H-T)_H750VBT_H753VI(H-T)\variant_generic.h(227):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742V(G-I)(H-T)_H743V(G-I)(H-T)_H750VBT_H753VI(H-T)\variant_WeActMiniH7xx.h(236): //   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742X(G-I)H_H743X(G-I)H_H745X(G-I)H_H747X(G-I)H_H750XBH_H753XIH_H755XIH_H757XIH\variant_generic.h(331):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742X(G-I)H_H743X(G-I)H_H745X(G-I)H_H747X(G-I)H_H750XBH_H753XIH_H755XIH_H757XIH\variant_STM32H747I_DISCO.h(320):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742Z(G-I)T_H743Z(G-I)T_H747A(G-I)I_H747I(G-I)T_H750ZBT_H753ZIT_H757AII_H757IIT\variant_generic.h(261):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742Z(G-I)T_H743Z(G-I)T_H747A(G-I)I_H747I(G-I)T_H750ZBT_H753ZIT_H757AII_H757IIT\variant_NUCLEO_H743ZI.h(376):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H742Z(G-I)T_H743Z(G-I)T_H747A(G-I)I_H747I(G-I)T_H750ZBT_H753ZIT_H757AII_H757IIT\variant_NUCLEO_H753ZI.h(248):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H745B(G-I)T_H755BIT\variant_generic.h(303):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H745I(G-I)K_H755IIK\variant_generic.h(285):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H745I(G-I)T_H755IIT\variant_generic.h(274):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H745Z(G-I)T_H755ZIT\variant_generic.h(246):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H747B(G-I)T_H757BIT\variant_generic.h(288):   #define HAL_ETH_MODULE_ENABLED
variants\STM32H7xx\H747ZIY_H757ZIY\variant_generic.h(246):   #define HAL_ETH_MODULE_ENABLED
variants\STM32MP1xx\MP151AA(B-D)_MP151CA(B-D)_MP151DA(B-D)_MP151FA(B-D)\variant_generic.h(253):   #define HAL_ETH_MODULE_ENABLED
variants\STM32MP1xx\MP151AAA_MP151CAA_MP151DAA_MP151FAA\variant_generic.h(363):   #define HAL_ETH_MODULE_ENABLED
variants\STM32MP1xx\MP151AAC_MP151CAC_MP151DAC_MP151FAC\variant_generic.h(329):   #define HAL_ETH_MODULE_ENABLED
variants\STM32MP1xx\MP153AA(B-D)_MP153CA(B-D)_MP153DA(B-D)_MP153FA(B-D)_MP157AA(B-D)_MP157CA(B-D)_MP157DA(B-D)_MP157FA(B-D)\variant_generic.h(253):   #define HAL_ETH_MODULE_ENABLED
variants\STM32MP1xx\MP153AAA_MP153CAA_MP153DAA_MP153FAA_MP157AAA_MP157CAA_MP157DAA_MP157FAA\variant_generic.h(363):   #define HAL_ETH_MODULE_ENABLED
variants\STM32MP1xx\MP153AAC_MP153CAC_MP153DAC_MP153FAC_MP157AAC_MP157CAC_MP157DAC_MP157FAC\variant_generic.h(329):   #define HAL_ETH_MODULE_ENABLED

NucleoにはオンボードにEthernet(LAN8742Aチップ)を持つボードが有りますが、ボード自体が結構高価です。
また、LAN8742Aチップを使った単体のEthernetモジュールは入手できません。

RMIIのインターフェイスを持つチップとして Microchip 社の LAN8720 があり、このチップを用いた安価なEthernetモジュールが広く出回っています。
こちらのF407ボード(DIYMORE_F407_VGT $8ぐらい)とLAN8720 ETH BOARD($2ぐらい)を使えば、非常に安価にEthernet通信を行うことができます。





こちらのボードはSTM32 F407VET6が実装されていますが、問題なく動きます。




最初に以下のライブラリを追加します。

https://github.com/stm32duino/STM32Ethernet

https://github.com/stm32duino/LwIP





STM32EthernetライブラリはLAN8742Aモジュールをサポートしています。
LAN8720とLAN8742Aは非常によく似ていますがPHY Addressが違います。
LAN8720のPHY Address:1
LAN8742AのPHY Address:0

そこで、任意のPHY Addressを指定できるように、こ ちらのファイルを一部変更します。
/* Section 2: PHY configuration section */
#if !defined  (LAN8742A_PHY_ADDRESS)
/* LAN8742A PHY Address*/
#define LAN8742A_PHY_ADDRESS            0x00U
#endif

これで、スケッチの中でLAN8742A_PHY_ADDRESSを好きな値に変更できるようになります。



こ ちらのサンプルスケッチを開き、新規タブで以下の内容を追加します。
HAL_ETH_LEGACY_MODULE_ENABLEのおまじないはSTMのエンジニアから教えてもらいました。


スケッチを適当な場所に保存したら、一旦Arduino-IDEを再起動します。
再起動すると変更が反映されます。



STM32F407ボードとLAN8720モジュールの結線は、こ ちらのページに公開されています。
VCC/GNDを含めて11本の配線が必要です。
スケッチを書き込むとDHCPからアドレスを取得し、NTPサーバーから現在日時を読み込みます。
UTC(協定世界時)なので日本時間とは9時間の時差が有ります。


ST Microは、DP83438とLAN8720のEthernetモジュールをSTコアで使えるように考えてはいるようです。
開発状況はこ ちらで知ることができますが、2019年から全く進捗が無いです(~_~メ)

続く....