1.接线原理图:
注:红线接5V,不是3.3V(电压小,带不动)
实物图:
PWM驱动舵机:按下按键,舵机输出轴角度变化,OLED显示该角度。
2.代码部分如下:
主函数代码部分:
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Servo.h" #include "Key.h" uint8_t KeyNum; float Angle; int main(void) { OLED_Init(); Servo_Init(); Key_Init(); OLED_ShowString(1,1,"Angle:"); Servo_SetAngle(90);//通过自定义函数设置角度 //注:以下分别设置也可完成任务,但是比较麻烦不方便。 // PWM_SetCompare2(500);//舵机为0° 示波器观察结果:f=50Hz,高电平时间=0.5ms // PWM_SetCompare2(2500);//舵机自动转为180° 示波器观察结果:f=50Hz,高电平时间=2.5ms // PWM_SetCompare2(1500);//舵机自动转为90° 示波器观察结果:f=50Hz,高电平时间=1.5ms while(1) { KeyNum=Key_GetNum(); if(KeyNum==1) { Angle+=30; if(Angle>180) { Angle=0; } }Servo_SetAngle(Angle); OLED_ShowNum(1,7,Angle,3); } }
PWM.c部分:
#include "stm32f10x.h" // Device header
//操作思路可参考:(图PWM基本结构)
//1.RCC开启时钟,把TIM外设和GPIO外设的时钟打开。
//2.初始化时基单元,选择内部时钟-----使用的还是TIM2,所以直接复制粘贴内部时钟部分代码
//3.初始化输出比较单元,(重要函数TIM_OC1Init)包括CCR的值,输出比较模式,极性选择,输出使能。(结构体)
//4.初始化GPIO,(第三步的输出比较模式的值需要GPIO输出展示)TIM_OC1Init,GPIO口初始化为复用推挽输出
//5.运行控制,启动计数器。
void PWM_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//第一步:开启APB1时钟,因为TIM2是APB1总线的外设 TIM_InternalClockConfig(TIM2);//第二步:选择时基单元的内部时钟 //第二步:配置时基单元----TIM_TimeBaseInit的第二个参数是结构体,配置结构体 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //TIM_CKD_DIV1,1分频,不分配;TIM_CKD_DIV2,2分频;TIM_CKD_DIV4,4分频----要求不高时,随意选择 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //TIM_CounterMode_Up向上计数;TIM_CounterMode_Down向下计数;TIM_CounterMode_CenterAligned1等为中央对齐 TIM_TimeBaseInitStructure.TIM_Period=20000-1;//ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;//PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//高级定时器用到,通用定时器选0 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//结构体 //第三步:初始化输出比较单元,总共有四个,需要哪个通道(GPIO口),初始化哪个函数 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值 //TIM_OCInitStructure.TIM_OCIdleState=;//高级定时器才会用到 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式 //TIM_OCInitStructure.TIM_OCNIdleState=;//高级定时器 //TIM_OCInitStructure.TIM_OCNPolarity=;//高级定时器才会用到 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//设置输出比较极性,TIM_OCPolarity_High有效电平为高电平 //TIM_OCInitStructure.TIM_OutputNState=;//高级定时器才会用到 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//设置输出使能,TIM_OutputState_Enable使能 TIM_OCInitStructure.TIM_Pulse=0;//设置CCR寄存器的值。 TIM_OC2Init(TIM2, &TIM_OCInitStructure);//此处使用PWM输出第二通道。 //第四步:初始化输出PWM的GPIO(舵机使用通道2,GPIO_PIN_1) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//选择复用推挽输出,使外设控制引脚(参照复用开漏/推挽输出图) GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//使用通道2,改为GPIO_Pin_1 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); //第五步:启动定时器 TIM_Cmd(TIM2,ENABLE); //ARR,PSC,CCR是用来计算频率,占空比和分辨率的(参考参数计算) //计算时,频率,占空比,分辨率是自己设置的,是已知的,因此,变为解方程组, //(频率为1/20ms=50Hz,占空比50%,分辨率1%) //PSC+1=72,ARR+1=20K;CCR=500~2500--->CCR=0.5ms~2.5ms } //舵机使用2通道设置CCR //更改占空比需要用的PWM_SetCompare2 void PWM_SetCompare2(uint16_t Compare) { TIM_SetCompare1(TIM2,Compare); } //补充: //PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1) //PWM占空比: Duty = CCR / (ARR + 1) //PWM分辨率: Reso = 1 / (ARR + 1) //PSC+1=72; ARR+1=20KHz CCR=500--->0.5ms CCR=2500--->2.5ms
PWM.h部分:
#ifndef __PWM_H #define __PWM_H void PWM_Init(void); void PWM_SetCompare2(uint16_t Compare); #endif
舵机控制角度 Servo.c部分:
#include "stm32f10x.h" // Device header #include "PWM.h" void Servo_Init(void) { PWM_Init(); } void Servo_SetAngle(float Angle)//舵机设置角度 { PWM_SetCompare2(Angle/180*2000+500);//输入值/180是占用总角度的比例,然后*2000是总比例+500是起始偏转角 }
舵机控制角度Servo.h部分:
#ifndef __SERVO_H #define __SERVO_H void Servo_SetAngle(float Angle); void Servo_Init(void); #endif
3.补充部分:
//补充:
//PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
//PWM占空比: Duty = CCR / (ARR + 1)
//PWM分辨率: Reso = 1 / (ARR + 1)
//PSC+1=72; ARR+1=20KHz CCR=500--->0.5ms CCR=2500--->2.5ms