first commit

This commit is contained in:
wieerwill 2024-11-12 08:46:50 +01:00
commit 57a257d47b
17 changed files with 545 additions and 0 deletions

21
.cargo/config.toml Normal file
View File

@ -0,0 +1,21 @@
[target.thumbv6m-none-eabi]
runner = 'probe-rs run --chip STM32L010RBTx'
rustflags = [
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
"-C", "link-arg=--no-rosegment"
]
[build]
target = "thumbv6m-none-eabi"
[env]
DEFMT_LOG = "trace"
[alias]
main = "embed --release --bin main"
rb = "embed --release --bin"
s = "size --bin main -- -B -x"
sr = "size --release --bin main -- -B -x"

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

72
Cargo.toml Normal file
View File

@ -0,0 +1,72 @@
[package]
name = "stm32_app"
version = "0.1.0"
edition = "2021"
authors = ["wieerwill <jeutter@wieerwill.de>"]
resolver = "2"
[dependencies]
embassy-stm32 = { version = "0.1.0", features = [
"defmt",
"memory-x",
"stm32l010rb",
"time-driver-any",
"exti",
"unstable-pac",
"exti",
"time",
] }
cortex-m = { version = "0.7.6", features = [
"inline-asm",
"critical-section-single-core",
] }
cortex-m-rt = "0.7.0"
defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }
embassy-sync = { version = "0.6.0", features = [
"defmt",
] }
embassy-executor = { version = "0.6.0", features = [
"arch-cortex-m",
"executor-thread",
"executor-interrupt",
"defmt",
"integrated-timers",
] }
embassy-time = { version = "0.3.1", features = [
"defmt",
"defmt-timestamp-uptime",
"tick-hz-32_768",
] }
embassy-futures = { version = "0.1.1", features = [
"defmt",
] }
static_cell = "2"
portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }
heapless = "0.8.0"
# cargo build/run
[profile.dev]
codegen-units = 1
debug = 2
debug-assertions = true
incremental = false
opt-level = 0
overflow-checks = true
# cargo build/run --release
[profile.release]
codegen-units = 1 # reduce Code Generation Units
debug = true # symbols are nice and they don't increase the size on Flash
debug-assertions = false
incremental = false
lto = true # Link Time Optimization
opt-level = "z" # Optimize for size "s" or "z"
overflow-checks = true
# strip = "symbols" # or true, removes all output from rtt
# panic = "abort" # abort immediately rather than unwind
# location-detail="none" # Remove Location Details
[target.stm32l010rb]
runner = 'probe-rs run --chip stm32l010rbtx'

103
Embed.toml Normal file
View File

@ -0,0 +1,103 @@
[default.probe]
# USB vendor ID
# usb_vid = "1337"
# USB product ID
# usb_pid = "1337"
# Serial number
# serial = "12345678"
# The protocol to be used for communicating with the target.
protocol = "Swd"
# The speed in kHz of the data link to the target.
# speed = 1337
[default.flashing]
# Whether or not the target should be flashed.
enabled = true
# Whether or not bytes erased but not rewritten with data from the ELF
# should be restored with their contents before erasing.
restore_unwritten_bytes = false
# The path where an SVG of the assembled flash layout should be written to.
# flash_layout_output_path = "out.svg"
# Triggers a full chip erase instead of a page by page erase.
do_chip_erase = false
# Whether to disable double buffering
disable_double_buffering = false
# Whether to verify flash contents after downloading
verify = true
[default.reset]
# Whether or not the target should be reset.
# When flashing is enabled as well, the target will be reset after flashing regardless of this setting.
enabled = true
# Whether or not the target should be halted after reset.
halt_afterwards = false
[default.general]
# The chip name of the chip to be debugged.
chip = "STM32L010RBTx"
# A list of chip descriptions to be loaded during runtime.
chip_descriptions = []
# The default log level to be used. Possible values are one of:
# "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"
# If not set, the `RUST_LOG` environment variable will be used.
log_level = "WARN"
# Use this flag to assert the nreset & ntrst pins during attaching the probe to the chip.
connect_under_reset = false
[default.rtt]
# Whether or not an RTTUI should be opened after flashing.
enabled = true
# The duration in ms for which the logger should retry to attach to RTT.
timeout = 10000
# Whether to save rtt history buffer on exit.
log_enabled = true
# Where to save rtt history buffer relative to manifest path.
log_path = "./target/logs"
# A list of up (target -> host) channel settings associations to be displayed. If left empty, all channels are displayed.
# object key - RTT channel identifier number
# mode (Optional) - RTT operation mode. Describes how the target handles RTT outputs that won't
# fit in the buffer. If left unset, the firmware will determine the default
# for each RTT up channel.
# * NoBlockSkip - Skip writing the data completely if it doesn't fit in its
# entirety.
# * NoBlockTrim - Write as much as possible of the data and ignore the rest.
# * BlockIfFull - Spin until the host reads data. Can result in app freezing.
# format (Optional) - How to interpret data from target firmware. One of:
# * String - Directly show output from the target (default)
# * Defmt - Format output on the host, see https://defmt.ferrous-systems.com/
# * BinaryLE - Display as raw hex
# show_location (Optional) - Whether to show the location of defmt messages in the UI.
# show_timestamps (Optional) - Whether to show the timestamps of String and Defmt messages in the UI, if available.
# socket (Optional) - Server socket address (for optional external frontend or endpoint).
# log_format (Optional) - Control the output format for `format = Defmt`.
up_channels = [
# { channel = 0, mode = "BlockIfFull", format = "Defmt", show_location = true },
# { channel = 1, mode = "BlockIfFull", format = "String", show_timestamps = false, socket = "127.0.0.1:12345" },
]
# A list of down (host -> target) channel settings. You can select a down channel for each UI tab,
# which will be used to send data to the target.
# object key - RTT channel identifier number
# mode (Optional) - RTT operation mode. Describes how the target handles RTT outputs that won't
# fit in the buffer. If left unset, the firmware will determine the default
# for each RTT down channel.
down_channels = [
# { channel = 0, mode = "BlockIfFull" }
]
# UI tab settings. All up channels are displayed, except when hidden here. You can specify how each
# tab is displayed and whether they allow sending data to the target.
# up_channel - The channel_number of the RTT up channel to display
# hide (Optional) - Whether to hide the tab. Defaults to false.
# down_channel (Optional) - The channel_number of the RTT down channel to use for this tab.
# name (Optional) - String to be displayed in the RTTUI tab. Defaults to the channel name.
tabs = [
# { up_channel = 0, down_channel = 0, name = "My channel" },
# { up_channel = 1, hide = true },
]
[default.gdb]
# Whether or not a GDB server should be opened after flashing.
enabled = false
# The connection string in host:port format wher the GDB server will open a socket.
gdb_connection_string = "127.0.0.1:1337"

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# STM32 Firmware with Rust and Embassy
Used Chip in this Project was a STM32L0, the low power edition. Other targets are easily exchangeable.
# Dev Tools
- rustup 1.27.1
- rustc 1.81.0
- Cargo 1.81.0
- Probe-RS
# Binaries
All binaries are compiled and then transfered to the connected board (if exist). Then the RTT Console will be opened to read and debug.
The ready binary is a .elf file in the `target` folder.
```sh
cargo rb blinky # blinky example
cargo main # main program
./elf-to-hex.sh # convert main program to Hex and versionize
```

15
elf-to-hex.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
objcopy -O ihex target/thumbv6m-none-eabi/release/main target/main.hex -S
# read version number
version=$(awk -F ' = ' '$1 ~ /version/ { gsub(/[\"]/, "", $2); printf("%s",$2) }' Cargo.toml)
# check if version was found
if [[ -z "$version" ]]; then
echo "Error: Version not found in Cargo.toml"
exit 1
fi
echo "Firmware Version $version"
# write version into filename
mv target/main.hex target/FW-$version-main.hex
echo "target/FW-$version-main.hex"

6
rust-toolchain.toml Normal file
View File

@ -0,0 +1,6 @@
# Before upgrading check that everything is available on all tier1 targets here:
# https://rust-lang.github.io/rustup-components-history
[toolchain]
channel = "stable"
components = [ "rustfmt" ]
targets = [ "thumbv6m-none-eabi" ]

37
src/bin/adc.rs Normal file
View File

@ -0,0 +1,37 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::adc::{Adc, SampleTime};
use embassy_stm32::peripherals::ADC1;
use embassy_stm32::{adc, bind_interrupts};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct AdcIrqs {
ADC1 => adc::InterruptHandler<ADC1>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut adc = Adc::new(p.ADC1, AdcIrqs);
adc.set_sample_time(SampleTime::CYCLES79_5);
let mut pin = p.PA1;
let mut vrefint = adc.enable_vref();
let vrefint_sample = adc.read(&mut vrefint).await;
let convert_to_millivolts = |sample| {
// Embedded reference voltage
const VREFINT_MV: u32 = 1224; // mV
(u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16
};
loop {
let v = adc.read(&mut pin).await;
info!("--> {} - {} mV", v, convert_to_millivolts(v));
Timer::after_millis(100).await;
}
}

25
src/bin/blinky.rs Normal file
View File

@ -0,0 +1,25 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut led = Output::new(p.PA9, Level::High, Speed::Low);
loop {
info!("high");
led.set_high();
Timer::after_millis(300).await;
info!("low");
led.set_low();
Timer::after_millis(300).await;
}
}

25
src/bin/button.rs Normal file
View File

@ -0,0 +1,25 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::exti::ExtiInput;
use embassy_stm32::gpio::Pull;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
// Initialize and create handle for devicer peripherals
let p = embassy_stm32::init(Default::default());
// Configure the button pin and obtain handler.
let mut button = ExtiInput::new(p.PC2, p.EXTI2, Pull::Up);
info!("Press the button...");
loop {
button.wait_for_falling_edge().await;
info!("Pressed!");
button.wait_for_rising_edge().await;
info!("Released!");
}
}

16
src/bin/hello.rs Normal file
View File

@ -0,0 +1,16 @@
#![no_std]
#![no_main]
use defmt::info;
use embassy_executor::Spawner;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let _p = embassy_stm32::init(Default::default());
loop {
Timer::after_secs(1).await;
info!("Hello World");
}
}

20
src/bin/main.rs Normal file
View File

@ -0,0 +1,20 @@
#![no_std]
#![no_main]
use defmt::{info, debug};
use embassy_executor::Spawner;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let _p = embassy_stm32::init(Default::default());
info!("Hello World");
debug!("Start your application here");
loop {
Timer::after_secs(1).await;
info!(".");
}
}

46
src/bin/pwm.rs Normal file
View File

@ -0,0 +1,46 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::gpio::OutputType::PushPull;
use embassy_stm32::time::hz;
use embassy_stm32::timer::low_level::CountingMode::EdgeAlignedUp;
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
use embassy_stm32::timer::Channel;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let p = embassy_stm32::init(Default::default());
let buzz_pin = PwmPin::new_ch1(p.PB13, PushPull);
let mut pwm = SimplePwm::new(
p.TIM21,
Some(buzz_pin),
None,
None,
None,
hz(2000),
EdgeAlignedUp,
);
let max_duty = pwm.get_max_duty();
// Configure the Duty Cycle to 50%
pwm.set_duty(Channel::Ch2, max_duty / 2);
pwm.enable(Channel::Ch2);
let mut frequence = 300;
loop {
info!("on, freq {}hz", frequence);
pwm.set_frequency(hz(frequence));
pwm.enable(Channel::Ch2);
Timer::after_millis(1000).await;
info!("off");
pwm.disable(Channel::Ch2);
Timer::after_millis(500).await;
frequence += 20;
}
}

28
src/bin/spawner.rs Normal file
View File

@ -0,0 +1,28 @@
#![no_std]
#![no_main]
use defmt::info;
use embassy_executor::Spawner;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::task]
async fn run() {
loop {
info!("Tick");
Timer::after_secs(1).await;
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let _p = embassy_stm32::init(Default::default());
spawner.spawn(run()).unwrap();
Timer::after_secs(1).await;
spawner.spawn(run()).unwrap();
loop {
Timer::after_secs(2).await;
info!("Tack");
}
}

31
src/bin/spi.rs Normal file
View File

@ -0,0 +1,31 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::spi::{Config, Spi};
use embassy_stm32::time::Hertz;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut spi_config = Config::default();
spi_config.frequency = Hertz(1_000_000);
let mut spi = Spi::new_blocking(p.SPI1, p.PB3, p.PA7, p.PA6, spi_config);
let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh);
loop {
let mut buf = [0x0Au8; 4];
cs.set_low();
unwrap!(spi.blocking_transfer_in_place(&mut buf));
cs.set_high();
info!("xfer {=[u8]:x}", buf);
Timer::after_millis(300).await;
}
}

42
src/bin/uart.rs Normal file
View File

@ -0,0 +1,42 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::usart::{Config, InterruptHandler, Uart};
use embassy_stm32::{bind_interrupts, peripherals};
use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs{
LPUART1 => InterruptHandler<peripherals::LPUART1>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Starting system");
let mut config = Config::default();
config.baudrate = 9600;
let mut usart = Uart::new(
p.LPUART1, p.PC0, p.PC1, Irqs, p.DMA1_CH2, p.DMA1_CH3, config,
)
.unwrap();
unwrap!(usart.write(b"Hello Embassy World!\r\n").await);
let mut buf = [0u8; 300];
loop {
let result = usart.read_until_idle(&mut buf).await;
match result {
Ok(size) => {
info!("result {}, size {}", result, size);
}
Err(_err) => {
info!("error...");
}
}
}
}

25
src/bin/watchdog.rs Normal file
View File

@ -0,0 +1,25 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::wdg::IndependentWatchdog;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
// Initialize and create handle for devicer peripherals
let p = embassy_stm32::init(Default::default());
// Configure the independent watchdog timer
let mut wdg = IndependentWatchdog::new(p.IWDG, 2_000_000);
info!("Watchdog start");
wdg.unleash();
loop {
Timer::after_secs(1).await;
wdg.pet();
}
}