Update stuff
This commit is contained in:
+82
@@ -0,0 +1,82 @@
|
||||
[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 the target should be halted after reset.
|
||||
# DEPRECATED, moved to reset section
|
||||
#halt_afterwards = false
|
||||
# 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
|
||||
|
||||
[default.reset]
|
||||
# Whether or not the target should be reset.
|
||||
# When flashing is enabled as well, the target will be reset after flashing.
|
||||
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 = "name"
|
||||
# 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"
|
||||
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
|
||||
# How the target handles RTT outputs that won't fit in the buffer. This can be
|
||||
# overridden per-channel. 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.
|
||||
#
|
||||
# up_mode = "BlockIfFull"
|
||||
|
||||
# A list of channel associations to be displayed. If left empty, all channels are displayed.
|
||||
# up, down (Optional) - RTT channel numbers
|
||||
# name (Optional) - String to be displayed in the RTTUI tab
|
||||
# up_mode (Optional) - RTT channel specific as described above
|
||||
# format (Required) - How to interpret data from target firmware. One of:
|
||||
# String - Directly show output from the target
|
||||
# Defmt - Format output on the host, see https://defmt.ferrous-systems.com/
|
||||
# BinaryLE - Display as raw hex
|
||||
#channels = [
|
||||
# # { up = 0, down = 0, name = "name", up_mode = "BlockIfFull", format = "Defmt" },
|
||||
#]
|
||||
# The duration in ms for which the logger should retry to attach to RTT.
|
||||
timeout = 3000
|
||||
# Whether timestamps in the RTTUI are enabled
|
||||
#show_timestamps = true
|
||||
# Whether to save rtt history buffer on exit.
|
||||
log_enabled = false
|
||||
# Where to save rtt history buffer relative to manifest path.
|
||||
log_path = "./logs"
|
||||
|
||||
[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"
|
||||
@@ -1,6 +1,9 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
pub mod ringbuffer;
|
||||
pub mod uart_parser;
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use defmt_rtt as _; // global logger
|
||||
|
||||
|
||||
+106
-24
@@ -1,13 +1,15 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use pid_control as _; // global logger + panicking-behavior + memory layout
|
||||
|
||||
// TODO
|
||||
// - protect from i windup -> done?
|
||||
// - uart configurable reference
|
||||
// - impl timer
|
||||
// - embedded-cli
|
||||
// - average adc samples -> done?
|
||||
// - uart configurable reference -> done
|
||||
// - fix analog reference -> done
|
||||
// - impl timer -> done
|
||||
// - protect from i windup -> done
|
||||
#[rtic::app(
|
||||
device = stm32h7xx_hal::pac,
|
||||
dispatchers = [EXTI0]
|
||||
@@ -15,14 +17,18 @@ use pid_control as _; // global logger + panicking-behavior + memory layout
|
||||
mod app {
|
||||
use defmt::info;
|
||||
|
||||
use embedded_cli::Command;
|
||||
use embedded_cli::{Command, cli::CliBuilder, writer::Writer};
|
||||
use pid_control::ringbuffer::Ringbuffer;
|
||||
use pid_control::uart_parser::Parser;
|
||||
|
||||
use stm32h7xx_hal::{
|
||||
self as hal,
|
||||
adc::{self, Adc},
|
||||
dac::{self, C1},
|
||||
gpio::{Analog, Pin, PinExt},
|
||||
pac,
|
||||
prelude::*,
|
||||
rcc::rec::AdcClkSel,
|
||||
serial::{self, Serial},
|
||||
stm32::{ADC1, ADC2, DAC, USART1},
|
||||
timer::{Event, Timer},
|
||||
@@ -33,10 +39,14 @@ mod app {
|
||||
const KI: f32 = 0.5;
|
||||
const KD: f32 = 0f32;
|
||||
|
||||
const AVGBUFSIZE: usize = 5000;
|
||||
|
||||
// Shared resources go here
|
||||
#[shared]
|
||||
struct Shared {
|
||||
target: f32,
|
||||
serial: Serial<USART1>,
|
||||
config: PIDConfig,
|
||||
}
|
||||
|
||||
// Local resources go here
|
||||
@@ -47,12 +57,14 @@ mod app {
|
||||
p: f32,
|
||||
i: f32,
|
||||
d: f32,
|
||||
reference: Pin<'C', 2, Analog>,
|
||||
input: Pin<'C', 3, Analog>,
|
||||
adc1: Adc<ADC1, adc::Enabled>,
|
||||
adc2: Adc<ADC2, adc::Enabled>,
|
||||
dac: C1<DAC, dac::Enabled>,
|
||||
serial: Serial<USART1>,
|
||||
// freq: fugit::Kilohertz<u32>,
|
||||
parser: Parser,
|
||||
freq: hal::time::Hertz,
|
||||
avgbuffer: Ringbuffer<AVGBUFSIZE>,
|
||||
}
|
||||
|
||||
#[init]
|
||||
@@ -66,7 +78,12 @@ mod app {
|
||||
let rcc = ctx.device.RCC.constrain();
|
||||
|
||||
// Configure clocks
|
||||
let ccdr = rcc.sys_ck(400.MHz()).freeze(pwrcfg, &ctx.device.SYSCFG);
|
||||
let mut ccdr = rcc
|
||||
.sys_ck(400.MHz())
|
||||
.pll1_q_ck(100.MHz())
|
||||
.freeze(pwrcfg, &ctx.device.SYSCFG);
|
||||
|
||||
ccdr.peripheral.kernel_adc_clk_mux(AdcClkSel::Per);
|
||||
|
||||
let mut delay = ctx.core.SYST.delay(ccdr.clocks);
|
||||
|
||||
@@ -144,7 +161,7 @@ mod app {
|
||||
info!("TIM3:");
|
||||
info!("\tfreq: {} Hz", freq.to_Hz());
|
||||
|
||||
let mut timer3 = ctx
|
||||
let timer3 = ctx
|
||||
.device
|
||||
.TIM3
|
||||
.timer(freq, ccdr.peripheral.TIM3, &ccdr.clocks);
|
||||
@@ -161,30 +178,62 @@ mod app {
|
||||
info!("===========init complete==========");
|
||||
|
||||
// Enable interrupts
|
||||
timer3.listen(Event::TimeOut); // NOTE: comment for digital reference via uart
|
||||
// timer3.listen(Event::TimeOut); // NOTE: comment for digital reference via uart
|
||||
timer2.listen(Event::TimeOut);
|
||||
serial.listen(serial::Event::Rxne);
|
||||
|
||||
// // create static buffers for use in cli (so we're not using stack memory)
|
||||
// // History buffer is 1 byte longer so max command fits in it (it requires extra byte at end)
|
||||
// // SAFETY: buffers are passed to cli and are used by cli only
|
||||
// let (command_buffer, history_buffer) = unsafe {
|
||||
// static mut COMMAND_BUFFER: [u8; 40] = [0; 40];
|
||||
// static mut HISTORY_BUFFER: [u8; 41] = [0; 41];
|
||||
// #[allow(static_mut_refs)]
|
||||
// (COMMAND_BUFFER.as_mut(), HISTORY_BUFFER.as_mut())
|
||||
// };
|
||||
// let writer = Writer::new(&mut tx);
|
||||
// let mut cli = CliBuilder::default()
|
||||
// .writer(writer)
|
||||
// .command_buffer(command_buffer)
|
||||
// .history_buffer(history_buffer)
|
||||
// .build()
|
||||
// .ok()?;
|
||||
|
||||
let target = 0.7 / 3.3 * 4095f32;
|
||||
|
||||
let averaging = 50;
|
||||
let config = PIDConfig { averaging };
|
||||
let avgbuffer = Ringbuffer::new(averaging);
|
||||
|
||||
(
|
||||
Shared { target },
|
||||
Shared {
|
||||
target,
|
||||
serial,
|
||||
config,
|
||||
},
|
||||
Local {
|
||||
avgbuffer,
|
||||
timer3,
|
||||
timer2,
|
||||
p: 0f32,
|
||||
i: 0f32,
|
||||
d: 0f32,
|
||||
reference,
|
||||
input,
|
||||
adc1,
|
||||
adc2,
|
||||
dac,
|
||||
serial,
|
||||
// TODO: freq,
|
||||
parser: Parser::new(),
|
||||
freq,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PIDConfig {
|
||||
averaging: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Command)]
|
||||
enum TargetCommand {
|
||||
/// Set Voltage
|
||||
@@ -202,28 +251,53 @@ mod app {
|
||||
},
|
||||
}
|
||||
|
||||
#[task(binds = USART1, local = [serial], shared = [target])]
|
||||
fn uart_target(ctx: uart_target::Context) {
|
||||
// TODO: Do I need to clear the interrupt flag?
|
||||
todo!() // TODO: use more embedded-cli
|
||||
#[task(binds = USART1, local = [parser], shared = [serial, target], priority = 2)]
|
||||
fn uart_target(mut ctx: uart_target::Context) {
|
||||
let parser = ctx.local.parser;
|
||||
|
||||
ctx.shared.serial.lock(|serial| {
|
||||
use core::fmt::Write;
|
||||
while serial.is_rxne() {
|
||||
if let Ok(data) = serial.read() {
|
||||
// echo back
|
||||
let _ = write!(serial, "{}", data as char);
|
||||
|
||||
// parse incoming byte
|
||||
if let Some(target) = parser.input_byte(data) {
|
||||
ctx.shared.target.lock(|t| {
|
||||
*t = target as f32;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[task(binds = TIM3, local = [timer3, adc1], shared = [target])]
|
||||
fn adc_target(ctx: adc_target::Context) {
|
||||
#[task(binds = TIM3, local = [timer3, adc1, reference], shared = [target])]
|
||||
fn adc_target(mut ctx: adc_target::Context) {
|
||||
// Clear interrupt flag
|
||||
ctx.local.timer3.clear_irq();
|
||||
let target: u32 = ctx.local.adc1.read(ctx.local.reference).unwrap(); // WHY COMPILER WHY
|
||||
ctx.shared.target.lock(|t| {
|
||||
*t = target as f32;
|
||||
});
|
||||
}
|
||||
|
||||
#[task(binds = TIM2, local = [timer2, p, i, d, input, adc2, dac], shared = [target], priority = 1)]
|
||||
// #[task(binds = TIM2, local = [timer2, p, i, d, input, adc2, dac, freq], shared = [target, serial], priority = 1)]
|
||||
#[task(binds = TIM2, local = [timer2, p, i, d, input, adc2, dac, freq, avgbuffer], shared = [target, config], priority = 1)]
|
||||
fn pid(mut ctx: pid::Context) {
|
||||
// Clear interrupt flag
|
||||
ctx.local.timer2.clear_irq();
|
||||
|
||||
// TODO get sensible T
|
||||
let t = 1.0;
|
||||
// let config: PIDConfig = ctx.shared.config.lock(|c| *c);
|
||||
let avgbuffer = ctx.local.avgbuffer;
|
||||
|
||||
let input: u32 = ctx.local.adc2.read(ctx.local.input).unwrap(); // WHY COMPILER WHY
|
||||
let input: f32 = input as f32;
|
||||
let t = 1f32 / (ctx.local.freq.to_kHz() as f32);
|
||||
|
||||
let input: u32 = ctx.local.adc2.read(ctx.local.input).unwrap();
|
||||
avgbuffer.push(input);
|
||||
// let input: f32 = input as f32;
|
||||
let input: f32 = avgbuffer.average() as f32;
|
||||
|
||||
let p = ctx.local.p;
|
||||
let i = ctx.local.i;
|
||||
@@ -255,5 +329,13 @@ mod app {
|
||||
"target: {:05},\tinput: {:05},\toutput: {:05},\tp: {:05},\ti: {:05},\td: {:05}",
|
||||
target, input, output, p, i, d
|
||||
);
|
||||
// let _ = ctx.shared.serial.lock(|serial| {
|
||||
// use core::fmt::Write;
|
||||
// write!(
|
||||
// serial,
|
||||
// "target: {:05},\tinput: {:05},\toutput: {:05},\tp: {:05},\ti: {:05},\td: {:05}",
|
||||
// target, input, output, p, i, d
|
||||
// )
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
pub struct Ringbuffer<const N: usize> {
|
||||
buf: [u32; N],
|
||||
start: usize,
|
||||
end: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> Ringbuffer<N> {
|
||||
pub fn new(len: usize) -> Self {
|
||||
let buf = [0; N];
|
||||
let start = 0;
|
||||
let end = 0;
|
||||
|
||||
Self {
|
||||
buf,
|
||||
start,
|
||||
end,
|
||||
len,
|
||||
}
|
||||
}
|
||||
|
||||
fn curr_len(&self) -> usize {
|
||||
(self.end - self.start + N) % N
|
||||
}
|
||||
|
||||
pub fn curr_start(&self) -> usize {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn push(&mut self, val: u32) {
|
||||
if self.curr_len() == self.len {
|
||||
self.start += 1;
|
||||
self.start %= N;
|
||||
}
|
||||
self.end += 1;
|
||||
self.end %= N;
|
||||
self.buf[self.end] = val;
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<u32> {
|
||||
if self.end - self.start > 0 {
|
||||
let i = self.start;
|
||||
self.start += 1;
|
||||
self.start %= N;
|
||||
Some(self.buf[i])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn average(&self) -> u32 {
|
||||
let mut sum = 0;
|
||||
for i in 0..self.curr_len() {
|
||||
sum += self.buf[(self.start + i) % N];
|
||||
}
|
||||
sum / self.curr_len() as u32
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, len: usize) {
|
||||
if self.curr_len() < len {
|
||||
assert!(len <= N); // panic if length exceeds buffer length
|
||||
} else {
|
||||
self.start = (self.end - len + N) % N;
|
||||
}
|
||||
self.len = len;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use core::f32;
|
||||
use core::str::{self, FromStr};
|
||||
|
||||
pub struct Parser {
|
||||
pub curr: u32,
|
||||
pub buffer: [u8; 100],
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
curr: 0,
|
||||
buffer: [0_u8; 100],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_byte(&mut self, b: u8) -> Option<u32> {
|
||||
if b == b'\n' || b == b'\r' {
|
||||
let mut split = self.buffer.split(|num| {
|
||||
num == &(u8::try_from(',').unwrap())
|
||||
|| num == &(u8::try_from(' ').unwrap())
|
||||
|| num == &0
|
||||
}); // TODO: the use \0 and ' ' as split is kinda hacky
|
||||
let buf = split.next()?;
|
||||
|
||||
let ret = if buf.contains(&b'.') {
|
||||
Some((4095f32 / 3.3 * f32::from_str(str::from_utf8(buf).ok()?).ok()?) as u32)
|
||||
} else {
|
||||
Some(u32::from_str(str::from_utf8(buf).ok()?).ok()?)
|
||||
};
|
||||
|
||||
for i in 0..self.curr {
|
||||
self.buffer[i as usize] = 0;
|
||||
}
|
||||
|
||||
self.curr = 0;
|
||||
ret
|
||||
} else {
|
||||
self.buffer[self.curr as usize] = b;
|
||||
self.curr += 1;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user