Commit 6a014fbc authored by Per Lindgren's avatar Per Lindgren
Browse files

sine generation works

parent 79e17d8f
......@@ -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;
}
}
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment