背景
組み込みrustという本をかってwioterminalでちょこちょこ載ってるプログラム動かしてみた
が、やはり自分である程度手を動かさないとわかんないよねということでサンプルに乗っていない何かしらをやろうということで
grooveコネクタからADCでセンサ出力を取り出すコードを書いてみた。
やってることは本の中の光センサのところと一緒ですが、Sets構造体やlightSensorメソッドが無かったらどうなるかという感じですね
ちなみに一番引っかかったのはwioクレートのverです。皆さん注意しましょう。
(本は0.3、今は0.6)
おおまかなイメージ
ピン番号の構造体を引っ張ってくる
↓
今回使うピン機能で初期化した構造体を返す
↓
メソッドにreadとか実装されてるはずだからそれから数値帰ってくるだろ!
という気持ち。
ドキュメントからの流れ
まずはwioのクレートのドキュメントからみていく。
以下は試行錯誤した内容
折りたたみ内容Pins構造体
https://docs.rs/wio_terminal/0.3.0/wio_terminal/struct.Pins.html
からadcっぽいフィールド名を見てみる↓
a0_d0: Pb8>
を使えばよさそう
Pb8>を見てみると
https://docs.rs/atsamd-hal/0.12.0/atsamd_hal/common/gpio/v1/type.Pb8.html
type Pb8 = Pin;
という記述がある。Mは本で出てきた型状態プログラミング?
とりあえずジェネリックなのでここでピンの機能を指定してあげるんだろうという気がした
下のところのトレイト実装をみてみると
impl Channel for Pb8
とありいかにもADCデスみたいな顔をしている
MのところはpfBを指定してあげればよさそう
ちなみにこのpfbはperipheral function Bを使うという意味で
このピンのfunctionBがADCなのだろうけど
どこにそれが記載してあるかわからなかった。
さて、てっきりこのトレイトにread的なメソッドが実装されていると思ったんだがない・・・
https://docs.rs/embedded-hal/0.2.4/embedded_hal/adc/trait.Channel.html
が、同ページの左下にひっそりとあるOneShotの項を見るとreadがある
https://docs.rs/embedded-hal/0.2.4/embedded_hal/adc/trait.OneShot.html
しかしながらMyadcってなんやねんという顔になる。
さて、ここでつまったわけですが、よく見るとembedded_halのクレート内を見ています。
oneshotを使いそうなのはなんとなくわかるのでatsamd-hal内にoneshotを実装した構造体がないか調べてみると
https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/prelude/trait._embedded_hal_adc_OneShot.html
どうもAdcという型に実装されていそうである。このページを見てみると
https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/thumbv6m/adc/struct.Adc.html
oneshotが実装されている!つまりこのAdcを使えばいいわけだ
下記が初期化っぽい。
pub fn adc(adc: ADC, pm: &mut PM, clocks: &mut GenericClockController) -> Self
ということで
Myadc = Adc::adc(~);
value = Myadc.read();
ってな感じでvalue内に値が入りそうな気がしてきた。
というわけで上のadc関数を見ていくとclockはまあいつものやつ
いろいろつまったのでwioのクレートに立ち返り
adcで調べてみる
https://docs.rs/wio_terminal/0.3.0/wio_terminal/prelude/trait._embedded_hal_adc_OneShot.html
がでてくる。readで読めそうだ。ADC構造体のページには飛べなかったが、最新版を確認すると
https://github.com/atsamd-rs/atsamd/blob/master/hal/src/thumbv7em/adc.rs
より
Adc構造体をinitしてあげればよさそうだ
fn $init(adc: $ADC, mclk: &mut MCLK, clocks: &mut GenericClockController, gclk:GEN_A)
より後ろ3つの変数はこれまでのものと同じでわかるが、\$ADCだけなにをいれればよくわからない。
ソースを読んでみると
macro_rules! adc_hal { ($($ADC:ident: ($init:ident, $mclk:ident, $apmask:ident, $compcal:ident, $refcal:ident, $r2rcal:ident),)+) => { $(
~省略
adc_hal! { ADC0: (adc0, apbdmask, adc0_, adc0_biascomp_scale_cal, adc0_biasref_scale_cal, adc0_biasr2r_scale_cal), ADC1: (adc1, apbdmask, adc1_, adc1_biascomp_scale_cal, adc1_biasref_scale_cal, adc1_biasr2r_scale_cal), }
このマクロはどうやら\$ADCがADC0かADC1なら($init 以下はadc_hal!以下のものに準ずるということ
\$ADCがADC0ならinitはadc0、\$mclkはapbdmask といった具合だ
ということはAdc構造体をinitしたものをreadしてやればよさそうな気がする
ちょっと上のwioのドキュメントに戻ってみると
pub fn read(&mut self, pin: &mut Pin) -> Result<Word, Error<Self::Error>>
ピン番号もいるらしい。おそらくこのピンを渡すときにADCとしての機能を割り当てる必要ありそう
というわけで
Pins構造体
https://docs.rs/wio_terminal/0.3.0/wio_terminal/struct.Pins.html
からadcっぽいフィールド名を見てみる↓
a0_d0: Pb8<Input<Floating>>
を使えばよさそう
Pb8<Input<Floating>>を見てみると
https://docs.rs/atsamd-hal/0.12.0/atsamd_hal/common/gpio/v1/type.Pb8.html
type Pb8<M> = Pin<PB08, M>;
という記述がある。これを渡せばよさそう。ちょっと下を見てみると
impl Channel<ADC> for Pb8<PfB>
という記述がある。PfBはオルタネートBというピン機能の割り当てをした結果っぽいので初期化前ののピンから
オルタネートBにしたピンを用意してあげればよい気がする
Pb8<Input<Floating>>にオルタネートにするメソッドがないか確認してみる。
Struct atsamd_hal::common::gpio::v1::Pin のメソッドを見てみると
pub fn into_function_b(self, port: &mut Port) -> Pin<I, PfB>
というメソッドがありこれを使えばよさそうだ
流れをまとめると
Adc構造体にreadメソッドがあることがわかった。
Adc構造体を使うにはピン番号を渡してあげる必要がありそう。
ピンには複数の機能がありそれを指定してからAdc構造体に渡す必要がある。
今回はオルタネートBというモードをピンに指定する。
というわけでコード。組み込みrustの本に倣ってデバイスドライバっぽくしている。
また、一部のみ記載している
pub struct Myadc {
pb8: Pb8<PfB>,
}
impl Myadc {
// PC26ピンを入力モードに設定する
pub fn new(pin: Pb8<Input<Floating>>, port: &mut Port) -> Myadc {
Myadc {
pb8: pin.into_function_b(port),
}
}
pub fn init(
self,
adc: ADC1,
clocks: &mut GenericClockController,
mclk: &mut MCLK,
) -> (Adc<ADC1>, Pb8<PfB>){
let adc1 = Adc::adc1(adc, mclk, clocks, GCLK11);
(adc1, self.pb8)
}
}
use wio_groove::{Led,Button1,Myadc,UART};//ここでlib.rsをインポートしています。自分の環境ではwio_grooveは適宜変更すること
let mut myadc= Myadc::new(pins.a0_d0, &mut pins.port);
let (mut groove, mut pb8) = myadc.init(
peripherals.ADC1,
&mut clocks,
&mut peripherals.MCLK,
);
loop{
let mut x :u16 = nb::block!(groove.read(&mut pb8)).unwrap();
}
これでxにアナログ入力の値が入ります。とりあえずgrooveコネクタのGNDとショートさせてみたら0近くになったので
大丈夫っぽい。
ちゃんと電圧が読めるかどうかは確認予定。
↓
ちゃんと読めました3.3vの12BITのようです
疲れた。