new lib files added + more reliable power button ahndling

master
Ondřej Hruška 7 years ago
parent 2204211cb4
commit 130e7fd781
  1. 7
      CMakeLists.txt
  2. 5
      Makefile
  3. 103
      lib/color.c
  4. 57
      lib/color.h
  5. 49
      lib/debounce.c
  6. 84
      lib/debounce.h
  7. 131
      lib/wsrgb.c
  8. 46
      lib/wsrgb.h
  9. 86
      main.c

@ -7,16 +7,23 @@ set(CMAKE_CXX_STANDARD GNU99)
set(SOURCE_FILES
main.c
debo_config.h
lib/calc.h
lib/iopins.c
lib/iopins.h
lib/adc.c
lib/adc.h
lib/debounce.c
lib/debounce.h
lib/nsdelay.h
lib/spi.c
lib/spi.h
lib/usart.c
lib/usart.h
lib/color.c
lib/color.h
lib/wsrgb.c
lib/wsrgb.h
pinout.h)
include_directories(lib

@ -30,6 +30,9 @@ OBJS += lib/usart.o
OBJS += lib/iopins.o
OBJS += lib/spi.o
OBJS += lib/adc.o
OBJS += lib/debounce.o
OBJS += lib/wsrgb.o
OBJS += lib/color.o
# Dirs with header files
INCL_DIRS = . lib/
@ -44,7 +47,7 @@ CFLAGS = -std=gnu99 -mmcu=$(MCU) $(DEFS) $(INCL_DIRS:%=-I%)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -ffreestanding
CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment
CFLAGS += -g2 -Wextra -Wfatal-errors -Wno-unused-but-set-variable
CFLAGS += -ffunction-sections -fdata-sections -Os
CFLAGS += -ffunction-sections -fdata-sections -Os -Wno-unused-parameter
LDFLAGS = -Wl,--gc-sections -Wl,--relax -lm

@ -0,0 +1,103 @@
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include "iopins.h"
#include "nsdelay.h"
#include "color.h"
// --- HSL ---
#ifdef HSL_LINEAR
const uint8_t FADE_128[] =
{
0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4,
5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 10, 10, 10, 11, 12, 13, 14,
14, 15, 16, 17, 18, 20, 21, 22, 24, 26, 27, 28, 30, 31, 32, 34, 35, 36,
38, 39, 40, 41, 42, 44, 45, 46, 48, 49, 50, 52, 54, 56, 58, 59, 61, 63,
65, 67, 68, 69, 71, 72, 74, 76, 78, 80, 82, 85, 88, 90, 92, 95, 98, 100,
103, 106, 109, 112, 116, 119, 122, 125, 129, 134, 138, 142, 147, 151,
153, 156, 160, 163, 165, 170, 175, 180, 185, 190, 195, 200, 207, 214, 218,
221, 225, 228, 232, 234, 241, 248, 254, 255
};
#endif
// based on: https://github.com/lewisd32/avr-hsl2rgb
xrgb_t hsl_xrgb(const hsl_t cc)
{
// 0 .. 256*3
const uint16_t hh = (uint16_t) cc.h * 3;
const uint8_t hue_mod = hh % 256;
uint8_t r_temp, g_temp, b_temp;
if (hh < 256)
{
r_temp = hue_mod ^ 255;
g_temp = hue_mod;
b_temp = 0;
}
else if (hh < 512)
{
r_temp = 0;
g_temp = hue_mod ^ 255;
b_temp = hue_mod;
}
else if (hh < 768)
{
r_temp = hue_mod;
g_temp = 0;
b_temp = hue_mod ^ 255;
}
else
{
r_temp = 0;
g_temp = 0;
b_temp = 0;
}
const uint8_t inverse_sat = (cc.s ^ 255);
xrgb_t rgb;
uint8_t t8;
uint16_t t16;
#ifdef HSL_LINEAR
const uint8_t bri = FADE_128[cc.l >> 1];
#else
const uint8_t bri = cc.l;
#endif
t8 = r_temp;
t16 = t8 * cc.s + t8;
t16 = t16 + t8;
t8 = t16 >> 8;
t8 = t8 + inverse_sat;
t16 = t8 * bri;
t16 = t16 + t8;
t8 = t16 >> 8;
rgb.r = t8;
t8 = g_temp;
t16 = t8 * cc.s;
t16 = t16 + t8;
t8 = t16 >> 8;
t8 = t8 + inverse_sat;
t16 = t8 * bri;
t16 = t16 + t8;
t8 = t16 >> 8;
rgb.g = t8;
t8 = b_temp;
t16 = t8 * cc.s;
t16 = t16 + t8;
t8 = t16 >> 8;
t8 = t8 + inverse_sat;
t16 = t8 * bri;
t16 = t16 + t8;
t8 = t16 >> 8;
rgb.b = t8;
return rgb;
}

@ -0,0 +1,57 @@
#pragma once
// --- color types ---
//
// The XXXc macros don't use cast, so they can be used in array initializers.
//
// xrgb ... 3-byte true-color RGB (8 bits per component)
// rgb24 ... 24-bit color value, with equal nr of bits per component
//
// XX_r (_g, _b) ... extract component from the color, and convert it to 0..255
// Define HSL_LINEAR to get more linear brightness in hsl->rgb conversion
typedef struct
{
uint8_t r;
uint8_t g;
uint8_t b;
} xrgb_t;
typedef uint32_t rgb24_t;
#define xrgb(rr, gg, bb) ((xrgb_t)xrgbc(rr, gg, bb))
// xrgb for constant array declarations
#define xrgbc(rr, gg, bb) { .r = ((uint8_t)(rr)), .g = ((uint8_t)(gg)), .b = ((uint8_t)(bb)) }
#define xrgb_r(c) ((uint8_t)(c.r))
#define xrgb_g(c) ((uint8_t)(c.g))
#define xrgb_b(c) ((uint8_t)(c.b))
#define xrgb_rgb24(c) ((((rgb24_t)c.r) << 16) | (((rgb24_t)c.g) << 8) | (((rgb24_t)c.b)))
#define xrgb_rgb15(c) (((((rgb15_t)c.r) & 0xF8) << 7) | ((((rgb15_t)c.g) & 0xF8) << 2) | ((((rgb15_t)c.b) & 0xF8) >> 3))
#define xrgb_rgb12(c) (((((rgb12_t)c.r) & 0xF0) << 4) | ((((rgb12_t)c.g) & 0xF0)) | ((((rgb12_t)c.b) & 0xF0) >> 4))
#define xrgb_rgb6(c) (((((rgb6_t)c.r) & 0xC0) >> 2) | ((((rgb6_t)c.g) & 0xC0) >> 4) | ((((rgb6_t)c.b) & 0xC0) >> 6))
#define rgb24c(r,g,b) (((((rgb24_t)r) & 0xFF) << 16) | ((((rgb24_t)g) & 0xFF) << 8) | (((rgb24_t)b) & 0xFF))
#define rgb24(r,g,b) ((rgb24_t) rgb24(r,g,b))
#define rgb24_r(c) ((((rgb24_t) (c)) >> 16) & 0xFF)
#define rgb24_g(c) ((((rgb24_t) (c)) >> 8) & 0xFF)
#define rgb24_b(c) ((((rgb24_t) (c)) >> 0) & 0xFF)
#define rgb24_xrgb(c) xrgb(rgb24_r(c), rgb24_g(c), rgb24_b(c))
#define rgb24_xrgbc(c) xrgbc(rgb24_r(c), rgb24_g(c), rgb24_b(c))
#define add_xrgb(x, y) ((xrgb_t) { (((y).r > (255 - (x).r)) ? 255 : ((x).r + (y).r)), (((y).g > (255 - (x).g)) ? 255 : ((x).g + (y).g)), (((y).b > 255 - (x).b) ? 255 : ((x).b + (y).b)) })
// HSL data structure
typedef struct
{
uint8_t h;
uint8_t s;
uint8_t l;
} hsl_t;
/* Convert HSL to XRGB */
xrgb_t hsl_xrgb(const hsl_t color);

@ -0,0 +1,49 @@
#include <avr/io.h>
#include <stdbool.h>
#include "debounce.h"
#include "calc.h"
#include "iopins.h"
/** Debounce data array */
uint8_t debo_next_slot = 0;
/** bit - range 0-63 */
uint8_t debo_register(PORT_P reg, uint8_t bit, bool invert, DebouncerCallback callback)
{
debo_slots[debo_next_slot] = (debo_slot_t) {
.reg = reg,
.bit = bit | ((invert & 1) << 7) | (get_bit_p(reg, bit) << 6), // bit 7 = invert, bit 6 = state
.count = 0,
.callback = callback,
};
return debo_next_slot++;
}
/** Check debounced pins, should be called periodically. */
void debo_tick(void)
{
for (uint8_t i = 0; i < debo_next_slot; i++) {
// current pin value
bool value = get_bit_p(debo_slots[i].reg, debo_slots[i].bit & 0x3F)
^get_bit(debo_slots[i].bit, 7);
if (value != get_bit(debo_slots[i].bit, 6)) {
// different pin state than last recorded state
if (debo_slots[i].count < DEBO_TICKS) {
debo_slots[i].count++;
} else {
// overflown -> latch value
set_bit(debo_slots[i].bit, 6, value); // set state bit
debo_slots[i].count = 0;
// Fire the callback, if not NULL
if (debo_slots[i].callback) {
debo_slots[i].callback(i, value);
}
}
} else {
debo_slots[i].count = 0; // reset the counter
}
}
}

@ -0,0 +1,84 @@
#ifndef DEBOUNCE_H
#define DEBOUNCE_H
//
// An implementation of button debouncer.
//
// ----
//
// You must provide a config file debo_config.h (next to your main.c)
//
// A pin is registered like this:
//
// #define BTN1 12 // pin D12
// #define BTN2 13
//
// debo_add(BTN0); // The function returns number assigned to the pin (0, 1, ...)
// debo_add_rev(BTN1); // active low
// debo_register(&PINB, PB2, 0); // direct access - register, pin & invert
//
// Then periodically call the tick function (perhaps in a timer interrupt):
//
// debo_tick();
//
// To check if input is active, use
//
// debo_get_pin(0); // state of input #0 (registered first)
// debo_get_pin(1); // state of input #1 (registered second)
//
#include <avr/io.h>
#include <stdbool.h>
#include <stdint.h>
#include "calc.h"
#include "iopins.h"
// Update as needed
#define DEBO_CHANNELS 5
#define DEBO_TICKS 50 // ms
/** Debouncer callback; state - state after applying inverse bit */
typedef void(*DebouncerCallback)(uint8_t n, bool state);
/* Internal debouncer entry */
typedef struct
{
PORT_P reg; // pointer to IO register
uint8_t bit; // bits 6 and 7 of this hold "state" & "invert" flag
uint8_t count; // number of ticks this was in the new state
DebouncerCallback callback;
} debo_slot_t;
debo_slot_t debo_slots[DEBO_CHANNELS];
/**
* Add a pin for debouncing (low level function)
*
* @param pin_reg_pointer - PINx pointer
* @param bit - number of the debounced bit in the register (0-7)
* @param invert - invert the value when reading (for buttons to GND, for example)
*/
uint8_t debo_register(PORT_P pin_reg_pointer, uint8_t bit, bool invert, DebouncerCallback cbk);
/**
* Add a pin for debouncing (must be used with constant args).
* Returns index in debouncer, used for callbacks.
*
* Arg: pin number, defined in iopins.h
*/
#define debo_add(pin, callback) debo_register(&_pin(pin), _pn(pin), false, callback)
#define debo_add_rev(pin, callback) debo_register(&_pin(pin), _pn(pin), true, callback)
/**
* Check debounced pins, should be called periodically.
* Updates internal states and fires callbacks.
*/
void debo_tick(void);
/**
* Get a value of debounced pin (after debounce - latched state)
*/
#define debo_get_pin(i) (get_bit(debo_slots[i].bit, 6) ^ get_bit(debo_slots[i].bit, 7))
#endif

@ -0,0 +1,131 @@
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include "iopins.h"
#include "nsdelay.h"
#include "wsrgb.h"
#include "color.h"
/* Driver code for WS2812B */
void ws_init()
{
as_output(WS_PIN);
}
/** Wait long enough for the colors to show */
void ws_show()
{
delay_ns_c(WS_T_LATCH, 0);
}
/** Send one byte to the RGB strip */
void ws_send_byte(const uint8_t bb)
{
for (volatile int8_t i = 7; i >= 0; --i) {
if ((bb) & (1 << i)) {
pin_up(WS_PIN);
delay_ns_c(WS_T_1H, -2);
pin_down(WS_PIN);
delay_ns_c(WS_T_1L, -10);
} else {
pin_up(WS_PIN);
delay_ns_c(WS_T_0H, -2);
pin_down(WS_PIN);
delay_ns_c(WS_T_0L, -10);
}
}
}
/** Send R,G,B color to the strip */
void ws_send_rgb(const uint8_t r, const uint8_t g, const uint8_t b)
{
ws_send_byte(g);
ws_send_byte(r);
ws_send_byte(b);
}
/** Send a RGB struct */
void ws_send_xrgb(xrgb_t xrgb)
{
ws_send_byte(xrgb.g);
ws_send_byte(xrgb.r);
ws_send_byte(xrgb.b);
}
/** Send color hex */
void ws_send_rgb24(rgb24_t rgb)
{
ws_send_byte(rgb24_g(rgb));
ws_send_byte(rgb24_r(rgb));
ws_send_byte(rgb24_b(rgb));
}
/** Send array of colors */
void ws_send_xrgb_array(const xrgb_t rgbs[], const uint8_t length)
{
for (uint8_t i = 0; i < length; i++) {
const xrgb_t c = rgbs[i];
ws_send_byte(c.g);
ws_send_byte(c.r);
ws_send_byte(c.b);
}
}
/** Send array of colors */
void ws_send_rgb24_array(const rgb24_t rgbs[], const uint8_t length)
{
for (uint8_t i = 0; i < length; i++) {
const rgb24_t c = rgbs[i];
ws_send_byte(rgb24_g(c));
ws_send_byte(rgb24_r(c));
ws_send_byte(rgb24_b(c));
}
}
//#define ws_send_rgb24_array(rgbs, length) __ws_send_array_proto((rgbs), (length), rgb24)
// prototype for sending array. it's ugly, sorry.
/*#define __ws_send_array_proto(rgbs, length, style) { \
for (uint8_t __rgb_sap_i = 0; __rgb_sap_i < length; __rgb_sap_i++) { \
style ## _t __rgb_sap2 = (rgbs)[__rgb_sap_i]; \
ws_send_ ## style(__rgb_sap2); \
} \
}*/
// /** Send a 2D array to a zig-zag display */
// #define ws_send_xrgb_array_zigzag(rgbs, width, height) { \
// int8_t __rgb_sxaz_y, __rgb_sxaz_x; \
// for(__rgb_sxaz_y = 0; __rgb_sxaz_y < (height); __rgb_sxaz_y ++) { \
// for(__rgb_sxaz_x = 0; __rgb_sxaz_x < (width); __rgb_sxaz_x++) { \
// ws_send_xrgb((rgbs)[__rgb_sxaz_y][__rgb_sxaz_x]); \
// } \
// __rgb_sxaz_y++; \
// for(__rgb_sxaz_x = (width) - 1; __rgb_sxaz_x >= 0; __rgb_sxaz_x--) { \
// ws_send_xrgb((rgbs)[__rgb_sxaz_y][__rgb_sxaz_x]); \
// } \
// } \
// }
// /* Send a linear array to a zig-zag display as a n*m board (row-by-row)
// #define ws_send_xrgb_array_zigzag_linear(rgbs, width, height) { \
// int8_t __rgb_sxazl_x, __rgb_sxazl_y; \
// for(__rgb_sxazl_y = 0; __rgb_sxazl_y < (height); __rgb_sxazl_y++) { \
// for(__rgb_sxazl_x = 0; __rgb_sxazl_x < (width); __rgb_sxazl_x++) { \
// ws_send_xrgb((rgbs)[__rgb_sxazl_y * (width) + __rgb_sxazl_x]); \
// } \
// __rgb_sxazl_y++; \
// for(__rgb_sxazl_x = width-1; __rgb_sxazl_x >=0; __rgb_sxazl_x--) { \
// ws_send_xrgb((rgbs)[__rgb_sxazl_y * (width) + __rgb_sxazl_x]); \
// } \
// } \
// }

@ -0,0 +1,46 @@
#pragma once
//
// Utils for driving a WS2812 RGB LED strips, and color manipulation in general.
//
// Timing is critical!
//
#include <stdint.h>
#include "iopins.h"
#include "color.h"
// Config
#define WS_T_1H 700
#define WS_T_1L 150
#define WS_T_0H 150
#define WS_T_0L 700
#define WS_T_LATCH 7000
#define WS_PIN 2
// --- functions for RGB strips ---
/** Initialize OI */
void ws_init();
/** Wait long enough for the colors to show */
void ws_show();
/** Send one byte to the RGB strip */
void ws_send_byte(const uint8_t bb);
/** Send R,G,B color to the strip */
void ws_send_rgb(const uint8_t r, const uint8_t g, const uint8_t b);
/** Send a RGB struct */
void ws_send_xrgb(xrgb_t xrgb);
/** Send color hex */
void ws_send_rgb24(rgb24_t rgb);
/** Send array of colors */
void ws_send_xrgb_array(const xrgb_t rgbs[], const uint8_t length);
/** Send array of colors */
void ws_send_rgb24_array(const rgb24_t rgbs[], const uint8_t length);

@ -11,6 +11,7 @@
#include "lib/usart.h"
#include "lib/spi.h"
#include "lib/adc.h"
#include "lib/debounce.h"
#include "pinout.h"
@ -30,8 +31,8 @@ void setup_io(void)
as_input(PIN_KEY_4);
as_output(PIN_NEOPIXEL);
as_output(PIN_NEOPIXEL_PWRN);
pin_up(PIN_NEOPIXEL_PWRN); // turn neopixels OFF
pin_up(PIN_NEOPIXEL_PWRN); // turn neopixels OFF - it's a PMOS
as_output(PIN_NEOPIXEL_PWRN); // configure DDR for output (pull-up becomes hard up)
as_input(PIN_PWR_KEY);
as_output(PIN_PWR_HOLD);
@ -61,13 +62,86 @@ ISR(TIMER2_OVF_vect)
{
// convert in background
if (adc_ready()) {
disp_brightness = 255 - adc_read_8bit();
disp_brightness = 255 - adc_read_8bit(); // inverse
adc_start_conversion(LIGHT_ADC_CHANNEL);
}
OCR2B = disp_brightness;
}
// --- Debouncer slot allocation constants ---
volatile bool booting = true;
volatile uint16_t time_ms = 0;
// (normally those would be retvals from debo_add())
#define DB_KEY_POWER 0
#define DB_KEY_1 1
#define DB_KEY_2 2
#define DB_KEY_3 3
#define DB_KEY_4 4
volatile uint16_t time_pwr_pressed = 0;
/** Power button state changed */
void key_cb_power(uint8_t num, bool state)
{
if (state) {
time_pwr_pressed = time_ms;
} else {
if (booting) {
// Ignore this one - user still holding BTN after power ON
usart_puts("Power button released, leaving boot mode.\r\n");
booting = false;
return;
}
}
}
/** Button state changed */
void key_cb_button(uint8_t num, bool state)
{
// TODO
// num - 1,2,3,4
usart_puts("BTN ");
usart_tx('0'+num);
usart_tx(' ');
usart_tx('0'+state);
usart_puts("\r\n");
}
void setup_debouncer(void)
{
// Debouncer config
debo_add(PIN_PWR_KEY, key_cb_power);
debo_add(PIN_KEY_1, key_cb_button);
debo_add(PIN_KEY_2, key_cb_button);
debo_add(PIN_KEY_3, key_cb_button);
debo_add(PIN_KEY_4, key_cb_button);
// Timer 1 - CTC, to 16000 (1 ms interrupt)
OCR1A = 16000;
TIMSK1 |= _BV(OCIE1A);
TCCR1B |= _BV(WGM12) | _BV(CS10);
}
ISR(TIMER1_COMPA_vect)
{
// Tick 1 ms
debo_tick();
time_ms++;
// Shut down by just holding the button - better feedback for user
if (debo_get_pin(DB_KEY_POWER)
&& !booting
&& (time_ms - time_pwr_pressed > 1000)) {
usart_puts("Power OFF\r\n");
// shut down
pin_down(PIN_PWR_HOLD);
}
}
/**
* Main function
*/
@ -84,8 +158,6 @@ void main()
pin_up(D13); // the on-board LED (also SPI clk) - indication for the user
// Stay on - hold the EN pin high
pin_up(PIN_PWR_HOLD);
// Wait for user to release the power key (no debounce needed here)
while (pin_is_high(PIN_PWR_KEY));
// SPI conf
// TODO verify the cpha and cpol. those seem to work, but it's a guess
@ -93,6 +165,8 @@ void main()
adc_init(ADC_PRESC_128);
setup_pwm();
setup_debouncer();
// Turn neopixels power ON - voltage will have stabilized by now
// and no glitches should occur
pin_down(PIN_NEOPIXEL_PWRN);
@ -112,7 +186,7 @@ void main()
_delay_ms(100);
sprintf(buf, "%d\n", disp_brightness);
sprintf(buf, "BRT = %d\r\n", disp_brightness);
usart_puts(buf);
}
}

Loading…
Cancel
Save