【嵌入式开源库】timeslice的使用,完全解耦的时间片轮询框架构
完全解耦的时间片轮询框架构
- 简介
- 项目代码
- timeslice.h
- timeslice.c
- list.h
- list.c
- 创建工程
- 移植代码
- 实验
- 函数说明
- timeslice_task_init
- timeslice_task_add
- timeslice_tak_del
- timeslice_get_task_num
- 结尾
简介
timeslice是一个时间片轮询框架,他是一个完全解耦的时间片轮询框架,他的使用非常方便,该项目一共有四个文件分别是tieslice的头文件和源文件以及list的头文件和源文件,tieslice是负责轮询任务,list是一个双向链表负责任务的管理,在Linux内核中使用非常广泛也很经典,该框架是参考rtt实时操作系统的侵入式链表实现的,本章文章是将该框架移植到stm32单片机上实验,使用也非常容易,单片机只需要启用一个定时器作为时钟即可;
本章使用环境:
stm32f407vet6
代码工程使用cubemx创建
项目代码
该项目的代码是我在微信公众号上看到的一个文章,代码并没有上传在github上,这里直接贴上源代码;
timeslice.h
#ifndef _TIMESLICE_H
#define _TIMESLICE_H#include "list.h"typedef enum {TASK_STOP,TASK_RUN
} IsTaskRun;typedef struct timesilce
{unsigned int id;void (*task_hdl)(void);IsTaskRun is_run;unsigned int timer;unsigned int timeslice_len;ListObj timeslice_task_list;
} TimesilceTaskObj;void timeslice_exec(void);
void timeslice_tick(void);
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);
void timeslice_task_add(TimesilceTaskObj* obj);
void timeslice_task_del(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_num(void);
unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);#endif
timeslice.c
#include "timeslice.h"static LIST_HEAD(timeslice_task_list);void timeslice_exec()
{ListObj* node;TimesilceTaskObj* task;list_for_each(node, ×lice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (task->is_run == TASK_RUN){task->task_hdl();task->is_run = TASK_STOP;}}
}void timeslice_tick()
{ListObj* node;TimesilceTaskObj* task;list_for_each(node, ×lice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (task->timer != 0){task->timer--;if (task->timer == 0){task->is_run = TASK_RUN;task->timer = task->timeslice_len;}}}
}unsigned int timeslice_get_task_num()
{return list_len(×lice_task_list);
}void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
{obj->id = id;obj->is_run = TASK_STOP;obj->task_hdl = task_hdl;obj->timer = timeslice_len;obj->timeslice_len = timeslice_len;
}void timeslice_task_add(TimesilceTaskObj* obj)
{list_insert_before(×lice_task_list, &obj->timeslice_task_list);
}void timeslice_task_del(TimesilceTaskObj* obj)
{if (timeslice_task_isexist(obj))list_remove(&obj->timeslice_task_list);elsereturn;
}unsigned char timeslice_task_isexist(TimesilceTaskObj* obj)
{unsigned char isexist = 0;ListObj* node;TimesilceTaskObj* task;list_for_each(node, ×lice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (obj->id == task->id)isexist = 1;}return isexist;
}unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj)
{return obj->timeslice_len;
}
list.h
#ifndef _LIST_H
#define _LIST_H#define offset_of(type, member) (unsigned long) &((type*)0)->member
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offset_of(type, member)))typedef struct list_structure
{struct list_structure* next;struct list_structure* prev;
} ListObj;#define LIST_HEAD_INIT(name) {&(name), &(name)}
#define LIST_HEAD(name) ListObj name = LIST_HEAD_INIT(name)void list_init(ListObj* list);
void list_insert_after(ListObj* list, ListObj* node);
void list_insert_before(ListObj* list, ListObj* node);
void list_remove(ListObj* node);
int list_isempty(const ListObj* list);
unsigned int list_len(const ListObj* list);#define list_entry(node, type, member) \container_of(node, type, member)#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); pos = pos->next)#define list_for_each_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)#endif
list.c
#include "list.h"void list_init(ListObj* list)
{list->next = list->prev = list;
}void list_insert_after(ListObj* list, ListObj* node)
{list->next->prev = node;node->next = list->next;list->next = node;node->prev = list;
}void list_insert_before(ListObj* list, ListObj* node)
{list->prev->next = node;node->prev = list->prev;list->prev = node;node->next = list;
}void list_remove(ListObj* node)
{node->next->prev = node->prev;node->prev->next = node->next;node->next = node->prev = node;
}int list_isempty(const ListObj* list)
{return list->next == list;
}unsigned int list_len(const ListObj* list)
{unsigned int len = 0;const ListObj* p = list;while (p->next != list){p = p->next;len++;}return len;
}
创建工程
配置高速时钟和低速时钟为外部晶振提供

配置调试模式为sw调试模式
配置时钟频率
配置led,这里我的板子是这三个io接入的是led

配置一个10ms的定时器(1000000hz / 1000ms == 1ms = 1khz 就得到10ms需要计数10000重载)记得打开中断


配置工程并生成,工程名设置,单独生成.c.h文件拷贝库文件



下面开始代码的移植工作;
移植代码
首先我们需要将该开源项目的代码添加到工程中
在定时器中断服务函数中添加timeslice轮询函数


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){timeslice_tick();}
}
然后我们需要在main函数中启动定时器并在while1中调用exec函数调度时间片
HAL_TIM_Base_Start_IT(&htim3);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */timeslice_exec();}
到这里我们的移植工作就做完了,该项目的解耦效果真的非常好,移植相当简单,然后我们创建几个任务实验一下效果;
实验
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "timeslice.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){timeslice_tick();}
}// 创建3个任务对象
TimesilceTaskObj task_1, task_2, task_3;// 具体的任务函数
void task1_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_13);
}void task2_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_14 );
}void task3_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_15);
}// 初始化任务对象,并且将任务添加到时间片轮询调度中
void task_init()
{timeslice_task_init(&task_1, task1_hdl, 1, 1);timeslice_task_init(&task_2, task2_hdl, 2, 1);timeslice_task_init(&task_3, task3_hdl, 3, 1);timeslice_task_add(&task_1);timeslice_task_add(&task_2);timeslice_task_add(&task_3);
}
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */HAL_TIM_Base_Start_IT(&htim3);task_init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */timeslice_exec();}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 25;RCC_OscInitStruct.PLL.PLLN = 336;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
核心部分
// 创建3个任务对象
TimesilceTaskObj task_1, task_2, task_3;// 具体的任务函数
void task1_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_13);
}void task2_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_14 );
}void task3_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_15);
}// 初始化任务对象,并且将任务添加到时间片轮询调度中
void task_init()
{timeslice_task_init(&task_1, task1_hdl, 1, 1);timeslice_task_init(&task_2, task2_hdl, 2, 2);timeslice_task_init(&task_3, task3_hdl, 3, 3);timeslice_task_add(&task_1);timeslice_task_add(&task_2);timeslice_task_add(&task_3);
}
需要注意的是必须要有一个任务是需要在exec前创建,这样才能保证运行,其他的任务可以在这个任务中再创建,上面的实验是实现三个任务,三个任务分别为一个中断触发一次,第二个任务是每隔两个中断触发一次,第三个任务是每隔三个中断触发一次任务;
函数说明
timeslice_task_init
初始化任务函数
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
{obj->id = id;obj->is_run = TASK_STOP;obj->task_hdl = task_hdl;obj->timer = timeslice_len;obj->timeslice_len = timeslice_len;
}
在这个函数中将任务结构体参数初始化,id类似于任务名称用于区分任务,is_run是一个标志位用于判断该任务在该次中断是否需要执行,task_hd1表示函数指针也就是我们的任务函数,timer表示每多少次中断触发一次计数,timeslice_len 表示没多少次中断触发一次计数初始值,在timeslice_tick中当timer的值减到0任务将触发并会重新复位timer的值为 timeslice_len ;

timeslice_task_add
添加任务到双向链表中
void timeslice_task_add(TimesilceTaskObj* obj)
{list_insert_before(×lice_task_list, &obj->timeslice_task_list);
}void list_insert_before(ListObj* list, ListObj* node)
{list->prev->next = node;node->prev = list->prev;list->prev = node;node->next = list;
}
该链表(timeslice_task_list)在timeslice_tick中会轮询进行遍历
timeslice_tak_del
删除正在运行的任务链表
void timeslice_task_del(TimesilceTaskObj* obj)
{if (timeslice_task_isexist(obj))list_remove(&obj->timeslice_task_list);elsereturn;
}
在该函数中会通过timeslice_task_isexist函数去判断链表中是否存在该任务的id,如果存在将返回退出允许,这里涉及到一个Linux中的函数list_entry->container_of,该函数是通过结构体的某个变量获取整个结构体的指针位置,有兴趣可以去学习一下该项目代码的实现;
timeslice_get_task_num
获取当前任务数量,也就是链表的长度
unsigned int timeslice_get_task_num()
{return list_len(×lice_task_list);
}unsigned int list_len(const ListObj* list)
{unsigned int len = 0;const ListObj* p = list;while (p->next != list){p = p->next;len++;}return len;
}
结尾
整体的代码不算复杂但是是值得学习的一个项目,我是凉开水白菜祝各位程序员们节日快乐~ 咱们下文见~
相关文章:
【嵌入式开源库】timeslice的使用,完全解耦的时间片轮询框架构
完全解耦的时间片轮询框架构 简介项目代码timeslice.htimeslice.clist.hlist.c 创建工程移植代码实验函数说明timeslice_task_inittimeslice_task_addtimeslice_tak_deltimeslice_get_task_num 结尾 简介 timeslice是一个时间片轮询框架,他是一个完全解耦的时间片轮…...
人工智能期末考试(刷题篇部分题有答案)
参考: 人工智能 经典考试试题及答案 - 百度文库 大学人工智能期末考试题库 - 百度文库 人工智能【期末复习题】 - 百度文库 人工智能期末试题及答案完整版 - 百度文库 一、选择题 AI的缩写是 Artificial Intelligence反演归结(消解)证明…...
手写Vue渲染器render函数
使用js对象来描述UI更加的灵活。“这种对象”在vue框架中被称为虚拟DOM,渲染函数内部可以创建虚拟DOM,然后vue.js可以将其内容进行渲染。 1.渲染器的介绍 渲染器的作用就是把虚拟DOM渲染为真实DOM 思考下,我们有一个虚拟 DOM,如…...
CGAL+QT
先安装CGAL和QT 安装完QT其中MSVC 这两个没配置 1、x32配置选择的是 x64配置选择的是 2、CGAL 5.4.5 - Manual: Using CGAL on Windows (with Visual C) 参数文章配置一些环境变量 3、 测试 新建build 进行cmake QT、Boost、CGAL都自动匹配上了(环境变量已经配…...
GBase8a SSL 配置
GBase8a SSL 配置 GBase8a MPP Cluster 支持 SSL 标准协议, SSL 协议是一种安全性更高的协议标准, 它加入了数字签名和数字证书来实现客户端和服务器的双向身份验证,保证了通信双方更加安全的数据传输。 配置客户端使用 SSL 安全连接的方式连…...
数据结构之队列(源代码➕图解➕习题)
前言 在学过栈之后,会了解到栈的底层是根据顺序表或者链表来构建的,那么我们今天要学习的队列是否也是基于顺序表和链表呢?那我们直接进入正题吧! 1. 队列的概念(图解) 还是跟上节一样,依旧用图…...
社区迭代|ETLCloud社区新增“论坛”啦!
ETLCloud社区是谷云科技RestCloud旗下面向开发工程师、集成研发人员等技术人员提供全方位交流和学习的开放式平台,也是ETLCloud在产品生态赋能上的一大亮点,旨在能够帮助更多的用户更快捷高效的掌握技能,也为企业提供集成人才培养赋能&#x…...
ohos的代码同步以及添加自己的代码
首先我们需要获取到官方的repo工具,命令如下curl -s https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > ./repo,这里我们就拿到repo工具了,这个repo可以放任意地方,也可以放 /usr/local/bin/repo下,这样可以…...
Python的Pandas库(二)进阶使用
Python开发实用教程 DataFrame的运算 DataFrame重载了运算符,支持许多的运算 算术运算 运算方法运算说明df.add(other)对应元素的加,如果是标量,就每个元素加上标量df.radd(other)等效于otherdfdf.sub(other)对应元素相减,如果…...
如何才能从程序员到架构师?
1 引言 小团队一般 10 人左右,其中常常是技术最牛的人做架构师(或TL)。所以,架构师在广大码农中的占比大概平均不到 10%。而架构师也可以分为初级、中级、高级三档,江湖上真正高水平的软件架构师就更少了。 所以&…...
dvadmin-打包发布-nginx-静态服务器配置-防火墙设置
文章目录 1.下载nginx2.nginx常用命令3.dvadmin打包发布4.防火墙设置 1.下载nginx 也从作者下载的网址下载:https://download.csdn.net/download/m0_67316550/88470098 2.nginx常用命令 注意:一定要在dos窗口启动,不要直接双击nginx.exe&a…...
Win10中Pro/E鼠标滚轮不能缩放该怎么办?
Pro/E安装好后,鼠标滚轮不能缩放模型,该怎么办?问题多发生在win8/win10上,新装了PROE,发现滑动鼠标中键不能放大缩小。 彩虹图纸管理软件_图纸管理系统_图纸文档管理软件系统_彩虹EDM【官网】彩虹EDM图纸管理软件系统…...
腾讯云轻量应用服务器性能如何?值得入手吗?
腾讯云轻量应用服务器性能怎么样?轻量服务器的CPU内存计算性能和同规格的标准型云服务器CVM性能处于同一水准,性能很不错,具有100%CPU性能,并且价格很优惠,值得买。腾讯云百科txybk.com分享腾讯云轻量应用服务器性能测…...
主流大语言模型的技术细节
主流大语言模型的技术原理细节从预训练到微调https://mp.weixin.qq.com/s/P1enjLqH-UWNy7uaIviWRA 比较 LLaMA、ChatGLM、Falcon 等大语言模型的细节:tokenizer、位置编码、Layer Normalization、激活函数等。2. 大语言模型的分布式训练技术:数据并行、…...
面试经典150题——Day22
文章目录 一、题目二、题解 一、题目 6. Zigzag Conversion The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) P A H N A P L S I I G …...
for循环三种跳出循环的方法(retrun、continue、break)
1、continue:指的是跳出当前循环,即不执行continue后的语句,直接进入下次循环。 【continue语句和break语句差不多。不同的是,它不是退出一个循环,而是跳出当前循环,进行下一轮循环】 public static void…...
React中的受控组件(controlled component)和非受控组件(uncontrolled component)
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...
python 查找波峰和波谷
import numpy as np import matplotlib.pyplot as plt from scipy.signal import find_peaks# 生成示例信号 x np.array([1, 3, 7, 1, 2, 6, 0, 4, 3, 2, 5, 1])# 寻找波峰 peaks, _ find_peaks(x)# 寻找波谷(使用信号的负数形式) valleys, _ find_pe…...
深入理解 Document Load 和 Document Ready 的区别
目录 前言: 一、Document Ready 二、Document Load 三、理解和总结 前言: 在前端开发中,理解页面加载的不同阶段是至关重要的。特别是当我们需要在页面加载到特定阶段时执行某些操作时,我们需要知道应该使用 document ready 还…...
有趣的算法(七) ——快速排序改进算法
有趣的算法(七) ——快速排序改进算法 目录 有趣的算法(七) ——快速排序改进算法 本文章向大家介绍有趣的算法(七) ——快速排序改进算法,主要内容包括其使用实例、应用技巧、基本知识点总结…...
EMS智慧能源管理、物联网双碳、建筑用能、能耗统计、能源流向、损耗分析、班组用能、水电数据、能耗分析、零碳园区、碳汇管理、工艺优化分析、用能诊断、计量仪表、用能预警、配电
基于 Vue3 / Spring Boot/Spring Cloud & Alibaba 微服务架构 项目技术框架 RuoYi-Cloud 基础框架上开发而成 源智优控AI能源大脑,能源AI版,即将上线 仓库地址: https://gitee.com/guangdong122/energy-management 一、系统介绍 能源…...
lvgl_v8之设置label背景颜色一种方式
void lv_label_demo() {static lv_style_t style;lv_style_init(&style);lv_style_set_radius...
GNSS导航信号模拟器 卫星导航定位模拟器 GNSS卫星导航定位信号模拟器行业应用解决方案 GNSS模拟器
随着全球卫星导航系统的全面建设与深度应用,各类卫星导航定位授时终端已广泛渗透到交通、物联网、通信、测绘、消费电子等众多领域。但在终端产品的研发、测试、量产全流程中,行业长期面临诸多核心痛点:传统外场实地测试模式需投入大量人力物…...
Redis 不止缓存!从零到一吃透 Redis 向量数据库
前言大模型时代,检索增强生成(RAG)、智能推荐、多模态检索等场景已成为业务创新的核心方向,而向量数据库正是支撑这些场景的底层基石。很多开发者提起向量数据库,第一反应是Milvus、Pinecone这类专业组件,却…...
车轨桥刚柔耦合仿真与 Simpack 与 Abaqus 联合仿真那些事儿
1.simpack与abaqus联合仿真教程 2.车轨桥刚柔耦合仿真教程,柔性钢轨建模,fbi文件生成,ftr文件书写 3.包括模型在工程仿真领域,车轨桥刚柔耦合仿真以及 Simpack 与 Abaqus 联合仿真都是极具实用价值的技术,今天就来给大…...
Claude Code 使用秘籍大公开!从零基础到精通,字节跳动官方手册等你拿!
本文提供了一份详尽的 Claude Code 使用手册,专为从零基础到精通的学习者设计。手册采用手把手教学方式,步骤清晰,技巧实用,无需复杂代码知识即可上手。文中特别强调了对使用 Gemini3 的伙伴的适用性,并鼓励读者点赞、…...
杰理AD14N/AD15N---自定义GPIO唤醒与长按键开关机配置实战
1. 认识杰理AD14N/AD15N的GPIO唤醒机制 第一次接触杰理AD14N/AD15N芯片的开发者,往往会被其GPIO唤醒机制搞得一头雾水。我刚开始调试时也踩过不少坑,比如明明按照手册配置了唤醒引脚,设备却怎么都唤不醒。后来才发现,问题出在对底…...
黑客技术?没你想象的那么难!—— DNS 劫持篇
黑客技术?没你想象的那么难!——dns劫持篇 什么是DNS劫持? DNS劫持就是通过劫持了DNS服务器,通过某些手段取得某域名的解析记录控制权,进而修改此域名的解析结果,导致对该域名的访问由原IP地址转入到修改后…...
机器人通信协议全览:30种核心技术解析
各类机器人常用通信协议抽象总结表(按协议合并,带序号)序号通信协议物理接口核心特点适用机器人场景1EtherCATRJ45微秒级延迟、纳秒级同步,实时性极强工业机器人、移动机器人(AGV/AMR)、四足机器人…...
MySQL 生产环境故障排查与性能优化全攻略(8.0 版本实战)
前言MySQL 作为目前企业级应用最广泛的开源关系型数据库,在生产环境中承担着核心数据存储与处理任务。默认配置往往无法满足高并发、大数据量的业务场景,同时运维过程中也会频繁遇到各类故障。本文基于 MySQL 8.0 版本,从单实例故障、主从复制…...
