diff --git a/.vscode/settings.json b/.vscode/settings.json index 5234c64..bacd3af 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "rust-analyzer.cargo.target": "thumbv6m-none-eabi", "rust-analyzer.checkOnSave.allTargets": false, - "editor.formatOnSave": true + "editor.formatOnSave": true, + "workbench.colorTheme": "Default Dark+" } \ No newline at end of file diff --git a/README.md b/README.md index 6d9f121..b79110e 100644 --- a/README.md +++ b/README.md @@ -1,279 +1,12 @@ -# Project template for rp2040-hal +# RP2040 Rust Robot +Robot controller code in Rust for a two wheel robot driven by a _Raspberry Pi Pico (RP2040)_. -This template is intended as a starting point for developing your own firmware based on the rp2040-hal. +_This is a Work in Progress_ -It includes all of the `knurling-rs` tooling as showcased in https://github.com/knurling-rs/app-template (`defmt`, `defmt-rtt`, `panic-probe`, `flip-link`) to make development as easy as possible. +This project is a fork of the [rp2040-project-template](https://github.com/rp-rs/rp2040-project-template). See the template's [README.md](https://github.com/rp-rs/rp2040-project-template/blob/main/README.md) for details on how to build the code found here for the RP2040. -`probe-rs` is configured as the default runner, so you can start your program as easy as -```sh -cargo run --release -``` -If you aren't using a debugger (or want to use other debugging configurations), check out [alternative runners](#alternative-runners) for other options - - -
- -

Table of Contents

-
    -
  1. Requirements
  2. -
  3. Installation of development dependencies
  4. -
  5. Running
  6. -
  7. Alternative runners
  8. -
  9. Notes on using rp2040_boot2
  10. -
  11. Feature flags
  12. -
  13. Roadmap
  14. -
  15. Contributing
  16. -
  17. Code of conduct
  18. -
  19. License
  20. -
  21. Contact
  22. -
-
- - -
-

Requirements

- -- The standard Rust tooling (cargo, rustup) which you can install from https://rustup.rs/ - -- Toolchain support for the cortex-m0+ processors in the rp2040 (thumbv6m-none-eabi) - -- flip-link - this allows you to detect stack-overflows on the first core, which is the only supported target for now. - -- (by default) A [`probe-rs` installation](https://probe.rs/docs/getting-started/installation/) - -- A [`probe-rs` compatible](https://probe.rs/docs/getting-started/probe-setup/) probe - - You can use a second - [Pico as a CMSIS-DAP debug probe](debug_probes.md#raspberry-pi-pico). Details - on other supported debug probes can be found in - [debug_probes.md](debug_probes.md) - -
- - -
-

Installation of development dependencies

- -```sh -rustup target install thumbv6m-none-eabi -cargo install flip-link -# Installs the probe-rs tools, including probe-rs run, our recommended default runner -cargo install probe-rs --features=cli --locked -# If you want to use elf2uf2-rs instead, do... -cargo install elf2uf2-rs --locked -``` -If you get the error ``binary `cargo-embed` already exists`` during installation of probe-rs, run `cargo uninstall cargo-embed` to uninstall your older version of cargo-embed before trying again. - -
- - - -
-

Running

- -For a debug build -```sh -cargo run -``` -For a release build -```sh -cargo run --release -``` - -If you do not specify a DEFMT_LOG level, it will be set to `debug`. -That means `println!("")`, `info!("")` and `debug!("")` statements will be printed. -If you wish to override this, you can change it in `.cargo/config.toml` -```toml -[env] -DEFMT_LOG = "off" -``` -You can also set this inline (on Linux/MacOS) -```sh -DEFMT_LOG=trace cargo run -``` - -or set the _environment variable_ so that it applies to every `cargo run` call that follows: -#### Linux/MacOS/unix -```sh -export DEFMT_LOG=trace -``` - -Setting the DEFMT_LOG level for the current session -for bash -```sh -export DEFMT_LOG=trace -``` - -#### Windows -Windows users can only override DEFMT_LOG through `config.toml` -or by setting the environment variable as a separate step before calling `cargo run` -- cmd -```cmd -set DEFMT_LOG=trace -``` -- powershell -```ps1 -$Env:DEFMT_LOG = trace -``` - -```cmd -cargo run -``` - -
- -
-

Alternative runners

- -If you don't have a debug probe or if you want to do interactive debugging you can set up an alternative runner for cargo. - -Some of the options for your `runner` are listed below: - -* **`cargo embed`** - This is basically a more configurable version of `probe-rs run`, our default runner. - See [the `cargo-embed` tool docs page](https://probe.rs/docs/tools/cargo-embed/) for - more information. - - *Step 1* - Install `cargo-embed`. This is part of the [`probe-rs`](https://crates.io/crates/probe-rs) tools: - - ```console - $ cargo install probe-rs --features=cli --locked - ``` - - *Step 2* - Update settings in [Embed.toml](./Embed.toml) - - The defaults are to flash, reset, and start a defmt logging session - You can find all the settings and their meanings [in the probe-rs repo](https://github.com/probe-rs/probe-rs/blob/c0610e98008cbb34d0dc056fcddff0f2d4f50ad5/probe-rs/src/bin/probe-rs/cmd/cargo_embed/config/default.toml) - - *Step 3* - Use the command `cargo embed`, which will compile the code, flash the device - and start running the configuration specified in Embed.toml - - ```console - $ cargo embed --release - ``` - -* **probe-rs-debugger** - *Step 1* - Install Visual Studio Code from https://code.visualstudio.com/ - - *Step 2* - Install `probe-rs` - ```console - $ cargo install probe-rs --features=cli --locked - ``` - - *Step 3* - Open this project in VSCode - - *Step 4* - Install `debugger for probe-rs` via the VSCode extensions menu (View > Extensions) - - *Step 5* - Launch a debug session by choosing `Run`>`Start Debugging` (or press F5) - -* **Loading a UF2 over USB** - *Step 1* - Install [`elf2uf2-rs`](https://github.com/JoNil/elf2uf2-rs): - - ```console - $ cargo install elf2uf2-rs --locked - ``` - - *Step 2* - Modify `.cargo/config` to change the default runner - - ```toml - [target.`cfg(all(target-arch = "arm", target_os = "none"))`] - runner = "elf2uf2-rs -d" - ``` - - The all-Arm wildcard `'cfg(all(target_arch = "arm", target_os = "none"))'` is used - by default in the template files, but may also be replaced by - `thumbv6m-none-eabi`. - - *Step 3* - Boot your RP2040 into "USB Bootloader mode", typically by rebooting - whilst holding some kind of "Boot Select" button. On Linux, you will also need - to 'mount' the device, like you would a USB Thumb Drive. - - *Step 4* - Use `cargo run`, which will compile the code and start the - specified 'runner'. As the 'runner' is the `elf2uf2-rs` tool, it will build a UF2 - file and copy it to your RP2040. - - ```console - $ cargo run --release - ``` - -* **Loading with picotool** - As ELF files produced by compiling Rust code are completely compatible with ELF - files produced by compiling C or C++ code, you can also use the Raspberry Pi - tool [picotool](https://github.com/raspberrypi/picotool). The only thing to be - aware of is that picotool expects your ELF files to have a `.elf` extension, and - by default Rust does not give the ELF files any extension. You can fix this by - simply renaming the file. - - This means you can't easily use it as a cargo runner - yet. - - Also of note is that the special - [pico-sdk](https://github.com/raspberrypi/pico-sdk) macros which hide - information in the ELF file in a way that `picotool info` can read it out, are - not supported in Rust. An alternative is TBC. - -
- -
-

Notes on using rp2040_boot2

- - The second-stage boot loader must be written to the .boot2 section. That - is usually handled by the board support package (e.g.`rp-pico`). If you don't use - one, you should initialize the boot loader manually. This can be done by adding the - following to the beginning of main.rs: - ```rust - use rp2040_boot2; - #[link_section = ".boot2"] - #[used] - pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; - ``` - -
- - -
-

Feature flags

- - There are several [feature flags in rp2040-hal](https://docs.rs/rp2040-hal/latest/rp2040_hal/#crate-features). - If you want to enable some of them, uncomment the `rp2040-hal` dependency in `Cargo.toml` and add the - desired feature flags there. For example, to enable ROM functions for f64 math using the feature `rom-v2-intrinsics`: - ``` - rp2040-hal = { version="0.9", features=["rt", "critical-section-impl", "rom-v2-intrinsics"] } - ``` -
- - - -## Roadmap - -NOTE These packages are under active development. As such, it is likely to -remain volatile until a 1.0.0 release. - -See the [open issues](https://github.com/rp-rs/rp2040-project-template/issues) for a list of -proposed features (and known issues). - -## Contributing - -Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. - -The steps are: - -1. Fork the Project by clicking the 'Fork' button at the top of the page. -2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) -3. Make some changes to the code or documentation. -4. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) -5. Push to the Feature Branch (`git push origin feature/AmazingFeature`) -6. Create a [New Pull Request](https://github.com/rp-rs/rp-hal/pulls) -7. An admin will review the Pull Request and discuss any changes that may be required. -8. Once everyone is happy, the Pull Request can be merged by an admin, and your work is part of our project! - -## Code of Conduct - -Contribution to this crate is organized under the terms of the [Rust Code of -Conduct][CoC], and the maintainer of this crate, the [rp-rs team], promises -to intervene to uphold that code of conduct. - -[CoC]: CODE_OF_CONDUCT.md -[rp-rs team]: https://github.com/orgs/rp-rs/teams/rp-rs +## Hardware ## License @@ -284,8 +17,3 @@ information on each specific licence. Any submissions to this project (e.g. as Pull Requests) must be made available under these terms. - -## Contact - -Raise an issue: [https://github.com/rp-rs/rp2040-project-template/issues](https://github.com/rp-rs/rp2040-project-template/issues) -Chat to us on Matrix: [#rp-rs:matrix.org](https://matrix.to/#/#rp-rs:matrix.org) diff --git a/src/l298n/mod.rs b/src/l298n/mod.rs new file mode 100644 index 0000000..dc57be1 --- /dev/null +++ b/src/l298n/mod.rs @@ -0,0 +1 @@ +pub mod motor_controller; diff --git a/src/l298n/motor_controller.rs b/src/l298n/motor_controller.rs new file mode 100644 index 0000000..d30ed59 --- /dev/null +++ b/src/l298n/motor_controller.rs @@ -0,0 +1,125 @@ +use embedded_hal::digital::v2::OutputPin; +use embedded_hal::PwmPin; + +pub struct MotorController { + ina1: INA1, + ina2: INA2, + inb1: INB1, + inb2: INB2, + ena: ENA, + enb: ENB, +} + +#[allow(dead_code)] +impl MotorController +where + INA1: OutputPin, + INA2: OutputPin, + INB1: OutputPin, + INB2: OutputPin, + ENA: PwmPin, + ENB: PwmPin, +{ + pub fn new(ina1: INA1, ina2: INA2, inb1: INB1, inb2: INB2, ena: ENA, enb: ENB) -> Self + where + INA1: OutputPin, + INA2: OutputPin, + INB1: OutputPin, + INB2: OutputPin, + ENA: PwmPin, + ENB: PwmPin, + { + Self { + ina1, + ina2, + inb1, + inb2, + ena, + enb, + } + } + + pub fn set_duty(&mut self, duty_a: ENA::Duty, duty_b: ENB::Duty) { + self.ena.set_duty(duty_a); + self.enb.set_duty(duty_b); + } + + pub fn set_duty_a(&mut self, duty: ENA::Duty) { + self.ena.set_duty(duty); + } + + pub fn set_duty_b(&mut self, duty: ENB::Duty) { + self.enb.set_duty(duty); + } + + pub fn get_duty_a(&self) -> ENA::Duty { + self.ena.get_duty() + } + + pub fn get_duty_b(&self) -> ENB::Duty { + self.enb.get_duty() + } + + pub fn forward(&mut self) { + self.ina1.set_high().ok(); + self.ina2.set_low().ok(); + self.inb1.set_high().ok(); + self.inb2.set_low().ok(); + self.ena.enable(); + self.enb.enable(); + } + + pub fn forward_a(&mut self) { + self.ina1.set_high().ok(); + self.ina2.set_low().ok(); + self.ena.enable(); + } + + pub fn forward_b(&mut self) { + self.inb1.set_high().ok(); + self.inb2.set_low().ok(); + self.enb.enable(); + } + + pub fn reverse(&mut self) { + self.ina1.set_low().ok(); + self.ina2.set_high().ok(); + self.inb1.set_low().ok(); + self.inb2.set_high().ok(); + self.ena.enable(); + self.enb.enable(); + } + + pub fn reverse_a(&mut self) { + self.ina1.set_low().ok(); + self.ina2.set_high().ok(); + self.ena.enable(); + } + + pub fn reverse_b(&mut self) { + self.inb1.set_low().ok(); + self.inb2.set_high().ok(); + self.enb.enable(); + } + + pub fn stop(&mut self) { + self.ena.disable(); + self.enb.disable(); + self.ina1.set_low().ok(); + self.ina2.set_low().ok(); + self.inb1.set_low().ok(); + self.inb2.set_low().ok(); + } + + pub fn stop_a(&mut self) { + self.ena.disable(); + self.ina1.set_low().ok(); + self.ina2.set_low().ok(); + } + + pub fn stop_b(&mut self) { + self.enb.disable(); + self.inb1.set_low().ok(); + self.inb2.set_low().ok(); + } +} diff --git a/src/main.rs b/src/main.rs index 71b0223..21ad126 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ //! This will blink an LED attached to GP25, which is the pin the Pico uses for the on-board LED. #![no_std] #![no_main] +mod l298n; use bsp::entry; use defmt::*; @@ -18,10 +19,13 @@ use rp_pico as bsp; use bsp::hal::{ clocks::{init_clocks_and_plls, Clock}, pac, + pwm::Slices, sio::Sio, watchdog::Watchdog, }; +use l298n::motor_controller::MotorController; + #[entry] fn main() -> ! { info!("Program start"); @@ -53,6 +57,32 @@ fn main() -> ! { &mut pac.RESETS, ); + // Init PWMs + let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS); + // Configure PWM + let mut pwm3 = pwm_slices.pwm3; + + pwm3.set_ph_correct(); + pwm3.enable(); + + let mut channel_a = pwm3.channel_a; + let mut channel_b = pwm3.channel_b; + + channel_a.output_to(pins.gpio6); + channel_b.output_to(pins.gpio7); + + // set up motor controller + let mut motor_controller = MotorController::new( + pins.gpio8.into_push_pull_output(), + pins.gpio9.into_push_pull_output(), + pins.gpio10.into_push_pull_output(), + pins.gpio11.into_push_pull_output(), + channel_a, + channel_b, + ); + motor_controller.set_duty(20, 20); + info!("motor_controller created"); + // This is the correct pin on the Raspberry Pico board. On other boards, even if they have an // on-board LED, it might need to be changed. // Notably, on the Pico W, the LED is not connected to any of the RP2040 GPIOs but to the cyw43 module instead. If you have @@ -62,11 +92,13 @@ fn main() -> ! { loop { info!("on!"); + motor_controller.forward(); led_pin.set_high().unwrap(); - delay.delay_ms(500); + delay.delay_ms(1000); info!("off!"); + motor_controller.stop(); led_pin.set_low().unwrap(); - delay.delay_ms(500); + delay.delay_ms(1000); } }