从树莓派 Pico 使用 Rust 读取 SPI 的值

huangapple go评论82阅读模式
英文:

Read value from SPI on Raspberry Pi Pico using Rust

问题

我正在尝试通过Raspberry Pi Pico上的SPI读取传感器BMP280的值,但我得到了一个意外的值。

我基于rp2040-project-template创建了一个新的仓库,并对其进行了修改以添加SPI功能。

我添加了以下导入:

use embedded_hal::prelude::_embedded_hal_spi_FullDuplex;
use rp_pico::hal::spi;
use rp_pico::hal::gpio;
use fugit::RateExtU32;

然后,我在主函数的底部设置了SPI:

let _spi_sclk = pins.gpio2.into_mode::<gpio::FunctionSpi>();
let _spi_mosi = pins.gpio3.into_mode::<gpio::FunctionSpi>();
let _spi_miso = pins.gpio4.into_mode::<gpio::FunctionSpi>();
let mut spi_cs = pins.gpio5.into_push_pull_output_in_state(PinState::Low);

let spi = spi::Spi::<_, _, 8>::new(pac.SPI0);

let mut spi = spi.init(
    &mut pac.RESETS,
    clocks.peripheral_clock.freq(),
    10.MHz(), // bmp280 has 10MHz as maximum
    &embedded_hal::spi::MODE_0,
);

spi_cs.set_high().unwrap();
delay.delay_ms(200);

然后,我尝试读取ID寄存器:

spi_cs.set_low().unwrap();
let res_w = spi.send(0xd0 as u8);
let res_r = spi.read();
spi_cs.set_high().unwrap();

match res_w {
    Ok(_) => info!("write worked"),
    Err(_) => info!("failed to write"),
}

match res_r {
    Ok(v) => info!("read value from SPI: {}", v),
    Err(_) => info!("failed to read SPI"),
}

使用此代码,SPI读取失败。为什么会这样?

也许在读取ID之前需要在传感器上设置一个模式。我可以在读取之前添加以下代码以设置强制模式:

spi_cs.set_low().unwrap();
spi.send(0xf4 - 128 as u8).expect("failed to send first byte");
spi.send(0x1 as u8).expect("failed to send second byte");
spi_cs.set_high().unwrap();

现在ID寄存器的读取正常,但我得到的值是255而不是预期的0x58

我做错了什么?


我还尝试使用以下代码使用transfer

let mut data: [u8; 2] = [0xd0, 0x0];
let transfer_success = spi.transfer(&mut data);
match transfer_success {
    Ok(v) => info!("read data {}", v),
    Err(_) => info!("failed to read"),
}

但是,使用此代码,我读取的值是[255, 255],而不是预期的0x58

英文:

I am trying to read a value from a sensor, BMP280 over SPI on a Raspberry Pi Pico. But I am getting an unexpected value.

I created a new repo based on the rp2040-project-template and modified it to add SPI functionality.

I added these imports:

use embedded_hal::prelude::_embedded_hal_spi_FullDuplex;
use rp_pico::hal::spi;
use rp_pico::hal::gpio;
use fugit::RateExtU32;

Then I setup SPI in the bottom of main function:

let _spi_sclk = pins.gpio2.into_mode::&lt;gpio::FunctionSpi&gt;();
let _spi_mosi = pins.gpio3.into_mode::&lt;gpio::FunctionSpi&gt;();
let _spi_miso = pins.gpio4.into_mode::&lt;gpio::FunctionSpi&gt;();
let mut spi_cs = pins.gpio5.into_push_pull_output_in_state(PinState::Low); // initial pull down, for SPI

let spi = spi::Spi::&lt;_, _, 8&gt;::new(pac.SPI0);

let mut spi = spi.init(
    &amp;mut pac.RESETS,
    clocks.peripheral_clock.freq(),
    10.MHz(),                           // bmp280 has 10MHz as maximum
    &amp;embedded_hal::spi::MODE_0,
);

spi_cs.set_high().unwrap();   // pull up, set as inactive after init
delay.delay_ms(200);                     // some delay for testing

Then I try to read the ID registry

spi_cs.set_low().unwrap();
let res_w = spi.send(0xd0 as u8); // 0xd0 is address for ID, with msb 1
let res_r = spi.read();
spi_cs.set_high().unwrap();

// check results
match res_w {
    Ok(_) =&gt; info!(&quot;write worked&quot;),
    Err(_) =&gt; info!(&quot;failed to write&quot;)
}

match res_r {
    Ok(v) =&gt; info!(&quot;read value from SPI: {}&quot;, v),
    Err(_) =&gt; info!(&quot;failed to read SPI&quot;)
}

With this code, the SPI read fails. Why is that?

Perhaps it is necessary to set a mode on the sensor, before reading the ID. I can add this code above the read, to set forced mode.

spi_cs.set_low().unwrap();
spi.send(0xf4-128 as u8).expect(&quot;failed to send first byte&quot;);  // registry 0xf4 with msb 0
spi.send(0x1 as u8).expect(&quot;failed to send second byte&quot;);
spi_cs.set_high().unwrap();

Now the read of ID registry works, but I get value 255 and not the expected 0x58.

What am I doing wrong?


I have also tried with transfer using this code:

let mut data: [u8; 2] = [0xd0, 0x0];
let transfer_success = spi.transfer(&amp;mut data);
match transfer_success {
    Ok(v) =&gt; info!(&quot;read data {}&quot;, v),
    Err(_) =&gt; info!(&quot;failed to read&quot;)
}

But I read the values as [255, 255] with this code, not the expected 0x58.

答案1

得分: 3

read() 可能不是你想在这里使用的函数;它实际上不执行任何总线操作,只是提供上次 send() 期间读取的字节。

你实际想要使用的函数是 transfer()。在全双工 SPI 总线上,"读" 操作始终也是 "写" 操作,transfer 执行两者。请注意,如果你只想读取,你需要写入相同数量的零,因为只有总线主控可以提供时钟来执行任何操作。

所以,如果你想写入 0xd0,然后读取一个字节,你需要通过 transfer() 发送值 [0xd0, 0x00]。你用于将发送数据放入 transfer()相同数组将包含接收到的数据;很可能是 [0x00, <data>](或 [0xff, <data>],不确定。可能是 0xff,因为你已经提到你读取了 255)。

transfer 的实现 显示了 read() 实际上应该如何使用:

fn transfer&lt;&#39;w&gt;(&amp;mut self, words: &amp;&#39;w mut [W]) -&gt; Result&lt;&amp;&#39;w [W], S::Error&gt; {
    for word in words.iter_mut() {
        block!(self.send(word.clone()))?;
        *word = block!(self.read())?;
    }

    Ok(words)
}

请注意这里的 block!() - 在嵌入式系统中,通常异步调用会返回指示操作将被阻塞直到完成的错误。block!() 宏将异步调用转换为阻塞调用,这很可能是你的错误来源。

无论如何,我建议从官方示例中派生你的代码,这些示例通常很好地演示了应该如何使用对象的预期方式。

英文:

read() is probably not the function you want to use here; it doesn't acutally perform any bus action but only gives you the byte that was read during the last send().

The function you actually want to use is transfer(). On a full-duplex SPI bus, a "read" action is always also a "write" action, and transfer performs both. Be aware that if you only want to read, you need to write the same amount of zeros, because only the bus master can provide the clock to do anything.

So if you want to write 0xd0, followed by reading a single byte, you need to transfer() the values [0xd0, 0x00]. The same array that you use to put your sent data into transfer() will then contain the received data; most likely [0x00, &lt;data&gt;] (or [0xff, &lt;data&gt;], not sure. Probably 0xff, as you already mentioned that you read a 255).

The implementation of transfer shows how read() is actually supposed to be used:

fn transfer&lt;&#39;w&gt;(&amp;mut self, words: &amp;&#39;w mut [W]) -&gt; Result&lt;&amp;&#39;w [W], S::Error&gt; {
    for word in words.iter_mut() {
        block!(self.send(word.clone()))?;
        *word = block!(self.read())?;
    }

    Ok(words)
}

Note the block!() here - in embedded, asynchronous calls usually return an error indicating that the operation would block, until it is finished. The block!() macro converts an asynchronous call to a blocking call, which is most likely where your error comes from.

Either way, I would recommend deriving your code from the official example, those are usually pretty good at demonstrating the intended way an object should be used.

huangapple
  • 本文由 发表于 2023年6月1日 20:46:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76382019.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定