• Tlsr8258开发-修改蓝牙hid mouse


    1. 修改app_att.c文件 

    1. 首先在官网下载B85M_SINGLE_BLE_SDK
    2. 在b85m_ble_sample的基础上进行修改
    3. 修改app_att.c中的报告描述符
    4. 将键盘和音频类设备的UUID注释掉,换成鼠标UUID。
    5. 注释掉键盘和音频类设备的报告特征数组,添加鼠标报告特征数组。
    1. /********************************************************************************************************
    2. * @file app_att.c
    3. *
    4. * @brief This is the source file for BLE SDK
    5. *
    6. * @author BLE GROUP
    7. * @date 06,2020
    8. *
    9. * @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
    10. * All rights reserved.
    11. *
    12. * Redistribution and use in source and binary forms, with or without
    13. * modification, are permitted provided that the following conditions are met:
    14. *
    15. * 1. Redistributions of source code must retain the above copyright
    16. * notice, this list of conditions and the following disclaimer.
    17. *
    18. * 2. Unless for usage inside a TELINK integrated circuit, redistributions
    19. * in binary form must reproduce the above copyright notice, this list of
    20. * conditions and the following disclaimer in the documentation and/or other
    21. * materials provided with the distribution.
    22. *
    23. * 3. Neither the name of TELINK, nor the names of its contributors may be
    24. * used to endorse or promote products derived from this software without
    25. * specific prior written permission.
    26. *
    27. * 4. This software, with or without modification, must only be used with a
    28. * TELINK integrated circuit. All other usages are subject to written permission
    29. * from TELINK and different commercial license may apply.
    30. *
    31. * 5. Licensee shall be solely responsible for any claim to the extent arising out of or
    32. * relating to such deletion(s), modification(s) or alteration(s).
    33. *
    34. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    35. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    36. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    37. * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
    38. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    39. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    41. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    43. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    44. *
    45. *******************************************************************************************************/
    46. #include "tl_common.h"
    47. #include "stack/ble/ble.h"
    48. #include "app.h"
    49. #include "app_att.h"
    50. typedef struct
    51. {
    52. /** Minimum value for the connection event (interval. 0x0006 - 0x0C80 * 1.25 ms) */
    53. u16 intervalMin;
    54. /** Maximum value for the connection event (interval. 0x0006 - 0x0C80 * 1.25 ms) */
    55. u16 intervalMax;
    56. /** Number of LL latency connection events (0x0000 - 0x03e8) */
    57. u16 latency;
    58. /** Connection Timeout (0x000A - 0x0C80 * 10 ms) */
    59. u16 timeout;
    60. } gap_periConnectParams_t;
    61. #define CHAR_2_LEN 5//5
    62. #define CHAR_16_LEN 19//19
    63. static const u16 clientCharacterCfgUUID = GATT_UUID_CLIENT_CHAR_CFG;
    64. static const u16 extReportRefUUID = GATT_UUID_EXT_REPORT_REF;
    65. static const u16 reportRefUUID = GATT_UUID_REPORT_REF;
    66. static const u16 characterPresentFormatUUID = GATT_UUID_CHAR_PRESENT_FORMAT;
    67. static const u16 userdesc_UUID = GATT_UUID_CHAR_USER_DESC;
    68. static const u16 serviceChangeUUID = GATT_UUID_SERVICE_CHANGE;
    69. static const u16 my_primaryServiceUUID = GATT_UUID_PRIMARY_SERVICE;
    70. static const u16 my_characterUUID = GATT_UUID_CHARACTER;
    71. static const u16 my_devServiceUUID = SERVICE_UUID_DEVICE_INFORMATION;
    72. static const u16 my_PnPUUID = CHARACTERISTIC_UUID_PNP_ID;
    73. static const u16 my_devNameUUID = GATT_UUID_DEVICE_NAME;
    74. static const u16 my_gapServiceUUID = SERVICE_UUID_GENERIC_ACCESS;
    75. static const u16 my_appearanceUIID = GATT_UUID_APPEARANCE;
    76. static const u16 my_periConnParamUUID = GATT_UUID_PERI_CONN_PARAM;
    77. static const u16 my_appearance = GAP_APPEARE_UNKNOWN;
    78. static const u16 my_gattServiceUUID = SERVICE_UUID_GENERIC_ATTRIBUTE;
    79. static const gap_periConnectParams_t my_periConnParameters = {20, 40, 0, 1000};
    80. static u16 serviceChangeVal[2] = {0};
    81. static u8 serviceChangeCCC[2] = {0,0};
    82. static const u8 my_devName[] = {'t','S','a','m','p','l','e'};
    83. static const u8 my_PnPtrs [] = {0x02, 0x8a, 0x24, 0x66, 0x82, 0x01, 0x00};
    84. Battery /
    85. static const u16 my_batServiceUUID = SERVICE_UUID_BATTERY;
    86. static const u16 my_batCharUUID = CHARACTERISTIC_UUID_BATTERY_LEVEL;
    87. static u8 batteryValueInCCC[2];
    88. static u8 my_batVal[1] = {99};
    89. HID /
    90. static const u16 my_hidServiceUUID = SERVICE_UUID_HUMAN_INTERFACE_DEVICE;
    91. static const u16 hidServiceUUID = SERVICE_UUID_HUMAN_INTERFACE_DEVICE;
    92. static const u16 hidProtocolModeUUID = CHARACTERISTIC_UUID_HID_PROTOCOL_MODE;
    93. static const u16 hidReportUUID = CHARACTERISTIC_UUID_HID_REPORT;
    94. static const u16 hidReportMapUUID = CHARACTERISTIC_UUID_HID_REPORT_MAP;
    95. static const u16 hidbootKeyInReportUUID = CHARACTERISTIC_UUID_HID_BOOT_KEY_INPUT;
    96. static const u16 hidbootKeyOutReportUUID = CHARACTERISTIC_UUID_HID_BOOT_KEY_OUTPUT;
    97. //static const u16 hidbootMouseInReportUUID = CHARACTERISTIC_UUID_HID_BOOT_MOUSE_INPUT;
    98. static const u16 hidinformationUUID = CHARACTERISTIC_UUID_HID_INFORMATION;
    99. static const u16 hidCtrlPointUUID = CHARACTERISTIC_UUID_HID_CONTROL_POINT;
    100. static const u16 hidIncludeUUID = GATT_UUID_INCLUDE;
    101. //移植鼠标添加区域
    102. #if 1
    103. static u8 reportMouseIn[6];
    104. static u8 reportMouseInCCC[2] = {0, 0};
    105. static u8 reportRefMouseIn[2] = {3, HID_REPORT_TYPE_INPUT };
    106. const u8 reportMouseInChar[5] =
    107. {
    108. CHAR_PROP_READ | CHAR_PROP_NOTIFY | CHAR_PROP_WRITE,
    109. HID_MOUSE_REPORT_INPUT_DP_H, 0x00,
    110. 0x4D, 0x2a,
    111. };
    112. const u16 hidbootMouseInReportUUID = CHARACTERISTIC_UUID_HID_BOOT_MOUSE_INPUT;
    113. static u8 bootMouseInReport;
    114. static u8 bootMouseInReportCCC[2] = {0, 0};
    115. const u8 bootMouseInChar[5] =
    116. {
    117. CHAR_PROP_READ | CHAR_PROP_NOTIFY | CHAR_PROP_WRITE,
    118. HID_BOOT_MOUSE_REPORT_INPUT_DP_H, 0x00,
    119. 0x33, 0x2a,
    120. };
    121. static u8 reportUseIn[4];
    122. static u8 reportUseInCCC[2] = {0, 0};
    123. static u8 reportRefUseIn[2] = {REPORT_ID_USER_FEATURE, HID_REPORT_TYPE_FEATURE};
    124. const u8 reportUseInChar[5] =
    125. {
    126. CHAR_PROP_READ | CHAR_PROP_NOTIFY | CHAR_PROP_WRITE,
    127. HID_USER_REPORT_INPUT_DP_H, 0x00,
    128. 0x4D, 0x2a,
    129. };
    130. int cccWrite(void *p)
    131. {
    132. #if 0
    133. conn_step = SMP_DONE_AAA;
    134. connect_begin_tick = clock_time() | 1;
    135. //for debug test
    136. gpio_write(PIN_24G_LED, 1);
    137. #endif
    138. return 0;
    139. }
    140. #endif
    141. static u8 protocolMode = DFLT_HID_PROTOCOL_MODE;
    142. // Key in Report characteristic variables
    143. static u8 reportKeyIn[8];
    144. static u8 reportKeyInCCC[2];
    145. // HID Report Reference characteristic descriptor, key input
    146. static u8 reportRefKeyIn[2] =
    147. { HID_REPORT_ID_KEYBOARD_INPUT, HID_REPORT_TYPE_INPUT };
    148. // Key out Report characteristic variables
    149. static u8 reportKeyOut[1];
    150. static u8 reportRefKeyOut[2] =
    151. { HID_REPORT_ID_KEYBOARD_INPUT, HID_REPORT_TYPE_OUTPUT };
    152. // Consumer Control input Report
    153. static u8 reportConsumerControlIn[2];
    154. static u8 reportConsumerControlInCCC[2];
    155. static u8 reportRefConsumerControlIn[2] =
    156. { HID_REPORT_ID_CONSUME_CONTROL_INPUT, HID_REPORT_TYPE_INPUT };
    157. // Boot Keyboard Input Report
    158. static u8 bootKeyInReport;
    159. static u8 bootKeyInReportCCC[2];
    160. // Boot Keyboard Output Report
    161. static u8 bootKeyOutReport;
    162. // HID Information characteristic
    163. static const u8 hidInformation[] =
    164. {
    165. U16_LO(0x0111), U16_HI(0x0111), // bcdHID (USB HID version)
    166. 0x00, // bCountryCode
    167. 0x01 // Flags
    168. };
    169. // HID Control Point characteristic
    170. static u8 controlPoint;
    171. // HID Report Map characteristic
    172. // Keyboard report descriptor (using format for Boot interface descriptor)
    173. static const u8 reportMap[] =
    174. {
    175. #if 0
    176. //keyboard report in
    177. 0x05, 0x01, // Usage Pg (Generic Desktop)
    178. 0x09, 0x06, // Usage (Keyboard)
    179. 0xA1, 0x01, // Collection: (Application)
    180. 0x85, HID_REPORT_ID_KEYBOARD_INPUT, // Report Id (keyboard)
    181. //
    182. 0x05, 0x07, // Usage Pg (Key Codes)
    183. 0x19, 0xE0, // Usage Min (224) VK_CTRL:0xe0
    184. 0x29, 0xE7, // Usage Max (231) VK_RWIN:0xe7
    185. 0x15, 0x00, // Log Min (0)
    186. 0x25, 0x01, // Log Max (1)
    187. //
    188. // Modifier byte
    189. 0x75, 0x01, // Report Size (1) 1 bit * 8
    190. 0x95, 0x08, // Report Count (8)
    191. 0x81, 0x02, // Input: (Data, Variable, Absolute)
    192. //
    193. // Reserved byte
    194. 0x95, 0x01, // Report Count (1)
    195. 0x75, 0x08, // Report Size (8)
    196. 0x81, 0x01, // Input: (static constant)
    197. //keyboard output
    198. //5 bit led ctrl: NumLock CapsLock ScrollLock Compose kana
    199. 0x95, 0x05, //Report Count (5)
    200. 0x75, 0x01, //Report Size (1)
    201. 0x05, 0x08, //Usage Pg (LEDs )
    202. 0x19, 0x01, //Usage Min
    203. 0x29, 0x05, //Usage Max
    204. 0x91, 0x02, //Output (Data, Variable, Absolute)
    205. //3 bit reserved
    206. 0x95, 0x01, //Report Count (1)
    207. 0x75, 0x03, //Report Size (3)
    208. 0x91, 0x01, //Output (static constant)
    209. // Key arrays (6 bytes)
    210. 0x95, 0x06, // Report Count (6)
    211. 0x75, 0x08, // Report Size (8)
    212. 0x15, 0x00, // Log Min (0)
    213. 0x25, 0xF1, // Log Max (241)
    214. 0x05, 0x07, // Usage Pg (Key Codes)
    215. 0x19, 0x00, // Usage Min (0)
    216. 0x29, 0xf1, // Usage Max (241)
    217. 0x81, 0x00, // Input: (Data, Array)
    218. 0xC0, // End Collection
    219. //consumer report in
    220. 0x05, 0x0C, // Usage Page (Consumer)
    221. 0x09, 0x01, // Usage (Consumer Control)
    222. 0xA1, 0x01, // Collection (Application)
    223. 0x85, HID_REPORT_ID_CONSUME_CONTROL_INPUT, // Report Id
    224. 0x75,0x10, //global, report size 16 bits
    225. 0x95,0x01, //global, report count 1
    226. 0x15,0x01, //global, min 0x01
    227. 0x26,0x8c,0x02, //global, max 0x28c
    228. 0x19,0x01, //local, min 0x01
    229. 0x2a,0x8c,0x02, //local, max 0x28c
    230. 0x81,0x00, //main, input data varible, absolute
    231. 0xc0, //main, end collection
    232. #endif
    233. #if 1
    234. //mouse report in
    235. 0x05, 0x01, // Usage Page (Generic Desktop)
    236. 0x09, 0x02, // Usage (Mouse)
    237. 0xA1, 0x01, // Collection (Application)
    238. 0x85, 3, // Report Id
    239. 0x09, 0x01, // Usage (Pointer)
    240. 0xA1, 0x00, // Collection (Physical)
    241. 0x05, 0x09, // Usage Page (Buttons)
    242. 0x19, 0x01, // Usage Minimum (01) - Button 1
    243. 0x29, 0x03, // Usage Maximum (03) - Button 3
    244. 0x15, 0x00, // Logical Minimum (0)
    245. 0x25, 0x01, // Logical Maximum (1)
    246. 0x75, 0x01, // Report Size (1)
    247. 0x95, 0x05, // Report Count (3)
    248. 0x81, 0x02, // Input (Data, Variable, Absolute) - Button states
    249. 0x75, 0x03, // Report Size (5)
    250. 0x95, 0x01, // Report Count (1)
    251. 0x81, 0x01, // Input (Constant) - Padding or Reserved bits
    252. 0x05, 0x01, // Usage Page (Generic Desktop Control)
    253. 0x09, 0x30, // Usage (X)
    254. 0x09, 0x31, // Usage (Y)
    255. //mouse data len 等于6
    256. 0x16, 0x01, 0x80, // LOGICAL_MINIMUM(0)
    257. 0x26, 0xff, 0x7f,
    258. 0x75, 0x10, // Report Size (16)
    259. 0x95, 0x02, // Report Count (2)
    260. 0x81, 0x06, // Input (Data, Variable, Relative)
    261. 0x09, 0x38, // Usage (Wheel)
    262. 0x15, 0x81, // Logical Minimum (-4)
    263. 0x25, 0x7F, // Logical Maximum (3)
    264. 0x75, 0x08, // Report Size (3)
    265. 0x95, 0x01, // Report Count (1)
    266. 0x81, 0x06, // Input (Data, Variable, Relative)
    267. 0xC0, // End Collection
    268. 0xC0, // End Collection
    269. 0x06, 0x01, 0xFF,
    270. 0x09, 0x01,
    271. 0xA1, 0x01,
    272. 0x85, 5,
    273. 0x09, 0x05,
    274. 0x15, 0x00,
    275. 0x26, 0xFF, 0x00,
    276. 0x75, 0x08,
    277. 0x95, 0x04,
    278. 0xB1, 0x02,
    279. 0xC0,
    280. #endif
    281. };
    282. // HID External Report Reference Descriptor for report map
    283. static u16 extServiceUUID;
    284. /
    285. static const u8 my_OtaUUID[16] = WRAPPING_BRACES(TELINK_SPP_DATA_OTA);
    286. static const u8 my_OtaServiceUUID[16] = WRAPPING_BRACES(TELINK_OTA_UUID_SERVICE);
    287. static u8 my_OtaData = 0x00;
    288. static u8 otaDataCCC[2] = {0,0};
    289. static const u8 my_MicName[] = {'M', 'i', 'c'};
    290. static const u8 my_SpeakerName[] = {'S', 'p', 'e', 'a', 'k', 'e', 'r'};
    291. static const u8 my_OtaName[] = {'O', 'T', 'A'};
    292. // Include attribute (Battery service)
    293. static const u16 include[3] = {BATT_PS_H, BATT_LEVEL_INPUT_CCB_H, SERVICE_UUID_BATTERY};
    294. GAP attribute values
    295. static const u8 my_devNameCharVal[5] = {
    296. CHAR_PROP_READ | CHAR_PROP_NOTIFY,
    297. U16_LO(GenericAccess_DeviceName_DP_H), U16_HI(GenericAccess_DeviceName_DP_H),
    298. U16_LO(GATT_UUID_DEVICE_NAME), U16_HI(GATT_UUID_DEVICE_NAME)
    299. };
    300. static const u8 my_appearanceCharVal[5] = {
    301. CHAR_PROP_READ,
    302. U16_LO(GenericAccess_Appearance_DP_H), U16_HI(GenericAccess_Appearance_DP_H),
    303. U16_LO(GATT_UUID_APPEARANCE), U16_HI(GATT_UUID_APPEARANCE)
    304. };
    305. static const u8 my_periConnParamCharVal[5] = {
    306. CHAR_PROP_READ,
    307. U16_LO(CONN_PARAM_DP_H), U16_HI(CONN_PARAM_DP_H),
    308. U16_LO(GATT_UUID_PERI_CONN_PARAM), U16_HI(GATT_UUID_PERI_CONN_PARAM)
    309. };
    310. GATT attribute values
    311. static const u8 my_serviceChangeCharVal[5] = {
    312. CHAR_PROP_INDICATE,
    313. U16_LO(GenericAttribute_ServiceChanged_DP_H), U16_HI(GenericAttribute_ServiceChanged_DP_H),
    314. U16_LO(GATT_UUID_SERVICE_CHANGE), U16_HI(GATT_UUID_SERVICE_CHANGE)
    315. };
    316. device Information attribute values
    317. static const u8 my_PnCharVal[5] = {
    318. CHAR_PROP_READ,
    319. U16_LO(DeviceInformation_pnpID_DP_H), U16_HI(DeviceInformation_pnpID_DP_H),
    320. U16_LO(CHARACTERISTIC_UUID_PNP_ID), U16_HI(CHARACTERISTIC_UUID_PNP_ID)
    321. };
    322. HID attribute values
    323. static const u8 my_hidProtocolModeCharVal[5] = {
    324. CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RSP,
    325. U16_LO(HID_PROTOCOL_MODE_DP_H), U16_HI(HID_PROTOCOL_MODE_DP_H),
    326. U16_LO(CHARACTERISTIC_UUID_HID_PROTOCOL_MODE), U16_HI(CHARACTERISTIC_UUID_HID_PROTOCOL_MODE)
    327. };
    328. #if 0
    329. static const u8 my_hidbootKeyInReporCharVal[5] = {
    330. CHAR_PROP_READ | CHAR_PROP_NOTIFY,
    331. U16_LO(HID_BOOT_KB_REPORT_INPUT_DP_H), U16_HI(HID_BOOT_KB_REPORT_INPUT_DP_H),
    332. U16_LO(CHARACTERISTIC_UUID_HID_BOOT_KEY_INPUT), U16_HI(CHARACTERISTIC_UUID_HID_BOOT_KEY_INPUT)
    333. };
    334. static const u8 my_hidbootKeyOutReporCharVal[5] = {
    335. CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_WRITE_WITHOUT_RSP,
    336. U16_LO(HID_BOOT_KB_REPORT_OUTPUT_DP_H), U16_HI(HID_BOOT_KB_REPORT_OUTPUT_DP_H),
    337. U16_LO(CHARACTERISTIC_UUID_HID_BOOT_KEY_OUTPUT), U16_HI(CHARACTERISTIC_UUID_HID_BOOT_KEY_OUTPUT)
    338. };
    339. static const u8 my_hidReportCCinCharVal[5] = {
    340. CHAR_PROP_READ | CHAR_PROP_NOTIFY,
    341. U16_LO(HID_CONSUME_REPORT_INPUT_DP_H), U16_HI(HID_CONSUME_REPORT_INPUT_DP_H),
    342. U16_LO(CHARACTERISTIC_UUID_HID_REPORT), U16_HI(CHARACTERISTIC_UUID_HID_REPORT)
    343. };
    344. static const u8 my_hidReportKEYinCharVal[5] = {
    345. CHAR_PROP_READ | CHAR_PROP_NOTIFY,
    346. U16_LO(HID_NORMAL_KB_REPORT_INPUT_DP_H), U16_HI(HID_NORMAL_KB_REPORT_INPUT_DP_H),
    347. U16_LO(CHARACTERISTIC_UUID_HID_REPORT), U16_HI(CHARACTERISTIC_UUID_HID_REPORT)
    348. };
    349. static const u8 my_hidReportKEYoutCharVal[5] = {
    350. CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_WRITE_WITHOUT_RSP,
    351. U16_LO(HID_NORMAL_KB_REPORT_OUTPUT_DP_H), U16_HI(HID_NORMAL_KB_REPORT_OUTPUT_DP_H),
    352. U16_LO(CHARACTERISTIC_UUID_HID_REPORT), U16_HI(CHARACTERISTIC_UUID_HID_REPORT)
    353. };
    354. #endif
    355. static const u8 my_hidReportMapCharVal[5] = {
    356. CHAR_PROP_READ,
    357. U16_LO(HID_REPORT_MAP_DP_H), U16_HI(HID_REPORT_MAP_DP_H),
    358. U16_LO(CHARACTERISTIC_UUID_HID_REPORT_MAP), U16_HI(CHARACTERISTIC_UUID_HID_REPORT_MAP)
    359. };
    360. static const u8 my_hidinformationCharVal[5] = {
    361. CHAR_PROP_READ,
    362. U16_LO(HID_INFORMATION_DP_H), U16_HI(HID_INFORMATION_DP_H),
    363. U16_LO(CHARACTERISTIC_UUID_HID_INFORMATION), U16_HI(CHARACTERISTIC_UUID_HID_INFORMATION)
    364. };
    365. static const u8 my_hidCtrlPointCharVal[5] = {
    366. CHAR_PROP_WRITE_WITHOUT_RSP,
    367. U16_LO(HID_CONTROL_POINT_DP_H), U16_HI(HID_CONTROL_POINT_DP_H),
    368. U16_LO(CHARACTERISTIC_UUID_HID_CONTROL_POINT), U16_HI(CHARACTERISTIC_UUID_HID_CONTROL_POINT)
    369. };
    370. Battery attribute values
    371. static const u8 my_batCharVal[5] = {
    372. CHAR_PROP_READ | CHAR_PROP_NOTIFY,
    373. U16_LO(BATT_LEVEL_INPUT_DP_H), U16_HI(BATT_LEVEL_INPUT_DP_H),
    374. U16_LO(CHARACTERISTIC_UUID_BATTERY_LEVEL), U16_HI(CHARACTERISTIC_UUID_BATTERY_LEVEL)
    375. };
    376. OTA attribute values
    377. static const u8 my_OtaCharVal[19] = {
    378. CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RSP | CHAR_PROP_NOTIFY,
    379. U16_LO(OTA_CMD_OUT_DP_H), U16_HI(OTA_CMD_OUT_DP_H),
    380. TELINK_SPP_DATA_OTA,
    381. };
    382. // TM : to modify
    383. static const attribute_t my_Attributes[] = {
    384. {ATT_END_H - 1, 0,0,0,0,0}, // total num of attribute
    385. // 0001 - 0007 gap
    386. {7,ATT_PERMISSIONS_READ,2,2,(u8*)(&my_primaryServiceUUID), (u8*)(&my_gapServiceUUID), 0},
    387. {0,ATT_PERMISSIONS_READ,2,sizeof(my_devNameCharVal),(u8*)(&my_characterUUID), (u8*)(my_devNameCharVal), 0},
    388. {0,ATT_PERMISSIONS_READ,2,sizeof(my_devName), (u8*)(&my_devNameUUID), (u8*)(my_devName), 0},
    389. {0,ATT_PERMISSIONS_READ,2,sizeof(my_appearanceCharVal),(u8*)(&my_characterUUID), (u8*)(my_appearanceCharVal), 0},
    390. {0,ATT_PERMISSIONS_READ,2,sizeof (my_appearance), (u8*)(&my_appearanceUIID), (u8*)(&my_appearance), 0},
    391. {0,ATT_PERMISSIONS_READ,2,sizeof(my_periConnParamCharVal),(u8*)(&my_characterUUID), (u8*)(my_periConnParamCharVal), 0},
    392. {0,ATT_PERMISSIONS_READ,2,sizeof (my_periConnParameters),(u8*)(&my_periConnParamUUID), (u8*)(&my_periConnParameters), 0},
    393. // 0008 - 000b gatt
    394. {4,ATT_PERMISSIONS_READ,2,2,(u8*)(&my_primaryServiceUUID), (u8*)(&my_gattServiceUUID), 0},
    395. {0,ATT_PERMISSIONS_READ,2,sizeof(my_serviceChangeCharVal),(u8*)(&my_characterUUID), (u8*)(my_serviceChangeCharVal), 0},
    396. {0,ATT_PERMISSIONS_READ,2,sizeof (serviceChangeVal), (u8*)(&serviceChangeUUID), (u8*)(&serviceChangeVal), 0},
    397. {0,ATT_PERMISSIONS_RDWR,2,sizeof (serviceChangeCCC),(u8*)(&clientCharacterCfgUUID), (u8*)(serviceChangeCCC), 0},
    398. // 000c - 000e device Information Service
    399. {3,ATT_PERMISSIONS_READ,2,2,(u8*)(&my_primaryServiceUUID), (u8*)(&my_devServiceUUID), 0},
    400. {0,ATT_PERMISSIONS_READ,2,sizeof(my_PnCharVal),(u8*)(&my_characterUUID), (u8*)(my_PnCharVal), 0},
    401. {0,ATT_PERMISSIONS_READ,2,sizeof (my_PnPtrs),(u8*)(&my_PnPUUID), (u8*)(my_PnPtrs), 0},
    402. /// 4. HID Service /
    403. // 000f
    404. //{27, ATT_PERMISSIONS_READ,2,2,(u8*)(&my_primaryServiceUUID), (u8*)(&my_hidServiceUUID), 0},
    405. {HID_CONTROL_POINT_DP_H - HID_PS_H + 1, ATT_PERMISSIONS_READ,2,2,(u8*)(&my_primaryServiceUUID), (u8*)(&my_hidServiceUUID), 0},
    406. // 0010 include battery service
    407. //{0,ATT_PERMISSIONS_READ,2,sizeof(include),(u8*)(&hidIncludeUUID), (u8*)(include), 0},
    408. // 0011 - 0012 protocol mode
    409. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidProtocolModeCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidProtocolModeCharVal), 0}, //prop
    410. {0,ATT_PERMISSIONS_RDWR,2, sizeof(protocolMode),(u8*)(&hidProtocolModeUUID), (u8*)(&protocolMode), 0}, //value
    411. #if 0
    412. // 0013 - 0015 boot keyboard input report (char-val-client)
    413. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidbootKeyInReporCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidbootKeyInReporCharVal), 0}, //prop
    414. {0,ATT_PERMISSIONS_READ,2,sizeof(bootKeyInReport),(u8*)(&hidbootKeyInReportUUID), (u8*)(&bootKeyInReport), 0}, //value
    415. {0,ATT_PERMISSIONS_RDWR,2,sizeof(bootKeyInReportCCC),(u8*)(&clientCharacterCfgUUID), (u8*)(bootKeyInReportCCC), 0}, //value
    416. // 0016 - 0017 boot keyboard output report (char-val)
    417. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidbootKeyOutReporCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidbootKeyOutReporCharVal), 0}, //prop
    418. {0,ATT_PERMISSIONS_RDWR,2, sizeof(bootKeyOutReport), (u8*)(&hidbootKeyOutReportUUID), (u8*)(&bootKeyOutReport), 0}, //value
    419. // 0018 - 001b. consume report in: 4 (char-val-client-ref)
    420. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidReportCCinCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidReportCCinCharVal), 0}, //prop
    421. {0,ATT_PERMISSIONS_READ,2, sizeof(reportConsumerControlIn),(u8*)(&hidReportUUID), (u8*)(reportConsumerControlIn), 0}, //value
    422. {0,ATT_PERMISSIONS_RDWR,2,sizeof(reportConsumerControlInCCC),(u8*)(&clientCharacterCfgUUID), (u8*)(reportConsumerControlInCCC), 0}, //value
    423. {0,ATT_PERMISSIONS_RDWR,2,sizeof(reportRefConsumerControlIn),(u8*)(&reportRefUUID), (u8*)(reportRefConsumerControlIn), 0}, //value
    424. // 001c - 001f . keyboard report in : 4 (char-val-client-ref)
    425. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidReportKEYinCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidReportKEYinCharVal), 0}, //prop
    426. {0,ATT_PERMISSIONS_READ,2, sizeof(reportKeyIn),(u8*)(&hidReportUUID), (u8*)(reportKeyIn), 0}, //value
    427. {0,ATT_PERMISSIONS_RDWR,2,sizeof(reportKeyInCCC),(u8*)(&clientCharacterCfgUUID), (u8*)(reportKeyInCCC), 0}, //value
    428. {0,ATT_PERMISSIONS_RDWR,2,sizeof(reportRefKeyIn),(u8*)(&reportRefUUID), (u8*)(reportRefKeyIn), 0}, //value
    429. // 0020 - 0022 . keyboard report out: 3 (char-val-ref)
    430. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidReportKEYoutCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidReportKEYoutCharVal), 0}, //prop
    431. {0,ATT_PERMISSIONS_RDWR,2,sizeof(reportKeyOut),(u8*)(&hidReportUUID), (u8*)(reportKeyOut), 0}, //value
    432. {0,ATT_PERMISSIONS_RDWR,2,sizeof(reportRefKeyOut),(u8*)(&reportRefUUID), (u8*)(reportRefKeyOut), 0}, //value
    433. #endif
    434. #if 1//鼠标移植
    435. // 0013 - 0015 boot keyboard input report (char-val-client)
    436. {0, ATT_PERMISSIONS_READ, 2, CHAR_2_LEN, (u8 *)(&my_characterUUID), (u8 *)(&bootMouseInChar[0]), 0}, //prop
    437. {0, ATT_PERMISSIONS_READ, 2, sizeof(bootMouseInReport), (u8 *)(&bootMouseInChar[3]), (u8 *)(&bootMouseInReport), 0}, //value
    438. {0, ATT_PERMISSIONS_RDWR, 2, 2, (u8 *)(&clientCharacterCfgUUID), (u8 *)(bootMouseInReportCCC), 0}, //value
    439. // 001c - 001f . keyboard report in : 4 (char-val-client-ref)
    440. {0, ATT_PERMISSIONS_READ, 2, CHAR_2_LEN, (u8 *)(&my_characterUUID), (u8 *)(&reportMouseInChar[0]), 0}, //prop
    441. {0, ATT_PERMISSIONS_RDWR, 2, sizeof(reportMouseIn), (u8 *)(&reportMouseInChar[3]), (u8 *)(reportMouseIn), 0}, //value
    442. {0, ATT_PERMISSIONS_RDWR, 2, 2, (u8 *)(&clientCharacterCfgUUID), (u8 *)(reportMouseInCCC), &cccWrite}, //value
    443. {0, ATT_PERMISSIONS_READ, 2, 2, (u8 *)(&reportRefUUID), (u8 *)(reportRefMouseIn), 0}, //value
    444. {0, ATT_PERMISSIONS_READ, 2, CHAR_2_LEN, (u8 *)(&my_characterUUID), (u8 *)(&reportUseInChar[0]), 0}, //prop
    445. {0, ATT_PERMISSIONS_RDWR, 2, sizeof(reportUseIn), (u8 *)(&reportUseInChar[3]), (u8 *)(reportUseIn), 0}, //value
    446. {0, ATT_PERMISSIONS_RDWR, 2, 2, (u8 *)(&clientCharacterCfgUUID), (u8 *)(reportUseInCCC), &cccWrite}, //value
    447. {0, ATT_PERMISSIONS_READ, 2, 2, (u8 *)(&reportRefUUID), (u8 *)(reportRefUseIn), 0}, //value
    448. #endif
    449. // 0023 - 0025 . report map: 3
    450. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidReportMapCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidReportMapCharVal), 0}, //prop
    451. {0,ATT_PERMISSIONS_READ,2,sizeof(reportMap),(u8*)(&hidReportMapUUID), (u8*)(reportMap), 0}, //value
    452. //{0,ATT_PERMISSIONS_RDWR,2,sizeof(extServiceUUID),(u8*)(&extReportRefUUID), (u8*)(&extServiceUUID), 0}, //value
    453. // 0026 - 0027 . hid information: 2
    454. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidinformationCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidinformationCharVal), 0}, //prop
    455. {0,ATT_PERMISSIONS_READ,2, sizeof(hidInformation),(u8*)(&hidinformationUUID), (u8*)(hidInformation), 0}, //value
    456. // 0028 - 0029 . control point: 2
    457. {0,ATT_PERMISSIONS_READ,2,sizeof(my_hidCtrlPointCharVal),(u8*)(&my_characterUUID), (u8*)(my_hidCtrlPointCharVal), 0}, //prop
    458. {0,ATT_PERMISSIONS_WRITE,2, sizeof(controlPoint),(u8*)(&hidCtrlPointUUID), (u8*)(&controlPoint), 0}, //value
    459. // Battery Service /
    460. // 002a - 002d
    461. {4,ATT_PERMISSIONS_READ,2,2,(u8*)(&my_primaryServiceUUID), (u8*)(&my_batServiceUUID), 0},
    462. {0,ATT_PERMISSIONS_READ,2,sizeof(my_batCharVal),(u8*)(&my_characterUUID), (u8*)(my_batCharVal), 0}, //prop
    463. {0,ATT_PERMISSIONS_READ,2,sizeof(my_batVal),(u8*)(&my_batCharUUID), (u8*)(my_batVal), 0}, //value
    464. {0,ATT_PERMISSIONS_RDWR,2,sizeof(batteryValueInCCC),(u8*)(&clientCharacterCfgUUID), (u8*)(batteryValueInCCC), 0}, //value
    465. // OTA /
    466. // 002e - 0032
    467. {5,ATT_PERMISSIONS_READ, 2,16,(u8*)(&my_primaryServiceUUID), (u8*)(&my_OtaServiceUUID), 0},
    468. {0,ATT_PERMISSIONS_READ, 2, sizeof(my_OtaCharVal),(u8*)(&my_characterUUID), (u8*)(my_OtaCharVal), 0}, //prop
    469. {0,ATT_PERMISSIONS_RDWR,16,sizeof(my_OtaData),(u8*)(&my_OtaUUID), (&my_OtaData), &otaWrite, NULL}, //value
    470. {0,ATT_PERMISSIONS_RDWR,2,sizeof(otaDataCCC),(u8*)(&clientCharacterCfgUUID), (u8*)(otaDataCCC), 0}, //value
    471. {0,ATT_PERMISSIONS_READ, 2,sizeof (my_OtaName),(u8*)(&userdesc_UUID), (u8*)(my_OtaName), 0},
    472. };
    473. /**
    474. * @brief Initialize the attribute table
    475. * @param[in] none
    476. * @return none
    477. */
    478. void my_att_init (void)
    479. {
    480. bls_att_setAttributeTable ((u8 *)my_Attributes);
    481. }

    2. 修改app_att.h

    • 注释掉键盘音频报告,添加鼠标报告
    1. /********************************************************************************************************
    2. * @file app_att.h
    3. *
    4. * @brief This is the header file for BLE SDK
    5. *
    6. * @author BLE GROUP
    7. * @date 06,2020
    8. *
    9. * @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
    10. * All rights reserved.
    11. *
    12. * Redistribution and use in source and binary forms, with or without
    13. * modification, are permitted provided that the following conditions are met:
    14. *
    15. * 1. Redistributions of source code must retain the above copyright
    16. * notice, this list of conditions and the following disclaimer.
    17. *
    18. * 2. Unless for usage inside a TELINK integrated circuit, redistributions
    19. * in binary form must reproduce the above copyright notice, this list of
    20. * conditions and the following disclaimer in the documentation and/or other
    21. * materials provided with the distribution.
    22. *
    23. * 3. Neither the name of TELINK, nor the names of its contributors may be
    24. * used to endorse or promote products derived from this software without
    25. * specific prior written permission.
    26. *
    27. * 4. This software, with or without modification, must only be used with a
    28. * TELINK integrated circuit. All other usages are subject to written permission
    29. * from TELINK and different commercial license may apply.
    30. *
    31. * 5. Licensee shall be solely responsible for any claim to the extent arising out of or
    32. * relating to such deletion(s), modification(s) or alteration(s).
    33. *
    34. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    35. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    36. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    37. * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
    38. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    39. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    41. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    43. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    44. *
    45. *******************************************************************************************************/
    46. #ifndef APP_ATT_H_
    47. #define APP_ATT_H_
    48. / ATT HANDLER define ///
    49. typedef enum
    50. {
    51. ATT_H_START = 0,
    52. Gap
    53. /**********************************************************************************************/
    54. GenericAccess_PS_H, //UUID: 2800, VALUE: uuid 1800
    55. GenericAccess_DeviceName_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    56. GenericAccess_DeviceName_DP_H, //UUID: 2A00, VALUE: device name
    57. GenericAccess_Appearance_CD_H, //UUID: 2803, VALUE: Prop: Read
    58. GenericAccess_Appearance_DP_H, //UUID: 2A01, VALUE: appearance
    59. CONN_PARAM_CD_H, //UUID: 2803, VALUE: Prop: Read
    60. CONN_PARAM_DP_H, //UUID: 2A04, VALUE: connParameter
    61. gatt
    62. /**********************************************************************************************/
    63. GenericAttribute_PS_H, //UUID: 2800, VALUE: uuid 1801
    64. GenericAttribute_ServiceChanged_CD_H, //UUID: 2803, VALUE: Prop: Indicate
    65. GenericAttribute_ServiceChanged_DP_H, //UUID: 2A05, VALUE: service change
    66. GenericAttribute_ServiceChanged_CCB_H, //UUID: 2902, VALUE: serviceChangeCCC
    67. device information
    68. /**********************************************************************************************/
    69. DeviceInformation_PS_H, //UUID: 2800, VALUE: uuid 180A
    70. DeviceInformation_pnpID_CD_H, //UUID: 2803, VALUE: Prop: Read
    71. DeviceInformation_pnpID_DP_H, //UUID: 2A50, VALUE: PnPtrs
    72. HID
    73. /**********************************************************************************************/
    74. HID_PS_H, //UUID: 2800, VALUE: uuid 1812
    75. //include
    76. //HID_INCLUDE_H, //UUID: 2802, VALUE: include
    77. //protocol
    78. HID_PROTOCOL_MODE_CD_H, //UUID: 2803, VALUE: Prop: read | write_without_rsp
    79. HID_PROTOCOL_MODE_DP_H, //UUID: 2A4E, VALUE: protocolMode
    80. #if 0
    81. //boot keyboard input report
    82. HID_BOOT_KB_REPORT_INPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    83. HID_BOOT_KB_REPORT_INPUT_DP_H, //UUID: 2A22, VALUE: bootKeyInReport
    84. HID_BOOT_KB_REPORT_INPUT_CCB_H, //UUID: 2902, VALUE: bootKeyInReportCCC
    85. //boot keyboard output report
    86. HID_BOOT_KB_REPORT_OUTPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | write| write_without_rsp
    87. HID_BOOT_KB_REPORT_OUTPUT_DP_H, //UUID: 2A32, VALUE: bootKeyOutReport
    88. //consume report in
    89. HID_CONSUME_REPORT_INPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    90. HID_CONSUME_REPORT_INPUT_DP_H, //UUID: 2A4D, VALUE: reportConsumerIn
    91. HID_CONSUME_REPORT_INPUT_CCB_H, //UUID: 2902, VALUE: reportConsumerInCCC
    92. HID_CONSUME_REPORT_INPUT_REF_H, //UUID: 2908 VALUE: REPORT_ID_CONSUMER, TYPE_INPUT
    93. //keyboard report in
    94. HID_NORMAL_KB_REPORT_INPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    95. HID_NORMAL_KB_REPORT_INPUT_DP_H, //UUID: 2A4D, VALUE: reportKeyIn
    96. HID_NORMAL_KB_REPORT_INPUT_CCB_H, //UUID: 2902, VALUE: reportKeyInInCCC
    97. HID_NORMAL_KB_REPORT_INPUT_REF_H, //UUID: 2908 VALUE: REPORT_ID_KEYBOARD, TYPE_INPUT
    98. //keyboard report out
    99. HID_NORMAL_KB_REPORT_OUTPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | write| write_without_rsp
    100. HID_NORMAL_KB_REPORT_OUTPUT_DP_H, //UUID: 2A4D, VALUE: reportKeyOut
    101. HID_NORMAL_KB_REPORT_OUTPUT_REF_H, //UUID: 2908 VALUE: REPORT_ID_KEYBOARD, TYPE_OUTPUT
    102. #endif
    103. #if 1
    104. HID_BOOT_MOUSE_REPORT_INPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    105. HID_BOOT_MOUSE_REPORT_INPUT_DP_H, //UUID: 2A22, VALUE: bootKeyInReport
    106. HID_BOOT_MOUSE_REPORT_INPUT_CCB_H, //UUID: 2902, VALUE: bootKeyInReportCCC
    107. HID_MOUSE_REPORT_INPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    108. HID_MOUSE_REPORT_INPUT_DP_H, //UUID: 2A4D, VALUE: reportConsumerIn
    109. HID_MOUSE_REPORT_INPUT_CCB_H, //UUID: 2902, VALUE: reportConsumerInCCC
    110. HID_MOUSE_REPORT_INPUT_REF_H, //UUID: 2908 VALUE: REPORT_ID_CONSUMER, TYPE_INPUT
    111. HID_USER_REPORT_INPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    112. HID_USER_REPORT_INPUT_DP_H, //UUID: 2A4D, VALUE: reportConsumerIn
    113. HID_USER_REPORT_INPUT_CCB_H, //UUID: 2902, VALUE: reportConsumerInCCC
    114. HID_USER_REPORT_INPUT_REF_H, //UUID: 2908 VALUE: REPORT_ID_CONSUMER, TYPE_INPUT
    115. #endif
    116. // report map
    117. HID_REPORT_MAP_CD_H, //UUID: 2803, VALUE: Prop: Read
    118. HID_REPORT_MAP_DP_H, //UUID: 2A4B, VALUE: reportKeyIn
    119. //HID_REPORT_MAP_EXT_REF_H, //UUID: 2907 VALUE: extService
    120. //hid information
    121. HID_INFORMATION_CD_H, //UUID: 2803, VALUE: Prop: read
    122. HID_INFORMATION_DP_H, //UUID: 2A4A VALUE: hidInformation
    123. //control point
    124. HID_CONTROL_POINT_CD_H, //UUID: 2803, VALUE: Prop: write_without_rsp
    125. HID_CONTROL_POINT_DP_H, //UUID: 2A4C VALUE: controlPoint
    126. battery service
    127. /**********************************************************************************************/
    128. BATT_PS_H, //UUID: 2800, VALUE: uuid 180f
    129. BATT_LEVEL_INPUT_CD_H, //UUID: 2803, VALUE: Prop: Read | Notify
    130. BATT_LEVEL_INPUT_DP_H, //UUID: 2A19 VALUE: batVal
    131. BATT_LEVEL_INPUT_CCB_H, //UUID: 2902, VALUE: batValCCC
    132. Ota
    133. /**********************************************************************************************/
    134. OTA_PS_H, //UUID: 2800, VALUE: telink ota service uuid
    135. OTA_CMD_OUT_CD_H, //UUID: 2803, VALUE: Prop: read | write_without_rsp | Notify
    136. OTA_CMD_OUT_DP_H, //UUID: telink ota uuid, VALUE: otaData
    137. OTA_CMD_INPUT_CCB_H, //UUID: 2902, VALUE: otaDataCCC
    138. OTA_CMD_OUT_DESC_H, //UUID: 2901, VALUE: otaName
    139. ATT_END_H,
    140. }ATT_HANDLE;
    141. /**
    142. * @brief Initialize the attribute table
    143. * @param[in] none
    144. * @return none
    145. */
    146. void my_att_init(void);
    147. #endif /* APP_ATT_H_ */

     3. 修改app.c

    • 更改adv Packet data
    1. const u8 tbl_advData[] = {
    2. #if 1
    3. #if 0
    4. 0x05, 0x09, 'V', 'H', 'I', 'D',
    5. 0x02, 0x01, 0x05, // BLE limited discoverable mode and BR/EDR not supported
    6. 0x03, 0x19, 0x80, 0x01, // 384, Generic Remote Control, Generic category
    7. 0x05, 0x02, 0x12, 0x18, 0x0F, 0x18, // incomplete list of service class UUIDs (0x1812, 0x180F)
    8. #endif
    9. 0x05, 0x09, 'V', 'H', 'I', 'D',
    10. 0x02, 0x01, 0x05, // BLE limited discoverable mode and BR/EDR not supported
    11. 0x03, 0x19, 0xC2, 0x03, // 384, Generic Remote Control, Generic category
    12. 0x05, 0x02, 0x12, 0x18, 0x0F, 0x18, // incomplete list of service class UUIDs (0x1812, 0x180F)
    13. #else
    14. //0x05, 0x09, 'V', 'H', 'I', 'D',
    15. 0x02, 0x01, 0x05, // BLE limited discoverable mode and BR/EDR not supported
    16. 0x03, 0x19, 0xc2, 0x03, // 384, Keyboard, Generic category,
    17. 0x03, 0x03, 0x12, 0x18, // incomplete list of service class UUIDs (0x1812-HID SERVICE, 180F-BATTERY)
    18. 0x06, 0xff, 0x06, 0x00, 0x03, 0x00, 0x80,
    19. 12, 0x09,
    20. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    21. #endif
    22. };

    5. 在button.c中增加测试函数

    1. /********************************************************************************************************
    2. * @file button.c
    3. *
    4. * @brief This is the source file for BLE SDK
    5. *
    6. * @author BLE GROUP
    7. * @date 06,2020
    8. *
    9. * @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
    10. * All rights reserved.
    11. *
    12. * Redistribution and use in source and binary forms, with or without
    13. * modification, are permitted provided that the following conditions are met:
    14. *
    15. * 1. Redistributions of source code must retain the above copyright
    16. * notice, this list of conditions and the following disclaimer.
    17. *
    18. * 2. Unless for usage inside a TELINK integrated circuit, redistributions
    19. * in binary form must reproduce the above copyright notice, this list of
    20. * conditions and the following disclaimer in the documentation and/or other
    21. * materials provided with the distribution.
    22. *
    23. * 3. Neither the name of TELINK, nor the names of its contributors may be
    24. * used to endorse or promote products derived from this software without
    25. * specific prior written permission.
    26. *
    27. * 4. This software, with or without modification, must only be used with a
    28. * TELINK integrated circuit. All other usages are subject to written permission
    29. * from TELINK and different commercial license may apply.
    30. *
    31. * 5. Licensee shall be solely responsible for any claim to the extent arising out of or
    32. * relating to such deletion(s), modification(s) or alteration(s).
    33. *
    34. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    35. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    36. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    37. * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
    38. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    39. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    41. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    43. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    44. *
    45. *******************************************************************************************************/
    46. #include "tl_common.h"
    47. #include "drivers.h"
    48. #include "stack/ble/ble.h"
    49. #include "application/usbstd/usbkeycode.h"
    50. #include "app.h"
    51. #include "app_att.h"
    52. #include "gpio.h"
    53. #include "mat_log.h"
    54. #include "tl_common.h"
    55. #include "drivers.h"
    56. #include "stack/ble/ble.h"
    57. #include "application/usbstd/usbkeycode.h"
    58. #include "app.h"
    59. #include "app_att.h"
    60. #define KEY2 GPIO_PB2
    61. #define KEY3 GPIO_PB3
    62. #define KEY4 GPIO_PB4
    63. #define KEY5 GPIO_PB5
    64. #define LED1 GPIO_PD2
    65. #define LED2 GPIO_PD3
    66. #define LED3 GPIO_PD4
    67. #define LED4 GPIO_PD5
    68. typedef enum
    69. {
    70. K1,
    71. K2,
    72. K3,
    73. K4
    74. }key_t;
    75. typedef struct
    76. {
    77. u8 btn;
    78. s16 x;
    79. s16 y;
    80. s8 wheel;
    81. } mouse_data_t;
    82. void led_init(void)
    83. {
    84. gpio_set_func(LED1, AS_GPIO);
    85. gpio_set_output_en(LED1, 1);
    86. gpio_write(LED1,0);
    87. gpio_set_func(LED2, AS_GPIO);
    88. gpio_set_output_en(LED2, 1);
    89. gpio_write(LED2,0);
    90. gpio_set_func(LED3, AS_GPIO);
    91. gpio_set_output_en(LED3, 1);
    92. gpio_write(LED3,0);
    93. gpio_set_func(LED4, AS_GPIO);
    94. gpio_set_output_en(LED4, 1);
    95. gpio_write(LED4,0);
    96. }
    97. void key_init(void)
    98. {
    99. gpio_set_func(KEY2, AS_GPIO);
    100. gpio_set_input_en(KEY2, ENABLE);
    101. gpio_setup_up_down_resistor(KEY2, PM_PIN_PULLUP_10K);
    102. gpio_set_func(KEY3, AS_GPIO);
    103. gpio_set_input_en(KEY3, ENABLE);
    104. gpio_setup_up_down_resistor(KEY3, PM_PIN_PULLUP_10K);
    105. gpio_set_func(KEY4, AS_GPIO);
    106. gpio_set_input_en(KEY4, ENABLE);
    107. gpio_setup_up_down_resistor(KEY4, PM_PIN_PULLUP_10K);
    108. #if 1
    109. gpio_set_func(KEY5, AS_GPIO);
    110. gpio_set_input_en(KEY5, ENABLE);
    111. gpio_setup_up_down_resistor(KEY5, PM_PIN_PULLUP_10K);
    112. #endif
    113. }
    114. u8 key(GPIO_PinTypeDef key)
    115. {
    116. u8 *states;
    117. u8 ret = 0;
    118. static u8 k2_states = 0;
    119. static u8 k3_states = 0;
    120. static u8 k4_states = 0;
    121. static u8 k5_states = 0;
    122. if(key == KEY2)
    123. {
    124. states = &k2_states;
    125. }
    126. if(key == KEY3)
    127. {
    128. states = &k3_states;
    129. }
    130. if(key == KEY4)
    131. {
    132. states = &k4_states;
    133. }
    134. if(key == KEY5)
    135. {
    136. states = &k5_states;
    137. }
    138. switch(*states)
    139. {
    140. case 0:
    141. if(gpio_read(key) == 0)
    142. {
    143. *states = 1;
    144. }
    145. break;
    146. case 1:
    147. if(gpio_read(key) == 0)
    148. {
    149. *states = 2;
    150. ret = 1;
    151. }
    152. else
    153. {
    154. *states = 0;
    155. }
    156. break;
    157. case 2:
    158. if(gpio_read(key) == 0)
    159. {
    160. *states = 2;
    161. ret = 0;
    162. }
    163. else
    164. {
    165. *states = 0;
    166. }
    167. break;
    168. }
    169. //LOG_INFO("ret = %d\r\n", ret);
    170. return ret;
    171. }
    172. int key_event(void)
    173. {
    174. mouse_data_t buf = {0};
    175. if(key(KEY2) == 1)
    176. {
    177. LOG_INFO("key2 press\r\n");
    178. buf.btn = 1;
    179. //LOG_INFO("ble state = %d\r\n",blc_gatt_pushHandleValueNotify (BLS_CONN_HANDLE, HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, 6));
    180. LOG_INFO("ble state = %d\r\n",bls_att_pushNotifyData (HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, sizeof(mouse_data_t)));
    181. //bls_ll_terminateConnection(HCI_ERR_REMOTE_USER_TERM_CONN);
    182. }
    183. if(key(KEY3) == 1)
    184. {
    185. LOG_INFO("key3 press\r\n");
    186. buf.x = 10;
    187. //LOG_INFO("ble state = %d\r\n",blc_gatt_pushHandleValueNotify (BLS_CONN_HANDLE, HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, 6));
    188. LOG_INFO("ble state = %d\r\n",bls_att_pushNotifyData (HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, sizeof(mouse_data_t)));
    189. //bls_ll_terminateConnection(HCI_ERR_REMOTE_USER_TERM_CONN);
    190. }
    191. if(key(KEY4) == 1)
    192. {
    193. LOG_INFO("key4 press\r\n");
    194. buf.y = 20;
    195. //LOG_INFO("ble state = %d\r\n",blc_gatt_pushHandleValueNotify (BLS_CONN_HANDLE, HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, 6));
    196. LOG_INFO("ble state = %d\r\n",bls_att_pushNotifyData (HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, sizeof(mouse_data_t)));
    197. //bls_ll_terminateConnection(HCI_ERR_REMOTE_USER_TERM_CONN);
    198. }
    199. #if 1
    200. if(key(KEY5) == 1)
    201. {
    202. LOG_INFO("key5 press\r\n");
    203. buf.wheel = 4;
    204. //LOG_INFO("ble state = %d\r\n",blc_gatt_pushHandleValueNotify (BLS_CONN_HANDLE, HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, 6));
    205. LOG_INFO("ble state = %d\r\n",bls_att_pushNotifyData (HID_MOUSE_REPORT_INPUT_DP_H, &buf.btn, sizeof(mouse_data_t)));
    206. //bls_ll_terminateConnection(HCI_ERR_REMOTE_USER_TERM_CONN);
    207. }
    208. #endif
    209. #if 0
    210. temp++;
    211. if(temp > 100)
    212. {
    213. LOG_INFO("每1s打印一次\r\n");
    214. temp = 0;
    215. }
    216. #endif
    217. return 0;
    218. }
    219. int led_event(void)
    220. {
    221. #if 1
    222. static u8 temp = 0;
    223. temp++;
    224. if(temp % 4 == 1)
    225. {
    226. gpio_toggle(LED1);
    227. }
    228. if(temp % 4 == 2)
    229. {
    230. gpio_toggle(LED2);
    231. }
    232. if(temp % 4 == 3)
    233. {
    234. gpio_toggle(LED3);
    235. }
    236. if(temp % 4 == 4)
    237. {
    238. gpio_toggle(LED4);
    239. temp = 0;
    240. }
    241. #endif
    242. }
    243. #if (UI_BUTTON_ENABLE)
    244. /
    245. #define MAX_BTN_SIZE 2
    246. #define BTN_VALID_LEVEL 0
    247. #define USER_BTN_1 0x01
    248. #define USER_BTN_2 0x02
    249. _attribute_data_retention_ u32 ctrl_btn[] = {SW1_GPIO, SW2_GPIO};
    250. _attribute_data_retention_ u8 btn_map[MAX_BTN_SIZE] = {USER_BTN_1, USER_BTN_2};
    251. _attribute_data_retention_ int button_not_released = 0;
    252. _attribute_data_retention_ static int button0_press_flag;
    253. _attribute_data_retention_ static u32 button0_press_tick;
    254. _attribute_data_retention_ static int button1_press_flag;
    255. _attribute_data_retention_ static u32 button1_press_tick;
    256. _attribute_data_retention_ static int consumer_report = 0;
    257. typedef struct{
    258. u8 cnt; //count button num
    259. u8 btn_press;
    260. u8 keycode[MAX_BTN_SIZE]; //6 btn
    261. }vc_data_t;
    262. _attribute_data_retention_ vc_data_t vc_event;
    263. typedef struct{
    264. u8 btn_history[4]; //vc history btn save
    265. u8 btn_filter_last;
    266. u8 btn_not_release;
    267. u8 btn_new; //new btn flag
    268. }btn_status_t;
    269. _attribute_data_retention_ btn_status_t btn_status;
    270. /**
    271. * @brief the function server to debounce the key
    272. * @param[in] btn_v - the pointer ponit to the button press value
    273. * @return 1 - key change press effect
    274. * 0 - key change press no effect
    275. */
    276. u8 btn_debounce_filter(u8 *btn_v)
    277. {
    278. u8 change = 0;
    279. for(int i=3; i>0; i--){
    280. btn_status.btn_history[i] = btn_status.btn_history[i-1];
    281. }
    282. btn_status.btn_history[0] = *btn_v;
    283. if( btn_status.btn_history[0] == btn_status.btn_history[1] && btn_status.btn_history[1] == btn_status.btn_history[2] && \
    284. btn_status.btn_history[0] != btn_status.btn_filter_last ){
    285. change = 1;
    286. btn_status.btn_filter_last = btn_status.btn_history[0];
    287. }
    288. return change;
    289. }
    290. /**
    291. * @brief the function detect wheather or not the key press/release
    292. * @param[in] read_key - enable or diable store key value in buffer
    293. * @return 1 - key change press or release
    294. * 0 - key no change
    295. */
    296. u8 vc_detect_button(int read_key)
    297. {
    298. u8 btn_changed, i;
    299. memset(&vc_event,0,sizeof(vc_data_t)); //clear vc_event
    300. //vc_event.btn_press = 0;
    301. for(i=0; i
    302. if(BTN_VALID_LEVEL != !gpio_read(ctrl_btn[i])){
    303. vc_event.btn_press |= BIT(i);
    304. }
    305. }
    306. btn_changed = btn_debounce_filter(&vc_event.btn_press);
    307. if(btn_changed && read_key){
    308. for(i=0; i
    309. if(vc_event.btn_press & BIT(i)){
    310. vc_event.keycode[vc_event.cnt++] = btn_map[i];
    311. }
    312. }
    313. return 1;
    314. }
    315. return 0;
    316. }
    317. /**
    318. * @brief this function is used to detect if button pressed or released.
    319. * @param[in] e - event type when this function is triggered by LinkLayer event
    320. * @param[in] p - event callback data pointer for when this function is triggered by LinkLayer event
    321. * @param[in] n - event callback data length when this function is triggered by LinkLayer event
    322. * @return none
    323. */
    324. void proc_button (u8 e, u8 *p, int n)
    325. {
    326. int det_key = vc_detect_button (1);
    327. if (det_key) //key change: press or release
    328. {
    329. extern u32 latest_user_event_tick;
    330. latest_user_event_tick = clock_time();
    331. u8 key0 = vc_event.keycode[0];
    332. // u8 key1 = vc_event.keycode[1];
    333. button_not_released = 1;
    334. if(vc_event.cnt == 2) //two key press
    335. {
    336. }
    337. else if(vc_event.cnt == 1) //one key press
    338. {
    339. if(key0 == USER_BTN_1)
    340. {
    341. button0_press_flag = 1;
    342. button0_press_tick = clock_time();
    343. //send "Vol+"
    344. u16 consumer_key = MKEY_VOL_UP;
    345. blc_gatt_pushHandleValueNotify (BLS_CONN_HANDLE, HID_CONSUME_REPORT_INPUT_DP_H, (u8 *)&consumer_key, 2);
    346. consumer_report = 1;
    347. }
    348. else if(key0 == USER_BTN_2)
    349. {
    350. button1_press_flag = 1;
    351. button1_press_tick = clock_time();
    352. //send "Vol-"
    353. u16 consumer_key = MKEY_VOL_DN;
    354. blc_gatt_pushHandleValueNotify (BLS_CONN_HANDLE, HID_CONSUME_REPORT_INPUT_DP_H, (u8 *)&consumer_key, 2);
    355. consumer_report = 1;
    356. }
    357. }
    358. else{ //release
    359. button_not_released = 0;
    360. button0_press_flag = 0;
    361. button1_press_flag = 0;
    362. //send release Vol+/Vol-
    363. if(consumer_report){
    364. consumer_report = 0;
    365. u16 consumer_key = 0;
    366. blc_gatt_pushHandleValueNotify (BLS_CONN_HANDLE, HID_CONSUME_REPORT_INPUT_DP_H, (u8 *)&consumer_key, 2);
    367. }
    368. }
    369. }
    370. }
    371. #endif //end of UI_BUTTON_ENABLE

    6. main.c

    1. /********************************************************************************************************
    2. * @file main.c
    3. *
    4. * @brief This is the source file for BLE SDK
    5. *
    6. * @author BLE GROUP
    7. * @date 06,2020
    8. *
    9. * @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
    10. * All rights reserved.
    11. *
    12. * Redistribution and use in source and binary forms, with or without
    13. * modification, are permitted provided that the following conditions are met:
    14. *
    15. * 1. Redistributions of source code must retain the above copyright
    16. * notice, this list of conditions and the following disclaimer.
    17. *
    18. * 2. Unless for usage inside a TELINK integrated circuit, redistributions
    19. * in binary form must reproduce the above copyright notice, this list of
    20. * conditions and the following disclaimer in the documentation and/or other
    21. * materials provided with the distribution.
    22. *
    23. * 3. Neither the name of TELINK, nor the names of its contributors may be
    24. * used to endorse or promote products derived from this software without
    25. * specific prior written permission.
    26. *
    27. * 4. This software, with or without modification, must only be used with a
    28. * TELINK integrated circuit. All other usages are subject to written permission
    29. * from TELINK and different commercial license may apply.
    30. *
    31. * 5. Licensee shall be solely responsible for any claim to the extent arising out of or
    32. * relating to such deletion(s), modification(s) or alteration(s).
    33. *
    34. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    35. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    36. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    37. * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
    38. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    39. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    41. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    43. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    44. *
    45. *******************************************************************************************************/
    46. #include "tl_common.h"
    47. #include "drivers.h"
    48. #include "stack/ble/ble.h"
    49. #include "app.h"
    50. #include "mat_log.h"
    51. //#include "blt_soft_timer.h"
    52. /**
    53. * @brief IRQ handler
    54. * @param none.
    55. * @return none.
    56. */
    57. _attribute_ram_code_ void irq_handler(void)
    58. {
    59. irq_blt_sdk_handler ();
    60. }
    61. void MG_GPIO_Init(void)
    62. {
    63. gpio_set_func(GPIO_PD2,AS_GPIO);
    64. gpio_set_output_en(GPIO_PD2,1);
    65. gpio_write(GPIO_PD2,0);
    66. gpio_set_func(GPIO_PD3,AS_GPIO);
    67. gpio_set_output_en(GPIO_PD3,1);
    68. gpio_write(GPIO_PD3,0);
    69. gpio_set_func(GPIO_PD4,AS_GPIO);
    70. gpio_set_output_en(GPIO_PD4,1);
    71. gpio_write(GPIO_PD4,0);
    72. }
    73. /**
    74. * @brief This is main function
    75. * @param[in] none
    76. * @return none
    77. */
    78. _attribute_ram_code_ int main (void) //must run in ramcode
    79. {
    80. DBG_CHN0_LOW; //debug
    81. blc_pm_select_internal_32k_crystal();
    82. #if(MCU_CORE_TYPE == MCU_CORE_825x)
    83. cpu_wakeup_init();
    84. #elif(MCU_CORE_TYPE == MCU_CORE_827x)
    85. cpu_wakeup_init(LDO_MODE,EXTERNAL_XTAL_24M);
    86. #endif
    87. int deepRetWakeUp = pm_is_MCU_deepRetentionWakeup(); //MCU deep retention wakeUp
    88. rf_drv_init(RF_MODE_BLE_1M);
    89. gpio_init( !deepRetWakeUp ); //analog resistance will keep available in deepSleep mode, so no need initialize again
    90. clock_init(SYS_CLK_TYPE);
    91. if(!deepRetWakeUp){//read flash size
    92. blc_readFlashSize_autoConfigCustomFlashSector();
    93. }
    94. blc_app_loadCustomizedParameters(); //load customized freq_offset cap value
    95. if( deepRetWakeUp ){
    96. user_init_deepRetn ();
    97. }
    98. else{
    99. user_init_normal ();
    100. }
    101. key_init();
    102. led_init();
    103. blt_soft_timer_add(key_event,20000);
    104. blt_soft_timer_add(led_event,500000);
    105. irq_enable();
    106. while (1) {
    107. #if (MODULE_WATCHDOG_ENABLE)
    108. wd_clear(); //clear watch dog
    109. #endif
    110. main_loop ();
    111. blt_soft_timer_process(MAINLOOP_ENTRY);//增加周期处理
    112. }
    113. }

    7. 这里添加一个头文件

    1. /*
    2. * mat_log.h
    3. *
    4. * Created on: 2022-8-16
    5. * Author: YQ19755
    6. */
    7. #ifndef MAT_LOG_H
    8. #define MAT_LOG_H
    9. #include "tl_common.h"
    10. #define DEBUG 1
    11. #define ERR 1
    12. #define INFO 1
    13. #if INFO
    14. #define LOG_INFO(str, ...) printf("[INFO]:"str "\r\n", ##__VA_ARGS__)
    15. #else
    16. #define LOG_INFO(str, ...)
    17. #endif
    18. #if ERR
    19. #define LOG_ERR(str, ...) printf("[ERR]:"str "\r\n", ##__VA_ARGS__)
    20. #else
    21. #define LOG_ERR(str, ...)
    22. #endif
    23. #if DEBUG
    24. #define LOG_DEBUG(str, ...) printf("[DEBUG]:"str "\r\n", ##__VA_ARGS__)
    25. #else
    26. #define LOG_DEBUG(str, ...)
    27. #endif
    28. enum
    29. {
    30. DISABLE = 0,
    31. ENABLE = 1
    32. };
    33. //临时借用这个头文件来测试按键
    34. void key_init(void);
    35. int key_event(void);
    36. int led_event(void);
    37. void led_init(void);
    38. #endif
  • 相关阅读:
    KubeSphere 社区双周报|2024.02.29-03.14
    java计算机毕业设计停车场管理系统源程序+mysql+系统+lw文档+远程调试
    20-SpringCloudAlibaba-2
    C#设计模式---工厂方法模式
    内存管理【C++】
    线性表的顺序表示和实现(Java)
    用C++元编程实现任意函数签名的回调
    Day53【动态规划】1143.最长公共子序列、1035.不相交的线、53.最大子序和
    Docker搭建nginx
    2022年山东省赛总结
  • 原文地址:https://blog.csdn.net/qq_38591801/article/details/126645990