|
#include "stm8s.h" |
|
#include <stdbool.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
#define NUM_LEDS 30 |
|
#define BRIGHTNESS 150 |
|
|
|
void _delay_us(uint16_t microseconds) { |
|
TIM4->PSCR = TIM4_PRESCALER_1; // Set prescaler |
|
|
|
// Set count to approximately 1uS (clock/microseconds in 1 second) |
|
// The -1 adjusts for other runtime costs of this function |
|
TIM4->ARR = ((16000000L)/1000000) - 1; |
|
|
|
TIM4->CR1 = TIM4_CR1_CEN; // Enable counter |
|
|
|
for (; microseconds > 1; --microseconds) { |
|
while ((TIM4->SR1 & TIM4_SR1_UIF) == 0); |
|
|
|
// Clear overflow flag |
|
TIM4->SR1 &= ~TIM4_SR1_UIF; |
|
} |
|
} |
|
|
|
void ws_write_byte(uint8_t byte) |
|
{ |
|
for (uint8_t mask = 0x80; mask != 0; mask >>= 1) { |
|
if ((byte & mask) != 0) { |
|
// Set pin high |
|
__asm__("bset 20495, #4"); |
|
|
|
// Delay for 850ns minus overheads |
|
nop(); nop(); nop(); nop(); |
|
nop(); nop(); nop(); nop(); |
|
|
|
// Set pin low |
|
__asm__("bres 20495, #4"); |
|
|
|
// Delay 400ns minus overhead |
|
nop(); nop(); nop(); nop(); |
|
|
|
} else { |
|
// Set pin high |
|
__asm__("bset 20495, #4"); |
|
|
|
// Delay for 400ns minus overheads |
|
nop(); nop(); nop(); nop(); |
|
nop(); nop(); |
|
|
|
// Set pin low |
|
__asm__("bres 20495, #4"); |
|
|
|
// Delay for 850ns minus overheads |
|
nop(); nop(); nop(); nop(); |
|
|
|
} |
|
} |
|
} |
|
|
|
void ws_write_grb(uint8_t* colour) |
|
{ |
|
for (uint8_t i = 0; i < 3; ++i) { |
|
ws_write_byte(colour[i]); |
|
} |
|
} |
|
|
|
inline volatile void ws_reset() |
|
{ |
|
GPIOD->ODR &= ~(1<<4); |
|
|
|
// Pull the line low for >50uS to reset |
|
_delay_us(55); |
|
} |
|
|
|
void applyCieBrightness(uint8_t* colour) |
|
{ |
|
// CIE1931 luminance correction table |
|
// Generated from code here: http://jared.geek.nz/2013/feb/linear-led-pwm |
|
static const unsigned char CIE_CORRECTION[256] = { |
|
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, |
|
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, |
|
2, 2, 2, 3, 3, 3, 3, 3, 3, 3, |
|
3, 4, 4, 4, 4, 4, 4, 5, 5, 5, |
|
5, 5, 6, 6, 6, 6, 6, 7, 7, 7, |
|
7, 8, 8, 8, 8, 9, 9, 9, 10, 10, |
|
10, 10, 11, 11, 11, 12, 12, 12, 13, 13, |
|
13, 14, 14, 15, 15, 15, 16, 16, 17, 17, |
|
17, 18, 18, 19, 19, 20, 20, 21, 21, 22, |
|
22, 23, 23, 24, 24, 25, 25, 26, 26, 27, |
|
28, 28, 29, 29, 30, 31, 31, 32, 32, 33, |
|
34, 34, 35, 36, 37, 37, 38, 39, 39, 40, |
|
41, 42, 43, 43, 44, 45, 46, 47, 47, 48, |
|
49, 50, 51, 52, 53, 54, 54, 55, 56, 57, |
|
58, 59, 60, 61, 62, 63, 64, 65, 66, 67, |
|
68, 70, 71, 72, 73, 74, 75, 76, 77, 79, |
|
80, 81, 82, 83, 85, 86, 87, 88, 90, 91, |
|
92, 94, 95, 96, 98, 99, 100, 102, 103, 105, |
|
106, 108, 109, 110, 112, 113, 115, 116, 118, 120, |
|
121, 123, 124, 126, 128, 129, 131, 132, 134, 136, |
|
138, 139, 141, 143, 145, 146, 148, 150, 152, 154, |
|
155, 157, 159, 161, 163, 165, 167, 169, 171, 173, |
|
175, 177, 179, 181, 183, 185, 187, 189, 191, 193, |
|
196, 198, 200, 202, 204, 207, 209, 211, 214, 216, |
|
218, 220, 223, 225, 228, 230, 232, 235, 237, 240, |
|
242, 245, 247, 250, 252, 255, |
|
}; |
|
|
|
colour[0] = CIE_CORRECTION[colour[0]]; |
|
colour[1] = CIE_CORRECTION[colour[1]]; |
|
colour[2] = CIE_CORRECTION[colour[2]]; |
|
} |
|
|
|
uint8_t fade_to(uint8_t *current, uint8_t *target) |
|
{ |
|
uint8_t changed = 0; |
|
|
|
for (uint8_t i = 0; i < 3; i++) { |
|
if (current[i] != target[i]) { |
|
changed++; |
|
|
|
if (current[i] < target[i]) { |
|
current[i]++; |
|
} else { |
|
current[i]--; |
|
} |
|
} |
|
} |
|
|
|
return changed; |
|
} |
|
|
|
static uint8_t lights[NUM_LEDS][3]; |
|
static uint8_t lights_index = 0; |
|
static uint8_t lights_direction = 1; |
|
static uint8_t light_ticks = 0; |
|
|
|
// Actual value of the LEDs |
|
static uint8_t colour[] = {50, 0, 0}; |
|
|
|
// Value the LEDs need to fade to |
|
static uint8_t target[] = {50, 0, 0}; |
|
|
|
// Bit mask of which colours should be fully lit (lower 3 bits) |
|
// This uses binary addition to get all permutations of RGB cheaply |
|
static uint8_t colourMask = 0; |
|
|
|
void timer2_overflow_interrupt(void) __interrupt (ITC_IRQ_TIM2_OVF) |
|
{ |
|
// Step towards the target colour |
|
// If we're at the target, set the next target |
|
if (!fade_to(colour, target)) { |
|
|
|
// Step to next permutation of RGB |
|
colourMask = (colourMask + 1) % 8; |
|
|
|
// Never fade to black as that's boring visually |
|
if (colourMask == 0) { |
|
colourMask = 1; |
|
} |
|
|
|
// Set target colour based on bitmask |
|
memset(target, 0, sizeof(target)); |
|
|
|
if (colourMask & (1<<0)) { |
|
target[0] = BRIGHTNESS; |
|
} |
|
if (colourMask & (1<<1)) { |
|
target[1] = BRIGHTNESS; |
|
} |
|
if (colourMask & (1<<2)) { |
|
target[2] = BRIGHTNESS; |
|
} |
|
|
|
applyCieBrightness(colour); |
|
} |
|
|
|
// Write to LED strip |
|
ws_reset(); |
|
for (uint8_t i = 0; i < NUM_LEDS; ++i) { |
|
ws_write_grb(colour); |
|
} |
|
|
|
// Clear the timer overflow so the interrupt doesn't fire again |
|
TIM2->SR1 &= ~TIM2_SR1_UIF; |
|
} |
|
|
|
int main(void) |
|
{ |
|
|
|
// Configure the clock for maximum speed on the 16MHz HSI oscillator |
|
// At startup the clock output is divided by 8 |
|
CLK->CKDIVR = 0x0; |
|
|
|
// Pin D4 in fast push-pull mode |
|
// This starts high as the light looks for low signals |
|
GPIOD->DDR |= (1<<4); |
|
GPIOD->CR1 |= (1<<4); |
|
GPIOD->CR2 |= (1<<4); |
|
GPIOD->ODR &= ~(1<<4); |
|
|
|
// B5 (LED) in fast push-pull mode |
|
GPIOB->DDR |= (1<<5); |
|
GPIOB->CR1 |= (1<<5); |
|
GPIOB->ODR |= (1<<5); |
|
GPIOB->ODR &= ~(1<<5); |
|
|
|
// Configure timer 2 (16-bit general purpose) |
|
TIM2->PSCR = TIM2_PRESCALER_256; // Set prescaler |
|
TIM2->ARRH = 0x30; // Set auto-reload value for 200ms |
|
TIM2->ARRL = 0xD4; |
|
// TIM2->ARRH = 0x02; // Set auto-reload value for 40ms |
|
// TIM2->ARRL = 0xC4; |
|
TIM2->IER = TIM2_IER_UIE; // Enable interrupt |
|
TIM2->CR1 = TIM2_CR1_CEN; // Enable counter |
|
|
|
// Initialise lights array |
|
memset(lights, 0, sizeof(lights)); |
|
|
|
enableInterrupts(); |
|
|
|
for (;;) { |
|
} |
|
} |