Skip to content

【6-4】流水灯&舵机电机驱动

LED流水灯

接线图

image-20231205185627553

程序部分

PWM.h

c
#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
#endif

PWM.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驱动舵机

接线图

image-20231206183126371

程序实例

PWM.h

c
#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);
#endif

PWM.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);
#endif

Servo.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驱动直流电机

接线图

image-20231206194930453

程序实例

PWM.h

c
#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);
#endif

PWM.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);


#endif

Motor.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);
	}
}
最近更新