STM32标准库函数源码详解与实践指南
简介:STM32标准库函数源码是一个基于ARM Cortex-M内核的开发工具包,由意法半导体提供,便于开发者高效利用STM32微控制器的功能。库函数源码包括HAL、LL库、CMSIS、系统初始化、外设驱动、中断与事件处理及调试工具等关键组件,适合初学者和开发者深入学习和应用STM32系列微控制器。 
1. STM32标准库函数概述
在嵌入式系统开发领域,STM32微控制器因其高性能和灵活性而广受欢迎。为了满足开发者的不同需求,STM32提供了多种软件库,其中最著名的是标准库函数。本章将对STM32标准库函数进行概述,包括其设计理念、核心功能以及在项目开发中的应用。
1.1 标准库函数设计理念
STM32标准库函数旨在为开发者提供一个清晰、一致且易于使用的编程接口。它抽象了硬件细节,允许开发者无需深入了解底层硬件就能实现功能丰富的应用程序。标准库函数还提供了丰富的配置选项,使得开发者可以灵活地控制微控制器的硬件资源。
1.2 核心功能与特点
标准库函数的核心在于它提供的硬件抽象层,这层抽象使得对STM32的外设编程变得更加简洁。无论是GPIO、定时器还是通信接口,标准库都封装了相应的操作函数,提供了一套稳定可靠的API。此外,标准库还支持复杂的外设组合和事件处理机制,极大地方便了开发工作。
1.3 在项目开发中的应用
在实际项目开发中,STM32标准库函数的应用非常广泛。开发者可以利用标准库函数快速搭建原型系统,测试和验证概念。随着项目深入,标准库也为更高级的优化和定制化提供了可能。下一章将深入探讨HAL硬件抽象层的封装与使用,这将进一步阐明如何利用标准库函数简化开发流程。
2. HAL硬件抽象层的封装与使用
2.1 HAL库的组成和结构
2.1.1 HAL库的主要组件
STM32的HAL(Hardware Abstraction Layer)库是用于STM32微控制器的硬件抽象层库。HAL库的设计旨在提供一个标准化的软件接口,以简化和加速开发过程。HAL库的主要组件包括:
- 初始化代码 :用于初始化各个外设和配置时钟系统。
- 通用函数 :提供通用功能,如延时、内存操作等。
- 外设驱动 :为STM32的各种外设(如GPIO、ADC、USART等)提供API。
- 中断处理 :管理中断服务函数,处理与外设相关的中断事件。
- HAL库固件 :包含核心功能和硬件抽象层的实现代码。
2.1.2 HAL库的目录结构与文件组织
HAL库的目录结构清晰地组织了不同模块的代码和资源。典型的目录结构包括:
Inc:包含所有的头文件(.h),定义了HAL库提供的函数接口。Src:包含所有的源文件(.c),实现了接口定义的功能。Examples:提供基于HAL库的示例代码,方便开发者学习和验证。
2.2 HAL库的基本操作
2.2.1 初始化与配置
初始化和配置是使用HAL库的首要步骤。通过一系列函数调用,系统可以被配置为所需的运行状态。初始化通常包括:
- 时钟配置 :选择合适的时钟源,并配置系统时钟以满足性能需求。
- 外设初始化 :根据应用需求,初始化GPIO、ADC、定时器等外设。
- 中断配置 :设置所需的中断优先级和服务函数。
一个典型的初始化函数可能如下所示:
void HAL_MspInit(void)
{
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* System interrupt init */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
在此代码段中, HAL_MspInit 函数用于配置系统时钟和中断优先级分组。
2.2.2 HAL库中的通用函数
HAL库提供了一系列通用函数,用于实现标准的软件操作,例如延时、内存复制等。例如, HAL_Delay 函数可实现毫秒级延时:
void HAL_Delay(uint32_t Delay)
{
// 实现延时逻辑
}
2.3 HAL库的高级特性
2.3.1 低功耗模式的管理
HAL库支持多种低功耗模式,例如睡眠、停止和待机模式。通过特定的API,可以方便地管理设备的功耗。例如,进入睡眠模式的函数为:
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
在此函数中, PWR_MAINREGULATOR_ON 和 PWR_SLEEPENTRY_WFI 定义了睡眠模式的具体行为。
2.3.2 实时调试接口的使用
HAL库还提供了实时调试接口,这些接口可以用于监控程序运行状态,帮助开发者快速定位问题。例如,通过 HAL_Delay 函数实现的延时,开发者可以在调试过程中查看时间的流逝,帮助评估程序性能。
现在,我们已经完成了对HAL库的初步介绍。接下来,我们将深入了解LL库的设计理念与优势,以及如何在实际项目中应用这些功能。
3. LL低层库的特点与应用
3.1 LL库的设计理念与优势
3.1.1 LL库的性能优势
LL(Low Layer)库被设计为提供更接近硬件层面的操作,相较于HAL库,LL库的性能优势体现在其更高效的执行速度和更低的内存占用。由于LL库直接操作寄存器,它减少了抽象层的开销,使得开发者能够编写出更加优化的代码。在执行关键任务或对时间敏感的场合,这种性能优势尤为明显。
3.1.2 直接硬件访问的便利性
LL库使得开发者能够直接通过寄存器访问硬件资源。这样的设计使得开发者可以精确地控制外设的工作方式,进行自定义的配置,甚至是实现库函数本身未提供的功能。它极大地提升了编程的灵活性,对高级用户和性能要求较高的应用来说,是一个非常吸引人的选项。
3.2 LL库的实现原理
3.2.1 寄存器操作封装机制
LL库为每种外设提供了专门的函数来操作其控制寄存器。例如,GPIO的置位、清除和读取操作,都是通过相应的寄存器直接设置的。这种封装机制不仅保证了操作的安全性,也使得代码更加易读和易维护。下面是设置GPIO输出模式的一个示例代码:
/* 设置GPIO端口模式为输出 */
LL_GPIO_SetPinMode(GPIOx, GPIO_PIN_Y, LL_GPIO_MODE_OUTPUT);
3.2.2 内核驱动的构建
在LL库中,内核驱动(Kernel Driver)被简化为更小的功能块,每个块对应硬件的一个简单功能。这些驱动块被设计得尽可能简洁,以减少内存占用。尽管如此,为了保证功能的完整性,内核驱动仍然需要处理各种硬件事件和状态。比如,定时器的内核驱动需要处理溢出和更新事件。
3.3 LL库在项目中的应用实例
3.3.1 通用外设的初始化与控制
由于LL库提供直接寄存器访问,开发者在进行通用外设如ADC、DAC、SPI等初始化时可以做到非常细致的操作。例如,初始化ADC时,可以通过直接设置特定寄存器来配置采样时间、分辨率等参数。这种控制的精确性是其他库难以比拟的。
3.3.2 定制化的功能扩展
在特定的应用场景中,开发者可能需要定制化的功能扩展,这时LL库提供了极大的灵活性。比如,在没有现成的库函数支持某种特殊通信协议的情况下,可以借助LL库直接操作通信外设的寄存器来实现。
下面是一个使用LL库初始化SPI通信外设的代码片段:
/* 初始化SPI外设 */
void SPI_Init(void) {
LL_SPI_InitTypeDef LL_SPI_InitStruct = {0};
/* 使能SPI外设的时钟 */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPIx);
/* 配置SPI基本参数 */
LL_SPI_InitStruct.BaudRate = 3200000;
LL_SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
LL_SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
LL_SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
LL_SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
LL_SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
LL_SPI_InitStruct.Direction = LL_SPI_DIRECTION_2LINES;
LL_SPI_InitStruct.DozeMode = LL_SPI_DOZEMODE_DISABLE;
LL_SPI_InitStruct.CPOL = LL_SPI_POLARITY_HIGH;
LL_SPI_InitStruct.CPHA = LL_SPI_PHASE_2EDGE;
LL_SPI_Init(SPIx, &LL_SPI_InitStruct);
/* 使能SPI外设 */
LL_SPI_Enable(SPIx);
}
这个例子中,我们配置了SPI外设的速率、数据宽度、时钟极性和相位、主从模式、传输模式、休眠模式及CPOL和CPHA的设置。代码块后面需要有逻辑分析和参数说明等扩展性说明。
通过对LL库在项目中应用的深入学习,开发者可以更有效地利用STM32微控制器的硬件特性,从而设计出更可靠、高效且更具个性化的嵌入式系统。
4. CMSIS接口标准与Cortex-M处理器交互
CMSIS(Cortex Microcontroller Software Interface Standard)是一种为ARM Cortex-M系列处理器提供标准化软件接口的标准。它简化了与微控制器硬件的交互,使得软件开发者能够使用一致的接口访问处理器核心和外设。CMSIS为软件开发提供了一系列的组件,包括启动代码、硬件抽象层、中间件组件、系统函数以及调试功能。
4.1 CMSIS核心组件介绍
4.1.1 CMSIS标准的核心功能
CMSIS标准的核心功能包括对处理器核心寄存器的访问、系统初始化、运行时环境和中间件的管理等。CMSIS为硬件访问提供了一系列定义明确的数据类型、宏、函数和接口,使得开发者不需要依赖于特定厂商的硬件平台,从而实现硬件抽象。
4.1.2 硬件抽象层的统一接口
CMSIS定义了一个统一的硬件抽象层接口,允许开发者编写可移植的软件。开发者可以使用标准函数如 __set_PRIMASK() 来控制中断屏蔽状态,而不需要关心具体硬件实现的差异。这样的接口不仅减少了代码的复杂性,也提高了代码的重用性。
4.2 Cortex-M处理器的CMSIS支持
4.2.1 核心寄存器的访问
CMSIS为Cortex-M处理器的核心寄存器提供了一组访问函数。通过这些函数,开发者可以以一种安全和一致的方式访问和修改状态寄存器。例如,使用 CPSID_i 函数可以禁用中断,而 CPSIE_i 函数可以启用中断。
// 关闭全局中断
__disable_irq();
// 指令前的优先级不变,执行指令后恢复
__set_PRIMASK(1);
// 暂时关闭全局中断,直到下一个__enable_irq()或__set_PRIMASK(0)
__disable_irq();
// 读取特殊功能寄存器NVIC->IP[15]的优先级值
uint8_t priority = NVIC_GetPriority(EXTI15_IRQn);
// 设置特殊功能寄存器NVIC->IP[15]的优先级值
NVIC_SetPriority(EXTI15_IRQn, priority);
4.2.2 系统异常处理与调度
CMSIS提供了一套用于异常处理的函数,包括异常优先级配置、异常调度和异常返回。通过这些函数,开发者可以方便地配置中断优先级,并在中断服务例程中实现更复杂的调度逻辑。
// 设置异常优先级
NVIC_SetPriority(EXTI15_IRQn, 0); // 设置EXTI15_IRQn优先级为0
NVIC_EnableIRQ(EXTI15_IRQn); // 启用EXTI15_IRQn中断
// 在异常服务例程中
void EXTI15_IRQHandler(void)
{
if(EXTI->PR & (1 << 15)) // 检查EXTI线15上的挂起标志位
{
// 清除挂起标志位
EXTI->PR |= (1 << 15);
// 执行中断处理任务
}
}
4.3 CMSIS在实时操作系统中的应用
4.3.1 RTOS的集成与管理
CMSIS提供了RTOS接口,允许开发者在Cortex-M处理器上集成和使用实时操作系统。通过这些接口,开发者可以创建任务、同步对象和定时器等,实现多任务的管理和调度。
#include "cmsis_os2.h"
osThreadId_t tid1; // 定义一个线程标识符
void thread1(void *argument)
{
for(;;)
{
// 执行任务
}
}
int main(void)
{
osKernelInitialize(); // 初始化内核
tid1 = osThreadNew(thread1, NULL, NULL); // 创建线程tid1
if (tid1 == NULL)
{
// 如果线程创建失败则停止
while(1);
}
osKernelStart(); // 启动内核
for(;;);
}
4.3.2 系统优化与性能调优
CMSIS支持系统性能优化和调优。例如,开发者可以使用 osDelay() 函数来实现精确的延时,这对实时操作系统的任务调度非常重要。
void thread2(void *argument)
{
osDelay(100); // 延时100ms
// 继续执行后续任务
}
使用CMSIS与RTOS的结合,开发者可以利用CMSIS提供的硬件抽象层优势,同时通过RTOS实现复杂任务的管理和调度。这使得软件开发更加高效,同时确保了系统的实时性和稳定性。
5. 系统初始化与外设驱动开发
5.1 系统初始化的必要步骤
在开发基于STM32的嵌入式系统时,系统初始化是一个至关重要的步骤,它确保了微控制器在执行任何用户代码之前处于一个已知和可预期的状态。初始化过程通常包括对时钟系统、堆栈指针以及中断向量表的配置。
5.1.1 时钟系统配置
STM32的时钟系统非常灵活,它包括了多个时钟源,如内部高速时钟(HSI)、内部低速时钟(LSI)、外部高速时钟(HSE)和外部低速时钟(LSE)。此外,还可以使用相位锁定环(PLL)来生成更高的时钟频率。系统初始化时,开发者需要根据应用需求配置时钟树,确保CPU和其他外设能够获得正确的时钟信号。
// 示例代码:时钟系统配置
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 启用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;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_9; // 根据需要调整倍频系数
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟源,并设置AHB、APB总线时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
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;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3);
}
5.1.2 堆栈和中断向量表的初始化
在 Cortex-M 架构中,堆栈初始化通常涉及设置主堆栈指针(MSP)。中断向量表是中断处理的跳转表,必须放置在内存的开始位置。在启动代码中,通常会设置初始堆栈指针,并确保中断向量表位于正确的位置。
// 示例代码:堆栈初始化
void Reset_Handler(void) {
// 初始化数据段
extern int __main_stack_end;
extern int __main_stack_top;
__asm volatile(
"ldr sp, =__main_stack_top\n"
"b main"
);
}
// 中断向量表定义
typedef void (*pfunc)(void);
void (*g_pfnVectors[])(void) __attribute__((section(".isr_vector")));
g_pfnVectors[0] = (pfunc)&_estack; // 初始堆栈指针
g_pfnVectors[1] = Reset_Handler; // 重置中断处理函数
// ... 填充其他中断向量
5.2 外设驱动基础
5.2.1 GPIO的配置与使用
通用输入输出端口(GPIO)是STM32微控制器中最基本的外设之一。对GPIO的配置包括设置其模式(输入、输出、模拟或特殊功能)、输出类型(推挽或开漏)、速度、上拉/下拉电阻等。
// 示例代码:GPIO配置与使用
void GPIO_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIO端口时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
// 配置GPIO端口的模式和输出类型
GPIO_InitStructure.Pin = GPIO_PIN_13;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
// 使用GPIO
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 设置GPIO引脚为高电平
}
5.2.2 定时器的编程与应用
定时器是另一个常见的外设,可用于实现时间基准、产生定时中断、测量输入信号的频率和脉冲宽度等。定时器配置涉及时钟源选择、分频器、自动重装载值、中断使能等。
// 示例代码:定时器的编程与应用
void TIM_Configuration(void) {
TIM_HandleTypeDef htim1;
// 初始化定时器结构体
htim1.Instance = TIM1;
htim1.Init.Prescaler = (uint32_t)(SystemCoreClock / 10000U) - 1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 10000 - 1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&htim1);
// 启动定时器计数
HAL_TIM_Base_Start_IT(&htim1);
}
// 定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM1) {
// 定时器溢出处理逻辑
}
}
5.3 高级外设驱动功能
5.3.1 串口通信与配置
串行通信接口(如USART、UART)在嵌入式系统中被广泛使用于数据传输。串口的初始化包括设置波特率、字长、停止位和奇偶校验位。
// 示例代码:串口通信与配置
void USART_Configuration(void) {
UART_HandleTypeDef huart2;
// 初始化串口结构体
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
// 串口发送数据
char *data = "Hello, STM32!\r\n";
HAL_UART_Transmit(&huart2, (uint8_t *)data, strlen(data), HAL_MAX_DELAY);
}
5.3.2 ADC和DAC的精确控制
模拟数字转换器(ADC)和数字模拟转换器(DAC)允许微控制器处理模拟信号。ADC的配置包括选择输入通道、分辨率、采样时间等。DAC则涉及输出类型和数据格式的设置。
// 示例代码:ADC的精确控制
void ADC_Configuration(void) {
ADC_ChannelConfTypeDef sConfig = {0};
ADC_HandleTypeDef hadc1;
// 初始化ADC结构体
hadc1.Instance = ADC1;
HAL_ADC_Init(&hadc1);
// 配置ADC通道
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 开始ADC转换
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint32_t adcValue = HAL_ADC_GetValue(&hadc1);
}
// 示例代码:DAC的精确控制
void DAC_Configuration(void) {
DAC_HandleTypeDef hdac;
// 初始化DAC结构体
hdac.Instance = DAC;
HAL_DAC_Init(&hdac);
// 设置DAC输出值
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2047);
// 启动DAC输出
HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
}
在这一章节中,我们详细介绍了STM32微控制器系统初始化的必要步骤,以及基本和高级外设驱动的配置和使用。理解这些概念和技能对于设计和开发任何嵌入式系统至关重要。无论是用于GPIO控制还是进行ADC和DAC的精确控制,这些基础知识都为开发者提供了构建复杂应用的工具和方法。在接下来的章节中,我们将深入探讨中断与事件处理机制、调试辅助功能与代码优化,以及深入理解源码结构和底层硬件工作原理。
6. 中断与事件处理机制
6.1 STM32中断系统概述
6.1.1 中断向量表与优先级设置
STM32的中断系统是非常灵活且强大的,它使用中断向量表来存储中断服务函数_ISR的入口地址。中断向量表位于内存的起始位置,每个中断源都有一个固定的入口。
STM32的每个中断源都有一个优先级,这个优先级由用户配置。中断优先级的配置是非常重要的,因为它决定了在同时有两个或多个中断请求时,中断控制器如何决定响应哪一个中断。在STM32中,优先级由一个8位的数值来表示,数值越小,优先级越高。
NVIC_SetPriority(EXTI0_IRQn, 0x01); // 配置EXTI0的中断优先级为最高
6.1.2 外设中断的注册与管理
外设中断的注册与管理涉及到中断的使能、配置和响应。首先,需要使能外设的中断,然后配置中断触发条件,最后在中断服务函数_ISR中编写中断处理逻辑。
// 外设中断的注册示例
void EXTI0_IRQHandler(void) {
if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 处理中断
// ...
EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志位
}
}
6.2 中断服务函数_ISR的开发
6.2.1 ISR的设计原则
中断服务函数_ISR的设计原则主要有:简洁、高效、及时。ISR应该尽量短小,避免执行复杂的逻辑;ISR应该高效,避免阻塞;ISR应该尽快返回,避免影响系统的实时性。
6.2.2 事件管理与中断服务的协同
在STM32中,事件管理与中断服务是协同工作的。当外设发生中断时,中断服务函数会被调用,然后在中断服务函数中,我们可以根据事件的状态进行相应的处理。
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
// 处理定时器更新事件
// ...
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志位
}
}
6.3 实时事件处理优化
6.3.1 实时性需求分析与处理
在实时系统中,中断处理的实时性是非常重要的。为了保证实时性,我们需要分析中断处理的需求,比如中断处理的优先级、中断处理的响应时间等。
6.3.2 中断与轮询的平衡策略
在实际的开发中,我们需要平衡中断和轮询的使用。对于一些对实时性要求不是很高的事件,我们可以使用轮询的方式来处理,而对于一些对实时性要求很高的事件,我们需要使用中断来处理。
// 轮询方式处理
while(1) {
if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) == Bit_SET) {
// 处理事件
// ...
}
}
// 中断方式处理
void EXTIx_IRQHandler(void) {
if (EXTI_GetITStatus(EXTIx) != RESET) {
// 处理事件
// ...
EXTI_ClearITPendingBit(EXTIx); // 清除中断标志位
}
}
简介:STM32标准库函数源码是一个基于ARM Cortex-M内核的开发工具包,由意法半导体提供,便于开发者高效利用STM32微控制器的功能。库函数源码包括HAL、LL库、CMSIS、系统初始化、外设驱动、中断与事件处理及调试工具等关键组件,适合初学者和开发者深入学习和应用STM32系列微控制器。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)