main firmware: WIP

very rough WIP of main badge code.

some LED programs are here, and it will build if the btn code is excluded.
This commit is contained in:
true
2023-08-03 18:48:16 -07:00
parent 46a3ab6007
commit 7bbd46fe5f
94 changed files with 40861 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
/*
* adxl.c
*
* Created on: Jul 18, 2023
* Author: true
*/
#include "adxl.h"
#include "i8atan2.h"
#include <string.h>
#include <stdlib.h>
adxl345_axes adxl;
adxl345_axes adxl_last[4];
adxl345_axes adxl_smoothing;
uint8_t buf[7];
uint8_t last_idx = 0;
int16_t movement = 0;
uint16_t movement_worst = 0;
static const uint8_t adxl_dfmt = ADXL345_DATA_FORMAT_RANGE_2G; // | ADXL345_DATA_FORMAT_FULL_RES;
void adxl345_init()
{
buf[0] = ADXL345_REG_BW_RATE | ADXL345_MODE_WR | ADXL345_MODE_MB;
buf[1] = ADXL345_BW_RATE_200; // | ADXL345_BW_RATE_LOW_POWER; // BW_RATE
buf[2] = ADXL345_POWER_CTL_MEASURE; // POWER_CTL
buf[3] = 0; // INT_ENABLE
buf[4] = 0xff; // INT_MAP
adxl_spi_xfer(0, buf, 5);
buf[0] = ADXL345_REG_DATA_FORMAT | ADXL345_MODE_WR;
buf[1] = adxl_dfmt;
adxl_spi_xfer(0, buf, 2);
}
void adxl345_tick()
{
adxl_last[last_idx].x = adxl.x;
adxl_last[last_idx].y = adxl.y;
adxl_last[last_idx++].z = adxl.z;
last_idx &= 0x3;
adxl345_get_axes(&adxl_smoothing);
adxl.x += adxl_smoothing.x; adxl.x >>= 1;
adxl.y += adxl_smoothing.y; adxl.y >>= 1;
adxl.z += adxl_smoothing.z; adxl.z >>= 1;
if (!last_idx) {
movement = 0;
for (uint8_t i = 0; i < 4; i++) {
movement += (adxl_last[i].x - adxl.x) + (adxl_last[i].y - adxl.y) + (adxl_last[i].z - adxl.z);
}
if (abs(movement) > movement_worst) {
movement_worst = abs(movement);
}
}
}
void adxl345_get_axes(struct adxl345_axes *adxl)
{
buf[0] = ADXL345_REG_DATAX0 | ADXL345_MODE_RD | ADXL345_MODE_MB;
memset(&buf[1], 0x00, 6);
adxl_spi_xfer(buf, buf, 7);
adxl->x = buf[1] | (buf[2] << 8);
adxl->y = buf[3] | (buf[4] << 8);
adxl->z = buf[5] | (buf[6] << 8);
}
int8_t adxl345_get_rotation(struct adxl345_axes *adxl)
{
int8_t nx, ny, ret;
nx = -adxl->x;
ny = adxl->y;
ret = i8atan2(nx, ny) >> 1;
if (ret < 0) {
ret += 128;
}
return ret;
}
int16_t adxl345_movement()
{
return movement;
}
void adxl345_set_reg8(uint8_t reg, uint8_t val)
{
buf[0] = reg | ADXL345_MODE_WR;
buf[1] = val;
adxl_spi_xfer(0, buf, 2);
}
void adxl345_set_intr(uint8_t int2_map, uint8_t interrupt_flags)
{
buf[0] = ADXL345_REG_INT_ENABLE | ADXL345_MODE_WR | ADXL345_MODE_MB;
buf[1] = interrupt_flags; // set interrupts which are enabled
buf[2] = int2_map; // which interrupts map to INT2
adxl_spi_xfer(0, buf, 3);
}
void adxl345_set_intr_polarity(uint8_t active_polarity)
{
buf[0] = ADXL345_REG_DATA_FORMAT | ADXL345_MODE_WR;
buf[1] = adxl_dfmt | (active_polarity ? 0 : ADXL345_DFMT_INT_INVERT);
adxl_spi_xfer(buf, buf, 2);
}
uint8_t adxl345_get_intr_flag()
{
buf[0] = ADXL345_REG_INT_SOURCE | ADXL345_MODE_RD;
buf[1] = 0x00;
adxl_spi_xfer(buf, buf, 2);
return buf[1];
}

View File

@@ -0,0 +1,108 @@
/*
* btn.c
*
* Created on: Aug 3, 2023
* Author: true
*/
static uint8_t mode = BTN_MODE_IDLE;
#include "btn.h"
Btn_t btn[BTN_COUNT] = {0};
void btn_init()
{
}
static void btn_check(uint8_t idx, uint8_t state)
{
if (!state) { // buttons active low
if (btn[idx].held < BTN_MAX_HOLD) {
btn[idx].held++;
}
} else {
btn[idx].held = 0;
}
}
void btn_tick()
{
uint8_t i;
// increment hold
btn_check(0, BTN1);
btn_check(1, BTN2);
btn_check(2, BTN3);
// process
for (i = 0; i < BTN_COUNT; i++) {
// test for hold / release, can only happen after push
if ((btn[i].state & BTN_PUSH) || (btn[i].state & BTN_HELD)) {
// release first
if (btn[i].held == 0) {
// yes, released
btn[i].state = BTN_RELEASE;
// do we trigger hold?
} else if (btn[i].hold_time && (btn[i].held >> BTN_HOLD_SHIFT) == btn[i].hold_time) {
// yes, held, but only set if we didn't already
if (!(btn[i].state & BTN_HELD)) {
btn[i].state = BTN_HELD;
btn[i].hold_rt_ctr = btn[i].hold_retrig;
}
}
} else {
// push
if (btn[i].held == BTN_DEBOUNCE) {
btn[i].state = BTN_PUSH;
}
}
}
}
void btn_callback()
{
uint8_t i;
uint8_t s;
for (i = 0; i < BTN_COUNT; i++) {
s = btn[i].state;
if ((s & BTN_PUSH) && !(s & BTN_PUSH_CB)) {
btn[i].state |= BTN_PUSH_CB;
if (btn[i].push_cb) {
btn[i].push_cb();
}
}
if (btn[i].hold_retrig) {
if (btn[i].hold_rt_ctr) {
btn[i].hold_rt_ctr--;
} else {
btn[i].hold_rt_ctr = btn[i].hold_retrig;
btn[i].state &= ~(BTN_HELD_CB);
}
}
if ((s & BTN_HELD) && !(s & BTN_HELD_CB)) {
btn[i].state |= BTN_HELD_CB;
if (btn[i].held_cb) {
btn[i].held_cb();
}
}
if ((s & BTN_RELEASE) && !(s & BTN_RELEASE_CB)) {
btn[i].state |= BTN_RELEASE_CB;
if (btn[i].release_cb) {
btn[i].release_cb();
}
}
}
}

View File

@@ -0,0 +1,141 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file hk32f030m_it.c
* @brief Interrupt Service Routines.
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "../../code/Inc/hk32f030m_it.h"
#include "hk32f030m_tim.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M0 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVC_IRQn 0 */
/* USER CODE END SVC_IRQn 0 */
/* USER CODE BEGIN SVC_IRQn 1 */
/* USER CODE END SVC_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* hk32f030m Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_hk32f030m.s). */
/******************************************************************************/
/* USER CODE BEGIN 1 */
//void TIM2_IRQHandler(void)
//{
// if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
// {
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
//}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT HKMicroChip *****END OF FILE****/

View File

@@ -0,0 +1,94 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 B. Stultiens
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hsv2rgb.h"
void hsv2rgb_8b(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b)
{
uint8_t sextant;
uint8_t bb;
uint16_t ww;
uint8_t h_fraction;
if (!(s)) {
*(r) = *(g) = *(b) = (v);
return;
}
sextant = h >> 8;
HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space
HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in
*g = v; // Top level
// Perform actual calculations
/*
* Bottom level: v * (1.0 - s)
* --> (v * (255 - s) + error_corr) / 256
*/
bb = ~s;
ww = v * bb;
ww += 1; // Error correction
ww += ww >> 8; // Error correction
*b = ww >> 8;
h_fraction = h & 0xff; // 0...255
if(!(sextant & 1)) {
// *r = ...slope_up...;
/*
* Slope up: v * (1.0 - s * (1.0 - h))
* --> (v * (255 - (s * (256 - h) + error_corr1) / 256) + error_corr2) / 256
*/
ww = !h_fraction ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_fraction));
ww += ww >> 8; // Error correction 1
bb = ww >> 8;
bb = ~bb;
ww = v * bb;
ww += v >> 1; // Error correction 2
*r = ww >> 8;
} else {
// *r = ...slope_down...;
/*
* Slope down: v * (1.0 - s * h)
* --> (v * (255 - (s * h + error_corr1) / 256) + error_corr2) / 256
*/
ww = s * h_fraction;
ww += ww >> 8; // Error correction 1
bb = ww >> 8;
bb = ~bb;
ww = v * bb;
ww += v >> 1; // Error correction 2
*r = ww >> 8;
/*
* A perfect match for h_fraction == 0 implies:
* *r = (ww >> 8) + (h_fraction ? 0 : 1)
* However, this is an extra calculation that may not be required.
*/
}
}

View File

@@ -0,0 +1,51 @@
/*
* i8atan2.c
*
* Created on: Jul 18, 2023
* Author: true
*/
#include <stdint.h>
static int8_t iat2(int8_t y, int8_t x)
{
return ((y * 32 + (x / 2)) / x) * 2;
}
int8_t i8atan2(int8_t y, int8_t x)
{
// determine octant
if (y >= 0) { // oct 0,1,2,3
if (x >= 0) { // oct 0,1
if (x > y) {
return iat2(-y, -x) / 2 + (0 * 32);
} else {
if (y == 0) return 0; // (x=0,y=0)
return -iat2(-x, -y) / 2 + (2 * 32);
}
} else { // oct 2,3
if (x >= -y) {
return iat2(x, -y) / 2 + (2 * 32);
} else {
return -iat2(-y, x) / 2 + (4 * 32);
}
}
} else { // oct 4,5,6,7
if (x < 0) { // oct 4,5
if (x < y) {
return iat2(y, x) / 2 + (-4 * 32);
} else {
return -iat2(x, y) / 2 + (-2 * 32);
}
} else { // oct 6,7
if (-x >= y) {
return iat2(-x, y) / 2 + (-2 * 32);
} else {
return -iat2(y, -x) / 2 + (-0 * 32);
}
}
}
}

View File

@@ -0,0 +1,296 @@
/*
* led_prog.c
*
* Created on: Aug 3, 2023
* Author: true
*
*/
#include <stdint.h>
#include "hsv2rgb.h"
#include "led_sk6x_spi.h"
#include "prng.h"
void ledprog0_twinkle_white();
void ledprog1_twinkle_rgb_rand();
void ledprog2_twinkle_rgb_set();
void ledprog3_rainbow();
void ledprog4_flicker_same();
void ledprog5_flicker_all();
void ledprog6_ramper();
static uint8_t settings[16][4];
const void (*led_prog_list[])() = {
ledprog0_twinkle_white,
ledprog1_twinkle_rgb_rand,
ledprog2_twinkle_rgb_set,
ledprog3_rainbow,
ledprog4_flicker_same,
ledprog5_flicker_all,
ledprog6_ramper
};
void (*ledprog)();
static uint32_t work[4];
static uint8_t r, g, b;
void ledprog_default()
{
settings[3][2] = 2;
settings[3][3] = 30;
settings[4][1] = 248;
settings[4][2] = 170;
settings[4][3] = 16;
settings[6][1] = 192;
settings[6][2] = 30;
settings[6][3] = 6;
}
void ledprog_change(uint8_t idx)
{
uint8_t i;
ledprog = led_prog_list[idx];
for (i = 0; i < 4; i++) {
work[i] = 0;
}
}
void ledprogi_twinkle(uint8_t r, uint8_t g, uint8_t b)
{
uint8_t x;
x = prng_get8();
led_sk6x_set_all(0, 0, 0);
if (x > 240) {
x = prng_get32() & 0x7;
led_sk6x_set(x, r, g, b);
}
}
/*
* settings:
* 0 dwell
* 1 threshold
*/
void ledprog0_twinkle_white()
{
ledprogi_twinkle(0xff, 0xff, 0xff);
}
/*
* settings:
* 0 dwell
* 1 threshold
*/
void ledprog1_twinkle_rgb_rand()
{
uint32_t x;
x = prng_get32();
// set values
r = (x >> 24);
g = (x >> 16) & 0xff;
b = (x >> 8) & 0xff;
// randomly reduce colors to remove "whiteness"
switch (x & 0x3) {
case 1: {
r >>= 4;
g >>= 4;
break;
}
case 2: {
g >>= 4;
b >>= 4;
break;
}
case 3: {
b >>= 4;
r >>= 4;
break;
}
}
ledprogi_twinkle(r, g, b);
}
/*
* settings:
* 0 dwell
* 1 threshold
* 2 hue
* 3 val
*/
void ledprog2_twinkle_rgb_set()
{
hsv2rgb_8b(settings[2][2] * 6, 255, settings[2][3], &r, &g, &b);
ledprogi_twinkle(r, g, b);
}
/*
* settings:
* 0 dwell
* 1 threshold
* 2 speed
* 3 skip
*
* work:
* 0 base hue
*/
void ledprog3_rainbow()
{
uint8_t i;
uint16_t w;
work[0] += (settings[3][2] * 6) + 1;
work[0] %= 1536;
w = work[0];
for (i = 0; i < 8; i++) {
hsv2rgb_8b(w, 255, 255, &r, &g, &b);
led_sk6x_set(i, r, g, b);
w += (settings[3][3] * 6) + 1;
w %= 1536;
}
}
void ledprogi_flicker()
{
uint16_t h;
uint8_t x;
h = settings[4][2] * 6;
x = prng_get8();
if (x > settings[4][1]) {
x = 255;
} else {
x = settings[4][3];
}
x &= ~0x07;
x |= prng_get32() & 0x07;
hsv2rgb_8b(h, 255, x, &r, &g, &b);
}
/*
* settings:
* 0 dwell
* 1 threshold
* 2 hue
* 3 base
*/
void ledprog4_flicker_same()
{
ledprogi_flicker();
led_sk6x_set_all(r, g, b);
}
/*
* settings (uses ledprog4)
* 0 dwell
* 1 threshold
* 2 hue
* 3 val
*/
void ledprog5_flicker_all()
{
uint8_t i;
for (i = 0; i < 8; i++) {
ledprogi_flicker();
led_sk6x_set(i, r, g, b);
}
}
/*
* settings
* 0 dwell
* 1 speed
* 2 hue
* 3 faderate
*
* work:
* 0 ramper state
*/
void ledprog6_ramper()
{
uint8_t i;
uint16_t h;
int16_t spd;
uint16_t floor = 0;
uint8_t v[8];
uint8_t fr;
h = settings[6][2] * 6;
fr = settings[6][3] & 0x3f;
// speed -64 to -1, +1 to +64
spd = (settings[6][1] >> 1) - 64;
if (spd >= 0) spd++;
work[0] += spd;
work[0] &= 0xfff;
// unpack fade values
for (i = 0; i < 4; i++) {
v[0 + i] = work[2] >> (i * 8);
v[4 + i] = work[3] >> (i * 8);
}
for (i = 0; i < 8; i++) {
// set to max if within range
if (work[0] >= floor && work[0] <= floor + 64) {
v[i] = 255;
}
// set LED
hsv2rgb_8b(h, 255, v[i], &r, &g, &b);
led_sk6x_set(i, r, g, b);
// set fadeout value
if (fr > v[i]) {
v[i] = 0;
} else {
v[i] -= fr;
}
// both sides of top LEDs match
if (i == 5) {
i++;
led_sk6x_set(i, r, g, b);
}
// work up the chain
floor += 0x180;
}
// pack fade values
work[2] = work[3] = 0;
for (i = 0; i < 4; i++) {
work[2] |= v[0 + i] << (i * 8);
work[3] |= v[4 + i] << (i * 8);
}
}

View File

@@ -0,0 +1,146 @@
/*
* led_sk6x_spi.c
*
* Created on: Jun 19, 2023
* Author: true
*
* communicates with SK68xx LEDs using the SPI peripheral.
*
* this code operates the SPI at 4MHz and uses 5 bits to represent
* each on-wire bit, resulting in 800KHz data rate.
*
* originally,
* for the working buffer, each 5-bit word is stored in an 8-bit byte.
* so, one byte per on-wire bit will be used. for RGB LEDs, 24 bits
* per LED are required, so working memory use will be 24*LED_COUNT bytes.
* the spi peripheral will only transfer the lower 5 bits of each byte.
*
* however,
* this MCU supports 5-bit SPI mode, but has no DMA and isn't fast enough
* to maintain continuous transfer at this rate. instead of trying to make
* this work, we'll instead waste time and memory with a separate output
* buffer. data will be generated for the output buffer from a separate
* LEDvalue buffer by bit manipulating values into it.
*
* another note:
* this would likely work perfectly fine with the weird delays between bits.
* I was focused on making a very nice looking output waveform. I may test
* and address this after con to reduce battery life, but considering the
* draw of the LEDs and any addons, a worst case extra 1mA or so isn't shit.
*
*/
#include <string.h>
#include "led_sk6x_spi.h"
#include "spi.h"
#include "adxl.h"
#define SK6X_FILL(COLOR) \
for (uint8_t mask = 0x80; mask; mask >>= 1) { \
if (COLOR & mask) { \
*p++ = SK6X_LO; \
} else { \
*p++ = SK6X_HI; \
} \
}
const uint8_t sk6x_map[8] = {0, 1, 2, 3, 4, 7, 6, 5};
uint8_t sk6x_led[SK6X_LED_MAX_COUNT][3]; // G-R-B order
uint8_t sk6x_buf[SK6X_BUF_SIZE];
uint8_t sk6x_brightness = 3;
void led_sk6x_cb();
void led_sk6x_init()
{
// configure ADXL to enable power
adxl345_set_intr_polarity(ADXL345_INTR_ACTIVE_LO);
// clear led buffers
memset(sk6x_led, 0, sizeof(sk6x_led));
memset(sk6x_buf, 0, sizeof(sk6x_buf));
}
__attribute__ ((long_call, section(".ramfunc"))) void led_sk6x_process()
{
uint8_t i, j;
uint16_t n = 0;
uint8_t b[8];
j = 0;
// pack RGB values into 5-bits-per-bit format
for (i = 0; i < SK6X_LED_MAX_COUNT; i++) {
for (j = 0; j < 3; j++) {
// pack values for next color
uint8_t *p = b;
SK6X_FILL(sk6x_led[sk6x_map[i]][j] >> sk6x_brightness);
// pack 8 bits into 5 bytes in final buffer
sk6x_buf[n + 0] = (b[0] << 3) | (b[1] >> 2);
sk6x_buf[n + 1] = (b[1] << 6) | (b[2] << 1) | (b[3] >> 4);
sk6x_buf[n + 2] = (b[3] << 4) | (b[4] >> 1);
sk6x_buf[n + 3] = (b[4] << 7) | (b[5] << 2) | (b[6] >> 3);
sk6x_buf[n + 4] = (b[6] << 5) | (b[7]);
n += 5;
}
}
}
__attribute__ ((long_call, section(".ramfunc"))) void led_sk6x_update()
{
// configure MOSI pin and bit depth
spi_mosi_sel(SPI_MOSI_LED);
// begin sending data
spi_xfer(sk6x_buf, 0, sizeof(sk6x_buf), SPI_NO_CALLBACK);
}
__attribute__ ((long_call, section(".ramfunc"))) void led_sk6x_set(uint8_t index, uint8_t r, uint8_t g, uint8_t b)
{
/*
uint8_t *p = &sk6x_buf[24 * index];
SK6X_FILL(g);
SK6X_FILL(r);
SK6X_FILL(b);
*/
sk6x_led[index][0] = g;
sk6x_led[index][1] = r;
sk6x_led[index][2] = b;
}
__attribute__ ((long_call, section(".ramfunc"))) void led_sk6x_set_all(uint8_t r, uint8_t g, uint8_t b)
{
/*
uint8_t *p = sk6x_buf;
for (uint8_t i = 0; i < SK6X_LED_MAX_COUNT; i++) {
SK6X_FILL(g);
SK6X_FILL(r);
SK6X_FILL(b);
}
*/
uint8_t i;
for (i = 0; i < SK6X_LED_MAX_COUNT; i++) {
led_sk6x_set(i, r, g, b);
}
}
void led_sk6x_cb()
{
// there is nothing to handle after updating LEDs
}

View File

@@ -0,0 +1,8 @@
/*
* led_user.c
*
* Created on: Aug 3, 2023
* Author: true
*/

View File

@@ -0,0 +1,280 @@
/*
* main.c.c
*
* Created on: Jun 19, 2023
* Author: true
*
* HK32F030MF4P6 MCU running at 32MHz
* (wanted to run slower, but want perfect waveform using SPI for LED)
*
*/
#include "hk32f030m.h"
#include "adxl.h"
#include "btn.h"
#include "led_prog.h"
#include "led_sk6x_spi.h"
#include "prng.h"
#include "spi.h"
#include "timer.h"
#define GPIO_RCC_AHB_GPIO_ALL RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | \
RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD
static uint32_t uptime = 0; // uptime in seconds
volatile uint16_t cnt = 0; // 1/1000 second tick
static uint16_t cnt2 = 0; // low priority loop check
static void gpio_init()
{
GPIO_InitTypeDef gpio;
// enable clocks
RCC_AHBPeriphClockCmd(GPIO_RCC_AHB_GPIO_ALL, ENABLE);
// I2C AF config
GPIOB->AFR[0] = 0x00010000; // _, _, _, USART RX, _, _, _, _
GPIOC->AFR[0] = 0x20000000; // SPI MISO, I2C SCL, I2C SDA, _, _, _, _, _
GPIOD->AFR[0] = 0x00022210; // _, _, _, LED MOSI, SPI SCK, SPI MOSI, USART TX, _
/*
GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_0); // I2C SDA
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_0); // I2C SCL
// UART AF config
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_1); // USART RX
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_1); // USART TX
// SPI AF config
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_2); // SPI MISO
GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_2); // SPI MOSI
GPIO_PinAFConfig(GPIOD, GPIO_PinSource3, GPIO_AF_2); // SPI Clock
GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_2); // LED MOSI
*/
// output pins, including defaults for AF pins
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Schmit = GPIO_Schmit_Enable;
// ADXL !SS
GPIOC->ODR = GPIO_Pin_4;
gpio.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOC, &gpio);
// User LED
GPIOD->ODR = GPIO_Pin_7;
gpio.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOD, &gpio);
// for now, float I2C pins
gpio.GPIO_OType = GPIO_OType_OD;
gpio.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
GPIOC->ODR = GPIO_Pin_5 | GPIO_Pin_6;
GPIO_Init(GPIOC, &gpio);
// SPI MOSI, LED MOSI
// SPI MOSI will always idle low
// LED MOSI will idle high when in standby, otherwise idle low
gpio.GPIO_Speed = GPIO_Speed_10MHz;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_4;
GPIOD->ODR = GPIO_Pin_3 | GPIO_Pin_4;
GPIO_Init(GPIOD, &gpio);
// --digital input pins--
gpio.GPIO_Mode = GPIO_Mode_IN;
// ADXL_INT
gpio.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOC, &gpio);
// BTN0-BTN2
gpio.GPIO_PuPd = GPIO_PuPd_UP;
gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_Init(GPIOA, &gpio);
// --analog input pins--
gpio.GPIO_Mode = GPIO_Mode_AN;
// VBAT
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOD, &gpio);
// --alternate function pins--
gpio.GPIO_Mode = GPIO_Mode_AF;
// I2C SDA, I2C SCL
//gpio.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
//GPIO_Init(GPIOC, &gpio);
// SPI MISO
gpio.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOC, &gpio);
// USART TX, SPI SCK
gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3;
GPIO_Init(GPIOD, &gpio);
// USART RX
gpio.GPIO_PuPd = GPIO_PuPd_DOWN;
gpio.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOB, &gpio);
}
// startup
int main(void)
{
// configure system IO
gpio_init();
// configure SPI and peripheral
spi_init();
adxl345_init();
adxl345_tick();
// configure prng
tinymt32_init(&tinymt32_s, (adxl.x << 16) | (adxl.y << 8) | adxl.z);
// configure addressable LEDs
led_sk6x_init();
ledprog_default();
ledprog_change(6);
// configure user input
btn_init();
// configure mainline timer
#ifdef DEBUG
RCC_APB2PeriphClockCmd(RCC_APB2Periph_DBGMCU, ENABLE);
DBGMCU->CR = DBGMCU_CR_DBG_STOP; // enable debug while in STOP (low power debug)
DBGMCU->APB1FZ |= DBGMCU_TIM6_STOP; // stop timer while debugging
#endif
tim6_init();
// do nothing
while(1) {
if (cnt2 != cnt) {
cnt2++;
if (cnt2 >= 1024) cnt2 = 0;
// button processing (512Hz)
if (cnt2 & 1) {
btn_callback();
}
// led programs (128Hz)
if ((cnt2 & 0x7) == 0) {
if (ledprog) ledprog();
// stuff next LED bits
led_sk6x_process();
}
}
__WFI();
}
}
// main code loop
__attribute__ ((long_call, section(".ramfunc"))) void TIM6_IRQHandler()
{
// clear interrupt
TIM6->SR = 0;
// attempt sending LEDs
if ((cnt & 0x7) == 0) {
led_sk6x_update();
}
// read buttons
if (cnt & 1) {
btn_tick();
}
// temp: run LED program
/*
static uint8_t v[3];
static uint8_t x = 0;
if (!(cnt % 4)) {
v[x]++;
if (!v[x]) {
x++;
if (x >= 3) x = 0;
}
}
*/
// run next LED program
/*
uint8_t w[2];
w[0] = v[0] >> 2;
w[1] = v[1] >> 2;
w[2] = v[2] >> 2;
led_sk6x_set(0, w[0], w[1], w[2]);
led_sk6x_set(1, w[1], w[2], w[0]);
led_sk6x_set(2, w[2], w[0], w[1]);
led_sk6x_set(3, w[1], w[2], w[0]);
led_sk6x_set(4, w[0], w[1], w[2]);
led_sk6x_set(5, w[0], w[1], w[2]);
led_sk6x_set(6, w[1], w[2], w[0]);
led_sk6x_set(7, w[2], w[0], w[1]);
*/
/*
for (int i = 1; i < 8; i++) {
led_sk6x_set(i, v[0] + (i * 32), v[1] + (i * 32), v[2] + (i * 32));
}
*/
// accelerometer: tick
if ((cnt & 0x3f) == 0x3f) {
adxl345_tick();
}
// timing
cnt++;
if (cnt >= 1024) {
cnt = 0;
uptime++;
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(char* file , uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif /* USE_FULL_ASSERT */

View File

@@ -0,0 +1,158 @@
/*
* prng.c
*
* Created on: Aug 3, 2023
* Author: true
*/
/**
* Tiny Mersenne Twister: only 127-bit internal state.
* Derived from the reference implementation version 1.1 (2015/04/24)
* by Mutsuo Saito (Hiroshima University) and Makoto Matsumoto
* (Hiroshima University).
*/
#include <stdint.h>
#include "prng.h"
static void tinymt32_next_state(tinymt32_t *s);
static uint32_t tinymt32_temper(tinymt32_t *s);
tinymt32_t tinymt32_s;
/**
* Parameter set to use for this IETF specification. Don't change.
* This parameter set is the first entry of the precalculated
* parameter sets in tinymt32dc/tinymt32dc.0.1048576.txt by
* Kenji Rikitake, available at:
* https://github.com/jj1bdx/tinymtdc-longbatch/.
* It is also the parameter set used in:
* Rikitake, K., "TinyMT pseudo random number generator for
* Erlang", Proceedings of the 11th ACM SIGPLAN Erlang Workshop,
* September 2012.
*/
const uint32_t TINYMT32_MAT1_PARAM = UINT32_C(0x8f7011ee);
const uint32_t TINYMT32_MAT2_PARAM = UINT32_C(0xfc78ff1f);
const uint32_t TINYMT32_TMAT_PARAM = UINT32_C(0x3793fdff);
/**
* This function initializes the internal state array with a
* 32-bit unsigned integer seed.
* @param s pointer to tinymt internal state.
* @param seed a 32-bit unsigned integer used as a seed.
*/
void tinymt32_init (tinymt32_t* s, uint32_t seed)
{
const uint32_t MIN_LOOP = 8;
const uint32_t PRE_LOOP = 8;
s->status[0] = seed;
s->status[1] = s->mat1 = TINYMT32_MAT1_PARAM;
s->status[2] = s->mat2 = TINYMT32_MAT2_PARAM;
s->status[3] = s->tmat = TINYMT32_TMAT_PARAM;
for (int i = 1; i < MIN_LOOP; i++) {
s->status[i & 3] ^= i + UINT32_C(1812433253)
* (s->status[(i - 1) & 3]
^ (s->status[(i - 1) & 3] >> 30));
}
/*
* NB: The parameter set of this specification warrants
* that none of the possible 2^^32 seeds leads to an
* all-zero 127-bit internal state. Therefore, the
* period_certification() function of the original
* TinyMT32 source code has been safely removed. If
* another parameter set is used, this function will
* have to be reintroduced here.
*/
for (int i = 0; i < PRE_LOOP; i++) {
tinymt32_next_state(s);
}
}
/**
* This function outputs a 32-bit unsigned integer from
* the internal state.
* @param s pointer to tinymt internal state.
* @return 32-bit unsigned integer r (0 <= r < 2^32).
*/
uint32_t tinymt32_get_uint32(tinymt32_t* s)
{
tinymt32_next_state(s);
return tinymt32_temper(s);
}
/**
* Internal tinymt32 constants and functions.
* Users should not call these functions directly.
*/
const uint32_t TINYMT32_SH0 = 1;
const uint32_t TINYMT32_SH1 = 10;
const uint32_t TINYMT32_SH8 = 8;
const uint32_t TINYMT32_MASK = UINT32_C(0x7fffffff);
/**
* This function changes the internal state of tinymt32.
* @param s pointer to tinymt internal state.
*/
static void tinymt32_next_state (tinymt32_t* s)
{
uint32_t x;
uint32_t y;
y = s->status[3];
x = (s->status[0] & TINYMT32_MASK)
^ s->status[1]
^ s->status[2];
x ^= (x << TINYMT32_SH0);
y ^= (y >> TINYMT32_SH0) ^ x;
s->status[0] = s->status[1];
s->status[1] = s->status[2];
s->status[2] = x ^ (y << TINYMT32_SH1);
s->status[3] = y;
/*
* The if (y & 1) {...} block below replaces:
* s->status[1] ^= -((int32_t)(y & 1)) & s->mat1;
* s->status[2] ^= -((int32_t)(y & 1)) & s->mat2;
* The adopted code is equivalent to the original code
* but does not depend on the representation of negative
* integers by 2's complements. It is therefore more
* portable but includes an if branch, which may slow
* down the generation speed.
*/
if (y & 1) {
s->status[1] ^= s->mat1;
s->status[2] ^= s->mat2;
}
}
/**
* This function outputs a 32-bit unsigned integer from
* the internal state.
* @param s pointer to tinymt internal state.
* @return 32-bit unsigned pseudorandom number.
*/
static uint32_t tinymt32_temper (tinymt32_t* s)
{
uint32_t t0, t1;
t0 = s->status[3];
t1 = s->status[0] + (s->status[2] >> TINYMT32_SH8);
t0 ^= t1;
/*
* The if (t1 & 1) {...} block below replaces:
* t0 ^= -((int32_t)(t1 & 1)) & s->tmat;
* The adopted code is equivalent to the original code
* but does not depend on the representation of negative
* integers by 2's complements. It is therefore more
* portable but includes an if branch, which may slow
* down the generation speed.
*/
if (t1 & 1) {
t0 ^= s->tmat;
}
return t0;
}

View File

@@ -0,0 +1,228 @@
/*
* spi.c
*
* Created on: Jun 19, 2023
* Author: true
*/
#include "hk32f030m.h"
#include "spi.h"
uint8_t *spi_txbuf;
uint8_t *spi_rxbuf;
uint16_t spi_xfer_len;
uint16_t spi_txpos;
uint16_t spi_rxpos;
void (*spi_done_cb)(void);
/*
* sets up SPI peripheral.
*/
void spi_init()
{
// NVIC_InitTypeDef nvic;
// enable SPI clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// run at 4MHz, master mode 3
SPI->CR1 = SPI_BaudRatePrescaler_8 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR | SPI_CR1_CPOL | SPI_CR1_CPHA;
// default to 8-bit, interrupts disabled, motorola mode
SPI->CR2 = SPI_CR2_FRXTH | SPI_DataSize_8b;
/*
* originally I wanted to use interrupts, but it ended up being WAY too slow.
* all of our transfers are pretty quick anyway, so using the hardware peripheral
* just to make sure timings are good. we aren't wasting too much energy I guess...
*
// configure and enable IRQ
nvic.NVIC_IRQChannel = SPI1_IRQn;
nvic.NVIC_IRQChannelPriority = 0;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
*/
// enable SPI
SPI->CR1 |= SPI_CR1_SPE;
}
/*
* starts an SPI transfer. assumes you have set slave select before calling.
* all transfers are BLOCKING.
* if callback is set to a function, will be executed when xfer is done.
*/
__attribute__ ((long_call, section(".ramfunc"))) void spi_xfer(uint8_t *tx, uint8_t *rx, uint16_t len, void (*cb)(void))
{
uint16_t flags;
uint16_t val;
uint8_t txb = 0;
uint8_t rxb = 0;
volatile uint8_t *spi_dr = (volatile uint8_t *)((uint32_t)SPI + 0x0c);
// wait until the SPI is not busy
while (SPI->SR & SPI_SR_BSY);
// transfer all of our data
while (txb != len) {
flags = SPI->SR;
// load next byte to send
val = tx ? tx[txb] : 0;
// transmit FIFO has space? if so, send byte
if ((flags & SPI_SR_FTLVL) != SPI_SR_FTLVL) {
*spi_dr = val;
txb++;
}
// receive FIFO has data for us? if so, receive byte
if (flags & SPI_SR_RXNE) {
val = *spi_dr;
if (rx) rx[rxb] = val;
rxb++;
}
}
// we shouldn't hit this code, but if there are bytes left over in FIFO...
while (rxb != len) {
flags = SPI->SR;
// receive FIFO has data for us? if so, receive byte
if (flags & SPI_SR_RXNE) {
val = *spi_dr;
if (rx) rx[rxb] = val;
rxb++;
}
}
if (cb) {
cb();
}
/*
// load first byte to send
val = tx ? tx[0] : 0;
// and send it
SPI_SendData8(SPI, tx[0]);
// set values for this transfer
spi_txbuf = tx;
spi_rxbuf = rx;
spi_xfer_len = len;
spi_done_cb = cb;
// set initial buffer positions
spi_txpos = 1;
spi_rxpos = 0;
// enable SPI
// SPI->CR1 |= SPI_CR1_SPE;
// enable tx/rx interrupt.
// interrupt handler will deal with the transmission
// SPI->CR2 |= SPI_CR2_TXEIE | SPI_CR2_RXNEIE;
*/
}
/*
* returns whether the SPI peripheral is busy with a transfer.
*/
/*
uint8_t spi_isbusy()
{
return spi_xfer_len ? 1 : 0;
}
*/
/*
* selects which mosi to activate.
* note: when going from NONE to LED, must wait at least 1ms before
* sending data. so just architect the system to never do this.
*/
void spi_mosi_sel(uint8_t which)
{
uint16_t moder;
moder = SPI_MOSI_SPI_PORT->MODER & \
~((0x3 << (SPI_MOSI_SPI_PIN * 2)) | \
(0x3 << (SPI_CLK_PIN * 2)) | \
(0x3 << (SPI_MOSI_LED_PIN * 2)));
switch (which) {
case SPI_MOSI_NONE: {
SPI_MOSI_LED_PORT->BSRR = (1 << SPI_MOSI_LED_PIN);
moder |= ( \
(GPIO_Mode_OUT << (SPI_MOSI_SPI_PIN * 2)) | \
(GPIO_Mode_OUT << (SPI_CLK_PIN * 2)) | \
(GPIO_Mode_IN << (SPI_MOSI_LED_PIN * 2)));
break;
}
case SPI_MOSI_SPI: {
moder |= ( \
(GPIO_Mode_AF << (SPI_MOSI_SPI_PIN * 2)) | \
(GPIO_Mode_AF << (SPI_CLK_PIN * 2)) | \
(GPIO_Mode_OUT << (SPI_MOSI_LED_PIN * 2)));
break;
}
case SPI_MOSI_LED: {
SPI_MOSI_LED_PORT->BRR = (1 << SPI_MOSI_LED_PIN);
moder |= ( \
(GPIO_Mode_OUT << (SPI_MOSI_SPI_PIN * 2)) | \
(GPIO_Mode_OUT << (SPI_CLK_PIN * 2)) | \
(GPIO_Mode_AF << (SPI_MOSI_LED_PIN * 2)));
break;
}
}
SPI_MOSI_SPI_PORT->MODER = moder;
}
// interrupt handler
/*
__attribute__ ((long_call, section(".ramfunc"))) void SPI1_IRQHandler()
{
uint8_t data;
// transmit buffer
if (SPI->SR & SPI_SR_TXE) {
if (spi_txpos >= spi_xfer_len) {
// we are done
SPI->CR2 &= (uint16_t)(~(uint16_t)SPI_CR2_TXEIE);
} else {
// load next byte
SPI_SendData8(SPI, spi_txbuf[spi_txpos++]);
}
}
if (SPI->SR & SPI_SR_RXNE) {
data = SPI_ReceiveData8(SPI);
if (spi_rxbuf) {
spi_rxbuf[spi_rxpos++] = data;
}
// are we done?
if (spi_rxpos >= spi_xfer_len) {
// we are done
SPI->CR2 &= (uint16_t)(~(uint16_t)SPI_CR2_RXNEIE);
spi_xfer_len = 0;
// disable SPI
// SPI->CR1 &= (uint16_t)(~(uint16_t)SPI_CR1_SPE);
// do callback
if (spi_done_cb) {
spi_done_cb();
}
}
}
}
*/

View File

@@ -0,0 +1,41 @@
/*
* timer.c
*
* Created on: Jun 25, 2023
* Author: true
*/
#include "hk32f030m.h"
/*
* main code loop setup
*/
void tim6_init()
{
NVIC_InitTypeDef nvic;
// enable timer clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
// configure timer for continuous 1KHz interrupt rate
TIM6->CR1 = 0; // default settings
TIM6->CNT = 0; // clear count
TIM6->PSC = 16 - 1; // set prescale
TIM6->ARR = (2000000 / 1024) - 1; // set interrupt frequency
TIM6->EGR = TIM_EGR_UG; // reload values
// enable timer, send update event, clear interrupt then enable interrupt
TIM6->SR = 0; // clear interrupt if set
TIM6->CR1 = TIM_CR1_ARPE | \
TIM_CR1_URS | TIM_CR1_CEN; // enable timer as upcounter
TIM6->DIER = TIM_DIER_UIE; // enable interrupt source
// configure and enable IRQ
nvic.NVIC_IRQChannel = TIM6_IRQn;
nvic.NVIC_IRQChannelPriority = 1;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
}