From 27b07dc4a8f8d92d64b2f7d20029374da8c7460e Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 3 Nov 2024 22:35:50 -0500 Subject: [PATCH] Add extensive explanation of the code. --- halloween-leds/src/main.rs | 58 ++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/halloween-leds/src/main.rs b/halloween-leds/src/main.rs index 1a5d589..9b6ae75 100644 --- a/halloween-leds/src/main.rs +++ b/halloween-leds/src/main.rs @@ -1,14 +1,21 @@ #![no_main] #![no_std] -use embedded_hal::delay::DelayNs; -use embedded_hal::spi::SpiBus; +/// This application demonstrates using a Raspberry Pi Pico to control an individual SK9822 module. +/// 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 rp_pico::{ entry, hal::{ clocks::init_clocks_and_plls, fugit::RateExtU32, + gpio::{ + bank0::{Gpio10, Gpio11}, + FunctionSpi, Pin, PullDown, + }, spi::Spi, Clock, Sio, Timer, Watchdog, }, @@ -21,15 +28,28 @@ const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // MHz, https://forums.raspberrypi.co #[entry] unsafe fn main() -> ! { + // rp_pico::pac::Peripherals is a reference to physical hardware defined on the Pico. 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); + + // 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); + + // Here we grab the GPIO pins in bank 0. let pins = Pins::new( peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS, ); + + // Initialize an abstraction of the clock system with a batch of standard hardware clocks. let clocks = init_clocks_and_plls( XOSC_CRYSTAL_FREQ, peripherals.XOSC, @@ -42,15 +62,23 @@ unsafe fn main() -> ! { .ok() .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 spi_clk = pins.gpio10.into_function(); - let spi_sdo = pins.gpio11.into_function(); - let spi = Spi::<_, _, _, 8>::new(peripherals.SPI1, (spi_sdo, spi_clk)); - let mut spi = spi.init( + // Grab the clock and data pins for SPI1. For Clock pins and for Data pins, there are only two + // pins each on the Pico which can function for SPI1. + let spi_clk: Pin = pins.gpio10.into_function(); + let spi_sdo: Pin = 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, + // The SPI system uses the peripheral clock clocks.peripheral_clock.freq(), + // Transmit data at a rate of 1Mbit. 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, ); @@ -59,13 +87,27 @@ unsafe fn main() -> ! { // 4 for the end frame // = 20 bytes 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[9] = 0xff; lights[10] = 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 step = 1; loop { @@ -75,8 +117,6 @@ unsafe fn main() -> ! { step = 1; }; lights[5] = brightness as u8; - lights[6] = 255; - // lights[7] = brightness as u8; brightness = brightness + step; let _ = spi.write(lights.as_slice());