• 芯科蓝牙BG27开发笔记9-蓝牙温控器例程阅读


    源码:

    https://download.csdn.net/download/hxkrrzq/88353283

    以上源码都是官方资源,可以自行gitbub下载(参见之前笔记)

    蓝牙广播格式化

    之前的笔记中广播数据是直接使用的十六进制字符串,关于这32bytes数据的格式化在这个例程中,有实例代码。

    同时这些例程的readme十分完善。

    1. // Advertising flags (common)
    2. #define ADVERTISE_FLAGS_LENGTH 2
    3. #define ADVERTISE_FLAGS_TYPE 0x01
    4. // Bit mask for flags advertising data type
    5. #define ADVERTISE_FLAGS_LE_LIMITED_DISCOVERABLE 0x01
    6. #define ADVERTISE_FLAGS_LE_GENERAL_DISCOVERABLE 0x02
    7. #define ADVERTISE_FLAGS_BR_EDR_NOT_SUPPORTED 0x04
    8. // Scan Response
    9. #define ADVERTISE_MANDATORY_DATA_LENGTH 5
    10. #define ADVERTISE_MANDATORY_DATA_TYPE_MANUFACTURER 0xFF
    11. // Advertise ID
    12. #define ADVERTISE_COMPANY_ID 0x0047
    13. #define ADVERTISE_FIRMWARE_ID 0x0000
    14. // Complete local name
    15. #define ADVERTISE_TYPE_LOCAL_NAME 0x09
    16. #define ADVERTISE_DEVICE_NAME_LEN 10
    17. #define ADVERTISE_DEVICE_NAME "Thermostat"
    18. // Helper macro
    19. #define UINT16_TO_BYTES(x) { (uint8_t)(x), (uint8_t)((x) >> 8) }
    20. // Default advertising scan response parameters
    21. #define ADVERTISE_SCAN_RESPONSE_DEFAULT \
    22. { \
    23. .flags_length = ADVERTISE_FLAGS_LENGTH, \
    24. .flags_type = ADVERTISE_FLAGS_TYPE, \
    25. .flags = ADVERTISE_FLAGS_LE_GENERAL_DISCOVERABLE \
    26. | ADVERTISE_FLAGS_BR_EDR_NOT_SUPPORTED, \
    27. .mandatory_data_length = ADVERTISE_MANDATORY_DATA_LENGTH, \
    28. .mandatory_data_type = ADVERTISE_MANDATORY_DATA_TYPE_MANUFACTURER, \
    29. .company_id = UINT16_TO_BYTES(ADVERTISE_COMPANY_ID), \
    30. .firmware_id = UINT16_TO_BYTES(ADVERTISE_FIRMWARE_ID), \
    31. .local_name_length = ADVERTISE_DEVICE_NAME_LEN + 1, \
    32. .local_name_type = ADVERTISE_TYPE_LOCAL_NAME, \
    33. .local_name = ADVERTISE_DEVICE_NAME \
    34. }
    35. /***************************************************************************//**
    36. * @brief
    37. * Structure that holds Scan Response data
    38. ******************************************************************************/
    39. typedef struct {
    40. uint8_t flags_length; /**< Length of the Flags field. */
    41. uint8_t flags_type; /**< Type of the Flags field. */
    42. uint8_t flags; /**< Flags field. */
    43. uint8_t mandatory_data_length; /**< Length of the mandata field. */
    44. uint8_t mandatory_data_type; /**< Type of the mandata field. */
    45. uint8_t company_id[2]; /**< Company ID. */
    46. uint8_t firmware_id[2]; /**< Firmware ID */
    47. uint8_t local_name_length; /**< Length of the local name field. */
    48. uint8_t local_name_type; /**< Type of the local name field. */
    49. uint8_t local_name[ADVERTISE_DEVICE_NAME_LEN]; /**< Local name field. */
    50. } advertise_scan_response_t;
    51. // The advertising set handle allocated from Bluetooth stack.
    52. static uint8_t advertising_set_handle = 0xff;
    53. static const advertise_scan_response_t adv_scan_response
    54. = ADVERTISE_SCAN_RESPONSE_DEFAULT;
    55. ...
    56. ...
    57. ...
    58. [设置广播数据]
    59. sc = sl_bt_legacy_advertiser_set_data(advertising_set_handle,
    60. 0, // 类型是广播,而不是广播回包
    61. sizeof(adv_scan_response),
    62. (uint8_t *)&adv_scan_response);

    蓝牙事件

    1. void sl_bt_on_event(sl_bt_msg_t *evt)
    2. {
    3. sl_status_t sc;
    4. bd_addr address;
    5. uint8_t address_type;
    6. uint8_t system_id[8];
    7. switch (SL_BT_MSG_ID(evt->header)) {
    8. case sl_bt_evt_system_boot_id:
    9. ...
    10. case sl_bt_evt_connection_opened_id:
    11. ...
    12. case sl_bt_evt_connection_closed_id:
    13. ...
    14. ///
    15. // Add additional event handlers here as your application requires! //
    16. ///
    17. // -------------------------------
    18. // Handle configuration characteristics.
    19. case sl_bt_evt_gatt_server_attribute_value_id:
    20. break;
    21. case sl_bt_evt_gatt_server_user_write_request_id:
    22. thermostat_process_evt_gatt_server_user_write_request(
    23. &(evt->data.evt_gatt_server_user_write_request));
    24. break;
    25. case sl_bt_evt_gatt_server_user_read_request_id:
    26. thermostat_process_evt_gatt_server_user_read_request(
    27. &(evt->data.evt_gatt_server_user_read_request));
    28. break;
    29. case sl_bt_evt_system_external_signal_id:
    30. thermostat_process_evt_external_signal(
    31. evt->data.evt_system_external_signal.extsignals);
    32. break;
    33. case sl_bt_evt_system_soft_timer_id:
    34. break;
    35. // -------------------------------
    36. // Default event handler.
    37. default:
    38. break;
    39. }
    40. }

    代码中添加了几个蓝牙协议栈的事件,需要搞清楚如何使用:

    sl_bt_evt_gatt_server_attribute_value_id

    最常见的,手机侧写数据后,会进入此事件,识别写入的value,做出相应动作;


    sl_bt_evt_gatt_server_user_write_request_id

    简单阅读api.h函数说明后还是不清楚;

    参考:

    GATT Server and Client Roles,文档中有一段较详细的对比说明:

    这个user的意思是,只上报写事件,不直接处理接收到的数据,这个操作粒度比“sl_bt_evt_gatt_server_attribute_value”事件更细。

    sl_bt_evt_gatt_server_user_read_request_id

    同上


    sl_bt_evt_system_soft_timer_id

    api文档中:

    提示了,此函数超时后会发出此事件。

    sl_bt_evt_system_external_signal_id

    外部信号是什么?关键的是何时会产生此事件?

    同样的搜索api.h和官方帮助文档,都不能获得有效信息,只是说这是外部信号,没找到怎么使用,猜测它是可以自定义的。

    继续寻找……

    根据api.h文档可以找到“sl_bt_evt_system_external_signal_t”结构体,在接口文档中公开,必然有些地方会使用它。

    检索整个蓝牙例程包,发现有多个例程用到了:

    具体可以参考例程:bluetooth_ethernet_gateway

    最后找到发送信号的函数:

    1. /**
    2. * @brief Signal the Bluetooth stack that an external event has happened.
    3. *
    4. * Signals can be used to report status changes from interrupt context or from
    5. * other threads to application. Signals are bits that are automatically cleared
    6. * after application has been notified.
    7. *
    8. * If the Platform Core Interrupt API has been configured to use the
    9. * CORE_ATOMIC_METHOD_BASEPRI as the implementation method of atomic sections,
    10. * this function must not be called from an interrupt handler with a priority
    11. * higher than CORE_ATOMIC_BASE_PRIORITY_LEVEL.
    12. *
    13. * @param signals is a bitmask defining active signals that are reported back to
    14. * the application by system_external_signal-event.
    15. * @return SL_STATUS_OK if the operation is successful,
    16. * SL_STATUS_NO_MORE_RESOURCE indicating the request could not be processed
    17. * due to resource limitation at the moment, or SL_STATUS_INVALID_STATE when
    18. * the on-demand start feature is used and the stack is currently stopped.
    19. */
    20. sl_status_t sl_bt_external_signal(uint32_t signals);

    反过来,检索sl_bt_external_signal,看看那些地方用到了,怎么使用的。

    结果是很多例程使用到了。选“bluetooth_air_quality_monitor”看看:

    产生信号的地方:

    1. /***************************************************************************//**
    2. * Simple Button
    3. * Button state changed callback
    4. * @param[in] handle
    5. * Button event handle
    6. ******************************************************************************/
    7. void sl_button_on_change(const sl_button_t *handle)
    8. {
    9. // Button released.
    10. if (sl_button_get_state(handle) == SL_SIMPLE_BUTTON_RELEASED) {
    11. if (&sl_button_btn0 == handle) {
    12. sl_bt_external_signal(AIR_QUALITY_MONITOR_BUTTON_EVENT);
    13. }
    14. }
    15. }

    接收信号:

    1. /***************************************************************************//**
    2. * Bluetooth stack event handler.
    3. * This overrides the dummy weak implementation.
    4. *
    5. * @param[in] evt Event coming from the Bluetooth stack.
    6. ******************************************************************************/
    7. void sl_bt_on_event(sl_bt_msg_t *evt)
    8. {
    9. sl_status_t sc;
    10. bd_addr address;
    11. uint8_t address_type;
    12. uint8_t system_id[8];
    13. switch (SL_BT_MSG_ID(evt->header)) {
    14. // -------------------------------
    15. // This event indicates the device has started and the radio is ready.
    16. // Do not call any stack command before receiving this boot event!
    17. case sl_bt_evt_system_boot_id:
    18. .....
    19. break;
    20. // -------------------------------
    21. // This event indicates that a new connection was opened.
    22. case sl_bt_evt_connection_opened_id:
    23. connection_opened_handler(evt);
    24. break;
    25. // -------------------------------
    26. // This event indicates that a connection was closed.
    27. case sl_bt_evt_connection_closed_id:
    28. connection_closed_handler(evt);
    29. break;
    30. // -------------------------------
    31. // The parameters event
    32. case sl_bt_evt_connection_parameters_id:
    33. connection_parameters_handler(evt);
    34. break;
    35. // -------------------------------
    36. // The confirm_bonding event
    37. case sl_bt_evt_sm_confirm_bonding_id:
    38. sm_confirm_bonding_handler(evt);
    39. break;
    40. // -------------------------------
    41. // This event triggered after the pairing or bonding procedure is
    42. // successfully completed.
    43. case sl_bt_evt_sm_bonded_id:
    44. app_log("Bluetooth Stack Event : BONDED\r\n");
    45. break;
    46. // -------------------------------
    47. // This event is triggered if the pairing or bonding procedure fails.
    48. case sl_bt_evt_sm_bonding_failed_id:
    49. sm_bonding_failed_handler(evt);
    50. break;
    51. // Service the gatt server user write request event
    52. case sl_bt_evt_gatt_server_user_write_request_id:
    53. // Service write handlers
    54. air_quality_user_write_callback(evt);
    55. break;
    56. // Service the gatt server user read request event
    57. case sl_bt_evt_gatt_server_user_read_request_id:
    58. air_quality_user_read_callback(evt);
    59. break;
    60. // -------------------------------
    61. // External signal indication (comes from the interrupt handler)
    62. case sl_bt_evt_system_external_signal_id:
    63. air_quality_process_event(evt->data.evt_system_external_signal.extsignals);
    64. break;
    65. ///
    66. // Add additional event handlers here as your application requires! //
    67. ///
    68. // -------------------------------
    69. // Default event handler.
    70. default:
    71. break;
    72. }
    73. }

    可知“bluetooth_air_quality_monitor”是一个主机应用,暂且记录下来,以后再去看这些event的用法。

    回到【bluetooth_thermostat】应用,关于系统外部信号:

    1. 【定义】
    2. #define THERMOSTAT_TIMER_EVENT (1 << 0)
    3. #define THERMOSTAT_BUTTON_EVENT (1 << 1)
    4. 【发送】
    5. /***************************************************************************//**
    6. * Callback on button change.
    7. ******************************************************************************/
    8. void sl_button_on_change(const sl_button_t *handle)
    9. {
    10. if (sl_button_get_state(handle) == SL_SIMPLE_BUTTON_RELEASED) {
    11. if (&sl_button_btn0 == handle) {
    12. sl_bt_external_signal(THERMOSTAT_BUTTON_EVENT);
    13. }
    14. }
    15. }
    16. /***************************************************************************//**
    17. * Callback on timer period.
    18. ******************************************************************************/
    19. static void thermostat_periodic_timer_callback(
    20. sl_sleeptimer_timer_handle_t *timer, void *data)
    21. {
    22. (void) timer;
    23. (void) data;
    24. sl_bt_external_signal(THERMOSTAT_TIMER_EVENT);
    25. }
    26. 【接收-处理】
    27. 蓝牙事件处理中:
    28. case sl_bt_evt_system_external_signal_id:
    29. thermostat_process_evt_external_signal(
    30. evt->data.evt_system_external_signal.extsignals);
    31. break;
    32. /***************************************************************************//**
    33. * Thermostat Application Process External Signal.
    34. ******************************************************************************/
    35. void thermostat_process_evt_external_signal(uint32_t extsignals)
    36. {
    37. if (extsignals & THERMOSTAT_TIMER_EVENT) {
    38. thermostat_timer_event_handler();
    39. }
    40. if (extsignals & THERMOSTAT_BUTTON_EVENT) {
    41. thermostat_button_event_handler();
    42. }
    43. }

    关于延时函数

    在程序包中可以看到多个与延时相关的函数:

    1. /***************************************************************************//**
    2. * @brief
    3. * Delay microseconds
    4. * @param[in] us
    5. * Microseconds
    6. ******************************************************************************/
    7. sl_udelay_wait(us)
    8. /***************************************************************************//**
    9. * @brief
    10. * Delay milliseconds
    11. * @param[in] ms
    12. * Milliseconds
    13. ******************************************************************************/
    14. sl_sleeptimer_delay_millisecond(ms)
    15. /***************************************************************************//**
    16. * @brief
    17. * Get curent tick count
    18. * @return
    19. * Current tick count
    20. ******************************************************************************/
    21. sl_sleeptimer_get_tick_count()
    22. /***************************************************************************//**
    23. * @brief
    24. * Get current tick count in milliseconds unit
    25. * @return
    26. * Current tick count in milliseconds
    27. ******************************************************************************/
    28. sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count())

    以上信息:

    tick与毫秒需要转化;

    sl_udelay_wait似乎不存在?看看是否需要安装组件?

    安装完成后,自动添加了微秒延时的代码了(参见下文),注意这是基于核心频率实现的,与ms不同,他是使用sleeptimer时钟实现。

    1. /*
    2. * @brief
    3. * Hardware delay loop
    4. *
    5. * @detail
    6. * This is the hardware specific delay loop. It is designed specifically to
    7. * execute in 4 or 3 cycles for each iteration depending on the architecture.
    8. * Using this information the caller can use the core clock frequency to
    9. * calculate the number of loops required in order to delay a specific time
    10. * period.
    11. *
    12. * @param[in] n (r0)
    13. * n is the number of loops to execute. Each loop will execute in 4 cycles.
    14. * Note that we assume that r0 > 0, so this invariant should be checked by
    15. * the caller.
    16. */
    17. sli_delay_loop:
    18. subs r0, r0, #1
    19. beq done
    20. b.n sli_delay_loop
    21. done:
    22. bx lr
    23. .end
    24. // 以上汇编,摘自驱动文件“sl_udelay_armv6m_gcc.S”
    25. void sli_delay_loop(unsigned n);
    26. void sl_udelay_wait(unsigned us)
    27. {
    28. uint32_t freq_khz;
    29. uint32_t ns_period;
    30. uint32_t cycles;
    31. uint32_t loops;
    32. freq_khz = SystemCoreClockGet() / 1000U;
    33. if (freq_khz == 0) {
    34. EFM_ASSERT(false);
    35. return;
    36. }
    37. ns_period = 1000000U / freq_khz;
    38. if (ns_period == 0) {
    39. EFM_ASSERT(false);
    40. return;
    41. }
    42. cycles = us * 1000U / ns_period;
    43. loops = cycles / HW_LOOP_CYCLE;
    44. if (loops > 0U) {
    45. sli_delay_loop(loops);
    46. }
    47. }

    例程中的Flash操作

    参考例程的readme:

    上电之后,首先初始化数据,从flash读取,如果没有数据则将其设置为缺省值;

    读参数操作是在gatt属性读的处理代码中;

    写参数操作是在gatt属性写的处理代码中;

    代码对nvm3的原生函数进行了一层封装,加入了最大最小值的判定。

    初始化:

    1. // Max and min keys for data objects
    2. #define MODE_KEY (NVM3_KEY_MIN)
    3. #define SETPOINT_KEY (NVM3_KEY_MIN + 1)
    4. #define HYSTERESIS_KEY (NVM3_KEY_MIN + 2)
    5. #define LOWER_THRESHOLD_KEY (NVM3_KEY_MIN + 3)
    6. #define UPPER_THRESHOLD_KEY (NVM3_KEY_MIN + 4)
    7. #define THRESHOLD_ALARM_STATUS_KEY (NVM3_KEY_MIN + 5)
    8. #define MODE_DEFAULT (0)
    9. #define THRESHOLD_ALARM_DEFAULT (1)
    10. #define SETPOINT_VALUE_MIN (-3500)
    11. #define SETPOINT_VALUE_MAX (12000)
    12. #define SETPOINT_VALUE_DEFAULT (2500)
    13. #define HYSTERESIS_VALUE_MIN (0)
    14. #define HYSTERESIS_VALUE_MAX (15500)
    15. #define HYSTERESIS_VALUE_DEFAULT (100)
    16. #define LOWER_THRESHOLD_VALUE_MIN (-3500)
    17. #define LOWER_THRESHOLD_VALUE_MAX (12000)
    18. #define LOWER_THRESHOLD_VALUE_DEFAULT (0)
    19. #define UPPER_THRESHOLD_VALUE_MIN (-3500)
    20. #define UPPER_THRESHOLD_VALUE_MAX (12000)
    21. #define UPPER_THRESHOLD_VALUE_DEFAULT (5000)
    22. // Use the default nvm3 handle from nvm3_default.h
    23. #define NVM3_DEFAULT_HANDLE nvm3_defaultHandle
    24. // nvm3函数的封装
    25. static void conf_data_u8_init(nvm3_ObjectKey_t key,
    26. uint8_t min_value,
    27. uint8_t max_value,
    28. uint8_t default_value)
    29. {
    30. Ecode_t err;
    31. uint8_t read_value;
    32. // check if the designated keys contain data, and initialize if needed.
    33. err = conf_data_u8_read(key, &read_value);
    34. if ((err == ECODE_NVM3_OK)
    35. && (read_value >= min_value)
    36. && (read_value <= max_value)) {
    37. return;
    38. } else {
    39. nvm3_deleteObject(NVM3_DEFAULT_HANDLE, key);
    40. }
    41. // Write default value
    42. err = nvm3_writeData(NVM3_DEFAULT_HANDLE,
    43. key,
    44. (unsigned char *)&default_value,
    45. sizeof(default_value));
    46. }
    47. static void conf_data_i16_init(nvm3_ObjectKey_t key,
    48. uint16_t min_value,
    49. uint16_t max_value,
    50. uint16_t default_value)
    51. {
    52. Ecode_t err;
    53. int16_t read_value;
    54. // check if the designated keys contain data, and initialize if needed.
    55. err = conf_data_i16_read(key, &read_value);
    56. if ((err == ECODE_NVM3_OK)
    57. && (read_value >= min_value)
    58. && (read_value <= max_value)) {
    59. return;
    60. } else {
    61. nvm3_deleteObject(NVM3_DEFAULT_HANDLE, key);
    62. }
    63. // Write default value
    64. err = nvm3_writeData(NVM3_DEFAULT_HANDLE,
    65. key,
    66. (unsigned char *)&default_value,
    67. sizeof(default_value));
    68. }
    69. /***************************************************************************//**
    70. * Initialize NVM3 config.
    71. ******************************************************************************/
    72. void user_config_nvm3_init(void)
    73. {
    74. Ecode_t err;
    75. // This will call nvm3_open() with default parameters for
    76. // memory base address and size, cache size, etc.
    77. err = nvm3_initDefault();
    78. EFM_ASSERT(err == ECODE_NVM3_OK);
    79. // Initialize the mode config.
    80. conf_data_u8_init(MODE_KEY, 0, 1,
    81. MODE_DEFAULT);
    82. // Initialize the setpoint config.
    83. conf_data_i16_init(SETPOINT_KEY,
    84. SETPOINT_VALUE_MIN,
    85. SETPOINT_VALUE_MAX,
    86. SETPOINT_VALUE_DEFAULT);
    87. // Initialize the hysteresis config.
    88. conf_data_i16_init(HYSTERESIS_KEY,
    89. HYSTERESIS_VALUE_MIN,
    90. HYSTERESIS_VALUE_MAX,
    91. HYSTERESIS_VALUE_DEFAULT);
    92. // Initialize the lower threshold config.
    93. conf_data_i16_init(LOWER_THRESHOLD_KEY,
    94. LOWER_THRESHOLD_VALUE_MIN,
    95. LOWER_THRESHOLD_VALUE_MAX,
    96. LOWER_THRESHOLD_VALUE_DEFAULT);
    97. // Initialize the upper threshold config.
    98. conf_data_i16_init(UPPER_THRESHOLD_KEY,
    99. UPPER_THRESHOLD_VALUE_MIN,
    100. UPPER_THRESHOLD_VALUE_MAX,
    101. UPPER_THRESHOLD_VALUE_DEFAULT);
    102. // Initialize the notification enable config.
    103. conf_data_u8_init(THRESHOLD_ALARM_STATUS_KEY, 0, 2, THRESHOLD_ALARM_DEFAULT);
    104. }
    105. /***************************************************************************//**
    106. * Application Init.
    107. ******************************************************************************/
    108. void thermostat_app_init(void)
    109. {
    110. oled_app_init();
    111. // Load configuration from NVM
    112. user_config_nvm3_init();
    113. hysteresis = user_config_nvm3_get_hysteresis() / 100;
    114. setpoint = user_config_nvm3_get_setpoint() / 100;
    115. lower_threshold = user_config_nvm3_get_lower_threshold() / 100;
    116. upper_threshold = user_config_nvm3_get_uppper_threshold() / 100;
    117. is_alarm_enable = user_config_nvm3_get_alarm_status();
    118. buzz2_app_init();
    119. temphum9_app_init();
    120. // Create oled display periodic timer
    121. sl_sleeptimer_start_periodic_timer_ms(&thermostat_timer,
    122. 5000,
    123. thermostat_periodic_timer_callback,
    124. NULL,
    125. 0,
    126. 0);
    127. }

    读取参数:

    1. /***************************************************************************//**
    2. * Get NVM3 Setpoint.
    3. ******************************************************************************/
    4. int16_t user_config_nvm3_get_setpoint(void)
    5. {
    6. int16_t setpoint;
    7. Ecode_t err;
    8. err = conf_data_i16_read(SETPOINT_KEY, &setpoint);
    9. app_assert_s(err == ECODE_NVM3_OK);
    10. return setpoint;
    11. }

    设置参数:

    1. /***************************************************************************//**
    2. * Set NVM3 Setpoint.
    3. ******************************************************************************/
    4. sl_status_t user_config_nvm3_set_setpoint(int16_t setpoint)
    5. {
    6. Ecode_t err;
    7. if ((setpoint > SETPOINT_VALUE_MAX)
    8. || (setpoint < SETPOINT_VALUE_MIN)) {
    9. app_log("Invalid setpoint config: %d\r\n", setpoint);
    10. return SL_STATUS_INVALID_RANGE;
    11. }
    12. err = nvm3_writeData(NVM3_DEFAULT_HANDLE,
    13. SETPOINT_KEY,
    14. (unsigned char *)&setpoint,
    15. sizeof(setpoint));
    16. if (ECODE_NVM3_OK == err) {
    17. app_log("Stored setpoint config: %d\r\n", setpoint);
    18. return SL_STATUS_OK;
    19. } else {
    20. app_log("Error storing setpoint config\r\n");
    21. return SL_STATUS_FAIL;
    22. }
    23. }

    大数据量的存储需要什么

    flash的操作可以照抄上边的代码。但是还缺少一些基础知识,参考《笔记8》

    1. 正确配置nvm3组件参数

    缓存大小(Cache Size):减少了键值检索的时间,需要大于等于flash中存在的所有数据对象的数目,包含删除的对象(删除后没有擦除page时)。一个cache占用8byte的RAM,在大数据量多条目存储时,不可能有这么大的ram。

    最大数据对象的大小:208 -4096 bytes.。这个影响了nvm3的通用开销。
    Flash垃圾回收(Flash repack)的剩余空间阈值:默认值0,和强制垃圾回收的阈值相同,此处不需要自定义。

    存储空间大小:单位是byte,最小值是3个page

    2. nvm3只有键值对,对key的要求是固定的,必须在0x00000 - 0x0FFFF之内,所以存储的数据条目最多65536条。目前产品设计需求,每分钟一条数据,此处限制了最多45天。

    按照500K的存储空间计算,8byte数据长度,可以存多少条数据?

    数据object的数据大小事8byte,但是每个对象的开销还有4byte,共12byte。

    忽略整个数据的一些共同开销,大约只有几百byte。可以算出500*1024/12 = 42666个

    折算到可以存储26天的数据。

    3. 需要提供的功能:

    数据记录写函数;

    数据记录读函数;

    能够查找、定位到当前最新的数据,即使重新上电,也能继续记录,这可能需要用到:

    nvm3_countObjects()

    nvm3_enumObjects()

    具体参考:

    《nvm3_generic.h》,头文件的最后较大篇幅的使用文档。

    蓝牙温控器其他代码

    回到主题,蓝牙温控器还有哪些代码值得关注?

    该例程最后的温控执行器是用LED模拟的:

    1. static void thermostat_output_control(output_control_t output_control)
    2. {
    3. if (output_control == ACTUATOR_ON) {
    4. sl_led_turn_on(SL_SIMPLE_LED_INSTANCE(0));
    5. } else {
    6. sl_led_turn_off(SL_SIMPLE_LED_INSTANCE(0));
    7. }
    8. }

    温控逻辑在readme中有流程图,这里不需具体分析,控制流所在函数:

    static void thermostat_data_process(void)

    温控器的传感器数据采集:

     mikroe_shtc3_get_temperature_and_humidity(SHTC3_DATA_MODE_NORMAL, &measurement_value);

    以上数据采集,温度控制都放在了定时器的回调中:

    static void thermostat_timer_event_handler(void)

    值得关注的是,该函数并非直接注册为软件定时器的回调函数,这个定时器循环只负责发送“外部信号”,外部信号触发了协议栈的事件,循环执行的代码是在蓝牙协议栈的【sl_bt_evt_system_external_signal_id】处理中进行的,所以此处的疑问是,为什么要兜一圈放到蓝牙协议栈的事件处理中,而不是直接在app_time的定时调用?

    需要搞清楚main函数中调用的sl_system_process_action();

    void sl_system_process_action(void)

    {

      sl_platform_process_action();       // 空

      sl_service_process_action();       // app_timer此中执行

      sl_stack_process_action();       // 蓝牙协议栈此中执行

      sl_internal_app_process_action();       // 空

    }

    1. sl_stack_process_action(); ---> sl_bt_step();
    2. void sl_bt_step(void)
    3. {
    4. sl_bt_msg_t evt;
    5. sl_bt_run(); // 运行协议栈状态机
    6. uint32_t event_len = sl_bt_event_pending_len();
    7. // For preventing from data loss, the event will be kept in the stack's queue
    8. // if application cannot process it at the moment.
    9. if ((event_len == 0) || (!sl_bt_can_process_event(event_len))) {
    10. return;
    11. }
    12. // Pop (non-blocking) a Bluetooth stack event from event queue.
    13. sl_status_t status = sl_bt_pop_event(&evt);
    14. if(status != SL_STATUS_OK){
    15. return;
    16. }
    17. sl_bt_process_event(&evt); // 将协议栈运行的结果传递出来
    18. }
    19. // 分发这些事件
    20. void sl_bt_process_event(sl_bt_msg_t *evt)
    21. {
    22. sl_gatt_service_device_information_on_event(evt);
    23. sl_bt_on_event(evt);
    24. }

      sl_service_process_action()中运行了所有的软件定时器的定时任务。接下来才会进入协议栈的运行和事件分发。相对来说协议栈的事件更加严谨可靠,猜测,协议栈内部的优先级处理可以保证关键蓝牙任务的执行,不会优先响应这个外部信号的事件。如果放到sl_service_process_action()直接运行,那可能会影响蓝牙协议栈的实时性。

    总而言之,这个操作如果真有用的话,那一定体现在把何时执行定时任务的控制权全部交给蓝牙协议栈统筹。

    补充231026:最为关键的原因是为了满足低功耗,导致此处计时操作需要在低功耗状态运行,计时完成后发出中断,中断的后续处理则进入蓝牙状态机,跳出低功耗,使用核心时钟,运行业务代码,然后跟随统一管理,再次进入低功耗。

  • 相关阅读:
    nlp之文本转向量
    Android 引入FFmpeg
    C语言实现面向对象编程 | 干货
    【系统架构设计师】第四章 计算机网络
    大模型产品化第一年​:战术、运营与战略
    transformers架构实现
    图像领域-深度学习网络结构(从浅入深)——基础到对比到改进
    LeetCode:第305场周赛【总结】
    RT-DETR手把手教程,注意力机制如何添加在网络的不同位置进行创新优化,EMA注意力为案列
    浅谈城市轨道交通视频监控与AI视频智能分析解决方案
  • 原文地址:https://blog.csdn.net/hxkrrzq/article/details/132985255