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

源码:

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

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

蓝牙广播格式化

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

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


// Advertising flags (common)
#define ADVERTISE_FLAGS_LENGTH                      2
#define ADVERTISE_FLAGS_TYPE                        0x01// Bit mask for flags advertising data type
#define ADVERTISE_FLAGS_LE_LIMITED_DISCOVERABLE     0x01
#define ADVERTISE_FLAGS_LE_GENERAL_DISCOVERABLE     0x02
#define ADVERTISE_FLAGS_BR_EDR_NOT_SUPPORTED        0x04// Scan Response
#define ADVERTISE_MANDATORY_DATA_LENGTH             5
#define ADVERTISE_MANDATORY_DATA_TYPE_MANUFACTURER  0xFF// Advertise ID
#define ADVERTISE_COMPANY_ID                        0x0047
#define ADVERTISE_FIRMWARE_ID                       0x0000// Complete local name
#define ADVERTISE_TYPE_LOCAL_NAME                   0x09
#define ADVERTISE_DEVICE_NAME_LEN                   10
#define ADVERTISE_DEVICE_NAME                       "Thermostat"// Helper macro
#define UINT16_TO_BYTES(x) { (uint8_t)(x), (uint8_t)((x) >> 8) }// Default advertising scan response parameters
#define ADVERTISE_SCAN_RESPONSE_DEFAULT                                \{                                                                    \.flags_length = ADVERTISE_FLAGS_LENGTH,                            \.flags_type = ADVERTISE_FLAGS_TYPE,                                \.flags = ADVERTISE_FLAGS_LE_GENERAL_DISCOVERABLE                   \| ADVERTISE_FLAGS_BR_EDR_NOT_SUPPORTED,                   \.mandatory_data_length = ADVERTISE_MANDATORY_DATA_LENGTH,          \.mandatory_data_type = ADVERTISE_MANDATORY_DATA_TYPE_MANUFACTURER, \.company_id = UINT16_TO_BYTES(ADVERTISE_COMPANY_ID),               \.firmware_id = UINT16_TO_BYTES(ADVERTISE_FIRMWARE_ID),             \.local_name_length = ADVERTISE_DEVICE_NAME_LEN + 1,                \.local_name_type = ADVERTISE_TYPE_LOCAL_NAME,                      \.local_name = ADVERTISE_DEVICE_NAME                                \}/***************************************************************************//*** @brief*    Structure that holds Scan Response data******************************************************************************/
typedef struct {uint8_t flags_length;          /**< Length of the Flags field. */uint8_t flags_type;            /**< Type of the Flags field. */uint8_t flags;                 /**< Flags field. */uint8_t mandatory_data_length; /**< Length of the mandata field. */uint8_t mandatory_data_type;   /**< Type of the mandata field. */uint8_t company_id[2];         /**< Company ID. */uint8_t firmware_id[2];        /**< Firmware ID */uint8_t local_name_length;     /**< Length of the local name field. */uint8_t local_name_type;       /**< Type of the local name field. */uint8_t local_name[ADVERTISE_DEVICE_NAME_LEN]; /**< Local name field. */
} advertise_scan_response_t;// The advertising set handle allocated from Bluetooth stack.
static uint8_t advertising_set_handle = 0xff;
static const advertise_scan_response_t adv_scan_response= ADVERTISE_SCAN_RESPONSE_DEFAULT;...
...
...
[设置广播数据]
sc = sl_bt_legacy_advertiser_set_data(advertising_set_handle,0,        // 类型是广播,而不是广播回包sizeof(adv_scan_response),(uint8_t *)&adv_scan_response);

蓝牙事件

void sl_bt_on_event(sl_bt_msg_t *evt)
{sl_status_t sc;bd_addr address;uint8_t address_type;uint8_t system_id[8];switch (SL_BT_MSG_ID(evt->header)) {case sl_bt_evt_system_boot_id:
...case sl_bt_evt_connection_opened_id:
...case sl_bt_evt_connection_closed_id:
...///// Add additional event handlers here as your application requires!      /////// -------------------------------// Handle configuration characteristics.case sl_bt_evt_gatt_server_attribute_value_id:break;case sl_bt_evt_gatt_server_user_write_request_id:thermostat_process_evt_gatt_server_user_write_request(&(evt->data.evt_gatt_server_user_write_request));break;case sl_bt_evt_gatt_server_user_read_request_id:thermostat_process_evt_gatt_server_user_read_request(&(evt->data.evt_gatt_server_user_read_request));break;case sl_bt_evt_system_external_signal_id:thermostat_process_evt_external_signal(evt->data.evt_system_external_signal.extsignals);break;case sl_bt_evt_system_soft_timer_id:break;// -------------------------------// Default event handler.default:break;}
}

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

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

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

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

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

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

产生信号的地方:

/***************************************************************************//*** Simple Button* Button state changed callback* @param[in] handle *    Button event handle******************************************************************************/
void sl_button_on_change(const sl_button_t *handle)
{// Button released.if (sl_button_get_state(handle) == SL_SIMPLE_BUTTON_RELEASED) {if (&sl_button_btn0 == handle) {sl_bt_external_signal(AIR_QUALITY_MONITOR_BUTTON_EVENT);}}
}

接收信号:

/***************************************************************************//*** Bluetooth stack event handler.* This overrides the dummy weak implementation.** @param[in] evt Event coming from the Bluetooth stack.******************************************************************************/
void sl_bt_on_event(sl_bt_msg_t *evt)
{sl_status_t sc;bd_addr address;uint8_t address_type;uint8_t system_id[8];switch (SL_BT_MSG_ID(evt->header)) {// -------------------------------// This event indicates the device has started and the radio is ready.// Do not call any stack command before receiving this boot event!case sl_bt_evt_system_boot_id:.....break;// -------------------------------// This event indicates that a new connection was opened.case sl_bt_evt_connection_opened_id:connection_opened_handler(evt);break;// -------------------------------// This event indicates that a connection was closed.case sl_bt_evt_connection_closed_id:connection_closed_handler(evt);break;// -------------------------------// The parameters eventcase sl_bt_evt_connection_parameters_id:connection_parameters_handler(evt);break;// -------------------------------// The confirm_bonding eventcase sl_bt_evt_sm_confirm_bonding_id:sm_confirm_bonding_handler(evt);break;// -------------------------------// This event triggered after the pairing or bonding procedure is// successfully completed.case sl_bt_evt_sm_bonded_id:app_log("Bluetooth Stack Event : BONDED\r\n");break;// -------------------------------// This event is triggered if the pairing or bonding procedure fails.case sl_bt_evt_sm_bonding_failed_id:sm_bonding_failed_handler(evt);break;// Service the gatt server user write request eventcase sl_bt_evt_gatt_server_user_write_request_id:// Service write handlersair_quality_user_write_callback(evt);break;// Service the gatt server user read request eventcase sl_bt_evt_gatt_server_user_read_request_id:air_quality_user_read_callback(evt);break;// -------------------------------// External signal indication (comes from the interrupt handler)case sl_bt_evt_system_external_signal_id:air_quality_process_event(evt->data.evt_system_external_signal.extsignals);break;///// Add additional event handlers here as your application requires!      /////// -------------------------------// Default event handler.default:break;}
}

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

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

【定义】
#define THERMOSTAT_TIMER_EVENT           (1 << 0)
#define THERMOSTAT_BUTTON_EVENT          (1 << 1)【发送】/***************************************************************************//*** Callback on button change.******************************************************************************/
void sl_button_on_change(const sl_button_t *handle)
{if (sl_button_get_state(handle) == SL_SIMPLE_BUTTON_RELEASED) {if (&sl_button_btn0 == handle) {sl_bt_external_signal(THERMOSTAT_BUTTON_EVENT);}}
}/***************************************************************************//*** Callback on timer period.******************************************************************************/
static void thermostat_periodic_timer_callback(sl_sleeptimer_timer_handle_t *timer, void *data)
{(void) timer;(void) data;sl_bt_external_signal(THERMOSTAT_TIMER_EVENT);
}【接收-处理】蓝牙事件处理中:case sl_bt_evt_system_external_signal_id:thermostat_process_evt_external_signal(evt->data.evt_system_external_signal.extsignals);break;/***************************************************************************//*** Thermostat Application Process External Signal.******************************************************************************/
void thermostat_process_evt_external_signal(uint32_t extsignals)
{if (extsignals & THERMOSTAT_TIMER_EVENT) {thermostat_timer_event_handler();}if (extsignals & THERMOSTAT_BUTTON_EVENT) {thermostat_button_event_handler();}
}

关于延时函数

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

/***************************************************************************//*** @brief*    Delay microseconds* @param[in] us*    Microseconds******************************************************************************/
sl_udelay_wait(us)/***************************************************************************//*** @brief*    Delay milliseconds* @param[in] ms*    Milliseconds******************************************************************************/
sl_sleeptimer_delay_millisecond(ms)/***************************************************************************//*** @brief*    Get curent tick count* @return*    Current tick count******************************************************************************/
sl_sleeptimer_get_tick_count()/***************************************************************************//*** @brief*    Get current tick count in milliseconds unit* @return*    Current tick count in milliseconds******************************************************************************/
sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count())

以上信息:

tick与毫秒需要转化;

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

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

/** @brief*   Hardware delay loop** @detail*   This is the hardware specific delay loop. It is designed specifically to*   execute in 4 or 3 cycles for each iteration depending on the architecture.*   Using this information the caller can use the core clock frequency to*   calculate the number of loops required in order to delay a specific time*   period.* * @param[in] n (r0)*   n is the number of loops to execute. Each loop will execute in 4 cycles.*   Note that we assume that r0 > 0, so this invariant should be checked by*   the caller.*/
sli_delay_loop:subs  r0, r0, #1beq   doneb.n   sli_delay_loop
done:bx    lr.end// 以上汇编,摘自驱动文件“sl_udelay_armv6m_gcc.S”void sli_delay_loop(unsigned n);void sl_udelay_wait(unsigned us)
{uint32_t freq_khz;uint32_t ns_period;uint32_t cycles;uint32_t loops;freq_khz = SystemCoreClockGet() / 1000U;if (freq_khz == 0) {EFM_ASSERT(false);return;}ns_period = 1000000U / freq_khz;if (ns_period == 0) {EFM_ASSERT(false);return;}cycles = us * 1000U / ns_period;loops = cycles / HW_LOOP_CYCLE;if (loops > 0U) {sli_delay_loop(loops);}
}

例程中的Flash操作

参考例程的readme:

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

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

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

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

初始化:

// Max and min keys for data objects
#define MODE_KEY                       (NVM3_KEY_MIN)
#define SETPOINT_KEY                   (NVM3_KEY_MIN + 1)
#define HYSTERESIS_KEY                 (NVM3_KEY_MIN + 2)
#define LOWER_THRESHOLD_KEY            (NVM3_KEY_MIN + 3)
#define UPPER_THRESHOLD_KEY            (NVM3_KEY_MIN + 4)
#define THRESHOLD_ALARM_STATUS_KEY     (NVM3_KEY_MIN + 5)#define MODE_DEFAULT                   (0)#define THRESHOLD_ALARM_DEFAULT        (1)
#define SETPOINT_VALUE_MIN             (-3500)
#define SETPOINT_VALUE_MAX             (12000)
#define SETPOINT_VALUE_DEFAULT         (2500)#define HYSTERESIS_VALUE_MIN           (0)
#define HYSTERESIS_VALUE_MAX           (15500)
#define HYSTERESIS_VALUE_DEFAULT       (100)#define LOWER_THRESHOLD_VALUE_MIN      (-3500)
#define LOWER_THRESHOLD_VALUE_MAX      (12000)
#define LOWER_THRESHOLD_VALUE_DEFAULT  (0)#define UPPER_THRESHOLD_VALUE_MIN      (-3500)
#define UPPER_THRESHOLD_VALUE_MAX      (12000)
#define UPPER_THRESHOLD_VALUE_DEFAULT  (5000)// Use the default nvm3 handle from nvm3_default.h
#define NVM3_DEFAULT_HANDLE            nvm3_defaultHandle// nvm3函数的封装
static void conf_data_u8_init(nvm3_ObjectKey_t key,uint8_t min_value,uint8_t max_value,uint8_t default_value)
{Ecode_t err;uint8_t read_value;// check if the designated keys contain data, and initialize if needed.err = conf_data_u8_read(key, &read_value);if ((err == ECODE_NVM3_OK)&& (read_value >= min_value)&& (read_value <= max_value)) {return;} else {nvm3_deleteObject(NVM3_DEFAULT_HANDLE, key);}// Write default valueerr = nvm3_writeData(NVM3_DEFAULT_HANDLE,key,(unsigned char *)&default_value,sizeof(default_value));
}static void conf_data_i16_init(nvm3_ObjectKey_t key,uint16_t min_value,uint16_t max_value,uint16_t default_value)
{Ecode_t err;int16_t read_value;// check if the designated keys contain data, and initialize if needed.err = conf_data_i16_read(key, &read_value);if ((err == ECODE_NVM3_OK)&& (read_value >= min_value)&& (read_value <= max_value)) {return;} else {nvm3_deleteObject(NVM3_DEFAULT_HANDLE, key);}// Write default valueerr = nvm3_writeData(NVM3_DEFAULT_HANDLE,key,(unsigned char *)&default_value,sizeof(default_value));
}/***************************************************************************//*** Initialize NVM3 config.******************************************************************************/
void user_config_nvm3_init(void)
{Ecode_t err;// This will call nvm3_open() with default parameters for// memory base address and size, cache size, etc.err = nvm3_initDefault();EFM_ASSERT(err == ECODE_NVM3_OK);// Initialize the mode config.conf_data_u8_init(MODE_KEY, 0, 1,MODE_DEFAULT);// Initialize the setpoint config.conf_data_i16_init(SETPOINT_KEY,SETPOINT_VALUE_MIN,SETPOINT_VALUE_MAX,SETPOINT_VALUE_DEFAULT);// Initialize the hysteresis config.conf_data_i16_init(HYSTERESIS_KEY,HYSTERESIS_VALUE_MIN,HYSTERESIS_VALUE_MAX,HYSTERESIS_VALUE_DEFAULT);// Initialize the lower threshold config.conf_data_i16_init(LOWER_THRESHOLD_KEY,LOWER_THRESHOLD_VALUE_MIN,LOWER_THRESHOLD_VALUE_MAX,LOWER_THRESHOLD_VALUE_DEFAULT);// Initialize the upper threshold config.conf_data_i16_init(UPPER_THRESHOLD_KEY,UPPER_THRESHOLD_VALUE_MIN,UPPER_THRESHOLD_VALUE_MAX,UPPER_THRESHOLD_VALUE_DEFAULT);// Initialize the notification enable config.conf_data_u8_init(THRESHOLD_ALARM_STATUS_KEY, 0, 2, THRESHOLD_ALARM_DEFAULT);
}/***************************************************************************//*** Application Init.******************************************************************************/
void thermostat_app_init(void)
{oled_app_init();// Load configuration from NVMuser_config_nvm3_init();hysteresis = user_config_nvm3_get_hysteresis() / 100;setpoint = user_config_nvm3_get_setpoint() / 100;lower_threshold = user_config_nvm3_get_lower_threshold() / 100;upper_threshold = user_config_nvm3_get_uppper_threshold() / 100;is_alarm_enable = user_config_nvm3_get_alarm_status();buzz2_app_init();temphum9_app_init();// Create oled display periodic timersl_sleeptimer_start_periodic_timer_ms(&thermostat_timer,5000,thermostat_periodic_timer_callback,NULL,0,0);
}

读取参数:

/***************************************************************************//*** Get NVM3 Setpoint.******************************************************************************/
int16_t user_config_nvm3_get_setpoint(void)
{int16_t setpoint;Ecode_t err;err = conf_data_i16_read(SETPOINT_KEY, &setpoint);app_assert_s(err == ECODE_NVM3_OK);return setpoint;
}

设置参数:

/***************************************************************************//*** Set NVM3 Setpoint.******************************************************************************/
sl_status_t user_config_nvm3_set_setpoint(int16_t setpoint)
{Ecode_t err;if ((setpoint > SETPOINT_VALUE_MAX)|| (setpoint < SETPOINT_VALUE_MIN)) {app_log("Invalid setpoint config: %d\r\n", setpoint);return SL_STATUS_INVALID_RANGE;}err = nvm3_writeData(NVM3_DEFAULT_HANDLE,SETPOINT_KEY,(unsigned char *)&setpoint,sizeof(setpoint));if (ECODE_NVM3_OK == err) {app_log("Stored setpoint config: %d\r\n", setpoint);return SL_STATUS_OK;} else {app_log("Error storing setpoint config\r\n");return SL_STATUS_FAIL;}
}

大数据量的存储需要什么

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模拟的:

static void thermostat_output_control(output_control_t output_control)
{if (output_control == ACTUATOR_ON) {sl_led_turn_on(SL_SIMPLE_LED_INSTANCE(0));} else {sl_led_turn_off(SL_SIMPLE_LED_INSTANCE(0));}
}

温控逻辑在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();       // 空

}

sl_stack_process_action(); --->    sl_bt_step();void sl_bt_step(void)
{sl_bt_msg_t evt;sl_bt_run();        // 运行协议栈状态机uint32_t event_len = sl_bt_event_pending_len();// For preventing from data loss, the event will be kept in the stack's queue// if application cannot process it at the moment.if ((event_len == 0) || (!sl_bt_can_process_event(event_len))) {return;}// Pop (non-blocking) a Bluetooth stack event from event queue.sl_status_t status = sl_bt_pop_event(&evt);if(status != SL_STATUS_OK){return;}sl_bt_process_event(&evt);        // 将协议栈运行的结果传递出来
}// 分发这些事件
void sl_bt_process_event(sl_bt_msg_t *evt)
{sl_gatt_service_device_information_on_event(evt);sl_bt_on_event(evt);
}

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

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

(完)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/114985.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】系统编程线程互斥与同步(C++)

目录 【1】线程互斥 【1.1】进程线程间的互斥相关背景概念 【1.2】互斥量mutex 【1.3】互斥量实现原理探究 【1.4】RAII的加锁风格 【2】可重入VS线程安全 【2.1】概念 【2.2】常见的线程不安全的情况 【2.3】常见的线程安全的情况 【2.4】常见不可重入的情况 【2.5…

Linux之ASCII码表查询tools(五十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

基于复旦微的FMQL45T900全国产化ARM核心模块(100%国产化)

TES745D是一款基于上海复旦微电子FMQL45T900的全国产化ARM核心板。该核心板将复旦微的FMQL45T900&#xff08;与XILINX的XC7Z045-2FFG900I兼容&#xff09;的最小系统集成在了一个87*117mm的核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩展&#xff0c;能…

Flask框架-2-[单聊]: flask-socketio实现websocket的功能,实现单对单聊天,flask实现单聊功能

一、概述和项目结构 在使用flask-socketio实现单聊时&#xff0c;需要将会话id(sid) 与用户进行绑定&#xff0c;通过emit(事件,消息,tosid) ,就可以把消息单独发送给某个用户了。 flask_websocket |--static |--js |--jquery-3.7.0.min.js |--socket.io_4.3.1.js |--template…

实验五 熟悉 Hive 的基本操作

实验环境&#xff1a; 1.操作系统&#xff1a;CentOS 7。 2.Hadoop 版本&#xff1a;3.3.0。 3.Hive 版本&#xff1a;3.1.2。 4.JDK 版本&#xff1a;1.8。 实验内容与完成情况&#xff1a; &#xff08;1&#xff09;创建一个内部表 stocks&#xff0c;字段分隔符为英文逗号…

Linux 中的make/makefile

一&#xff1a;背景 make是一个命令工具&#xff0c;是一个解释makefifile中指令的命令工具&#xff0c;一般来说&#xff0c;大多数的IDE都有这个命令&#xff0c;比如&#xff1a;Delphi的make&#xff0c;Visual C的nmake&#xff0c;Linux下GNU的make。可见&#xff0c;mak…

《Python等级考试(1~6级)历届真题解析》专栏总目录

❤️ 专栏名称&#xff1a;《Python等级考试&#xff08;1~6级&#xff09;历届真题解析》 &#x1f338; 专栏介绍&#xff1a;中国电子学会《全国青少年软件编程等级考试》Python编程&#xff08;1~6级&#xff09;历届真题解析。 &#x1f680; 订阅专栏&#xff1a;订阅后可…

为什么伦敦金获得连续盈利这么难

相信在伦敦金市场中投资的投资者都有这个感受&#xff0c;我们很容易在市场中获取力量利润&#xff0c;但是要长期的在市场中稳定的盈利&#xff0c;持续不断地获利&#xff0c;这对很多投资者来说都有点难&#xff0c;可以这么说&#xff0c;稳定盈利是普通投资者一个阶段性的…

【SpringCloud】微服务技术栈入门2 - Nacos框架与Feign

目录 Nacos下载 Nacos 并运行配置 NacosNacos 集群Nacos 负载均衡Nacos 环境隔离Nacos 注册细节Nacos 更多配置项快速上手自动更新 Feign取代 RestTemplateFeign 自定义配置性能优化 Nacos 下载 Nacos 并运行 首先下载对应的 release 包&#xff0c;主要要选择已经打包编译好…

Vue之vue-cli搭建SPA项目

目录 ​编辑 前言 一、vue-cli简介 1. 什么是vue-cli 2. vue-cli的重要性 3. vue-cli的应用场景 二、Vue-cli搭建SPA项目 1. 构建前提&#xff08;node.js安装完成&#xff09; 2. 安装vue-cli 3. 使用脚手架vue-cli(2.X版)来构建项目 4. 分析创建spa项目的八个问题 …

【深度学习】 Python 和 NumPy 系列教程(十四):Matplotlib详解:1、2d绘图(下):箱线图、热力图、面积图、等高线图、极坐标图

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 0. 设置中文字体 1-5. 折线图、散点图、柱状图、直方图、饼图 6. 箱线图&#xff08;Box Plot&#xff09; 7. 热力图&#xff08;Heatmap&#xff09; 8. 面积图&#xff08;Area Plot&#xff09; 9. 等…

步步为营,如何将GOlang引用库的安全漏洞修干净

文章目录 引场景构建第一步、直接引用的第三方库升级修复策略1.确认是否为直接引用的第三方库2.找到需要升级的版本是否为release版本 第二步、间接引用的第三方库升级修复策略那么问题来了&#xff0c;我们这么间接引用库的对应的直接引用库是哪个呢&#xff1f; &#xff08;…