首页 > 其他分享 >esp32笔记[8]-rust的定时器中断点灯

esp32笔记[8]-rust的定时器中断点灯

时间:2023-10-21 21:00:31浏览次数:37  
标签:mut 定时器 中断 esp32 interrupt let Interrupt Mutex rust

摘要

使用rust开发esp32c3实现定时器中断点亮led灯.

超链接

esp32笔记[7]-使用rust+zig开发入门

平台信息

  • esp32c3
  • rust

Mutex锁

use core::cell::RefCell;
use critical_section::Mutex;//no-std库专用的Mutex

我们首先注意到的是静态变量BUTTON。我们需要它,因为在中断处理程序中,我们必须清除按钮上的待处理中断,并且我们需要将按钮从主函数传递到中断处理程序中。由于中断处理程序不能有参数,我们需要使用静态变量将按钮传递到中断处理程序中。我们需要使用互斥锁(Mutex)来确保对按钮的访问安全。请注意,这里使用的Mutex不同于你可能熟悉的libstd中的Mutex,而是来自临界区(critical-section),这也是为什么我们需要将其作为依赖项的原因。

use critical_section::Mutex; 是导入了一个名为Mutex的类型,它是为无标准库环境(no-std)设计的一种互斥锁。在嵌入式系统或某些特定的环境中,可能无法使用标准库中的互斥锁,因此需要使用类似critical_section这样的库来提供互斥锁功能。

互斥锁是一种同步机制,用于控制对共享资源的访问。当多个线程或中断处理程序需要同时访问某个共享资源时,互斥锁可以确保每一时刻只有一个线程或中断处理程序可以访问该资源,从而避免出现竞争条件和数据损坏。

在提到代码中的Mutex时,它可能是用于保护静态变量BUTTON的访问。通过在代码中使用Mutex,可以在不同的线程或中断处理程序之间提供互斥性,确保每一时刻只有一个线程或中断处理程序可以访问按钮。这样可以安全地在中断处理程序中处理按钮的待处理中断,并在主函数中使用按钮的值而不会出现竞争条件。

需要注意的是,该Mutex类型来自于critical_section库,而不是标准库中的Mutex。这是因为在无标准库环境中,通常无法使用标准库提供的互斥锁实现,而需要使用特定于该环境的替代方案,如critical_section库中提供的互斥锁。

构造io口的全局变量以在中断中操作io口

要在中断中访问led变量,需要将其设置为一个全局静态变量。这样,在中断处理函数中就可以直接使用它,而不需要通过锁来访问。

关键代码:

use core::cell::RefCell;//内部可变性类型RefCell
use critical_section::Mutex;//no-std库专用的Mutex
static mut LED_D4: Option<esp32c3_hal::gpio::Gpio12<Output<PushPull>>> = None;

unsafe {
    LED_D4.replace(io.pins.gpio12.into_push_pull_output());
}

//翻转led_d4电平
unsafe {
    if let Some(led) = &mut LED_D4 {
        led.toggle();
    } else {
        esp_println::println!("Toggle LED_D4 failed!");
    }
}

完整示例参照如下代码.

esp-rs的中断及中断服务函数

[https://esp-rs.github.io/no_std-training/03_4_interrupt.html]

//! GPIO interrupt
//!
//! This prints "Interrupt" when the boot button is pressed.
//! It also blinks an LED like the blinky example.

#![no_std]
#![no_main]

use core::cell::RefCell;

use critical_section::Mutex;
use esp32c3_hal::{
    clock::ClockControl,
    gpio::{Event, Gpio9, Input, PullDown, IO},
    interrupt,
    peripherals::{self, Peripherals},
    prelude::*,
    riscv,
    Delay,
};
use esp_backtrace as _;

static BUTTON: Mutex<RefCell<Option<Gpio9<Input<PullDown>>>>> = Mutex::new(RefCell::new(None));//我们需要使用互斥锁Mutex来保证对按钮的访问是安全的

#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // Set GPIO5 as an output
    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    let mut led = io.pins.gpio5.into_push_pull_output();

    // Set GPIO9 as an input
    let mut button = io.pins.gpio9.into_pull_down_input();

    /*
    我们需要在按钮引脚上调用listen函数来配置外设以触发中断。我们可以为不同的事件触发中断 - 在这里,我们希望在下降沿时触发中断。
    */
    button.listen(Event::FallingEdge);

    /*
    在 critical_section::with 闭包中运行的代码在临界区内执行,cs 是一个令牌,你可以用它来向某个 API "证明" 这一点。
    */
    critical_section::with(|cs| BUTTON.borrow_ref_mut(cs).replace(button));

    /*
    这里的第一个参数是我们想要的中断类型。有几种可能的中断类型。第二个参数选择了优先级,我们选择了Priority3。优先级决定了在多个中断同时触发时首先运行哪个中断。
    */
    interrupt::enable(peripherals::Interrupt::GPIO,interrupt::Priority::Priority3).unwrap();


    // 允许中断
    unsafe {
        riscv::interrupt::enable();
    }

    let mut delay = Delay::new(&clocks);
    loop {
        led.toggle().unwrap();
        delay.delay_ms(500u32);
    }
}

//中断服务函数,函数名`GPIO`需要匹配`peripherals::Interrupt::GPIO,`的GPIO函数
#[interrupt]
fn GPIO() {
    critical_section::with(|cs| {
        esp_println::println!("GPIO interrupt");
        BUTTON
            .borrow_ref_mut(cs)
            .as_mut()
            .unwrap()
            .clear_interrupt();
    });
}

实现

[https://esp-rs.github.io/no_std-training/03_4_interrupt.html]
[https://github.com/esp-rs/esp-hal]
[https://github.com/esp-rs/esp-hal/blob/main/esp32c3-hal/examples/timer_interrupt.rs]

主要代码

src/main.rs

/*
备注:
- 使用no-std,没有常规的main函数
目标平台:
- esp32c3(riscv32imc)
依赖:
- esp32c3-hal(0.12.0)
- esp-backtrace(0.8.0)
- esp-println(0.6.0)
- critical_section(1.1.1)
编译及烧录命令:
- cargo-offline run
- cargo-offline build --release
*/
#![no_std]
#![no_main]

/* start 与zig代码通信 */
#[link(name = "main")]
extern "C" {
    fn add(x: i32, y: i32) -> i32;
}
/* end 与zig代码通信 */

/* start 导入库 */
use esp_println::println;//串口打印
use core::cell::RefCell;//内部可变性类型RefCell
use critical_section::Mutex;//no-std库专用的Mutex

use esp32c3_hal::{
    clock::ClockControl, 
    peripherals::{self, Peripherals, TIMG0, TIMG1},
    prelude::*, 
    timer::{Timer, Timer0, TimerGroup},
    Rtc,
    Delay,
    gpio::{AnyPin,Input, Output, PullDown, PushPull, IO},
    systimer::SystemTimer,
    interrupt,
    riscv,
};

use esp_backtrace as _;// 获取调用堆栈信息
/* end 导入库 */

/* start 全局变量 */
static TIMER0: Mutex<RefCell<Option<Timer<Timer0<TIMG0>>>>> = Mutex::new(RefCell::new(None));
static TIMER1: Mutex<RefCell<Option<Timer<Timer0<TIMG1>>>>> = Mutex::new(RefCell::new(None));
static mut LED_D4: Option<esp32c3_hal::gpio::Gpio12<Output<PushPull>>> = None;
/* end 全局变量 */

/* start 程序入口点 */
#[entry]
fn main() -> ! {
    // 实例化对象和定义变量
    let peripherals = Peripherals::take();
    let mut system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // TIMG0和TIMG1各自包含一个通用定时器和一个看门狗定时器
    let mut rtc = Rtc::new(peripherals.RTC_CNTL);
    let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks,&mut system.peripheral_clock_control);
    let mut wdt0 = timer_group0.wdt;
    let mut timer0 = timer_group0.timer0;
    let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks,&mut system.peripheral_clock_control);
    let mut wdt1 = timer_group1.wdt;
    let mut timer1 = timer_group1.timer0;

    // 延时函数初始化
    let mut delay = Delay::new(&clocks);

    // 配置gpio口
    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

    // 配置引脚功能
    //let mut led_d4 = io.pins.gpio12.into_push_pull_output();
    let mut led_d5 = io.pins.gpio13.into_push_pull_output();

    unsafe {
        LED_D4.replace(io.pins.gpio12.into_push_pull_output());
    }

    // 初始化定时器0中断
    interrupt::enable(
        peripherals::Interrupt::TG0_T0_LEVEL,
        interrupt::Priority::Priority1,
    )
    .unwrap();
    timer0.start(500u64.millis());
    timer0.listen();

    // 初始化定时器1中断
    interrupt::enable(
        peripherals::Interrupt::TG1_T0_LEVEL,
        interrupt::Priority::Priority1,
    )
    .unwrap();
    timer1.start(1u64.secs());
    timer1.listen();

    // 打开定时器引用锁Mutex,使得定时器中断handler安全跳转
    critical_section::with(|cs| {
        TIMER0.borrow_ref_mut(cs).replace(timer0);
        TIMER1.borrow_ref_mut(cs).replace(timer1);
    });

    // 允许中断
    unsafe {
        riscv::interrupt::enable();
    }

    // 关闭rtc和wdt
    //rtc.swd.disable();
    //rtc.rwdt.disable();
    //wdt0.disable();
    //wdt1.disable();

    println!("Compute");
    unsafe {
        println!("{}", add(4,5));
    }

    // 开始循环
    loop {
        println!("Compute(ziglang):");
        unsafe {
            println!("{}", add(4,5));//println波特率115200
        }

        // 翻转led_d5电平
        led_d5.toggle().unwrap();

        delay.delay_ms(2000u32);//延时2000ms
    }

}
/* end 程序入口点 */

/* start 中断处理函数 */
#[interrupt]
fn TG0_T0_LEVEL() {
    critical_section::with(|cs| {
        esp_println::println!("Interrupt 1");

        let mut timer0 = TIMER0.borrow_ref_mut(cs);
        let timer0 = timer0.as_mut().unwrap();

        timer0.clear_interrupt();
        timer0.start(500u64.millis());
    });
}

#[interrupt]
fn TG1_T0_LEVEL() {
    critical_section::with(|cs| {
        esp_println::println!("Interrupt 11");

        //翻转led_d4电平
        unsafe {
            if let Some(led) = &mut LED_D4 {
                led.toggle();
            } else {
                esp_println::println!("Toggle LED_D4 failed!");
            }
        }

        let mut timer1 = TIMER1.borrow_ref_mut(cs);
        let timer1 = timer1.as_mut().unwrap();

        timer1.clear_interrupt();
        timer1.start(1u64.secs());
    });
}
/* end 中断处理函数 */

.cargo/config.toml

[target.riscv32imc-unknown-none-elf]
runner = "espflash flash --monitor"
#runner = "cargo espflash flash --release --monitor"
#runner = "espflash: cargo build --release && espflash flash './target/riscv32imc-unknown-none-elf/release/esp32c3_zig'"
#runner = "wokwi-server --chip esp32c3"

[build]
rustflags = [
  # enable the atomic codegen option for RISCV
  "-C", "target-feature=+a",

  # Tell the `core` library that we have atomics, even though it's not
  # specified in the target definition
  "--cfg", "target_has_atomic_load_store",
  "--cfg", 'target_has_atomic_load_store="8"',
  "--cfg", 'target_has_atomic_load_store="16"',
  "--cfg", 'target_has_atomic_load_store="32"',
  "--cfg", 'target_has_atomic_load_store="ptr"',
  "--cfg", "target_has_atomic",
  "--cfg", 'target_has_atomic="8"',
  "--cfg", 'target_has_atomic="16"',
  "--cfg", 'target_has_atomic="32"',
  "--cfg", 'target_has_atomic="ptr"',

  # Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.)
  # NOTE: May negatively impact performance of produced code
  "-C", "force-frame-pointers",
  "-C", "link-arg=-Tlinkall.x",
  "-C", "link-arg=-Lziglib",
  "-C", "link-arg=-lmain"
]
target = "riscv32imc-unknown-none-elf"

[unstable]
build-std = ["core"]

Cargo.toml

[package]
name = 'esp32c3_zig'
edition = '2021'
version = '0.1.0'
authors = [
    'Juraj Michálek <[email protected]>',
    'qsbye',
]
license = 'MIT OR Apache-2.0'

[package.metadata]
last-modified-system-time = 1697885007
[dependencies.critical-section]
version = '1.1.1'
features = []

[dependencies.esp-backtrace]
version = '0.8.0'
features = [
    'esp32c3',
    'panic-handler',
    'print-uart',
    'exception-handler',
]

[dependencies.esp-println]
version = '0.6.0'
features = ['esp32c3']

[dependencies.esp32c3-hal]
version = '0.12.0'
features = []

编译及运行

#cargo-offline可以用cargo命令替代
#测试编译
cargo-offline build --release
#编译及运行
cargo-offline run

效果

闪灯效果动图

输出:

Compute(ziglang):
9
Interrupt 11
Interrupt 1
Interrupt 1
Interrupt 11
Interrupt 1
Interrupt 1
Compute(ziglang):
9
Interrupt 11
Interrupt 1
Interrupt 1
Interrupt 11
Interrupt 1
Interrupt 1
Compute(ziglang):
9
Interrupt 11
Interrupt 1
Interrupt 1
Interrupt 11
Interrupt 1
Interrupt 1
Compute(ziglang):
9
Interrupt 11
Interrupt 1

标签:mut,定时器,中断,esp32,interrupt,let,Interrupt,Mutex,rust
From: https://www.cnblogs.com/qsbye/p/17779543.html

相关文章

  • Rust,linker but `link.exe` was not found
    themsvctargetsdependonthemsvclinkerbut`link.exe`wasnotfound这是提示未安装vsstudioc++组件,由于vsstudio组件较大,可以选择安装gnu的,具体如下:命令行执行下边指令rustuptoolchaininstallstable-x86_64-pc-windows-gnurustupdefaultstable-x86_64-pc-wind......
  • esp32 factory+双OTA分区
    #Name,Type,SubType,Offset,Size,Flags#Note:ifyouhaveincreasedthebootloadersize,makesuretoupdatetheoffsetstoavoidoverlap,,,,nvs,data,nvs,0x4000,otadata,data,ota,0x4000,phy_init,data,phy,0x1000,factory,......
  • rust笔记-变量
    rust变量默认是不可变的,需要手动设置可变性。可变的变量给编程提供了灵活性,不可变的变量提供了安全性。而rust中需要根据实际场景来指定的变量类型。变量声明变量声明使用关键字let,语法格式如下:let变量名=值;变量声明后,变量名可以被使用,但是值不能被修改。这种方式申请的......
  • rustdesk 1.2.3-1 尝鲜,有很大提升空间
    产品说的很好。测试了一下电脑从archlinuxcn下的rustdesk1.2.3-1,手机端fdroid下的,版本是1.1.10-1,同一局域网,未自建服务器。电脑端启动后显示账号和一次性密码。手机输入账号密码后连接,手机画面黑屏,过一回依旧黑屏不显示,再一会断联。手机可选显示哪个桌面(一共两)。密码刷新后......
  • Rust: function
    /***file:nested.rs***////公有函数pubfnfunction(){println!("called`my::nested::function()`");}///私有函数#[allow(dead_code)]fnprivate_function(){println!("called`my::nested::private_function()`");}/......
  • 嵌入式系统中的低功耗定时器应用与优化实战
    嵌入式系统的开发在现代科技中发挥着至关重要的作用。它们被广泛应用于从智能家居到工业自动化的各种领域。在本文中,我们将聚焦于使用ARMCortex-M系列微控制器实现低功耗定时器的应用。我们将详细介绍在嵌入式系统中如何实现低功耗的定时器功能,并附上代码示例。嵌入式系统低功耗定......
  • 10_rust的结构体struct
    rust的struct定义和实例化struct使用struct关键字,并对整个struct命名。在花括号内,对所有字段(Field)定义名称和类型。创建struct实例:为每个字段指定具体值,无需按声明顺序进行指定。structUser{name:String,id:u64,is_act:bool,}fnmain(){letu1=......
  • python报错解决-ValueError: Trusted host URL must include a host part: '#!
    删掉#后面的字符参考:pipinstall总是报错:ValueError:TrustedhostURLmustincludeahostpart:‘#‘-CSDN博客......
  • Codeforces Round #870 (Div. 2) A. Trust Nobody
    题解#include<cstdio>#include<vector>#include<queue>#include<cstring>#include<algorithm>#include<iostream>#include<stack>#include<bitset>#include<cstdlib>#include<cmath>#include......
  • 从内存使用角度的比较:Go vs Rust
    Go和Rust是最近几年非常火的语言,经常有人问到底该怎么选择,特别是谁更适合搭建网络后台服务,哪一个性能更好,稳定性更高。网络上Go和Rust的比较文章很多,大体上是做一个测试或写几段测试代码,根据运行的时长来比较哪个性能更好,但这种测试可能会陷入误区:1)比来比去,比的是网络IO,因为这种......