Compare commits
10 Commits
88126c31b7
...
2bc8143045
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bc8143045 | ||
|
|
b66cab7029 | ||
|
|
63ec27d6e2 | ||
|
|
5b0b659002 | ||
|
|
decd87a456 | ||
|
|
57558522d2 | ||
|
|
634ebad7cd | ||
|
|
4eb0c6cdd2 | ||
|
|
b5fb2372fb | ||
|
|
54e09bc059 |
4
Makefile
4
Makefile
@@ -6,10 +6,12 @@ SRCS := \
|
||||
CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwr.c \
|
||||
CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_sys.c \
|
||||
CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer0.c \
|
||||
CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer1.c \
|
||||
CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer3.c \
|
||||
CH5xx_ble_firmware_library/RVMSIS/core_riscv.c \
|
||||
CH5xx_ble_firmware_library/Startup/startup_CH583.S \
|
||||
$(wildcard src/*.c) \
|
||||
$(wildcard src/*.S) \
|
||||
$(wildcard src/*/*.c)
|
||||
|
||||
|
||||
@@ -50,7 +52,7 @@ LDFLAGS = -march=rv32imac -mabi=ilp32 -msmall-data-limit=8 \
|
||||
-lc -lm -lnosys \
|
||||
./CH5xx_ble_firmware_library/StdPeriphDriver/libISP583.a \
|
||||
./CH5xx_ble_firmware_library/BLE/LIBCH58xBLE.a \
|
||||
-T CH5xx_ble_firmware_library/Ld/Link.ld -nostartfiles -Xlinker --gc-sections
|
||||
-T link.ld -nostartfiles -Xlinker --gc-sections
|
||||
|
||||
all: build/$(PLATFORM).bin
|
||||
|
||||
|
||||
186
link.ld
Normal file
186
link.ld
Normal file
@@ -0,0 +1,186 @@
|
||||
ENTRY( _bldr_start )
|
||||
|
||||
MEMORY
|
||||
{
|
||||
BLDRFLASH (rx) : ORIGIN = 0x00000000, LENGTH = 4K
|
||||
FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 444K
|
||||
RAM (xrw) : ORIGIN = 0x20003800, LENGTH = 32K
|
||||
}
|
||||
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.bldr_init :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(SORT_NONE(.bldr_init)))
|
||||
. = ALIGN(4);
|
||||
} >BLDRFLASH AT>BLDRFLASH
|
||||
|
||||
.init :
|
||||
{
|
||||
_sinit = .;
|
||||
. = ALIGN(4);
|
||||
KEEP(*(SORT_NONE(.init)))
|
||||
. = ALIGN(4);
|
||||
_einit = .;
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
/* .vector :
|
||||
{
|
||||
*(.vector);
|
||||
} >FLASH AT>FLASH */
|
||||
|
||||
.highcodelalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_highcode_lma = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.highcode :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_highcode_vma_start = .);
|
||||
*(.vector);
|
||||
KEEP(*(SORT_NONE(.vector_handler)))
|
||||
*(.highcode);
|
||||
*(.highcode.*);
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_highcode_vma_end = .);
|
||||
} >RAM AT>FLASH
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(SORT_NONE(.handle_reset)))
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
*(.sdata2.*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.gnu.linkonce.t.*)
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP(*(SORT_NONE(.fini)))
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
PROVIDE( _etext = . );
|
||||
PROVIDE( _eitcm = . );
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.ctors :
|
||||
{
|
||||
/* gcc uses crtbegin.o to find the start of
|
||||
the constructors, so we make sure it is
|
||||
first. Because this is a wildcard, it
|
||||
doesn't matter if the user does not
|
||||
actually link against crtbegin.o; the
|
||||
linker won't look for a file to match a
|
||||
wildcard. The wildcard also means that it
|
||||
doesn't matter which directory crtbegin.o
|
||||
is in. */
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*crtbegin?.o(.ctors))
|
||||
/* We don't want to include the .ctor section from
|
||||
the crtend.o file until after the sorted ctors.
|
||||
The .ctor section from the crtend file contains the
|
||||
end of ctors marker and it must be last */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*crtbegin?.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.dalign :
|
||||
{
|
||||
. = ORIGIN(RAM) + MAX(0x800 , SIZEOF(.highcode));
|
||||
} >RAM AT>FLASH
|
||||
|
||||
.dlalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_lma = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_vma = .);
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.data .data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||
*(.sdata .sdata.*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
. = ALIGN(8);
|
||||
*(.srodata.cst16)
|
||||
*(.srodata.cst8)
|
||||
*(.srodata.cst4)
|
||||
*(.srodata.cst2)
|
||||
*(.srodata .srodata.*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _edata = .);
|
||||
} >RAM AT>FLASH
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _sbss = .);
|
||||
*(.sbss*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
*(.bss*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _ebss = .);
|
||||
} >RAM AT>FLASH
|
||||
|
||||
PROVIDE( _end = _ebss);
|
||||
PROVIDE( end = . );
|
||||
|
||||
.stack ORIGIN(RAM)+LENGTH(RAM) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_eusrstack = . );
|
||||
} >RAM
|
||||
}
|
||||
|
||||
|
||||
|
||||
56
src/badge_screen.c
Normal file
56
src/badge_screen.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "screen.h"
|
||||
#include "led.h"
|
||||
#include "wang.h"
|
||||
#include "menu.h"
|
||||
|
||||
static uint16_t badge_fb[44];
|
||||
|
||||
int image_index = 0;
|
||||
int frame = 0;
|
||||
int subframe = 99999;
|
||||
|
||||
int anim_render(uint16_t *fb, int index, int frame);
|
||||
static void badge_event(struct screen_event event) {
|
||||
if (event.type == SE_TICK) {
|
||||
if (!flash_header_valid) {
|
||||
screen_push(&menu_screen);
|
||||
return;
|
||||
}
|
||||
|
||||
subframe++;
|
||||
if (subframe > 33 && flash_header_valid) {
|
||||
screen_dirty = 1;
|
||||
subframe = 0;
|
||||
if (anim_render(badge_fb, image_index, frame++)) {
|
||||
frame = 0;
|
||||
}
|
||||
}
|
||||
} else if (event.type == SE_BUTTON_PRESS) {
|
||||
if (event.data == 0) {
|
||||
display_brightness = (display_brightness + 1) % 4;
|
||||
} else if (event.data == 1) {
|
||||
if (flash_header_valid) {
|
||||
image_index++;
|
||||
if (!flash_header.widths[image_index]) image_index = 0;
|
||||
frame = 0;
|
||||
subframe = 99999;
|
||||
}
|
||||
}
|
||||
} else if (event.type == SE_BUTTON_HOLD) {
|
||||
if (event.data == 0) {
|
||||
display_brightness = (display_brightness + 3) % 4;
|
||||
screen_push(&menu_screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void badge_draw(struct row_buf *buf) {
|
||||
display_make_buf_all(badge_fb, buf);
|
||||
}
|
||||
|
||||
struct screen badge_screen = {
|
||||
badge_draw,
|
||||
badge_event,
|
||||
};
|
||||
5
src/bldr.S
Normal file
5
src/bldr.S
Normal file
@@ -0,0 +1,5 @@
|
||||
.section .bldr_init,"ax",@progbits
|
||||
.global _bldr_start
|
||||
.align 1
|
||||
_bldr_start:
|
||||
j handle_reset
|
||||
151
src/ble.c
151
src/ble.c
@@ -5,10 +5,70 @@
|
||||
#include "CH58x_sys.h"
|
||||
#include "CH58xBLE_LIB.h"
|
||||
#include "led.h"
|
||||
#include "screen.h"
|
||||
#include "wang.h"
|
||||
|
||||
|
||||
// Digits are from Spleen, under BSD-3-Clause.
|
||||
const uint8_t digit_font[10 * 4] = {
|
||||
0x1e,0x29,0x25,0x1e,
|
||||
0x00,0x22,0x3f,0x20,
|
||||
0x32,0x29,0x29,0x26,
|
||||
0x12,0x21,0x25,0x1a,
|
||||
0x0f,0x08,0x3e,0x08,
|
||||
0x27,0x25,0x25,0x19,
|
||||
0x1e,0x25,0x25,0x18,
|
||||
0x03,0x31,0x09,0x07,
|
||||
0x1a,0x25,0x25,0x1a,
|
||||
0x06,0x29,0x29,0x1e
|
||||
};
|
||||
|
||||
static const uint16_t magic = 0x201;
|
||||
static const uint16_t magic2 = 0x1FE;
|
||||
|
||||
static uint32_t ble_pin = 0;
|
||||
|
||||
static void ble_popup_render(struct row_buf *buf) {
|
||||
uint16_t fb[44] = {0};
|
||||
int num = ble_pin;
|
||||
|
||||
fb[6] = magic2;
|
||||
fb[7] = magic;
|
||||
fb[37] = magic;
|
||||
fb[38] = magic2;
|
||||
|
||||
for (int i = 5; i >= 0; i--) {
|
||||
int x = 8 + (i * 5);
|
||||
int digit = (num % 10);
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
fb[x + j] = magic | (((uint16_t) digit_font[digit * 4 + j]) << 2);
|
||||
}
|
||||
|
||||
fb[x + 4] = magic;
|
||||
|
||||
num = num / 10;
|
||||
}
|
||||
display_make_buf_all(fb, buf);
|
||||
}
|
||||
|
||||
static void ble_popup_event(struct screen_event event) {
|
||||
if (event.type == SE_BUTTON_PRESS) {
|
||||
screen_pop();
|
||||
}
|
||||
}
|
||||
|
||||
struct screen ble_screen = {
|
||||
ble_popup_render,
|
||||
ble_popup_event,
|
||||
};
|
||||
|
||||
int ble_on = 0;
|
||||
int ble_connected = 0;
|
||||
|
||||
// Whether the device is bonded or was previously bonded and recovered the keys successfully.
|
||||
int ble_paired = 0;
|
||||
|
||||
void ble_toggle(void) {
|
||||
uint8_t ble_mode = ble_on ? FALSE : TRUE;
|
||||
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(ble_mode), &ble_mode);
|
||||
@@ -23,6 +83,27 @@ static void ble_calib_cb(void) {
|
||||
Calibration_LSI(Level_128);
|
||||
}
|
||||
|
||||
|
||||
static void passcode_callback(uint8_t *device_addr, uint16_t handle, uint8_t uiInputs, uint8_t uiOutputs) {
|
||||
ble_pin = tmos_rand() % 1000000;
|
||||
screen_dirty = 1;
|
||||
|
||||
GAPBondMgr_PasscodeRsp(handle, SUCCESS, ble_pin);
|
||||
}
|
||||
|
||||
static void pair_state_callback(uint16_t handle, uint8_t state, uint8_t status) {
|
||||
if (state == GAPBOND_PAIRING_STATE_STARTED) {
|
||||
screen_push(&ble_screen);
|
||||
} else {
|
||||
if (current_screen == &ble_screen) screen_pop();
|
||||
}
|
||||
|
||||
if (status != SUCCESS) return;
|
||||
if (state != GAPBOND_PAIRING_STATE_BONDED && state != GAPBOND_PAIRING_STATE_COMPLETE) return;
|
||||
|
||||
ble_paired = 1;
|
||||
}
|
||||
|
||||
static uint8_t periph_task = INVALID_TASK_ID;
|
||||
|
||||
static uint16_t peripheral_task(uint8_t task_id, uint16_t events) {
|
||||
@@ -41,7 +122,24 @@ static uint16_t peripheral_task(uint8_t task_id, uint16_t events) {
|
||||
|
||||
static void gap_onParamUpdate(uint16_t a, uint16_t b, uint16_t c, uint16_t d) {
|
||||
}
|
||||
static void gap_onStateChange(gapRole_States_t a, gapRoleEvent_t *b) {
|
||||
static gapRole_States_t previous_state = GAPROLE_INIT;
|
||||
|
||||
static void gap_onStateChange(gapRole_States_t new_state, gapRoleEvent_t *event) {
|
||||
if (previous_state == GAPROLE_CONNECTED && (new_state & GAPROLE_STATE_ADV_MASK) != GAPROLE_CONNECTED) {
|
||||
ble_paired = 0;
|
||||
ble_connected = 0;
|
||||
}
|
||||
|
||||
if ((new_state & GAPROLE_STATE_ADV_MASK) == GAPROLE_ADVERTISING && event->gap.opcode == GAP_LINK_TERMINATED_EVENT) {
|
||||
uint8_t ble_mode = TRUE;
|
||||
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(ble_mode), &ble_mode);
|
||||
}
|
||||
|
||||
if (new_state == GAPROLE_CONNECTED) {
|
||||
ble_connected = 1;
|
||||
}
|
||||
|
||||
previous_state = new_state & GAPROLE_STATE_ADV_MASK;
|
||||
}
|
||||
|
||||
uint8_t advertData[] = {
|
||||
@@ -71,7 +169,7 @@ uint8_t scanRspData[31] = {
|
||||
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
|
||||
'L', 'S', 'L', 'E', 'D', ' ', 'B', 'a', 'd', 'g', 'e', ' ', 'W', 'i', 't', 'c', 'h', 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 0,
|
||||
};
|
||||
|
||||
static gapRolesBroadcasterCBs_t broadcast_handlers = {
|
||||
@@ -85,30 +183,31 @@ static gapRolesCBs_t gap_handlers = {
|
||||
};
|
||||
|
||||
static gapBondCBs_t bond_managers = {
|
||||
0,
|
||||
0,
|
||||
passcode_callback,
|
||||
pair_state_callback,
|
||||
};
|
||||
|
||||
static const uint16_t service_uuid = 0xFEE0;
|
||||
static const gattAttrType_t service = {2, (uint8_t *) &service_uuid};
|
||||
static const uint8_t service_uuid[2] = {LO_UINT16(0xFEE0), HI_UINT16(0xFEE0)};
|
||||
static const gattAttrType_t service = {2, service_uuid};
|
||||
|
||||
static const uint16_t rx_char_uuid = 0xFEE1;
|
||||
static const uint8_t rx_char_uuid[2] = {LO_UINT16(0xFEE1), HI_UINT16(0xFEE1)};
|
||||
static uint8_t rx_char_props = GATT_PROP_WRITE;
|
||||
static uint8_t rx_char_val[16];
|
||||
|
||||
static gattAttribute_t attr_table[] = {
|
||||
{{2, &primaryServiceUUID}, GATT_PERMIT_READ, 0, &service},
|
||||
{{2, &characterUUID}, GATT_PERMIT_READ, 0, &rx_char_props},
|
||||
{{2, &rx_char_uuid}, GATT_PERMIT_WRITE, 0, &rx_char_val},
|
||||
{{2, primaryServiceUUID}, GATT_PERMIT_READ, 0, (uint8_t *)&service},
|
||||
{{2, characterUUID}, GATT_PERMIT_READ, 0, &rx_char_props},
|
||||
{{2, rx_char_uuid}, GATT_PERMIT_WRITE, 0, (uint8_t *)&rx_char_val},
|
||||
};
|
||||
|
||||
uint16_t blefb[44] = {0};
|
||||
|
||||
static bStatus_t write_handler(uint16_t conn_handle, gattAttribute_t *pAttr, uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method) {
|
||||
if (gattPermitAuthorWrite(pAttr->permissions)) return ATT_ERR_INSUFFICIENT_AUTHOR;
|
||||
uint8_t bonded_count = 0;
|
||||
GAPBondMgr_GetParameter(GAPBOND_BOND_COUNT, &bonded_count);
|
||||
|
||||
if (gattPermitAuthorWrite(pAttr->permissions) || (bonded_count && !ble_paired)) return ATT_ERR_INSUFFICIENT_AUTHOR;
|
||||
|
||||
uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);
|
||||
if (uuid != rx_char_uuid) return ATT_ERR_ATTR_NOT_FOUND;
|
||||
if (uuid != 0xFEE1) return ATT_ERR_ATTR_NOT_FOUND;
|
||||
|
||||
wang_rx(pValue, len);
|
||||
|
||||
@@ -121,6 +220,17 @@ static gattServiceCBs_t service_handlers = {
|
||||
0,
|
||||
};
|
||||
|
||||
uint32_t ble_read_flash(uint32_t addr, uint32_t num, uint32_t *buf) {
|
||||
EEPROM_READ(addr, buf, num * 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ble_write_flash(uint32_t addr, uint32_t num, uint32_t *buf) {
|
||||
EEPROM_ERASE(addr, num * 4);
|
||||
EEPROM_WRITE(addr, buf, num * 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ble_init(void) {
|
||||
bleConfig_t cfg = {0};
|
||||
|
||||
@@ -128,8 +238,11 @@ void ble_init(void) {
|
||||
cfg.MEMAddr = (uint32_t) BLE_BUF;
|
||||
cfg.MEMLen = (uint32_t) sizeof(BLE_BUF);
|
||||
|
||||
// not using the bonding information, as we do not support _bonding_.
|
||||
// This should be changed. but. you know.
|
||||
cfg.SNVAddr = (uint32_t) EEPROM_MAX_SIZE - 0x200;
|
||||
cfg.SNVBlock = 256;
|
||||
cfg.SNVNum = 1;
|
||||
cfg.readFlashCB = ble_read_flash;
|
||||
cfg.writeFlashCB = ble_write_flash;
|
||||
|
||||
// TODO: magic numbers
|
||||
// amount of buffered packets
|
||||
@@ -177,10 +290,10 @@ void ble_init(void) {
|
||||
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, 200);
|
||||
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_DEFAULT_PASSCODE, uint32_t, 0);
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_PAIRING_MODE, uint8_t, GAPBOND_PAIRING_MODE_NO_PAIRING);
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_MITM_PROTECTION, uint8_t, FALSE);
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_PAIRING_MODE, uint8_t, GAPBOND_PAIRING_MODE_WAIT_FOR_REQ);
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_MITM_PROTECTION, uint8_t, TRUE);
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_IO_CAPABILITIES, uint8_t, GAPBOND_IO_CAP_DISPLAY_ONLY);
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_BONDING_ENABLED, uint8_t, FALSE);
|
||||
SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_BONDING_ENABLED, uint8_t, TRUE);
|
||||
|
||||
GAPRole_BroadcasterSetCB(&broadcast_handlers);
|
||||
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
#include "CH58x_sys.h"
|
||||
#include "CH58xBLE_LIB.h"
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
volatile int button_count[2] = {15, 0};
|
||||
volatile int button_pressed[2] = {1, 0};
|
||||
uint16_t last_battery_value[24] = {0};
|
||||
uint16_t last_battery_index = 0;
|
||||
|
||||
volatile int button_count[BUTTON_COUNT] = {15, 0};
|
||||
volatile int button_pressed[BUTTON_COUNT] = {1, 0};
|
||||
|
||||
static void handle_button(int i, int state) {
|
||||
if (state && button_count[i] < 16) button_count[i]++;
|
||||
@@ -20,10 +24,21 @@ static void handle_button(int i, int state) {
|
||||
__INTERRUPT
|
||||
__HIGH_CODE
|
||||
void TMR3_IRQHandler(void) {
|
||||
TMR3_ClearITFlag(TMR0_3_IT_CYC_END);
|
||||
|
||||
handle_button(0, !!GPIOA_ReadPortPin(GPIO_Pin_1));
|
||||
handle_button(1, !GPIOB_ReadPortPin(GPIO_Pin_22));
|
||||
|
||||
TMR3_ClearITFlag(TMR0_3_IT_CYC_END);
|
||||
ADC_ChannelCfg(1);
|
||||
last_battery_value[last_battery_index++] = ADC_ExcutSingleConver();
|
||||
if (last_battery_index >= 24) last_battery_index = 0;
|
||||
|
||||
#if BUTTON_COUNT == 4
|
||||
ADC_ChannelCfg(3);
|
||||
uint16_t aux_button_value = ADC_ExcutSingleConver();
|
||||
handle_button(3, (aux_button_value < 0xf00) && (aux_button_value > 0x400));
|
||||
handle_button(2, aux_button_value < 0x100);
|
||||
#endif
|
||||
}
|
||||
|
||||
void button_init(void)
|
||||
@@ -46,13 +61,17 @@ void button_init(void)
|
||||
ADC_ChannelCfg(1);
|
||||
GPIOA_ModeCfg(GPIO_Pin_0, GPIO_ModeIN_PU);
|
||||
GPIOA_ModeCfg(GPIO_Pin_2, GPIO_ModeIN_PD);
|
||||
|
||||
#if BUTTON_COUNT == 4
|
||||
GPIOA_ModeCfg(GPIO_Pin_13, GPIO_ModeIN_Floating);
|
||||
#endif
|
||||
}
|
||||
|
||||
int get_battery_percentage(void) {
|
||||
uint32_t adc = 0;
|
||||
|
||||
for (int i = 0; i < 24; i++) {
|
||||
adc += ADC_ExcutSingleConver();
|
||||
adc += last_battery_value[i];
|
||||
}
|
||||
|
||||
adc /= 24;
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
void button_init(void);
|
||||
extern volatile int button_pressed[2];
|
||||
extern volatile int button_pressed[BUTTON_COUNT];
|
||||
|
||||
int get_battery_percentage(void);
|
||||
int is_charging(void);
|
||||
@@ -137,9 +137,6 @@ struct row_buf *display_screen = display_screens;
|
||||
static struct row_buf *display_screen_render = display_screens + 22;
|
||||
|
||||
void display_flip(void) {
|
||||
struct row_buf *new_render = display_screen == display_screens ? (display_screens + 22) : display_screens;
|
||||
struct row_buf *old_render = display_screen;
|
||||
|
||||
__atomic_exchange(&display_screen, &display_screen_render, &display_screen_render, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
@@ -163,7 +160,7 @@ void TMR0_IRQHandler(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_bness > display_brightness) {
|
||||
if ( cur_bness+2 > 1 << 1 << display_brightness ) { // sorry I don't know how to make this nicer
|
||||
display_commit(&blank);
|
||||
} else {
|
||||
display_commit(display_screen_render + cur_index);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern uint32_t pa_mask;
|
||||
extern uint32_t pb_mask;
|
||||
|
||||
|
||||
125
src/main.c
125
src/main.c
@@ -6,13 +6,13 @@
|
||||
#include "CH58xBLE_LIB.h"
|
||||
#include "usb/core.h"
|
||||
#include "led.h"
|
||||
#include "button.h"
|
||||
#include "input.h"
|
||||
#include "menu.h"
|
||||
#include "ble.h"
|
||||
#include "cdc.h"
|
||||
#include "wang.h"
|
||||
|
||||
int anim_render(uint16_t *fb, int index, int frame);
|
||||
#include "platform.h"
|
||||
#include "screen.h"
|
||||
|
||||
enum usb_control_resp bl_handler(enum usb_control_state state) {
|
||||
if ((usb_control_request.bmRequestType & 0x7f) != 0x43) return USB_CONTROL_RESP_PASS;
|
||||
@@ -25,63 +25,10 @@ enum usb_control_resp bl_handler(enum usb_control_state state) {
|
||||
}
|
||||
|
||||
|
||||
int btn1_was_pressed = 1;
|
||||
int btn2_was_pressed = 0;
|
||||
int btn1_hold = 0;
|
||||
int btn2_hold = 0;
|
||||
static int button_was_pressed[BUTTON_COUNT] = {0};
|
||||
static int button_hold[BUTTON_COUNT] = {0};
|
||||
|
||||
int image_index = 0;
|
||||
|
||||
int frame = 0;
|
||||
int subframe = 99999;
|
||||
|
||||
int main_handler(void) {
|
||||
|
||||
#define TIMER(btn0, btn1, v, count) if (button_pressed[0] != btn0 || button_pressed[1] != btn1) { v = 0; } else if (v < count) { v += 1; } else
|
||||
static int menu_timer = 0;
|
||||
TIMER(1, 0, menu_timer, 500) {
|
||||
display_brightness = (display_brightness + 15) % 16;
|
||||
menu_timer = 0;
|
||||
menu_switch();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (button_pressed[0] && !btn1_was_pressed) {
|
||||
display_brightness = (display_brightness + 1) % 16;
|
||||
}
|
||||
|
||||
if (button_pressed[1] && !btn2_was_pressed) {
|
||||
if (flash_header_valid) {
|
||||
image_index++;
|
||||
if (!flash_header.widths[image_index]) image_index = 0;
|
||||
frame = 0;
|
||||
subframe = 99999;
|
||||
}
|
||||
display_flip();
|
||||
}
|
||||
|
||||
subframe++;
|
||||
if (subframe > 33 && flash_header_valid) {
|
||||
// approximately 30fps
|
||||
subframe = 0;
|
||||
|
||||
uint16_t fb[44];
|
||||
if (anim_render(fb, image_index, frame)) {
|
||||
frame = 0;
|
||||
} else {
|
||||
frame++;
|
||||
}
|
||||
display_make_buf_all(fb, display_screen);
|
||||
display_flip();
|
||||
}
|
||||
|
||||
btn2_was_pressed = button_pressed[1];
|
||||
btn1_was_pressed = button_pressed[0];
|
||||
|
||||
if (!flash_header_valid) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
extern struct screen badge_screen;
|
||||
|
||||
int main()
|
||||
{
|
||||
@@ -98,18 +45,25 @@ int main()
|
||||
TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END);
|
||||
PFIC_EnableIRQ(TMR0_IRQn);
|
||||
|
||||
TMR1_TimerInit((FREQ_SYS / 1000));
|
||||
TMR1_ITCfg(ENABLE, TMR0_3_IT_CYC_END);
|
||||
|
||||
TMR3_TimerInit(FREQ_SYS / 200);
|
||||
TMR3_ITCfg(ENABLE, TMR0_3_IT_CYC_END);
|
||||
PFIC_EnableIRQ(TMR3_IRQn);
|
||||
|
||||
wang_init();
|
||||
main_handler();
|
||||
|
||||
usb_register_handler(bl_handler);
|
||||
usb_init();
|
||||
|
||||
ble_init();
|
||||
|
||||
screen_push(&badge_screen);
|
||||
|
||||
// set SEVONPEND
|
||||
PFIC->SCTLR |= (1 << 4);
|
||||
|
||||
while (1) {
|
||||
WWDG_SetCounter(0x7F);
|
||||
static int btldr_timer = 0;
|
||||
@@ -120,20 +74,51 @@ int main()
|
||||
btldr_timer = 0;
|
||||
}
|
||||
|
||||
static int cur_handler = 0;
|
||||
if (cur_handler) {
|
||||
cur_handler = menu_handler();
|
||||
if (!cur_handler) {
|
||||
btn2_was_pressed = button_pressed[1];
|
||||
btn1_was_pressed = button_pressed[0];
|
||||
subframe = 99999;
|
||||
struct screen_event ev = {
|
||||
SE_TICK,
|
||||
0
|
||||
};
|
||||
current_screen->event_handler(ev);
|
||||
|
||||
for (int i = 0; i < BUTTON_COUNT; i++) {
|
||||
int is_pressed = button_pressed[i];
|
||||
if (button_was_pressed[i] && is_pressed) {
|
||||
button_hold[i]++;
|
||||
if (button_hold[i] == 500) {
|
||||
ev.type = SE_BUTTON_HOLD;
|
||||
ev.data = i;
|
||||
current_screen->event_handler(ev);
|
||||
} else if (button_hold[i] > 500) {
|
||||
button_hold[i] = 501;
|
||||
}
|
||||
} else if (is_pressed && !button_was_pressed[i]) {
|
||||
ev.type = SE_BUTTON_PRESS;
|
||||
ev.data = i;
|
||||
current_screen->event_handler(ev);
|
||||
button_was_pressed[i] = 1;
|
||||
button_hold[i] = 0;
|
||||
} else if (!is_pressed && button_was_pressed[i]) {
|
||||
ev.type = button_hold[i] >= 500 ? SE_BUTTON_RELEASE_HOLD : SE_BUTTON_RELEASE_SHORT;
|
||||
ev.data = i;
|
||||
current_screen->event_handler(ev);
|
||||
button_was_pressed[i] = 0;
|
||||
}
|
||||
} else
|
||||
cur_handler = main_handler();
|
||||
}
|
||||
|
||||
if (screen_dirty) {
|
||||
screen_dirty = 0;
|
||||
current_screen->draw_handler(display_screen);
|
||||
display_flip();
|
||||
}
|
||||
|
||||
TMOS_SystemProcess();
|
||||
cdc_tick();
|
||||
hid_tick();
|
||||
DelayMs(1);
|
||||
|
||||
while (!TMR1_GetITFlag(TMR0_3_IT_CYC_END)) {
|
||||
__SEV();
|
||||
__asm__ volatile("wfi");
|
||||
}
|
||||
TMR1_ClearITFlag(TMR0_3_IT_CYC_END);
|
||||
}
|
||||
}
|
||||
|
||||
96
src/menu.c
96
src/menu.c
@@ -7,8 +7,9 @@
|
||||
#include "usb/core.h"
|
||||
#include "led.h"
|
||||
#include "ble.h"
|
||||
#include "button.h"
|
||||
#include "input.h"
|
||||
#include "wang.h"
|
||||
#include "screen.h"
|
||||
|
||||
#include "img/menu.xbm"
|
||||
|
||||
@@ -38,7 +39,7 @@ static void boop()
|
||||
LowPower_Shutdown(0);
|
||||
}
|
||||
|
||||
static void render_xbm(unsigned char *bits, struct row_buf *b) {
|
||||
static void render_xbm(uint8_t *bits, struct row_buf *b) {
|
||||
uint16_t fb[45] = {0};
|
||||
int index = 0;
|
||||
for (int j = 0; j < 11; j++) {
|
||||
@@ -53,13 +54,34 @@ static void render_xbm(unsigned char *bits, struct row_buf *b) {
|
||||
display_make_buf_all(fb, b);
|
||||
}
|
||||
|
||||
int menu_index = 0;
|
||||
static int menu_index = 0;
|
||||
|
||||
static int btn0_pressed = 0;
|
||||
static int btn1_pressed = 0;
|
||||
|
||||
void menu_render(void) {
|
||||
char buffer[(48 * 11) / 8];
|
||||
static int menu_ticks = 0;
|
||||
static void menu_event(struct screen_event event) {
|
||||
if (event.type == SE_TICK) {
|
||||
if (menu_ticks++ > 125) {
|
||||
screen_dirty = 1;
|
||||
}
|
||||
} else if (event.type == SE_BUTTON_PRESS) {
|
||||
if (event.data == 0) {
|
||||
menu_index = (menu_index + 1) % 3;
|
||||
if (!flash_header_valid && !menu_index) menu_index = 1;
|
||||
screen_dirty = 1;
|
||||
} else if (event.data == 1) {
|
||||
if (menu_index == 0) {
|
||||
screen_pop();
|
||||
return;
|
||||
} else if (menu_index == 1) {
|
||||
ble_toggle();
|
||||
screen_dirty = 1;
|
||||
} else if (menu_index == 2) {
|
||||
boop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void menu_draw(struct row_buf *rb) {
|
||||
uint8_t buffer[(48 * 11) / 8];
|
||||
memcpy(buffer, menu_bits, sizeof(buffer));
|
||||
int invert_index = 1 + menu_index * 13;
|
||||
for (int i = invert_index; i < (invert_index + 9); i++) {
|
||||
@@ -99,57 +121,11 @@ void menu_render(void) {
|
||||
}
|
||||
}
|
||||
|
||||
render_xbm(buffer, display_screen);
|
||||
display_flip();
|
||||
}
|
||||
|
||||
void menu_switch(void) {
|
||||
btn0_pressed = button_pressed[0];
|
||||
btn1_pressed = button_pressed[1];
|
||||
menu_index = flash_header_valid ? 0 : 1;
|
||||
|
||||
menu_render();
|
||||
}
|
||||
|
||||
int menu_handler(void) {
|
||||
static int btldr_timer = 0;
|
||||
static int render_ticks = 0;
|
||||
render_ticks++;
|
||||
|
||||
if (button_pressed[1] && button_pressed[0]) {
|
||||
btldr_timer++;
|
||||
if (btldr_timer > 2000) asm volatile("j 0x00");
|
||||
} else {
|
||||
btldr_timer = 0;
|
||||
}
|
||||
|
||||
if (button_pressed[0] && !btn0_pressed && !button_pressed[1]) {
|
||||
menu_index = (menu_index + 1) % 3;
|
||||
if (!flash_header_valid && !menu_index) menu_index = 1;
|
||||
menu_render();
|
||||
render_ticks = 0;
|
||||
}
|
||||
|
||||
if (button_pressed[1] && !btn1_pressed && !button_pressed[0]) {
|
||||
if (menu_index == 0) {
|
||||
return 0;
|
||||
} else if (menu_index == 1) {
|
||||
ble_toggle();
|
||||
menu_render();
|
||||
render_ticks = 0;
|
||||
} else if (menu_index == 2) {
|
||||
boop();
|
||||
}
|
||||
}
|
||||
|
||||
if (render_ticks > 125) {
|
||||
render_ticks = 0;
|
||||
menu_render();
|
||||
}
|
||||
|
||||
btn0_pressed = button_pressed[0];
|
||||
btn1_pressed = button_pressed[1];
|
||||
|
||||
return 1;
|
||||
render_xbm(buffer, rb);
|
||||
menu_ticks = 0;
|
||||
}
|
||||
|
||||
struct screen menu_screen = {
|
||||
menu_draw,
|
||||
menu_event,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void menu_switch(void);
|
||||
int menu_handler(void);
|
||||
#include "screen.h"
|
||||
|
||||
extern struct screen menu_screen;
|
||||
|
||||
36
src/screen.c
Normal file
36
src/screen.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "screen.h"
|
||||
|
||||
static void blank_draw_handler(struct row_buf *buf) {
|
||||
}
|
||||
|
||||
static void blank_event_handler(struct screen_event event) {
|
||||
}
|
||||
|
||||
static struct screen blank_screen = {
|
||||
blank_draw_handler,
|
||||
blank_event_handler,
|
||||
};
|
||||
|
||||
struct screen *current_screen = &blank_screen;
|
||||
static struct screen *screen_stack[4] = {0};
|
||||
static int screen_stack_idx = 0;
|
||||
|
||||
void screen_push(struct screen *screen) {
|
||||
screen_stack[screen_stack_idx++] = current_screen;
|
||||
current_screen = screen;
|
||||
screen_dirty = 1;
|
||||
}
|
||||
|
||||
void screen_replace(struct screen *screen) {
|
||||
current_screen = screen;
|
||||
}
|
||||
|
||||
void screen_pop() {
|
||||
if (screen_stack_idx == 0) {
|
||||
current_screen = &blank_screen;
|
||||
} else {
|
||||
current_screen = screen_stack[--screen_stack_idx];
|
||||
}
|
||||
}
|
||||
|
||||
int screen_dirty = 0;
|
||||
31
src/screen.h
Normal file
31
src/screen.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "led.h"
|
||||
|
||||
enum screen_event_type {
|
||||
SE_TICK = 0,
|
||||
SE_BUTTON_PRESS = 1,
|
||||
SE_BUTTON_RELEASE_SHORT = 2,
|
||||
SE_BUTTON_HOLD = 3,
|
||||
SE_BUTTON_RELEASE_HOLD = 4,
|
||||
};
|
||||
|
||||
struct screen_event {
|
||||
enum screen_event_type type;
|
||||
uint8_t data;
|
||||
};
|
||||
|
||||
typedef void (*screen_draw)(struct row_buf *);
|
||||
typedef void (*screen_event)(struct screen_event);
|
||||
|
||||
struct screen {
|
||||
screen_draw draw_handler;
|
||||
screen_event event_handler;
|
||||
};
|
||||
|
||||
void screen_push(struct screen *);
|
||||
void screen_replace(struct screen *);
|
||||
void screen_pop();
|
||||
|
||||
extern struct screen *current_screen;
|
||||
extern int screen_dirty;
|
||||
@@ -135,13 +135,13 @@ void usb_init() {
|
||||
R8_USB_DEV_AD = 0x00;
|
||||
R8_USB_INT_FG = 0xFF;
|
||||
|
||||
R16_UEP0_DMA = ep04_buf;
|
||||
R16_UEP1_DMA = epbuf + 0 * 128;
|
||||
R16_UEP2_DMA = epbuf + 1 * 128;
|
||||
R16_UEP3_DMA = epbuf + 2 * 128;
|
||||
R16_UEP5_DMA = epbuf + 3 * 128;
|
||||
R16_UEP6_DMA = epbuf + 4 * 128;
|
||||
R16_UEP7_DMA = epbuf + 5 * 128;
|
||||
R16_UEP0_DMA = (uint16_t) (uintptr_t) ep04_buf;
|
||||
R16_UEP1_DMA = (uint16_t) (uintptr_t) (epbuf + 0 * 128);
|
||||
R16_UEP2_DMA = (uint16_t) (uintptr_t) (epbuf + 1 * 128);
|
||||
R16_UEP3_DMA = (uint16_t) (uintptr_t) (epbuf + 2 * 128);
|
||||
R16_UEP5_DMA = (uint16_t) (uintptr_t) (epbuf + 3 * 128);
|
||||
R16_UEP6_DMA = (uint16_t) (uintptr_t) (epbuf + 4 * 128);
|
||||
R16_UEP7_DMA = (uint16_t) (uintptr_t) (epbuf + 5 * 128);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
set_endpoint_state(i, USB_EP_STATE_NAK);
|
||||
@@ -203,7 +203,7 @@ int16_t usb_recv(uint8_t endpoint, void *buffer, size_t buflen) {
|
||||
|
||||
recvlens[endpoint] = 0xFF;
|
||||
|
||||
memcpy(buffer, buf_for_ep(endpoint, 0), buflen);
|
||||
memcpy(buffer, (uint8_t *)buf_for_ep(endpoint, 0), buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ int16_t usb_xmit(uint8_t endpoint, void *buffer, size_t buflen) {
|
||||
|
||||
if (buflen > 64) buflen = 64;
|
||||
|
||||
memcpy(buf_for_ep(endpoint, 1), buffer, buflen);
|
||||
memcpy((uint8_t *)buf_for_ep(endpoint, 1), buffer, buflen);
|
||||
*ep_t_len_regs[endpoint] = buflen;
|
||||
set_endpoint_state(endpoint | 0x80, USB_EP_STATE_ACK);
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ uint8_t config_descriptor[0x6b] = {
|
||||
0x24, // bDescriptorType
|
||||
0x01, // bDescriptorSubtype
|
||||
0x00, // bmCapabilities
|
||||
0x01, // bDataInterface
|
||||
0x02, // bDataInterface
|
||||
|
||||
// ACM descripotor
|
||||
0x04, // bLength
|
||||
@@ -124,8 +124,8 @@ uint8_t config_descriptor[0x6b] = {
|
||||
0x05, // bLength
|
||||
0x24, // bDescriptorType
|
||||
0x06, // bDescriptorSubtype
|
||||
0x00, // bMasterInterface
|
||||
0x01, // bSlaveInterface0
|
||||
0x01, // bMasterInterface
|
||||
0x02, // bSlaveInterface0
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType
|
||||
@@ -189,7 +189,7 @@ static enum usb_control_resp handle_hid_request(enum usb_control_state state) {
|
||||
|
||||
__HIGH_CODE
|
||||
static enum usb_control_resp handle_main_request(enum usb_control_state state) {
|
||||
if (usb_control_request.bmRequestType & 0x7F != 0x00) return USB_CONTROL_RESP_PASS;
|
||||
if ((usb_control_request.bmRequestType & 0x7F) != 0x00) return USB_CONTROL_RESP_PASS;
|
||||
|
||||
switch (usb_control_request.bRequest) {
|
||||
case 0: // GET_STATUS
|
||||
@@ -291,7 +291,7 @@ void handle_ctrl_transfer(int is_in) {
|
||||
// DMA.
|
||||
|
||||
if (control_transfer_len > 0) {
|
||||
memcpy(ep04_buf, control_transfer_buf, control_transfer_len > 64 ? 64 : control_transfer_len);
|
||||
memcpy((uint8_t *)ep04_buf, control_transfer_buf, control_transfer_len > 64 ? 64 : control_transfer_len);
|
||||
R8_UEP0_T_LEN = control_transfer_len > 64 ? 64 : control_transfer_len;
|
||||
set_endpoint_state(0x80, USB_EP_STATE_ACK);
|
||||
} else {
|
||||
@@ -306,7 +306,7 @@ void handle_ctrl_transfer(int is_in) {
|
||||
}
|
||||
|
||||
control_transfer_len -= ack_bytes;
|
||||
memcpy(control_transfer_buf, ep04_buf, ack_bytes);
|
||||
memcpy(control_transfer_buf, (uint8_t *)ep04_buf, ack_bytes);
|
||||
control_transfer_buf += ack_bytes;
|
||||
|
||||
if (control_transfer_len > 0) {
|
||||
@@ -323,7 +323,7 @@ void handle_ctrl_transfer(int is_in) {
|
||||
__HIGH_CODE
|
||||
void handle_setup_request() {
|
||||
// Copy the setup request from the EP0 buffer.
|
||||
memcpy(&usb_control_request, ep04_buf, 8);
|
||||
memcpy(&usb_control_request, (uint8_t *)ep04_buf, 8);
|
||||
|
||||
// We don't expect the previous request to be driven anymore.
|
||||
// Reset the control transfer.
|
||||
@@ -352,7 +352,7 @@ void handle_setup_request() {
|
||||
if (ctrlreq_type() != USB_CONTROL_REQUEST_TYPE_OUT) {
|
||||
int mlen = control_transfer_len;
|
||||
if (mlen > 64) mlen = 64;
|
||||
memcpy(ep04_buf, control_transfer_buf, mlen);
|
||||
memcpy((uint8_t *)ep04_buf, control_transfer_buf, mlen);
|
||||
R8_UEP0_T_LEN = mlen;
|
||||
set_endpoint_state(0x80, USB_EP_STATE_ACK);
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "led.h"
|
||||
|
||||
struct wang_header {
|
||||
// First 16-byte chunk
|
||||
uint8_t magic[6]; // "wang\0\0"
|
||||
|
||||
Reference in New Issue
Block a user