Keil MDK STM32系列

这篇和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 CoreFull MCU package的对应关系在README.md的表格中, 现在最新的是v2.6.7, 对应的完整库版本为v1.26.2
    • 这个库与Windows Keil5 MDK下使用的标准外设库Standard peripherals library不兼容, 后者已经不再更新, 最高版本到1.8.0
  • STM32Cube完整库的仓库

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

  1. 要么自己修订标准外设库, 使其兼容v2.6.x的CMSIS核心库, 这个需要对各外设的变量和结构体很熟, 我这样刚入门的估计是搞不定
  2. 要么不用标准外设库, 纯使用核心库变量开发. 这个难度也不小, 类似于回到8051开发的模式了, 每写一步都要查寄存器手册.
  3. 要么直接用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 里装一下

  1. 点击 New Project,
  2. 填入项目名称,
  3. 在 Board 中输入 stm32f401 在过滤结果的列表中, 选择 stm32f401cc, 如果是常见的 WeAct Studio BlackPill, 可以直接在底下找到对应的专门的board
  4. 选择 Framework 为 STM32Cube
  5. 如果不希望建在默认目录的话, 取消 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

需要修改的两处

  1. 模块启用部分. 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
  1. 设置外部晶振频率, 设置成板载晶振的频率
#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 %d\r\n", 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;
}

参考

Keil MDK STM32系列(十) Ubuntu下的PlatformIO开发环境的更多相关文章

  1. Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  2. Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  3. Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  4. Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  5. Keil MDK STM32系列(四) 基于抽象外设库HAL的STM32F401开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  6. Keil MDK STM32系列(五) 使用STM32CubeMX创建项目基础结构

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  7. Keil MDK STM32系列(六) 基于抽象外设库HAL的ADC模数转换

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  8. Keil MDK STM32系列(七) STM32F4基于HAL的PWM和定时器

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  9. Keil MDK STM32系列(八) STM32F4基于HAL的PWM和定时器输出音频

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  10. Ubuntu下的PHP开发环境架设

    Ubuntu下的PHP开发环境架设   今天重新装了ubuntu那么就吧过程记录下. 打开终端,也就是命令提示符. 我们先来最小化组建安装,按照自己的需求一步一步装其他扩展.命令提示符输入如下命令: ...

随机推荐

  1. Memory Bist

    SRAMC主要是对SRAM进行控制 对于SRAM的逻辑,根据地址将数据存储到SRAM中,然后根据地址将SRAM中的数据读取出来 如何测试Memory,生产工艺原因造成的问题,采用DFT或者Bist测试 ...

  2. OpenKruise :Kubernetes背后的托底

    本文分享自华为云社区<OpenKruise核心能力和工作原理>,作者:可以交个朋友. 一. 诞生背景 Kubernetes 自身提供的应用部署管理功能,无法满足大规模应用场景的需求,例如应 ...

  3. 战略设计- DDD

    随着系统的增长,它会变得越来越复杂,当我们无法通过分析对象来理解系统的时候,就需要掌握一些操纵和理解大模型的技术了.本文将介绍一些原则.遵循这些原则,就可以对非常复杂的领域进行建模.大部分这样的决策都 ...

  4. C#调用C++——CLR方式

    一直是在写C#,最近接触到的项目中有C#调用C++接口的逻辑,自己学习了下,写个步骤日志,C#掉用C++的托管代码 项目分三个项目:1.底层C++动态库项目,2.中间层的CLR项目,3.上层的C#项目 ...

  5. [转帖]下载 SQL Server Management Studio (SSMS)

    https://learn.microsoft.com/zh-CN/sql/ssms/download-sql-server-management-studio-ssms?view=sql-serve ...

  6. [转帖]Linux之pure-ftpd安装和使用

    一.pure-ftpd简介   PureFTPd是一款专注于程序健壮和软件安全的免费FTP服务器软件(基于BSD License),以安全和配置简单为设计目标,支持虚拟主机,IPV6,PAM等功能.. ...

  7. [转帖]【Redis】Redis中使用Lua脚本

    Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能. Lua具体语法参考:https://www.runoob. ...

  8. [转帖]Docker限制容器的资源

      docker在默认运行容器的情况下,是不会对运行的容器进行资源限制的,在自己的实验环境的话是随便你怎么弄的,不过在生产中是一定会对docker运行的容器进行资源限制的,如果不限制的话在生产中会带来 ...

  9. elementui中el-checkbox 选中时的详细介绍

    checkbox-group 把多个checkbox管理为一组(需要注意的坑) 很多时候我们需要会遇见这样的场景. 比如用户需要选择多个值. 这个时候我们需要把多个 checkbox 放置在check ...

  10. 【k哥爬虫普法】爬虫第一案,侵犯个人隐私,“入侵”短视频服务器!

    我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...