实操之STM32 F103C8T6 控制舵机TS90A/SG90(PWM驱动)

STM32 F103C8T6 控制舵机TS90A/SG90(PWM驱动)

  • 一、TS90A/SG90三个引脚
  • 二、C8T6引脚分布
  • 三、 接线说明(另:[串口下载–USB转TTL接线](https://blog.csdn.net/weixin_63084090/article/details/140678686))
  • 四、代码部分
  • 五、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梦的奇妙口袋

    作者:学习小心

    物联沃分享整理
    物联沃-IOTWORD物联网 » 实操之STM32 F103C8T6 控制舵机TS90A/SG90(PWM驱动)

    发表回复