File dotstar.h¶
Go to the documentation of this file
Source Code¶
#pragma once
#ifndef DSY_DOTSTAR_H
#define DSY_DOTSTAR_H
#include "per/i2c.h"
#include "per/spi.h"
namespace daisy
{
class DotStarSpiTransport
{
public:
struct Config
{
SpiHandle::Config::Peripheral periph;
SpiHandle::Config::BaudPrescaler baud_prescaler;
Pin clk_pin;
Pin data_pin;
void Defaults()
{
periph = SpiHandle::Config::Peripheral::SPI_1;
baud_prescaler = SpiHandle::Config::BaudPrescaler::PS_4;
clk_pin = Pin(PORTG, 11);
data_pin = Pin(PORTB, 5);
};
};
inline void Init(Config &config)
{
SpiHandle::Config spi_cfg;
spi_cfg.periph = config.periph;
spi_cfg.mode = SpiHandle::Config::Mode::MASTER;
spi_cfg.direction = SpiHandle::Config::Direction::TWO_LINES_TX_ONLY;
spi_cfg.clock_polarity = SpiHandle::Config::ClockPolarity::LOW;
spi_cfg.clock_phase = SpiHandle::Config::ClockPhase::ONE_EDGE;
spi_cfg.datasize = 8;
spi_cfg.nss = SpiHandle::Config::NSS::SOFT;
spi_cfg.baud_prescaler = config.baud_prescaler;
spi_cfg.pin_config.sclk = config.clk_pin;
spi_cfg.pin_config.mosi = config.data_pin;
spi_cfg.pin_config.miso = Pin();
spi_cfg.pin_config.nss = Pin();
spi_.Init(spi_cfg);
};
bool Write(uint8_t *data, size_t size)
{
return spi_.BlockingTransmit(data, size) == SpiHandle::Result::OK;
};
private:
SpiHandle spi_;
};
template <typename Transport>
class DotStar
{
public:
enum class Result
{
OK,
ERR_INVALID_ARGUMENT,
ERR_TRANSPORT
};
struct Config
{
enum ColorOrder : uint8_t
{
// R G B
RGB = ((0 << 4) | (1 << 2) | (2)),
RBG = ((0 << 4) | (2 << 2) | (1)),
GRB = ((1 << 4) | (0 << 2) | (2)),
GBR = ((2 << 4) | (0 << 2) | (1)),
BRG = ((1 << 4) | (2 << 2) | (0)),
BGR = ((2 << 4) | (1 << 2) | (0)),
};
typename Transport::Config
transport_config;
ColorOrder color_order;
uint16_t num_pixels;
void Defaults()
{
transport_config.Defaults();
color_order = ColorOrder::RGB;
num_pixels = 1;
};
};
DotStar(){};
~DotStar(){};
Result Init(Config &config)
{
if(config.num_pixels > kMaxNumPixels)
{
return Result::ERR_INVALID_ARGUMENT;
}
transport_.Init(config.transport_config);
num_pixels_ = config.num_pixels;
// first color byte is always global brightness (hence +1 offset)
r_offset_ = ((config.color_order >> 4) & 0b11) + 1;
g_offset_ = ((config.color_order >> 2) & 0b11) + 1;
b_offset_ = (config.color_order & 0b11) + 1;
// Minimum brightness by default. These LEDs can
// be very current hungry. See datasheet for details.
SetAllGlobalBrightness(1);
Clear();
return Result::OK;
};
void SetAllGlobalBrightness(uint16_t b)
{
for(uint16_t i = 0; i < num_pixels_; i++)
{
SetPixelGlobalBrightness(i, b);
}
};
Result SetPixelGlobalBrightness(uint16_t idx, uint16_t b)
{
if(idx >= num_pixels_)
{
return Result::ERR_INVALID_ARGUMENT;
}
uint8_t *pixel = (uint8_t *)(&pixels_[idx]);
pixel[0] = 0xE0 | std::min(b, (uint16_t)31);
return Result::OK;
};
uint16_t GetPixelColor(uint16_t idx)
{
if(idx >= num_pixels_)
return 0;
uint32_t c = 0;
const uint8_t *pixel = (uint8_t *)&pixels_[idx];
c = c | (pixel[r_offset_] << 16);
c = c | (pixel[g_offset_] << 8);
c = c | pixel[b_offset_];
return c;
}
void SetPixelColor(uint16_t idx, const Color &color)
{
SetPixelColor(idx, color.Red8(), color.Green8(), color.Blue8());
}
void SetPixelColor(uint16_t idx, uint32_t color)
{
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
SetPixelColor(idx, r, g, b);
}
Result SetPixelColor(uint16_t idx, uint8_t r, uint8_t g, uint8_t b)
{
if(idx >= num_pixels_)
{
return Result::ERR_INVALID_ARGUMENT;
}
uint8_t *pixel = (uint8_t *)(&pixels_[idx]);
pixel[r_offset_] = r;
pixel[b_offset_] = b;
pixel[g_offset_] = g;
return Result::OK;
};
void Fill(const Color &color)
{
for(uint16_t i = 0; i < num_pixels_; i++)
{
SetPixelColor(i, color);
}
}
void Fill(uint32_t color)
{
for(uint16_t i = 0; i < num_pixels_; i++)
{
SetPixelColor(i, color);
}
}
void Fill(uint8_t r, uint8_t g, uint8_t b)
{
for(uint16_t i = 0; i < num_pixels_; i++)
{
SetPixelColor(i, r, g, b);
}
}
void Clear()
{
for(uint16_t i = 0; i < num_pixels_; i++)
{
SetPixelColor(i, 0);
}
};
Result Show()
{
uint8_t sf[4] = {0x00, 0x00, 0x00, 0x00};
uint8_t ef[4] = {0xFF, 0xFF, 0xFF, 0xFF};
if(!transport_.Write(sf, 4))
{
return Result::ERR_TRANSPORT;
}
for(uint16_t i = 0; i < num_pixels_; i++)
{
if(!transport_.Write((uint8_t *)&pixels_[i], 4))
{
return Result::ERR_TRANSPORT;
}
}
if(!transport_.Write(ef, 4))
{
return Result::ERR_TRANSPORT;
}
return Result::OK;
};
private:
static const size_t kMaxNumPixels = 64;
Transport transport_;
uint16_t num_pixels_;
uint32_t pixels_[kMaxNumPixels];
uint8_t r_offset_, g_offset_, b_offset_;
};
using DotStarSpi = DotStar<DotStarSpiTransport>;
} // namespace daisy
#endif