赞
踩
希望做一款舵机复现手臂运动的桌面装置,分为穿戴端和桌面端。
穿戴端计划采用单片机收集2个陀螺仪数据,通过无线收发装置(验证版采用zigbee透传模块,后续考虑接入局域网控制)。
本实验目的为开发穿戴端的收发程序。
网购了两块开发板,商家商品标注不明,实际使用的是国产的pin-to-pin兼容芯片。但这也导致难以使用Cube IDE进行开发(烧写会报错“ Could not verify ST device! Abort connection.”有教程进行跳过:ST-Link设备连接。 Could not verify ST device! Abort connection._error in final launch sequence: failed to start gd-CSDN博客)。不过笔者电脑未安装STM32 ST-LINK Utility,遂作罢。选择Keil 5开发。
芯片是Geehy的,公司官网:珠海极海半导体有限公司 (geehy.com)
SDK下载地址:珠海极海半导体有限公司 | APM32F103系列 (geehy.com)(向下拉,选择APM32F10x_SDK 与 APM32F1xx_DFP Pack 下载,前者是代码库,后者是Keil Pack开发板资源)
芯片数据手册:APM32F103xC数据手册 V1.7.pdf (geehy.com)
我使用的不是官方推荐的APM32F103VC MINI开发板(使用手册:APM32F103VC MINI开发板使用说明书V1.1.pdf (geehy.com)),而是下面这款商品:https://m.tb.cn/h.gjxTn2heTm3fWv3?tk=d4Uo32ljgY6
实拍照片:
缺少部分外设,不过不影响使用官网软件开发包进行开发。
先双击安装开发板资源。
为了节省时间,我选择直接基于部分例程(APM32F10x_SDK_V1.8\Examples\GPIO\GPIO_Toggle)进行开发。后续有时间自己再重走一遍。
程序烧写采用ST-LINK连接(菜单栏:Flash -> Configure Flash Tools... -> Debug -> 下图标注处,选择连接方式)。
通过Keil 5打开项目文件夹APM32F10x_SDK_V1.8\Examples\GPIO\GPIO_Toggle\Project\MDK\GPIO_Toggle.uvprojx,进入项目编辑。
修改 Board_APM32F103_MINI.h 中的部分宏定义以适配开发板。开发板载LED(除电源指示LED)引脚PC13。找到相关代码,修改引脚(代码为修改后):
- /** @addtogroup APM32F103_MINI_LED
- * @{
- */
- #define LEDn 2
-
- #define LED2_PIN GPIO_PIN_13
- #define LED2_GPIO_PORT GPIOC
- #define LED2_GPIO_CLK RCM_APB2_PERIPH_GPIOC
然后就可以在main函数(所有#include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"的文件)中调用以下函数:
- void APM_MINI_LEDInit(Led_TypeDef Led);
- void APM_MINI_LEDOn(Led_TypeDef Led);
- void APM_MINI_LEDOff(Led_TypeDef Led);
- void APM_MINI_LEDToggle(Led_TypeDef Led);
操作LED2了。同样地,也可以将LED3重定义为某些与LED操作方式一致的外设。
在打开DMA前,先实现串口回响(收什么发什么)。
新增文件uart_driver.h、uart_driver.c,撰写串口相关函数。注意新增的头文件要放入指定的文件夹(如本项目中的APM32F10x_SDK_V1.8\Examples\GPIO\GPIO_Toggle\Include),如果想自定义头文件需要自行添加目录(菜单栏:Flash -> Configure Flash Tools... -> C/C++ -> 下图标注处,添加路径)。
在uart_driver.c中定义USART1的初始化函数、中断服务函数与收发函数:
- // 文件:uart_driver.c
- // 功能:串口回响
-
- #include <stdint.h>
-
- #include "apm32f10x_usart.h"
- // #include "apm32f10x_dma.h" //未开启DMA时不需要
- #include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"
-
- #include "uart_driver.h"
-
- // USART1 TEST 初始化
- // 非DMA版本
- void USART1_Init()
- {
-
- // 使用结构体初始化串口USART1
- USART_Config_T U1_Configer;
-
- U1_Configer.baudRate = 9600; // 波特率,9600
- U1_Configer.wordLength = USART_WORD_LEN_8B; // 数据位,8bit
- U1_Configer.stopBits = USART_STOP_BIT_1; // 停止位,1bit
- U1_Configer.parity = USART_PARITY_NONE; //校验位,0bit
- U1_Configer.mode = USART_MODE_TX_RX; // 模式,收发
- U1_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE; // 硬件流控制,无
-
- APM_MINI_COMInit(COM1, &U1_Configer); // 这是SDK自带的函数,包含GPIO初始化及串口设置
- __NVIC_EnableIRQ(USART1_IRQn); // 与STM32不同的函数
- USART_EnableInterrupt(USART1, USART_INT_RXBNE); // 开启串口1的接收缓冲区非空中断(RXBNE,与STM的RBNE不同)
-
- }
- //
-
- // USART1接收中断处理函数
- void USART1_IRQHandler(void)
- {
- if (USART_ReadIntFlag(USART1, USART_INT_RXBNE) != RESET)
- {
- // 读取接收到的数据
- uint16_t data[2] = {USART_RxData(USART1), 0};
-
- // 发送数据回显
- USART1_Send_Str(data);
-
- // 清除接收中断标志位
- USART_ClearIntFlag(USART1, USART_INT_RXBNE);
-
- // 重新开启中断
- USART_EnableInterrupt(USART1, USART_INT_RXBNE);
- }
-
- }
- //
-
- // 串口发送字符串函数
- void USART1_Send_Str(uint8_t* str)
- {
- uint8_t cnt = 0;
- while (*str && cnt <= USART1_TX_MAX_LEN) {
- cnt ++;
- USART_TxData(USART1, *str++);
- while (USART_ReadStatusFlag(USART1, USART_FLAG_TXC) == RESET) {
- // 等待发送完成
- }
- }
- }
在uart_driver.h文件中声明相关函数:
- // 文件:uart_driver.h
- // 功能:串口回响
-
- #ifndef _UART_DRIVER_H_
- #define _UART_DRIVER_H_
-
- #include "apm32f10x_usart.h"
-
- void USART1_Init(void);
- void USART1_IRQHandler(void);
- void USART1_Send_Str(uint8_t* str);
-
- #endif
main.c中#include "uart_driver.h",main函数:
- int main(void)
- {
- // ==================== 外设初始化 ====================
- APM_MINI_LEDInit(LED2);
- APM_MINI_LEDOff(LED2);
- USART1_Init();
- USART1_DMA_Init();
-
- while (1)
- {
- // ==================== 发送测试 ====================
-
- USART_TxData(USART1, 'X');
- Delay();
-
- }
测试效果:
实现与JY62的交互。该模块可通过串口以定频率(可调节,≤256Hz)持续发送数据帧,并可通过串口发送指令进行设置。详见:JY62产品资料 (yuque.com)
常规发送功能即可满足需求,接收功能有两种解决方案:(1)DMA接收定长数据:开启传输完成中断(DMA_INT_TC),每接收2个数据帧长度触发中断服务,进行数据帧截取与读取;(2)DMA接收不定长数据:开启串口空闲中断,每个数据帧之间中断触发中断服务,进行数据帧截取与读取。
这里先选择实现第一个方案,第二个后续找时间实现。根据资料,USART1 RX 对应的DMA通道为 DMA1 Channel5。在uart_driver.c中定义相关的初始化函数、中断服务函数与收发函数:
- // 文件:uart_driver.c
- // 功能:串口1 DMA定长消息接收
-
- #include <stdint.h>
- #include <stdio.h>
-
- // ================================================== Std Periph Driver ==================================================
- #include "apm32f10x_usart.h"
- #include "apm32f10x_dma.h"
- #include "apm32f10x_misc.h"
- #include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"
-
- #include "uart_driver.h"
- #include "pwm_driver.h"
- #include "jy62.h" // 我自己写的JY62消息解码程序头文件
- // MPU_frame_data数组就是上面这个文件定义的extern变量
- #include "main.h"
-
- uint8_t USART1_RX_Buffer[64] = {0};
- #define USART1_RX_BUFFER_SIZE 64
- #define USART1_TX_MAX_LEN 32
-
- // USART1 DMA接收 初始化
- void USART1_DMA_Init(void)
- {
- // 开启相关外设时钟
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOA);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);
- RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
-
- // GPIO初始化
- GPIO_Config_T GPIO_configStruct;
- /* Configure USART Tx as alternate function push-pull */
- GPIO_configStruct.mode = GPIO_MODE_AF_PP;
- GPIO_configStruct.pin = GPIO_PIN_9;
- GPIO_configStruct.speed = GPIO_SPEED_50MHz;
- GPIO_Config(GPIOA, &GPIO_configStruct);
- /* Configure USART Rx as input floating */
- GPIO_configStruct.mode = GPIO_MODE_IN_FLOATING;
- GPIO_configStruct.pin = GPIO_PIN_10;
- GPIO_Config(GPIOB, &GPIO_configStruct);
-
- // 初始化串口USART1
- USART_Config_T U1_Configer;
-
- U1_Configer.baudRate = 9600; // 波特率,9600
- U1_Configer.wordLength = USART_WORD_LEN_8B; // 数据位,8bit
- U1_Configer.stopBits = USART_STOP_BIT_1; // 停止位,1bit
- U1_Configer.parity = USART_PARITY_NONE; //校验位,0bit
- U1_Configer.mode = USART_MODE_TX_RX; // 模式,收发
- U1_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE; // 硬件流控制,无
- /* USART configuration */
- USART_Config(USART1, &U1_Configer);
- /* Enable USART */
- USART_EnableRx(USART1);
- USART_EnableTx(USART1);
- USART_Enable(USART1);
-
-
- // 配置 DMA
- DMA_Config_T DMA_ConfigStructure;
-
- DMA_ConfigStructure.peripheralBaseAddr = (uint32_t)/*(USART1->DATA)*/ (&(USART1->DATA_B)); // 外设地址
- DMA_ConfigStructure.memoryBaseAddr = (uint32_t)USART1_RX_Buffer; // 内存地址
- DMA_ConfigStructure.dir = DMA_DIR_PERIPHERAL_SRC; // 外设作为数据源
- DMA_ConfigStructure.bufferSize = USART1_RX_BUFFER_SIZE; // 缓冲区大小
- DMA_ConfigStructure.peripheralInc = DMA_PERIPHERAL_INC_DISABLE; // 外设地址不增
- DMA_ConfigStructure.memoryInc = DMA_MEMORY_INC_ENABLE; // 内存地址增
- DMA_ConfigStructure.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_BYTE; // 外设数据宽度为8位
- DMA_ConfigStructure.memoryDataSize = DMA_MEMORY_DATA_SIZE_BYTE; // 内存数据宽度为8位
- DMA_ConfigStructure.loopMode = DMA_MODE_NORMAL; // 正常模式
- DMA_ConfigStructure.priority = DMA_PRIORITY_HIGH; // 高优先级
- DMA_ConfigStructure.M2M = DMA_M2MEN_DISABLE; // 禁用内存到内存传输
-
- DMA_Reset(DMA1_Channel5);
- DMA_Config(DMA1_Channel5, &DMA_ConfigStructure); // 初始化DMA1通道
- DMA_ConfigDataNumber(DMA1_Channel5, 22); // 设置传输数据长度
-
- // 配置 DMA 中断
- NVIC_EnableIRQRequest(DMA1_Channel5_IRQn, 0, 0); // 注册中断服务
- DMA_EnableInterrupt(DMA1_Channel5, DMA_INT_TC);
- // APM32的TC(transmit complete)中断相当于STM32的FTF(full transmit finish)中断
- // 这里注意不要把标志位 DMA1_FLAG_INT_TC5 和中断类型 DMA_INT_TC 搞混
-
- // 初始化后初次使能USART1 DMA接收
- USART_EnableDMA(USART1, USART_DMA_RX);
- DMA_Enable(DMA1_Channel5);
- USART1_DMA_Receive();
- }
- //
-
-
- // 重启USART1 DMA接收函数
- void USART1_DMA_Receive()
- {
-
- // 使能 DMA 通道
- DMA_ConfigDataNumber(DMA1_Channel5, 22);
- DMA_Enable(DMA1_Channel5);
-
- // 配置USART1的DMA接收
- USART_EnableDMA(USART1, USART_DMA_RX);
-
- }
- //
-
-
- // DMA接收中断处理函数
- void DMA1_Channel5_IRQHandler(void)
- {
- // 解码
- if(DMA_ReadIntFlag(DMA1_INT_FLAG_TC5))
- {
- // 清除中断标志
- DMA_ClearIntFlag(DMA1_INT_FLAG_TC5);
-
- // 去使能 DMA
- DMA_Disable(DMA1_Channel5);
-
- // 数据帧处理,从两数据帧长的接收信息中截取0x55开头的消息
- // 保证接收到,但牺牲了一半的数据频率
- uint8_t i = 0;
- while(i + 10 < 22 && USART1_RX_Buffer[i] != 0x55)
- { i++;}
- uint8_t ii = 0;
- for(ii = 0; ii<10;ii++)
- {
- MPU_frame_data[ii] = USART1_RX_Buffer[i + ii];
- }
- MPU_frame_dataRecord(); // from JY62.h
-
- // 重使能 DMA
- USART1_DMA_Receive();
- }
- }
- //
-
-
- // 串口1发送字符串函数
- void USART1_Send_Str(uint8_t* str)
- {
- uint8_t cnt = 0;
- while (*str && cnt <= USART1_TX_MAX_LEN) {
- cnt ++;
- USART_TxData(USART1, *str++);
- while (USART_ReadStatusFlag(USART1, USART_FLAG_TXC) == RESET) {
- // 等待发送完成
- }
- }
- }
- //
类似地在在uart_driver.h文件中声明相关函数。在main函数中加入数据回传、在中断服务函数中加入LED点亮函数,测试是否正常工作符合预期:
- // 文件:main.c
- // 函数:int main(void)
- // 功能:隔时发送距中断剩余传输字节数
-
- int main(void)
- {
- // ================================================== 外设初始化 ==================================================
- APM_MINI_LEDInit(LED2);
- APM_MINI_LEDOff(LED2);
- USART1_DMA_Init();
-
- while (1)
- {
- // ================================================== 收发测试 ==================================================
-
- // 测试DMA传输数据cnt剩余量,正常应随字节接收从22到1后重置为22
- sprintf(txt, " %d \n", DMA_ReadDataNumber(DMA1_Channel5));
- USART1_Send_Str(txt);
- Delay();
-
- }
- }
定时发送字节,测试效果如下,符合预期:
先仿照USART1的常规收发代码,初始化USART2作为数据回传串口。
- // 文件:uart_driver.c
- // 功能:串口1 DMA定长消息接收;串口2 常规收发,数据回传
-
- #include <stdint.h>
- #include <stdio.h>
-
- // ================================================== Std Periph Driver ==================================================
- #include "apm32f10x_usart.h"
- #include "apm32f10x_dma.h"
- #include "apm32f10x_misc.h"
- #include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"
-
- #include "uart_driver.h"
- #include "pwm_driver.h"
- #include "jy62.h"
- #include "main.h"
-
- uint8_t USART1_RX_Buffer[64] = {0};
- #define USART1_RX_BUFFER_SIZE 64
- #define USART1_TX_MAX_LEN 32
-
- #define USART2_TX_MAX_LEN 32
-
- // USART1 DMA接收 初始化
- void USART1_DMA_Init(void)
- {
- // 开启相关外设时钟
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOA);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);
- RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
-
- // GPIO初始化
- GPIO_Config_T GPIO_configStruct;
- /* Configure USART Tx as alternate function push-pull */
- GPIO_configStruct.mode = GPIO_MODE_AF_PP;
- GPIO_configStruct.pin = GPIO_PIN_9;
- GPIO_configStruct.speed = GPIO_SPEED_50MHz;
- GPIO_Config(GPIOA, &GPIO_configStruct);
- /* Configure USART Rx as input floating */
- GPIO_configStruct.mode = GPIO_MODE_IN_FLOATING;
- GPIO_configStruct.pin = GPIO_PIN_10;
- GPIO_Config(GPIOB, &GPIO_configStruct);
-
- // 初始化串口USART1
- USART_Config_T U1_Configer;
-
- U1_Configer.baudRate = 9600; // 波特率,9600
- U1_Configer.wordLength = USART_WORD_LEN_8B; // 数据位,8bit
- U1_Configer.stopBits = USART_STOP_BIT_1; // 停止位,1bit
- U1_Configer.parity = USART_PARITY_NONE; //校验位,0bit
- U1_Configer.mode = USART_MODE_TX_RX; // 模式,收发
- U1_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE; // 硬件流控制,无
- /* USART configuration */
- USART_Config(USART1, &U1_Configer);
- /* Enable USART */
- USART_EnableRx(USART1);
- USART_EnableTx(USART1);
- USART_Enable(USART1);
-
-
- // 配置 DMA
- DMA_Config_T DMA_ConfigStructure;
-
- DMA_ConfigStructure.peripheralBaseAddr = (uint32_t)/*(USART1->DATA)*/ (&(USART1->DATA_B)); // 外设地址
- DMA_ConfigStructure.memoryBaseAddr = (uint32_t)USART1_RX_Buffer; // 内存地址
- DMA_ConfigStructure.dir = DMA_DIR_PERIPHERAL_SRC; // 外设作为数据源
- DMA_ConfigStructure.bufferSize = USART1_RX_BUFFER_SIZE; // 缓冲区大小
- DMA_ConfigStructure.peripheralInc = DMA_PERIPHERAL_INC_DISABLE; // 外设地址不增
- DMA_ConfigStructure.memoryInc = DMA_MEMORY_INC_ENABLE; // 内存地址增
- DMA_ConfigStructure.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_BYTE; // 外设数据宽度为8位
- DMA_ConfigStructure.memoryDataSize = DMA_MEMORY_DATA_SIZE_BYTE; // 内存数据宽度为8位
- DMA_ConfigStructure.loopMode = DMA_MODE_NORMAL; // 正常模式
- DMA_ConfigStructure.priority = DMA_PRIORITY_HIGH; // 高优先级
- DMA_ConfigStructure.M2M = DMA_M2MEN_DISABLE; // 禁用内存到内存传输
-
- DMA_Reset(DMA1_Channel5);
- DMA_Config(DMA1_Channel5, &DMA_ConfigStructure); // 初始化DMA1通道5
- DMA_ConfigDataNumber(DMA1_Channel5, 22);
-
- // 配置 DMA 中断
- NVIC_EnableIRQRequest(DMA1_Channel5_IRQn, 0, 0);
- DMA_EnableInterrupt(DMA1_Channel5, DMA_INT_TC);
-
- // 使能USART1 DMA接收
- USART_EnableDMA(USART1, USART_DMA_RX);
- DMA_Enable(DMA1_Channel5);
- USART1_DMA_Receive();
- }
- //
-
-
- // 重启USART1 DMA接收函数
- void USART1_DMA_Receive()
- {
-
- // 使能 DMA 通道
- DMA_ConfigDataNumber(DMA1_Channel5, 22);
- DMA_Enable(DMA1_Channel5);
-
- // 配置USART1的DMA接收
- USART_EnableDMA(USART1, USART_DMA_RX);
-
- }
- //
-
-
- // DMA接收中断处理函数
- void DMA1_Channel5_IRQHandler(void)
- {
- //debug
- APM_MINI_LEDOn(LED2);
-
- // 解码
- if(DMA_ReadIntFlag(DMA1_INT_FLAG_TC5))
- {
- // 清除中断标志
- DMA_ClearIntFlag(DMA1_INT_FLAG_TC5);
-
- // 去使能 DMA
- DMA_Disable(DMA1_Channel5);
-
- // 数据帧处理
- uint8_t i = 0;
- while(i + 10 < 22 && USART1_RX_Buffer[i] != 0x55)
- { i++;}
- uint8_t ii = 0;
- for(ii = 0; ii<11; ii++)
- {
- MPU_frame_data[ii] = USART1_RX_Buffer[i + ii];
- }
- MPU_frame_dataRecord();
-
- // 重使能 DMA
- USART1_DMA_Receive();
- }
- }
- //
-
-
- // 串口1发送字符串函数
- void USART1_Send_Str(uint8_t* str)
- {
- uint8_t cnt = 0;
- while (*str && cnt <= USART1_TX_MAX_LEN) {
- cnt ++;
- USART_TxData(USART1, *str++);
- while (USART_ReadStatusFlag(USART1, USART_FLAG_TXC) == RESET) {
- // 等待发送完成
- }
- }
- }
- //
-
-
- // USART2 初始化
- void USART2_Init()
- {
-
- // 使用结构体初始化串口USART2
- USART_Config_T U2_Configer;
- //NVIC_InitTypeDef NVIC_InitStructure;
-
- U2_Configer.baudRate = 9600; // 波特率,9600
- U2_Configer.wordLength = USART_WORD_LEN_8B; // 数据位,8bit
- U2_Configer.stopBits = USART_STOP_BIT_1; // 停止位,1bit
- U2_Configer.parity = USART_PARITY_NONE; //校验位,0bit
- U2_Configer.mode = USART_MODE_TX_RX; // 模式,收发
- U2_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE; // 硬件流控制,无
-
- APM_MINI_COMInit(COM2, &U2_Configer);
- NVIC_EnableIRQRequest(USART2_IRQn, 0, 0);
- USART_EnableInterrupt(USART2, USART_INT_RXBNE);
-
- }
-
- //
- // USART1接收中断服务函数
- void USART2_IRQHandler(void)
- {
- if (USART_ReadIntFlag(USART2, USART_INT_RXBNE) != RESET)
- {
- // 读取接收到的数据
- uint16_t data = USART_RxData(USART2);
-
- // 这里添加服务
-
- // 清除接收中断标志位
- USART_ClearIntFlag(USART2, USART_INT_RXBNE);
-
- // 重新开启中断
- USART_EnableInterrupt(USART2, USART_INT_RXBNE);
- }
- }
- //
-
- // 串口2发送字符串函数
- void USART2_Send_Str(uint8_t* str)
- {
- uint8_t cnt = 0;
- while (*str && cnt <= USART2_TX_MAX_LEN) {
- cnt ++;
- USART_TxData(USART2, *str++);
- while (USART_ReadStatusFlag(USART2, USART_FLAG_TXC) == RESET) {
- // 等待发送完成
- }
- }
- }
- //
-
main函数添加相关代码:
- // 文件:main.c
- // 函数:int main(void)
- // 功能:隔时回传JY62姿态角
-
- int main(void)
- {
- // ================================================== 外设初始化 ==================================================
- APM_MINI_LEDInit(LED2);
- APM_MINI_LEDOff(LED2);
-
- USART2_Init();
- USART1_DMA_Init();
-
- while (1)
- {
- // ================================================== 数据回传测试 ==================================================
- // 结构体来自jy62.h
- sprintf(txt, "r %f ", Angle.roll);
- USART2_Send_Str(txt);
- Delay();
- sprintf(txt, "p %f ", Angle.pitch);
- USART2_Send_Str(txt);
- Delay();
- sprintf(txt, "y %f ", Angle.yaw);
- USART2_Send_Str(txt);
- Delay();
-
- }
- }
实验效果:
下次实验计划进行定时器TMR与PWM输出的驱动,以实现呼吸灯与舵机控制。
第一次写博客,也是在对以往实验记录方式进行了反思之后做出的决定。以后会更多地把这些个人的小实验作线上记录,给自己一个动力,也希望能吸引到同好交流与指教。
APM32作为国产 Arm Cortex-M3 架构单片机的优秀代表之一,单片机性能不错,价格也比较实惠,对于个人开发者来说性价比良好。美中不足的是SDK提供的库文件与STM32的表述区别还是很多的,移植并没有那么容易;且部分注释仍然不够充足、清晰。不过充足的示例还是为学习提供了便利。以后会多考虑。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。