39 Commits

Author SHA1 Message Date
lmx
6c9ec6cec8 skiing_mtp 2025-12-11 10:49:13 +08:00
lmx
ea610c24d3 串口输出原始数据到上位机验证 2025-12-09 09:59:41 +08:00
lmx
45158aebcf cun 2025-12-05 19:37:31 +08:00
lmx
138275a04b cun 2025-12-05 19:06:54 +08:00
lmx
89f1c93f74 蓝牙发送速率提升 2025-12-04 18:51:38 +08:00
lmx
8c2db49083 iic速率调整到400k 2025-12-02 10:24:01 +08:00
lmx
627780ea20 11月28日发客户的版本 2025-11-28 18:13:35 +08:00
lmx
ade4b0a1f8 feat: Add rfid feature and .gitignore file 2025-11-28 16:25:35 +08:00
lmx
818e8c3778 chore: Remove tracked build artifacts 2025-11-28 16:24:27 +08:00
lmx
892ed9267b feat: Add rfid feature and .gitignore file 2025-11-28 16:23:06 +08:00
lmx
4af4f13ac6 存档 2025-11-25 18:52:49 +08:00
lmx
4c5da2298f 数据按照小端模式发送 2025-11-25 18:01:17 +08:00
lmx
5c7d9ab822 cun 2025-11-25 15:27:26 +08:00
lmx
60a4e95386 cun 2025-11-25 14:27:19 +08:00
lmx
845cc33fe8 gitignore 2025-11-25 14:16:22 +08:00
lmx
f3710fbb4b 蓝牙协议完成 2025-11-21 18:50:19 +08:00
lmx
91b08dbe47 差气压计的蓝牙协议 2025-11-21 17:10:36 +08:00
lmx
591e7632d2 cun 2025-11-21 15:17:10 +08:00
lmx
baa5979ee1 暂存:数据发送协议完善中 2025-11-21 14:54:21 +08:00
lmx
bdadd5de1e cun 2025-11-21 10:53:47 +08:00
lmx
9ccf1acda8 地磁8面校准完成 2025-11-20 19:30:34 +08:00
lmx
2bfdc81991 部分驱动代码完成,待测试 2025-11-20 09:24:11 +08:00
lmx
054ea8644a 四元数求角度和去重力分量,误差减少 2025-11-18 18:47:05 +08:00
lmx
ad3ab64b72 cun 2025-11-18 17:28:00 +08:00
lmx
ebca849be3 cun 2025-11-18 17:27:06 +08:00
lmx
d0d9c0a630 存档 2025-11-18 10:15:00 +08:00
lmx
b621ef7e44 重力分量去除后仍有偏差 2025-11-13 20:30:10 +08:00
lmx
046986c5c3 cun 2025-11-13 11:13:03 +08:00
lmx
5e587e0527 最新代码 2025-11-13 09:50:42 +08:00
lmx
c88cb70bb1 启动阈值调整 2025-11-12 13:57:59 +08:00
lmx
58ad14691e 修改了六轴配置 2025-11-11 19:31:34 +08:00
lmx
23a71377a2 cun 2025-11-11 09:38:51 +08:00
lmx
d12252dfda 存档 2025-11-10 19:27:37 +08:00
lmx
289a6b780b 存档 2025-11-07 17:14:58 +08:00
lmx
ae980789b6 第6版 2025-11-06 19:24:51 +08:00
lmx
ac7299e7ad cun 2025-11-04 19:20:58 +08:00
lmx
6be3cd1070 3 2025-11-04 14:40:55 +08:00
lmx
671730a351 cun 2025-11-03 18:48:15 +08:00
lmx
97e85df2f8 Ma 2025-10-31 18:12:10 +08:00
1245 changed files with 11867 additions and 295273 deletions

17
.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# 编译生成的目标文件
*.o
*.so
*.d
# 编译生成的最终产物
*.elf
*.fw
*.ufw
*.map
*.lst
*.bc
# VSCode 用户个人设置
# 团队协作时,每个人的配置可能不同,通常不建议提交
.vscode/settings.json

24
.vscode/settings.json vendored
View File

@ -1,24 +0,0 @@
{
"files.associations": {
"board_config.h": "c",
"board_jl701n_demo_cfg.h": "c",
"colorful_lights.h": "c",
"board_jl701n_anc_cfg.h": "c",
"update_loader_download.h": "c",
"iic_soft.h": "c",
"circle_buffer.h": "c",
"default_event_handler.h": "c",
"ui_manage.h": "c",
"charge.h": "c",
"app_main.h": "c",
"app_config.h": "c",
"app_action.h": "c",
"includes.h": "c",
"key_event_deal.h": "c",
"app_umidigi_chargestore.h": "c",
"hci_lmp.h": "c",
"bluetooth.h": "c",
"SCU722.C": "cpp",
"math.h": "c"
}
}

View File

@ -248,6 +248,11 @@ INCLUDES := \
-Iapps/earphone/xtell_Sensor/buffer \
-Iapps/earphone/xtell_Sensor/sensor \
-Iapps/earphone/xtell_Sensor \
-Iapps/earphone/xtell_Sensor/calculate \
-Iapps/earphone/xtell_Sensor/ano \
-Iapps/earphone/xtell_Sensor/sensor/ \
-Iapps/earphone/xtell_Sensor/sensor/ \
-Iapps/earphone/xtell_Sensor/sensor/ \
-I$(SYS_INC_DIR) \
@ -617,7 +622,12 @@ c_SRC_FILES := \
apps/earphone/xtell_Sensor/send_data.c \
apps/earphone/xtell_Sensor/buffer/circle_buffer.c \
apps/earphone/xtell_Sensor/sensor/LIS2DH12.c \
apps/earphone/xtell_Sensor/sensor/SCU722.c \
apps/earphone/xtell_Sensor/sensor/SC7U22.c \
apps/earphone/xtell_Sensor/calculate/skiing_tracker.c \
apps/earphone/xtell_Sensor/ano/ano_protocol.c \
apps/earphone/xtell_Sensor/sensor/MMC56.c \
apps/earphone/xtell_Sensor/sensor/BMP280.c \
apps/earphone/xtell_Sensor/sensor/AK8963.c \
# 需要编译的 .S 文件

View File

@ -18,12 +18,24 @@
#include "bt_tws.h"
#endif
#define ENABLE_XLOG 1
#ifdef xlog
#undef xlog
#endif
#if ENABLE_XLOG
#define xlog(format, ...) printf("[XT:%s] " format, __func__, ##__VA_ARGS__)
#else
#define xlog(format, ...) ((void)0)
#endif
spinlock_t iic_lock;
#define LOG_TAG "[GSENSOR]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define xlog_ENABLE
/* #define LOG_DUMP_ENABLE */
#define LOG_CLI_ENABLE
#include "debug.h"
@ -82,20 +94,20 @@ void gSensor_int_io_detect(void *priv)
u8 int_io_status = 0;
u8 det_result = 0;
int_io_status = gpio_read(platform_data->gSensor_int_io);
//log_info("status %d\n",int_io_status);
//xlog("status %d\n",int_io_status);
gSensor_hdl->gravity_sensor_ctl(GSENSOR_INT_DET, &int_io_status);
if (gSensor_hdl->gravity_sensor_check == NULL) {
return;
}
det_result = gSensor_hdl->gravity_sensor_check();
if (det_result == 1) {
log_info("GSENSOR_EVENT_CLICK\n");
xlog("GSENSOR_EVENT_CLICK\n");
gSensor_event_to_user(KEY_EVENT_CLICK);
} else if (det_result == 2) {
log_info("GSENSOR_EVENT_DOUBLE_CLICK\n");
xlog("GSENSOR_EVENT_DOUBLE_CLICK\n");
gSensor_event_to_user(KEY_EVENT_DOUBLE_CLICK);
} else if (det_result == 3) {
log_info("GSENSOR_EVENT_THREE_CLICK\n");
xlog("GSENSOR_EVENT_THREE_CLICK\n");
gSensor_event_to_user(KEY_EVENT_TRIPLE_CLICK);
}
}
@ -117,7 +129,7 @@ int gSensor_read_data(u8 *buf, u8 buflen)
//
int get_gSensor_data(short *buf)
{
// printf("%s",__func__);
// xlog("%s",__func__);
axis_info_t accel_data[32];
if (!gpio_read(platform_data->gSensor_int_io)) {
gSensor_hdl->gravity_sensor_ctl(READ_GSENSOR_DATA, accel_data);
@ -126,7 +138,7 @@ int get_gSensor_data(short *buf)
buf[i * 2] = accel_data[i].x;
buf[i * 2 + 1] = accel_data[i].y;
buf[i * 2 + 2] = accel_data[i].z;
// printf("cnt:%1d x:%5d y:%5d z:%5d\n", i, accel_data[i].x, accel_data[i].y, accel_data[i].z);
// xlog("cnt:%1d x:%5d y:%5d z:%5d\n", i, accel_data[i].x, accel_data[i].y, accel_data[i].z);
}
@ -144,7 +156,7 @@ int read_gsensor_buf(short *buf)
static u8 wr_lock;
int read_gsensor_nbuf(short *buf, short datalen)
{
// printf("%s",__func__);
// xlog("%s",__func__);
if (data_w_cbuf == NULL) {
return 0;
}
@ -161,7 +173,7 @@ int read_gsensor_nbuf(short *buf, short datalen)
return 0;
}
} else {
printf("%s NOT ONLINE ", __func__);
xlog("%s NOT ONLINE ", __func__);
return 0;
}
}
@ -175,91 +187,156 @@ void write_gsensor_data_handle(void)
if (gSensor_info->init_flag == 1) {
// if (read_write_status == 0) {
// printf("%s ",__func__);
// xlog("%s ",__func__);
// return;
// }
if (!gpio_read(platform_data->gSensor_int_io)) {
gSensor_hdl->gravity_sensor_ctl(READ_GSENSOR_DATA, accel_data);
/*for(int i=0;i<29;i++){
printf("cnt:%1d x:%5d y:%5d z:%5d\n", i, accel_data[i].x, accel_data[i].y, accel_data[i].z);
xlog("cnt:%1d x:%5d y:%5d z:%5d\n", i, accel_data[i].x, accel_data[i].y, accel_data[i].z);
}*/
u8 wlen;
wlen = cbuf_write(data_w_cbuf, accel_data, 2 * 3 * 29);
/* for(int i=0;i<29;i++){ */
/* printf("sour x=%06d y=%06d z=%06d",accel_data[i].x,accel_data[i].y,accel_data[i].z); */
/* xlog("sour x=%06d y=%06d z=%06d",accel_data[i].x,accel_data[i].y,accel_data[i].z); */
/* } */
if (wlen == 0) {
printf("data_w_cbuf_full");
xlog("data_w_cbuf_full");
}
}
} else {
// printf("%s ",__func__);
// xlog("%s ",__func__);
return ;
}
}
// 临时的设备扫描诊断函数
void i2c_scanner_probe(u8* device_addr, u8* found_number)
{
printf("Starting I2C bus scan...\n");
int devices_found = 0;
// I2C地址范围是 0x08 到 0x77
for (uint8_t addr_7bit = 0x00; addr_7bit < 0x7F; addr_7bit++)
{
// 构建8位的写地址
uint8_t write_addr_8bit = (addr_7bit << 1);
iic_start(gSensor_info->iic_hdl);
// 尝试发送写地址,并检查返回值
// iic_tx_byte 返回 1 表示收到了 ACK
if (iic_tx_byte(gSensor_info->iic_hdl, write_addr_8bit))
{
device_addr[devices_found] = addr_7bit;
printf("=====================================================================\n");
printf("I2C device found at 7-bit address: 0x%02X\n", addr_7bit);
printf("I2C device found at 8-bit address: 0x%02X\n", write_addr_8bit);
printf("=====================================================================\n");
devices_found++;
}
iic_stop(gSensor_info->iic_hdl);
delay(gSensor_info->iic_delay); // 短暂延时
}
*found_number = devices_found;
if (devices_found == 0) {
printf("Scan finished. No I2C devices found.\n");
} else {
printf("Scan finished. Found %d device(s).\n", devices_found);
}
}
char w_log_buffer_1[100];
char w_log_buffer_2[100];
char w_log_buffer_3[100];
char w_log_buffer_4[100];
char w_log_buffer_5[100];
u8 gravity_sensor_command(u8 w_chip_id, u8 register_address, u8 function_command)
{
// spin_lock(&sensor_iic);
/* os_mutex_pend(&SENSOR_IIC_MUTEX,0); */
u8 ret = 1;
// xlog("iic_start\n");
iic_start(gSensor_info->iic_hdl);
// xlog("iic_tx_byte id\n");
if (0 == iic_tx_byte(gSensor_info->iic_hdl, w_chip_id)) {
ret = 0;
log_e("\n gsen iic wr err 0\n");
xlog("\n gsen iic wr err 0\n");
strcpy(&w_log_buffer_1, "gsen iic wr err 0\n");
goto __gcend;
}
// xlog("iic delay\n");
delay(gSensor_info->iic_delay);
// xlog("iic_tx_byte: address\n");
if (0 == iic_tx_byte(gSensor_info->iic_hdl, register_address)) {
ret = 0;
log_e("\n gsen iic wr err 1\n");
xlog("\n gsen iic wr err 1\n");
strcpy(&w_log_buffer_2, "gsen iic wr err 1\n");
goto __gcend;
}
delay(gSensor_info->iic_delay);
// xlog("iic_tx_byte: command\n");
if (0 == iic_tx_byte(gSensor_info->iic_hdl, function_command)) {
ret = 0;
log_e("\n gsen iic wr err 2\n");
xlog("\n gsen iic wr err 2\n");
strcpy(&w_log_buffer_3, "gsen iic wr err 3\n");
goto __gcend;
}
strcpy(&w_log_buffer_4, "gsen iic wr sucess\n");
// xlog("\n gsen iic wr sucess\n");
__gcend:
iic_stop(gSensor_info->iic_hdl);
// spin_unlock(&sensor_iic);
/* os_mutex_post(&SENSOR_IIC_MUTEX); */
return ret;
}
char sen_log_buffer_1[100];
char sen_log_buffer_2[100];
char sen_log_buffer_3[100];
char sen_log_buffer_4[100];
char sen_log_buffer_5[100];
u8 _gravity_sensor_get_ndata(u8 r_chip_id, u8 register_address, u8 *buf, u8 data_len)
{
// printf("%s",__func__);
// xlog("%s",__func__);
// spin_lock(&sensor_iic);
/* os_mutex_pend(&SENSOR_IIC_MUTEX,0); */
u8 read_len = 0;
iic_start(gSensor_info->iic_hdl);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, r_chip_id - 1)) {
log_e("\n gsen iic rd err 0\n");
xlog("I2C NACK on writing ADDR: 0x%X\n", r_chip_id - 1);
read_len = 0;
strcpy(&sen_log_buffer_1, "gsen iic rd err 0\n");
goto __gdend;
}
delay(gSensor_info->iic_delay);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, register_address)) {
log_e("\n gsen iic rd err 1\n");
xlog("I2C NACK on register ADDR: 0x%X\n", register_address);
// xlog("\n gsen iic rd err 1\n");
read_len = 0;
strcpy(&sen_log_buffer_2, "gsen iic rd err 1\n");
goto __gdend;
}
iic_start(gSensor_info->iic_hdl);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, r_chip_id)) {
log_e("\n gsen iic rd err 2\n");
xlog("\n gsen iic rd err 2\n");
read_len = 0;
strcpy(&sen_log_buffer_3, "gsen iic rd err 2\n" );
goto __gdend;
}
@ -272,14 +349,16 @@ u8 _gravity_sensor_get_ndata(u8 r_chip_id, u8 register_address, u8 *buf, u8 data
*buf = iic_rx_byte(gSensor_info->iic_hdl, 0);
read_len ++;
strcpy(&sen_log_buffer_4, "gsen iic rd success\n");
// xlog("\n gsen iic rd success\n");
__gdend:
iic_stop(gSensor_info->iic_hdl);
delay(gSensor_info->iic_delay);
// spin_unlock(&sensor_iic);
/* os_mutex_post(&SENSOR_IIC_MUTEX); */
// strcpy(&sen_log_buffer_5, "gsen iic rd err\n");
return read_len;
}
void gsensor_io_ctl(u8 cmd, void *arg)
@ -302,13 +381,13 @@ int gravity_sensor_init(void *_data)
gSensor_info->iic_hdl = platform_data->iic;
retval = iic_init(gSensor_info->iic_hdl);
log_e("\n gravity_sensor_init\n");
xlog("\n gravity_sensor_init\n");
if (retval < 0) {
log_e("\n open iic for gsensor err\n");
xlog("\n open iic for gsensor err\n");
return retval;
} else {
log_info("\n iic open succ\n");
xlog("\n iic open succ\n");
}
retval = -EINVAL;
@ -320,14 +399,14 @@ int gravity_sensor_init(void *_data)
}
if (retval < 0) {
log_e(">>>gSensor_hdl logo err\n");
xlog(">>>gSensor_hdl logo err\n");
return retval;
}
if (gSensor_hdl->gravity_sensor_init()) {
log_e(">>>>gSensor_Int ERROR\n");
xlog(">>>>gSensor_Int ERROR\n");
} else {
log_info(">>>>gSensor_Int SUCC\n");
xlog(">>>>gSensor_Int SUCC\n");
gSensor_info->init_flag = 1;
if (platform_data->gSensor_int_io != -1) {
gpio_set_pull_up(platform_data->gSensor_int_io, 1);
@ -336,7 +415,7 @@ int gravity_sensor_init(void *_data)
gpio_set_die(platform_data->gSensor_int_io, 1);
data_buf = zalloc(BUF_SIZE);
if (data_buf == NULL) {
printf("gsensor_cbuf_error!");
xlog("gsensor_cbuf_error!");
return 0;
}
@ -347,7 +426,7 @@ int gravity_sensor_init(void *_data)
cbuf_init(data_w_cbuf, data_buf, BUF_SIZE);
/* port_edge_wkup_set_callback(write_gsensor_data_handle); */
/* 已改为使用port_edge_wkup_set_callback_by_index,使用时需要重新实现 */
printf("cbuf_init");
xlog("cbuf_init");
// spin_lock_init(&iic_lock);
// sys_s_hi_timer_add(NULL, gSensor_int_io_detect, 10); //10ms
@ -388,7 +467,7 @@ int gsensor_enable(void)
//工作空间
data_buf = zalloc(BUF_SIZE);
if (data_buf == NULL) {
printf("gsensor_cbuf_error!");
xlog("gsensor_cbuf_error!");
return -1;
}
data_w_cbuf = zalloc(sizeof(cbuffer_t));
@ -396,7 +475,7 @@ int gsensor_enable(void)
return -1;
}
cbuf_init(data_w_cbuf, data_buf, BUF_SIZE);
printf("cbuf_init");
xlog("cbuf_init");
//设置参数
valid = 0;
gSensor_hdl->gravity_sensor_ctl(GSENSOR_RESET_INT, &valid);
@ -404,6 +483,6 @@ int gsensor_enable(void)
if (valid == -1) {
return -1;
}
printf("gsensor_reset_succeed\n");
xlog("gsensor_reset_succeed\n");
return 0;
}

View File

@ -49,7 +49,7 @@
#include "classic/tws_api.h"
#include "rcsp_adv_user_update.h"
#include "bt_tws.h"
// #include "le_trans_data.h"
#if (TCFG_BLE_DEMO_SELECT == DEF_BLE_DEMO_ADV_RCSP)
#if TCFG_CHARGE_BOX_ENABLE
@ -86,8 +86,10 @@ static const char user_tag_string[] = {EIR_TAG_STRING};
/* #include "debug.h" */
//------
#define ATT_LOCAL_PAYLOAD_SIZE (200) //note: need >= 20
#define ATT_SEND_CBUF_SIZE (512) //note: need >= 20,缓存大小,可修改
// #define ATT_LOCAL_PAYLOAD_SIZE (200*10) //note: need >= 20
// #define ATT_SEND_CBUF_SIZE (1024 * 50) //note: need >= 20,缓存大小,可修改
#define ATT_LOCAL_PAYLOAD_SIZE (517) //note: need >= 20
#define ATT_SEND_CBUF_SIZE (1024 * 2) //note: need >= 20,缓存大小,可修改
#define ATT_RAM_BUFSIZE (ATT_CTRL_BLOCK_SIZE + ATT_LOCAL_PAYLOAD_SIZE + ATT_SEND_CBUF_SIZE) //note:
static u8 att_ram_buffer[ATT_RAM_BUFSIZE] __attribute__((aligned(4)));
//---------------
@ -109,9 +111,10 @@ static const uint8_t sm_min_key_size = 7;
static const uint8_t connection_update_enable = 1; ///0--disable, 1--enable
static uint8_t connection_update_cnt = 0; //
static const struct conn_update_param_t connection_param_table[] = {
{16, 24, 16, 600},
{12, 28, 14, 600},//11
{8, 20, 20, 600},//3.7
{6, 12, 0, 400},//3.7
// {16, 24, 16, 600},
// {12, 28, 14, 600},//11
// {8, 20, 20, 600},//3.7
/* {12, 28, 4, 600},//3.7 */
/* {12, 24, 30, 600},//3.05 */
};
@ -299,6 +302,17 @@ void test_data_send_packet(void)
static void can_send_now_wakeup(void)
{
// static signed char acc_data_buf[60] = {
// 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22
// };
// extern void send_data_to_ble_client(const u8* data, u16 length);
// send_data_to_ble_client(&acc_data_buf, 60);
putchar('E');
if (ble_resume_send_wakeup) {
ble_resume_send_wakeup();
@ -315,6 +329,24 @@ u8 ble_update_get_ready_jump_flag(void)
return 0;
}
// 添加到 le_rcsp_adv_module.c
static void set_connection_data_length(u16 tx_octets, u16 tx_time) //xtell
{
if (con_handle) {
ble_user_cmd_prepare(BLE_CMD_SET_DATA_LENGTH, 3, con_handle, tx_octets, tx_time);
}
}
static void set_connection_data_phy(u8 tx_phy, u8 rx_phy)//xtell
{
if (0 == con_handle) {
return;
}
u8 all_phys = 0;
u16 phy_options = 0; // 根据你的 SDK 定义调整
ble_user_cmd_prepare(BLE_CMD_SET_PHY, 5, con_handle, all_phys, tx_phy, rx_phy, phy_options);
}
/*
* @section Packet Handler
*
@ -370,6 +402,16 @@ static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
connection_update_complete_success(packet);
break;
}
case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE: //xtell
log_info("APP HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE\n");
set_connection_data_phy(2, 2); // 2 表示 2M PHY
break;
case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE://xtell
log_info("APP HCI_SUBEVENT_LE_PHY_UPDATE %s\n", packet[4] ? "Fail" : "Succ"); // packet[4] 是 status
log_info("Tx PHY: %d\n", packet[5]); // packet[5] 是 TX_PHY
log_info("Rx PHY: %d\n", packet[6]); // packet[6] 是 RX_PHY
break;
break;
case HCI_EVENT_DISCONNECTION_COMPLETE:
@ -410,6 +452,7 @@ static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3;
log_info("ATT MTU = %u\n", mtu);
ble_user_cmd_prepare(BLE_CMD_ATT_MTU_SIZE, 1, mtu);
set_connection_data_length(251, 2120);//xtell
break;
case HCI_EVENT_VENDOR_REMOTE_TEST:
@ -546,6 +589,8 @@ static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_h
app_recieve_callback(0, buffer, buffer_size);
}
// JL_rcsp_auth_recieve(data, len);
extern void le_user_app_send_event(size_t command, unsigned char* data, size_t size);
le_user_app_send_event(ATT_CHARACTERISTIC_ae01_01_VALUE_HANDLE, buffer, buffer_size);
break;
@ -569,6 +614,7 @@ static int app_send_user_data(u16 handle, u8 *data, u16 len, u8 handle_type)
ret = ble_user_cmd_prepare(BLE_CMD_ATT_SEND_DATA, 4, handle, data, len, handle_type);
if (ret == BLE_BUFFER_FULL) {
printf("app_send_user_data: buffer full\n");
ret = APP_BLE_BUFF_FULL;
}
@ -588,7 +634,7 @@ static const u8 dueros_dma_uuid_16bit[] = {0x04, 0xFE};
extern u8 *get_chargebox_adv_addr(void);
#endif
static void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf)
void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf)
{
#if (MUTIl_CHARGING_BOX_EN)
u8 *mac_addr = get_chargebox_adv_addr();
@ -1423,9 +1469,25 @@ void ble_module_enable(u8 en)
#if(TCFG_CHARGE_BOX_ENABLE)
extern u8 get_chgbox_lid_status(void);
#endif
void user_ble_gap_device_set(char* name){ //xtell-set
if(strlen(name) < BT_NAME_LEN_MAX){
strcpy(gap_device_name,name);
//刷新广播
bt_ble_adv_enable(0);
make_set_adv_data();
make_set_rsp_data();
bt_ble_adv_enable(1);
}
}
void bt_ble_init(void)
{
log_info("***** ble_init******\n");
//xtell-set
// extern char xt_ble_new_name[9];
// user_ble_gap_device_set(xt_ble_new_name);
gap_device_name = bt_get_local_name();
gap_device_name_len = strlen(gap_device_name);
@ -1645,12 +1707,8 @@ void send_version_to_sibling(void)
data[2] = ver >> 8;
tws_api_send_data_to_sibling(data, sizeof(data), TWS_FUNC_ID_SEQ_RAND_SYNC);
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////
void send_data_to_ble_client(const u8* data, u16 length)
{
// 检查数据长度是否有效
@ -1659,8 +1717,8 @@ void send_data_to_ble_client(const u8* data, u16 length)
return;
}
// 检查缓冲区空间
if (app_send_user_data_check(length)) {
// // 检查缓冲区空间
// if (app_send_user_data_check(length)) {
// 发送数据
int ret = app_send_user_data(ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE, data, length, ATT_OP_NOTIFY);
if (ret == 0) { // 假设 0 表示成功
@ -1668,7 +1726,12 @@ void send_data_to_ble_client(const u8* data, u16 length)
} else {
// printf("Failed to send data: Length %d, Error code: %d\n", length, ret);
}
} else {
// } else {
// printf("Insufficient buffer space to send data: Length %d\n", length);
}
// }
}
#endif

View File

@ -530,6 +530,7 @@ void JL_rcsp_msg_deal(void *hdl, u8 event, u8 *msg)
case MSG_JL_ENTER_UPDATE_MODE:
rcsp_printf("MSG_JL_ENTER_UPDATE_MODE:%x %x\n", msg[0], msg[1]);
clk_set("sys",96*1000000L);
bt_set_low_latency_mode(0);
if (support_dual_bank_update_en && !tws_api_get_role()) {
u8 status = 0;

View File

@ -81,9 +81,9 @@ extern void printf_buf(u8 *buf, u32 len);
//------
//ATT发送的包长, note: 20 <=need >= MTU
#define ATT_LOCAL_MTU_SIZE (200) //
#define ATT_LOCAL_MTU_SIZE (517) //200
//ATT缓存的buffer大小, note: need >= 20,可修改
#define ATT_SEND_CBUF_SIZE (512) //
#define ATT_SEND_CBUF_SIZE (512 * 20) //
//共配置的RAM
#define ATT_RAM_BUFSIZE (ATT_CTRL_BLOCK_SIZE + ATT_LOCAL_MTU_SIZE + ATT_SEND_CBUF_SIZE) //note:
@ -125,9 +125,10 @@ static uint8_t connection_update_cnt = 0; //
//参数表
static const struct conn_update_param_t connection_param_table[] = {
{16, 24, 10, 600},//11
{12, 28, 10, 600},//3.7
{8, 20, 10, 600},
{6, 12, 0, 400},//11
// {16, 24, 10, 600},//11
// {12, 28, 10, 600},//3.7
// {8, 20, 10, 600},
/* {12, 28, 4, 600},//3.7 */
/* {12, 24, 30, 600},//3.05 */
};
@ -706,6 +707,8 @@ static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_h
u16 handle = att_handle;
log_info("write_callback, handle= 0x%04x,size = %d\n", handle, buffer_size);
extern void le_user_app_event(u8* buffer);
le_user_app_event(buffer);
switch (handle) {
@ -1389,6 +1392,44 @@ void hangup_ans_call_handle(u8 en)
}
#endif
void send_data_to_ble_client(const u8* data, u16 length)
{
// 检查数据长度是否有效
if (length == 0 || length > 512) {
printf("Error: Data length %d is out of range (1-512)\n", length);
return;
}
// // 检查缓冲区空间
// if (app_send_user_data_check(length)) {
// 发送数据
int ret = app_send_user_data(ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE, data, length, ATT_OP_NOTIFY);
if (ret == 0) { // 假设 0 表示成功
// printf("Data sent successfully: Length %d\n", length);
} else {
printf("Failed to send data: Length %d, Error code: %d\n", length, ret);
}
// } else {
// printf("Insufficient buffer space to send data: Length %d\n", length);
// }
}
void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf)
{
#if (MUTIl_CHARGING_BOX_EN)
u8 *mac_addr = get_chargebox_adv_addr();
if (mac_addr) {
swapX(mac_addr, mac_addr_buf, 6);
}
/* printf("mac_addr:"); */
/* printf_buf(mac_addr_buf, 6); */
#else
swapX(bt_get_mac_addr(), mac_addr_buf, 6);
#endif
}
#endif

View File

@ -37,7 +37,7 @@ extern void printf_buf(u8 *buf, u32 len);
//------
//ATT发送的包长, note: 20 <=need >= MTU
#define ATT_LOCAL_MTU_SIZE (517) //
//ATT缓存的buffer大小, note: need >= 20,可修改
//ATT缓存的buffer大小, +: need >= 20,可修改
#if(APP_MAIN == APP_WIRELESS_MIC_2T1)
#define ATT_SEND_CBUF_SIZE (90)
#else
@ -92,7 +92,8 @@ static uint8_t connection_update_cnt = 0; //
//参数表
static const struct conn_update_param_t connection_param_table[] = {
{WIRELESS_BLE_CONNECT_INTERVAL, WIRELESS_BLE_CONNECT_INTERVAL, 0, 100},//11
// {1, 5, 4, 600},//11
{12, 24, 0, 400}, // 建议修改为此值 (15ms - 30ms interval)
};
//共可用的参数组数
@ -421,7 +422,7 @@ static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE:
log_info("APP HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE\n");
set_connection_data_phy(CONN_SET_2M_PHY, CONN_SET_2M_PHY);
// set_connection_data_phy(CONN_SET_2M_PHY, CONN_SET_2M_PHY);
break;
case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE:
@ -453,7 +454,7 @@ static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3;
log_info("ATT MTU = %u\n", mtu);
ble_op_att_set_send_mtu(mtu);
set_connection_data_length(251, 2120);
// set_connection_data_length(251, 2120);
break;
case HCI_EVENT_VENDOR_REMOTE_TEST:
@ -1067,6 +1068,44 @@ void ble_server_send_test_key_num(u8 key_num)
;
}
void send_data_to_ble_client(const u8* data, u16 length)
{
// 检查数据长度是否有效
if (length == 0 || length > 512) {
printf("Error: Data length %d is out of range (1-512)\n", length);
return;
}
// // 检查缓冲区空间
// if (app_send_user_data_check(length)) {
// 发送数据
int ret = app_send_user_data(ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE, data, length, ATT_OP_NOTIFY);
if (ret == 0) { // 假设 0 表示成功
printf("Data sent successfully: Length %d\n", length);
} else {
printf("Failed to send data: Length %d, Error code: %d\n", length, ret);
}
// } else {
// printf("Insufficient buffer space to send data: Length %d\n", length);
// }
}
void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf)
{
#if (MUTIl_CHARGING_BOX_EN)
u8 *mac_addr = get_chargebox_adv_addr();
if (mac_addr) {
swapX(mac_addr, mac_addr_buf, 6);
}
/* printf("mac_addr:"); */
/* printf_buf(mac_addr_buf, 6); */
#else
swapX(bt_get_mac_addr(), mac_addr_buf, 6);
#endif
}
#endif

View File

@ -57,14 +57,14 @@ const struct task_info task_info_table[] = {
#else
{"btstack", 3, 0, 768, 256 },
#endif
{"audio_dec", 5, 0, 800, 128 },
{"aud_effect", 5, 1, 800, 128 },
// {"audio_dec", 5, 0, 800, 128 },
// {"aud_effect", 5, 1, 800, 128 },
/*
*为了防止dac buf太大通话一开始一直解码
*导致编码输入数据需要很大的缓存,这里提高编码的优先级
*/
{"audio_enc", 6, 0, 768, 128 },
{"aec", 2, 1, 768, 128 },
// {"audio_enc", 6, 0, 768, 128 },
// {"aec", 2, 1, 768, 128 },
#if TCFG_AUDIO_HEARING_AID_ENABLE
{"HearingAid", 6, 0, 768, 128 },
#endif/*TCFG_AUDIO_HEARING_AID_ENABLE*/
@ -86,9 +86,9 @@ const struct task_info task_info_table[] = {
{"tws_ota_msg", 2, 0, 256, 128 },
{"dw_update", 2, 0, 256, 128 },
{"rcsp_task", 2, 0, 640, 128 },
{"aud_capture", 4, 0, 512, 256 },
{"data_export", 5, 0, 512, 256 },
{"anc", 3, 1, 512, 128 },
// {"aud_capture", 4, 0, 512, 256 },
// {"data_export", 5, 0, 512, 256 },
// {"anc", 3, 1, 512, 128 },
#endif
#if TCFG_GX8002_NPU_ENABLE
@ -102,9 +102,9 @@ const struct task_info task_info_table[] = {
#if TCFG_KWS_VOICE_RECOGNITION_ENABLE
{"kws", 2, 0, 256, 64 },
#endif /* #if TCFG_KWS_VOICE_RECOGNITION_ENABLE */
{"usb_msd", 1, 0, 512, 128 },
// {"usb_msd", 1, 0, 512, 128 },
#if !TCFG_USB_MIC_CVP_ENABLE
{"usbmic_write", 2, 0, 256, 128 },
// {"usbmic_write", 2, 0, 256, 128 },
#endif
#if AI_APP_PROTOCOL
{"app_proto", 2, 0, 768, 64 },
@ -112,12 +112,12 @@ const struct task_info task_info_table[] = {
#if (TCFG_SPI_LCD_ENABLE||TCFG_SIMPLE_LCD_ENABLE)
{"ui", 2, 0, 768, 256 },
#else
{"ui", 3, 0, 384 - 64, 128 },
// {"ui", 3, 0, 384 - 64, 128 },
#endif
#if (TCFG_DEV_MANAGER_ENABLE)
{"dev_mg", 3, 0, 512, 512 },
#endif
{"audio_vad", 1, 1, 512, 128 },
// {"audio_vad", 1, 1, 512, 128 },
#if TCFG_KEY_TONE_EN
{"key_tone", 5, 0, 256, 32 },
#endif
@ -137,7 +137,7 @@ const struct task_info task_info_table[] = {
{"icsd_src", 2, 1, 512, 128 },
#endif /*TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN*/
{"pmu_task", 6, 0, 256, 128 },
{"WindDetect", 2, 0, 256, 128 },
// {"WindDetect", 2, 0, 256, 128 },
{0, 0},
};

View File

@ -249,7 +249,7 @@ const struct vad_mic_platform_data vad_mic_data = {
.mic_ldo2PAD_en = 1,
.mic_bias_en = 0,
.mic_bias_res = 0,
.mic_bias_inside = TCFG_AUDIO_MIC0_BIAS_EN,
// .mic_bias_inside = TCFG_AUDIO_MIC0_BIAS_EN,
/* ADC偏置电阻配置*/
.adc_rbs = 3,
/* ADC输入电阻配置*/
@ -520,11 +520,13 @@ const struct hw_iic_config hw_iic_cfg[] = {
{IO_PORTC_02, IO_PORTC_03}, //group c
{IO_PORTA_05, IO_PORTA_06}, //group d
*/
.port = TCFG_HW_I2C0_PORTS,
// .port = TCFG_HW_I2C0_PORTS,
// .port = {IO_PORTC_04,IO_PORTC_05}, // portB: scl、sda
.port = {IO_PORTB_04,IO_PORTB_05}, // portA: scl、sda
.baudrate = TCFG_HW_I2C0_CLK, //IIC通讯波特率
.hdrive = 0, //是否打开IO口强驱
.io_filter = 1, //是否打开滤波器(去纹波)
.io_pu = 1, //是否打开上拉电阻如果外部电路没有焊接上拉电阻需要置1
.io_pu = 0, //是否打开上拉电阻如果外部电路没有焊接上拉电阻需要置1
},
};
@ -715,7 +717,7 @@ struct port_wakeup port0 = {
.pullup_down_enable = ENABLE, //配置I/O 内部上下拉是否使能
.edge = FALLING_EDGE, //唤醒方式选择,可选:上升沿\下降沿
.filter = PORT_FLT_8ms,
.iomap = IO_PORTB_01, //唤醒口选择
.iomap = IO_PORTA_04, //唤醒口选择
};
#if (TCFG_TEST_BOX_ENABLE || TCFG_CHARGESTORE_ENABLE || TCFG_ANC_BOX_ENABLE || TCFG_UMIDIGI_BOX_ENABLE)

View File

@ -21,10 +21,12 @@
//*********************************************************************************//
// UART配置 //
//*********************************************************************************//
#define TCFG_UART0_ENABLE ENABLE_THIS_MOUDLE //串口打印模块使能
// #define TCFG_UART0_ENABLE ENABLE_THIS_MOUDLE //串口打印模块使能
#define TCFG_UART0_ENABLE 0 //串口打印模块使能
#define TCFG_UART0_RX_PORT NO_CONFIG_PORT //串口接收脚配置用于打印可以选择NO_CONFIG_PORT
#define TCFG_UART0_TX_PORT IO_PORT_DP //串口发送脚配置
#define TCFG_UART0_BAUDRATE 1000000 //串口波特率配置
// #define TCFG_UART0_BAUDRATE 115200 //串口波特率配置
//*********************************************************************************//
// IIC配置 //
@ -34,15 +36,18 @@
#define TCFG_SW_I2C0_DAT_PORT IO_PORTA_06 //软件IIC DAT脚选择
#define TCFG_SW_I2C0_DELAY_CNT 10 //IIC延时参数影响通讯时钟频率
/*硬件IIC端口选择
/*硬件IIC端口选择 -- 具体看手册,这里写的不准 -- lmx
SCL SDA
'A': IO_PORT_DP IO_PORT_DM
'B': IO_PORTA_09 IO_PORTA_10
'C': IO_PORTA_07 IO_PORTA_08
'D': IO_PORTA_05 IO_PORTA_06
具体要选择哪个iic口去board_jl701n_demo.c中设置hw_iic_cfg
*/
#define TCFG_HW_I2C0_PORTS 'B'
#define TCFG_HW_I2C0_CLK 100000 //硬件IIC波特率
#define TCFG_HW_I2C0_CLK 1000000 //硬件IIC波特率:400k
//*********************************************************************************//
// 硬件SPI 配置 //
@ -82,7 +87,7 @@
//*********************************************************************************//
// USB 配置 //
//*********************************************************************************//
#define TCFG_PC_ENABLE 1//DISABLE_THIS_MOUDLE//PC模块使能
#define TCFG_PC_ENABLE 0//DISABLE_THIS_MOUDLE//PC模块使能
#define TCFG_UDISK_ENABLE 0//ENABLE_THIS_MOUDLE//U盘模块使能
#define TCFG_OTG_USB_DEV_EN BIT(0)//USB0 = BIT(0) USB1 = BIT(1)
@ -101,7 +106,7 @@
#define MULT_KEY_ENABLE 1//DISABLE //是否使能组合按键消息, 使能后需要配置组合按键映射表
#define TCFG_KEY_TONE_EN ENABLE//DISABLE xtell // 按键提示音。
#define TCFG_KEY_TONE_EN DISABLE//DISABLE xtell // 按键提示音。
//*********************************************************************************//
// iokey 配置 //
@ -724,9 +729,9 @@ DAC硬件上的连接方式,可选的配置:
// 充电舱/蓝牙测试盒/ANC测试三者为同级关系,开启任一功能都会初始化PP0通信接口 //
//*********************************************************************************//
#define TCFG_CHARGESTORE_ENABLE DISABLE_THIS_MOUDLE //是否支持智能充电舱
#define TCFG_TEST_BOX_ENABLE ENABLE_THIS_MOUDLE //是否支持蓝牙测试盒
#define TCFG_TEST_BOX_ENABLE DISABLE_THIS_MOUDLE//ENABLE_THIS_MOUDLE //是否支持蓝牙测试盒 //xtell
#define TCFG_ANC_BOX_ENABLE CONFIG_ANC_ENABLE //是否支持ANC测试盒
#define TCFG_UMIDIGI_BOX_ENABLE ENABLE_THIS_MOUDLE //是否支持UMIDIGI充电舱
#define TCFG_UMIDIGI_BOX_ENABLE DISABLE_THIS_MOUDLE //是否支持UMIDIGI充电舱 //xtell
#if TCFG_UMIDIGI_BOX_ENABLE
#define _20MS_BIT 20 //传输20ms/Bit时使用
#define _40MS_BIT 40 //传输40ms/Bit时使用
@ -802,7 +807,7 @@ DAC硬件上的连接方式,可选的配置:
// EQ配置 //
//*********************************************************************************//
//EQ配置使用在线EQ时EQ文件和EQ模式无效。有EQ文件时使能TCFG_USE_EQ_FILE,默认不用EQ模式切换功能
#define TCFG_EQ_ENABLE 1 //支持EQ功能,EQ总使能
#define TCFG_EQ_ENABLE 0 //支持EQ功能,EQ总使能
// #if TCFG_EQ_ENABLE
#define TCFG_EQ_ONLINE_ENABLE 0 //支持在线EQ调试,如果使用蓝牙串口调试,需要打开宏 APP_ONLINE_DEBUG否则默认使用uart调试(二选一)
#define TCFG_BT_MUSIC_EQ_ENABLE 1 //支持蓝牙音乐EQ
@ -915,7 +920,7 @@ DAC硬件上的连接方式,可选的配置:
#define TCFG_STK8321_EN 0
#define TCFG_IRSENSOR_ENABLE 0
#define TCFG_JSA1221_ENABLE 0
#define TCFG_GSENOR_USER_IIC_TYPE 0 //0:软件IIC 1:硬件IIC
#define TCFG_GSENOR_USER_IIC_TYPE 1 //0:软件IIC 1:硬件IIC
//*********************************************************************************//
// imu-sensor配置 //
@ -1005,7 +1010,7 @@ DAC硬件上的连接方式,可选的配置:
//*********************************************************************************//
#define TCFG_USER_TWS_ENABLE 0 //tws功能使能
#define TCFG_USER_BLE_ENABLE 1 //BLE功能使能
#define TCFG_BT_SUPPORT_AAC 1 //AAC格式支持
#define TCFG_BT_SUPPORT_AAC 0 //AAC格式支持
#define TCFG_BT_SUPPORT_LDAC 0 //LDAC格式支持
#if TCFG_BT_SUPPORT_LDAC
@ -1079,7 +1084,7 @@ DAC硬件上的连接方式,可选的配置:
// 编解码格式配置(CodecFormat) //
//*********************************************************************************//
/*解码格式使能*/
#define TCFG_DEC_MP3_ENABLE ENABLE
#define TCFG_DEC_MP3_ENABLE DISABLE
#define TCFG_DEC_WTGV2_ENABLE ENABLE
#define TCFG_DEC_G729_ENABLE DISABLE
#define TCFG_DEC_WMA_ENABLE DISABLE
@ -1125,7 +1130,7 @@ DAC硬件上的连接方式,可选的配置:
//*********************************************************************************//
#if TCFG_USER_BLE_ENABLE
#define DUEROS_DMA_EN 0
#define TRANS_DATA_EN 0//1 //xtellota
// #define TRANS_DATA_EN 0//1 //xtellota
#define BLE_HID_EN 0
#if (DUEROS_DMA_EN)

View File

@ -8,12 +8,12 @@
/* Following Macros Affect Periods Of Both Code Compiling And Post-build */
#define CONFIG_DOUBLE_BANK_ENABLE 0 //单双备份选择(若打开了改宏,FLASH结构变为双备份结构适用于接入第三方协议的OTA PS: JL-OTA同样支持双备份升级, 需要根据实际FLASH大小同时配置CONFIG_FLASH_SIZE)
#define CONFIG_APP_OTA_ENABLE 0 //是否支持RCSP升级(JL-OTA)
#define CONFIG_DOUBLE_BANK_ENABLE 1 //单双备份选择(若打开了改宏,FLASH结构变为双备份结构适用于接入第三方协议的OTA PS: JL-OTA同样支持双备份升级, 需要根据实际FLASH大小同时配置CONFIG_FLASH_SIZE)
#define CONFIG_APP_OTA_ENABLE 1 //是否支持RCSP升级(JL-OTA)
#define CONFIG_UPDATE_JUMP_TO_MASK 0 //配置升级到loader的方式0为直接reset,1为跳转(适用于芯片电源由IO口KEEP住的方案,需要注意检查跳转前是否将使用DMA的硬件模块全部关闭)
#define CONFIG_IO_KEY_EN 0 //配置是否使用IO按键配合RESET1
#define CONFIG_IO_KEY_EN 1 //配置是否使用IO按键配合RESET1
#define CONFIG_UPDATE_WITH_MD5_CHECK_EN 0 //配置升级是否支持MD5校验
#define CONFIG_ANC_ENABLE 0 //配置是否支持ANC
@ -33,7 +33,7 @@
/* Following Macros Only For Post Bulid Configuaration */
#define CONFIG_DB_UPDATE_DATA_GENERATE_EN 0 //是否生成db_data.bin(用于第三方协议接入使用)
#define CONFIG_ONLY_GRENERATE_ALIGN_4K_CODE 0 //ufw只生成1份4K对齐的代码
#define CONFIG_ONLY_GRENERATE_ALIGN_4K_CODE 1 //ufw只生成1份4K对齐的代码
//config for supported chip version
#ifdef CONFIG_BR30_C_VERSION
@ -73,8 +73,8 @@
#if CONFIG_IO_KEY_EN
#define CONFIG_SUPPORT_RESET1
#define CONFIG_RESET1_PIN PB01 //io pin
#define CONFIG_RESET1_TIME 08 //unit:second
#define CONFIG_RESET1_PIN PA04 //io pin
#define CONFIG_RESET1_TIME 01 //unit:second
#define CONFIG_RESET1_LEVEL 0 //tigger level(0/1)
#endif

View File

@ -21,9 +21,10 @@
#define CONFIG_APP_BT_ENABLE
#ifdef CONFIG_APP_BT_ENABLE
#define TRANS_DATA_EN 0
#define BLE_WIRELESS_SERVER_EN 0
#define TRANS_DATA_EN 1
#define RCSP_BTMATE_EN 0
#define RCSP_ADV_EN 1
#define RCSP_ADV_EN 0
#define AI_APP_PROTOCOL 0
#define LL_SYNC_EN 0
#define TUYA_DEMO_EN 0
@ -65,8 +66,8 @@
#if CONFIG_APP_OTA_ENABLE
#define RCSP_UPDATE_EN 1 //是否支持rcsp升级
#if CONFIG_DOUBLE_BANK_ENABLE //双备份才能打开同步升级流程
#define OTA_TWS_SAME_TIME_ENABLE 1 //是否支持TWS同步升级
#define OTA_TWS_SAME_TIME_NEW 1 //使用新的tws ota流程
#define OTA_TWS_SAME_TIME_ENABLE 0 //是否支持TWS同步升级
#define OTA_TWS_SAME_TIME_NEW 0 //使用新的tws ota流程
#else
#define OTA_TWS_SAME_TIME_ENABLE 1//0 xtellota //是否支持TWS同步升级
#define OTA_TWS_SAME_TIME_NEW 1//0 //使用新的tws ota流程

View File

@ -204,6 +204,18 @@ int app_protocol_sys_event_handler(struct sys_event *event);
#define EARPHONE_STATE_SNIFF(a)
#define EARPHONE_STATE_ROLE_SWITCH(a)
#elif BLE_WIRELESS_SERVER_EN
#define EARPHONE_STATE_INIT() do { } while(0)
#define EARPHONE_STATE_SET_PAGE_SCAN_ENABLE() do { } while(0)
#define EARPHONE_STATE_GET_CONNECT_MAC_ADDR() do { } while(0)
#define EARPHONE_STATE_CANCEL_PAGE_SCAN() do { } while(0)
#define EARPHONE_STATE_ENTER_SOFT_POWEROFF() do { } while(0)
#define EARPHONE_STATE_TWS_INIT(a) do { } while(0)
#define EARPHONE_STATE_TWS_CONNECTED(a, b) do { } while(0)
#define SYS_EVENT_HANDLER_SPECIFIC(a) do { } while(0)
#define SYS_EVENT_REMAP(a) 0
#define EARPHONE_STATE_SNIFF(a)
#define EARPHONE_STATE_ROLE_SWITCH(a)
#else
int adv_earphone_state_init();
int adv_earphone_state_set_page_scan_enable();

View File

@ -232,7 +232,8 @@ const uint64_t config_btctler_le_features = LE_ENCRYPTION;
#else
const int config_btctler_le_roles = (LE_ADV | LE_SLAVE);
const uint64_t config_btctler_le_features = 0;
// const uint64_t config_btctler_le_features = 0;
const uint64_t config_btctler_le_features = LE_ENCRYPTION | LE_DATA_PACKET_LENGTH_EXTENSION | LE_2M_PHY;
#endif
#else
const int config_btctler_le_roles = 0;
@ -280,9 +281,13 @@ const int config_btctler_le_acl_total_nums = 1;
const int config_btctler_le_afh_en = 0;
const u32 config_vendor_le_bb = 0;
const int config_btctler_le_rx_nums = 5;
const int config_btctler_le_acl_packet_length = 27;
const int config_btctler_le_acl_total_nums = 5;
// const int config_btctler_le_rx_nums = 5;
// const int config_btctler_le_acl_packet_length = 27;
// const int config_btctler_le_acl_total_nums = 5;
const int config_btctler_le_rx_nums = 8;
const int config_btctler_le_acl_packet_length = 251;
const int config_btctler_le_acl_total_nums = 8;
#endif
/*-----------------------------------------------------------*/

View File

@ -202,7 +202,9 @@ void cfg_file_parse(u8 idx)
log_info("read bt name err");
} else if (ret >= LOCAL_NAME_LEN) {
memset(bt_cfg.edr_name, 0x00, LOCAL_NAME_LEN);
memcpy(bt_cfg.edr_name, tmp, LOCAL_NAME_LEN);
// memcpy(bt_cfg.edr_name, tmp, LOCAL_NAME_LEN);
extern char xt_ble_new_name[9];
memcpy(bt_cfg.edr_name, xt_ble_new_name, LOCAL_NAME_LEN);
bt_cfg.edr_name[LOCAL_NAME_LEN - 1] = 0;
} else {
memset(bt_cfg.edr_name, 0x00, LOCAL_NAME_LEN);

View File

@ -0,0 +1,219 @@
/*
静态ZUPT+卡尔曼,效果貌似还行
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h" // 包含传感器驱动头文件以调用姿态解算函数
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f // 重力加速度 (m/s^2)
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- 状态检测阈值 ---
// 判断是否静止的加速度阈值 (m/s^2)。当加速度的模长减去重力后,小于此值,则认为可能静止。
#define STATIC_ACC_THRESHOLD 1.0f
// 连续多少帧满足静止条件才确认为静止状态
#define STATIC_FRAMES_REQUIRED 50 // 累加,超过这个数加速度仍变化不大,说明近似静止
/**
* @brief 初始化滑雪追踪器
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = SKIING_STATE_STATIC; // 初始状态为静止
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 修正#1驱动输出的角度与标准航空定义相反需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 修正#2使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
// 这个矩阵将设备测量的加速度(ax, ay, az)正确地转换到世界坐标系(acc_world)。
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
/**
* @brief 更新滑雪状态机
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device)
{
// 计算当前加速度的模长
float acc_magnitude = sqrtf(acc_device[0] * acc_device[0] + acc_device[1] * acc_device[1] + acc_device[2] * acc_device[2]);
// 状态判断逻辑
switch (tracker->state) {
case SKIING_STATE_STATIC:
// 如果加速度变化较大,则切换到滑雪状态
if (fabsf(acc_magnitude - G_ACCELERATION) > STATIC_ACC_THRESHOLD * 2.0f) { // 使用一个更大的阈值来启动
tracker->state = SKIING_STATE_SKIING;
tracker->static_frames_count = 0;
}
break;
case SKIING_STATE_SKIING:
// 检测是否进入静止状态 (零速更新 ZUPT)
if (fabsf(acc_magnitude - G_ACCELERATION) < STATIC_ACC_THRESHOLD) {
tracker->static_frames_count++;
if (tracker->static_frames_count >= STATIC_FRAMES_REQUIRED) {
tracker->state = SKIING_STATE_STATIC;
// 进入静止状态,强制将速度清零以消除漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
}
} else {
// 如果在运动,则重置静止计数器
tracker->static_frames_count = 0;
}
break;
// 可以在此添加摔倒等其他状态的判断
case SKIING_STATE_FALLEN:
// TODO: 添加从摔倒状态恢复的逻辑
break;
default:
tracker->state = SKIING_STATE_UNKNOWN;
break;
}
}
/**
* @brief 主更新函数
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc, float *angle, float dt)
{
if (!tracker || !acc || !angle || dt <= 0) {
return;
}
// 将原始g单位的加速度转换为 m/s^2
float acc_device_ms2[3];
acc_device_ms2[0] = acc[0] * G_ACCELERATION;
acc_device_ms2[1] = acc[1] * G_ACCELERATION;
acc_device_ms2[2] = acc[2] * G_ACCELERATION;
// 更新状态机
update_state_machine(tracker, acc_device_ms2);
// 只有在滑雪状态下才进行计算
if (tracker->state == SKIING_STATE_SKIING) {
// 坐标系转换: 首先,利用姿态角(Pitch, Roll)将传感器测得的总加速度(运动加速度+重力)
// 从不断变化的“设备坐标系”转换到一个固定的“世界坐标系”。在这个世界坐标系里Z轴永远垂直于地面指向上方。
// 执行坐标系转换
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
// 转换完成后重力就变成了一个恒定的、方向沿Z轴向下的矢量。
// 修正#3经过正确的坐标转换后静止时重力在世界坐标系Z轴上的分量精确为+g。
// 必须减去这个g才能得到纯粹的运动加速度。
tracker->acc_world[2] -= G_ACCELERATION;
// 积分计算速度 (v = v0 + a*t)
tracker->velocity[0] += tracker->acc_world[0] * dt;
tracker->velocity[1] += tracker->acc_world[1] * dt;
tracker->velocity[2] += tracker->acc_world[2] * dt; // 垂直方向速度也计算在内
// 计算当前速率
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1] +
tracker->velocity[2] * tracker->velocity[2]);
// 积分计算距离 (d = d0 + v*t)
tracker->distance += tracker->speed * dt;
}
}
// 传感器数据采集与处理任务
void sensor_processing_task(signed short * acc_data_buf,signed short * gyr_data_buf) {
// --- 1. 定义静态变量 ---
static skiing_tracker_t my_skiing_tracker;
static int initialized = 0;
static int calibration_done = 0; // 新增:用于标记一次性静态校准是否完成
static signed short combined_raw_data[6];
static float final_angle_data[3];
static float calibrated_acc_g[3];
// sensor_processing_task的调用频率, dt = 0.001s
const float delta_time = 0.01f;
// --- 2. 初始化 ---
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
// --- 3. 数据处理 ---
//合并加速度和陀螺仪数据
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
unsigned char status;
if (get_calibration_state() == 0) { //正在校准
//领票校准
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
if (status == 1) {
calibration_done = 1;
printf("Sensor calibration successful! Skiing mode is active.\n");
}
} else {
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
}
// c. 检查姿态解算是否成功
if (status == 1) {
// 将校准后的加速度数据从 LSB (原始值) 转换为 g (重力单位)
// ±8g量程下8192 LSB 对应 1g
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, final_angle_data, delta_time);
static count = 0;
if(count < 10){
count++;
return;
}else{
count = 0;
}
printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
my_skiing_tracker.state,
my_skiing_tracker.speed,
my_skiing_tracker.distance);
} else if (status == 0) {
// 传感器正在进行静态校准
// printf("Sensor is calibrating...\n");
} else {
// status == 2, 表示校准失败或发生错误
// printf("Angle calculation error or calibration not finished.\n");
}
}

View File

@ -0,0 +1,41 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
// 定义滑雪者可能的状态
typedef enum {
SKIING_STATE_STATIC, // 静止
SKIING_STATE_SKIING, // 正在滑雪
SKIING_STATE_FALLEN, // 已摔倒
SKIING_STATE_UNKNOWN // 未知状态
} skiing_state_t;
// 追踪器数据结构体
typedef struct {
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 私有成员,用于内部计算
int static_frames_count; // 用于判断静止状态的帧计数器
float acc_world[3]; // 在世界坐标系下的加速度
} skiing_tracker_t;
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
/**
* @brief 处理传感器数据并更新滑雪状态
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
* @param acc 校准后的加速度数据 [x, y, z],单位: g (1g = 9.8m/s^2)
* @param angle 姿态角数据 [pitch, roll, yaw],单位: 度
* @param dt 采样时间间隔,单位: 秒 (s)
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc, float *angle, float dt);
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,596 @@
/*
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- 静止检测 ---
//两个判断是否静止的必要条件:动态零速更新(ZUPT)阈值
// 加速方差阈值,提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define STOP_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define STOP_GYR_VARIANCE_THRESHOLD 5.0f
// 静止时候的陀螺仪模长
#define STOP_GYR_MAG_THRESHOLD 15
// --- --- ---
// --- 启动滑雪阈值 ---
// 加速度模长与重力的差值大于此值,认为开始运动;降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_ACC_MAG_THRESHOLD 1.0f //0.5、1
// 陀螺仪方差阈值,以允许启动瞬间的正常抖动,但仍能过滤掉混乱的、非滑雪的晃动。
#define START_GYR_VARIANCE_THRESHOLD 15.0f
// --- --- ---
// --- 滑雪过程 ---
//加速度 模长(不含重力),低于此值视为 在做匀速运动
#define SKIING_ACC_MAG_THRESHOLD 0.5f
//陀螺仪 模长,高于此值视为 摔倒了
#define FALLEN_GRY_MAG_THRESHOLD 2000.0f //未确定
// --- --- ---
// --- 原地旋转抖动 ---
// 加速度 方差 阈值。此值比 静止检测 阈值更宽松,
#define WOBBLE_ACC_VARIANCE_THRESHOLD 0.5f
// 加速度 模长 阈值
#define WOBBLE_ACC_MAG_THRESHOLD 1.0f
// 角速度 总模长 大于此值(度/秒),认为正在进行非滑雪的旋转或摆动
#define ROTATION_GYR_MAG_THRESHOLD 30.0f
// --- --- ---
// --- 滑雪转弯动 ---
// 加速度 方差 阈值,大于此值,滑雪过程可能发生了急转弯
#define WHEEL_ACC_VARIANCE_THRESHOLD 7.0f
// 角速度 总模长 大于此值(度/秒),认为滑雪过程中进行急转弯
#define WHEEL_GYR_MAG_THRESHOLD 500.0f //
// --- --- ---
// --- 跳跃 ---
// 加速度模长低于此值(g),认为进入失重状态(IN_AIR)
#define AIRBORNE_ACC_MAG_LOW_THRESHOLD 0.4f
// 加速度模长高于此值(g),认为发生落地冲击(LANDING)
#define LANDING_ACC_MAG_HIGH_THRESHOLD 3.5f
// 起跳加速度阈值(g)用于进入TAKING_OFF状态
#define TAKEOFF_ACC_MAG_HIGH_THRESHOLD 1.8f
// 进入空中状态确认计数需要连续3个采样点加速度低于阈值才判断为起跳
#define AIRBORNE_CONFIRM_COUNT 3
// 落地状态确认计数加速度恢复到1g附近并持续2个采样点(20ms)则认为已落地
#define GROUNDED_CONFIRM_COUNT 2
// 最大滞空时间(秒),超过此时间强制认为已落地,防止状态锁死
#define MAX_TIME_IN_AIR 12.5f
// --- --- ---
// --- 用于消除积分漂移的滤波器和阈值 ---
// 高通滤波器系数 (alpha)。alpha 越接近1滤除低频(直流偏移)的效果越强,但可能滤掉真实的慢速运动。
// alpha = RC / (RC + dt)参考RC电路而来fc ≈ (1 - alpha) / (2 * π * dt)
#define HPF_ALPHA 0.999f
//0.995f 0.08 Hz 的信号
//0.999f 0.0159 Hz
// --- --- ---
// --- 低通滤波器 ---
// 低通滤波器系数 (alpha)。alpha 越小,滤波效果越强(更平滑),但延迟越大。
// alpha 推荐范围 0.7 ~ 0.95。可以从 0.85 开始尝试。
#define LPF_ALPHA 0.7f
// 加速度死区阈值 (m/s^2)。低于此阈值的加速度被认为是噪声,不参与积分。
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
//参考0.2f ~ 0.4f
#define ACC_DEAD_ZONE_THRESHOLD 0.05f
// --- 模拟摩擦力,进行速度衰减 ---
#define SPEED_ATTENUATION 1.0f //暂不模拟
BLE_KS_send_data_t KS_data;
#ifdef XTELL_TEST
debug_t debug1;
debug_t debug2;
#endif
static skiing_tracker_t my_skiing_tracker;
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
void clear_speed(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
void start_detection(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.distance = 0;
my_skiing_tracker.speed = 0;
}
void stop_detection(void){
my_skiing_tracker.state = STOP_DETECTION;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = STATIC;
}
/**
* @brief 当检测到落地时,计算空中的水平飞行距离并累加到总距离
*/
static void calculate_air_distance(skiing_tracker_t *tracker) {
float horizontal_speed_on_takeoff = sqrtf(
tracker->initial_velocity_on_takeoff[0] * tracker->initial_velocity_on_takeoff[0] +
tracker->initial_velocity_on_takeoff[1] * tracker->initial_velocity_on_takeoff[1]
);
float distance_in_air = horizontal_speed_on_takeoff * tracker->time_in_air;
tracker->distance += distance_in_air;
}
/**
* @brief 在设备坐标系下,从原始加速度数据中移除重力分量
* @param acc_device 输入:设备坐标系下的原始加速度 [x, y, z], 单位 m/s^2
* @param angle 输入:姿态角 [pitch, roll, yaw],单位: 度
* @param acc_linear_device 输出:设备坐标系下移除重力后的线性加速度 [x, y, z]
*/
void remove_gravity_in_device_frame(const float *acc_device, const float *angle, float *acc_linear_device)
{
float pitch = angle[0] * DEG_TO_RAD; // 绕 Y 轴
float roll = angle[1] * DEG_TO_RAD; // 绕 X 轴
float yaw = angle[2] * DEG_TO_RAD; // 绕 Z 轴
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float cy = cosf(yaw);
float sy = sinf(yaw);
// 世界坐标系下的重力矢量
const float g_world[3] = {0.0f, 0.0f, G_ACCELERATION};
// 计算旋转矩阵 R 的转置矩阵 R_transpose
// R (Z-Y-X) =
// [ cy*cp, cy*sp*sr - sy*cr, cy*sp*cr + sy*sr]
// [ sy*cp, sy*sp*sr + cy*cr, sy*sp*cr - cy*sr]
// [ -sp, cp*sr, cp*cr ]
//
// R_transpose =
// [ cy*cp, sy*cp, -sp ]
// [ cy*sp*sr - sy*cr, sy*sp*sr + cy*cr, cp*sr ]
// [ cy*sp*cr + sy*sr, sy*sp*cr - cy*sr, cp*cr ]
// 计算重力在设备坐标系下的投影 G_device = R_transpose * G_world
// 由于 G_world 只有 z 分量,计算可以简化
float g_device[3];
g_device[0] = (-sp) * g_world[2];
g_device[1] = (cp * sr) * g_world[2];
g_device[2] = (cp * cr) * g_world[2];
// 从原始设备加速度中减去重力投影
acc_linear_device[0] = acc_device[0] - g_device[0];
acc_linear_device[1] = acc_device[1] - g_device[1];
acc_linear_device[2] = acc_device[2] - g_device[2];
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*
* @param buffer 传进来的三轴数据:陀螺仪/加速度
* @return float 返回方差和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 摩擦力模拟,进行速度衰减
*
* @param tracker
*/
void forece_of_friction(skiing_tracker_t *tracker){
// 增加速度衰减,模拟摩擦力
tracker->velocity[0] *= SPEED_ATTENUATION;
tracker->velocity[1] *= SPEED_ATTENUATION;
tracker->velocity[2] = 0; // 垂直速度强制归零
}
/**
* @brief 状态机更新
*
* @param tracker 传入同步修改后传出
* @param acc_device_ms2 三轴加速度m/s^2
* @param gyr_dps 三轴陀螺仪dps
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]); //dps
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]); //m/s^s
float acc_magnitude_g = acc_magnitude / G_ACCELERATION; // 转换为g单位用于跳跃判断
#ifdef XTELL_TEST
debug1.acc_variance =acc_variance;
debug1.gyr_variance =gyr_variance;
debug1.gyr_magnitude=gyr_magnitude;
debug1.acc_magnitude=fabsf(acc_magnitude - G_ACCELERATION);
#endif
// --- 状态机逻辑 (核心修改区域) ---
#if 0 //暂时不考虑空中
// 1. 空中/落地状态的后续处理
if (tracker->state == IN_AIR) {
// A. 检测巨大冲击 -> 落地
if (acc_magnitude_g > LANDING_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = LANDING;
// B. 检测超时 -> 强制落地 (安全机制)
} else if (tracker->time_in_air > MAX_TIME_IN_AIR) {
tracker->state = LANDING;
// C. 检测恢复正常重力 (平缓落地)
} else if (acc_magnitude_g > 0.8f && acc_magnitude_g < 1.5f) {
tracker->grounded_entry_counter++;
if (tracker->grounded_entry_counter >= GROUNDED_CONFIRM_COUNT) {
tracker->state = LANDING;
}
} else {
tracker->grounded_entry_counter = 0;
}
return; // 在空中或刚切换到落地,结束本次状态判断
}
// 2. 严格的 "起跳->空中" 状态转换逻辑
// 只有当处于滑行状态时,才去检测起跳意图
if (tracker->state == NO_CONSTANT_SPEED || tracker->state == CONSTANT_SPEED || tracker->state == WHEEL) {
if (acc_magnitude_g > TAKEOFF_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = TAKING_OFF;
tracker->airborne_entry_counter = 0; // 准备检测失重
return;
}
}
// 只有在TAKING_OFF状态下才去检测是否进入失重
if (tracker->state == TAKING_OFF) {
if (acc_magnitude_g < AIRBORNE_ACC_MAG_LOW_THRESHOLD) {
tracker->airborne_entry_counter++;
if (tracker->airborne_entry_counter >= AIRBORNE_CONFIRM_COUNT) {
memcpy(tracker->initial_velocity_on_takeoff, tracker->velocity, sizeof(tracker->velocity));
tracker->time_in_air = 0;
tracker->state = IN_AIR;
tracker->airborne_entry_counter = 0;
tracker->grounded_entry_counter = 0;
return;
}
} else {
// 如果在起跳冲击后一段时间内没有失重,说明只是一个颠簸,恢复滑行
// 可以加一个小的超时计数器,这里为了简单先直接恢复
tracker->state = NO_CONSTANT_SPEED;
}
return; // 无论是否切换,都结束本次判断
}
#endif
// --- 静止判断 ---
if (acc_variance < STOP_ACC_VARIANCE_THRESHOLD && gyr_variance < STOP_GYR_VARIANCE_THRESHOLD && gyr_magnitude < STOP_GYR_MAG_THRESHOLD) {
tracker->state = STATIC;
return;
}
// --- 地面状态切换逻辑 ---
switch (tracker->state) {
case LANDING:
tracker->state = STATIC;
break;
case STATIC:
// 优先判断是否进入 WOBBLE 状态
// 条件:陀螺仪活动剧烈,但整体加速度变化不大(说明是原地转或晃)
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) < WOBBLE_ACC_MAG_THRESHOLD) {
tracker->state = WOBBLE;
}
// 只有在陀螺仪和加速度都满足“前进”特征时,才启动
else if (gyr_variance > START_GYR_VARIANCE_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
break;
case WOBBLE:
// 从 WOBBLE 状态启动的条件应该和从 STATIC 启动一样严格
if (gyr_variance > START_GYR_VARIANCE_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
// 如果陀螺仪活动减弱,则可能恢复静止
else if (gyr_magnitude < ROTATION_GYR_MAG_THRESHOLD * 0.8f) { // 增加迟滞,避免抖动
// 不直接跳回STATIC而是依赖下一轮的全局静止判断
}
break;
case NO_CONSTANT_SPEED: //非匀速状态
//暂时不考虑摔倒
// if (gyr_magnitude > FALLEN_GRY_MAG_THRESHOLD) {
// tracker->state = FALLEN; //摔倒
// } else
if (gyr_magnitude > WHEEL_GYR_MAG_THRESHOLD && acc_variance > WHEEL_ACC_VARIANCE_THRESHOLD) {
tracker->state = WHEEL; //转弯
} else if (fabsf(acc_magnitude - G_ACCELERATION) < SKIING_ACC_MAG_THRESHOLD) {
tracker->state = CONSTANT_SPEED; //匀速
}
break;
case CONSTANT_SPEED: //匀速状态
if (fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
//TODO可以添加进入转弯或摔倒的判断
break;
case WHEEL:
// 从转弯状态,检查转弯是否结束
// 如果角速度和加速度方差都降下来了,就回到普通滑行状态
if (gyr_magnitude < WHEEL_GYR_MAG_THRESHOLD * 0.8f && acc_variance < WHEEL_ACC_VARIANCE_THRESHOLD * 0.8f) { // 乘以一个滞后系数避免抖动
tracker->state = NO_CONSTANT_SPEED;
}
break;
case FALLEN:
// TODO回到 STATIC
break;
}
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps
* @param angle 欧若拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
if(my_skiing_tracker.state == STOP_DETECTION)
return;
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 根据状态执行不同的计算逻辑 ---
switch (tracker->state) {
case TAKING_OFF:
tracker->speed = 0.0f;
break;
case IN_AIR:
// 在空中时,只累加滞空时间
tracker->time_in_air += dt;
break;
case LANDING:
// 刚落地,计算空中距离
calculate_air_distance(tracker);
// 清理速度和滤波器状态,为恢复地面追踪做准备
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
break;
case WHEEL:
case NO_CONSTANT_SPEED:
remove_gravity_in_device_frame(acc_device_ms2, angle, tracker->acc_no_g);
float acc_world_temp[3]; // 临时变量存储当前周期的加速度
for (int i = 0; i < 2; i++) { // 只处理水平方向的 x 和 y 轴
// --- 核心修改:颠倒滤波器顺序为 HPF -> LPF ---
// 1. 高通滤波 (HPF) 先行: 消除因姿态误差导致的重力泄漏(直流偏置)
// HPF的瞬态响应会产生尖峰这是正常的。
tracker->acc_world_filtered[i] = HPF_ALPHA * (tracker->acc_world_filtered[i] + tracker->acc_no_g[i] - tracker->acc_world_unfiltered_prev[i]);
tracker->acc_world_unfiltered_prev[i] = tracker->acc_no_g[i];
// 2. 低通滤波 (LPF) 殿后: 平滑掉HPF产生的尖峰和传感器自身的高频振动噪声。
// 这里使用 tracker->acc_world_filtered[i] 作为LPF的输入。
tracker->acc_world_lpf[i] = (1.0f - LPF_ALPHA) * tracker->acc_world_filtered[i] + LPF_ALPHA * tracker->acc_world_lpf[i];
// 将最终处理完的加速度值存入临时变量
acc_world_temp[i] = tracker->acc_world_lpf[i];
}
// 计算处理后加速度的水平模长
float acc_horizontal_mag = sqrtf(acc_world_temp[0] * acc_world_temp[0] +
acc_world_temp[1] * acc_world_temp[1]);
#if XTELL_TEST
debug2.acc_magnitude = acc_horizontal_mag;
#endif
// 应用死区,并积分
if (acc_horizontal_mag > ACC_DEAD_ZONE_THRESHOLD) {
tracker->velocity[0] += acc_world_temp[0] * dt;
tracker->velocity[1] += acc_world_temp[1] * dt;
}
// 更新速度和距离
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
tracker->distance += tracker->speed * dt;
break;
case CONSTANT_SPEED:
//保持上次的速度不变。只更新距离
tracker->distance += tracker->speed * dt;
break;
case STATIC:
case WOBBLE:
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
#if XTELL_TEST
debug2.acc_magnitude = 0;
#endif
break;
case FALLEN:
// TODO
break;
default:
break;
}
}
/**
* @brief 滑雪数据计算
*
* @param acc_data_buf 传入的三轴加速度数据
* @param gyr_data_buf 传入的三轴陀螺仪数据
* @param angle_data 传入的欧若拉角数据
* @return BLE_send_data_t 要发送给蓝牙的数据
*/
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data) {
static int initialized = 0;
static float acc_data_g[3];
static float gyr_data_dps[3];
// const float delta_time = DELTA_TIME+0.01f;
// const float delta_time = DELTA_TIME + 0.005f;
const float delta_time = DELTA_TIME;
BLE_send_data_t BLE_send_data;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
#if ACC_RANGE==2
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 16384.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 16384.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 16384.0f;
#endif
#if ACC_RANGE==4
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 8192.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 8192.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 8192.0f;
#endif
#if ACC_RANGE==8
//±8g 4096
acc_data_g[0] = (float)acc_data_buf[0] / 4096.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 4096.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 4096.0f; //az
#endif
#if ACC_RANGE==16
//±16g 2048
acc_data_g[0] = (float)acc_data_buf[0] / 2048.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 2048.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 2048.0f; //az
#endif
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
gyr_data_dps[0] = (float)gyr_data_buf[0] * 0.061f;
gyr_data_dps[1] = (float)gyr_data_buf[1] * 0.061f;
gyr_data_dps[2] = (float)gyr_data_buf[2] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, acc_data_g, gyr_data_dps, angle_data, delta_time);
BLE_send_data.skiing_state = my_skiing_tracker.state;
for (int i = 0; i < 3; i++) {
#ifdef XTELL_TEST
BLE_send_data.acc_data[i] = (short)(acc_data_g[i] * 9.8f) * 100; //cm/^s2
BLE_send_data.gyr_data[i] = (short)gyr_data_dps[i]; //dps
BLE_send_data.angle_data[i] = angle_data[i];
#else
BLE_send_data.acc_data[i] = (short)acc_data_buf[i]; //原始adc数据
BLE_send_data.gyr_data[i] = (short)gyr_data_buf[i]; //原始adc数据
BLE_send_data.angle_data[i] = angle_data[i];
#endif
}
BLE_send_data.speed_cms = (int)(my_skiing_tracker.speed * 100);
BLE_send_data.distance_cm = (int)(my_skiing_tracker.distance * 100);
// printf("Calculate the time interval =============== end\n");
return BLE_send_data;
}

View File

@ -0,0 +1,88 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
// 定义滑雪者可能的状态
typedef enum {
STATIC, // 静止或动态稳定0
NO_CONSTANT_SPEED, // 正在滑雪非匀速1
CONSTANT_SPEED, // 正在滑雪匀速2
WOBBLE, // 正在原地旋转3
WHEEL, // 转弯4
FALLEN, // 已摔倒5
TAKING_OFF, // 起跳冲击阶段6
IN_AIR, // 空中失重阶段7
LANDING, // 落地冲击阶段8
STOP_DETECTION, // 停止检测9
UNKNOWN // 未知状态10
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 5 // 用于计算方差的数据窗口大小 (5个样本 @ 100Hz = 50ms),减小延迟,提高实时性
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_no_g[3]; // 去掉重力分量后的加速度
// 用于空中距离计算
float time_in_air; // 滞空时间计时器
float initial_velocity_on_takeoff[3]; // 起跳瞬间的速度向量
int airborne_entry_counter; // 进入空中状态的确认计数器
int grounded_entry_counter; // 落地确认计数器
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3]; //过滤过的
float acc_world_unfiltered_prev[3]; //上一次没过滤的
float acc_world_lpf[3]; // 经过低通滤波后的世界坐标系加速度
} skiing_tracker_t;
//ble发送的数据
typedef struct{ //__attribute__((packed)){ //该结构体取消内存对齐
char sensor_state;
char skiing_state;
int speed_cms; //求出的速度cm/s
int distance_cm; //求出的距离cm
short acc_data[3]; //三轴加速度, g
short gyr_data[3]; //三轴陀螺仪, dps
float angle_data[3]; //欧若拉角
}BLE_send_data_t;
typedef struct{
int acc_KS[3]; //卡尔曼后LSB转换后的 三轴加速度数据cm/s^2
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
#ifdef XTELL_TEST
typedef struct{
float acc_variance; //三轴加速度方差之和
float gyr_variance; //三轴陀螺仪方差之和
float acc_magnitude; //三轴加速度模长
float gyr_magnitude; //三轴陀螺仪模长
}debug_t;
#endif
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data) ;
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,377 @@
/*
使用四元数求角度和去掉重力分量
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define ENABLE_XLOG 1
#ifdef xlog
#undef xlog
#endif
#if ENABLE_XLOG
#define xlog(format, ...) printf("[XT:%s] " format, __func__, ##__VA_ARGS__)
#else
#define xlog(format, ...) ((void)0)
#endif
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
BLE_KS_send_data_t KS_data;
static float quaternion_data[4];
#ifdef XTELL_TEST
debug_t debug1;
debug_t debug2;
#endif
static skiing_tracker_t my_skiing_tracker;
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
void clear_speed(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
void start_detection(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.distance = 0;
my_skiing_tracker.speed = 0;
}
void stop_detection(void){
my_skiing_tracker.state = STOP_DETECTION;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = STATIC;
}
/**
* @brief 当检测到落地时,计算空中的水平飞行距离并累加到总距离
*/
static void calculate_air_distance(skiing_tracker_t *tracker) {
float horizontal_speed_on_takeoff = sqrtf(
tracker->initial_velocity_on_takeoff[0] * tracker->initial_velocity_on_takeoff[0] +
tracker->initial_velocity_on_takeoff[1] * tracker->initial_velocity_on_takeoff[1]
);
float distance_in_air = horizontal_speed_on_takeoff * tracker->time_in_air;
tracker->distance += distance_in_air;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系,去掉重力分量
*
* @param acc_device
* @param angle
* @param acc_linear_world
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_linear_world)
{
// 1. 将输入的角度从度转换为弧度
// angle[0] -> pitch, angle[1] -> roll, angle[2] -> yaw
float pitch_rad = -angle[0] * (M_PI / 180.0f);
float roll_rad = -angle[1] * (M_PI / 180.0f);
float yaw_rad = -angle[2] * (M_PI / 180.0f);
// 2. 预先计算三角函数值,以提高效率
float c_r = cosf(roll_rad);
float s_r = sinf(roll_rad);
float c_p = cosf(pitch_rad);
float s_p = sinf(pitch_rad);
float c_y = cosf(yaw_rad);
float s_y = sinf(yaw_rad);
// 3. 构建从设备坐标系到世界坐标系的旋转矩阵 R_device_to_world
// 该矩阵基于 Z-Y-X (Yaw-Pitch-Roll) 欧拉角顺序
// R = R_z(yaw) * R_y(pitch) * R_x(roll)
float R[3][3];
R[0][0] = c_y * c_p;
R[0][1] = c_y * s_p * s_r - s_y * c_r;
R[0][2] = c_y * s_p * c_r + s_y * s_r;
R[1][0] = s_y * c_p;
R[1][1] = s_y * s_p * s_r + c_y * c_r;
R[1][2] = s_y * s_p * c_r - c_y * s_r;
R[2][0] = -s_p;
R[2][1] = c_p * s_r;
R[2][2] = c_p * c_r;
// 4. 将设备坐标系的加速度计总读数旋转到世界坐标系
// a_raw_world = R * acc_device
float ax_raw_world = R[0][0] * acc_device[0] + R[0][1] * acc_device[1] + R[0][2] * acc_device[2];
float ay_raw_world = R[1][0] * acc_device[0] + R[1][1] * acc_device[1] + R[1][2] * acc_device[2];
float az_raw_world = R[2][0] * acc_device[0] + R[2][1] * acc_device[1] + R[2][2] * acc_device[2];
// 5. 在世界坐标系中减去重力分量,得到线性加速度
// 假设世界坐标系Z轴垂直向上重力矢量为 [0, 0, -g]
// 线性加速度 = 总加速度 - 重力加速度
// az_linear = az_raw_world - (-g) = az_raw_world + g (如果Z轴向上)
// az_linear = az_raw_world - (+g) = az_raw_world - g (如果Z轴向下)
// 这里我们采用 Z 轴向上的标准惯性系 (ENU)
acc_linear_world[0] = ax_raw_world;
acc_linear_world[1] = ay_raw_world;
acc_linear_world[2] = az_raw_world - G_ACCELERATION; // Z轴向上重力为正值所以减去
}
/**
* @brief 在设备坐标系下,从原始加速度数据中移除重力分量
* @param acc_device 输入:设备坐标系下的原始加速度 [x, y, z], 单位 m/s^2
* @param angle 输入:姿态角 [pitch, roll, yaw],单位: 度
* @param acc_linear_device 输出:设备坐标系下移除重力后的线性加速度 [x, y, z]
*/
void remove_gravity_in_device_frame(const float *acc_device, const float *angle, float *acc_linear_device)
{
float pitch = -angle[0] * DEG_TO_RAD; // 绕 Y 轴
float roll = -angle[1] * DEG_TO_RAD; // 绕 X 轴
float yaw = -angle[2] * DEG_TO_RAD; // 绕 Z 轴
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float cy = cosf(yaw);
float sy = sinf(yaw);
// 世界坐标系下的重力矢量
const float g_world[3] = {0.0f, 0.0f, G_ACCELERATION};
// 计算旋转矩阵 R 的转置矩阵 R_transpose
// R (Z-Y-X) =
// [ cy*cp, cy*sp*sr - sy*cr, cy*sp*cr + sy*sr]
// [ sy*cp, sy*sp*sr + cy*cr, sy*sp*cr - cy*sr]
// [ -sp, cp*sr, cp*cr ]
//
// R_transpose =
// [ cy*cp, sy*cp, -sp ]
// [ cy*sp*sr - sy*cr, sy*sp*sr + cy*cr, cp*sr ]
// [ cy*sp*cr + sy*sr, sy*sp*cr - cy*sr, cp*cr ]
// 计算重力在设备坐标系下的投影 G_device = R_transpose * G_world
// 由于 G_world 只有 z 分量,计算可以简化
float g_device[3];
g_device[0] = (-sp) * g_world[2];
g_device[1] = (cp * sr) * g_world[2];
g_device[2] = (cp * cr) * g_world[2];
// 从原始设备加速度中减去重力投影
acc_linear_device[0] = acc_device[0] - g_device[0];
acc_linear_device[1] = acc_device[1] - g_device[1];
acc_linear_device[2] = acc_device[2] - g_device[2];
}
/**
* @brief 使用四元数直接从设备坐标系的加速度中移除重力分量
* @details 这种方法比使用欧拉角更精确、更稳定,且避免了万向节死锁。
* @param acc_device 输入:设备坐标系下的原始加速度 [x, y, z], 单位 m/s^2
* @param q 输入:表示姿态的四元数 [w, x, y, z]
* @param acc_linear_device 输出:设备坐标系下移除重力后的线性加速度 [x, y, z]
*/
void q_remove_gravity_with_quaternion(const float *acc_device, const float *q, float *acc_linear_device)
{
// 从四元数计算重力在设备坐标系下的投影
// G_device = R_transpose * G_world
// G_world = [0, 0, g]
// R_transpose 的第三列即为重力投影方向
float gx = 2.0f * (q[1] * q[3] - q[0] * q[2]);
float gy = 2.0f * (q[0] * q[1] + q[2] * q[3]);
float gz = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
// 从原始加速度中减去重力分量
acc_linear_device[0] = acc_device[0] - gx * G_ACCELERATION;
acc_linear_device[1] = acc_device[1] - gy * G_ACCELERATION;
acc_linear_device[2] = acc_device[2] - gz * G_ACCELERATION;
}
/**
* @brief 使用四元数将设备坐标系的线性加速度转换到世界坐标系,并且移除重力分量
* @details 同样,此方法比使用欧拉角更优。
* @param acc_linear_device 输入:设备坐标系下的线性加速度 [x, y, z]
* @param q 输入:表示姿态的四元数 [w, x, y, z]
* @param acc_linear_world 输出:世界坐标系下的线性加速度 [x, y, z]
*/
void q_transform_to_world_with_quaternion(const float *acc_linear_device, const float *q, float *acc_linear_world)
{
// 这是 R_device_to_world * acc_linear_device 的展开形式
acc_linear_world[0] = (1.0f - 2.0f*q[2]*q[2] - 2.0f*q[3]*q[3]) * acc_linear_device[0] +
(2.0f*q[1]*q[2] - 2.0f*q[0]*q[3]) * acc_linear_device[1] +
(2.0f*q[1]*q[3] + 2.0f*q[0]*q[2]) * acc_linear_device[2];
acc_linear_world[1] = (2.0f*q[1]*q[2] + 2.0f*q[0]*q[3]) * acc_linear_device[0] +
(1.0f - 2.0f*q[1]*q[1] - 2.0f*q[3]*q[3]) * acc_linear_device[1] +
(2.0f*q[2]*q[3] - 2.0f*q[0]*q[1]) * acc_linear_device[2];
acc_linear_world[2] = (2.0f*q[1]*q[3] - 2.0f*q[0]*q[2]) * acc_linear_device[0] +
(2.0f*q[2]*q[3] + 2.0f*q[0]*q[1]) * acc_linear_device[1] +
(1.0f - 2.0f*q[1]*q[1] - 2.0f*q[2]*q[2]) * acc_linear_device[2];
acc_linear_world[2] -= G_ACCELERATION;
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps
* @param angle 欧若拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
if(my_skiing_tracker.state == STOP_DETECTION)
return;
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
#if 1 //测试禁止状态下陀螺仪的三轴加速度,去掉重力分量后是否正常
float tmp_device_acc[3];
float tmp_world_acc[3];
// remove_gravity_in_device_frame(acc_device_ms2,angle,tmp_device_acc);
// transform_acc_to_world_frame(acc_device_ms2,angle,tmp_world_acc);
q_remove_gravity_with_quaternion(acc_device_ms2,quaternion_data,tmp_device_acc);
q_transform_to_world_with_quaternion(acc_device_ms2,quaternion_data,tmp_world_acc);
// 计算处理后加速度的水平模长
float all_device_mag = sqrtf(tmp_device_acc[0] * tmp_device_acc[0] +
tmp_device_acc[1] * tmp_device_acc[1] +
tmp_device_acc[2] * tmp_device_acc[2]);
float all_world_mag = sqrtf(tmp_world_acc[0] * tmp_world_acc[0] +
tmp_world_acc[1] * tmp_world_acc[1] +
tmp_world_acc[2] * tmp_world_acc[2]);
static int count = 0;
if(count > 100){
xlog("===original(g): x %.2f, y %.2f, z %.2f===\n",acc_g[0],acc_g[1],acc_g[2]);
xlog("===device(m/s^2) no g: x %.2f, y %.2f, z %.2f, all %.2f===\n",tmp_device_acc[0],tmp_device_acc[1],tmp_device_acc[2],all_device_mag); //去掉重力加速度
xlog("===world(m/s^2) no g: x %.2f, y %.2f, z %.2f, all %.2f===\n",tmp_world_acc[0],tmp_world_acc[1],tmp_world_acc[2],all_world_mag); //去掉重力加速度
xlog("===gyr(dps) : x %.2f, y %.2f, z %.2f, all %.2f===\n",gyr_dps[0],gyr_dps[1],gyr_dps[2]); //angle
xlog("===angle : x %.2f, y %.2f, z %.2f,===\n",angle[0],angle[1],angle[2]);
count = 0;
}
count++;
#endif
}
/**
* @brief 滑雪数据计算
*
* @param acc_data_buf 传入的三轴加速度数据
* @param gyr_data_buf 传入的三轴陀螺仪数据
* @param angle_data 传入的欧若拉角数据
* @return BLE_send_data_t 要发送给蓝牙的数据
*/
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data, float* quaternion) {
static int initialized = 0;
static float acc_data_g[3];
static float gyr_data_dps[3];
if(quaternion != NULL){
memcpy(quaternion_data, quaternion, 4 * sizeof(float));
}
// const float delta_time = DELTA_TIME+0.01f;
// const float delta_time = DELTA_TIME + 0.005f;
const float delta_time = DELTA_TIME;
BLE_send_data_t BLE_send_data;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
#if ACC_RANGE==2
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 16384.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 16384.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 16384.0f;
#endif
#if ACC_RANGE==4
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 8192.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 8192.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 8192.0f;
#endif
#if ACC_RANGE==8
//±8g 4096
acc_data_g[0] = (float)acc_data_buf[0] / 4096.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 4096.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 4096.0f; //az
#endif
#if ACC_RANGE==16
//±16g 2048
acc_data_g[0] = (float)acc_data_buf[0] / 2048.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 2048.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 2048.0f; //az
#endif
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
gyr_data_dps[0] = (float)gyr_data_buf[0] * 0.061f;
gyr_data_dps[1] = (float)gyr_data_buf[1] * 0.061f;
gyr_data_dps[2] = (float)gyr_data_buf[2] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, acc_data_g, gyr_data_dps, angle_data, delta_time);
// BLE_send_data.skiing_state = my_skiing_tracker.state;
// for (int i = 0; i < 3; i++) {
// #ifdef XTELL_TEST
// BLE_send_data.acc_data[i] = (short)(acc_data_g[i] * 9.8f) * 100; //cm/^s2
// BLE_send_data.gyr_data[i] = (short)gyr_data_dps[i]; //dps
// BLE_send_data.angle_data[i] = angle_data[i];
// #else
// BLE_send_data.acc_data[i] = (short)acc_data_buf[i]; //原始adc数据
// BLE_send_data.gyr_data[i] = (short)gyr_data_buf[i]; //原始adc数据
// BLE_send_data.angle_data[i] = angle_data[i];
// #endif
// }
// BLE_send_data.speed_cms = (int)(my_skiing_tracker.speed * 100);
// BLE_send_data.distance_cm = (int)(my_skiing_tracker.distance * 100);
// // printf("Calculate the time interval =============== end\n");
return BLE_send_data;
}

View File

@ -0,0 +1,88 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
// 定义滑雪者可能的状态
typedef enum {
STATIC, // 静止或动态稳定0
NO_CONSTANT_SPEED, // 正在滑雪非匀速1
CONSTANT_SPEED, // 正在滑雪匀速2
WOBBLE, // 正在原地旋转3
WHEEL, // 转弯4
FALLEN, // 已摔倒5
TAKING_OFF, // 起跳冲击阶段6
IN_AIR, // 空中失重阶段7
LANDING, // 落地冲击阶段8
STOP_DETECTION, // 停止检测9
UNKNOWN // 未知状态10
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 5 // 用于计算方差的数据窗口大小 (5个样本 @ 100Hz = 50ms),减小延迟,提高实时性
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的加速度
// 用于空中距离计算
float time_in_air; // 滞空时间计时器
float initial_velocity_on_takeoff[3]; // 起跳瞬间的速度向量
int airborne_entry_counter; // 进入空中状态的确认计数器
int grounded_entry_counter; // 落地确认计数器
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3]; //过滤过的
float acc_world_unfiltered_prev[3]; //上一次没过滤的
float acc_world_lpf[3]; // 经过低通滤波后的世界坐标系加速度
} skiing_tracker_t;
//ble发送的数据
typedef struct{ //__attribute__((packed)){ //该结构体取消内存对齐
char sensor_state;
char skiing_state;
int speed_cms; //求出的速度cm/s
int distance_cm; //求出的距离cm
short acc_data[3]; //三轴加速度, g
short gyr_data[3]; //三轴陀螺仪, dps
float angle_data[3]; //欧若拉角
}BLE_send_data_t;
typedef struct{
int acc_KS[3]; //卡尔曼后LSB转换后的 三轴加速度数据cm/s^2
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
#ifdef XTELL_TEST
typedef struct{
float acc_variance; //三轴加速度方差之和
float gyr_variance; //三轴陀螺仪方差之和
float acc_magnitude; //三轴加速度模长
float gyr_magnitude; //三轴陀螺仪模长
}debug_t;
#endif
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data, float* quaternion);
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,259 @@
/*
动态ZUPT+卡尔曼
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- 算法阈值定义 ---
// 动态零速更新(ZUPT)阈值:加速度方差小于此值,认为处于动态稳定状态
#define ZUPT_ACC_VARIANCE_THRESHOLD 0.05f
// 旋转检测阈值Z轴角速度大于此值(度/秒),认为正在原地旋转
#define ROTATION_GYR_Z_THRESHOLD 60.0f
// 启动滑雪阈值:加速度模长与重力的差值大于此值,认为开始运动
#define START_SKIING_ACC_THRESHOLD 1.5f
/**
* @brief 初始化滑雪追踪器
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = SKIING_STATE_STATIC;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 驱动输出的角度与标准航空定义相反,需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
// 这个矩阵将设备测量的加速度(ax, ay, az)正确地转换到世界坐标系(acc_world)。
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 1. 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 2. 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 升级后的状态机,包含旋转检测和动态零速更新
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = SKIING_STATE_STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer);
float gyr_z_abs = fabsf(gyr_dps[2]);
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]);
// --- 状态切换逻辑 (按优先级) ---
// 旋转检测
// 如果Z轴角速度很大则判断为原地旋转暂停积分
if (gyr_z_abs > ROTATION_GYR_Z_THRESHOLD) {
tracker->state = SKIING_STATE_ROTATING;
return; // 直接返回,不执行后续判断
}
// 动态零速更新 (ZUPT)
// 如果加速度在窗口期内非常稳定(方差很小),则认为是动态稳定状态,进行零速校正
if (acc_variance < ZUPT_ACC_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_STATIC;
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
return;
}
// 从静止/旋转状态启动
if (tracker->state == SKIING_STATE_STATIC || tracker->state == SKIING_STATE_ROTATING) {
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD) {
tracker->state = SKIING_STATE_SKIING;
return;
}
}
// 滑雪
if (tracker->state != SKIING_STATE_STATIC) {
tracker->state = SKIING_STATE_SKIING;
}
}
/**
* @brief 主更新函数
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
// --- 1. 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 2. 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 3. 根据状态进行计算 ---
// 只有在明确的“滑雪”状态下才进行积分
if (tracker->state == SKIING_STATE_SKIING) {
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
tracker->acc_world[2] -= G_ACCELERATION;
tracker->velocity[0] += tracker->acc_world[0] * dt;
tracker->velocity[1] += tracker->acc_world[1] * dt;
tracker->velocity[2] += tracker->acc_world[2] * dt;
}
// 在其他状态下(静止、旋转),速度已经在状态机内部被清零或保持不变
// --- 4. 更新速率和距离 ---
// 速率和距离总是在更新但在非滑雪状态下速度为0所以它们不会增加
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1] +
tracker->velocity[2] * tracker->velocity[2]);
tracker->distance += tracker->speed * dt;
}
// 传感器数据采集与处理任务
void sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) {
static skiing_tracker_t my_skiing_tracker;
static int initialized = 0;
static int calibration_done = 0;
static signed short combined_raw_data[6];
static float final_angle_data[3];
static float calibrated_acc_g[3];
static float calibrated_gyr_dps[3]; // 新增:用于存放转换后的陀螺仪数据
const float delta_time = 0.01f;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
unsigned char status;
if (!calibration_done) {
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
if (status == 1) {
calibration_done = 1;
printf("Sensor calibration successful! Skiing mode is active.\n");
}
} else {
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
}
if (status == 1) {
// 加速度 LSB to g
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
calibrated_gyr_dps[0] = (float)combined_raw_data[3] * 0.061f;
calibrated_gyr_dps[1] = (float)combined_raw_data[4] * 0.061f;
calibrated_gyr_dps[2] = (float)combined_raw_data[5] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, calibrated_gyr_dps, final_angle_data, delta_time);
// 打印逻辑保持不变
static int count = 0;
if(count < 10){
count++;
return;
} else {
count = 0;
}
printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
my_skiing_tracker.state,
my_skiing_tracker.speed,
my_skiing_tracker.distance);
} else if (status == 0) {
// printf("Sensor is calibrating...\n");
} else {
// printf("Angle calculation error or calibration not finished.\n");
}
}

View File

@ -0,0 +1,50 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
// 定义滑雪者可能的状态
typedef enum {
SKIING_STATE_STATIC, // 静止或动态稳定
SKIING_STATE_SKIING, // 正在滑雪
SKIING_STATE_ROTATING, // 正在原地旋转 (新增)
SKIING_STATE_FALLEN, // 已摔倒
SKIING_STATE_UNKNOWN // 未知状态
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 15 // 用于计算方差的数据窗口大小 (15个样本 @ 100Hz = 150ms)
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的加速度
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
} skiing_tracker_t;
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
/**
* @brief 处理传感器数据并更新滑雪状态
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
* @param acc_g 校准后的加速度数据 [x, y, z],单位: g (1g = 9.8m/s^2)
* @param gyr_dps 角速度
* @param angle 姿态角数据 [pitch, roll, yaw],单位: 度
* @param dt 采样时间间隔,单位: 秒 (s)
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt);
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,277 @@
/*
动态ZUPT+卡尔曼
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- 算法阈值定义 ---
//两个判断是否静止的必要条件
// 动态零速更新(ZUPT)阈值
// 提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define ZUPT_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define ZUPT_GYR_VARIANCE_THRESHOLD 5.0f
// 旋转/摆动检测阈值:角速度总模长大于此值(度/秒),认为正在进行非滑雪的旋转或摆动
#define ROTATION_GYR_MAG_THRESHOLD 45.0f
// 启动滑雪阈值:加速度模长与重力的差值大于此值,认为开始运动
// 降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_SKIING_ACC_THRESHOLD 0.5f
// 新增:速度阻尼系数,用于模拟摩擦力,抑制漂移
#define VELOCITY_DAMPING_FACTOR 0.98f
/**
* @brief 初始化滑雪追踪器
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = SKIING_STATE_STATIC;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 驱动输出的角度与标准航空定义相反,需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
// 这个矩阵将设备测量的加速度(ax, ay, az)正确地转换到世界坐标系(acc_world)。
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 1. 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 2. 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 升级后的状态机,包含旋转检测和动态零速更新
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = SKIING_STATE_STATIC;
return;
}
// --- 1. 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]);
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]);
// --- 2. 状态切换逻辑 (按优先级) ---
// 优先级1原地旋转/摆动检测 (最终版)
// 增加一个关键前提:只在当前不处于滑雪状态时,才检测原地旋转。
// 这可以防止滑雪过程中的高速转弯被误判为原地旋转。
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && tracker->state != SKIING_STATE_SKIING) {
tracker->state = SKIING_STATE_ROTATING;
return;
}
// 动态零速更新 (ZUPT)
// 必须同时满足加速度和角速度都稳定,才能判断为“真静止”,以区分匀速运动
if (acc_variance < ZUPT_ACC_VARIANCE_THRESHOLD && gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_STATIC;
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
return;
}
// 从静止/旋转状态启动
if (tracker->state == SKIING_STATE_STATIC || tracker->state == SKIING_STATE_ROTATING) {
// 最终版启动逻辑:必须同时满足“有足够大的线性加速度”和“旋转稳定”两个条件
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD && gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_SKIING;
return;
}
}
// 滑雪
if (tracker->state != SKIING_STATE_STATIC) {
tracker->state = SKIING_STATE_SKIING;
}
}
/**
* @brief 主更新函数
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
// --- 1. 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 2. 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 3. 根据状态进行计算 ---
// 只有在明确的“滑雪”状态下才进行积分
if (tracker->state == SKIING_STATE_SKIING) {
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
tracker->acc_world[2] -= G_ACCELERATION;
tracker->velocity[0] += tracker->acc_world[0] * dt;
tracker->velocity[1] += tracker->acc_world[1] * dt;
tracker->velocity[2] += tracker->acc_world[2] * dt;
}
// 在其他状态下(静止、旋转),速度已经在状态机内部被清零或保持不变
// --- 4. 引入速度阻尼(软件摩擦力) ---
// 只要不处于滑雪状态,就对速度进行衰减,以对抗漂移和抑制抖动产生的微小速度
if (tracker->state != SKIING_STATE_SKIING) {
tracker->velocity[0] *= VELOCITY_DAMPING_FACTOR;
tracker->velocity[1] *= VELOCITY_DAMPING_FACTOR;
tracker->velocity[2] *= VELOCITY_DAMPING_FACTOR;
}
// --- 5. 更新速率和距离 ---
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1] +
tracker->velocity[2] * tracker->velocity[2]);
tracker->distance += tracker->speed * dt;
}
// 传感器数据采集与处理任务
void sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) {
static skiing_tracker_t my_skiing_tracker;
static int initialized = 0;
static int calibration_done = 0;
static signed short combined_raw_data[6];
static float final_angle_data[3]; // 计算得到的欧若拉角
static float calibrated_acc_g[3]; // 转换后的加速度计数据
static float calibrated_gyr_dps[3]; // 转换后的陀螺仪数据
const float delta_time = 0.01f;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
unsigned char status;
if (!calibration_done) { //第1次启动开启零漂检测
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
if (status == 1) {
calibration_done = 1;
printf("Sensor calibration successful! Skiing mode is active.\n");
}
} else {
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
}
if (status == 1) {
// 加速度 LSB to g
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
calibrated_gyr_dps[0] = (float)combined_raw_data[3] * 0.061f;
calibrated_gyr_dps[1] = (float)combined_raw_data[4] * 0.061f;
calibrated_gyr_dps[2] = (float)combined_raw_data[5] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, calibrated_gyr_dps, final_angle_data, delta_time);
// 打印逻辑保持不变
static int count = 0;
if(count < 10){
count++;
return;
} else {
count = 0;
}
printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
my_skiing_tracker.state,
my_skiing_tracker.speed,
my_skiing_tracker.distance);
} else if (status == 0) {
// printf("Sensor is calibrating...\n");
} else {
// printf("Angle calculation error or calibration not finished.\n");
}
}

View File

@ -0,0 +1,50 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
// 定义滑雪者可能的状态
typedef enum {
SKIING_STATE_STATIC, // 静止或动态稳定
SKIING_STATE_SKIING, // 正在滑雪
SKIING_STATE_ROTATING, // 正在原地旋转 (新增)
SKIING_STATE_FALLEN, // 已摔倒
SKIING_STATE_UNKNOWN // 未知状态
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 15 // 用于计算方差的数据窗口大小 (15个样本 @ 100Hz = 150ms)
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的加速度
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
} skiing_tracker_t;
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
/**
* @brief 处理传感器数据并更新滑雪状态
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
* @param acc_g 校准后的加速度数据 [x, y, z],单位: g (1g = 9.8m/s^2)
* @param gyr_dps 角速度
* @param angle 姿态角数据 [pitch, roll, yaw],单位: 度
* @param dt 采样时间间隔,单位: 秒 (s)
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt);
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,465 @@
/**
* 效果不行,对原地转动太灵敏了
*/
#include "skiing_tracker.h"
#include <math.h>
#include <string.h>
// --- 核心算法参数 ---
#define COMPLEMENTARY_FILTER_ALPHA 0.96f // 互补滤波器alpha值 (降低alpha, 增加对加速度计的信任, 更好地抑制陀螺仪漂移)
#define ACC_SENSITIVITY 8192.0f // 加速度计灵敏度 (LSB/g), 对应 ±4g 量程
#define GYRO_SENSITIVITY 16.4f // 陀螺仪灵敏度 (LSB/dps), 对应 ±2000dps 量程
#define SKI_DT_FIXED 0.01f // 固定时间间隔 10ms (100Hz)
// --- 状态机和阈值 ---
#define SKI_STATIC_THRESHOLD_ACC 0.3f // 静止检测加速度阈值 (m/s^2) - 提高以忽略更多噪声
#define SKI_STATIC_THRESHOLD_GYRO 3.0f // 静止检测角速度阈值 (dps) - 更严格
#define SKI_MOVEMENT_THRESHOLD 1.2f // 运动启动阈值 (m/s^2) - 显著提高,需要更明确的运动意图
#define SKI_WINDOW_SIZE 10 // 滑动窗口大小 (当前未使用)
#define SKI_CALIBRATION_SAMPLES 100 // 校准采样点数 (恢复到100确保校准精度)
// 滑雪数据结构
typedef struct {
// 状态相关
SkiState_t current_state;
uint32_t state_duration; // 状态持续时间(ms)
uint8_t static_counter; // 静态计数器
// --- 核心运动数据 ---
// 姿态数据 (Roll, Pitch)
float attitude[2];
// 世界坐标系下的加速度
float world_acc[3];
// 世界坐标系下的速度
float velocity[3];
// 水平面速度和总距离
float horizontal_speed;
float total_horizontal_distance;
// 历史数据 (用于滤波)
float acc_history[SKI_WINDOW_SIZE][3];
float gyro_history[SKI_WINDOW_SIZE][3];
uint8_t history_index;
// 时间相关
float dt; // 时间间隔
// 运动检测
float movement_score; // 运动评分
float turning_score; // 转弯评分
// 校准数据 (用于陀螺仪零偏)
float gyro_bias[3];
float acc_bias[3];
float calibration_sum[6];
uint32_t calibration_count;
uint8_t calibrated;
// 状态标志和计数器
uint32_t update_count;
float velocity_decay_factor;
uint8_t is_moving;
} SkiData_t;
static SkiData_t ski_data = {0};
// --- 内部核心函数声明 ---
// 校准
static void Ski_CalibrateSensor(float *acc, float *gyro); // 校准传感器
// 姿态解算
static void Ski_UpdateAttitude(float *acc, float *gyro); // 更新姿态
// 坐标系转换
static void Ski_TransformToWorldFrame(float *acc, float *world_acc); // 转换到世界坐标系
// 速度与距离积分
static void Ski_UpdateVelocityAndDistance(void); // 更新速度和距离
// --- 辅助函数声明 ---
static float Ski_CalculateMovementScore(void); // 计算运动评分
static float Ski_CalculateTurningScore(float *gyro);
static void Ski_UpdateStateMachine(void);
static void Ski_MovingAverageFilter(float *new_acc, float *new_gyro, float *acc_filtered, float *gyro_filtered);
// 修改初始化函数
void Ski_Init(void)
{
memset(&ski_data, 0, sizeof(SkiData_t));
ski_data.current_state = SKI_STATE_STATIC;
// 初始化参数
ski_data.velocity_decay_factor = 0.8f; // 速度衰减因子
ski_data.is_moving = 0;
ski_data.update_count = 0;
ski_data.dt = SKI_DT_FIXED;
}
// 滑动窗口滤波
static void Ski_MovingAverageFilter(float *new_acc, float *new_gyro,
float *acc_filtered, float *gyro_filtered)
{
// 更新历史数据
ski_data.acc_history[ski_data.history_index][0] = new_acc[0];
ski_data.acc_history[ski_data.history_index][1] = new_acc[1];
ski_data.acc_history[ski_data.history_index][2] = new_acc[2];
ski_data.gyro_history[ski_data.history_index][0] = new_gyro[0];
ski_data.gyro_history[ski_data.history_index][1] = new_gyro[1];
ski_data.gyro_history[ski_data.history_index][2] = new_gyro[2];
ski_data.history_index = (ski_data.history_index + 1) % SKI_WINDOW_SIZE;
// 计算平均值
memset(acc_filtered, 0, 3 * sizeof(float));
memset(gyro_filtered, 0, 3 * sizeof(float));
for (int i = 0; i < SKI_WINDOW_SIZE; i++) {
acc_filtered[0] += ski_data.acc_history[i][0];
acc_filtered[1] += ski_data.acc_history[i][1];
acc_filtered[2] += ski_data.acc_history[i][2];
gyro_filtered[0] += ski_data.gyro_history[i][0];
gyro_filtered[1] += ski_data.gyro_history[i][1];
gyro_filtered[2] += ski_data.gyro_history[i][2];
}
acc_filtered[0] /= SKI_WINDOW_SIZE;
acc_filtered[1] /= SKI_WINDOW_SIZE;
acc_filtered[2] /= SKI_WINDOW_SIZE;
gyro_filtered[0] /= SKI_WINDOW_SIZE;
gyro_filtered[1] /= SKI_WINDOW_SIZE;
gyro_filtered[2] /= SKI_WINDOW_SIZE;
}
// =================================================================================
// 核心更新函数 - 已重构
// =================================================================================
void Ski_UpdateData(int16_t acc_x, int16_t acc_y, int16_t acc_z,
int16_t gyro_x, int16_t gyro_y, int16_t gyro_z)
{
ski_data.update_count++;
// 1. LSB原始数据转换为物理单位 (g 和 度/秒)
float acc[3] = { (float)acc_x / ACC_SENSITIVITY, (float)acc_y / ACC_SENSITIVITY, (float)acc_z / ACC_SENSITIVITY };
float gyro[3] = { (float)gyro_x / GYRO_SENSITIVITY, (float)gyro_y / GYRO_SENSITIVITY, (float)gyro_z / GYRO_SENSITIVITY };
// 2. 传感器校准 (移除零偏)
if (!ski_data.calibrated) {
Ski_CalibrateSensor(acc, gyro);
return; // 等待校准完成
}
acc[0] -= ski_data.acc_bias[0];
acc[1] -= ski_data.acc_bias[1];
acc[2] -= ski_data.acc_bias[2];
gyro[0] -= ski_data.gyro_bias[0];
gyro[1] -= ski_data.gyro_bias[1];
gyro[2] -= ski_data.gyro_bias[2];
// 3. 姿态解算 (互补滤波器)
Ski_UpdateAttitude(acc, gyro);
// 4. 将身体坐标系的加速度转换为世界坐标系
Ski_TransformToWorldFrame(acc, ski_data.world_acc);
// 5. 在世界坐标系中移除重力 (重力永远是 [0, 0, 1]g)
ski_data.world_acc[2] -= 1.0f;
// 6. 对世界坐标系下的加速度进行滤波 (可选,但推荐)
// 注意: 如果需要,可在此处重用滑动平均滤波器,但暂时我们直接使用原始世界坐标系加速度
// 7. 基于世界坐标系的数据更新状态机
Ski_UpdateStateMachine();
// 8. 基于世界坐标系的数据更新速度和距离
Ski_UpdateVelocityAndDistance();
}
// 计算转弯评分
static float Ski_CalculateTurningScore(float *gyro)
{
// 主要考虑Z轴旋转(偏航)和X轴旋转(俯仰)
float turning_score = fabsf(gyro[2]) * 0.6f + fabsf(gyro[0]) * 0.4f;
return turning_score;
}
// =================================================================================
// 新增核心算法函数
// =================================================================================
/**
* @brief 在静止状态下计算传感器零偏
*/
static void Ski_CalibrateSensor(float *acc, float *gyro)
{
float acc_mag = sqrtf(acc[0]*acc[0] + acc[1]*acc[1] + acc[2]*acc[2]);
float gyro_mag = sqrtf(gyro[0]*gyro[0] + gyro[1]*gyro[1] + gyro[2]*gyro[2]);
// 放宽静止判断条件,使其更容易满足
if (fabsf(acc_mag - 1.0f) < 0.1f && gyro_mag < SKI_STATIC_THRESHOLD_GYRO) {
ski_data.calibration_sum[0] += acc[0];
ski_data.calibration_sum[1] += acc[1];
ski_data.calibration_sum[2] += acc[2];
ski_data.calibration_sum[3] += gyro[0];
ski_data.calibration_sum[4] += gyro[1];
ski_data.calibration_sum[5] += gyro[2];
ski_data.calibration_count++;
if (ski_data.calibration_count >= SKI_CALIBRATION_SAMPLES) {
// 计算加速度计零偏
ski_data.acc_bias[0] = ski_data.calibration_sum[0] / ski_data.calibration_count;
ski_data.acc_bias[1] = ski_data.calibration_sum[1] / ski_data.calibration_count;
// Z轴的零偏是相对于1g的, 需要找到重力轴
float gravity_mag = sqrtf(powf(ski_data.calibration_sum[0] / ski_data.calibration_count, 2) +
powf(ski_data.calibration_sum[1] / ski_data.calibration_count, 2) +
powf(ski_data.calibration_sum[2] / ski_data.calibration_count, 2));
ski_data.acc_bias[2] = ski_data.calibration_sum[2] / ski_data.calibration_count - gravity_mag;
// 计算陀螺仪零偏
ski_data.gyro_bias[0] = ski_data.calibration_sum[3] / ski_data.calibration_count;
ski_data.gyro_bias[1] = ski_data.calibration_sum[4] / ski_data.calibration_count;
ski_data.gyro_bias[2] = ski_data.calibration_sum[5] / ski_data.calibration_count;
ski_data.calibrated = 1;
printf("传感器校准完成!\n");
}
} else {
// 如果检测到移动,则重置校准计数
ski_data.calibration_count = 0;
memset(ski_data.calibration_sum, 0, sizeof(ski_data.calibration_sum));
// 增加调试打印,方便定位问题
// printf("Calibration reset. Acc mag: %.3f, Gyro mag: %.3f\n", acc_mag, gyro_mag);
}
}
/**
* @brief 使用互补滤波器更新姿态角(Roll, Pitch)
*/
static void Ski_UpdateAttitude(float *acc, float *gyro)
{
float dt = ski_data.dt;
float alpha = COMPLEMENTARY_FILTER_ALPHA;
// 从加速度计计算角度 (单位: 度)
float roll_acc = atan2f(acc[1], acc[2]) * 180.0f / M_PI;
float pitch_acc = atan2f(-acc[0], sqrtf(acc[1] * acc[1] + acc[2] * acc[2])) * 180.0f / M_PI;
// 陀螺仪积分预测角度
// 注意: 这里的gyro单位是 度/秒
float roll_gyro = ski_data.attitude[0] + gyro[0] * dt;
float pitch_gyro = ski_data.attitude[1] + gyro[1] * dt;
// 互补滤波融合
ski_data.attitude[0] = alpha * roll_gyro + (1.0f - alpha) * roll_acc;
ski_data.attitude[1] = alpha * pitch_gyro + (1.0f - alpha) * pitch_acc;
}
/**
* @brief 将身体坐标系的加速度转换为世界坐标系
*/
static void Ski_TransformToWorldFrame(float *acc, float *world_acc)
{
// 将姿态角转换为弧度
float roll = ski_data.attitude[0] * M_PI / 180.0f;
float pitch = ski_data.attitude[1] * M_PI / 180.0f;
float cos_roll = cosf(roll);
float sin_roll = sinf(roll);
float cos_pitch = cosf(pitch);
float sin_pitch = sinf(pitch);
// 通过旋转矩阵将身体坐标系的加速度(acc)转换到世界坐标系(world_acc)
// (简化版假设偏航角yaw=0)
world_acc[0] = acc[0] * cos_pitch + acc[2] * sin_pitch;
world_acc[1] = acc[0] * sin_pitch * sin_roll + acc[1] * cos_roll - acc[2] * cos_pitch * sin_roll;
world_acc[2] = -acc[0] * sin_pitch * cos_roll + acc[1] * sin_roll + acc[2] * cos_pitch * cos_roll;
}
// 改进的运动评分计算
static float Ski_CalculateMovementScore(void)
{
// 在世界坐标系下计算运动评分,只考虑水平加速度
float horizontal_acc_mag = sqrtf(ski_data.world_acc[0]*ski_data.world_acc[0] + ski_data.world_acc[1]*ski_data.world_acc[1]);
return horizontal_acc_mag * 9.81f; // 从 g 转换为 m/s^2
}
// 改进的状态机 - 添加更严格的转换条件
/**
* @brief 基于世界坐标系下的加速度更新状态机
*/
static void Ski_UpdateStateMachine(void)
{
ski_data.state_duration++;
ski_data.movement_score = Ski_CalculateMovementScore();
switch (ski_data.current_state) {
case SKI_STATE_STATIC:
if (ski_data.movement_score > SKI_MOVEMENT_THRESHOLD) {
ski_data.static_counter++;
if (ski_data.static_counter > 5) { // 需要连续几帧的运动才切换
ski_data.current_state = SKI_STATE_MOVING;
ski_data.is_moving = 1;
ski_data.state_duration = 0;
ski_data.static_counter = 0;
}
} else {
ski_data.static_counter = 0;
}
break;
case SKI_STATE_MOVING:
// 使用更低的阈值来检测停止,形成迟滞效应,防止状态抖动
if (ski_data.movement_score < SKI_STATIC_THRESHOLD_ACC) {
ski_data.static_counter++;
if (ski_data.static_counter > 8) { // 减少进入静止状态的等待时间
ski_data.current_state = SKI_STATE_STATIC;
ski_data.is_moving = 0;
ski_data.state_duration = 0;
ski_data.static_counter = 0;
// 停止时强制将速度清零,以防止漂移
memset(ski_data.velocity, 0, sizeof(ski_data.velocity));
}
} else {
ski_data.static_counter = 0;
}
// TODO: 如果需要,可以增加转弯状态的检测
break;
// 如果需要,可以增加其他状态 (例如 TURNING, STOPPING)
default:
ski_data.current_state = SKI_STATE_STATIC;
break;
}
}
// 改进的速度和距离更新 - 添加零速度更新(ZUPT)
/**
* @brief 在世界坐标系下更新速度和距离
*/
static void Ski_UpdateVelocityAndDistance(void)
{
// 将世界坐标系下的加速度从 g 转换为 m/s^2
float acc_ms2[3];
acc_ms2[0] = ski_data.world_acc[0] * 9.81f;
acc_ms2[1] = ski_data.world_acc[1] * 9.81f;
acc_ms2[2] = ski_data.world_acc[2] * 9.81f;
// 是否进行积分的决定由状态机控制
if (ski_data.is_moving) {
// 速度积分
ski_data.velocity[0] += acc_ms2[0] * ski_data.dt;
ski_data.velocity[1] += acc_ms2[1] * ski_data.dt;
// 如果需要,也可以对垂直速度进行积分
// ski_data.velocity[2] += acc_ms2[2] * ski_data.dt;
// 通过累加每一步的位移来计算总距离
float distance_step = sqrtf(
(ski_data.velocity[0] * ski_data.dt) * (ski_data.velocity[0] * ski_data.dt) +
(ski_data.velocity[1] * ski_data.dt) * (ski_data.velocity[1] * ski_data.dt)
);
ski_data.total_horizontal_distance += distance_step;
} else {
// 当静止时,对速度进行衰减
ski_data.velocity[0] *= ski_data.velocity_decay_factor;
ski_data.velocity[1] *= ski_data.velocity_decay_factor;
ski_data.velocity[2] *= ski_data.velocity_decay_factor;
}
// 更新当前水平速度
ski_data.horizontal_speed = sqrtf(ski_data.velocity[0]*ski_data.velocity[0] + ski_data.velocity[1]*ski_data.velocity[1]);
}
// 获取当前速度 (m/s)
float Ski_GetSpeed(void)
{
return ski_data.horizontal_speed;
}
// 获取总移动距离 (m)
float Ski_GetDistance(void)
{
return ski_data.total_horizontal_distance;
}
// 获取当前状态
SkiState_t Ski_GetState(void)
{
return ski_data.current_state;
}
// 重置距离计数
void Ski_ResetDistance(void)
{
ski_data.total_horizontal_distance = 0;
// 速度也应该一起重置
memset(ski_data.velocity, 0, sizeof(ski_data.velocity));
ski_data.horizontal_speed = 0;
}
// 获取校准状态
uint8_t Ski_IsCalibrated(void)
{
return ski_data.calibrated;
}
// 手动设置重力向量
// 这个函数不再需要,将被姿态估计算法取代
/*
void Ski_SetGravityVector(float gx, float gy, float gz)
{
...
}
*/
// 添加调试信息函数
void Ski_PrintDebugInfo(void)
{
printf("State: %d, MoveScore: %.2f, Speed: %.3f, Dist: %.3f, Roll: %.2f, Pitch: %.2f\n",
ski_data.current_state, ski_data.movement_score, ski_data.horizontal_speed, ski_data.total_horizontal_distance,
ski_data.attitude[0], ski_data.attitude[1]);
}
void sensor_processing_task(void){
static int first_init = 0;
if(!first_init){
Ski_Init();
first_init = 1;
}
static signed short acc_data[3], gyr_data[3];
static signed short combined_raw_data[6];
static float final_angle_data[3];
// 读取传感器数据
SL_SC7U22_RawData_Read(acc_data, gyr_data);
memcpy(&combined_raw_data[0], acc_data, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data, 3 * sizeof(signed short));
// 校准逻辑已内置于Ski_UpdateData中不再需要外部检查
// if(get_calibration_state() == 0){ ... }
// 更新滑雪数据
Ski_UpdateData(combined_raw_data[0], combined_raw_data[1], combined_raw_data[2],
combined_raw_data[3], combined_raw_data[4], combined_raw_data[5]);
static int count = 0;
if(count < 10){
count++;
return;
}else{
count = 0;
}
// 打印结果和调试信息
float speed = Ski_GetSpeed();
float distance = Ski_GetDistance();
SkiState_t state = Ski_GetState();
// printf("Speed: %.3f m/s, Distance: %.3f m, State: %d\n", speed, distance, state);
Ski_PrintDebugInfo();
}

View File

@ -0,0 +1,40 @@
#ifndef SKI_SPEED_DISTANCE_H
#define SKI_SPEED_DISTANCE_H
#include <stdint.h>
// 滑雪状态定义
typedef enum {
SKI_STATE_STATIC = 0, // 静止状态
SKI_STATE_STARTING, // 启动状态
SKI_STATE_MOVING, // 运动状态
SKI_STATE_TURNING, // 转弯状态
SKI_STATE_STOPPING // 停止状态
} SkiState_t;
// 初始化滑雪模块
void Ski_Init(void);
// 更新滑雪数据
void Ski_UpdateData(int16_t acc_x, int16_t acc_y, int16_t acc_z,
int16_t gyro_x, int16_t gyro_y, int16_t gyro_z);
// 获取当前速度 (m/s)
float Ski_GetSpeed(void);
// 获取总移动距离 (m)
float Ski_GetDistance(void);
// 获取当前状态
SkiState_t Ski_GetState(void);
// 重置距离计数
void Ski_ResetDistance(void);
// 获取校准状态
uint8_t Ski_IsCalibrated(void);
// 手动设置重力向量(可选)
void Ski_SetGravityVector(float gx, float gy, float gz);
#endif

View File

@ -0,0 +1,311 @@
/*
动态ZUPT+卡尔曼
多了加速度死区、摩擦力速度衰减、高通滤波
原地摆动产生的速度、距离变化还是没法消除
水平移动、斜坡移动效果貌似还行
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- 算法阈值定义 ---
//两个判断是否静止的必要条件
// 动态零速更新(ZUPT)阈值
// 提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define ZUPT_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define ZUPT_GYR_VARIANCE_THRESHOLD 5.0f
// 旋转/摆动检测阈值:角速度总模长大于此值(度/秒),认为正在进行非滑雪的旋转或摆动 -- 没法完全消除
#define ROTATION_GYR_MAG_THRESHOLD 45.0f
// 启动滑雪阈值:加速度模长与重力的差值大于此值,认为开始运动
// 降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_SKIING_ACC_THRESHOLD 0.5f
// --- 用于消除积分漂移的滤波器和阈值 ---
// 高通滤波器系数 (alpha)。alpha 越接近1滤除低频(直流偏移)的效果越强,但可能滤掉真实的慢速运动。
// alpha = RC / (RC + dt)
#define HPF_ALPHA 0.95f
// 加速度死区阈值 (m/s^2)。低于此阈值的加速度被认为是噪声,不参与积分。
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
#define ACC_DEAD_ZONE_THRESHOLD 0.1f
#define SPEED_ATTENUATION 0.98f //模拟摩擦力,进行速度衰减
/**
* @brief 初始化滑雪追踪器
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = SKIING_STATE_STATIC;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 驱动输出的角度与标准航空定义相反,需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
// TODO: 当引入三轴磁力计后,这里的 yaw 应由磁力计和陀螺仪融合解算得出,以解决航向漂移问题。
// 目前 yaw 暂时不参与计算,因为仅靠加速度计和陀螺仪无法获得准确的绝对航向角。
// float yaw = -angle[2] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
// 这个矩阵将设备测量的加速度(ax, ay, az)正确地转换到世界坐标系(acc_world)。
// 注意这里没有使用yaw主要关心的是坡面上的运动绝对航向暂时不影响速度和距离的计算。
// TODO
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 1. 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 2. 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 状态机更新
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = SKIING_STATE_STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]);
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]);
// --- 状态切换逻辑---
// 原地旋转/摆动检测
// 增加一个关键前提:只在当前不处于滑雪状态时,才检测原地旋转。
// 这可以防止滑雪过程中的高速转弯被误判为原地旋转。
// 暂时没办法完全消除
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && tracker->state != SKIING_STATE_SKIING) {
tracker->state = SKIING_STATE_ROTATING;
return;
}
// 动态零速更新 (ZUPT)
// 必须同时满足加速度和角速度都稳定,才能判断为“真静止”,以区分匀速运动
if (acc_variance < ZUPT_ACC_VARIANCE_THRESHOLD && gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_STATIC;
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
//当检测到静止时,必须重置高通滤波器的状态,否则下次启动时会有跳变
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
return;
}
// 从静止/旋转状态启动
if (tracker->state == SKIING_STATE_STATIC || tracker->state == SKIING_STATE_ROTATING) {
// 最终版启动逻辑:必须同时满足“有足够大的线性加速度”和“旋转稳定”两个条件
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD && gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_SKIING;
return;
}
}
// 滑雪
if (tracker->state != SKIING_STATE_STATIC) {
tracker->state = SKIING_STATE_SKIING;
}
}
/**
* @brief 主更新函数
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 根据状态进行计算 ---
if (tracker->state == SKIING_STATE_SKIING) {
// 坐标转换 & 移除重力
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
tracker->acc_world[2] -= G_ACCELERATION;
// 对世界坐标系下的加速度进行高通滤波,消除直流偏置和重力残差
for (int i = 0; i < 3; i++) {
tracker->acc_world_filtered[i] = HPF_ALPHA * (tracker->acc_world_filtered[i] + tracker->acc_world[i] - tracker->acc_world_unfiltered_prev[i]);
tracker->acc_world_unfiltered_prev[i] = tracker->acc_world[i];
}
// 应用加速度死区,忽略微小抖动和噪声
float acc_horizontal_mag = sqrtf(tracker->acc_world_filtered[0] * tracker->acc_world_filtered[0] +
tracker->acc_world_filtered[1] * tracker->acc_world_filtered[1]);
if (acc_horizontal_mag > ACC_DEAD_ZONE_THRESHOLD) {
// 只有当水平加速度足够大时,才进行速度积分
tracker->velocity[0] += tracker->acc_world_filtered[0] * dt;
tracker->velocity[1] += tracker->acc_world_filtered[1] * dt;
// 垂直方向的速度暂时不积分,极易受姿态误差影响而漂移
// tracker->velocity[2] += tracker->acc_world_filtered[2] * dt;
}
// 如果加速度小于阈值,则不更新速度,相当于速度保持不变(或受下一步的阻尼影响而衰减)
} else {
// 在静止或旋转状态下,速度已经在状态机内部被清零
// 额外增加速度衰减,模拟摩擦力,进一步抑制漂移
tracker->velocity[0] *= SPEED_ATTENUATION;
tracker->velocity[1] *= SPEED_ATTENUATION;
tracker->velocity[2] = 0; // 垂直速度强制归零
}
// --- 更新速率和距离 ---
// 只基于水平速度计算速率和距离
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
tracker->distance += tracker->speed * dt;
}
// 传感器数据采集与处理任务
void sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) {
static skiing_tracker_t my_skiing_tracker;
static int initialized = 0;
static int calibration_done = 0;
static signed short combined_raw_data[6];
static float final_angle_data[3]; // 计算得到的欧若拉角
static float calibrated_acc_g[3]; // 转换后的加速度计数据
static float calibrated_gyr_dps[3]; // 转换后的陀螺仪数据
const float delta_time = 0.01f;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
unsigned char status;
if (!calibration_done) { //第1次启动开启零漂检测
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
if (status == 1) {
calibration_done = 1;
printf("Sensor calibration successful! Skiing mode is active.\n");
}
} else {
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
}
if (status == 1) {
// 加速度 LSB to g
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
calibrated_gyr_dps[0] = (float)combined_raw_data[3] * 0.061f;
calibrated_gyr_dps[1] = (float)combined_raw_data[4] * 0.061f;
calibrated_gyr_dps[2] = (float)combined_raw_data[5] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, calibrated_gyr_dps, final_angle_data, delta_time);
// 打印逻辑保持不变
static int count = 0;
if(count < 10){
count++;
return;
} else {
count = 0;
}
printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
my_skiing_tracker.state,
my_skiing_tracker.speed,
my_skiing_tracker.distance);
} else if (status == 0) {
// printf("Sensor is calibrating...\n");
} else {
// printf("Angle calculation error or calibration not finished.\n");
}
}

View File

@ -0,0 +1,55 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
// 定义滑雪者可能的状态
typedef enum {
SKIING_STATE_STATIC, // 静止或动态稳定
SKIING_STATE_SKIING, // 正在滑雪
SKIING_STATE_ROTATING, // 正在原地旋转 (新增)
SKIING_STATE_FALLEN, // 已摔倒
SKIING_STATE_UNKNOWN // 未知状态
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 15 // 用于计算方差的数据窗口大小 (15个样本 @ 100Hz = 150ms)
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的加速度
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3];
float acc_world_unfiltered_prev[3];
} skiing_tracker_t;
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
/**
* @brief 处理传感器数据并更新滑雪状态
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
* @param acc_g 校准后的加速度数据 [x, y, z],单位: g (1g = 9.8m/s^2)
* @param gyr_dps 角速度
* @param angle 姿态角数据 [pitch, roll, yaw],单位: 度
* @param dt 采样时间间隔,单位: 秒 (s)
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt);
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,374 @@
/*
动态ZUPT+卡尔曼+巴特沃斯一阶滤波器
针对启动滑雪和停止滑雪,设置不同阈值
启动滑雪和ZUPT更新的陀螺仪方差阈值分开设置
- 启动滑雪的陀螺仪阈值会更宽松一些
原地旋转和ZUPT更新的加速度方差阈值分开设置
- 原地旋转的加速度阈值更宽松
能够从静止状态到变化状态,去根据阈值来判断这个“变化”:进入滑行状态 / 只是原地摆动
- 但是还是不够灵敏
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- 算法阈值定义 ---
//两个判断是否静止的必要条件:动态零速更新(ZUPT)阈值
// 加速方差阈值,提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define ZUPT_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define ZUPT_GYR_VARIANCE_THRESHOLD 5.0f
// 用于原地旋转判断的加速度方差阈值。此值比ZUPT阈值更宽松
// 以允许原地旋转时身体的正常晃动,但仍能与真实滑行时的剧烈加速度变化区分开。
#define ROTATING_ACC_VARIANCE_THRESHOLD 0.8f
// 用于启动滑雪判断的陀螺仪方差阈值。此值比ZUPT阈值更宽松
// 以允许启动瞬间的正常抖动,但仍能过滤掉混乱的、非滑雪的晃动。
#define SKIING_GYR_VARIANCE_THRESHOLD 15.0f
// 旋转/摆动检测阈值:角速度总模长大于此值(度/秒),认为正在进行非滑雪的旋转或摆动
#define ROTATION_GYR_MAG_THRESHOLD 45.0f
// 启动滑雪阈值:加速度模长与重力的差值大于此值,认为开始运动
// 降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_SKIING_ACC_THRESHOLD 0.5f
// --- 用于消除积分漂移的滤波器和阈值 ---
// 高通滤波器系数 (alpha)。alpha 越接近1滤除低频(直流偏移)的效果越强,但可能滤掉真实的慢速运动。
// alpha = RC / (RC + dt)
#define HPF_ALPHA 0.95f // 换算大概就是衰减频率低于约 0.84 Hz 的信号
// 任何比“大约1秒钟变化一次”还要慢的运动其加速度信号也会被部分衰减。
// 而滑雪时的快速转弯、加减速等动作,其频率远高于 0.84 Hz它们的信号会被保留下来。
// 加速度死区阈值 (m/s^2)。低于此阈值的加速度被认为是噪声,不参与积分。
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
#define ACC_DEAD_ZONE_THRESHOLD 0.15f
// --- 模拟摩擦力,进行速度衰减 ---
#define SPEED_ATTENUATION 0.98f
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = SKIING_STATE_STATIC;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 驱动输出的角度与标准航空定义相反,需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
// TODO: 当引入三轴磁力计后,这里的 yaw 应由磁力计和陀螺仪融合解算得出,以解决航向漂移问题。
// 目前 yaw 暂时不参与计算,因为仅靠加速度计和陀螺仪无法获得准确的绝对航向角。
// float yaw = -angle[2] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
// 这个矩阵将设备测量的加速度(ax, ay, az)正确地转换到世界坐标系(acc_world)。
// 注意这里没有使用yaw主要关心的是坡面上的运动绝对航向暂时不影响速度和距离的计算。
// TODO
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*
* @param buffer 传进来的三轴数据:陀螺仪/加速度
* @return float 返回方差和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 状态机更新
*
* @param tracker
* @param acc_device_ms2 三轴加速度m/s^2
* @param gyr_dps 三轴陀螺仪dps
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = SKIING_STATE_STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]);
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]);
// --- 状态切换逻辑 (按优先级) ---
// 优先级1动态零速更新 (ZUPT) - 最严格和最优先的“刹车”
if (acc_variance < ZUPT_ACC_VARIANCE_THRESHOLD && gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_STATIC;
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
// 关键:当检测到静止时,必须重置高通滤波器的状态
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
return;
}
// 优先级2原地旋转 - 特殊的、非滑雪的运动状态
// 条件:角速度很大,同时线性加速度的晃动在一个“中等”范围内。
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && acc_variance < ROTATING_ACC_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_ROTATING;
return;
}
// 优先级3启动滑雪 - “油门”
// 条件:有足够大的线性加速度,同时陀螺仪的抖动在一个“合理”(而非“完全静止”)的范围内。
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD && gyr_variance < SKIING_GYR_VARIANCE_THRESHOLD) {
tracker->state = SKIING_STATE_SKIING;
return;
}
// 如果不满足任何启动或停止条件,则保持当前状态(滑雪中)
// 如果当前是静止或旋转但没有满足启动条件则状态会保持直到满足ZUPT或旋转条件。
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps
* @param angle 欧若拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 根据状态进行计算 ---
if (tracker->state == SKIING_STATE_SKIING) {
// 坐标转换 & 移除重力
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
tracker->acc_world[2] -= G_ACCELERATION;
// 对世界坐标系下的加速度进行高通滤波,消除直流偏置和重力残差
for (int i = 0; i < 3; i++) {
tracker->acc_world_filtered[i] = HPF_ALPHA * (tracker->acc_world_filtered[i] + tracker->acc_world[i] - tracker->acc_world_unfiltered_prev[i]);
tracker->acc_world_unfiltered_prev[i] = tracker->acc_world[i];
}
// 应用加速度死区,忽略微小抖动和噪声
float acc_horizontal_mag = sqrtf(tracker->acc_world_filtered[0] * tracker->acc_world_filtered[0] +
tracker->acc_world_filtered[1] * tracker->acc_world_filtered[1]);
if (acc_horizontal_mag > ACC_DEAD_ZONE_THRESHOLD) {
// 只有当水平加速度足够大时,才进行速度积分
tracker->velocity[0] += tracker->acc_world_filtered[0] * dt;
tracker->velocity[1] += tracker->acc_world_filtered[1] * dt;
// 垂直方向的速度暂时不积分,极易受姿态误差影响而漂移
// tracker->velocity[2] += tracker->acc_world_filtered[2] * dt;
}
// 如果加速度小于阈值,则不更新速度,相当于速度保持不变(或受下一步的阻尼影响而衰减)
} else {
// 在静止或旋转状态下,速度已经在状态机内部被清零
// 额外增加速度衰减,模拟摩擦力,进一步抑制漂移
tracker->velocity[0] *= SPEED_ATTENUATION;
tracker->velocity[1] *= SPEED_ATTENUATION;
tracker->velocity[2] = 0; // 垂直速度强制归零
}
// --- 更新速率和距离 ---
// 只基于水平速度计算速率和距离
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
tracker->distance += tracker->speed * dt;
}
/**
* @brief 传感器数据采集与处理任务外部每10ms调用一次如果需要更新时间间隔也需要同步更新宏“ DELTA_TIME ”
*
* @param acc_data_buf 三轴加速度原始数据
* @param gyr_data_buf 三轴陀螺仪原始数据
* @return BLE_send_data_t
*/
BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) {
static skiing_tracker_t my_skiing_tracker;
static int initialized = 0;
static int calibration_done = 0;
static signed short combined_raw_data[6];
static float final_angle_data[3]; // 计算得到的欧若拉角
static float calibrated_acc_g[3]; // 转换后的加速度计数据
static float calibrated_gyr_dps[3]; // 转换后的陀螺仪数据
const float delta_time = DELTA_TIME;
BLE_send_data_t BLE_send_data;
BLE_KS_send_data_t KS_data;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
unsigned char status;
if (!calibration_done) { //第1次启动开启零漂检测
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
if (status == 1) {
calibration_done = 1;
printf("Sensor calibration successful! Skiing mode is active.\n");
}
} else {
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
}
if (status == 1) {
// 加速度 LSB to g
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
calibrated_gyr_dps[0] = (float)combined_raw_data[3] * 0.061f;
calibrated_gyr_dps[1] = (float)combined_raw_data[4] * 0.061f;
calibrated_gyr_dps[2] = (float)combined_raw_data[5] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, calibrated_gyr_dps, final_angle_data, delta_time);
// 打印逻辑保持不变
// static int count = 0;
// if(count >= 10){
// printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
// my_skiing_tracker.state,
// my_skiing_tracker.speed,
// my_skiing_tracker.distance);
// count = 0;
// } else {
// count++;
// }
BLE_send_data.sensor_state = status;
BLE_send_data.skiing_state = my_skiing_tracker.state;
for (int i = 0; i < 3; i++) {
#ifndef XTELL_TEST
BLE_send_data.acc_original[i] = (int)acc_data_buf[i];
BLE_send_data.gyr_original[i] = (int)gyr_data_buf[i];
#endif
#if KS_BLE
KS_data.acc_KS_g[i] = (int)calibrated_acc_g[i];
KS_data.gyr_KS_dps[i] = (int)calibrated_gyr_dps[i];
KS_data.angle_KS[i] = (int)final_angle_data[i];
#endif
}
BLE_send_data.speed_cms = (int)(my_skiing_tracker.speed * 100);
BLE_send_data.distance_cm = (int)(my_skiing_tracker.distance * 100);
} else if (status == 0) {
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
BLE_send_data.sensor_state = status;
#if KS_BLE
memset(&KS_data, 0, sizeof(BLE_send_data_t));
#endif
// printf("Sensor is calibrating...\n");
} else {
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
BLE_send_data.sensor_state = status;
#if KS_BLE
memset(&KS_data, 0, sizeof(BLE_send_data_t));
#endif
// printf("Angle calculation error or calibration not finished.\n");
}
return BLE_send_data;
}

View File

@ -0,0 +1,74 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
// 定义滑雪者可能的状态
typedef enum {
SKIING_STATE_STATIC, // 静止或动态稳定
SKIING_STATE_SKIING, // 正在滑雪
SKIING_STATE_ROTATING, // 正在原地旋转 (新增)
SKIING_STATE_FALLEN, // 已摔倒
SKIING_STATE_UNKNOWN // 未知状态
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 5 // 用于计算方差的数据窗口大小 (5个样本 @ 100Hz = 50ms),减小延迟,提高实时性
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的加速度
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3]; //过滤过的
float acc_world_unfiltered_prev[3]; //上一次没过滤的
} skiing_tracker_t;
//ble发送的数据
typedef struct __attribute__((packed)){ //该结构体取消内存对齐
char sensor_state;
char skiing_state;
int speed_cms; //求出的速度cm/s
int distance_cm; //求出的距离cm
#ifndef XTELL_TEST
int acc_original[3]; //直接读取传感器得到的原始三轴加速度
int gyr_original[3]; //直接读取传感器得到的原始三轴陀螺仪
#endif
}BLE_send_data_t;
typedef struct{
int acc_KS_g[3]; //卡尔曼后LSB to g 三轴加速度数据
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
/**
* @brief 传感器数据采集与处理任务外部每10ms调用一次如果需要更新时间间隔也需要同步更新宏“ DELTA_TIME ”
*
* @param acc_data_buf 三轴加速度原始数据
* @param gyr_data_buf 三轴陀螺仪原始数据
* @return BLE_send_data_t
*/
BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) ;
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,276 @@
/*
简化的滑雪追踪器:
- 直接读取六轴数据
- 分离重力分量
- 直接积分求速度和距离
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define ENABLE_XLOG 1
#ifdef xlog
#undef xlog
#endif
#if ENABLE_XLOG
#define xlog(format, ...) printf("[XT:%s] " format, __func__, ##__VA_ARGS__)
#else
#define xlog(format, ...) ((void)0)
#endif
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// 运动检测阈值 (m/s^2)。当水平加速度的模长大于此值时,认为处于运动状态。
#define MOTION_THRESHOLD 0.2f
static skiing_tracker_t my_skiing_tracker;
#ifdef XTELL_TEST
BLE_KS_send_data_t KS_data;
debug_t debug1;
debug_t debug2;
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = SKIING_STATE_STATIC;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 驱动输出的角度与标准航空定义相反,需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
void clear_speed(void){
my_skiing_tracker.state = SKIING_STATE_STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
void start_detection(void){
my_skiing_tracker.state = SKIING_STATE_STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.distance = 0;
my_skiing_tracker.speed = 0;
}
void stop_detection(void){
my_skiing_tracker.state = SKINNG_STOP_DETECTION;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps (当前简化版本未使用)
* @param angle 欧拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !angle || dt <= 0) {
return;
}
//停止检测
if(my_skiing_tracker.state == SKINNG_STOP_DETECTION)
return;
// --- 数据预处理 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// --- 坐标转换 & 移除重力 ---
// 1. 将设备坐标系下的总加速度转换到世界坐标系
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
// 2. 在世界坐标系下减去Z轴的重力分量得到线性加速度
tracker->acc_world[2] -= G_ACCELERATION;
// --- 运动状态判断与计算 ---
// 计算水平方向的线性加速度模长
float acc_horizontal_mag = sqrtf(tracker->acc_world[0] * tracker->acc_world[0] +
tracker->acc_world[1] * tracker->acc_world[1]);
if (acc_horizontal_mag > MOTION_THRESHOLD) {
// 加速
tracker->state = SKIING_STATE_SKIING;
// 积分计算速度(只考虑水平方向,避免垂直方向漂移)
tracker->velocity[0] += tracker->acc_world[0] * dt;
tracker->velocity[1] += tracker->acc_world[1] * dt;
tracker->velocity[2] = 0; // 垂直速度强制为0
// --- 更新速率和距离 ---
// 只基于水平速度计算速率
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
// 累加距离
tracker->distance += tracker->speed * dt;
return;
} else {
//匀速
tracker->distance += tracker->speed * dt;
return;
}
}
// 获取当前 RTC 时间戳
time_t get_rtc_timestamp(void) {
static void *rtc_hdl = NULL;
struct sys_time rtc_time = {0};
dev_ioctl(rtc_hdl, IOCTL_GET_SYS_TIME, (u32)&rtc_time);
return rtc_sys_time_to_timestamp(&rtc_time);
}
/**
* @brief 传感器数据采集与处理任务外部每10ms调用一次如果需要更新时间间隔也需要同步更新宏“ DELTA_TIME ”
*
* @param acc_data_buf 三轴加速度原始数据
* @param gyr_data_buf 三轴陀螺仪原始数据
* @return BLE_send_data_t
*/
BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) {
static int initialized = 0;
static int calibration_done = 0;
static signed short combined_raw_data[6];
static float final_angle_data[3]; // 计算得到的欧若拉角
static float calibrated_acc_g[3]; // 转换后的加速度计数据
static float calibrated_gyr_dps[3]; // 转换后的陀螺仪数据
float delta_time = DELTA_TIME + 0.03f; //观察得到实际上调用间隔会多30ms
BLE_send_data_t BLE_send_data;
// xlog("===========start\n");
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
unsigned char status;
if (!calibration_done) { //第1次启动开启零漂检测
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
if (status == 1) {
calibration_done = 1;
printf("Sensor calibration successful! Skiing mode is active.\n");
}
} else {
// printf("Calculate the time interval =============== start\n");
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
// printf("Calculate the time interval =============== end\n");
}
if (status == 1) {
// 加速度 LSB to g
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
calibrated_gyr_dps[0] = (float)combined_raw_data[3] * 0.061f;
calibrated_gyr_dps[1] = (float)combined_raw_data[4] * 0.061f;
calibrated_gyr_dps[2] = (float)combined_raw_data[5] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, calibrated_gyr_dps, final_angle_data, delta_time);
// static int count = 0;
// if(count >= 10){
// printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
// my_skiing_tracker.state,
// my_skiing_tracker.speed,
// my_skiing_tracker.distance);
// printf("calibrated_acc_g: %.2f, %.2f, %.2f\n",
// calibrated_acc_g[0],
// calibrated_acc_g[1],
// calibrated_acc_g[2]);
// count = 0;
// } else {
// count++;
// }
BLE_send_data.sensor_state = status;
BLE_send_data.skiing_state = my_skiing_tracker.state;
for (int i = 0; i < 3; i++) {
#ifndef XTELL_TEST
BLE_send_data.acc_original[i] = (int)acc_data_buf[i];
BLE_send_data.gyr_original[i] = (int)gyr_data_buf[i];
#endif
#if KS_BLE
KS_data.acc_KS[i] = (int)(calibrated_acc_g[i] * G_ACCELERATION * 100); //cm/s^s
KS_data.gyr_KS_dps[i] = (int)calibrated_gyr_dps[i];
KS_data.angle_KS[i] = (int)final_angle_data[i];
#endif
}
BLE_send_data.speed_cms = (int)(my_skiing_tracker.speed * 100);
BLE_send_data.distance_cm = (int)(my_skiing_tracker.distance * 100);
//
} else if (status == 0) {
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
BLE_send_data.sensor_state = status;
#if KS_BLE
memset(&KS_data, 0, sizeof(BLE_send_data_t));
#endif
// printf("Sensor is calibrating...\n");
} else {
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
BLE_send_data.sensor_state = status;
#if KS_BLE
memset(&KS_data, 0, sizeof(BLE_send_data_t));
#endif
// printf("Angle calculation error or calibration not finished.\n");
}
// xlog("end++++++++++++++\n");
return BLE_send_data;
}

View File

@ -0,0 +1,67 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
// 定义滑雪者可能的状态
typedef enum {
SKIING_STATE_STATIC, // 静止
SKIING_STATE_SKIING, // 正在滑雪
SKINNG_STOP_DETECTION, // 停止检测
} skiing_state_t;
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的线性加速度
} skiing_tracker_t;
//ble发送的数据
typedef struct __attribute__((packed)){ //该结构体取消内存对齐
char sensor_state;
char skiing_state;
int speed_cms; //求出的速度cm/s
int distance_cm; //求出的距离cm
#ifndef XTELL_TEST
int acc_original[3]; //直接读取传感器得到的原始三轴加速度
int gyr_original[3]; //直接读取传感器得到的原始三轴陀螺仪
#endif
}BLE_send_data_t;
typedef struct{
int acc_KS[3]; //卡尔曼后LSB转换后的 三轴加速度数据cm/s^2
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
#ifdef XTELL_TEST
typedef struct{
float acc_variance; //三轴加速度方差之和
float gyr_variance; //三轴陀螺仪方差之和
float acc_magnitude; //三轴加速度模长
float gyr_magnitude; //三轴陀螺仪模长
}debug_t;
#endif
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
/**
* @brief 传感器数据采集与处理任务外部每10ms调用一次如果需要更新时间间隔也需要同步更新宏“ DELTA_TIME ”
*
* @param acc_data_buf 三轴加速度原始数据
* @param gyr_data_buf 三轴陀螺仪原始数据
* @return BLE_send_data_t
*/
BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) ;
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,501 @@
/*
虽然sensor_processing_task是10ms调用一次
但是实际上上一次调用该函数的时间点和下一次调用该函数的时间点会相差40ms
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- ZUPT ---
//两个判断是否静止的必要条件:动态零速更新(ZUPT)阈值
// 加速方差阈值,提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define ZUPT_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define ZUPT_GYR_VARIANCE_THRESHOLD 5.0f
// --- 启动滑雪阈值 ---
// 加速度模长与重力的差值大于此值,认为开始运动;降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_SKIING_ACC_THRESHOLD 0.5f
// 陀螺仪方差阈值,以允许启动瞬间的正常抖动,但仍能过滤掉混乱的、非滑雪的晃动。
#define SKIING_GYR_VARIANCE_THRESHOLD 15.0f
// --- 滑雪过程 ---
//加速度 模长,低于此值视为 在做匀速运动
#define SKIING_ACC_MAG_THRESHOLD 0.5f
//陀螺仪 模长,高于此值视为 摔倒了
#define FALLEN_GRY_MAG_THRESHOLD 1000.0f //未确定
// --- 原地旋转抖动 ---
// 用于原地旋转判断的加速度方差阈值。此值比ZUPT阈值更宽松
// 以允许原地旋转时身体的正常晃动,但仍能与真实滑行时的剧烈加速度变化区分开。
#define ROTATING_ACC_VARIANCE_THRESHOLD 0.8f
// 旋转/摆动检测阈值:角速度总模长大于此值(度/秒),认为正在进行非滑雪的旋转或摆动
#define ROTATION_GYR_MAG_THRESHOLD 120.0f
// --- 滑雪转弯动 ---
// 加速度方差阈值,大于此值,滑雪过程可能发生了急转弯
#define WHEEL_ACC_VARIANCE_THRESHOLD 7.0f
// 旋转/摆动检测阈值:角速度总模长大于此值(度/秒),认为滑雪过程中进行急转弯
#define WHEEL_GYR_MAG_THRESHOLD 220.0f // 150.0f 到 250.0f之间进行调整
// --- 用于消除积分漂移的滤波器和阈值 ---
// 高通滤波器系数 (alpha)。alpha 越接近1滤除低频(直流偏移)的效果越强,但可能滤掉真实的慢速运动。
// alpha = RC / (RC + dt)参考RC电路而来
#define HPF_ALPHA 0.995
// 加速度死区阈值 (m/s^2)。低于此阈值的加速度被认为是噪声,不参与积分。
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
#define ACC_DEAD_ZONE_THRESHOLD 0.05f
// --- 模拟摩擦力,进行速度衰减 ---
#define SPEED_ATTENUATION 1.0f //暂不模拟
#ifdef XTELL_TEST
BLE_KS_send_data_t KS_data;
debug_t debug1;
debug_t debug2;
#endif
static skiing_tracker_t my_skiing_tracker;
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
void clear_speed(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
void start_detection(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.distance = 0;
my_skiing_tracker.speed = 0;
}
void stop_detection(void){
my_skiing_tracker.state = STOP_DETECTION;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = STATIC;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 驱动输出的角度与标准航空定义相反,需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
// TODO: 当引入三轴磁力计后,这里的 yaw 应由磁力计和陀螺仪融合解算得出,以解决航向漂移问题。
// 目前 yaw 暂时不参与计算,因为仅靠加速度计和陀螺仪无法获得准确的绝对航向角。
// float yaw = -angle[2] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
// 这个矩阵将设备测量的加速度(ax, ay, az)正确地转换到世界坐标系(acc_world)。
// 注意这里没有使用yaw主要关心的是坡面上的运动绝对航向暂时不影响速度和距离的计算。
// TODO
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*
* @param buffer 传进来的三轴数据:陀螺仪/加速度
* @return float 返回方差和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 状态机更新
*
* @param tracker 传入同步修改后传出
* @param acc_device_ms2 三轴加速度m/s^2
* @param gyr_dps 三轴陀螺仪dps
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]);
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]);
#ifdef XTELL_TEST
debug1.acc_variance =acc_variance;
debug1.gyr_variance =gyr_variance;
debug1.gyr_magnitude=gyr_magnitude;
debug1.acc_magnitude=fabsf(acc_magnitude - G_ACCELERATION);
#endif
#if 0
//正在滑雪
if(tracker->state == NO_CONSTANT_SPEED ) {
//摔倒了
if(gyr_magnitude > FALLEN_GRY_MAG_THRESHOLD){
tracker->state = FALLEN;
return;
}
//可能进入了匀速状态
if(gyr_magnitude > SKIING_GYR_MAG_THRESHOLD && acc_magnitude < SKIING_ACC_MAG_THRESHOLD){
tracker->state = CONSTANT_SPEED;
return;
}
//急转弯
if(gyr_magnitude > WHEEL_GYR_MAG_THRESHOLD && acc_variance > WHEEL_ACC_VARIANCE_THRESHOLD){
//TODO可以考虑清掉速度消除积分带来的漂移
tracker->state = WHEEL;
return;
}
}
// --- 状态切换逻辑 (按优先级) ---
// 优先级1静止
if (acc_variance < ZUPT_ACC_VARIANCE_THRESHOLD && gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD) {
tracker->state = STATIC;
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
return;
}
// 优先级2原地旋转 - 特殊的、非滑雪的运动状态
// 条件:角速度很大,同时线性加速度的晃动在一个“中等”范围内。
if (tracker->state == STATIC && gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && acc_variance < ROTATING_ACC_VARIANCE_THRESHOLD) {
tracker->state = ROTATING;
return;
}
// 优先级3启动滑雪 - “油门”
// 条件:有足够大的线性加速度,同时陀螺仪的抖动在一个“合理”(而非“完全静止”)的范围内。
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD && gyr_variance < SKIING_GYR_VARIANCE_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
return;
}
// 如果不满足任何启动或停止条件,则保持当前状态(滑雪中)
// 如果当前是静止或旋转但没有满足启动条件则状态会保持直到满足ZUPT或旋转条件。
#else
// 无论当前是什么状态,静止总是最高优先级
if (acc_variance < ZUPT_ACC_VARIANCE_THRESHOLD && gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD) {
tracker->state = STATIC;
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
return;
}
switch (tracker->state) {
case STATIC:
//不break会往下执行判断是否进入非匀速状态
case ROTATING: // 从静止或原地旋转可以启动
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD && gyr_variance < SKIING_GYR_VARIANCE_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
} else if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && acc_variance < ROTATING_ACC_VARIANCE_THRESHOLD) {
tracker->state = ROTATING;
}
break;
case NO_CONSTANT_SPEED: //非匀速状态
if (gyr_magnitude > FALLEN_GRY_MAG_THRESHOLD) {
tracker->state = FALLEN; //摔倒
} else if (gyr_magnitude > WHEEL_GYR_MAG_THRESHOLD && acc_variance > WHEEL_ACC_VARIANCE_THRESHOLD) {
tracker->state = WHEEL; //转弯
} else if (fabsf(acc_magnitude - G_ACCELERATION) < SKIING_ACC_MAG_THRESHOLD) {
tracker->state = CONSTANT_SPEED; //匀速
}
break;
case CONSTANT_SPEED: //匀速状态
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
//TODO可以添加进入转弯或摔倒的判断
break;
case WHEEL:
// 从转弯状态,检查转弯是否结束
// 如果角速度和加速度方差都降下来了,就回到普通滑行状态
if (gyr_magnitude < WHEEL_GYR_MAG_THRESHOLD * 0.8f && acc_variance < WHEEL_ACC_VARIANCE_THRESHOLD * 0.8f) { // 乘以一个滞后系数避免抖动
tracker->state = NO_CONSTANT_SPEED;
}
break;
case FALLEN:
// TODO回到 STATIC
break;
}
#endif
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps
* @param angle 欧若拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
if(my_skiing_tracker.state == STOP_DETECTION)
return;
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// 坐标转换 & 移除重力
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
tracker->acc_world[2] -= G_ACCELERATION;
// 对世界坐标系下的加速度进行高通滤波,消除直流偏置和重力残差
for (int i = 0; i < 3; i++) {
tracker->acc_world_filtered[i] = HPF_ALPHA * (tracker->acc_world_filtered[i] + tracker->acc_world[i] - tracker->acc_world_unfiltered_prev[i]);
tracker->acc_world_unfiltered_prev[i] = tracker->acc_world[i];
}
// 应用加速度死区,忽略微小抖动和噪声
float acc_horizontal_mag = sqrtf(tracker->acc_world_filtered[0] * tracker->acc_world_filtered[0] +
tracker->acc_world_filtered[1] * tracker->acc_world_filtered[1]);
// --- 根据状态进行计算 ---
if (tracker->state == NO_CONSTANT_SPEED) {
if (acc_horizontal_mag > ACC_DEAD_ZONE_THRESHOLD) {
// 只有当水平加速度足够大时,才进行速度积分
tracker->velocity[0] += tracker->acc_world_filtered[0] * dt;
tracker->velocity[1] += tracker->acc_world_filtered[1] * dt;
// 垂直方向的速度暂时不积分,极易受姿态误差影响而漂移
// tracker->velocity[2] += tracker->acc_world_filtered[2] * dt;
}
#ifdef XTELL_TEST
debug2.acc_magnitude = acc_horizontal_mag;
#endif
// 如果加速度小于阈值,则不更新速度,相当于速度保持不变(或受下一步的阻尼影响而衰减)
// --- 更新速率和距离 ---
// 只基于水平速度计算速率和距离
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
tracker->distance += tracker->speed * dt;
}else if(tracker->state == CONSTANT_SPEED){ //匀速
#ifdef XTELL_TEST
debug2.acc_magnitude = acc_horizontal_mag;
#endif
//保持上次的速度不变。只更新距离
tracker->distance += tracker->speed * dt;
}else{
// 在静止或旋转状态下,速度已经在状态机内部被清零
// 额外增加速度衰减,模拟摩擦力,进一步抑制漂移
tracker->velocity[0] *= SPEED_ATTENUATION;
tracker->velocity[1] *= SPEED_ATTENUATION;
tracker->velocity[2] = 0; // 垂直速度强制归零
}
}
/**
* @brief 传感器数据采集与处理任务外部每10ms调用一次如果需要更新时间间隔也需要同步更新宏“ DELTA_TIME ”
*
* @param acc_data_buf 三轴加速度原始数据
* @param gyr_data_buf 三轴陀螺仪原始数据
* @return BLE_send_data_t
*/
BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) {
static int initialized = 0;
static int calibration_done = 0;
static signed short combined_raw_data[6];
static float final_angle_data[3]; // 计算得到的欧若拉角
static float calibrated_acc_g[3]; // 转换后的加速度计数据
static float calibrated_gyr_dps[3]; // 转换后的陀螺仪数据
const float delta_time = DELTA_TIME + 0.03f;
BLE_send_data_t BLE_send_data;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
unsigned char status;
if (!calibration_done) { //第1次启动开启零漂检测
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
if (status == 1) {
calibration_done = 1;
printf("Sensor calibration successful! Skiing mode is active.\n");
}
} else {
// printf("Calculate the time interval =============== start\n");
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
}
if (status == 1) {
// 加速度 LSB to g
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
calibrated_gyr_dps[0] = (float)combined_raw_data[3] * 0.061f;
calibrated_gyr_dps[1] = (float)combined_raw_data[4] * 0.061f;
calibrated_gyr_dps[2] = (float)combined_raw_data[5] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, calibrated_gyr_dps, final_angle_data, delta_time);
// static int count = 0;
// if(count >= 10){
// printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
// my_skiing_tracker.state,
// my_skiing_tracker.speed,
// my_skiing_tracker.distance);
// printf("calibrated_acc_g: %.2f, %.2f, %.2f\n",
// calibrated_acc_g[0],
// calibrated_acc_g[1],
// calibrated_acc_g[2]);
// count = 0;
// } else {
// count++;
// }
BLE_send_data.sensor_state = status;
BLE_send_data.skiing_state = my_skiing_tracker.state;
for (int i = 0; i < 3; i++) {
#ifndef XTELL_TEST
BLE_send_data.acc_original[i] = (int)acc_data_buf[i];
BLE_send_data.gyr_original[i] = (int)gyr_data_buf[i];
#endif
#if KS_BLE
KS_data.acc_KS[i] = (int)(calibrated_acc_g[i] * G_ACCELERATION * 100); //cm/s^s
KS_data.gyr_KS_dps[i] = (int)calibrated_gyr_dps[i];
KS_data.angle_KS[i] = (int)final_angle_data[i];
#endif
}
BLE_send_data.speed_cms = (int)(my_skiing_tracker.speed * 100);
BLE_send_data.distance_cm = (int)(my_skiing_tracker.distance * 100);
// printf("Calculate the time interval =============== end\n");
} else if (status == 0) {
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
BLE_send_data.sensor_state = status;
#if KS_BLE
memset(&KS_data, 0, sizeof(BLE_send_data_t));
#endif
// printf("Sensor is calibrating...\n");
} else {
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
BLE_send_data.sensor_state = status;
#if KS_BLE
memset(&KS_data, 0, sizeof(BLE_send_data_t));
#endif
// printf("Angle calculation error or calibration not finished.\n");
}
return BLE_send_data;
}

View File

@ -0,0 +1,85 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
// 定义滑雪者可能的状态
typedef enum {
STATIC, // 静止或动态稳定
NO_CONSTANT_SPEED, // 正在滑雪,非匀速
CONSTANT_SPEED, // 正在滑雪,匀速
ROTATING, // 正在原地旋转
WHEEL, // 转弯
FALLEN, // 已摔倒
STOP_DETECTION, // 停止检测
UNKNOWN // 未知状态
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 5 // 用于计算方差的数据窗口大小 (5个样本 @ 100Hz = 50ms),减小延迟,提高实时性
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的加速度
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3]; //过滤过的
float acc_world_unfiltered_prev[3]; //上一次没过滤的
} skiing_tracker_t;
//ble发送的数据
typedef struct __attribute__((packed)){ //该结构体取消内存对齐
char sensor_state;
char skiing_state;
int speed_cms; //求出的速度cm/s
int distance_cm; //求出的距离cm
#ifndef XTELL_TEST
int acc_original[3]; //直接读取传感器得到的原始三轴加速度
int gyr_original[3]; //直接读取传感器得到的原始三轴陀螺仪
#endif
}BLE_send_data_t;
typedef struct{
int acc_KS[3]; //卡尔曼后LSB转换后的 三轴加速度数据cm/s^2
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
#ifdef XTELL_TEST
typedef struct{
float acc_variance; //三轴加速度方差之和
float gyr_variance; //三轴陀螺仪方差之和
float acc_magnitude; //三轴加速度模长
float gyr_magnitude; //三轴陀螺仪模长
}debug_t;
#endif
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
/**
* @brief 传感器数据采集与处理任务外部每10ms调用一次如果需要更新时间间隔也需要同步更新宏“ DELTA_TIME ”
*
* @param acc_data_buf 三轴加速度原始数据
* @param gyr_data_buf 三轴陀螺仪原始数据
* @return BLE_send_data_t
*/
BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) ;
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,583 @@
/*
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
// --- 静止检测 ---
//两个判断是否静止的必要条件:动态零速更新(ZUPT)阈值
// 加速方差阈值,提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define STOP_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define STOP_GYR_VARIANCE_THRESHOLD 5.0f
// 静止时候的陀螺仪模长
#define STOP_GYR_MAG_THRESHOLD 15
// --- --- ---
// --- 启动滑雪阈值 ---
// 加速度模长与重力的差值大于此值,认为开始运动;降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_ACC_MAG_THRESHOLD 1.0f //0.5、1
// 陀螺仪方差阈值,以允许启动瞬间的正常抖动,但仍能过滤掉混乱的、非滑雪的晃动。
#define START_GYR_VARIANCE_THRESHOLD 15.0f
// --- --- ---
// --- 滑雪过程 ---
//加速度 模长(不含重力),低于此值视为 在做匀速运动
#define SKIING_ACC_MAG_THRESHOLD 0.5f
//陀螺仪 模长,高于此值视为 摔倒了
#define FALLEN_GRY_MAG_THRESHOLD 2000.0f //未确定
// --- --- ---
// --- 原地旋转抖动 ---
// 加速度 方差 阈值。此值比 静止检测 阈值更宽松,
#define WOBBLE_ACC_VARIANCE_THRESHOLD 0.5f
// 加速度 模长 阈值
#define WOBBLE_ACC_MAG_THRESHOLD 1.0f
// 角速度 总模长 大于此值(度/秒),认为正在进行非滑雪的旋转或摆动
#define ROTATION_GYR_MAG_THRESHOLD 30.0f
// --- --- ---
// --- 滑雪转弯动 ---
// 加速度 方差 阈值,大于此值,滑雪过程可能发生了急转弯
#define WHEEL_ACC_VARIANCE_THRESHOLD 7.0f
// 角速度 总模长 大于此值(度/秒),认为滑雪过程中进行急转弯
#define WHEEL_GYR_MAG_THRESHOLD 500.0f //
// --- --- ---
// --- 跳跃 ---
// 加速度模长低于此值(g),认为进入失重状态(IN_AIR)
#define AIRBORNE_ACC_MAG_LOW_THRESHOLD 0.4f
// 加速度模长高于此值(g),认为发生落地冲击(LANDING)
#define LANDING_ACC_MAG_HIGH_THRESHOLD 3.5f
// 起跳加速度阈值(g)用于进入TAKING_OFF状态
#define TAKEOFF_ACC_MAG_HIGH_THRESHOLD 1.8f
// 进入空中状态确认计数需要连续3个采样点加速度低于阈值才判断为起跳
#define AIRBORNE_CONFIRM_COUNT 3
// 落地状态确认计数加速度恢复到1g附近并持续2个采样点(20ms)则认为已落地
#define GROUNDED_CONFIRM_COUNT 2
// 最大滞空时间(秒),超过此时间强制认为已落地,防止状态锁死
#define MAX_TIME_IN_AIR 12.5f
// --- --- ---
// --- 用于消除积分漂移的滤波器和阈值 ---
// 高通滤波器系数 (alpha)。alpha 越接近1滤除低频(直流偏移)的效果越强,但可能滤掉真实的慢速运动。
// alpha = RC / (RC + dt)参考RC电路而来fc ≈ (1 - alpha) / (2 * π * dt)
#define HPF_ALPHA 0.999f
//0.995f 0.08 Hz 的信号
//0.999f 0.0159 Hz
// --- --- ---
// --- 低通滤波器 ---
// 低通滤波器系数 (alpha)。alpha 越小,滤波效果越强(更平滑),但延迟越大。
// alpha 推荐范围 0.7 ~ 0.95。可以从 0.85 开始尝试。
#define LPF_ALPHA 0.7f
// 加速度死区阈值 (m/s^2)。低于此阈值的加速度被认为是噪声,不参与积分。
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
//参考0.2f ~ 0.4f
#define ACC_DEAD_ZONE_THRESHOLD 0.05f
// --- 模拟摩擦力,进行速度衰减 ---
#define SPEED_ATTENUATION 1.0f //暂不模拟
BLE_KS_send_data_t KS_data;
#ifdef XTELL_TEST
debug_t debug1;
debug_t debug2;
#endif
static skiing_tracker_t my_skiing_tracker;
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
void clear_speed(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
void start_detection(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.distance = 0;
my_skiing_tracker.speed = 0;
}
void stop_detection(void){
my_skiing_tracker.state = STOP_DETECTION;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = STATIC;
}
/**
* @brief 当检测到落地时,计算空中的水平飞行距离并累加到总距离
*/
static void calculate_air_distance(skiing_tracker_t *tracker) {
float horizontal_speed_on_takeoff = sqrtf(
tracker->initial_velocity_on_takeoff[0] * tracker->initial_velocity_on_takeoff[0] +
tracker->initial_velocity_on_takeoff[1] * tracker->initial_velocity_on_takeoff[1]
);
float distance_in_air = horizontal_speed_on_takeoff * tracker->time_in_air;
tracker->distance += distance_in_air;
}
/**
* @brief 将设备坐标系下的加速度转换为世界坐标系
* @param acc_device 设备坐标系下的加速度 [x, y, z]
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
*/
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
{
// 驱动输出的角度与标准航空定义相反,需要取反才能用于标准旋转矩阵。
float pitch = -angle[0] * DEG_TO_RAD;
float roll = -angle[1] * DEG_TO_RAD;
// TODO: 当引入三轴磁力计后,这里的 yaw 应由磁力计和陀螺仪融合解算得出,以解决航向漂移问题。
// 目前 yaw 暂时不参与计算,因为仅靠加速度计和陀螺仪无法获得准确的绝对航向角。
// float yaw = -angle[2] * DEG_TO_RAD;
float cp = cosf(pitch);
float sp = sinf(pitch);
float cr = cosf(roll);
float sr = sinf(roll);
float ax = acc_device[0];
float ay = acc_device[1];
float az = acc_device[2];
// 使用经过验证的、正确的身体坐标系到世界坐标系的旋转矩阵 (基于 Y-X 旋转顺序)
// 这个矩阵将设备测量的加速度(ax, ay, az)正确地转换到世界坐标系(acc_world)。
// 注意这里没有使用yaw主要关心的是坡面上的运动绝对航向暂时不影响速度和距离的计算。
// TODO
acc_world[0] = cp * ax + sp * sr * ay + sp * cr * az;
acc_world[1] = 0 * ax + cr * ay - sr * az;
acc_world[2] = -sp * ax + cp * sr * ay + cp * cr * az;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*
* @param buffer 传进来的三轴数据:陀螺仪/加速度
* @return float 返回方差和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 摩擦力模拟,进行速度衰减
*
* @param tracker
*/
void forece_of_friction(skiing_tracker_t *tracker){
// 增加速度衰减,模拟摩擦力
tracker->velocity[0] *= SPEED_ATTENUATION;
tracker->velocity[1] *= SPEED_ATTENUATION;
tracker->velocity[2] = 0; // 垂直速度强制归零
}
/**
* @brief 状态机更新
*
* @param tracker 传入同步修改后传出
* @param acc_device_ms2 三轴加速度m/s^2
* @param gyr_dps 三轴陀螺仪dps
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]); //dps
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]); //m/s^s
float acc_magnitude_g = acc_magnitude / G_ACCELERATION; // 转换为g单位用于跳跃判断
#ifdef XTELL_TEST
debug1.acc_variance =acc_variance;
debug1.gyr_variance =gyr_variance;
debug1.gyr_magnitude=gyr_magnitude;
debug1.acc_magnitude=fabsf(acc_magnitude - G_ACCELERATION);
#endif
// --- 状态机逻辑 (核心修改区域) ---
#if 0 //暂时不考虑空中
// 1. 空中/落地状态的后续处理
if (tracker->state == IN_AIR) {
// A. 检测巨大冲击 -> 落地
if (acc_magnitude_g > LANDING_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = LANDING;
// B. 检测超时 -> 强制落地 (安全机制)
} else if (tracker->time_in_air > MAX_TIME_IN_AIR) {
tracker->state = LANDING;
// C. 检测恢复正常重力 (平缓落地)
} else if (acc_magnitude_g > 0.8f && acc_magnitude_g < 1.5f) {
tracker->grounded_entry_counter++;
if (tracker->grounded_entry_counter >= GROUNDED_CONFIRM_COUNT) {
tracker->state = LANDING;
}
} else {
tracker->grounded_entry_counter = 0;
}
return; // 在空中或刚切换到落地,结束本次状态判断
}
// 2. 严格的 "起跳->空中" 状态转换逻辑
// 只有当处于滑行状态时,才去检测起跳意图
if (tracker->state == NO_CONSTANT_SPEED || tracker->state == CONSTANT_SPEED || tracker->state == WHEEL) {
if (acc_magnitude_g > TAKEOFF_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = TAKING_OFF;
tracker->airborne_entry_counter = 0; // 准备检测失重
return;
}
}
// 只有在TAKING_OFF状态下才去检测是否进入失重
if (tracker->state == TAKING_OFF) {
if (acc_magnitude_g < AIRBORNE_ACC_MAG_LOW_THRESHOLD) {
tracker->airborne_entry_counter++;
if (tracker->airborne_entry_counter >= AIRBORNE_CONFIRM_COUNT) {
memcpy(tracker->initial_velocity_on_takeoff, tracker->velocity, sizeof(tracker->velocity));
tracker->time_in_air = 0;
tracker->state = IN_AIR;
tracker->airborne_entry_counter = 0;
tracker->grounded_entry_counter = 0;
return;
}
} else {
// 如果在起跳冲击后一段时间内没有失重,说明只是一个颠簸,恢复滑行
// 可以加一个小的超时计数器,这里为了简单先直接恢复
tracker->state = NO_CONSTANT_SPEED;
}
return; // 无论是否切换,都结束本次判断
}
#endif
// --- 静止判断 ---
if (acc_variance < STOP_ACC_VARIANCE_THRESHOLD && gyr_variance < STOP_GYR_VARIANCE_THRESHOLD && gyr_magnitude < STOP_GYR_MAG_THRESHOLD) {
tracker->state = STATIC;
return;
}
// --- 地面状态切换逻辑 ---
switch (tracker->state) {
case LANDING:
tracker->state = STATIC;
break;
case STATIC:
// 优先判断是否进入 WOBBLE 状态
// 条件:陀螺仪活动剧烈,但整体加速度变化不大(说明是原地转或晃)
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) < WOBBLE_ACC_MAG_THRESHOLD) {
tracker->state = WOBBLE;
}
// 只有在陀螺仪和加速度都满足“前进”特征时,才启动
else if (gyr_variance > START_GYR_VARIANCE_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
break;
case WOBBLE:
// 从 WOBBLE 状态启动的条件应该和从 STATIC 启动一样严格
if (gyr_variance > START_GYR_VARIANCE_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
// 如果陀螺仪活动减弱,则可能恢复静止
else if (gyr_magnitude < ROTATION_GYR_MAG_THRESHOLD * 0.8f) { // 增加迟滞,避免抖动
// 不直接跳回STATIC而是依赖下一轮的全局静止判断
}
break;
case NO_CONSTANT_SPEED: //非匀速状态
//暂时不考虑摔倒
// if (gyr_magnitude > FALLEN_GRY_MAG_THRESHOLD) {
// tracker->state = FALLEN; //摔倒
// } else
if (gyr_magnitude > WHEEL_GYR_MAG_THRESHOLD && acc_variance > WHEEL_ACC_VARIANCE_THRESHOLD) {
tracker->state = WHEEL; //转弯
} else if (fabsf(acc_magnitude - G_ACCELERATION) < SKIING_ACC_MAG_THRESHOLD) {
tracker->state = CONSTANT_SPEED; //匀速
}
break;
case CONSTANT_SPEED: //匀速状态
if (fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
//TODO可以添加进入转弯或摔倒的判断
break;
case WHEEL:
// 从转弯状态,检查转弯是否结束
// 如果角速度和加速度方差都降下来了,就回到普通滑行状态
if (gyr_magnitude < WHEEL_GYR_MAG_THRESHOLD * 0.8f && acc_variance < WHEEL_ACC_VARIANCE_THRESHOLD * 0.8f) { // 乘以一个滞后系数避免抖动
tracker->state = NO_CONSTANT_SPEED;
}
break;
case FALLEN:
// TODO回到 STATIC
break;
}
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps
* @param angle 欧若拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
if(my_skiing_tracker.state == STOP_DETECTION)
return;
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 根据状态执行不同的计算逻辑 ---
switch (tracker->state) {
case TAKING_OFF:
tracker->speed = 0.0f;
break;
case IN_AIR:
// 在空中时,只累加滞空时间
tracker->time_in_air += dt;
break;
case LANDING:
// 刚落地,计算空中距离
calculate_air_distance(tracker);
// 清理速度和滤波器状态,为恢复地面追踪做准备
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
break;
case WHEEL:
case NO_CONSTANT_SPEED:
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
tracker->acc_world[2] -= G_ACCELERATION;
float acc_world_temp[3]; // 临时变量存储当前周期的加速度
for (int i = 0; i < 2; i++) { // 只处理水平方向的 x 和 y 轴
// --- 核心修改:颠倒滤波器顺序为 HPF -> LPF ---
// 1. 高通滤波 (HPF) 先行: 消除因姿态误差导致的重力泄漏(直流偏置)
// HPF的瞬态响应会产生尖峰这是正常的。
tracker->acc_world_filtered[i] = HPF_ALPHA * (tracker->acc_world_filtered[i] + tracker->acc_world[i] - tracker->acc_world_unfiltered_prev[i]);
tracker->acc_world_unfiltered_prev[i] = tracker->acc_world[i];
// 2. 低通滤波 (LPF) 殿后: 平滑掉HPF产生的尖峰和传感器自身的高频振动噪声。
// 这里使用 tracker->acc_world_filtered[i] 作为LPF的输入。
tracker->acc_world_lpf[i] = (1.0f - LPF_ALPHA) * tracker->acc_world_filtered[i] + LPF_ALPHA * tracker->acc_world_lpf[i];
// 将最终处理完的加速度值存入临时变量
acc_world_temp[i] = tracker->acc_world_lpf[i];
}
// 计算处理后加速度的水平模长
float acc_horizontal_mag = sqrtf(acc_world_temp[0] * acc_world_temp[0] +
acc_world_temp[1] * acc_world_temp[1]);
#if XTELL_TEST
debug2.acc_magnitude = acc_horizontal_mag;
#endif
// 应用死区,并积分
if (acc_horizontal_mag > ACC_DEAD_ZONE_THRESHOLD) {
tracker->velocity[0] += acc_world_temp[0] * dt;
tracker->velocity[1] += acc_world_temp[1] * dt;
}
// 更新速度和距离
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
tracker->distance += tracker->speed * dt;
break;
case CONSTANT_SPEED:
//保持上次的速度不变。只更新距离
tracker->distance += tracker->speed * dt;
break;
case STATIC:
case WOBBLE:
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
#if XTELL_TEST
debug2.acc_magnitude = 0;
#endif
break;
case FALLEN:
// TODO
break;
default:
break;
}
}
/**
* @brief 滑雪数据计算
*
* @param acc_data_buf 传入的三轴加速度数据
* @param gyr_data_buf 传入的三轴陀螺仪数据
* @param angle_data 传入的欧若拉角数据
* @return BLE_send_data_t 要发送给蓝牙的数据
*/
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data) {
static int initialized = 0;
static float acc_data_g[3];
static float gyr_data_dps[3];
// const float delta_time = DELTA_TIME+0.01f;
// const float delta_time = DELTA_TIME + 0.005f;
const float delta_time = DELTA_TIME;
BLE_send_data_t BLE_send_data;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
#if ACC_RANGE==2
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 16384.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 16384.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 16384.0f;
#endif
#if ACC_RANGE==4
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 8192.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 8192.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 8192.0f;
#endif
#if ACC_RANGE==8
//±8g 4096
acc_data_g[0] = (float)acc_data_buf[0] / 4096.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 4096.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 4096.0f; //az
#endif
#if ACC_RANGE==16
//±16g 2048
acc_data_g[0] = (float)acc_data_buf[0] / 2048.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 2048.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 2048.0f; //az
#endif
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
gyr_data_dps[0] = (float)gyr_data_buf[0] * 0.061f;
gyr_data_dps[1] = (float)gyr_data_buf[1] * 0.061f;
gyr_data_dps[2] = (float)gyr_data_buf[2] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, acc_data_g, gyr_data_dps, angle_data, delta_time);
BLE_send_data.skiing_state = my_skiing_tracker.state;
for (int i = 0; i < 3; i++) {
#ifdef XTELL_TEST
BLE_send_data.acc_data[i] = (short)(acc_data_g[i] * 9.8f) * 100; //cm/^s2
BLE_send_data.gyr_data[i] = (short)gyr_data_dps[i]; //dps
BLE_send_data.angle_data[i] = angle_data[i];
#else
BLE_send_data.acc_data[i] = (short)acc_data_buf[i]; //原始adc数据
BLE_send_data.gyr_data[i] = (short)gyr_data_buf[i]; //原始adc数据
BLE_send_data.angle_data[i] = angle_data[i];
#endif
}
BLE_send_data.speed_cms = (int)(my_skiing_tracker.speed * 100);
BLE_send_data.distance_cm = (int)(my_skiing_tracker.distance * 100);
// printf("Calculate the time interval =============== end\n");
return BLE_send_data;
}

View File

@ -0,0 +1,88 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
// 定义滑雪者可能的状态
typedef enum {
STATIC, // 静止或动态稳定0
NO_CONSTANT_SPEED, // 正在滑雪非匀速1
CONSTANT_SPEED, // 正在滑雪匀速2
WOBBLE, // 正在原地旋转3
WHEEL, // 转弯4
FALLEN, // 已摔倒5
TAKING_OFF, // 起跳冲击阶段6
IN_AIR, // 空中失重阶段7
LANDING, // 落地冲击阶段8
STOP_DETECTION, // 停止检测9
UNKNOWN // 未知状态10
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 5 // 用于计算方差的数据窗口大小 (5个样本 @ 100Hz = 50ms),减小延迟,提高实时性
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_world[3]; // 在世界坐标系下的加速度
// 用于空中距离计算
float time_in_air; // 滞空时间计时器
float initial_velocity_on_takeoff[3]; // 起跳瞬间的速度向量
int airborne_entry_counter; // 进入空中状态的确认计数器
int grounded_entry_counter; // 落地确认计数器
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3]; //过滤过的
float acc_world_unfiltered_prev[3]; //上一次没过滤的
float acc_world_lpf[3]; // 经过低通滤波后的世界坐标系加速度
} skiing_tracker_t;
//ble发送的数据
typedef struct{ //__attribute__((packed)){ //该结构体取消内存对齐
char sensor_state;
char skiing_state;
int speed_cms; //求出的速度cm/s
int distance_cm; //求出的距离cm
short acc_data[3]; //三轴加速度, g
short gyr_data[3]; //三轴陀螺仪, dps
float angle_data[3]; //欧若拉角
}BLE_send_data_t;
typedef struct{
int acc_KS[3]; //卡尔曼后LSB转换后的 三轴加速度数据cm/s^2
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
#ifdef XTELL_TEST
typedef struct{
float acc_variance; //三轴加速度方差之和
float gyr_variance; //三轴陀螺仪方差之和
float acc_magnitude; //三轴加速度模长
float gyr_magnitude; //三轴陀螺仪模长
}debug_t;
#endif
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data) ;
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,651 @@
/*
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#include <math.h>
#include <string.h>
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
#define ENABLE_XLOG 1
#ifdef xlog
#undef xlog
#endif
#if ENABLE_XLOG
#define xlog(format, ...) printf("[XT:%s] " format, __func__, ##__VA_ARGS__)
#else
#define xlog(format, ...) ((void)0)
#endif
// --- 静止检测 ---
//两个判断是否静止的必要条件:动态零速更新(ZUPT)阈值
// 加速方差阈值,提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define STOP_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define STOP_GYR_VARIANCE_THRESHOLD 5.0f
// 静止时候的陀螺仪模长
#define STOP_GYR_MAG_THRESHOLD 15
// --- --- ---
// --- 启动滑雪阈值 ---
// 加速度模长与重力的差值大于此值,认为开始运动;降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_ACC_MAG_THRESHOLD 1.0f //0.5、1
// 陀螺仪方差阈值,以允许启动瞬间的正常抖动,但仍能过滤掉混乱的、非滑雪的晃动。
#define START_GYR_VARIANCE_THRESHOLD 15.0f
// --- --- ---
// --- 滑雪过程 ---
//加速度 模长(不含重力),低于此值视为 在做匀速运动
#define SKIING_ACC_MAG_THRESHOLD 0.5f
//陀螺仪 模长,高于此值视为 摔倒了
#define FALLEN_GRY_MAG_THRESHOLD 2000.0f //未确定
// --- --- ---
// --- 原地旋转抖动 ---
// 加速度 方差 阈值。此值比 静止检测 阈值更宽松,
#define WOBBLE_ACC_VARIANCE_THRESHOLD 0.5f
// 加速度 模长 阈值
#define WOBBLE_ACC_MAG_THRESHOLD 1.0f
// 角速度 总模长 大于此值(度/秒),认为正在进行非滑雪的旋转或摆动
#define ROTATION_GYR_MAG_THRESHOLD 30.0f
// --- --- ---
// --- 滑雪转弯动 ---
// 加速度 方差 阈值,大于此值,滑雪过程可能发生了急转弯
#define WHEEL_ACC_VARIANCE_THRESHOLD 7.0f
// 角速度 总模长 大于此值(度/秒),认为滑雪过程中进行急转弯
#define WHEEL_GYR_MAG_THRESHOLD 500.0f //
// --- --- ---
// --- 跳跃 ---
// 加速度模长低于此值(g),认为进入失重状态(IN_AIR)
#define AIRBORNE_ACC_MAG_LOW_THRESHOLD 0.4f
// 加速度模长高于此值(g),认为发生落地冲击(LANDING)
#define LANDING_ACC_MAG_HIGH_THRESHOLD 3.5f
// 起跳加速度阈值(g)用于进入TAKING_OFF状态
#define TAKEOFF_ACC_MAG_HIGH_THRESHOLD 1.8f
// 进入空中状态确认计数需要连续3个采样点加速度低于阈值才判断为起跳
#define AIRBORNE_CONFIRM_COUNT 3
// 落地状态确认计数加速度恢复到1g附近并持续2个采样点(20ms)则认为已落地
#define GROUNDED_CONFIRM_COUNT 2
// 最大滞空时间(秒),超过此时间强制认为已落地,防止状态锁死
#define MAX_TIME_IN_AIR 12.5f
// --- --- ---
// --- 用于消除积分漂移的滤波器和阈值 ---
// 高通滤波器系数 (alpha)。alpha 越接近1滤除低频(直流偏移)的效果越强,但可能滤掉真实的慢速运动。
// alpha = RC / (RC + dt)参考RC电路而来fc ≈ (1 - alpha) / (2 * π * dt)
#define HPF_ALPHA 0.999f
//0.995f 0.08 Hz 的信号
//0.999f 0.0159 Hz
// --- --- ---
// --- 低通滤波器 ---
// 低通滤波器系数 (alpha)。alpha 越小,滤波效果越强(更平滑),但延迟越大。
// alpha 推荐范围 0.7 ~ 0.95。可以从 0.85 开始尝试。
#define LPF_ALPHA 0.7f
// 加速度死区阈值 (m/s^2)。低于此阈值的加速度被认为是噪声,不参与积分。
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
//参考0.2f ~ 0.4f
#define ACC_DEAD_ZONE_THRESHOLD 0.05f
// --- 模拟摩擦力,进行速度衰减 ---
#define SPEED_ATTENUATION 1.0f //暂不模拟
BLE_KS_send_data_t KS_data;
static float quaternion_data[4];
#ifdef XTELL_TEST
debug_t debug1;
debug_t debug2;
#endif
static skiing_tracker_t my_skiing_tracker;
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
void clear_speed(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
void start_detection(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.distance = 0;
my_skiing_tracker.speed = 0;
}
void stop_detection(void){
my_skiing_tracker.state = STOP_DETECTION;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = STATIC;
}
/**
* @brief 当检测到落地时,计算空中的水平飞行距离并累加到总距离
*/
static void calculate_air_distance(skiing_tracker_t *tracker) {
float horizontal_speed_on_takeoff = sqrtf(
tracker->initial_velocity_on_takeoff[0] * tracker->initial_velocity_on_takeoff[0] +
tracker->initial_velocity_on_takeoff[1] * tracker->initial_velocity_on_takeoff[1]
);
float distance_in_air = horizontal_speed_on_takeoff * tracker->time_in_air;
tracker->distance += distance_in_air;
}
/**
* @brief 使用四元数直接从设备坐标系的加速度中移除重力分量
* @details 这种方法比使用欧拉角更精确、更稳定,且避免了万向节死锁。
* @param acc_device 输入:设备坐标系下的原始加速度 [x, y, z], 单位 m/s^2
* @param q 输入:表示姿态的四元数 [w, x, y, z]
* @param acc_linear_device 输出:设备坐标系下移除重力后的线性加速度 [x, y, z]
*/
void q_remove_gravity_with_quaternion(const float *acc_device, const float *q, float *acc_linear_device)
{
// 从四元数计算重力在设备坐标系下的投影
// G_device = R_transpose * G_world
// G_world = [0, 0, g]
// R_transpose 的第三列即为重力投影方向
float gx = 2.0f * (q[1] * q[3] - q[0] * q[2]);
float gy = 2.0f * (q[0] * q[1] + q[2] * q[3]);
float gz = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
// 从原始加速度中减去重力分量
acc_linear_device[0] = acc_device[0] - gx * G_ACCELERATION;
acc_linear_device[1] = acc_device[1] - gy * G_ACCELERATION;
acc_linear_device[2] = acc_device[2] - gz * G_ACCELERATION;
}
/**
* @brief 使用四元数将设备坐标系的线性加速度转换到世界坐标系
* @details 同样,此方法比使用欧拉角更优。
* @param acc_linear_device 输入:设备坐标系下的线性加速度 [x, y, z]
* @param q 输入:表示姿态的四元数 [w, x, y, z]
* @param acc_linear_world 输出:世界坐标系下的线性加速度 [x, y, z]
*/
void q_transform_to_world_with_quaternion(const float *acc_linear_device, const float *q, float *acc_linear_world)
{
// 这是 R_device_to_world * acc_linear_device 的展开形式
acc_linear_world[0] = (1.0f - 2.0f*q[2]*q[2] - 2.0f*q[3]*q[3]) * acc_linear_device[0] +
(2.0f*q[1]*q[2] - 2.0f*q[0]*q[3]) * acc_linear_device[1] +
(2.0f*q[1]*q[3] + 2.0f*q[0]*q[2]) * acc_linear_device[2];
acc_linear_world[1] = (2.0f*q[1]*q[2] + 2.0f*q[0]*q[3]) * acc_linear_device[0] +
(1.0f - 2.0f*q[1]*q[1] - 2.0f*q[3]*q[3]) * acc_linear_device[1] +
(2.0f*q[2]*q[3] - 2.0f*q[0]*q[1]) * acc_linear_device[2];
acc_linear_world[2] = (2.0f*q[1]*q[3] - 2.0f*q[0]*q[2]) * acc_linear_device[0] +
(2.0f*q[2]*q[3] + 2.0f*q[0]*q[1]) * acc_linear_device[1] +
(1.0f - 2.0f*q[1]*q[1] - 2.0f*q[2]*q[2]) * acc_linear_device[2];
// acc_linear_world[2] -= G_ACCELERATION;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*
* @param buffer 传进来的三轴数据:陀螺仪/加速度
* @return float 返回方差和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 摩擦力模拟,进行速度衰减
*
* @param tracker
*/
void forece_of_friction(skiing_tracker_t *tracker){
// 增加速度衰减,模拟摩擦力
tracker->velocity[0] *= SPEED_ATTENUATION;
tracker->velocity[1] *= SPEED_ATTENUATION;
tracker->velocity[2] = 0; // 垂直速度强制归零
}
/**
* @brief 状态机更新
*
* @param tracker 传入同步修改后传出
* @param acc_device_ms2 三轴加速度m/s^2
* @param gyr_dps 三轴陀螺仪dps
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]); //dps
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]); //m/s^s
float acc_magnitude_g = acc_magnitude / G_ACCELERATION; // 转换为g单位用于跳跃判断
#ifdef XTELL_TEST
debug1.acc_variance =acc_variance;
debug1.gyr_variance =gyr_variance;
debug1.gyr_magnitude=gyr_magnitude;
debug1.acc_magnitude=fabsf(acc_magnitude - G_ACCELERATION);
#endif
// --- 状态机逻辑 (核心修改区域) ---
#if 0 //暂时不考虑空中
// 1. 空中/落地状态的后续处理
if (tracker->state == IN_AIR) {
// A. 检测巨大冲击 -> 落地
if (acc_magnitude_g > LANDING_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = LANDING;
// B. 检测超时 -> 强制落地 (安全机制)
} else if (tracker->time_in_air > MAX_TIME_IN_AIR) {
tracker->state = LANDING;
// C. 检测恢复正常重力 (平缓落地)
} else if (acc_magnitude_g > 0.8f && acc_magnitude_g < 1.5f) {
tracker->grounded_entry_counter++;
if (tracker->grounded_entry_counter >= GROUNDED_CONFIRM_COUNT) {
tracker->state = LANDING;
}
} else {
tracker->grounded_entry_counter = 0;
}
return; // 在空中或刚切换到落地,结束本次状态判断
}
// 2. 严格的 "起跳->空中" 状态转换逻辑
// 只有当处于滑行状态时,才去检测起跳意图
if (tracker->state == NO_CONSTANT_SPEED || tracker->state == CONSTANT_SPEED || tracker->state == WHEEL) {
if (acc_magnitude_g > TAKEOFF_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = TAKING_OFF;
tracker->airborne_entry_counter = 0; // 准备检测失重
return;
}
}
// 只有在TAKING_OFF状态下才去检测是否进入失重
if (tracker->state == TAKING_OFF) {
if (acc_magnitude_g < AIRBORNE_ACC_MAG_LOW_THRESHOLD) {
tracker->airborne_entry_counter++;
if (tracker->airborne_entry_counter >= AIRBORNE_CONFIRM_COUNT) {
memcpy(tracker->initial_velocity_on_takeoff, tracker->velocity, sizeof(tracker->velocity));
tracker->time_in_air = 0;
tracker->state = IN_AIR;
tracker->airborne_entry_counter = 0;
tracker->grounded_entry_counter = 0;
return;
}
} else {
// 如果在起跳冲击后一段时间内没有失重,说明只是一个颠簸,恢复滑行
// 可以加一个小的超时计数器,这里为了简单先直接恢复
tracker->state = NO_CONSTANT_SPEED;
}
return; // 无论是否切换,都结束本次判断
}
#endif
// --- 静止判断 ---
if (acc_variance < STOP_ACC_VARIANCE_THRESHOLD && gyr_variance < STOP_GYR_VARIANCE_THRESHOLD && gyr_magnitude < STOP_GYR_MAG_THRESHOLD) {
tracker->state = STATIC;
return;
}
// --- 地面状态切换逻辑 ---
switch (tracker->state) {
case LANDING:
tracker->state = STATIC;
break;
case STATIC:
// 优先判断是否进入 WOBBLE 状态
// 条件:陀螺仪活动剧烈,但整体加速度变化不大(说明是原地转或晃)
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) < WOBBLE_ACC_MAG_THRESHOLD) {
tracker->state = WOBBLE;
}
// 只有在陀螺仪和加速度都满足“前进”特征时,才启动
else if (gyr_variance > START_GYR_VARIANCE_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
break;
case WOBBLE:
// 从 WOBBLE 状态启动的条件应该和从 STATIC 启动一样严格
if (gyr_variance < START_GYR_VARIANCE_THRESHOLD * 2 && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
// 如果陀螺仪活动减弱,则可能恢复静止
else if (gyr_magnitude < ROTATION_GYR_MAG_THRESHOLD * 0.8f) { // 增加迟滞,避免抖动
// 不直接跳回STATIC而是依赖下一轮的全局静止判断
}
break;
case NO_CONSTANT_SPEED: //非匀速状态
//暂时不考虑摔倒
// if (gyr_magnitude > FALLEN_GRY_MAG_THRESHOLD) {
// tracker->state = FALLEN; //摔倒
// } else
if (gyr_magnitude > WHEEL_GYR_MAG_THRESHOLD && acc_variance > WHEEL_ACC_VARIANCE_THRESHOLD) {
tracker->state = WHEEL; //转弯
} else if (fabsf(acc_magnitude - G_ACCELERATION) < SKIING_ACC_MAG_THRESHOLD) {
tracker->state = CONSTANT_SPEED; //匀速
}
break;
case CONSTANT_SPEED: //匀速状态
if (fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
//TODO可以添加进入转弯或摔倒的判断
break;
case WHEEL:
// 从转弯状态,检查转弯是否结束
// 如果角速度和加速度方差都降下来了,就回到普通滑行状态
if (gyr_magnitude < WHEEL_GYR_MAG_THRESHOLD * 0.8f && acc_variance < WHEEL_ACC_VARIANCE_THRESHOLD * 0.8f) { // 乘以一个滞后系数避免抖动
tracker->state = NO_CONSTANT_SPEED;
}
break;
case FALLEN:
// TODO回到 STATIC
break;
}
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps
* @param angle 欧若拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
if(my_skiing_tracker.state == STOP_DETECTION)
return;
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 根据状态执行不同的计算逻辑 ---
switch (tracker->state) {
case TAKING_OFF:
tracker->speed = 0.0f;
break;
case IN_AIR:
// 在空中时,只累加滞空时间
tracker->time_in_air += dt;
break;
case LANDING:
// 刚落地,计算空中距离
calculate_air_distance(tracker);
// 清理速度和滤波器状态,为恢复地面追踪做准备
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
break;
case WHEEL:
case NO_CONSTANT_SPEED:
float linear_acc_device[3];
float linear_acc_world[3];
// 在设备坐标系下,移除重力,得到线性加速度
q_remove_gravity_with_quaternion(acc_device_ms2, quaternion_data, linear_acc_device);
// 将设备坐标系下的线性加速度,旋转到世界坐标系
q_transform_to_world_with_quaternion(linear_acc_device, quaternion_data, linear_acc_world);
// 将最终用于积分的加速度存入 tracker 结构体
memcpy(tracker->acc_no_g, linear_acc_world, sizeof(linear_acc_world));
float acc_world_temp[3]; // 临时变量存储当前周期的加速度
for (int i = 0; i < 2; i++) { // 只处理水平方向的 x 和 y 轴
// --- 核心修改:颠倒滤波器顺序为 HPF -> LPF ---
// 1. 高通滤波 (HPF) 先行: 消除因姿态误差导致的重力泄漏(直流偏置)
// HPF的瞬态响应会产生尖峰这是正常的。
tracker->acc_world_filtered[i] = HPF_ALPHA * (tracker->acc_world_filtered[i] + tracker->acc_no_g[i] - tracker->acc_world_unfiltered_prev[i]);
tracker->acc_world_unfiltered_prev[i] = tracker->acc_no_g[i];
// 2. 低通滤波 (LPF) 殿后: 平滑掉HPF产生的尖峰和传感器自身的高频振动噪声。
// 这里使用 tracker->acc_world_filtered[i] 作为LPF的输入。
tracker->acc_world_lpf[i] = (1.0f - LPF_ALPHA) * tracker->acc_world_filtered[i] + LPF_ALPHA * tracker->acc_world_lpf[i];
// 将最终处理完的加速度值存入临时变量
acc_world_temp[i] = tracker->acc_world_lpf[i];
}
// 计算处理后加速度的水平模长
float acc_horizontal_mag = sqrtf(acc_world_temp[0] * acc_world_temp[0] +
acc_world_temp[1] * acc_world_temp[1]);
#if XTELL_TEST
debug2.acc_magnitude = acc_horizontal_mag;
#endif
// 应用死区,并积分
if (acc_horizontal_mag > ACC_DEAD_ZONE_THRESHOLD) {
tracker->velocity[0] += acc_world_temp[0] * dt;
tracker->velocity[1] += acc_world_temp[1] * dt;
}
// 更新速度和距离
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
tracker->distance += tracker->speed * dt;
break;
case CONSTANT_SPEED:
//保持上次的速度不变。只更新距离
tracker->distance += tracker->speed * dt;
break;
case STATIC:
case WOBBLE:
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
#if XTELL_TEST
debug2.acc_magnitude = 0;
#endif
break;
case FALLEN:
// TODO
break;
default:
break;
}
#if 1
float linear_acc_device[3];
float linear_acc_world[3];
float tmp_world_acc[3];
// 在设备坐标系下,移除重力,得到线性加速度
q_remove_gravity_with_quaternion(acc_device_ms2, quaternion_data, linear_acc_device);
// 将设备坐标系下的线性加速度,旋转到世界坐标系
q_transform_to_world_with_quaternion(linear_acc_device, quaternion_data, tmp_world_acc);
float all_world_mag = sqrtf(tmp_world_acc[0] * tmp_world_acc[0] +
tmp_world_acc[1] * tmp_world_acc[1] +
tmp_world_acc[2] * tmp_world_acc[2]);
static int count = 0;
if(count > 100){
xlog("===original(g): x %.2f, y %.2f, z %.2f===\n",acc_g[0],acc_g[1],acc_g[2]);
xlog("===world(m/s^2) no g: x %.2f, y %.2f, z %.2f, all %.2f===\n",tmp_world_acc[0],tmp_world_acc[1],tmp_world_acc[2],all_world_mag); //去掉重力加速度
xlog("===gyr(dps) : x %.2f, y %.2f, z %.2f===\n",gyr_dps[0],gyr_dps[1],gyr_dps[2]); //angle
xlog("===angle : x %.2f, y %.2f, z %.2f,===\n",angle[0],angle[1],angle[2]);
count = 0;
}
count++;
#endif
}
/**
* @brief 滑雪数据计算
*
* @param acc_data_buf 传入的三轴加速度数据
* @param gyr_data_buf 传入的三轴陀螺仪数据
* @param angle_data 传入的欧若拉角数据
* @return BLE_send_data_t 要发送给蓝牙的数据
*/
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data, float* quaternion) {
static int initialized = 0;
static float acc_data_g[3];
static float gyr_data_dps[3];
if(quaternion != NULL){
memcpy(quaternion_data, quaternion, 4 * sizeof(float));
}
// const float delta_time = DELTA_TIME+0.01f;
// const float delta_time = DELTA_TIME + 0.005f;
const float delta_time = DELTA_TIME;
BLE_send_data_t BLE_send_data;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
#if ACC_RANGE==2
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 16384.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 16384.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 16384.0f;
#endif
#if ACC_RANGE==4
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 8192.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 8192.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 8192.0f;
#endif
#if ACC_RANGE==8
//±8g 4096
acc_data_g[0] = (float)acc_data_buf[0] / 4096.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 4096.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 4096.0f; //az
#endif
#if ACC_RANGE==16
//±16g 2048
acc_data_g[0] = (float)acc_data_buf[0] / 2048.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 2048.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 2048.0f; //az
#endif
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
gyr_data_dps[0] = (float)gyr_data_buf[0] * 0.061f;
gyr_data_dps[1] = (float)gyr_data_buf[1] * 0.061f;
gyr_data_dps[2] = (float)gyr_data_buf[2] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, acc_data_g, gyr_data_dps, angle_data, delta_time);
BLE_send_data.skiing_state = my_skiing_tracker.state;
for (int i = 0; i < 3; i++) {
#ifdef XTELL_TEST
BLE_send_data.acc_data[i] = (short)(acc_data_g[i] * 9.8f) * 100; //cm/^s2
BLE_send_data.gyr_data[i] = (short)gyr_data_dps[i]; //dps
BLE_send_data.angle_data[i] = angle_data[i];
#else
BLE_send_data.acc_data[i] = (short)acc_data_buf[i]; //原始adc数据
BLE_send_data.gyr_data[i] = (short)gyr_data_buf[i]; //原始adc数据
BLE_send_data.angle_data[i] = angle_data[i];
#endif
}
BLE_send_data.speed_cms = (int)(my_skiing_tracker.speed * 100);
BLE_send_data.distance_cm = (int)(my_skiing_tracker.distance * 100);
// printf("Calculate the time interval =============== end\n");
return BLE_send_data;
}

View File

@ -0,0 +1,88 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
// 定义滑雪者可能的状态
typedef enum {
STATIC, // 静止或动态稳定0
NO_CONSTANT_SPEED, // 正在滑雪非匀速1
CONSTANT_SPEED, // 正在滑雪匀速2
WOBBLE, // 正在原地旋转3
WHEEL, // 转弯4
FALLEN, // 已摔倒5
TAKING_OFF, // 起跳冲击阶段6
IN_AIR, // 空中失重阶段7
LANDING, // 落地冲击阶段8
STOP_DETECTION, // 停止检测9
UNKNOWN // 未知状态10
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 5 // 用于计算方差的数据窗口大小 (5个样本 @ 100Hz = 50ms),减小延迟,提高实时性
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_no_g[3]; // 去掉重力分量后的加速度
// 用于空中距离计算
float time_in_air; // 滞空时间计时器
float initial_velocity_on_takeoff[3]; // 起跳瞬间的速度向量
int airborne_entry_counter; // 进入空中状态的确认计数器
int grounded_entry_counter; // 落地确认计数器
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3]; //过滤过的
float acc_world_unfiltered_prev[3]; //上一次没过滤的
float acc_world_lpf[3]; // 经过低通滤波后的世界坐标系加速度
} skiing_tracker_t;
//ble发送的数据
typedef struct{ //__attribute__((packed)){ //该结构体取消内存对齐
char sensor_state;
char skiing_state;
int speed_cms; //求出的速度cm/s
int distance_cm; //求出的距离cm
short acc_data[3]; //三轴加速度, g
short gyr_data[3]; //三轴陀螺仪, dps
float angle_data[3]; //欧若拉角
}BLE_send_data_t;
typedef struct{
int acc_KS[3]; //卡尔曼后LSB转换后的 三轴加速度数据cm/s^2
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
#ifdef XTELL_TEST
typedef struct{
float acc_variance; //三轴加速度方差之和
float gyr_variance; //三轴陀螺仪方差之和
float acc_magnitude; //三轴加速度模长
float gyr_magnitude; //三轴陀螺仪模长
}debug_t;
#endif
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
BLE_send_data_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data, float* quaternion);
#endif // SKIING_TRACKER_H

View File

@ -0,0 +1,95 @@
# 时间间隔 -- 软件模拟iic的情况下
目前测试代码如下:
```c
create_process(&test_id, "test",NULL, test, (int)(DELTA_TIME*1000));
```
对于test函数的调用时间设置的是10ms调用一次test代码如下
```c
void test(){
signed short acc_data_buf[3] = {0};
signed short gyr_data_buf[3] = {0};
signed short acc_gyro_input[6] = {0};
float Angle_output[3] = {0};
SL_SC7U22_RawData_Read(acc_data_buf,gyr_data_buf);
BLE_send_data = sensor_processing_task(acc_data_buf, gyr_data_buf);
//----省略-----
//一些ble数据发送
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
memset(&data, 0, 50);
// xlog("end============\n");
}
```
sensor_processing_task当中就进行了计算包括卡尔曼等在timer设置成10ms的情况下实际上test函数或者是sensor_processing_task函数距离上次调用到本次调用实际间隔为40ms
计算距离不能直接采用timers设置的时间间隔作为dt来求距离实际每次计算求速度的时间应该是40ms
# 11.13
代码主要文件夹apps\earphone\xtell_Sensor
- apps\earphone\xtell_Sensor\send_data.c xtell_task_create 函数,传感器计算程序逻辑开始位置,包括传感器读取数据的任务、蓝牙发送任务、速度距离计算任务
- apps\earphone\xtell_Sensor\calculate目前只有计算传感器去除重力分量的代码
- 问题:存在漂移
- 水平的情况下三轴去掉重力分量,计算出来的结果, 误差有0.0x m/s^s
- original(g): x -0.02, y 0.00, z 1.00
device(m/s^2) no g: x -0.08, y -0.01, z -0.04, all 0.08
- 在板子任意倾斜角度下去掉各轴重力分量的情况下误差有0.3x m/s^s
- ===original(g): x -0.20, y -0.85, z 0.41===
- ===device(m/s^2) no g: x 0.06, y 0.31, z -0.10, all 0.33===
- apps\earphone\xtell_Sensor\sensor传感器驱动参与编译的为SC7U22.c和SC7U22.h
- apps\earphone\xtell_Sensor\A_hide速度和距离计算代码
- 最新一版为apps\earphone\xtell_Sensor\A_hide\10\水平距离测速2、3m误差
- 要使用只需要把代码复制粘贴到calculate文件夹下
# 11.18
去除重力分量后仍有误差:
- 数据对齐?
- 有没有丢数据?
- 重复定位的数据?
- 静态时的角度误差?
定时器1的回调函数10ms调用一次**A**读取传感器数据放进buff
定时器2的回调函数5ms调用一次**B**读取buff的传感器数据去除重力分离的计算
- **数据没有对齐**A 的回调调用计数 > B 的回调调用计数
- **丢数据了**A 读取传感器数据的回调函数中打印了buff已满的log
- **重复定位**:移动后回到原先的位置,前后的计算得到的三轴角度相同
- **静态时的角度误差**1°左右
- 定时器2不进行重力分离计算只进行计数也仍然有数据没有对齐和丢数据的情况
将读取传感器数据、去除重力分量计算放到同一个任务下,同步进行
- 数据没有丢失,数据也对齐了
- 在小倾斜的坡面下,去除重力分量后的总的加速度,**小于0.1m/s^2**
- 在大倾斜的坡面下如旋转超过70°去除重力分量后的总的加速度在**0.4m/s^2上下**
- 貌似是角度越大,越接近方向锁,导致角度更容易漂移造错数据错误
采用四元数的方式做去除重力分量的计算:
- 将读取传感器数据、去除重力分量计算放到同一个任务下
- 在小倾斜的坡面下,去除重力分量后的总的加速度,低于**0.04m/s^2**
- 在大倾斜的坡面下如旋转超过70°去除重力分量后的总的加速度在**0.1m/s^2上下**
- 大倾斜角度的误差要靠磁力计来消除yaw无法通过加速度计来消除偏差

View File

@ -0,0 +1,166 @@
/*
发送数据给上位机的,需要将log打印出口关闭
*/
#include "ano_protocol.h"
#include "asm/uart_dev.h"
#include "app_config.h"
// 定义协议常量
#define ANO_FRAME_HEADER 0xAA
#define ANO_TO_COMPUTER_ADDR 0xFF
// 用于保存 uart_dev_open 返回的句柄
static const uart_bus_t *ano_uart_dev = NULL;
/**
* @brief 计算并填充协议的校验和
* @param frame_buffer 指向数据帧缓冲区的指针
*/
static void ano_calculate_checksum(u8 *frame_buffer) {
#if TCFG_UART0_ENABLE==0
u8 sum_check = 0;
u8 add_check = 0;
// 数据长度在索引为 3 的位置
u8 data_len = frame_buffer[3];
// 需要计算校验和的总长度 = 帧头(1) + 地址(1) + ID(1) + 长度(1) + 数据(data_len)
u16 checksum_len = 4 + data_len;
for (u16 i = 0; i < checksum_len; i++) {
sum_check += frame_buffer[i];
add_check += sum_check;
}
// 将计算出的校验和填充到帧的末尾
frame_buffer[checksum_len] = sum_check;
frame_buffer[checksum_len + 1] = add_check;
#endif
}
/**
* @brief 初始化用于上位机通信的串口
*/
int ano_protocol_init(u32 baudrate) {
#if TCFG_UART0_ENABLE==0
// 防止重复初始化
if (ano_uart_dev) {
return 0;
}
struct uart_platform_data_t ut_arg = {0};
// TCFG_ONLINE_TX_PORT 和 TCFG_ONLINE_RX_PORT 通常在 app_config.h 中定义
ut_arg.tx_pin = TCFG_ONLINE_TX_PORT;
ut_arg.rx_pin = (u8)-1; // -1 表示不使用该引脚,因为我们只发送数据
ut_arg.baud = baudrate;
// 以下为接收相关配置由于只发送全部设为0或NULL
ut_arg.rx_cbuf = NULL;
ut_arg.rx_cbuf_size = 0;
ut_arg.frame_length = 0;
ut_arg.rx_timeout = 0;
ut_arg.isr_cbfun = NULL;
ano_uart_dev = (uart_bus_t *)uart_dev_open(&ut_arg);
if (ano_uart_dev == NULL) {
return -1;
}
#endif
return 0;
}
/**
* @brief 发送惯性传感器数据 (ID: 0x01)
*/
void ano_send_inertial_data(s16 acc_x, s16 acc_y, s16 acc_z,
s16 gyr_x, s16 gyr_y, s16 gyr_z,
u8 shock_sta) {
#if TCFG_UART0_ENABLE==0
if (ano_uart_dev == NULL) {
return; // 如果串口未初始化,则不执行任何操作
}
// 帧总长度 = 4(固定头) + 13(数据) + 2(校验) = 19 字节
u8 frame_buffer[19];
u8 data_idx = 4; // DATA区域从索引4开始
// 1. 填充帧头、地址、ID、长度
frame_buffer[0] = ANO_FRAME_HEADER;
frame_buffer[1] = ANO_TO_COMPUTER_ADDR;
frame_buffer[2] = 0x01; // 功能码 ID
frame_buffer[3] = 13; // 数据长度 LEN
// 2. 填充数据内容 (DATA),注意小端模式 (低字节在前)
frame_buffer[data_idx++] = (u8)(acc_x & 0xFF);
frame_buffer[data_idx++] = (u8)(acc_x >> 8);
frame_buffer[data_idx++] = (u8)(acc_y & 0xFF);
frame_buffer[data_idx++] = (u8)(acc_y >> 8);
frame_buffer[data_idx++] = (u8)(acc_z & 0xFF);
frame_buffer[data_idx++] = (u8)(acc_z >> 8);
frame_buffer[data_idx++] = (u8)(gyr_x & 0xFF);
frame_buffer[data_idx++] = (u8)(gyr_x >> 8);
frame_buffer[data_idx++] = (u8)(gyr_y & 0xFF);
frame_buffer[data_idx++] = (u8)(gyr_y >> 8);
frame_buffer[data_idx++] = (u8)(gyr_z & 0xFF);
frame_buffer[data_idx++] = (u8)(gyr_z >> 8);
frame_buffer[data_idx++] = shock_sta;
// 3. 计算并填充校验和
ano_calculate_checksum(frame_buffer);
// 4. 通过串口发送整个数据帧
ano_uart_dev->write(frame_buffer, sizeof(frame_buffer));
#endif
}
/**
* @brief 发送飞控姿态数据 (ID: 0x03)
*
* @param rol
* @param pit
* @param yaw
* @param fusion_sta
*/
void ano_send_attitude_data(float rol, float pit, float yaw, u8 fusion_sta) {
#if TCFG_UART0_ENABLE==0
if (ano_uart_dev == NULL) {
return; // 如果串口未初始化,则不执行任何操作
}
// 帧总长度 = 4(固定头) + 7(数据) + 2(校验) = 13 字节
u8 frame_buffer[13];
u8 data_idx = 4; // DATA区域从索引4开始
// 1. 填充帧头、地址、ID、长度
frame_buffer[0] = ANO_FRAME_HEADER;
frame_buffer[1] = ANO_TO_COMPUTER_ADDR;
frame_buffer[2] = 0x03; // 功能码 ID
frame_buffer[3] = 7; // 数据长度 LEN
// 2. 转换浮点数为整数并填充 (DATA),注意小端模式
s16 rol_int = (s16)(rol * 100);
s16 pit_int = (s16)(pit * 100);
s16 yaw_int = (s16)(yaw * 100);
frame_buffer[data_idx++] = (u8)(rol_int & 0xFF);
frame_buffer[data_idx++] = (u8)(rol_int >> 8);
frame_buffer[data_idx++] = (u8)(pit_int & 0xFF);
frame_buffer[data_idx++] = (u8)(pit_int >> 8);
frame_buffer[data_idx++] = (u8)(yaw_int & 0xFF);
frame_buffer[data_idx++] = (u8)(yaw_int >> 8);
frame_buffer[data_idx++] = fusion_sta;
// 3. 计算并填充校验和
ano_calculate_checksum(frame_buffer);
// 4. 通过串口发送整个数据帧
ano_uart_dev->write(frame_buffer, sizeof(frame_buffer));
#endif
}

View File

@ -0,0 +1,37 @@
#ifndef __ANO_PROTOCOL_H__
#define __ANO_PROTOCOL_H__
#include "system/includes.h"
/**
* @brief 初始化用于上位机通信的串口
*
* @param baudrate 波特率,例如 115200
* @return 0: 成功, -1: 失败
*/
int ano_protocol_init(u32 baudrate);
/**
* @brief 发送惯性传感器数据 (ID: 0x01)
* @param acc_x X轴加速度
* @param acc_y Y轴加速度
* @param acc_z Z轴加速度
* @param gyr_x X轴陀螺仪
* @param gyr_y Y轴陀螺仪
* @param gyr_z Z轴陀螺仪
* @param shock_sta 震动状态
*/
void ano_send_inertial_data(s16 acc_x, s16 acc_y, s16 acc_z,
s16 gyr_x, s16 gyr_y, s16 gyr_z,
u8 shock_sta);
/**
* @brief 发送飞控姿态数据 (ID: 0x03)
* @param rol 横滚角 (单位: 度)
* @param pit 俯仰角 (单位: 度)
* @param yaw 航向角 (单位: 度)
* @param fusion_sta 融合状态
*/
void ano_send_attitude_data(float rol, float pit, float yaw, u8 fusion_sta);
#endif // __ANO_PROTOCOL_H__

View File

@ -1,117 +1,84 @@
#include "circle_buffer.h"
#include <string.h>
//////////////////////////////////////////////////////////////////////////////////////////////////
//START -- 宏定义
#define ENABLE_XLOG 1
#ifdef xlog
#undef xlog
#endif
#if ENABLE_XLOG
#define xlog(format, ...) printf("[%s] " format, __func__, ##__VA_ARGS__)
#else
#define xlog(format, ...) ((void)0)
#endif
//END -- 宏定义
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//START -- 变量定义
//END -- 变量定义
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//START -- 函数定义
//END -- 函数定义
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
// 初始化环形缓冲区
void circle_buffer_init(circle_buffer_t *cb, u8 *buffer, u16 capacity) {
cb->buffer = buffer;
void circle_buffer_init(circle_buffer_t *cb, void *buffer, u16 capacity, u16 element_size) {
cb->buffer = (u8 *)buffer;
cb->capacity = capacity;
cb->element_size = element_size;
cb->head = 0;
cb->tail = 0;
cb->size = 0;
os_mutex_create(&cb->mutex);
}
// 环形缓冲区写入数据
u16 circle_buffer_write(circle_buffer_t *cb, const u8 *data, u16 length) {
if (length > circle_buffer_get_free_space(cb)) {
// 如果剩余空间不足,则只写入能放下的部分
length = circle_buffer_get_free_space(cb);
}
if (length == 0) {
return 0;
}
// 检查是否需要回环
if (cb->head + length > cb->capacity) {
u16 part1_len = cb->capacity - cb->head;
u16 part2_len = length - part1_len;
memcpy(cb->buffer + cb->head, data, part1_len);
memcpy(cb->buffer, data + part1_len, part2_len);
cb->head = part2_len;
} else {
memcpy(cb->buffer + cb->head, data, length);
cb->head += length;
if (cb->head == cb->capacity) {
cb->head = 0;
}
}
cb->size += length;
return length;
// 销毁环形缓冲区
void circle_buffer_deinit(circle_buffer_t *cb) {
os_mutex_del(&cb->mutex, 0);
}
// 环形缓冲区读取数据
u16 circle_buffer_read(circle_buffer_t *cb, u8 *data, u16 length) {
if (length > cb->size) {
// 如果要读取的长度超过了已有的数据,则只读取已有的部分
length = cb->size;
// 环形缓冲区写入一个元素
bool circle_buffer_write(circle_buffer_t *cb, const void *element) {
os_mutex_pend(&cb->mutex, 0);
if (circle_buffer_is_full(cb)) {
os_mutex_post(&cb->mutex);
return false; // 缓冲区已满
}
if (length == 0) {
return 0;
}
u8 *dest = cb->buffer + (cb->head * cb->element_size);
memcpy(dest, element, cb->element_size);
// 检查是否需要回环
if (cb->tail + length > cb->capacity) {
u16 part1_len = cb->capacity - cb->tail;
u16 part2_len = length - part1_len;
memcpy(data, cb->buffer + cb->tail, part1_len);
memcpy(data + part1_len, cb->buffer, part2_len);
cb->tail = part2_len;
} else {
memcpy(data, cb->buffer + cb->tail, length);
cb->tail += length;
if (cb->tail == cb->capacity) {
cb->tail = 0;
}
}
cb->size -= length;
return length;
cb->head = (cb->head + 1) % cb->capacity;
cb->size++;
os_mutex_post(&cb->mutex);
return true;
}
// 获取已用空间的大小
// 从环形缓冲区读取一个元素
bool circle_buffer_read(circle_buffer_t *cb, void *element) {
os_mutex_pend(&cb->mutex, 0);
if (circle_buffer_is_empty(cb)) {
os_mutex_post(&cb->mutex);
return false; // 缓冲区为空
}
u8 *src = cb->buffer + (cb->tail * cb->element_size);
memcpy(element, src, cb->element_size);
cb->tail = (cb->tail + 1) % cb->capacity;
cb->size--;
os_mutex_post(&cb->mutex);
return true;
}
// 获取已用空间的大小(以元素为单位)
u16 circle_buffer_get_size(circle_buffer_t *cb) {
return cb->size;
os_mutex_pend(&cb->mutex, 0);
u16 size = cb->size;
os_mutex_post(&cb->mutex);
return size;
}
// 获取剩余空间的大小
// 获取剩余空间的大小(以元素为单位)
u16 circle_buffer_get_free_space(circle_buffer_t *cb) {
return cb->capacity - cb->size;
os_mutex_pend(&cb->mutex, 0);
u16 free_space = cb->capacity - cb->size;
os_mutex_post(&cb->mutex);
return free_space;
}
// 检查缓冲区是否已满
bool circle_buffer_is_full(circle_buffer_t *cb) {
os_mutex_pend(&cb->mutex, 0);
bool is_full = (cb->size == cb->capacity);
os_mutex_post(&cb->mutex);
return is_full;
}
// 检查缓冲区是否为空
bool circle_buffer_is_empty(circle_buffer_t *cb) {
os_mutex_pend(&cb->mutex, 0);
bool is_empty = (cb->size == 0);
os_mutex_post(&cb->mutex);
return is_empty;
}

View File

@ -2,54 +2,77 @@
#define CIRCLE_BUFFER_H
#include "system/includes.h"
#include "os/os_api.h"
// 定义环形缓冲区的结构体
typedef struct {
u8 *buffer; // 缓冲区指针
u16 capacity; // 缓冲区总容量
u16 head; // 头部指针(写入位置
u16 tail; // 部指针(读取位置
u16 size; // 当前已用大小
u16 capacity; // 缓冲区总容量(以元素为单位)
u16 element_size; // 每个元素的大小(以字节为单位
u16 head; // 部指针(写入位置,以元素为单位
u16 tail; // 尾部指针(读取位置,以元素为单位)
u16 size; // 当前已用大小(以元素为单位)
OS_MUTEX mutex; // 用于保护缓冲区的互斥锁
} circle_buffer_t;
/**
* @brief 初始化环形缓冲区
* @param cb 指向环形缓冲区结构体的指针
* @param buffer 外部提供的缓冲区内存
* @param capacity 缓冲区的总容量
* @param capacity 缓冲区的总容量(以元素数量为单位)
* @param element_size 每个元素的大小(字节)
*/
void circle_buffer_init(circle_buffer_t *cb, u8 *buffer, u16 capacity);
void circle_buffer_init(circle_buffer_t *cb, void *buffer, u16 capacity, u16 element_size);
/**
* @brief 环形缓冲区写入数据
* @brief 销毁环形缓冲区,释放相关资源
* @param cb 指向环形缓冲区结构体的指针
* @param data 要写入的数据的指针
* @param length 要写入的数据的长度
* @return 实际写入的字节数
*/
u16 circle_buffer_write(circle_buffer_t *cb, const u8 *data, u16 length);
void circle_buffer_deinit(circle_buffer_t *cb);
/**
* @brief 环形缓冲区读取数据
* @brief 环形缓冲区写入一个元素
* @param cb 指向环形缓冲区结构体的指针
* @param data 用于存放读取数据的缓冲区的指针
* @param length 想要读取的数据的长度
* @return 实际读取的字节数
* @param element 要写入的元素的指针
* @return 成功返回true失败返回false
*/
u16 circle_buffer_read(circle_buffer_t *cb, u8 *data, u16 length);
bool circle_buffer_write(circle_buffer_t *cb, const void *element);
/**
* @brief 获取环形缓冲区中已用空间的大小
* @brief 环形缓冲区读取一个元素
* @param cb 指向环形缓冲区结构体的指针
* @return 已用空间的大小
* @param element 用于存放读取元素的缓冲区的指针
* @return 成功返回true失败返回false
*/
bool circle_buffer_read(circle_buffer_t *cb, void *element);
/**
* @brief 获取环形缓冲区中已用空间的大小(以元素为单位)
* @param cb 指向环形缓冲区结构体的指针
* @return 已用空间的大小(元素数量)
*/
u16 circle_buffer_get_size(circle_buffer_t *cb);
/**
* @brief 获取环形缓冲区中剩余空间的大小
* @brief 获取环形缓冲区中剩余空间的大小(以元素为单位)
* @param cb 指向环形缓冲区结构体的指针
* @return 剩余空间的大小
* @return 剩余空间的大小(元素数量)
*/
u16 circle_buffer_get_free_space(circle_buffer_t *cb);
/**
* @brief 检查缓冲区是否已满
* @param cb 指向环形缓冲区结构体的指针
* @return 如果已满返回true否则返回false
*/
bool circle_buffer_is_full(circle_buffer_t *cb);
/**
* @brief 检查缓冲区是否为空
* @param cb 指向环形缓冲区结构体的指针
* @return 如果为空返回true否则返回false
*/
bool circle_buffer_is_empty(circle_buffer_t *cb);
#endif // CIRCLE_BUFFER_H

View File

@ -0,0 +1,635 @@
/*
*/
#include "skiing_tracker.h"
#include "../sensor/SC7U22.h"
#define G_ACCELERATION 9.81f
#define DEG_TO_RAD (3.14159265f / 180.0f)
#define ENABLE_XLOG 1
#ifdef xlog
#undef xlog
#endif
#if ENABLE_XLOG
#define xlog(format, ...) printf("[XT:%s] " format, __func__, ##__VA_ARGS__)
#else
#define xlog(format, ...) ((void)0)
#endif
// --- 静止检测 ---
//两个判断是否静止的必要条件:动态零速更新(ZUPT)阈值
// 加速方差阈值,提高阈值,让“刹车”更灵敏,以便在波浪式前进等慢速漂移时也能触发零速更新
#define STOP_ACC_VARIANCE_THRESHOLD 0.2f
// 陀螺仪方差阈值
#define STOP_GYR_VARIANCE_THRESHOLD 5.0f
// 静止时候的陀螺仪模长
#define STOP_GYR_MAG_THRESHOLD 15
// --- --- ---
// --- 启动滑雪阈值 ---
// 加速度模长与重力的差值大于此值,认为开始运动;降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
#define START_ACC_MAG_THRESHOLD 1.0f //0.5、1
// 陀螺仪方差阈值,以允许启动瞬间的正常抖动,但仍能过滤掉混乱的、非滑雪的晃动。
#define START_GYR_VARIANCE_THRESHOLD 15.0f
// --- --- ---
// --- 滑雪过程 ---
//加速度 模长(不含重力),低于此值视为 在做匀速运动
#define SKIING_ACC_MAG_THRESHOLD 0.5f
//陀螺仪 模长,高于此值视为 摔倒了
#define FALLEN_GRY_MAG_THRESHOLD 2000.0f //未确定
// --- --- ---
// --- 原地旋转抖动 ---
// 加速度 方差 阈值。此值比 静止检测 阈值更宽松,
#define WOBBLE_ACC_VARIANCE_THRESHOLD 0.5f
// 加速度 模长 阈值
#define WOBBLE_ACC_MAG_THRESHOLD 1.0f
// 角速度 总模长 大于此值(度/秒),认为正在进行非滑雪的旋转或摆动
#define ROTATION_GYR_MAG_THRESHOLD 30.0f
// --- --- ---
// --- 滑雪转弯动 ---
// 加速度 方差 阈值,大于此值,滑雪过程可能发生了急转弯
#define WHEEL_ACC_VARIANCE_THRESHOLD 7.0f
// 角速度 总模长 大于此值(度/秒),认为滑雪过程中进行急转弯
#define WHEEL_GYR_MAG_THRESHOLD 500.0f //
// --- --- ---
// --- 跳跃 ---
// 加速度模长低于此值(g),认为进入失重状态(IN_AIR)
#define AIRBORNE_ACC_MAG_LOW_THRESHOLD 0.4f
// 加速度模长高于此值(g),认为发生落地冲击(LANDING)
#define LANDING_ACC_MAG_HIGH_THRESHOLD 3.5f
// 起跳加速度阈值(g)用于进入TAKING_OFF状态
#define TAKEOFF_ACC_MAG_HIGH_THRESHOLD 1.8f
// 进入空中状态确认计数需要连续3个采样点加速度低于阈值才判断为起跳
#define AIRBORNE_CONFIRM_COUNT 3
// 落地状态确认计数加速度恢复到1g附近并持续2个采样点(20ms)则认为已落地
#define GROUNDED_CONFIRM_COUNT 2
// 最大滞空时间(秒),超过此时间强制认为已落地,防止状态锁死
#define MAX_TIME_IN_AIR 12.5f
// --- --- ---
// --- 用于消除积分漂移的滤波器和阈值 ---
// 高通滤波器系数 (alpha)。alpha 越接近1滤除低频(直流偏移)的效果越强,但可能滤掉真实的慢速运动。
// alpha = RC / (RC + dt)参考RC电路而来fc ≈ (1 - alpha) / (2 * π * dt)
#define HPF_ALPHA 0.999f
//0.995f 0.08 Hz 的信号
//0.999f 0.0159 Hz
// --- --- ---
// --- 低通滤波器 ---
// 低通滤波器系数 (alpha)。alpha 越小,滤波效果越强(更平滑),但延迟越大。
// alpha 推荐范围 0.7 ~ 0.95。可以从 0.85 开始尝试。
#define LPF_ALPHA 0.7f
// 加速度死区阈值 (m/s^2)。低于此阈值的加速度被认为是噪声,不参与积分。
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
//参考0.2f ~ 0.4f
#define ACC_DEAD_ZONE_THRESHOLD 0.05f
// --- 模拟摩擦力,进行速度衰减 ---
#define SPEED_ATTENUATION 1.0f //暂不模拟
BLE_KS_send_data_t KS_data;
static float quaternion_data[4];
#ifdef XTELL_TEST
debug_t debug1;
debug_t debug2;
#endif
static skiing_tracker_t my_skiing_tracker;
//////////////////////////////////////////////////////////////////////////////////////////////////
//实现
void clear_speed(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
void start_detection(void){
my_skiing_tracker.state = STATIC;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.distance = 0;
my_skiing_tracker.speed = 0;
}
void stop_detection(void){
my_skiing_tracker.state = STOP_DETECTION;
memset(my_skiing_tracker.velocity, 0, sizeof(my_skiing_tracker.velocity));
my_skiing_tracker.speed = 0;
}
/**
* @brief 初始化滑雪追踪器
*
* @param tracker
*/
void skiing_tracker_init(skiing_tracker_t *tracker)
{
if (!tracker) {
return;
}
// 使用memset一次性清零整个结构体包括新增的缓冲区
memset(tracker, 0, sizeof(skiing_tracker_t));
tracker->state = STATIC;
}
/**
* @brief 当检测到落地时,计算空中的水平飞行距离并累加到总距离
*/
static void calculate_air_distance(skiing_tracker_t *tracker) {
float horizontal_speed_on_takeoff = sqrtf(
tracker->initial_velocity_on_takeoff[0] * tracker->initial_velocity_on_takeoff[0] +
tracker->initial_velocity_on_takeoff[1] * tracker->initial_velocity_on_takeoff[1]
);
float distance_in_air = horizontal_speed_on_takeoff * tracker->time_in_air;
tracker->distance += distance_in_air;
}
/**
* @brief 使用四元数直接从设备坐标系的加速度中移除重力分量
* @details 这种方法比使用欧拉角更精确、更稳定,且避免了万向节死锁。
* @param acc_device 输入:设备坐标系下的原始加速度 [x, y, z], 单位 m/s^2
* @param q 输入:表示姿态的四元数 [w, x, y, z]
* @param acc_linear_device 输出:设备坐标系下移除重力后的线性加速度 [x, y, z]
*/
void q_remove_gravity_with_quaternion(const float *acc_device, const float *q, float *acc_linear_device)
{
// 从四元数计算重力在设备坐标系下的投影
// G_device = R_transpose * G_world
// G_world = [0, 0, g]
// R_transpose 的第三列即为重力投影方向
float gx = 2.0f * (q[1] * q[3] - q[0] * q[2]);
float gy = 2.0f * (q[0] * q[1] + q[2] * q[3]);
float gz = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
// 从原始加速度中减去重力分量
acc_linear_device[0] = acc_device[0] - gx * G_ACCELERATION;
acc_linear_device[1] = acc_device[1] - gy * G_ACCELERATION;
acc_linear_device[2] = acc_device[2] - gz * G_ACCELERATION;
}
/**
* @brief 使用四元数将设备坐标系的线性加速度转换到世界坐标系
* @details 同样,此方法比使用欧拉角更优。
* @param acc_linear_device 输入:设备坐标系下的线性加速度 [x, y, z]
* @param q 输入:表示姿态的四元数 [w, x, y, z]
* @param acc_linear_world 输出:世界坐标系下的线性加速度 [x, y, z]
*/
void q_transform_to_world_with_quaternion(const float *acc_linear_device, const float *q, float *acc_linear_world)
{
// 这是 R_device_to_world * acc_linear_device 的展开形式
acc_linear_world[0] = (1.0f - 2.0f*q[2]*q[2] - 2.0f*q[3]*q[3]) * acc_linear_device[0] +
(2.0f*q[1]*q[2] - 2.0f*q[0]*q[3]) * acc_linear_device[1] +
(2.0f*q[1]*q[3] + 2.0f*q[0]*q[2]) * acc_linear_device[2];
acc_linear_world[1] = (2.0f*q[1]*q[2] + 2.0f*q[0]*q[3]) * acc_linear_device[0] +
(1.0f - 2.0f*q[1]*q[1] - 2.0f*q[3]*q[3]) * acc_linear_device[1] +
(2.0f*q[2]*q[3] - 2.0f*q[0]*q[1]) * acc_linear_device[2];
acc_linear_world[2] = (2.0f*q[1]*q[3] - 2.0f*q[0]*q[2]) * acc_linear_device[0] +
(2.0f*q[2]*q[3] + 2.0f*q[0]*q[1]) * acc_linear_device[1] +
(1.0f - 2.0f*q[1]*q[1] - 2.0f*q[2]*q[2]) * acc_linear_device[2];
// acc_linear_world[2] -= G_ACCELERATION;
}
/**
* @brief 计算缓冲区内三轴数据的方差之和
*
* @param buffer 传进来的三轴数据:陀螺仪/加速度
* @return float 返回方差和
*/
static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
{
float mean[3] = {0};
float variance[3] = {0};
// 计算均值
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
mean[0] += buffer[i][0];
mean[1] += buffer[i][1];
mean[2] += buffer[i][2];
}
mean[0] /= VARIANCE_BUFFER_SIZE;
mean[1] /= VARIANCE_BUFFER_SIZE;
mean[2] /= VARIANCE_BUFFER_SIZE;
// 计算方差
for (int i = 0; i < VARIANCE_BUFFER_SIZE; i++) {
variance[0] += (buffer[i][0] - mean[0]) * (buffer[i][0] - mean[0]);
variance[1] += (buffer[i][1] - mean[1]) * (buffer[i][1] - mean[1]);
variance[2] += (buffer[i][2] - mean[2]) * (buffer[i][2] - mean[2]);
}
variance[0] /= VARIANCE_BUFFER_SIZE;
variance[1] /= VARIANCE_BUFFER_SIZE;
variance[2] /= VARIANCE_BUFFER_SIZE;
// 返回三轴方差之和,作为一个综合的稳定度指标
return variance[0] + variance[1] + variance[2];
}
/**
* @brief 摩擦力模拟,进行速度衰减
*
* @param tracker
*/
void forece_of_friction(skiing_tracker_t *tracker){
// 增加速度衰减,模拟摩擦力
tracker->velocity[0] *= SPEED_ATTENUATION;
tracker->velocity[1] *= SPEED_ATTENUATION;
tracker->velocity[2] = 0; // 垂直速度强制归零
}
/**
* @brief 状态机更新
*
* @param tracker 传入同步修改后传出
* @param acc_device_ms2 三轴加速度m/s^2
* @param gyr_dps 三轴陀螺仪dps
*/
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device_ms2, const float *gyr_dps)
{
// 缓冲区未填满时,不进行状态判断,默认为静止
if (!tracker->buffer_filled) {
tracker->state = STATIC;
return;
}
// --- 计算关键指标 ---
float acc_variance = calculate_variance(tracker->acc_buffer); // 计算加速度方差
float gyr_variance = calculate_variance(tracker->gyr_buffer); // 计算陀螺仪方差
float gyr_magnitude = sqrtf(gyr_dps[0]*gyr_dps[0] + gyr_dps[1]*gyr_dps[1] + gyr_dps[2]*gyr_dps[2]); //dps
float acc_magnitude = sqrtf(acc_device_ms2[0]*acc_device_ms2[0] + acc_device_ms2[1]*acc_device_ms2[1] + acc_device_ms2[2]*acc_device_ms2[2]); //m/s^s
float acc_magnitude_g = acc_magnitude / G_ACCELERATION; // 转换为g单位用于跳跃判断
#ifdef XTELL_TEST
debug1.acc_variance =acc_variance;
debug1.gyr_variance =gyr_variance;
debug1.gyr_magnitude=gyr_magnitude;
debug1.acc_magnitude=fabsf(acc_magnitude - G_ACCELERATION);
#endif
// --- 状态机逻辑 (核心修改区域) ---
#if 0 //暂时不考虑空中
// 1. 空中/落地状态的后续处理
if (tracker->state == IN_AIR) {
// A. 检测巨大冲击 -> 落地
if (acc_magnitude_g > LANDING_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = LANDING;
// B. 检测超时 -> 强制落地 (安全机制)
} else if (tracker->time_in_air > MAX_TIME_IN_AIR) {
tracker->state = LANDING;
// C. 检测恢复正常重力 (平缓落地)
} else if (acc_magnitude_g > 0.8f && acc_magnitude_g < 1.5f) {
tracker->grounded_entry_counter++;
if (tracker->grounded_entry_counter >= GROUNDED_CONFIRM_COUNT) {
tracker->state = LANDING;
}
} else {
tracker->grounded_entry_counter = 0;
}
return; // 在空中或刚切换到落地,结束本次状态判断
}
// 2. 严格的 "起跳->空中" 状态转换逻辑
// 只有当处于滑行状态时,才去检测起跳意图
if (tracker->state == NO_CONSTANT_SPEED || tracker->state == CONSTANT_SPEED || tracker->state == WHEEL) {
if (acc_magnitude_g > TAKEOFF_ACC_MAG_HIGH_THRESHOLD) {
tracker->state = TAKING_OFF;
tracker->airborne_entry_counter = 0; // 准备检测失重
return;
}
}
// 只有在TAKING_OFF状态下才去检测是否进入失重
if (tracker->state == TAKING_OFF) {
if (acc_magnitude_g < AIRBORNE_ACC_MAG_LOW_THRESHOLD) {
tracker->airborne_entry_counter++;
if (tracker->airborne_entry_counter >= AIRBORNE_CONFIRM_COUNT) {
memcpy(tracker->initial_velocity_on_takeoff, tracker->velocity, sizeof(tracker->velocity));
tracker->time_in_air = 0;
tracker->state = IN_AIR;
tracker->airborne_entry_counter = 0;
tracker->grounded_entry_counter = 0;
return;
}
} else {
// 如果在起跳冲击后一段时间内没有失重,说明只是一个颠簸,恢复滑行
// 可以加一个小的超时计数器,这里为了简单先直接恢复
tracker->state = NO_CONSTANT_SPEED;
}
return; // 无论是否切换,都结束本次判断
}
#endif
// --- 静止判断 ---
if (acc_variance < STOP_ACC_VARIANCE_THRESHOLD && gyr_variance < STOP_GYR_VARIANCE_THRESHOLD && gyr_magnitude < STOP_GYR_MAG_THRESHOLD) {
tracker->state = STATIC;
return;
}
// --- 地面状态切换逻辑 ---
switch (tracker->state) {
case LANDING:
tracker->state = STATIC;
break;
case STATIC:
// 优先判断是否进入 WOBBLE 状态
// 条件:陀螺仪活动剧烈,但整体加速度变化不大(说明是原地转或晃)
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) < WOBBLE_ACC_MAG_THRESHOLD) {
tracker->state = WOBBLE;
}
// 只有在陀螺仪和加速度都满足“前进”特征时,才启动
else if (gyr_variance > START_GYR_VARIANCE_THRESHOLD && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
break;
case WOBBLE:
// 从 WOBBLE 状态启动的条件应该和从 STATIC 启动一样严格
if (gyr_variance < START_GYR_VARIANCE_THRESHOLD * 2 && fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
// 如果陀螺仪活动减弱,则可能恢复静止
else if (gyr_magnitude < ROTATION_GYR_MAG_THRESHOLD * 0.8f) { // 增加迟滞,避免抖动
// 不直接跳回STATIC而是依赖下一轮的全局静止判断
}
break;
case NO_CONSTANT_SPEED: //非匀速状态
//暂时不考虑摔倒
// if (gyr_magnitude > FALLEN_GRY_MAG_THRESHOLD) {
// tracker->state = FALLEN; //摔倒
// } else
if (gyr_magnitude > WHEEL_GYR_MAG_THRESHOLD && acc_variance > WHEEL_ACC_VARIANCE_THRESHOLD) {
tracker->state = WHEEL; //转弯
} else if (fabsf(acc_magnitude - G_ACCELERATION) < SKIING_ACC_MAG_THRESHOLD) {
tracker->state = CONSTANT_SPEED; //匀速
}
break;
case CONSTANT_SPEED: //匀速状态
if (fabsf(acc_magnitude - G_ACCELERATION) > START_ACC_MAG_THRESHOLD) {
tracker->state = NO_CONSTANT_SPEED;
}
//TODO可以添加进入转弯或摔倒的判断
break;
case WHEEL:
// 从转弯状态,检查转弯是否结束
// 如果角速度和加速度方差都降下来了,就回到普通滑行状态
if (gyr_magnitude < WHEEL_GYR_MAG_THRESHOLD * 0.8f && acc_variance < WHEEL_ACC_VARIANCE_THRESHOLD * 0.8f) { // 乘以一个滞后系数避免抖动
tracker->state = NO_CONSTANT_SPEED;
}
break;
case FALLEN:
// TODO回到 STATIC
break;
}
}
/**
* @brief 主更新函数
*
* @param tracker
* @param acc_g 三轴加速度g
* @param gyr_dps 三轴陀螺仪dps
* @param angle 欧若拉角
* @param dt 采样时间间隔,会用来积分求速度
*/
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_dps, float *angle, float dt)
{
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
return;
}
if(my_skiing_tracker.state == STOP_DETECTION)
return;
// --- 数据预处理和缓冲 ---
float acc_device_ms2[3];
acc_device_ms2[0] = acc_g[0] * G_ACCELERATION;
acc_device_ms2[1] = acc_g[1] * G_ACCELERATION;
acc_device_ms2[2] = acc_g[2] * G_ACCELERATION;
// 将最新数据存入缓冲区
memcpy(tracker->acc_buffer[tracker->buffer_index], acc_device_ms2, sizeof(acc_device_ms2));
memcpy(tracker->gyr_buffer[tracker->buffer_index], gyr_dps, 3 * sizeof(float));
tracker->buffer_index++;
if (tracker->buffer_index >= VARIANCE_BUFFER_SIZE) {
tracker->buffer_index = 0;
tracker->buffer_filled = 1; // 标记缓冲区已满
}
// --- 更新状态机 ---
update_state_machine(tracker, acc_device_ms2, gyr_dps);
// --- 根据状态执行不同的计算逻辑 ---
switch (tracker->state) {
case TAKING_OFF:
tracker->speed = 0.0f;
break;
case IN_AIR:
// 在空中时,只累加滞空时间
tracker->time_in_air += dt;
break;
case LANDING:
// 刚落地,计算空中距离
calculate_air_distance(tracker);
// 清理速度和滤波器状态,为恢复地面追踪做准备
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
break;
case WHEEL:
case NO_CONSTANT_SPEED:
float linear_acc_device[3];
float linear_acc_world[3];
// 在设备坐标系下,移除重力,得到线性加速度
q_remove_gravity_with_quaternion(acc_device_ms2, quaternion_data, linear_acc_device);
// 将设备坐标系下的线性加速度,旋转到世界坐标系
q_transform_to_world_with_quaternion(linear_acc_device, quaternion_data, linear_acc_world);
// 将最终用于积分的加速度存入 tracker 结构体
memcpy(tracker->acc_no_g, linear_acc_world, sizeof(linear_acc_world));
float acc_world_temp[3]; // 临时变量存储当前周期的加速度
for (int i = 0; i < 2; i++) { // 只处理水平方向的 x 和 y 轴
// --- 核心修改:颠倒滤波器顺序为 HPF -> LPF ---
// 1. 高通滤波 (HPF) 先行: 消除因姿态误差导致的重力泄漏(直流偏置)
// HPF的瞬态响应会产生尖峰这是正常的。
tracker->acc_world_filtered[i] = HPF_ALPHA * (tracker->acc_world_filtered[i] + tracker->acc_no_g[i] - tracker->acc_world_unfiltered_prev[i]);
tracker->acc_world_unfiltered_prev[i] = tracker->acc_no_g[i];
// 2. 低通滤波 (LPF) 殿后: 平滑掉HPF产生的尖峰和传感器自身的高频振动噪声。
// 这里使用 tracker->acc_world_filtered[i] 作为LPF的输入。
tracker->acc_world_lpf[i] = (1.0f - LPF_ALPHA) * tracker->acc_world_filtered[i] + LPF_ALPHA * tracker->acc_world_lpf[i];
// 将最终处理完的加速度值存入临时变量
acc_world_temp[i] = tracker->acc_world_lpf[i];
}
// 计算处理后加速度的水平模长
float acc_horizontal_mag = sqrtf(acc_world_temp[0] * acc_world_temp[0] +
acc_world_temp[1] * acc_world_temp[1]);
#if XTELL_TEST
debug2.acc_magnitude = acc_horizontal_mag;
#endif
// 应用死区,并积分
if (acc_horizontal_mag > ACC_DEAD_ZONE_THRESHOLD) {
tracker->velocity[0] += acc_world_temp[0] * dt;
tracker->velocity[1] += acc_world_temp[1] * dt;
}
// 更新速度和距离
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
tracker->velocity[1] * tracker->velocity[1]);
tracker->distance += tracker->speed * dt;
break;
case CONSTANT_SPEED:
//保持上次的速度不变。只更新距离
tracker->distance += tracker->speed * dt;
break;
case STATIC:
case WOBBLE:
// 速度清零,抑制漂移
memset(tracker->velocity, 0, sizeof(tracker->velocity));
tracker->speed = 0.0f;
memset(tracker->acc_world_unfiltered_prev, 0, sizeof(tracker->acc_world_unfiltered_prev));
memset(tracker->acc_world_filtered, 0, sizeof(tracker->acc_world_filtered));
memset(tracker->acc_world_lpf, 0, sizeof(tracker->acc_world_lpf)); // 清理新增的LPF状态
#if XTELL_TEST
debug2.acc_magnitude = 0;
#endif
break;
case FALLEN:
// TODO
break;
default:
break;
}
#if 1
float linear_acc_device[3];
float linear_acc_world[3];
float tmp_world_acc[3];
// 在设备坐标系下,移除重力,得到线性加速度
q_remove_gravity_with_quaternion(acc_device_ms2, quaternion_data, linear_acc_device);
// 将设备坐标系下的线性加速度,旋转到世界坐标系
q_transform_to_world_with_quaternion(linear_acc_device, quaternion_data, tmp_world_acc);
float all_world_mag = sqrtf(tmp_world_acc[0] * tmp_world_acc[0] +
tmp_world_acc[1] * tmp_world_acc[1] +
tmp_world_acc[2] * tmp_world_acc[2]);
static int count = 0;
if(count > 100){
xlog("===original(g): x %.2f, y %.2f, z %.2f===\n",acc_g[0],acc_g[1],acc_g[2]);
xlog("===world(m/s^2) no g: x %.2f, y %.2f, z %.2f, all %.2f===\n",tmp_world_acc[0],tmp_world_acc[1],tmp_world_acc[2],all_world_mag); //去掉重力加速度
xlog("===gyr(dps) : x %.2f, y %.2f, z %.2f===\n",gyr_dps[0],gyr_dps[1],gyr_dps[2]); //angle
xlog("===angle : x %.2f, y %.2f, z %.2f,===\n",angle[0],angle[1],angle[2]);
xlog("===speed(cm/s): %d\n",(int)(tracker->speed*100) );
count = 0;
}
count++;
#endif
}
/**
* @brief 滑雪数据计算
*
* @param acc_data_buf 传入的三轴加速度数据
* @param gyr_data_buf 传入的三轴陀螺仪数据
* @param angle_data 传入的欧若拉角数据
* @return 速度cm/s
*/
uint16_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data, float* quaternion) {
static int initialized = 0;
static float acc_data_g[3];
static float gyr_data_dps[3];
if(quaternion != NULL){
memcpy(quaternion_data, quaternion, 4 * sizeof(float));
}
// const float delta_time = DELTA_TIME+0.01f;
// const float delta_time = DELTA_TIME + 0.005f;
const float delta_time = DELTA_TIME;
if (!initialized) {
skiing_tracker_init(&my_skiing_tracker);
initialized = 1;
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
}
#if ACC_RANGE==2
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 16384.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 16384.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 16384.0f;
#endif
#if ACC_RANGE==4
// 加速度 LSB to g
acc_data_g[0] = (float)acc_data_buf[0] / 8192.0f;
acc_data_g[1] = (float)acc_data_buf[1] / 8192.0f;
acc_data_g[2] = (float)acc_data_buf[2] / 8192.0f;
#endif
#if ACC_RANGE==8
//±8g 4096
acc_data_g[0] = (float)acc_data_buf[0] / 4096.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 4096.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 4096.0f; //az
#endif
#if ACC_RANGE==16
//±16g 2048
acc_data_g[0] = (float)acc_data_buf[0] / 2048.0f; //ax
acc_data_g[1] = (float)acc_data_buf[1] / 2048.0f; //ay
acc_data_g[2] = (float)acc_data_buf[2] / 2048.0f; //az
#endif
// 陀螺仪 LSB to dps (度/秒)
// ±2000dps量程下转换系数约为 0.061
gyr_data_dps[0] = (float)gyr_data_buf[0] * 0.061f;
gyr_data_dps[1] = (float)gyr_data_buf[1] * 0.061f;
gyr_data_dps[2] = (float)gyr_data_buf[2] * 0.061f;
skiing_tracker_update(&my_skiing_tracker, acc_data_g, gyr_data_dps, angle_data, delta_time);
return (uint16_t)(my_skiing_tracker.speed * 100);
}

View File

@ -0,0 +1,83 @@
#ifndef SKIING_TRACKER_H
#define SKIING_TRACKER_H
#include "../xtell.h"
#include <math.h>
#include <string.h>
// 定义滑雪者可能的状态
typedef enum {
STATIC, // 静止或动态稳定0
NO_CONSTANT_SPEED, // 正在滑雪非匀速1
CONSTANT_SPEED, // 正在滑雪匀速2
WOBBLE, // 正在原地旋转3
WHEEL, // 转弯4
FALLEN, // 已摔倒5
TAKING_OFF, // 起跳冲击阶段6
IN_AIR, // 空中失重阶段7
LANDING, // 落地冲击阶段8
STOP_DETECTION, // 停止检测9
UNKNOWN // 未知状态10
} skiing_state_t;
#define VARIANCE_BUFFER_SIZE 5 // 用于计算方差的数据窗口大小 (5个样本 @ 100Hz = 50ms),减小延迟,提高实时性
#define DELTA_TIME 0.01f
// 追踪器数据结构体
typedef struct {
// 公开数据
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
float distance; // 总滑行距离,单位: m
float speed; // 当前速率 (标量),单位: m/s
skiing_state_t state; // 当前滑雪状态
// 内部计算使用的私有成员
float acc_no_g[3]; // 去掉重力分量后的加速度
// 用于空中距离计算
float time_in_air; // 滞空时间计时器
float initial_velocity_on_takeoff[3]; // 起跳瞬间的速度向量
int airborne_entry_counter; // 进入空中状态的确认计数器
int grounded_entry_counter; // 落地确认计数器
// --- 内部计算使用的私有成员 ---
// 用于动态零速更新和旋转检测的缓冲区
float acc_buffer[VARIANCE_BUFFER_SIZE][3]; // 加速度数据窗口
float gyr_buffer[VARIANCE_BUFFER_SIZE][3]; // 角速度数据窗口
int buffer_index; // 缓冲区当前索引
int buffer_filled; // 缓冲区是否已填满的标志
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
float acc_world_filtered[3]; //过滤过的
float acc_world_unfiltered_prev[3]; //上一次没过滤的
float acc_world_lpf[3]; // 经过低通滤波后的世界坐标系加速度
} skiing_tracker_t;
typedef struct{
int acc_KS[3]; //卡尔曼后LSB转换后的 三轴加速度数据cm/s^2
int gyr_KS_dps[3]; //卡尔曼后LSB to dps 三轴陀螺仪数据
int angle_KS[3]; //卡尔曼后,计算得到的欧若拉角数据
}BLE_KS_send_data_t;
#ifdef XTELL_TEST
typedef struct{
float acc_variance; //三轴加速度方差之和
float gyr_variance; //三轴陀螺仪方差之和
float acc_magnitude; //三轴加速度模长
float gyr_magnitude; //三轴陀螺仪模长
}debug_t;
#endif
/**
* @brief 初始化滑雪追踪器
*
* @param tracker 指向 skiing_tracker_t 结构体的指针
*/
void skiing_tracker_init(skiing_tracker_t *tracker);
uint16_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data, float* quaternion);
#endif // SKIING_TRACKER_H

View File

@ -6,6 +6,8 @@
#include "tone_player.h"
#include "ui_manage.h"
#include "gpio.h"
#include <math.h>
#include <string.h>
#include "app_main.h"
#include "asm/charge.h"
#include "update.h"
@ -15,11 +17,19 @@
#include "bt_profile_cfg.h"
#include "dev_manager/dev_manager.h"
#include "update_loader_download.h"
#include "LIS2DH12.h"
#include "circle_buffer.h"
#include "circle_buffer.h"
#include "./sensor/SC7U22.h"
#include "./buffer/circle_buffer.h"
#include "btstack/avctp_user.h"
#include "calculate/skiing_tracker.h"
#include "xtell.h"
#include "./ano/ano_protocol.h"
#include "./sensor/MMC56.h"
#include "./sensor/BMP280.h"
#include "./sensor/AK8963.h"
#include "asm/rtc.h"
#include "system/timer.h"
#include "adv_time_stamp_setting.h"
#include "btstack/le/le_user.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
//宏定义
#define ENABLE_XLOG 1
@ -31,98 +41,481 @@
#else
#define xlog(format, ...) ((void)0)
#endif
#define SENSOR_DATA_BUFFER_SIZE 200 // 定义缓冲区可以存储XXX个sensor_data_t元素
#define MPU_FIFO_INTERVAL 4 //隔多久读取六轴fifo单位10ms
#define MPU_FIFO_LEN 16 //(MPU_FIFO_INTERVAL*10/2.5) //400hz采用频率那每40ms的时间fifo就有16组六轴数据
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//START -- 函数定义
void send_data_to_ble_client(const u8* data, u16 length);
extern void create_process(u16* pid, const char* name, void *priv, void (*func)(void *priv), u32 msec);
extern void close_process(u16* pid,char* name);
void BLE_send_fuc(void);
//END -- 函数定义
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//START -- 变量定义
static u32 timer_offset_ms = 0;
// --- 任务ID ---
static u16 xtell_i2c_test_id;
static u16 collect_data_id;
static u16 send_data_id;
typedef struct{
signed short SC7U22_data[6]; //12字节
int mmc5603nj_buffer[3]; //12字节
int16_t temperature; //2
uint32_t pressure; //4
}sensor_package_t __attribute__((packed));
typedef struct{
uint8_t checkout_1;
uint8_t checkout_2;
uint8_t foot; //1左脚2右脚
uint8_t package_index;
sensor_package_t sensor_package[MPU_FIFO_LEN];//一次蓝牙发送MPU_FIFO_LEN组传感器数据
}ble_send_data_t; //一次蓝牙发送的数据内容
// --- 环形缓冲区 ---
#define SENSOR_DATA_BUFFER_SIZE 512
static u8 sensor_data_buffer[SENSOR_DATA_BUFFER_SIZE];
static circle_buffer_t sensor_cb;
static circle_buffer_t g_ble_send_cb; // 环形缓冲区管理结构体
static ble_send_data_t g_sensor_data_storage[SENSOR_DATA_BUFFER_SIZE]; //缓冲区
extern u8 foot_init;
static OS_SEM receiver_ready_sem; // 用于启动同步的信号量
static const uart_bus_t *uart_bus = NULL;
static u16 test_id = 0;
//END -- 变量定义
//////////////////////////////////////////////////////////////////////////////////////////////////
// 采集传感器数据并存入环形缓冲区的任务
void collect_and_buffer_sensor_data_task(void) {
// 重置计时器
void reset_ms_timer(void) {
timer_offset_ms = sys_timer_get_ms();
xlog("Timer has been reset.\n");
}
motion_data_t current_motion;
axis_info_xtell total_accel;
axis_info_xtell linear_accel;
char data_string[256];
// 获取从上次重置后经过的毫秒数
u32 get_ms_timer(void) {
return sys_timer_get_ms() - timer_offset_ms;
}
// 从驱动获取最新的运动数据
get_motion_data(&current_motion);
total_accel = get_current_accel_mss();
linear_accel = get_linear_accel_mss();
// 将浮点数据转换为整数乘以100以保留两位小数进行格式化
int len = snprintf(data_string, sizeof(data_string),
"T:%d,%d,%d;L:%d,%d,%d;V:%d,%d,%d;D:%d,%d,%d\n",
(int)(total_accel.x * 100), (int)(total_accel.y * 100), (int)(total_accel.z * 100),
(int)(linear_accel.x * 100), (int)(linear_accel.y * 100), (int)(linear_accel.z * 100),
(int)(current_motion.velocity.x * 100), (int)(current_motion.velocity.y * 100), (int)(current_motion.velocity.z * 100),
(int)(current_motion.distance.x * 100), (int)(current_motion.distance.y * 100), (int)(current_motion.distance.z * 100));
/**
* @brief 传感器采集任务
*
*/
void sensor_collect_task(void){
static ble_send_data_t send_data;
mmc5603nj_mag_data_t mmc5603nj_buffer;
float temperature = 0;
float pressure = 0;
int interval = 0;
signed short accx_buf[100];
signed short accy_buf[100];
signed short accz_buf[100];
signed short gyrx_buf[100];
signed short gyry_buf[100];
signed short gyrz_buf[100];
int fifo_num = 0;
static int SL_data_index = 0;
u8 package_index = 1;
int tmp_index = 0;
// 写入环形缓冲区
u16 written = circle_buffer_write(&sensor_cb, (u8*)data_string, len);
if (written < len) {
xlog("The circular buffer is full!\n");
while(1){//4组地磁数据、16组六轴数据、1组气压计数据
interval++;
mmc5603nj_read_mag_data(&mmc5603nj_buffer);
for(int i = (interval-1)*4; i < interval*4; i++){
send_data.sensor_package[i].mmc5603nj_buffer[0] = (int32_t)(mmc5603nj_buffer.x * 1000.0f);
send_data.sensor_package[i].mmc5603nj_buffer[1] = (int32_t)(mmc5603nj_buffer.y * 1000.0f);
send_data.sensor_package[i].mmc5603nj_buffer[2] = (int32_t)(mmc5603nj_buffer.z * 1000.0f);
}
// xlog("MAG x: %.2f,y: %.2f,z: %.2f\n",mmc5603nj_buffer.x,mmc5603nj_buffer.y,mmc5603nj_buffer.z);
SL_SC7U22_FIFO_Read(accx_buf,accy_buf,accz_buf,gyrx_buf,gyry_buf,gyrz_buf); //一次性读取内置fifo的数据
for(int i = 0; i < MPU_FIFO_LEN/4; i++){
tmp_index = SL_data_index + i;
// if(tmp_index >= MPU_FIFO_LEN-1) tmp_index = MPU_FIFO_LEN-1;
send_data.sensor_package[tmp_index].SC7U22_data[0] = accx_buf[i]; //acc_x
send_data.sensor_package[tmp_index].SC7U22_data[1] = accy_buf[i]; //acc_y
send_data.sensor_package[tmp_index].SC7U22_data[2] = accz_buf[i]; //acc_z
send_data.sensor_package[tmp_index].SC7U22_data[3] = gyrx_buf[i]; //gyr_x
send_data.sensor_package[tmp_index].SC7U22_data[4] = gyry_buf[i]; //gyr_y
send_data.sensor_package[tmp_index].SC7U22_data[5] = gyrz_buf[i]; //gyr_z
// xlog(" Acc_x : %4d, Acc_y : %4d, Acc_z : %4d,\r\n", send_data.sensor_package[tmp_index].SC7U22_data[0], send_data.sensor_package[tmp_index].SC7U22_data[1], send_data.sensor_package[tmp_index].SC7U22_data[2]);
#if 0
float acc_g[3];
float gyr_dps[3];
acc_g[0] = (float)send_data.sensor_package[tmp_index].SC7U22_data[0] / 2048.0f;
acc_g[1] = (float)send_data.sensor_package[tmp_index].SC7U22_data[1] / 2048.0f;
acc_g[2] = (float)send_data.sensor_package[tmp_index].SC7U22_data[2] / 2048.0f;
gyr_dps[0] = (float)send_data.sensor_package[tmp_index].SC7U22_data[3] * 0.061f;
gyr_dps[1] = (float)send_data.sensor_package[tmp_index].SC7U22_data[4] * 0.061f;
gyr_dps[2] = (float)send_data.sensor_package[tmp_index].SC7U22_data[5] * 0.061f;
printf(" ACC(g): x=%.3f, y=%.3f, z=%.3f\n", acc_g[0], acc_g[1], acc_g[2]);
printf(" GYR(dps):x=%.3f, y=%.3f, z=%.3f\n", gyr_dps[0], gyr_dps[1], gyr_dps[2]);
#endif
}
SL_data_index += MPU_FIFO_LEN/4;
if(interval >= 4){
interval = 0;
SL_data_index = 0;
// bmp280_read_data(&temperature, &pressure);//每40ms读取一次
for(int i = 0;i<MPU_FIFO_LEN;i++){
send_data.sensor_package[i].temperature = (int16_t)(temperature * 1000.0f);
send_data.sensor_package[i].pressure = (int32_t)(pressure * 1000.0f);
}
// xlog("temperature: %.2f,pressure: %.2f\n",temperature,pressure);
// xlog("fifo_num:%d\n",fifo_num);
send_data.checkout_1 = 0xBE;
send_data.checkout_2 = 0xBB;
send_data.foot = foot_init;
send_data.package_index = package_index;
circle_buffer_write(&g_ble_send_cb, &send_data);
os_sem_post(&receiver_ready_sem); //通知另一个发送任务
memset(&send_data, 0, sizeof(ble_send_data_t));
memset(&accx_buf, 0, sizeof(accx_buf));
memset(&accy_buf, 0, sizeof(accy_buf));
memset(&accz_buf, 0, sizeof(accz_buf));
memset(&gyrx_buf, 0, sizeof(gyrx_buf));
memset(&gyry_buf, 0, sizeof(gyry_buf));
memset(&gyrz_buf, 0, sizeof(gyrz_buf));
package_index++;
if(package_index >= 0xFF) package_index = 1;
// xlog("=====================%d============================\n",get_ms_timer());
}
os_time_dly(1); //10ms为单位
}
}
// 定义数组大小
#define ARRAY_SIZE (178)
// 在 main 函数外部声明为全局变量,它将被存储在静态数据区
unsigned char global_data_array[ARRAY_SIZE];
// 从环形缓冲区读取数据并发送
void send_sensor_data_task(void) {
void data_log(uint8_t* data){
static u8 imu_airplane[MPU_FIFO_LEN][12];
// 检查数据包头部
if (data[0] != 0xBE || data[1] != 0xBB) {
printf("Error: Invalid data packet header.\n");
return;
}
// printf("xtell_ble_send\n");
send_data_to_ble_client(&global_data_array,ARRAY_SIZE);
//左右脚
uint8_t package_foot = data[2];
// 解析包索引
uint8_t package_index = data[3];
printf("--- Parsing Data Packet Index: %d ---\n", package_index);
uint8_t* p = &data[4]; // 指向数据负载的起始位置
// 循环解析16组数据
for (int i = 0; i < MPU_FIFO_LEN; i++) {
// 1. 解析六轴传感器数据 (12 bytes)
int16_t imu_raw[6];
for (int j = 0; j < 6; j++) {
imu_airplane[i][2*j] = p[0];
imu_airplane[i][2*j+1] = p[1];
// 小端模式: 低字节在前, 高字节在后
imu_raw[j] = (int16_t)(((uint16_t)p[1] << 8) | (uint16_t)p[0]);
p += 2;
}
float acc_g[3];
float gyr_dps[3];
acc_g[0] = (float)imu_raw[0] / 2048.0f;
acc_g[1] = (float)imu_raw[1] / 2048.0f;
acc_g[2] = (float)imu_raw[2] / 2048.0f;
gyr_dps[0] = (float)imu_raw[3] * 0.061f;
gyr_dps[1] = (float)imu_raw[4] * 0.061f;
gyr_dps[2] = (float)imu_raw[5] * 0.061f;
// 2. 解析地磁传感器数据 (12 bytes)
int32_t mag_raw[3];
for (int j = 0; j < 3; j++) {
// 小端模式
mag_raw[j] = (int32_t)(((uint32_t)p[3] << 24) | ((uint32_t)p[2] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[0]);
p += 4;
}
float mag_gauss[3];
mag_gauss[0] = (float)mag_raw[0] / 1000.0f;
mag_gauss[1] = (float)mag_raw[1] / 1000.0f;
mag_gauss[2] = (float)mag_raw[2] / 1000.0f;
// 3. 解析温度数据 (2 bytes)
int16_t temp_raw = (int16_t)(((uint16_t)p[1] << 8) | (uint16_t)p[0]);
p += 2;
float temperature = (float)temp_raw / 1000.0f;
// 4. 解析气压数据 (4 bytes)
uint32_t press_raw = (uint32_t)(((uint32_t)p[3] << 24) | ((uint32_t)p[2] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[0]);
p += 4;
float pressure = (float)press_raw / 1000.0f;
// 打印解析后的数据
// if(i % 8 == 0){
// printf(" ==================ble index: %d\n", *p);
// printf("Package[%d]:\n", i);
// printf(" ACC(g): x=%.3f, y=%.3f, z=%.3f\n", acc_g[0], acc_g[1], acc_g[2]);
// printf(" GYR(dps):x=%.3f, y=%.3f, z=%.3f\n", gyr_dps[0], gyr_dps[1], gyr_dps[2]);
// printf(" MAG(Gs): x=%.3f, y=%.3f, z=%.3f\n", mag_gauss[0], mag_gauss[1], mag_gauss[2]);
// printf(" TEMP(C): %.3f, PRESS(Pa): %.3f\n", temperature, pressure);
// }
}
// printf("--- End of Packet ---\n\n");
extern void uartSendData(void *buf, u16 len) ; // 确保u16是uint16_t或unsigned short
// uartSendData(imu_airplane, sizeof(imu_airplane));
uartSendData(data, 484); // 发送总共17字节
}
extern void create_process(u16* pid, const char* name, void *priv, void (*func)(void *priv), u32 msec);
/**
* @brief ble数据发送函数
*
*/
void BLE_send_fuc(void){
ble_send_data_t send_data;
uint8_t send_buffer[484];
while(1){
os_sem_pend(&receiver_ready_sem, 0); //阻塞等待
circle_buffer_read(&g_ble_send_cb, &send_data);
void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf) //by xtell
{
swapX(bt_get_mac_addr(), mac_addr_buf, 6);
// 逐字节打包数据到 send_buffer, 采用小端模式
uint8_t *p = send_buffer;
*p++ = send_data.checkout_1;
*p++ = send_data.checkout_2;
*p++ = send_data.foot;
*p++ = send_data.package_index;
for (int i = 0; i < MPU_FIFO_LEN; i++) {
sensor_package_t *pkg = &send_data.sensor_package[i];
// 1. 打包六轴数据 (6 * int16_t)
for (int j = 0; j < 6; j++) {
*p++ = (uint8_t)(pkg->SC7U22_data[j] & 0xFF);
*p++ = (uint8_t)((pkg->SC7U22_data[j] >> 8) & 0xFF);
}
// 2. 打包地磁数据 (3 * int32_t)
for (int j = 0; j < 3; j++) {
*p++ = (uint8_t)(pkg->mmc5603nj_buffer[j] & 0xFF);
*p++ = (uint8_t)((pkg->mmc5603nj_buffer[j] >> 8) & 0xFF);
*p++ = (uint8_t)((pkg->mmc5603nj_buffer[j] >> 16) & 0xFF);
*p++ = (uint8_t)((pkg->mmc5603nj_buffer[j] >> 24) & 0xFF);
}
// 3. 打包温度数据 (int16_t)
*p++ = (uint8_t)(pkg->temperature & 0xFF);
*p++ = (uint8_t)((pkg->temperature >> 8) & 0xFF);
// 4. 打包气压数据 (uint32_t)
*p++ = (uint8_t)(pkg->pressure & 0xFF);
*p++ = (uint8_t)((pkg->pressure >> 8) & 0xFF);
*p++ = (uint8_t)((pkg->pressure >> 16) & 0xFF);
*p++ = (uint8_t)((pkg->pressure >> 24) & 0xFF);
#if 0
float acc_g[3];
float gyr_dps[3];
acc_g[0] = (float)send_data.sensor_package[i].SC7U22_data[0] / 2048.0f;
acc_g[1] = (float)send_data.sensor_package[i].SC7U22_data[1] / 2048.0f;
acc_g[2] = (float)send_data.sensor_package[i].SC7U22_data[2] / 2048.0f;
gyr_dps[0] = (float)send_data.sensor_package[i].SC7U22_data[3] * 0.061f;
gyr_dps[1] = (float)send_data.sensor_package[i].SC7U22_data[4] * 0.061f;
gyr_dps[2] = (float)send_data.sensor_package[i].SC7U22_data[5] * 0.061f;
printf(" ACC(g): x=%.3f, y=%.3f, z=%.3f\n", acc_g[0], acc_g[1], acc_g[2]);
printf(" GYR(dps):x=%.3f, y=%.3f, z=%.3f\n", gyr_dps[0], gyr_dps[1], gyr_dps[2]);
#endif
}
// extern void uartSendData(void *buf, u16 len) ; // 确保u16是uint16_t或unsigned short
// uartSendData(send_buffer, 484); // 发送总共17字节
send_data_to_ble_client(send_buffer, 484); // 发送数据
// data_log(send_buffer);
}
}
// ------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
/**
* @brief 开始采集传感器数据并通过ble发送
*
*/
void start_clloct(void){
os_task_create(sensor_collect_task,NULL,5,1024,32,"sensor_collect_task");
os_task_create(BLE_send_fuc,NULL,5,1024,32,"BLE_send_fuc");
}
/**
* @brief 停止采集和ble发送
*
*/
void stop_clloct(void){
os_task_del("sensor_collect_task");
os_task_del("BLE_send_fuc");
}
/**
* @brief 初始化在app_main.c的app_main函数被调用
*
*/
void xtell_task_create(void){
#if TCFG_GSENOR_USER_IIC_TYPE
int ret = hw_iic_init(0);
xlog("init iic result:%d\n", ret); //返回0成功
#else
int ret = soft_iic_init(0);
int num_chars_written = snprintf(log_buffer_1, sizeof(log_buffer_1),"init iic: %d\n", ret);
#endif
// MPU9250_Mag_Init();
//iic总线设备扫描
// extern void i2c_scanner_probe(void);
// i2c_scanner_probe();
xlog("xtell_task_create\n");
//写入测试数据
for (int i = 0; i < ARRAY_SIZE; i++) { //ARRAY_SIZE字节的数组
global_data_array[i] = i % 256;
circle_buffer_init(&g_ble_send_cb, g_sensor_data_storage, SENSOR_DATA_BUFFER_SIZE, sizeof(ble_send_data_t));
os_sem_create(&receiver_ready_sem, 0);
extern void test_uart_init(void);
test_uart_init();
}
/**
* @brief 发给上位机
*
*/
void test_uart_init(void){
#if TCFG_UART0_ENABLE == 0
static u8 buff[40];
struct uart_platform_data_t u_arg = {0};
u_arg.tx_pin = IO_PORT_DP;
u_arg.rx_cbuf = buff;
u_arg.rx_cbuf_size = 32;
u_arg.frame_length = 6;
u_arg.rx_timeout = 100;
u_arg.isr_cbfun = NULL;
u_arg.baud = 1000000;
u_arg.is_9bit = 0;
uart_bus = uart_dev_open(&u_arg);
#endif
}
void uartSendData(void *buf, u16 len) //发送数据的接口。
{
#if TCFG_UART0_ENABLE == 0
if (uart_bus) {
uart_bus->write(buf, len); //把数据写到DMA
}
#endif
}
//////////////////////////////////////////////////////////////////////////////
//test
//
#define BUFF_LEN 500
static signed char acc_data_buf[BUFF_LEN] = {0};
// 1. 定义一个全局的信号量
static OS_SEM ble_send_sem;
int j = 0;
void data_send_task(void){
signed short accx_buf[100];
signed short accy_buf[100];
signed short accz_buf[100];
signed short gyrx_buf[100];
signed short gyry_buf[100];
signed short gyrz_buf[100];
SL_SC7U22_FIFO_Read(accx_buf,accy_buf,accz_buf,gyrx_buf,gyry_buf,gyrz_buf); //一次性读取内置fifo的数据
#if 1
// 定义新的Packet ID和数据长度
#define PACKET_ID_RAW_IMU 0x04
#define PACKET_LENGTH_RAW_IMU 12 // 6个传感器值每个2字节
// 声明一个发送缓冲区用于包含帧头、ID、长度、数据和校验和
// 帧头 (2) + ID (1) + 长度 (1) + 数据 (12) + 校验和 (1) = 17 字节
uint8_t tx_buffer[2 + 1 + 1 + PACKET_LENGTH_RAW_IMU + 1];
uint8_t checksum = 0;
int i; // 用于循环计算校验和
// 填充帧头
tx_buffer[0] = 0xAA;
tx_buffer[1] = 0xFF;
// 填充Packet ID和长度
tx_buffer[2] = PACKET_ID_RAW_IMU;
tx_buffer[3] = PACKET_LENGTH_RAW_IMU;
// 填充原始传感器数据 (与你原先的processing_data内容相同)
tx_buffer[4] = (uint8_t)(accx_buf[0] & 0xFF); // accX LSB
tx_buffer[5] = (uint8_t)(accx_buf[0] >> 8 & 0xFF); // accX MSB
tx_buffer[6] = (uint8_t)(accy_buf[0] & 0xFF); // accY LSB
tx_buffer[7] = (uint8_t)(accy_buf[0] >> 8 & 0xFF); // accY MSB
tx_buffer[8] = (uint8_t)(accz_buf[0] & 0xFF); // accZ LSB
tx_buffer[9] = (uint8_t)(accz_buf[0] >> 8 & 0xFF); // accZ MSB
tx_buffer[10] = (uint8_t)(gyrx_buf[0] & 0xFF); // gyrX LSB
tx_buffer[11] = (uint8_t)(gyrx_buf[0] >> 8 & 0xFF); // gyrX MSB
tx_buffer[12] = (uint8_t)(gyry_buf[0] & 0xFF); // gyrY LSB
tx_buffer[13] = (uint8_t)(gyry_buf[0] >> 8 & 0xFF); // gyrY MSB
tx_buffer[14] = (uint8_t)(gyrz_buf[0] & 0xFF); // gyrZ LSB
tx_buffer[15] = (uint8_t)(gyrz_buf[0] >> 8 & 0xFF); // gyrZ MSB
// 计算校验和 (从 Packet ID 到所有数据字节的和)
checksum = tx_buffer[2] + tx_buffer[3]; // ID + Length
for (i = 0; i < PACKET_LENGTH_RAW_IMU; i++) {
checksum += tx_buffer[4 + i]; // 加上所有数据字节
}
tx_buffer[4 + PACKET_LENGTH_RAW_IMU] = checksum; // 校验和是最后一个字节
// 发送整个缓冲区
extern void uartSendData(void *buf, u16 len) ; // 确保u16是uint16_t或unsigned short
uartSendData(tx_buffer, sizeof(tx_buffer)); // 发送总共17字节
#endif
}
static u16 gtest_id = 0;
void test_func(void){
// a. 初始化信号量初始值为0
// os_sem_create(&ble_send_sem, 0);
// b. 注册回调函数,让协议栈知道在准备好时该调用谁
// struct ble_server_operation_t *ble_ops;
// ble_get_server_operation_table(&ble_ops);
// ble_ops->regist_wakeup_send(NULL, on_ble_can_send);
for(int i = 0;i<BUFF_LEN;i++){
acc_data_buf[i] = i;
}
// 初始化环形缓冲区
circle_buffer_init(&sensor_cb, sensor_data_buffer, SENSOR_DATA_BUFFER_SIZE);
// 创建一个定时器每200ms调用一次核心计算任务
// create_process(&xtell_i2c_test_id, "xtell_i2c_test", NULL, xtell_i2c_test, (u32)(SAMPLING_PERIOD_S * 1000));
// 创建一个定时器每1000ms采集一次数据
// create_process(&collect_data_id, "collect_data", NULL, collect_and_buffer_sensor_data_task, 1000);
// 创建一个定时器每200ms尝试发送一次数据
create_process(&send_data_id, "send_data", NULL, send_sensor_data_task, 1);
SL_SC7U22_Config();
mmc5603nj_init();
bmp280_init();
os_task_create(BLE_send_fuc,NULL,5,1024,32,"BLE_send_fuc");
os_task_create(sensor_collect_task,NULL,5,1024,32,"sensor_collect_task");
// create_process(&test_id, "sensor_test",NULL,data_send_task ,3);
// data_send_task();
}

View File

@ -0,0 +1,133 @@
#include "AK8963.h"
#include "math.h"
#include "os/os_api.h"
#include "../xtell.h"
#include "printf.h"
// 用于存放从Fuse ROM读取的磁力计灵敏度校准值
static float mag_asa_x = 1.0f;
static float mag_asa_y = 1.0f;
static float mag_asa_z = 1.0f;
// 磁力计在16-bit分辨率下的转换因子 (单位: uT/LSB)
#define MAG_RAW_TO_UT_FACTOR (4912.0f / 32760.0f)
/**
* @brief 初始化MPU9250的磁力计AK8963
* @return 0: 成功, 1: MPU9250连接失败, 2: AK8963连接失败
*/
u8 MPU9250_Mag_Init(void) {
u8 temp_data[3];
// --- 检查 MPU9250 连接并复位 ---
_gravity_sensor_get_ndata(MPU9250_ADDR_R, MPU9250_WHO_AM_I, temp_data, 1);
if (temp_data[0] != 0x71 && temp_data[0] != 0x73) {
printf("MPU9250 comm failed, read ID: 0x%X\n", temp_data[0]);
return 1;
}
printf("MPU9250 get id:0x%X\n", temp_data[0]);
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_PWR_MGMT_1, 0x80); // 软复位
os_time_dly(10); // 等待复位完成
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_PWR_MGMT_1, 0x01); // 退出睡眠,选择时钟源
os_time_dly(2);
// --- 强制复位 I2C Master 模块并开启旁路 ---
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_USER_CTRL, 0x20);
os_time_dly(1);
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_USER_CTRL, 0x00);
os_time_dly(1);
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_INT_PIN_CFG, 0x02);
os_time_dly(2);
// --- 再次验证 AK8963 连接 ---
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_WIA, temp_data, 1);
if (temp_data[0] != 0x48) {
printf("AK8963 comm failed after final attempt, read ID: 0x%X\n", temp_data[0]);
return 2;
}
printf("AK8963 get id: 0x%X\n", temp_data[0]);
// ------------------ 配置 AK8963 ------------------
// Power-down模式
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x00);
os_time_dly(1);
// Fuse ROM access模式
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x0F);
os_time_dly(1);
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_ASAX, temp_data, 3);
// 计算校准系数
mag_asa_x = (float)(temp_data[0] - 128) / 256.0f + 1.0f;
mag_asa_y = (float)(temp_data[1] - 128) / 256.0f + 1.0f;
mag_asa_z = (float)(temp_data[2] - 128) / 256.0f + 1.0f;
// 再次进入Power-down模式
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x00);
os_time_dly(1);
// 设置工作模式16-bit分辨率100Hz连续测量模式 (0x16)
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x16);
os_time_dly(1);
printf("AK8963 configured successfully.\n");
return 0; // 初始化成功
}
/**
* @brief 读取磁力计的三轴原始数据
* @param mx, my, mz - 用于存放X, Y, Z轴数据的指针 (int16_t类型)
* @return 0: 成功, 1: 数据未就绪, 2: 数据溢出
*/
u8 MPU9250_Read_Mag_Raw(int16_t *mx, int16_t *my, int16_t *mz) {
u8 read_buf[7];
// 检查数据是否准备好 (使用8位读地址)
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_ST1, read_buf, 1);
if (!(read_buf[0] & 0x01)) {
return 1; // 数据未就绪
}
// 连续读取7个字节 (使用8位读地址)
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_HXL, read_buf, 7);
// 检查数据是否溢出
if (read_buf[6] & 0x08) {
return 2; // 数据溢出
}
// 组合数据
*mx = (int16_t)((read_buf[1] << 8) | read_buf[0]);
*my = (int16_t)((read_buf[3] << 8) | read_buf[2]);
*mz = (int16_t)((read_buf[5] << 8) | read_buf[4]);
return 0; // 读取成功
}
/**
* @brief 读取磁力计的三轴数据并转换为uT(微特斯拉) (此函数内部逻辑不变)
* @param mx, my, mz - 用于存放X, Y, Z轴数据的指针 (float类型)
* @return 0: 成功, 1: 数据未就绪, 2: 数据溢出
*/
u8 MPU9250_Read_Mag_uT(float *mx, float *my, float *mz) {
int16_t raw_mx, raw_my, raw_mz;
u8 status = MPU9250_Read_Mag_Raw(&raw_mx, &raw_my, &raw_mz);
if (status != 0) {
return status;
}
// 应用灵敏度校准并转换为uT单位
*mx = (float)raw_mx * mag_asa_x * MAG_RAW_TO_UT_FACTOR;
*my = (float)raw_my * mag_asa_y * MAG_RAW_TO_UT_FACTOR;
*mz = (float)raw_mz * mag_asa_z * MAG_RAW_TO_UT_FACTOR;
return 0;
}

View File

@ -0,0 +1,46 @@
// mpu9250_mag.h
#ifndef __MPU9250_MAG_H
#define __MPU9250_MAG_H
#include "stdint.h" // 假设你有标准整数类型u8 对应 uint8_t
#include "gSensor/gSensor_manage.h"
//==================================================================================
// MPU9250 和 AK8963 的 I2C 地址 (已转换为8位格式)
//==================================================================================
// MPU9250的7位地址是 0x68(接地)
#define MPU9250_ADDR_7BIT 0x69
#define MPU9250_ADDR_W (MPU9250_ADDR_7BIT << 1 | 0) // 8位写地址: 0xD0
#define MPU9250_ADDR_R (MPU9250_ADDR_7BIT << 1 | 1) // 8位读地址: 0xD1
// AK8963磁力计的7位地址是 0x0C
#define AK8963_ADDR_7BIT 0x0C
#define AK8963_ADDR_W (AK8963_ADDR_7BIT << 1 | 0) // 8位写地址: 0x18
#define AK8963_ADDR_R (AK8963_ADDR_7BIT << 1 | 1) // 8位读地址: 0x19
//==================================================================================
// MPU9250 相关寄存器 (用于开启旁路模式)
//==================================================================================
#define MPU9250_WHO_AM_I 0x75
#define MPU9250_INT_PIN_CFG 0x37
#define MPU9250_USER_CTRL 0x6A
#define MPU9250_PWR_MGMT_1 0x6B
//==================================================================================
// AK8963 磁力计相关寄存器
//==================================================================================
#define AK8963_WIA 0x00
#define AK8963_ST1 0x02
#define AK8963_HXL 0x03
#define AK8963_ST2 0x09
#define AK8963_CNTL1 0x0A
#define AK8963_ASAX 0x10
u8 MPU9250_Mag_Init(void);
u8 MPU9250_Read_Mag_Raw(int16_t *mx, int16_t *my, int16_t *mz);
u8 MPU9250_Read_Mag_uT(float *mx, float *my, float *mz);
#endif // __MPU9250_MAG_H

View File

@ -0,0 +1,216 @@
/*
气压计
根据手册,对于室内导航的配置推荐:
t_standby=0.5ms, filter=16, spi_en=0
osrs_t=x2, osrs_p=x16, mode=normal
采样率为26.3Hz外部每40ms读取一次
*/
#include "BMP280.h"
#include <string.h>
#include "os/os_api.h"
#include "gSensor/gSensor_manage.h"
/*==================================================================================*/
/* BMP280 内部定义 */
/*==================================================================================*/
// 存储校准参数的静态全局变量
static uint16_t t1;
static int16_t t2, t3;
static uint16_t p1;
static int16_t p2, p3, p4, p5, p6, p7, p8, p9;
static int32_t t_fine;
/*==================================================================================*/
/* 封装的底层I2C读写函数 */
/*==================================================================================*/
/**
* @brief 写入单个字节到BMP280寄存器
*/
static uint8_t bmp280_write_reg(uint8_t reg, uint8_t data) {
gravity_sensor_command(BMP_IIC_WRITE_ADDRESS, reg, data);
return 0;
}
/**
* @brief 从BMP280读取多个字节
*/
static uint8_t bmp280_read_regs(uint8_t reg, uint8_t *buf, uint16_t len) {
return _gravity_sensor_get_ndata(BMP_IIC_READ_ADDRESS, reg, buf, len);
}
/*==================================================================================*/
/* 核心算法 */
/*==================================================================================*/
/**
* @brief 温度补偿计算
* @param adc_T - 原始温度数据
* @return 补偿后的温度值 (单位: °C)
*/
static float compensate_temperature(int32_t adc_T) {
float var1, var2, temperature;
var1 = (((float)adc_T) / 16384.0f - ((float)t1) / 1024.0f) * ((float)t2);
var2 = ((((float)adc_T) / 131072.0f - ((float)t1) / 8192.0f) *
(((float)adc_T) / 131072.0f - ((float)t1) / 8192.0f)) *
((float)t3);
t_fine = (int32_t)(var1 + var2);
temperature = (var1 + var2) / 5120.0f;
if (temperature < -40.0f) return -40.0f;
if (temperature > 85.0f) return 85.0f;
return temperature;
}
/**
* @brief 气压补偿计算
* @param adc_P - 原始气压数据
* @return 补偿后的气压值 (单位: Pa)
*/
static float compensate_pressure(int32_t adc_P) {
float var1, var2, pressure;
var1 = ((float)t_fine / 2.0f) - 64000.0f;
var2 = var1 * var1 * ((float)p6) / 32768.0f;
var2 = var2 + var1 * ((float)p5) * 2.0f;
var2 = (var2 / 4.0f) + (((float)p4) * 65536.0f);
var1 = (((float)p3) * var1 * var1 / 524288.0f + ((float)p2) * var1) / 524288.0f;
var1 = (1.0f + var1 / 32768.0f) * ((float)p1);
if (var1 == 0.0f) {
return 0; // 避免除以零
}
pressure = 1048576.0f - (float)adc_P;
pressure = (pressure - (var2 / 4096.0f)) * 6250.0f / var1;
var1 = ((float)p9) * pressure * pressure / 2147483648.0f;
var2 = pressure * ((float)p8) / 32768.0f;
pressure = pressure + (var1 + var2 + ((float)p7)) / 16.0f;
if (pressure < 30000.0f) return 30000.0f;
if (pressure > 110000.0f) return 110000.0f;
return pressure;
}
/*==================================================================================*/
/* 外部接口函数实现 */
/*==================================================================================*/
uint8_t bmp280_init(void) {
uint8_t id;
uint8_t calib_data[24];
// 1. 检查芯片ID
if (bmp280_read_regs(BMP280_REG_ID, &id, 1) == 0) {
printf("bmp280 get id error:%d\n",id );
return 1; // I2C读取失败
}
if (id != 0x58) {
printf("bmp280 check diff:%d\n",id );
return 1; // ID不匹配
}
printf("bmp280 get id:0%X\n",id );
// 2. 软复位
bmp280_write_reg(BMP280_REG_RESET, 0xB6);
os_time_dly(10); // 等待复位完成
// 3. 一次性读取所有校准参数
if (bmp280_read_regs(BMP280_REG_CALIB_START, calib_data, 24) == 0) {
return 2; // 读取校准数据失败
}
// 4. 解析校准参数
t1 = (uint16_t)(((uint16_t)calib_data[1] << 8) | calib_data[0]);
t2 = (int16_t)(((int16_t)calib_data[3] << 8) | calib_data[2]);
t3 = (int16_t)(((int16_t)calib_data[5] << 8) | calib_data[4]);
p1 = (uint16_t)(((uint16_t)calib_data[7] << 8) | calib_data[6]);
p2 = (int16_t)(((int16_t)calib_data[9] << 8) | calib_data[8]);
p3 = (int16_t)(((int16_t)calib_data[11] << 8) | calib_data[10]);
p4 = (int16_t)(((int16_t)calib_data[13] << 8) | calib_data[12]);
p5 = (int16_t)(((int16_t)calib_data[15] << 8) | calib_data[14]);
p6 = (int16_t)(((int16_t)calib_data[17] << 8) | calib_data[16]);
p7 = (int16_t)(((int16_t)calib_data[19] << 8) | calib_data[18]);
p8 = (int16_t)(((int16_t)calib_data[21] << 8) | calib_data[20]);
p9 = (int16_t)(((int16_t)calib_data[23] << 8) | calib_data[22]);
// 5. 配置传感器 (推荐设置: 正常模式,高精度)
// t_standby=0.5ms, filter=16, spi_en=0
uint8_t config_reg = (0 << 5) | (4 << 2) | (0 << 0);
bmp280_write_reg(BMP280_REG_CONFIG, config_reg);
// osrs_t=x2, osrs_p=x16, mode=normal
uint8_t ctrl_meas_reg = (2 << 5) | (5 << 2) | (3 << 0);
bmp280_write_reg(BMP280_REG_CTRL_MEAS, ctrl_meas_reg);
os_time_dly(10); // 等待配置生效
printf("bmp280 init success\n");
return 0; // 初始化成功
}
/**
* @brief 获取转换后的温度和压力数据
*
* @param temperature 传出,温度
* @param pressure 传出,压力
* @return uint8_t
*/
uint8_t bmp280_read_data(float *temperature, float *pressure) {
uint8_t data[6];
int32_t adc_P, adc_T;
// printf("==========debug1===========\n");
// 一次性读取6个字节的温度和气压原始数据
if (bmp280_read_regs(BMP280_REG_PRESS_MSB, data, 6) == 0) {
printf("bmp280:read data error\n");
return 1; // 读取失败
}
// printf("==========debug2===========\n");
// 组合原始数据 (20位)
adc_P = (int32_t)((((uint32_t)(data[0])) << 12) | (((uint32_t)(data[1])) << 4) | (((uint32_t)(data[2])) >> 4));
adc_T = (int32_t)((((uint32_t)(data[3])) << 12) | (((uint32_t)(data[4])) << 4) | (((uint32_t)(data[5])) >> 4));
// 如果没有数据,直接返回错误 (ADC读数为0x80000是未测量状态)
if (adc_T == 0x80000 || adc_P == 0x80000) {
*temperature = 0.0f;
*pressure = 0.0f;
printf("bmp280:no data\n");
return 1;
}
// printf("==========debug3===========\n");
// 进行补偿计算
*temperature = compensate_temperature(adc_T);
*pressure = compensate_pressure(adc_P);
return 0; // 成功
}
/**
* @brief 获取该气压计的原始adc数据
*
* @param adc_P 传出,气压
* @param adc_T 传出,温度
*/
void bmp280_read_originanl_data(int* adc_P, int* adc_T){
uint8_t data[6];
// 一次性读取6个字节的温度和气压原始数据
if (bmp280_read_regs(BMP280_REG_PRESS_MSB, data, 6) != 0) {
return; // 读取失败
}
// 组合原始数据 (20位)
adc_P = (int32_t)((((uint32_t)(data[0])) << 12) | (((uint32_t)(data[1])) << 4) | (((uint32_t)(data[2])) >> 4));
adc_T = (int32_t)((((uint32_t)(data[3])) << 12) | (((uint32_t)(data[4])) << 4) | (((uint32_t)(data[5])) >> 4));
}

View File

@ -0,0 +1,54 @@
#ifndef BMP280_DRIVER_H
#define BMP280_DRIVER_H
#include <stdint.h>
#define BMP_PULL_UP 0 //外部是否接的上拉
// I2C 从设备地址
#if BMP_PULL_UP == 1 //外部接的高
#define BMP_IIC_7BIT_ADDRESS 0x76 //7位,外部接高为0x77
#define BMP_IIC_WRITE_ADDRESS (BMP_IIC_7BIT_ADDRESS<<1) //8位地址
#define BMP_IIC_READ_ADDRESS (BMP_IIC_WRITE_ADDRESS | 0x01)
#else
#define BMP_IIC_7BIT_ADDRESS 0x77 //7位,外部接低为0x76
#define BMP_IIC_WRITE_ADDRESS (BMP_IIC_7BIT_ADDRESS<<1) //8位地址
#define BMP_IIC_READ_ADDRESS (BMP_IIC_WRITE_ADDRESS | 0x01)
#endif
// BMP280 寄存器地址
#define BMP280_REG_CALIB_START 0x88
#define BMP280_REG_ID 0xD0
#define BMP280_REG_RESET 0xE0
#define BMP280_REG_STATUS 0xF3
#define BMP280_REG_CTRL_MEAS 0xF4
#define BMP280_REG_CONFIG 0xF5
#define BMP280_REG_PRESS_MSB 0xF7
/**
* @brief 初始化BMP280传感器
* @return 0: 成功, 1: 芯片ID错误, 2: 读取校准参数失败
* @note 此函数会完成ID检查、软复位、读取校准参数并设置传感器为连续测量模式。
*/
uint8_t bmp280_init(void);
/**
* @brief 从BMP280读取温度和气压数据
* @param[out] temperature - 指向浮点数变量的指针,用于存储温度值 (单位: °C)
* @param[out] pressure - 指向浮点数变量的指针,用于存储气压值 (单位: Pa)
* @return 0: 成功, 1: 读取数据失败
*/
uint8_t bmp280_read_data(float *temperature, float *pressure);
/**
* @brief 获取该气压计的原始adc数据
*
* @param adc_P 传出,气压
* @param adc_T 传出,温度
*/
void bmp280_read_originanl_data(int* adc_P, int* adc_T);
#endif // BMP280_DRIVER_H

View File

@ -33,7 +33,7 @@
#define LIS2DH12_R_ADDR 0x33
// --- IIC 寄存器地址宏定义 ---
#define LIS2DH12_WHO_AM_I 0x0F
#define LIS2DH12_WHO_AM_I 0x01 //0F
#define LIS2DH12_CTRL_REG1 0x20
#define LIS2DH12_CTRL_REG4 0x23
#define LIS2DH12_CTRL_REG5 0x24
@ -93,7 +93,7 @@ static u8 SL_MEMS_i2cWrite(u8 addr, u8 reg, u8 data) {
char LIS2DH12_Check() {
u8 reg_value = 0;
SL_MEMS_i2cRead(LIS2DH12_R_ADDR, LIS2DH12_WHO_AM_I, 1, &reg_value);
if (reg_value == 0x33) {
if (reg_value == 0x6A) { //0x33
return 0x01;
}
return 0x00;
@ -150,7 +150,7 @@ void LIS2DH12_calibrate() {
// 初始化并配置LIS2DH12传感器
u8 LIS2DH12_Config(void) {
if (LIS2DH12_Check() != 1) {
xlog("LIS2DH12 I2C检查失败\n");
xlog("LIS2DH12 I2C error\n");
return -1;
}
@ -163,7 +163,7 @@ u8 LIS2DH12_Config(void) {
// 执行开机校准
LIS2DH12_calibrate();
xlog("LIS2DH12 I2C检查成功\n");
xlog("LIS2DH12 I2C success\n");
return 0;
}

View File

@ -0,0 +1,266 @@
/*
MMC5603nj
1-255的采样率这里设置为200Hz,5ms
*/
#include "MMC56.h"
#include "math.h"
#include "os/os_api.h"
#include "../xtell.h"
#include "gSensor/gSensor_manage.h"
#include "printf.h"
#define CALIBRATION_TIME 20000 //校准持续时间 ms
#define SAMPLE_INTERVAL 100 //校准采样间隔
// 用于跟踪当前是否处于连续测量模式
static uint8_t g_continuous_mode_enabled = 0;
mmc5603nj_cal_data_t cal_data; //校准数据
static void mmc5603nj_write_reg(uint8_t reg, uint8_t data) {
gravity_sensor_command(MMC_IIC_WRITE_ADDRESS, reg, data);
}
static uint32_t mmc5603nj_read_regs(uint8_t reg, uint8_t *buf, uint8_t len) {
return _gravity_sensor_get_ndata(MMC_IIC_READ_ADDRESS, reg, buf, len);
}
// 外部接口函数实现
uint8_t mmc5603nj_get_pid(void) {
uint8_t pid = 0;
mmc5603nj_read_regs(MMC_PID, &pid, 1);
return pid;
}
int mmc5603nj_init(void) {
// ID
if ( mmc5603nj_get_pid() != 0x10) {
printf("MMC5603NJ init failed: wrong Product ID (read: 0x%X)\n", mmc5603nj_get_pid());
// return 0;
}
// 软件复位
mmc5603nj_write_reg(MMC_INCTRL1, 0x80); // SW_RESET bit
os_time_dly(20); // 等待复位完成
// 设置20位分辨率 (BW[1:0] = 11)
// 同时确保所有轴都使能 (X/Y/Z_inhibit = 0)
// mmc5603nj_write_reg(MMC_INCTRL1, 0x03);
mmc5603nj_write_reg(MMC_INCTRL1, 0x03);
os_time_dly(1);
// 设置内部控制寄存器2
// CMM_EN = 1 (使能连续模式功能)
// HPOWER = 0
// mmc5603nj_write_reg(MMC_INCTRL2, 0x10); // 0b00010000
mmc5603nj_write_reg(MMC_INCTRL2, 0x10); // 0b10010000
// 设置自动SET/RESET功能
// AUTO_SR_EN = 1
mmc5603nj_write_reg(MMC_INCTRL0, 0x20); // 0b00100000
g_continuous_mode_enabled = 0;
printf("MMC5603NJ initialized successfully.\n");
// mmc5603nj_enable_continuous_mode(0xC8); //200Hz的采样率最高支持255
mmc5603nj_enable_continuous_mode(0xCF);
return 1;
}
void mmc5603nj_start_calibration(void){
printf("\n--- Magnetometer Calibration Start ---\n");
printf("Slowly rotate the device in all directions (like drawing a 3D '8')...\n");
printf("Calibration will last for 20 seconds.\n\n");
printf("will start after 5 seconds\n\n");
os_time_dly(500);
// 初始化最大最小值
// 使用一个临时变量来读取数据避免干扰read函数的正常逻辑
mmc5603nj_mag_data_t temp_mag_data;
// 首次读取以获取初始值
mmc5603nj_read_mag_data(&temp_mag_data); // 首次读取不应用校准
float max_x = temp_mag_data.x;
float min_x = temp_mag_data.x;
float max_y = temp_mag_data.y;
float min_y = temp_mag_data.y;
float max_z = temp_mag_data.z;
float min_z = temp_mag_data.z;
uint32_t start_time = os_time_get(); // 假设os_time_get()返回毫秒级时间戳
int samples = 0;
int over = CALIBRATION_TIME/SAMPLE_INTERVAL;
while (samples <= over) {
// 读取原始磁力计数据
mmc5603nj_read_mag_data(&temp_mag_data);
// 更新最大最小值
if (temp_mag_data.x > max_x) max_x = temp_mag_data.x;
if (temp_mag_data.x < min_x) min_x = temp_mag_data.x;
if (temp_mag_data.y > max_y) max_y = temp_mag_data.y;
if (temp_mag_data.y < min_y) min_y = temp_mag_data.y;
if (temp_mag_data.z > max_z) max_z = temp_mag_data.z;
if (temp_mag_data.z < min_z) min_z = temp_mag_data.z;
samples++;
os_time_dly(SAMPLE_INTERVAL / 10);
}
// 检查数据范围是否合理,防止传感器未动或故障
if ((max_x - min_x < 0.1f) || (max_y - min_y < 0.1f) || (max_z - min_z < 0.1f)) {
printf("\n--- Calibration Failed ---\n");
printf("Device might not have been rotated enough.\n");
printf("X range: %.2f, Y range: %.2f, Z range: %.2f\n", max_x - min_x, max_y - min_y, max_z - min_z);
return;
}
// 计算硬磁偏移 (椭球中心)
cal_data.offset_x = (max_x + min_x) / 2.0f;
cal_data.offset_y = (max_y + min_y) / 2.0f;
cal_data.offset_z = (max_z + min_z) / 2.0f;
printf("\n--- Calibration Complete ---\n");
printf("Collected %d samples.\n", samples);
printf("Offsets (Gauss):\n");
printf(" X: %.4f\n", cal_data.offset_x);
printf(" Y: %.4f\n", cal_data.offset_y);
printf(" Z: %.4f\n", cal_data.offset_z);
printf("Please save these values and apply them in your code.\n\n");
}
void mmc5603nj_enable_continuous_mode(uint8_t rate) {
// 在连续模式下ODR寄存器必须被设置
mmc5603nj_write_reg(MMC_ODR, rate); //要设置频率
// mmc5603nj_set_data_rate(0x04);
// 启用连续模式 (INCTRL2的CMM_EN位已在init中设置)
// 只需要设置 INCTRL0 的 CMM_FREQ_EN 位
mmc5603nj_write_reg(MMC_INCTRL0, 0xA0); // 0b10100000 (CMM_FREQ_EN=1, AUTO_SR_EN=1)
g_continuous_mode_enabled = 1;
}
void mmc5603nj_disable_continuous_mode(void) {
// 禁用连续模式
mmc5603nj_write_reg(MMC_INCTRL0, 0x20); // 恢复到仅使能 AUTO_SR_EN 的状态
g_continuous_mode_enabled = 0;
}
float mmc5603nj_get_temperature(void) {
uint8_t status = 0;
uint8_t temp_raw = 0;
uint8_t timeout = 20;
// 触发一次温度测量
mmc5603nj_write_reg(MMC_INCTRL0, 0x02); // TAKE_MEAS_T
// 等待测量完成
do {
os_time_dly(10);
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
timeout--;
} while ((status & 0x80) == 0 && timeout > 0);
if (timeout == 0) {
printf("Error: Temperature measurement timeout!\n");
return -273.15f; // 返回一个绝对零度的错误值
}
mmc5603nj_read_regs(MMC_TOUT, &temp_raw, 1);
return ((float)temp_raw * 0.8f) - 75.0f;
}
void mmc5603nj_read_mag_data(mmc5603nj_mag_data_t *mag_data) {
uint8_t buffer[9];
if (g_continuous_mode_enabled) {
// 连续模式下,只需检查数据是否就绪
uint8_t status = 0;
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
if ((status & 0x40) == 0) { // Meas_M_done bit
// 数据未就绪,可以选择返回或等待,这里我们直接返回旧数据
return;
}
} else {
// 单次测量模式
uint8_t status = 0;
uint8_t timeout = 20;
// 触发一次带自动SET/RESET的磁场测量
mmc5603nj_write_reg(MMC_INCTRL0, 0x21); // 0b00100001 (TAKE_MEAS_M=1, AUTO_SR_EN=1)
// 等待测量完成
do {
os_time_dly(10);
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
timeout--;
} while ((status & 0x40) == 0 && timeout > 0);
if (timeout == 0) {
// printf("Error: Magnetic measurement timeout!\n");
mag_data->x = mag_data->y = mag_data->z = 0.0f;
return;
}
}
// 读取9个字节的原始数据
mmc5603nj_read_regs(MMC_XOUT0, buffer, 9);
// 解析数据 (20位分辨率)
int32_t raw_x = ((uint32_t)buffer[0] << 12) | ((uint32_t)buffer[1] << 4) | ((uint32_t)buffer[6] & 0x0F);
int32_t raw_y = ((uint32_t)buffer[2] << 12) | ((uint32_t)buffer[3] << 4) | ((uint32_t)buffer[6] >> 4);
int32_t raw_z = ((uint32_t)buffer[4] << 12) | ((uint32_t)buffer[5] << 4) | ((uint32_t)buffer[8] & 0x0F);
// 应用偏置和灵敏度进行转换
mag_data->x = ((float)raw_x - 524288.0f) / 16384.0f;
mag_data->y = ((float)raw_y - 524288.0f) / 16384.0f;
mag_data->z = ((float)raw_z - 524288.0f) / 16384.0f;
//减去偏移
mag_data->x -= cal_data.offset_x;
mag_data->y -= cal_data.offset_y;
mag_data->z -= cal_data.offset_z;
}
void mmc5603nj_read_origin_data(uint8_t *buffer) {
if (g_continuous_mode_enabled) {
// 连续模式下,只需检查数据是否就绪
uint8_t status = 0;
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
if ((status & 0x40) == 0) { // Meas_M_done bit
// 数据未就绪,可以选择返回或等待,这里我们直接返回旧数据
return;
}
} else {
// 单次测量模式
uint8_t status = 0;
uint8_t timeout = 20;
// 触发一次带自动SET/RESET的磁场测量
mmc5603nj_write_reg(MMC_INCTRL0, 0x21); // 0b00100001 (TAKE_MEAS_M=1, AUTO_SR_EN=1)
// 等待测量完成
do {
os_time_dly(10);
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
timeout--;
} while ((status & 0x40) == 0 && timeout > 0);
if (timeout == 0) {
printf("Error: Magnetic measurement timeout!\n");
return;
}
}
// 读取9个字节的原始数据
mmc5603nj_read_regs(MMC_XOUT0, buffer, 9);
}

View File

@ -0,0 +1,103 @@
#ifndef MMC5603NJ_DRIVER_H
#define MMC5603NJ_DRIVER_H
#include <stdint.h>
//该芯片的iic地址是固定的, 没法通过外部上下拉来改变
#define BMP_IIC_7BIT_ADDRESS 0x30 //0110000 手册第12页
//8位地址:
#define MMC_IIC_WRITE_ADDRESS (BMP_IIC_7BIT_ADDRESS <<1) // 0x60 : 01100000
#define MMC_IIC_READ_ADDRESS (MMC_IIC_WRITE_ADDRESS | 0x01) // 0x61 : 01100001
// 寄存器地址定义 -- 数据手册第6页
#define MMC_XOUT0 0x00
#define MMC_XOUT1 0x01
#define MMC_YOUT0 0x02
#define MMC_YOUT1 0x03
#define MMC_ZOUT0 0x04
#define MMC_ZOUT1 0x05
#define MMC_XOUT2 0x06
#define MMC_YOUT2 0x07
#define MMC_ZOUT2 0x08
#define MMC_TOUT 0x09
#define MMC_STATUS1 0x18
#define MMC_ODR 0x1A
#define MMC_INCTRL0 0x1B
#define MMC_INCTRL1 0x1C
#define MMC_INCTRL2 0x1D
#define MMC_ST_X_TH 0x1E
#define MMC_ST_Y_TH 0x1F
#define MMC_ST_Z_TH 0x20
#define MMC_ST_X 0x27
#define MMC_ST_Y 0x28
#define MMC_ST_Z 0x29
#define MMC_PID 0x39
// 定义一个结构体来存放三轴磁场数据(原始数据)
typedef struct {
float x;
float y;
float z;
} mmc5603nj_original_data_t;
// 定义一个结构体来存放三轴磁场数据(单位:高斯 Gauss
typedef struct {
float x;
float y;
float z;
} mmc5603nj_mag_data_t;
// 定义一个结构体来存放磁力计的硬磁偏移校准数据
typedef struct {
float offset_x;
float offset_y;
float offset_z;
} mmc5603nj_cal_data_t;
/**
* @brief 初始化MMC5603NJ传感器
* 该函数会对传感器进行软件复位并检查设备ID。
* @return 0 表示成功, -1 表示失败 (设备ID不匹配).
*/
int mmc5603nj_init(void);
/**
* @brief 设置传感器的数据输出速率 (ODR - Output Data Rate)
* @param rate 速率值具体含义请参考datasheet ODR寄存器说明。
*/
void mmc5603nj_set_data_rate(uint8_t rate);
/**
* @brief 启用连续测量模式
*/
void mmc5603nj_enable_continuous_mode(uint8_t rate);
/**
* @brief 禁用连续测量模式
*/
void mmc5603nj_disable_continuous_mode(void);
/**
* @brief 获取产品ID
* @return 产品的ID值对于MMC5603NJ应为0x10.
*/
uint8_t mmc5603nj_get_pid(void);
/**
* @brief 读取传感器的温度
* @return 温度值 (单位: 摄氏度 °C).
*/
float mmc5603nj_get_temperature(void);
/**
* @brief 读取三轴磁场数据
* 此函数会根据当前是连续模式还是单次模式来读取数据。
* @param mag_data 指向 mmc5603nj_mag_data_t 结构体的指针,用于存放结果。
*/
void mmc5603nj_read_mag_data(mmc5603nj_mag_data_t *mag_data);
#endif // MMC5603NJ_DRIVER_H

File diff suppressed because it is too large Load Diff

View File

@ -9,22 +9,23 @@ Copyright (c) 2022 Silan MEMS. All Rights Reserved.
#include "gSensor/gSensor_manage.h"
#include "printf.h"
#include "MMC56.h"
//是否使能串口打印调试
#define SL_Sensor_Algo_Release_Enable 0x01
#define SL_Sensor_Algo_Release_Enable 0x00
//是否开启FIFO模式默认STREAM模式
#define SL_SC7U22_FIFO_ENABLE 0x00
#define SL_SC7U22_FIFO_ENABLE 0x01
/***使用前请根据实际情况配置以下参数******/
/**SC7U22的SDO 接地: 0****************/
/**SC7U22的SDO 接电源:1****************/
#define SL_SC7U22_SDO_VDD_GND 1
#define SL_SC7U22_SDO_VDD_GND 0
/*****************************************/
/***使用前请根据实际IIC地址配置参数***/
/**SC7U22的IIC 接口地址为 7bits: 0****/
/**SC7U22的IIC 接口地址为 8bits: 1****/
#define SL_SC7U22_IIC_7BITS_8BITS 0
#define SL_SC7U22_IIC_7BITS_8BITS 1
/*****************************************/
#if SL_SC7U22_SDO_VDD_GND==0
#define SL_SC7U22_IIC_7BITS_ADDR 0x18
@ -130,7 +131,11 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en,signed short *
/**output Angle_output[2]: Yaw*******************************/
/**input yaw_rst: reset yaw value***************************/
void set_SC7U22_Error_Flag(char flag);
unsigned char Original_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short *acc_gyro_input, float *Angle_output, unsigned char yaw_rst);
unsigned char SIX_SL_SC7U22_Angle_Output(unsigned char auto_calib_start, signed short *acc_gyro_input, float *Angle_output, unsigned char yaw_rst);
unsigned char Q_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short *acc_gyro_input, float *Angle_output, const mmc5603nj_mag_data_t *mag_data_input, unsigned char yaw_rst, float *quaternion_output);
unsigned char get_calibration_state(void);
/**寄存器宏定义*******************************/
#define SC7U22_WHO_AM_I 0x01

View File

@ -1,5 +1,4 @@
#include "SCU722.h"
#include "SC7U22.h"
#include "math.h"
#include "os/os_api.h"
@ -119,10 +118,9 @@ unsigned char SL_SC7U22_Config(void)
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x7D, 0x0E);//PWR_CTRL ENABLE ACC+GYR+TEMP
os_time_dly(1);//10ms
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x40, 0x06);//ACC_CONF 0x07=50Hz
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x41, 0x01);//ACC_RANGE ±8G
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x42, 0x86);//GYR_CONF 0x87=50Hz
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x43, 0x00);//GYR_RANGE 2000dps
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x40, 0x08);//ACC_CONF 0x08=100Hz
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x41, 0x01);//ACC_RANGE ±4G
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x42, 0x88);//GYR_CONF 0x88=100Hz
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x43, 0x00);//GYR_RANGE 2000dps
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE, 0x04, 0x50);//COM_CFG
@ -149,9 +147,6 @@ unsigned char SL_SC7U22_Config(void)
#endif
#if SL_SC7U22_SDO_PullUP_ENABLE ==0x01
SL_SC7U22_I2c_Spi_Write(SL_SPI_IIC_INTERFACE,0x7F, 0x8C);//goto 0x8C
sl_delay(1);
@ -642,50 +637,128 @@ unsigned char SL_SC7U22_WakeUp_SET(unsigned char odr_mode,unsigned char acc_rang
#if SL_SC7U22_FIFO_ENABLE ==0x00
// =================================================================================================
// 卡尔曼滤波器Kalman Filter相关变量定义
// 卡尔曼滤波器是一种高效的递归滤波器,它能够从一系列不完全及包含噪声的测量中,估计动态系统的状态。
// 在这里它被用来融合加速度计和陀螺仪的数据以获得更精确、更稳定的姿态角Pitch 和 Roll
// Madgwick AHRS 滤波器相关变量和函数
// -------------------------------------------------------------------------------------------------
// --- 状态变量 ---
float angle[3] = {0, 0, 0}, angle_dot[3] = {0, 0, 0}; // 姿态角Pitch, Roll, Yaw和角速度
float angle0[3] = {0, 0, 0}, angle_dot0[3] = {0, 0, 0}; // 姿态角的估计值
// 定义常量
#define sampleFreq 100.0f // 传感器采样频率 (Hz),必须与实际的传感器数据更新频率一致
#define betaDef 0.1f // 算法的比例增益 beta影响加速度计修正陀螺仪的权重
// --- 卡尔曼滤波器参数 ---
// Q_angle: 过程噪声协方差,表示角度预测模型的不确定性。值越小,表示越相信陀螺仪的积分结果。
// Q_gyro: 过程噪声协方差表示陀螺仪偏置bias的不确定性。
// R_angle: 测量噪声协方差,表示通过加速度计计算出的角度测量值的不确定性。值越小,表示越相信加速度计的测量结果。
// dt: 采样时间间隔单位这里是10ms (0.01s)对应100Hz的采样率。
//float Q_angle=0.0003, Q_gyro=0.001, R_angle=0.005, dt=0.005;//5ms ST
//float Q_angle=0.00001, Q_gyro=0.00001, R_angle=0.005, dt=0.0025;//5ms ST
float Q_angle = 0.0003, Q_gyro = 0.001, R_angle = 0.005, dt = 0.01; //10ms
// 全局变量
static volatile float beta = betaDef; // 算法增益 beta
static volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // 表示姿态的四元数 (w, x, y, z)
// --- 协方差矩阵 P ---
// P矩阵表示系统状态估计的不确定性程度。它是一个2x2的矩阵
// P[0][0]: 角度估计的方差
// P[0][1], P[1][0]: 角度和陀螺仪偏置的协方差
// P[1][1]: 陀螺仪偏置估计的方差
// P0, P1, P2 分别用于 Pitch, Roll, Yaw 的计算尽管Yaw未使用卡尔曼滤波
float P[2][2] = {{ 1, 0 }, { 0, 1 }};
float P0[2][2] = {{ 1, 0 }, { 0, 1 }}; // Pitch 轴的协方差矩阵
float P1[2][2] = {{ 1, 0 }, { 0, 1 }}; // Roll 轴的协方差矩阵
float P2[2][2] = {{ 1, 0 }, { 0, 1 }}; // Yaw 轴的协方差矩阵(未使用)
/**
* @brief 1/sqrt(x)
* @param x
* @return 1/sqrt(x)
*/
static float invSqrt(float x) {
float halfx = 0.5f * x;
float y = x;
long i = *(long*)&y;
i = 0x5f3759df - (i>>1);
y = *(float*)&i;
y = y * (1.5f - (halfx * y * y)); // 牛顿迭代法,提高精度
return y;
}
// --- 中间计算变量 ---
float Pdot0[4] = {0, 0, 0, 0}; // P0矩阵的微分用于预测步骤
float Pdot1[4] = {0, 0, 0, 0}; // P1矩阵的微分用于预测步骤
float Pdot2[4] = {0, 0, 0, 0}; // P2矩阵的微分用于预测步骤
const float C_0 = 1.0; // 测量矩阵H的元素因为直接测量角度所以为1
const float C_1 = 1.0;
const float C_2 = 1.0;
float q_bias0[3] = {0, 0, 0}; // 陀螺仪零点偏置bias的估计值
float angle_err0[3] = {0, 0, 0}; // 测量值与预测值之间的误差
float PCt0_0[3] = {0, 0, 0}, PCt0_1[3] = {0, 0, 0}; // 中间变量,用于计算卡尔曼增益
float E0[3] = {0, 0, 0}; // 误差的协方差
float K0_0[3] = {0, 0, 0}, K0_1[3] = {0, 0, 0}; // 卡尔曼增益K
float t0_0[3] = {0, 0, 0}, t0_1[3] = {0, 0, 0}; // 中间变量用于更新P矩阵
/**
* @brief Madgwick AHRS 姿 (IMU版本)
* @details 姿
* 1. 使姿
* 2. 使
* 3. 使姿
* @param gx, gy, gz (: rad/s)
* @param ax, ay, az (: g)
*/
static void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) {
float recipNorm;
float s0, s1, s2, s3;
float qDot1, qDot2, qDot3, qDot4;
float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3;
float dt = 1.0f / sampleFreq; // 采样时间间隔
// --- 1. 陀螺仪积分:计算四元数的变化率 ---
// 姿态运动学的基本方程,描述了姿态如何随角速度变化。
qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);
// --- 2. 加速度计修正 ---
// 仅当加速度计读数有效时即模长不为0才进行修正防止计算NaN。
if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
// 将加速度计读数归一化,得到单位向量
recipNorm = invSqrt(ax * ax + ay * ay + az * az);
ax *= recipNorm;
ay *= recipNorm;
az *= recipNorm;
// 预先计算一些重复使用的值,提高效率
_2q0 = 2.0f * q0;
_2q1 = 2.0f * q1;
_2q2 = 2.0f * q2;
_2q3 = 2.0f * q3;
_4q0 = 4.0f * q0;
_4q1 = 4.0f * q1;
_4q2 = 4.0f * q2;
_8q1 = 8.0f * q1;
_8q2 = 8.0f * q2;
q0q0 = q0 * q0;
q1q1 = q1 * q1;
q2q2 = q2 * q2;
q3q3 = q3 * q3;
// --- 梯度下降法:计算修正量 ---
// 目标函数 f(q, a) = [2(q1q3 - q0q2) - ax, 2(q0q1 + q2q3) - ay, 2(0.5 - q1^2 - q2^2) - az]^T
// s0, s1, s2, s3 是目标函数 f 对四元数 q 的雅可比矩阵 J 与 f 的乘积。
// 这个结果代表了误差函数的梯度方向,用于修正四元数的变化率。
s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;
recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // 归一化梯度
s0 *= recipNorm;
s1 *= recipNorm;
s2 *= recipNorm;
s3 *= recipNorm;
// --- 应用修正量 ---
// 将计算出的修正量梯度从陀螺仪积分结果中减去beta是修正的权重。
qDot1 -= beta * s0;
qDot2 -= beta * s1;
qDot3 -= beta * s2;
qDot4 -= beta * s3;
}
// --- 3. 积分:更新四元数 ---
// 使用一阶龙格-库塔法(即欧拉法)进行积分,得到新的四元数。
q0 += qDot1 * dt;
q1 += qDot2 * dt;
q2 += qDot3 * dt;
q3 += qDot4 * dt;
// --- 4. 归一化四元数 ---
// 保持四元数的模长为1防止由于计算误差导致的累积漂移。
recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 *= recipNorm;
q1 *= recipNorm;
q2 *= recipNorm;
q3 *= recipNorm;
}
// =================================================================================================
// --- 静态校准相关变量 ---
static unsigned char SL_SC7U22_Error_Flag=0;
static unsigned char SL_SC7U22_Error_cnt=0;
static unsigned char SL_SC7U22_Error_cnt2=0;
static signed short Temp_Accgyro[6] ={0};
static signed short Error_Accgyro[6]={0};
static signed int Sum_Avg_Accgyro[6] ={0};
static float yaw_offset = 0.0f;
static signed short SL_GetAbsShort(signed short v_Val_s16r)
{
if(v_Val_s16r==(-32768))
@ -693,18 +766,12 @@ static signed short SL_GetAbsShort(signed short v_Val_s16r)
return (v_Val_s16r < 0) ? -v_Val_s16r : v_Val_s16r;
}
unsigned char SL_SC7U22_Error_Flag=0;
unsigned char SL_SC7U22_Error_cnt=0;
unsigned char SL_SC7U22_Error_cnt2=0;
signed short Temp_Accgyro[6] ={0};
signed short Error_Accgyro[6]={0};
signed int Sum_Avg_Accgyro[6] ={0};
/**
* @brief 姿
* @details
*
* 1.
* 2. 姿使PitchRollYaw
* 2. 姿使 Madgwick PitchRollYaw
*
* @param calibration_en 使0
* @param acc_gyro_input 6 [ACC_X, ACC_Y, ACC_Z, GYR_X, GYR_Y, GYR_Z]
@ -720,8 +787,6 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short
{
unsigned short acc_gyro_delta[2];
unsigned char sl_i = 0;
float angle_acc[3] = {0};
float gyro_val[3] = {0};
// 如果外部强制使能校准则将标志位置1
if (calibration_en == 0) {
@ -729,11 +794,8 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short
}
// =================================================================================
// 步骤 1: 静态校准
// 步骤 1: 静态校准 (与原版逻辑相同)
// ---------------------------------------------------------------------------------
// SL_SC7U22_Error_Flag 为0时表示需要进行校准。
// 校准的原理是:当传感器长时间处于静止状态时,认为其三轴加速度输出应为(0, 0, g),三轴角速度输出应为(0, 0, 0)。
// 通过采集一段时间的静止数据并求平均,可以得到传感器的零点偏移量。
if (SL_SC7U22_Error_Flag == 0) {
// 计算当前数据与上一帧数据的差值,用于判断是否静止
acc_gyro_delta[0] = 0;
@ -748,6 +810,7 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short
}
// 判断是否处于静止状态:加速度变化量、陀螺仪变化量、各轴加速度值都在一个很小的范围内
// 假设1g = 8192 (对应 +/-4g 量程)
if ((acc_gyro_delta[0] / 8 < 80) && (acc_gyro_delta[1] < 20) && (SL_GetAbsShort(acc_gyro_input[0]) < 3000) && (SL_GetAbsShort(acc_gyro_input[1]) < 3000) && (SL_GetAbsShort(acc_gyro_input[2] - 8192) < 3000)) { //acc<80mg gyro<20 lsb
if (SL_SC7U22_Error_cnt < 200) {
SL_SC7U22_Error_cnt++; // 静止计数器累加
@ -773,7 +836,7 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short
}
// 计算零点偏移:理想值 - 实际平均值
// 加速度Z轴的理想值是8192对应1g假设量程为±8g
// 加速度Z轴的理想值是8192对应1g假设量程为±4g
Error_Accgyro[0] = 0 - Sum_Avg_Accgyro[0];
Error_Accgyro[1] = 0 - Sum_Avg_Accgyro[1];
Error_Accgyro[2] = 8192 - Sum_Avg_Accgyro[2];
@ -797,7 +860,7 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short
}
// =================================================================================
// 步骤 2: 姿态解算
// 步骤 2: 姿态解算 (Madgwick)
// ---------------------------------------------------------------------------------
if (SL_SC7U22_Error_Flag == 1) { // 确认已经校准完成
// --- 2.1 数据预处理 ---
@ -811,209 +874,54 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short
acc_gyro_input[sl_i] = Temp_Accgyro[sl_i];
}
#endif
// --- 2.2 使用加速度计计算姿态角 ---
// 将加速度原始值转换为归一化的重力分量
angle_acc[0] = (float)Temp_Accgyro[0] / 8192; //ax
angle_acc[1] = (float)Temp_Accgyro[1] / 8192; //ay
angle_acc[2] = (float)Temp_Accgyro[2] / 8192; //az
// --- 2.2 转换数据单位 ---
// 将校准后的传感器原始值 (LSB) 转换为 Madgwick 算法所需的物理单位。
// 加速度: LSB -> g (重力加速度)。转换系数 = 量程 / (2^15)。假设 +/-4g 量程, 系数 = 4 / 32768 = 1/8192。
float ax = (float)Temp_Accgyro[0] / 8192.0f;
float ay = (float)Temp_Accgyro[1] / 8192.0f;
float az = (float)Temp_Accgyro[2] / 8192.0f;
// 角速度: LSB -> rad/s (弧度/秒)。转换系数 = (量程 * PI) / (180 * 2^15)。
// 假设 +/-2000dps 量程, 系数 = (2000 * 3.14159) / (180 * 32768) ≈ 0.001064
float gx = (float)Temp_Accgyro[3] * 0.001064f;
float gy = (float)Temp_Accgyro[4] * 0.001064f;
float gz = (float)Temp_Accgyro[5] * 0.001064f;
// 限制范围防止asinf/atanf计算错误
if (angle_acc[0] > 1.0) angle_acc[0] = 1.0;
if (angle_acc[0] < -1.0) angle_acc[0] = -1.0;
if (angle_acc[1] > 1.0) angle_acc[1] = 1.0;
if (angle_acc[1] < -1.0) angle_acc[1] = -1.0;
if (angle_acc[2] > 1.0) angle_acc[2] = 1.0;
if (angle_acc[2] < -1.0) angle_acc[2] = -1.0;
// --- 2.3 调用 Madgwick 更新函数 ---
// 将处理好的物理单位数据传入滤波器,更新姿态四元数。
MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az);
// 根据重力分量计算Pitch和Roll角单位
// Pitch = arcsin(ax / g)
angle_acc[0] = asinf(angle_acc[0]) * 57.32484; //Pitch:-90~+90
// Roll = -arctan(ay / az)
angle_acc[1] = -atanf(angle_acc[1] / angle_acc[2]) * 57.32484; //Roll: -180~+180
// Roll角进行象限补偿
if (angle_acc[2] < 0) {
if (angle_acc[1] >= 0) {
angle_acc[1] = -180 + angle_acc[1];
} else {
angle_acc[1] = 180 + angle_acc[1];
}
// --- 2.4 将四元数转换为欧拉角 ---
// 欧拉角(Pitch, Roll, Yaw更直观便于使用。转换公式如下。
// 转换结果单位为度 (乘以 180/PI ≈ 57.29578)。
float yaw, pitch, roll;
// Roll (横滚角绕x轴旋转)
roll = atan2f(2.0f * (q0 * q1 + q2 * q3), 1.0f - 2.0f * (q1 * q1 + q2 * q2)) * 57.29578f;
// Pitch (俯仰角绕y轴旋转)
float sinp = 2.0f * (q0 * q2 - q3 * q1);
if (fabsf(sinp) >= 1)
pitch = copysignf(3.14159265f / 2, sinp) * 57.29578f; // 防止万向节死锁当sinp接近+/-1时直接赋+/-90度
else
pitch = asinf(sinp) * 57.29578f;
// Yaw (偏航角绕z轴旋转)
yaw = atan2f(2.0f * (q0 * q3 + q1 * q2), 1.0f - 2.0f * (q2 * q2 + q3 * q3)) * 57.29578f;
// --- 2.5 处理Yaw轴重置 ---
// Yaw角无法通过加速度计校正会随时间漂移。提供一个重置机制将当前Yaw角作为新的零点。
if (yaw_rst) {
yaw_offset = yaw;
}
// --- 2.3 转换陀螺仪数据单位 ---
// 将陀螺仪原始值LSB转换为角速度度/秒)
// 转换系数0.061 ≈ 2000dps / 32768 LSB
gyro_val[0] = Temp_Accgyro[4] * 0.061; // GYR-Y -> Pitch
gyro_val[1] = Temp_Accgyro[3] * 0.061; // GYR-X -> Roll
gyro_val[2] = Temp_Accgyro[5] * 0.061; // GYR-Z -> Yaw
// =================================================================================
// 步骤 2.4: 卡尔曼滤波
// 对Pitch和Roll分别进行滤波
// ---------------------------------------------------------------------------------
/************** Pitch 轴滤波 **************/
// --- 预测步骤 ---
// 1. 预测状态:根据上一时刻的角度和当前角速度,预测当前角度
angle0[0] += (gyro_val[0] - q_bias0[0]) * dt;
// 2. 预测协方差更新P矩阵表示预测状态的不确定性
Pdot0[0] = Q_angle - P0[0][1] - P0[1][0] + P0[1][1] * dt;
Pdot0[1] = -P0[1][1];
Pdot0[2] = -P0[1][1];
Pdot0[3] = Q_gyro;
P0[0][0] += Pdot0[0] * dt;
P0[0][1] += Pdot0[1] * dt;
P0[1][0] += Pdot0[2] * dt;
P0[1][1] += Pdot0[3] * dt;
// --- 更新步骤 ---
// 1. 计算卡尔曼增益 K
PCt0_0[0] = C_0 * P0[0][0];
PCt0_1[0] = C_0 * P0[1][0];
E0[0] = R_angle + C_0 * PCt0_0[0];
if (E0[0] == 0) { E0[0] = 0.0001; } // 防止除零
K0_0[0] = PCt0_0[0] / E0[0];
K0_1[0] = PCt0_1[0] / E0[0];
// 2. 计算测量余差innovation
angle_err0[0] = angle_acc[0] - angle0[0];
// 3. 更新状态估计:结合预测值和测量值,得到最优估计
angle0[0] += K0_0[0] * angle_err0[0];
// 4. 更新陀螺仪偏置估计
q_bias0[0] += K0_1[0] * angle_err0[0];
angle_dot0[0] = gyro_val[0] - q_bias0[0];
// 5. 更新协方差矩阵 P
t0_0[0] = PCt0_0[0];
t0_1[0] = C_0 * P0[0][1];
P0[0][0] -= K0_0[0] * t0_0[0];
P0[0][1] -= K0_0[0] * t0_1[0];
P0[1][0] -= K0_1[0] * t0_0[0];
P0[1][1] -= K0_1[0] * t0_1[0];
// 输出最终的Pitch角
Angle_output[0] = angle0[0];
/************** Roll 轴滤波 (过程同Pitch) **************/
// --- 预测步骤 ---
angle0[1] += (gyro_val[1] - q_bias0[1]) * dt;
Pdot1[0] = Q_angle - P1[0][1] - P1[1][0] + P1[1][1] * dt;
Pdot1[1] = -P1[1][1];
Pdot1[2] = -P1[1][1];
Pdot1[3] = Q_gyro;
P1[0][0] += Pdot1[0] * dt;
P1[0][1] += Pdot1[1] * dt;
P1[1][0] += Pdot1[2] * dt;
P1[1][1] += Pdot1[3] * dt;
// --- 更新步骤 ---
PCt0_0[1] = C_1 * P1[0][0];
PCt0_1[1] = C_1 * P1[1][0];
E0[1] = R_angle + C_1 * PCt0_0[1];
if (E0[1] == 0) { E0[1] = 0.0001; }
K0_0[1] = PCt0_0[1] / E0[1];
K0_1[1] = PCt0_1[1] / E0[1];
angle_err0[1] = angle_acc[1] - angle0[1];
angle0[1] += K0_0[1] * angle_err0[1];
q_bias0[1] += K0_1[1] * angle_err0[1];
angle_dot0[1] = gyro_val[1] - q_bias0[1];
t0_0[1] = PCt0_0[1];
t0_1[1] = C_1 * P1[0][1];
P1[0][0] -= K0_0[1] * t0_0[1];
P1[0][1] -= K0_0[1] * t0_1[1];
P1[1][0] -= K0_1[1] * t0_0[1];
P1[1][1] -= K0_1[1] * t0_1[1];
// 输出最终的Roll角
Angle_output[1] = angle0[1];
/************** Yaw 轴计算 **************/
// Yaw角无法通过加速度计重力来校正因此这里只使用陀螺仪进行简单积分。
// 这种方法会因为陀螺仪的漂移而导致误差随时间累积。
if (yaw_rst == 1) {
Angle_output[2] = 0; // 如果有复位信号,则清零
}
// 增加一个简单的阈值,当角速度较小时,认为没有转动,以减少漂移
if (SL_GetAbsShort(Temp_Accgyro[5]) > 8) {
Angle_output[2] += gyro_val[2] * dt;
}
// --- 2.6 输出最终角度 ---
// 将计算出的欧拉角存入输出数组。
Angle_output[0] = pitch;
Angle_output[1] = roll;
Angle_output[2] = yaw - yaw_offset; // 输出减去偏移量的相对Yaw角
return 1; // 返回1表示计算成功
}
return 2; // 校准未完成,返回错误状态
}
#endif
/*
//-----------------------------------------
-by_lmx
//1.
// 定义用于存放传感器数据的缓冲区
static signed short acc_raw_data[3]; // [0]: acc_x, [1]: acc_y, [2]: acc_z
static signed short gyr_raw_data[3]; // [0]: gyr_x, [1]: gyr_y, [2]: gyr_z
static signed short combined_raw_data[6]; // 用于合并 acc 和 gyr 数据
static float final_angle_data[3]; // [0]: Pitch, [1]: Roll, [2]: Yaw
// 传感器数据处理任务
void sensor_processing_task(void *priv)
{
while (1) {
// 4. 周期性调用读取函数,获取原始数据
SL_SC7U22_RawData_Read(acc_raw_data, gyr_raw_data);
// 5. 合并加速度和角速度数据到一个数组中
// SL_SC7U22_Angle_Output 函数需要一个包含6个元素的数组作为输入
memcpy(&combined_raw_data[0], acc_raw_data, sizeof(acc_raw_data));
memcpy(&combined_raw_data[3], gyr_raw_data, sizeof(gyr_raw_data));
// 6. 调用姿态解算函数
// 参数: (校准使能, 输入的6轴数据, 输出的角度数据, Yaw轴复位标志)
// calibration_en = 1: 让函数内部自动管理校准过程
// yaw_rst = 0: 不复位Yaw角
unsigned char status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
// 7. 检查函数返回的状态
if (status == 1) {
// 计算成功final_angle_data 中的数据有效
printf("Pitch: %.2f, Roll: %.2f, Yaw: %.2f\n", final_angle_data[0], final_angle_data[1], final_angle_data[2]);
} else if (status == 0) {
// 传感器正在进行静态校准,此时角度数据可能不准确
printf("Sensor is calibrating...\n");
} else {
// status == 2, 表示校准未完成或发生错误
printf("Angle calculation error or calibration not finished.\n");
}
// 延时一段时间例如10ms (对应100Hz)
os_time_dly(1);
}
}
// 应用程序主入口或初始化函数
void app_main()
{
// ... 其他初始化代码 ...
// 2. 调用初始化函数来配置SCU722传感器
unsigned char init_success = SL_SC7U22_Config();
if (init_success) {
printf("SCU722 初始化成功!\n");
// 3. 创建一个任务来周期性地读取和处理数据
task_create(sensor_processing_task, NULL, "sensor_task");
} else {
printf("SCU722 初始化失败!\n");
}
// ...
}
//-----------------------------------------
*/

View File

@ -0,0 +1,181 @@
/*
气压计 - WF282A
*/
#include "wf282a.h"
#include <math.h>
#include <stdint.h> // 推荐使用标准类型
#include "gSensor/gSensor_manage.h"
/*==================================================================================*/
/* WF282A 内部定义 */
/*==================================================================================*/
// 存储校准系数的静态全局变量
static int16_t c0, c1, c01, c11, c20, c21, c30;
static int32_t c00, c10;
/*==================================================================================*/
/* 封装的底层I2C读写函数 */
/*==================================================================================*/
/**
* @brief 写入单个字节到WF282A寄存器
*/
static void wf282a_write_reg(uint8_t reg, uint8_t data) {
gravity_sensor_command(WF_IIC_WRITE_ADDRESS, reg, data);
}
/**
* @brief 从WF282A读取多个字节
*/
static uint32_t wf282a_read_regs(uint8_t reg, uint8_t *buf, uint8_t len) {
return _gravity_sensor_get_ndata(WF_IIC_READ_ADDRESS, reg, buf, len);
}
/*==================================================================================*/
/* 内部辅助函数 */
/*==================================================================================*/
/**
* @brief 从缓冲区中解析所有校准系数
* @param buf 包含从寄存器0x10开始读取的18个字节的校准数据
*/
static void parse_calibration_data(const uint8_t *buf) {
// c0 (12-bit)
c0 = ((int16_t)buf[0] << 4) | (buf[1] >> 4);
if (c0 & (1 << 11)) c0 |= 0xF000;
// c1 (12-bit)
c1 = (((int16_t)buf[1] & 0x0F) << 8) | buf[2];
if (c1 & (1 << 11)) c1 |= 0xF000;
// c00 (20-bit)
c00 = ((int32_t)buf[3] << 12) | ((int32_t)buf[4] << 4) | (buf[5] >> 4);
if (c00 & (1 << 19)) c00 |= 0xFFF00000;
// c10 (20-bit)
c10 = (((int32_t)buf[5] & 0x0F) << 16) | ((int32_t)buf[6] << 8) | buf[7];
if (c10 & (1 << 19)) c10 |= 0xFFF00000;
// c01, c11, c20, c21, c30 (16-bit)
c01 = (int16_t)((uint16_t)buf[8] << 8 | buf[9]);
c11 = (int16_t)((uint16_t)buf[10] << 8 | buf[11]);
c20 = (int16_t)((uint16_t)buf[12] << 8 | buf[13]);
c21 = (int16_t)((uint16_t)buf[14] << 8 | buf[15]);
c30 = (int16_t)((uint16_t)buf[16] << 8 | buf[17]);
}
/**
* @brief 获取原始温度值 (ADC)
*/
static int32_t Get_Traw() {
uint8_t buff[3];
int32_t Traw;
// 从 MSB 寄存器 WF_TMP_B2 (0x03) 开始连续读取3个字节
wf282a_read_regs(WF_TMP_B2, buff, 3);
// buff[0] = B2 (MSB), buff[1] = B1, buff[2] = B0 (LSB)
Traw = (int32_t)buff[0] << 16 | (int32_t)buff[1] << 8 | (int32_t)buff[2];
// 24位二进制补码转32位
if (Traw & (1 << 23)) {
Traw |= 0xFF000000;
}
return Traw;
}
/**
* @brief 获取原始气压值 (ADC)
*/
static int32_t Get_Praw() {
uint8_t buff[3];
int32_t Praw;
// 从 MSB 寄存器 WF_PRS_B2 (0x00) 开始连续读取3个字节
wf282a_read_regs(WF_PRS_B2, buff, 3);
// buff[0] = B2 (MSB), buff[1] = B1, buff[2] = B0 (LSB)
Praw = (int32_t)buff[0] << 16 | (int32_t)buff[1] << 8 | (int32_t)buff[2];
// 24位二进制补码转32位
if (Praw & (1 << 23)) {
Praw |= 0xFF000000;
}
return Praw;
}
/*==================================================================================*/
/* 4. 外部接口函数实现 */
/*==================================================================================*/
uint8_t WF_Init() {
uint8_t calib_buf[18];
uint8_t check_cfg;
// 1. 配置传感器工作模式
// 推荐配置压力8次过采样温度1次过采样测量速率16Hz
wf282a_write_reg(WF_PRS_CFG, (PM_RATE_16 << 4) | PM_PRC_8);
wf282a_write_reg(WF_TMP_CFG, (TMP_RATE_16 << 4) | TMP_PRC_1 | TMP_INT_SENSOR);
wf282a_write_reg(WF_MEAS_CFG, 0x07); // 启动连续压力和温度测量
wf282a_write_reg(WF_CFG_REG, 0x00); // 无中断或FIFO移位配置
// 2. 一次性读取所有校准系数 (从0x10到0x21共18字节)
if (wf282a_read_regs(COEF_C0, calib_buf, 18) != 0) {
return 2; // 读取校准数据失败
}
parse_calibration_data(calib_buf);
// 3. 检查配置是否写入成功
wf282a_read_regs(WF_MEAS_CFG, &check_cfg, 1);
if (check_cfg != 0x07) {
return 1; // 错误
} else {
return 0; // 成功
}
}
void WF_Sleep() {
wf282a_write_reg(WF_MEAS_CFG, 0x00); // 待机模式
}
void WF_Wakeup() {
wf282a_write_reg(WF_MEAS_CFG, 0x07); // 恢复连续测量
}
uint8_t WF_GetID() {
uint8_t id;
wf282a_read_regs(WF_ID_REG, &id, 1);
return id;
}
float WF_Temperature_Calculate() {
float Traw_sc;
int32_t Traw = Get_Traw();
Traw_sc = (float)Traw / KT; // 缩放原始温度值
return (float)c0 * 0.5f + (float)c1 * Traw_sc;
}
float WF_Pressure_Calculate() {
float Traw_sc, Praw_sc, Pcomp;
int32_t Traw = Get_Traw();
int32_t Praw = Get_Praw();
Traw_sc = (float)Traw / KT; // 缩放原始温度值
Praw_sc = (float)Praw / KP; // 缩放原始压力值
// 公式: 手册给出
Pcomp = (float)c00
+ Praw_sc * ((float)c10 + Praw_sc * ((float)c20 + Praw_sc * (float)c30))
+ Traw_sc * (float)c01
+ Traw_sc * Praw_sc * ((float)c11 + Praw_sc * (float)c21);
return Pcomp;
}
float WF_Altitude_Calculate() {
float pressure_pa = WF_Pressure_Calculate();
// 使用标准大气压公式计算海拔
// P = P0 * (1 - L*h / T0)^(g*M / (R*L))
// 简化公式: h = 44330 * (1 - (P/P0)^(1/5.255))
// 1/5.255 ≈ 0.1903
if (pressure_pa <= 0) {
return 0.0f; // 避免无效计算
}
return 44330.0f * (1.0f - powf(pressure_pa / 101325.0f, 0.1902949f));
}

View File

@ -0,0 +1,148 @@
#ifndef _WF282A_H_
#define _WF282A_H_
#include <stdint.h> // 使用标准整数类型
// 标定值
#define KT 524288.0f
#define KP 1572864.0f
#define WF_PULL_UP 1 //外部是否接的上拉
// I2C 从设备地址
#if WF_PULL_UP == 1 //外部接的高
#define WF_IIC_7BIT_ADDRESS 0x77 //7位,外部接高为0x77
#define WF_IIC_WRITE_ADDRESS (WF_IIC_7BIT_ADDRESS<<1) //8位地址
#define WF_IIC_READ_ADDRESS (WF_IIC_WRITE_ADDRESS | 0x01)
#else
#define WF_IIC_7BIT_ADDRESS 0x76 //7位,外部接低为0x76
#define WF_IIC_WRITE_ADDRESS (WF_IIC_7BIT_ADDRESS<<1) //8位地址
#define WF_IIC_READ_ADDRESS (WF_IIC_WRITE_ADDRESS | 0x01)
#endif
#define WF_CHIP_ID 0X10
// 寄存器映射
// 压力数据
#define WF_PRS_B2 0x00
#define WF_PRS_B1 0x01
#define WF_PRS_B0 0x02
// 温度数据
#define WF_TMP_B2 0x03
#define WF_TMP_B1 0x04
#define WF_TMP_B0 0x05
// 配置寄存器
#define WF_PRS_CFG 0x06
#define WF_TMP_CFG 0x07
#define WF_MEAS_CFG 0x08
#define WF_CFG_REG 0x09
#define WF_INT_STS 0x0A
#define WF_FIFO_STS 0x0B
#define WF_RESET_REG 0x0C
// ID寄存器
#define WF_ID_REG 0x0D
// 校准系数寄存器
#define COEF_C0 0x10
#define COEF_C0_C1 0x11
#define COEF_C1 0x12
#define COEF_C00_H 0x13
#define COEF_C00_L 0x14
#define COEF_C00_C10 0x15
#define COEF_C10_M 0x16
#define COEF_C10_L 0x17
#define COEF_C01_H 0x18
#define COEF_C01_L 0x19
#define COEF_C11_H 0x1A
#define COEF_C11_L 0x1B
#define COEF_C20_H 0x1C
#define COEF_C20_L 0x1D
#define COEF_C21_H 0x1E
#define COEF_C21_L 0x1F
#define COEF_C30_H 0x20
#define COEF_C30_L 0x21
// --- 配置宏 ---
// 压力配置 (PRS_CFG[6:4]) - 测量速率
#define PM_RATE_1 0x00 // 1 次/秒
#define PM_RATE_2 0x01 // 2 次/秒
#define PM_RATE_4 0x02 // 4 次/秒
#define PM_RATE_8 0x03 // 8 次/秒
#define PM_RATE_16 0x04 // 16 次/秒
#define PM_RATE_32 0x05 // 32 次/秒
#define PM_RATE_64 0x06 // 64 次/秒
#define PM_RATE_128 0x07 // 128 次/秒
// 压力配置 (PRS_CFG[3:0]) - 过采样率
#define PM_PRC_1 0x00 // 1 次 (单次)
#define PM_PRC_2 0x01 // 2 次 (低功耗)
#define PM_PRC_4 0x02 // 4 次
#define PM_PRC_8 0x03 // 8 次 (标准)
#define PM_PRC_16 0x04 // 16 次 (需要移位)
#define PM_PRC_32 0x05 // 32 次 (需要移位)
#define PM_PRC_64 0x06 // 64 次 (高精度, 需要移位)
#define PM_PRC_128 0x07 // 128 次 (需要移位)
// 温度配置 (TMP_CFG[7]) - 传感器源
#define TMP_EXT_SENSOR 0x80 // 使用外部传感器
#define TMP_INT_SENSOR 0x00 // 使用内部传感器
// 温度配置 (TMP_CFG[6:4]) - 测量速率
#define TMP_RATE_1 0x00 // 1 次/秒
#define TMP_RATE_2 0x01 // 2 次/秒
#define TMP_RATE_4 0x02 // 4 次/秒
#define TMP_RATE_8 0x03 // 8 次/秒
#define TMP_RATE_16 0x04 // 16 次/秒
#define TMP_RATE_32 0x05 // 32 次/秒
#define TMP_RATE_64 0x06 // 64 次/秒
#define TMP_RATE_128 0x07 // 128 次/秒
// 温度配置 (TMP_CFG[3:0]) - 过采样率
#define TMP_PRC_1 0x00 // 1 次
#define TMP_PRC_2 0x01 // 2 次
#define TMP_PRC_4 0x02 // 4 次
#define TMP_PRC_8 0x03 // 8 次
#define TMP_PRC_16 0x04 // 16 次
#define TMP_PRC_32 0x05 // 32 次
#define TMP_PRC_64 0x06 // 64 次
#define TMP_PRC_128 0x07 // 128 次
/**
* @brief 初始化WF282A传感器
* @return 0: 成功, 1: 失败
*/
uint8_t WF_Init(void);
/**
* @brief 使传感器进入休眠/待机模式
*/
void WF_Sleep(void);
/**
* @brief 唤醒传感器,开始连续测量
*/
void WF_Wakeup(void);
/**
* @brief 获取传感器芯片ID
* @return 芯片ID (应为 0x10)
*/
uint8_t WF_GetID(void);
/**
* @brief 计算并返回当前海拔高度
* @return 海拔高度 (单位: 米)
*/
float WF_Altitude_Calculate(void);
/**
* @brief 计算并返回补偿后的压力值
* @return 压力 (单位: Pa)
*/
float WF_Pressure_Calculate(void);
/**
* @brief 计算并返回补偿后的温度值
* @return 温度 (单位: °C)
*/
float WF_Temperature_Calculate(void);
#endif // _WF282A_H_

View File

@ -0,0 +1,10 @@
#ifndef XTELL_H
#define XTELL_H
#include "system/includes.h"
#include "generic/typedef.h"
// #define KS_BLE 1
#define XTELL_TEST 1
#define ACC_RANGE 16 //g加速度满量程:2、4、8、16
#endif

View File

@ -100,6 +100,10 @@ void close_BL(){
close_process(&close_BL_number,__func__);
}
void xtell_set_ble_name(char* name){
}
extern u32 timer_get_ms(void);
void xtell_app_main()
@ -145,13 +149,13 @@ void xtell_app_main()
//////////////////////////////////////////////////
//开机必须延时关闭经典蓝牙,不然底层代码会再次把蓝牙 打开
create_process(&close_BL_number, "close_BL",NULL, close_BL, 3000);
// create_process(&close_BL_number, "close_BL",NULL, close_BL, 3000);
soft_iic_init(0);
extern u8 LIS2DH12_Config(void);
LIS2DH12_Config();
u8 mac_data[6];
extern void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf);
rcsp_adv_fill_mac_addr(mac_data); //读取MAC地址
xlog("xtell BT mac data:%x:%x:%x:%x:%x:%x",mac_data[0],mac_data[1],mac_data[2],mac_data[3],mac_data[4],mac_data[5]);

View File

@ -44,7 +44,12 @@
#include "bt_background.h"
#include "default_event_handler.h"
#include "debug.h"
#include "system/event.h"
#include "./ano/ano_protocol.h"
#include "./sensor/MMC56.h"
#include "./sensor/BMP280.h"
#include "./sensor/AK8963.h"
#include "./calculate/skiing_tracker.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
//宏定义
#define LOG_TAG_CONST EARPHONE
@ -78,15 +83,31 @@ extern u8 init_ok;
extern u8 sniff_out;
unsigned char xtell_bl_state=0; //存放经典蓝牙的连接状态0断开1是连接
u8 bt_newname =0;
unsigned char xt_ble_new_name[9] = "CM-11111";
unsigned char xt_ble_new_name[9] = "xtell_1";
static u16 play_poweron_ok_timer_id = 0;
// -- 初始化标志位 --
u8 SC7U22_init = 0x10; //六轴是否初始化
u8 MMC5603nj_init = 0x20; //地磁是否初始化
u8 BMP280_init = 0x30; //气压计初始化
u8 foot_init = 0x40; //数据来源初始化左脚0x41 or 右脚0x42
// -- 线程id --
u16 gsensor_test_id = 0;
//
///////////////////////////////////////////////////////////////////////////////////////////////////
extern int bt_hci_event_handler(struct bt_event *bt);
extern void SC7U22_static_calibration(void);
extern void create_process(u16* pid, const char* name, void *priv, void (*func)(void *priv), u32 msec);
extern void close_process(u16* pid,char* name);
extern void start_collect_fuc(void);
extern void BLE_send_fuc(void);
extern void start_calibration(void);
extern void start_clloct(void);
extern void stop_clloct(void);
extern void set_foot_state(u8 state);
extern void stop_calibration(void);
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 模式状态机, 通过start_app()控制状态切换
@ -170,6 +191,239 @@ static int state_machine(struct application *app, enum app_state state, struct i
///////////////////////////////////////////////////////////////////////////////////////////////////
//handle
void le_user_app_event(u8* buffer){
if (buffer[0] == 0xBE && buffer[1] == 0xBB) {
if(buffer[2] == 0x01){ //后面的数据长度 1
switch (buffer[3]){
case 0x01:
// extern void gsensor_test(void);
// create_process(&gsensor_test_id,"gsensor_test",NULL,gsensor_test,1000);
xlog("ota_test");
cpu_reset();
break;
case 0xff: //测试
u8 device_buff[10];
u8 founds = 0;
extern void i2c_scanner_probe(u8* device_addr, u8* found_number);
i2c_scanner_probe(device_buff,&founds);
for(int i = 0;i < founds;i++){
send_data_to_ble_client(&device_buff,founds);
}
break;
case 0x02:
extern void test_func(void);
test_func();
break;
default:
break;
}
}else if(buffer[2] == 0x02){ //后面数据长度为2
switch (buffer[3]){ //数据包类型
case 0x00: //数据包类型为:指定传感器初始化
u8 send2_0[5] = {0xBB,0xBE,0x02,0x00,0x00};
if(buffer[4] == 0x01){ //六轴
// stop_calibration();
if (SL_SC7U22_Config() == 0) {
SC7U22_init = 0x10;
}else{
SC7U22_init = 0x11;
}
send2_0[4] = SC7U22_init;
send_data_to_ble_client(&send2_0,5);
// start_calibration();
}else if(buffer[4] == 0x02){ //地磁
if(mmc5603nj_init() == 0){
MMC5603nj_init = 0x20;
send2_0[4] = MMC5603nj_init; //地磁初始化失败
send_data_to_ble_client(&send2_0,5);
return;
}
MMC5603nj_init = 0x21;
send2_0[4] = MMC5603nj_init; //地磁初始化成功
send_data_to_ble_client(&send2_0,5);
}else if(buffer[4] == 0x03){ //气压计初始化
if(bmp280_init() != 0){
//初始化失败
BMP280_init = 0x30;
send2_0[4] = BMP280_init;
send_data_to_ble_client(&send2_0,5);
return;
}
BMP280_init = 0x31;
send2_0[4] = BMP280_init; //气压计初始化成功
send_data_to_ble_client(&send2_0,5);
}
break;
case 0x01: //设置传感器采集对象左脚or右脚
u8 send2_1[5] = {0xBB,0xBE,0x06,0x05,0x00};
if(buffer[4] == 0x01){ //设定数据来源是左脚
foot_init = 0x41;
}else if(buffer[4] == 0x02){//设定数据来源是右脚
foot_init = 0x42;
}
send2_1[4] = foot_init;
send_data_to_ble_client(&send2_1,9);
break;
case 0x02: //数据包类型为:获取指定传感器初始化状态
u8 send2_2[5] = {0xBB,0xBE,0x02,0x00,0x00};
if(buffer[4] == 0x01){ //六轴
send2_2[4] = SC7U22_init;
}else if(buffer[4] == 0x02){ //地磁
send2_2[4] = MMC5603nj_init;
}else if(buffer[4] == 0x03){ //气压计
send2_2[4] = BMP280_init;
}
send_data_to_ble_client(&send2_2,5);
break;
case 0x03: //开始/停止滑雪计算
if(buffer[4] == 0x01){ //开始滑雪计算
if(SC7U22_init == 0x10 || MMC5603nj_init == 0x20 || BMP280_init == 0x30){ //传感器未进行初始化
u8 send2_3[5] = {0xBB,0xBE,0x02,0x00,0x00};
send_data_to_ble_client(&send2_3,5);
return;
}
start_clloct();
}else if(buffer[4] == 0x02){ //停止滑雪计算
stop_clloct();
}
break;
}
}
}
}
void le_user_app_send_event(size_t command, unsigned char* data, size_t size)
{
// 中断->事件
static unsigned char buffer[512];
if(data && size && size <= sizeof(buffer)) {
// 拷贝到缓存,避免转发事件的时候,地址发送改变。
memcpy(buffer, data, size);
struct sys_event event;
event.type = SYS_APP_USER_EVENT;
event.u.app.command = command;
event.u.app.buffer = buffer;
event.u.app.size = size;
sys_event_notify(&event);
}
}
void le_user_app_event_handler(struct sys_event* event){
switch (event->type) {
// 打印接收到的数据
printf("BLE data\n");
put_buf(event->u.app.buffer, event->u.app.size);
case SYS_APP_USER_EVENT:
if (event->u.app.buffer[0] == 0xBE && event->u.app.buffer[1] == 0xBB) {
if(event->u.app.buffer[2] == 0x01){ //后面的数据长度 1
switch (event->u.app.buffer[3]){
case 0x01:
// extern void gsensor_test(void);
// create_process(&gsensor_test_id,"gsensor_test",NULL,gsensor_test,1000);
xlog("ota_test");
cpu_reset();
break;
case 0xff: //测试
u8 device_buff[10];
u8 founds = 0;
extern void i2c_scanner_probe(u8* device_addr, u8* found_number);
i2c_scanner_probe(device_buff,&founds);
for(int i = 0;i < founds;i++){
send_data_to_ble_client(&device_buff,founds);
}
break;
case 0x02:
extern void test_func(void);
test_func();
break;
default:
break;
}
}else if(event->u.app.buffer[2] == 0x02){ //后面数据长度为2
switch (event->u.app.buffer[3]){ //数据包类型
case 0x00: //数据包类型为:指定传感器初始化
u8 send2_0[5] = {0xBB,0xBE,0x02,0x00,0x00};
if(event->u.app.buffer[4] == 0x01){ //六轴
// stop_calibration();
if (SL_SC7U22_Config() == 0) {
SC7U22_init = 0x10; //初始化失败
}else{
SC7U22_init = 0x11;
}
send2_0[4] = SC7U22_init;
send_data_to_ble_client(&send2_0,5);
// start_calibration();
}else if(event->u.app.buffer[4] == 0x02){ //地磁
if(mmc5603nj_init() == 0){
MMC5603nj_init = 0x20;
send2_0[4] = MMC5603nj_init; //地磁初始化失败
send_data_to_ble_client(&send2_0,5);
return;
}
MMC5603nj_init = 0x21;
send2_0[4] = MMC5603nj_init; //地磁初始化成功
send_data_to_ble_client(&send2_0,5);
}else if(event->u.app.buffer[4] == 0x03){ //气压计初始化
if(bmp280_init() != 0){
//初始化失败
BMP280_init = 0x30;
send2_0[4] = BMP280_init;
send_data_to_ble_client(&send2_0,5);
return;
}
BMP280_init = 0x31;
send2_0[4] = BMP280_init; //气压计初始化成功
send_data_to_ble_client(&send2_0,5);
}
break;
case 0x01: //设置传感器采集对象左脚or右脚
u8 send2_1[9] = {0xBB,0xBE,0x06,0x05,0x00,0x00,0x00,0x00,0x00};
if(event->u.app.buffer[4] == 0x01){ //设定数据来源是左脚
foot_init = 0x41;
}else if(event->u.app.buffer[4] == 0x02){//设定数据来源是右脚
foot_init = 0x42;
}
send2_1[4] = foot_init;
send_data_to_ble_client(&send2_1,9);
break;
case 0x02: //数据包类型为:获取指定传感器初始化状态
u8 send2_2[5] = {0xBB,0xBE,0x02,0x00,0x00};
if(event->u.app.buffer[4] == 0x01){ //六轴
send2_2[4] = SC7U22_init;
}else if(event->u.app.buffer[4] == 0x02){ //地磁
send2_2[4] = MMC5603nj_init;
}else if(event->u.app.buffer[4] == 0x03){ //气压计
send2_2[4] = BMP280_init;
}
send_data_to_ble_client(&send2_2,5);
break;
case 0x03: //开始/停止滑雪计算
if(event->u.app.buffer[4] == 0x01){ //开始滑雪计算
if(SC7U22_init == 0x10 || MMC5603nj_init == 0x20 || BMP280_init == 0x30){ //传感器未进行初始化
u8 send2_3[5] = {0xBB,0xBE,0x02,0x00,0x00};
send_data_to_ble_client(&send2_3,5);
return;
}
start_clloct();
}else if(event->u.app.buffer[4] == 0x02){ //停止滑雪计算
stop_clloct();
}
break;
}
}
}
break;
default:
xlog("%d\n",event->type);
break;
}
}
static void play_poweron_ok_timer(void *priv)
{
@ -250,7 +504,7 @@ static int bt_connction_status_event_handler(struct bt_event *bt)
case BT_STATUS_FIRST_CONNECTED:
xlog("BT_STATUS_CONNECTED\n");
xtell_bl_state = 1; //蓝牙连接成功 置1
if(strcmp(xt_ble_new_name,"CM-1111") != 0){
if(strcmp(xt_ble_new_name,"CM-11111") != 0){
//蓝牙连接成功
bt_newname =1;
u8 temp[5]={0xBB,0xBE,0x02,0x04,0x00};
@ -355,9 +609,11 @@ static int bt_connction_status_event_handler(struct bt_event *bt)
}
static int event_handler(struct application *app, struct sys_event *event)
{
le_user_app_event_handler(event);
if (SYS_EVENT_REMAP(event)) {
g_printf("****SYS_EVENT_REMAP**** \n");
return 0;

View File

@ -1,259 +0,0 @@
#include "system/includes.h"
/*#include "btcontroller_config.h"*/
#include "btstack/btstack_task.h"
#include "app_config.h"
#include "app_action.h"
#include "asm/pwm_led.h"
#include "tone_player.h"
#include "gpio.h"
#include "app_main.h"
#include "asm/charge.h"
#include "update.h"
#include "app_power_manage.h"
#include "app_charge.h"
#include "bt_profile_cfg.h"
#include "dev_manager/dev_manager.h"
#include "update_loader_download.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
/* #define LOG_DUMP_ENABLE */
#define LOG_CLI_ENABLE
#include "debug.h"
#ifdef CONFIG_BOARD_AISPEECH_VAD_ASR
u8 user_at_cmd_send_support = 1;
#endif
/*任务列表 */
const struct task_info task_info_table[] = {
{"app_core", 1, 0, 768, 256 },
{"sys_event", 7, 0, 256, 0 },
{"systimer", 7, 0, 128, 0 },
{"btctrler", 4, 0, 512, 384 },
{"btencry", 1, 0, 512, 128 },
{"tws", 5, 0, 512, 128 },
#if (BT_FOR_APP_EN)
{"btstack", 3, 0, 1024, 256 },
#else
{"btstack", 3, 0, 768, 256 },
#endif
{"audio_dec", 5, 0, 800, 128 },
{"aud_effect", 5, 1, 800, 128 },
/*
*为了防止dac buf太大通话一开始一直解码
*导致编码输入数据需要很大的缓存,这里提高编码的优先级
*/
{"audio_enc", 6, 0, 768, 128 },
{"aec", 2, 1, 768, 128 },
#if TCFG_AUDIO_HEARING_AID_ENABLE
{"HearingAid", 6, 0, 768, 128 },
#endif/*TCFG_AUDIO_HEARING_AID_ENABLE*/
#ifdef CONFIG_BOARD_AISPEECH_NR
{"aispeech_enc", 2, 1, 512, 128 },
#endif /*CONFIG_BOARD_AISPEECH_NR*/
#ifdef CONFIG_BOARD_AISPEECH_VAD_ASR
{"asr", 1, 0, 768, 128 },
{"audio_asr_export_task", 1, 0, 512, 128 },
#endif/*CONFIG_BOARD_AISPEECH_VAD_ASR*/
#ifndef CONFIG_256K_FLASH
{"aec_dbg", 3, 0, 128, 128 },
#if AUDIO_ENC_MPT_SELF_ENABLE
{"enc_mpt_self", 3, 0, 512, 128 },
#endif/*AUDIO_ENC_MPT_SELF_ENABLE*/
{"update", 1, 0, 256, 0 },
{"tws_ota", 2, 0, 256, 0 },
{"tws_ota_msg", 2, 0, 256, 128 },
{"dw_update", 2, 0, 256, 128 },
{"rcsp_task", 2, 0, 640, 128 },
{"aud_capture", 4, 0, 512, 256 },
{"data_export", 5, 0, 512, 256 },
{"anc", 3, 1, 512, 128 },
#endif
#if TCFG_GX8002_NPU_ENABLE
{"gx8002", 2, 0, 256, 64 },
#endif /* #if TCFG_GX8002_NPU_ENABLE */
#if TCFG_GX8002_ENC_ENABLE
{"gx8002_enc", 2, 0, 128, 64 },
#endif /* #if TCFG_GX8002_ENC_ENABLE */
#if TCFG_KWS_VOICE_RECOGNITION_ENABLE
{"kws", 2, 0, 256, 64 },
#endif /* #if TCFG_KWS_VOICE_RECOGNITION_ENABLE */
{"usb_msd", 1, 0, 512, 128 },
#if !TCFG_USB_MIC_CVP_ENABLE
{"usbmic_write", 2, 0, 256, 128 },
#endif
#if AI_APP_PROTOCOL
{"app_proto", 2, 0, 768, 64 },
#endif
#if (TCFG_SPI_LCD_ENABLE||TCFG_SIMPLE_LCD_ENABLE)
{"ui", 2, 0, 768, 256 },
#else
{"ui", 3, 0, 384 - 64, 128 },
#endif
#if (TCFG_DEV_MANAGER_ENABLE)
{"dev_mg", 3, 0, 512, 512 },
#endif
{"audio_vad", 1, 1, 512, 128 },
#if TCFG_KEY_TONE_EN
{"key_tone", 5, 0, 256, 32 },
#endif
#if (TCFG_WIRELESS_MIC_ENABLE)
{"wl_mic_enc", 2, 0, 768, 128 },
#endif
#if (TUYA_DEMO_EN)
{"user_deal", 7, 0, 512, 512 },//定义线程 tuya任务调度
{"dw_update", 2, 0, 256, 128 },
#endif
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
{"imu_trim", 1, 0, 256, 128 },
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
#if TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN
{"speak_to_chat", 2, 0, 256, 128 },
{"icsd_adt", 2, 0, 512, 128 },
{"icsd_src", 2, 1, 512, 128 },
#endif /*TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN*/
{"pmu_task", 6, 0, 256, 128 },
{"WindDetect", 2, 0, 256, 128 },
{0, 0},
};
APP_VAR app_var;
/*
* 2ms timer中断回调函数
*/
void timer_2ms_handler()
{
}
void app_var_init(void)
{
memset((u8 *)&bt_user_priv_var, 0, sizeof(BT_USER_PRIV_VAR));
app_var.play_poweron_tone = 1;
}
void app_earphone_play_voice_file(const char *name);
void clr_wdt(void);
void check_power_on_key(void)
{
u32 delay_10ms_cnt = 0;
#if 0 //PC_MODE_DETECTION
gpio_set_pull_up(IO_PORTP_00, 0);
gpio_set_pull_down(IO_PORTP_00, 1);
gpio_set_direction(IO_PORTP_00, 1);
gpio_set_die(IO_PORTP_00, 1);
#endif
while (1) {
clr_wdt();
os_time_dly(1);
extern u8 get_power_on_status(void);
if (get_power_on_status()) {
log_info("+");
delay_10ms_cnt++;
if (delay_10ms_cnt > 70) {
/* extern void set_key_poweron_flag(u8 flag); */
/* set_key_poweron_flag(1); */
return;
}
} else {
log_info("-");
delay_10ms_cnt = 0;
log_info("enter softpoweroff\n");
power_set_soft_poweroff();
}
}
}
extern int cpu_reset_by_soft();
extern int audio_dec_init();
extern int audio_enc_init();
__attribute__((weak))
u8 get_charge_online_flag(void)
{
return 0;
}
/*充电拔出,CPU软件复位, 不检测按键,直接开机*/
static void app_poweron_check(int update)
{
#if (CONFIG_BT_MODE == BT_NORMAL)
if (!update && cpu_reset_by_soft()) {
app_var.play_poweron_tone = 0;
return;
}
#if TCFG_CHARGE_OFF_POWERON_NE
if (is_ldo5v_wakeup()) {
app_var.play_poweron_tone = 0;
return;
}
#endif
//#ifdef CONFIG_RELEASE_ENABLE
#if TCFG_POWER_ON_NEED_KEY
check_power_on_key();
#endif
//#endif
#endif
}
extern u32 timer_get_ms(void);
void app_main()
{
int update = 0;
u32 addr = 0, size = 0;
struct intent it;
if (!UPDATE_SUPPORT_DEV_IS_NULL()) {
update = update_result_deal();
}
app_var_init();
// if (get_charge_online_flag()) {
#if(TCFG_SYS_LVD_EN == 1)
vbat_check_init();
#endif
// init_intent(&it);
// it.name = "idle";
// it.action = ACTION_IDLE_MAIN;
// start_app(&it);
// } else {
check_power_on_voltage();
app_poweron_check(update);
init_intent(&it);
it.name = "handler";
it.action = ACTION_EARPHONE_MAIN;
start_app(&it);
// }
log_info("app_main\n");
app_var.start_time = timer_get_ms();
}

View File

@ -1,66 +0,0 @@
#include "app_config.h"
SECTIONS
{
.text : ALIGN(4)
{
gsensor_dev_begin = .;
KEEP(*(.gsensor_dev))
gsensor_dev_end = .;
fm_dev_begin = .;
KEEP(*(.fm_dev))
fm_dev_end = .;
fm_emitter_dev_begin = .;
KEEP(*(.fm_emitter_dev))
fm_emitter_dev_end = .;
storage_device_begin = .;
KEEP(*(.storage_device))
storage_device_end = .;
imusensor_dev_begin = .;
KEEP(*(.imusensor_dev))
imusensor_dev_end = .;
#if TCFG_APP_PC_EN
aac_dec_code_begin = .;
*(.bt_aac_dec_code)
*(.bt_aac_dec_sparse_code)
aac_dec_code_end = .;
aac_dec_code_size = aac_dec_code_end - aac_dec_code_begin ;
. = ALIGN(4);
bt_aac_dec_const_begin = .;
*(.bt_aac_dec_const)
*(.bt_aac_dec_sparse_const)
bt_aac_dec_const_end = .;
bt_aac_dec_const_size = bt_aac_dec_const_end - bt_aac_dec_const_begin ;
*(.bt_aac_dec_data)
*(.bt_aac_dec_bss)
. = ALIGN(4);
*(.aac_mem)
*(.aac_ctrl_mem)
/* . += 0x5fe8 ; */
/* . += 0xef88 ; */
#endif
. = ALIGN(32);
}
.data ALIGN(32):
{
} > ram0
.bss ALIGN(32):
{
} > ram0
.data_code ALIGN(32):
{
} > ram0
}

View File

@ -1,164 +0,0 @@
OVERLAY : AT(0x200000) SUBALIGN(4)
{
.overlay_aec
{
aec_code_begin = . ;
*(.text._*)
*(.data._*)
*(.aec_code)
*(.aec_const)
*(.res_code)
*(.res_const)
*(.ns_code)
*(.ns_const)
*(.bark_const)
*(.fft_code)
*(.fft_const)
*(.agc_code)
*(.dms_code)
*(.dms_const)
*(.dms_sparse_code)
aec_code_end = . ;
aec_code_size = aec_code_end - aec_code_begin ;
*(.msbc_enc)
*(.cvsd_codec)
*(.aec_bss)
*(.aec_data)
*(.res_data)
*(.ns_data)
*(.dns_common_data)
*(.dns_param_data_single)
*(.dns_param_data_dual)
*(.jlsp_nlp_code)
*(.jlsp_nlp_const)
*(.jlsp_aec_code)
*(.jlsp_aec_const)
*(.jlsp_prep_code)
*(.jlsp_prep_const)
*(.jlsp_enc_code)
*(.jlsp_enc_const)
*(.jlsp_wn_code)
*(.jlsp_wn_const)
*(.jlsp_tri_code)
*(.jlsp_tri_const)
*(.jlsp_agc_code)
*(.jlsp_agc_const)
*(.res_bss)
*(.ns_bss)
*(.aec_mem)
}
.overlay_aac
{
#if !TCFG_APP_PC_EN
aac_dec_code_begin = .;
*(.bt_aac_dec_code)
*(.bt_aac_dec_sparse_code)
aac_dec_code_end = .;
aac_dec_code_size = aac_dec_code_end - aac_dec_code_begin ;
. = ALIGN(4);
bt_aac_dec_const_begin = .;
*(.bt_aac_dec_const)
*(.bt_aac_dec_sparse_const)
bt_aac_dec_const_end = .;
bt_aac_dec_const_size = bt_aac_dec_const_end - bt_aac_dec_const_begin ;
*(.bt_aac_dec_data)
*(.bt_aac_dec_bss)
. = ALIGN(4);
*(.aac_mem)
*(.aac_ctrl_mem)
/* . += 0x5fe8 ; */
/* . += 0xef88 ; */
#endif
}
/*
.overlay_lc3
{
lc3_dec_code_begin = .;
*(.lc3_dec_code)
lc3_dec_code_end = .;
lc3_dec_code_size = lc3_dec_code_end - lc3_dec_code_begin;
. = ALIGN(4);
lc3_dec_const_begin = .;
*(.lc3_dec_const)
lc3_dec_const_end = .;
lc3_dec_const_size = lc3_dec_const_end - lc3_dec_const_begin;
*(.lc3_dec_data)
*(.lc3_dec_bss)
}
*/
.overlay_mp3
{
*(.mp3_mem)
*(.mp3_ctrl_mem)
*(.mp3pick_mem)
*(.mp3pick_ctrl_mem)
*(.dec2tws_mem)
}
.overlay_wma
{
*(.wma_mem)
*(.wma_ctrl_mem)
*(.wmapick_mem)
*(.wmapick_ctrl_mem)
}
.overlay_wav
{
*(.wav_mem)
*(.wav_ctrl_mem)
}
.overlay_ape
{
*(.ape_mem)
*(.ape_ctrl_mem)
}
.overlay_flac
{
*(.flac_mem)
*(.flac_ctrl_mem)
}
.overlay_m4a
{
*(.m4a_mem)
*(.m4a_ctrl_mem)
}
.overlay_amr
{
*(.amr_mem)
*(.amr_ctrl_mem)
}
.overlay_dts
{
*(.dts_mem)
*(.dts_ctrl_mem)
}
.overlay_fm
{
*(.fm_mem)
}
.overlay_pc
{
*(.usb_audio_play_dma)
*(.usb_audio_rec_dma)
*(.uac_rx)
*(.mass_storage)
*(.usb_ep0)
*(.usb_msd_dma)
*(.usb_hid_dma)
*(.usb_iso_dma)
*(.usb_cdc_dma)
*(.uac_var)
*(.usb_config_var)
*(.cdc_var)
}
} > ram0

View File

@ -1,27 +0,0 @@
#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H
/*
* 板级配置选择
*/
#define CONFIG_BOARD_JL701N_DEMO //编译正常 2025-4-29
// #define CONFIG_BOARD_JL701N_BTEMITTER
// #define CONFIG_BOARD_JL701N_ANC
// #define CONFIG_BOARD_JL7016G_HYBRID
// #define CONFIG_BOARD_JL7018F_DEMO //编译不过 2025-4-29
#include "media/audio_def.h"
#include "board_jl701n_demo_cfg.h"
#include "board_jl701n_btemitter_cfg.h"
#include "board_jl701n_anc_cfg.h"
#include "board_jl7016g_hybrid_cfg.h"
#include "board_jl7018f_demo_cfg.h"
#define DUT_AUDIO_DAC_LDO_VOLT DACVDD_LDO_1_25V
#ifdef CONFIG_NEW_CFG_TOOL_ENABLE
#define CONFIG_ENTRY_ADDRESS 0x6000100
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,123 +0,0 @@
#ifndef CONFIG_BOARD_JL7016G_HYBRID_POST_BUILD_CFG_H
#define CONFIG_BOARD_JL7016G_HYBRID_POST_BUILD_CFG_H
/* 改文件只添加和isd_config.ini相关的配置用以生成isd_config.ini */
/* 其他不相关的配置请勿添加在改文件 */
#ifdef CONFIG_BOARD_JL7016G_HYBRID
/* Following Macros Affect Periods Of Both Code Compiling And Post-build */
#define CONFIG_DOUBLE_BANK_ENABLE 0 //单双备份选择(若打开了改宏,FLASH结构变为双备份结构适用于接入第三方协议的OTA PS: JL-OTA同样支持双备份升级, 需要根据实际FLASH大小同时配置CONFIG_FLASH_SIZE)
#define CONFIG_APP_OTA_ENABLE 0 //是否支持RCSP升级(JL-OTA)
#define CONFIG_UPDATE_JUMP_TO_MASK 0 //配置升级到loader的方式0为直接reset,1为跳转(适用于芯片电源由IO口KEEP住的方案,需要注意检查跳转前是否将使用DMA的硬件模块全部关闭)
#define CONFIG_IO_KEY_EN 0 //配置是否使用IO按键配合RESET1
#define CONFIG_UPDATE_WITH_MD5_CHECK_EN 0 //配置升级是否支持MD5校验
#define CONFIG_ANC_ENABLE 1 //配置是否支持ANC
//flash size vaule definition
#define FLASH_SIZE_256K 0x40000
#define FLASH_SIZE_512K 0x80000
#define FLASH_SIZE_1M 0x100000
#define FLASH_SIZE_2M 0x200000
#define FLASH_SIZE_4M 0x400000
#define CONFIG_FLASH_SIZE FLASH_SIZE_1M //配置FLASH大小
/* Above Macros Affect Periods Of Both Code Compiling And Post-build */
/* Following Macros Only For Post Bulid Configuaration */
#define CONFIG_DB_UPDATE_DATA_GENERATE_EN 0 //是否生成db_data.bin(用于第三方协议接入使用)
#define CONFIG_ONLY_GRENERATE_ALIGN_4K_CODE 0 //ufw只生成1份4K对齐的代码
//config for supported chip version
#ifdef CONFIG_BR30_C_VERSION
#define CONFIG_SUPPORTED_CHIP_VERSION C
#else
#define CONFIG_SUPPORTED_CHIP_VERSION B,D,E,M,N,O,P
#endif
//DON'T MODIFY THIS CONFIG EXCEPT SDK PUBLISHER
#define CONFIG_CHIP_NAME AC701N //除了SDK发布者,请不要修改
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_PID AC701N //烧写或强制升级之前可以修改,之后升级要保持一致
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_VID 0.01 //烧写或强制升级之前可以修改,之后升级要保持一致
//Project with bluetooth,it must use OSC as PLL_SOURCE;
#define CONFIG_PLL_SOURCE_USING_LRC 0 //PLL时钟源选择 1:LRC 2:OSC
//config alignment size unit
#ifdef CONFIG_256K_FLASH
#define ALIGN_UNIT_256B 1 //FLASH对齐方式选择如果是256K的FLASH选择256BYTE对齐方式
#else
#define ALIGN_UNIT_256B 0
#endif
//partial platform check this config to select the uart IO for wired update
#define CONFIG_UART_UPDATE_PIN PP00
//isd_download loader/uboot/update_loader debug io config
//#define CONFIG_UBOOT_DEBUG_PIN PA05
//#define CONFIG_UBOOT_DEBUG_BAUD_RATE 1000000
//config long-press reset io pin,time,trigger level
#define CONFIG_RESET_PIN LDO //io pin
#define CONFIG_RESET_TIME 04 //unit:second
#define CONFIG_RESET_LEVEL 1 //tigger level(0/1)
#if CONFIG_IO_KEY_EN
#define CONFIG_SUPPORT_RESET1
#define CONFIG_RESET1_PIN PB01 //io pin
#define CONFIG_RESET1_TIME 08 //unit:second
#define CONFIG_RESET1_LEVEL 0 //tigger level(0/1)
#endif
//reserved three custom cfg item for the future definition
//#define CONFIG_CUSTOM_CFG1_TYPE POWER_PIN
//#define CONFIG_CUSTOM_CFG1_VALUE PC01_1
//#define CONFIG_CUSTOM_CFG2_TYPE
//#define CONFIG_CUSTOM_CFG2_VALUE
//#define CONFIG_CUSTOM_CFG3_TYPE
//#define CONFIG_CUSTOM_CFG3_VALUE
//#define CONFIG_VDDIO_LVD_LEVEL 4 ////VDDIO_LVD挡位0: 1.9V 1: 2.0V 2: 2.1V 3: 2.2V 4: 2.3V 5: 2.4V 6: 2.5V 7: 2.6V
//with single-bank mode,actual vm size should larger this VM_LEAST_SIZE,and dual bank mode,actual vm size equals this;
#define CONFIG_VM_LEAST_SIZE 8K
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_VM_OPT 1
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_BTIF_OPT 1
//reserved two custom cfg area for the future definition
//#define CONFIG_RESERVED_AREA1 EXIF1
#ifdef CONFIG_RESERVED_AREA1
#define CONFIG_RESERVED_AREA1_ADDR AUTO
#define CONFIG_RESERVED_AREA1_LEN 0x1000
#define CONFIG_RESERVED_AREA1_OPT 1
//#define CONFIG_RESERVED_AREA1_FILE anc_gains.bin
#endif
//#define CONFIG_RESERVED_AREA2 EXIF2
#ifdef CONFIG_RESERVED_AREA2
#define CONFIG_RESERVED_AREA2_ADDR AUTO
#define CONFIG_RESERVED_AREA2_LEN 0x1000
#define CONFIG_RESERVED_AREA2_OPT 1
//#define CONFIG_RESERVED_AREA2_FILE anc_gains.bin
#endif
/* Above Macros Only For Post Bulid Configuaration */
#endif /* #ifdef CONFIG_BOARD_JL7016G_HYBRID */
#endif /* #ifndef CONFIG_BOARD_JL701N_ANC_POST_BUILD_CFG_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,123 +0,0 @@
#ifndef CONFIG_BOARD_JL7018F_DEMO_POST_BUILD_CFG_H
#define CONFIG_BOARD_JL7018F_DEMO_POST_BUILD_CFG_H
/* 改文件只添加和isd_config.ini相关的配置用以生成isd_config.ini */
/* 其他不相关的配置请勿添加在改文件 */
#ifdef CONFIG_BOARD_JL7018F_DEMO
/* Following Macros Affect Periods Of Both Code Compiling And Post-build */
#define CONFIG_DOUBLE_BANK_ENABLE 0 //单双备份选择(若打开了改宏,FLASH结构变为双备份结构适用于接入第三方协议的OTA PS: JL-OTA同样支持双备份升级, 需要根据实际FLASH大小同时配置CONFIG_FLASH_SIZE)
#define CONFIG_APP_OTA_ENABLE 0 //是否支持RCSP升级(JL-OTA)
#define CONFIG_UPDATE_JUMP_TO_MASK 0 //配置升级到loader的方式0为直接reset,1为跳转(适用于芯片电源由IO口KEEP住的方案,需要注意检查跳转前是否将使用DMA的硬件模块全部关闭)
#define CONFIG_IO_KEY_EN 0 //配置是否使用IO按键配合RESET1
#define CONFIG_UPDATE_WITH_MD5_CHECK_EN 0 //配置升级是否支持MD5校验
#define CONFIG_ANC_ENABLE 1 //配置是否支持ANC
//flash size vaule definition
#define FLASH_SIZE_256K 0x40000
#define FLASH_SIZE_512K 0x80000
#define FLASH_SIZE_1M 0x100000
#define FLASH_SIZE_2M 0x200000
#define FLASH_SIZE_4M 0x400000
#define CONFIG_FLASH_SIZE FLASH_SIZE_1M //配置FLASH大小
/* Above Macros Affect Periods Of Both Code Compiling And Post-build */
/* Following Macros Only For Post Bulid Configuaration */
#define CONFIG_DB_UPDATE_DATA_GENERATE_EN 0 //是否生成db_data.bin(用于第三方协议接入使用)
#define CONFIG_ONLY_GRENERATE_ALIGN_4K_CODE 0 //ufw只生成1份4K对齐的代码
//config for supported chip version
#ifdef CONFIG_BR30_C_VERSION
#define CONFIG_SUPPORTED_CHIP_VERSION C
#else
#define CONFIG_SUPPORTED_CHIP_VERSION B,D,E,M,N,O,P
#endif
//DON'T MODIFY THIS CONFIG EXCEPT SDK PUBLISHER
#define CONFIG_CHIP_NAME AC701N //除了SDK发布者,请不要修改
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_PID AC701N //烧写或强制升级之前可以修改,之后升级要保持一致
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_VID 0.01 //烧写或强制升级之前可以修改,之后升级要保持一致
//Project with bluetooth,it must use OSC as PLL_SOURCE;
#define CONFIG_PLL_SOURCE_USING_LRC 0 //PLL时钟源选择 1:LRC 2:OSC
//config alignment size unit
#ifdef CONFIG_256K_FLASH
#define ALIGN_UNIT_256B 1 //FLASH对齐方式选择如果是256K的FLASH选择256BYTE对齐方式
#else
#define ALIGN_UNIT_256B 0
#endif
//partial platform check this config to select the uart IO for wired update
#define CONFIG_UART_UPDATE_PIN PP00
//isd_download loader/uboot/update_loader debug io config
//#define CONFIG_UBOOT_DEBUG_PIN PA05
//#define CONFIG_UBOOT_DEBUG_BAUD_RATE 1000000
//config long-press reset io pin,time,trigger level
#define CONFIG_RESET_PIN LDO //io pin
#define CONFIG_RESET_TIME 04 //unit:second
#define CONFIG_RESET_LEVEL 1 //tigger level(0/1)
#if CONFIG_IO_KEY_EN
#define CONFIG_SUPPORT_RESET1
#define CONFIG_RESET1_PIN PB01 //io pin
#define CONFIG_RESET1_TIME 08 //unit:second
#define CONFIG_RESET1_LEVEL 0 //tigger level(0/1)
#endif
//reserved three custom cfg item for the future definition
//#define CONFIG_CUSTOM_CFG1_TYPE POWER_PIN
//#define CONFIG_CUSTOM_CFG1_VALUE PC01_1
//#define CONFIG_CUSTOM_CFG2_TYPE
//#define CONFIG_CUSTOM_CFG2_VALUE
//#define CONFIG_CUSTOM_CFG3_TYPE
//#define CONFIG_CUSTOM_CFG3_VALUE
//#define CONFIG_VDDIO_LVD_LEVEL 4 ////VDDIO_LVD挡位0: 1.9V 1: 2.0V 2: 2.1V 3: 2.2V 4: 2.3V 5: 2.4V 6: 2.5V 7: 2.6V
//with single-bank mode,actual vm size should larger this VM_LEAST_SIZE,and dual bank mode,actual vm size equals this;
#define CONFIG_VM_LEAST_SIZE 8K
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_VM_OPT 1
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_BTIF_OPT 1
//reserved two custom cfg area for the future definition
//#define CONFIG_RESERVED_AREA1 EXIF1
#ifdef CONFIG_RESERVED_AREA1
#define CONFIG_RESERVED_AREA1_ADDR AUTO
#define CONFIG_RESERVED_AREA1_LEN 0x1000
#define CONFIG_RESERVED_AREA1_OPT 1
//#define CONFIG_RESERVED_AREA1_FILE anc_gains.bin
#endif
//#define CONFIG_RESERVED_AREA2 EXIF2
#ifdef CONFIG_RESERVED_AREA2
#define CONFIG_RESERVED_AREA2_ADDR AUTO
#define CONFIG_RESERVED_AREA2_LEN 0x1000
#define CONFIG_RESERVED_AREA2_OPT 1
//#define CONFIG_RESERVED_AREA2_FILE anc_gains.bin
#endif
/* Above Macros Only For Post Bulid Configuaration */
#endif /* #ifdef CONFIG_BOARD_JL7018F_DEMO */
#endif /* #ifndef CONFIG_BOARD_JL7018F_DEMO_POST_BUILD_CFG_H */

View File

@ -1,992 +0,0 @@
#include "app_config.h"
#ifdef CONFIG_BOARD_JL701N_ANC
#include "system/includes.h"
#include "media/includes.h"
#include "asm/sdmmc.h"
#include "asm/chargestore.h"
#include "asm/umidigi_chargestore.h"
#include "asm/charge.h"
#include "asm/pwm_led.h"
#include "tone_player.h"
#include "audio_config.h"
#include "gSensor/gSensor_manage.h"
#include "key_event_deal.h"
#include "asm/lp_touch_key_api.h"
#include "user_cfg.h"
#include "norflash_sfc.h"
#include "asm/power/power_port.h"
#include "app_umidigi_chargestore.h"
#include "audio_link.h"
#if TCFG_AUDIO_ANC_ENABLE
#include "audio_anc.h"
#endif/*TCFG_AUDIO_ANC_ENABLE*/
#define LOG_TAG_CONST BOARD
#define LOG_TAG "[BOARD]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
/* #define LOG_DUMP_ENABLE */
#define LOG_CLI_ENABLE
#include "debug.h"
void board_power_init(void);
/*各个状态下默认的闪灯方式和提示音设置如果USER_CFG中设置了USE_CONFIG_STATUS_SETTING为1则会从配置文件读取对应的配置来填充改结构体*/
STATUS_CONFIG status_config = {
//灯状态设置
.led = {
.charge_start = PWM_LED1_ON,
.charge_full = PWM_LED0_ON,
.power_on = PWM_LED0_ON,
.power_off = PWM_LED1_FLASH_THREE,
.lowpower = PWM_LED1_SLOW_FLASH,
.max_vol = PWM_LED_NULL,
.phone_in = PWM_LED_NULL,
.phone_out = PWM_LED_NULL,
.phone_activ = PWM_LED_NULL,
.bt_init_ok = PWM_LED0_LED1_SLOW_FLASH,
.bt_connect_ok = PWM_LED0_ONE_FLASH_5S,
.bt_disconnect = PWM_LED0_LED1_FAST_FLASH,
.tws_connect_ok = PWM_LED0_LED1_FAST_FLASH,
.tws_disconnect = PWM_LED0_LED1_SLOW_FLASH,
},
//提示音设置
.tone = {
.charge_start = IDEX_TONE_NONE,
.charge_full = IDEX_TONE_NONE,
.power_on = IDEX_TONE_POWER_ON,
.power_off = IDEX_TONE_POWER_OFF,
.lowpower = IDEX_TONE_LOW_POWER,
.max_vol = IDEX_TONE_MAX_VOL,
.phone_in = IDEX_TONE_NONE,
.phone_out = IDEX_TONE_NONE,
.phone_activ = IDEX_TONE_NONE,
.bt_init_ok = IDEX_TONE_BT_MODE,
.bt_connect_ok = IDEX_TONE_BT_CONN,
.bt_disconnect = IDEX_TONE_BT_DISCONN,
.tws_connect_ok = IDEX_TONE_TWS_CONN,
.tws_disconnect = IDEX_TONE_TWS_DISCONN,
}
};
#define __this (&status_config)
/************************** KEY MSG****************************/
/*各个按键的消息设置如果USER_CFG中设置了USE_CONFIG_KEY_SETTING为1则会从配置文件读取对应的配置来填充改结构体*/
#if CLIENT_BOARD == CUSTOM10_CFG
u8 key_table[KEY_NUM_MAX][KEY_EVENT_MAX] = {
// SHORT LONG HOLD UP DOUBLE TRIPLE
{KEY_MUSIC_PP, KEY_NULL, KEY_NULL, KEY_NULL, KEY_ANC_SWITCH, KEY_NULL}, //KEY_0
{KEY_MUSIC_NEXT, KEY_VOL_UP, KEY_VOL_UP, KEY_NULL, KEY_OPEN_SIRI, KEY_NULL}, //KEY_1
{KEY_MUSIC_PREV, KEY_VOL_DOWN, KEY_VOL_DOWN, KEY_NULL, KEY_HID_CONTROL, KEY_NULL}, //KEY_2
};
#else
u8 key_table[KEY_NUM_MAX][KEY_EVENT_MAX] = {
// SHORT LONG HOLD UP DOUBLE TRIPLE
{KEY_MUSIC_PP, KEY_ANC_SWITCH, KEY_NULL, KEY_NULL, KEY_CALL_LAST_NO, KEY_NULL}, //KEY_0
{KEY_MUSIC_NEXT, KEY_VOL_UP, KEY_VOL_UP, KEY_NULL, KEY_OPEN_SIRI, KEY_NULL}, //KEY_1
{KEY_MUSIC_PREV, KEY_VOL_DOWN, KEY_VOL_DOWN, KEY_NULL, KEY_HID_CONTROL, KEY_NULL}, //KEY_2
};
#endif
// *INDENT-OFF*
/************************** UART config****************************/
#if TCFG_UART0_ENABLE
UART0_PLATFORM_DATA_BEGIN(uart0_data)
.tx_pin = TCFG_UART0_TX_PORT, //串口打印TX引脚选择
.rx_pin = TCFG_UART0_RX_PORT, //串口打印RX引脚选择
.baudrate = TCFG_UART0_BAUDRATE, //串口波特率
.flags = UART_DEBUG, //串口用来打印需要把改参数设置为UART_DEBUG
UART0_PLATFORM_DATA_END()
#endif //TCFG_UART0_ENABLE
/************************** CHARGE config****************************/
#if TCFG_CHARGE_ENABLE
CHARGE_PLATFORM_DATA_BEGIN(charge_data)
.charge_en = TCFG_CHARGE_ENABLE, //内置充电使能
.charge_poweron_en = TCFG_CHARGE_POWERON_ENABLE, //是否支持充电开机
.charge_full_V = TCFG_CHARGE_FULL_V, //充电截止电压
.charge_full_mA = TCFG_CHARGE_FULL_MA, //充电截止电流
.charge_mA = TCFG_CHARGE_MA, //恒流充电电流
.charge_trickle_mA = TCFG_CHARGE_TRICKLE_MA, //涓流充电电流
/*ldo5v拔出过滤值过滤时间 = (filter*2 + 20)ms,ldoin<0.6V且时间大于过滤时间才认为拔出
对于充满直接从5V掉到0V的充电仓该值必须设置成0对于充满由5V先掉到0V之后再升压到xV的
充电仓,需要根据实际情况设置该值大小*/
.ldo5v_off_filter = 100,
.ldo5v_on_filter = 50,
.ldo5v_keep_filter = 220,
.ldo5v_pulldown_lvl = CHARGE_PULLDOWN_200K, //下拉电阻档位选择
.ldo5v_pulldown_keep = 0,
//1、对于自动升压充电舱,若充电舱需要更大的负载才能检测到插入时请将该变量置1,并且根据需求配置下拉电阻档位
//2、对于按键升压,并且是通过上拉电阻去提供维持电压的舱,请将该变量设置1,并且根据舱的上拉配置下拉需要的电阻挡位
//3、对于常5V的舱,可将改变量设为0,省功耗
//4、为LDOIN防止被误触发唤醒,可设置为200k下拉
.ldo5v_pulldown_en = 1,
CHARGE_PLATFORM_DATA_END()
#endif//TCFG_CHARGE_ENABLE
/************************** chargestore config****************************/
#if TCFG_CHARGESTORE_ENABLE || TCFG_TEST_BOX_ENABLE || TCFG_ANC_BOX_ENABLE
CHARGESTORE_PLATFORM_DATA_BEGIN(chargestore_data)
.io_port = TCFG_CHARGESTORE_PORT,
CHARGESTORE_PLATFORM_DATA_END()
#endif
/************************** DAC ****************************/
#if TCFG_AUDIO_DAC_ENABLE
struct dac_platform_data dac_data = {
.mode = TCFG_AUDIO_DAC_MODE, //dac输出模式
.ldo_id = TCFG_AUDIO_DAC_LDO_SEL, //保留位
.pa_mute_port = TCFG_AUDIO_DAC_PA_PORT, //暂时无作用
.vcmo_en = 0, //是否打开VCOMO
.pa_mute_value = 1, //暂时无作用
.output = TCFG_AUDIO_DAC_CONNECT_MODE, //DAC输出配置和具体硬件连接有关需根据硬件来设置
.lpf_isel = 0xf,
.sys_vol_type = SYS_VOL_TYPE, //系统音量选择:模拟音量/数字音量,调节时调节对应的音量
.max_ana_vol = MAX_ANA_VOL, //模拟音量最大等级
.max_dig_vol = MAX_DIG_VOL, //数字音量最大等级
/* .dig_vol_tab = (s16 *)dig_vol_table, //数字音量表 */
.vcm_cap_en = 1, //配1代表走外部通路,vcm上有电容时,可以提升电路抑制电源噪声能力提高ADC的性能配0相当于vcm上无电容抑制电源噪声能力下降,ADC性能下降
#if (SYS_VOL_TYPE == VOL_TYPE_AD)
.digital_gain_limit = 16384,
#endif // #if (SYS_VOL_TYPE == VOL_TYPE_AD)
.power_on_mode = 0,
};
#endif
/************************** ADC ****************************/
#if TCFG_AUDIO_ADC_ENABLE
#ifndef TCFG_AUDIO_MIC0_BIAS_EN
#define TCFG_AUDIO_MIC0_BIAS_EN 0
#endif/*TCFG_AUDIO_MIC0_BIAS_EN*/
#ifndef TCFG_AUDIO_MIC1_BIAS_EN
#define TCFG_AUDIO_MIC1_BIAS_EN 0
#endif/*TCFG_AUDIO_MIC1_BIAS_EN*/
#ifndef TCFG_AUDIO_MIC2_BIAS_EN
#define TCFG_AUDIO_MIC2_BIAS_EN 0
#endif/*TCFG_AUDIO_MIC2_BIAS_EN*/
#ifndef TCFG_AUDIO_MIC3_BIAS_EN
#define TCFG_AUDIO_MIC3_BIAS_EN 0
#endif/*TCFG_AUDIO_MIC3_BIAS_EN*/
#ifndef TCFG_AUDIO_MIC_LDO_EN
#define TCFG_AUDIO_MIC_LDO_EN 0
#endif/*TCFG_AUDIO_MIC_LDO_EN*/
struct adc_platform_data adc_data = {
/*MIC LDO电流档位设置
0:0.625ua 1:1.25ua 2:1.875ua 3:2.5ua*/
.mic_ldo_isel = TCFG_AUDIO_ADC_LD0_SEL,
/*mic_mode 工作模式定义
#define AUDIO_MIC_CAP_MODE 0 //单端隔直电容模式
#define AUDIO_MIC_CAP_DIFF_MODE 1 //差分隔直电容模式
#define AUDIO_MIC_CAPLESS_MODE 2 //单端省电容模式
*/
.mic_mode = TCFG_AUDIO_MIC_MODE,
.mic1_mode = TCFG_AUDIO_MIC1_MODE,
.mic2_mode = TCFG_AUDIO_MIC2_MODE,
.mic3_mode = TCFG_AUDIO_MIC3_MODE,
.mic_bias_inside = TCFG_AUDIO_MIC0_BIAS_EN,
.mic1_bias_inside = TCFG_AUDIO_MIC1_BIAS_EN,
.mic2_bias_inside = TCFG_AUDIO_MIC2_BIAS_EN,
.mic3_bias_inside = TCFG_AUDIO_MIC3_BIAS_EN,
/*MICLDO供电输出到PAD(PA0)控制使能*/
.mic_ldo_pwr = TCFG_AUDIO_MIC_LDO_EN, // MIC LDO 输出到 PA0
/*MIC免电容方案需要设置影响MIC的偏置电压
0b0001~0b1001 : 0.5k ~ 4.5k step = 0.5k
0b1010~0b1111 : 5k ~ 10k step = 1k */
.mic_bias_res = 4,
.mic1_bias_res = 4,
.mic2_bias_res = 4,
.mic3_bias_res = 4,
/*MIC LDO电压档位设置,也会影响MIC的偏置电压
3:2.0v 4:2.2v 5:2.4v 6:2.6v 7:2.8v */
.mic_ldo_vsel = 5,
//mic的去直流dcc寄存器配置值,可配0到15,数值越大,其高通转折点越低
.mic_dcc = 8,
/*ADC低功耗等级,越大功耗越低对应影响THD和底噪, 范围 (0 - 2)*/
.lowpower_lvl = 0,
};
#endif
const struct vad_mic_platform_data vad_mic_data = {
.mic_data = { //
.mic_mode = TCFG_AUDIO_MIC_MODE,
.mic_ldo_isel = 2,
.mic_ldo_vsel = 5,
.mic_ldo2PAD_en = 1,
.mic_bias_en = 0,
.mic_bias_res = 0,
.mic_bias_inside = TCFG_AUDIO_MIC0_BIAS_EN,
/* ADC偏置电阻配置*/
.adc_rbs = 3,
/* ADC输入电阻配置*/
.adc_rin = 3,
/*.adc_test = 1,*/
},
.power_data = {
/*VADLDO电压档位0~7*/
.ldo_vs = 3,
/*VADLDO误差运放电流档位0~3*/
#if TCFG_VAD_LOWPOWER_CLOCK == VAD_CLOCK_USE_PMU_STD12M
.ldo_is = 1,
#else
.ldo_is = 2,
#endif
.clock = TCFG_VAD_LOWPOWER_CLOCK, /*VAD时钟选项*/
.acm_select = 8,
},
};
/* struct audio_pf_data audio_pf_d = { */
/* #if TCFG_AUDIO_DAC_ENABLE */
/* .adc_pf_data = &adc_data, */
/* #endif */
/* #if TCFG_AUDIO_ADC_ENABLE */
/* .dac_pf_data = &dac_data, */
/* #endif */
/* }; */
/* struct audio_platform_data audio_data = { */
/* .private_data = (void *) &audio_pf_d, */
/* }; */
/************************** IO KEY ****************************/
#if TCFG_IOKEY_ENABLE
const struct iokey_port iokey_list[] = {
{
.connect_way = TCFG_IOKEY_POWER_CONNECT_WAY, //IO按键的连接方式
.key_type.one_io.port = TCFG_IOKEY_POWER_ONE_PORT, //IO按键对应的引脚
.key_value = 0, //按键值
},
};
const struct iokey_platform_data iokey_data = {
.enable = TCFG_IOKEY_ENABLE, //是否使能IO按键
.num = ARRAY_SIZE(iokey_list), //IO按键的个数
.port = iokey_list, //IO按键参数表
};
#if MULT_KEY_ENABLE
//组合按键消息映射表
//配置注意事项:单个按键按键值需要按照顺序编号,如power:0, prev:1, next:2
//bit_value = BIT(0) | BIT(1) 指按键值为0和按键值为1的两个按键被同时按下,
//remap_value = 3指当这两个按键被同时按下后重新映射的按键值;
const struct key_remap iokey_remap_table[] = {
{.bit_value = BIT(0) | BIT(1), .remap_value = 3},
{.bit_value = BIT(0) | BIT(2), .remap_value = 4},
{.bit_value = BIT(1) | BIT(2), .remap_value = 5},
};
const struct key_remap_data iokey_remap_data = {
.remap_num = ARRAY_SIZE(iokey_remap_table),
.table = iokey_remap_table,
};
#endif
#endif
/*********************** LP TOUCH KEY ****************************/
#if TCFG_LP_TOUCH_KEY_ENABLE
const struct lp_touch_key_platform_data lp_touch_key_config = {
/*触摸按键*/
.ch[0].enable = TCFG_LP_TOUCH_KEY0_EN,
.ch[0].wakeup_enable = TCFG_LP_TOUCH_KEY0_WAKEUP_EN,
.ch[0].port = IO_PORTB_00,
.ch[0].sensitivity = TCFG_LP_TOUCH_KEY0_SENSITIVITY,
.ch[0].key_value = 0,
.ch[1].enable = TCFG_LP_TOUCH_KEY1_EN,
.ch[1].wakeup_enable = TCFG_LP_TOUCH_KEY1_WAKEUP_EN,
.ch[1].port = IO_PORTB_01,
.ch[1].sensitivity = TCFG_LP_TOUCH_KEY1_SENSITIVITY,
.ch[1].key_value = 0,
.ch[2].enable = TCFG_LP_TOUCH_KEY2_EN,
.ch[2].wakeup_enable = TCFG_LP_TOUCH_KEY2_WAKEUP_EN,
.ch[2].port = IO_PORTB_02,
.ch[2].sensitivity = TCFG_LP_TOUCH_KEY2_SENSITIVITY,
.ch[2].key_value = 1,
.ch[3].enable = TCFG_LP_TOUCH_KEY3_EN,
.ch[3].wakeup_enable = TCFG_LP_TOUCH_KEY3_WAKEUP_EN,
.ch[3].port = IO_PORTB_04,
.ch[3].sensitivity = TCFG_LP_TOUCH_KEY3_SENSITIVITY,
.ch[3].key_value = 2,
.ch[4].enable = TCFG_LP_TOUCH_KEY4_EN,
.ch[4].wakeup_enable = TCFG_LP_TOUCH_KEY4_WAKEUP_EN,
.ch[4].port = IO_PORTB_05,
.ch[4].sensitivity = TCFG_LP_TOUCH_KEY4_SENSITIVITY,
.ch[4].key_value = 3,
//把触摸按键之间的滑动也当做按键处理,有上滑,下滑,左滑,右滑
.slide_mode_en = TCFG_LP_SLIDE_KEY_ENABLE,
.slide_mode_key_value = 3,
//入耳检测相关的配置
.eartch_en = TCFG_LP_EARTCH_KEY_ENABLE,
.eartch_ch = TCFG_LP_EARTCH_KEY_CH,
.eartch_ref_ch = TCFG_LP_EARTCH_KEY_REF_CH,
.eartch_soft_inear_val = TCFG_LP_EARTCH_SOFT_INEAR_VAL,
.eartch_soft_outear_val = TCFG_LP_EARTCH_SOFT_OUTEAR_VAL,
};
#endif /* #if TCFG_LP_TOUCH_KEY_ENABLE */
/************************** PLCNT TOUCH_KEY ****************************/
#if TCFG_TOUCH_KEY_ENABLE
const const struct touch_key_port touch_key_list[] = {
{
.press_delta = TCFG_TOUCH_KEY0_PRESS_DELTA,
.port = TCFG_TOUCH_KEY0_PORT,
.key_value = TCFG_TOUCH_KEY0_VALUE,
},
{
.press_delta = TCFG_TOUCH_KEY1_PRESS_DELTA,
.port = TCFG_TOUCH_KEY1_PORT,
.key_value = TCFG_TOUCH_KEY1_VALUE,
},
};
const struct touch_key_platform_data touch_key_data = {
.num = ARRAY_SIZE(touch_key_list),
.port_list = touch_key_list,
};
#endif /* #if TCFG_TOUCH_KEY_ENABLE */
/************************** AD KEY ****************************/
#if TCFG_ADKEY_ENABLE
const struct adkey_platform_data adkey_data = {
.enable = TCFG_ADKEY_ENABLE, //AD按键使能
.adkey_pin = TCFG_ADKEY_PORT, //AD按键对应引脚
.ad_channel = TCFG_ADKEY_AD_CHANNEL, //AD通道值
.extern_up_en = TCFG_ADKEY_EXTERN_UP_ENABLE, //是否使用外接上拉电阻
.ad_value = { //根据电阻算出来的电压值
TCFG_ADKEY_VOLTAGE0,
TCFG_ADKEY_VOLTAGE1,
TCFG_ADKEY_VOLTAGE2,
TCFG_ADKEY_VOLTAGE3,
TCFG_ADKEY_VOLTAGE4,
TCFG_ADKEY_VOLTAGE5,
TCFG_ADKEY_VOLTAGE6,
TCFG_ADKEY_VOLTAGE7,
TCFG_ADKEY_VOLTAGE8,
TCFG_ADKEY_VOLTAGE9,
},
.key_value = { //AD按键各个按键的键值
TCFG_ADKEY_VALUE0,
TCFG_ADKEY_VALUE1,
TCFG_ADKEY_VALUE2,
TCFG_ADKEY_VALUE3,
TCFG_ADKEY_VALUE4,
TCFG_ADKEY_VALUE5,
TCFG_ADKEY_VALUE6,
TCFG_ADKEY_VALUE7,
TCFG_ADKEY_VALUE8,
TCFG_ADKEY_VALUE9,
},
};
#endif
/************************** RDEC_KEY ****************************/
#if TCFG_RDEC_KEY_ENABLE
const struct rdec_device rdeckey_list[] = {
{
.index = RDEC0 ,
.sin_port0 = TCFG_RDEC0_ECODE1_PORT,
.sin_port1 = TCFG_RDEC0_ECODE2_PORT,
.key_value0 = TCFG_RDEC0_KEY0_VALUE | BIT(7),
.key_value1 = TCFG_RDEC0_KEY1_VALUE | BIT(7),
},
{
.index = RDEC1 ,
.sin_port0 = TCFG_RDEC1_ECODE1_PORT,
.sin_port1 = TCFG_RDEC1_ECODE2_PORT,
.key_value0 = TCFG_RDEC1_KEY0_VALUE | BIT(7),
.key_value1 = TCFG_RDEC1_KEY1_VALUE | BIT(7),
},
{
.index = RDEC2 ,
.sin_port0 = TCFG_RDEC2_ECODE1_PORT,
.sin_port1 = TCFG_RDEC2_ECODE2_PORT,
.key_value0 = TCFG_RDEC2_KEY0_VALUE | BIT(7),
.key_value1 = TCFG_RDEC2_KEY1_VALUE | BIT(7),
},
};
const struct rdec_platform_data rdec_key_data = {
.enable = 1, //TCFG_RDEC_KEY_ENABLE, //是否使能RDEC按键
.num = ARRAY_SIZE(rdeckey_list), //RDEC按键的个数
.rdec = rdeckey_list, //RDEC按键参数表
};
#endif
/************************** IIS config ****************************/
#if (TCFG_AUDIO_INPUT_IIS || TCFG_AUDIO_OUTPUT_IIS)
ALINK_PARM alink0_platform_data = {
.module = ALINK0,
.mclk_io = TCFG_IIS_MCLK_IO,
.sclk_io = TCFG_SCLK_IO,
.lrclk_io = TCFG_LRCLK_IO,
.ch_cfg[0].data_io = TCFG_DATA0_IO,
.ch_cfg[1].data_io = TCFG_DATA1_IO,
.ch_cfg[2].data_io = TCFG_DATA2_IO,
.ch_cfg[3].data_io = TCFG_DATA3_IO,
.mode = ALINK_MD_IIS,
#if TCFG_IIS_MODE
.role = ALINK_ROLE_SLAVE,
#else
.role = ALINK_ROLE_MASTER,
#endif /*TCFG_IIS_MODE*/
.clk_mode = ALINK_CLK_FALL_UPDATE_RAISE_SAMPLE,
.bitwide = ALINK_LEN_16BIT,
.sclk_per_frame = ALINK_FRAME_32SCLK,
.dma_len = 4 * 1024,
.sample_rate = TCFG_IIS_SR,
.buf_mode = ALINK_BUF_CIRCLE,
/*.iperiod = 64, //配置该项可以控制输入的延时*/
};
#endif
/************************** PWM_LED ****************************/
#if TCFG_PWMLED_ENABLE
LED_PLATFORM_DATA_BEGIN(pwm_led_data)
.io_mode = TCFG_PWMLED_IOMODE, //推灯模式设置:支持单个IO推两个灯和两个IO推两个灯
.io_cfg.one_io.pin = TCFG_PWMLED_PIN, //单个IO推两个灯的IO口配置
LED_PLATFORM_DATA_END()
#endif
const struct soft_iic_config soft_iic_cfg[] = {
#if 0
//iic0 data
{
.scl = TCFG_SW_I2C0_CLK_PORT, //IIC CLK脚
.sda = TCFG_SW_I2C0_DAT_PORT, //IIC DAT脚
.delay = TCFG_SW_I2C0_DELAY_CNT, //软件IIC延时参数影响通讯时钟频率
.io_pu = 1, //是否打开上拉电阻如果外部电路没有焊接上拉电阻需要置1
},
#endif
#if 0
//iic1 data
{
.scl = IO_PORTA_05,
.sda = IO_PORTA_06,
.delay = 50,
.io_pu = 1,
},
#endif
};
const struct hw_iic_config hw_iic_cfg[] = {
#if 0
//iic0 data
{
/*硬件IIC端口下选择
SCL SDA
{IO_PORT_DP, IO_PORT_DM}, //group a
{IO_PORTC_04, IO_PORTC_05}, //group b
{IO_PORTC_02, IO_PORTC_03}, //group c
{IO_PORTA_05, IO_PORTA_06}, //group d
*/
.port = TCFG_HW_I2C0_PORTS,
.baudrate = TCFG_HW_I2C0_CLK, //IIC通讯波特率
.hdrive = 0, //是否打开IO口强驱
.io_filter = 1, //是否打开滤波器(去纹波)
.io_pu = 1, //是否打开上拉电阻如果外部电路没有焊接上拉电阻需要置1
},
#endif
};
#if TCFG_SD0_ENABLE
SD0_PLATFORM_DATA_BEGIN(sd0_data)
.port = {
TCFG_SD0_PORT_CMD,
TCFG_SD0_PORT_CLK,
TCFG_SD0_PORT_DA0,
TCFG_SD0_PORT_DA1,
TCFG_SD0_PORT_DA2,
TCFG_SD0_PORT_DA3,
},
.data_width = TCFG_SD0_DAT_MODE,
.speed = TCFG_SD0_CLK,
.detect_mode = TCFG_SD0_DET_MODE,
.priority = 3,
#if (TCFG_SD0_DET_MODE == SD_IO_DECT)
.detect_io = TCFG_SD0_DET_IO,
.detect_io_level = TCFG_SD0_DET_IO_LEVEL,
.detect_func = sdmmc_0_io_detect,
.power = sd_set_power,
/* .power = NULL, */
#elif (TCFG_SD0_DET_MODE == SD_CLK_DECT)
.detect_io_level = TCFG_SD0_DET_IO_LEVEL,
.detect_func = sdmmc_0_clk_detect,
.power = sd_set_power,
/* .power = NULL, */
#else
.detect_func = sdmmc_cmd_detect,
.power = NULL,
#endif
SD0_PLATFORM_DATA_END()
#endif /* #if TCFG_SD0_ENABLE */
REGISTER_DEVICES(device_table) = {
/* { "audio", &audio_dev_ops, (void *) &audio_data }, */
#if TCFG_CHARGE_ENABLE
{ "charge", &charge_dev_ops, (void *)&charge_data },
#endif
#if TCFG_SD0_ENABLE
{ "sd0", &sd_dev_ops, (void *) &sd0_data},
#endif
};
/************************** power_param ****************************/
const struct low_power_param power_param = {
.config = TCFG_LOWPOWER_LOWPOWER_SEL, //低功耗使能,蓝牙&&系统空闲可进入低功耗
.btosc_hz = TCFG_CLOCK_OSC_HZ, //蓝牙晶振频率
.delay_us = TCFG_CLOCK_SYS_HZ / 1000000L, //提供给低功耗模块的延时(不需要需修改)
.vddiom_lev = TCFG_LOWPOWER_VDDIOM_LEVEL, //vddiom等级
.osc_type = TCFG_LOWPOWER_OSC_TYPE, //低功耗晶振类型btosc/lrc
#if (TCFG_LOWPOWER_RAM_SIZE)
.mem_init_con = MEM_PWR_RAM_SET(TCFG_LOWPOWER_RAM_SIZE),
#else
.mem_init_con = 0,
#endif
#if (TCFG_LP_TOUCH_KEY_ENABLE && \
(TCFG_LP_TOUCH_KEY0_WAKEUP_EN || \
TCFG_LP_TOUCH_KEY1_WAKEUP_EN || \
TCFG_LP_TOUCH_KEY2_WAKEUP_EN || \
TCFG_LP_TOUCH_KEY3_WAKEUP_EN || \
TCFG_LP_TOUCH_KEY4_WAKEUP_EN ))
.lpctmu_en = 1,
#else
.lpctmu_en = 0,
#endif
};
/************************** wk_param ****************************/
struct port_wakeup port0 = {
.pullup_down_enable = ENABLE, //配置I/O 内部上下拉是否使能
.edge = FALLING_EDGE, //唤醒方式选择,可选:上升沿\下降沿
.filter = PORT_FLT_8ms,
.iomap = TCFG_IOKEY_POWER_ONE_PORT, //唤醒口选择
};
#if (TCFG_TEST_BOX_ENABLE || TCFG_CHARGESTORE_ENABLE || TCFG_ANC_BOX_ENABLE || TCFG_UMIDIGI_BOX_ENABLE)
struct port_wakeup port1 = {
.pullup_down_enable = DISABLE, //配置I/O 内部上下拉是否使能
.edge = FALLING_EDGE, //唤醒方式选择,可选:上升沿\下降沿
.filter = PORT_FLT_1ms,
.iomap = TCFG_CHARGESTORE_PORT, //唤醒口选择
};
#endif
#if TCFG_CHARGE_ENABLE
struct port_wakeup charge_port = {
.edge = RISING_EDGE, //唤醒方式选择,可选:上升沿\下降沿\双边沿
.filter = PORT_FLT_16ms,
.iomap = IO_CHGFL_DET, //唤醒口选择
};
struct port_wakeup vbat_port = {
.edge = BOTH_EDGE, //唤醒方式选择,可选:上升沿\下降沿\双边沿
.filter = PORT_FLT_16ms,
.iomap = IO_VBTCH_DET, //唤醒口选择
};
struct port_wakeup ldoin_port = {
.edge = BOTH_EDGE, //唤醒方式选择,可选:上升沿\下降沿\双边沿
.filter = PORT_FLT_16ms,
.iomap = IO_LDOIN_DET, //唤醒口选择
};
#endif
const struct wakeup_param wk_param = {
#if (!(TCFG_LP_TOUCH_KEY_ENABLE && TCFG_LP_TOUCH_KEY1_EN))
.port[1] = &port0,
#endif
#if (TCFG_TEST_BOX_ENABLE || TCFG_CHARGESTORE_ENABLE || TCFG_ANC_BOX_ENABLE || TCFG_UMIDIGI_BOX_ENABLE)
.port[2] = &port1,
#endif
#if TCFG_CHARGE_ENABLE
.aport[0] = &charge_port,
.aport[1] = &vbat_port,
.aport[2] = &ldoin_port,
#endif
};
void gSensor_wkupup_disable(void)
{
log_info("gSensor wkup disable\n");
power_wakeup_index_enable(1, 0);
}
void gSensor_wkupup_enable(void)
{
log_info("gSensor wkup enable\n");
power_wakeup_index_enable(1, 1);
}
void debug_uart_init(const struct uart_platform_data *data)
{
#if TCFG_UART0_ENABLE
if (data) {
uart_init(data);
} else {
uart_init(&uart0_data);
}
#endif
}
STATUS *get_led_config(void)
{
return &(__this->led);
}
STATUS *get_tone_config(void)
{
return &(__this->tone);
}
u8 get_sys_default_vol(void)
{
return 21;
}
u8 get_power_on_status(void)
{
#if TCFG_IOKEY_ENABLE
struct iokey_port *power_io_list = NULL;
power_io_list = iokey_data.port;
if (iokey_data.enable) {
if (gpio_read(power_io_list->key_type.one_io.port) == power_io_list->connect_way){
return 1;
}
}
#endif
#if TCFG_ADKEY_ENABLE
if (adkey_data.enable) {
return 1;
}
#endif
#if TCFG_LP_TOUCH_KEY_ENABLE
return lp_touch_key_power_on_status();
#endif
return 0;
}
static void board_devices_init(void)
{
#if TCFG_PWMLED_ENABLE
pwm_led_init(&pwm_led_data);
#endif
#if (TCFG_IOKEY_ENABLE || TCFG_ADKEY_ENABLE || TCFG_RDEC_KEY_ENABLE || TCFG_TOUCH_KEY_ENABLE)
key_driver_init();
#endif
#if TCFG_UART_KEY_ENABLE
extern int uart_key_init(void);
uart_key_init();
#endif /* #if TCFG_UART_KEY_ENABLE */
#if TCFG_LP_TOUCH_KEY_ENABLE
lp_touch_key_init(&lp_touch_key_config);
#endif /* #if TCFG_LP_TOUCH_KEY_ENABLE */
#if (!TCFG_CHARGE_ENABLE)
CHARGE_EN(0);
CHGGO_EN(0);
#endif
#if TCFG_CHARGESTORE_ENABLE || TCFG_TEST_BOX_ENABLE || TCFG_ANC_BOX_ENABLE
chargestore_api_init(&chargestore_data);
#endif
}
//外置触摸电源控制 1:输出高 0:输出地
static void pwr_set_external_touch(u8 high_low)
{
if(TCFG_EXTERNAL_TOUCH_KEY_POWER_PORT == NO_CONFIG_PORT){
return;
}
gpio_set_pull_up(TCFG_EXTERNAL_TOUCH_KEY_POWER_PORT, 1);
gpio_set_pull_down(TCFG_EXTERNAL_TOUCH_KEY_POWER_PORT, 0);
gpio_set_direction(TCFG_EXTERNAL_TOUCH_KEY_POWER_PORT, 0);
gpio_set_output_value(TCFG_EXTERNAL_TOUCH_KEY_POWER_PORT, high_low);
}
#if CLIENT_BOARD == CUSTOM9_CFG
#define TCFG_TALKING_FB_MIC_IO IO_PORTB_02
/*
0:MIC0 作为FB MIC,引脚输出低电平。
1MIC0 作为通话MIC引脚输出高电平。
*/
void TALKING_FB_MIC_sel(u8 mic_type)
{
gpio_set_pull_up(TCFG_TALKING_FB_MIC_IO , 0);
gpio_set_pull_down(TCFG_TALKING_FB_MIC_IO , 0);
gpio_set_direction(TCFG_TALKING_FB_MIC_IO , 0);
gpio_set_die(TCFG_TALKING_FB_MIC_IO , 1);
switch (mic_type)
{
case 0:
gpio_set_output_value(TCFG_TALKING_FB_MIC_IO , 0);
break;
case 1:
gpio_set_output_value(TCFG_TALKING_FB_MIC_IO , 1);
break;
default:
break;
}
}
static u8 dcda_en_state = 0;
#define DCDC_EN_PORT IO_PORTC_02
static void dcdc_en(u8 en)
{
if (dcda_en_state == en)
{
return ;
}
dcda_en_state = en;
gpio_set_die(DCDC_EN_PORT, 0);
gpio_set_direction(IO_PORTB_04, 0);
gpio_set_pull_up(DCDC_EN_PORT, 0);
gpio_set_pull_down(DCDC_EN_PORT, 0);
switch (en)
{
case 0:
printf("external dcdc disable \n");
gpio_direction_output(DCDC_EN_PORT, 0);
break;
case 1:
printf("external dcdc enable \n");
gpio_direction_output(DCDC_EN_PORT, 1);
break;
default:
break;
}
}
static void dcdc_init(void)
{
gpio_set_die(DCDC_EN_PORT, 0);
gpio_set_direction(IO_PORTB_04, 0);
gpio_set_pull_up(DCDC_EN_PORT, 0);
gpio_set_pull_down(DCDC_EN_PORT, 0);
gpio_direction_output(DCDC_EN_PORT, 0);
dcda_en_state = 0;
}
/*dcdc switch*/
void audio_dac_power_state(u8 state)
{
switch (state)
{
case DAC_ANALOG_OPEN_PREPARE:
dcdc_en(1);
break;
case DAC_ANALOG_OPEN_FINISH:
break;
case DAC_ANALOG_CLOSE_PREPARE:
break;
case DAC_ANALOG_CLOSE_FINISH:
dcdc_en(0);
break;
default:
break;
}
}
#endif
extern void cfg_file_parse(u8 idx);
void board_init()
{
pwr_set_external_touch(1);
board_power_init();
//adc_vbg_init();
adc_init();
#if CLIENT_BOARD == CUSTOM9_CFG
dcdc_init();
TALKING_FB_MIC_sel(0);
#endif
cfg_file_parse(0);
devices_init();
#if TCFG_AUDIO_ANC_ENABLE
anc_init();
#endif/*TCFG_AUDIO_ANC_ENABLE*/
board_devices_init();
#if TCFG_CHARGE_ENABLE
if(get_charge_online_flag())
#else
if (0)
#endif
{
power_set_mode(PWR_LDO15);
}else{
power_set_mode(TCFG_LOWPOWER_POWER_SEL);
}
//针对硅mic要输出1给mic供电
/* gpio_set_pull_up(IO_PORTA_04, 0); */
/* gpio_set_pull_down(IO_PORTA_04, 0); */
/* gpio_set_direction(IO_PORTA_04, 0); */
/* gpio_set_output_value(IO_PORTA_04,1); */
#if TCFG_UART0_ENABLE
if (uart0_data.rx_pin < IO_MAX_NUM) {
gpio_set_die(uart0_data.rx_pin, 1);
}
#endif
#if TCFG_SMART_VOICE_ENABLE
int audio_smart_voice_detect_init(struct vad_mic_platform_data *mic_data);
audio_smart_voice_detect_init((struct vad_mic_platform_data *)&vad_mic_data);
#endif /* #if TCFG_SMART_VOICE_ENABLE */
#ifdef CONFIG_BOARD_AISPEECH_VAD_ASR
extern int audio_ais_platform_asr_init(struct vad_mic_platform_data *mic_data);
audio_ais_platform_asr_init((struct vad_mic_platform_data *)&vad_mic_data);
#endif /*CONFIG_BOARD_AISPEECH_VAD_ASR*/
#ifdef AUDIO_PCM_DEBUG
extern void uartSendInit();
uartSendInit();
#endif/*AUDIO_PCM_DEBUG*/
}
/*进软关机之前默认将IO口都设置成高阻状态需要保留原来状态的请修改该函数*/
extern void dac_power_off(void);
void board_set_soft_poweroff(void)
{
//power按键
#if TCFG_IOKEY_ENABLE
soff_gpio_protect(TCFG_IOKEY_POWER_ONE_PORT);
#endif
soff_gpio_protect(TCFG_EXTERNAL_TOUCH_KEY_POWER_PORT);//软关机KEEP外置触摸供电
#if (!(TCFG_LP_TOUCH_KEY_ENABLE && TCFG_LP_TOUCH_KEY1_EN))
//默认唤醒io
soff_gpio_protect(IO_PORTB_01);
#endif
#if (TCFG_TEST_BOX_ENABLE || TCFG_CHARGESTORE_ENABLE || TCFG_ANC_BOX_ENABLE || TCFG_UMIDIGI_BOX_ENABLE)
power_wakeup_index_enable(2, 0);
#endif
board_set_soft_poweroff_common(NULL);
dac_power_off();
}
#define APP_IO_DEBUG_0(i,x) {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
#define APP_IO_DEBUG_1(i,x) {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}
void sleep_exit_callback(u32 usec)
{
sleep_exit_callback_common(NULL);
putchar('>');
}
void sleep_enter_callback(u8 step)
{
/* 此函数禁止添加打印 */
if (step == 1) {
putchar('<');
} else {
//外置触摸供电
pwr_set_external_touch(1);
sleep_enter_callback_common(NULL);
}
}
static void port_wakeup_callback(u8 index, u8 gpio)
{
log_info("%s:%d,%d",__FUNCTION__,index,gpio);
#if TCFG_UMIDIGI_BOX_ENABLE
if (index == 2) {
ldo_port_wakeup_to_cmessage();
}
#endif
switch (index) {
#if (TCFG_TEST_BOX_ENABLE || TCFG_CHARGESTORE_ENABLE || TCFG_ANC_BOX_ENABLE)
case 2:
extern void chargestore_ldo5v_fall_deal(void);
chargestore_ldo5v_fall_deal();
break;
#endif
}
}
static void aport_wakeup_callback(u8 index, u8 gpio, u8 edge)
{
log_info("%s:%d,%d",__FUNCTION__,index,gpio);
#if TCFG_CHARGE_ENABLE
switch (gpio) {
case IO_CHGFL_DET://charge port
charge_wakeup_isr();
break;
case IO_VBTCH_DET://vbat port
case IO_LDOIN_DET://ldoin port
ldoin_wakeup_isr();
break;
}
#endif
}
void board_power_init(void)
{
log_info("Power init : %s", __FILE__);
power_init(&power_param);
power_set_callback(TCFG_LOWPOWER_LOWPOWER_SEL, sleep_enter_callback, sleep_exit_callback, board_set_soft_poweroff);
#if TCFG_UMIDIGI_BOX_ENABLE
gpio_set_die(TCFG_CHARGESTORE_PORT, 1);
umidigi_chargestore_message_callback(app_umidigi_chargetore_message_deal);
#endif
power_keep_dacvdd_en(0);
power_wakeup_init(&wk_param);
power_awakeup_set_callback(aport_wakeup_callback);
power_wakeup_set_callback(port_wakeup_callback);
}
#endif /* #ifdef CONFIG_BOARD_JL701N_ANC */

File diff suppressed because it is too large Load Diff

View File

@ -1,123 +0,0 @@
#ifndef CONFIG_BOARD_JL701N_ANC_POST_BUILD_CFG_H
#define CONFIG_BOARD_JL701N_ANC_POST_BUILD_CFG_H
/* 改文件只添加和isd_config.ini相关的配置用以生成isd_config.ini */
/* 其他不相关的配置请勿添加在改文件 */
#ifdef CONFIG_BOARD_JL701N_ANC
/* Following Macros Affect Periods Of Both Code Compiling And Post-build */
#define CONFIG_DOUBLE_BANK_ENABLE 0 //单双备份选择(若打开了改宏,FLASH结构变为双备份结构适用于接入第三方协议的OTA PS: JL-OTA同样支持双备份升级, 需要根据实际FLASH大小同时配置CONFIG_FLASH_SIZE)
#define CONFIG_APP_OTA_ENABLE 0 //是否支持RCSP升级(JL-OTA)
#define CONFIG_UPDATE_JUMP_TO_MASK 0 //配置升级到loader的方式0为直接reset,1为跳转(适用于芯片电源由IO口KEEP住的方案,需要注意检查跳转前是否将使用DMA的硬件模块全部关闭)
#define CONFIG_IO_KEY_EN 0 //配置是否使用IO按键配合RESET1
#define CONFIG_UPDATE_WITH_MD5_CHECK_EN 0 //配置升级是否支持MD5校验
#define CONFIG_ANC_ENABLE 1 //配置是否支持ANC
//flash size vaule definition
#define FLASH_SIZE_256K 0x40000
#define FLASH_SIZE_512K 0x80000
#define FLASH_SIZE_1M 0x100000
#define FLASH_SIZE_2M 0x200000
#define FLASH_SIZE_4M 0x400000
#define CONFIG_FLASH_SIZE FLASH_SIZE_1M //配置FLASH大小
/* Above Macros Affect Periods Of Both Code Compiling And Post-build */
/* Following Macros Only For Post Bulid Configuaration */
#define CONFIG_DB_UPDATE_DATA_GENERATE_EN 0 //是否生成db_data.bin(用于第三方协议接入使用)
#define CONFIG_ONLY_GRENERATE_ALIGN_4K_CODE 0 //ufw只生成1份4K对齐的代码
//config for supported chip version
#ifdef CONFIG_BR30_C_VERSION
#define CONFIG_SUPPORTED_CHIP_VERSION C
#else
#define CONFIG_SUPPORTED_CHIP_VERSION B,D,E,M,N,O,P
#endif
//DON'T MODIFY THIS CONFIG EXCEPT SDK PUBLISHER
#define CONFIG_CHIP_NAME AC701N //除了SDK发布者,请不要修改
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_PID AC701N //烧写或强制升级之前可以修改,之后升级要保持一致
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_VID 0.01 //烧写或强制升级之前可以修改,之后升级要保持一致
//Project with bluetooth,it must use OSC as PLL_SOURCE;
#define CONFIG_PLL_SOURCE_USING_LRC 0 //PLL时钟源选择 1:LRC 2:OSC
//config alignment size unit
#ifdef CONFIG_256K_FLASH
#define ALIGN_UNIT_256B 1 //FLASH对齐方式选择如果是256K的FLASH选择256BYTE对齐方式
#else
#define ALIGN_UNIT_256B 0
#endif
//partial platform check this config to select the uart IO for wired update
#define CONFIG_UART_UPDATE_PIN PP00
//isd_download loader/uboot/update_loader debug io config
//#define CONFIG_UBOOT_DEBUG_PIN PA05
//#define CONFIG_UBOOT_DEBUG_BAUD_RATE 1000000
//config long-press reset io pin,time,trigger level
#define CONFIG_RESET_PIN LDO //io pin
#define CONFIG_RESET_TIME 04 //unit:second
#define CONFIG_RESET_LEVEL 1 //tigger level(0/1)
#if CONFIG_IO_KEY_EN
#define CONFIG_SUPPORT_RESET1
#define CONFIG_RESET1_PIN PB01 //io pin
#define CONFIG_RESET1_TIME 08 //unit:second
#define CONFIG_RESET1_LEVEL 0 //tigger level(0/1)
#endif
//reserved three custom cfg item for the future definition
//#define CONFIG_CUSTOM_CFG1_TYPE POWER_PIN
//#define CONFIG_CUSTOM_CFG1_VALUE PC01_1
//#define CONFIG_CUSTOM_CFG2_TYPE
//#define CONFIG_CUSTOM_CFG2_VALUE
//#define CONFIG_CUSTOM_CFG3_TYPE
//#define CONFIG_CUSTOM_CFG3_VALUE
//#define CONFIG_VDDIO_LVD_LEVEL 4 ////VDDIO_LVD挡位0: 1.9V 1: 2.0V 2: 2.1V 3: 2.2V 4: 2.3V 5: 2.4V 6: 2.5V 7: 2.6V
//with single-bank mode,actual vm size should larger this VM_LEAST_SIZE,and dual bank mode,actual vm size equals this;
#define CONFIG_VM_LEAST_SIZE 8K
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_VM_OPT 1
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_BTIF_OPT 1
//reserved two custom cfg area for the future definition
//#define CONFIG_RESERVED_AREA1 EXIF1
#ifdef CONFIG_RESERVED_AREA1
#define CONFIG_RESERVED_AREA1_ADDR AUTO
#define CONFIG_RESERVED_AREA1_LEN 0x1000
#define CONFIG_RESERVED_AREA1_OPT 1
//#define CONFIG_RESERVED_AREA1_FILE anc_gains.bin
#endif
//#define CONFIG_RESERVED_AREA2 EXIF2
#ifdef CONFIG_RESERVED_AREA2
#define CONFIG_RESERVED_AREA2_ADDR AUTO
#define CONFIG_RESERVED_AREA2_LEN 0x1000
#define CONFIG_RESERVED_AREA2_OPT 1
//#define CONFIG_RESERVED_AREA2_FILE anc_gains.bin
#endif
/* Above Macros Only For Post Bulid Configuaration */
#endif /* #ifdef CONFIG_BOARD_JL701N_ANC */
#endif /* #ifndef CONFIG_BOARD_JL701N_ANC_POST_BUILD_CFG_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,123 +0,0 @@
#ifndef CONFIG_BOARD_JL701N_BTEMITTER_BUILD_CFG_H
#define CONFIG_BOARD_JL701N_BTEMITTER_BUILD_CFG_H
/* 改文件只添加和isd_config.ini相关的配置用以生成isd_config.ini */
/* 其他不相关的配置请勿添加在改文件 */
#ifdef CONFIG_BOARD_JL701N_BTEMITTER
/* Following Macros Affect Periods Of Both Code Compiling And Post-build */
#define CONFIG_DOUBLE_BANK_ENABLE 0 //单双备份选择(若打开了改宏,FLASH结构变为双备份结构适用于接入第三方协议的OTA PS: JL-OTA同样支持双备份升级, 需要根据实际FLASH大小同时配置CONFIG_FLASH_SIZE)
#define CONFIG_APP_OTA_ENABLE 0 //是否支持RCSP升级(JL-OTA)
#define CONFIG_UPDATE_JUMP_TO_MASK 0 //配置升级到loader的方式0为直接reset,1为跳转(适用于芯片电源由IO口KEEP住的方案,需要注意检查跳转前是否将使用DMA的硬件模块全部关闭)
#define CONFIG_IO_KEY_EN 0 //配置是否使用IO按键配合RESET1
#define CONFIG_UPDATE_WITH_MD5_CHECK_EN 0 //配置升级是否支持MD5校验
#define CONFIG_ANC_ENABLE 0 //配置是否支持ANC
//flash size vaule definition
#define FLASH_SIZE_256K 0x40000
#define FLASH_SIZE_512K 0x80000
#define FLASH_SIZE_1M 0x100000
#define FLASH_SIZE_2M 0x200000
#define FLASH_SIZE_4M 0x400000
#define CONFIG_FLASH_SIZE FLASH_SIZE_1M //配置FLASH大小
/* Above Macros Affect Periods Of Both Code Compiling And Post-build */
/* Following Macros Only For Post Bulid Configuaration */
#define CONFIG_DB_UPDATE_DATA_GENERATE_EN 0 //是否生成db_data.bin(用于第三方协议接入使用)
#define CONFIG_ONLY_GRENERATE_ALIGN_4K_CODE 0 //ufw只生成1份4K对齐的代码
//config for supported chip version
#ifdef CONFIG_BR30_C_VERSION
#define CONFIG_SUPPORTED_CHIP_VERSION C
#else
#define CONFIG_SUPPORTED_CHIP_VERSION B,D,E,M,N,O,P
#endif
//DON'T MODIFY THIS CONFIG EXCEPT SDK PUBLISHER
#define CONFIG_CHIP_NAME AC701N //除了SDK发布者,请不要修改
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_PID AC701N //烧写或强制升级之前可以修改,之后升级要保持一致
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_VID 0.01 //烧写或强制升级之前可以修改,之后升级要保持一致
//Project with bluetooth,it must use OSC as PLL_SOURCE;
#define CONFIG_PLL_SOURCE_USING_LRC 0 //PLL时钟源选择 1:LRC 2:OSC
//config alignment size unit
#ifdef CONFIG_256K_FLASH
#define ALIGN_UNIT_256B 1 //FLASH对齐方式选择如果是256K的FLASH选择256BYTE对齐方式
#else
#define ALIGN_UNIT_256B 0
#endif
//partial platform check this config to select the uart IO for wired update
#define CONFIG_UART_UPDATE_PIN PP00
//isd_download loader/uboot/update_loader debug io config
//#define CONFIG_UBOOT_DEBUG_PIN PA05
//#define CONFIG_UBOOT_DEBUG_BAUD_RATE 1000000
//config long-press reset io pin,time,trigger level
#define CONFIG_RESET_PIN LDO //io pin
#define CONFIG_RESET_TIME 04 //unit:second
#define CONFIG_RESET_LEVEL 1 //tigger level(0/1)
#if CONFIG_IO_KEY_EN
#define CONFIG_SUPPORT_RESET1
#define CONFIG_RESET1_PIN PB01 //io pin
#define CONFIG_RESET1_TIME 08 //unit:second
#define CONFIG_RESET1_LEVEL 0 //tigger level(0/1)
#endif
//reserved three custom cfg item for the future definition
//#define CONFIG_CUSTOM_CFG1_TYPE POWER_PIN
//#define CONFIG_CUSTOM_CFG1_VALUE PC01_1
//#define CONFIG_CUSTOM_CFG2_TYPE
//#define CONFIG_CUSTOM_CFG2_VALUE
//#define CONFIG_CUSTOM_CFG3_TYPE
//#define CONFIG_CUSTOM_CFG3_VALUE
//#define CONFIG_VDDIO_LVD_LEVEL 4 ////VDDIO_LVD挡位0: 1.9V 1: 2.0V 2: 2.1V 3: 2.2V 4: 2.3V 5: 2.4V 6: 2.5V 7: 2.6V
//with single-bank mode,actual vm size should larger this VM_LEAST_SIZE,and dual bank mode,actual vm size equals this;
#define CONFIG_VM_LEAST_SIZE 8K
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_VM_OPT 1
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_BTIF_OPT 1
//reserved two custom cfg area for the future definition
//#define CONFIG_RESERVED_AREA1 EXIF1
#ifdef CONFIG_RESERVED_AREA1
#define CONFIG_RESERVED_AREA1_ADDR AUTO
#define CONFIG_RESERVED_AREA1_LEN 0x1000
#define CONFIG_RESERVED_AREA1_OPT 1
//#define CONFIG_RESERVED_AREA1_FILE anc_gains.bin
#endif
//#define CONFIG_RESERVED_AREA2 EXIF2
#ifdef CONFIG_RESERVED_AREA2
#define CONFIG_RESERVED_AREA2_ADDR AUTO
#define CONFIG_RESERVED_AREA2_LEN 0x1000
#define CONFIG_RESERVED_AREA2_OPT 1
//#define CONFIG_RESERVED_AREA2_FILE anc_gains.bin
#endif
/* Above Macros Only For Post Bulid Configuaration */
#endif /* #ifdef CONFIG_BOARD_JL701N_BTEMITTER */
#endif /* #ifndef CONFIG_BOARD_JL701N_BTEMITTER_BUILD_CFG_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,123 +0,0 @@
#ifndef CONFIG_BOARD_JL701N_DEMO_POST_BUILD_CFG_H
#define CONFIG_BOARD_JL701N_DEMO_POST_BUILD_CFG_H
/* 改文件只添加和isd_config.ini相关的配置用以生成isd_config.ini */
/* 其他不相关的配置请勿添加在改文件 */
#ifdef CONFIG_BOARD_JL701N_DEMO
/* Following Macros Affect Periods Of Both Code Compiling And Post-build */
#define CONFIG_DOUBLE_BANK_ENABLE 0 //单双备份选择(若打开了改宏,FLASH结构变为双备份结构适用于接入第三方协议的OTA PS: JL-OTA同样支持双备份升级, 需要根据实际FLASH大小同时配置CONFIG_FLASH_SIZE)
#define CONFIG_APP_OTA_ENABLE 0 //是否支持RCSP升级(JL-OTA)
#define CONFIG_UPDATE_JUMP_TO_MASK 0 //配置升级到loader的方式0为直接reset,1为跳转(适用于芯片电源由IO口KEEP住的方案,需要注意检查跳转前是否将使用DMA的硬件模块全部关闭)
#define CONFIG_IO_KEY_EN 0 //配置是否使用IO按键配合RESET1
#define CONFIG_UPDATE_WITH_MD5_CHECK_EN 0 //配置升级是否支持MD5校验
#define CONFIG_ANC_ENABLE 0 //配置是否支持ANC
//flash size vaule definition
#define FLASH_SIZE_256K 0x40000
#define FLASH_SIZE_512K 0x80000
#define FLASH_SIZE_1M 0x100000
#define FLASH_SIZE_2M 0x200000
#define FLASH_SIZE_4M 0x400000
#define CONFIG_FLASH_SIZE FLASH_SIZE_1M //配置FLASH大小
/* Above Macros Affect Periods Of Both Code Compiling And Post-build */
/* Following Macros Only For Post Bulid Configuaration */
#define CONFIG_DB_UPDATE_DATA_GENERATE_EN 0 //是否生成db_data.bin(用于第三方协议接入使用)
#define CONFIG_ONLY_GRENERATE_ALIGN_4K_CODE 0 //ufw只生成1份4K对齐的代码
//config for supported chip version
#ifdef CONFIG_BR30_C_VERSION
#define CONFIG_SUPPORTED_CHIP_VERSION C
#else
#define CONFIG_SUPPORTED_CHIP_VERSION B,D,E,M,N,O,P
#endif
//DON'T MODIFY THIS CONFIG EXCEPT SDK PUBLISHER
#define CONFIG_CHIP_NAME AC701N //除了SDK发布者,请不要修改
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_PID AC701N //烧写或强制升级之前可以修改,之后升级要保持一致
//it can be modified before first programming,but keep the same as the original version
#define CONFIG_VID 0.01 //烧写或强制升级之前可以修改,之后升级要保持一致
//Project with bluetooth,it must use OSC as PLL_SOURCE;
#define CONFIG_PLL_SOURCE_USING_LRC 0 //PLL时钟源选择 1:LRC 2:OSC
//config alignment size unit
#ifdef CONFIG_256K_FLASH
#define ALIGN_UNIT_256B 1 //FLASH对齐方式选择如果是256K的FLASH选择256BYTE对齐方式
#else
#define ALIGN_UNIT_256B 0
#endif
//partial platform check this config to select the uart IO for wired update
#define CONFIG_UART_UPDATE_PIN PP00
//isd_download loader/uboot/update_loader debug io config
//#define CONFIG_UBOOT_DEBUG_PIN PA05
//#define CONFIG_UBOOT_DEBUG_BAUD_RATE 1000000
//config long-press reset io pin,time,trigger level
#define CONFIG_RESET_PIN LDO //io pin
#define CONFIG_RESET_TIME 04 //unit:second
#define CONFIG_RESET_LEVEL 1 //tigger level(0/1)
#if CONFIG_IO_KEY_EN
#define CONFIG_SUPPORT_RESET1
#define CONFIG_RESET1_PIN PB01 //io pin
#define CONFIG_RESET1_TIME 08 //unit:second
#define CONFIG_RESET1_LEVEL 0 //tigger level(0/1)
#endif
//reserved three custom cfg item for the future definition
//#define CONFIG_CUSTOM_CFG1_TYPE POWER_PIN
//#define CONFIG_CUSTOM_CFG1_VALUE PC01_1
//#define CONFIG_CUSTOM_CFG2_TYPE
//#define CONFIG_CUSTOM_CFG2_VALUE
//#define CONFIG_CUSTOM_CFG3_TYPE
//#define CONFIG_CUSTOM_CFG3_VALUE
//#define CONFIG_VDDIO_LVD_LEVEL 4 ////VDDIO_LVD挡位0: 1.9V 1: 2.0V 2: 2.1V 3: 2.2V 4: 2.3V 5: 2.4V 6: 2.5V 7: 2.6V
//with single-bank mode,actual vm size should larger this VM_LEAST_SIZE,and dual bank mode,actual vm size equals this;
#define CONFIG_VM_LEAST_SIZE 8K
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_VM_OPT 1
//config whether erased this area when do a update,1-No Operation,0-Erase
#define CONFIG_BTIF_OPT 1
//reserved two custom cfg area for the future definition
//#define CONFIG_RESERVED_AREA1 EXIF1
#ifdef CONFIG_RESERVED_AREA1
#define CONFIG_RESERVED_AREA1_ADDR AUTO
#define CONFIG_RESERVED_AREA1_LEN 0x1000
#define CONFIG_RESERVED_AREA1_OPT 1
//#define CONFIG_RESERVED_AREA1_FILE anc_gains.bin
#endif
//#define CONFIG_RESERVED_AREA2 EXIF2
#ifdef CONFIG_RESERVED_AREA2
#define CONFIG_RESERVED_AREA2_ADDR AUTO
#define CONFIG_RESERVED_AREA2_LEN 0x1000
#define CONFIG_RESERVED_AREA2_OPT 1
//#define CONFIG_RESERVED_AREA2_FILE anc_gains.bin
#endif
/* Above Macros Only For Post Bulid Configuaration */
#endif /* #ifdef CONFIG_BOARD_JL701N_DEMO */
#endif /* #ifndef CONFIG_BOARD_JL701N_DEMO_POST_BUILD_CFG_H */

View File

@ -1,418 +0,0 @@
#include "system/includes.h"
#include "media/includes.h"
#include "tone_player.h"
#include "earphone.h"
#include "app_config.h"
#include "app_action.h"
#include "app_task.h"
#include "btstack/avctp_user.h"
#include "btstack/btstack_task.h"
#include "btctrler/btctrler_task.h"
#include "btstack/frame_queque.h"
#include "user_cfg.h"
// #include "aec_user.h"
#include "classic/hci_lmp.h"
#include "bt_common.h"
#include "bt_ble.h"
#include "bt_tws.h"
#include "pbg_user.h"
#include "btstack/bluetooth.h"
#include "colorful_lights/colorful_lights.h"
#include "app_chargestore.h"
#include "jl_kws/jl_kws_api.h"
#include "asm/charge.h"
#include "app_charge.h"
#include "ui_manage.h"
#include "app_chargestore.h"
#include "app_umidigi_chargestore.h"
#include "app_testbox.h"
#include "app_online_cfg.h"
#include "app_main.h"
#include "app_power_manage.h"
#include "gSensor/gSensor_manage.h"
#include "key_event_deal.h"
#include "classic/tws_api.h"
#include "asm/pwm_led.h"
#include "ir_sensor/ir_manage.h"
#include "in_ear_detect/in_ear_manage.h"
#include "vol_sync.h"
#include "bt_background.h"
#include "default_event_handler.h"
#if(USE_DMA_UART_TEST) //使用dm串口测试时不能同时打开
#define MY_SNIFF_EN 0
#else
#define MY_SNIFF_EN 1 //默认打开
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
//变量
u8 init_ok = 0;
static u8 sniff_out = 0;
unsigned char xtell_bl_state=0; //存放经典蓝牙的连接状态0断开1是连接
u8 bt_newname =0;
unsigned char xt_ble_new_name[9] = "CM-11111";
static u16 play_poweron_ok_timer_id = 0;
///////////////////////////////////////////////////////////////////////////////////////////////////
u8 get_bt_init_status(void)
{
return init_ok;
}
u8 get_sniff_out_status()
{
return sniff_out;
}
void clear_sniff_out_status()
{
sniff_out = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 模式状态机, 通过start_app()控制状态切换
*/
/* extern int audio_mic_init(); */
static int state_machine(struct application *app, enum app_state state, struct intent *it){
int error = 0;
static u8 tone_player_err = 0;
r_printf("bt_state_machine=%d\n", state);
switch (state) {
case APP_STA_CREATE:
/* set_adjust_conn_dac_check(0); */
STATUS *p_tone = get_tone_config();
tone_play_index(p_tone->bt_init_ok, 1);
break;
case APP_STA_START:
if (!it) {
break;
}
switch (it->action) {
case ACTION_EARPHONE_MAIN:
/*
* earphone 模式初始化
*/
clk_set("sys", BT_NORMAL_HZ);
u32 sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
/* bredr_set_dut_enble(1, 1); */
bt_function_select_init();
bredr_handle_register();
EARPHONE_STATE_INIT();
btstack_init();
sys_auto_shut_down_enable();
bt_sniff_feature_init();
sys_auto_sniff_controle(MY_SNIFF_EN, NULL);
app_var.dev_volume = -1;
break;
case ACTION_A2DP_START: //蓝牙音频传输协议
break;
case ACTION_BY_KEY_MODE:
break;
case ACTION_TONE_PLAY:
STATUS *p_tone = get_tone_config();
tone_play_index(p_tone->bt_init_ok, 1);
break;
case ACTION_DO_NOTHING:
break;
}
break;
case APP_STA_PAUSE:
break;
case APP_STA_RESUME:
//恢复前台运行
sys_auto_shut_down_disable();
sys_key_event_enable();
break;
case APP_STA_STOP:
break;
case APP_STA_DESTROY:
r_printf("APP_STA_DESTROY\n");
if (!app_var.goto_poweroff_flag) {
bt_app_exit(NULL);
}
break;
}
return error;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//handle
static void play_poweron_ok_timer(void *priv)
{
app_var.wait_timer_do = 0;
log_d("\n-------play_poweron_ok_timer-------\n", priv);
if (is_dac_power_off()) {
#if TCFG_USER_TWS_ENABLE
bt_tws_poweron();
#else
bt_wait_connect_and_phone_connect_switch(0);
#endif
return;
}
app_var.wait_timer_do = sys_timeout_add(priv, play_poweron_ok_timer, 100);
}
static void play_bt_connect_dly(void *priv)
{
app_var.wait_timer_do = 0;
log_d("\n-------play_bt_connect_dly-------\n", priv);
if (!app_var.goto_poweroff_flag) {
STATUS *p_tone = get_tone_config();
tone_play_index(p_tone->bt_connect_ok, 1);
}
}
static int bt_connction_status_event_handler(struct bt_event *bt)
{
STATUS *p_tone = get_tone_config();
u8 *phone_number = NULL;
switch (bt->event) {
case BT_STATUS_INIT_OK:
/*
* 蓝牙初始化完成
*/
log_info("BT_STATUS_INIT_OK\n");
init_ok = 1;
__set_sbc_cap_bitpool(38);
#if (TCFG_USER_BLE_ENABLE)
if (BT_MODE_IS(BT_BQB)) {
ble_bqb_test_thread_init();
} else {
#if !TCFG_WIRELESS_MIC_ENABLE
bt_ble_init();
#endif
}
#endif
bt_init_ok_search_index();
#if TCFG_TEST_BOX_ENABLE
testbox_set_bt_init_ok(1);
#endif
#if ((CONFIG_BT_MODE == BT_BQB)||(CONFIG_BT_MODE == BT_PER))
bt_wait_phone_connect_control(1);
#else
if (is_dac_power_off()) {
bt_wait_connect_and_phone_connect_switch(0);
} else {
app_var.wait_timer_do = sys_timeout_add(NULL, play_poweron_ok_timer, 100);
}
#endif
/*if (app_var.play_poweron_tone) {
tone_play_index(p_tone->power_on, 1);
}*/
break;
case BT_STATUS_SECOND_CONNECTED:
clear_current_poweron_memory_search_index(0);
case BT_STATUS_FIRST_CONNECTED:
log_info("BT_STATUS_CONNECTED\n");
xtell_bl_state = 1; //蓝牙连接成功 置1
if(strcmp(xt_ble_new_name,"CM-1111") != 0){
//蓝牙连接成功
bt_newname =1;
u8 temp[5]={0xBB,0xBE,0x02,0x04,0x00};
temp[4] = xtell_bl_state; //经典蓝牙连接状态
send_data_to_ble_client(&temp,5);
}
earphone_change_pwr_mode(PWR_DCDC15, 3000);
sys_auto_shut_down_disable();
ui_update_status(STATUS_BT_CONN); //单台在此处设置连接状态,对耳的连接状态需要同步在bt_tws.c中去设置
/* tone_play(TONE_CONN); */
/*os_time_dly(40); // for test*/
log_info("tone status:%d\n", tone_get_status());
if (get_call_status() == BT_CALL_HANGUP) {
if (app_var.phone_dly_discon_time) {
sys_timeout_del(app_var.phone_dly_discon_time);
app_var.phone_dly_discon_time = 0;
} else {
app_var.wait_timer_do = sys_timeout_add(NULL, play_bt_connect_dly, 1600);
/* tone_play_index(p_tone->bt_connect_ok, 1); */
}
}
/*int timeout = 5000 + rand32() % 10000;
sys_timeout_add(NULL, connect_phone_test, timeout);*/
break;
case BT_STATUS_FIRST_DISCONNECT:
case BT_STATUS_SECOND_DISCONNECT:
log_info("BT_STATUS_DISCONNECT\n");
xtell_bl_state = 0; //断开蓝牙 清0
//蓝牙断开连接
if(bt_newname){ //已经改成新蓝牙名字,断开才播报
bt_newname=0;
u8 temp[5]={0xBB,0xBE,0x02,0x04,0x00};
temp[4] = xtell_bl_state; //经典蓝牙连接状态
send_data_to_ble_client(&temp,5);
}
if (app_var.goto_poweroff_flag) {
/*关机不播断开提示音*/
/*关机时不改UI*/
break;
}
bt_discon_dly_handle(NULL);
break;
//phone status deal
case BT_STATUS_PHONE_INCOME:
break;
case BT_STATUS_PHONE_OUT:
break;
case BT_STATUS_PHONE_ACTIVE:
break;
case BT_STATUS_PHONE_HANGUP:
break;
case BT_STATUS_PHONE_NUMBER:
break;
case BT_STATUS_INBAND_RINGTONE: //铃声
break;
case BT_STATUS_CALL_VOL_CHANGE:
break;
case BT_STATUS_SNIFF_STATE_UPDATE:
log_info(" BT_STATUS_SNIFF_STATE_UPDATE %d\n", bt->value); //0退出SNIFF
if (bt->value == 0) {
sniff_out = 1;
sys_auto_sniff_controle(MY_SNIFF_EN, bt->args);
} else {
sys_auto_sniff_controle(0, bt->args);
}
break;
case BT_STATUS_LAST_CALL_TYPE_CHANGE:
break;
case BT_STATUS_CONN_A2DP_CH:
case BT_STATUS_CONN_HFP_CH:
if ((!is_1t2_connection()) && (get_current_poweron_memory_search_index(NULL))) { //回连下一个device
if (get_esco_coder_busy_flag()) {
clear_current_poweron_memory_search_index(0);
} else {
user_send_cmd_prepare(USER_CTRL_START_CONNECTION, 0, NULL);
}
}
break;
case BT_STATUS_PHONE_MANUFACTURER:
break;
case BT_STATUS_VOICE_RECOGNITION:
break;
case BT_STATUS_AVRCP_INCOME_OPID:
#define AVC_VOLUME_UP 0x41
#define AVC_VOLUME_DOWN 0x42
log_info("BT_STATUS_AVRCP_INCOME_OPID:%d\n", bt->value);
if (bt->value == AVC_VOLUME_UP) {
}
if (bt->value == AVC_VOLUME_DOWN) {
}
break;
default:
log_info(" BT STATUS DEFAULT\n");
break;
}
return 0;
}
static int event_handler(struct application *app, struct sys_event *event)
{
if (SYS_EVENT_REMAP(event)) {
g_printf("****SYS_EVENT_REMAP**** \n");
return 0;
}
switch (event->type) {
case SYS_KEY_EVENT:
break;
case SYS_BT_EVENT:
/*
* 蓝牙事件处理
*/
if ((u32)event->arg == SYS_BT_EVENT_TYPE_CON_STATUS) {
printf("in event_handler:bt_connction_status_event_handler");
bt_connction_status_event_handler(&event->u.bt);
} else if ((u32)event->arg == SYS_BT_EVENT_TYPE_HCI_STATUS) {
bt_hci_event_handler(&event->u.bt);
}
break;
case SYS_DEVICE_EVENT:
/*
* 系统设备事件处理
*/
if ((u32)event->arg == DEVICE_EVENT_FROM_CHARGE) {
} else if ((u32)event->arg == DEVICE_EVENT_FROM_POWER) {
return app_power_event_handler(&event->u.dev);
}
#if TCFG_UMIDIGI_BOX_ENABLE
else if ((u32)event->arg == DEVICE_EVENT_UMIDIGI_CHARGE_STORE) {
app_umidigi_chargestore_event_handler(&event->u.umidigi_chargestore);
}
#endif
#if TCFG_TEST_BOX_ENABLE
else if ((u32)event->arg == DEVICE_EVENT_TEST_BOX) {
app_testbox_event_handler(&event->u.testbox);
}
#endif
break;
default:
return false;
}
SYS_EVENT_HANDLER_SPECIFIC(event);
#ifdef CONFIG_BT_BACKGROUND_ENABLE
if (app) {
default_event_handler(event);
}
#endif
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
static const struct application_operation app_handler_ops = {
.state_machine = state_machine,
.event_handler = event_handler,
};
/*
* 注册earphone模式
*/
REGISTER_APPLICATION(app_handler) = {
.name = "handler",
.action = ACTION_EARPHONE_MAIN,
.ops = &app_handler_ops,
.state = APP_STA_DESTROY,
};

View File

@ -1,31 +0,0 @@
#ifndef APP_ACTION_H
#define APP_ACTION_H
#define ACTION_EARPHONE_MAIN 0x0001
#define ACTION_A2DP_START 0x0002
#define ACTION_ESCO_START 0x0003
#define ACTION_BY_KEY_MODE 0x0004
#define ACTION_IDLE_MAIN 0x0010
#define ACTION_IDLE_POWER_OFF 0x0011
#define ACTION_MUSIC_MAIN 0x0020
#define ACTION_MUSIC_TWS_RX 0x0021
#define ACTION_PC_MAIN 0x0030
#define ACTION_AUX_MAIN 0x0040
#define APP_NAME_BT "earphone"
#define APP_NAME_IDLE "idle"
#define APP_NAME_PC "pc"
#define APP_NAME_MUSIC "music"
#define APP_NAME_AUX "aux"
extern void task_switch(const char *name, int action);
#define task_switch_to_bt() task_switch(APP_NAME_BT, ACTION_EARPHONE_MAIN)
#endif

View File

@ -1,12 +0,0 @@
#ifndef _APP_ANCBOX_H_
#define _APP_ANCBOX_H_
#include "typedef.h"
#include "system/event.h"
extern int app_ancbox_event_handler(struct ancbox_event *anc_dev);
extern u8 ancbox_get_status(void);
extern void ancbox_clear_status(void);
#endif //_APP_CHARGESTORE_H_

View File

@ -1,13 +0,0 @@
#ifndef _APP_ANCTOOL_H_
#define _APP_ANCTOOL_H_
#include "typedef.h"
#include "anctool.h"
u8 app_anctool_spp_rx_data(u8 *packet, u16 size);
void app_anctool_spp_connect(void);
void app_anctool_spp_disconnect(void);
u8 get_app_anctool_spp_connected_flag();
#endif //_APP_CHARGESTORE_H_

View File

@ -1,17 +0,0 @@
#ifndef _APP_CHARGE_H_
#define _APP_CHARGE_H_
#include "typedef.h"
#include "system/event.h"
extern void charge_close_deal(void);
extern void charge_start_deal(void);
extern void ldo5v_keep_deal(void);
extern void charge_full_deal(void);
extern void charge_ldo5v_in_deal(void);
extern void charge_ldo5v_off_deal(void);
extern u8 get_charge_full_flag(void);
extern int app_charge_event_handler(struct device_event *dev);
#endif //_APP_CHARGE_H_

View File

@ -1,78 +0,0 @@
#ifndef _APP_CHARGESTORE_H_
#define _APP_CHARGESTORE_H_
#include "typedef.h"
#include "system/event.h"
//LDOIN升级口命令定义
#define CMD_TWS_CHANNEL_SET 0x01
#define CMD_TWS_REMOTE_ADDR 0x02
#define CMD_TWS_ADDR_DELETE 0x03
#define CMD_BOX_TWS_CHANNEL_SEL 0x04//测试盒获取地址
#define CMD_BOX_TWS_REMOTE_ADDR 0x05//测试盒交换地址
#define CMD_POWER_LEVEL_OPEN 0x06//开盖充电舱报告/获取电量
#define CMD_POWER_LEVEL_CLOSE 0x07//合盖充电舱报告/获取电量
#define CMD_RESTORE_SYS 0x08//恢复出厂设置
#define CMD_ENTER_DUT 0x09//进入测试模式
#define CMD_EX_FIRST_READ_INFO 0x0A//F95读取数据首包信息
#define CMD_EX_CONTINUE_READ_INFO 0x0B//F95读取数据后续包信息
#define CMD_EX_FIRST_WRITE_INFO 0x0C//F95写入数据首包信息
#define CMD_EX_CONTINUE_WRITE_INFO 0x0D//F95写入数据后续包信息
#define CMD_EX_INFO_COMPLETE 0x0E//F95完成信息交换
#define CMD_TWS_SET_CHANNEL 0x0F//F95设置左右声道信息
#define CMD_BOX_UPDATE 0x20//测试盒升级
#define CMD_BOX_MODULE 0x21//测试盒一级命令
#define CMD_SHUT_DOWN 0x80//充电舱关机,充满电关机,或者是低电关机
#define CMD_CLOSE_CID 0x81//充电舱盒盖
#define CMD_ANC_MODULE 0x90//ANC一级命令
#define CMD_FAIL 0xfe//失败
#define CMD_UNDEFINE 0xff//未知命令回复
enum {
TWS_CHANNEL_LEFT = 1, //左耳
TWS_CHANNEL_RIGHT, //右耳
};
enum {
TWS_DEL_TWS_ADDR = 1, //删除对箱地址
TWS_DEL_PHONE_ADDR,//删除手机地址
TWS_DEL_ALL_ADDR,//删除手机与对箱地址
};
struct _CHARGE_STORE_INFO {
u8 tws_local_addr[6];
u8 tws_remote_addr[6];
u8 tws_mac_addr[6];
u32 search_aa;
u32 pair_aa;
u8 local_channel;
u16 device_ind;
u16 reserved_data;
} _GNU_PACKED_;
typedef struct _CHARGE_STORE_INFO CHARGE_STORE_INFO;
extern void chargestore_set_tws_channel_info(u8 channel);
extern bool chargestore_set_tws_remote_info(u8 *data, u8 len);
extern u16 chargestore_get_tws_remote_info(u8 *data);
extern u8 chargestore_get_power_level(void);
extern u8 chargestore_get_power_status(void);
extern u8 chargestore_get_cover_status(void);
extern u8 chargestore_get_sibling_power_level(void);
extern void chargestore_set_bt_init_ok(u8 flag);
extern int app_chargestore_event_handler(struct chargestore_event *chargestore_dev);
extern u8 chargestore_get_earphone_online(void);
extern u8 chargestore_get_earphone_pos(void);
extern int chargestore_sync_chg_level(void);
extern void chargestore_set_power_level(u8 power);
extern void chargestore_set_sibling_chg_lev(u8 chg_lev);
extern void chargestore_set_phone_disconnect(void);
extern void chargestore_set_phone_connect(void);
extern u8 chargestore_check_going_to_poweroff(void);
extern void chargestore_shutdown_reset(void);
extern void testbox_set_testbox_tws_paired(u8 flag);
extern u8 testbox_get_testbox_tws_paired(void);
extern u8 testbox_get_touch_trim_en(u8 *sec);
extern u8 testbox_get_softpwroff_after_paired(void);
#endif //_APP_CHARGESTORE_H_

View File

@ -1,555 +0,0 @@
#ifndef APP_CONFIG_H
#define APP_CONFIG_H
/*
* 系统打印总开关
*/
#define LIB_DEBUG 1 //1 //xtelllog 1是打开库打印
#define CONFIG_DEBUG_LIB(x) (x & LIB_DEBUG)
#define CONFIG_DEBUG_ENABLE //xtelllog 注释就关闭log
#ifndef CONFIG_DEBUG_ENABLE
//#define CONFIG_DEBUG_LITE_ENABLE //轻量级打印开关, 默认关闭
#endif
//*********************************************************************************//
// AI配置 //
//*********************************************************************************//
#define CONFIG_APP_BT_ENABLE
#ifdef CONFIG_APP_BT_ENABLE
#define TRANS_DATA_EN 0
#define RCSP_BTMATE_EN 0
#define RCSP_ADV_EN 1
#define AI_APP_PROTOCOL 0
#define LL_SYNC_EN 0
#define TUYA_DEMO_EN 0
#else
#define TRANS_DATA_EN 1
#define RCSP_BTMATE_EN 0
#define RCSP_ADV_EN 0
#define AI_APP_PROTOCOL 0
#define LL_SYNC_EN 0
#define TUYA_DEMO_EN 0
#endif
#include "board_config.h"
#ifdef TCFG_AUDIO_CVP_NS_MODE
#if (TCFG_AUDIO_CVP_NS_MODE==CVP_DNS_MODE) || (TCFG_KWS_VOICE_RECOGNITION_ENABLE == 1)||(TCFG_AUDIO_ANC_ENABLE==1&&TCFG_AUDIO_DUAL_MIC_ENABLE==1)
#undef CONFIG_MOVABLE_ENABLE
#define CONFIG_MOVABLE_ENABLE //省ram空间将部分ram空间的代码挪到flash
#endif
#endif
#if defined(TCFG_CVP_DEVELOP_ENABLE) && (TCFG_CVP_DEVELOP_ENABLE == 1)
#undef CONFIG_MOVABLE_ENABLE
#define CONFIG_MOVABLE_ENABLE //省ram空间将部分ram空间的代码挪到flash
#endif
#if CONFIG_UPDATE_WITH_MD5_CHECK_EN
#define UPDATE_MD5_ENABLE 1
#else
#define UPDATE_MD5_ENABLE 0
#endif
//升级需要打开CONFIG_APP_OTA_ENABLE,同时单双备份的配置也在board_xxx_global_cfg里配置需要注意只有JL_RCSP才支持单备份其余升级都是只支持双备份升级
//支持TWS同步升级OTA_TWS_SAME_TIME_NEW宏需要配置为1旧的流程已不再支持
#if (RCSP_ADV_EN) //rcsp需要打开ble
#define JL_EARPHONE_APP_EN 1
// #define CONFIG_CHARGESTORE_REMAP_ENABLE //充电仓重映射接收函数使能
#if CONFIG_APP_OTA_ENABLE
#define RCSP_UPDATE_EN 1 //是否支持rcsp升级
#if CONFIG_DOUBLE_BANK_ENABLE //双备份才能打开同步升级流程
#define OTA_TWS_SAME_TIME_ENABLE 1 //是否支持TWS同步升级
#define OTA_TWS_SAME_TIME_NEW 1 //使用新的tws ota流程
#else
#define OTA_TWS_SAME_TIME_ENABLE 1//0 xtellota //是否支持TWS同步升级
#define OTA_TWS_SAME_TIME_NEW 1//0 //使用新的tws ota流程
#endif //CONFIG_DOUBLE_BANK_ENABLE
#else
#define RCSP_UPDATE_EN 0 //是否支持rcsp升级
#define OTA_TWS_SAME_TIME_ENABLE 0 //是否支持TWS同步升级
#define OTA_TWS_SAME_TIME_NEW 0 //使用新的tws ota流程
#endif
#undef TCFG_USER_BLE_ENABLE
#define TCFG_USER_BLE_ENABLE 1 //BLE功能使能
#elif (AI_APP_PROTOCOL)
#define BT_MIC_EN 0
#define OTA_TWS_SAME_TIME_ENABLE 0 //是否支持TWS同步升级
#elif (LL_SYNC_EN)
#define JL_EARPHONE_APP_EN 0
#define OTA_TWS_SAME_TIME_ENABLE 1
#define OTA_TWS_SAME_TIME_NEW 1 //使用新的tws ota流程
#elif (TUYA_DEMO_EN)
#define JL_EARPHONE_APP_EN 0
#define OTA_TWS_SAME_TIME_ENABLE 0
#define OTA_TWS_SAME_TIME_NEW 0 //使用新的tws ota流程
#else
#define JL_EARPHONE_APP_EN 0
#define OTA_TWS_SAME_TIME_ENABLE 0
#define OTA_TWS_SAME_TIME_NEW 0 //使用新的tws ota流程
#endif
#define CONFIG_MEDIA_LIB_USE_MALLOC 1
#include "usb_std_class_def.h"
#undef USB_MALLOC_ENABLE
#define USB_MALLOC_ENABLE 1
///USB 配置重定义
// #undef USB_DEVICE_CLASS_CONFIG
// #define USB_DEVICE_CLASS_CONFIG (AUDIO_CLASS)
/////要确保 上面 undef 后在include usb
#include "usb_common_def.h"
#define USB_PC_NO_APP_MODE 0
#if ((TCFG_CHARGESTORE_ENABLE || TCFG_TEST_BOX_ENABLE || TCFG_ANC_BOX_ENABLE) \
&& TCFG_CHARGESTORE_PORT == IO_PORT_DP)
#undef TCFG_OTG_MODE
#define TCFG_OTG_MODE (TCFG_OTG_MODE_HOST|TCFG_OTG_MODE_SLAVE|TCFG_OTG_MODE_CHARGE|OTG_DET_DM_ONLY)
#endif
#include "btcontroller_mode.h"
#include "user_cfg_id.h"
#ifndef __LD__
#include "bt_profile_cfg.h"
#endif
// #ifdef CONFIG_APP_BT_ENABLE
// #if(APP_ONLINE_DEBUG)
// #error "they can not enable at the same time,just select one!!!"
// #endif
// #endif
#ifdef CONFIG_SDFILE_ENABLE
#define SDFILE_DEV "sdfile"
#define SDFILE_MOUNT_PATH "mnt/sdfile"
#if (USE_SDFILE_NEW)
#define SDFILE_APP_ROOT_PATH SDFILE_MOUNT_PATH"/app/" //app分区
#define SDFILE_RES_ROOT_PATH SDFILE_MOUNT_PATH"/res/" //资源文件分区
#else
#define SDFILE_RES_ROOT_PATH SDFILE_MOUNT_PATH"/C/"
#endif
#endif
#define STYLE_JL_WTACH (1)//彩屏demo
#define STYLE_JL_SOUNDBOX (2)//点阵屏demo
#define STYLE_JL_CHARGE (3)//点阵屏充电仓
#define STYLE_JL_LED7 (4)//led7
#define STYLE_UI_SIMPLE (5)//没有ui框架
//*********************************************************************************//
// 测试模式配置 //
//*********************************************************************************//
#if (CONFIG_BT_MODE != BT_NORMAL)
#undef TCFG_BD_NUM
#define TCFG_BD_NUM 1
#undef TCFG_USER_TWS_ENABLE
#define TCFG_USER_TWS_ENABLE 0 //tws功能使能
#undef TCFG_USER_BLE_ENABLE
#define TCFG_USER_BLE_ENABLE 1 //BLE功能使能
#undef TCFG_AUTO_SHUT_DOWN_TIME
#define TCFG_AUTO_SHUT_DOWN_TIME 0
#undef TCFG_SYS_LVD_EN
#define TCFG_SYS_LVD_EN 0
#undef TCFG_LOWPOWER_LOWPOWER_SEL
#define TCFG_LOWPOWER_LOWPOWER_SEL 0
#undef TCFG_AUDIO_DAC_LDO_VOLT
#define TCFG_AUDIO_DAC_LDO_VOLT DUT_AUDIO_DAC_LDO_VOLT
#undef TCFG_LOWPOWER_POWER_SEL
#define TCFG_LOWPOWER_POWER_SEL PWR_LDO15
#undef TCFG_PWMLED_ENABLE
#define TCFG_PWMLED_ENABLE DISABLE_THIS_MOUDLE
#undef TCFG_ADKEY_ENABLE
#define TCFG_ADKEY_ENABLE DISABLE_THIS_MOUDLE
#undef TCFG_IOKEY_ENABLE
#define TCFG_IOKEY_ENABLE DISABLE_THIS_MOUDLE
#undef TCFG_TEST_BOX_ENABLE
#define TCFG_TEST_BOX_ENABLE 0
#undef TCFG_AUTO_SHUT_DOWN_TIME
#define TCFG_AUTO_SHUT_DOWN_TIME 0
#undef TCFG_POWER_ON_NEED_KEY
#define TCFG_POWER_ON_NEED_KEY 0
/* #undef TCFG_UART0_ENABLE
#define TCFG_UART0_ENABLE DISABLE_THIS_MOUDLE */
dzfghsdfhgsfgh
#endif //(CONFIG_BT_MODE != BT_NORMAL)
#if TCFG_USER_TWS_ENABLE
#define CONFIG_TWS_COMMON_ADDR_AUTO 0 /* 自动生成TWS配对后的MAC地址 */
#define CONFIG_TWS_COMMON_ADDR_USED_LEFT 1 /* 使用左耳的MAC地址作为TWS配对后的地址
可配合烧写器MAC地址自增功能一起使用
多台交叉配对会出现MAC地址相同情况 */
#define CONFIG_TWS_COMMON_ADDR_SELECT CONFIG_TWS_COMMON_ADDR_AUTO
//*********************************************************************************//
// 对耳配置方式配置 //
//*********************************************************************************//
#define CONFIG_TWS_CONNECT_SIBLING_TIMEOUT 4 /* 开机或超时断开后对耳互连超时时间单位s */
// #define CONFIG_TWS_REMOVE_PAIR_ENABLE [> 不连手机的情况下双击按键删除配对信息 <]
#define CONFIG_TWS_POWEROFF_SAME_TIME 1 /*按键关机时两个耳机同时关机*/
#define ONE_KEY_CTL_DIFF_FUNC 1 /*通过左右耳实现一个按键控制两个功能*/
#define CONFIG_TWS_SCO_ONLY_MASTER 0 /*通话的时候只有主机出声音*/
/* 配对方式选择 */
#define CONFIG_TWS_PAIR_BY_CLICK 0 /* 按键发起配对 */
#define CONFIG_TWS_PAIR_BY_AUTO 1 /* 开机自动配对 */
#define CONFIG_TWS_PAIR_BY_FAST_CONN 2 /* 开机快速连接,连接速度比自动配对快,不支持取消配对操作 */
#define CONFIG_TWS_PAIR_MODE CONFIG_TWS_PAIR_BY_AUTO
/* 声道确定方式选择 */
#define CONFIG_TWS_MASTER_AS_LEFT 0 //主机作为左耳
#define CONFIG_TWS_AS_LEFT_CHANNEL 1 //固定左耳
#define CONFIG_TWS_AS_RIGHT_CHANNEL 2 //固定右耳
#define CONFIG_TWS_LEFT_START_PAIR 3 //双击发起配对的耳机做左耳
#define CONFIG_TWS_RIGHT_START_PAIR 4 //双击发起配对的耳机做右耳
#define CONFIG_TWS_EXTERN_UP_AS_LEFT 5 //外部有上拉电阻作为左耳
#define CONFIG_TWS_EXTERN_DOWN_AS_LEFT 6 //外部有下拉电阻作为左耳
#define CONFIG_TWS_SECECT_BY_CHARGESTORE 7 //充电仓决定左右耳
#define CONFIG_TWS_CHANNEL_SELECT CONFIG_TWS_LEFT_START_PAIR //配对方式选择
#define CONFIG_TWS_CHANNEL_CHECK_IO IO_PORTA_07 //上下拉电阻检测引脚
#if CONFIG_TWS_PAIR_MODE != CONFIG_TWS_PAIR_BY_CLICK
#if (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_LEFT_START_PAIR) ||\
(CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_RIGHT_START_PAIR)
#undef CONFIG_TWS_CHANNEL_SELECT
#define CONFIG_TWS_CHANNEL_SELECT CONFIG_TWS_MASTER_AS_LEFT
#endif
#if CONFIG_TWS_PAIR_MODE == CONFIG_TWS_PAIR_BY_AUTO
#define CONFIG_TWS_AUTO_PAIR_WITHOUT_UNPAIR /* 不取消配对也可以配对新的耳机 */
#endif
#if CONFIG_TWS_PAIR_MODE == CONFIG_TWS_PAIR_BY_FAST_CONN
#undef CONFIG_TWS_REMOVE_PAIR_ENABLE
#endif
#endif
#define CONFIG_A2DP_GAME_MODE_ENABLE 0 //游戏模式
#define CONFIG_A2DP_GAME_MODE_DELAY_TIME 35 //游戏模式延时ms
//*********************************************************************************//
// 低延时游戏模式脚步声、枪声增强,需使能蓝牙音乐10段eq以及蓝牙音乐drc
// 用户开关宏AUDIO_GAME_EFFECT_CONFIG(开关蓝牙低延时模式的游戏音效)
// 低延时eq效果文件使用eq_game_eff.bin,调试时需保存成该文件,并在批处理-res后添加
// 非低延时eq效果文件使用eq_cfg_hw.bin,也需在批处理-res后添加
//*********************************************************************************//
#if CONFIG_A2DP_GAME_MODE_ENABLE
#define AUDIO_GAME_EFFECT_CONFIG 1 //低延时游戏模式脚步声、枪声增强 1:使能、0关闭
#else
#define AUDIO_GAME_EFFECT_CONFIG 0 //低延时游戏模式脚步声、枪声增强 1:使能、0关闭
#endif
/***********************************非用户配置区***********************************/
//以下宏定义,是游戏音效使能后,需要开启的宏定义,已配好,用户不用修改
#if AUDIO_GAME_EFFECT_CONFIG
#if (TCFG_EQ_ENABLE == 0)
#undef TCFG_EQ_ENABLE
#define TCFG_EQ_ENABLE 1
#endif/* (TCFG_EQ_ENABLE == 0) */
#if (TCFG_BT_MUSIC_EQ_ENABLE == 0)
#undef TCFG_BT_MUSIC_EQ_ENABLE
#define TCFG_BT_MUSIC_EQ_ENABLE 1
#endif/*(TCFG_BT_MUSIC_EQ_ENABLE == 0)*/
#if (TCFG_DRC_ENABLE == 0)
#undef TCFG_DRC_ENABLE
#define TCFG_DRC_ENABLE 1
#endif/* (TCFG_DRC_ENABLE == 0) */
#if (TCFG_BT_MUSIC_DRC_ENABLE == 0)
#undef TCFG_BT_MUSIC_DRC_ENABLE
#define TCFG_BT_MUSIC_DRC_ENABLE 1
#endif/* (TCFG_BT_MUSIC_DRC_ENABLE == 0) */
#if (EQ_SECTION_MAX < 10)
#undef EQ_SECTION_MAX
#define EQ_SECTION_MAX 10
#endif/* (EQ_SECTION_MAX < 10) */
#if (TCFG_USE_EQ_FILE == 0)
#undef TCFG_USE_EQ_FILE
#define TCFG_USE_EQ_FILE 1
#endif/* TCFG_USE_EQ_FILE */
#endif/* AUDIO_GAME_EFFECT_CONFIG */
/**********************************************************************************/
#if CONFIG_TWS_PAIR_MODE == CONFIG_TWS_PAIR_BY_CLICK
#define CONFIG_TWS_BY_CLICK_PAIR_WITHOUT_PAIR /*双击按键可以配对已配对过的样机,即交叉配对 */
#ifdef CONFIG_TWS_BY_CLICK_PAIR_WITHOUT_PAIR
#define CONFIG_TWS_AUTO_PAIR_WITHOUT_UNPAIR /* 不取消配对也可以配对新的耳机 */
#endif
#endif
#if TCFG_CHARGESTORE_ENABLE
#undef CONFIG_TWS_CHANNEL_SELECT
#define CONFIG_TWS_CHANNEL_SELECT CONFIG_TWS_SECECT_BY_CHARGESTORE //充电仓区分左右
#endif //TCFG_CHARGESTORE_ENABLE
#if TCFG_TEST_BOX_ENABLE && (!TCFG_CHARGESTORE_ENABLE)
#define CONFIG_TWS_SECECT_CHARGESTORE_PRIO 1 //测试盒配置左右耳优先
#else
#define CONFIG_TWS_SECECT_CHARGESTORE_PRIO 0
#endif //TCFG_TEST_BOX_ENABLE
//*********************************************************************************//
// 对耳电量显示方式 //
//*********************************************************************************//
#if BT_SUPPORT_DISPLAY_BAT
#define CONFIG_DISPLAY_TWS_BAT_LOWER 1 //对耳手机端电量显示,显示低电量耳机的电量
#define CONFIG_DISPLAY_TWS_BAT_HIGHER 2 //对耳手机端电量显示,显示高电量耳机的电量
#define CONFIG_DISPLAY_TWS_BAT_LEFT 3 //对耳手机端电量显示,显示左耳的电量
#define CONFIG_DISPLAY_TWS_BAT_RIGHT 4 //对耳手机端电量显示,显示右耳的电量
#define CONFIG_DISPLAY_TWS_BAT_TYPE CONFIG_DISPLAY_TWS_BAT_LOWER
#endif //BT_SUPPORT_DISPLAY_BAT
#define CONFIG_DISPLAY_DETAIL_BAT 0 //BLE广播显示具体的电量
#define CONFIG_NO_DISPLAY_BUTTON_ICON 1 //BLE广播不显示按键界面,智能充电仓置1
#endif //TCFG_USER_TWS_ENABLE
#ifndef CONFIG_BT_RX_BUFF_SIZE
#define CONFIG_BT_RX_BUFF_SIZE (14 * 1024)
#endif
#ifdef CONFIG_APP_BT_ENABLE
#if TCFG_BT_SUPPORT_AAC || TCFG_BT_SUPPORT_LDAC
#define CONFIG_BT_TX_BUFF_SIZE (5 * 1024)
#else
#define CONFIG_BT_TX_BUFF_SIZE (4 * 1024)
#endif
#else
#if TCFG_BT_SUPPORT_AAC || TCFG_BT_SUPPORT_LDAC
#define CONFIG_BT_TX_BUFF_SIZE (4 * 1024)
#else
#define CONFIG_BT_TX_BUFF_SIZE (3 * 1024)
#endif
#endif
#ifndef CONFIG_NEW_BREDR_ENABLE
#if TCFG_USER_TWS_ENABLE
#ifdef CONFIG_LOCAL_TWS_ENABLE
#define CONFIG_TWS_BULK_POOL_SIZE (4 * 1024)
#else
#define CONFIG_TWS_BULK_POOL_SIZE (2 * 1024)
#endif
#endif
#endif
#if (CONFIG_BT_MODE != BT_NORMAL)
////bqb 如果测试3M tx buf 最好加大一点
#undef CONFIG_BT_TX_BUFF_SIZE
#define CONFIG_BT_TX_BUFF_SIZE (6 * 1024)
#endif
//*********************************************************************************//
// 电源切换配置 //
//*********************************************************************************//
#define PHONE_CALL_USE_LDO15 CONFIG_PHONE_CALL_USE_LDO15
//*********************************************************************************//
// 时钟切换配置 //
//*********************************************************************************//
#define BT_NORMAL_HZ CONFIG_BT_NORMAL_HZ
#define BT_CONNECT_HZ CONFIG_BT_CONNECT_HZ
#define BT_A2DP_HZ CONFIG_BT_A2DP_HZ
#define BT_CALL_HZ CONFIG_BT_CALL_HZ
#define BT_CALL_ADVANCE_HZ CONFIG_BT_CALL_ADVANCE_HZ
#define BT_CALL_16k_HZ CONFIG_BT_CALL_16k_HZ
#define BT_CALL_16k_ADVANCE_HZ CONFIG_BT_CALL_16k_ADVANCE_HZ
#define MUSIC_DEC_FASTEST_CLOCK CONFIG_MUSIC_DEC_FASTEST_CLOCK
#define MUSIC_DEC_FAST_CLOCK CONFIG_MUSIC_DEC_FAST_CLOCK
#define MUSIC_DEC_CLOCK CONFIG_MUSIC_DEC_CLOCK
#define MUSIC_DEC_IDLE_CLOCK CONFIG_MUSIC_IDLE_CLOCK
#define MUSIC_FSCAN_CLOCK CONFIG_MUSIC_FSCAN_CLOCK
#define LINEIN_CLOCK CONFIG_LINEIN_CLOCK
#define FM_CLOCK CONFIG_FM_CLOCK
#define FM_EMITTER_CLOCK CONFIG_FM_EMITTER_CLOCK
#define PC_CLOCK CONFIG_PC_CLOCK
#define RECODRD_CLOCK CONFIG_RECORD_CLOCK
#define SPDIF_CLOCK CONFIG_SPDIF_CLOCK
////////////////////////
#if TCFG_BT_SUPPORT_AAC || TCFG_BT_SUPPORT_LDAC
#define BT_A2DP_STEREO_EQ_HZ 48 * 1000000L
#else
#define BT_A2DP_STEREO_EQ_HZ 32 * 1000000L
#endif
#define BT_A2DP_AAC_HZ 48 * 1000000L
#define BT_A2DP_TWS_AAC_HZ 64 * 1000000L
#define BT_A2DP_MONO_EQ_HZ 32 * 1000000L
#define BT_A2DP_ONLINE_EQ_HZ 48 * 1000000L
#define BT_CALL_SIMPLEX_HZ 96 * 1000000L
#ifdef CONFIG_ANS_V2
//#define BT_CALL_16k_HZ 96 * 1000000L
//#define BT_CALL_16k_ADVANCE_HZ 120 * 1000000L
#else
//#define BT_CALL_16k_HZ 80 * 1000000L
//#define BT_CALL_16k_ADVANCE_HZ 96 * 1000000L
#endif
#define BT_CALL_16k_SIMPLEX_HZ 120 * 1000000L
////////////////////////
#ifdef CONFIG_FPGA_ENABLE
// #undef TCFG_CLOCK_OSC_HZ
// #define TCFG_CLOCK_OSC_HZ 12000000
#endif
#ifdef CONFIG_CPU_BR26
#undef BT_CALL_16k_HZ
#undef BT_CALL_16k_ADVANCE_HZ
#define BT_CALL_16k_HZ 96 * 1000000L
#define BT_CALL_16k_ADVANCE_HZ 96 * 1000000L
#endif
#ifdef CONFIG_CPU_BR23
#undef BT_A2DP_STEREO_EQ_HZ
#define BT_A2DP_STEREO_EQ_HZ 48 * 1000000L
#undef BT_A2DP_MONO_EQ_HZ
#define BT_A2DP_MONO_EQ_HZ 48 * 1000000L
#endif
#ifdef CONFIG_CPU_BR25
#undef BT_A2DP_STEREO_EQ_HZ
#define BT_A2DP_STEREO_EQ_HZ 48 * 1000000L
#undef BT_A2DP_MONO_EQ_HZ
#define BT_A2DP_MONO_EQ_HZ 48 * 1000000L
#endif
#ifdef CONFIG_FPGA_ENABLE
// #undef TCFG_CLOCK_OSC_HZ
// #define TCFG_CLOCK_OSC_HZ 12000000
#undef TCFG_MC_BIAS_AUTO_ADJUST
#define TCFG_MC_BIAS_AUTO_ADJUST MC_BIAS_ADJUST_DISABLE
#endif
//*********************************************************************************//
// 低功耗配置 //
//*********************************************************************************//
#if TCFG_IRKEY_ENABLE
#undef TCFG_LOWPOWER_LOWPOWER_SEL
#define TCFG_LOWPOWER_LOWPOWER_SEL 0 //开红外不进入低功耗
#endif /* #if TCFG_IRKEY_ENABLE */
//*********************************************************************************//
// LED使用 16SLOT TIMER 同步 //
//*********************************************************************************//
//LED模块使用slot timer同步使用注意点:
// 1.soundbox不开该功能, 原因: 默认打开osc时钟, 使用原来的osc流程同步即可
// 2.带sd卡earphone不开该功能, 一般为单耳, 不需要同步, 使用原来的流程(lrc)
// 3.一般用在tws应用中, 而且默认关闭osc;
#if TCFG_USER_TWS_ENABLE
#define TCFG_PWMLED_USE_SLOT_TIME ENABLE_THIS_MOUDLE
#endif
//*********************************************************************************//
// 升级配置 //
//*********************************************************************************//
//升级LED显示使能
#define UPDATE_LED_REMIND
//升级提示音使能
#define UPDATE_VOICE_REMIND
#ifndef CONFIG_UPDATE_JUMP_TO_MASK
#define CONFIG_UPDATE_JUMP_TO_MASK 0
#endif
#if CONFIG_UPDATE_JUMP_TO_MASK
//升级IO保持使能
#define DEV_UPDATE_SUPPORT_JUMP //目前只有br23\br25支持
#endif
#if TCFG_APP_MUSIC_EN
#define CONFIG_SD_UPDATE_ENABLE
#define CONFIG_USB_UPDATE_ENABLE
#endif
//*********************************************************************************//
// Audio配置 //
//*********************************************************************************//
#if TCFG_AUDIO_ANC_ENABLE
#if ((defined VOL_TYPE_ANALOG) && (SYS_VOL_TYPE == VOL_TYPE_ANALOG)) || \
((defined VOL_TYPE_AD) && (SYS_VOL_TYPE == VOL_TYPE_AD))
#error "ANC can not use VOL_TYPE_ANALOG and VOL_TYPE_AD!!!"
#endif/*(SYS_VOL_TYPE = VOL_TYPE_ANALOG) || (SYS_VOL_TYPE = VOL_TYPE_AD)*/
#endif/*TCFG_AUDIO_ANC_ENABLE*/
/*通话语音处理算法放在.data段*/
#ifndef TCFG_AUDIO_CVP_CODE_AT_RAM
#define TCFG_AUDIO_CVP_CODE_AT_RAM 1
#endif/*TCFG_AUDIO_CVP_CODE_AT_RAM*/
/*AAC解码算法放在.data段*/
#ifndef TCFG_AUDIO_AAC_CODE_AT_RAM
#define TCFG_AUDIO_AAC_CODE_AT_RAM 1
#endif/*TCFG_AUDIO_AAC_CODE_AT_RAM*/
#if (TCFG_AUDIO_DUAL_MIC_ENABLE && TCFG_AUDIO_TRIPLE_MIC_ENABLE)
#error "TCFG_AUDIO_DUAL_MIC_ENABLE and TCFG_AUDIO_TRIPLE_MIC_ENABLE can not enable together !!!"
#endif
//*********************************************************************************//
// 充电中按键清除手机配对信息配置 //
//*********************************************************************************//
#define CHARGING_CLEAN_PHONE_INFO 0
#define PC_MODE_DETECTION
#endif

View File

@ -1,90 +0,0 @@
#ifndef APP_MAIN_H
#define APP_MAIN_H
typedef struct {
float talk;
float ff;
float fb;
} audio_mic_cmp_t;
typedef struct _APP_VAR {
s8 bt_volume;
s8 dev_volume;
s8 music_volume;
s8 call_volume;
s8 wtone_volume;
u8 opid_play_vol_sync;
u8 aec_dac_gain;
u8 aec_mic_gain;
u8 aec_mic1_gain;
u8 aec_mic2_gain;
u8 aec_mic3_gain;
u8 rf_power;
u8 goto_poweroff_flag;
u8 goto_poweroff_cnt;
u8 play_poweron_tone;
u8 remote_dev_company;
u8 siri_stu;
u8 cycle_mode;
u8 have_mass_storage;
int auto_stop_page_scan_timer; //用于1拖2时有一台连接上后超过三分钟自动关闭Page Scan
volatile int auto_shut_down_timer;
volatile int wait_exit_timer;
u16 auto_off_time;
u16 warning_tone_v;
u16 poweroff_tone_v;
u16 phone_dly_discon_time;
u8 usb_mic_gain;
int wait_timer_do;
u32 start_time;
float audio_mic_array_diff_cmp;//麦克风阵列补偿值
u8 audio_mic_array_trim_en; //麦克风阵列校准
audio_mic_cmp_t audio_mic_cmp;
float enc_degradation;//default:1,range[0:1]
/*3麦通话配置使用:选择第几个mic*/
u8 talk_mic_ch; //主MIC通道选择
u8 talk_ref_mic_ch; //副MIC通道选择
u8 talk_fb_mic_ch; //FB通道选择
} APP_VAR;
typedef struct _BT_USER_PRIV_VAR {
//phone
u8 phone_ring_flag: 1;
u8 phone_num_flag: 1;
u8 phone_income_flag: 1;
u8 phone_call_dec_begin: 1;
u8 phone_con_sync_num_ring: 1;
u8 phone_con_sync_ring: 1;
// u8 reserved: 2;
u8 emitter_or_receiver: 2;
u8 get_phone_num_timecnt;
u8 inband_ringtone;
u8 phone_vol;
u16 phone_timer_id;
u8 last_call_type;
u8 income_phone_num[30];
u8 income_phone_len;
s32 auto_connection_counter;
int auto_connection_timer;
u8 auto_connection_addr[6];
int tws_con_timer;
u8 tws_start_con_cnt;
u8 tws_conn_state;
bool search_tws_ing;
int sniff_timer;
bool fast_test_mode;
} BT_USER_PRIV_VAR;
#define BT_EMITTER_EN 1
#define BT_RECEIVER_EN 2
typedef struct _BT_USER_COMM_VAR {
} BT_USER_COMM_VAR;
extern APP_VAR app_var;
extern BT_USER_PRIV_VAR bt_user_priv_var;
#define earphone (&bt_user_priv_var)
#endif

View File

@ -1,8 +0,0 @@
#ifndef CONFIG_APP_MUSIC_H
#define CONFIG_APP_MUSIC_H
void app_music_exit();
int music_app_check(void);
#endif

View File

@ -1,13 +0,0 @@
#ifndef ONLINE_CONFIG_H
#define ONLINE_CONFIG_H
#define DEVICE_EVENT_FROM_CI_UART (('C' << 24) | ('I' << 16) | ('U' << 8) | '\0')
#define DEVICE_EVENT_FROM_CI_TWS (('C' << 24) | ('I' << 16) | ('T' << 8) | '\0')
#define CI_UART 0
#define CI_TWS 1
void ci_data_rx_handler(u8 type);
u32 eq_cfg_sync(u8 priority);
#endif

View File

@ -1,39 +0,0 @@
#ifndef APP_POWER_MANAGE_H
#define APP_POWER_MANAGE_H
#include "typedef.h"
#include "system/event.h"
#define LOW_POWER_SHUTDOWN 300 //低电直接关机电压-拔出不开机-开盖不开机
#define LOW_POWER_OFF_VAL 330 //低电关机电压
#define LOW_POWER_WARN_VAL 340 //低电提醒电压
#define LOW_POWER_WARN_TIME (60 * 1000) //低电提醒时间
#define DEVICE_EVENT_FROM_POWER (('P' << 24) | ('O' << 16) | ('W' << 8) | '\0')
enum {
POWER_EVENT_POWER_NORMAL,
POWER_EVENT_POWER_WARNING,
POWER_EVENT_POWER_LOW,
POWER_EVENT_POWER_CHANGE,
POWER_EVENT_SYNC_TWS_VBAT_LEVEL,
POWER_EVENT_POWER_CHARGE,
};
int app_power_event_handler(struct device_event *dev);
void check_power_on_voltage(void);
u16 get_vbat_level(void);
u8 get_vbat_percent(void);
void vbat_check_init(void);
void vbat_timer_update(u32 msec);
void vbat_timer_delete(void);
void tws_sync_bat_level(void);
u8 get_tws_sibling_bat_level(void);
u8 get_tws_sibling_bat_persent(void);
bool get_vbat_need_shutdown(void);
u8 get_self_battery_level(void);
void app_power_set_tws_sibling_bat_level(u8 vbat, u8 percent);
#endif

View File

@ -1,6 +0,0 @@
#ifndef CONFIG_APP_SD_MUSIC_H
#define CONFIG_APP_SD_MUSIC_H
int music_app_check(void);//music 在线检测 切换模式判断使用
#endif

View File

@ -1,31 +0,0 @@
#ifndef APP_TASK_H
#define APP_TASK_H
#include "typedef.h"
#define NULL_VALUE 0
enum {
APP_BT_TASK,
#if TCFG_APP_MUSIC_EN
APP_MUSIC_TASK,
#endif
#if TCFG_PC_ENABLE
APP_PC_TASK,
#endif
#if TCFG_APP_AUX_EN
APP_AUX_TASK,
#endif
};
extern u8 app_curr_task;
extern u8 app_next_task;
extern u8 app_prev_task;
int app_task_switch_check(u8 app_task);
int app_core_back_to_prev_app_over_check(void);
int app_task_switch_to(u8 app_task, int priv);
void app_task_switch_next(void);
void app_task_switch_prev(void);
u8 app_get_curr_task(void);
#endif

View File

@ -1,18 +0,0 @@
#ifndef _APP_TESTBOX_H_
#define _APP_TESTBOX_H_
#include "typedef.h"
#include "system/event.h"
extern void testbox_set_bt_init_ok(u8 flag);
extern u8 testbox_get_status(void);
extern void testbox_clear_status(void);
extern u8 testbox_get_ex_enter_dut_flag(void);
extern u8 testbox_get_ex_enter_storage_mode_flag(void);
extern u8 testbox_get_connect_status(void);
extern void testbox_clear_connect_status(void);
extern u8 testbox_get_keep_tws_conn_flag(void);
extern int app_testbox_event_handler(struct testbox_event *testbox_dev);
#endif //_APP_TESTBOX_H_

View File

@ -1,50 +0,0 @@
#ifndef _APP_UMIDIGI_CHARGESTORE_H_
#define _APP_UMIDIGI_CHARGESTORE_H_
#include "typedef.h"
#include "system/event.h"
#include "board_config.h"
/*******************************UMIDIGI充电舱数据包格式*****************************/
/*
* |<--1bit-->||<-------4bits----->||<--------------8bits------------>||<--1bit-->||<--1bits-->|
* |<-start-->||<-------CMD------->||<--------------DATA------------->|| crc_odd ||<--stop--->|
* | 14 || 13 | 12 | 11 | 10 || 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 || 1 || 0 |
*/
/*充电舱发送的命令列表*/
#define CMD_RESERVED_0 0x00 //reserved
#define CMD_FACTORY_RESET 0x01 //恢复出厂设置
#define CMD_RESERVED_2 0x02 //reserved
#define CMD_OPEN_CASE 0x03 //充电舱开盖
#define CMD_CLOSE_CASE 0x04 //充电舱关盖
#define CMD_ENTER_PAIRING_MODE 0x06 //进入配对模式
#define CMD_DUT_MODE_CMD 0x08 //进入DUT模式
#define CMD_USB_STATE 0x0A //USB状态
#define CMD_RESERVED_B 0x0B //reserved(RTK internal use)
#define CMD_SEND_CASE_BATTERY 0x0D //发送充电舱电量
/*message中的CMD与DATA位置用于从一个完整的数据包中截取CMD和DATA*/
/*CMD_IN_MESSAGE = 0xf000*/
#define CMD_IN_MESSAGE (BIT(13) | BIT(12) | BIT(11) | BIT(10))
/*DATA_IN_MESSAGE = 0x0ff0*/
#define DATA_IN_MESSAGE (BIT(9) | BIT(8) | BIT(7) | BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2))
void app_umidigi_chargetore_message_deal(u16 message);
extern u8 umidigi_chargestore_get_power_level(void); //获取充电舱电池电量
extern u8 umidigi_chargestore_get_power_status(void); //获取充电舱充电状态
extern u8 umidigi_chargestore_get_cover_status(void); //获取充电舱合盖或开盖状态
extern u8 umidigi_chargestore_get_earphone_online(void); //获取合盖状态时耳机在仓数量
extern void umidigi_chargestore_set_bt_init_ok(u8 flag); //获取蓝牙初始化成功标志位
extern u8 umidigi_chargestore_check_going_to_poweroff(void); //获取允许poweroff标志位
extern void umidigi_chargestore_set_phone_disconnect(void);
extern void umidigi_chargestore_set_phone_connect(void);
extern void umidigi_chargestore_set_sibling_chg_lev(u8 chg_lev);//设置对耳同步的充电舱电量
extern void umidigi_chargestore_set_power_level(u8 power); //设置充电舱电池电量
extern int umidigi_chargestore_sync_chg_level(void);
extern int app_umidigi_chargestore_event_handler(struct umidigi_chargestore_event *umidigi_chargestore_dev);
#endif

View File

@ -1,69 +0,0 @@
#ifndef __AUDIO_ENC_MPT_FRE_RESPONE_H_
#define __AUDIO_ENC_MPT_FRE_RESPONE_H_
#include "asm/cpu.h"
/********************用户配置****************************/
//计算长度 输入单位s16/输出单位float
#define AUDIO_ENC_MPT_FRERES_POINT 1024
/********************非用户配置****************************/
//MIC频响测试通道ID u16
#define AUDIO_ENC_MPT_FF_MIC 0X01 //测试TWS FFMIC or 头戴式LFF MIC
#define AUDIO_ENC_MPT_FB_MIC 0X02 //测试TWS FBMIC or 头戴式LFB MIC
#define AUDIO_ENC_MPT_RFF_MIC 0X04 //测试头戴式RFF MIC
#define AUDIO_ENC_MPT_RFB_MIC 0X08 //测试头戴式RFB MIC
#define AUDIO_ENC_MPT_CVP_OUT 0X10 //测试通话算法输出
#define AUDIO_ENC_MPT_TALK_MIC 0X20 //测试通话TALK 主MIC
#define AUDIO_ENC_MPT_SLAVE_MIC 0X40 //测试通话TALK 副MIC
#define AUDIO_ENC_MPT_TALK_FB_MIC 0X80 //测试通话TALK FBMIC
//常见通道组合
//FF+TALK+算法输出频响测试默认关闭DNS, 外部喇叭发声
#define AUDIO_ENC_MPT_CH_TWS_CVP_ENC (AUDIO_ENC_MPT_SLAVE_MIC | AUDIO_ENC_MPT_TALK_MIC | AUDIO_ENC_MPT_CVP_OUT)
//FF回声/气密性+FB频响测试耳机喇叭发声
#define AUDIO_ENC_MPT_CH_TWS_FF_FB (AUDIO_ENC_MPT_FF_MIC | AUDIO_ENC_MPT_FB_MIC)
#define AUDIO_ENC_MPT_FRERES_ASYNC 1 //是否异步处理, 异步算不过来要加时钟提醒
enum {
ENC_FRE_RES_STATE_START = 0,
ENC_FRE_RES_STATE_RUN,
ENC_FRE_RES_STATE_STOP,
};
enum {
ENC_FRE_RESPONE_MSG_RUN = 0xA1,
};
//更新目标通道输入buf、len
void audio_enc_mpt_fre_response_inbuf(u16 id, s16 *buf, int len);
//音频测试频响计算运行
void audio_enc_mpt_fre_response_post_run(u16 id);
//音频测试频响计算启动, ch 对应目标的通道
void audio_enc_mpt_fre_response_start(u16 ch);
//音频测试频响计算停止
void audio_enc_mpt_fre_response_stop(void);
//工具获取数据文件
int audio_enc_mpt_fre_response_file_get(u8 **buf);
//数据获取结束,释放内存
void audio_enc_mpt_fre_response_release(void);
//音频测试MIC频响开启 ch 对应目标的通道
void audio_enc_mpt_fre_response_open(u16 ch);
//音频测试MIC频响关闭
void audio_enc_mpt_fre_response_close(void);
#endif /*__AUDIO_ENC_MPT_FRE_RESPONE_H_*/

View File

@ -1,43 +0,0 @@
#ifndef BT_BACKGROUND_H
#define BT_BACKGROUND_H
#include "generic/typedef.h"
bool bt_in_background();
void bt_switch_to_foreground(int action, bool exit_curr_app);
int bt_switch_to_background();
void bt_stop_a2dp_slience_detect();
void bt_start_a2dp_slience_detect(int ingore_packet_num);
int bt_background_event_probe_handler(struct bt_event *bt);
#endif

Some files were not shown because too many files have changed in this diff Show More