首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >rust embedded将GPIO引脚从输出更改为输入

rust embedded将GPIO引脚从输出更改为输入
EN

Stack Overflow用户
提问于 2021-01-17 07:23:00
回答 1查看 329关注 0票数 1

访问GPIO的Rust STM32xxx_hal代码片段

代码语言:javascript
复制
let gpioa = dp.GPIOA.split(&mut rcc);

let mut p12 = gpioa.pa12;

loop {
   p12.into_push_pull_output().set_low().unwrap();
   //after some processing, time to switch p12 to input
   p12.into_floating_input();

}

编译器抱怨在into_push_pull_output()之后,p12已经“移动”了。

目标是能够动态地将一个GPIO引脚从输出切换到输入。

如何在Rust中实现这一点?

按照马克的回答编辑(见下文),这是用STM32G031J SO-8的3个GPIO引脚对6个LED进行充电的STM32G0代码

代码语言:javascript
复制
#![deny(warnings)]
#![deny(unsafe_code)]
#![no_main]
#![no_std]

// #[allow(unused)]
// use panic_halt;

#[allow(unused)]
use panic_semihosting;

use cortex_m_rt::entry;
use stm32g0xx_hal as hal;

use hal::prelude::*;
use hal::rcc::Config;
use hal::stm32;

#[entry]
fn main() -> ! {
    let dp = stm32::Peripherals::take().expect("cannot take peripherals");
    let mut rcc = dp.RCC.freeze(Config::lsi());
    let mut delay = dp.TIM16.delay(&mut rcc);

    let gpioa = dp.GPIOA.split(&mut rcc);
    let gpiob = dp.GPIOB.split(&mut rcc);

    let mut l3 = gpioa.pa12.into_push_pull_output();
    let mut l2 = gpioa.pa11.into_push_pull_output();
    let mut l1 = gpiob.pb7.into_push_pull_output();
        
    loop {        
        let _l1b = l1.into_floating_input();
        l3.set_high().unwrap();
        l2.set_low().unwrap();
        delay.delay(500.ms());

        l2.set_high().unwrap();
        l3.set_low().unwrap();
        delay.delay(500.ms());
        l1 = _l1b.into_push_pull_output();


        let _l2b = l2.into_floating_input();
        l1.set_high().unwrap();
        l3.set_low().unwrap();
        delay.delay(500.ms());

        l3.set_high().unwrap();
        l1.set_low().unwrap();
        delay.delay(500.ms());
        l2 = _l2b.into_push_pull_output();


        let _l3b = l3.into_floating_input();
        l1.set_high().unwrap();
        l2.set_low().unwrap();
        delay.delay(500.ms());

        l2.set_high().unwrap();
        l1.set_low().unwrap();
        delay.delay(500.ms());
        l3 = _l3b.into_push_pull_output();

    }
}
EN

回答 1

Stack Overflow用户

发布于 2021-01-18 01:30:50

为什么p12要“搬家”?

为了澄清"move“消息:您正在使用的API (例如,into_push_pull_output()函数)具有以下签名:

代码语言:javascript
复制
pub fn into_push_pull_output(
    self,
    moder: &mut MODER,
    otyper: &mut OTYPER
) -> PA12<Output<PushPull>>

这里有很多东西,但需要注意的是,第一个参数是self,而不是&self&mut self。这意味着如果调用p12.into_push_pull_output(),则值p12ownership将从您的函数中转移到into_push_pull_output函数中。由于所有权已转移,因此您正在编写的函数不再可以使用p12的值。换句话说,即使没有循环,也可能会遇到以下错误:

代码语言:javascript
复制
let p12 = gpioa.pa12;
p12.into_push_pull_output().set_low().unwrap();
return p12; // ERROR! `p12` has already moved!

但是为什么开发人员要制作一个能阻止我使用p12值的API呢?

在其他语言和框架中,一种常见的模式是让单个对象表示外围设备,具有改变该对象的方法,然后将可以使用该外围设备做的所有可能的事情都作为方法。代码最终可能如下所示:

代码语言:javascript
复制
let mut p12 = gpioa.pa12;
p12.into_floating_input();
p12.set_low();

这段代码有一个问题。如果IO引脚处于“output”状态,set_low可能是一个有效的操作,但如果代码试图在IO引脚处于输入状态时运行此操作,则可能是一个错误。理想情况下,我们希望能够在编译时检测到此类问题。

许多Rust嵌入式项目中的API将在编译时发现这些类型的错误,而不是使用“类型状态”模式。这将使用Rust语言的所有权特性来强制您在切换状态时获取新值。该新值可能具有不同的类型,并且该新类型将只具有对管脚的状态有效的方法。

例如,在调用into_floating_input()之后,您的管脚将由类型为PA12<Input<Floating>>的值表示。同样,在调用into_push_pull_output()之后,您的管脚将由类型为PA12<Output<PushPull>>的值表示。这两种类型为它们实现了不同的方法。

因此,使用许多用于Rust的嵌入式API将要求您在编写代码时改变思维方式。您可以使用多个p12值来表示引脚,而不是使用p12值来表示引脚处于特定状态。Rust编译器将确保一次只有一个这样的值在运行,并且仅当pin处于特定状态时才允许调用任何方法。从这个角度看,我上面展示的有buggy的代码是在编译时捕获的:

代码语言:javascript
复制
let p12 = gpioa.pa12;

// `into_floating_input()` returns a new value, which represents the pin in
// the "floating input" state.
let new_p12 = p12.into_floating_input();
// This will cause a compiler error, since the type `PA12<Input<Floating>>`
// of `new_p12` wouldn't have a `set_low()` method.
new_p12.set_low();

如果您还没有,请查看Rust Embedded Book中的“静态保证”一章,了解有关此模式的更多信息。

好的,这听起来很好,但是我如何在循环中使用它呢?

要做到这一点,一种简单的方法是为备用状态定义一个起始变量,然后确保在循环结束时将一个值重新分配回起始状态变量。当然,这个值需要具有正确的类型(即,引脚需要处于相同的“状态”)。

代码语言:javascript
复制
// Define a value which has a specific type `PA12<Output<PushPull>>`
let mut p12 = gpioa.pa12.into_push_pull_output().set_low().unwrap();

loop {
    // Use `let` to assign a new value, which has the type `PA12<Input<Floating>>`.
    let p12_input = p12.into_floating_input();
    
    // Perform method calls on `p12_input`
    // ...

    // Get ourselves back to a `PA12<Output<PushPull>>` value.
    p12 = p12_input.into_push_pull_output().set_low().unwrap();
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65755746

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档