組み込み・マイコン開発
NagiScript での組み込みシステム開発を学びます。
組み込み・マイコン開発は、現行コンパイラー v0.2.0
ではまだ対応していない分野です(標準ライブラリが未実装のため)。下記の内容は将来仕様であり、現行の
nagiscript
コマンドでは動きません。現行で動作するのは組み込み
print / println /
panic と、C 標準関数の
extern "C" fn 呼び出しのみです。
概要
NagiScript は以下の特性により、組み込み開発に適しています(将来計画):
- 軽量ランタイム: libc のみに依存
- C ABI 互換: 既存の C ライブラリをそのまま利用可能
- Unsafe ブロック: 低レベルなハードウェア操作をサポート
- 静的型付け: 実行時エラーを排除
対応プラットフォーム(準備中)
| プラットフォーム | 状態 | 用途 |
|---|---|---|
| ARM Cortex-M | 🔶 準備中 | STM32, nRF52, etc. |
| RISC-V | 🔶 準備中 | ESP32-C3, etc. |
| x86 ベアメタル | 🔶 準備中 | UEFI, etc. |
ハードウェア定義と現行構文の基礎
下記コードはすべて将来仕様で、文法のみ現行構文(extern "C" fn、戻り値は -> と明示 return、string
型)に合わせてあります。
C言語との連携(基本形)
// ハードウェア定義(C言語で記述)→ 現行では extern "C" fn で宣言
extern "C" fn gpio_init(pin: i32)
extern "C" fn gpio_set(pin: i32, state: bool)
extern "C" fn delay(ms: i32)
// NagiScript での利用
fn blink_led(pin: i32, count: i32) {
gpio_init(pin)
var i = 0
while i < count {
gpio_set(pin, true)
delay(500)
gpio_set(pin, false)
delay(500)
i = i + 1
}
}
fn main() {
blink_led(13, 10) // ピン 13 の LED を 10 回点滅
}
GPIO 操作
ピン定義
extern "C" fn gpio_init(pin: i32)
extern "C" fn gpio_set(pin: i32, state: bool)
extern "C" fn gpio_read(pin: i32) -> bool
struct GpioPin {
pin: i32
mode: GpioMode
}
enum GpioMode { Input Output Alternate }
impl GpioPin {
fn write(self: GpioPin, state: bool) {
gpio_set(self.pin, state)
}
}
fn pin_read(pin: i32) -> bool {
return gpio_read(pin)
}
センサー読み取り
温度センサーの例
extern "C" fn adc_read(channel: i32) -> i32
extern "C" fn delay(ms: i32)
fn read_temperature(sensor_pin: i32) -> f64 {
val raw = adc_read(sensor_pin)
// 10 ビット ADC の場合
val voltage = (raw as f64) * 3.3 / 1023.0
// TMP36 センサーの場合
return (voltage - 0.5) * 100.0
}
fn main() {
while true {
val temp = read_temperature(0)
println(temp)
delay(1000)
}
}
UART 通信
extern "C" fn uart_init(baud_rate: i32)
extern "C" fn uart_send(data: *u8, len: i32)
extern "C" fn uart_receive(buffer: *u8, len: i32) -> i32
// ロードマップ: 文字列は *u8 + 長さで扱う(現行では string の生ポインタ取得は未検証)
fn send_bytes(data: *u8, len: i32) {
uart_send(data, len)
}
fn receive_bytes(buffer: *u8, buffer_size: i32) -> i32 {
return uart_receive(buffer, buffer_size)
}
I2C デバイス
extern "C" fn i2c_init(sda: i32, scl: i32, freq: i32)
extern "C" fn i2c_write(addr: u8, data: *u8, len: i32)
extern "C" fn i2c_read(addr: u8, buffer: *u8, len: i32)
struct I2cDevice {
addr: u8
}
impl I2cDevice {
fn write(self: I2cDevice, data: u8) {
var buf = data
i2c_write(self.addr, &buf as *u8, 1)
}
}
SPI 通信
extern "C" fn spi_init(mosi: i32, miso: i32, sck: i32, cs: i32)
extern "C" fn spi_transfer(data: u8) -> u8
fn spi_write(buffer: *u8, len: i32) {
var i = 0
while i < len {
unsafe {
spi_transfer(*buffer)
}
i = i + 1
}
}
割り込み
extern "C" fn interrupt_enable(irq: i32, handler_id: i32)
extern "C" fn interrupt_disable(irq: i32)
extern "C" fn delay(ms: i32)
fn button_handler() {
println("Button pressed!")
}
fn setup_interrupts() {
interrupt_enable(10, 0) // IRQ 10, ハンドラ ID 0 → if 分岐でハンドラ選択(ロードマップ)
}
fn main() {
setup_interrupts()
while true {
// メインループ
delay(100)
}
}
パワーマネジメント
extern "C" fn sleep_enter()
extern "C" fn sleep_exit()
extern "C" fn deep_sleep(ms: i32)
fn low_power_mode() {
sleep_enter()
// 低消費電力モード
sleep_exit()
}
fn power_save(seconds: i32) {
deep_sleep(seconds * 1000)
}
実践的な例: IoT センサーノード
この例はハードウェア制御(GPIO / ADC /
UART)を前提としており、現行では利用できません。文法のみ現行構文に合わせてあります。loop
は現行では使えないため
while true に、文字列連結
+ は使わない形に直しています。
extern "C" fn gpio_init(pin: i32)
extern "C" fn gpio_set(pin: i32, state: bool)
extern "C" fn adc_read(channel: i32) -> i32
extern "C" fn uart_init(baud_rate: i32)
extern "C" fn uart_send(data: *u8, len: i32)
extern "C" fn delay(ms: i32)
extern "C" fn deep_sleep(ms: i32)
struct SensorNode {
led_pin: i32
sensor_pin: i32
uart_baud: i32
}
impl SensorNode {
fn init(self: SensorNode) {
gpio_init(self.led_pin)
uart_init(self.uart_baud)
}
fn read_sensor(self: SensorNode) -> f64 {
val raw = adc_read(self.sensor_pin)
return (raw as f64) * 3.3 / 1023.0
}
fn send_data(self: SensorNode, data: *u8, len: i32) {
gpio_set(self.led_pin, true)
uart_send(data, len)
gpio_set(self.led_pin, false)
}
}
fn main() {
val node = SensorNode {
led_pin: 13,
sensor_pin: 0,
uart_baud: 115200
}
node.init()
while true {
val temp = node.read_sensor()
println(temp)
// 1 秒間スリープ
deep_sleep(1000)
}
}
ビルドとフラッシュ
クロスコンパイル(組み込み向けターゲット)は現行では未対応です。下記は将来仕様です。
# アーム Cortex-M の場合(将来仕様)
nagiscript build main.ngs --target arm-cortex-m4 -o firmware.bin
# フラッシュ
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
-c "program firmware.bin verify reset exit 0x08000000"
注意事項
- スタックサイズ: 組み込み環境ではスタックが限られる
- ヒープ確保: Rc を使用する場合はヒープサイズに注意
- 割り込み安全性: 割り込みハンドラ内でのメモリ確保は避ける
- ボルテージ: ハードウェアの仕様を確認する
