【6-4】流水灯&舵机电机驱动
LED流水灯
接线图

程序部分
PWM.h
c
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
#endifPWM.c
c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
/*------下面是复用端口为PA15的部分-----
//初始化AFIO:用于复用端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟
//引脚重映射配置
//将PA0的TIM2复用到PA15上
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//关闭PA15的调试端口复用功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
注意:下面的GPIO初始化结构体中也要改为初始化PA15口
*/
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出模式,引脚控制权交给了片上外设(定时器)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA1和PA2引脚初始化为推挽输出
/*设置GPIO初始化后的默认电平*/
GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2); //设置PA1和PA2引脚为高电平
//RCC开启时钟
//要使用APB1的开启时钟函数,因为TIM2是APB1总线的外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟,这里选择内部时钟
TIM_InternalClockConfig(TIM2);
//配置结构体用于初始化时基单元
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;//就是PSC预分频器的值
//因为预分频器和计数器都有1一个数的偏差,所以这里要再减个1
//注意PSC和ARR的取值都要在0-65535之间,不要超范围了。
//这里我们预分频是对72M进行7200分频,得到的就是10k的计数频率,在10k的频率下,计10000个数,就是1s的时间
//计算定时器定时时间的公式:CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1),单位是频率,取倒数就是定时时间
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//就是重复计数器的值,是高级定时器才有的,这里不需要用给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 = 10;//直译:脉冲,设置CCR(比较值,CCR比较寄存器)
//初始化输出比较单元
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}main.c
c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
while (1)
{
for(i = 0;i<=100;i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i = 0;i<=100;i++)
{
PWM_SetCompare1(100 - i);
Delay_ms(10);
}
}
}PWM驱动舵机
接线图

程序实例
PWM.h
c
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);
#endifPWM.c
c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
/*------下面是复用端口为PA15的部分-----
//初始化AFIO:用于复用端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟
//引脚重映射配置
//将PA0的TIM2复用到PA15上
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//关闭PA15的调试端口复用功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
注意:下面的GPIO初始化结构体中也要改为初始化PA15口
*/
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出模式,引脚控制权交给了片上外设(定时器)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA1和PA2引脚初始化为推挽输出
/*设置GPIO初始化后的默认电平*/
GPIO_SetBits(GPIOA, GPIO_Pin_1); //设置PA1和PA2引脚为高电平
//RCC开启时钟
//要使用APB1的开启时钟函数,因为TIM2是APB1总线的外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟,这里选择内部时钟
TIM_InternalClockConfig(TIM2);
//配置结构体用于初始化时基单元
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;//就是PSC预分频器的值
//因为预分频器和计数器都有1一个数的偏差,所以这里要再减个1
//注意PSC和ARR的取值都要在0-65535之间,不要超范围了。
//这里我们预分频是对72M进行7200分频,得到的就是10k的计数频率,在10k的频率下,计10000个数,就是1s的时间
//计算定时器定时时间的公式:CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1),单位是频率,取倒数就是定时时间
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//就是重复计数器的值,是高级定时器才有的,这里不需要用给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(比较值,CCR比较寄存器)
//初始化输出比较单元
TIM_OC2Init(TIM2,&TIM_OCInitStruct);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2,Compare);
}Servo.h
c
#ifndef __SERVO_H
#define __SERVO_H
void Servo_Init(void);
void Servo_SetAngle(float Angle);
#endifServo.c
c
#include "stm32f10x.h" // Device header
#include "PWM.h"
//舵机初始化函数
void Servo_Init(void)
{
PWM_Init();
}
//设置舵机角度函数,Angle范围0-180
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle / 180 * 2000 + 500);
}main.c
c
#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:");
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驱动直流电机
接线图

程序实例
PWM.h
c
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);
#endifPWM.c
c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
/*------下面是复用端口为PA15的部分-----
//初始化AFIO:用于复用端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟
//引脚重映射配置
//将PA0的TIM2复用到PA15上
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//关闭PA15的调试端口复用功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
注意:下面的GPIO初始化结构体中也要改为初始化PA15口
*/
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出模式,引脚控制权交给了片上外设(定时器)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA1和PA2引脚初始化为推挽输出
/*设置GPIO初始化后的默认电平*/
GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2); //设置PA1和PA2引脚为高电平
//RCC开启时钟
//要使用APB1的开启时钟函数,因为TIM2是APB1总线的外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟,这里选择内部时钟
TIM_InternalClockConfig(TIM2);
//配置结构体用于初始化时基单元
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 = 36 - 1;//就是PSC预分频器的值
//因为预分频器和计数器都有1一个数的偏差,所以这里要再减个1
//注意PSC和ARR的取值都要在0-65535之间,不要超范围了。
//这里我们预分频是对72M进行7200分频,得到的就是10k的计数频率,在10k的频率下,计10000个数,就是1s的时间
//计算定时器定时时间的公式:CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1),单位是频率,取倒数就是定时时间
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//就是重复计数器的值,是高级定时器才有的,这里不需要用给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 = 10;//直译:脉冲,设置CCR(比较值,CCR比较寄存器)
//初始化输出比较单元
TIM_OC3Init(TIM2,&TIM_OCInitStruct);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
//设置占空比
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2,Compare);
}Motor.h
c
#ifndef __MOTOR_H
#define __MOTOR_H
void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);
#endifMotor.c
c
#include "stm32f10x.h" // Device header
#include "PWM.h"
//直流电机初始化
void Motor_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;//用于电机方向控制
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA1和PA2引脚初始化为推挽输出
PWM_Init();
}
void Motor_SetSpeed(int8_t Speed)
{
if(Speed >= 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
PWM_SetCompare3(Speed);
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
GPIO_SetBits(GPIOA,GPIO_Pin_5);
PWM_SetCompare3(-Speed);
}
}main.c
c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
uint8_t KeyNum;
int8_t Speed;
int main(void)
{
OLED_Init();
Motor_Init();
Key_Init();
OLED_ShowString(1,1,"Speed:");
while (1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
Speed += 20;
if(Speed > 100)
Speed = -100;
}
Motor_SetSpeed(Speed);
OLED_ShowSignedNum(1,7,Speed,3);
}
}