引言
上一篇文章讲述了如何使用蓝牙连接stm32进行数据收发控制步进电机,这篇在之前的基础上通过移植操作系统(FreeRTOS或者其他的也可以,原理操作都类似)实现步进电机控制。
上篇博客指路:STM32蓝牙连接Android实现云端数据通信(电机控制-开源)_从蓝牙获取信息 发送到云端-CSDN博客https://blog.csdn.net/m0_74325713/article/details/146500274?spm=1011.2124.3001.6209
FreeRTOS介绍
(简单讲一下)
FreeRTOS 是一款开源的实时操作系统(Real-Time Operating System, RTOS),专为嵌入式系统和微控制器(MCU)设计。
核心特性
轻量级:内核代码仅需几 KB 内存,适合资源受限的微控制器(如 ARM Cortex-M、ESP32、AVR 等)。
可移植性:支持 40+ 种处理器架构,通过抽象层适配不同硬件平台。
实时性:提供确定性的任务调度机制,满足硬实时(Hard Real-Time)或软实时(Soft Real-Time)需求。
模块化设计:核心功能简洁,可通过插件扩展(如 TCP/IP 协议栈、文件系统、低功耗支持等)。
核心功能模块
任务(Tasks)
多任务并发执行,每个任务是一个独立的线程。
任务优先级可配置,支持抢占式调度(Preemptive)或协作式调度(Cooperative)。
调度器(Scheduler)
抢占式调度:高优先级任务可中断低优先级任务。
时间片轮转:同优先级任务按时间片分配 CPU。
支持协程(Coroutines,轻量级任务)。
同步与通信
队列(Queues):任务间传递数据的 FIFO 缓冲区,支持阻塞式读写。
信号量(Semaphores):二进制/计数信号量,用于资源管理和任务同步。
互斥量(Mutexes):防止资源竞争的互斥锁。
事件组(Event Groups):任务间事件通知机制。
内存管理
提供动态内存分配算法(如 heap_1 到 heap_5),支持不同场景的需求。
可自定义内存分配策略以适配硬件。
中断管理
中断服务程序(ISR)与任务间的高效通信。
延迟中断处理(Deferred Interrupt Handling)机制,减少中断延迟。
软件定时器
基于任务调度的软件定时器,支持单次或周期性触发回调函数。
功能实现
主要涉及以下五点功能:
1. 蓝牙通信:通过 USART 接收蓝牙指令,解析指令并执行相应的操作。
2. 步进电机控制:根据接收到的指令控制电机的启动、停止、转动方向和速度。
3. OLED 显示:实时显示电机的状态,如当前速度、运行模式等。
4. FreeRTOS 任务管理:使用 RTOS 管理不同的任务,即蓝牙数据处理和电机控制。
5. 中断处理:利用 TIM 定时器中断驱动步进电机的步进操作,确保精确的时序控制。
CubeMAX配置
这里只展示新增部分,其他部分配置和上篇博客中配置基本一致。
STM32蓝牙连接Android实现云端数据通信(电机控制-开源)_从蓝牙获取信息 发送到云端-CSDN博客
时钟基
选择 FREERTOS 系统 CMSIS_V2 版本。
列表添加对应蓝牙、电机控制的任务,主要优先级问题。(OLED也可以添加,感兴趣的朋友可以都加进去)
Keil函数添加
加入oled,motor相关函数,在usart.c补充回调函数。主函数中补充中断回调函数。基本把上篇博客的代码复制粘贴过去就可以用。oled.c、oled.h、oledfont.h 上篇全部给出,这里就不在重复写了。
motor.c 和 motor.h
#include "motor.h"
const uint16_t step_sequence[] = {
GPIO_PIN_8, // Step 1: IN1
GPIO_PIN_8 | GPIO_PIN_9, // Step 2: IN1+IN2
GPIO_PIN_9, // Step 3: IN2
GPIO_PIN_9 | GPIO_PIN_12, // Step 4: IN2+IN3
GPIO_PIN_12, // Step 5: IN3
GPIO_PIN_12 | GPIO_PIN_13, // Step 6: IN3+IN4
GPIO_PIN_13, // Step 7: IN4
GPIO_PIN_13 | GPIO_PIN_8 // Step 8: IN4+IN1
};
volatile uint8_t current_step = 0; // 当前步序号(volatile确保中断中可见)
volatile int8_t direction = 1; // 方向(1正转,-1反转)
volatile int32_t steps_remaining = 0; // 剩余步数
volatile uint8_t continuous_mode = 0; // 连续旋转模式标志 1-持续 0-停止
uint32_t current_speed = 100; // 当前速度(步/秒)
volatile uint32_t steps_divider = 1; // 步进分频系数(实际速度=1000Hz/steps_divider)
volatile uint32_t step_counter = 0; // 步进计数器
// 速度控制---PSC=72-1 ARR=999 HCLK=72MHZ
void Set_Stepper_Speed(uint32_t speed_steps)
{
// 限速
if(speed_steps < 10) speed_steps = 10; // 最低10步/秒
if(speed_steps > 1000) speed_steps = 1000; // 最高1000步/秒
// 更新当前速度
current_speed = speed_steps;
// 计算分频系数
uint32_t new_divider = (1000 + speed_steps / 2) / speed_steps;
if(new_divider < 1) new_divider = 1; // 允许最大速度
steps_divider = new_divider;
// 显示更新
uint8_t speed_str[5];
snprintf((char*)speed_str, sizeof(speed_str), "%4lu", speed_steps);
OLED_show_string(4,1, speed_str);
printf("Speed: %lu\n", speed_steps);
}
#ifndef _MOTOR_H_
#define _MOTOR_H_
#include <stdint.h>
#include "stm32f1xx_hal.h"
#include "stdio.h"
#include "oled.h"
extern const uint16_t step_sequence[8];
extern volatile uint8_t current_step;
extern volatile int8_t direction;
extern volatile int32_t steps_remaining;
extern volatile uint8_t continuous_mode;
extern uint32_t current_speed;
extern volatile uint32_t steps_divider;
extern volatile uint32_t step_counter;
#define SPEED_STEP 100 // 设定固定步长
void Set_Stepper_Speed(uint32_t speed_steps);
#endif
usart.c(结尾添加回调函数) usart.h(声明变量)
/* USER CODE BEGIN 1 */
volatile uint8_t rdata;
volatile uint8_t rflag = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart1) {
rflag = 1;
HAL_UART_Receive_IT(&huart1, (uint8_t*)&rdata, 1);
}
}
/* USER CODE END 1 */
// UART接收缓存
extern volatile uint8_t rdata;
extern volatile uint8_t rflag;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
主函数中,中断回调函数补充内容。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM4) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM2)
{
step_counter++;
if (step_counter >= steps_divider && (steps_remaining > 0 || continuous_mode))
{
step_counter = 0;
GPIOB->ODR &= ~(GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_12 | GPIO_PIN_13);
GPIOB->ODR |= step_sequence[current_step];
current_step = (current_step + direction + 8) % 8;
if (!continuous_mode) steps_remaining--;
if (!continuous_mode && steps_remaining <= 0)
{
HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_1);
GPIOB->ODR &= ~(GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_12 | GPIO_PIN_13);
}
}
}
/* USER CODE END Callback 1 */
}
freertos.c 中补充任务函数调用。
void StartBluetoothTask(void *argument)
{
/* USER CODE BEGIN StartBluetoothTask */
/* Infinite loop */
OLED_show_string(1, 1, "28BYJ-48:");
HAL_UART_Receive_IT(&huart1, (uint8_t *)&rdata, 1);
for (;;)
{
if (rflag == 1)
{
rflag = 0;
if (rdata == 'm') {
continuous_mode = 1;
direction = 1;
printf("START\n");
OLED_show_string(2, 1, "START ");
}
else if (rdata == 'n') {
continuous_mode = 0;
steps_remaining = 0;
printf("STOP\n");
OLED_show_string(2, 1, "STOP ");
}
else if (rdata == 'j' || rdata == 'k') {
if (rdata == 'j') {
current_speed += SPEED_STEP;
} else {
current_speed = (current_speed > SPEED_STEP) ? current_speed - SPEED_STEP : 10;
}
Set_Stepper_Speed(current_speed);
}
else {
int16_t angle = 0;
switch (rdata)
{
case 'a': angle = 90; break;
case 'b': angle = 180; break;
case 'c': angle = 270; break;
case 'd': angle = 360; break;
case 'x': angle = -90; break;
case 'y': angle = -180; break;
case 'z': angle = -270; break;
case 'w': angle = -360; break;
}
if (angle != 0)
{
steps_remaining = abs(angle) * 4096 / 360;
direction = (angle > 0) ? 1 : -1;
char angle_str[6];
snprintf(angle_str, sizeof(angle_str), "%4d", angle);
OLED_show_string(3, 1, (uint8_t *)angle_str);
printf("angle: %d\n", angle);
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&rdata, 1);
}
osDelay(10);
}
/* USER CODE END StartBluetoothTask */
}
/* USER CODE BEGIN Header_StartMotorTask */
/**
* @brief Function implementing the MotorTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartMotorTask */
void StartMotorTask(void *argument)
{
/* USER CODE BEGIN StartMotorTask */
/* Infinite loop */
step_counter = 0;
for (;;)
{
// 模拟PWM节拍(每 steps_divider 次循环走一步)
step_counter++;
if (step_counter >= steps_divider)
{
step_counter = 0;
if (steps_remaining > 0 || continuous_mode)
{
GPIOB->ODR &= ~(GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_12 | GPIO_PIN_13);
GPIOB->ODR |= step_sequence[current_step];
current_step = (current_step + direction + 8) % 8;
if (!continuous_mode) {
steps_remaining--;
}
}
else if (!continuous_mode && steps_remaining <= 0)
{
GPIOB->ODR &= ~(GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_12 | GPIO_PIN_13);
}
}
osDelay(1);
}
/* USER CODE END StartMotorTask */
}
完整代码
【免费】STM32F103C8T6-基于FreeRTOS系统实现步进电机控制资源-CSDN文库