275 lines
9.2 KiB
C++
275 lines
9.2 KiB
C++
|
#include "bhorealMini.h"
|
||
|
|
||
|
// DEFINITION PINS FOR MINI
|
||
|
|
||
|
#define PIN_LED 8 // ??????
|
||
|
int NUM_LEDS = 16;
|
||
|
const uint16_t numBytes = 48;
|
||
|
byte row[4] = { // ROW pins for matrix pushbottons
|
||
|
13, 5, 10, 9};
|
||
|
byte column[4] = { // COL pins for matrix pushbottons
|
||
|
8, 6, 12, 4};
|
||
|
|
||
|
BhorealMini Bhoreal_;
|
||
|
uint8_t pixels[numBytes];
|
||
|
|
||
|
// boolean pressed[4][4] = { // pushbottons states matrix
|
||
|
// {1,1,1,1},
|
||
|
// {1,1,1,1},
|
||
|
// {1,1,1,1},
|
||
|
// {1,1,1,1}
|
||
|
// };
|
||
|
|
||
|
boolean pressed[8][8] = { // pushbottons states matrix
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1}
|
||
|
};
|
||
|
|
||
|
const byte remap[4][4] = // mapping matrix for Mini
|
||
|
{
|
||
|
{ 3, 4, 11, 12 },
|
||
|
{ 2, 5, 10, 13 },
|
||
|
{ 1, 6, 9, 14 },
|
||
|
{ 0, 7, 8, 15 }
|
||
|
};
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
////////////////////// BHOREAL BEGIN //////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void BhorealMini::begin()
|
||
|
{
|
||
|
port = portOutputRegister(digitalPinToPort(PIN_LED));
|
||
|
pinMask = digitalPinToBitMask(PIN_LED);
|
||
|
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
////////////////////// STARTUP //////////////////////
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Run this animation once at startup. Currently unfinished.
|
||
|
void BhorealMini::startup(){
|
||
|
|
||
|
for(int x = 0; x < NUM_LEDS; ++x){
|
||
|
uint32_t c = hue2rgb((x+1)*8); // 128 HUE steps / 16 leds, 8 steps x led
|
||
|
if (x == (NUM_LEDS-1)) c = hue2rgb(1); // 128 HUE steps / 64 leds, 2 steps x led
|
||
|
uint8_t
|
||
|
r = (uint8_t)(c >> 16),
|
||
|
g = (uint8_t)(c >> 8),
|
||
|
b = (uint8_t)c;
|
||
|
setPixelColor(remap[x>>2][x%4], r, g, b);
|
||
|
}
|
||
|
show();
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
////////////////////// MIDI PRESS & RELEASE //////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void BhorealMini::on_press(byte r, byte c){
|
||
|
}
|
||
|
|
||
|
void BhorealMini::on_release(byte r, byte c){
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////
|
||
|
////////////////////// CHECK BUTTONS //////////////////////
|
||
|
///////////////////////////////////////////////////////////////
|
||
|
|
||
|
byte count_column = 0;
|
||
|
byte count_file = 0;
|
||
|
unsigned long time_button = 0;
|
||
|
|
||
|
void BhorealMini::checkButtons(){
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
////////////////////// REFRESH LED //////////////////////
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
byte refresh_led = 0;
|
||
|
unsigned long time_led = 0;
|
||
|
void BhorealMini::displayRefresh(){
|
||
|
if (((refresh_led>0)&&((micros()-time_led)>1000))||(refresh_led>=NUM_LEDS)||((refresh_led<NUM_LEDS)&&((micros()-time_led)>25000))&&(refresh_led>0))
|
||
|
{
|
||
|
refresh_led=0;
|
||
|
show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
////////////////////// REFRESH MIDI & LED /////////////////////
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void BhorealMini::midiRefresh(){
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
////////////////////// CHECK ADC INPUTS //////////////////////
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void BhorealMini::checkADC(){
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////
|
||
|
////////////////////// HUE -> RGB //////////////////////
|
||
|
///////////////////////////////////////////////////////////////
|
||
|
|
||
|
uint8_t rh;
|
||
|
uint8_t gh;
|
||
|
uint8_t bh;
|
||
|
|
||
|
uint32_t BhorealMini::hue2rgb(uint16_t hueValue)
|
||
|
{
|
||
|
if (hueValue==0)
|
||
|
{
|
||
|
rh = 0;
|
||
|
gh = 0;
|
||
|
bh = 0;
|
||
|
}
|
||
|
else if (hueValue>=127)
|
||
|
{
|
||
|
rh = 255;
|
||
|
gh = 255;
|
||
|
bh = 255;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hueValue<<= 3; // 128 midi steps -> 1024 hue steps
|
||
|
|
||
|
if (hueValue < 341) { // Lowest third of the potentiometer's range (0-340)
|
||
|
hueValue = (hueValue * 3) / 4; // Normalize to 0-255
|
||
|
|
||
|
rh = 255 - hueValue; // Red from full to off
|
||
|
gh = hueValue; // Green from off to full
|
||
|
bh = 1; // Blue off
|
||
|
}
|
||
|
else if (hueValue < 682) { // Middle third of potentiometer's range (341-681)
|
||
|
hueValue = ( (hueValue-341) * 3) / 4; // Normalize to 0-255
|
||
|
|
||
|
rh = 1; // Red off
|
||
|
gh = 255 - hueValue; // Green from full to off
|
||
|
bh = hueValue; // Blue from off to full
|
||
|
}
|
||
|
else { // Upper third of potentiometer"s range (682-1023)
|
||
|
hueValue = ( (hueValue-683) * 3) / 4; // Normalize to 0-255
|
||
|
|
||
|
rh = hueValue; // Red from off to full
|
||
|
gh = 1; // Green off
|
||
|
bh = 255 - hueValue; // Blue from full to off
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ((uint32_t)rh << 16) | ((uint32_t)gh << 8) | bh;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////
|
||
|
////////////////////// Control led //////////////////////
|
||
|
///////////////////////////////////////////////////////////////
|
||
|
|
||
|
void BhorealMini::show(void) {
|
||
|
|
||
|
if(!pixels) return;
|
||
|
|
||
|
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||
|
// put a delay at the end of the function, the ending time is noted and
|
||
|
// the function will simply hold off (if needed) on issuing the
|
||
|
// subsequent round of data until the latch time has elapsed. This
|
||
|
// allows the mainline code to start generating the next frame of data
|
||
|
// rather than stalling for the latch.
|
||
|
while((micros() - endTime) < 50L);
|
||
|
// endTime is a private member (rather than global var) so that mutliple
|
||
|
// instances on different pins can be quickly issued in succession (each
|
||
|
// instance doesn't delay the next).
|
||
|
|
||
|
// In order to make this code runtime-configurable to work with any pin,
|
||
|
// SBI/CBI instructions are eschewed in favor of full PORT writes via the
|
||
|
// OUT or ST instructions. It relies on two facts: that peripheral
|
||
|
// functions (such as PWM) take precedence on output pins, so our PORT-
|
||
|
// wide writes won't interfere, and that interrupts are globally disabled
|
||
|
// while data is being issued to the LEDs, so no other code will be
|
||
|
// accessing the PORT. The code takes an initial 'snapshot' of the PORT
|
||
|
// state, computes 'pin high' and 'pin low' values, and writes these back
|
||
|
// to the PORT register as needed.
|
||
|
|
||
|
noInterrupts(); // Need 100% focus on instruction timing
|
||
|
|
||
|
volatile uint16_t i = numBytes; // Loop counter
|
||
|
volatile uint8_t *ptr = pixels; // Pointer to next byte
|
||
|
volatile uint8_t b = *ptr++; // Current byte value
|
||
|
volatile uint8_t hi; // PORT w/output bit set high
|
||
|
volatile uint8_t lo; // PORT w/output bit set low
|
||
|
|
||
|
// WS2811 and WS2812 have different hi/lo duty cycles; this is
|
||
|
// similar but NOT an exact copy of the prior 400-on-8 code.
|
||
|
|
||
|
// 20 inst. clocks per bit: HHHHHxxxxxxxxLLLLLLL
|
||
|
// ST instructions: ^ ^ ^ (T=0,5,13)
|
||
|
|
||
|
volatile uint8_t next, bit;
|
||
|
|
||
|
hi = *port | pinMask;
|
||
|
lo = *port & ~pinMask;
|
||
|
next = lo;
|
||
|
bit = 8;
|
||
|
|
||
|
asm volatile(
|
||
|
"head20:" "\n\t" // Clk Pseudocode (T = 0)
|
||
|
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
|
||
|
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128)
|
||
|
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
|
||
|
"dec %[bit]" "\n\t" // 1 bit-- (T = 5)
|
||
|
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7)
|
||
|
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8)
|
||
|
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above)
|
||
|
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
|
||
|
"rjmp .+0" "\n\t" // 2 nop nop (T = 12)
|
||
|
"nop" "\n\t" // 1 nop (T = 13)
|
||
|
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
|
||
|
"nop" "\n\t" // 1 nop (T = 16)
|
||
|
"rjmp .+0" "\n\t" // 2 nop nop (T = 18)
|
||
|
"rjmp head20" "\n\t" // 2 -> head20 (next bit out)
|
||
|
"nextbyte20:" "\n\t" // (T = 10)
|
||
|
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11)
|
||
|
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13)
|
||
|
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
|
||
|
"nop" "\n\t" // 1 nop (T = 16)
|
||
|
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
|
||
|
"brne head20" "\n" // 2 if(i != 0) -> (next byte)
|
||
|
: [port] "+e" (port),
|
||
|
[byte] "+r" (b),
|
||
|
[bit] "+r" (bit),
|
||
|
[next] "+r" (next),
|
||
|
[count] "+w" (i)
|
||
|
: [ptr] "e" (ptr),
|
||
|
[hi] "r" (hi),
|
||
|
[lo] "r" (lo));
|
||
|
|
||
|
interrupts();
|
||
|
endTime = micros(); // Save EOD time for latch on next call
|
||
|
}
|
||
|
|
||
|
// Set pixel color from separate R,G,B components:
|
||
|
void BhorealMini::setPixelColor(
|
||
|
uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
|
||
|
if(n < NUM_LEDS) {
|
||
|
uint8_t *p = &pixels[n * 3];
|
||
|
*p++ = g;
|
||
|
*p++ = r;
|
||
|
*p = b;
|
||
|
}
|
||
|
}
|