1. 硬件选型与系统架构设计这个项目最核心的硬件就是STM32F103C8T6开发板、蓝牙模块和SG90舵机。先说说为什么选这些硬件我在实际项目中测试过好几款芯片最终选定STM32F103C8T6是因为它性价比超高而且资源足够用。这款芯片有72MHz主频、64KB Flash、20KB RAM最关键的是它内置了丰富的外设接口包括多个USART和定时器正好满足我们同时控制蓝牙和舵机的需求。蓝牙模块我选的是市面上最常见的HC-05价格便宜而且稳定。实测下来这种模块在10米范围内都能稳定传输数据完全够用。这里有个坑要提醒大家买蓝牙模块一定要确认是主从一体的有些模块只能做从机这样就没法主动连接手机。我刚开始就买错过结果手机连不上折腾了好久才发现问题。舵机选SG90是因为它便宜又好用180度的转动范围足够大多数场景使用。不过要注意的是SG90的工作电压是4.8-6V而STM32和蓝牙模块都是3.3V系统所以电源设计要特别注意。我建议用两个独立电源供电或者用5V稳压模块给舵机单独供电。整个系统的架构是这样的手机通过蓝牙发送指令给STM32STM32解析指令后通过PWM控制舵机转动。听起来简单但实际做的时候有几个关键点要注意蓝牙通信的稳定性PWM信号的精度电源隔离防止干扰2. 蓝牙模块配置与通信实现蓝牙模块的配置是整个项目的第一步也是最容易出问题的地方。我用的是HC-05模块它支持AT指令配置。这里分享几个我踩过的坑首先蓝牙模块的默认波特率是38400但很多教程都说是9600这个要看具体模块型号。我建议先用ATBAUD?指令查询当前波特率然后再设置。设置波特率的指令是ATBAUD1其中1代表9600。注意设置完要断电重启才能生效。蓝牙模块与STM32的连接很简单就四根线VCC接3.3VGND接地TX接STM32的PA3(RX)RX接STM32的PA2(TX)这里有个重要细节蓝牙模块的RX要接STM32的TXTX接RX千万别接反了。我第一次就接反了结果数据死活传不过去。在代码实现上我用了HAL库的UART中断接收方式。这样不会阻塞主程序响应更及时。关键代码如下void User_UART2_Init(void) { g_uart2_handler.Instance USART2; g_uart2_handler.Init.BaudRate 9600; g_uart2_handler.Init.WordLength UART_WORDLENGTH_8B; g_uart2_handler.Init.StopBits UART_STOPBITS_1; g_uart2_handler.Init.Parity UART_PARITY_NONE; g_uart2_handler.Init.Mode UART_MODE_TX_RX; HAL_UART_Init(g_uart2_handler); HAL_UART_Receive_IT(g_uart2_handler, g_rx_buff, 1); }实际测试中发现蓝牙模块有时候会丢数据。解决方法是在手机端发送指令时加上校验码STM32收到后先校验再执行。我通常用简单的累加和校验虽然简单但很有效。3. 舵机控制原理与PWM实现SG90舵机的控制原理是通过PWM信号的脉宽来控制角度。具体来说0.5ms脉宽对应0度1.5ms脉宽对应90度2.5ms脉宽对应180度PWM信号的周期固定为20ms50Hz这个很重要周期不对舵机就不会正常工作。我在初期调试时就犯过这个错误把周期设成了10ms结果舵机抖得厉害。在STM32上实现PWM需要使用定时器。我用的TIM3通道1对应PA6引脚。关键配置如下void User_PWM_Init(uint16_t psc, uint16_t arr) { TIM_OC_InitTypeDef my_ocpwm_handler; my_pwm_handler.Instance TIM3; my_pwm_handler.Init.Prescaler psc; my_pwm_handler.Init.Period arr; HAL_TIM_PWM_Init(my_pwm_handler); my_ocpwm_handler.OCMode TIM_OCMODE_PWM1; my_ocpwm_handler.Pulse arr / 2; //初始90度位置 my_ocpwm_handler.OCPolarity TIM_OCPOLARITY_HIGH; HAL_TIM_PWM_ConfigChannel(my_pwm_handler, my_ocpwm_handler, TIM_CHANNEL_1); HAL_TIM_PWM_Start(my_pwm_handler, TIM_CHANNEL_1); }这里psc和arr的计算很关键。假设系统时钟是72MHz要产生50Hz的PWM信号预分频psc7172MHz/(711)1MHz自动重装载值arr199991MHz/2000050Hz这样每个PWM周期就是20ms。然后通过改变Pulse值来控制脉宽0度Pulse5000.5ms90度Pulse15001.5ms180度Pulse25002.5ms4. 系统集成与优化技巧把各个模块整合到一起时有几个优化点可以大幅提升系统性能首先是电源管理。舵机工作时电流可能达到500mA如果和STM32共用电源会导致电压不稳。我的解决方案是用两个USB口分别供电或者用大容量锂电池配合稳压模块。其次是抗干扰设计。蓝牙模块和PWM信号都容易受到干扰。我加了几个0.1uF的滤波电容在电源引脚附近效果立竿见影。还有PWM信号线要尽量短如果必须延长建议用双绞线。在软件层面我优化了指令解析算法。最初的版本只能处理单个字符指令如1转0度2转180度后来改成了可以解析角度值的字符串指令比如ANG90表示转到90度。这样控制更精准。主循环的处理也很关键。我采用了状态机的方式把蓝牙数据接收、指令解析、舵机控制分成不同状态避免阻塞。核心代码如下typedef enum { STATE_IDLE, STATE_RECEIVING, STATE_PROCESSING } SystemState; SystemState currentState STATE_IDLE; char commandBuffer[10]; uint8_t bufferIndex 0; while(1) { switch(currentState) { case STATE_IDLE: if(g_rx_flag) { currentState STATE_RECEIVING; g_rx_flag 0; } break; case STATE_RECEIVING: commandBuffer[bufferIndex] g_rx_buff[0]; if(g_rx_buff[0] \n || bufferIndex 9) { commandBuffer[bufferIndex] \0; currentState STATE_PROCESSING; } break; case STATE_PROCESSING: ProcessCommand(commandBuffer); bufferIndex 0; currentState STATE_IDLE; break; } }最后是增加了一些安全机制比如限制舵机转动速度防止过冲加入软件看门狗防止死机。这些细节处理让整个系统更加稳定可靠。
STM32F103C8T6:基于蓝牙模块的舵机无线控制方案
1. 硬件选型与系统架构设计这个项目最核心的硬件就是STM32F103C8T6开发板、蓝牙模块和SG90舵机。先说说为什么选这些硬件我在实际项目中测试过好几款芯片最终选定STM32F103C8T6是因为它性价比超高而且资源足够用。这款芯片有72MHz主频、64KB Flash、20KB RAM最关键的是它内置了丰富的外设接口包括多个USART和定时器正好满足我们同时控制蓝牙和舵机的需求。蓝牙模块我选的是市面上最常见的HC-05价格便宜而且稳定。实测下来这种模块在10米范围内都能稳定传输数据完全够用。这里有个坑要提醒大家买蓝牙模块一定要确认是主从一体的有些模块只能做从机这样就没法主动连接手机。我刚开始就买错过结果手机连不上折腾了好久才发现问题。舵机选SG90是因为它便宜又好用180度的转动范围足够大多数场景使用。不过要注意的是SG90的工作电压是4.8-6V而STM32和蓝牙模块都是3.3V系统所以电源设计要特别注意。我建议用两个独立电源供电或者用5V稳压模块给舵机单独供电。整个系统的架构是这样的手机通过蓝牙发送指令给STM32STM32解析指令后通过PWM控制舵机转动。听起来简单但实际做的时候有几个关键点要注意蓝牙通信的稳定性PWM信号的精度电源隔离防止干扰2. 蓝牙模块配置与通信实现蓝牙模块的配置是整个项目的第一步也是最容易出问题的地方。我用的是HC-05模块它支持AT指令配置。这里分享几个我踩过的坑首先蓝牙模块的默认波特率是38400但很多教程都说是9600这个要看具体模块型号。我建议先用ATBAUD?指令查询当前波特率然后再设置。设置波特率的指令是ATBAUD1其中1代表9600。注意设置完要断电重启才能生效。蓝牙模块与STM32的连接很简单就四根线VCC接3.3VGND接地TX接STM32的PA3(RX)RX接STM32的PA2(TX)这里有个重要细节蓝牙模块的RX要接STM32的TXTX接RX千万别接反了。我第一次就接反了结果数据死活传不过去。在代码实现上我用了HAL库的UART中断接收方式。这样不会阻塞主程序响应更及时。关键代码如下void User_UART2_Init(void) { g_uart2_handler.Instance USART2; g_uart2_handler.Init.BaudRate 9600; g_uart2_handler.Init.WordLength UART_WORDLENGTH_8B; g_uart2_handler.Init.StopBits UART_STOPBITS_1; g_uart2_handler.Init.Parity UART_PARITY_NONE; g_uart2_handler.Init.Mode UART_MODE_TX_RX; HAL_UART_Init(g_uart2_handler); HAL_UART_Receive_IT(g_uart2_handler, g_rx_buff, 1); }实际测试中发现蓝牙模块有时候会丢数据。解决方法是在手机端发送指令时加上校验码STM32收到后先校验再执行。我通常用简单的累加和校验虽然简单但很有效。3. 舵机控制原理与PWM实现SG90舵机的控制原理是通过PWM信号的脉宽来控制角度。具体来说0.5ms脉宽对应0度1.5ms脉宽对应90度2.5ms脉宽对应180度PWM信号的周期固定为20ms50Hz这个很重要周期不对舵机就不会正常工作。我在初期调试时就犯过这个错误把周期设成了10ms结果舵机抖得厉害。在STM32上实现PWM需要使用定时器。我用的TIM3通道1对应PA6引脚。关键配置如下void User_PWM_Init(uint16_t psc, uint16_t arr) { TIM_OC_InitTypeDef my_ocpwm_handler; my_pwm_handler.Instance TIM3; my_pwm_handler.Init.Prescaler psc; my_pwm_handler.Init.Period arr; HAL_TIM_PWM_Init(my_pwm_handler); my_ocpwm_handler.OCMode TIM_OCMODE_PWM1; my_ocpwm_handler.Pulse arr / 2; //初始90度位置 my_ocpwm_handler.OCPolarity TIM_OCPOLARITY_HIGH; HAL_TIM_PWM_ConfigChannel(my_pwm_handler, my_ocpwm_handler, TIM_CHANNEL_1); HAL_TIM_PWM_Start(my_pwm_handler, TIM_CHANNEL_1); }这里psc和arr的计算很关键。假设系统时钟是72MHz要产生50Hz的PWM信号预分频psc7172MHz/(711)1MHz自动重装载值arr199991MHz/2000050Hz这样每个PWM周期就是20ms。然后通过改变Pulse值来控制脉宽0度Pulse5000.5ms90度Pulse15001.5ms180度Pulse25002.5ms4. 系统集成与优化技巧把各个模块整合到一起时有几个优化点可以大幅提升系统性能首先是电源管理。舵机工作时电流可能达到500mA如果和STM32共用电源会导致电压不稳。我的解决方案是用两个USB口分别供电或者用大容量锂电池配合稳压模块。其次是抗干扰设计。蓝牙模块和PWM信号都容易受到干扰。我加了几个0.1uF的滤波电容在电源引脚附近效果立竿见影。还有PWM信号线要尽量短如果必须延长建议用双绞线。在软件层面我优化了指令解析算法。最初的版本只能处理单个字符指令如1转0度2转180度后来改成了可以解析角度值的字符串指令比如ANG90表示转到90度。这样控制更精准。主循环的处理也很关键。我采用了状态机的方式把蓝牙数据接收、指令解析、舵机控制分成不同状态避免阻塞。核心代码如下typedef enum { STATE_IDLE, STATE_RECEIVING, STATE_PROCESSING } SystemState; SystemState currentState STATE_IDLE; char commandBuffer[10]; uint8_t bufferIndex 0; while(1) { switch(currentState) { case STATE_IDLE: if(g_rx_flag) { currentState STATE_RECEIVING; g_rx_flag 0; } break; case STATE_RECEIVING: commandBuffer[bufferIndex] g_rx_buff[0]; if(g_rx_buff[0] \n || bufferIndex 9) { commandBuffer[bufferIndex] \0; currentState STATE_PROCESSING; } break; case STATE_PROCESSING: ProcessCommand(commandBuffer); bufferIndex 0; currentState STATE_IDLE; break; } }最后是增加了一些安全机制比如限制舵机转动速度防止过冲加入软件看门狗防止死机。这些细节处理让整个系统更加稳定可靠。