当前位置:网站首页>[analysis notes] source code analysis of siliconlabs efr32bg22 Bluetooth mesh sensorclient
[analysis notes] source code analysis of siliconlabs efr32bg22 Bluetooth mesh sensorclient
2022-06-13 02:05:00 【lovemengx】
Hardware environment : SLTB010A(BRD4184A Rev A02 / EFR32BG22C224F512IM40)
Software environment : SimplicityStudio5/gecko_sdk_3.2.3
Analytical engineering : Bluetooth Mesh SensorClient
It's a bad remedy BluetoothMesh Related knowledge , For the first time contact SiliconLabs chip , Search the whole network , There are so few Chinese materials , Another person has been gnawing at many official documents in English , Understand the burning 、 compile 、 Debugging and other basic knowledge , But the way of software development is still in the clouds , Then start to analyze the source code to understand SiliconLabs Source code framework , Finally, I know how to play with this chip .
This chapter mainly analyzes the problems after the equipment has been connected to the network SensorClinet Power on operation process , It should be the only one in the whole network .
The code structure :
main There is one while(1), Keep querying and getting from the event queue mesh event , And then we can do different processing according to different events .
void sl_btmesh_step(void)
{
sl_btmesh_msg_t evt;
uint32_t event_len = sl_btmesh_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_btmesh_can_process_event(event_len))) {
return;
}
// Pop (non-blocking) a Bluetooth stack event from event queue.
sl_status_t status = sl_btmesh_pop_event(&evt);
if(status != SL_STATUS_OK){
return;
}
sl_btmesh_process_event(&evt);
}
void sl_btmesh_process_event(sl_btmesh_msg_t *evt)
{
sl_btmesh_handle_btmesh_logging_events(evt);
sl_btmesh_handle_provisioning_decorator_event(evt);
sl_btmesh_factory_reset_on_event(evt);
sl_btmesh_handle_sensor_client_on_event(evt);
sl_btmesh_on_event(evt);
}Source code overview :
It mainly analyzes three events , When the network has been accessed , And the device nodes respond , There are only three events :
1. sl_btmesh_evt_node_initialized_id
2. sl_btmesh_evt_sensor_client_descriptor_status_id
3. sl_btmesh_evt_sensor_client_status_id
One 、sl_btmesh_evt_node_initialized_id
The event sent after the Bluetooth protocol stack completes initialization after the chip is powered on ( guess )
Analysis and summary :
1. If you have access to the network , Then initialize the sensor client model : sl_btmesh_sensor_client_init();
[sl_btmesh_sensor_client.c]sl_btmesh_handle_sensor_client_on_event(evt); // important : initialization sensor client Model
case sl_btmesh_evt_node_initialized_id:
if (evt->data.evt_node_initialized.provisioned)
mesh_sensor_client_init();
--> sl_btmesh_sensor_client_init(); // Initialize the sensor client model .Sensor Client There is no internal configuration , It only activates the models in the Bluetooth grid stack .2. If you have access to the network , Then start the single timer , If you are not connected to the network, publish a broadcast to let the configurator know , Waiting to be configured for network access
[app.c]sl_btmesh_on_event(evt); // important : If already configured , Then start the single timer , If it is not configured, start broadcasting , Waiting for configuration .
--> case sl_btmesh_evt_node_initialized_id:
--> handle_node_initialized_event(&(evt->data.evt_node_initialized));
if (evt->provisioned) // If the device has been configured ( That is, it has been connected to the network )
sl_simple_timer_start(..., DEVICE_REGISTER_SHORT_TIMEOUT(100), app_update_registered_devices_timer_cb,..., true);
else // Turn on the broadcast and wait for the configuration to access the network
sl_btmesh_node_start_unprov_beaconing(PB_ADV | PB_GATT);3. The single timer will clear the list of registered devices first , And record the temperature sensor of interest ID, Then start the search for the specified Sensor ID, Restart cycle 2 Second timer requests data
[app.c]app_update_registered_devices_timer_cb()
--> [sl_btmesh_sensor_client.c]sl_btmesh_sensor_client_update_registered_devices(property:current_property(0x004f))
registered_devices.count = 0; // Clear the list of registered devices
memset(registered_devices.address_table, 0, sizeof(registered_devices.address_table));
registering_property = property; // Will default to the temperature sensor to be registered ID Put the global variable 0x004f
sl_btmesh_sensor_client_on_discovery_started(property_id:property); // Only the print information is output here
--> app_log("BT mesh Sensor Device discovery is started. (property_id: 0x%04x)\r\n", property_id);
// a key : Start exploring the specified Sensor 0x004f, If there is a node response , Will trigger sl_btmesh_evt_sensor_client_descriptor_status_id event
sc = sl_btmesh_sensor_client_get_descriptor(PUBLISH_ADDRESS(0x0000), BTMESH_SENSOR_CLIENT_MAIN(0), IGNORED(0), NO_FLAGS, property);
if (SL_STATUS_OK == sc) {
log_info(SENSOR_CLIENT_LOGGING_START_REGISTERING_DEVICES, property);
--> #define SENSOR_CLIENT_LOGGING_START_REGISTERING_DEVICES "Registration of devices for property ID %4.4x started\r\n"
else
log_btmesh_status_f(sc, SENSOR_CLIENT_LOGGING_REGISTERING_DEVICES_FAILED, property);
--> #define SENSOR_CLIENT_LOGGING_REGISTERING_DEVICES_FAILED "Registration of devices for property ID %4.4x failed\r\n"
// a key : The starting cycle is 2 The second timer sends a request for data at a fixed time
--> sl_simple_timer_start(..., SENSOR_DATA_TIMEOUT(2000), app_sensor_data_timer_cb, ..., true); 4. cycle 2 Second timer , Will 2 The frequency of seconds is sent to acquire the specified sensor data of all nodes ( Here is the temperature sensor 0x004f)
[app.c]app_sensor_data_timer_cb
--> sl_btmesh_sensor_client_get_sensor_data(property:current_property(0x004f));
sc = sl_btmesh_sensor_client_get(PUBLISH_ADDRESS(0x0000), BTMESH_SENSOR_CLIENT_MAIN(0), IGNORED(0), NO_FLAGS, property);
if (SL_STATUS_OK == sc)
log_info(SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY, property);
--> #define SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY "Get Sensor Data from property ID %4.4x started\r\n"
else
log_btmesh_status_f(sc, SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY_FAIL, property);
--> #define SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY_FAIL "Get Sensor Data from property ID %4.4x failed\r\n"The source code walkthrough :
[main.c]main() -> while(1) // This event will be triggered by the protocol stack after power on
--> [sl_system_process_action.c]sl_system_process_action()
--> [sl_event_handler.c]sl_stack_process_action()
--> [sl_btmesh.c]sl_btmesh_step()
sl_btmesh_pop_event(&evt); // Take out mesh Event type
--> sl_btmesh_process_event(&evt); // Handle mest Event type
--> [sl_btmesh_event_log.c]sl_btmesh_handle_btmesh_logging_events(evt); // This is only used to print the currently received event type
--> case sl_btmesh_evt_node_initialized_id:
app_log("evt:mesh_node_initialized\r\n");
--> [sl_btmesh_provisioning_decorator.c]sl_btmesh_handle_provisioning_decorator_event(evt); // Whether the printout has been configured
--> case sl_btmesh_evt_node_initialized_id:
--> sl_btmesh_on_provision_init_status(provisioned, address, iv_index);
--> if (provisioned) // If the device has been configured ( That is, it has been connected to the network )
app_show_btmesh_node_provisioned(address, iv_index);
--> app_log("BT mesh node is provisioned (address: 0x%04x, iv_index: 0x%lx)\r\n", address, iv_index);
else
app_log("BT mesh node is unprovisioned, started unprovisioned " "beaconing...\r\n");
--> [sl_btmesh_factory_reset.c]sl_btmesh_factory_reset_on_event(evt); // Ignore : Only reset events are processed sl_btmesh_evt_node_reset_id
--> [sl_btmesh_sensor_client.c]sl_btmesh_handle_sensor_client_on_event(evt); // important : initialization sensor client Model
case sl_btmesh_evt_node_initialized_id:
if (evt->data.evt_node_initialized.provisioned)
mesh_sensor_client_init();
--> sl_btmesh_sensor_client_init(); // Initialize the sensor client model .Sensor Client There is no internal configuration , It only activates the models in the Bluetooth grid stack .
--> [app.c]sl_btmesh_on_event(evt); // important : If already configured , Then start the single timer , If it is not configured, start broadcasting , Waiting for configuration .
case sl_btmesh_evt_node_initialized_id:
--> handle_node_initialized_event(&(evt->data.evt_node_initialized));
if (evt->provisioned) // If the device has been configured ( That is, it has been connected to the network )
sl_simple_timer_start(..., DEVICE_REGISTER_SHORT_TIMEOUT(100), app_update_registered_devices_timer_cb,..., true);
else // Turn on the broadcast and wait for the configuration to access the network
sl_btmesh_node_start_unprov_beaconing(PB_ADV | PB_GATT);It mainly starts to explore the specified sensor and starts to 2 Seconds is the cycle timer to request the acquisition of sensor data
[app.c]app_update_registered_devices_timer_cb()
--> [sl_btmesh_sensor_client.c]sl_btmesh_sensor_client_update_registered_devices(property:current_property(0x004f))
registered_devices.count = 0; // Clear the list of registered devices
memset(registered_devices.address_table, 0, sizeof(registered_devices.address_table));
registering_property = property; // Will default to the temperature sensor to be registered ID Put the global variable 0x004f
sl_btmesh_sensor_client_on_discovery_started(property_id:property); // Only the print information is output here
--> app_log("BT mesh Sensor Device discovery is started. (property_id: 0x%04x)\r\n", property_id);
// a key : Start exploring the specified Sensor 0x004f, If there is a node response , Will trigger sl_btmesh_evt_sensor_client_descriptor_status_id event
sc = sl_btmesh_sensor_client_get_descriptor(PUBLISH_ADDRESS(0x0000), BTMESH_SENSOR_CLIENT_MAIN(0), IGNORED(0), NO_FLAGS, property);
if (SL_STATUS_OK == sc) {
log_info(SENSOR_CLIENT_LOGGING_START_REGISTERING_DEVICES, property);
--> #define SENSOR_CLIENT_LOGGING_START_REGISTERING_DEVICES "Registration of devices for property ID %4.4x started\r\n"
else
log_btmesh_status_f(sc, SENSOR_CLIENT_LOGGING_REGISTERING_DEVICES_FAILED, property);
--> #define SENSOR_CLIENT_LOGGING_REGISTERING_DEVICES_FAILED "Registration of devices for property ID %4.4x failed\r\n"
// a key : The starting cycle is 2 The second timer sends a request for data at a fixed time
--> sl_simple_timer_start(..., SENSOR_DATA_TIMEOUT(2000), app_sensor_data_timer_cb, ..., true); The period is 2 Second timer timing acquisition Server All sensor data in the model
// The period is 2 Second timer timing acquisition Server All sensor data in the model
[app.c]app_sensor_data_timer_cb
--> sl_btmesh_sensor_client_get_sensor_data(property:current_property(0x004f));
sc = sl_btmesh_sensor_client_get(PUBLISH_ADDRESS(0x0000), BTMESH_SENSOR_CLIENT_MAIN(0), IGNORED(0), NO_FLAGS, property);
if (SL_STATUS_OK == sc)
log_info(SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY, property);
--> #define SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY "Get Sensor Data from property ID %4.4x started\r\n"
else
log_btmesh_status_f(sc, SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY_FAIL, property);
--> #define SENSOR_CLIENT_LOGGING_GET_DATA_FROM_PROPERTY_FAIL "Get Sensor Data from property ID %4.4x failed\r\n"Two 、sl_btmesh_evt_sensor_client_descriptor_status_id
The server node responds to events , Server if the node does not exist , There will be no such event
Analysis and summary :
1. When starting the search sensor , If there is a node response , This event will be triggered .
2. It will check whether the current node attribute is the temperature sensor of interest .
3. And check whether the node is in the device list , Add to the equipment list when you are not there .
4. This equipment list is very important , Only the data of nodes in this list will not be ignored .
if (descriptor.property_id == registering_property && number_of_devices < SENSOR_CLIENT_DISPLAYED_SENSORS(5)
&& !mesh_address_already_exists(®istered_devices, evt->server_address))
registered_devices.address_table[number_of_devices] = evt->server_address;
registered_devices.count = number_of_devices + 1;The source code walkthrough :
[main.c]main() -> while(1) // The incident was caused by sl_btmesh_sensor_client_get_descriptor() Trigger
--> [sl_system_process_action.c]sl_system_process_action()
--> [sl_event_handler.c]sl_stack_process_action()
--> [sl_btmesh.c]sl_btmesh_step()
sl_btmesh_pop_event(&evt); // Take out mesh Event type
--> sl_btmesh_process_event(&evt); // Handle mest Event type
--> [sl_btmesh_event_log.c]sl_btmesh_handle_btmesh_logging_events(evt); // This is only used to print the currently received event type
--> case sl_btmesh_evt_sensor_client_descriptor_status_id:
app_log("evt:mesh_sensor_client_descriptor_status\r\n");
--> [sl_btmesh_provisioning_decorator.c]sl_btmesh_handle_provisioning_decorator_event(evt); // Ignore : This function does not handle this event
--> [sl_btmesh_factory_reset.c]sl_btmesh_factory_reset_on_event(evt); // Ignore : Only reset events are processed sl_btmesh_evt_node_reset_id
--> [sl_btmesh_sensor_client.c]sl_btmesh_handle_sensor_client_on_event(evt);
case sl_btmesh_evt_sensor_client_descriptor_status_id:
--> handle_sensor_client_events(&(evt->data.evt_sensor_client_descriptor_status));
case sl_btmesh_evt_sensor_client_descriptor_status_id:
--> handle_sensor_client_descriptor_status(&(evt->data.evt_sensor_client_descriptor_status));
--> [sl_btmesh_sensor.c]mesh_lib_sensor_descriptors_from_buf(&descriptor, evt->descriptors.data, SIZE_OF_DESCRIPTOR); // Data is converted to a sensor descriptor
// If at present mesh The nature of the event ID That's what we want registering_property:0x004f, If it is not in the device registration list, add it to the list
uint8_t number_of_devices = registered_devices.count;
if (descriptor.property_id == registering_property && number_of_devices < SENSOR_CLIENT_DISPLAYED_SENSORS(5)
&& !mesh_address_already_exists(®istered_devices, evt->server_address))
registered_devices.address_table[number_of_devices] = evt->server_address;
registered_devices.count = number_of_devices + 1;
--> [app_out_log.c]sl_btmesh_sensor_client_on_new_device_found(descriptor.property_id, evt->server_address);
--> app_log("BT mesh Sensor Device (address: 0x%04x, property_id: 0x%04x) is found.\r\n", address, property_id);
3、 ... and 、sl_btmesh_evt_sensor_client_status_id
Server node status response event , Server if the node status is not responding or does not exist , There will be no such event
Analysis and summary :
1. The packet attached to this event may carry multiple node data , Therefore, it is necessary to split them one by one
2. Filter nodes that are not in the device list , Only the temperature sensor data in the equipment list and of interest will be processed .
The source code walkthrough :
[main.c]main() -> while(1) // The incident was caused by sl_btmesh_sensor_client_get_descriptor() Trigger
--> [sl_system_process_action.c]sl_system_process_action()
--> [sl_event_handler.c]sl_stack_process_action()
--> [sl_btmesh.c]sl_btmesh_step()
sl_btmesh_pop_event(&evt); // Take out mesh Event type
--> sl_btmesh_process_event(&evt); // Handle mest Event type
--> [sl_btmesh_event_log.c]sl_btmesh_handle_btmesh_logging_events(evt); // This is only used to print the currently received event type
--> case sl_btmesh_evt_sensor_client_status_id: // The address of the printout current response is the group address 、 Unicast address or virtual address
app_log("evt:mesh_sensor_client_status %s\r\n", (evt->data.evt_sensor_client_status.client_address & 0xC000) == 0xC000 ? "(group broadcast)" : (evt->data.evt_sensor_client_status.client_address & 0x1000) == 0 ? "(unicast)" : "(virtual)");
--> [sl_btmesh_provisioning_decorator.c]sl_btmesh_handle_provisioning_decorator_event(evt); // Ignore : This function does not handle this event
--> [sl_btmesh_factory_reset.c]sl_btmesh_factory_reset_on_event(evt); // Ignore : Only reset events are processed sl_btmesh_evt_node_reset_id
--> [sl_btmesh_sensor_client.c]sl_btmesh_handle_sensor_client_on_event(evt); // a key : This function will extract the match Server The sensor data
case sl_btmesh_evt_sensor_client_status_id:
--> handle_sensor_client_events(&(evt->data.evt_sensor_client_descriptor_status));
case sl_btmesh_evt_sensor_client_status_id:
--> static void handle_sensor_client_status(sl_btmesh_evt_sensor_client_status_t *evt)
{
uint8_t *sensor_data = evt->sensor_data.data;
uint8_t data_len = evt->sensor_data.len;
uint8_t pos = 0;
while (pos < data_len)
{ // Go through each Server Node messages
if (data_len - pos > PROPERTY_ID_SIZE) {
mesh_device_properties_t property_id = (mesh_device_properties_t)(sensor_data[pos] + (sensor_data[pos + 1] << 8));
uint8_t property_len = sensor_data[pos + PROPERTY_ID_SIZE];
uint8_t *property_data = NULL;
// Only those in the device registry are processed Service Node messages
if (mesh_address_already_exists(®istered_devices, evt->server_address)) {
sl_btmesh_sensor_client_data_status_t status;
uint16_t address;
uint8_t sensor_idx;
if (property_len && (data_len - pos > PROPERTY_HEADER_SIZE)) {
property_data = &sensor_data[pos + PROPERTY_HEADER_SIZE];
}
address = evt->server_address;
sensor_idx = mesh_get_sensor_index(®istered_devices, address);
status = SL_BTMESH_SENSOR_CLIENT_DATA_NOT_AVAILABLE;
switch (property_id) {
......
case PRESENT_AMBIENT_TEMPERATURE:
temperature_8_t temperature = SL_BTMESH_SENSOR_CLIENT_TEMPERATURE_UNKNOWN;
if (property_len == 1) {
mesh_device_property_t new_property = mesh_sensor_data_from_buf(PRESENT_AMBIENT_TEMPERATURE, property_data);
temperature = new_property.temperature_8;
if (temperature == SL_BTMESH_SENSOR_CLIENT_TEMPERATURE_UNKNOWN) {
status = SL_BTMESH_SENSOR_CLIENT_DATA_UNKNOWN;
} else {
status = SL_BTMESH_SENSOR_CLIENT_DATA_VALID;
}
} else {
status = SL_BTMESH_SENSOR_CLIENT_DATA_NOT_AVAILABLE;
}
--> [app_out_log.c]sl_btmesh_sensor_client_on_new_temperature_data(sensor_idx, address, status, temperature);
if (PRESENT_AMBIENT_TEMPERATURE != app_get_current_property()) {
return;
}
if (SL_BTMESH_SENSOR_CLIENT_DATA_VALID == status) {
app_log("BT mesh Sensor Temperature (from 0x%04x): %3d.%1d Abduct C\r\n", address, INT_TEMP(temperature), FRAC_TEMP(temperature));
} else if (SL_BTMESH_SENSOR_CLIENT_DATA_UNKNOWN == status) {
app_log("BT mesh Sensor Temperature (from 0x%04x): UNKNOWN\r\n", address);
} else {
app_log("BT mesh Sensor Temperature (from 0x%04x): NOT AVAILABLE\r\n", address);
}
break;
......
}
pos += PROPERTY_HEADER_SIZE + property_len;
}
} else {
pos = data_len;
}
}
}Four 、 Key handling logic
The above is the core logic processing , As for the logic of key triggering , With the above foundation , It's a lot easier .
app.c By redefining app_button_press.c The key processing callback of is used to intercept key events
[app_button_press.c ]SL_WEAK void app_button_press_cb(uint8_t button, uint8_t duration)
{
(void)button;
(void)duration;
}
[app.c]void app_button_press_cb(uint8_t button, uint8_t duration)
{
(void)duration;
if (duration == APP_BUTTON_PRESS_NONE) {
return;
}
// button pressed
if (button == BUTTON_PRESS_BUTTON_0) {
if (duration < APP_BUTTON_PRESS_DURATION_LONG) {
app_log("PB0 pressed\r\n");
sensor_client_change_current_property(); // Press the key briefly to adjust the sensor type of interest
} else {
app_log("PB0 long pressed\r\n");
update_registered_devices(); // Long press to restart the search for the specified Sensor ID
}
} else if (button == BUTTON_PRESS_BUTTON_1) {
app_log("PB1 pressed\r\n");
update_registered_devices();
}
}
[app.c]static void sensor_client_change_current_property(void)
{
switch (current_property) {
case PRESENT_AMBIENT_TEMPERATURE:
current_property = PEOPLE_COUNT;
break;
case PEOPLE_COUNT:
current_property = PRESENT_AMBIENT_LIGHT_LEVEL;
break;
case PRESENT_AMBIENT_LIGHT_LEVEL:
current_property = PRESENT_AMBIENT_TEMPERATURE;
break;
default:
app_log("Unsupported property ID change\r\n");
break;
}
}
// Reference resources sl_btmesh_evt_node_initialized_id The third and fourth step of
void update_registered_devices(void)
{
sl_status_t sc;
sl_btmesh_sensor_client_update_registered_devices(current_property);
sc = sl_simple_timer_start(&app_sensor_data_timer,SENSOR_DATA_TIMEOUT, app_sensor_data_timer_cb, NO_CALLBACK_DATA,true);
app_assert_status_f(sc, "Failed to start periodic timer\n");
}
5、 ... and 、 The way of chip development
1. It can be found that the final results are in the current project app_out_log.c Output
2. and app_out_log.c It's through sl_btmesh_sensor_client.c Inside SL_WEAK Decorated interface to intercept the results
3. When a source file in the project has a connection with SL_WEAK When the modified functions are the same , The compiler will only select the function implementation that compiles the source file .
4. So be familiar with sl_btmesh_sensor_client.c The logical implementation of , What results need to be achieved again SL_WEAK The modified function is OK .
边栏推荐
- What is the path field—— Competitive advertising
- Introduction to Google unit testing tools GTEST and gmoke
- Vscode configuration header file -- Take opencv and its own header file as an example
- Bluetooth module: use problem collection
- Delphi7 compressed pictures (BMP, JPG, PNG)
- SQLserver2008 拒绝了对对象 '****' (数据库 '****',架构 'dbo')的 SELECT 权限
- rsync 传输排除目录
- 华为设备配置IP和虚拟专用网混合FRR
- Read routing table
- Swiper horizontal rotation grid
猜你喜欢
![[the second day of actual combat of smart lock project based on stm32f401ret6 in 10 days] GPIO and register](/img/eb/9bd411be74937371de0bbf3f04267e.jpg)
[the second day of actual combat of smart lock project based on stm32f401ret6 in 10 days] GPIO and register

STM32F103 IIC OLED program migration complete engineering code

Why is Huawei matebook x Pro 2022 leading a "laptop" revolution

指针链表的实现

What is solid angle

Server installation jupyterab and remote login configuration

What is the path field—— Competitive advertising
![[the 4th day of the 10 day smart lock project based on stm32f401ret6] what is interrupt, interrupt service function, system tick timer](/img/c4/0d97def5fb587b8301bcb907fc6fcf.jpg)
[the 4th day of the 10 day smart lock project based on stm32f401ret6] what is interrupt, interrupt service function, system tick timer

【Unity】打包WebGL項目遇到的問題及解决記錄
![[learning notes] xr872 GUI littlevgl 8.0 migration (display part)](/img/5e/fc8c3fe3029c36648fbc3f48bc0c2f.jpg)
[learning notes] xr872 GUI littlevgl 8.0 migration (display part)
随机推荐
Opencv camera calibration (1): internal and external parameters, distortion coefficient calibration and 3D point to 2D image projection
Vivo released originos ocean, and the domestic customized system is getting better and better
Use mediapipe+opencv to make a simple virtual keyboard
Logging system in chromium
PyFlink实现自定义SourceFunction
STM32 timer interrupt learning notes
SQLserver2008 拒绝了对对象 '****' (数据库 '****',架构 'dbo')的 SELECT 权限
How do you use your own data to achieve your marketing goals?
Vs how to enter chromium subprocess debugging
dfs与bfs解决宝岛探险
[the third day of actual combat of smart lock project based on stm32f401ret6 in 10 days] communication foundation and understanding serial port
华为设备配置双反射器优化虚拟专用网骨干层
Ruixing coffee 2022, extricating itself from difficulties and ushering in a smooth path
10 days based on stm32f401ret6 smart lock project practice day 1 (environment construction and new construction)
A DPU architecture without CPU: Hyperion
JS get element
Detailed explanation of deep learning parameter adjustment skills
4、 Improvement of warehousing management function
[the second day of actual combat of smart lock project based on stm32f401ret6 in 10 days] (lighting with library function and register respectively)
Interruption of 51 single chip microcomputer learning notes (external interruption, timer interruption, interrupt nesting)