Skip to content
Snippets Groups Projects
Commit 6a014fbc authored by Per Lindgren's avatar Per Lindgren
Browse files

sine generation works

parent 79e17d8f
Branches
No related tags found
No related merge requests found
......@@ -20,7 +20,7 @@ panic-halt = "0.2.0"
#panic-itm = "0.4.2"
# Uncomment for the rtt-timing example.
panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
# panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
# Uncomment for the panic example.
#panic-semihosting = "0.5.6"
......
......@@ -26,24 +26,33 @@
- [USB Cable]
| Signl | Color | Pin |
| ----- | ----- | ---- |
| V+ | Red | ---- |
| D- | White | PA11 |
| D+ | Green | PA12 |
| Gnd | Black | ---- |
| Signal | Color | Pin |
| ------ | ----- | ---- |
| V+ | Red | ---- |
| D- | White | PA11 |
| D+ | Green | PA12 |
| Gnd | Black | ---- |
D+ used for re-enumeration
---
## Debug interface
- Serial Wire debugging uses pins PA13 and PA14. So refrain from using those unless absolutely necessary.
---
## Examples
---
## Troubleshooting
### Fail to connect with openocd
First check that your stilnk nucleo programmer is found by the host.
First check that your stlink nucleo programmer is found by the host.
```shell
> lsusb
......
......@@ -8,12 +8,15 @@
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use core::f64::consts::PI;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::{
io::{Result, Write},
path::{Path, PathBuf},
};
fn main() {
fn main() -> Result<()> {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
......@@ -28,4 +31,24 @@ fn main() {
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
// generate a sine table
println!("cargo:rerun-if-changed=build.rs");
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("sin_abs_const.rs");
let mut f = File::create(&dest_path).unwrap();
const SINE_BUF_SIZE: usize = 256;
write!(f, "const SINE_BUF_SIZE: usize = {};\n", SINE_BUF_SIZE)?;
write!(f, "const SINE_BUF: [u8; SINE_BUF_SIZE] = [")?;
for i in 0..SINE_BUF_SIZE {
let s = ((i as f64) * 2.0 * PI / SINE_BUF_SIZE as f64).sin();
let v = (128.0 + 128.0 * s) as u8;
write!(f, " {},", v)?;
}
write!(f, "];\n")?;
Ok(())
}
......@@ -103,6 +103,60 @@ const APP: () = {
.set_bit()
});
// Setup TIMER2
let tim2 = dp.TIM2;
// Here we need unsafe as we are "stealing" the RCC peripheral
// At this point it has been contrained into SysConf and used to set clocks
let rcc = unsafe { &(*stm32::RCC::ptr()) };
rcc.apb1enr.modify(|_, w| w.tim2en().set_bit());
rcc.apb1rstr.modify(|_, w| w.tim2rst().set_bit());
rcc.apb1rstr.modify(|_, w| w.tim2rst().clear_bit());
// auto-reload, preload
tim1.cr1.modify(|_, w| w.arpe().set_bit());
let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
// check that its actually 48_000_000
rprintln!("clk {}", clk);
// assume we have a sine at
let pre = 0;
rprintln!("pre {}", pre);
tim2.psc.write(|w| w.psc().bits(pre));
// we want 8 bits of resolution
// so our ARR = 2^8 - 1 = 256 - 1 = 255
let arr = 255;
rprintln!("arr {}", arr);
tim1.arr.write(|w| unsafe { w.bits(arr) });
// Trigger update event to load the registers
tim1.cr1.modify(|_, w| w.urs().set_bit());
tim1.egr.write(|w| w.ug().set_bit());
tim1.cr1.modify(|_, w| w.urs().clear_bit());
// Set main output enable of all Output Compare (OC) registers
tim1.bdtr.modify(|_, w| w.moe().set_bit());
// Set output enable for channels 1 and 2
// Channel 1 (bit 0)
unsafe { bb::set(&tim1.ccer, 0) }
// Channel 4 (bit 0)
unsafe { bb::set(&tim1.ccer, 4) }
// Setup the timer
tim1.cr1.write(|w| {
w.cms()
.bits(0b00) // edge aligned mode
.dir() // counter used as upcounter
.clear_bit()
.opm() // one pulse mode
.clear_bit()
.cen() // enable counter
.set_bit()
});
// Set duty cycle of Channels
tim1.ccr1.write(|w| unsafe { w.ccr().bits(128) });
tim1.ccr2.write(|w| unsafe { w.ccr().bits(128) });
......@@ -119,14 +173,21 @@ const APP: () = {
rprintln!("here");
tim1.sr.modify(|_, w| w.uif().clear());
let mut dma_buff = [0u8; 256];
for i in 0..256 {
dma_buff[i] = i as u8;
}
loop {
for i in 0..256 {
// wait until next update event
while tim1.sr.read().uif().is_clear() {}
tim1.sr.modify(|_, w| w.uif().clear());
tim1.ccr1.write(|w| unsafe { w.ccr().bits(i) });
tim1.ccr2.write(|w| unsafe { w.ccr().bits(i) });
tim1.ccr1
.write(|w| unsafe { w.ccr().bits(dma_buff[i] as u16) });
tim1.ccr2
.write(|w| unsafe { w.ccr().bits(dma_buff[i] as u16) });
}
}
}
......
//! examples/rtt-pwm-sine.rs
//! cargo run --examples rtt-pwm-sine
// #![deny(unsafe_code)]
// #![deny(warnings)]
#![no_main]
#![no_std]
use core::f32::consts::PI;
use cortex_m::{asm, peripheral::DWT};
use panic_halt as _;
use rtt_target::{rprint, rprintln, rtt_init_print};
use stm32f4xx_hal::{bb, dma, gpio::Speed, prelude::*, pwm, stm32};
include!(concat!(env!("OUT_DIR"), "/sin_abs_const.rs"));
#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true)]
const APP: () = {
#[init]
fn init(mut cx: init::Context) {
rtt_init_print!();
rprintln!("init");
let dp = cx.device;
// Initialize (enable) the monotonic timer (CYCCNT)
cx.core.DCB.enable_trace();
cx.core.DWT.enable_cycle_counter();
let rcc = dp.RCC.constrain();
// Set up the system clock. 48 MHz?
let clocks = rcc
.cfgr
// .use_hse(8.mhz())
.sysclk(48.mhz())
.pclk1(24.mhz())
.freeze();
let gpioa = dp.GPIOA.split();
// we set the pins to VeryHigh to get the sharpest waveform possible
// (rise and fall times should have similar characteristics)
let channels = (
gpioa.pa8.into_alternate_af1().set_speed(Speed::VeryHigh),
gpioa.pa9.into_alternate_af1().set_speed(Speed::VeryHigh),
);
// Setup PWM RAW
let tim1 = dp.TIM1;
// Here we need unsafe as we are "stealing" the RCC peripheral
// At this point it has been contrained into SysConf and used to set clocks
let rcc = unsafe { &(*stm32::RCC::ptr()) };
rcc.apb2enr.modify(|_, w| w.tim1en().set_bit());
rcc.apb2rstr.modify(|_, w| w.tim1rst().set_bit());
rcc.apb2rstr.modify(|_, w| w.tim1rst().clear_bit());
// Setup chanel 1 and 2 as pwm_mode1
tim1.ccmr1_output()
.modify(|_, w| w.oc1pe().set_bit().oc1m().pwm_mode1());
tim1.ccmr1_output()
.modify(|_, w| w.oc2pe().set_bit().oc2m().pwm_mode1());
// The reference manual is a bit ambiguous about when enabling this bit is really
// necessary, but since we MUST enable the preload for the output channels then we
// might as well enable for the auto-reload too
tim1.cr1.modify(|_, w| w.arpe().set_bit());
let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
// check that its actually 48_000_000
rprintln!("clk {}", clk);
// we want maximum performance, thus we set the prescaler to 0
let pre = 0;
rprintln!("pre {}", pre);
tim1.psc.write(|w| w.psc().bits(pre));
// we want 8 bits of resolution
// so our ARR = 2^8 - 1 = 256 - 1 = 255
let arr = 255;
rprintln!("arr {}", arr);
tim1.arr.write(|w| unsafe { w.bits(arr) });
// Trigger update event to load the registers
tim1.cr1.modify(|_, w| w.urs().set_bit());
tim1.egr.write(|w| w.ug().set_bit());
tim1.cr1.modify(|_, w| w.urs().clear_bit());
// Set main output enable of all Output Compare (OC) registers
tim1.bdtr.modify(|_, w| w.moe().set_bit());
// Set output enable for channels 1 and 2
// Channel 1 (bit 0)
unsafe { bb::set(&tim1.ccer, 0) }
// Channel 4 (bit 0)
unsafe { bb::set(&tim1.ccer, 4) }
// Setup the timer
tim1.cr1.write(|w| {
w.cms()
.bits(0b00) // edge aligned mode
.dir() // counter used as upcounter
.clear_bit()
.opm() // one pulse mode
.clear_bit()
.cen() // enable counter
.set_bit()
});
// Setup TIMER2
let tim2 = dp.TIM2;
// Here we need unsafe as we are "stealing" the RCC peripheral
// At this point it has been contrained into SysConf and used to set clocks
let rcc = unsafe { &(*stm32::RCC::ptr()) };
rcc.apb1enr.modify(|_, w| w.tim2en().set_bit());
rcc.apb1rstr.modify(|_, w| w.tim2rst().set_bit());
rcc.apb1rstr.modify(|_, w| w.tim2rst().clear_bit());
// auto-reload, preload
tim1.cr1.modify(|_, w| w.arpe().set_bit());
let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
// check that its actually 48_000_000
rprintln!("clk {}", clk);
// assuinclude!(concat!(env!("OUT_DIR"), "/sin.rs"));c.write(|w| w.psc().bits(pre));
// we want 8 bits of resolution
// so our ARR = 2^8 - 1 = 256 - 1 = 255
let arr = 255;
rprintln!("arr {}", arr);
tim1.arr.write(|w| unsafe { w.bits(arr) });
// Trigger update event to load the registers
tim1.cr1.modify(|_, w| w.urs().set_bit());
tim1.egr.write(|w| w.ug().set_bit());
tim1.cr1.modify(|_, w| w.urs().clear_bit());
// Set main output enable of all Output Compare (OC) registers
tim1.bdtr.modify(|_, w| w.moe().set_bit());
// Set output enable for channels 1 and 2
// Channel 1 (bit 0)
unsafe { bb::set(&tim1.ccer, 0) }
// Channel 4 (bit 0)
unsafe { bb::set(&tim1.ccer, 4) }
// Setup the timer
tim1.cr1.write(|w| {
w.cms()
.bits(0b00) // edge aligned mode
.dir() // counter used as upcounter
.clear_bit()
.opm() // one pulse mode
.clear_bit()
.cen() // enable counter
.set_bit()
});
// Set duty cycle of Channels
tim1.ccr1.write(|w| unsafe { w.ccr().bits(128) });
tim1.ccr2.write(|w| unsafe { w.ccr().bits(128) });
// Set preload for the CCx
tim1.cr2.write(|w| w.ccpc().set_bit());
tim1.dier.write(|w| w.uie().enabled());
tim1.sr.modify(|_, w| w.uif().clear());
// Set divider to 4, (48_000_000/256)/4
tim1.rcr.modify(|_, w| unsafe { w.rep().bits(4) });
while tim1.sr.read().uif().is_clear() {
rprint!("-");
}
rprintln!("here");
tim1.sr.modify(|_, w| w.uif().clear());
loop {
for i in 0..SINE_BUF_SIZE {
// wait until next update event
while tim1.sr.read().uif().is_clear() {}
tim1.sr.modify(|_, w| w.uif().clear());
tim1.ccr1
.write(|w| unsafe { w.ccr().bits(SINE_BUF[i] as u16) });
tim1.ccr2
.write(|w| unsafe { w.ccr().bits(SINE_BUF[i] as u16) });
}
}
}
// [task(resources = [GPIOA], schedule = [toggle])]
// fn toggle(cx: toggle::Context) {
// static mut TOGGLE: bool = false;
// hprintln!("foo @ {:?}", Instant::now()).unwrap();
// if *TOGGLE {
// cx.resources.GPIOA.bsrr.write(|w| w.bs5().set_bit());
// } else {
// cx.resources.GPIOA.bsrr.write(|w| w.br5().set_bit());
// }
// *TOGGLE = !*TOGGLE;
// cx.schedule
// .toggle(cx.scheduled + 8_000_000.cycles())
// .unwrap();
// }
#[idle]
fn idle(_cx: idle::Context) -> ! {
rprintln!("idle");
loop {
continue;
}
}
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment