Create a small application that controls a dotstar from a raspberry pi pico #254

Merged
savanni merged 5 commits from halloween__lights into main 2024-11-04 03:48:46 +00:00
1 changed files with 49 additions and 9 deletions
Showing only changes of commit 99573ff7cf - Show all commits

View File

@ -1,14 +1,21 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
use embedded_hal::delay::DelayNs; /// This application demonstrates using a Raspberry Pi Pico to control an individual SK9822 module.
use embedded_hal::spi::SpiBus; /// Keep in mind that the Pico, though it accepts 5V for power, it runs on 3.3V logic. The GPIO
/// pins will emit only 3.3 volts, and the SK9822 needs 5V logic. So, make sure that the GPIO pins
/// run through a transistor or a logic level lhifter to go from 3.3V logic to 5V logic.
use embedded_hal::{delay::DelayNs, spi::SpiBus};
use panic_halt as _; use panic_halt as _;
use rp_pico::{ use rp_pico::{
entry, entry,
hal::{ hal::{
clocks::init_clocks_and_plls, clocks::init_clocks_and_plls,
fugit::RateExtU32, fugit::RateExtU32,
gpio::{
bank0::{Gpio10, Gpio11},
FunctionSpi, Pin, PullDown,
},
spi::Spi, spi::Spi,
Clock, Sio, Timer, Watchdog, Clock, Sio, Timer, Watchdog,
}, },
@ -21,15 +28,28 @@ const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // MHz, https://forums.raspberrypi.co
#[entry] #[entry]
unsafe fn main() -> ! { unsafe fn main() -> ! {
// rp_pico::pac::Peripherals is a reference to physical hardware defined on the Pico.
let mut peripherals = pac::Peripherals::take().unwrap(); let mut peripherals = pac::Peripherals::take().unwrap();
// SIO inidcates "Single Cycle IO". I don't know what this means, but it could mean that this
// is a class of IO operations that can be run in a single clock cycle, such as switching a
// GPIO pin on or off.
let sio = Sio::new(peripherals.SIO); let sio = Sio::new(peripherals.SIO);
// Many of the following systems require a watchdog. I do not know what this does, either, but
// it may be some failsafe software that will reset operations if the watchdog detects a lack
// of activity.
let mut watchdog = Watchdog::new(peripherals.WATCHDOG); let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
// Here we grab the GPIO pins in bank 0.
let pins = Pins::new( let pins = Pins::new(
peripherals.IO_BANK0, peripherals.IO_BANK0,
peripherals.PADS_BANK0, peripherals.PADS_BANK0,
sio.gpio_bank0, sio.gpio_bank0,
&mut peripherals.RESETS, &mut peripherals.RESETS,
); );
// Initialize an abstraction of the clock system with a batch of standard hardware clocks.
let clocks = init_clocks_and_plls( let clocks = init_clocks_and_plls(
XOSC_CRYSTAL_FREQ, XOSC_CRYSTAL_FREQ,
peripherals.XOSC, peripherals.XOSC,
@ -42,15 +62,23 @@ unsafe fn main() -> ! {
.ok() .ok()
.unwrap(); .unwrap();
// An abstraction for a timer which we can use to delay the code.
let mut timer = Timer::new(peripherals.TIMER, &mut peripherals.RESETS, &clocks); let mut timer = Timer::new(peripherals.TIMER, &mut peripherals.RESETS, &clocks);
let spi_clk = pins.gpio10.into_function(); // Grab the clock and data pins for SPI1. For Clock pins and for Data pins, there are only two
let spi_sdo = pins.gpio11.into_function(); // pins each on the Pico which can function for SPI1.
let spi = Spi::<_, _, _, 8>::new(peripherals.SPI1, (spi_sdo, spi_clk)); let spi_clk: Pin<Gpio10, FunctionSpi, PullDown> = pins.gpio10.into_function();
let mut spi = spi.init( let spi_sdo: Pin<Gpio11, FunctionSpi, PullDown> = pins.gpio11.into_function();
// Now, create the SPI function abstraction for SPI1 with spi_clk and spi_sdo.
let mut spi = Spi::<_, _, _, 8>::new(peripherals.SPI1, (spi_sdo, spi_clk)).init(
&mut peripherals.RESETS, &mut peripherals.RESETS,
// The SPI system uses the peripheral clock
clocks.peripheral_clock.freq(), clocks.peripheral_clock.freq(),
// Transmit data at a rate of 1Mbit.
1_u32.MHz(), 1_u32.MHz(),
// Run with SPI Mode 1. This means that the clock line should start high and that data will
// be sampled starting at the first falling edge.
embedded_hal::spi::MODE_1, embedded_hal::spi::MODE_1,
); );
@ -59,13 +87,27 @@ unsafe fn main() -> ! {
// 4 for the end frame // 4 for the end frame
// = 20 bytes // = 20 bytes
let mut lights: [u8; 12] = [0; 12]; let mut lights: [u8; 12] = [0; 12];
// We just skip the first four bytes, because the start frame is four bytes of 0.
// Set the first byte of the one and only lamp. The first byte follows the pattern of three 1
// bits followed by five additional bits that indicate an overall brightness level of the
// pixel. The datasheet for the SK9822 doesn't specify the exact effect, but it does mean that
// the higher this number is, the brighter 255 means for an given LED in the array. 1 is the
// lowest brightness that emits light, and 31 is the highest supported brightness.
lights[4] = 0xe0 + 1;
// Set the Blue light of the dotstar to 255, assuming the dotstar frame format is RBG. Note
// that the standard SK9822 datasheed indicates that the format is BGR. Your mileage may vary.
lights[6] = 255;
// The end frame is four bytes of 255.
lights[8] = 0xff; lights[8] = 0xff;
lights[9] = 0xff; lights[9] = 0xff;
lights[10] = 0xff; lights[10] = 0xff;
lights[11] = 0xff; lights[11] = 0xff;
lights[4] = 0xe0 + 1;
// The rest of this is just a stock pulsating animation which is slightly brightening and
// dimming the *blue* LED (on my set of dotstars).
let mut brightness = 1; let mut brightness = 1;
let mut step = 1; let mut step = 1;
loop { loop {
@ -75,8 +117,6 @@ unsafe fn main() -> ! {
step = 1; step = 1;
}; };
lights[5] = brightness as u8; lights[5] = brightness as u8;
lights[6] = 255;
// lights[7] = brightness as u8;
brightness = brightness + step; brightness = brightness + step;
let _ = spi.write(lights.as_slice()); let _ = spi.write(lights.as_slice());