Initial commit
This commit is contained in:
commit
507bd0dfef
|
@ -0,0 +1,19 @@
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output)
|
||||||
|
runner = "probe-run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "linker=flip-link",
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
|
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi" # Cortex-M3
|
||||||
|
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
rb = "run --bin"
|
||||||
|
rrb = "run --release --bin"
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,80 @@
|
||||||
|
[package]
|
||||||
|
authors = ["LongHairedHacker <sebastian@sebastians-site.de>"]
|
||||||
|
name = "wspr-beacon"
|
||||||
|
edition = "2018"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.6"
|
||||||
|
cortex-m-rt = "0.7.2"
|
||||||
|
cortex-m-rtic = "1.1.3"
|
||||||
|
defmt = "0.3.2"
|
||||||
|
defmt-rtt = "0.3.2"
|
||||||
|
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||||
|
stm32f1xx-hal = { version = "0.9.0", features = ["stm32f103", "rt"] }
|
||||||
|
embedded-hal = {version = "0.2.3"}
|
||||||
|
nb = "1.0.0"
|
||||||
|
arrayvec = {version = "0.7.0", default-features = false}
|
||||||
|
systick-monotonic = "1.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# set logging levels here
|
||||||
|
default = [
|
||||||
|
"defmt-default",
|
||||||
|
# "dependency-a/defmt-trace",
|
||||||
|
]
|
||||||
|
|
||||||
|
# do NOT modify these features
|
||||||
|
defmt-default = []
|
||||||
|
defmt-trace = []
|
||||||
|
defmt-debug = []
|
||||||
|
defmt-info = []
|
||||||
|
defmt-warn = []
|
||||||
|
defmt-error = []
|
||||||
|
|
||||||
|
# cargo build/run
|
||||||
|
[profile.dev]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo test
|
||||||
|
[profile.test]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
# cargo test --release
|
||||||
|
[profile.bench]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
# uncomment this to switch from the crates.io version of defmt to its git version
|
||||||
|
# check app-template's README for instructions
|
||||||
|
# [patch.crates-io]
|
||||||
|
# defmt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" }
|
||||||
|
# defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" }
|
||||||
|
# defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" }
|
||||||
|
# panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" }
|
|
@ -0,0 +1,6 @@
|
||||||
|
/* Fake bluepill using STM32F103C8T6 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
use defmt_rtt as _; // global logger
|
||||||
|
|
||||||
|
use panic_probe as _;
|
||||||
|
use stm32f1xx_hal as _;
|
||||||
|
|
||||||
|
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||||
|
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||||
|
#[defmt::panic_handler]
|
||||||
|
fn panic() -> ! {
|
||||||
|
cortex_m::asm::udf()
|
||||||
|
}
|
||||||
|
|
||||||
|
use rtic::app;
|
||||||
|
|
||||||
|
mod si5153;
|
||||||
|
|
||||||
|
#[app(device = stm32f1xx_hal::pac, peripherals = true, dispatchers = [TIM2])]
|
||||||
|
mod app {
|
||||||
|
|
||||||
|
use core::num::dec2flt::float;
|
||||||
|
|
||||||
|
use stm32f1xx_hal::{
|
||||||
|
adc,
|
||||||
|
gpio::{
|
||||||
|
self, gpioa, gpiob, gpioc, Alternate, Analog, Floating, Input, OpenDrain, Output,
|
||||||
|
PushPull, CRL,
|
||||||
|
},
|
||||||
|
i2c,
|
||||||
|
i2c::BlockingI2c,
|
||||||
|
pac::{ADC1, I2C1},
|
||||||
|
prelude::*,
|
||||||
|
serial::{self, Config, Serial},
|
||||||
|
stm32,
|
||||||
|
timer::{self, Event},
|
||||||
|
};
|
||||||
|
|
||||||
|
use systick_monotonic::Systick;
|
||||||
|
|
||||||
|
use arrayvec::ArrayString;
|
||||||
|
|
||||||
|
use crate::si5153;
|
||||||
|
|
||||||
|
type AppI2C1 = BlockingI2c<
|
||||||
|
I2C1,
|
||||||
|
(
|
||||||
|
gpiob::PB6<Alternate<OpenDrain>>,
|
||||||
|
gpiob::PB7<Alternate<OpenDrain>>,
|
||||||
|
),
|
||||||
|
>;
|
||||||
|
|
||||||
|
#[monotonic(binds = SysTick, default = true)]
|
||||||
|
type MonoTimer = Systick<1_000_000>;
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
pll: si5153::Si5153<AppI2C1>,
|
||||||
|
i2c: AppI2C1,
|
||||||
|
board_led: gpioc::PC13<Output<PushPull>>,
|
||||||
|
adc1: adc::Adc<ADC1>,
|
||||||
|
mic_in: gpio::Pin<Analog, CRL, 'A', 4>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
|
||||||
|
let mut flash = cx.device.FLASH.constrain();
|
||||||
|
let rcc = cx.device.RCC.constrain();
|
||||||
|
|
||||||
|
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
|
||||||
|
// `clocks`
|
||||||
|
let clocks = rcc
|
||||||
|
.cfgr
|
||||||
|
.use_hse(8.MHz())
|
||||||
|
.sysclk(72.MHz())
|
||||||
|
.pclk1(36.MHz())
|
||||||
|
.freeze(&mut flash.acr);
|
||||||
|
|
||||||
|
defmt::info!("Clock Setup done");
|
||||||
|
|
||||||
|
let mono = Systick::new(cx.core.SYST, clocks.sysclk().to_Hz());
|
||||||
|
|
||||||
|
let mut afio = cx.device.AFIO.constrain();
|
||||||
|
|
||||||
|
// Acquire the GPIOC peripheral
|
||||||
|
let mut gpioa = cx.device.GPIOA.split();
|
||||||
|
let mut gpiob = cx.device.GPIOB.split();
|
||||||
|
let mut gpioc = cx.device.GPIOC.split();
|
||||||
|
|
||||||
|
let board_led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
|
||||||
|
|
||||||
|
let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl);
|
||||||
|
let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl);
|
||||||
|
let mut i2c = i2c::BlockingI2c::i2c1(
|
||||||
|
cx.device.I2C1,
|
||||||
|
(scl, sda),
|
||||||
|
&mut afio.mapr,
|
||||||
|
i2c::Mode::Standard {
|
||||||
|
frequency: 400.kHz(),
|
||||||
|
},
|
||||||
|
clocks,
|
||||||
|
5,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut pll = si5153::Si5153::new(&i2c);
|
||||||
|
pll.init(&mut i2c, 25000000, 800000000, 800000000);
|
||||||
|
pll.set_ms_source(&mut i2c, si5153::Multisynth::MS0, si5153::PLL::A);
|
||||||
|
|
||||||
|
let adc1 = adc::Adc::adc1(cx.device.ADC1, clocks);
|
||||||
|
let mic_in = gpioa.pa4.into_analog(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut pwm =
|
||||||
|
cx.device
|
||||||
|
.TIM2
|
||||||
|
.pwm_hz::<Tim3NoRemap, _, _>(pins, &mut afio.mapr, 4800.Hz(), &clocks);
|
||||||
|
|
||||||
|
transmit::spawn().unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
Shared {},
|
||||||
|
Local {
|
||||||
|
i2c,
|
||||||
|
pll,
|
||||||
|
board_led,
|
||||||
|
adc1,
|
||||||
|
mic_in,
|
||||||
|
},
|
||||||
|
init::Monotonics(mono),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(local=[pll, i2c, adc1, mic_in])]
|
||||||
|
fn transmit(mut ctx: transmit::Context) {
|
||||||
|
let mut adc = ctx.local.adc1;
|
||||||
|
let mut mic_in = ctx.local.mic_in;
|
||||||
|
|
||||||
|
let data: u16 = adc.read(&mut *mic_in).unwrap();
|
||||||
|
let sample = (data as f32 / u16::MAX as f32);
|
||||||
|
|
||||||
|
transmit::spawn_after(208.micros().into()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use embedded_hal::blocking::i2c;
|
||||||
|
|
||||||
|
const I2C_ADDR: u8 = 96;
|
||||||
|
|
||||||
|
const CLK_ENABLE_CONTROL: u8 = 3;
|
||||||
|
//const PLLX_SRC: u8 = 15;
|
||||||
|
const PLL_RESET: u8 = 177;
|
||||||
|
const XTAL_LOAD_CAP: u8 = 183;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
pub enum PLL {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
const PLL_BASE_ADDR: [u8; 2] = [26, 34];
|
||||||
|
|
||||||
|
impl PLL {
|
||||||
|
fn base_address(&self) -> u8 {
|
||||||
|
return PLL_BASE_ADDR[*self as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Multisynth {
|
||||||
|
MS0,
|
||||||
|
MS1,
|
||||||
|
MS2,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MS_BASE_ADDR: [u8; 3] = [42, 50, 58];
|
||||||
|
const MS_CTRL_ADDR: [u8; 3] = [16, 17, 18];
|
||||||
|
|
||||||
|
impl Multisynth {
|
||||||
|
fn base_address(&self) -> u8 {
|
||||||
|
return MS_BASE_ADDR[*self as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ctrl_address(&self) -> u8 {
|
||||||
|
return MS_CTRL_ADDR[*self as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PllParams {
|
||||||
|
pub p1: u32,
|
||||||
|
pub p2: u32,
|
||||||
|
pub p3: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Si5153<I2C> {
|
||||||
|
// Marker that makes sure we always get the same I2C
|
||||||
|
i2c: PhantomData<I2C>,
|
||||||
|
pll_freqs: [u32; 2],
|
||||||
|
outputs: u8,
|
||||||
|
ms_srcs: [PLL; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2C, E> Si5153<I2C>
|
||||||
|
where
|
||||||
|
I2C: i2c::Write<Error = E>,
|
||||||
|
{
|
||||||
|
pub fn new(_i2c: &I2C) -> Self {
|
||||||
|
Si5153 {
|
||||||
|
i2c: PhantomData,
|
||||||
|
pll_freqs: [0, 0],
|
||||||
|
outputs: 0,
|
||||||
|
ms_srcs: [PLL::A, PLL::A, PLL::A],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self, i2c: &mut I2C, freq_xtal: u32, freq_a: u32, freq_b: u32) {
|
||||||
|
self.pll_freqs[PLL::A as usize] = freq_a;
|
||||||
|
self.pll_freqs[PLL::B as usize] = freq_b;
|
||||||
|
|
||||||
|
self.outputs = 0xFF;
|
||||||
|
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs); // Disable all outputs
|
||||||
|
self.write_byte_reg(i2c, XTAL_LOAD_CAP, 0xD2); //crystal load capacitor = 10pF
|
||||||
|
|
||||||
|
self.write_byte_reg(i2c, PLL_RESET, 0xA0); // Reset both PLLs
|
||||||
|
|
||||||
|
for ms in [Multisynth::MS0, Multisynth::MS1, Multisynth::MS2].iter() {
|
||||||
|
self.ms_srcs[*ms as usize] = PLL::A;
|
||||||
|
self.write_byte_reg(i2c, ms.ctrl_address(), 0x0F); // MSi as Source, PLLA to MSi, 8 mA output
|
||||||
|
}
|
||||||
|
|
||||||
|
for pll in [PLL::A, PLL::B].iter() {
|
||||||
|
let fdiv = self.pll_freqs[*pll as usize] / freq_xtal;
|
||||||
|
let rm = self.pll_freqs[*pll as usize] % freq_xtal;
|
||||||
|
|
||||||
|
//TODO: Find better way to determine c and b
|
||||||
|
let c = 0x0FFFFF;
|
||||||
|
let a = fdiv;
|
||||||
|
let b = ((rm as u64) * (c as u64) / (freq_xtal as u64)) as u32;
|
||||||
|
|
||||||
|
let params = PllParams {
|
||||||
|
p1: 128 * a + (128 * b / c) - 512,
|
||||||
|
p2: 128 * b - c * (128 * b / c),
|
||||||
|
p3: c,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.write_params(i2c, pll.base_address(), ¶ms)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_ms_output(&mut self, i2c: &mut I2C, synth: Multisynth) {
|
||||||
|
self.outputs &= !(1 << (synth as u8));
|
||||||
|
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_ms_output(&mut self, i2c: &mut I2C, synth: Multisynth) {
|
||||||
|
self.outputs |= 1 << (synth as u8);
|
||||||
|
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ms_source(&mut self, i2c: &mut I2C, synth: Multisynth, pll: PLL) {
|
||||||
|
let value: u8 = if pll == PLL::A {
|
||||||
|
self.ms_srcs[synth as usize] = PLL::A;
|
||||||
|
0x0F // MS as Source, PLLA to MS, 8 mA output
|
||||||
|
} else {
|
||||||
|
self.ms_srcs[synth as usize] = PLL::B;
|
||||||
|
0x2F // MS as Source, PLLB to MS, 8 mA output
|
||||||
|
};
|
||||||
|
|
||||||
|
self.write_byte_reg(i2c, synth.ctrl_address(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ms_freq(&mut self, i2c: &mut I2C, synth: Multisynth, freq: u32) {
|
||||||
|
let pll = self.ms_srcs[synth as usize];
|
||||||
|
let fdiv = self.pll_freqs[pll as usize] / freq;
|
||||||
|
let rm = self.pll_freqs[pll as usize] % freq;
|
||||||
|
|
||||||
|
//TODO: Find better way to determine c and b
|
||||||
|
let c: u32 = 0x0FFFFF;
|
||||||
|
let a: u32 = fdiv;
|
||||||
|
let b: u32 = ((rm as u64) * (c as u64) / (freq as u64)) as u32;
|
||||||
|
|
||||||
|
let params = PllParams {
|
||||||
|
p1: 128 * a + (128 * b / c) - 512,
|
||||||
|
p2: 128 * b - c * (128 * b / c),
|
||||||
|
p3: c,
|
||||||
|
};
|
||||||
|
self.write_params(i2c, synth.base_address(), ¶ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_byte_reg(&self, i2c: &mut I2C, reg_addr: u8, data: u8) {
|
||||||
|
let res = i2c.write(I2C_ADDR, &[reg_addr, data]);
|
||||||
|
if res.is_err() {
|
||||||
|
panic!("i2c write failed. regAdder: {}", reg_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_params(&self, i2c: &mut I2C, base: u8, params: &PllParams) {
|
||||||
|
let data: [u8; 9] = [
|
||||||
|
base,
|
||||||
|
((params.p3 & 0x00FF00) >> 8) as u8,
|
||||||
|
(params.p3 & 0x0000FF) as u8,
|
||||||
|
((params.p1 & 0x030000) >> 16) as u8,
|
||||||
|
((params.p1 & 0x00FF00) >> 8) as u8,
|
||||||
|
(params.p1 & 0x0000FF) as u8,
|
||||||
|
(((params.p3 & 0x0F0000) >> 12) | ((params.p2 & 0x0F0000) >> 16)) as u8,
|
||||||
|
((params.p2 & 0x00FF00) >> 8) as u8,
|
||||||
|
(params.p2 & 0x0000FF) as u8,
|
||||||
|
];
|
||||||
|
|
||||||
|
let res = i2c.write(I2C_ADDR, &data);
|
||||||
|
if res.is_err() {
|
||||||
|
panic!("i2c write failed. regAdder: {}", base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_synth_params(&self, i2c: &mut I2C, synth: Multisynth, params: &PllParams) {
|
||||||
|
self.write_params(i2c, synth.base_address(), params);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue