STM32F103C8T6-基于FreeRTOS系统实现步进电机控制

news2025/5/25 20:31:40

引言

        上一篇文章讲述了如何使用蓝牙连接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文库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2336486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

macOS安装java

一、下载 官网Java Downloads | Oracle 安装载java8,下载对应的JDK Java Downloads | Oracle 二、双击安装 安装 完成 三、查看安装位置 打开终端窗口&#xff0c;执行命令&#xff1a; /usr/libexec/java_home -V /Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Content…

zkmall模块商城:B2C 场景下 Vue3 前端性能优化的广度探索与实践

ZKmall作为面向B2C场景的模块化电商平台&#xff0c;其前端性能优化在Vue3框架下的实践融合了架构设计、渲染机制与业务特性&#xff0c;形成了一套多维度的优化体系。以下从技术实现与业务适配两个维度展开分析&#xff1a; 一、Vue3响应式系统深度适配 ​Proxy驱动的精准更新…

【Netty篇】Future Promise 详解

目录 一、 Netty Future 与 Promise —— 异步世界的“信使”与“传话筒”&#x1f680;1、 理解 Netty Future2、 理解 Netty Promise 二、 代码案例解读&#x1f4bb;例1&#xff1a;同步处理任务成功&#x1f44d;例2&#xff1a;异步处理任务成功&#x1f4f2;例3&#xff…

视频分析设备平台EasyCVR安防视频管理系统,打造电石生产智能视频监控新体系

一、背景介绍 电石生产中的出炉工序是整个生产流程中最为繁重且危险的环节。在开堵炉眼的过程中&#xff0c;电石极易发生飞溅现象&#xff0c;尤其在进行吹氧操作时&#xff0c;人员灼伤的风险极高。鉴于此&#xff0c;该工序正逐步由传统的人工操作模式向智能化方向转变。然…

从 PyTorch 到 ONNX:深度学习模型导出全解析

在模型训练完毕后&#xff0c;我们通常希望将其部署到推理平台中&#xff0c;比如 TensorRT、ONNX Runtime 或移动端框架。而 ONNX&#xff08;Open Neural Network Exchange&#xff09;正是 PyTorch 与这些平台之间的桥梁。 本文将以一个图像去噪模型 SimpleDenoiser 为例&a…

Android 应用添加Tile到SystemUI QuickSettings

安卓源码里有谷歌给的关于 Tile 的说明。 frameworks/base/packages/SystemUI/docs/qs-tiles.md SystemUI QuickSettings 简称QS&#xff0c;指的是 下拉菜单里的区域。区域里的一个选项就是一个 Tile 。 下图是 frameworks/base/packages/SystemUI/docs/ 里的附图示例&#…

【MySQL】前缀索引、索引下推、访问方法,自适应哈希索引

最左前缀原则 对于INDEX(name, age)来说最左前缀可以是联合索引的最左N个字段, 也可以是字符串索引的最左M个字符。 SELECT * FROM t WHERE name LIKE 张%其效果和单独创建一个INDEX(name)的效果是一样的若通过调整索引字段的顺序, 可以少维护一个索引树, 那么这个顺序就是需要…

Android Studio开发知识:从基础到进阶

引言 Android开发作为移动应用开发的主流方向之一&#xff0c;曾吸引了无数开发者投身其中。然而&#xff0c;随着市场饱和和技术迭代&#xff0c;当前的Android开发就业形势并不乐观&#xff0c;竞争日益激烈。尽管如此&#xff0c;掌握扎实的开发技能仍然是脱颖而出的关键。本…

ocr-身份证正反面识别

在阿里云官网&#xff0c;申请一个token [阿里官方]身份证OCR文字识别_API专区_云市场-阿里云 (aliyun.com) 观察一下post请求body部分json字符串&#xff0c;我们根据这个创建一个java对象 先默认是人像面 public class IdentityBody {public String image;class configure…

单节锂电池4.2V升压5V都有哪些国产芯片推荐?国产SL4011高效,高性价比

针对单节锂电池&#xff08;4.2V&#xff09;升压至5V应用中 SL4011升压芯片 的核心优势解析&#xff0c;结合其技术参数与典型应用场景进行详细说明&#xff1a; 1. 宽输入电压与高兼容性 输入范围&#xff1a;2.7V-12V&#xff0c;完美覆盖单节锂电池全周期电压&#xff08;3…

机器学习 | 神经网络介绍 | 概念向

文章目录 &#x1f4da;从生物神经元到人工神经元&#x1f4da;神经网络初识&#x1f407;激活函数——让神经元“动起来”&#x1f407;权重与偏置——调整信息的重要性&#x1f407;训练神经网络——学习的过程&#x1f407;过拟合与正则化——避免“死记硬背” &#x1f440…

视频孪生重构施工逻辑:智慧工地的数字化升级

当"智慧工地"概念在2017年首次写入《建筑业发展"十三五"规划》时&#xff0c;行业普遍将其等同于摄像头与传感器的简单叠加。十年数字浪潮冲刷下&#xff0c;智慧工地的内涵已发生本质跃迁&#xff1a;从工具层面的信息化改造&#xff0c;进化为基于视频数…

六根觉性:穿透表象的清净觉知之光

在喧嚣的禅堂里&#xff0c;老禅师轻叩茶盏&#xff0c;清脆的声响划破沉寂。这声"叮"不仅震动耳膜&#xff0c;更叩击着修行者的心性——这正是佛教揭示的六根觉性在世间万相中的妙用。当我们凝视《楞严经》中二十五圆通法门&#xff0c;六根觉性犹如六道澄明之光&a…

spring:注解@Component、@Controller、@Service、@Reponsitory

背景 spring框架的一个核心功能是IOC&#xff0c;就是将Bean初始化加载到容器中&#xff0c;Bean是如何加载到容器的&#xff0c;可以使用spring注解方式或者spring XML配置方式。 spring注解方式直接对项目中的类进行注解&#xff0c;减少了配置文件内容&#xff0c;更加便于…

Halcon应用:九点标定-手眼标定

提示&#xff1a;若没有查找的算子&#xff0c;可以评论区留言&#xff0c;会尽快更新 Halcon应用&#xff1a;九点标定-手眼标定 前言一、Halcon应用&#xff1f;二、应用实战1、图形理解[eye-to-hand]&#xff1a;1.1、开始应用2 图形理解[eye-in-hand] 前言 本篇博文主要用…

【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(一)

自动引用计数 前言alloc/retain/release/dealloc实现苹果的实现 autoreleaseautorelease实现苹果的实现 总结 前言 此前&#xff0c;写过一遍对自动引用计数的简单学习&#xff0c;因此掠过其中相同的部分&#xff1a;引用计数初步学习 alloc/retain/release/dealloc实现 由于…

Python爬虫第15节-2025今日头条街拍美图抓取实战

目录 一、项目背景与概述 二、环境准备与工具配置 2.1 开发环境要求 2.2 辅助工具配置 三、详细抓取流程解析 3.1 页面加载机制分析 3.2 关键请求识别技巧 3.3 参数规律深度分析 四、爬虫代码实现 五、实现关键 六、法律与道德规范 一、项目概述 在当今互联网时代&a…

智慧城市像一张无形大网,如何紧密连接你我他?

智慧城市作为复杂巨系统&#xff0c;其核心在于通过技术创新构建无缝连接的网络&#xff0c;使物理空间与数字空间深度融合。这张"无形大网"由物联网感知层、城市数据中台、人工智能中枢、数字服务入口和安全信任机制五大支柱编织而成&#xff0c;正在重塑城市运行规…

网络安全·第四天·扫描工具Nmap的运用

今天我们要介绍网络安全中常用的一种扫描工具Nmap&#xff0c;它被设计用来快速扫描大型网络&#xff0c;主要功能包括主机探测、端口扫描以及版本检测&#xff0c;小编将在下文详细介绍Nmap相应的命令。 Nmap的下载安装地址为&#xff1a;Nmap: the Network Mapper - Free Se…

黑龙江 GPU 服务器租用:开启高效计算新征程

随着人工智能、深度学习、大数据分析等技术的广泛应用&#xff0c;对强大计算能力的需求日益迫切。GPU 服务器作为能够提供卓越并行计算能力的关键设备&#xff0c;在这一进程中发挥着至关重要的作用。对于黑龙江地区的企业、科研机构和开发者而言&#xff0c;选择合适的 GPU 服…