在前面的實作中,我們已經可以將文字印出在 TFT 螢幕上,接下來我們要結合迴圈跟繪製文字的機制來製作簡易的跑馬燈效果。
之前我們用來印出訊息的 puts
無法指定坐標,但是我們希望跑馬燈可以透過 Ruby 來控制捲動的方向,因此需要新增一個方法來處理座標跟位置的處理。
因為跑馬燈效果只在 TFT 螢幕上有效果,所以會跳過
native
上的實作。
// include/embed_methods.h
// ...
void mrb_draw_text(mrb_state* mrb);
// ...
我們先增加一個 mrb_draw_text
方法的定義,然後對 hal_native
和 hal_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
的機制。