2017-08-30

ESP8266/ESP32でのサブマイクロ秒の時間調整

ESP8266 ESP32

はじめに

外部の機器との通信のためにサブマイクロ秒オーダーでGPIOを制御したくなるときがあるかもしれません。しかしdelayMicroseconds()やvTaskDelay()では、1マイクロ秒やそれ以下の時間制御は不可能です。

今回は、アセンブラの「何もしない」命令であるNOP命令を使って時間調整を行いました。NOP命令では何もせずに1クロック分の時間を消費するため、よく時間調整に用いられる命令です。

例えばESP32が160MHzで動作している時、1秒間に160,000,000クロック分の処理が行われております。そして1マイクロ秒間には160クロック分の処理が行われるため、ある時刻から160クロック分増加するまで待機すれば1マイクロ秒の時間調整が可能です。

今回はwhileでCPUクロック数がおよそ160増加するまでNOP命令を実行し続ける手法を取ります。

実装

まず関数get_ccount()を定義します。

static inline unsigned get_ccount(void)
{
   unsigned r;
   asm volatile ("rsr %0, ccount" : "=r"(r));
   return r;
}

そして160MHz ESP32/ESP-IDFの場合、1マイクロ秒LOW、3マイクロ秒HIGH、その後再びLOWにしたいときには以下のコードで実現できます。

ESP32では1マイクロ秒間にCPUクロック数は160増加するため、待ち時間に応じたクロック数をwhileの条件式に代入することで、マイクロ秒オーダでのGPIO制御が可能となります。

long startCount;

startCount = get_ccount();
gpio_set_level(GPIO_INPUT_IO_0, LOW);
while (get_ccount() - startCount < 160) __asm__ __volatile__ ("nop");

startCount = get_ccount();
gpio_set_level(GPIO_INPUT_IO_0, HIGH);
while (get_ccount() - startCount < 160*3) __asm__ __volatile__ ("nop");

gpio_set_level(GPIO_INPUT_IO_0, LOW);

誤差はおよそ0.1マイクロ秒程度であり、1Mbpsの通信は十分行えました。さらに厳密に時間調整したい場合は、whileのループ外にもNOPすることで可能になります。

参考にしたページ

Precise timing needed (ex. for IR decoding)

投稿をシェアする