Merge pull request #181 from pimoroni/plasma2040

Plasma 2040 library & examples
pull/192/head
Philip Howard 2021-08-17 10:07:19 +01:00 zatwierdzone przez GitHub
commit 852fdcaa45
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
29 zmienionych plików z 1479 dodań i 4 usunięć

Wyświetl plik

@ -743,6 +743,13 @@ namespace pimoroni {
return encoder_offset[channel] + value;
}
void IOExpander::clear_rotary_encoder(uint8_t channel) {
channel -= 1;
encoder_last[channel] = 0;
uint8_t reg = ENC_COUNT[channel];
i2c->reg_write_uint8(address, reg, 0);
}
uint8_t IOExpander::get_bit(uint8_t reg, uint8_t bit) {
// Returns the specified bit (nth position from right) from a register
return i2c->reg_read_uint8(address, reg) & (1 << bit);

Wyświetl plik

@ -214,6 +214,7 @@ namespace pimoroni {
void setup_rotary_encoder(uint8_t channel, uint8_t pin_a, uint8_t pin_b, uint8_t pin_c = 0, bool count_microsteps = false);
int16_t read_rotary_encoder(uint8_t channel);
void clear_rotary_encoder(uint8_t channel);
private:
uint8_t i2c_reg_read_uint8(uint8_t reg);

Wyświetl plik

@ -16,6 +16,8 @@ add_subdirectory(breakout_msa301)
add_subdirectory(breakout_bme688)
add_subdirectory(breakout_bmp280)
add_subdirectory(breakout_bme280)
add_subdirectory(breakout_as7262)
add_subdirectory(breakout_bh1745)
add_subdirectory(pico_display)
add_subdirectory(pico_unicorn)
@ -30,5 +32,5 @@ add_subdirectory(pico_tof_display)
add_subdirectory(pico_trackball_display)
add_subdirectory(pico_audio)
add_subdirectory(pico_wireless)
add_subdirectory(breakout_as7262)
add_subdirectory(breakout_bh1745)
add_subdirectory(plasma_2040)

Wyświetl plik

@ -0,0 +1,2 @@
include(plasma2040_rotary.cmake)
include(plasma2040_rainbow.cmake)

Wyświetl plik

@ -0,0 +1,10 @@
add_executable(plasma2040_rainbow plasma2040_rainbow.cpp)
target_link_libraries(plasma2040_rainbow
pico_stdlib
plasma2040
rgbled
button
)
pico_add_extra_outputs(plasma2040_rainbow)

Wyświetl plik

@ -0,0 +1,66 @@
#include <stdio.h>
#include <math.h>
#include <cstdint>
#include "pico/stdlib.h"
#include "plasma2040.hpp"
#include "common/pimoroni_common.hpp"
#include "rgbled.hpp"
#include "button.hpp"
/*
Press "B" to speed up the LED cycling effect.
Press "A" to slow it down again.
*/
using namespace pimoroni;
// Set how many LEDs you have
const uint N_LEDS = 30;
// Pick *one* LED type by uncommenting the relevant line below:
// APA102-style LEDs with Data/Clock lines. AKA DotStar
//plasma::APA102 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT, plasma::PIN_CLK);
// WS28X-style LEDs with a single signal line. AKA NeoPixel
// by default the WS2812 LED strip will be 400KHz, RGB with no white element
plasma::WS2812 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT);
Button button_a(plasma::BUTTON_A, Polarity::ACTIVE_LOW, 50);
Button button_b(plasma::BUTTON_B, Polarity::ACTIVE_LOW, 50);
RGBLED led(plasma::LED_R, plasma::LED_G, plasma::LED_B);
int main() {
stdio_init_all();
led_strip.start(60);
int speed = 10;
float offset = 0.0f;
while (true) {
bool a = button_a.read();
bool b = button_b.read();
if(a) speed--;
if(b) speed++;
speed = std::min((int)255, std::max((int)1, speed));
offset += float(speed) / 2000.0f;
for(auto i = 0u; i < led_strip.num_leds; ++i) {
float hue = float(i) / led_strip.num_leds;
led_strip.set_hsv(i, hue + offset, 1.0f, 1.0f);
}
led.set_rgb(speed, 0, 255 - speed);
// Sleep time controls the rate at which the LED buffer is updated
// but *not* the actual framerate at which the buffer is sent to the LEDs
sleep_ms(1000 / 60);
}
}

Wyświetl plik

@ -0,0 +1,12 @@
add_executable(plasma2040_rotary plasma2040_rotary.cpp)
target_link_libraries(plasma2040_rotary
pico_stdlib
plasma2040
breakout_encoder
pimoroni_i2c
rgbled
button
)
pico_add_extra_outputs(plasma2040_rotary)

Wyświetl plik

@ -0,0 +1,152 @@
#include <stdio.h>
#include <math.h>
#include <cstdint>
#include "pico/stdlib.h"
#include "plasma2040.hpp"
#include "common/pimoroni_common.hpp"
#include "breakout_encoder.hpp"
#include "rgbled.hpp"
#include "button.hpp"
using namespace pimoroni;
// Set how many LEDs you have
const uint N_LEDS = 30;
// Pick *one* LED type by uncommenting the relevant line below:
// APA102-style LEDs with Data/Clock lines. AKA DotStar
//plasma::APA102 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT, plasma::PIN_CLK);
// WS28X-style LEDs with a single signal line. AKA NeoPixel
plasma::WS2812 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT);
Button button_a(plasma::BUTTON_A);
Button button_b(plasma::BUTTON_B);
RGBLED led(plasma::LED_R, plasma::LED_G, plasma::LED_B);
I2C i2c(BOARD::PICO_EXPLORER);
BreakoutEncoder enc(&i2c);
enum ENCODER_MODE {
COLOUR,
ANGLE,
BRIGHTNESS,
TIME
};
void colour_cycle(float hue, float t, float angle) {
t /= 200.0f;
for (auto i = 0u; i < led_strip.num_leds; ++i) {
float offset = (M_PI * i) / led_strip.num_leds;
offset = sinf(offset + t) * angle;
led_strip.set_hsv(i, (hue + offset) / 360.0f, 1.0f, 1.0f);
}
}
void gauge(uint v, uint vmax = 100) {
uint light_pixels = led_strip.num_leds * v / vmax;
for (auto i = 0u; i < led_strip.num_leds; ++i) {
if(i < light_pixels) {
led_strip.set_rgb(i, 0, 255, 0);
} else {
led_strip.set_rgb(i, 255, 0, 0);
}
}
}
int main() {
stdio_init_all();
led_strip.start(60);
bool encoder_detected = enc.init();
enc.clear_interrupt_flag();
int speed = 50;
float hue = 0;
int angle = 120;
int8_t brightness = 16;
bool cycle = true;
ENCODER_MODE mode = ENCODER_MODE::COLOUR;
while (true) {
uint32_t t = millis();
if(encoder_detected) {
if(enc.get_interrupt_flag()) {
int count = enc.read();
enc.clear_interrupt_flag();
enc.clear();
cycle = false;
switch(mode) {
case ENCODER_MODE::COLOUR:
hue += count;
brightness = std::min((int8_t)359, brightness);
brightness = std::max((int8_t)0, brightness);
colour_cycle(hue, 0, (float)angle);
break;
case ENCODER_MODE::ANGLE:
angle += count;
angle = std::min((int)359, angle);
angle = std::max((int)0, angle);
colour_cycle(hue, 0, (float)angle);
break;
case ENCODER_MODE::BRIGHTNESS:
brightness += count;
brightness = std::min((int8_t)31, brightness);
brightness = std::max((int8_t)0, brightness);
led_strip.set_brightness(brightness);
gauge(brightness, 31);
break;
case ENCODER_MODE::TIME:
speed += count;
speed = std::min((int)100, speed);
speed = std::max((int)0, speed);
gauge(speed, 100);
break;
}
}
}
bool a_pressed = button_a.read();
bool b_pressed = button_b.read();
if(b_pressed) cycle = true;
switch(mode) {
case ENCODER_MODE::COLOUR:
led.set_rgb(255, 0, 0);
if(a_pressed) mode = ENCODER_MODE::ANGLE;
break;
case ENCODER_MODE::ANGLE:
led.set_rgb(255, 255, 0);
if(a_pressed) mode = ENCODER_MODE::BRIGHTNESS;
break;
case ENCODER_MODE::BRIGHTNESS:
led.set_rgb(0, 255, 0);
if(a_pressed) mode = ENCODER_MODE::TIME;
break;
case ENCODER_MODE::TIME:
led.set_rgb(0, 0, 255);
if(a_pressed) mode = ENCODER_MODE::COLOUR;
break;
}
if(cycle) colour_cycle(hue, t * speed / 100, (float)angle);
auto first_led = led_strip.get(0);
enc.set_led(first_led.r, first_led.g, first_led.b);
// Sleep time controls the rate at which the LED buffer is updated
// but *not* the actual framerate at which the buffer is sent to the LEDs
sleep_ms(1000 / 60);
}
}

Wyświetl plik

@ -22,3 +22,4 @@ add_subdirectory(pico_scroll)
add_subdirectory(pico_explorer)
add_subdirectory(pico_rgb_keypad)
add_subdirectory(pico_wireless)
add_subdirectory(plasma2040)

Wyświetl plik

@ -87,6 +87,10 @@ namespace pimoroni {
return (ioe.get_interrupt_flag() > 0);
}
void BreakoutEncoder::clear() {
ioe.clear_rotary_encoder(ENC_CHANNEL);
}
int16_t BreakoutEncoder::read() {
int16_t count = ioe.read_rotary_encoder(ENC_CHANNEL);
if(direction != DIRECTION_CW)

Wyświetl plik

@ -23,7 +23,6 @@ namespace pimoroni {
static const uint8_t DEFAULT_I2C_ADDRESS = 0x0F;
static constexpr float DEFAULT_BRIGHTNESS = 1.0f; //Effectively the maximum fraction of the period that the LED will be on
static const Direction DEFAULT_DIRECTION = DIRECTION_CW;
static const uint8_t PIN_UNUSED = UINT8_MAX;
static const uint32_t DEFAULT_TIMEOUT = 1;
private:
@ -90,6 +89,7 @@ namespace pimoroni {
bool available();
int16_t read();
void clear();
};
}

Wyświetl plik

@ -0,0 +1 @@
include(plasma2040.cmake)

Wyświetl plik

@ -0,0 +1,98 @@
#include "apa102.hpp"
namespace plasma {
APA102::APA102(uint num_leds, PIO pio, uint sm, uint pin_dat, uint pin_clk, uint freq, RGB* buffer) : buffer(buffer), num_leds(num_leds), pio(pio), sm(sm) {
uint offset = pio_add_program(pio, &apa102_program);
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_clk) | (1u << pin_dat));
pio_sm_set_pindirs_with_mask(pio, sm, ~0u, (1u << pin_clk) | (1u << pin_dat));
pio_gpio_init(pio, pin_clk);
pio_gpio_init(pio, pin_dat);
pio_sm_config c = apa102_program_get_default_config(offset);
sm_config_set_out_pins(&c, pin_dat, 1);
sm_config_set_sideset_pins(&c, pin_clk);
sm_config_set_out_shift(&c, false, true, 32);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
// The PIO program transmits 1 bit every 2 execution cycles
float div = (float)clock_get_hz(clk_sys) / (2 * freq);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
dma_channel = dma_claim_unused_channel(true);
dma_channel_config config = dma_channel_get_default_config(dma_channel);
channel_config_set_bswap(&config, true);
channel_config_set_dreq(&config, pio_get_dreq(pio, sm, true));
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
channel_config_set_read_increment(&config, true);
dma_channel_configure(dma_channel, &config, &pio->txf[sm], NULL, 0, false);
if(this->buffer == nullptr) {
this->buffer = new RGB[num_leds];
}
}
bool APA102::dma_timer_callback(struct repeating_timer *t) {
((APA102*)t->user_data)->update();
return true;
}
void APA102::update(bool blocking) {
if(dma_channel_is_busy(dma_channel) && !blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
pio->txf[sm] = 0x00000000; // Output the APA102 start-of-frame bytes
dma_channel_set_trans_count(dma_channel, num_leds, false);
dma_channel_set_read_addr(dma_channel, buffer, true);
if (!blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
}
bool APA102::start(uint fps) {
add_repeating_timer_ms(-(1000 / fps), dma_timer_callback, (void*)this, &timer);
return true;
}
bool APA102::stop() {
return cancel_repeating_timer(&timer);
}
void APA102::clear() {
for (auto i = 0u; i < num_leds; ++i) {
set_rgb(i, 0, 0, 0);
}
}
void APA102::set_hsv(uint32_t index, float h, float s, float v) {
float i = floor(h * 6.0f);
float f = h * 6.0f - i;
v *= 255.0f;
uint8_t p = v * (1.0f - s);
uint8_t q = v * (1.0f - f * s);
uint8_t t = v * (1.0f - (1.0f - f) * s);
switch (int(i) % 6) {
case 0: set_rgb(index, v, t, p); break;
case 1: set_rgb(index, q, v, p); break;
case 2: set_rgb(index, p, v, t); break;
case 3: set_rgb(index, p, q, v); break;
case 4: set_rgb(index, t, p, v); break;
case 5: set_rgb(index, v, p, q); break;
}
}
void APA102::set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b) {
buffer[index].rgb(r, g, b);
}
void APA102::set_brightness(uint8_t b) {
for (auto i = 0u; i < num_leds; ++i) {
buffer[i].brightness(b);
}
}
}

Wyświetl plik

@ -0,0 +1,84 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
This code is significantly modified from the PIO apa102 example
found here: https://github.com/raspberrypi/pico-examples/tree/master/pio/apa102
*/
#pragma once
#include <math.h>
#include <cstdint>
#include "apa102.pio.h"
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/clocks.h"
#include "hardware/timer.h"
namespace plasma {
class APA102 {
public:
static const uint DEFAULT_SERIAL_FREQ = 20 * 1000 * 1000; // 20MHz
#pragma pack(push, 1)
union alignas(4) RGB {
struct {
uint8_t sof = 0b11101111;
uint8_t b;
uint8_t g;
uint8_t r;
} ;
uint32_t srgb;
void operator=(uint32_t v) {
srgb = v;
};
void brightness(uint8_t b) {
sof = 0b11100000 | b;
};
void rgb(uint8_t r, uint8_t g, uint8_t b) {
this->r = r;
this->g = g;
this->b = b;;
}
RGB() : sof(0b11101111), b(0), g(0), r(0) {};
};
#pragma pack(pop)
RGB *buffer;
uint32_t num_leds;
APA102(uint num_leds, PIO pio, uint sm, uint pin_dat, uint pin_clk, uint freq=DEFAULT_SERIAL_FREQ, RGB* buffer=nullptr);
~APA102() {
stop();
clear();
update(true);
dma_channel_unclaim(dma_channel);
pio_sm_unclaim(pio, sm);
delete[] buffer;
}
bool start(uint fps=60);
bool stop();
void update(bool blocking=false);
void clear();
void set_hsv(uint32_t index, float h, float s, float v);
void set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b);
void set_brightness(uint8_t b);
RGB get(uint32_t index) {return buffer[index];};
static bool dma_timer_callback(struct repeating_timer *t);
private:
uint32_t fps;
PIO pio;
uint sm;
int dma_channel;
struct repeating_timer timer;
};
}

Wyświetl plik

@ -0,0 +1,10 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program apa102
.side_set 1
out pins, 1 side 0 ; Stall here when no data (still asserts clock low)
nop side 1

Wyświetl plik

@ -0,0 +1,17 @@
add_library(plasma2040 INTERFACE)
target_sources(plasma2040 INTERFACE
${CMAKE_CURRENT_LIST_DIR}/apa102.cpp
${CMAKE_CURRENT_LIST_DIR}/ws2812.cpp
)
target_include_directories(plasma2040 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(plasma2040 INTERFACE
pico_stdlib
hardware_pio
hardware_dma
)
pico_generate_pio_header(plasma2040 ${CMAKE_CURRENT_LIST_DIR}/apa102.pio)
pico_generate_pio_header(plasma2040 ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)

Wyświetl plik

@ -0,0 +1,17 @@
#pragma once
#include "pico/stdlib.h"
#include "apa102.hpp"
#include "ws2812.hpp"
namespace plasma {
const uint LED_R = 16;
const uint LED_G = 17;
const uint LED_B = 18;
const uint BUTTON_A = 12;
const uint BUTTON_B = 13;
const uint PIN_CLK = 14; // Used only for APA102
const uint PIN_DAT = 15; // Used for both APA102 and WS2812
}

Wyświetl plik

@ -0,0 +1,113 @@
#include "ws2812.hpp"
namespace plasma {
WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, bool rgbw, COLOR_ORDER color_order, RGB* buffer) : buffer(buffer), num_leds(num_leds), color_order(color_order), pio(pio), sm(sm) {
uint offset = pio_add_program(pio, &ws2812_program);
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); // Discard first (APA102 global brightness) byte. TODO support RGBW WS281X LEDs
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
dma_channel = dma_claim_unused_channel(true);
dma_channel_config config = dma_channel_get_default_config(dma_channel);
channel_config_set_bswap(&config, true);
channel_config_set_dreq(&config, pio_get_dreq(pio, sm, true));
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
channel_config_set_read_increment(&config, true);
dma_channel_set_trans_count(dma_channel, num_leds, false);
dma_channel_set_read_addr(dma_channel, (uint32_t *)buffer, false);
dma_channel_configure(dma_channel, &config, &pio->txf[sm], NULL, 0, false);
if(!this->buffer) {
this->buffer = new RGB[num_leds];
}
}
bool WS2812::dma_timer_callback(struct repeating_timer *t) {
((WS2812*)t->user_data)->update();
return true;
}
void WS2812::update(bool blocking) {
if(dma_channel_is_busy(dma_channel) && !blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
dma_channel_set_trans_count(dma_channel, num_leds, false);
dma_channel_set_read_addr(dma_channel, buffer, true);
if (!blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
}
bool WS2812::start(uint fps) {
add_repeating_timer_ms(-(1000 / fps), dma_timer_callback, (void*)this, &timer);
return true;
}
bool WS2812::stop() {
return cancel_repeating_timer(&timer);
}
void WS2812::clear() {
for (auto i = 0u; i < num_leds; ++i) {
set_rgb(i, 0, 0, 0);
}
}
void WS2812::set_hsv(uint32_t index, float h, float s, float v) {
float i = floor(h * 6.0f);
float f = h * 6.0f - i;
v *= 255.0f;
uint8_t p = v * (1.0f - s);
uint8_t q = v * (1.0f - f * s);
uint8_t t = v * (1.0f - (1.0f - f) * s);
switch (int(i) % 6) {
case 0: set_rgb(index, v, t, p); break;
case 1: set_rgb(index, q, v, p); break;
case 2: set_rgb(index, p, v, t); break;
case 3: set_rgb(index, p, q, v); break;
case 4: set_rgb(index, t, p, v); break;
case 5: set_rgb(index, v, p, q); break;
}
}
void WS2812::set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
switch(color_order) {
case COLOR_ORDER::RGB:
buffer[index].rgb(r, g, b, w);
break;
case COLOR_ORDER::RBG:
buffer[index].rgb(r, b, g, w);
break;
case COLOR_ORDER::GRB:
buffer[index].rgb(g, r, b, w);
break;
case COLOR_ORDER::GBR:
buffer[index].rgb(g, b, r, w);
break;
case COLOR_ORDER::BRG:
buffer[index].rgb(b, r, g, w);
break;
case COLOR_ORDER::BGR:
buffer[index].rgb(b, g, r, w);
break;
}
}
void WS2812::set_brightness(uint8_t b) {
// WS2812 LEDs have no global brightness
}
}

Wyświetl plik

@ -0,0 +1,93 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
This code is significantly modified from the PIO apa102 example
found here: https://github.com/raspberrypi/pico-examples/tree/master/pio/ws2812
*/
#pragma once
#include <math.h>
#include <cstdint>
#include "ws2812.pio.h"
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/clocks.h"
#include "hardware/timer.h"
namespace plasma {
class WS2812 {
public:
static const uint SERIAL_FREQ_400KHZ = 400000;
static const uint SERIAL_FREQ_800KHZ = 800000;
static const uint DEFAULT_SERIAL_FREQ = SERIAL_FREQ_400KHZ;
enum class COLOR_ORDER {
RGB,
RBG,
GRB,
GBR,
BRG,
BGR
};
#pragma pack(push, 1)
union alignas(4) RGB {
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t w = 0b00000000;
} ;
uint32_t srgb;
void operator=(uint32_t v) {
srgb = v;
};
void rgb(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0) {
this->r = r;
this->g = g;
this->b = b;
this->w = w;
};
RGB() : r(0), g(0), b(0), w(0) {};
};
#pragma pack(pop)
RGB *buffer;
uint32_t num_leds;
COLOR_ORDER color_order;
WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq=DEFAULT_SERIAL_FREQ, bool rgbw=false, COLOR_ORDER color_order=COLOR_ORDER::RGB, RGB* buffer=nullptr);
~WS2812() {
stop();
clear();
update(true);
dma_channel_unclaim(dma_channel);
pio_sm_unclaim(pio, sm);
delete[] buffer;
}
bool start(uint fps=60);
bool stop();
void update(bool blocking=false);
void clear();
void set_hsv(uint32_t index, float h, float s, float v);
void set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w=0);
void set_brightness(uint8_t b);
RGB get(uint32_t index) {return buffer[index];};
static bool dma_timer_callback(struct repeating_timer *t);
private:
uint32_t fps;
PIO pio;
uint sm;
int dma_channel;
struct repeating_timer timer;
};
}

Wyświetl plik

@ -0,0 +1,26 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program ws2812
.side_set 1
.define public T1 2
.define public T2 5
.define public T3 3
.lang_opt python sideset_init = pico.PIO.OUT_HIGH
.lang_opt python out_init = pico.PIO.OUT_HIGH
.lang_opt python out_shiftdir = 1
.wrap_target
bitloop:
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
do_one:
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
do_zero:
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
.wrap

Wyświetl plik

@ -0,0 +1,19 @@
import plasma
import time
NUM_LEDS = 30
# WS2812 / NeoPixel™ LEDs
led_strip = plasma.WS2812(NUM_LEDS, 0, 0, 15)
# APA102 / DotStar™ LEDs
# led_strip = plasma.APA102(NUM_LEDS, 0, 0, 15, 14)
# Start updating the LED strip
led_strip.start()
# Make rainbows
while True:
t = time.ticks_ms() / 1000.0 / 5.0
for i in range(NUM_LEDS):
led_strip.set_hsv(i, t + (i / NUM_LEDS))
time.sleep(1.0 / 60)

Wyświetl plik

@ -0,0 +1,21 @@
import time
# Import pin constants from plasma
from plasma import PIN_LED_R, PIN_LED_G, PIN_LED_B, PIN_BUTTON_A, PIN_BUTTON_B
# Import helpers for RGB LEDs and Buttons
from pimoroni import RGBLED, Button
led = RGBLED(PIN_LED_R, PIN_LED_G, PIN_LED_B)
led.set_rgb(255, 0, 0)
button_a = Button(PIN_BUTTON_A)
button_b = Button(PIN_BUTTON_B)
while True:
if button_a.read():
print("Pressed A - {}".format(time.ticks_ms()))
led.set_rgb(0, 255, 0)
if button_b.read():
print("Pressed B - {}".format(time.ticks_ms()))
led.set_rgb(0, 0, 255)

Wyświetl plik

@ -32,4 +32,5 @@ include(pico_unicorn/micropython)
include(pico_display/micropython)
include(pico_explorer/micropython)
include(pico_wireless/micropython)
include(plasma_2040/micropython)
include(ulab/code/micropython)

Wyświetl plik

@ -0,0 +1,205 @@
# Plasma <!-- omit in toc -->
The Plasma library is intended to drive APA102 / DotStar™ or WS2812 / NeoPixel™ LEDs on the Plasma 2040 board, though it can be used with your own custom pins/wiring.
- [Notes On PIO Limitations](#notes-on-pio-limitations)
- [WS2812](#ws2812)
- [Getting Started](#getting-started)
- [RGBW and Setting Colour Order](#rgbw-and-setting-colour-order)
- [Set An LED](#set-an-led)
- [RGB](#rgb)
- [HSV](#hsv)
- [Set Brightness](#set-brightness)
- [APA102](#apa102)
- [Getting Started](#getting-started-1)
- [Set An LED](#set-an-led-1)
- [RGB](#rgb-1)
- [HSV](#hsv-1)
- [Using the Buttons & RGB LED](#using-the-buttons--rgb-led)
- [Buttons](#buttons)
- [RGBLED](#rgbled)
## Notes On PIO Limitations
The WS2812 and APA102 drivers use the PIO hardware on the RP2040. There are only two PIOs with four state machines each, placing a hard limit on how many separate LED strips you can drive.
In most cases you'll use `0` for PIO and `0` for PIO state-machine, but you should change these if you plan on running different strand types together, or if you're using something else that uses PIO.
## WS2812
### Getting Started
Construct a new `WS2812` instance, specifying the number of LEDs, PIO, PIO state-machine and GPIO pin.
```python
import plasma
LEDS = 30
FPS = 60
led_strip = plasma.WS2812(LEDS, 0, 0, 15)
```
Start the LED strip by calling `start`. This sets up a timer which tells the RP2040 to DMA the pixel data into the PIO (a fast, asyncronous memory->peripheral copy) at the specified framerate.
```python
led_strip.start(FPS)
```
### RGBW and Setting Colour Order
Some WS2812-style LED strips have varying colour orders and support an additional white element. Two keyword arguments are supplied to configure this:
```
import plasma
LEDS = 30
FPS = 60
led_strip = plasma.WS2812(LEDS, 0, 0, 15, rgbw=True, color_order=plasma.COLOR_ORDER_GRB)
```
The available orders are defined as constants in `plasma`:
* `COLOR_ORDER_RGB`
* `COLOR_ORDER_RBG`
* `COLOR_ORDER_GRB`
* `COLOR_ORDER_GBR`
* `COLOR_ORDER_BRG`
* `COLOR_ORDER_BGR`
### Set An LED
You can set the colour of an LED in either the RGB colourspace, or HSV (Hue, Saturation, Value). HSV is useful for creating rainbow patterns.
#### RGB
Set the first LED - `0` - to Purple `255, 0, 255`:
```python
led_strip.set_led(0, 255, 0, 255)
```
#### HSV
Set the first LED - `0` - to Red `0.0`:
```python
led_strip.set_hsv(0, 0.0, 1.0, 1.0)
```
### Set Brightness
APA102 pixels support global brightness, allowing their brightness to be specified independent of their colour. You can set the overall brightness of your strip by calling:
```python
led_strip.set_brightness(15)
```
You can set brightness from `0` to `31`. This directly maps to the 5-bit brightness value sent to the APA102 LEDs.
## APA102
### Getting Started
Construct a new `APA102` instance, specifying the number of LEDs, PIO, PIO state-machine and GPIO data/clock pins.
```python
import plasma
LEDS = 30
FPS = 60
led_strip = plasma.APA102(LEDS, 0, 0, 15, 14)
```
Start the LED strip by calling `start`. This sets up a timer which tells the RP2040 to DMA the pixel data into the PIO (a fast, asyncronous memory->peripheral copy) at the specified framerate.
```python
led_strip.start(FPS)
```
### Set An LED
You can set the colour of an LED in either the RGB colourspace, or HSV (Hue, Saturation, Value). HSV is useful for creating rainbow patterns.
#### RGB
Set the first LED - `0` - to Purple `255, 0, 255`:
```python
led_strip.set_led(0, 255, 0, 255)
```
#### HSV
Set the first LED - `0` - to Red `0.0`:
```python
led_strip.set_hsv(0, 0.0, 1.0, 1.0)
```
## Using the Buttons & RGB LED
The `pimoroni` module contains `Button` and `RGBLED` classes to simplify button debounce, auto-repeat and PWM'ing an RGB LED.
```python
Button(button, invert=True, repeat_time=200, hold_time=1000)
```
```python
RGBLED(r, g, b, invert=True)
```
The `plasma` module contains constants for the LED and button pins:
* `plasma.PIN_LED_R` = 16
* `plasma.PIN_LED_G` = 17
* `plasma.PIN_LED_B` = 18
* `plasma.PIN_BUTTON_A` = 12
* `plasma.PIN_BUTTON_B` = 13
### Buttons
Import the `Button` class from the `pimoroni` module and the pin constants for the buttons:
```python
from pimoroni import Button
from plasma import PIN_BUTTON_A, PIN_BUTTON_B
```
Set up an instance of `Button` for each button:
```python
button_a = Button(PIN_BUTTON_A)
button_b = Button(PIN_BUTTON_B)
```
To get the button state, call `.read()`. If the button is held down, then this will return `True` at the interval specified by `repeat_time` until `hold_time` is reached, at which point it will return `True` every `hold_time / 3` milliseconds. This is useful for rapidly increasing/decreasing values such as hue:
```python
state = button_a.read()
```
### RGBLED
Import the `RGBLED` class from `pimoroni` and the pin constants for the buttons:
```python
from pimoroni import RGBLED
from plasma import PIN_LED_R, PIN_LED_G, PIN_LED_B
```
And set up an instance of `RGBLED` for the LED:
```python
led = RGBLED(PIN_LED_R, PIN_LED_G, PIN_LED_B)
```
To set the LED colour, call `.set_rgb(r, g, b)`. Each value should be between 0 and 255:
```python
led.set_rgb(255, 0, 0) # Full red
led.set_rgb(0, 255, 0) # Full green
led.set_rgb(0, 0, 255) # Full blue
```

Wyświetl plik

@ -0,0 +1,23 @@
set(MOD_NAME plasma_2040)
string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER)
add_library(usermod_${MOD_NAME} INTERFACE)
target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/plasma2040/apa102.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/plasma2040/ws2812.cpp
)
pico_generate_pio_header(usermod_${MOD_NAME} ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/plasma2040/apa102.pio)
pico_generate_pio_header(usermod_${MOD_NAME} ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/plasma2040/ws2812.pio)
target_include_directories(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/plasma2040/
)
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
MODULE_PLASMA_ENABLED=1
)
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})

Wyświetl plik

@ -0,0 +1,89 @@
#include "plasma_2040.h"
/***** Methods *****/
MP_DEFINE_CONST_FUN_OBJ_1(PlasmaAPA102___del___obj, PlasmaAPA102___del__);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_set_rgb_obj, 5, PlasmaAPA102_set_rgb);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_set_hsv_obj, 3, PlasmaAPA102_set_hsv);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_set_brightness_obj, 2, PlasmaAPA102_set_brightness);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_start_obj, 1, PlasmaAPA102_start);
MP_DEFINE_CONST_FUN_OBJ_1(PlasmaAPA102_clear_obj, PlasmaAPA102_clear);
MP_DEFINE_CONST_FUN_OBJ_1(PlasmaWS2812___del___obj, PlasmaWS2812___del__);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaWS2812_set_rgb_obj, 5, PlasmaWS2812_set_rgb);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaWS2812_set_hsv_obj, 3, PlasmaWS2812_set_hsv);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaWS2812_start_obj, 1, PlasmaWS2812_start);
MP_DEFINE_CONST_FUN_OBJ_1(PlasmaWS2812_clear_obj, PlasmaWS2812_clear);
/***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t PlasmaAPA102_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&PlasmaAPA102___del___obj) },
{ MP_ROM_QSTR(MP_QSTR_set_rgb), MP_ROM_PTR(&PlasmaAPA102_set_rgb_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_hsv), MP_ROM_PTR(&PlasmaAPA102_set_hsv_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&PlasmaAPA102_set_brightness_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&PlasmaAPA102_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&PlasmaAPA102_clear_obj) },
};
STATIC const mp_rom_map_elem_t PlasmaWS2812_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&PlasmaWS2812___del___obj) },
{ MP_ROM_QSTR(MP_QSTR_set_rgb), MP_ROM_PTR(&PlasmaWS2812_set_rgb_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_hsv), MP_ROM_PTR(&PlasmaWS2812_set_hsv_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&PlasmaWS2812_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&PlasmaWS2812_clear_obj) },
};
STATIC MP_DEFINE_CONST_DICT(PlasmaAPA102_locals_dict, PlasmaAPA102_locals_dict_table);
STATIC MP_DEFINE_CONST_DICT(PlasmaWS2812_locals_dict, PlasmaWS2812_locals_dict_table);
/***** Class Definition *****/
const mp_obj_type_t PlasmaAPA102_type = {
{ &mp_type_type },
.name = MP_QSTR_plasma_apa102,
.print = PlasmaAPA102_print,
.make_new = PlasmaAPA102_make_new,
.locals_dict = (mp_obj_dict_t*)&PlasmaAPA102_locals_dict,
};
const mp_obj_type_t PlasmaWS2812_type = {
{ &mp_type_type },
.name = MP_QSTR_plasma_ws2812,
.print = PlasmaWS2812_print,
.make_new = PlasmaWS2812_make_new,
.locals_dict = (mp_obj_dict_t*)&PlasmaWS2812_locals_dict,
};
typedef struct _mp_obj_float_t {
mp_obj_base_t base;
mp_float_t value;
} mp_obj_float_t;
mp_obj_float_t shunt_resistor = {{&mp_type_float}, 0.015f};
/***** Globals Table *****/
STATIC const mp_map_elem_t plasma_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_plasma) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_APA102), (mp_obj_t)&PlasmaAPA102_type },
{ MP_OBJ_NEW_QSTR(MP_QSTR_WS2812), (mp_obj_t)&PlasmaWS2812_type },
{ MP_ROM_QSTR(MP_QSTR_PIN_LED_R), MP_ROM_INT(16) },
{ MP_ROM_QSTR(MP_QSTR_PIN_LED_G), MP_ROM_INT(17) },
{ MP_ROM_QSTR(MP_QSTR_PIN_LED_B), MP_ROM_INT(18) },
{ MP_ROM_QSTR(MP_QSTR_PIN_BUTTON_A), MP_ROM_INT(12) },
{ MP_ROM_QSTR(MP_QSTR_PIN_BUTTON_B), MP_ROM_INT(13) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_RGB), MP_ROM_INT(0x00) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_RBG), MP_ROM_INT(0x01) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_GRB), MP_ROM_INT(0x02) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_GBR), MP_ROM_INT(0x03) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_BRG), MP_ROM_INT(0x04) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_BGR), MP_ROM_INT(0x05) },
{ MP_ROM_QSTR(MP_QSTR_SHUNT_RESISTOR), MP_ROM_PTR(&shunt_resistor) },
{ MP_ROM_QSTR(MP_QSTR_ADC_GAIN), MP_ROM_INT(50) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_plasma_globals, plasma_globals_table);
/***** Module Definition *****/
const mp_obj_module_t plasma_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_plasma_globals,
};
MP_REGISTER_MODULE(MP_QSTR_plasma, plasma_user_cmodule, MODULE_PLASMA_ENABLED);

Wyświetl plik

@ -0,0 +1,359 @@
#include "libraries/plasma2040/plasma2040.hpp"
#include <cstdio>
#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o))
// SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins.
#define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c))
#define IS_VALID_SDA(i2c, pin) (((pin) & 1) == 0 && (((pin) & 2) >> 1) == (i2c))
using namespace plasma;
extern "C" {
#include "plasma_2040.h"
#include "py/builtin.h"
typedef struct _mp_obj_float_t {
mp_obj_base_t base;
mp_float_t value;
} mp_obj_float_t;
const mp_obj_float_t const_float_1 = {{&mp_type_float}, 1.0f};
/********** WS2812 **********/
/***** Variables Struct *****/
typedef struct _PlasmaWS2812_obj_t {
mp_obj_base_t base;
WS2812* ws2812;
void *buf;
} _PlasmaWS2812_obj_t;
/***** Print *****/
void PlasmaWS2812_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind; //Unused input parameter
_PlasmaWS2812_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PlasmaWS2812_obj_t);
mp_print_str(print, "WS2812(");
mp_print_str(print, "num_leds = ");
mp_obj_print_helper(print, mp_obj_new_int(self->ws2812->num_leds), PRINT_REPR);
mp_print_str(print, ")");
}
/***** Destructor ******/
mp_obj_t PlasmaWS2812___del__(mp_obj_t self_in) {
_PlasmaWS2812_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PlasmaWS2812_obj_t);
delete self->ws2812;
return mp_const_none;
}
/***** Constructor *****/
mp_obj_t PlasmaWS2812_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
_PlasmaWS2812_obj_t *self = nullptr;
enum {
ARG_num_leds,
ARG_pio,
ARG_sm,
ARG_dat,
ARG_freq,
ARG_buffer,
ARG_rgbw,
ARG_color_order
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_num_leds, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_pio, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_sm, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_dat, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = WS2812::DEFAULT_SERIAL_FREQ} },
{ MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = nullptr} },
{ MP_QSTR_rgbw, MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_color_order, MP_ARG_INT, {.u_int = (uint8_t)WS2812::COLOR_ORDER::RGB} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int num_leds = args[ARG_num_leds].u_int;
PIO pio = args[ARG_pio].u_int == 0 ? pio0 : pio1;
int sm = args[ARG_sm].u_int;
int dat = args[ARG_dat].u_int;
int freq = args[ARG_freq].u_int;
bool rgbw = args[ARG_rgbw].u_bool;
WS2812::COLOR_ORDER color_order = (WS2812::COLOR_ORDER)args[ARG_color_order].u_int;
void *buffer = nullptr;
if (args[ARG_buffer].u_obj) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
buffer = bufinfo.buf;
if(bufinfo.len < (size_t)(num_leds * 4)) {
mp_raise_ValueError("Supplied buffer is too small for LED count!");
}
}
self = m_new_obj_with_finaliser(_PlasmaWS2812_obj_t);
self->base.type = &PlasmaWS2812_type;
self->buf = buffer;
self->ws2812 = new WS2812(num_leds, pio, sm, dat, freq, rgbw, color_order, (WS2812::RGB *)buffer);
return MP_OBJ_FROM_PTR(self);
}
mp_obj_t PlasmaWS2812_clear(mp_obj_t self_in) {
_PlasmaWS2812_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PlasmaWS2812_obj_t);
self->ws2812->clear();
return mp_const_none;
}
mp_obj_t PlasmaWS2812_start(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_fps };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_fps, MP_ARG_INT, {.u_int = 60} }
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
_PlasmaWS2812_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaWS2812_obj_t);
self->ws2812->start(args[ARG_fps].u_int);
return mp_const_none;
}
mp_obj_t PlasmaWS2812_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_index, ARG_r, ARG_g, ARG_b };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int index = args[ARG_index].u_int;
int r = args[ARG_r].u_int;
int g = args[ARG_g].u_int;
int b = args[ARG_b].u_int;
_PlasmaWS2812_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaWS2812_obj_t);
self->ws2812->set_rgb(index, r, g, b);
return mp_const_none;
}
mp_obj_t PlasmaWS2812_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_index, ARG_h, ARG_s, ARG_v };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_hue, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_sat, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&const_float_1)} },
{ MP_QSTR_val, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&const_float_1)} }
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int index = args[ARG_index].u_int;
float h = mp_obj_get_float(args[ARG_h].u_obj);
float s = mp_obj_get_float(args[ARG_s].u_obj);
float v = mp_obj_get_float(args[ARG_v].u_obj);
_PlasmaWS2812_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaWS2812_obj_t);
self->ws2812->set_hsv(index, h, s, v);
return mp_const_none;
}
/********** APA102 **********/
/***** Variables Struct *****/
typedef struct _PlasmaAPA102_obj_t {
mp_obj_base_t base;
APA102* apa102;
void *buf;
} _PlasmaAPA102_obj_t;
/***** Print *****/
void PlasmaAPA102_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind; //Unused input parameter
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PlasmaAPA102_obj_t);
mp_print_str(print, "APA102(");
mp_print_str(print, "num_leds = ");
mp_obj_print_helper(print, mp_obj_new_int(self->apa102->num_leds), PRINT_REPR);
mp_print_str(print, ")");
}
/***** Destructor ******/
mp_obj_t PlasmaAPA102___del__(mp_obj_t self_in) {
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PlasmaAPA102_obj_t);
delete self->apa102;
return mp_const_none;
}
/***** Constructor *****/
mp_obj_t PlasmaAPA102_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
_PlasmaAPA102_obj_t *self = nullptr;
enum {
ARG_num_leds,
ARG_pio,
ARG_sm,
ARG_dat,
ARG_clk,
ARG_freq,
ARG_buffer
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_num_leds, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_pio, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_sm, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_dat, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_clk, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = APA102::DEFAULT_SERIAL_FREQ} },
{ MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = nullptr} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int num_leds = args[ARG_num_leds].u_int;
PIO pio = args[ARG_pio].u_int == 0 ? pio0 : pio1;
int sm = args[ARG_sm].u_int;
int dat = args[ARG_dat].u_int;
int clk = args[ARG_clk].u_int;
int freq = args[ARG_freq].u_int;
void *buffer = nullptr;
if (args[ARG_buffer].u_obj) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
buffer = bufinfo.buf;
if(bufinfo.len < (size_t)(num_leds * 4)) {
mp_raise_ValueError("Supplied buffer is too small for LED count!");
}
}
self = m_new_obj_with_finaliser(_PlasmaAPA102_obj_t);
self->base.type = &PlasmaAPA102_type;
self->buf = buffer;
self->apa102 = new APA102(num_leds, pio, sm, dat, clk, freq, (APA102::RGB *)buffer);
return MP_OBJ_FROM_PTR(self);
}
mp_obj_t PlasmaAPA102_clear(mp_obj_t self_in) {
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PlasmaAPA102_obj_t);
self->apa102->clear();
return mp_const_none;
}
mp_obj_t PlasmaAPA102_start(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_fps };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_fps, MP_ARG_INT, {.u_int = 60} }
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaAPA102_obj_t);
self->apa102->start(args[ARG_fps].u_int);
return mp_const_none;
}
mp_obj_t PlasmaAPA102_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_brightness };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_brightness, MP_ARG_REQUIRED | MP_ARG_INT }
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int brightness = args[ARG_brightness].u_int;
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaAPA102_obj_t);
self->apa102->set_brightness(brightness);
return mp_const_none;
}
mp_obj_t PlasmaAPA102_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_index, ARG_r, ARG_g, ARG_b };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int index = args[ARG_index].u_int;
int r = args[ARG_r].u_int;
int g = args[ARG_g].u_int;
int b = args[ARG_b].u_int;
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaAPA102_obj_t);
self->apa102->set_rgb(index, r, g, b);
return mp_const_none;
}
mp_obj_t PlasmaAPA102_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_index, ARG_h, ARG_s, ARG_v };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_hue, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_sat, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&const_float_1)} },
{ MP_QSTR_val, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&const_float_1)} }
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int index = args[ARG_index].u_int;
float h = mp_obj_get_float(args[ARG_h].u_obj);
float s = mp_obj_get_float(args[ARG_s].u_obj);
float v = mp_obj_get_float(args[ARG_v].u_obj);
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaAPA102_obj_t);
self->apa102->set_hsv(index, h, s, v);
return mp_const_none;
}
}

Wyświetl plik

@ -0,0 +1,26 @@
// Include MicroPython API.
#include "py/runtime.h"
/***** Extern of Class Definition *****/
extern const mp_obj_type_t PlasmaAPA102_type;
extern const mp_obj_type_t PlasmaWS2812_type;
/***** Extern of Class Methods *****/
extern void PlasmaAPA102_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
extern mp_obj_t PlasmaAPA102_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
extern mp_obj_t PlasmaAPA102___del__(mp_obj_t self_in);
extern mp_obj_t PlasmaAPA102_start(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_clear(mp_obj_t self_in);
extern void PlasmaWS2812_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
extern mp_obj_t PlasmaWS2812_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
extern mp_obj_t PlasmaWS2812___del__(mp_obj_t self_in);
extern mp_obj_t PlasmaWS2812_start(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaWS2812_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaWS2812_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaWS2812_clear(mp_obj_t self_in);
extern bool Pimoroni_mp_obj_to_i2c(mp_obj_t in, void *out);

Wyświetl plik

@ -1,5 +1,21 @@
import time
from machine import Pin, PWM
from machine import Pin, PWM, ADC
class Analog:
def __init__(self, pin, amplifier_gain=1, resistor=0):
self.gain = amplifier_gain
self.resistor = resistor
self.pin = ADC(pin)
def read_voltage(self):
return self.pin.read_u16() * 3.3 / 65535 / self.gain
def read_current(self):
if self.resistor > 0:
return self.read_voltage() / self.resistor
else:
return self.read_voltage()
class Button: