init: DLD154Pro 单线圈蓝牙车检器,从 DLD110SV4 复制初始代码和文档

This commit is contained in:
wangfq
2026-07-21 10:38:23 +08:00
commit 5f5f697992
128 changed files with 64619 additions and 0 deletions
@@ -0,0 +1,855 @@
/*
* dbn_ble_srv.c
*
* Created on: Aug 27, 2024
* Author: Thinkpad
*/
#include "dbn_ble_srv.h"
#include "cmcng.h"
#include "storage.h"
#include "CH59x_common.h"
uint8_t g_flag_notify_temp = 0; //临时通知notify flag, 0 disable, 1 enable
BLE_Notify_Buf g_notify_buftemp = {0,0, ""};
BLE_Notify_Buf g_ble_rcv_buf = {0,0, ""};
BLE_Notify_Buf g_tmp_report_buf = {0,0, ""};
Buf_DBN_BLE g_buf_ble_response = {0,0,0,0,0,0,0,""};
uint8_t tmp_ble_buf[MAX_BLE_TMP_BUF_LEN] = "";
extern uint8_t loop1_FLAG_CUT;
void clear_ble_notify_buf(BLE_Notify_Buf * buf)
{
buf->len = 0;
buf->flag = 0;
memset(buf, 0, MAX_BLE_Notify_Buf_LEN);
}
void clear_buf_dbn_ble(Buf_DBN_BLE * buf)
{
uint8_t i = 0;
buf->flag = 0;
buf->magic = 0;
buf->cmd = 0;
memset(buf->dat, 0, buf->dat_len);
buf->dat_len = 0;
buf->dat_offset = 0;
buf->pkg_amount = 0;
buf->pkg_seq = 0;
}
void clear_buf_dbn_ble_all(Buf_DBN_BLE * buf)
{
uint8_t i = 0;
// if(g_flag_debug)
{
// PRINT("\nclear_buf_dbn_ble_flag:%02X,magic:%02X,pkg_amount:%02X,seq:%02X,cmd:%02X, dat_len:%d,offset:%d\n",
// buf->flag,buf->magic,buf->pkg_amount, buf->pkg_seq,buf->cmd, buf->dat_len,buf->dat_offset);
// for(i = 0; i < buf->dat_len; i++)
// {
// PRINT(" %02X", buf->dat[i]);
// }
// PRINT("\n");
}
buf->flag = 0;
buf->magic = 0;
buf->cmd = 0;
memset(buf->dat, 0, MAX_BLE_BUF_LEN);
buf->dat_len = 0;
buf->dat_offset = 0;
buf->pkg_amount = 0;
buf->pkg_seq = 0;
}
// 0: 包检测异常
//检测包是否完整,Magic Byte, Header Byte, CK Byte, 校验字节(异或校验,以及和校验)
uint8_t check_pkg(uint8_t * pkg, uint8_t len)
{
if(len < 6) //min length of ble pkg is 6
return 0;
uint8_t magic = pkg[0];
uint8_t _len = pkg[2]; //data_len
if(_len + 5 < len)
{
return 0;
}
uint8_t _dxor = 0;
uint8_t _dsum = 0;
uint8_t _cklen = _len + 3; //不计算 校验字节
uint8_t i = 0;
for(i = 1; i < _cklen; i++)
{
_dxor ^= pkg[i];
_dsum += pkg[i];
}
if((_dxor != pkg[_cklen]) || (_dsum != pkg[_cklen+1]))
{
// if(g_flag_debug)
// {
// PRINT("_dxor:0x%02X - 0x%02X, _dsum:0x%02X - 0x%02X\r\n", _dxor, pkg[_cklen], _dsum, pkg[_cklen + 1] );
// }
return 0;
}
return magic;
}
static uint8_t set_dev_info_to_ready(uint8_t *dat_dst)
{
uint8_t i = 0, j = 0;
for(i = 0; i < 6; i++)
{
dat_dst[i] = MACAddr[i];
// PRINT(" %02X",dat_dst[i] );
}
// memcpy(dat_dst, MACAddr, sizeof(MACAddr));
// i += sizeof(MACAddr);
dat_dst[i++] = HARDWARE_VER_MAIN;
dat_dst[i++] = HARDWARE_VER_SUB;
dat_dst[i++] = FIRMWARE_VER_MAIN;
dat_dst[i++] = FIRMWARE_VER_SUB;
// Dev Model
dat_dst[i++] = strlen(PRODUCT_MODEL);
memcpy(&dat_dst[i], PRODUCT_MODEL, strlen(PRODUCT_MODEL));
i += strlen(PRODUCT_MODEL);
// Dev Number
for(j = 0; j < 6; j++, i++)
{
dat_dst[i] = g_dev_number[j];
}
// sub_code
// memcpy(&dat_dst[i], &(g_sub_code_enable.code_set), sizeof(g_sub_code_enable.code_set));
// i += sizeof(g_sub_code_enable.code_set);
dat_dst[i++] = 0;
dat_dst[i++] = 0;
dat_dst[i++] = 0; // g_bus_dus_amount_stored.bus1_amount;
dat_dst[i++] = 0; //g_bus_dus_amount_stored.bus2_amount;
return i;
}
uint8_t set_response_buf(Buf_DBN_BLE *response_dst, uint8_t magic, uint8_t cmd, uint8_t * dat, uint8_t dat_len)
{
uint8_t ret = 0;
uint8_t i = 0;
clear_buf_dbn_ble(response_dst);
response_dst->magic = magic;
response_dst->cmd = cmd;
uint8_t _amount = dat_len / MAX_BLE_DAT_RESPONSE_LEN;
if((dat_len % MAX_BLE_DAT_RESPONSE_LEN) > 0)
{
_amount++;
}
response_dst->pkg_amount = _amount;
response_dst->pkg_seq = 0;
response_dst->dat_len = dat_len;
for(i = 0; i < dat_len; i++)
{
response_dst->dat[i] = dat[i];
}
response_dst->flag = 1;
return ret;
}
uint16_t compute_ckb(uint8_t header_0, uint8_t header_1, uint8_t header_cmd, uint8_t *dat, uint8_t dat_len)
{
uint16_t ret = 0;
uint8_t i = 0;
uint8_t _dxor = header_0 ^ header_1 ^ header_cmd;
uint8_t _dsum = header_0 + header_1 + header_cmd;
for(i = 0; i < dat_len; i++)
{
_dxor ^= dat[i];
_dsum += dat[i];
}
ret = _dxor;
ret = (ret << 8) + (_dsum % 0x100);
return ret;
}
//response_ori must set_response_buf first
uint8_t set_response_to_notify(Buf_DBN_BLE *response_ori, BLE_Notify_Buf * notify_dst)
{
uint8_t ret = 0;
uint8_t i = 0;
uint16_t _ckbl = 0;
if(notify_dst->flag)
{ //dst not idle
return ret;
}
if(response_ori->flag == 0)
{ //ori idle
return ret;
}
if(response_ori->pkg_amount <= 1)
{
//没有分包的情况
notify_dst->buf[0] = response_ori->magic;
if(response_ori->magic == MAGIC_BYTE_DBN_TRANSPARENT)
{
notify_dst->buf[1] = response_ori->pkg_seq;
}
else
{
notify_dst->buf[1] = 0;
}
notify_dst->buf[2] = response_ori->dat_len + 1; // 1 is cmd_byte
notify_dst->buf[3] = response_ori->cmd;
_ckbl = compute_ckb(notify_dst->buf[1], notify_dst->buf[2], notify_dst->buf[3], response_ori->dat, response_ori->dat_len);
for(i = 0; i < response_ori->dat_len; i++)
{
notify_dst->buf[i + 4] = response_ori->dat[i];
}
notify_dst->buf[i + 4] = (uint8_t)(_ckbl >> 8);
i++;
notify_dst->buf[i + 4] = (uint8_t)(_ckbl);
i++;
notify_dst->len = i + 4;
// if(g_flag_debug)
// {
// PRINT("set_response_to_notify_dst__\n");
// for(i = 0; i < notify_dst->len; i++)
// {
// PRINT(" %02X", notify_dst->buf[i]);
// }
// }
notify_dst->flag = 1;
clear_buf_dbn_ble(response_ori);
return 1;
}
else
{
notify_dst->buf[0] = response_ori->magic;
uint8_t _pkg_amount = response_ori->pkg_amount;
uint8_t _pkg_seq = response_ori->pkg_seq;
uint8_t _remain_len = response_ori->dat_len - response_ori->dat_offset;
if(_remain_len > MAX_BLE_DAT_RESPONSE_LEN)
{
_remain_len = MAX_BLE_DAT_RESPONSE_LEN;
}
_pkg_seq += 1;
notify_dst->buf[1] = (_pkg_amount << 4)|(_pkg_seq);
notify_dst->buf[2] = _remain_len + 1; // 1 is cmd_byte
notify_dst->buf[3] = response_ori->cmd;
_ckbl = compute_ckb(notify_dst->buf[1], notify_dst->buf[2], notify_dst->buf[3],
&(response_ori->dat[response_ori->dat_offset]), _remain_len);
for(i = 0; i < _remain_len; i++)
{
notify_dst->buf[i + 4] = response_ori->dat[response_ori->dat_offset + i];
}
notify_dst->buf[i + 4] = (uint8_t)(_ckbl >> 8);
i++;
notify_dst->buf[i + 4] = (uint8_t)(_ckbl);
i++;
response_ori->dat_offset += _remain_len;
response_ori->pkg_seq += 1;
notify_dst->len = i + 4;
// if(g_flag_debug)
// {
// PRINT("\nset_notify_dst,amount:%d,seq:%d,offset:%d\n", response_ori->pkg_amount,response_ori->pkg_seq, response_ori->dat_offset);
// for(i = 0; i < notify_dst->len; i++)
// {
// PRINT(" %02X", notify_dst->buf[i]);
// }
// PRINT("\n");
// }
notify_dst->flag = 1;
if(response_ori->pkg_amount == response_ori->pkg_seq)
{
clear_buf_dbn_ble(response_ori);
}
return 1;
}
}
static uint8_t unpack_packs(uint8_t *pkg, uint8_t len)
{
static uint8_t _offset = 0;
uint8_t _header_0 = pkg[1];
uint8_t _header_0_high = _header_0 >> 4;
uint8_t _header_0_low = _header_0 % 0x10;
uint8_t _len = pkg[2];
uint8_t _cmd = pkg[3];
uint8_t i = 0, j = 0, k = 0;
uint8_t _amount = 0;
if(_header_0_high)
{
if(_header_0_low == 1)
{
g_buf_ble_response.magic = pkg[0];
g_buf_ble_response.pkg_amount = _header_0_high;
g_buf_ble_response.pkg_seq = _header_0_low;
g_buf_ble_response.cmd = _cmd;
memcpy(&g_buf_ble_response.dat, &pkg[4], _len - 1);
_offset = _len - 1;
g_buf_ble_response.dat_len = _len - 1;
}
else
{
if((g_buf_ble_response.pkg_amount != _header_0_high) || (g_buf_ble_response.cmd != _cmd))
{
//printf("\nNot_the_right_sub_pack!\n");
clear_buf_dbn_ble_all(&g_buf_ble_response);
return 0;
}
if(g_buf_ble_response.pkg_seq != (_header_0_low -1))
{
//printf("\nNot_the_right_sub_pack!!\n");
clear_buf_dbn_ble_all(&g_buf_ble_response);
return 0;
}
g_buf_ble_response.pkg_seq = _header_0_low;
g_buf_ble_response.dat_len += (_len - 1); // not include cmd_byte
memcpy(&g_buf_ble_response.dat[_offset], &pkg[4], _len - 1);
_offset += (_len - 1);
}
}
else
{
clear_buf_dbn_ble(&g_buf_ble_response);
g_buf_ble_response.magic = pkg[0];
g_buf_ble_response.pkg_amount = 1;
g_buf_ble_response.pkg_seq = 0;
g_buf_ble_response.cmd = _cmd;
g_buf_ble_response.dat_len = (_len - 1);
memcpy(g_buf_ble_response.dat, &pkg[4], _len - 1);
}
if(_header_0_high == _header_0_low)
{
// received end.
// if(g_flag_debug)
// {
// printf("\nReceive_pack_over:\n");
// for(i = 0; i < g_buf_ble_response.dat_len; i++)
// {
// printf(" %02X", g_buf_ble_response.dat[i]);
// }
// printf("\nEnd\n");
// }
if(g_buf_ble_response.cmd == CMD_DBN_CHECK_PASS)
{
// memset(tmp_ble_buf, 0, MAX_BLE_TMP_BUF_LEN);
// if(g_buf_ble_response.dat_len != 6)
// {
// //response pass failed
// tmp_ble_buf[0] = 0x01;
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, 1);
// }
// else
// {
// char _pass = check_ble_safe_pass(g_buf_ble_response.dat);
// if(_pass)
// {
// //response ok
// tmp_ble_buf[0] = 0x0;
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, 1);
// }
// else
// {
// //response failed
// tmp_ble_buf[0] = 0x01;
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, 1);
// }
// }
} //end if g_buf_ble_response.cmd == CMD_DBN_CHECK_PASS
else if(g_buf_ble_response.cmd == CMD_DBN_MODIFY_PASS)
{
// memset(tmp_ble_buf, 0, MAX_BLE_TMP_BUF_LEN);
// if(g_buf_ble_response.dat_len != 12)
// {
// //response failed
// tmp_ble_buf[0] = 0x01;
// }
// else
// {
// char _pass = check_ble_safe_pass(g_buf_ble_response.dat);
// if(_pass)
// {
// //response ok
// set_ble_safe_pass(&g_buf_ble_response.dat[6]);
// tmp_ble_buf[0] = 0x00;
// }
// else
// {
// //response failed
// tmp_ble_buf[0] = 0x01;
// }
// }
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, 1);
} //end if g_buf_ble_response.cmd == CMD_DBN_MODIFY_PASS
}
return 0;
}
static void unpack_pkg_set_cjq_param(uint8_t *pkg, uint8_t len)
{
uint8_t i = 4, k = 0;
uint8_t tmp[7] = {0};
k = pkg[i++];
g_loop_cng_unit.sensitvity = k & 0x0F;
g_loop_cng_unit.loopFreq_Level = k >> 4;
g_loop_cng_unit.delay_time = pkg[i++];
k = pkg[i++];
g_loop_cng_unit.output_mode = k & 0x03;
g_loop_cng_unit.loopSafe_Timeout = k >> 3;
g_loop_cng_unit.exist_mode = pkg[i++];
g_loop_cng_unit.direction_mode = pkg[i++];
g_function_mode = (g_loop_cng_unit.direction_mode >> 4) & 0x0F;
g_hold_time = g_loop_cng_unit.exist_mode * 30 * 20;
i = 0;
tmp[i++] = g_loop_cng_unit.sensitvity;
tmp[i++] = g_loop_cng_unit.delay_time;
tmp[i++] = g_loop_cng_unit.output_mode;
tmp[i++] = g_loop_cng_unit.direction_mode; //默认 0
tmp[i++] = g_loop_cng_unit.loopFreq_Level;
tmp[i++] = g_loop_cng_unit.loopSafe_Timeout;
tmp[i++] = g_loop_cng_unit.exist_mode;
write_cfg_to_store(4, tmp, 7);
}
void usart_packet_processing_acs_loop(uint8_t flag)
{
static uint32_t _current_freq = 0;
static uint32_t _last_freq = 0;
clear_ble_notify_buf(&g_tmp_report_buf);
uint8_t i = 0,j;
uint32_t _loop_capvd = g_loop_acs_info.loop_capvd;
g_tmp_report_buf.flag = flag;
g_loop_acs_info.condition = (g_env_activity > 150) ? 15 : (g_env_activity / 10);
if(loop1_FLAG_CUT){
g_loop_acs_info.condition = 15;
}
g_tmp_report_buf.buf[i++] = SENS_LOOP_DYNAMIC;
g_tmp_report_buf.buf[i++] = (g_loop_acs_info.loop_num << 6) | (g_freq_level << 4) | (g_loop_acs_info.condition & 0x0F);
g_tmp_report_buf.buf[i++] = (g_loop_acs_info.relay2_state << 6) | (g_loop_acs_info.relay2_cng << 4) | (g_loop_acs_info.relay1_state << 2) | (g_loop_acs_info.relay1_cng & 0x3);
g_tmp_report_buf.buf[i++] = (loop1_FLAG_CUT << 7) | (g_loop_acs_info.car_state << 4) | (g_loop_acs_info.direction & 0x0F);
g_freq_tmp = (float)(g_sys_freq )/((float)(_loop_capvd)/((float)(loop1_LPCNT))); // ?? ???????
// why + 360? balance value ,补偿,经验值,可以根据示波器比较再进行调整
_current_freq = (uint32_t)(g_freq_tmp) + 360 ;// * 32);
// if(flag)
{
g_loop_acs_info.frequent = _current_freq;
_last_freq = _current_freq;
}
// else
// {
// if(_current_freq > _last_freq)
// {
// if(_current_freq - _last_freq > 30)
// {
// //稳定显示,过滤低数值的变动
// g_loop_acs_info.frequent = _current_freq;
// _last_freq = _current_freq;
// }
// }
// else
// {
// if(_last_freq - _current_freq > 30)
// {
// // 稳定显示,过滤低数值的变动
// g_loop_acs_info.frequent = _current_freq;
// _last_freq = _current_freq;
// }
// }
// }
// g_tmp_report_buf.buf[i++] = (uint8_t)flt_mgr.init_freq;
// g_tmp_report_buf.buf[i++] = (flt_mgr.init_freq >> 8) % 0x100;
// g_tmp_report_buf.buf[i++] = (flt_mgr.init_freq >> 16) % 0x100;
if(g_loop_acs_info.flag_change){
g_loop_acs_info.flag_change = 0;
}
g_tmp_report_buf.buf[i++] = (g_loop_power_up_state == 0) ? 1 : ((uint8_t)g_loop_cut_amount);
g_tmp_report_buf.buf[i++] = (g_loop_power_up_state == 0) ? 0 : ((g_loop_cut_amount >> 8) % 0x100);
g_tmp_report_buf.buf[i++] = (g_loop_power_up_state == 0) ? 0 : ((g_loop_cut_amount >> 16) % 0x100);
g_tmp_report_buf.buf[i++] = (uint8_t)g_loop_acs_info.frequent;
g_tmp_report_buf.buf[i++] = (g_loop_acs_info.frequent >> 8) % 0x100;
g_tmp_report_buf.buf[i++] = (g_loop_acs_info.frequent >> 16) % 0x100;
g_tmp_report_buf.buf[i++] = (uint8_t)g_loop_acs_info.variation;
g_tmp_report_buf.buf[i++] = (g_loop_acs_info.variation >> 8) % 0x100;
g_tmp_report_buf.buf[i++] = (g_loop_acs_info.variation >> 16) % 0x100;
g_tmp_report_buf.len = i;
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, CMD_SUB_SENS_REPORT, g_tmp_report_buf.buf, g_tmp_report_buf.len);
}
void report_sens_acs(void)
{
uint8_t _ret = 0;
if(g_dbn_ble_state_acs_enable.enable)
{
// 上报线圈动态数据
// switch(g_dbn_ble_state_acs_enable.sens_type)
// {
// //case SENS_LOOP_DYNAMIC:
// default:
// {
// 线圈动态信息
if(g_loop_acs_info.flag_event)
{
// if(g_loop_acs_info.report_counter >= 3)
{
// refresh_loop_acs_info();
g_loop_acs_info.flag_event = 0;
g_loop_acs_info.report_counter = 0;
// report
usart_packet_processing_acs_loop(1);
}
}
if(g_loop_acs_info.report_counter >= 120)
{
// refresh_loop_acs_info();
g_loop_acs_info.report_counter = 0;
// report
usart_packet_processing_acs_loop(0);
// response_bt_dat(usart_packet_processing_acs_loop(0));
}
if(g_dbn_ble_state_acs_enable.timeout_min)
{
if(mstick() - g_dbn_ble_state_acs_enable.counter >= g_dbn_ble_state_acs_enable.timeout_counter)
{
g_dbn_ble_state_acs_enable.flag = 0;
g_dbn_ble_state_acs_enable.enable = 0;
}
}
// } break;
// }
}
}
void manage_dbn_ble_default(uint8_t *pkg, uint8_t len)
{
uint8_t _header_0 = pkg[1];
uint8_t _header_0_high = 0;
uint8_t _header_0_low = _header_0 % 0x10;
uint8_t _len = pkg[2];
uint8_t _cmd = pkg[3];
uint8_t _tmp = 0;
uint8_t i = 0, k = 0;
memset(tmp_ble_buf, 0, MAX_BLE_TMP_BUF_LEN);
switch(_cmd)
{
case CMD_DBN_GET_DEV_INFO:
{
i = set_dev_info_to_ready(tmp_ble_buf);
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
// printf("\nGet_dev_info\n");
} break;
case CMD_DBN_RESET_DEV:
{
//Restart Dev
SYS_ResetExecute();
} break;
// case CMD_DBN_CHECK_PASS:
// {
// unpack_packs(pkg, len);
// } break;
case CMD_DBN_SET_CJQ_PARAM:
{
// PRINT("\nSet_Cjq_param\n");
unpack_pkg_set_cjq_param(pkg, len);
tmp_ble_buf[0] = 0;
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, 1);
para_store_init();
} break;
case CMD_DBN_GET_CJQ_PARAM:
{
i = 0;
tmp_ble_buf[i++] = (g_freq_sens + 1) | (g_loop_cng_unit.loopFreq_Level << 4);
tmp_ble_buf[i++] = g_loop_cng_unit.delay_time;
tmp_ble_buf[i++] = g_loop_cng_unit.output_mode | (g_loop_cng_unit.loopSafe_Timeout << 3);
tmp_ble_buf[i++] = g_loop_cng_unit.exist_mode;
tmp_ble_buf[i++] = g_loop_sens_list.total << 4;
tmp_ble_buf[i++] = (g_loop_power_up_state == 0) ? 1 : ((uint8_t)(g_loop_cut_amount));
tmp_ble_buf[i++] = (uint8_t)(g_loop_cut_amount >> 8);
tmp_ble_buf[i++] = g_function_mode;
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
// PRINT("\nGet_Cjq_param\n");
} break;
// case CMD_DBN_LOOP_SAMPLE_PARAM:{
// // 获取/设置 地感采样参数
//// PRINT("SAMPLE_CNG: %02X\n", pkg[4]);
// if(pkg[4] == 0){
// // get param
// i = 0;
// tmp_ble_buf[i++] = 0x10;
// tmp_ble_buf[i++] = g_loop_balance_planB.sample_cng.flag;
// tmp_ble_buf[i++] = g_loop_balance_planB.sample_cng.max_amplitude;
// tmp_ble_buf[i++] = (g_loop_balance_planB.sample_cng.max_amplitude >> 8) & 0xFF;
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
// }
// else {
// // set param
// i = 0;
// k = 4;
// tmp_ble_buf[i++] = 0x11;
// tmp_ble_buf[i++] = pkg[k++];
// tmp_ble_buf[i++] = pkg[k++];
// tmp_ble_buf[i++] = pkg[k++];
//
// g_loop_balance_planB.sample_cng.flag = tmp_ble_buf[1];
// g_loop_balance_planB.sample_cng.max_amplitude = tmp_ble_buf[2] | ((tmp_ble_buf[3] << 8 ) & 0xFF00);
//
// write_cfg_to_store(Addr_Loop_PlanB_Cng_Offset, &tmp_ble_buf[1], sizeof(Loop_Sample_Cng));
//
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
//
// //TODO: 不整个设备复位,复位除了蓝牙的部分,即蓝牙正常,其他部分重新初始化
// para_store_init();
// }
//
// } break;
// case CMD_DBN_LOOP_BALANCE_PARAM: {
// // 获取/设置 漂移补偿参数
//// PRINT("Balance_CNG: %02X\n", pkg[4]);
// if(pkg[4] == 0){
// // get param
// i = 0;
// tmp_ble_buf[i++] = 0x10;
// tmp_ble_buf[i++] = g_loop_balance_planB.balance_ori_cng.flag;
// tmp_ble_buf[i++] = g_loop_balance_planB.balance_ori_cng.max_cnt;
// tmp_ble_buf[i++] = (g_loop_balance_planB.balance_ori_cng.max_cnt >> 8) & 0xFF;
//
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
// }
// else{
// // set param
// i = 0;
// k = 4;
// tmp_ble_buf[i++] = 0x11;
// tmp_ble_buf[i++] = pkg[k++];
// tmp_ble_buf[i++] = pkg[k++];
// tmp_ble_buf[i++] = pkg[k++];
//
// g_loop_balance_planB.balance_ori_cng.flag = tmp_ble_buf[1];
// g_loop_balance_planB.balance_ori_cng.max_cnt = tmp_ble_buf[2] | ((tmp_ble_buf[3] << 8 ) & 0xFF00);
//
// write_cfg_to_store(Addr_Loop_PlanB_Cng_Offset + sizeof(Loop_Sample_Cng), &tmp_ble_buf[1], sizeof(Loop_Balance_Ori_Cng));
//
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
//
// //TODO: 不整个设备复位,复位除了蓝牙的部分,即蓝牙正常,其他部分重新初始化
// para_store_init();
//
// }
//
// } break;
// case CMD_DBN_LOOP_RELEASE_PLANB: {
// // 获取/设置 释放去抖动算法参数
//// PRINT("Release2_CNG: %02X\n", pkg[4]);
// if(pkg[4] == 0){
// // get param
// i = 0;
// tmp_ble_buf[i++] = 0x10;
// tmp_ble_buf[i++] = g_loop_balance_planB.release_ori_planB.flag_weight;
// tmp_ble_buf[i++] = g_loop_balance_planB.release_ori_planB.max_amplitude;
// tmp_ble_buf[i++] = (g_loop_balance_planB.release_ori_planB.max_amplitude >> 8) & 0xFF;
// tmp_ble_buf[i++] = g_loop_balance_planB.release_ori_planB.timeout;
// tmp_ble_buf[i++] = g_loop_balance_planB.release_change_rate.flag_weight;
// tmp_ble_buf[i++] = g_loop_balance_planB.release_change_rate.rate_first;
// tmp_ble_buf[i++] = g_loop_balance_planB.release_change_rate.rate_second;
// tmp_ble_buf[i++] = g_loop_balance_planB.release_change_rate.mode;
//
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
// }
// else{
// // set param
// i = 0; k = 0;
// tmp_ble_buf[i++] = 0x11;
// memcpy(&tmp_ble_buf[1], &pkg[4], 8);
// i += 8;
//
// write_cfg_to_store(Addr_Loop_PlanB_Cng_Offset + sizeof(Loop_Sample_Cng) + sizeof(Loop_Balance_Ori_Cng), &tmp_ble_buf[1], 8);
//
// set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
//
// //TODO: 不整个设备复位,复位除了蓝牙的部分,即蓝牙正常,其他部分重新初始化
// para_store_init();
// }
// } break;
case CMD_DBN_LOOP_SENS_LIST: {
// PRINT("SENS_CNG: %02X\n", pkg[4]);
memset(tmp_ble_buf, 0, MAX_BLE_TMP_BUF_LEN);
if(pkg[4] == 0x00){
// get param
i = 0;
tmp_ble_buf[i++] = 0x10;
tmp_ble_buf[i++] = g_loop_sens_list.total;
for(k = 0; k < g_loop_sens_list.total; k++){
tmp_ble_buf[i++] = g_loop_sens_list.sens[k].sens_in;
tmp_ble_buf[i++] = g_loop_sens_list.sens[k].sens_in >> 8;
tmp_ble_buf[i++] = g_loop_sens_list.sens[k].sens_out;
tmp_ble_buf[i++] = g_loop_sens_list.sens[k].sens_out >> 8;
}
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
}
else {
// set param
i = 0; k = 5;
tmp_ble_buf[i++] = 0x11;
g_loop_sens_list.total = pkg[k];
if(g_loop_sens_list.total > MAX_LOOP_SENS_AMOUNT){
g_loop_sens_list.total = MAX_LOOP_SENS_AMOUNT;
}
k = 1 + g_loop_sens_list.total * 4;
memcpy(&tmp_ble_buf[i], &pkg[5], k);
i += k;
write_cfg_to_store(Addr_Loop_Sens_List_Offset, &tmp_ble_buf[1], (i - 1));
DelayMs(10);
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
write_cfg_to_store(Addr_Loop_Sens_List_Offset, &tmp_ble_buf[1], (i - 1));
DelayMs(10);
//TODO: 不整个设备复位,复位除了蓝牙的部分,即蓝牙正常,其他部分重新初始化
para_store_init();
}
} break;
case CMD_DBN_SET_CJQ_FACTORY: {
// 出厂初始化
tmp_ble_buf[0] = 0;
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, 1);
set_factory_param();
factory_dev();
DelayMs(10);
//TODO: 不整个设备复位,复位除了蓝牙的部分,即蓝牙正常,其他部分重新初始化
para_store_init();
} break;
case CMD_SENS_ACS_ENABLE: {
// 线圈动态
g_dbn_ble_state_acs_enable.enable = pkg[5];
if(g_dbn_ble_state_acs_enable.enable){
g_loop_acs_info.condition = 0;
g_dbn_ble_state_acs_enable.interval = pkg[6] * 0x7F;
g_dbn_ble_state_acs_enable.timeout_min = pkg[7];
if(g_dbn_ble_state_acs_enable.timeout_min){
g_dbn_ble_state_acs_enable.counter = mstick();
g_dbn_ble_state_acs_enable.timeout_counter = g_dbn_ble_state_acs_enable.timeout_min * 60 * 100; // TODO: min --> ms
}
g_dbn_ble_state_acs_enable.flag = 1;
}
else{
g_dbn_ble_state_acs_enable.flag = 0;
}
} break;
default:
{
} break;
}
}
void poll_dbn_ble(void)
{
uint8_t i = 0;
uint8_t magic = 0;
if(g_ble_rcv_buf.flag)
{
// if(g_flag_debug)
// {
// PRINT("profile ChangeCB CHAR1?..:%s, len:%d \n", g_ble_rcv_buf.buf, g_ble_rcv_buf.len);
// for(i = 0; i < g_ble_rcv_buf.len; i++)
// {
// PRINT(" %02X", g_ble_rcv_buf.buf[i]);
// }
// PRINT("\n");
// }
magic = check_pkg(g_ble_rcv_buf.buf, g_ble_rcv_buf.len);
// printf("_magic: %02X\n", magic);
if(magic != 0)
{
// if(magic == MAGIC_BYTE_DBN_DEFAULT)
{
manage_dbn_ble_default(g_ble_rcv_buf.buf, g_ble_rcv_buf.len);
}
}
// memcpy(g_notify_buftemp.buf, g_ble_rcv_buf.buf, g_ble_rcv_buf.len);
// g_notify_buftemp.len = g_ble_rcv_buf.len;
// g_flag_notify_temp = 1;
clear_ble_notify_buf(&g_ble_rcv_buf);
}
if(g_notify_buftemp.flag == 0)
{
report_sens_acs();
g_flag_notify_temp = set_response_to_notify(&g_buf_ble_response, &g_notify_buftemp);
}
}
@@ -0,0 +1,47 @@
/*
* cmcng.h
*
* Created on: Aug 27, 2024
* Author: Thinkpad
* 2026-07-18: ç®—æ³•ç§»æ¤ DLD154V4B, 移除 FltHistoryManager / SecondOrderState /
* StageRangeConfig 等旧算法结构
*/
#ifndef INCLUDE_CMCNG_H_
#define INCLUDE_CMCNG_H_
#include <stdint.h>
#define PRODUCT_MODEL "DLD110S"
#define FIRMWARE_VER "4.30"
#define HARDWARE_VER "4.01"
#define FIRMWARE_VER_MAIN 4
#define FIRMWARE_VER_SUB 30
#define HARDWARE_VER_MAIN 4
#define HARDWARE_VER_SUB 1
/* 功能模å¼ä½? (direction_mode é«?4ä½?, åè®®ä¿ç•™; 新算法ä¸å†æ¶ˆè´?) */
#define FUNCTION_A 0x01 // 二次判断 (已废�)
#define FUNCTION_B 0x02 // 二次判断增强æ¡ä»¶åˆ¤æ–­ (已废å¼?)
#define FUNCTION_C 0x04 //
#define FUNCTION_D 0x08 //
extern uint16_t g_hold_time;
extern char g_flag_debug;
extern uint8_t g_dev_number[6];
extern uint8_t MACAddr[6];
extern uint8_t g_loop_cut_last;
extern uint32_t g_loop_cut_amount;
extern uint8_t g_loop_power_up_state;
extern uint8_t g_function_mode;
/* 新算法状æ€? (DLD154V4B ç§»æ¤) */
extern uint8_t g_loop_stable; // ç¨³å®šæœŸç»“æŸæ ‡å¿? (0=æ”¶æ•›ä¸?, 1=稳定)
extern uint8_t g_env_activity; // 环境活跃åº? (BLE condition å­—æ®µæ¥æº)
uint32_t mstick(void);
#endif /* INCLUDE_CMCNG_H_ */
@@ -0,0 +1,108 @@
/*
* dbn_ble_srv.h
*
* Created on: Aug 27, 2024
* Author: Thinkpad
*/
#ifndef INCLUDE_DBN_BLE_SRV_H_
#define INCLUDE_DBN_BLE_SRV_H_
#include "CONFIG.h"
#include <stdint.h>
/*
* Magic | Header | Data | CheckByte
* | Addr/Sub Len CMD | | Xor Sum
* 1Byte | 1B 1B 1B | xx | 1B 1B
*
* * Len = len(CMD) + len(Data)
*/
#define MAGIC_BYTE_DBN_DEFAULT 0x8F
#define MAGIC_BYTE_DBN_TRANSPARENT 0x7F // 用于 显示屏协议、地感协议、探头协议等
#define CMD_DBN_GET_DEV_INFO 0x10
#define CMD_DBN_CHECK_PASS 0x1C
#define CMD_DBN_MODIFY_PASS 0x1D
#define CMD_DBN_SET_FACTORY 0x1E
#define CMD_DBN_RESET_DEV 0x1F
#define CMD_DBN_SET_NOTIFY 0x20
#define CMD_DBN_SET_NOTIFY_LOOP 0x21
#define CMD_DBN_SET_SUB_CODE 0x22 //使能 子功能
#define CMD_DBN_SET_CJQ_PARAM 0x23 // 设置 车检器参数
#define CMD_DBN_GET_CJQ_PARAM 0x24 // 读取 车检器参数
#define CMD_DBN_DEV_RESET 0x6D
#define CMD_DBN_LOOP_SAMPLE_PARAM 0x87
#define CMD_DBN_LOOP_BALANCE_PARAM 0x88
#define CMD_DBN_LOOP_RELEASE_PLANB 0x89
#define CMD_DBN_LOOP_SENS_LIST 0x8A
#define CMD_DBN_SET_CJQ_FACTORY 0x92
#define CMD_SUB_SENS_REPORT 0xC0 //设备主动上报传感信息
// #define CMD_REQUIRE_DEV_MAC_ADDR 0xC1
// #define CMD_CHANGE_DEV_ADDR_BY_MAC 0xC2
// #define CMD_SET_LED 0xC3
// #define CMD_SET_RELAY 0xC4
#define CMD_SENS_ACS_ENABLE 0xC5 // 设备主动上报 使能
#define MAX_BLE_DAT_RESPONSE_LEN (BLE_BUFF_MAX_LEN - 4) // 14 //返回 的所有字节数不能超过20个字节,除去 magic、header和ckb校验字节,剩下数据的字节数为14
#define MAX_BLE_Notify_Buf_LEN BLE_BUFF_MAX_LEN //24
#define MAX_BLE_BUF_LEN BLE_BUFF_MAX_LEN //128
#define MAX_BLE_TMP_BUF_LEN BLE_BUFF_MAX_LEN
typedef struct _BLE_Notify_Buf_
{
uint8_t flag; //0 idle, 1 buf ready
uint8_t len;
uint8_t buf[MAX_BLE_Notify_Buf_LEN];
}BLE_Notify_Buf;
typedef struct _BUF_DBN_BLE_
{
uint8_t flag;
uint8_t magic;
uint8_t cmd;
uint8_t dat_len ;
uint8_t dat_offset;
uint8_t pkg_amount;
uint8_t pkg_seq;
uint8_t dat[MAX_BLE_BUF_LEN];
} Buf_DBN_BLE;
extern Buf_DBN_BLE g_buf_ble_response;
extern BLE_Notify_Buf g_notify_buftemp;
extern uint8_t g_flag_notify_temp;
extern BLE_Notify_Buf g_ble_rcv_buf;
extern uint16_t loop1_LPCNT;
extern uint8_t g_freq_sens;
extern uint8_t loop1_SensLevel;
extern uint8_t g_freq_level;
extern uint32_t g_sys_freq;
extern float g_freq_tmp;
void clear_ble_notify_buf(BLE_Notify_Buf * buf);
void poll_dbn_ble(void);
void report_sens_acs(void);
void set_factory_param(void);
void factory_dev(void);
void para_store_init(void);
#endif /* INCLUDE_DBN_BLE_SRV_H_ */
@@ -0,0 +1,67 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : peripheral.h
* Author : WCH
* Version : V1.0
* Date : 2018/12/11
* Description :
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef PERIPHERAL_H
#define PERIPHERAL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************************************************************
* INCLUDES
*/
#include "dbn_ble_srv.h"
/*********************************************************************
* CONSTANTS
*/
// Peripheral Task Events
#define SBP_START_DEVICE_EVT 0x0001
#define SBP_PERIODIC_EVT 0x0002
#define SBP_READ_RSSI_EVT 0x0004
#define SBP_PARAM_UPDATE_EVT 0x0008
/*********************************************************************
* MACROS
*/
typedef struct
{
uint16_t connHandle; // Connection handle of current connection
uint16_t connInterval;
uint16_t connSlaveLatency;
uint16_t connTimeout;
} peripheralConnItem_t;
/*********************************************************************
* FUNCTIONS
*/
/*
* Task Initialization for the BLE Application
*/
extern void Peripheral_Init(void);
/*
* Task Event Processor for the BLE Application
*/
extern uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events);
/*********************************************************************
*********************************************************************/
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,187 @@
/*
* storage.h
*
* Created on: Aug 27, 2024
* Author: Thinkpad
*/
#ifndef INCLUDE_STORAGE_H_
#define INCLUDE_STORAGE_H_
#include <stdint.h>
// #define Addr_Loop_PlanB_Cng_Offset 0x10
// #define Addr_Flag_Sample_In 0x10
// #define Addr_Sample_Max_Amplitude 0x11
// #define Addr_Flag_Balance_Ori_In 0x13
// #define Addr_Balance_Ori_In_Max_Cnt 0x14
// #define Addr_Flag_Release_Ori_PlanB 0x16
// #define Addr_Release_Ori_PlanB_TimeOut 0x17
// #define Addr_Release_Ori_PlanB_Weight 0x18
// #define Addr_Release_Ori_Amplitude 0x19
// #define Addr_Flag_Release_Rate 0x1B
// #define Addr_Release_Rate_First 0x1C
// #define Addr_Release_Rate_Second 0x1D
// #define Addr_Release_Rate_Weight 0x1E
// #define Addr_Release_Rate_Mode 0x1F
#define Addr_Loop_Sens_List_Offset 0x20
#define Addr_Sens_Array_In 0x20
#define Addr_Sens_Array_Out 0x30
#define MAX_LOOP_SENS_AMOUNT 3 // 4 // 5
#pragma pack(1)
typedef struct _Loop_Sample_CNG_
{
uint8_t flag;
uint16_t max_amplitude; // 最大幅值
}Loop_Sample_Cng;
typedef struct _Loop_Balance_Ori_CNG_
{
uint8_t flag;
uint16_t max_cnt ; // 最大补偿次数
}Loop_Balance_Ori_Cng; // 漂移补偿
typedef struct _Loop_Release_Ori_PlanB_
{
uint8_t flag_weight;
uint16_t max_amplitude; //最大幅值
uint8_t timeout; // 单位:分钟
}Loop_Release_Ori_PlanB;
typedef struct _Loop_Release_Change_Rate_
{
uint8_t flag_weight;
uint8_t rate_first;
uint8_t rate_second;
uint8_t mode;
}Loop_Release_Change_Rate;
typedef struct _Loop_Balance_PlanB_
{
Loop_Sample_Cng sample_cng;
Loop_Balance_Ori_Cng balance_ori_cng;
Loop_Release_Ori_PlanB release_ori_planB;
Loop_Release_Change_Rate release_change_rate;
}Loop_Balance_PlanB;
extern Loop_Balance_PlanB g_loop_balance_planB;
typedef struct
{
uint8_t sensitvity; // 高四位表示灵敏度的数量,低四位表示当前灵敏度的序号(从0开始)
uint8_t delay_time;
uint8_t output_mode;
uint8_t direction_mode; // 方向判别模式,0 触发模式, 非0 表示方向判别模式
uint8_t loopFreq_Level; // 高低频
uint8_t loopSafe_Timeout; // 线圈安全模式, 0表示关闭,非0表示开启,以10秒为单位
uint8_t exist_mode; // 0 永久存在,非0,表示有限存在的时间,单位为10秒
}Loop_Cng_Unit;
extern Loop_Cng_Unit g_loop_cng_unit;
typedef struct
{
uint16_t sens_in;
uint16_t sens_out;
}Loop_Single_Sens;
typedef struct
{
uint8_t total;
Loop_Single_Sens sens[MAX_LOOP_SENS_AMOUNT];
}Loop_Sens_List;
extern Loop_Sens_List g_loop_sens_list;
typedef struct _DBN_BLE_STATE_
{
uint8_t flag; // if need to report
uint8_t enable; // 是否使能
uint8_t send_flag;
uint8_t obj_amount;
uint8_t cmd;
uint8_t sens_type;
uint8_t dat_len ;
uint8_t dat_offset;
uint8_t pkg_amount;
uint8_t pkg_seq;
uint16_t interval;
uint32_t counter;
uint8_t timeout_min; // minute
uint32_t timeout_counter;
} DBN_BLE_State;
extern DBN_BLE_State g_dbn_ble_state_acs_enable;
typedef enum
{
SENS_NONE = 0,
SENS_SUB_INFO = 1, // 传感器设备信息
SENS_OBJ_DYNAMIC = 2, // 动态目标类型
SENS_SOUTPUT_STATUS = 3, // 当前开关量输出的状态
SENS_DETAIL_CNG = 4, // 详细配置
SENS_LOOP_DYNAMIC = 5, // 线圈动态信息
SENS_OBJ_BackGround = 6, //
SENS_OBJ_INSIDE = 7, //
SENS_LOOP_TREND,
SENS_OBJ_DYNAMIC_SIMPLE,
SENS_OBJ_STATIC_XuJing, //10, 0x0A
SENS_DOBULE_LOOP_ESTIMATE //0x0B, 线圈环境评估
} SUB_SENS_TYPE; // 传感数据类型
typedef struct _LOOP_ACS_INFO
{
uint8_t flag_event;
uint8_t loop_num;
uint8_t freq_level;
uint8_t sensity;
uint8_t relay1_cng;
uint8_t relay1_state;
uint8_t relay2_cng;
uint8_t relay2_state;
uint8_t loop_state;
uint8_t output_mode;
uint8_t dir_mode;
uint8_t delay;
uint8_t car_state;
uint8_t direction;
uint8_t flag_loop_safe;
uint32_t loop_safe_counter;
uint8_t condition;
uint8_t flag_change;
uint32_t frequent;
uint32_t loop_capvd;
int32_t variation;
uint32_t event_counter;
uint32_t report_counter;
} Loop_ACS_Info;
extern Loop_ACS_Info g_loop_acs_info;
#pragma pack()
#define HOLD_TIME 5*1200
#define LC_HOLD_TIME 4*1200 //时间4分钟
#define IN_DELAY 10
#define OUT_DELAY 39
#define PLUSE_DELAY 10
void write_cfg_to_store(uint32_t wAddr, uint8_t * p, uint32_t len);
void read_cfg_from_store(uint32_t addr, uint8_t *p, uint32_t len);
#endif /* INCLUDE_STORAGE_H_ */
@@ -0,0 +1,784 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : peripheral.C
* Author : WCH
* Version : V1.0
* Date : 2018/12/10
* Description : 外设从机多连接应用程序,初始化广播连接参数,然后广播,连接主机后,
* 请求更新连接参数,通过自定义服务传输数据
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include "CONFIG.h"
#include "devinfoservice.h"
#include "gattprofile.h"
#include "peripheral.h"
/*********************************************************************
* MACROS
*/
extern uint8_t g_flag_bt_state;
/*********************************************************************
* CONSTANTS
*/
// How often to perform periodic event
#define SBP_PERIODIC_EVT_PERIOD 200 //800 //1600
// How often to perform read rssi event
#define SBP_READ_RSSI_EVT_PERIOD 3200
// Parameter update delay
#define SBP_PARAM_UPDATE_DELAY 6400
// What is the advertising interval when device is discoverable (units of 625us, 80=50ms)
#define DEFAULT_ADVERTISING_INTERVAL 80
// Limited discoverable mode advertises for 30.72s, and then stops
// General discoverable mode advertises indefinitely
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL
// Minimum connection interval (units of 1.25ms, 6=7.5ms)
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 6
// Maximum connection interval (units of 1.25ms, 100=125ms)
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 100
// Slave latency to use parameter update
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
// Supervision timeout value (units of 10ms, 100=1s)
#define DEFAULT_DESIRED_CONN_TIMEOUT 100
// Company Identifier: WCH
#define WCH_COMPANY_ID 0x07D7
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
/*********************************************************************
* EXTERNAL VARIABLES
*/
/*********************************************************************
* EXTERNAL FUNCTIONS
*/
/*********************************************************************
* LOCAL VARIABLES
*/
static uint8_t Peripheral_TaskID = INVALID_TASK_ID; // Task ID for internal task/event processing
// GAP - SCAN RSP data (max size = 31 bytes)
static uint8_t scanRspData[] = {
// complete name
// 0x12, // length of this data
0x08,
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
// 'S',
// 'i',
// 'm',
// 'p',
// 'l',
// 'e',
// ' ',
// 'P',
// 'e',
// 'r',
// 'i',
// 'p',
// 'h',
// 'e',
// 'r',
// 'a',
// 'l',
'D','L','D','1','1','0','S',
// connection interval range
0x05, // length of this data
GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL), // 100ms
HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),
LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL), // 1s
HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),
// Tx power level
0x02, // length of this data
GAP_ADTYPE_POWER_LEVEL,
0 // 0dBm
};
// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertising)
static uint8_t advertData[] = {
// Flags; this sets the device to use limited discoverable
// mode (advertises for 30 seconds at a time) instead of general
// discoverable mode (advertises indefinitely)
0x02, // length of this data
GAP_ADTYPE_FLAGS,
DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
// service UUID, to notify central devices what services are included
// in this peripheral
0x03, // length of this data
GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all
LO_UINT16(SIMPLEPROFILE_SERV_UUID),
HI_UINT16(SIMPLEPROFILE_SERV_UUID)
};
// GAP GATT Attributes
static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "DLD110S";// "Simple Peripheral";
// Connection item list
static peripheralConnItem_t peripheralConnList;
static uint16_t peripheralMTU = ATT_MTU_SIZE;
/*********************************************************************
* LOCAL FUNCTIONS
*/
static void Peripheral_ProcessTMOSMsg(tmos_event_hdr_t *pMsg);
static void peripheralStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent);
static void performPeriodicTask(void);
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len);
static void peripheralParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
uint16_t connSlaveLatency, uint16_t connTimeout);
static void peripheralInitConnItem(peripheralConnItem_t *peripheralConnList);
static void peripheralRssiCB(uint16_t connHandle, int8_t rssi);
static void peripheralChar4Notify(uint8_t *pValue, uint16_t len);
/*********************************************************************
* PROFILE CALLBACKS
*/
// GAP Role Callbacks
static gapRolesCBs_t Peripheral_PeripheralCBs = {
peripheralStateNotificationCB, // Profile State Change Callbacks
peripheralRssiCB, // When a valid RSSI is read from controller (not used by application)
peripheralParamUpdateCB
};
// Broadcast Callbacks
static gapRolesBroadcasterCBs_t Broadcaster_BroadcasterCBs = {
NULL, // Not used in peripheral role
NULL // Receive scan request callback
};
// GAP Bond Manager Callbacks
static gapBondCBs_t Peripheral_BondMgrCBs = {
NULL, // Passcode callback (not used by application)
NULL // Pairing / Bonding state Callback (not used by application)
};
// Simple GATT Profile Callbacks
static simpleProfileCBs_t Peripheral_SimpleProfileCBs = {
simpleProfileChangeCB // Characteristic value change callback
};
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/*********************************************************************
* @fn Peripheral_Init
*
* @brief Initialization function for the Peripheral App Task.
* This is called during initialization and should contain
* any application specific initialization (ie. hardware
* initialization/setup, table initialization, power up
* notificaiton ... ).
*
* @param task_id - the ID assigned by TMOS. This ID should be
* used to send messages and set timers.
*
* @return none
*/
void Peripheral_Init()
{
Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent);
// Setup the GAP Peripheral Role Profile
{
uint8_t initial_advertising_enable = TRUE;
uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
// Set the GAP Role Parameters
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);
GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);
}
// Set the GAP Characteristics
GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
{
uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
// Set advertising interval
GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);
// Enable scan req notify
GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE);
}
// Setup the GAP Bond Manager
{
uint32_t passkey = 0; // passkey "000000"
uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
uint8_t mitm = TRUE;
uint8_t bonding = TRUE;
uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
}
// Initialize GATT attributes
GGS_AddService(GATT_ALL_SERVICES); // GAP
GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes
DevInfo_AddService(); // Device Information Service
SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
// Setup the SimpleProfile Characteristic Values
{
uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = {1};
uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = {2};
uint8_t charValue3[SIMPLEPROFILE_CHAR3_LEN] = {3};
uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = {4};
// uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = {1, 2, 3, 4, 5};
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4);
// SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5);
}
// Init Connection Item
peripheralInitConnItem(&peripheralConnList);
// Register callback with SimpleGATTprofile
SimpleProfile_RegisterAppCBs(&Peripheral_SimpleProfileCBs);
// Register receive scan request callback
GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs);
// Setup a delayed profile startup
tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT);
}
/*********************************************************************
* @fn peripheralInitConnItem
*
* @brief Init Connection Item
*
* @param peripheralConnList -
*
* @return NULL
*/
static void peripheralInitConnItem(peripheralConnItem_t *peripheralConnList)
{
peripheralConnList->connHandle = GAP_CONNHANDLE_INIT;
peripheralConnList->connInterval = 0;
peripheralConnList->connSlaveLatency = 0;
peripheralConnList->connTimeout = 0;
}
/*********************************************************************
* @fn Peripheral_ProcessEvent
*
* @brief Peripheral Application Task event processor. This function
* is called to process all events for the task. Events
* include timers, messages and any other user defined events.
*
* @param task_id - The TMOS assigned task ID.
* @param events - events to process. This is a bit map and can
* contain more than one event.
*
* @return events not processed
*/
uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)
{
// VOID task_id; // TMOS required parameter that isn't used in this function
if(events & SYS_EVENT_MSG)
{
uint8_t *pMsg;
if((pMsg = tmos_msg_receive(Peripheral_TaskID)) != NULL)
{
Peripheral_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
// Release the TMOS message
tmos_msg_deallocate(pMsg);
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if(events & SBP_START_DEVICE_EVT)
{
// Start the Device
GAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs);
return (events ^ SBP_START_DEVICE_EVT);
}
if(events & SBP_PERIODIC_EVT)
{
// Restart timer
if(SBP_PERIODIC_EVT_PERIOD)
{
tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);
}
// Perform periodic application task
performPeriodicTask();
return (events ^ SBP_PERIODIC_EVT);
}
if(events & SBP_PARAM_UPDATE_EVT)
{
// Send connect param update request
GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,
DEFAULT_DESIRED_MIN_CONN_INTERVAL,
DEFAULT_DESIRED_MAX_CONN_INTERVAL,
DEFAULT_DESIRED_SLAVE_LATENCY,
DEFAULT_DESIRED_CONN_TIMEOUT,
Peripheral_TaskID);
return (events ^ SBP_PARAM_UPDATE_EVT);
}
if(events & SBP_READ_RSSI_EVT)
{
GAPRole_ReadRssiCmd(peripheralConnList.connHandle);
tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);
return (events ^ SBP_READ_RSSI_EVT);
}
// Discard unknown events
return 0;
}
/*********************************************************************
* @fn Peripheral_ProcessGAPMsg
*
* @brief Process an incoming task message.
*
* @param pMsg - message to process
*
* @return none
*/
static void Peripheral_ProcessGAPMsg(gapRoleEvent_t *pEvent)
{
switch(pEvent->gap.opcode)
{
case GAP_SCAN_REQUEST_EVENT:
{
// PRINT("Receive scan req from %x %x %x %x %x %x ..\n", pEvent->scanReqEvt.scannerAddr[0],
// pEvent->scanReqEvt.scannerAddr[1], pEvent->scanReqEvt.scannerAddr[2], pEvent->scanReqEvt.scannerAddr[3],
// pEvent->scanReqEvt.scannerAddr[4], pEvent->scanReqEvt.scannerAddr[5]);
break;
}
case GAP_PHY_UPDATE_EVENT:
{
// PRINT("Phy update Rx:%x Tx:%x ..\n", pEvent->linkPhyUpdate.connRxPHYS, pEvent->linkPhyUpdate.connTxPHYS);
break;
}
default:
break;
}
}
/*********************************************************************
* @fn Peripheral_ProcessTMOSMsg
*
* @brief Process an incoming task message.
*
* @param pMsg - message to process
*
* @return none
*/
static void Peripheral_ProcessTMOSMsg(tmos_event_hdr_t *pMsg)
{
switch(pMsg->event)
{
case GAP_MSG_EVENT:
{
Peripheral_ProcessGAPMsg((gapRoleEvent_t *)pMsg);
break;
}
case GATT_MSG_EVENT:
{
gattMsgEvent_t *pMsgEvent;
pMsgEvent = (gattMsgEvent_t *)pMsg;
if(pMsgEvent->method == ATT_MTU_UPDATED_EVENT)
{
peripheralMTU = pMsgEvent->msg.exchangeMTUReq.clientRxMTU;
// PRINT("mtu exchange: %d\n", pMsgEvent->msg.exchangeMTUReq.clientRxMTU);
}
break;
}
default:
break;
}
}
/*********************************************************************
* @fn Peripheral_LinkEstablished
*
* @brief Process link established.
*
* @param pEvent - event to process
*
* @return none
*/
static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent)
{
gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent;
// See if already connected
if(peripheralConnList.connHandle != GAP_CONNHANDLE_INIT)
{
GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle);
// PRINT("Connection max...\n");
}
else
{
peripheralConnList.connHandle = event->connectionHandle;
peripheralConnList.connInterval = event->connInterval;
peripheralConnList.connSlaveLatency = event->connLatency;
peripheralConnList.connTimeout = event->connTimeout;
peripheralMTU = ATT_MTU_SIZE;
// Set timer for periodic event
tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);
// Set timer for param update event
tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY);
// Start read rssi
tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);
// PRINT("Conn %x - Int %x \n", event->connectionHandle, event->connInterval);
}
}
/*********************************************************************
* @fn Peripheral_LinkTerminated
*
* @brief Process link terminated.
*
* @param pEvent - event to process
*
* @return none
*/
static void Peripheral_LinkTerminated(gapRoleEvent_t *pEvent)
{
gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *)pEvent;
if(event->connectionHandle == peripheralConnList.connHandle)
{
peripheralConnList.connHandle = GAP_CONNHANDLE_INIT;
peripheralConnList.connInterval = 0;
peripheralConnList.connSlaveLatency = 0;
peripheralConnList.connTimeout = 0;
tmos_stop_task(Peripheral_TaskID, SBP_PERIODIC_EVT);
tmos_stop_task(Peripheral_TaskID, SBP_READ_RSSI_EVT);
// Restart advertising
{
uint8_t advertising_enable = TRUE;
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertising_enable);
}
}
else
{
// PRINT("ERR..\n");
}
}
/*********************************************************************
* @fn peripheralRssiCB
*
* @brief RSSI callback.
*
* @param connHandle - connection handle
* @param rssi - RSSI
*
* @return none
*/
static void peripheralRssiCB(uint16_t connHandle, int8_t rssi)
{
// PRINT("RSSI -%d dB Conn %x \n", -rssi, connHandle);
}
/*********************************************************************
* @fn peripheralParamUpdateCB
*
* @brief Parameter update complete callback
*
* @param connHandle - connect handle
* connInterval - connect interval
* connSlaveLatency - connect slave latency
* connTimeout - connect timeout
*
* @return none
*/
static void peripheralParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
uint16_t connSlaveLatency, uint16_t connTimeout)
{
if(connHandle == peripheralConnList.connHandle)
{
peripheralConnList.connInterval = connInterval;
peripheralConnList.connSlaveLatency = connSlaveLatency;
peripheralConnList.connTimeout = connTimeout;
// PRINT("Update %x - Int %x \n", connHandle, connInterval);
}
else
{
// PRINT("ERR..\n");
}
}
/*********************************************************************
* @fn peripheralStateNotificationCB
*
* @brief Notification from the profile of a state change.
*
* @param newState - new state
*
* @return none
*/
static void peripheralStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent)
{
switch(newState & GAPROLE_STATE_ADV_MASK)
{
case GAPROLE_STARTED:
// PRINT("Initialized..\n");
break;
case GAPROLE_ADVERTISING:
if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
{
Peripheral_LinkTerminated(pEvent);
g_flag_bt_state = 0;
// PRINT("Disconnected.. Reason:%x\n", pEvent->linkTerminate.reason);
}
// PRINT("Advertising..\n");
break;
case GAPROLE_CONNECTED:
if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
{
Peripheral_LinkEstablished(pEvent);
}
g_flag_bt_state = 1;
// PRINT("Connected..\n");
break;
case GAPROLE_CONNECTED_ADV:
// PRINT("Connected Advertising..\n");
break;
case GAPROLE_WAITING:
if(pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT)
{
// PRINT("Waiting for advertising..\n");
}
else if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
{
Peripheral_LinkTerminated(pEvent);
g_flag_bt_state = 0;
// PRINT("Disconnected.. Reason:%x\n", pEvent->linkTerminate.reason);
}
else if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
{
if(pEvent->gap.hdr.status != SUCCESS)
{
// PRINT("Waiting for advertising..\n");
}
else
{
// PRINT("Error..\n");
}
}
else
{
// PRINT("Error..%x\n", pEvent->gap.opcode);
}
break;
case GAPROLE_ERROR:
// PRINT("Error..\n");
g_flag_bt_state = 0;
break;
default:
break;
}
}
/*********************************************************************
* @fn performPeriodicTask
*
* @brief Perform a periodic application task. This function gets
* called every five seconds as a result of the SBP_PERIODIC_EVT
* TMOS event. In this example, the value of the third
* characteristic in the SimpleGATTProfile service is retrieved
* from the profile, and then copied into the value of the
* the fourth characteristic.
*
* @param none
*
* @return none
*/
static void performPeriodicTask(void)
{
// uint8_t notiData[SIMPLEPROFILE_CHAR4_LEN] = {0x88};
// peripheralChar4Notify(notiData, SIMPLEPROFILE_CHAR4_LEN);
if(g_flag_notify_temp){
g_flag_notify_temp = 0;
peripheralChar4Notify(g_notify_buftemp.buf, g_notify_buftemp.len);
clear_ble_notify_buf(&g_notify_buftemp);
}
}
/*********************************************************************
* @fn peripheralChar4Notify
*
* @brief Prepare and send simpleProfileChar4 notification
*
* @param pValue - data to notify
* len - length of data
*
* @return none
*/
static void peripheralChar4Notify(uint8_t *pValue, uint16_t len)
{
attHandleValueNoti_t noti;
if(len > (peripheralMTU - 3))
{
// PRINT("Too large noti\n");
return;
}
noti.len = len;
noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0);
if(noti.pValue)
{
tmos_memcpy(noti.pValue, pValue, noti.len);
if(simpleProfile_Notify(peripheralConnList.connHandle, &noti) != SUCCESS)
{
GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);
}
}
}
void Jump_OTA(void);
/*********************************************************************
* @fn simpleProfileChangeCB
*
* @brief Callback from SimpleBLEProfile indicating a value change
*
* @param paramID - parameter ID of the value that was changed.
* pValue - pointer to data that was changed
* len - length of data
*
* @return none
*/
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len)
{
switch(paramID)
{
case SIMPLEPROFILE_CHAR1:
{
// uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN];
// tmos_memcpy(newValue, pValue, len);
// PRINT("profile ChangeCB CHAR1.. \n");
g_ble_rcv_buf.len = len;
tmos_memcpy(g_ble_rcv_buf.buf, pValue, len);
g_ble_rcv_buf.flag = 1;
break;
}
case SIMPLEPROFILE_CHAR3:
{
// uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN];
// tmos_memcpy(newValue, pValue, len);
g_ble_rcv_buf.len = len;
tmos_memcpy(g_ble_rcv_buf.buf, pValue, len);
// PRINT("profile ChangeCB CHAR3..len:%d,dat:%s.\n", len, g_ble_rcv_buf.buf);
if(g_ble_rcv_buf.buf[0] == 0x3F && g_ble_rcv_buf.buf[1] == 0xF3 && g_ble_rcv_buf.buf[2] == 0x23 &&
g_ble_rcv_buf.buf[3] == 'O' && g_ble_rcv_buf.buf[4] == 'T' && g_ble_rcv_buf.buf[5] == 'A'){
// 0x4F, 0x54, 0x41
// PRINT("jump OTA \n");
mDelaymS(5);
Jump_OTA();
}
break;
}
default:
// should not reach here!
break;
}
}
/* OTA 升级标志 */
#define IMAGE_OTA_FLAG 0x03
/* 存放在DataFlash地址,不能占用蓝牙的位置 */
#define OTA_DATAFLASH_ADD 0x00077000 - FLASH_ROM_MAX_SIZE
/* flash的数据临时存储 */
__attribute__((aligned(8))) uint8_t block_buf[16];
/*********************************************************************
* @fn Jump_OTA
*
* @brief 跳转OTA升级
*
* @return none
*/
void Jump_OTA(void)
{
uint16_t i;
uint32_t ver_flag;
/* 读取第一块 */
EEPROM_READ(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);
/* 擦除第一块 */
EEPROM_ERASE(OTA_DATAFLASH_ADD, EEPROM_PAGE_SIZE);
/* 更新Image信息 */
block_buf[0] = IMAGE_OTA_FLAG;
/* 编程DataFlash */
EEPROM_WRITE(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);
/* 软复位 */
SYS_ResetExecute();
}
/*********************************************************************
*********************************************************************/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
/*
* storage.c
*
* Created on: Aug 27, 2024
* Author: Thinkpad
*/
#include "storage.h"
#include "CH59x_common.h"
Loop_Balance_PlanB g_loop_balance_planB;
Loop_Cng_Unit g_loop_cng_unit;
Loop_Sens_List g_loop_sens_list;
void write_cfg_to_store(uint32_t wAddr, uint8_t * p, uint32_t len)
{
EEPROM_WRITE(wAddr, p, len);
}
void read_cfg_from_store(uint32_t addr, uint8_t *p, uint32_t len)
{
EEPROM_READ(addr, p, len);
}