当前位置: 首页 > article >正文

初试FreeRTOS:创建上位机接收数据驱动4个舵机任务,如裸机般无感

解析函数上位机数据协议协议格式 (LD150舵机)[0x55][0x55][ID][长度][命令][数据...][校验和] 2字节 1字节 1字节 1字节 N字节 1字节帧头: 0x55 0x55ID: 舵机ID (1-4) 或 0xFE (广播)数据: 每组5字节 ID time_low time_high pos_low pos_high位置: 0-1000 对应 0-240° (转换为 0-180°)定义全局量// 全局解析结果volatile 确保中断安全 volatile int g_servo_s1 0; volatile int g_servo_s2 0; volatile int g_servo_s3 0; volatile int g_servo_s4 0; volatile int g_delay_ms 1000; volatile bool g_new_command_ready false;解析函数实现/* 解析4个舵机二进制协议 */ void parse_servo_frame(const uint8_t *data, uint16_t len) { if (len SERVO_FRAME_MIN_LEN) return; // 检查帧头 0x55 0x55 if (data[0] ! 0x55 || data[1] ! 0x55) return; // 检查广播ID或有效ID uint8_t servo_id data[2]; if (servo_id ! SERVO_BROADCAST_ID (servo_id 1 || servo_id 4)) return; // 检查数据长度 uint8_t data_len data[3]; uint8_t expected_len 4 data_len 1; // header(2) id(1) len(1) data(n) checksum(1) if (len expected_len) return; // 检查命令 (0x03 位置写命令) if (data[4] ! SERVO_CMD_POS_WRITE) return; // 验证校验和 uint8_t calc_sum calc_checksum(data, expected_len - 1); if (calc_sum ! data[expected_len - 1]) { // 校验和错误 return; } // 解析舵机数据 (每个舵机5字节: id time_low time_high pos_low pos_high) uint8_t *frame_data (uint8_t*)data[5]; uint8_t servo_count data_len / 5; for (uint8_t i 0; i servo_count; i) { uint8_t id frame_data[i * 5]; // 位置: pos pos_high * 256 pos_low, 范围0-1000对应0-240度 uint16_t pos frame_data[i * 5 3] | (frame_data[i * 5 4] 8); // 转换为角度 (0-240度 - 0-180度) uint16_t angle (pos * 180) / 240; if (angle 180) angle 180; // 更新对应舵机 switch (id) { case 1: g_servo_s1 angle; break; case 2: g_servo_s2 angle; break; case 3: g_servo_s3 angle; break; case 4: g_servo_s4 angle; break; } } // 解析运动时间 (用于DELAY) if (servo_count 0) { uint16_t move_time frame_data[1] | (frame_data[2] 8); g_delay_ms move_time; // 单位ms } g_new_command_ready true; }串口通信函数先配置好串口4开DMA接收和IDLE中断配置的代码不是重点就不放上来了。void UART4_IRQHandler(void) { /* USER CODE BEGIN UART4_IRQn 0 */ uint32_t flag 0; uint32_t temp; flag __HAL_UART_GET_FLAG(huart4, UART_FLAG_IDLE); if(flag ! RESET) { __HAL_UART_CLEAR_IDLEFLAG(huart4); HAL_UART_DMAStop(huart4); temp __HAL_DMA_GET_COUNTER(hdma_uart4_rx); uint16_t rx_len UART4_RX_BUFFER_SIZE - temp; parse_uart4_command((char*)uart4_rx_buffer, rx_len); HAL_UART_Receive_DMA(huart4, uart4_rx_buffer, UART4_RX_BUFFER_SIZE); } /* USER CODE END UART4_IRQn 0 */ HAL_UART_IRQHandler(huart4); /* USER CODE BEGIN UART4_IRQn 1 */ /* USER CODE END UART4_IRQn 1 */ }在parse_uart4_command中调用parse_servo_frame。void parse_uart4_command(char *data, uint16_t len) { if (len 0 || len UART4_RX_BUFFER_SIZE) return; // 解析LD150二进制协议 (帧头 0x55 0x55) if (len SERVO_FRAME_MIN_LEN (uint8_t)data[0] 0x55 (uint8_t)data[1] 0x55) { parse_servo_frame((const uint8_t*)data, len); } }FreeRTOS集成对于不熟悉FreeRTOS的开发者来说重头戏来了。首先解释下领域信息要实现周期性精确调度就必须用到以下函数void vTaskDelayUntil(TickType_tpxPreviousWakeTime, TickType_t xTimeIncrement)。pxPreviousWakeTime指向一个变量该变量保存任务最后一次解除阻塞的时间。第一次使用前该变量必须初始化为当前时间。xTimeIncrement周期循环时间。当时间等于 (pxPreviousWakeTime xTimeIncrement) 时任务解除阻塞。因此还需要pdMS_TO_TICKS(),将毫秒转换为FreeRTOS能理解的节拍数作为xTimeIncrementxTaskGetTickCount()获取自系统启动以来的时钟节拍数作为pxPreviousWakeTime。这样代码的逻辑就清晰起来了。初始化调用pdMS_TO_TICKS和xTaskGetTickCount。之后在循环中用g_new_command_ready标志判断parse_servo_frame解析有没有成功有的话就Servo_SetPulse驱动舵机。由于舵机对应的通道和目标角度都是全局且在其他位置定义的所以代码可以用一行表示很简洁。驱动任务结束后再把g_new_command_ready置0。若g_new_command_ready为假则直接跳过执行舵机驱动等待下一次延时。/* 舵机控制命令任务 - 处理UART4收到的舵机指令 */ void vTaskServoControl(void *pvParameters) { TickType_t xLastWakeTime; const TickType_t xFrequency pdMS_TO_TICKS(10); // 10ms检查一次 xLastWakeTime xTaskGetTickCount(); for(;;) { // 检查是否有新命令 if (g_new_command_ready) { // 解析LD150舵机二进制协议帧 // 位置范围: 0-1000 (对应0-240度) // 注意: Servo_Channel枚举从0开始 Servo_SetPulse(SERVO_CH1, g_servo_s1); Servo_SetPulse(SERVO_CH2, g_servo_s2); Servo_SetPulse(SERVO_CH3, g_servo_s3); Servo_SetPulse(SERVO_CH4, g_servo_s4); // 清除标志 g_new_command_ready false; } vTaskDelayUntil(xLastWakeTime, xFrequency); } }数据来源链路UART4 DMAIDLE中断 ↓ parse_uart4_command() ↓ (检测0x55 0x55帧头) parse_servo_frame() ↓ (解析ID、位置、校验和) g_servo_s1~s5 position ↓ g_new_command_ready true ↓ vTaskServoControl() 读取并更新PWM

相关文章:

初试FreeRTOS:创建上位机接收数据驱动4个舵机任务,如裸机般无感

解析函数上位机数据协议:协议格式 (LD150舵机)[0x55][0x55][ID][长度][命令][数据...][校验和]2字节 1字节 1字节 1字节 N字节 1字节帧头: 0x55 0x55 ID: 舵机ID (1-4) 或 0xFE (广播) 数据: 每组5字节 ID time_low time_high pos_low pos_high 位置: …...

GraphRAG退场了,BookRAG知识像翻书一样简单

你是否曾面对一本厚厚的说明书、技术手册或学术著作,在寻找某个具体信息时感到无从下手?传统的检索增强生成(RAG)方法在处理这类结构复杂的长文档时,常常力不从心。它们要么将文档视为一盘散沙的文本,丢失了…...

7张图讲透Claude Code架构(非常详细),Harness设计从入门到精通,收藏这一篇就够了!

1. 整体概述 众所周知,Claude Code不仅仅是Coding产品,更是一个通用的终端Agent:能循环思考、调度工具、治理权限、恢复上下文、稳定长会话… 如何研读项目源码呢? 首先,我让AI帮着梳理了下目录架构和模块职责&…...

02_Elasticsearch知识体系之Mapping映射设计与索引建模实战

02_Elasticsearch知识体系之Mapping映射设计与索引建模实战 Elasticsearch知识体系 基础概念层数据存储层【本文】查询语言层搜索能力层数据处理层集群架构层开发集成层AI增强层行业应用层 关键词: Elasticsearch、Mapping、动态映射、显式映射、字段类型、分片、副…...

四开关Buck-Boost双向DC-DC电源系统全套学习资料:STM32F334C8T6控制下...

四开关Buck-Boost双向DC-DC电源整套学习资料 功能:采用STM32F334C8T6芯片,能够根据输入电压和输出电压的大小关系,实现自动切换工作模式,将参数信息进行显示,并且可以实现稳压输出 程序仿真硬件软件说明报告原理图计算…...

COMSOL水力压裂岩石多裂隙损伤耦合模型及含离散裂隙Matlab建模文件

comsol水力压裂岩石多裂隙损伤耦合模型,含离散裂隙matlab建模文件地下三千米的页岩层正在经历一场暴力美学——高压水柱像手术刀般精准切开岩石,形成错综复杂的裂缝网络。这个看似野蛮的过程背后,隐藏着流-固-损伤三场耦合的精密舞蹈。今天我…...

STM32F107单片机驱动Dp83848以太网芯片程序 项目开发用到了Dp83848这一个以...

STM32F107单片机驱动Dp83848以太网芯片程序 项目开发用到了Dp83848这一个以太网芯片,本人发现其配置起来比较麻烦,所以整理了一份STM32F107单片机驱动Dp83848的程序代码例程,方便大家学习相关代码的配置最近在项目里折腾STM32F107和DP83848这…...

基于MATLAB的多种概率分布拟合与KS检验:从GEV到Exponential分布选择与实践

11种概率分布的拟合与ks检验,可用于概率分析,可靠度计算等领域 案例中提供11种概率分布,具体包括:gev、logistic、gaussian、tLocationScale、Rayleigh、Loglogistic、Lognormal、GeneralizedPareto、Weibull、Gamma、Exponential…...

如何高效构建Steam游戏DRM解除自动化解决方案:开源框架技术实现

如何高效构建Steam游戏DRM解除自动化解决方案:开源框架技术实现 【免费下载链接】Steam-auto-crack Steam Game Automatic Cracker 项目地址: https://gitcode.com/gh_mirrors/st/Steam-auto-crack Steam游戏DRM解除自动化解决方案为技术爱好者提供了一套完整…...

3步彻底解决Windows多显示器DPI缩放难题:SetDPI工具完全指南

3步彻底解决Windows多显示器DPI缩放难题:SetDPI工具完全指南 【免费下载链接】SetDPI 项目地址: https://gitcode.com/gh_mirrors/se/SetDPI 还在为Windows系统下多显示器DPI缩放不一致而烦恼吗?主显示器清晰锐利,副显示器却模糊不堪…...

跳点搜索算法(JPS)融合动态窗口法,JPS规划全局路径,动态窗口法执行动态避障

跳点搜索算法(JPS)融合动态窗口法,JPS规划全局路径,动态窗口法执行动态避障最近在搞机器人路径规划,总得在效率和安全之间找平衡。今天聊点实战的——把跳点搜索(JPS)和动态窗口法(D…...

claw-code 源码详细分析:子系统目录地图——几十个顶层包如何用五条轴(会话 / 工具 / 扩展 / 入口 / 桥接)读懂?

范围:src/ 下 顶层包(含 */__init__.py 的目录)与 与会话/runtime 强相关的根模块;与 result/01_start.md 第十三节、「清单—路由—会话」叙事一致。1. 为什么用五条轴 src/ 里同时存在: 大量占位包(读 re…...

S7-200 MCGS 基于PLC的小型水厂恒压供水系统 带解释的梯形图接线图原理图图纸,io分配

S7-200 MCGS 基于PLC的小型水厂恒压供水系统 带解释的梯形图接线图原理图图纸,io分配,组态画面最近在搞一个小型水厂的恒压供水系统项目,用西门子S7-200 PLC搭配MCGS组态软件,效果挺有意思的。这个系统核心就仨字——稳如狗&#…...

全贴合工艺中Cover Lens Mura不良的关键影响因素与优化策略

1. 全贴合工艺中的Mura现象解析 第一次看到全贴合屏幕上出现发黄或发白的斑块时,我还以为是产品运输途中受了撞击。后来在产线蹲守三个月才发现,这些被称为"Mura"的光学缺陷,其实是贴合工艺中的隐形杀手。Mura这个词源自日语"…...

深入解析build.prop:从基础参数到高级定制指南

1. build.prop文件到底是什么? 第一次在Android系统目录里看到build.prop这个文件时,我也是一头雾水。这玩意儿看起来就像个普通的文本文件,但里面密密麻麻的参数却让人望而生畏。后来才发现,它其实是Android系统的"身份证&q…...

别只盯着TCP!拆解大疆源码里MQTT协议的双通道设计:BASIC与DRC到底有啥区别?

大疆源码中的MQTT双通道设计:BASIC与DRC的工程哲学 在分析大疆无人机开源项目的通信架构时,一个有趣的设计选择跃然眼前——MQTT协议同时运行在TCP和WebSocket两种传输层上。这种看似冗余的配置背后,隐藏着对物联网通信场景的深刻理解。本文将…...

一台机器也能玩转StarRocks?手把手教你搭建单机测试环境(附避坑指南)

一台机器玩转StarRocks:单机测试环境搭建实战与避坑指南 当你想快速验证StarRocks的功能特性,或者进行本地开发测试时,单机部署是最便捷的选择。虽然官方并不推荐在生产环境中使用单机模式,但对于个人开发者、学生或测试场景来说&…...

一次删错索引引发的血案:手把手教你复盘线上购物车故障(附完整报告模板)

一次删错索引引发的血案:手把手教你复盘线上购物车故障 那天凌晨3点,我被刺耳的电话铃声惊醒。值班同事急促的声音从听筒传来:"购物车服务完全瘫痪,用户投诉像雪片一样涌来。"当我跌跌撞撞赶到公司时,整个技…...

从零搭建WebRTC SFU服务器:基于Mediasoup的1080P视频会议部署教程

从零搭建WebRTC SFU服务器:基于Mediasoup的1080P视频会议部署教程 视频会议已成为现代远程协作的核心工具,而WebRTC技术让浏览器间的实时音视频通信变得触手可及。但当你需要支持10人以上的高清会议时,单纯的P2P连接就会暴露出带宽和性能瓶颈…...

Claude Code 接入 DeepSeek、GLM、MiniMax 等国产大模型,保姆级教程!

每天免费领 1亿 Token,白嫖DeepSeek、GLM、MiniMax、Kimi等大模型! 这份指南是专门为那些“只想赶紧上手开干”的朋友准备的。 咱们不整那些虚头巴脑的理论,直接帮你搞定这几件事: 怎么把 Claude Code 装好如何确定它已经能跑通…...

拆解Clonezilla镜像:除了partclone,你还需要知道的底层原理与工具链

拆解Clonezilla镜像:从分卷压缩到文件系统的技术全景解析 当我们需要从Clonezilla备份中提取单个文件时,传统方法往往要求完整恢复整个镜像——这种"全有或全无"的方式在存储资源有限的情况下显得尤为笨重。本文将带您深入Clonezilla镜像的底层…...

CSS 语音参考

CSS 语音参考 概述 CSS(层叠样式表)是网页设计中的核心组成部分,它允许开发者控制网页元素的样式,包括颜色、布局、字体等。在网页设计中,有时我们需要为特定的元素添加语音提示,以便于视觉障碍者或需要语音辅助的用户使用。本文将详细探讨CSS中语音参考的实现方法,包…...

AngularJS Http详解

AngularJS Http详解 引言 AngularJS是一个流行的JavaScript框架,用于构建动态和响应式的web应用。在AngularJS中,HTTP请求是数据交互的重要组成部分。本文将详细介绍AngularJS的Http服务,包括其基本用法、高级特性以及如何处理异步请求。 AngularJS Http服务简介 Angula…...

网站主机技术概述

网站主机技术概述 随着互联网技术的飞速发展,网站已经成为企业和个人展示形象、提供服务的必要平台。网站主机的选择对于网站的稳定性和访问速度至关重要。本文将详细阐述网站主机技术,包括其基本概念、类型、选择标准以及未来发展趋势。 一、网站主机基本概念 网站主机,…...

《Foundation 网格 - 大型设备》

《Foundation 网格 - 大型设备》 引言 在当今科技日新月异的时代,大型设备在各个领域都扮演着至关重要的角色。其中,Foundation 网格作为一项创新技术,正在逐渐改变着我们的生产方式和生活质量。本文将深入探讨Foundation 网格的特点、应用以及未来发展趋势。 一、Founda…...

Go语言的缓存策略与实现

Go语言的缓存策略与实现 1. 缓存简介 缓存是一种在计算机系统中用于提高数据访问速度的技术,它通过将频繁访问的数据存储在高速存储介质中,减少对慢速存储介质的访问,从而提高系统的响应速度和吞吐量。 缓存的优势 提高性能:缓存可…...

Go语言的消息队列应用

Go语言的消息队列应用 1. 消息队列简介 消息队列是一种在分布式系统中用于异步通信的组件,它允许不同的服务之间通过消息进行通信,而不需要直接相互调用。消息队列可以解耦系统组件,提高系统的可靠性、可扩展性和弹性。 消息队列的优势 解耦&…...

YOLOv11涨点改进| AAAI 2025 |自研创新首发、特征融合改进篇| 使用TAMoE任务自适应混合专家模块,多专家协同合作,各司其职,助力各种任务的目标检测,图像分割,多模态融合目标检测涨点

一、本文介绍 🔥本文给大家介绍使用 TAMoE任务自适应混合专家模块 改进YOLOv11网络模型,把原本固定的特征传递与融合方式改造成一种自适应的特征分配机制,使模型能够根据不同检测层和不同目标尺度的需求,动态选择更合适的特征组合来参与主干网络、颈部网络或检测头的融合…...

计算机毕业设计:Python地铁多维度运营分析与数据管理系统 Django框架 数据分析 可视化 大数据 机器学习 深度学习(建议收藏)✅

博主介绍:✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业项目实战6年之久,选择我们就是选择放心、选择安心毕业✌ > 🍅想要获取完整文章或者源码,或者代做,拉到文章底部即可与…...

SMU Debug Tool完全指南:AMD Ryzen硬件调试的终极解决方案

SMU Debug Tool完全指南:AMD Ryzen硬件调试的终极解决方案 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https:…...