Compare commits
34 Commits
ae980789b6
...
skiing_tmp
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c9ec6cec8 | |||
| ea610c24d3 | |||
| 45158aebcf | |||
| 138275a04b | |||
| 89f1c93f74 | |||
| 8c2db49083 | |||
| 627780ea20 | |||
| ade4b0a1f8 | |||
| 818e8c3778 | |||
| 892ed9267b | |||
| 4af4f13ac6 | |||
| 4c5da2298f | |||
| 5c7d9ab822 | |||
| 60a4e95386 | |||
| 845cc33fe8 | |||
| f3710fbb4b | |||
| 91b08dbe47 | |||
| 591e7632d2 | |||
| baa5979ee1 | |||
| bdadd5de1e | |||
| 9ccf1acda8 | |||
| 2bfdc81991 | |||
| 054ea8644a | |||
| ad3ab64b72 | |||
| ebca849be3 | |||
| d0d9c0a630 | |||
| b621ef7e44 | |||
| 046986c5c3 | |||
| 5e587e0527 | |||
| c88cb70bb1 | |||
| 58ad14691e | |||
| 23a71377a2 | |||
| d12252dfda | |||
| 289a6b780b |
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# 编译生成的目标文件
|
||||
*.o
|
||||
*.so
|
||||
*.d
|
||||
|
||||
# 编译生成的最终产物
|
||||
*.elf
|
||||
*.fw
|
||||
*.ufw
|
||||
*.map
|
||||
*.lst
|
||||
*.bc
|
||||
|
||||
|
||||
# VSCode 用户个人设置
|
||||
# 团队协作时,每个人的配置可能不同,通常不建议提交
|
||||
.vscode/settings.json
|
||||
28
.vscode/settings.json
vendored
28
.vscode/settings.json
vendored
@ -1,28 +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",
|
||||
"avctp_user.h": "c",
|
||||
"string.h": "c",
|
||||
"dev_manager.h": "c",
|
||||
"bt_tws.h": "c"
|
||||
}
|
||||
}
|
||||
8
Makefile
8
Makefile
@ -249,6 +249,10 @@ INCLUDES := \
|
||||
-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) \
|
||||
|
||||
|
||||
@ -620,6 +624,10 @@ c_SRC_FILES := \
|
||||
apps/earphone/xtell_Sensor/sensor/LIS2DH12.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 文件
|
||||
|
||||
@ -211,41 +211,102 @@ void write_gsensor_data_handle(void)
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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)
|
||||
{
|
||||
// xlog("%s",__func__);
|
||||
@ -255,16 +316,19 @@ u8 _gravity_sensor_get_ndata(u8 r_chip_id, u8 register_address, u8 *buf, u8 data
|
||||
|
||||
iic_start(gSensor_info->iic_hdl);
|
||||
if (0 == iic_tx_byte(gSensor_info->iic_hdl, r_chip_id - 1)) {
|
||||
xlog("\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)) {
|
||||
xlog("\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;
|
||||
}
|
||||
|
||||
@ -272,6 +336,7 @@ u8 _gravity_sensor_get_ndata(u8 r_chip_id, u8 register_address, u8 *buf, u8 data
|
||||
if (0 == iic_tx_byte(gSensor_info->iic_hdl, r_chip_id)) {
|
||||
xlog("\n gsen iic rd err 2\n");
|
||||
read_len = 0;
|
||||
strcpy(&sen_log_buffer_3, "gsen iic rd err 2\n" );
|
||||
goto __gdend;
|
||||
}
|
||||
|
||||
@ -284,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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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},
|
||||
};
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 'D'
|
||||
#define TCFG_HW_I2C0_CLK 100000 //硬件IIC波特率
|
||||
#define TCFG_HW_I2C0_PORTS 'B'
|
||||
#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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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流程
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
@ -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);
|
||||
|
||||
596
apps/earphone/xtell_Sensor/A_hide/10/skiing_tracker.c
Normal file
596
apps/earphone/xtell_Sensor/A_hide/10/skiing_tracker.c
Normal 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;
|
||||
}
|
||||
|
||||
88
apps/earphone/xtell_Sensor/A_hide/10/skiing_tracker.h
Normal file
88
apps/earphone/xtell_Sensor/A_hide/10/skiing_tracker.h
Normal 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
|
||||
377
apps/earphone/xtell_Sensor/A_hide/11_test/skiing_tracker.c
Normal file
377
apps/earphone/xtell_Sensor/A_hide/11_test/skiing_tracker.c
Normal 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;
|
||||
}
|
||||
|
||||
88
apps/earphone/xtell_Sensor/A_hide/11_test/skiing_tracker.h
Normal file
88
apps/earphone/xtell_Sensor/A_hide/11_test/skiing_tracker.h
Normal 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
|
||||
276
apps/earphone/xtell_Sensor/A_hide/7/skiing_tracker.c
Normal file
276
apps/earphone/xtell_Sensor/A_hide/7/skiing_tracker.c
Normal 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;
|
||||
}
|
||||
67
apps/earphone/xtell_Sensor/A_hide/7/skiing_tracker.h
Normal file
67
apps/earphone/xtell_Sensor/A_hide/7/skiing_tracker.h
Normal 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
|
||||
501
apps/earphone/xtell_Sensor/A_hide/8/skiing_tracker.c
Normal file
501
apps/earphone/xtell_Sensor/A_hide/8/skiing_tracker.c
Normal 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;
|
||||
}
|
||||
85
apps/earphone/xtell_Sensor/A_hide/8/skiing_tracker.h
Normal file
85
apps/earphone/xtell_Sensor/A_hide/8/skiing_tracker.h
Normal 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
|
||||
583
apps/earphone/xtell_Sensor/A_hide/9/skiing_tracker.c
Normal file
583
apps/earphone/xtell_Sensor/A_hide/9/skiing_tracker.c
Normal 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;
|
||||
}
|
||||
|
||||
88
apps/earphone/xtell_Sensor/A_hide/9/skiing_tracker.h
Normal file
88
apps/earphone/xtell_Sensor/A_hide/9/skiing_tracker.h
Normal 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
|
||||
651
apps/earphone/xtell_Sensor/A_hide/calculate/skiing_tracker.c
Normal file
651
apps/earphone/xtell_Sensor/A_hide/calculate/skiing_tracker.c
Normal 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;
|
||||
}
|
||||
|
||||
88
apps/earphone/xtell_Sensor/A_hide/calculate/skiing_tracker.h
Normal file
88
apps/earphone/xtell_Sensor/A_hide/calculate/skiing_tracker.h
Normal 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
|
||||
95
apps/earphone/xtell_Sensor/README.md
Normal file
95
apps/earphone/xtell_Sensor/README.md
Normal 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无法通过加速度计来消除偏差)
|
||||
166
apps/earphone/xtell_Sensor/ano/ano_protocol.c
Normal file
166
apps/earphone/xtell_Sensor/ano/ano_protocol.c
Normal 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
|
||||
}
|
||||
37
apps/earphone/xtell_Sensor/ano/ano_protocol.h
Normal file
37
apps/earphone/xtell_Sensor/ano/ano_protocol.h
Normal 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__
|
||||
@ -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);
|
||||
// 销毁环形缓冲区
|
||||
void circle_buffer_deinit(circle_buffer_t *cb) {
|
||||
os_mutex_del(&cb->mutex, 0);
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
// 向环形缓冲区写入一个元素
|
||||
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 (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;
|
||||
}
|
||||
u8 *dest = cb->buffer + (cb->head * cb->element_size);
|
||||
memcpy(dest, element, cb->element_size);
|
||||
|
||||
cb->head = (cb->head + 1) % cb->capacity;
|
||||
cb->size++;
|
||||
os_mutex_post(&cb->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
cb->size += length;
|
||||
return length;
|
||||
// 从环形缓冲区读取一个元素
|
||||
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; // 缓冲区为空
|
||||
}
|
||||
|
||||
// 从环形缓冲区读取数据
|
||||
u16 circle_buffer_read(circle_buffer_t *cb, u8 *data, u16 length) {
|
||||
if (length > cb->size) {
|
||||
// 如果要读取的长度超过了已有的数据,则只读取已有的部分
|
||||
length = cb->size;
|
||||
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;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 检查是否需要回环
|
||||
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;
|
||||
}
|
||||
|
||||
// 获取已用空间的大小
|
||||
// 获取已用空间的大小(以元素为单位)
|
||||
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;
|
||||
}
|
||||
@ -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
|
||||
@ -1,59 +1,129 @@
|
||||
/*
|
||||
动态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)
|
||||
|
||||
// --- 算法阈值定义 ---
|
||||
#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 ZUPT_ACC_VARIANCE_THRESHOLD 0.2f
|
||||
#define STOP_ACC_VARIANCE_THRESHOLD 0.2f
|
||||
// 陀螺仪方差阈值
|
||||
#define ZUPT_GYR_VARIANCE_THRESHOLD 5.0f
|
||||
#define STOP_GYR_VARIANCE_THRESHOLD 5.0f
|
||||
// 静止时候的陀螺仪模长
|
||||
#define STOP_GYR_MAG_THRESHOLD 15
|
||||
// --- --- ---
|
||||
|
||||
// 用于原地旋转判断的加速度方差阈值。此值比ZUPT阈值更宽松,
|
||||
// 以允许原地旋转时身体的正常晃动,但仍能与真实滑行时的剧烈加速度变化区分开。
|
||||
#define ROTATING_ACC_VARIANCE_THRESHOLD 0.8f
|
||||
// 用于启动滑雪判断的陀螺仪方差阈值。此值比ZUPT阈值更宽松,
|
||||
// 以允许启动瞬间的正常抖动,但仍能过滤掉混乱的、非滑雪的晃动。
|
||||
#define SKIING_GYR_VARIANCE_THRESHOLD 15.0f
|
||||
// --- 启动滑雪阈值 ---
|
||||
// 加速度模长与重力的差值大于此值,认为开始运动;降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
|
||||
#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 ROTATION_GYR_MAG_THRESHOLD 90.0f //测试记录:45.0f、90.0f
|
||||
// 启动滑雪阈值:加速度模长与重力的差值大于此值,认为开始运动
|
||||
// 降低阈值,让“油门”更灵敏,以便能捕捉到真实的慢速启动
|
||||
#define START_SKIING_ACC_THRESHOLD 0.5f
|
||||
// --- 原地旋转抖动 ---
|
||||
// 加速度 方差 阈值。此值比 静止检测 阈值更宽松,
|
||||
#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),
|
||||
#define HPF_ALPHA 0.95f // 换算大概就是衰减频率低于约 0.84 Hz 的信号
|
||||
// 任何比“大约1秒钟变化一次”还要慢的运动,其加速度信号也会被部分衰减。
|
||||
// 而滑雪时的快速转弯、加减速等动作,其频率远高于 0.84 Hz,它们的信号会被保留下来。
|
||||
// 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)。低于此阈值的加速度被认为是噪声,不参与积分。
|
||||
// 设得太高会忽略真实的慢速启动,设得太低则无法有效抑制噪声。
|
||||
#define ACC_DEAD_ZONE_THRESHOLD 0.15f
|
||||
//参考:0.2f ~ 0.4f
|
||||
#define ACC_DEAD_ZONE_THRESHOLD 0.05f
|
||||
|
||||
// --- 模拟摩擦力,进行速度衰减 ---
|
||||
#define SPEED_ATTENUATION 0.98f
|
||||
#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 初始化滑雪追踪器
|
||||
*
|
||||
@ -66,44 +136,72 @@ void skiing_tracker_init(skiing_tracker_t *tracker)
|
||||
}
|
||||
// 使用memset一次性清零整个结构体,包括新增的缓冲区
|
||||
memset(tracker, 0, sizeof(skiing_tracker_t));
|
||||
tracker->state = SKIING_STATE_STATIC;
|
||||
tracker->state = STATIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将设备坐标系下的加速度转换为世界坐标系
|
||||
* @param acc_device 设备坐标系下的加速度 [x, y, z]
|
||||
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
|
||||
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
|
||||
* @brief 当检测到落地时,计算空中的水平飞行距离并累加到总距离
|
||||
*/
|
||||
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;
|
||||
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 计算缓冲区内三轴数据的方差之和
|
||||
*
|
||||
@ -139,12 +237,22 @@ static float calculate_variance(float buffer[VARIANCE_BUFFER_SIZE][3])
|
||||
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 tracker 传入同步修改后传出
|
||||
* @param acc_device_ms2 三轴加速度,m/s^2
|
||||
* @param gyr_dps 三轴陀螺仪,dps
|
||||
*/
|
||||
@ -152,47 +260,146 @@ static void update_state_machine(skiing_tracker_t *tracker, const float *acc_dev
|
||||
{
|
||||
// 缓冲区未填满时,不进行状态判断,默认为静止
|
||||
if (!tracker->buffer_filled) {
|
||||
tracker->state = SKIING_STATE_STATIC;
|
||||
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]);
|
||||
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
|
||||
|
||||
|
||||
// --- 状态切换逻辑 (按优先级) ---
|
||||
|
||||
// 优先级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));
|
||||
// --- 状态机逻辑 (核心修改区域) ---
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
// 优先级2:原地旋转 - 特殊的、非滑雪的运动状态
|
||||
// 条件:角速度很大,同时线性加速度的晃动在一个“中等”范围内。
|
||||
if (gyr_magnitude > ROTATION_GYR_MAG_THRESHOLD && acc_variance < ROTATING_ACC_VARIANCE_THRESHOLD) {
|
||||
tracker->state = SKIING_STATE_ROTATING;
|
||||
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;
|
||||
}
|
||||
|
||||
// 优先级3:启动滑雪 - “油门”
|
||||
// 条件:有足够大的线性加速度,同时陀螺仪的抖动在一个“合理”(而非“完全静止”)的范围内。
|
||||
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD && gyr_variance < SKIING_GYR_VARIANCE_THRESHOLD) {
|
||||
tracker->state = SKIING_STATE_SKIING;
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果不满足任何启动或停止条件,则保持当前状态(滑雪中)
|
||||
// 如果当前是静止或旋转,但没有满足启动条件,则状态会保持,直到满足ZUPT或旋转条件。
|
||||
}
|
||||
|
||||
|
||||
@ -210,6 +417,8 @@ void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_d
|
||||
if (!tracker || !acc_g || !gyr_dps || !angle || dt <= 0) {
|
||||
return;
|
||||
}
|
||||
if(my_skiing_tracker.state == STOP_DETECTION)
|
||||
return;
|
||||
|
||||
// --- 数据预处理和缓冲 ---
|
||||
float acc_device_ms2[3];
|
||||
@ -230,70 +439,152 @@ void skiing_tracker_update(skiing_tracker_t *tracker, float *acc_g, float *gyr_d
|
||||
// --- 更新状态机 ---
|
||||
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;
|
||||
// --- 根据状态执行不同的计算逻辑 ---
|
||||
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);
|
||||
|
||||
// 对世界坐标系下的加速度进行高通滤波,消除直流偏置和重力残差
|
||||
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];
|
||||
// 将设备坐标系下的线性加速度,旋转到世界坐标系
|
||||
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(tracker->acc_world_filtered[0] * tracker->acc_world_filtered[0] +
|
||||
tracker->acc_world_filtered[1] * tracker->acc_world_filtered[1]);
|
||||
|
||||
// 计算处理后加速度的水平模长
|
||||
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] += 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->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 传感器数据采集与处理任务,外部每10ms调用一次,如果需要更新时间间隔,也需要同步更新宏“ DELTA_TIME ”
|
||||
* @brief 滑雪数据计算
|
||||
*
|
||||
* @param acc_data_buf 三轴加速度原始数据
|
||||
* @param gyr_data_buf 三轴陀螺仪原始数据
|
||||
* @return BLE_send_data_t
|
||||
* @param acc_data_buf 传入的三轴加速度数据
|
||||
* @param gyr_data_buf 传入的三轴陀螺仪数据
|
||||
* @param angle_data 传入的欧若拉角数据
|
||||
* @return 速度cm/s
|
||||
*/
|
||||
BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short * gyr_data_buf) {
|
||||
static skiing_tracker_t my_skiing_tracker;
|
||||
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 int calibration_done = 0;
|
||||
static float acc_data_g[3];
|
||||
static float gyr_data_dps[3];
|
||||
|
||||
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]; // 转换后的陀螺仪数据
|
||||
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;
|
||||
BLE_KS_send_data_t KS_data;
|
||||
|
||||
|
||||
|
||||
if (!initialized) {
|
||||
skiing_tracker_init(&my_skiing_tracker);
|
||||
@ -301,77 +592,44 @@ BLE_send_data_t sensor_processing_task(signed short * acc_data_buf, signed short
|
||||
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) {
|
||||
#if ACC_RANGE==2
|
||||
// 加速度 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;
|
||||
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
|
||||
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;
|
||||
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, calibrated_acc_g, calibrated_gyr_dps, final_angle_data, delta_time);
|
||||
skiing_tracker_update(&my_skiing_tracker, acc_data_g, gyr_data_dps, 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);
|
||||
// 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;
|
||||
return (uint16_t)(my_skiing_tracker.speed * 100);
|
||||
}
|
||||
|
||||
|
||||
@ -2,13 +2,23 @@
|
||||
#define SKIING_TRACKER_H
|
||||
|
||||
#include "../xtell.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// 定义滑雪者可能的状态
|
||||
typedef enum {
|
||||
SKIING_STATE_STATIC, // 静止或动态稳定
|
||||
SKIING_STATE_SKIING, // 正在滑雪
|
||||
SKIING_STATE_ROTATING, // 正在原地旋转 (新增)
|
||||
SKIING_STATE_FALLEN, // 已摔倒
|
||||
SKIING_STATE_UNKNOWN // 未知状态
|
||||
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),减小延迟,提高实时性
|
||||
@ -24,7 +34,13 @@ typedef struct {
|
||||
skiing_state_t state; // 当前滑雪状态
|
||||
|
||||
// 内部计算使用的私有成员
|
||||
float acc_world[3]; // 在世界坐标系下的加速度
|
||||
float acc_no_g[3]; // 去掉重力分量后的加速度
|
||||
|
||||
// 用于空中距离计算
|
||||
float time_in_air; // 滞空时间计时器
|
||||
float initial_velocity_on_takeoff[3]; // 起跳瞬间的速度向量
|
||||
int airborne_entry_counter; // 进入空中状态的确认计数器
|
||||
int grounded_entry_counter; // 落地确认计数器
|
||||
|
||||
// --- 内部计算使用的私有成员 ---
|
||||
// 用于动态零速更新和旋转检测的缓冲区
|
||||
@ -36,26 +52,26 @@ typedef struct {
|
||||
// 用于高通滤波器(巴特沃斯一阶滤波器)的私有成员,以消除加速度的直流偏置
|
||||
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
|
||||
#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 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 初始化滑雪追踪器
|
||||
*
|
||||
@ -63,12 +79,5 @@ typedef struct{
|
||||
*/
|
||||
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) ;
|
||||
uint16_t sensor_processing_task(signed short* acc_data_buf, signed short* gyr_data_buf, float* angle_data, float* quaternion);
|
||||
#endif // SKIING_TRACKER_H
|
||||
@ -1,312 +0,0 @@
|
||||
/*
|
||||
动态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) {
|
||||
// 最终版启动逻辑:必须同时满足“有足够大的线性加速度”和“旋转不剧烈”两个条件
|
||||
// 新增 gyr_magnitude 判断,防止原地旋转产生的离心加速度被误判为启动
|
||||
if (fabsf(acc_magnitude - G_ACCELERATION) > START_SKIING_ACC_THRESHOLD &&
|
||||
gyr_variance < ZUPT_GYR_VARIANCE_THRESHOLD &&
|
||||
gyr_magnitude < ROTATION_GYR_MAG_THRESHOLD) {
|
||||
tracker->state = SKIING_STATE_SKIING;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 最后的 fall-through 逻辑已移除,以修复原地旋转被错误判断为滑雪的bug。
|
||||
// 如果不满足任何状态切换条件,状态将保持不变,直到ZUPT或启动条件被满足。
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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");
|
||||
}
|
||||
}
|
||||
@ -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,12 +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
|
||||
@ -32,6 +41,12 @@
|
||||
#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组六轴数据
|
||||
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -39,197 +54,468 @@
|
||||
//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]; //缓冲区
|
||||
|
||||
BLE_send_data_t BLE_send_data;
|
||||
//--- test ---
|
||||
// 全局变量
|
||||
u16 gsensor_id=0;
|
||||
u16 test_id=0;
|
||||
extern u8 foot_init;
|
||||
static OS_SEM receiver_ready_sem; // 用于启动同步的信号量
|
||||
|
||||
static const uart_bus_t *uart_bus = NULL;
|
||||
|
||||
static u16 test_id = 0;
|
||||
//END -- 变量定义
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// /**
|
||||
// * @brief 向匿名上位机发送数据帧
|
||||
// * @param function_id 功能码 (例如 0x01, 0x03)
|
||||
// * @param data 指向 int16_t 数据数组的指针 (例如加速度、欧拉角)
|
||||
// * @param data_len int16_t 数据的个数 (例如发送6轴数据时为6,发送3个欧拉角时为3)
|
||||
// * @param status_byte 附加的状态字节 (例如 SHOCK_STA 或 FUSION_STA)
|
||||
// */
|
||||
// void send_data_anotc(uint8_t function_id, int16_t* data, uint8_t data_len, uint8_t status_byte) {
|
||||
// // 定义一个足够大的缓冲区来构建数据帧
|
||||
// // 最大长度(ID 0x01): 1(HEAD)+1(D_ADDR)+1(ID)+1(LEN)+13(DATA)+1(SC)+1(AC) = 19字节
|
||||
// uint8_t buffer[32];
|
||||
|
||||
// uint8_t data_payload_len = data_len * sizeof(int16_t) + sizeof(uint8_t);
|
||||
|
||||
// // 1. 填充帧头和地址
|
||||
// buffer[0] = 0xAA; // 帧头 HEAD
|
||||
// buffer[1] = 0xFF; // 目标地址 D_ADDR
|
||||
|
||||
// // 2. 填充功能码和数据长度
|
||||
// buffer[2] = function_id;
|
||||
// buffer[3] = data_payload_len;
|
||||
|
||||
// // 3. 填充数据内容 (DATA)
|
||||
// // 首先使用 memcpy 拷贝主要的 int16_t 数组数据
|
||||
// // &buffer[4] 是数据区的起始地址
|
||||
// memcpy(&buffer[4], (uint8_t*)data, data_len * sizeof(int16_t));
|
||||
// // 然后在数据区末尾填充状态字节
|
||||
// buffer[4 + data_len * sizeof(int16_t)] = status_byte;
|
||||
|
||||
// // 4. 计算校验和 (SC 和 AC)
|
||||
// uint8_t sum_check = 0;
|
||||
// uint8_t add_check = 0;
|
||||
|
||||
// // SC: 和校验 (从帧头到数据区最后一个字节)
|
||||
// for (int i = 0; i < 4 + data_payload_len; ++i) {
|
||||
// sum_check += buffer[i];
|
||||
// }
|
||||
|
||||
// // 将SC填充到缓冲区
|
||||
// buffer[4 + data_payload_len] = sum_check;
|
||||
|
||||
// // AC: 附加校验 (从帧头到SC)
|
||||
// for (int i = 0; i < 4 + data_payload_len + 1; ++i) {
|
||||
// add_check += buffer[i];
|
||||
// }
|
||||
|
||||
// // 将AC填充到缓冲区
|
||||
// buffer[4 + data_payload_len + 1] = add_check;
|
||||
|
||||
// // 5. 发送整个数据帧
|
||||
// uint16_t frame_length = 4 + data_payload_len + 2;
|
||||
// // Serial_Send_Buffer(buffer, frame_length);
|
||||
// for (int i = 0; i < frame_length; ++i) {
|
||||
// // 使用 %c 格式化字符来发送单个字节的原始值
|
||||
// printf("%c", buffer[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
// }
|
||||
|
||||
void ble_send_data(signed short *acc_gyro_input, float *Angle_output){
|
||||
char buffer[50]; //一次最多发送50字节
|
||||
u8 len = 0;
|
||||
//AA FF 01 六轴数据 EE
|
||||
//TO DO
|
||||
send_data_to_ble_client(&buffer,len);
|
||||
|
||||
|
||||
//AA FF 02 欧若拉角数据 EE
|
||||
// TO DO
|
||||
send_data_to_ble_client(&buffer,len);
|
||||
// 重置计时器
|
||||
void reset_ms_timer(void) {
|
||||
timer_offset_ms = sys_timer_get_ms();
|
||||
xlog("Timer has been reset.\n");
|
||||
}
|
||||
|
||||
// 从环形缓冲区读取数据并发送
|
||||
void send_sensor_data_task(void) {
|
||||
// printf("xtell_ble_send\n");
|
||||
// 获取从上次重置后经过的毫秒数
|
||||
u32 get_ms_timer(void) {
|
||||
return sys_timer_get_ms() - timer_offset_ms;
|
||||
}
|
||||
|
||||
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);
|
||||
/**
|
||||
* @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;
|
||||
|
||||
BLE_send_data = sensor_processing_task(acc_data_buf, gyr_data_buf);
|
||||
u8 data[50];
|
||||
data[0] = 0xBB;
|
||||
data[1] = 0xBE;
|
||||
data[2] = 0x01;
|
||||
data[3] = sizeof(BLE_send_data_t); //后续包的数据长度
|
||||
// send_data_to_ble_client(&data,sizeof(BLE_send_data_t)+4);
|
||||
memcpy(&data[4], &BLE_send_data, sizeof(BLE_send_data_t));
|
||||
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);
|
||||
|
||||
static int count = 0;
|
||||
if(count >=10){
|
||||
count = 0;
|
||||
#ifdef XTELL_TEST
|
||||
xlog("BLE_send_data_t:%d\n",sizeof(BLE_send_data_t));
|
||||
xlog("ACC_X:%d, ACC_Y:%d, ACC_Z:%d, GYR_X:%.d, GYR_Y:%d, GYR_Z:%d",
|
||||
acc_data_buf[0],acc_data_buf[1],acc_data_buf[2],gyr_data_buf[0],gyr_data_buf[1],gyr_data_buf[2]
|
||||
);
|
||||
printf("State: %d, Speed: %d cm/s, Distance: %d cm\n",
|
||||
BLE_send_data.skiing_state,
|
||||
BLE_send_data.speed_cms,
|
||||
BLE_send_data.distance_cm);
|
||||
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
|
||||
|
||||
char log_buffer[100]; // 100个字符应该足够了
|
||||
|
||||
// 使用 snprintf 进行格式化
|
||||
int num_chars_written = snprintf(
|
||||
log_buffer, // 目标缓冲区
|
||||
sizeof(log_buffer), // 目标缓冲区的最大容量
|
||||
"State: %d, Speed: %d cm/s, Distance: %d cm\n", // 格式化字符串
|
||||
BLE_send_data.skiing_state, // 第一个 %d 的参数
|
||||
BLE_send_data.speed_cms, // 第二个 %d 的参数
|
||||
BLE_send_data.distance_cm // 第三个 %d 的参数
|
||||
);
|
||||
send_data_to_ble_client(&log_buffer,strlen(log_buffer));
|
||||
// xlog("Pitch:%.2f, Roll:%.2f, Yaw:%.2f\n",
|
||||
// Angle_output[0],Angle_output[1],Angle_output[2]
|
||||
// );
|
||||
#else
|
||||
send_data_to_ble_client(&data,sizeof(BLE_send_data_t)+4);
|
||||
// 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
|
||||
}
|
||||
count++;
|
||||
SL_data_index += MPU_FIFO_LEN/4;
|
||||
|
||||
memset(&BLE_send_data, 0, sizeof(BLE_send_data_t));
|
||||
memset(&data, 0, 50);
|
||||
|
||||
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);
|
||||
}
|
||||
void gsensor_test(){
|
||||
|
||||
sys_timer_del(gsensor_id);
|
||||
// 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为单位
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//左右脚
|
||||
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字节
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
// 逐字节打包数据到 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){
|
||||
|
||||
// int ret = hw_iic_init(0);
|
||||
// xlog("hw_iic_init result:%d\n",ret);
|
||||
// //初始化传感器
|
||||
// SL_SC7U22_Config();
|
||||
#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);
|
||||
|
||||
soft_iic_init(0);
|
||||
gpio_set_direction(IO_PORTE_05,0); //设置PE5 输出模式
|
||||
gpio_set_pull_up(IO_PORTE_05,1);
|
||||
gpio_direction_output(IO_PORTE_05,1);
|
||||
#endif
|
||||
|
||||
SL_SC7U22_Config();
|
||||
// extern u8 LIS2DH12_Config(void);
|
||||
// LIS2DH12_Config();
|
||||
// MPU9250_Mag_Init();
|
||||
//iic总线设备扫描
|
||||
// extern void i2c_scanner_probe(void);
|
||||
// i2c_scanner_probe();
|
||||
|
||||
xlog("xtell_task_create\n");
|
||||
// 初始化环形缓冲区
|
||||
circle_buffer_init(&sensor_cb, sensor_data_buffer, SENSOR_DATA_BUFFER_SIZE);
|
||||
|
||||
circle_buffer_init(&g_ble_send_cb, g_sensor_data_storage, SENSOR_DATA_BUFFER_SIZE, sizeof(ble_send_data_t));
|
||||
|
||||
//初始化滑雪追踪器
|
||||
// SkiingTracker_Init(&skiing_data);
|
||||
xlog("SkiingTracker_Init\n");
|
||||
os_sem_create(&receiver_ready_sem, 0);
|
||||
|
||||
// create_process(&gsensor_id, "gsensor",NULL, gsensor_test, 1000);
|
||||
create_process(&test_id, "test",NULL, test, (int)(DELTA_TIME*1000));
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
133
apps/earphone/xtell_Sensor/sensor/AK8963.c
Normal file
133
apps/earphone/xtell_Sensor/sensor/AK8963.c
Normal 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;
|
||||
}
|
||||
46
apps/earphone/xtell_Sensor/sensor/AK8963.h
Normal file
46
apps/earphone/xtell_Sensor/sensor/AK8963.h
Normal 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
|
||||
216
apps/earphone/xtell_Sensor/sensor/BMP280.c
Normal file
216
apps/earphone/xtell_Sensor/sensor/BMP280.c
Normal 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));
|
||||
|
||||
}
|
||||
54
apps/earphone/xtell_Sensor/sensor/BMP280.h
Normal file
54
apps/earphone/xtell_Sensor/sensor/BMP280.h
Normal 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
|
||||
266
apps/earphone/xtell_Sensor/sensor/MMC56.c
Normal file
266
apps/earphone/xtell_Sensor/sensor/MMC56.c
Normal 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);
|
||||
|
||||
|
||||
}
|
||||
103
apps/earphone/xtell_Sensor/sensor/MMC56.h
Normal file
103
apps/earphone/xtell_Sensor/sensor/MMC56.h
Normal 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
@ -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 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,6 +131,10 @@ 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
|
||||
|
||||
@ -925,72 +925,3 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short
|
||||
}
|
||||
#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");
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
*/
|
||||
181
apps/earphone/xtell_Sensor/sensor/WF282A.c
Normal file
181
apps/earphone/xtell_Sensor/sensor/WF282A.c
Normal 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));
|
||||
}
|
||||
148
apps/earphone/xtell_Sensor/sensor/WF282A.h
Normal file
148
apps/earphone/xtell_Sensor/sensor/WF282A.h
Normal 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_
|
||||
@ -1,8 +1,10 @@
|
||||
#ifndef XTELL_H
|
||||
#define XTELL_H
|
||||
|
||||
#define KS_BLE 0
|
||||
#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
|
||||
@ -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,7 +149,7 @@ 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);
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -138,7 +138,7 @@ int hw_iic_init(hw_iic_dev iic)
|
||||
iic_end_pnd_clr(iic_regs[id]);
|
||||
iic_start_pnd_clr(iic_regs[id]);
|
||||
iic_enable(iic_regs[id]);
|
||||
#if 0
|
||||
#if 1
|
||||
printf("info->scl = %d\n", iic_get_scl(iic));
|
||||
printf("info->sda = %d\n", iic_get_sda(iic));
|
||||
printf("info->baudrate = %d\n", iic_info_baud(iic));
|
||||
@ -150,7 +150,7 @@ int hw_iic_init(hw_iic_dev iic)
|
||||
printf("IIC_CON1 0x%04x\n", iic_regs[id]->CON1);
|
||||
printf("IIC_BAUD 0x%02x\n", iic_regs[id]->BAUD);
|
||||
//printf("IIC_BUF %02x\n", iic_regs[id]->BUF);
|
||||
printf("IOMC1 0x%08x\n", JL_IOMAP->CON1);
|
||||
// printf("IOMC1 0x%08x\n", JL_IOMAP->CON1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@ -188,12 +188,18 @@ void hw_iic_stop(hw_iic_dev iic)
|
||||
|
||||
u8 hw_iic_tx_byte(hw_iic_dev iic, u8 byte)
|
||||
{
|
||||
// printf("====debug1=======\n");
|
||||
u8 id = iic_get_id(iic);
|
||||
// printf("====debug2=======\n");
|
||||
iic_dir_out(iic_regs[id]);
|
||||
// printf("====debug3=======\n");
|
||||
iic_buf_reg(iic_regs[id]) = byte;
|
||||
// printf("====debug4=======\n");
|
||||
iic_cfg_done(iic_regs[id]);
|
||||
// printf("====debug5=======\n");
|
||||
/* putchar('a'); */
|
||||
while (!iic_pnd(iic_regs[id]));
|
||||
// printf("====debug6=======\n");
|
||||
iic_pnd_clr(iic_regs[id]);
|
||||
/* putchar('b'); */
|
||||
return iic_send_is_ack(iic_regs[id]);
|
||||
|
||||
@ -253,6 +253,7 @@
|
||||
_MASK_MEM_BEGIN = ABSOLUTE(0x19fc00);
|
||||
_MASK_MEM_SIZE = ABSOLUTE(0x1a4);
|
||||
|
||||
|
||||
EXTERN(
|
||||
_start
|
||||
|
||||
@ -274,25 +275,14 @@ cvsd_decoder
|
||||
|
||||
|
||||
pcm_decoder
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mp3_decoder
|
||||
wtgv2_decoder
|
||||
|
||||
|
||||
|
||||
aac_decoder
|
||||
cvsd_encoder
|
||||
|
||||
|
||||
|
||||
msbc_encoder
|
||||
audio_dac_driver
|
||||
|
||||
);
|
||||
|
||||
UPDATA_SIZE = 0x80;
|
||||
@ -428,6 +418,7 @@ SECTIONS
|
||||
battery_notify_begin = .;
|
||||
*(.battery_notify)
|
||||
battery_notify_end = .;
|
||||
|
||||
. = ALIGN(4);
|
||||
__VERSION_BEGIN = .;
|
||||
KEEP(*(.sys.version))
|
||||
@ -518,6 +509,7 @@ SECTIONS
|
||||
*(.audio_track_data)
|
||||
*(.audio_adc_data)
|
||||
|
||||
|
||||
. = ALIGN(4);
|
||||
*(.data*)
|
||||
|
||||
@ -726,6 +718,7 @@ SECTIONS
|
||||
|
||||
} > ram0
|
||||
|
||||
|
||||
data_code_pc_limit_end = .;
|
||||
__report_overlay_end = .;
|
||||
|
||||
@ -815,6 +808,7 @@ SECTIONS
|
||||
}
|
||||
|
||||
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data : ALIGN(4)
|
||||
@ -846,6 +840,7 @@ SECTIONS
|
||||
|
||||
UPDATE_CODE_TOTAL_SIZE = update_code_end - update_code_start;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data : ALIGN(4)
|
||||
@ -948,6 +943,7 @@ BTSTACK_LE_HOST_MESH_RAM_TOTAL = BTSTACK_LE_HOST_MESH_DATA_SIZE + BTSTACK_LE_HOS
|
||||
BTSTACK_LE_HOST_MESH_FLASH_TOTAL = BTSTACK_LE_HOST_MESH_CODE_SIZE;
|
||||
|
||||
BTSTACK_CODE_SIZE = (btstack_code_end - btstack_code_start) + (btstack_data_end - btstack_data_start);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data : ALIGN(4)
|
||||
@ -1241,6 +1237,7 @@ SECTIONS
|
||||
*(.os_code)
|
||||
} > ram0
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data : ALIGN(4)
|
||||
@ -1450,6 +1447,7 @@ SECTIONS
|
||||
BTCTLER_RAM_TOTAL = (btctler_data_end - btctler_data_start) + (btctler_bss_end - btctler_bss_start);
|
||||
BTCTLER_CODE_TOTAL = (btctler_code_end - btctler_code_start);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data : ALIGN(4)
|
||||
@ -1567,6 +1565,7 @@ SECTIONS
|
||||
*(.timer.text.cache.L1)
|
||||
*(.gpio.text.cache.L1)
|
||||
*(.iic_hw.text.cache.L1)
|
||||
|
||||
driver_data_code_end = .;
|
||||
. = ALIGN(4);
|
||||
} > ram0
|
||||
@ -1577,6 +1576,7 @@ SECTIONS
|
||||
DRIVER_DATA_CODE_TOTAL = (driver_data_code_end - driver_data_code_start);
|
||||
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data : ALIGN(4)
|
||||
@ -2016,6 +2016,7 @@ SECTIONS
|
||||
} > ram0
|
||||
}
|
||||
|
||||
|
||||
text_begin = ADDR(.text);
|
||||
text_size = SIZEOF(.text);
|
||||
text_end = text_begin + text_size;
|
||||
|
||||
@ -33,19 +33,7 @@ cvsd_decoder
|
||||
|
||||
|
||||
pcm_decoder
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mp3_decoder
|
||||
wtgv2_decoder
|
||||
|
||||
|
||||
|
||||
aac_decoder
|
||||
cvsd_encoder
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -61,4 +61,4 @@ copy /b text.bin + data.bin + mov_slot.bin + data_code.bin + aec.bin + aac.bin +
|
||||
|
||||
del !bankfiles! common.bin text.bin data.bin bank.bin
|
||||
copy eq_cfg_hw_less.bin eq_cfg_hw.bin
|
||||
call download/earphone/download_app_ota.bat
|
||||
call download/earphone/download.bat
|
||||
|
||||
@ -10,7 +10,7 @@ copy ..\..\ota.bin .
|
||||
copy ..\..\anc_coeff.bin .
|
||||
copy ..\..\anc_gains.bin .
|
||||
|
||||
..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin ..\..\eq_cfg_hw.bin -uboot_compress -key AC69.key -format all -key 646-AC690X-7603.key
|
||||
..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin ..\..\eq_cfg_hw.bin -uboot_compress -format all -key 646-AC690X-7603.key
|
||||
|
||||
@REM..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br34 -boot 0x20000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin ..\..\cfg_tool.bin -res tone.cfg kws_command.bin p11_code.bin -uboot_compress
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -14,13 +14,12 @@
|
||||
|
||||
|
||||
[EXTRA_CFG_PARAM]
|
||||
NEW_FLASH_FS = YES;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BR22_TWS_DB = YES;
|
||||
FLASH_SIZE = 0x100000;
|
||||
BR22_TWS_VERSION = 0;
|
||||
FORCE_4K_ALIGN = YES;
|
||||
SPECIAL_OPT = 0;
|
||||
CHIP_NAME = AC701N;
|
||||
ENTRY = 0x6000100;
|
||||
PID = AC701N;
|
||||
@ -63,6 +62,12 @@ SPI = 2_3_0_0;
|
||||
UTTX = PB02;
|
||||
UTBD = 1000000;
|
||||
UTRX = PP00;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
RESET = PA04_01_0;
|
||||
[FW_ADDITIONAL]
|
||||
FILE_LIST = (file = ota.bin: type = 100);
|
||||
[RESERVED_CONFIG]
|
||||
|
||||
@ -205,7 +205,7 @@ UTRX = CONFIG_UART_UPDATE_PIN; //串口升级[PB00 PB05 PA05]
|
||||
/* RESET = CAT3(CONFIG_RESET_PIN, CONFIG_RESET_TIME, CONFIG_RESET_LEVEL); //port口_长按时间_有效电平(长按时间有00、01、02、04、08三个值可选,单位为秒,当长按时间为00时,则关闭长按复位功能。) */
|
||||
|
||||
#ifdef CONFIG_SUPPORT_RESET1
|
||||
RESET1 = CAT3(CONFIG_RESET1_PIN, CONFIG_RESET1_TIME, CONFIG_RESET1_LEVEL); //port口_长按时间_有效电平(长按时间有00、01、02、04、08三个值可选,单位为秒,当长按时间为00时,则关闭长按复位功能。)
|
||||
RESET = CAT3(CONFIG_RESET1_PIN, CONFIG_RESET1_TIME, CONFIG_RESET1_LEVEL); //port口_长按时间_有效电平(长按时间有00、01、02、04、08三个值可选,单位为秒,当长按时间为00时,则关闭长按复位功能。)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VDDIO_LVD_LEVEL
|
||||
|
||||
36858
cpu/br28/tools/rom.lst
36858
cpu/br28/tools/rom.lst
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
195303
cpu/br28/tools/sdk.lst
195303
cpu/br28/tools/sdk.lst
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -19,16 +19,17 @@
|
||||
#include "ble/ll_config.h"
|
||||
|
||||
// #define CONFIG_LE_FEATURES \
|
||||
(\
|
||||
LE_ENCRYPTION | \
|
||||
LE_CORE_V50_FEATURES \
|
||||
)
|
||||
// (\
|
||||
// LE_ENCRYPTION | \
|
||||
// LE_CORE_V50_FEATURES \
|
||||
// )
|
||||
|
||||
#define CONFIG_LE_FEATURES 0//(LE_ENCRYPTION)
|
||||
// #define CONFIG_LE_FEATURES (LE_CORE_V50_FEATURES | LE_DATA_PACKET_LENGTH_EXTENSION)//(LE_ENCRYPTION)
|
||||
#define CONFIG_LE_FEATURES 0
|
||||
|
||||
// #define CONFIG_LE_ROLES (LE_ADV|LE_SCAN|LE_INIT|LE_SLAVE|LE_MASTER)
|
||||
// #define CONFIG_LE_ROLES (LE_ADV|LE_SCAN)
|
||||
#define CONFIG_LE_ROLES (LE_ADV)
|
||||
#define CONFIG_LE_ROLES (LE_ADV|LE_SCAN)
|
||||
// #define CONFIG_LE_ROLES (LE_ADV)
|
||||
|
||||
#include "classic/lmp_config.h"
|
||||
|
||||
|
||||
@ -54,8 +54,7 @@
|
||||
#define SYS_TOUCHPAD_EVENT 0x0400
|
||||
#define SYS_ADT_EVENT 0x0800
|
||||
#define SYS_AUD_EVENT 0x1000
|
||||
|
||||
|
||||
#define SYS_APP_USER_EVENT 0x1000
|
||||
|
||||
|
||||
#define DEVICE_EVENT_FROM_AT_UART (('A' << 24) | ('T' << 16) | ('U' << 8) | '\0')
|
||||
@ -271,6 +270,12 @@ struct touchpad_event {
|
||||
s8 y;
|
||||
};
|
||||
|
||||
struct app_user_event {
|
||||
unsigned char* buffer;
|
||||
size_t command;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct sys_event {
|
||||
u16 type;
|
||||
u8 consumed;
|
||||
@ -302,6 +307,7 @@ struct sys_event {
|
||||
struct matrix_key_event matrix_key;
|
||||
struct touchpad_event touchpad;
|
||||
struct adt_event adt;
|
||||
struct app_user_event app;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user