当前位置: 首页 > news >正文

AT32固件库外设使用,ArduinoAPI接口移植,模块化

目录

  • 一、ArduinoAPI移植
  • 一、通用定时器使用
    • 1.计时
    • 1.
    • 2.ETR外部时钟计数
    • 4.ArduinoAPI - timer
  • 三、ADC
    • 1.ADC初始化(非DMA)
    • 2.ADC_DMA 规则通道扫描
  • 六、USB HID IAP
    • 1.准备好Bootloader和app
    • 2.配置好时钟,一定要打开USB
    • 3.将生成的时钟配置复制到bootloader和app对应位置
    • 4 设置bootloader和app的起始位置
    • 5 使用IAP Programmer下载,地址要设置为app地址

一、ArduinoAPI移植

通过arduinoapi实现封装,实现底层分离,支持arduino生态,
通过固件库模块化外设,由ArduinoAPI调用
参考FastShift的封装方式,由于F403A固件库升级,底层需要重新封装

一、通用定时器使用

1.计时

在这里插入图片描述

在这里插入图片描述
由图可以看出AHB时钟为240M
定时器时钟为240M

时间计算
Tout= ((arr+1)(psc+1))/Tclk;
Tclk:TIM3的输入时钟频率(单位为MHz)。
Tout:TIM3溢出时间(单位为us)。
例:计时1ms,输入时钟频率为240MHz。
  arr = 239,psc = 999。
  Tout = ((arr+1)
(psc+1))/Tclk = ((239+1) *(240+1))/240=1000(us)=1(ms)

void trm3_int_init(u16 arr, u16 psc)
{/* enable tmr1 clock */crm_periph_clock_enable(CRM_TMR3_PERIPH_CLOCK, TRUE);tmr_base_init(TMR3, arr, psc);tmr_cnt_dir_set(TMR3, TMR_COUNT_UP);tmr_interrupt_enable(TMR3, TMR_OVF_INT, TRUE);/* tmr1 overflow interrupt nvic init */nvic_priority_group_config(NVIC_PRIORITY_GROUP_0);nvic_irq_enable(TMR3_GLOBAL_IRQn, 1, 0);/* enable tmr3 */tmr_counter_enable(TMR3, TRUE);  
}void TMR3_GLOBAL_IRQHandler(void)
{ 		  TMR3->ists = 0;;lv_tick_inc(1);	     
}

1.

2.ETR外部时钟计数

对于外部脉冲(方波)计数,通用的方法为捕获比较方式,由于项目对于脉冲的精度要求比较高,在快速搭建代码测试过后,发现该方法并不能满足需求,进而寻求计数更为精确的方法----ETR计数。

定时器实际就是一个计数器
可选内部、外部、内部触发输入用作计数时钟
即使用外部触发ETR作为计数器触发源

参考https://blog.csdn.net/u010650845/article/details/81781670

4.ArduinoAPI - timer


void Timer_SetInterrupt(TIM_TypeDef* TIMx, uint32_t Time, Timer_CallbackFunction_t Function)
{uint16_t period = 0;uint16_t prescaler = 0;uint32_t clock = TIMER_GET_CLOCK_MAX(TIMx);if(!IS_TMR_ALL_PERIPH(TIMx) || Time == 0){return;}/*将定时中断时间转换为重装值和时钟分频值*/Timer_TimeFactorization(Time,clock,&period,&prescaler);/*定时中断配置*/Timer_SetInterruptBase(TIMx,period,prescaler,Function,TIMER_PREEMPTIONPRIORITY_DEFAULT,TIMER_SUBPRIORITY_DEFAULT);
}

三、ADC

1.ADC初始化(非DMA)


/*** @brief  ADC 配置* @param  ADCx: ADC地址* @retval 无*/
void ADCx_Init(adc_type* ADCx)
{adc_base_config_type adc_base_struct;if(ADCx == ADC1){crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);}else if(ADCx == ADC2){crm_periph_clock_enable(CRM_ADC2_PERIPH_CLOCK, TRUE);}else if(ADCx == ADC3){crm_periph_clock_enable(CRM_ADC3_PERIPH_CLOCK, TRUE);}else{return;}/* select combine mode */adc_combine_mode_select(ADC_INDEPENDENT_MODE);adc_base_default_para_init(&adc_base_struct);adc_base_struct.sequence_mode = FALSE;adc_base_struct.repeat_mode = FALSE;adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT;adc_base_struct.ordinary_channel_length = 1;adc_base_config(ADCx, &adc_base_struct);adc_resolution_set(ADCx, ADC_RESOLUTION_12B);adc_ordinary_conversion_trigger_set(ADCx, ADC_ORDINARY_TRIG_TMR1CH1, ADC_ORDINARY_TRIG_EDGE_NONE);adc_dma_mode_enable(ADCx, FALSE);adc_dma_request_repeat_enable(ADCx, FALSE);adc_interrupt_enable(ADCx, ADC_OCCO_INT, FALSE);adc_enable(ADCx, TRUE);while(adc_flag_get(ADCx, ADC_RDY_FLAG) == RESET);/* adc calibration */adc_calibration_init(ADCx);while(adc_calibration_init_status_get(ADCx));adc_calibration_start(ADCx);while(adc_calibration_status_get(ADCx));}
/*** @brief  获取 ADC 值* @param  ADCx: ADC地址* @param  ADC_Channel: ADC通道* @retval 无*/
uint16_t ADCx_GetValue(adc_type* ADCx, uint16_t ADC_Channel)
{
#if 0adc_ordinary_channel_set(ADCx, (adc_channel_select_type)ADC_Channel, 1, ADC_SAMPLETIME_47_5);adc_ordinary_software_trigger_enable(ADCx, TRUE);while(!adc_flag_get(ADCx, ADC_OCCE_FLAG));
#endifreturn adc_ordinary_conversion_data_get(ADCx);}

2.ADC_DMA 规则通道扫描

通道注册API

	pinMode(PWR_FWD_L_Pin, INPUT_ANALOG_DMA);pinMode(PWR_FWD_M_Pin, INPUT_ANALOG_DMA);pinMode(PWR_RET_L_Pin, INPUT_ANALOG_DMA);pinMode(PWR_RET_M_Pin, INPUT_ANALOG_DMA);

pinMode会调用ADC_DMA_Register

/*** @brief  注册需要DMA搬运的ADC通道* @param  ADC_Channel:ADC通道号* @retval 见ADC_DMA_Res_Type*/
ADC_DMA_Res_Type ADC_DMA_Register(uint8_t ADC_Channel)
{
#if 1/*初始化ADC通道列表*/static bool isInit = false;if(!isInit){uint8_t i;for(i = 0; i < ADC_DMA_REGMAX; i++){ADC_DMA_RegChannelList[i] = 0xFF;}isInit = true;}/*是否是合法ADC通道*/if(!IS_ADC_CHANNEL(ADC_Channel))return ADC_DMA_RES_NOT_ADC_CHANNEL;/*是否已在引脚列表重复注册*/if(ADC_DMA_SearchChannel(ADC_Channel) != -1)return ADC_DMA_RES_DUPLICATE_REGISTRATION;/*是否超出最大注册个数*/if(ADC_DMA_RegCnt >= ADC_DMA_REGMAX)return ADC_DMA_RES_MAX_NUM_OF_REGISTRATIONS_EXCEEDED;/*写入注册列表*/ADC_DMA_RegChannelList[ADC_DMA_RegCnt] = ADC_Channel;/*注册个数+1*/ADC_DMA_RegCnt++;
#endifreturn ADC_DMA_RES_OK;
}

ADC_DMA_Init函数必须放到所有ADC_DMA引脚注册完之后

/**
* @brief  ADC DMA 配置  配置ADC和对应DMA,固定ADC1 如果不需要DMA则使用ADCx_Init函数*       需要放到初始化最后,或者所有ADC_DMA引脚注册完之后* @param  无* @retval 无*/
void ADC_DMA_Init(void)
{uint8_t index;dma_init_type dma_init_structure;adc_base_config_type adc_base_struct;/*CLOCK CONFIG*/crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);/*INTERUPT COFIG*/nvic_irq_enable(DMA1_Channel1_IRQn, 0, 0);/*DMA CONFIG*/dma_reset(DMA1_CHANNEL1);dma_default_para_init(&dma_init_structure);dma_init_structure.buffer_size = ADC_DMA_RegCnt;dma_init_structure.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;dma_init_structure.memory_base_addr = (uint32_t)ADC_DMA_ConvertedValue;dma_init_structure.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;dma_init_structure.memory_inc_enable = TRUE;dma_init_structure.peripheral_base_addr = (uint32_t) (&(ADC1->odt));dma_init_structure.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;dma_init_structure.peripheral_inc_enable = FALSE;dma_init_structure.priority = DMA_PRIORITY_HIGH;dma_init_structure.loop_mode_enable = TRUE;dma_init(DMA1_CHANNEL1, &dma_init_structure);dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);dma_channel_enable(DMA1_CHANNEL1, TRUE);/*ADC CONFIG*/adc_combine_mode_select(ADC_INDEPENDENT_MODE);adc_base_default_para_init(&adc_base_struct);adc_base_struct.sequence_mode = TRUE;adc_base_struct.repeat_mode = TRUE;adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT;adc_base_struct.ordinary_channel_length = ADC_DMA_RegCnt;adc_base_config(ADC1, &adc_base_struct);for(index = 0; index < ADC_DMA_RegCnt; index++){adc_ordinary_channel_set(ADC1,(adc_channel_select_type)ADC_DMA_RegChannelList[index],index + 1,ADC_SAMPLETIME_41_5);}adc_ordinary_conversion_trigger_set(ADC1, ADC12_ORDINARY_TRIG_SOFTWARE, TRUE);adc_dma_mode_enable(ADC1, TRUE);adc_enable(ADC1, TRUE);adc_calibration_init(ADC1);while(adc_calibration_init_status_get(ADC1));adc_calibration_start(ADC1);while(adc_calibration_status_get(ADC1));}

两种方式 :1 DMA完成中断中再次软件触发,实现循环采样,中断频率较高
2 ADC_DMA_GetValue中软件触发,需要获取数据时才开启采集
通过CONFIG_ADC_CIRCLE_ENABLE 进行切换

void DMA1_Channel1_IRQHandler(void)
{if(dma_flag_get(DMA1_FDT1_FLAG) != RESET){dma_flag_clear(DMA1_FDT1_FLAG);#if (CONFIG_ADC_CIRCLE_ENABLE == 1)adc_ordinary_software_trigger_enable(ADC1, TRUE);
#elsedma_trans_complete_flag = 1;
#endif}
}

/*** @brief  获取DMA搬运的ADC值* @param  ADC_Channel:ADC通道号* @retval ADC值*/
uint16_t ADC_DMA_GetValue(uint8_t ADC_Channel)
{int16_t index;if(!IS_ADC_CHANNEL(ADC_Channel))return 0;index = ADC_DMA_SearchChannel(ADC_Channel);if(index == -1)return 0;#if (CONFIG_ADC_CIRCLE_ENABLE == 0) adc_ordinary_software_trigger_enable(ADC1, TRUE);while(dma_trans_complete_flag == 0);dma_trans_complete_flag = 0;
#endifreturn ADC_DMA_ConvertedValue[index];
}

六、USB HID IAP

1.准备好Bootloader和app

在这里插入图片描述

2.配置好时钟,一定要打开USB

在这里插入图片描述

3.将生成的时钟配置复制到bootloader和app对应位置

设置正确才能正确识别HID设备,并且免驱,否则无法识别usb

void system_clock_config(void)
{/* reset crm */crm_reset();/* enable hext */crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);/* wait till hext is ready */while(crm_hext_stable_wait() == ERROR){}/* config pll clock resource */crm_pll_config(CRM_PLL_SOURCE_HEXT, CRM_PLL_MULT_15, CRM_PLL_OUTPUT_RANGE_GT72MHZ);/* enable pll */crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);/* wait till pll is ready */while(crm_flag_get(CRM_PLL_STABLE_FLAG) != SET){}/* config ahbclk */crm_ahb_div_set(CRM_AHB_DIV_1);/* config apb2clk */crm_apb2_div_set(CRM_APB2_DIV_2);/* config apb1clk */crm_apb1_div_set(CRM_APB1_DIV_2);/* enable auto step mode */crm_auto_step_mode_enable(TRUE);/* select pll as system clock source */crm_sysclk_switch(CRM_SCLK_PLL);/* wait till pll is used as system clock source */while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL){}/* disable auto step mode */crm_auto_step_mode_enable(FALSE);/* update system_core_clock global variable */system_core_clock_update();
}

4 设置bootloader和app的起始位置

bootloader
在这里插入图片描述

app
在这里插入图片描述
在这里插入图片描述
保持一致

5 使用IAP Programmer下载,地址要设置为app地址

在这里插入图片描述

相关文章:

AT32固件库外设使用,ArduinoAPI接口移植,模块化

目录 一、ArduinoAPI移植一、通用定时器使用1.计时1.2.ETR外部时钟计数4.ArduinoAPI - timer 三、ADC1.ADC初始化&#xff08;非DMA&#xff09;2.ADC_DMA 规则通道扫描 六、USB HID IAP1.准备好Bootloader和app2.配置好时钟&#xff0c;一定要打开USB3.将生成的时钟配置复制到…...

【Postgres】Postgres常用命令

文章目录 1、导出数据库某张表2、导入某张表到数据库3、查看数据库占用磁盘页数情况4、查看数据库大小5、查看数据表大小6、查看索引大小7、对数据库中表索引按照大小排序8、对数据库中表按照大小排序9、回收空间&#xff08;建议先回收指定表&#xff09;10、设置主键自增序列…...

pthread 读写锁使用详解

pthread 读写锁使用 读写锁&#xff1a;提供了一种高效的机制来控制对共享资源的访问。允许多个线程同时读取共享资源&#xff0c;但只允许一个线程独占地写入访问。适用于读取远远超过写入的场景下&#xff0c;因为写入操作需要独占地访问资源&#xff0c;可能会影响读取操作…...

MySQL扩展语句

if not exists xiaobu&#xff1a;xiaobu这个表不存在&#xff0c;才会创建 zerofill&#xff1a;自动填充位置 1 0001 primary key&#xff1a;当前表的主键&#xff0c;主键只能有一个&#xff0c;而且唯一&#xff0c;而且不能为空 auto_increment&#xff1a;表示该字段…...

阿里云号码认证服务(一键登录)在连接wifi的情况下部分机型下存在的问题

手机型号&#xff1a; vivo S16 存在的现象&#xff1a; 安装手机卡(联通卡)&#xff0c;且连接wifi的情况下&#xff0c;APP登录唤起阿里云一键登录服务大概有90%左右必超时(按照阿里云一键登录官方文档设置的超时时间为5秒)。 解决方案&#xff1a; 1、APP端增加超时判断&…...

电脑屏幕监控软件,能够帮助企业完成哪些事情?

电脑屏幕监控软件是一种能够监控和管理员工在电脑上的操作行为的软件。分为两种监控方式&#xff1a;实时监控和屏幕记录监控。实时监控是对电脑屏幕进行实时录像&#xff0c;屏幕记录监控则是以屏幕快照的形式保存下来&#xff0c;供使用者随时查看。电脑屏幕监控软件&#xf…...

java--方法的其他形式

1.方法定义时&#xff1a;需要按照方法解决的实际业务需求&#xff0c;来设计合理的方法形式解决问题。 1.注意事项 ①如果方法不需要返回数据&#xff0c;返回值类型必须申明成void(无返回值申明)&#xff0c;此时方法内部不可以使用return返回数据。 ②方法如果不需要接收数…...

IDEA配置类、方法注释模板

一、打开 IDEA 的 Settings&#xff0c;点击 Editor–>File and Code Templates&#xff0c;点击右边 File 选项卡下面的 Class&#xff0c;在其中添加图中红框内的内容&#xff1a; /** * author li-kun * date ${YEAR}年${MONTH}月${DAY}日 ${TIME} */当你创建一个新的类…...

PowerDesigner 16数据库(mysql)逆向生成pdm

1、配置数据源 2、测试数据源 but~~~~没成功&#xff0c;shift...

Spring Cloud 之Feign

前言 Feign是一个声明式的Web服务客户端&#xff0c;使得编写HTTP客户端变得更简单。在Java程序中&#xff0c;只需要在方法前加上FeignClient注解&#xff0c;Feign就会自动创建一个HTTP客户端&#xff0c;向指定的URL发送请求。 核心概念 1、注解&#xff1a;在服务接口方…...

通用开源自动化测试框架 - Robot Framework

一、什么是 Robot Framework&#xff1f; 1. Robot Framework 的历史由来 Robot Framework是一种通用的自动化测试框架&#xff0c;最早由Pekka Klrck在2005年开发&#xff0c;并由Nokia Networks作为内部工具使用。后来&#xff0c;该项目以开源形式发布&#xff0c;并得到了…...

css position属性与js滚动

“视口”就是浏览器窗口中实际显示文档内容的区域&#xff0c;不包含浏览器的“外框”&#xff0c;如菜单、工具条和标签。文档则是指整个网页。 1 css 的position static 正常定位&#xff0c;是元素position属性的默认值&#xff0c;元素遵循常规流。 relative 相对定位&…...

python内置模块hashlib对于字符串的加密解密加盐

hash是一类算法而hashlib模块是Python的一个内置模块&#xff0c;主要功能是使用对应的hash算法&#xff0c;加密二进制内容解密二进制内容 常见的hash算法有md5、sha1&#xff0c;sha256, sha512等 特点 1.内容敏感,那怕一个很小的字符发生改变都很明显 2.不可逆,不能逆向求值…...

获取客户端请求IP及IP所属城市

添加pom依赖 <dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>2.6.5</version> </dependency> public class IpUtil { private…...

【洛谷 P1106】删数问题 题解(贪心+字符串)

删数问题 题目描述 键盘输入一个高精度的正整数 N N N&#xff08;不超过 250 250 250 位&#xff09;&#xff0c;去掉其中任意 k k k 个数字后剩下的数字按原左右次序将组成一个新的非负整数。编程对给定的 N N N 和 k k k&#xff0c;寻找一种方案使得剩下的数字组成…...

【Python · PyTorch】线性代数 微积分

本文采用Python及PyTorch版本如下&#xff1a; Python&#xff1a;3.9.0 PyTorch&#xff1a;2.0.1cpu 本文为博主自用知识点提纲&#xff0c;无过于具体介绍&#xff0c;详细内容请参考其他文章。 线性代数 & 微积分 1. 线性代数1.1 基础1.1.1 标量1.1.2 向量长度&…...

建模和图表工具:Software Ideas Modeler Crack

用于图表、软件设计和分析的 CASE 工具 Software Ideas Modeler 是一款智能CASE 工具和 图表软件&#xff0c;支持 UML、SysML、ERD、BPMN、ArchiMate、流程图、用户故事、线框图。 提升用户体验和人工智能集成 - Software Ideas Modeler 14.05 最近发布的 14.05 版本带来了一…...

Android开发,车载通讯应用——binder通讯原理解析

Binder简单理解 简单来说&#xff0c;Binder 就是用来Client 端和 Server 端通信的。并且 Client 端和 Server 端 可以在一个进程也可以不在同一个进程&#xff0c;Client 可以向 Server 端发起远程调用&#xff0c;也可以向Server传输数据&#xff08;当作函数参数来传&#…...

[算法]求n!在m进制下末尾有多少个0

参考链接&#xff1a;求n&#xff01;在m进制下末尾0的个数_.!零n,,m-CSDN博客 我们这里和参考链接不同 使用结构体去存储每个因数的信息 然后使用变量index作为索引&#xff0c;其最终值为因数的个数 具体原理&#xff1a; 例子1&#xff1a;求9&#xff01;在10进制下的…...

mysql之用户管理、权限管理、密码管理

用户管理 创建用户create user 杨20.0.0.13 identified by 123; 用户重命名rename user 杨20.0.0.13 to yang20.0.0.13; 删除用户drop user 杨20.0.0.13; 权限管理 查看用户权限show grants for 杨20.0.0.13; 赋予用户权限grant all privileges on *.* to 杨localhost id…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...