rp-hal-boards/boards/rp-pico/examples/pico_interpolator.rs
Kasil 31469c232f
Add support for the Interpolator (#371)
* Implementation of the interpolator.
* corrected formatting
* fixed documentation code
* add clamp flag to LaneCtrl
* addition of an example for the interpolator
* put documentation behind ///
* rewording comment for clarity
* using more idiomatic fn new
2022-08-25 18:02:56 +01:00

442 lines
13 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! # Pico Interpolator Example
//!
//! Example demonstrating the usage of the hardware interpolator.
//!
//! Runs several test programs, outputs the result on LEDs.
//! Green led for successful test connects to GPIO3.
//! Red led for unsuccessful test connects to GPIO4.
//! In case of failure, the system LED blinks the number of the test.
//! In case of success, the system LED stays lit.
//!
//! See the `Cargo.toml` file for Copyright and license details.
#![no_std]
#![no_main]
// The macro for our start-up function
use rp_pico::entry;
// Time handling traits
use embedded_time::rate::*;
// GPIO traits
use embedded_hal::digital::v2::OutputPin;
// Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked)
use panic_halt as _;
// A shorter alias for the Peripheral Access Crate, which provides low-level
// register access
use rp_pico::hal::pac;
// A shorter alias for the Hardware Abstraction Layer, which provides
// higher-level drivers.
use rp_pico::hal;
// Pull in any important traits
use rp_pico::hal::prelude::*;
use rp_pico::hal::sio::{Interp, Interp0, Interp1, Lane, LaneCtrl};
/// Entry point to our bare-metal application.
///
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables are initialised.
///
/// The function configures the RP2040 peripherals, then just reads the button
/// and sets the LED appropriately.
#[entry]
fn main() -> ! {
// Grab our singleton objects
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
// Set up the watchdog driver - needed by the clock setup code
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
// Configure the clocks
//
// The default is to generate a 125 MHz system clock
let clocks = hal::clocks::init_clocks_and_plls(
rp_pico::XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
// The delay object lets us wait for specified amounts of time (in
// milliseconds)
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
// The single-cycle I/O block controls our GPIO pins
let mut sio = hal::Sio::new(pac.SIO);
// Set the pins up according to their function on this particular board
let pins = rp_pico::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
// Our LED outputs
let mut system_led_pin = pins.led.into_push_pull_output();
let mut green_led_pin = pins.gpio3.into_push_pull_output();
let mut red_led_pin = pins.gpio4.into_push_pull_output();
system_led_pin.set_low().unwrap();
green_led_pin.set_low().unwrap();
red_led_pin.set_low().unwrap();
let mut choose_led = |index: u32, result: bool| {
if result {
// blink the green led once to indicate success
green_led_pin.set_high().unwrap();
delay.delay_ms(500);
green_led_pin.set_low().unwrap();
delay.delay_ms(500);
} else {
// turn the red led on to indicate failure
// and blink the on board led to indicate which test failed, looping forever
red_led_pin.set_high().unwrap();
loop {
for _ in 0..index {
system_led_pin.set_high().unwrap();
delay.delay_ms(200);
system_led_pin.set_low().unwrap();
delay.delay_ms(200);
}
delay.delay_ms(1000);
}
}
};
// Run forever, setting the LED according to the button
choose_led(1, multiplication_table(&mut sio.interp0));
choose_led(2, moving_mask(&mut sio.interp0));
choose_led(3, cross_lanes(&mut sio.interp0));
choose_led(4, simple_blend1(&mut sio.interp0));
choose_led(5, simple_blend2(&mut sio.interp0));
choose_led(6, clamp(&mut sio.interp1));
choose_led(7, texture_mapping(&mut sio.interp0));
// turn the on board led on to indicate testing is done
system_led_pin.set_high().unwrap();
loop {
delay.delay_ms(1000);
}
}
fn multiplication_table(interp: &mut Interp0) -> bool {
//get the default configuration that just keep adding base into accum
let config = LaneCtrl::new();
//write the configuration to the hardware.
interp.get_lane0().set_ctrl(config.encode());
//set the accumulator to 0 and the base to 9
interp.get_lane0().set_accum(0);
interp.get_lane0().set_base(9);
//the expected output for comparison
let expected = [9, 18, 27, 36, 45, 54, 63, 72, 81, 90];
for i in expected {
//returns the value of accum + base and sets accum to the same value
let value = interp.get_lane0().pop();
if value != i {
return false; //inform that the interpolator did not return the expected value
}
}
true
}
fn moving_mask(interp: &mut Interp0) -> bool {
//get the default configuration that just keep adding base into accum
let mut config = LaneCtrl::new();
interp.get_lane0().set_accum(0x1234ABCD);
let expected = [
0x0000_000D,
0x0000_00C0,
0x0000_0B00,
0x0000_A000,
0x0004_0000,
0x0030_0000,
0x0200_0000,
0x1000_0000,
];
for i in 0..8 {
// LSB, then MSB. These are inclusive, so 0,31 means "the entire 32 bit register"
config.mask_lsb = i * 4;
config.mask_msb = i * 4 + 3;
interp.get_lane0().set_ctrl(config.encode());
// Reading read_raw() returns the lane data
// after shifting, masking and sign extending, without adding base
if interp.get_lane0().read_raw() != expected[i as usize] {
return false;
}
}
let signed_expected = [
0xFFFF_FFFD,
0xFFFF_FFC0,
0xFFFF_FB00,
0xFFFF_A000,
0x0004_0000,
0x0030_0000,
0x0200_0000,
0x1000_0000,
];
config.signed = true;
for i in 0..8 {
config.mask_lsb = i * 4;
config.mask_msb = i * 4 + 3;
interp.get_lane0().set_ctrl(config.encode());
if interp.get_lane0().read_raw() != signed_expected[i as usize] {
return false;
}
}
true
}
fn cross_lanes(interp: &mut Interp0) -> bool {
// this configuration will at the time of pop()
// when applied to lane0 : set lane0 accumulator to the result from lane1
// when applied to lane1 : set lane1 accumulator to the result from lane0
let config = LaneCtrl {
cross_result: true,
..LaneCtrl::new()
};
let encoded_config = config.encode();
// each lane is used through an accessor,
// as lanes mutate each other, they can not be borrowed at the same time
interp.get_lane0().set_ctrl(encoded_config);
interp.get_lane1().set_ctrl(encoded_config);
interp.get_lane0().set_accum(123);
interp.get_lane1().set_accum(456);
// lane0 will add 1 to its result, lane1 will add nothing
interp.get_lane0().set_base(1);
interp.get_lane1().set_base(0);
let expected = [
(124, 456),
(457, 124),
(125, 457),
(458, 125),
(126, 458),
(459, 126),
(127, 459),
(460, 127),
(128, 460),
(461, 128),
];
for i in expected {
if i != (interp.get_lane0().peek(), interp.get_lane1().pop()) {
return false;
}
}
true
}
fn simple_blend1(interp: &mut Interp0) -> bool {
let config = LaneCtrl {
blend: true,
..LaneCtrl::new()
};
//enable blend mode
interp.get_lane0().set_ctrl(config.encode());
//make sure the default configuration is in lane1 as the value may be shifted and masked.
interp.get_lane1().set_ctrl(LaneCtrl::new().encode());
//set the minimum value for interp.get_lane0().set_accum(0) 0/256
interp.get_lane0().set_base(500);
//set the maximum value which is inaccessible
// as the blend is done between 0/256 and 255/256
interp.get_lane1().set_base(1000);
let expected = [500, 582, 666, 748, 832, 914, 998];
for i in 0..=6 {
interp.get_lane1().set_accum(255 * i / 6);
if expected[i as usize] != interp.get_lane1().peek() {
return false;
}
}
true
}
fn simple_blend2(interp: &mut Interp0) -> bool {
let config = LaneCtrl {
blend: true,
..LaneCtrl::new()
};
//enable blend mode
interp.get_lane0().set_ctrl(config.encode());
interp.get_lane0().set_base((-1000i32) as u32);
interp.get_lane1().set_base(1000);
let mut config1 = LaneCtrl {
signed: true,
..LaneCtrl::new()
};
interp.get_lane1().set_ctrl(config1.encode());
let expected_signed = [-1000, -672, -336, -8, 328, 656, 992];
for i in 0..=6 {
// write a value between 0 and 256 (exclusive)
interp.get_lane1().set_accum(255 * i / 6);
// reads it as a value between -1000 and 1000 (exclusive)
if interp.get_lane1().peek() as i32 != expected_signed[i as usize] {
return false;
}
}
config1.signed = false;
interp.get_lane1().set_ctrl(config1.encode());
let expected_unsigned = [
0xfffffc18, 0xd5fffd60, 0xaafffeb0, 0x80fffff8, 0x56000148, 0x2c000290, 0x010003e0,
];
for i in 0..=6 {
interp.get_lane1().set_accum(255 * i / 6);
// reads a value between 4294966296 and 1000
if interp.get_lane1().peek() != expected_unsigned[i as usize] {
return false;
}
}
true
}
///Divides by 4 and clamp the value between 0 and 255 inclusive
fn clamp(interp: &mut Interp1) -> bool {
// Enables Clamp ONLY AVAILABLE ON Interp1
// shift two bits to the right and mask the two most significant bits
// because sign extension is made after the mask
let config = LaneCtrl {
clamp: true,
shift: 2,
mask_lsb: 0,
mask_msb: 29,
signed: true,
..LaneCtrl::new()
};
interp.get_lane0().set_ctrl(config.encode());
//set minimum value of result
interp.get_lane0().set_base(0);
//set maximum value of result
interp.get_lane1().set_base(255);
let values: [(i32, i32); 9] = [
(-1024, 0),
(-768, 0),
(-512, 0),
(-256, 0),
(0, 0),
(256, 64),
(512, 128),
(768, 192),
(1024, 255),
];
for (arg, result) in values {
interp.get_lane0().set_accum(arg as u32);
if result != interp.get_lane0().peek() as i32 {
return false;
}
}
true
}
fn texture_mapping(interp: &mut Interp0) -> bool {
#[rustfmt::skip]
let texture: [u8;16] = [
0x00, 0x01, 0x02, 0x03,
0x10, 0x11, 0x12, 0x13,
0x20, 0x21, 0x22, 0x23,
0x30, 0x31, 0x32, 0x33,
];
// the position will be given in fixed point with 16 bits
// fractional part
let uv_fractional_bits = 16;
let texture_width_bits = 2;
let texture_height_bits = 2;
// bits
// 3322222222221111 1111110000000000
// 1098765432109876 5432109876543210
// accum0 u axis coordinate xx xxxxxxxxxxxxxxxx 18 bits
// after shift and mask xx
// accum1 v axis xx xxxxxxxxxxxxxxxx 18 bits
// after shift and mask xx
// add_raw make the interpolator increment the accumulator
// with the base value without masking or shifting
let config0 = LaneCtrl {
add_raw: true,
shift: uv_fractional_bits,
mask_lsb: 0,
mask_msb: texture_width_bits - 1,
..LaneCtrl::new()
};
interp.get_lane0().set_ctrl(config0.encode());
let config1 = LaneCtrl {
add_raw: true,
shift: uv_fractional_bits - texture_width_bits,
mask_lsb: texture_width_bits,
mask_msb: texture_width_bits + texture_height_bits - 1,
..LaneCtrl::new()
};
interp.get_lane1().set_ctrl(config1.encode());
interp.set_base(0);
// set starting position to 0x0
// will move 1/2 a pixel horizontally
// and 1/3 a pixel vertically per call to pop()
interp.get_lane0().set_accum(0);
interp.get_lane0().set_base(65536 / 2);
interp.get_lane1().set_accum(0);
interp.get_lane1().set_base(65536 / 3);
let expected = [
0x00, 0x00, 0x01, 0x01, 0x12, 0x12, 0x13, 0x23, 0x20, 0x20, 0x31, 0x31,
];
for i in expected {
if i != texture[interp.pop() as usize] {
return false;
}
}
// reset the starting position
interp.get_lane0().set_accum(0);
interp.get_lane1().set_accum(0);
interp.set_base(texture.as_ptr() as u32);
for i in expected {
// This is unsafe and should be done extremely carefully
// remember to follow memory alignment,
// reading or writing an unaligned address will crash
if i != unsafe { *(interp.pop() as *const u8) } {
return false;
}
}
true
}
// End of file