单片机
返回首页

Keil MDK STM32系列(十) Ubuntu下的PlatformIO开发环境

2022-08-15 来源:csdn

这篇和Keil MDK没什么关系, 但是HAL库和开发方式是一样的, 也放在这个系列下吧


PlatformIO

PlatformIO是VSCode的一个扩展, 主要面向的是嵌入式的开发, 因为VSCode的跨平台属性, PlatformIO也是跨平台的. 这里主要介绍在Ubuntu20.04下的PlatformIO环境.


安装, 略


PlatformIO下的STM32烧录工具

对于STM32, PlatformIO支持的烧录工具有 blackmagic, cmsis-dap, dfu, jlink, serial, stlink, 直接选stlink就可以了.


PlatformIO下的STM32封装库

在PlatformIO下, 在Platforms中安装ST STM32, 这个Platform中包含了多个适用于STM32开发的framework


Libopencm3

没找到安装路径... 这是一套面向ARM Cortex-M架构微处理器的开源固件库, 支持STM32, Atmel SAM3x, NXP LPC, TI LM4F等系列的MCU, 对这个不熟就不介绍了


Arduino

依托于Arduino的库环境, 使用Arduino的封装接口操作STM32, 适合初学者


package的路径在 [user home]/.platformio/packages/framework-arduinoststm32 , 大小接近500MB


Cmsis

这部分类似于Keil MDK下的SPL库, 包含多个package


cmsis核心库的安装路径是 /home/[your user]/.platformio/packages/framework-cmsis, 大小103MB

对应了STM32Cube完整库 Drivers/CMSIS/ 路径下的内容

多了一个Driver目录

stm32f4-framework库文件安装路径是 /home/[your user]/.platformio/packages/framework-cmsis-stm32f4, 大小30MB

这个库只是STM32Cube MCU Full Package 的核心定义部分, 在Github上的仓库地址: https://github.com/STMicroelectronics/cmsis_device_f4

这个库有自己的版本号v2.6.x, 容易与完整库的版本号混淆, 其与CMSIS Core和Full MCU package的对应关系在README.md的表格中, 现在最新的是v2.6.7, 对应的完整库版本为v1.26.2

这个库与Windows Keil5 MDK下使用的标准外设库Standard peripherals library不兼容, 后者已经不再更新, 最高版本到1.8.0

STM32Cube完整库的仓库

Github地址是 https://github.com/STMicroelectronics/STM32CubeF4

上面的framework库, 在这个仓库里的路径是 tree/master/Drivers/CMSIS/Device/ST/STM32F4xx, 可以对比两边的文件, 是一样的

完整库比较大, 压缩包接近300MB

综上, STM32F4的CMSIS环境下是没有可用的标准外设库(Standard peripherals library)的, 尝试过用1.8.0的标准外设库, 编译有不少错误, 所以用这个开发的话


要么自己修订标准外设库, 使其兼容v2.6.x的CMSIS核心库, 这个需要对各外设的变量和结构体很熟, 我这样刚入门的估计是搞不定

要么不用标准外设库, 纯使用核心库变量开发. 这个难度也不小, 类似于回到8051开发的模式了, 每写一步都要查寄存器手册.

要么直接用STM32Cube的完整外设库. 不过既然都用了STM32Cube的库了, 为什么不直接选framework时就选STM32Cube呢?

Stm32cube

文档: https://docs.platformio.org/en/latest/frameworks/stm32cube.html


和Windows下的STM32CubeMX使用了相同的HAL库, package路径 [user home]/.platformio/packages/framework-stm32cubef4 , 大小 78.3MB


这个package下的内容, 就是STM32CubeF4仓库的内容, 当前版本是1.26.2.


使用STM32CubeF4开发的项目, 用这个Framework是可以直接编译的.


STM32CubeF4 的文件结构

这个库有三个目录


STM32CubeF4/Drivers

这里包含了核心库 CMSIS, HAL库 STM32F4xx_HAL_Driver, 还有众多的外设支持, 简直是个宝库


├── Drivers

│   ├── BSP

│   │   ├── Adafruit_Shield

│   │   ├── Components

│   │   │   ├── ampire480272

│   │   │   ├── ampire640480

│   │   │   ├── Common

│   │   │   ├── cs43l22

│   │   │   ├── exc7200

│   │   │   ├── ft6x06

│   │   │   ├── i3g4250d

│   │   │   ├── ili9325

│   │   │   ├── ili9341

│   │   │   ├── l3gd20

│   │   │   ├── lis302dl

│   │   │   ├── lis3dsh

│   │   │   ├── ls016b8uy

│   │   │   ├── lsm303agr

│   │   │   ├── lsm303dlhc

│   │   │   ├── mfxstm32l152

│   │   │   ├── n25q128a

│   │   │   ├── n25q256a

│   │   │   ├── n25q512a

│   │   │   ├── nt35510

│   │   │   ├── otm8009a

│   │   │   ├── ov2640

│   │   │   ├── ov5640

│   │   │   ├── s25fl512s

│   │   │   ├── s5k5cag

│   │   │   ├── st7735

│   │   │   ├── st7789h2

│   │   │   ├── stmpe1600

│   │   │   ├── stmpe811

│   │   │   ├── ts3510

│   │   │   └── wm8994

│   │   ├── STM32412G-Discovery

│   │   ├── STM32446E_EVAL

│   │   ├── STM32469I-Discovery

│   │   ├── STM32469I_EVAL

│   │   ├── STM324x9I_EVAL

│   │   ├── STM324xG_EVAL

│   │   ├── STM32F401-Discovery

│   │   ├── STM32F411E-Discovery

│   │   ├── STM32F413H-Discovery

│   │   ├── STM32F429I-Discovery

│   │   ├── STM32F4-Discovery

│   │   ├── STM32F4xx-Nucleo

│   │   └── STM32F4xx_Nucleo_144

│   ├── CMSIS

│   │   ├── Core

│   │   ├── Core_A

│   │   ├── Device

│   │   ├── DSP

│   │   ├── Include

│   │   ├── Lib

│   │   │   └── GCC

│   │   ├── NN

│   │   ├── RTOS

│   │   └── RTOS2

│   └── STM32F4xx_HAL_Driver

│       ├── Inc

│       └── Src


STM32CubeF4/Middlewares

这个目录下是USB功能封装和一些常用的第三方库, 例如FatFS, LwIP等


├── Middlewares

│   ├── ST

│   │   ├── STemWin

│   │   ├── STM32_Audio

│   │   │   └── Addons

│   │   │       └── PDM

│   │   ├── STM32_USB_Device_Library

│   │   ├── STM32_USB_Host_Library

│   │   └── TouchGFX

│   └── Third_Party

│       ├── FatFS

│       ├── FreeRTOS

│       ├── LibJPEG

│       ├── LwIP

│       └── mbedTLS

STM32CubeF4/Utilities

这里是一些工具和字体


└── Utilities

    ├── CPU

    ├── Fonts

    ├── Log

    └── Media

        ├── Audio

        ├── Pictures

        └── Video

使用Stm32cube创建项目

打开PlatformIO:Home, 如果没装 Platform:ST STM32 的, 先在 Platform 里装一下


点击 New Project,

填入项目名称,

在 Board 中输入 stm32f401 在过滤结果的列表中, 选择 stm32f401cc, 如果是常见的 WeAct Studio BlackPill, 可以直接在底下找到对应的专门的board

选择 Framework 为 STM32Cube

如果不希望建在默认目录的话, 取消 Use Default Location 勾选, 指定位置, PlatformIO 会创建项目目录

platformio.ini

这个文件的内容如下, 一个项目里可以有多个env, 可以指定一个默认的, 在使用快捷键时, 会执行默认env的编译和写入.


[env:blackpill_f401cc]

platform = ststm32

board = blackpill_f401cc

framework = stm32cube


;通用的 stm32f401cc 配置

[env:stm32f401cc]

platform = ststm32

board = genericSTM32F401CC

framework = stm32cube

upload_protocol = stlink

board_build.stm32cube.custom_config_header = yes 

参数说明:


upload_protocol 用于指定不同的写入方式

board_build.stm32cube.custom_config_header = yes 禁止platformio自动生成stm32f4xx_hal_conf.h. 默认情况下, 在项目启动或编译时, platformio会自动在库文件目录, 基于stm32f4xx_hal_conf_template.h复制创建stm32f4xx_hal_conf.h, 而HAL项目中这个配置文件一般是和main.c一起放在用户代码目录下的, 如果按默认的方式, 编译时会优先使用库目录下的文件, 用户自己配置的就失效了. 加上这个选项可以禁止platformio自己生成这个文件.

.vscode/c_cpp_properties.json

这个文件很重要, 因为任何 include 的错误, 都可以在这里检查. 这个文件是 PlatformIO 自动生成的, 所以不需要去改它, 如果发现你修改了platformio.ini 后它没有更新, 重新打开 VS Code 就可以了.


stm32f4xx_hal_conf.h

从 [user home]/.platformio/packages/framework-stm32cubef4/Drivers/STM32F4xx_HAL_Driver/Inc 中复制 stm32f4xx_hal_conf_template.h 到 src 目录, 并重命名为 stm32f4xx_hal_conf.h


需要修改的两处


模块启用部分. define xxxx ENABLED 这部分只需要保留下面的这些, 其它全部注释掉, 只有用到的才需要取消注释

#define HAL_MODULE_ENABLED


#define HAL_GPIO_MODULE_ENABLED

#define HAL_EXTI_MODULE_ENABLED

#define HAL_DMA_MODULE_ENABLED

#define HAL_RCC_MODULE_ENABLED

#define HAL_FLASH_MODULE_ENABLED

#define HAL_PWR_MODULE_ENABLED

#define HAL_CORTEX_MODULE_ENABLED

设置外部晶振频率, 设置成板载晶振的频率

#define HSE_VALUE    8000000U /*!< Value of the External oscillator in Hz */

项目文件

以下文件都直接在 src 目录下创建, 演示A6, A7两个PIN连接的LED间隔2秒闪灯


main.c


#include 'main.h'


void SystemClock_Config(void);

void SystemClock_Config_HSE_84MHz(void);

static void MX_GPIO_Init(void);


int main(void)

{

  HAL_Init();

  SystemClock_Config_HSE_84MHz();

  MX_GPIO_Init();

  while (1)

  {

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6 | GPIO_PIN_7, GPIO_PIN_SET);

    HAL_Delay(2000);

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6 | GPIO_PIN_7, GPIO_PIN_RESET);

    HAL_Delay(2000);

  }

}


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 = 4;

  RCC_OscInitStruct.PLL.PLLN = 84;

  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_DIV2;

  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

  {

    Error_Handler();

  }

}


void SystemClock_Config_HSE_84MHz(void)

{

  RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};


  __HAL_RCC_PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  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 = 168;

  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;

  RCC_OscInitStruct.PLL.PLLQ = 4;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  {

    Error_Handler();

  }

  

  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_DIV2;

  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

  {

    Error_Handler();

  }

}


static void MX_GPIO_Init(void)

{

  GPIO_InitTypeDef GPIO_InitStruct = {0};


  /* GPIO Ports Clock Enable */

  __HAL_RCC_GPIOH_CLK_ENABLE();

  __HAL_RCC_GPIOA_CLK_ENABLE();


  /*Configure GPIO pin Output Level */

  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);


  /*Configure GPIO pins : PA6 PA7 */

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;

  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


}


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

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 %drn', file, line) */

  /* USER CODE END 6 */

}

#endif /* USE_FULL_ASSERT */

main.h

#ifndef __MAIN_H

#define __MAIN_H


#ifdef __cplusplus

extern 'C' {

#endif


/* Includes ------------------------------------------------------------------*/

#include 'stm32f4xx_hal.h'


void Error_Handler(void);


#ifdef __cplusplus

}

#endif


#endif /* __MAIN_H */

stm32f4xx_hal_msp.c

#include 'stm32f4xx_hal.h'


void HAL_MspInit(void)

{

  __HAL_RCC_SYSCFG_CLK_ENABLE();

  __HAL_RCC_PWR_CLK_ENABLE();

}

stm32f4xx_it.c

#include 'stm32f4xx_hal.h'

#include 'stm32f4xx_it.h'


/******************************************************************************/

/*           Cortex-M4 Processor Interruption and Exception Handlers          */

/******************************************************************************/


void NMI_Handler(void)

{

  while (1)

  {

  }

}


void HardFault_Handler(void)

{

  while (1)

  {

  }

}


void MemManage_Handler(void)

{

  while (1)

  {

  }

}


void BusFault_Handler(void)

{

  while (1)

  {

  }

}


void UsageFault_Handler(void)

{

  while (1)

  {

  }

}


void SVC_Handler(void)

{


}


void DebugMon_Handler(void)

{

}


void PendSV_Handler(void)

{

}


void SysTick_Handler(void)

{

  HAL_IncTick();

}

stm32f4xx_it.h

#ifndef __STM32F4xx_IT_H

#define __STM32F4xx_IT_H


#ifdef __cplusplus

 extern 'C' {

#endif


/* Exported functions prototypes ---------------------------------------------*/

void NMI_Handler(void);

void HardFault_Handler(void);

void MemManage_Handler(void);

void BusFault_Handler(void);

void UsageFault_Handler(void);

void SVC_Handler(void);

void DebugMon_Handler(void);

void PendSV_Handler(void);

void SysTick_Handler(void);


#ifdef __cplusplus

}

#endif


#endif /* __STM32F4xx_IT_H */

STM32F401CCxx 可用的 SystemClock_Config()

配置不正确的系统时钟会导致系统卡死, 下面两个方法是测试可用的


/**

* Generated by STM32Cube 1.26.x

* *) Enable HSE(25MHz), No LSE(32.768KHz)

* *) No PLL, No prescaler, HSE->SYSCLK->PHBPrescaler=1->AHB,APB1,APB2...

*/

void SystemClock_Config_HSE_25MHz(void)

{

  RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};


  __HAL_RCC_PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);


  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

  RCC_OscInitStruct.HSEState = RCC_HSE_ON;

  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  {

    Error_Handler();

  }


  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;

  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  //RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // Make APB2 12.5MHz


  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)

  {

    Error_Handler();

  }

}


/**

* Generated by STM32Cube 1.26.x

* *) Enable HSE(25MHz), No LSE(32.768KHz)

* *) Enable PLL

* *) HSE->PLL->/25->*168->/2->PPLCLK->SYSCLK(84MHz)->APB1 /2-> APB1

*/

void SystemClock_Config_HSE_84MHz(void)

{

  RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};


  __HAL_RCC_PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  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 = 168;

  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;

  RCC_OscInitStruct.PLL.PLLQ = 4;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  {

    Error_Handler();

  }

  

  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_DIV2;

  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

  {

    Error_Handler();

  }

}

用UART输出printf的宏定义

因为sdcc和keil5 mdk下的编译工具不一样, 所以这部分的定义也不一样, 可以用下面的宏语句统一处理


#if defined(__GNUC__)

int _write(int fd, char *ptr, int len)

{

    HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF);

    return len;

}

#elif defined (__ICCARM__)

size_t __write(int handle, const unsigned char * buffer, size_t size)

{

    HAL_UART_Transmit(&huart1, (uint8_t *) buffer, size, HAL_MAX_DELAY);

    return size;

}

#elif defined (__CC_ARM)

int fputc(int ch, FILE *f)

{

    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);

    return ch;

}

#endif


或者


#ifdef __GNUC__

#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)

#else

#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)

#endif /* __GNUC__ */


/**

  * @brief  Retargets the C library printf function to the USART.

  * @param  None

  * @retval None

  */

PUTCHAR_PROTOTYPE

{

  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);

  return ch;

}


参考

https://docs.platformio.org/en/latest/frameworks/stm32cube.html

https://docs.platformio.org/en/latest/tutorials/ststm32/stm32cube_debugging_unit_testing.html#tutorial-stm32cube-debugging-unit-testing

STM32 HAL库的介绍 https://blog.csdn.net/xuzhexing/article/details/90137754

HAL库的nRF24L01操作 https://github.com/pstolarz/NRF_HAL/blob/master/src/nrf_hal.cpp

同上 https://github.com/leech001/NRF24L01/blob/master/src/nrf24l01.c

同上 https://github.com/eos1d3/NRF24L01

同上 https://github.com/J20RC/STM32_RC_Transmitter/tree/master/software/Config


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 红外线探测报警器

  • 短波AM发射器电路设计图

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 开关电源的基本组成及工作原理

  • 用NE555制作定时器

    相关电子头条文章