找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4903|回复: 4
收起左侧

51单片机舵机控制程序

[复制链接]
ID:301968 发表于 2018-6-21 14:16 | 显示全部楼层 |阅读模式
/**********************
   舵机控制PWM/按键控制/LED显示
    V1.0版
******************************************/   

#include "STC15W40XXX.H"
#include "intrins.h"
//定义主时钟
#define FOSC 33177600UL
#define T1000Hz (FOSC / 12 / 1000)
#define CCP_S0 0x10 //P_SW1.4
#define CCP_S1 0x20 //P_SW1.5

//************** PWM8 变量和常量以及IO口定义 ***************
//********************  6通道8 bit 软PWM ********************

sbit  P_PWM6 =  P3^4;           //  舵机1 PWM定义IO
sbit  P_PWM5 =  P3^5;      //  舵机2 PWM定义IO
sbit  P_PWM4 =  P3^6;          //  舵机3 PWM定义IO
sbit  P_PWM3 =  P2^0;
sbit  P_PWM2 =  P2^1;
sbit  P_PWM1 =  P2^2;
sbit  LED1 = P2^3;
sbit  LED2 = P2^4;
sbit  LED3  = P2^5;
sbit  LED4  = P2^6;
sbit  LED5  = P2^7;
sbit  LED6  = P1^0;
sbit  Key1=P5^5;          //单个舵机转动
sbit  Key2=P5^4;          //全部舵机转动
sbit  Key3=P1^7;          //复位按键
bit Reset=0;
uint P1_11us=0;
uint cnt;
uint value;
uchar K1_cont=0;
uchar K2_cont=0;
unsigned char  j;
unsigned char KeySta[3] = { 1, 1,  1 };  //全部按键的当前状态
unsigned char backup[3] = { 1, 1,  1 };   //按键值备份,保存前一次的值
uint Ptem_angle[6]={90,90,90,90,90,90};     //6个舵机临时存储角度(在0-180内)
uint SetP_angle[6] ={90,90,90,90,90,90}; //6个设定舵机转动角(0-180)(11us*(45-225)=0.5ms -2.5ms

//--------------------
void UartInit();        [url=]//9600bps@33.1776MHz[/url]
void Timer0Init();      //Timer0 set
void PCATimerInit();    //Timer0 set
void Key_scan();      //按键扫描函数
void Key_Function();     //按键功能函数
void LDE_Shew();         //LED显示函数
void Motor_Run();         //LED显示函数
   
void Delay500ms()  [url=]//@33.1776MHz[/url]
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 64;
j = 9;
k = 179;
do
{
  do
  {
   while (--k);
  } while (--j);
} while (--i);
}

/**********************************************/
void main()
{

PCATimerInit();   //PCA定时器初始化
Timer0Init();    //定时器0初始化
UartInit();     //串口初始化
EA = 1;           //打开总中断
Delay500ms();

while(1)
{
Key_Function();
LDE_Shew();
}
}
/********************** Timer0 11us中断函数 ************************/
//为什么要定义11us的中断,因为20ms的周期信号控制舵机转动,0.5-2.5中,只有2ms时间高电平控制0-180的角度。
//因此转动1度时,需要11us的时间(一次中断产生),180度需要1980us,约2MS,例如转动90度是1.5MS高和18.5的低,
//一个周期20ms产生1800次的中断,其中90+45=135次中断为高,1800-135=1665次为低。循环得到结果
void timer0 (void) interrupt 1
{   

switch (K1_cont)

      {
    case 0:  P_PWM1=0;  break;
               
    case 1:  Motor_Run();  P_PWM2= P_PWM3= P_PWM4= P_PWM5= P_PWM6= 0;
        if(P1_11us<=Ptem_angle[K1_cont-1]) { P_PWM1 =  1; }  else  { P_PWM1 =  0; }  break;
      
    case 2:  Motor_Run(); P_PWM1=P_PWM3=P_PWM4=P_PWM5=P_PWM6= 0;
        if(P1_11us<=Ptem_angle[K1_cont-1]) { P_PWM2 =  1; }  else { P_PWM2 =  0; }  break;
   
    case 3:  Motor_Run(); P_PWM1 =P_PWM2=P_PWM4=P_PWM5=P_PWM6= 0;     
        if(P1_11us<=Ptem_angle[K1_cont-1]) { P_PWM3 =  1; } else { P_PWM3 =  0;  }  break;

    case 4:  Motor_Run(); P_PWM1 =P_PWM2=P_PWM3=P_PWM5=P_PWM6= 0;     
        if(P1_11us<=Ptem_angle[K1_cont-1]) { P_PWM4 =  1;  } else  { P_PWM4 =  0; }  break;
         
     case 5:  Motor_Run();  P_PWM1 =P_PWM2=P_PWM3=P_PWM4=P_PWM6=0;   
        if(P1_11us<=Ptem_angle[K1_cont-1]) { P_PWM5 =  1;  } else { P_PWM5 =  0;  }  break;
    case 6:  Motor_Run(); P_PWM1=P_PWM2=P_PWM3= P_PWM4= P_PWM5=0;   
        if(P1_11us<=Ptem_angle[K1_cont-1]) { P_PWM6 =  1;  } else { P_PWM6 =  0;  }  break;      
   
    default: break;
   
    }

  }
//PCA定时器中断
void PCA_isr() interrupt 7 using 1
{
    CCF0 = 0;                       //清中断标志
    CCAP0L = value;
    CCAP0H = value >> 8;            //更新比较值
    value += T1000Hz;
    if (cnt-- == 0)
    {
        cnt = 2000;                  //记数1000次
        SBUF=0XFF;
    }
  Key_scan();
}
//*************************************************************************
//UART  串口中断
void Uart() interrupt 4 using 1
{
  if (RI)
   {
     RI = 0;   
    }
if (TI)
  {
   TI = 0; //
  }
}

void Key_scan()//按键扫描函数
{
uchar j;                                   
static uchar keybuf[3] = { 0xFF, 0xFF, 0xFF };   //按键扫描缓冲区
  //将3 个按键值移入缓冲区
keybuf[0] = (keybuf[0] << 1) | Key3;
keybuf[1] = (keybuf[1] << 1) | Key2;
keybuf[2] = (keybuf[2] << 1) | Key1;
//消抖后更新按键状态
for (j=0; j<3; j++) //       3 个按键,所以循环 3 次
{
  if ((keybuf[j] & 0xFF) == 0x00)   //连续 3次扫描值为 0,即 24ms 内都是按下状态时,可认为按键已稳定的按下
  {
   KeySta[j] = 0;
  }
  else
  if ((keybuf[j] & 0xFF) == 0xFF)
   {        //连续 4次扫描值为 1,即16ms 内都是弹起状态时,可认为按键已稳定的弹起
    KeySta[j] = 1;
   }
  
}
}
void Key_Function()
{   
  static bit i=0;
for (j=0; j<3; j++)  //依次检测按键
  {
   if (backup[j] != KeySta[j]) //检测按键动作
   {
    if (backup[j] != 0) //按键按下时执行动作
    {
   
     switch (j)
          {
            case 0:  K2_cont=0; K1_cont++; if(K1_cont>=7)  K1_cont=0;      
        
        break;
        
        case 1:
            if(!i)
            {
             SetP_angle[K1_cont-1]-=10;
             if(SetP_angle[K1_cont-1]<=45)
             {
              i=!i;
             }
            }
            else
            {
              SetP_angle[K1_cont-1]+=10;
              if(SetP_angle[K1_cont-1]>=170)
              {
               i=!i;
              }
            }
            
            
            
      
        break;
        
        case 2:
        
        break;
            
        default: break;
       }
    }
     backup[j] = KeySta[j]; //更新前一次的备份值
   }
  }
}
//
//////LEDf显示函数
void LDE_Shew()
{
switch (K1_cont)
          {
            case 0:  LED1=LED2=LED3=LED4=LED5=LED6=1;  break;   
               
        case 1:  LED1=LED2=LED3=LED4=LED5=LED6=1; LED1=0;   break;
                                          
        case 2:  LED1=LED2=LED3=LED4=LED5=LED6=1; LED2=0;   break;
                              
        case 3:  LED1=LED2=LED3=LED4=LED5=LED6=1; LED3=0;  break;
      
        case 4:  LED1=LED2=LED3=LED4=LED5=LED6=1; LED4=0;   break;
                       
        case 5:  LED1=LED2=LED3=LED4=LED5=LED6=1; LED5=0;   break;
                     
        case 6:  LED1=LED2=LED3=LED4=LED5=LED6=1; LED6=0;   break;
                                 
        default: break;
       }
   
}
void Motor_Run()
{
  
  P1_11us++;           //一次中断,自加一次
  if (P1_11us>1800)    //20ms执行以下程序
  {
    P1_11us=0;        //清0
    if(Ptem_angle[K1_cont-1]>=(SetP_angle[K1_cont-1]+40))      
    {
     Ptem_angle[K1_cont-1]--;              //自加一次角度,电机缓慢运行,在设定的角度内反复转动,如0-90度
      
    }
    else
    {
     Ptem_angle[K1_cont-1]++;
   
    }
  }
}
//******************串口初始化
void UartInit(void)  [url=]//9600bps@33.1776MHz[/url]
{
SCON = 0x50;  //8位数据,可变波特率
AUXR |= 0x01;  //串口1选择定时器2为波特率发生器
AUXR &= 0xFB;  //定时器2时钟为Fosc/12,即12T
T2L = 0xB8;   //设定定时初值
T2H = 0xFF;   //设定定时初值
AUXR |= 0x10;  //启动定时器2
ES=1;      //允许串口中断
}
//定时器T0用作脉冲发生器,定时11us
void Timer0Init(void)  //11微秒@33.1776MHz
{
AUXR |= 0x80;  //定时器时钟1T模式
TMOD &= 0xF0;  //设置定时器模式
TL0 = 0x8F;   //设置定时初值
TH0 = 0xFE;   //设置定时初值
TF0 = 0;    //清除TF0标志
TR0 = 1;    //定时器0开始计时
ET0=1; //中断允许     
}
//PCA定时器设置 定时1MS
void PCATimerInit()
{
ACC = P_SW1;

ACC &= ~(CCP_S0 | CCP_S1);          //CCP_S0=0 CCP_S1=0
P_SW1 = ACC;                 //(P1.2/ECI, P1.1/CCP0, P1.0/CCP1, P3.7/CCP2)
CCON = 0;               //初始化PCA控制寄存器
                      //PCA定时器停止 清除CF标志 清除模块中断标志
CL = 0;               //复位PCA寄存器
CH  = 0;
CMOD = 0x00;        //设置PCA时钟源
                       //禁止PCA计数器中断
value = T1000Hz;
CCAP0L = value;
CCAP0H = value >> 8;    //初始化PCA模块0
value += T1000Hz;
CCAPM0 = 0x49;        //PCA模块0为16位定时器模式
CR = 1;               //PCA定时器开始工作
cnt = 0;
}

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:1 发表于 2018-6-21 17:32 | 显示全部楼层
原理图与头文件能分享一下吗?
回复

使用道具 举报

ID:346558 发表于 2018-6-22 23:50 | 显示全部楼层
大哥,能帮我调试一下我最近做的一个项目吗,跟您的控制三个舵机类似,必有重谢!!
回复

使用道具 举报

ID:301968 发表于 2018-7-28 10:53 | 显示全部楼层
Somiy 发表于 2018-6-22 23:50
大哥,能帮我调试一下我最近做的一个项目吗,跟您的控制三个舵机类似,必有重谢!!

可以
回复

使用道具 举报

ID:436733 发表于 2018-12-8 17:51 | 显示全部楼层
是蓝牙控制的吗
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表