From c95faf32a6a98975828717d596ff51dedeecdf56 Mon Sep 17 00:00:00 2001 From: Dien-Nhung Nguyen Date: Tue, 27 Aug 2024 20:08:24 +0700 Subject: Add animations (#15) * animation: add xbm animations * refactor: correct framebuffer terminology * animation: add animations and effect * animation: timing with TMOS scheduler --- BadgeBLE.md | 6 +- Makefile | 6 +- src/animation.c | 464 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/animation.h | 34 ++++ src/ble/peripheral.c | 14 +- src/ble/setup.h | 9 +- src/bmlist.c | 85 ++++++++++ src/bmlist.h | 41 +++++ src/data.c | 54 ++++-- src/data.h | 26 ++- src/fb.c | 87 ---------- src/fb.h | 53 ------ src/main.c | 206 ++++++++++++++++++----- 13 files changed, 869 insertions(+), 216 deletions(-) create mode 100644 src/animation.c create mode 100644 src/animation.h create mode 100644 src/bmlist.c create mode 100644 src/bmlist.h delete mode 100644 src/fb.c delete mode 100644 src/fb.h diff --git a/BadgeBLE.md b/BadgeBLE.md index da8120a..ec2b746 100644 --- a/BadgeBLE.md +++ b/BadgeBLE.md @@ -45,8 +45,8 @@ The "mode" bytes are a combination of two 4 bit values. The high nibble describe | 0x02 | scroll up | | 0x03 | scroll down | | 0x04 | fixed | -| 0x05 | "snowflake" | -| 0x06 | "picture" | -| 0x07 | "animation" | +| 0x05 | "animation" | +| 0x06 | "snowflake" | +| 0x07 | "picture" | | 0x08 | "laser" | diff --git a/Makefile b/Makefile index f0d8a0e..749073b 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ CH5xx_ble_firmware_library/RVMSIS/core_riscv.c \ src/main.c \ src/leddrv.c \ src/button.c \ -src/fb.c \ +src/bmlist.c \ src/ble/profile/legacy.c \ src/ble/profile/devinfo.c \ src/ble/setup.c \ @@ -64,6 +64,10 @@ src/usb/debug.c \ src/usb/dev.c \ src/usb/composite/hiddev.c \ src/usb/composite/cdc-serial.c \ +src/xbm.c \ +src/resource.c \ +src/animation.c \ + # ASM sources ASM_SOURCES = \ diff --git a/src/animation.c b/src/animation.c new file mode 100644 index 0000000..53eff58 --- /dev/null +++ b/src/animation.c @@ -0,0 +1,464 @@ +#include + +#include "xbm.h" +#include "leddrv.h" +#include "bmlist.h" + +#define ANI_ANIMATION_STEPS (5) // steps +#define ANI_FIXED_STEPS (LED_COLS) // steps + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define ALIGN(x, range) (((x % (range))>0)*(range) + (x / (range)) * (range)) + +// Shift left on positive and right on negative n +#define SIGNED_SHIFT(reg, n) ((n) >= 0) ? (reg) >> (n) : (reg) << abs(n) + +int mod(int a, int b) +{ + int r = a % b; + return r < 0 ? r + b : r; +} + +void fb_fill(uint16_t *fb, uint16_t v) +{ + for (int i = 0; i < LED_COLS; i++) { + fb[i] = v; + } +} + +int ani_xbm_next_frame(xbm_t *xbm, uint16_t *fb, int col, int row) +{ + static int i; + + xbm_t frame; + if (extract_frame(xbm, &frame, i) == NULL) { + i = 0; + return i; + } + xbm2fb(&frame, fb, col, row); + + return ++i; +} + +int ani_xbm_scroll_up(xbm_t *xbm, int vh, + uint16_t *fb, int col, int row) +{ + static int i; + + xbm_t frame; + if (xbm_croph(xbm, &frame, i, i + vh) == NULL) { + i = 0; + return i; + } + xbm2fb(&frame, fb, col, row); + + return ++i; +} + +static int scroll_pt_next(xbm_t *xbm, int vh, int pt, + uint16_t *fb, int col, int row) +{ + static int i; + xbm_t frame; + + if (i >= pt) { + i = 0; + return i; + } + if (xbm_croph(xbm, &frame, 0, i + (vh -pt)) == NULL) { + i = 0; + return i; + } + xbm2fb(&frame, fb, col, row +pt -i); + + return ++i; +} + +static int scroll_pb_next( xbm_t *xbm, int vh, int pb, + uint16_t *fb, int col, int row) +{ + static int i; + xbm_t frame; + + if (i >= pb) { + i = 0; + return i; + } + if (xbm_croph(xbm, &frame, xbm->h - (vh -1 -i), xbm->h) == NULL) { + i = 0; + return i; + } + xbm2fb(&frame, fb, col, row); + + return ++i; +} + +int ani_xbm_scrollup_pad( xbm_t *xbm, int vh, int pt, int pb, + uint16_t *fb, int col, int row) +{ + static int i; + + if (i == 0) + i = 1; + if (i == 1 && scroll_pt_next(xbm, vh, pt, fb, col, row) == 0) { + i = 2; + } + if (i == 2 && ani_xbm_scroll_up(xbm, vh, fb, col, row) == 0) { + i = 3; + } + if (i == 3 && scroll_pb_next(xbm, vh, pb, fb, col, row) == 0) { + i = 0; + } + return i; +} +/** + * Infinite scroll up + */ +int ani_xbm_scrollup_inf(xbm_t *xbm, uint16_t *fb, + int vh, int col, int row) +{ + static int i, r; + + xbm_t frame, frame_circle; + if (xbm_croph(xbm, &frame, i, i +vh) == NULL) { + + xbm_croph(xbm, &frame, i +r, i -1 +vh); + xbm_croph(xbm, &frame_circle, 0, r); + + xbm2fb(&frame, fb, col, row); + xbm2fb_dirty(&frame_circle, fb, col, row +vh -r); + + r++; + if (r >= vh) { + i = 0; + r = 0; + } + return i; + } + xbm2fb(&frame, fb, col, row); + + return ++i; +} + +void ani_scroll_x(bm_t *bm, uint16_t *fb, int dir) +{ + int x = mod(bm->anim_step, bm->width + LED_COLS) - LED_COLS; + bm->anim_step += (dir) ? -1 : 1; + + for (int i = 0; i < LED_COLS; i++) { + if (i + x >= bm->width) { + fb[i] = 0; + continue; + } + fb[i] = (i + x) >= 0 ? bm->buf[i + x] : 0; + } +} + +void ani_scroll_left(bm_t *bm, uint16_t *fb) +{ + ani_scroll_x(bm, fb, 0); +} + +void ani_scroll_right(bm_t *bm, uint16_t *fb) +{ + ani_scroll_x(bm, fb, 1); +} + +void ani_shift_y(bm_t *bm, uint16_t *fb, int y, int frame) +{ + frame *= LED_COLS; + + int size = MIN(LED_COLS, bm->width); + int i = 0; + for (; i < size; i++) { + if ((frame + i) >= bm->width) + break; + fb[i] = SIGNED_SHIFT(bm->buf[frame + i], y); + } + for (; i < LED_COLS; i++) + fb[i] = 0; +} + +void ani_scroll_y(bm_t *bm, uint16_t *fb) +{ + int frame_steps = LED_ROWS * 3; // in-still-out + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + + int y = mod(bm->anim_step, frame_steps); + + if (y < LED_ROWS) { // Scrolling up (in) + ani_shift_y(bm, fb, y - LED_ROWS, frame); + } else if (y < LED_ROWS * 2) { // Stay still + ani_shift_y(bm, fb, 0, frame); + } else { // Scrolling up (out) + ani_shift_y(bm, fb, y - LED_ROWS * 2, frame); + } +} + +void ani_scroll_up(bm_t *bm, uint16_t *fb) +{ + ani_scroll_y(bm, fb); + bm->anim_step++; +} + +void ani_scroll_down(bm_t *bm, uint16_t *fb) +{ + ani_scroll_y(bm, fb); + bm->anim_step--; +} + +static void laser_in(bm_t *bm, uint16_t *fb, int step, int frame) +{ + int c = mod(step, LED_COLS); + frame *= LED_COLS; + + int i = 0; + for (; i < c; i++) { + fb[i] = (frame + i < bm->width) ? bm->buf[frame + i] : 0; + } + for (; i < MIN(LED_COLS, bm->width - frame); i++) { + fb[i] = bm->buf[frame + c]; + } + for (; i < LED_COLS; i++) { + fb[i] = (frame + c < bm->width) ? bm->buf[frame + c] : 0; + } +} + +static void laser_out(bm_t *bm, uint16_t *fb, int step, int frame) +{ + int c = mod(step, LED_COLS); + frame *= LED_COLS; + + int i = 0; + for (; i < c; i++) { + fb[i] = (frame + c < bm->width) ? bm->buf[frame + c] : 0; + } + for (; i < MIN(LED_COLS, bm->width - frame); i++) { + fb[i] = bm->buf[frame + i]; + } + for (; i < LED_COLS; i++) { + fb[i] = 0; + } +} + +static void still(bm_t *bm, uint16_t *fb, int frame) +{ + int i = frame * LED_COLS; + int j = 0; + for (; j < LED_COLS; j++) { + if (i >= bm->width) + break; + fb[j] = bm->buf[i]; + i++; + } + for (; j< LED_COLS; j++) { + fb[j] = 0; + } +} + +void ani_laser(bm_t *bm, uint16_t *fb) +{ + int frame_steps = LED_COLS * 3; // in-still-out + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + + int c = mod(bm->anim_step, frame_steps); + bm->anim_step++; + + if (c < LED_COLS) + laser_in(bm, fb, c - LED_COLS, frame); + else if (c < LED_COLS * 2) + still(bm, fb, frame); + else + laser_out(bm, fb, c - LED_COLS * 2, frame); +} + +static uint32_t b16dialate(uint16_t w, int from, int to) +{ + uint32_t ret = 0; + int i = 0, j = from; + for (; i < from; i++) { + ret |= (w & (1 << i)); + } + for (; i < to; i++, j += 2) { + ret |= ((w & (1 << i)) >> i) << j; + } + for (; i < 16; i++, j++) { + ret |= ((w & (1 << i)) >> i) << j; + } + + return ret; +} + +static void snowflake_in(bm_t *bm, uint16_t *fb, int step, int frame) +{ + int y = mod(step, LED_ROWS*2) - LED_ROWS; + frame *= LED_COLS; + + int size = MIN(LED_COLS, bm->width - frame); + int i = 0; + for (; i < size; i++) { + if (y < 0) { + fb[i] = SIGNED_SHIFT(b16dialate(bm->buf[frame + i], 0, LED_ROWS), + LED_ROWS - y); + } else + fb[i] = SIGNED_SHIFT(b16dialate(bm->buf[frame + i], 0, LED_ROWS - y), + (LED_ROWS - y)); + } + for (; i < LED_COLS; i++) { + fb[i] = 0; + } +} + +static void snowflake_out(bm_t *bm, uint16_t *fb, int step, int frame) +{ + int y = mod(step, LED_ROWS*2) - LED_ROWS; + frame *= LED_COLS; + + int size = MIN(LED_COLS, bm->width - frame); + int i = 0; + for (; i < size; i++) { + if (y <= 0) { + fb[i] = SIGNED_SHIFT(b16dialate(bm->buf[frame + i], 0, LED_ROWS), + y); + } else { + fb[i] = b16dialate(bm->buf[frame + i], y, LED_ROWS); + } + } + for (; i < LED_COLS; i++) { + fb[i] = 0; + } +} + +void ani_snowflake(bm_t *bm, uint16_t *fb) +{ + int frame_steps = LED_ROWS * 6; // in-still-out, each costs 2xLED_ROWS step + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + + int c = mod(bm->anim_step, frame_steps); + bm->anim_step++; + + if (c < LED_ROWS * 2) { + snowflake_in(bm, fb, c - LED_ROWS * 2, frame); + } else if (c <= LED_ROWS * 4) { + still(bm, fb, frame); + } else { + snowflake_out(bm, fb, -(c - LED_ROWS * 4), frame); + } +} + +void ani_animation(bm_t *bm, uint16_t *fb) +{ + int frame_steps = ANI_ANIMATION_STEPS; + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + + bm->anim_step++; + + still(bm, fb, frame); +} + +void ani_fixed(bm_t *bm, uint16_t *fb) +{ + int frame_steps = ANI_FIXED_STEPS; + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + + bm->anim_step++; + still(bm, fb, frame); +} + +static void picture(bm_t *bm, uint16_t *fb, int step, int frame) +{ + int hc = LED_COLS / 2; + int range = mod(step - 1, LED_COLS); + + if (range > LED_COLS/2) { + still(bm, fb, frame); + return; + } + + frame *= LED_COLS; + int i = 0; + for (; i <= range; i++) { + fb[hc + i - 1] = (hc + i - 1 + frame >= bm->width) ? + 0 : bm->buf[hc + i - 1 + frame]; + fb[hc - i] = (hc - i + frame >= bm->width) ? + 0 : bm->buf[hc - i + frame]; + } + + if (i >= LED_COLS) + return; + fb[hc + i - 1] = -1; + fb[hc - i] = -1; + + for (i++; i< LED_COLS; i++) { + fb[hc + i - 1] = 0; + fb[hc - i] = 0; + } +} + +static void picture_out(bm_t *bm, uint16_t *fb, int step) +{ + int hc = LED_COLS / 2; + + if (step > hc) + return; + + int range = step; + int i = 0; + for (; i <= range; i++) { + fb[hc + i - 1] = 0; + fb[hc - i] = 0; + } + if (i >= LED_COLS) + return; + fb[hc + i - 1] = -1; + fb[hc - i] = -1; +} + +void ani_picture(bm_t *bm, uint16_t *fb) +{ + int last_steps = LED_COLS / 2; + int frame_steps = LED_COLS; + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS + 1; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + bm->anim_step++; + + if (frame == frames - 1) { + picture_out(bm, fb, mod(bm->anim_step, LED_COLS) ); + + /* picture_out() costs only half LED_COLS */ + if (mod(bm->anim_step, LED_COLS) >= last_steps) { + bm->anim_step = 0; + } + return; + } + picture(bm, fb, bm->anim_step, frame); +} + +void ani_marque(bm_t *bm, uint16_t *fb, int step) +{ + int tpl = 0b000100010001; + + int i; + for (i = 0 ; i < LED_COLS - 1; i++) { + fb[i] = fb[i] & ~((1 << (LED_ROWS - 1)) | 1); + + fb[i] |= !!(tpl & (1 << ((step + i) % 4)) ); + fb[i] |= !!(tpl & (1 << mod(-step + i - 2, 4))) << (LED_ROWS - 1); + } + + fb[0] = tpl << (step % 4); + fb[LED_COLS - 1] = tpl >> ((step + 3) % 4); + +} + +void ani_flash(bm_t *bm, uint16_t *fb, int step) +{ + if (!(step % 2)) + fb_fill(fb, 0); +} \ No newline at end of file diff --git a/src/animation.h b/src/animation.h new file mode 100644 index 0000000..cd8ea8e --- /dev/null +++ b/src/animation.h @@ -0,0 +1,34 @@ +#ifndef __ANIMATION_H__ +#define __ANIMATION_H__ + +#include + +#include "xbm.h" + +int ani_xbm_next_frame(xbm_t *xbm, uint16_t *fb, int col, int row); +int ani_xbm_scroll_up(xbm_t *xbm, int vh, uint16_t *fb, int col, int row); +int ani_xbm_scrollup_pad( xbm_t *xbm, int vh, int pt, int pb, + uint16_t *fb, int col, int row); +int ani_xbm_scrollup_inf(xbm_t *xbm, uint16_t *fb, + int vh, int col, int row); + +void fb_fill(uint16_t *fb, uint16_t v); +void ani_shift_y(bm_t *bm, uint16_t *fb, int dir, int frame); + +void ani_scroll_x(bm_t *bm, uint16_t *fb, int dir); +void ani_scroll_y(bm_t *bm, uint16_t *fb); + +void ani_scroll_left(bm_t *bm, uint16_t *fb); +void ani_scroll_right(bm_t *bm, uint16_t *fb); +void ani_scroll_up(bm_t *bm, uint16_t *fb); +void ani_scroll_down(bm_t *bm, uint16_t *fb); +void ani_fixed(bm_t *bm, uint16_t *fb); +void ani_laser(bm_t *bm, uint16_t *fb); +void ani_snowflake(bm_t *bm, uint16_t *fb); +void ani_animation(bm_t *bm, uint16_t *fb); +void ani_picture(bm_t *bm, uint16_t *fb); + +void ani_marque(bm_t *bm, uint16_t *fb, int step); +void ani_flash(bm_t *bm, uint16_t *fb, int step); + +#endif /* __ANIMATION_H__ */ diff --git a/src/ble/peripheral.c b/src/ble/peripheral.c index 955799a..f9c5e58 100644 --- a/src/ble/peripheral.c +++ b/src/ble/peripheral.c @@ -198,16 +198,26 @@ static uint16 peripheral_task(uint8 task_id, uint16 events) return 0; } +void ble_enable_advertise() +{ + uint8 e = TRUE; + GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8), &e); +} + +void ble_disable_advertise() +{ + uint8 e = FALSE; + GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8), &e); +} + static void gap_init() { GAPRole_PeripheralInit(); - static uint8 initial_advertising_enable = TRUE; static uint16 desired_min_interval = 6; static uint16 desired_max_interval = 500; // Set the GAP Role Parameters - GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8), &initial_advertising_enable); GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData); GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16), &desired_min_interval); diff --git a/src/ble/setup.h b/src/ble/setup.h index fd65b84..b814b36 100644 --- a/src/ble/setup.h +++ b/src/ble/setup.h @@ -1,8 +1,11 @@ -#ifndef __HAL_H__ -#define __HAL_H__ +#ifndef __BLE_SETUP_H__ +#define __BLE_SETUP_H__ void tmos_clockInit(void); void ble_hardwareInit(void); void peripheral_init(void); -#endif /* __HAL_H__ */ +void ble_enable_advertise(); +void ble_disable_advertise(); + +#endif /* __BLE_SETUP_H__ */ diff --git a/src/bmlist.c b/src/bmlist.c new file mode 100644 index 0000000..e3bb34f --- /dev/null +++ b/src/bmlist.c @@ -0,0 +1,85 @@ +#include "bmlist.h" +#include + +volatile static bm_t *current, *head, *tail; + +static void bm_add(bm_t *new, bm_t *prev, bm_t *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +bm_t *bmlist_insert(bm_t *at, bm_t *new) +{ + bm_add(new, at, at->next); + return new; +} + +bm_t *bmlist_append(bm_t *new) +{ + bmlist_insert(tail, new); + tail = new; + return new; +} + +bm_t *bmlist_gonext() +{ + current = current->next; + current->anim_step = 0; + return current; +} + +bm_t *bmlist_goprev() +{ + current = current->prev; + current->anim_step = 0; + return current; +} + +bm_t *bmlist_gohead() +{ + current = head; + current->anim_step = 0; + return current; +} + +bm_t *bmlist_current() +{ + return current; +} + +static void list_del(bm_t *prev, bm_t *next) +{ + prev->next = next; + next->prev = prev; +} + +bm_t *bmlist_drop(bm_t *bm) +{ + list_del(bm->prev, bm->next); + return bm->next; +} + +bm_t *bm_new(uint16_t width) +{ + bm_t *bm = malloc(sizeof(bm_t)); + memset(bm, 0, sizeof(bm_t)); + + bm->width = width; + bm->buf = malloc(width * sizeof(uint16_t)); + memset(bm->buf, 0, width * sizeof(uint16_t)); + + bm->next = bm; + bm->prev = bm; + + return bm; +} + +void bmlist_init(uint16_t first_bm_width) +{ + current = bm_new(first_bm_width); + head = current; + tail = current; +} diff --git a/src/bmlist.h b/src/bmlist.h new file mode 100644 index 0000000..1a7cf1a --- /dev/null +++ b/src/bmlist.h @@ -0,0 +1,41 @@ +#ifndef __BMLIST_H__ +#define __BMLIST_H__ + +#include +#include + +typedef struct bm_st { + uint16_t *buf; + uint16_t width; + uint8_t modes; + int is_flash; + int is_marquee; + // TODO: feat: Brightness for each bm + int brightness; + // TODO: feat: Timeout for each bm to switch to next bm + uint32_t timeout; // zero mean no timeout + uint32_t anim_step; // Animation step, zero means restart animation + + struct bm_st *next; + struct bm_st *prev; +} bm_t; + +bm_t *bm_new(uint16_t width); +static inline void bm_free(bm_t *bm) +{ + free(bm->buf); + free(bm); +} + +bm_t *bmlist_insert(bm_t *at, bm_t *new); +bm_t *bmlist_append(bm_t *new); +bm_t *bmlist_drop(bm_t *bm); + +bm_t *bmlist_gonext(); +bm_t *bmlist_goprev() ; +bm_t *bmlist_gohead(); +bm_t *bmlist_current(); + +void bmlist_init(uint16_t first_bm_width); + +#endif /* __BMLIST_H__ */ diff --git a/src/data.c b/src/data.c index f1a11af..eba5c70 100644 --- a/src/data.c +++ b/src/data.c @@ -24,33 +24,47 @@ uint32_t data_flatSave(uint8_t *data, uint32_t len) return EEPROM_WRITE(0, data, len); } +data_legacy_t *data_get_header(int read_anyway) +{ + static data_legacy_t *cache; + + if (cache == NULL) { + cache = malloc(sizeof(data_legacy_t)); + read_anyway = 1; + } + + if (read_anyway) { + EEPROM_READ(0, cache, LEGACY_HEADER_SIZE); + } + return cache; +} + uint16_t data_flash2newmem(uint8_t **chunk, uint32_t n) { - data_legacy_t header; - EEPROM_READ(0, &header, LEGACY_HEADER_SIZE); + data_legacy_t *header = data_get_header(0); - uint16_t size = bswap16(header.sizes[n]) * LED_ROWS; + uint16_t size = bswap16(header->sizes[n]) * LED_ROWS; if (size == 0) return 0; uint16_t offs = LEGACY_HEADER_SIZE - + bigendian16_sum(header.sizes, n) * LED_ROWS; + + bigendian16_sum(header->sizes, n) * LED_ROWS; *chunk = malloc(size); EEPROM_READ(offs, *chunk, size); return size; } -static void __chunk2buffer(uint16_t *fb, uint8_t *chunk, int col) +static void __chunk2buffer(uint16_t *bm, uint8_t *chunk, int col) { - uint16_t tmpfb[8] = {0}; + uint16_t tmpbm[8] = {0}; for (int i=0; i<8; i++) { for (int j=0; j<11; j++) { - tmpfb[i] |= ((chunk[j] >> (7-i)) & 0x01) << j; + tmpbm[i] |= ((chunk[j] >> (7-i)) & 0x01) << j; } } for (int i=0; i<8; i++) { - fb[col+i] = tmpfb[i]; + bm[col+i] = tmpbm[i]; } } @@ -61,24 +75,32 @@ void chunk2buffer(uint8_t *chunk, uint16_t size, uint16_t *buf) } } -void chunk2fb(uint8_t *chunk, uint16_t size, fb_t *fb) +void chunk2bm(uint8_t *chunk, uint16_t size, bm_t *bm) { - chunk2buffer(chunk, size, fb->buf); + chunk2buffer(chunk, size, bm->buf); } -fb_t *chunk2newfb(uint8_t *chunk, uint16_t size) +bm_t *chunk2newbm(uint8_t *chunk, uint16_t size) { - fb_t *fb = fb_new((size*8)/11); - chunk2fb(chunk, size, fb); - return fb; + bm_t *bm = bm_new((size*8)/11); + chunk2bm(chunk, size, bm); + return bm; } -fb_t *flash2newfb(uint32_t n) +bm_t *flash2newbm(uint32_t n) { uint8_t *buf; uint16_t size = data_flash2newmem(&buf, n); if (size == 0) return NULL; - return chunk2newfb(buf, size); + + bm_t *bm = chunk2newbm(buf, size); + data_legacy_t *header = data_get_header(0); + + bm->is_flash = (header->flash & (1 << n)) != 0; + bm->is_marquee = (header->marquee & (1 << n)) != 0; + bm->modes = header->modes[n]; + free(buf); + return bm; } diff --git a/src/data.h b/src/data.h index 813176d..ebdf104 100644 --- a/src/data.h +++ b/src/data.h @@ -3,10 +3,10 @@ #include -#include "fb.h" +#include "bmlist.h" typedef struct { - uint8_t header[6]; + uint8_t header[6]; // magic uint8_t flash; uint8_t marquee; uint8_t modes[8]; @@ -24,6 +24,20 @@ typedef struct { #define LEGACY_TRANSFER_WIDTH (16) #define LEGACY_HEADER_SIZE (sizeof(data_legacy_t) - sizeof(uint8_t *)) +#define LEGACY_GET_SPEED(mode) ((mode) >> 4) +#define LEGACY_GET_ANIMATION(mode) ((mode) & 0x0F) + +enum ANIMATION_MODES { + LEFT = 0, + RIGHT, + UP, + DOWN, + FIXED, + ANIMATION, + SNOWFLAKE, + PICTURE, + LASER, +}; static inline uint16_t bswap16(uint16_t i) { return (i >> 8) | (i << 8); @@ -33,10 +47,12 @@ uint32_t bigendian16_sum(uint16_t *s, int len); uint32_t data_flatSave(uint8_t *data, uint32_t len); uint16_t data_flash2newmem(uint8_t **chunk, uint32_t n); +data_legacy_t *data_get_header(int read_anyway); + void chunk2buffer(uint8_t *chunk, uint16_t size, uint16_t *buf); -void chunk2fb(uint8_t *chunk, uint16_t size, fb_t *fb); +void chunk2bm(uint8_t *chunk, uint16_t size, bm_t *bm); -fb_t *chunk2newfb(uint8_t *chunk, uint16_t size); -fb_t *flash2newfb(uint32_t n); +bm_t *chunk2newbm(uint8_t *chunk, uint16_t size); +bm_t *flash2newbm(uint32_t n); #endif /* __DATA_H__ */ diff --git a/src/fb.c b/src/fb.c deleted file mode 100644 index 08ae20c..0000000 --- a/src/fb.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "fb.h" -#include - -volatile static fb_t *current, *head, *tail; - -static void fb_add(fb_t *new, fb_t *prev, fb_t *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -fb_t *fblist_insert(fb_t *at, fb_t *new) -{ - fb_add(new, at, at->next); - return new; -} - -fb_t *fblist_append(fb_t *new) -{ - fblist_insert(tail, new); - tail = new; - return new; -} - -fb_t *fblist_gonext() -{ - current = current->next; - current->scroll = 0; - return current; -} - -fb_t *fblist_goprev() -{ - current = current->prev; - current->scroll = 0; - return current; -} - -fb_t *fblist_gohead() -{ - current = head; - current->scroll = 0; - return current; -} - -fb_t *fblist_currentfb() -{ - return current; -} - -static void list_del(fb_t *prev, fb_t *next) -{ - prev->next = next; - next->prev = prev; -} - -fb_t *fblist_drop(fb_t *fb) -{ - list_del(fb->prev, fb->next); - return fb->next; -} - -fb_t *fb_new(uint16_t width) -{ - fb_t *fb = malloc(sizeof(fb_t)); - memset(fb, 0, sizeof(fb_t)); - - fb->width = width; - fb->buf = malloc(width * sizeof(uint16_t)); - memset(fb->buf, 0, width * sizeof(uint16_t)); - - fb->modes = FIXED; - - fb->next = fb; - fb->prev = fb; - - return fb; -} - -void fblist_init(uint16_t first_fb_width) -{ - current = fb_new(first_fb_width); - head = current; - tail = current; -} diff --git a/src/fb.h b/src/fb.h deleted file mode 100644 index 4e5cb43..0000000 --- a/src/fb.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef __FB_H__ -#define __FB_H__ - -#include -#include - -enum ANIMATION_MODES { - LEFT = 0, - RIGHT, - UP, - DOWN, - FIXED, - SNOWFLAKE, - PICTURE, - ANIMATION, - LASER, -}; - -typedef struct fb_st { - uint16_t *buf; - uint16_t width; - uint8_t modes; - int is_flash; - int is_marquee; - // TODO: feat: Brightness for each fb - int brightness; - // TODO: feat: Timeout for each fb to switch to next fb - uint32_t timeout; // zero mean no timeout - uint16_t scroll; - - struct fb_st *next; - struct fb_st *prev; -} fb_t; - -fb_t *fb_new(uint16_t width); -static inline void fb_free(fb_t *fb) -{ - free((fb)->buf); - free((fb)); -} - -fb_t *fblist_insert(fb_t *at, fb_t *new); -fb_t *fblist_append(fb_t *new); -fb_t *fblist_drop(fb_t *fb); - -fb_t *fblist_gonext(); -fb_t *fblist_goprev() ; -fb_t *fblist_gohead(); -fb_t *fblist_currentfb(); - -void fblist_init(uint16_t first_fb_width); - -#endif /* __FB_H__ */ diff --git a/src/main.c b/src/main.c index cec43be..8a4c8b5 100644 --- a/src/main.c +++ b/src/main.c @@ -1,9 +1,13 @@ #include "CH58x_common.h" #include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" #include "leddrv.h" #include "button.h" -#include "fb.h" +#include "bmlist.h" +#include "resource.h" +#include "animation.h" + #include "power.h" #include "data.h" @@ -12,8 +16,6 @@ #include "usb/usb.h" -#define FB_WIDTH (LED_COLS * 4) -#define SCROLL_IRATIO (16) #define SCAN_F (2000) #define SCAN_T (FREQ_SYS / SCAN_F) @@ -28,9 +30,27 @@ enum MODES { POWER_OFF, MODES_COUNT, }; + #define BRIGHTNESS_LEVELS (4) -volatile int mode, brightness; +#define ANI_BASE_SPEED_T (200000) // uS +#define ANI_MARQUE_SPEED_T (100000) // uS +#define ANI_FLASH_SPEED_T (500000) // uS +#define SCAN_BOOTLD_BTN_SPEED_T (200000) // uS +#define ANI_SPEED_STRATEGY(speed_level) \ + (ANI_BASE_SPEED_T - ((speed_level) \ + * ANI_BASE_SPEED_T / 8)) + +#define ANI_NEXT_STEP (1 << 0) +#define ANI_MARQUE (1 << 1) +#define ANI_FLASH (1 << 2) +#define SCAN_BOOTLD_BTN (1 << 3) +#define BLE_NEXT_STEP (1 << 4) + +static tmosTaskID common_taskid = INVALID_TASK_ID ; + +volatile uint16_t fb[LED_COLS] = {0}; +volatile int mode, brightness = 0; __HIGH_CODE static void change_brightness() @@ -46,26 +66,30 @@ static void change_mode() } __HIGH_CODE -static void fb_transition() +static void bm_transition() { - fblist_gonext(); + bmlist_gonext(); } - -void draw_testfb() +void play_splash(xbm_t *xbm, int col, int row) { + while (ani_xbm_scrollup_pad(xbm, 11, 11, 11, fb, 0, 0) != 0) { + DelayMs(30); + } +} - fb_t *curr_fb = fblist_currentfb(); +void load_bmlist() +{ + bm_t *curr_bm = bmlist_current(); for (int i=0; i<8; i++) { - fb_t *fb = flash2newfb(i); - if (fb == NULL) + bm_t *bm = flash2newbm(i); + if (bm == NULL) continue; - fb->modes = LEFT; - fblist_append(fb); + bmlist_append(bm); } - fblist_gonext(); + bmlist_gonext(); - fblist_drop(curr_fb); + bmlist_drop(curr_bm); } void poweroff() @@ -84,12 +108,98 @@ void poweroff() LowPower_Shutdown(0); } -void ble_start() +static uint16_t common_tasks(tmosTaskID task_id, uint16_t events) +{ + static int marque_step, flash_step; + + if(events & SYS_EVENT_MSG) { + uint8 *pMsg = tmos_msg_receive(common_taskid); + if(pMsg != NULL) + { + tmos_msg_deallocate(pMsg); + } + return (events ^ SYS_EVENT_MSG); + } + + if(events & ANI_NEXT_STEP) { + + static void (*animations[])(bm_t *bm, uint16_t *fb) = { + ani_scroll_left, + ani_scroll_right, + ani_scroll_up, + ani_scroll_down, + ani_fixed, + ani_animation, + ani_snowflake, + ani_picture, + ani_laser + }; + + bm_t *bm = bmlist_current(); + if (animations[LEGACY_GET_ANIMATION(bm->modes)]) + animations[LEGACY_GET_ANIMATION(bm->modes)](bm, fb); + + if (bm->is_flash) { + ani_flash(bm, fb, flash_step); + } + if (bm->is_marquee) { + ani_marque(bm, fb, marque_step); + } + + uint32_t t = ANI_SPEED_STRATEGY(LEGACY_GET_SPEED(bm->modes)); + tmos_start_task(common_taskid, ANI_NEXT_STEP, t / 625); + + return events ^ ANI_NEXT_STEP; + } + + if (events & ANI_MARQUE) { + bm_t *bm = bmlist_current(); + marque_step++; + if (bm->is_marquee) { + ani_marque(bm, fb, marque_step); + } + + return events ^ ANI_MARQUE; + } + + if (events & SCAN_BOOTLD_BTN) { + static uint32_t hold; + hold = isPressed(KEY2) ? hold + 1 : 0; + if (hold > 10) { + reset_jump(); + } + + return events ^ SCAN_BOOTLD_BTN; + } + + if (events & ANI_FLASH) { + bm_t *bm = bmlist_current(); + flash_step++; + + if (bm->is_flash) { + ani_flash(bm, fb, flash_step); + } + /* After flash is applied, it will potentialy overwrite the marque + effect after it just wrote, results in flickering. So here apply the + marque effect again */ + if (bm->is_marquee) { + ani_marque(bm, fb, marque_step); + } + + return events ^ ANI_FLASH; + } + + return 0; +} + +void ble_setup() { ble_hardwareInit(); tmos_clockInit(); peripheral_init(); + ble_disable_advertise(); + devInfo_registerService(); legacy_registerService(); } @@ -128,6 +238,26 @@ static void usb_receive(uint8_t *buf, uint16_t len) } } +void spawn_tasks() +{ + common_taskid = TMOS_ProcessEventRegister(common_tasks); + + tmos_start_reload_task(common_taskid, ANI_MARQUE, ANI_MARQUE_SPEED_T / 625); + tmos_start_reload_task(common_taskid, ANI_FLASH, ANI_FLASH_SPEED_T / 625); + tmos_start_reload_task(common_taskid, SCAN_BOOTLD_BTN, + SCAN_BOOTLD_BTN_SPEED_T / 625); + tmos_start_task(common_taskid, ANI_NEXT_STEP, 500000 / 625); +} + +void ble_start() +{ + ble_enable_advertise(); + + tmos_stop_task(common_taskid, ANI_NEXT_STEP); + tmos_stop_task(common_taskid, ANI_MARQUE); + tmos_stop_task(common_taskid, ANI_FLASH); +} + void handle_mode_transition() { static int prev_mode; @@ -136,10 +266,10 @@ void handle_mode_transition() switch (mode) { case DOWNLOAD: - // Disable fb transition while in download mode + // Disable bitmap transition while in download mode btn_onOnePress(KEY2, NULL); - // Take control of the current fb to display + // Take control of the current bitmap to display // the Bluetooth animation ble_start(); while (mode == DOWNLOAD) { @@ -184,26 +314,24 @@ int main() TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END); PFIC_EnableIRQ(TMR0_IRQn); - fblist_init(FB_WIDTH); + bmlist_init(LED_COLS * 4); + + play_splash(&splash, 0, 0); - draw_testfb(); + load_bmlist(); btn_init(); btn_onOnePress(KEY1, change_mode); - btn_onOnePress(KEY2, fb_transition); + btn_onOnePress(KEY2, bm_transition); btn_onLongPress(KEY1, change_brightness); - while (1) { - uint32_t i = 0; - while (isPressed(KEY2)) { - i++; - if (i>10) { - reset_jump(); - } - DelayMs(200); - } + ble_setup(); + + spawn_tasks(); + while (1) { handle_mode_transition(); - } + TMOS_SystemProcess(); + } } __INTERRUPT @@ -213,30 +341,16 @@ void TMR0_IRQHandler(void) static int i; if (TMR0_GetITFlag(TMR0_3_IT_CYC_END)) { - - fb_t *fb = fblist_currentfb(); i += 1; if (i >= LED_COLS) { i = 0; - if ((fb->modes & 0x0f) == LEFT) { - fb->scroll++; - if (fb->scroll >= (fb->width-LED_COLS)*SCROLL_IRATIO) { - fb->scroll = 0; - } - } } if (i % 2) { if ((brightness + 1) % 2) leds_releaseall(); } else { - if (i + fb->scroll/SCROLL_IRATIO >= fb->width) { - leds_releaseall(); - return; - } - led_write2dcol(i/2, - fb->buf[i+ fb->scroll/SCROLL_IRATIO], - fb->buf[i+ fb->scroll/SCROLL_IRATIO + 1]); + led_write2dcol(i/2, fb[i], fb[i + 1]); } TMR0_ClearITFlag(TMR0_3_IT_CYC_END); -- cgit v1.2.3