草庐IT

【STM32】STM32F103C8T6+L298N通过PWM控制直流电机转速

K.Fire好好睡觉 2023-04-12 原文

系列文章目录 

·【STM32】新建工程模板及配置

·【STM32】STM32与PC端、HC-06、ROS进行USART串口通信

·【ROS】ROS上位机使用Serial库和boost::asio库与STM32进行USART通讯

·【STM32】STM32F103C8T6+L298N通过PWM控制直流电机转速

·【STM32】STM32F103C8T6使用外部中断法和输入捕获法进行编码器测速

·【STM32】STM32F103C8T6实现直流电机速度PID控制


目录

系列文章目录

前言

一、PWM调速原理

二、STM32编程实现

总结


前言

原来做的差速小车是基于Arduino控制的,感觉有些简单,也有些基础,Arduino方便简单的同时,可操作性感觉也少了很多,所以想将控制器换成STM32,然后将树莓派作为上位机,STM32作为下位机,通过树莓派和STM32进行通讯,实现对差速移动小车的控制,本人也是寒假期间初学STM32,也是奔着应用去的,所以对于STM32编程原理方面可能不太精通,这里偏重于记录应用层面的知识。


一、PWM调速原理

直流电机驱动是最简单的,给电机通上电就能转,根据电机的公式:

可知:当提高电压时,反电势升高,进而转速升高,电压与转速大致有如图所示的关系

所以我们只要控制给电机通电的电压即可控制电机的转速,但是在实际的控制中,控制直流电机需要通过H桥控制电机的正反转,如图,当T1和T4二极管导通时,有粉色通路;当T2和T3二极管导通时,有蓝色通路,这样我们就可以实现弱电控制强电,通过二极管的通断来控制电机的转向。

但是这样电机通电时电压就是Us,我们如果想自由的控制Us的电压值基本是不能实现的,因为电机是接到单片机的引脚上的,引脚的供电电压值是确定的,我们就要使用控制二极管的通断时间对电机的转速进行控制,即PWM控制。

图中的D1~D4二极管为续流的作用,因为电机中有绕组,在断电后,电感的电流不能瞬时变为0,所以在断电后电流沿棕色和绿色的通路放点。

       

 在一个周期内,我们通过控制通电的时间就可以调控平均电压,而平均电压的高低直接控制电机的转速,通电时间/周期,就可以得到占空比,我们也就是通过控制电机的占空比来控制电机的转速的。

 在实际应用过程中,我们不用自己搭建H桥,而是使用电机驱动板(如:L298N)对直流电机进行驱动,L298N内搭载两个H桥电路,可以实现对两个电机的转向和转速进行控制。

 这是淘宝商家提供的电机驱动板控制表,将IN1~4接到单片机的引脚,我们就可以通过引脚输出PWM控制信号,对直流电机进行控制。

二、STM32编程实现

在STM32中如果想输出PWM信号,需要借助定时器,通过定时器的捕获/比较通道的PWM输出

 当我们对定时器设置了预装载值arr和比较值ccr后,可以通过配置PWM模式,使定时器CNT计数值超过ccr后产生有效信号,并通过配置相应寄存器设置有效信号是1还是0,而配置PWM的输出方式,具体原理信息可以参考原子哥的视频,也可以参考中文参考手册的14.4.7内容。

 在程序中,我们使用库函数进行配置,配置步骤如下:

  1.  使能定时器和相关外设引脚时钟 :RCC_APBxPeriphClockCmd()
  2. 配置IO口为复用输出模式(查手册8.1.11)配置成相应的模式(复用推挽输出)
  3. 初始化定时器:TIM_TimeBaseInit()
  4. 初始化TIM2 Channe1234 PWM模式:TIM_OCxInit()
  5. 使能OCx通道的预装载寄存器:TIM_OC1PreloadConfig()
  6. 使能时钟:TIM_Cmd()
  7. 在主函数中配置占空比进行调速:TIM_SetCompare1()
#include "sys.h"

/********************
功能:通用时钟2用来产生通道1234四路PWM信号
函数:TIM2_PWM_Init(u16 arr,u16 psc)
作者:K.Fire
日期:2022.01.30
引脚:PA0 PA1 PA2 PA3
参数:arr:自动重装值 psc:时钟预分频数
*****************/

void TIM2_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	//使能GPIOA外设模块时钟  
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   
 
	//设置引脚为复用输出功能,输出TIM2 CH1的PWM脉冲波形	GPIOA.0
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM2_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
	
	//设置引脚为复用输出功能,输出TIM2 CH2的PWM脉冲波形	GPIOA.1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM2_CH2
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
	
    //设置引脚为复用输出功能,输出TIM2 CH3的PWM脉冲波形	GPIOA.2
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //TIM2_CH3
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
	
	//设置引脚为复用输出功能,输出TIM2 CH4的PWM脉冲波形	GPIOA.3
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //TIM2_CH4
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
 
	//使能定时器2时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	
    //初始化TIM2
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM2 Channe1234 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM2 OC1
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM2 OC2
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM2 OC3
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM2 OC4

	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);  //使能TIM2在CCR1上的预装载寄存器
	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);  //使能TIM2在CCR2上的预装载寄存器
	TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);  //使能TIM2在CCR3上的预装载寄存器
	TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);  //使能TIM2在CCR4上的预装载寄存器
 
	TIM_Cmd(TIM2, ENABLE);  //使能TIM2
	
}


总结

原理部分我讲的很简略,我也是看原子哥的视频学,如果大家对单片机控制原理有学习需要,可以去B站看原子哥的视频,博主主要是做控制和上位机的,所以这部分只需要会用即可 

 


 

有关【STM32】STM32F103C8T6+L298N通过PWM控制直流电机转速的更多相关文章

  1. Ruby Readline 在向上箭头上使控制台崩溃 - 2

    当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby​​安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少

  2. ruby-on-rails - 带 Spring 锁的 Rails 4 控制台 - 2

    我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.

  3. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  4. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  5. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  6. ruby-on-rails - 在 Rails 控制台中使用 asset_path - 2

    在我的Character模型中,我添加了:字符.rbbefore_savedoself.profile_picture_url=asset_path('icon.png')end但是,对于数据库中已存在的所有角色,它们的profile_picture_url为nil。因此,我想进入控制台并遍历所有这些并进行设置。在我试过的控制台中:Character.find_eachdo|c|c.profile_picture_url=asset_path('icon.png')end但这给出了错误:NoMethodError:undefinedmethod`asset_path'formain:O

  7. ruby-on-rails - 带有 Pry 的 Rails 控制台 - 2

    当我进入Rails控制台时,我已将pry设置为加载代替irb。我找不到该页面或不记得如何将其恢复为默认行为,因为它似乎干扰了我的Rubymine调试器。有什么建议吗? 最佳答案 我刚发现问题,pry-railsgem。忘记了它的目的是让“railsconsole”打开pry。 关于ruby-on-rails-带有Pry的Rails控制台,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/question

  8. ruby - 将全局 $stdout 重新分配给控制台 - ruby - 2

    我正在尝试将$stdout设置为临时写入一个文件,然后返回到一个文件。test.rb:old_stdout=$stdout$stdout.reopen("mytestfile.out",'w+')puts"thisgoesinmytestfile"$stdout=old_stdoutputs"thisshouldbeontheconsole"$stdout.reopen("mytestfile1.out",'w+')puts"thisgoesinmytestfile1:"$stdout=old_stdoutputs"thisshouldbebackontheconsole"这是输出。r

  9. ruby-on-rails - Ruby 流量控制 : throw an exception, 返回 nil 还是让它失败? - 2

    我在思考流量控制的最佳实践。我应该走哪条路?1)不要检查任何东西并让程序失败(更清晰的代码,自然的错误消息):defself.fetch(feed_id)feed=Feed.find(feed_id)feed.fetchend2)通过返回nil静默失败(但是,“CleanCode”说,你永远不应该返回null):defself.fetch(feed_id)returnunlessfeed_idfeed=Feed.find(feed_id)returnunlessfeedfeed.fetchend3)抛出异常(因为不按id查找feed是异常的):defself.fetch(feed_id

  10. ruby-on-rails - ruby 新手,有人可以帮我从控制台破译这个错误吗? - 2

    我真的只是不确定这意味着什么或我应该做什么才能让网页在我的本地主机上运行。现在它只是显示一个错误,上面写着“我们很抱歉,但出了点问题。”当我运行railsserver并在chrome中打开localhost:3000时。这是控制台输出:StartedGET"/users/sign_in"for127.0.0.1at2013-07-0512:07:07-0400ProcessingbyDevise::SessionsController#newasHTMLCompleted500InternalServerErrorin55msNoMethodError(undefinedmethod`

随机推荐