iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 26
0
IoT

拿到錘子的我想在微控制器上面執行 Ruby系列 第 26

Day 26 - 跑馬燈

在前面的實作中,我們已經可以將文字印出在 TFT 螢幕上,接下來我們要結合迴圈跟繪製文字的機制來製作簡易的跑馬燈效果。

繪製方法

之前我們用來印出訊息的 puts 無法指定坐標,但是我們希望跑馬燈可以透過 Ruby 來控制捲動的方向,因此需要新增一個方法來處理座標跟位置的處理。

因為跑馬燈效果只在 TFT 螢幕上有效果,所以會跳過 native 上的實作。

// include/embed_methods.h

// ...
void mrb_draw_text(mrb_state* mrb);
// ...

我們先增加一個 mrb_draw_text 方法的定義,然後對 hal_nativehal_arduino 增加對應的實作。

// src/hal_arduino/embed_methods.cpp

void mrb_draw_text(mrb_state* mrb) {
  int argc = mrb_get_argc(mrb);
  if (argc != 3) {
    // Arguments is not satisfied
    return;
  }

  mrb_value* argv = mrb_get_argv(mrb);

  int x = mrb_fixnum(argv[1]);
  int y = mrb_fixnum(argv[2]);

  tft.drawString(mrb_string(argv[0]), x, y, 1);
}

為了方便取出字串,額外在 lib/iron/value.h 裡面加入了 mrb_string 巨集,直接回傳 (char*)(o).value.p 設定,跟前面的實作相同但是會容易閱讀許多,至於 hal_native 的實作因為我們不需要,所以都是寫成空的方法來避免編譯失敗,這篇就不另外寫出來。

include/ruby.h 加入 define_method 來定義新方法

// include/ruby.h

void bootstrap_ruby() {
  mrb_state* mrb = mrb_open();

  mrb_define_method(mrb, "puts", mrb_puts);
  mrb_define_method(mrb, "draw_text", mrb_draw_text);

  mrb_run(mrb, app);
  mrb_close(mrb);
}

清除畫面

經過接近一個月的努力,我們要擴充這些處理已經相對容易許多,接下來用相同的方式把 tft.fillScreen 的處理實作出來。

// include/embed_methods.h

// ...
void mrb_clear(mrb_state* mrb);
// ...
// src/hal_arduino/embed_methods.cpp

void mrb_clear(mrb_state* mrb) {
  tft.fillScreen(TFT_BLACK);
}
// include/ruby.h

void bootstrap_ruby() {
  mrb_state* mrb = mrb_open();

  mrb_define_method(mrb, "puts", mrb_puts);
  mrb_define_method(mrb, "clear", mrb_clear);
  mrb_define_method(mrb, "draw_text", mrb_draw_text);

  mrb_run(mrb, app);
  mrb_close(mrb);
}

這個時候我們更新 Ruby 腳本為跑馬燈就能看到動態效果

i = 0
while true
  clear
  draw_text "Hello World", i, 8
  i += 2
  i = 0 if i >= 160
end

螢幕款度是 160 像素,所以移動到最右邊的時候就會重設,每次移動是 2 像素,如果只移動單個文字會出現扭曲的狀況,這應該跟 TFT_eSPI 的處理和文字的資料有關係。

延遲處理

用上面的方法實作後會發現有著「移動過快」的問題,因此我們需要加入 sleep 讓他不要以處理器能處理的最高速度進行處理。

// include/embed_methods.h

// ...
void mrb_sleep(mrb_state* mrb);
// ...
// src/hal_arduino/embed_methods.cpp

void mrb_sleep(mrb_state* mrb) {
  mrb_value* argv = mrb_get_argv(mrb);
  delay(mrb_fixnum(argv[0]));
}
// include/ruby.h

void bootstrap_ruby() {
  mrb_state* mrb = mrb_open();

  mrb_define_method(mrb, "puts", mrb_puts);
  mrb_define_method(mrb, "clear", mrb_clear);
  mrb_define_method(mrb, "sleep", mrb_sleep);
  mrb_define_method(mrb, "draw_text", mrb_draw_text);

  mrb_run(mrb, app);
  mrb_close(mrb);
}

最後稍微更新 app.rb 就可以看到正常運作的跑馬燈

i = 0
while true
  clear
  draw_text "Hello World", i, 8
  i += 2
  i = 0 if i >= 160
  sleep 100
end

目前我們實作的 Ruby VM 在板子上還有一些問題,會看到運行一段時間後就會當機並且重新啟動,因為不熟悉除錯的方法暫時沒有找到解決的方案。不過大致上我們已經完成我們原先預定的目標,下一篇會來挑戰實作 Block 的機制。


上一篇
Day 25 - 字串(二)
下一篇
Day 27 - Block(一)
系列文
拿到錘子的我想在微控制器上面執行 Ruby30

尚未有邦友留言

立即登入留言