实操之STM32 F103C8T6 控制舵机TS90A/SG90(PWM驱动)
STM32 F103C8T6 控制舵机TS90A/SG90(PWM驱动)
一、TS90A/SG90三个引脚
信号线接单片机IO引脚,用来接收单片机发送的PWM。
注意:由于舵机需要较大电压/电流(直接用单片机供电无法正常工作),一般采用外接电源,也可以用micro usb给单片机供电。
TS90A/SG90 | 引脚 | 功能 |
---|---|---|
红线 | VCC | 5V |
棕线 | GND | 共地 |
橙线 | 信号线 | PWM |
外接电源法:电源负极与单片机GND连接,再将舵机GND与单片机GND相连——达到共地,这样做可以确保电路中的电压和电流能够正确流动,避免因电压差异导致的电路问题。(本文即采用此法)
二、C8T6引脚分布
三、 接线说明(另:串口下载–USB转TTL接线)
舵机、电源和单片机接线:
TS90A/SG90 | 单片机c8t6 | 外部电源 |
---|---|---|
红线VCC | 5V | |
棕线GND | GND | 单片机GND 接 电源负极 |
橙线PWM | PA0 |
四、代码部分
main.c
#include "TIMER.h"
#include "delay.h"
int main(void)
{
delay_init();
TIM2_PWM_Init(9999, 143); //TIM2_Int_Init(u16 arr, u16 psc),初始化定时器TIM2--20ms
//定时时间=(arr+1)(psc+1)/Tclk,Tclk为内部通用定时器时钟,本例程默认设置为72MHZ
while(1)
{
delay_ms(100);
// TIM_SetCompare1(TIM2, PWM); //设置待装入捕获比较寄存器的脉冲值,相当于不断设置TIM_Pulse
// //也即设置占空比,输出的PWM值
// //在本例程中PWM值即为一个周期(20ms)内引脚A0输出的高电平时长(单位2^-3ms)
/*计数arr=10000-1,所以一个周期计数10000次,
0度 250/10000=2.50%
45度 500/10000=5.00%
90度 750/10000=7.50%
135度 1000/10000=10.00%
180度 1250/10000=12.50%
*/
TIM_SetCompare1(TIM2, 250);//0
delay_ms(5000);
TIM_SetCompare1(TIM2, 500);//45
delay_ms(5000);
TIM_SetCompare1(TIM2, 700);//90
delay_ms(5000);
TIM_SetCompare1(TIM2, 1000);//135
delay_ms(5000);
TIM_SetCompare1(TIM2, 1250);//180
delay_ms(5000);
}
}
TIMER.c
#include "TIMER.h"
//通用定时器2初始化函数,arr:自动重装载值,psc:预分频系数,默认定时时钟为72MHZ时,两者共同决定定时中断时间
void TIM2_Int_Init(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义一个定时中断的结构体
NVIC_InitTypeDef NVIC_InitStrue; //定义一个中断优先级初始化的结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能通用定时器2时钟
TIM_TimeBaseInitStrue.TIM_Period=arr; //计数模式为向上计数时,定时器从0开始计数,计数超过到arr时触发定时中断服务函数
TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数,决定每一个计数的时长
TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数模式:向上计数
TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //一般不使用,默认TIM_CKD_DIV1
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrue); //根据TIM_TimeBaseInitStrue的参数初始化定时器TIM2
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //使能TIM2中断,中断模式为更新中断:TIM_IT_Update
NVIC_InitStrue.NVIC_IRQChannel=TIM2_IRQn; //属于TIM2中断
NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE; //中断使能
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级为1级,值越小优先级越高,0级优先级最高
NVIC_InitStrue.NVIC_IRQChannelSubPriority=1; //响应优先级为1级,值越小优先级越高,0级优先级最高
NVIC_Init(&NVIC_InitStrue); //根据NVIC_InitStrue的参数初始化VIC寄存器,设置TIM2中断
TIM_Cmd(TIM2, ENABLE); //使能定时器TIM2
}
void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update)==1) //当发生中断时状态寄存器(TIMx_SR)的bit0会被硬件置1
{
PCout(13)=!PCout(13); //LED灯(C13引脚)状态取反,该函数封装在库函数"sys.h"中
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //状态寄存器(TIMx_SR)的bit0置0
}
}
void TIM2_PWM_Init(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义一个定时中断的结构体
TIM_OCInitTypeDef TIM_OCInitTypeStrue; //定义一个PWM输出的结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟,在STM32中使用IO口前都要使能对应时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能通用定时器2时钟,A0引脚对应TIM2CHN1
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//引脚0
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出模式,定时器功能为A0引脚复用功能
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //定义该引脚输出速度为50MHZ
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化引脚GPIOA0
TIM_TimeBaseInitStrue.TIM_Period=arr; //计数模式为向上计数时,定时器从0开始计数,计数超过到arr时触发定时中断服务函数
TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数,决定每一个计数的时长
TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数模式:向上计数
TIM_TimeBaseInitStrue.TIM_ClockDivision=0; //一般不使用,默认TIM_CKD_DIV1
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrue); //根据TIM_TimeBaseInitStrue的参数初始化定时器TIM2
TIM_OCInitTypeStrue.TIM_OCMode=TIM_OCMode_PWM1; //PWM模式1,当定时器计数小于TIM_Pulse时,定时器对应IO输出有效电平
TIM_OCInitTypeStrue.TIM_OCPolarity=TIM_OCNPolarity_High; //输出有效电平为高电平
TIM_OCInitTypeStrue.TIM_OutputState=TIM_OutputState_Enable; //使能PWM输出
TIM_OCInitTypeStrue.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OC1Init(TIM2, &TIM_OCInitTypeStrue); //根TIM_OCInitTypeStrue参数初始化定时器2通道1
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable); //CH1预装载使能
TIM_ARRPreloadConfig(TIM2, ENABLE); //CH1预装载使能
TIM_Cmd(TIM2, ENABLE); //使能定时器TIM2
}
TIMER.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
#include "stm32f10x_tim.h"
void TIM2_Int_Init(u16 arr, u16 psc);
void TIM2_PWM_Init(u16 arr, u16 psc);
#endif
sys.c
#include "sys.h"
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI
void WFI_SET(void)
{
__ASM volatile("wfi");
}
//关闭所有中断
void INTX_DISABLE(void)
{
__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
__ASM volatile("cpsie i");
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
sys.h
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS 0 //定义系统文件夹是否支持UCOS
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
//以下为汇编函数
void WFI_SET(void); //执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void); //开启所有中断
void MSR_MSP(u32 addr); //设置堆栈地址
#endif
五、PWM控制舵机原理
建议详读这篇文章
[1.]单片机——SG90舵机工作原理–掏一淘哆啦A梦的奇妙口袋
作者:学习小心