本帖最后由 飞向狙沙 于 2025-7-17 17:11 编辑
一、推荐理由业余玩家个人既全栈,需要的知识面比专业人士更广,你需要会编程,需要会电路设计,需要会UI,需要会3d设计,甚至需要会腐蚀电路板,需要会喷漆。作为一个平凡人,你拿什么跟专业人士拼深度,既然如此,在有限的时间里为什么不快速的入门上手,省出来更多的时间去做其他事情,等上升到一定高度了有需要的时候再回过头来去深入提高自己呢。 Arduino编程一直被一些专业或伪专业人士鄙视,他们确实有鄙视的资格,Arduino是在牺牲部分性能和灵活性的基础上完成的高度封装。但是作为业余玩家,很多人没有能力发挥出单片机的全部性能,甚至发挥不了单片机10%的性能,只是玩玩LED,搞搞时钟,性能瓶颈又和我们有什么关系呢,我们的首要目标不应该是快速实现想要的功能吗。另外Ardunio虽然性能较差,但是如果不考虑移植性完全可以混合编程,直接调用底层函数库,甚至直接操作寄存器,一样可以发掘性能潜力。 另外业余玩家对价格不敏感,这是业余玩家和专业工作者一个很大的区别,Arduino是牺牲了部分性能,但是这部分花点小钱很容易就能弥补回来,玩8266性能不够就上ESP32C3,还不够就上ESP32S3,对于一个玩性质的DIY,不需要量产的DIY,成本¥3块和¥13块并没有多大的区别,甚至对于部分人来说,上¥300的树莓派成本依旧不是问题,只要你实现了你想要的功能,看着自己完成的成品,你就已经成功了,压榨单片机性能这件事专业工作者没有选择,但是你有。 然后是最重要的一点,Arduino并不会影响玩家的学习高度,甚至有助于提升玩家的学习高度。随着代码规模的增大,代码封装变得更为重要,纷纷转向面向对象开发,C++天生支持面向对象,高C一等,上手起来更加顺畅。并且想要提升自己,阅读别人优秀代码是必不可少的一步,哪里有优秀的代码,自然是别人的优秀项目,别人的封装库,在这点上arduino的库函数有着无与伦比的魅力。
二、什么是ArduinoArduino是一款开源的电子原型平台,包含硬件(各种型号的开发板)和软件(Arduino IDE)。设计简单易用,适合初学者和专业开发者快速实现电子项目原型。本文推荐的主要是arduino这种编程语言,不限制相关硬件。 1. Arduino开发板- Arduino Uno
- Arduino Mega 2560
- Arduino Nano
- Arduino Due
- Arduino Leonardo
- 其他官方或非官方兼容开发板
- 其他核心板
部分开发板、核心板如下: 2. 开发软件、编程语言- Arduino IDE: 官方集成开发环境,支持跨平台(Windows, macOS, Linux)
- Arduino编程语言: 基于C/C++,但简化了许多复杂概念
- 库管理: 丰富的库生态系统,可轻松添加传感器、显示屏等外设支持
Arduino IDEArduino编程软件,推荐2.0+版本,类似Keil,自带智能提示,有限的代码跳转等功能,内置串口监视和串口绘图等功能,在线管理开发板、管理库,支持在线调试(1.0+版本不支持,2.0+支持但是还没用过,不多说了)。IDE整体好用,上手之后推荐再试试VS code,可以试用下,选合适自己的。 Arduino 编程风格编程风格统一,不管你用的官方板子还是stm32、esp32等三方板子,都可以用相同的函数编程,对于什么板子都有可能摸两把的玩家更容易上手,几乎无门槛。并且适合移植,如果使用基础函数开发,stm32开发的程序,想移植到esp32上简单到只需要修改几个IO定义即可完成。 - void setup() {
- // initialize digital pin LED_BUILTIN as an output.
- pinMode(LED_BUILTIN, OUTPUT);
- }
- // the loop function runs over and over again forever
- void loop() {
- digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
- delay(1000); // wait for a second
- digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
- delay(1000); // wait for a second
- }
复制代码 三、Arduino优点1. 简单易用,适合初学者- 直观的编程语言:基于 C/C++ 的简化语法,大量面向对象封装,避免复杂的底层操作、晦涩的时钟树配置、易忘的外设启动设置等。适合零基础用户,降低入门门槛,不至于出现半天点不亮,导致上火上头丧失理智,从入门到放弃。
- 集成开发环境(IDE):Arduino IDE 免费、轻量,支持跨平台(Windows/macOS/Linux),提供代码示例、库管理和串口监视器等工具。代码编辑、注释、格式化等操作更加便捷,界面更加美观。在线开发板管理,在线库管理,更换开发板、添加外设操作简单。
- 丰富的通用代码库:超过 2,000 个库支持常见硬件(如 Servo、DHT11、LCD)和协议(如 HTTP、MQTT),减少重复造轮子。通过标准化库(如Adafruit_ST7735)实现硬件无关化开发,仅需完成引脚映射即可驱动ST7735等常见屏幕,无需底层寄存器操作或依赖厂商Demo。各类传感器/执行器均有成熟库支持,提供硬件抽象层,降低移植成本。OneButton/MultiButton等库提供非阻塞式状态机实现,支持:单击/双击/多击识别、长短按区分防抖处理,典型按键功能3分钟内可完成集成,且符合用户预期交互体验。很多函数库封装的都很优秀,可以说比很多老开发的烂代码优秀多了,有时间阅读下这些优秀的库函数,能给你个人能力提升带来很大的帮助。
- 易于移植:核心API封装一致,如IO操作DigitalWrite、串口输出Serial.print等,一款单片机开发的功能,想移植到另一款单片机上只需要简单修改对应引脚映射即可完成。
2. 资源丰富,接口统一,众多官方维护Arduino开发板除了官方推出的型号,还有大量的三方板,STM32、PY32、ESP8266/ESP32、RP2040、NRF等大量单片机都有官方或非官方的移植支撑,现阶段移植工作已相对完善,包括DMA、PIO等相对高级功能也都有移植,并且拥有丰富的示例代码。作为业余玩家,很轻松做到0成本或低成本在STM32、ESP32、RP2040等单片机之间切换,专注于产品制作即可,不用再担心当前单片机资源不匹配,想换一款单片机又担心新款单片机难于入门,接触过ESP-IDF和Pico SDK的应该对此深有感触。 3. 跨平台兼容性- 支持多种主板:从入门级(Uno、Nano)到高性能(Due、Mega)、物联网(ESP8266/ESP32)等,满足不同需求。
- 外设兼容性:通过标准接口(如 GPIO、I²C、SPI、UART)连接传感器、显示屏、电机驱动等模块,生态配件丰富(如 Grove、Adafruit 系列)。
- 跨开发平台支持:Windows, macOS, Linux均有支持。
四、Arduino缺点
1. 性能限制Arduino的高度封装,简化代码使用的背后是大量冗余代码实现相关配置,如简单的IO操作,STC直接操作寄存器P2_0 = 1;或者并口操作P2=0xFF;效率极高。Arduino操作IO的digitalWrite有多层封装: - digitalWrite(LCD_RES, LOW);
- void digitalWrite(uint32_t ulPin, uint32_t ulVal)
- {
- digitalWriteFast(digitalPinToPinName(ulPin), ulVal);
- }
- static inline void digitalWriteFast(PinName pn, uint32_t ulVal)
- {
- digital_io_write(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), ulVal);
- }
- static inline void digital_io_write(GPIO_TypeDef *port, uint32_t pin, uint32_t val)
- {
- if (val) {
- LL_GPIO_SetOutputPin(port, pin);
- } else {
- LL_GPIO_ResetOutputPin(port, pin);
- }
- }
- 。。。底层继续调用LL库
复制代码
并且以上还是单个IO操作,Arduino不支持并口操作,要想操作PA0-PA15,需要单独针对每个IO调用digitalWrite,运行效率可能导致数倍甚至数十倍的降低。 2. 可操控灵活性降低简单易用和灵活操控很容易站在对立面,Arduino的一个优势就是不用关注如STM32那样的时钟树配置,同样的原因也就导致无法灵活配置系统时钟甚至每种外设的频率。这类情况在不同的框架下表现不一,如PY32支持如下图时钟源和主频配置,STM32则完全不支持。 3. 硬件资源占用较多 Arduino库函数因为大量多层封装通用函数,实现兼容操作,导致代码逻辑复杂,无效代码量剧增;Serial、Wire等核心库自动链接,即使用户未使用也有可能会被打包,导致打包文件整体偏大,过多占用flash空间。部分库分配缓冲区、大量定义全局变量等导致RAM被额外占用。 4. Arduion IDE不够智能 IDE智能了,但不完全智能,文件内方法支持跳转,引用库无法跳转;有时智能提示响应慢,自动输入不能及时更新;2.0版本依旧不支持工程管理,工具支持项目文件夹,但貌似无用; 5. Arduino IDE编译速度慢Arduino默认每次重新编译所有依赖库,不做缓存;单线程编译,无并行编译;代码臃肿,大量无效代码导致编译内容增加;优化策略保守等; 6. 硬件抽象层不完善 硬件抽象层封装依赖框架开发者水平,不通开发板不能确保函数功能一致;如单片机包含2个SPI接口,可能SPI2未封装,导致无法使用;部分单片机DMA不支持,想要使用只能混合调用底层库等; 五、怎么学习Arduino本章并不是教程贴,所以不会手把手嘴对嘴的教你怎样入门单片机,我的知识储备也不足以教导别人入门,只是以一个业余爱好者自学入门的过来人跟大家分享下自己的学习经历。再次声明:单片机没有速成,要是以点灯为门槛的话,确实可以无门槛实现快速入门,但是想玩转甚至玩好单片机,你要学的东西很多很多,没有人可以帮你速成;少整点心灵鸡汤,有那功夫不如想想自己啥功能没玩起来,问问deepseek咋用。想想自己能不能实现流水灯,能不能实现呼吸灯,太浮躁的人是没有资格玩单片机的。 第一步:拥有开发板想要入门Arduino首先需要一块开发板,推荐买官方板,资源更丰富,详细,但是相对硬件性能低点,某宝也就是十几块钱。如果已经单片机入门的可以优先考虑STM32核心板、RP2040核心板、ESP8266/ESP32核心板等,后期更具可玩性。特别是STM32,更加通用,学完Arduino再深入学习HAL、LL库也是个不错的方案。 第二步:点亮LED买开发板的优先以卖家教程材料为主,认识开发板,安装程序,了解管脚,学习编译下载,不管是谁家的开发板,一定有教程教你学会点亮第一个LED,知道LED为什么能亮,为什么能灭,下载程序控制它,那么你就跨出了学习Arduino或者学习单片机最重要的一步。零基础的推荐观看:Arduino零基础入门 第三步、驱动更多外设-阅读函数说明实际上控制单片机基本功能就是通过函数控制外设,控制LED亮灭、读取按键状态、读取ADC电压值,在此过程中就会接触到限流电阻、上拉电阻、模拟电压等扩展知识,这就是一个枯燥的补课状态,配合开发板试验体验其中的乐趣,多思考是什么、为什么、什么用。 然后是库函数,英文好点的,或者习惯使用翻译器的,可以直接查看官网Arduino函数说明,英文不太好的,可以找中文说明网,如Arduino编程参考,实际上有初中学历的结合翻译器足够使用了。阅读函数库的过程又是一个补课的过程,而且是一个重要的过程,零基础的需要好好阅读这部分内容,好好阅读这部分内容,好好阅读这部分内容,编程语法,这里就是数学里的加减乘除。  第四步:多做实验、多想、多问DS有些人喜欢看书学习,有些人喜欢看视频学习,但是都不要脱离开发板,自己去试验,教程教你点亮一颗LED,那你能不能点亮两颗LED;教程教你1秒闪烁一次,那你能不能一颗一秒闪一次,另一颗两秒闪一次;教程教你ADC读取外部电压,那你有没有想过它能读取的范围,超过范围的怎么处理;想不通的,去百度、去问Deepseek,得到结果就去继续试验,实在查不明白的在到各种论坛都可以去问,大家从来不抵触爱学习的人,但是大多人都会抵触从来不动脑子不走心的十万个为什么。 第五步:去做项目实际上到了这里至少已经算入门了,以后是否需要提升、能否提升看你对未来的要求,要是只想复刻别人的项目,那已经可以止步了,如果想继续玩下去,去做时钟、去做电压表、去做你各种感兴趣的项目,哪怕烂尾了也无所谓,在做的过程中总能积累各种各样的知识。论坛里、立创广场有各种各样的项目等着你去涉足,只有自己去做项目你才会发现到处是瓶颈,自己各种知识都欠缺。而且此处也不限于Arduino,从一开始就说了Arduino只是一个方便快速上手的工具. 六、Arduino封印解除本段内容非常规代码优化,涉及底层函数甚至寄存器混合开发,会破坏Arduino良好的可移植性,但是对于大部分不需要考虑移植的项目来说,能带来极大的性能提升,不失为一种优化手段。 例子1:IO速度优化4.2中有说到因为函数封装等原因导致Arduino性能下降,那么反过来思考,我想提升性能,解除封印不就好了,我不使用多层嵌套的库函数,而是直接使用底层函数不就不会性能下降了。如下代码,发现IO设置遇到瓶颈,想要加速,就去跟踪代码,找到源头,这里以STM32G0的代码为例:找到STM32G0的开发板框架路径 C:\Users\username\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32 vscode打开,方便检索 - //1.待优化Arduino函数
- digitalWrite(PA6, LOW);
- //2.vscode中全局搜索,找到源码定义,或者装好插件,使用代码跳转找到源码定义
- void digitalWrite(uint32_t ulPin, uint32_t ulVal)
- {
- digitalWriteFast(digitalPinToPinName(ulPin), ulVal);
- }
- //3.继续查找digitalWriteFast,并检查digitalPinToPinName定义,确定PIN映射
- static inline void digitalWriteFast(PinName pn, uint32_t ulVal)
- {
- digital_io_write(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), ulVal);
- }
- //4.继续查找digital_io_write定义,此处就是调用LL库函数
- static inline void digital_io_write(GPIO_TypeDef *port, uint32_t pin, uint32_t val)
- {
- if (val) {
- LL_GPIO_SetOutputPin(port, pin);
- } else {
- LL_GPIO_ResetOutputPin(port, pin);
- }
- }
- //5.直接使用LL库函数替换digitalWrite
- LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_6);
- //6.至此IO操作速度已经快了不少,但是Arduino库不支持并口操作
- //比如驱动8080并口屏幕时,效率依旧低下,那么检查LL库支持并口操作,直接用并口操作函数替换
- //速度又是至少8倍提升
- LL_GPIO_WriteOutputPort(GPIOA,0xFFFF);
- //7.继续优化,进入寄存器级别,直接使用WRITE_REG(GPIOx->ODR, PortValue)
- __STATIC_INLINE void LL_GPIO_WriteOutputPort(GPIO_TypeDef *GPIOx, uint32_t PortValue)
- {
- WRITE_REG(GPIOx->ODR, PortValue);
- }
- //8.继续检查WRITE_REG,为宏定义,即此时已经是直接操作寄存器,性能至少10倍+的提升
- #define WRITE_REG(REG, VAL) ((REG) = (VAL))
复制代码
例子2:STM32G时钟配置stm32duino框架封装时并不支持时钟树配置,默认使用内置HSI时钟源,默认16MHz,但是我想使用外部8MHz晶振,这个问题交给DS辅助处理: 
参考方法二,检查SystemClock_Config定义,在C:\Users\username\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.9.0\variants\STM32G0xx\G030C(6-8)T\generic_clock.c中有定义 - //WEAK函数,可覆盖
- WEAK void SystemClock_Config(void)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct = {};
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {};
- /* Configure the main internal regulator output voltage */
- HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
- /*
- * Initializes the RCC Oscillators according to the specified parameters
- * in the RCC_OscInitTypeDef structure.
- */
- //内部HSI时钟源,即RC时钟,16MHz
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
- RCC_OscInitStruct.HSIState = RCC_HSI_ON;
- RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
- RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
- RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
- RCC_OscInitStruct.PLL.PLLN = 8;
- RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
- RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
- 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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
- if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
- Error_Handler();
- }
- }
复制代码
配合DS完成修改,定义在ino中覆盖即可实现外部晶振配置 - void SystemClock_Config(void)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct = {};
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {};
- HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
- // 启用HSE并配置PLL
- 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; // PLL源改为HSE
- RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1; // HSE不分频 (8MHz)
- RCC_OscInitStruct.PLL.PLLN = 16; // 倍频至128MHz (8MHz * 16)
- RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 分频得64MHz系统时钟
- RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
- if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
- {
- Error_Handler();
- }
- // 时钟树配置保持不变
- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟源仍为PLL
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟=64MHz
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB时钟=64MHz
- if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
- {
- Error_Handler();
- }
- }
复制代码
小结 寄存器入门也好,库函数入门也好,Arduino入门也好,并不冲突,不要那么非此即彼。这里只是推荐学习下Arduino,方便横向扩展,而不是说随便学学Arduino就能搞天搞地搞空气。不要想着哪种语言能让你一日筑基、三天飞升,想玩好单片机需要学的知识是很多的,不光数电,还有模电,只要你想好好玩,就要好好学,逃不掉的。别人能帮你的,无非是能不能快速入门,提升自信心,自己耐心学习,才是王道。
|