Compare commits

...

10 Commits

Author SHA1 Message Date
hueso
caaa1758a0 fix compiler warnings
Some checks failed
Badgemagic Firmware build / build (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
2026-02-09 04:06:06 -03:00
puckipedia
5df3122a75 Handle button events outside interrupt context (#125)
A lot of the button tasks configure TMOS tasks, which causes a lot of
graphical glitches, when the bluetooth icon is drawn but the other
tasks for rendering the effects still run.

This also fixes the "WFI inside the interrupt handler" issue causing
poweroff to hang.
2026-02-01 13:07:57 +01:00
Marco A. Gutierrez
1d46c5edd1 Merge pull request #120 from marcoag/fix_build_workflow
Move toolchain location to community mirror.
2026-01-01 20:47:12 +01:00
Marco A. Gutierrez
9db6a55518 Move toolchain location to community mirror.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2025-12-29 23:01:54 +01:00
Marco A. Gutierrez
13fcf9232b Replace MounRiver Toolchain links with updated ones
Updated MounRiver Toolchain links in README.
2025-12-29 22:23:10 +01:00
Marco A. Gutierrez
267dcfd475 Update MounRiver Toolchain links in README.
Removed the MounRiver Toolchain link and added a community mirror link.
2025-12-29 22:18:29 +01:00
Hong Phuc Dang
a794becdb9 Merge pull request #116 from bessman/doc/readme-bootloader
Clarify bootloader instructions
2025-12-28 15:49:27 +01:00
Alexander Bessman
72b3310851 Add firmware update instructions to README
Added instructions for updating firmware without battery removal.
2025-12-28 12:58:04 +01:00
Alexander Bessman
11c0e31f3f Fix README.md for clarity on dmesg command 2025-12-28 12:55:33 +01:00
Alexander Bessman
d8606ef0d9 Fix typo in README about ISP mode 2025-12-28 12:51:44 +01:00
23 changed files with 121 additions and 44 deletions

View File

@@ -6,7 +6,7 @@ on:
branches: [master]
env:
MRS_TOOLCHAIN: MRS_Toolchain_Linux_x64_V1.91
MRS_TOOLCHAIN: MRS_Toolchain_Linux_x64_V1.92
USBC_BUILD_DIR: usb-c
MICROB_BUILD_DIR: micro-b
BIN_TYPES: '{.bin,.elf}'
@@ -29,7 +29,7 @@ jobs:
- if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }}
name: Download toolchain
run: |
wget http://file-oss.mounriver.com/tools/${{ env.MRS_TOOLCHAIN }}.tar.xz
wget https://github.com/ch32-riscv-ug/MounRiver_Studio_Community_miror/releases/download/1.92-toolchain/${{ env.MRS_TOOLCHAIN }}.tar.xz
tar -xvf ${{ env.MRS_TOOLCHAIN }}.tar.xz
- name: Build firmware

View File

@@ -81,7 +81,7 @@ void I2C_Init(I2C_ModeTypeDef I2C_Mode, UINT32 I2C_ClockSpeed, I2C_DutyTypeDef I
R16_I2C_CTRL1 &= ~(RB_I2C_SMBUS | RB_I2C_SMBTYPE | RB_I2C_ACK);
R16_I2C_CTRL1 |= I2C_Mode | I2C_Ack;
R16_I2C_OADDR1 &= ~0xFFFF;
R16_I2C_OADDR1 &= ~0xFFFFU;
R16_I2C_OADDR1 |= I2C_AckAddr | I2C_OwnAddress1;
}

View File

@@ -56,7 +56,6 @@ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2dev.c \
CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi1.c \
CH5xx_ble_firmware_library/RVMSIS/core_riscv.c \
src/main.c \
src/debug.c \
src/leddrv.c \
src/button.c \
src/bmlist.c \
@@ -74,7 +73,6 @@ src/data.c \
src/usb/utils.c \
src/usb/setup.c \
src/usb/ctrl.c \
src/usb/debug.c \
src/usb/dev.c \
src/usb/composite/hiddev.c \
src/usb/composite/cdc-serial.c \
@@ -84,6 +82,11 @@ src/animation.c \
src/font.c \
src/power.c \
ifdef DEBUG
C_SOURCES += \
src/debug.c \
src/usb/debug.c
endif
# ASM sources
ASM_SOURCES = \

View File

@@ -16,10 +16,9 @@ the boot pin is pulled down in one of two ways:
- Alternatively, connect the USB, press and hold KEY2, then short and release
the C3 capacitor.
If the badge has succesfully entered ISP mode, a single pixel roughly in the middle of the display will be lit. The badge will stay in ISP mode for approximately ten seconds before rebooting into normal mode.
If the badge has successfully entered ISP mode, a single pixel roughly in the middle of the display will be lit. The badge will stay in ISP mode for approximately ten seconds before rebooting into normal mode.
You can also check `dmesg` if the chip has entered the ISP mode with idVendor=4348 and
idProduct=55e0.
On Linux, you can also check `dmesg` if the chip has entered the ISP mode with idVendor=4348 and idProduct=55e0.
![c3](assets/burn-badge.svg)
@@ -41,6 +40,14 @@ Select the `badgemagic-ch582.bin` file and click 'Download'.
Where badgemagic-ch582.bin is the binary downloaded above, the .elf file also
works.
### Updating
Once the open source firmware is installed, there is no further need to remove the battery or short-circuit C3 to enter ISP mode. Simply long-press KEY2 to enter ISP mode. Then, flash updated firmware with
```sh
wchisp config reset
wchisp flash badgemagic-ch582.bin
```
## Usage
@@ -99,7 +106,9 @@ version repeatedly.
### Tools
- [GNU make](https://www.gnu.org/software/make/)
- [MounRiver Toolchain](http://file-oss.mounriver.com/tools/MRS_Toolchain_Linux_x64_V1.91.tar.xz)
- ~[MounRiver Toolchain (removed)](http://file-oss.mounriver.com/tools/MRS_Toolchain_Linux_x64_V1.91.tar.xz)~
- [MounRiver Toolchain (community mirror)](https://github.com/ch32-riscv-ug/MounRiver_Studio_Community_miror/releases), specifically:
- [MRS_Toolchain_Linux_x64_V1.92.tar.xz](https://github.com/ch32-riscv-ug/MounRiver_Studio_Community_miror/releases/download/1.92-toolchain/MRS_Toolchain_Linux_x64_V1.92.tar.xz)
- [wchisp](https://github.com/ch32-rs/wchisp)
### Build

View File

@@ -10,7 +10,7 @@
#include <stddef.h>
typedef struct {
uint8_t *bytes;
const uint8_t *bytes;
int size;
} byte_t;

View File

@@ -1,7 +1,7 @@
#include "bmlist.h"
#include <memory.h>
volatile static bm_t *current, *head, *tail;
static bm_t *current, *head, *tail;
static void bm_add(bm_t *new, bm_t *prev, bm_t *next)
{
@@ -63,13 +63,14 @@ static void list_del(bm_t *prev, bm_t *next)
bm_t *bmlist_drop(bm_t *bm)
{
bm_t *next = bm->next;
list_del(bm->prev, bm->next);
if (bm == head)
head = bm->next;
if (bm == tail)
tail = bm->prev;
free(bm);
return bm->next;
return next;
}
bm_t *bm_new(uint16_t width)

View File

@@ -1,4 +1,6 @@
#include "CH58xBLE_LIB.h"
#include "button.h"
#include <stdbool.h>
#define DEBOUNCE_HIGH_THRES (200) // 0-255
#define DEBOUNCE_LOW_THRES (55) // 0-255
@@ -8,6 +10,13 @@
static volatile void (*onePressHandler[KEY_INDEX])(void) = { NULL };
static volatile void (*longPressHandler[KEY_INDEX])(void) = { NULL };
static volatile bool onePressPending[KEY_INDEX] = { false };
static volatile bool longPressPending[KEY_INDEX] = { false };
static tmosTaskID button_task_id = INVALID_TASK_ID;
#define BTN_PRESS (1 << 0)
static uint16_t btn_task(tmosTaskID, uint16_t);
void btn_init()
{
GPIOA_ModeCfg(KEY1_PIN, GPIO_ModeIN_PD);
@@ -18,6 +27,11 @@ void btn_init()
PFIC_EnableIRQ(TMR3_IRQn);
}
void btn_init_task(void)
{
button_task_id = TMOS_ProcessEventRegister(btn_task);
}
void btn_onOnePress(int key, void (*handler)(void))
{
if (key >= KEY_INDEX) return;
@@ -30,6 +44,41 @@ void btn_onLongPress(int key, void (*handler)(void))
longPressHandler[key] = handler;
}
void btn_tick(void)
{
for (int i = 0; i < KEY_INDEX; i++) {
if (onePressPending[i]) {
onePressPending[i] = false;
if (onePressHandler[i]) onePressHandler[i]();
}
if (longPressPending[i]) {
longPressPending[i] = false;
if (longPressHandler[i]) longPressHandler[i]();
}
}
}
static uint16_t btn_task(tmosTaskID id, uint16_t events)
{
if(events & SYS_EVENT_MSG) {
uint8 *pMsg = tmos_msg_receive(button_task_id);
if(pMsg != NULL)
{
tmos_msg_deallocate(pMsg);
}
return (events ^ SYS_EVENT_MSG);
}
if (events & BTN_PRESS) {
btn_tick();
return (events ^ BTN_PRESS);
}
return events;
}
__HIGH_CODE
static int debounce(int key, int is_press)
{
@@ -59,11 +108,17 @@ static void check(int k)
hold[k]++;
if (hold[k] >= LONGPRESS_THRES && is_longpress[k] == 0) {
is_longpress[k] = 1;
if (longPressHandler[k]) longPressHandler[k]();
longPressPending[k] = true;
if (button_task_id != INVALID_TASK_ID) {
tmos_set_event(button_task_id, BTN_PRESS);
}
}
} else {
if (hold[k] > 0 && hold[k] < LONGPRESS_THRES) {
if (onePressHandler[k]) onePressHandler[k]();
onePressPending[k] = true;
if (button_task_id != INVALID_TASK_ID) {
tmos_set_event(button_task_id, BTN_PRESS);
}
}
is_longpress[k] = 0;
hold[k] = 0;

View File

@@ -19,5 +19,7 @@ enum keys {
void btn_onOnePress(int key, void (*handler)(void));
void btn_onLongPress(int key, void (*handler)(void));
void btn_init();
void btn_init_task(void);
void btn_tick(void);
#endif /* __BUTTON_H__ */

View File

@@ -4,7 +4,6 @@
*/
#include "config.h"
#include "resource.h"
#include "res/foss-asia-2.xbm"
#include "debug.h"
#include "util/crc.h"

View File

@@ -7,11 +7,16 @@
#include "data.h"
#include "leddrv.h"
uint32_t bigendian16_sum(uint16_t *s, int len)
uint32_t bigendian16_sum(const uint8_t *s, int len)
{
uint32_t sum = 0;
while (len-- > 0) {
sum += bswap16(s[len]);
// Read the big-endian bytes for this uint16_t
uint8_t high = s[len * 2]; // MSB (most significant byte)
uint8_t low = s[len * 2 + 1]; // LSB (least significant byte)
// Reconstruct the uint16_t value (big-endian to little-endian equivalent)
uint16_t val = (high << 8) | low;
sum += val;
}
return sum;
}
@@ -42,7 +47,7 @@ uint16_t data_flash2newmem(uint8_t **chunk, uint32_t n)
return 0;
uint16_t offs = LEGACY_HEADER_SIZE
+ bigendian16_sum(header.sizes, n) * LED_ROWS;
+ bigendian16_sum((const uint8_t*)header.sizes, n) * LED_ROWS;
*chunk = malloc(size);
EEPROM_READ(offs, *chunk, size);

View File

@@ -42,7 +42,7 @@ enum ANIMATION_MODES {
static inline uint16_t bswap16(uint16_t i) {
return (i >> 8) | (i << 8);
}
uint32_t bigendian16_sum(uint16_t *s, int len);
uint32_t bigendian16_sum(const uint8_t *s, int len);
uint32_t data_flatSave(uint8_t *data, uint32_t len);
uint16_t data_flash2newmem(uint8_t **chunk, uint32_t n);

View File

@@ -50,7 +50,7 @@ int legacy_ble_rx(uint8_t *val, uint16_t len)
if (c == 1) {
data_legacy_t *d = (data_legacy_t *)data;
n = bigendian16_sum(d->sizes, 8);
n = bigendian16_sum((uint8_t*)d->sizes, 8);
data_len = LEGACY_HEADER_SIZE + LED_ROWS * n;
PRINT("Data len: %d\n", data_len);
data = realloc(data, data_len);
@@ -96,7 +96,7 @@ int legacy_usb_rx(uint8_t *buf, uint16_t len)
if (!data_len) {
data_legacy_t *d = (data_legacy_t *)data;
uint16_t n = bigendian16_sum(d->sizes, 8);
uint16_t n = bigendian16_sum((uint8_t*)d->sizes, 8);
data_len = LEGACY_HEADER_SIZE + LED_ROWS * n;
data = realloc(data, data_len);
}

View File

@@ -49,7 +49,7 @@ enum MODES {
static tmosTaskID common_taskid = INVALID_TASK_ID ;
volatile uint16_t fb[LED_COLS] = {0};
uint16_t fb[LED_COLS] = {0};
volatile int mode, is_play_sequentially = 1;
__HIGH_CODE
@@ -344,6 +344,7 @@ static void disp_charging()
{
int blink = 0;
while (mode == BOOT) {
btn_tick();
int percent = batt_raw2percent(batt_raw());
if (charging_status()) {
@@ -434,7 +435,7 @@ int main()
disp_charging();
cfg_init();
xbm_t spl = {
.bits = &(badge_cfg.splash_bm_bits),
.bits = badge_cfg.splash_bm_bits,
.w = badge_cfg.splash_bm_w,
.h = badge_cfg.splash_bm_h,
.fh = badge_cfg.splash_bm_fh,
@@ -446,6 +447,7 @@ int main()
ble_setup();
spawn_tasks();
btn_init_task();
mode = NORMAL;
while (1) {

View File

@@ -125,7 +125,7 @@ uint8_t load_fallback_cfg(uint8_t *val, uint16_t len)
{
PRINT(__func__);
PRINT("\n");
cfg_fallback(&badge_cfg);
cfg_fallback();
return 0;
}

View File

@@ -27,8 +27,7 @@ void power_init()
GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeIN_Floating);
ADC_ExtSingleChSampInit(SampleFreq_3_2, ADC_PGA_0);
int16_t adc_calib = ADC_DataCalib_Rough();
PRINT("RoughCalib_Value = %d \n", adc_calib);
PRINT("RoughCalib_Value = %d \n", ADC_DataCalib_Rough());
ADC_ChannelCfg(1);
}
@@ -38,7 +37,6 @@ int batt_raw()
int ret = 0;
PRINT("ADC reading: \n");
uint16_t buf[20];
for(int i = 0; i < 20; i++) {
uint16_t adc = ADC_ExcutSingleConver();
ret += adc;

View File

@@ -1,6 +1,6 @@
#define foss_asia_2_width 44
#define foss_asia_2_height 22
static unsigned char foss_asia_2_bits[] = {
unsigned const char foss_asia_2_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xf9, 0x8f, 0xff, 0xfc, 0x07,
0xfe, 0xfd, 0xdf, 0xff, 0xfe, 0x07, 0x06, 0x0c, 0xd8, 0x00, 0x06, 0x00,
0xff, 0x0d, 0xd8, 0x7f, 0xfe, 0x03, 0xff, 0x0d, 0x98, 0xff, 0xfc, 0x07,

View File

@@ -13,8 +13,6 @@
#define USB_DESCTYPE_CS_INTERFACE 0x24
static __attribute__((aligned(4))) uint8_t noti_ep_buf[64 + 64];
static uint8_t *const noti_ep_out = noti_ep_buf;
static uint8_t *const noti_ep_in = noti_ep_buf + 64;
static __attribute__((aligned(4))) uint8_t data_ep_buf[64 + 64];
static uint8_t *const data_ep_out = data_ep_buf;

View File

@@ -19,7 +19,7 @@ void usb_set_address(uint8_t ad)
address = ad;
}
void ctrl_start_load_block(void *buf, uint16_t len)
void ctrl_start_load_block(const void *buf, uint16_t len)
{
usb_start_load_block(ep0buf, buf, len, 1);
}
@@ -53,7 +53,9 @@ static void ep_handler()
switch(token) {
case UIS_TOKEN_SETUP:
print_setuppk(req);
#ifdef DEBUG
print_setuppk(request);
#endif
if (recip == USB_REQ_RECIP_DEVICE) {
handle_devreq(request);
@@ -128,12 +130,16 @@ __HIGH_CODE
void USB_IRQHandler(void) {
uint8_t intflag = R8_USB_INT_FG;
clear_handshake_sent_flag();
#ifdef DEBUG
PRINT("\nusb: new interrupt\n");
print_intflag_reg();
#endif
if (intflag & RB_UIF_TRANSFER) {
#ifdef DEBUG
PRINT("usb: RX Length reg: %d\n", R8_USB_RX_LEN);
print_status_reg();
#endif
route_enpoints();
}

View File

@@ -28,7 +28,7 @@ USB_DEV_DESCR dev_desc = {
};
/* String Descriptor Zero, Specifying Languages Supported by the Device */
static uint8_t lang_desc[] = {
static uint16_t lang_desc[] = {
0x04, /* bLength */
0x03, /* bDescriptorType */
0x09, 0x04 /* wLANGID - en-US */
@@ -91,7 +91,7 @@ static void desc_config(USB_SETUP_REQ *request)
static void desc_string(USB_SETUP_REQ *request)
{
uint8_t *string_index[32] = {
uint16_t *string_index[32] = {
lang_desc,
vendor_info,
product_info,

View File

@@ -5,11 +5,11 @@
void cdc_fill_IN(uint8_t *buf, uint8_t len);
int cdc_tx_poll(uint8_t *buf, int len, uint16_t timeout_ms);
void cdc_onWrite(void (*cb)(uint8_t *buf, uint16_t len));
void cdc_onWrite(int (*cb)(uint8_t *buf, uint16_t len));
void hiddev_fill_IN(uint8_t *buf, uint8_t len);
int hiddev_tx_poll(uint8_t *buf, int len, uint16_t timeout_ms);
void hiddev_onWrite(void (*cb)(uint8_t *buf, uint16_t len));
void hiddev_onWrite(int (*cb)(uint8_t *buf, uint16_t len));
void usb_start();

View File

@@ -35,9 +35,8 @@ static void __handshake(uint8_t ep_num, int dir, int type, int tog, uint8_t len)
if (ep_num > 7)
return;
char *type_mean[] = { "ACK", "NO_RESP", "NAK", "STALL"};
PRINT("Loaded %d byte, DATA%d with an %s to EP%dIN.\n",
len, tog != 0, type_mean[type], ep_num);
len, tog != 0, ((char *[]){ "ACK", "NO_RESP", "NAK", "STALL"})[type], ep_num);
uint8_t ctrl = type << (dir ? 0 : 2);
ctrl |= (tog) ? RB_UEP_T_TOG : RB_UEP_R_TOG;
@@ -69,11 +68,11 @@ int handshake_sent()
return res_sent;
}
static uint8_t *p_buf;
static const uint8_t *p_buf;
static uint16_t remain_len, req_len, _tog;
static uint16_t
load_chunk(void *ep_buf, void *block, uint16_t block_len, uint16_t req_len,
load_chunk(void *ep_buf, const void *block, uint16_t block_len, uint16_t req_len,
int tog)
{
_TRACE();
@@ -119,7 +118,7 @@ int usb_load_next_chunk()
}
uint16_t
usb_start_load_block(void *ep_IN_buf, void *buf, uint16_t len, int tog)
usb_start_load_block(void *ep_IN_buf, const void *buf, uint16_t len, int tog)
{
_TRACE();
req_len = len;

View File

@@ -16,8 +16,8 @@ int if_cb_register(uint8_t if_num, void (*cb)(USB_SETUP_REQ *request));
void dma_register(uint8_t endpoint_number, void *buf_addr);
uint16_t
usb_start_load_block(void *ep_IN_buf, void *buf, uint16_t len, int tog);
void ctrl_start_load_block(void *buf, uint16_t len);
usb_start_load_block(void *ep_IN_buf, const void *buf, uint16_t len, int tog);
void ctrl_start_load_block(const void *buf, uint16_t len);
int usb_load_next_chunk();
void usb_flush();

View File

@@ -7,7 +7,7 @@
#define ALIGN_8BIT(x) (ALIGN_1BYTE(x) * 8)
typedef struct {
uint8_t *bits;
const uint8_t *bits;
int w; // Width
int h; // Height
int fh; // Frame height