如果不想看的可以直接使用git把我的代码下载出来,里面工程挺全的,后期会慢慢的补注释之类的
码云地址:stm32学习笔记: stm32学习笔记源码
如果不会使用git快速下载可以选择直接下载压缩包或者去看看git的使用
git的使用(下载及上传_git如何下载文件_八月风贼冷的博客-CSDN博客
应用为PWM波形,章节使用PWM波驱动电机和使用PWM波混色LED以及呼吸灯LED
目录
一、理论学习部分。
1、输出比较的介绍
2、什么是PWM波形
3、输出比较通道
4、输出pwm的计算
5、高级定时器的输出比较和捕获电路
二、硬件部分与代码
1、PWM点灯(呼吸灯
1)、正常选择定时器配置
2)、使用复用功能来配置定时器
2、舵机(也是伺服电机
1)硬件部分
2)舵机代码部分
3、电机
1)硬件部分
1)软件部分
一、理论学习部分。
1、输出比较的介绍
通过:CNT:计数器与CCR寄存器比较来产生的,并且输入捕获和输出比较使用的是同一个寄存器,然后有四个输出比较,但是CNT用的是同一个。
2、什么是PWM波形
以数字量等效出来的模拟量变化,效果如左1图,在一个周期内高电平长就等效的为上半,否则为下半。
频率:周期的倒数嘛就是频率,
占空比:如图 就是高电平站一整个周期的大小。
分辨率:补距 就是说占空比精度 比如占空比从50只能到51那分辨率就是1,
原理!!!在一段时间内以快速变换频率让两端时间折中,比如电机马力全开与关闭折中,转速就只有了1/2 这样就是pwm,并且可以看成等效电压(0+5)/2 这样整个电压就是2.5v也可以算作一半功率。
3、输出比较通道
左侧输入为CNT和CCR经过比较寄存器的值,左上角ETRF为定时器的写一个小模式,一般不用占时没管,以后再写,当输出比较之后产生的REF有两路,一个是到主模式控制器,一个是近过非门,上面那个是可以吧REF映射到主模式的TRGP输出(本次不使用)。下方是经过一个寄存器,用户往寄存器写值,如果写的是0,信号直接不改变走上面输出,如果输入为1,信号会经过下面的非门之后再输出。最后经过一个使能寄存器。
ok然后我们看一下输出模式控制器如何输出REF的值,直接看表把= =,用户往寄存器写数据就行,用官方库直接配置函数就行,但还是看一下吧,本次主要使用的是PWM模式,一般习惯向上计数模式,模式1和模式2是取反的,其他的都一样的,
最后看一下PWM的结构,可以很明显看出来输出比较和计数器比较,CCR的值是由直接设定的,在计数器自增或自减到ARR的过程中,达到了用户设定的CCR值后会让REF为高低电平,最后再通过一个极性选择可以再控制一次pwm的占空比(反向一下= =我感觉这个设定还是直接往寄存器写0不反向吧,不然我前面算pwm搞毛???)
4、输出pwm的计算
这个频率就是一次ARR的计数值= =,等于就是一个溢出频率
占空比就是:ccr(自己设定的那个输出比较寄存器的值与ARR计数总数的比值,就比如你这设置的是10,总共要计数100次,那你高电平输出就站总周期的百分之10,占空比就是百分之10
分辨率:还是100呗,1/100是百分之1的精度嘛= =,你也可以让ARR很大,那样就能精确的控制百分之0.1之类的变换,但是肯定是对硬件有一定要求的,
5、高级定时器的输出比较和捕获电路
这一部分不用,先了解一下吧、看着很简单,就多了一个死区生成和一个输出使能,因为外部的两个mos管形成的电路(后面有图,需要两个输出相反的引脚来使能,使用这里就有了两路输出使能,然后为了防止再切换外部两个mos'管状态的时候,因为一些器件原因造成的不能马上切换,所以加了一个死去发生器来防止外部两个mos管的损坏(就是再关闭的瞬间,给了给延时让下面的管子晚一点导通。
这个玩意就能驱动一个电机了,但是电机是大功率的,所以gpio无法直接驱动,只能使用外部驱动。三个mos管组成的这个电路就能驱动一个三相无刷电机了(好像是?后面做飞行器的时候不对我再回来删。
二、硬件部分与代码
1、PWM点灯(呼吸灯
1)、正常选择定时器配置
使用的为PB1的灯(当然如果你自己买了灯你就能随便选哪个定时器的PWM了,我现在只能选这三个,查一下数据手册该口对应的TIM,通用定时器为TIM3的通道4
先打开这个GPIO口吧前面都配置过不讲解了,但是要说一下输出这里要配置为复用推挽输出,这里看图,我们现在不想GPIO是引脚直接控制的,而是是输出由片上外设来控制,所以这里我们不能直接使用输出数据寄存器来输出,需要复用功能输出,这里找一下输出模式的图,就是复用推挽了,复用推挽就是输出来自片上的外设的。
代码如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; //推挽复用输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
然后是打开定时器的开关,定时器我们用的是定时器3,直接复制个定时器初始化过来。
还是一样的配置,滤波分配配置为1、计数模式为向上,重装载为100、分频为720(72M/720/100=100k/100=1khz),最后重复计数器为0(还是一样的配不配没影响,通用定时器压根没有这个硬件)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=99; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=719;//PCS
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
最后就是本节的PWM配置了
首先声明自己的结构体,然后调用函数初始化一下结构体内的值,这个很重要记住,因为使用的是通用定时器,但是这个结构体内有很多是高级定时器使用的,所以如果你不配置完就可能出现一些奇怪的错误,当然你也可以全部手动配置。
然后是结构体初始化,先回忆一下流程图,我们只需要配置CCR的值来与计数值CNT相比较(所以这里肯定得找到写入CCR得参数,然后是输出模式控制器(这个肯定也是要找到的,之后还一个一个反相器得参数,最后就是开启使能得参数了,这个流程上面的输出比较通道图有啊,忘记得朋友可以返回去看看。
之后我们开始配置参数,
首先CCR得值:参数TIM_Pulse 这里我们先初始化为0,之后会调用函数去重新配置他得
模式控制:OCMode;这里配置为PWM1,这个上面得模式图也是有的哈
反向设置:OCPolarity 这里选择输出高为高 就是不经过那个非门
之后使能: OuputState 这里肯定是选使能了
最后不要忘记打开计数器得使能哦,不然计数器不动。
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct); //初始化
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse=0; //CCR
TIM_OC4Init(TIM3,&TIM_OCInitStruct);
TIM_Cmd(TIM3,ENABLE);
最后写一个可以输入参数得CCR控制
TIM_SetCompare4(TIM3,Compare); 这个函数,可以直接控制CCR写一个函数,带一个形参,方便后面做循环。记住用的是通道4,这里就调Compare4,通道不一样记得要看一下哦。
void PWM_SetCompare4(u16 Compare)
{
TIM_SetCompare4(TIM3,Compare);
}
主函数写循环,让其CCR得值从小到大,再从大到小,记得加点延时,不加的话他的变换太快了肉眼不一定可见
int main(void)
{
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART_Config();
delay_init();
PWM_LEDInit();
while(1)
{
u8 i=0;
for(i=0;i<=100;i++)
{
PWM_SetCompare4(i);
delay_ms(10);
}
for(i=0;i<=100;i++)
{
PWM_SetCompare4(100-i);
delay_ms(10);
}
}
}
2)、使用复用功能来配置定时器
这次选PB5得复用功能(红色箭头指的位置,有TIM3得通道2,因为PB1、PB0两个得复用是高级定时器、本次写的是通用定时器得PWM所以就没有找那两个引脚口,当然想用也是可以配得。
这里我门选择得是TIM3的引脚重映像,可以查一下TIM3的AFIO复用说明等下好配置复用模式。找到参考手册的AFIO章节。
如图,我们下使用PB5是部分重印象1模式
找到GPIOI里面的函数GPIO_PinRemapConfig,查看其参数需要配置模式和使能。找到配置的模式、这里有说明,我们配置TIM3为部分重映射。
整体代码就如下了,这样PB5的红色灯就成为了呼吸灯了,其他位置都没变动
如果你使用的引脚刚好是这些调试引脚,你需要自己去失能这些引脚,还是使用 GPIO_PinRemapConfig代码里面加了,但是被我屏蔽掉了。
void PWM_LEDInit(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //开启引脚复用功能
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //重映射配置
//如果你的引脚刚好是调试引脚,你可以调用这个函数,配置引脚失能
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5; //PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=99; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=719;//PCS
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct); //初始化
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse=0; //CCR
TIM_OC2Init(TIM3,&TIM_OCInitStruct); //初始化为通道2的初始化
TIM_Cmd(TIM3,ENABLE);
}
void PWM_SetCompare2(u16 Compare) //记得吧函数名字也改成2别后面吧直接整蒙了
{
TIM_SetCompare2(TIM3,Compare); //这里要改成通道2哦
}
2、舵机(也是伺服电机
1)硬件部分
这东西就是通过一个pwm来控制转动度数,一般的推荐周期就是20ms,算一下1/0.02=50hz,频率等于周期分之一,记得先把ms换成m统一度量衡朋友们。
然后是接线图、很符合电路常识的配色,但是又不是所以搞单片机的都会电路.JPG2)、舵机代码部分
使用PB1来输出PWM:黄线接PB1就行,代码使用的为TIM2的通道2
代码如下:这里上面说过推荐周期是20ms,所以就是50hz,这里直接72分频让他为1M会好算很多,现在的周期就是20ms了,参考上面的图,就是高电平0.5ms-2.5ms是0-180度,这就是配置占空比了,
现在占空比分母是20k,20k=20ms,那我们需要0.5ms就是500
void PWM_ServerInit(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=20000-1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;//PCS
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct); //这里需要改
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct); //初始化
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse=0; //CCR
TIM_OC2Init(TIM2,&TIM_OCInitStruct); //改成通道2和定时器2
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare2(u16 Compare)
{
TIM_SetCompare2(TIM2,Compare); //改了引脚这里也要改
}
这样就能转动了,但是大部分人肯定是喜欢直接输入度数对的,所以我们自己写一个度数转换函数就好了。这样就能直接输入度数了,公式可以这推出来的。这
void server_Angle(float Angle)
{
PWM_SetCompare2(Angle/180*2000+500);
}
3、电机
1)硬件部分
TB6612引脚接线图
但是我自己买的是便宜的 DRV8833所以代码是 DRV8833的哈,先看下DRV8833的图
管脚说明:
ANI1:AO1的逻辑输入控制端口,电平0-5V。
AIN2:AO2的逻辑输入控制端口,电平0-5V。
BNI1:BO1的逻辑输入控制端口,电平0-5V。
BIN2:BO2的逻辑输入控制端口,电平0-5V。
AO1、AO2为1路H桥输出端口,接一个直流电机的两个脚。
BO1、BO2为2路H桥输出端口,接另一个外直接电机的两个脚。
GND:接地。
VM:芯片和电机供电脚,电压范围2.7 V – 10.8 V。
STBY:接地或悬空芯片不工作,无输出,接5V工作;电平0-5V。
NC:空脚
看得出来的没有单独的PWM线的,他的电机控制速度是直接有两个引脚其中一个输出pwm来控制,这样你如果需要正反转的话一个电机需要占用两个通道= =(应该是这样的吧,不知道是不是我实力不够)凑合用。
1)软件部分
控制电机我们需要两个GPIO并且将其中一个GPIO配置为PWM输出(如果想正反转两个都要是PWM)代码如下首先将一个gpio口初始化,并配置为PWM模式,这里直接初始化了两个通道。主函数直接调用文章来源:https://www.toymoban.com/news/detail-699844.html
void PWM_Motor1Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=100-1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;//PCS
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct); //初始化
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse=0; //CCR
TIM_OC1Init(TIM3,&TIM_OCInitStruct);
TIM_OC2Init(TIM3,&TIM_OCInitStruct);
TIM_Cmd(TIM3,ENABLE);
}
int main(void)
{
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART_Config();
delay_init();
PWM_Motor1Init();
while(1)
{
TIM_SetCompare1(TIM3,100);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
TIM_SetCompare2(TIM3,100);
GPIO_SetBits(GPIOA,GPIO_Pin_7);
}
}
一个写PWM一个写高电平就能实现正反转了,屏蔽一个开一个,别两个同时开。文章来源地址https://www.toymoban.com/news/detail-699844.html
到了这里,关于stm32 配置PWM及实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!