diff options
| author | Dien-Nhung Nguyen <kein@kienlab.com> | 2024-08-27 20:08:24 +0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-27 15:08:24 +0200 |
| commit | c95faf32a6a98975828717d596ff51dedeecdf56 (patch) | |
| tree | 8fe52862e0c34bfe82509adf0c98a8e57056d4c7 | |
| parent | f5874d607315ff88b27414299089b8528b5bb07c (diff) | |
Add animations (#15)
* animation: add xbm animations
* refactor: correct framebuffer terminology
* animation: add animations and effect
* animation: timing with TMOS scheduler
| -rw-r--r-- | BadgeBLE.md | 6 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | src/animation.c | 464 | ||||
| -rw-r--r-- | src/animation.h | 34 | ||||
| -rw-r--r-- | src/ble/peripheral.c | 14 | ||||
| -rw-r--r-- | src/ble/setup.h | 9 | ||||
| -rw-r--r-- | src/bmlist.c | 85 | ||||
| -rw-r--r-- | src/bmlist.h | 41 | ||||
| -rw-r--r-- | src/data.c | 54 | ||||
| -rw-r--r-- | src/data.h | 26 | ||||
| -rw-r--r-- | src/fb.c | 87 | ||||
| -rw-r--r-- | src/fb.h | 53 | ||||
| -rw-r--r-- | src/main.c | 206 |
13 files changed, 869 insertions, 216 deletions
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" | @@ -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 <stdlib.h> + +#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 <stdint.h> + +#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 <memory.h> + +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 <stdint.h> +#include <stdlib.h> + +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__ */ @@ -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; } @@ -3,10 +3,10 @@ #include <stdint.h> -#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 <memory.h> - -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 <stdint.h> -#include <stdlib.h> - -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__ */ @@ -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);
|