STM32F103C8T6 实现舵机与电机的控制 2个定时器输出不同频率的PWM

2023-05-16

智能小家居------舵机开门,电机做风扇 or 拉窗帘、呼吸灯做提示,小OLED屏幕显示当前状态。

在这里插入图片描述

文章目录

  • 直接上代码
    • main.c
    • pwm.h
    • pwm.c
    • servo.h
    • servo.c
    • motor.h
    • motor.c
    • 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

直接上代码

背景:我觉得我看了那么多教程了,然而只会玩单个东西,串起来就不太懂。
本项目的代码部分实现用到了2个时钟,TIM2 和 TIM3。
经历的问题:原本我是想只用一个时钟,不同通道来实现PWM的输出,但是我发现 我如果把TIM_TimeBaseInitStructure.TIM_Period = ARR ; //ARR 自动重装器的值
ARR = 20000 -1 时,只能驱动舵机,不能驱动电机。
ARR = 100 -1 时,只能驱动电机,不能驱动舵机。
后来发现: SG90舵机接收的PWM信号频率为50HZ,T=1/f,所以周期为20ms。 当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。
解决方案:所以后来使用了2个时钟,分别输出上面这2个ARR的PWM。
明天准备再加一个呼吸灯功能
如果你有更好的方案,可以留言或者私信我哦,我们可以交流交流哈哈哈,一起进步呀。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "OLED.h"
#include "Servo.h"
#include "PWM.h"
#include "Motor.h"

extern void Motor_Set_Dir(int8_t Speed);
extern void Exti_Test_Pull_Level_Set(void);
//extern void PWM_SetCompare3(uint16_t Compare);

uint8_t i,KeyNum;

int main(void)
{
	Key_Init();
	OLED_Init();
	Servo_Init();
	Motor_Init();
	
	OLED_ShowString(1,1,"Angle:");
	OLED_ShowString(2,1,"KeyNum:");
	OLED_ShowString(3,1,"Speed:");
	
	Servo_Set_Angle(0);//设置舵机初始角度
	while (1)
	{		
		KeyNum = Key_GetNum();
		Servo_Turn(KeyNum);
		Motor_Speed_Set(KeyNum);
	}
}

pwm.h

#ifndef __PWM_H
#define __PWM_H

#include "stm32f10x.h"                  // Device header

void Servo_PWM_Init();
void Motor_PWM_Init();
void PWM_SetCompare_Servo(uint16_t Compare);//实际为void PwmCompare()函数
void PWM_SetCompare_Motor(uint16_t Compare);//实际为void PwmCompare()函数

#endif


pwm.c

#include "stm32f10x.h"                  // Device header

#define  TIM2_CH2 GPIO_Pin_1
#define  TIM3_CH1 GPIO_Pin_6

void Servo_PWM_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure; 			//定义GPIO初始化结构体变量
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启定时器2
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//通道2时钟使能函数

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//设置GPIO为推挽输出模式
	GPIO_InitStructure.GPIO_Pin = TIM2_CH2; //PA1 PA2			
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度设置为 50MHz
	GPIO_Init(GPIOA, &GPIO_InitStructure);	//按照以上参数进行 GPIO的初始化
	TIM_InternalClockConfig(TIM2);//TIM的时基单元由内部时钟控制

	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //ARR 自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC 预分频器的值 对72M(720000000)进行 7200分频 即10K的频率下 计10000个数 1s的时间
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值 CCR
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); 

	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性选择
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出状态使能
	TIM_OCInitStructure.TIM_Pulse = 20;//CCR,即占空比为 10%
	TIM_OC2Init(TIM2,&TIM_OCInitStructure);//OC编号要与通道编号对应

	TIM_Cmd(TIM2,ENABLE);
}

void Motor_PWM_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure; 			//定义GPIO初始化结构体变量
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  //开启定时器3
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//通道2时钟使能函数

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//设置GPIO为推挽输出模式
	GPIO_InitStructure.GPIO_Pin = TIM3_CH1; //PA6		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度设置为 50MHz
	GPIO_Init(GPIOA, &GPIO_InitStructure);	//按照以上参数进行 GPIO的初始化
	TIM_InternalClockConfig(TIM3); //内部时钟配置

	
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR 自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC 预分频器的值 
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值 CCR
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); 
	
	
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性选择
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出状态使能
	TIM_OCInitStructure.TIM_Pulse = 50;//CCR,即占空比为 10%
	TIM_OC1Init(TIM3,&TIM_OCInitStructure);//OC编号要与通道编号对应
	TIM_OC2Init(TIM3,&TIM_OCInitStructure);//OC编号要与通道编号对应

	TIM_Cmd(TIM3,ENABLE);
}

void PWM_SetCompare_Servo(uint16_t Compare)
{//TIM2_CH2 GPIO_Pin_1
	TIM_SetCompare2(TIM2, Compare);
}

void PWM_SetCompare_Motor(uint16_t Compare)
{//TIM3_CH1 GPIO_Pin_6
		TIM_SetCompare1(TIM3, Compare);

servo.h

#ifndef __Servo_H
#define __Servo_H

#include "stm32f10x.h"                  // Device header

void Servo_Init(void);
void Servo_Set_Angle(float Angle);
void Servo_Turn(uint8_t KeyNum);

#endif

servo.c

#include "stm32f10x.h"                  // Device header
#include "PWM.H"
#include "OLED.h"

float Angle;
		

void Servo_Init(void)
{
	Servo_PWM_Init();
}

void Servo_Set_Angle(float Angle)
{
	PWM_SetCompare_Servo(Angle / 180 * 2000 +500);
}

void Servo_Turn(uint8_t KeyNum)
{
//因为我板子上只有1个按键可以用,所以我用这两个引脚模拟按键
	if(KeyNum == 1)//PB1           
		{
			Angle = 0;
		}	
		if(KeyNum == 2)//PB11
		{
			Angle = 180;
		}
	Servo_Set_Angle(Angle);//执行电机转角
	OLED_ShowNum(1,7,Angle,3);
}
 

motor.h

#ifndef __MOTOR_H
#define __MOTOR_H

#include "stm32f10x.h"                  // Device header

void Motor_Init();
void Motor_Set_Dir(int8_t Speed);
void Motor_Speed_Set(uint8_t KeyNum);

#endif

motor.c

该功能有用到TB6612FNC电机驱动模块,用PWM来控制 直流电机的转速

#include "stm32f10x.h"                  // Device header
#include "PWM.h"
#include "Delay.h"
#include "OLED.h"

uint8_t Speed;

void Motor_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//通道2时钟使能函数
	
	GPIO_InitTypeDef GPIO_InitStructure; 				//定义GPIO初始化结构体变量
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;	//设置GPIO为推挽输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;//电机方向控制脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度设置为 50MHz
	GPIO_Init(GPIOA, &GPIO_InitStructure);				//按照以上参数进行 GPIO的初始化
	
	Motor_PWM_Init();
	
}

void Motor_Set_Dir(int8_t Speed)
{
	if(Speed >= 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_4);
		GPIO_ResetBits(GPIOA, GPIO_Pin_5);
		PWM_SetCompare_Motor(Speed);
	}
	else
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);
		GPIO_SetBits(GPIOA, GPIO_Pin_5);
		PWM_SetCompare_Motor(-Speed);
	}
	
}

void Motor_Speed_Set(uint8_t KeyNum)
{
	if(KeyNum == 3)
		{
			Delay_ms(200);
			Speed += 20;
			if(Speed > 100)
			{
				Speed = -100;
			}
		}
	OLED_ShowNum(3,7,Speed,3);
	OLED_ShowNum(2,8,KeyNum,1);
	Motor_Set_Dir(Speed);
}

笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F103C8T6 实现舵机与电机的控制 2个定时器输出不同频率的PWM 的相关文章

  • 【数据结构与算法】(JAVA版)递归,迷宫问题的解决

    递归 递归需要遵守的规则 xff1a span class token number 1 span span class token punctuation span 执行一个方法时 xff0c 就在JVM栈中开辟一块内存 xff0c 用于
  • socket编程十三:send()/recv()和write()/read():发送数据和接收数据

    在 Linux 和 Windows 平台下 xff0c 使用不同的函数发送和接收 socket 数据 xff0c 下面我们分别讲解 Linux下数据的接收和发送 Linux 不区分套接字文件和普通文件 xff0c 使用 write 可以向套
  • 【数据结构与算法】(JAVA版)8大排序算法带图文解说,选择排序,冒泡排序,插入排序,希尔排序,快速排序,归并排序,基数排序,堆排序

    排序算法 常见排序列表 xff1a 1 冒泡排序 xff08 Bubble Sorting xff09 span class token keyword package span span class token namespace que
  • 【数据结构与算法】(Java)二分法查找,插值查找,斐波那契查找,哈希表应用场景:员工信息管理在内存中

    查找算法 有序表查找 二分法查找 span class token keyword package span span class token namespace dataStructure span span class token pu
  • ubuntu 20.04 | 设置默认 python、pip(软连接)并换源

    ubuntu 20 04 设置默认python pip xff08 软连接 xff09 并换源 PythonPip1 安装pip32 建立软连接3 更换pip源4 更新pip5 卸载pip6 使用pip6 1 pip安装软件包6 2 pip
  • JavaWeb实现简单登录功能

    创建一个web项目 xff0c 配置好Tomcat xff0c java环境 xff0c 并部署到idea中 xff0c 安装好MySQL并导入jar包 mysql connector的jar包 jquery的js包 servlet api
  • telnet是什么

    Telnet是远程连接服务 xff0c 它工作于tcp ip协议的应用层 telnet命令通常用来远程登录 是Internet远程登陆服务的标准协议和主要方式 它为用户提供了在本地计算机上完成远程主机工作的能力 在终端使用者的电脑上使用te
  • 头文件与库文件的区别

    一 头文件与库文件的区别 头文件一般而言 xff0c 是申明和定义 库文件是已经编译好的二进制代码 这个二进制代码可以是动态的 xff0c 如 so xff1b 也可以是静态的 xff0c 如 a 如果是动态的 xff0c 则最后生成的程序
  • C++入门 vector的使用 + 进阶【模拟实现】

    目录 基本接口函数介绍迭代器空间容量增删查改迭代器失效问题探讨 vector模拟实现reserve迭代器空间容量删除insert析构函数vector拷贝构造函数拷贝赋值运算符 基本接口函数介绍 函数名功能vector xff08 重点 xf
  • C++11:继承

    目录 继承的基本概念 继承方式 基类和派生类对象赋值转换 切片 继承中的作用域 派生类的四个成员函数 xff1a 构造函数 拷贝构造函数 赋值重载 析构函数 静态成员 继承与友元 多继承 菱形继承 多继承的指针偏移问题 组合 继承的基本概念
  • socket编程二十六:基于UDP的服务器端和客户端

    前面的文章中我们给出了几个 TCP 的例子 xff0c 对于 UDP 而言 xff0c 只要能理解前面的内容 xff0c 实现并非难事 UDP中的服务器端和客户端没有连接 UDP 不像 TCP xff0c 无需在连接状态下交换数据 xff0
  • 下载高清电影的必须收藏的网站

    下载高清电影的必须收藏的网站 Posted 2012 12 06 分类 生活范儿 电影 生活范儿 电影 CHD 虽然蓝光推出 xff0c 但是高清已经势不可挡 xff0c 动辄几G甚至几十G一部的电影冲击着我们的视觉 xff0c 也考验着我
  • 电赛总结|电赛注意事项

    电赛总结 赛前 1 准备模块非常重要 如果没有提前准备模块 xff0c 在赛中也是在想尽办法买模块 xff0c 只是花更多的钱和运费等 xff0c 也不会去自己搭 所以赛前一定要准备模块 常见模块 降压模块 xff0c 升压稳压模块 xff
  • Putty使用教程

    Putty作为免费且开源的老牌 SSH 客户端 xff0c PuTTY 经常用于 Windows 下连接管理远程服务器 为方便刚接触 VPS 的新手参考使用 xff0c 本文配合截图介绍 PuTTY 的基础用法及一些设置技巧 xff0c 希
  • #Python实现话题的发布与订阅

    Python实现话题的发布与订阅 首先我们的先了解ROS文件系统的基本框架 xff0c 如下图所示 xff1a 由上图可知 xff0c py文件放在工作包里面的scripts文件夹内 xff0c 所以 xff0c 整活 xff01 1 在工
  • #创建自定义topic

    创建自定义topic 前面我们学了用C 43 43 和Python创建发布者与订阅者 xff0c 这次我们创建自定义的话题 xff0c 其实同C 43 43 实现topic差不多 xff0c 都是编写 cpp文件 步骤有点多且繁琐 xff0
  • #使用TF实现海龟机器人跟随

    使用TF实现海龟机器人跟随 昨天粗略地讲解了一会儿TF变换 xff0c 用的是ROS系统中自带的功能包实现小海龟跟随的功能 xff08 具体见 初识TF变换 xff09 今天我们将用自己编写节点的方式实现小海龟跟随的功能 xff0c 并且
  • #创建虚拟机器人URDF模型

    创建虚拟机器人URDF模型 题外话 xff1a 作业发布已有一两天了 xff0c 之所以今天才编辑这篇博客 xff0c 是因为我也遇到问题了 xff0c 现在以及解决了 xff08 小细节 xff1a 创建功能包之前先编译工作空间确保里面已
  • # gazebo 仿真

    gazebo 仿真 1 给 base link 添加惯性 xff0c 碰撞以及 gazebo 属性 在路径xqrobot description urdf xacro 件夹下新建 件夹 gazebo xff0c 并在 gazebo 件下创建
  • #Gmapping

    Gmapping 开始之前先安装两个功能包 xff0c 命令如下 xff1a sudo apt span class token operator span get install ros span class token operator

随机推荐

  • #navigation

    navigation 1 安装相关依赖 sudo apt span class token operator span get install ros span class token operator span kinetic span
  • # Qt_day1

    Qt day1 1 项目框架 span class token macro property span class token directive hash span span class token directive keyword i
  • ros先订阅后发布 无法收到消息的解决办法

    现象 今天遇到的问题是 使用的是Ros1 在先订阅后发布时 会导致订阅者无法收到订阅的消息 除非在发布者发布后重新订阅 思考 以前使用的是Ros2似乎并不关心订阅和发布的先后顺序 nbsp 似乎都可以收到消息 nbsp nbsp 这个问题后
  • C/C++中关于struct和class类的区别

    struct和class的主要的区别在于两者默认的访问权限有所不同 在不设置类中的成员属性和成员方法的权限时 xff0c struct默认的访问权限是公共权限 xff0c class默认的访问权限是私有权限 补充 xff1a 成员属性和成员
  • C++中STL容器的主要使用及含义

    1 stack栈容器的使用 假如栈中存放的是字符串 xff0c 我们做如下定义 xff1a stack lt string gt ss 设该变量名为ss 其主要用法如下 xff1a ss push a 存入栈中元素a ss top 读取栈顶
  • 电赛备赛记录第一篇(控制部分)

    2022 5 25 九校联赛备赛阶段第一天 联赛小车系统沿用去年国赛使用的树莓派驱动底板与外设 整车情况良好 xff0c 摄像头通信 连接均正常 xff0c 现已拼装完整 复产复工的初步成果为 xff1a 小车可以实现开机自启动的程序运行
  • 数据结构——栈详解

    1 栈 Stack 是一种线性存储结构 xff0c 它具有如下特点 xff1a xff08 1 xff09 栈中的数据元素遵守 先进后出 34 First In Last Out 的原则 xff0c 简称FILO结构 xff08 后进先出的
  • 双目相机标定

    一 运行环境 opencv2 windows vs 二 图像获取 分割 保存 参考博客opencv打开双目摄像头 图像切割保存 scutqq的博客 CSDN博客 双目图像分割 include amp lt opencv2 core core
  • uart1接收不定长度数据和发送:STM32 HAL库串口+DMA+IDLE空闲中断

    DMA增加 xff1a usart1 gpio 默认即可 usart1中断必须打开 在 STM32 中 USART 发送接收有三种基本方式 xff0c 轮询 中断和 DMA 1 轮询方式为堵塞模式 xff0c 使用超时管理机制 它每次接收一
  • 串口、网口等自定义通信协议的问题

    自定义通信协议的问题 一 串口1 通信分为网络通信和串口通信2 协议格式3 协议设计4 代码实现 二 网口1 TCP粘包与拆包 包的划分 出现TCP粘包的原因 粘包与拆包的几种情况 常见的粘包与拆包解决方案 2 为什么UDP没有粘包 xff
  • 如何理解奇偶校验位?

    奇偶校验位提供对传输数据的简单错误 xff08 奇偶校验 xff09 检查 此表描述奇偶校验的类型 奇偶校验类型 xff1a Even 描述 xff1a 数据位加上奇偶校验位产生偶数个1 xfffc 奇偶校验类型 xff1a Mark 描述
  • C语言----隐藏代码文件

    在C语言中 xff0c 常用的文件主要是后缀为 c的源文件以及后缀名为 h的头文件 我们通常使用头文件对函数进行声明 xff0c 使用源文件对具体的函数进行实现 有些时候会由于各种原因需要将函数的功能交给别人使用 xff0c 但是又不想将具
  • c++入门系列(三)之头文件

    1 什么是头文件 xff1f 在C语言家族程序中 xff0c 头文件被大量使用 一般而言 xff0c 每个C 43 43 C程序通常由头文件和定义文件组成 头文件作为一种包含功能的函数 数据接口声明的载体文件 xff0c 主要用于保存程序的
  • std::atomic_thread_fence

    在原子变量的存取上应用不同的memory order可以实现不同的内存序来达到数据同步的目的 xff0c 而在C 43 43 11及之后的标准里 xff0c 除了利用原子操作指定内存序 xff0c 还定义了单独使用 内存栅栏 xff08 s
  • 【数据结构】【期末复习】知识点总结

    算法 线性表 概念明晰 xff1a 随机存取 顺序存取 随机存储和顺序存储 随机存取 顺序存取 随机存储和顺序存储这四个概念是完全不一样的 xff0c 切不可将之混淆 很多人包括我可能认为随机存取就是随机存储 xff0c 顺序存取就是顺序存
  • 【单片机学习】51单片机【定时/计数器】,详细介绍

    51单片机学习 一 先知先会1 CPU时序的有关知识1 1 周期换算2 在学习定时器之前需要明白的3 定时 计数器的工作原理4 51单片机定时器结构 二 定时 计数器的控制1 工作方式寄存器TMOD2 控制寄存器TCON3 定时 计数器的工
  • 【跟着江科大学Stm32】GPIO_LED_流水灯_蜂鸣器

    只要坚持下来了 xff0c 一定会有收获 xff01 一 LED闪烁 span class token macro property span class token directive hash span span class token
  • STM32F103C8T6 PWM驱动舵机(SG90)

    小知识 xff1a 同一个定时器 xff0c 不同通道输出不同输出PWM的特点 对于同一个定时器的不同通道输出PWM xff0c 因为它们是共用一个计数器的 xff0c 所以频率必须一样 xff0c 而占空比由各自的CCR决定 xff0c
  • STM32 PWM周期与频率的计算

    文章目录 STM32 PWM周期与频率的计算频率的计算占空比的计算笔记仅供自学 xff0c 用来回看复习 xff0c 不一定适合你 xff0c 如有错误请指出 STM32 PWM周期与频率的计算 TIM TimeBaseInitTypeDe
  • STM32F103C8T6 实现舵机与电机的控制 2个定时器输出不同频率的PWM

    智能小家居 舵机开门 xff0c 电机做风扇 or 拉窗帘 呼吸灯做提示 xff0c 小OLED屏幕显示当前状态 文章目录 直接上代码main cpwm hpwm cservo hservo cmotor hmotor c笔记仅供自学 xf