STM32 学习日志:PWM 驱动直流电机、DWT 精确延时、DHT11 单总线通信与 Keil 工程模板

发布时间:2026/10/23 2:23:22

STM32 学习日志:PWM 驱动直流电机、DWT 精确延时、DHT11 单总线通信与 Keil 工程模板 STM32 学习日志PWM 驱动直流电机、DWT 精确延时、DHT11 单总线通信与 Keil 工程模板学习日期2026 年 8 月 19 日今天主要学习了 PWM 控制 TB6612FNG 有刷直流电机、DWT 微秒延时、DHT11 温湿度传感器的单总线通信以及手动建立 Keil 工程模板的方法。一、PWM 基础知识PWM 即脉宽调制通过控制一个方波中“高电平持续时间”的比例来控制平均输出效果。对于 STM32 定时器PWM 频率 定时器计数时钟 / [(PSC 1) × (ARR 1)] 占空比 CCR / (ARR 1) 占空比分辨率 1 / (ARR 1)其中PSC预分频器决定定时器计数速度。 ARR自动重装载值决定一个 PWM 周期需要计多少次。 CCR比较寄存器决定高电平持续到第几次计数。 CNT定时器当前正在计数的值。可以简单记忆PSC 控制计数速度。 ARR 决定 PWM 周期。 CCR 决定高电平时间也就是占空比。例如PSC 71 ARR 99 定时器时钟 72MHz则PWM 频率 72MHz / 72 / 100 10kHz如果CCR 20占空比约为20 / 100 20%如果CCR 80占空比约为80 / 100 80%注意代码中直接写20、80作为 PWM 速度值的前提是ARR 99。如果 ARR 不是 99CCR 的范围也必须跟着改变。二、TB6612FNG 电机驱动模块STM32 的 GPIO 引脚只能输出很小的电流不能直接驱动直流电机。因此需要 TB6612FNG 这种电机驱动模块。TB6612FNG 可以理解为电机的“功率放大器”STM32 负责发出控制命令。 TB6612FNG 负责使用 VM 电源给电机提供实际电流。本次电机 1 使用的核心连接如下STM32 引脚TB6612FNG 引脚作用PB0PWMAPWM 调速信号PA4AIN1电机方向控制PA5AIN2电机方向控制PA7STBY驱动芯片使能KEY1用户按键切换快慢KEY2用户按键切换正反转需要重点记住PWMA控制电机速度。 AIN1、AIN2控制电机方向。 STBY控制驱动芯片是否工作。 AO1、AO2连接电机两根线。 VM电机电源。 VCC驱动芯片逻辑电源。 GNDSTM32、模块、电机电源必须共地。三、为什么 PWM 极性通常设置为 HighTB6612FNG 的 PWMA/PWMB 是高电平有效。CubeMX 中设置CH Polarity High表示PWM 高电平驱动器允许输出。 PWM 低电平驱动器停止输出或进入制动状态。这样逻辑最直观CCR 越大 → 高电平时间越长 → 电机获得驱动的时间越长 → 电机转速越快注意PWM 极性不决定电机正反转。PWM控制速度。 AIN1/AIN2控制方向。四、TB6612FNG 电机控制程序1. 先启动 PWM再使能电机驱动芯片在/* USER CODE BEGIN 2 */中加入HAL_TIM_PWM_Start(htim3,TIM_CHANNEL_3);HAL_GPIO_WritePin(STBY_GPIO_Port,STBY_Pin,GPIO_PIN_SET);含义第一句让 TIM3_CH3 开始从 PB0 输出 PWM。 第二句把 STBY 拉高让 TB6612FNG 退出待机并开始工作。如果 STBY 没有拉高即使 PWM 和方向引脚都正确电机也不会转动。2. 编写电机方向与速度控制函数voidMotor_Set(uint8_tdir,uint16_tspeed){__HAL_TIM_SET_COMPARE(htim3,TIM_CHANNEL_3,speed);if(dir0){HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(AIN2_GPIO_Port,AIN2_Pin,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_RESET);HAL_GPIO_WritePin(AIN2_GPIO_Port,AIN2_Pin,GPIO_PIN_SET);}}这个函数有两个参数dir方向。 speedPWM 比较值也就是 CCR 值。方向逻辑AIN1AIN2电机状态高低正转低高反转如果实际电机方向和程序描述相反通常只是电机两根线接反了。可以交换 AO1/AO2 的电机接线或者交换程序中正反转逻辑。3. 定义速度和状态变量#defineSPEED_LOW20#defineSPEED_HIGH80uint8_tspeed_mode0;uint8_tdir_mode0;含义SPEED_LOW低速 PWM 值。 SPEED_HIGH高速 PWM 值。 speed_mode 0低速。 speed_mode 1高速。 dir_mode 0正转。 dir_mode 1反转。4. 按键控制速度和方向if(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin)GPIO_PIN_SET){speed_mode!speed_mode;uint16_tspeed(speed_mode0)?SPEED_LOW:SPEED_HIGH;Motor_Set(dir_mode,speed);while(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin)GPIO_PIN_SET);}!speed_mode的作用是切换 0 和 10 变 1。 1 变 0。三目运算符(speed_mode0)?SPEED_LOW:SPEED_HIGH;意思是如果当前是低速模式speed 取 SPEED_LOW。 否则 speed 取 SPEED_HIGH。while等待松手的作用是防止按住按键时不断切换状态。实际使用时还应加入约 10ms 到 20ms 的消抖延时否则机械按键可能一次按下被识别多次。五、DWT 精确延时DWT 是 Cortex-M 内核自带的高速计数器全称为Data Watchpoint and Trace 数据观察点与跟踪单元初学阶段最常用的是DWT_CYCCNTCPU 周期计数器。STM32F103 常用主频为 72MHz1 秒 72,000,000 个时钟周期。 1 微秒 72 个时钟周期。因此 DWT 可以实现微秒级延时DWT_DelayUs(10);DWT_DelayMs(100);DWT_DelayS(1);DWT 初始化的核心逻辑DEMCR|(uint32_t)DEMCR_TRCENA;DWT_CYCCNT0;DWT_CTRL|(uint32_t)DWT_CTRL_CYCCNTENA;含义DEMCR_TRCENA打开内核调试与跟踪功能。 DWT_CYCCNT 0先将周期计数器清零。 DWT_CTRL_CYCCNTENA让周期计数器开始计数。DWT 适合DHT11 单总线时序。 超声波模块微秒延时。 测量一段程序的运行时间。 精确软件延时。注意DWT 延时属于忙等待延时期间 CPU 一直在循环等待不适合长时间延时。短延时DWT_DelayUs()。 较长延时HAL_Delay()、定时器中断或非阻塞计时。六、DHT11 单总线通信流程DHT11 使用一根 DATA 数据线进行双向通信。空闲状态下DATA 由上拉电阻保持高电平。完整流程1. DHT11 上电后等待至少 1 秒。 2. STM32 将 DATA 配置为输出。 3. STM32 拉低 DATA 至少 18ms。 4. STM32 释放 DATA并改为输入。 5. DHT11 拉低约 83us表示收到请求。 6. DHT11 拉高约 87us表示准备发送数据。 7. DHT11 发送 40 位数据。 8. STM32 读取数据并校验。DHT11 一次发送 5 个字节湿度整数 湿度小数 温度整数 温度小数 校验和校验和计算校验和 湿度整数 湿度小数 温度整数 温度小数如果计算结果和接收到的第五个字节相同说明数据正确。DHT11 每一位数据的判断方式先低电平约 50us。 高电平约 26us表示 0。 高电平约 70us表示 1。所以程序会在高电平开始后约 40us 判断40us 后仍是高电平这一位是 1。 40us 后已经变低这一位是 0。读取 DHT11 时等待电平变化必须加入超时判断。否则 DHT11 未接好或没有供电时程序可能永久卡在while循环中。七、GPIO 配置结构体的理解DHT11 中经常需要让一个 GPIO 在输出和输入之间切换GPIO_InitTypeDef GPIO_InitStruct{0};这句含义是创建一个 GPIO 配置变量。 并把结构体中所有成员先初始化为 0。后续填写GPIO_InitStruct.PinDHT11_DATA_Pin;GPIO_InitStruct.Modemode;GPIO_InitStruct.Pullpull;GPIO_InitStruct.SpeedGPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(DHT11_DATA_GPIO_Port,GPIO_InitStruct);可以理解为先准备一张 GPIO 配置表。 填写要配置哪个引脚、输入还是输出、是否上拉、输出速度。 最后调用 HAL_GPIO_Init()将配置真正写入 STM32。八、Keil 工程目录作用手动创建 STM32 工程时常见目录如下文件夹作用Doc项目文档、接线图、模块说明、readme 等LibrariesCMSIS 核心库和 HAL 驱动库ProjectKeil 工程文件、编译输出文件等User用户编写的 main.c、外设驱动、BSP 文件等常见 User 文件作用文件作用main.c主函数入口主要应用逻辑main.hmain.c 对应头文件放函数声明和全局定义stm32f1xx_hal_conf.hHAL 外设模块开关配置stm32f1xx_it.c中断服务函数实现stm32f1xx_it.h中断函数声明system_stm32f1xx.c系统初始化、时钟变量更新九、手动建立 Keil 工程模板步骤第一步建立 Doc、Libraries、Project、User 四个文件夹。 第二步复制 CMSIS、STM32F1xx_HAL_Driver 到 Libraries。 第三步复制 main.c、main.h、stm32f1xx_hal_conf.h、 stm32f1xx_it.c、stm32f1xx_it.h、system_stm32f1xx.c 到 User。 第四步在 Keil 中新建工程选择正确的 STM32 芯片。 第五步建立分组 STARTUP、CMSIS、STM32F1xx_HAL_Driver、USER、DOC。 第六步分别把启动文件、system 文件、HAL 源文件、 User 源文件和文档添加到对应分组。 第七步在魔术棒中设置芯片、时钟、输出 HEX、 宏定义、头文件路径和下载调试器。 第八步编写 main.c编译验证。常用宏定义USE_HAL_DRIVER STM32F103xB十、新增模块文件时的注意事项在已有工程中加入一个新模块例如 DHT11、OLED、DWT 或电机模块时1. 在 User 文件夹建立模块目录例如 dht11。 2. 放入该模块的 .c 和 .h 文件。 3. 在 Keil 的 USER 分组中添加对应的 .c 文件。 4. 在使用该模块的源文件中包含对应 .h 文件。 5. 确认 HAL 配置文件已打开所需外设模块。 6. 重新编译查看错误和警告。今天还遇到了一个典型问题MX_USART1_UART_Init();函数虽然已经在串口源文件中实现但main.c没有包含串口头文件导致编译器提示function MX_USART1_UART_Init declared implicitly解决方法是在main.c加入#includeusart/bsp_usart.h这说明.c文件负责实现函数.h文件负责声明函数。使用某模块时不能只把.c文件加入 Keil还要在需要调用的位置包含对应的.h文件。十一、今日总结今天学习的内容可以概括为PWM 中ARR 决定周期CCR 决定占空比。 TB6612FNG 中PWMA 控制速度 AIN1/AIN2 控制方向 STBY 必须拉高才能工作。 DWT 是内核自带的高速周期计数器 适合微秒延时和程序运行时间测量。 DHT11 使用单总线通信 主机低电平至少 18ms 发起请求 传感器回应后发送 40 位温湿度数据。 Keil 工程中 模块的 .c 文件要加入工程 模块的 .h 文件要在使用处包含。
延伸阅读

更多相关文章

2026/10/22 6:57:10

OSP和沉金哪个好怎么选?OSP和沉金避坑要点

在产品立项阶段,表面处理工艺直接影响 PCB 物料成本。很多项目简单全部选用沉金,造成不必要成本浪费;也有部分项目一味追求低成本选用 OSP,后期 SMT 良率低,返工成本远大于板材节省的费用。想要做好成本管控&#xff0…

2026/10/22 13:37:56

Python教程(16)——lambda表达式详解

函数介绍 我们平常常常能够于代码里见到一种起始的此类表达式, 要是未曾学习过相关知识, 或许会一脸茫然 , 不明白究竟这个关键字是用来做什么的 , 用以表示什么。 实际上这个就是函数。 函数是一种特殊的匿名函数, 它并非仅存在这种情况, 它能够让我们迅速定义简单的函数, …

2026/10/20 22:58:55

DM管理对象依赖性:提升数据库系统稳定性的关键技术

一、DM管理对象依赖性基础 1.1 对象依赖性的定义与重要性 对象依赖性是指数据库中各种对象(如表、视图、存储过程、函数等)之间存在的引用关系。在DM数据库中,当一个对象引用了另一个对象时,它们之间就建立了依赖关系。这种依赖关…

2026/10/23 2:18:38

数据库如何实现动态分批数据处理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/23 2:18:38

C++类与对象入门:从封装、构造函数到析构函数的完整实践

如果你刚接触 C 不久,准备认认真真把类与对象搞明白,那这一篇可以带着你从零走一遍。我在初学阶段最大的感受是:函数写熟了,结构体也用过,但一遇到 class 就卡住——不是看不懂语法,而是不知道“为什么非要…

2026/10/23 2:18:38

科技成果转化调研方法:从技术到场景的断点识别

一位做产业研究的老友最近带队去考察了一家智能机器人企业,回来后跟我聊了很久。他说现在看科技企业,最怕的就是被一堆“自研算法”“行业领先”“全栈能力”的词砸晕,真正有价值的调研,必须围绕“转化应用”这四个字展开&#xf…

2026/10/23 2:18:38

SSM商店管理系统毕业设计:从选型到答辩的完整指南

1. 为什么“SSM 商店管理系统”总是摆在毕设选题列表第一名1.1 这个题目到底想考你什么每年一到毕业季,总有同学私信问我:“师兄,我在选题系统里又看到了 SSM 商店管理系统,还带源码和论文,这东西到底好不好做&#x…

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/23 0:02:26

Verilog实现FPGA密码锁:矩阵键盘与状态机设计实战

说实话,这几年接触了不少想入门FPGA的朋友,如果你问哪个项目能一口气把Verilog的核心基本功串起来,我通常会推荐电子密码锁。这个项目看着不起眼,却把矩阵键盘扫描、按键消抖、状态机设计、动态显示刷新、外设控制这些数字逻辑里的…

2026/10/23 0:02:26

数据服务监控运维实战:从指标体系到告警治理与故障排查

周末凌晨两点,手机震了。第一反应是“数据服务挂了”。打开电脑看了下,果然,某张核心报表的数据产出时间已经超过SLA两小时,下游三个业务方连着发来问询。这种场景,做过大数据运维的人都不会陌生。数据服务的监控与运维…

2026/10/23 0:02:26

FreeRTOS任务通知实战详解:内核原理、API与避坑指南

裸机编程切到RTOS之后,任务之间的通信和同步永远是躲不开的坎。前面聊过队列、信号量、互斥锁、事件组,这次把FreeRTOS任务通知(Task Notification)单独拎出来讲透。原因很简单:任务通知是FreeRTOS里最容易用错、也最容…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑