//! # 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; // GPIO traits use embedded_hal::digital::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().to_Hz()); // 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