电子DIY小家 发表于 2023-2-3 11:27:58

驱动舵机, STC8H8K64U高级PWM, STC驱动教程系列

驱动舵机:STC驱动教程系列——STC8H8K64U高级PWM驱动舵机
一、前言
看到最近有朋友在群里问下图的这种舵机(SG90)怎么驱动,有小伙伴直接毫不犹豫脱口而出了一句定时器,当然早期的MCU用这个当然没问题,但是新款的MCU还有这个就显得有那么点过时了,这里我们分享一种新的思路——PWM驱动(当然驱动方式包括但不限于PWM,这里只提供一种比较常见的解决方案)。





二、舵机驱动原理及使用PWM驱动的意义
众所周知,舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。在高档遥控玩具,如飞机、潜艇模型,遥控机器人中已经得到了普遍应用。他可以根据输入脉冲信号的高电平时间直接输出对应的角度,高电平时间和角度的关系如下图所示:



可以看到上图中每一个脉冲的周期是20ms(相邻两个上升沿信号之间的时间差为20ms),即频率为50HZ,其次脉冲宽度为0.5ms舵机处于0度位置,脉冲宽度为2.5ms,舵机处于180度位置,脉冲宽度为1.5ms舵机处于90度位置,中间的角度就可以按照这个关系去映射咯。


当然上述的波形直接用软件的延时也是可以时间的对吧,例如我要转到0度,我也可以这样弄。现在ISP软件的软件延时计算器里生成如下两个延时:


然后假设使用P60端口那么将端口初始化为准双向口或者推挽输出口,随后写如下代码就可以将舵机保持在0度了对不。
P60=1;
Delay500us();
P60=0;
Delay500us();
Delay19ms();当然,用延时也能输出的波形,定时器当然也可以,但是用延时太浪费MCU的性能,尤其是多路舵机同时以不同的角度控制的话,简直生不如dead;这里强烈不建议,定时器的话虽然能解决CPU占用的问题,但是他会频繁的进入中断,对于我们做产品的人来说,如果能有最优解那么定时器的方法并不可取。这里直接使用硬件的PWM,我们只要配置好周期,往比较寄存器写入比较值就能直接输出PWM了,不需要进入中断,更不需要延时,只要MCU不断电,这个PWM能跑到地老天荒,毕竟这是硬件实现的,稳定性更高,精度更高。


三、代码的实现
这里直接贴上我们的测试代码再来分段讲解:
#include    "reg51.h"       //包含此头文件后,里面声明的寄存器不需要再手动输入,避免重复定义
#include    "intrins.h"

#define   MAIN_Fosc       24000000L   //定义主时钟

typedef   unsigned char   u8;
typedef   unsigned int    u16;
typedef   unsigned long   u32;

//手动输入声明"reg51.h"头文件里面没有定义的寄存器
sfr TH2= 0xD6;
sfr TL2= 0xD7;
sfr IE2   = 0xAF;
sfr INT_CLKO = 0x8F;
sfr AUXR = 0x8E;
sfr P_SW1 = 0xA2;
sfr P_SW2 = 0xBA;

sfr P4   = 0xC0;
sfr P5   = 0xC8;
sfr P6   = 0xE8;
sfr P7   = 0xF8;
sfr P1M1 = 0x91;    //PxM1.n,PxM0.n   =00--->Standard,    01--->push-pull
sfr P1M0 = 0x92;    //                  =10--->pure input,11--->open drain
sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xB1;
sfr P3M0 = 0xB2;
sfr P4M1 = 0xB3;
sfr P4M0 = 0xB4;
sfr P5M1 = 0xC9;
sfr P5M0 = 0xCA;
sfr P6M1 = 0xCB;
sfr P6M0 = 0xCC;
sfr P7M1 = 0xE1;
sfr P7M0 = 0xE2;

sbit P00 = P0^0;
sbit P01 = P0^1;
sbit P02 = P0^2;
sbit P03 = P0^3;
sbit P04 = P0^4;
sbit P05 = P0^5;
sbit P06 = P0^6;
sbit P07 = P0^7;
sbit P10 = P1^0;
sbit P11 = P1^1;
sbit P12 = P1^2;
sbit P13 = P1^3;
sbit P14 = P1^4;
sbit P15 = P1^5;
sbit P16 = P1^6;
sbit P17 = P1^7;
sbit P20 = P2^0;
sbit P21 = P2^1;
sbit P22 = P2^2;
sbit P23 = P2^3;
sbit P24 = P2^4;
sbit P25 = P2^5;
sbit P26 = P2^6;
sbit P27 = P2^7;
sbit P30 = P3^0;
sbit P31 = P3^1;
sbit P32 = P3^2;
sbit P33 = P3^3;
sbit P34 = P3^4;
sbit P35 = P3^5;
sbit P36 = P3^6;
sbit P37 = P3^7;
sbit P40 = P4^0;
sbit P41 = P4^1;
sbit P42 = P4^2;
sbit P43 = P4^3;
sbit P44 = P4^4;
sbit P45 = P4^5;
sbit P46 = P4^6;
sbit P47 = P4^7;
sbit P50 = P5^0;
sbit P51 = P5^1;
sbit P52 = P5^2;
sbit P53 = P5^3;
sbit P54 = P5^4;
sbit P55 = P5^5;
sbit P56 = P5^6;
sbit P57 = P5^7;

/****************************** 用户定义宏 ***********************************/

#define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒

#define PWMA_ENO   (*(unsigned charvolatile xdata *)0xFEB1)
#define PWMA_PS      (*(unsigned charvolatile xdata *)0xFEB2)
#define PWMB_ENO   (*(unsigned charvolatile xdata *)0xFEB5)
#define PWMB_PS      (*(unsigned charvolatile xdata *)0xFEB6)                              

#define PWMA_CR1   (*(unsigned charvolatile xdata *)0xFEC0)
#define PWMA_CR2   (*(unsigned charvolatile xdata *)0xFEC1)
#define PWMA_SMCR    (*(unsigned charvolatile xdata *)0xFEC2)
#define PWMA_ETR   (*(unsigned charvolatile xdata *)0xFEC3)
#define PWMA_IER   (*(unsigned charvolatile xdata *)0xFEC4)
#define PWMA_SR1   (*(unsigned charvolatile xdata *)0xFEC5)
#define PWMA_SR2   (*(unsigned charvolatile xdata *)0xFEC6)
#define PWMA_EGR   (*(unsigned charvolatile xdata *)0xFEC7)
#define PWMA_CCMR1   (*(unsigned charvolatile xdata *)0xFEC8)
#define PWMA_CCMR2   (*(unsigned charvolatile xdata *)0xFEC9)
#define PWMA_CCMR3   (*(unsigned charvolatile xdata *)0xFECA)
#define PWMA_CCMR4   (*(unsigned charvolatile xdata *)0xFECB)
#define PWMA_CCER1   (*(unsigned charvolatile xdata *)0xFECC)
#define PWMA_CCER2   (*(unsigned charvolatile xdata *)0xFECD)
#define PWMA_CNTRH   (*(unsigned charvolatile xdata *)0xFECE)
#define PWMA_CNTRL   (*(unsigned charvolatile xdata *)0xFECF)
#define PWMA_PSCRH   (*(unsigned charvolatile xdata *)0xFED0)
#define PWMA_PSCRL   (*(unsigned charvolatile xdata *)0xFED1)
#define PWMA_ARRH    (*(unsigned charvolatile xdata *)0xFED2)
#define PWMA_ARRL    (*(unsigned charvolatile xdata *)0xFED3)
#define PWMA_RCR   (*(unsigned charvolatile xdata *)0xFED4)
#define PWMA_CCR1H   (*(unsigned charvolatile xdata *)0xFED5)
#define PWMA_CCR1L   (*(unsigned charvolatile xdata *)0xFED6)
#define PWMA_CCR2H   (*(unsigned charvolatile xdata *)0xFED7)
#define PWMA_CCR2L   (*(unsigned charvolatile xdata *)0xFED8)
#define PWMA_CCR3H   (*(unsigned charvolatile xdata *)0xFED9)
#define PWMA_CCR3L   (*(unsigned charvolatile xdata *)0xFEDA)
#define PWMA_CCR4H   (*(unsigned charvolatile xdata *)0xFEDB)
#define PWMA_CCR4L   (*(unsigned charvolatile xdata *)0xFEDC)
#define PWMA_BKR   (*(unsigned charvolatile xdata *)0xFEDD)
#define PWMA_DTR   (*(unsigned charvolatile xdata *)0xFEDE)
#define PWMA_OISR    (*(unsigned charvolatile xdata *)0xFEDF)

/*****************************************************************************/

#define PWM1_1      0x00      //P:P1.0N:P1.1
#define PWM1_2      0x01      //P:P2.0N:P2.1
#define PWM1_3      0x02      //P:P6.0N:P6.1

#define PWM2_1      0x00      //P:P1.2/P5.4N:P1.3
#define PWM2_2      0x04      //P:P2.2N:P2.3
#define PWM2_3      0x08      //P:P6.2N:P6.3

#define PWM3_1      0x00      //P:P1.4N:P1.5
#define PWM3_2      0x10      //P:P2.4N:P2.5
#define PWM3_3      0x20      //P:P6.4N:P6.5

#define PWM4_1      0x00      //P:P1.6N:P1.7
#define PWM4_2      0x40      //P:P2.6N:P2.7
#define PWM4_3      0x80      //P:P6.6N:P6.7
#define PWM4_4      0xC0      //P:P3.4N:P3.3

#define ENO1P       0x01
#define ENO1N       0x02
#define ENO2P       0x04
#define ENO2N       0x08
#define ENO3P       0x10
#define ENO3N       0x20
#define ENO4P       0x40
#define ENO4N       0x80

#define PWM_PSCR    99   //设置分频
#define PWM_PERIOD4799    //设置周期值

#define PWM_Min   120
#define PWM_Max   600

/*************本地变量声明    **************/
bit B_1ms;          //1ms标志

u16 PWM1_Duty = PWM_Min;
u16 PWM2_Duty = PWM_Min;
u16 PWM3_Duty = PWM_Min;
u16 PWM4_Duty = PWM_Min;

bit PWM1_Flag;
bit PWM2_Flag;
bit PWM3_Flag;
bit PWM4_Flag;

void UpdatePwm(void);

/******************** 主函数 **************************/
void main(void)
{
    P_SW2 |= 0x80; //扩展寄存器(XFR)访问使能

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    PWM1_Flag = 0;
    PWM2_Flag = 0;
    PWM3_Flag = 0;
    PWM4_Flag = 0;

    PWM1_Duty = 0;
    PWM2_Duty = 256;
    PWM3_Duty = 512;
    PWM4_Duty = 1024;

    //Timer0初始化
    AUXR = 0x80;    //Timer0 set as 1T, 16 bits timer auto-reload,
    TH0 = (u8)(Timer0_Reload / 256);
    TL0 = (u8)(Timer0_Reload % 256);
    ET0 = 1;    //Timer0 interrupt enable
    TR0 = 1;    //Tiner0 run

    PWMA_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
    PWMA_CCER2 = 0x00;
    PWMA_CCMR1 = 0x68; //通道模式配置
    PWMA_CCMR2 = 0x68;
    PWMA_CCMR3 = 0x68;
    PWMA_CCMR4 = 0x68;
    PWMA_CCER1 = 0x55; //配置通道输出使能和极性
    PWMA_CCER2 = 0x55;

    PWMA_PSCRH = (u8)(PWM_PSCR>>8);   //设置分频
    PWMA_PSCRL = (u8)(PWM_PSCR);

    PWMA_ARRH = (u8)(PWM_PERIOD >> 8);//设置周期时间
    PWMA_ARRL = (u8)PWM_PERIOD;

    PWMA_ENO = 0x00;
    PWMA_ENO |= ENO1P; //使能输出
    PWMA_ENO |= ENO1N; //使能输出
    PWMA_ENO |= ENO2P; //使能输出
    PWMA_ENO |= ENO2N; //使能输出
    PWMA_ENO |= ENO3P; //使能输出
    PWMA_ENO |= ENO3N; //使能输出
    PWMA_ENO |= ENO4P; //使能输出
    PWMA_ENO |= ENO4N; //使能输出

    PWMA_PS = 0x00;//高级 PWM 通道输出脚选择位
    PWMA_PS |= PWM1_3; //选择 PWM1_3 通道
    PWMA_PS |= PWM2_3; //选择 PWM2_3 通道
    PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
    PWMA_PS |= PWM4_3; //选择 PWM4_3 通道

    PWMA_BKR = 0x80; //使能主输出
    PWMA_CR1 |= 0x01; //开始计时

    P40 = 0;                //给LED供电
    EA = 1;   //打开总中断

    while (1)
    {
    }
}


/********************** Timer0 1ms中断函数 ************************/
void timer0(void) interrupt 1
{
    static u8 count=0;
    count++;
    if( count>=50 )
    {
      count = 0;
      if(!PWM1_Flag)
      {
            PWM1_Duty+=10;
            if(PWM1_Duty >PWM_Max) PWM1_Flag = 1;
      }
      else
      {
            PWM1_Duty-=10;
            if(PWM1_Duty <= PWM_Min) PWM1_Flag = 0;
      }

      if(!PWM2_Flag)
      {
            PWM2_Duty+=10;
            if(PWM2_Duty > PWM_Max) PWM2_Flag = 1;
      }
      else
      {
            PWM2_Duty-=10;
            if(PWM2_Duty <= PWM_Min) PWM2_Flag = 0;
      }

      if(!PWM3_Flag)
      {
            PWM3_Duty+=10;
            if(PWM3_Duty > PWM_Max) PWM3_Flag = 1;
      }
      else
      {
            PWM3_Duty-=10;
            if(PWM3_Duty <= PWM_Min) PWM3_Flag = 0;
      }

      if(!PWM4_Flag)
      {
            PWM4_Duty+=10;
            if(PWM4_Duty > PWM_Max) PWM4_Flag = 1;
      }
      else
      {
            PWM4_Duty-=10;
            if(PWM4_Duty <= PWM_Min) PWM4_Flag = 0;
      }
      
      UpdatePwm();
    }
}


//========================================================================
// 函数: UpdatePwm(void)
// 描述: 更新PWM占空比.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void UpdatePwm(void)
{
    PWMA_CCR1H = (u8)(PWM1_Duty >> 8); //设置占空比时间
    PWMA_CCR1L = (u8)(PWM1_Duty);
    PWMA_CCR2H = (u8)(PWM2_Duty >> 8); //设置占空比时间
    PWMA_CCR2L = (u8)(PWM2_Duty);
    PWMA_CCR3H = (u8)(PWM3_Duty >> 8); //设置占空比时间
    PWMA_CCR3L = (u8)(PWM3_Duty);
    PWMA_CCR4H = (u8)(PWM4_Duty >> 8); //设置占空比时间
    PWMA_CCR4L = (u8)(PWM4_Duty);
}注:以上代码基于开天斧的试验箱程序“25-高级PWM1-PWM2-PWM3-PWM4,驱动P6口呼吸灯实验程序”改编。


上述代码:
1.main函数之前就是头文件应用,寄存器定义,变量定义等,具体的可以查阅手册对照哈,这里暂时先不赘述了。
2.main函数开始先将打开这个P_SW2的寄存器的最高位,因为手册说过要用一些特殊寄存器必须打开这,我们的PWM的寄存器都在这里面。

3.然后就是PWMx_Flag和PWMx_Duty(x取值0-3)这两个变量的初始化了,PWMx_Flag是用来设定当前PWM的高电平时间是越来越大还是越来越小的,PWMx_Duty就是当前PWM的高电平计数值。PWMx_Flag为0,PWMx_Duty会往上计数,数值越来越大,角度也越来越大,反之亦然。
4.然后是定时器的初始化,我们想要他每隔一定的时间转动一定的角度,那么这个“一定的时间”怎么来的,我们就用定时器来实现咯,定时器初始化如上,这里初始化为1ms一次,当然自己不会使用定时器代码的话,我们依旧可以借助工具——ISP软件


5.然后开始是PWM的初始化部分,
    5.1 先是PWMA_CCER1 = 0x00;PWMA_CCER2 = 0x00;这里先把捕获/比较寄存器的数值全部清空,在写模式
    5.2 然后PWMA_CCMR1 = 0x68; 这一步主要是为了将PWM设置为“PWM模式1”

    上图看起来可能不太清晰,这里直接上图,如下图,运行的时候有一个计数器PWM_CNT,如果是向上计数他会从0开始计数到我们设定的PWM_ARR的值,到了设定值会变成0重新开始计数,这个计数值我们称之为PWM_CNT,然后我们设定一个比较值PWM_CCR,只要CNT小于CCR,就输出高电平,反之输出低电平,那么我们只要设置好ARR就可以调节输出的频率,调节CCR就可以改变输出的高电平时间对不对。(一个PWM有4组通道,4组占用一个ARR也就是四组PWM频率相同,每组通道有独立的CCR,也就是每组通道的占空比都可以自由设定,且每组通道都有PWMA_CCMR寄存器,也就是每组PWM都可以设置独立的工作模式)

    5.3这里PWMA_CCER1 = 0x55; 这里主要是为了开启捕获/比较的通道和开启比较输出。



    5.4 这里四行是为了设定我们的频率,和上图的5.2图中所讲的一样,设定ARR的值。

      PWMA_PSCRH = (u8)(PWM_PSCR>>8);   //设置分频
      PWMA_PSCRL = (u8)(PWM_PSCR);
      PWMA_ARRH = (u8)(PWM_PERIOD >> 8);//设置周期时间
      PWMA_ARRL = (u8)PWM_PERIOD;



    计算的频率公式如上所示,这里PWM_PSCR = 99,PWM_PERIOD = 4799,主频24M,这里我们设置为边沿对齐模式(后面讲)所以输出频率= 24000000/(99+1)/(4799+1)=50hz刚刚好:lol。
   
    5.5下面几行代码就是控制哪几个通道输出。我们之前提过一个PWM有四组通道,一组有2个输出通道,这里把八个都打开了,其实我们驱动一舵机只要一个就够了,这里我们就先不去动他(我懒!)
      PWMA_ENO = 0x00;
      PWMA_ENO |= ENO1P; //使能输出
      PWMA_ENO |= ENO1N; //使能输出
      PWMA_ENO |= ENO2P; //使能输出
      PWMA_ENO |= ENO2N; //使能输出
      PWMA_ENO |= ENO3P; //使能输出
      PWMA_ENO |= ENO3N; //使能输出
      PWMA_ENO |= ENO4P; //使能输出
      PWMA_ENO |= ENO4N; //使能输出

    5.6 下面几行代码就是选择输出的通道,因为我们有好多组通道可以选择,如果这时候刚巧某个通道的某一个IO口被占用了,我们就可以换一个,具体可以如图

      PWMA_PS = 0x00;//高级 PWM 通道输出脚选择位
      PWMA_PS |= PWM1_3; //选择 PWM1_3 通道
      PWMA_PS |= PWM2_3; //选择 PWM2_3 通道
      PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
      PWMA_PS |= PWM4_3; //选择 PWM4_3 通道

    可以看下图,我们这个程序里配置的就是P6端口的输出,当然你也可以指定你要的端口,只要这表里有就可输出。


    5.7 PWMA_BKR = 0x80; 因为之前我们已经开启了CC1E位,我们这里遵循手册只要打开MOEn这个位就可以开启OC和OCn的输出了。


    5.8 PWMA_CR1 |= 0x01; 这里设置了边沿模式,并且开启了向上计数的模式



    至此PWM的地方就配置完成了。当然我们自己写代码可以不需要从头到尾深究每一个寄存器,我们可以参考手册的案例,参考试验箱例程,基于这个修改自己想要的模式或者功能就好了,非常的方便。
    最后,我们在定时器中断里,如果PWM1_Flag是0,就是向上计数,每隔50ms让PWM1_Duty增加10,让角度越来越大到了最大数值就把PWM1_Flag变成1,每隔50ms让PWM1_Duty减少10,角度越来越小,记得这个变量更新了以后,把我们最后的数值写入CCR的比较值寄存器,是不是初始化完了输出PWM就非常的方便了,修改这一个寄存器就可以输出我们想要的任意波形。我们这个程序最终就是4路舵机(P60,P62,P64,P66四个端口)角度在0-180度之间不断的来回运动。这里需要注意舵机不要突然给他一个瞬态变化的值,不然瞬态电流会非常大,比如你上一秒还是0度的数值,下一秒就输出180度,我们要给他缓缓的变换角度,0先到45,再到90,再到135,再到180,千万别一下就给他那啥了~另外我们这个试验用的小舵机,大功率的舵机请单独供电,不然MCU容易掉电重启。

    还有这个最大值和最小值是怎么来的,因为计数的PWM_ARR是不是4799,从0-4799总共4800个数字。已知4800数值对应的是20ms的周期,我们想要高电平时间时间是10ms,那么这个CCR的数值就是2400对不对,我们想要高电平时间是5ms,那么这个CCR的数值就是1200对不对,所以这里我们最小的高电平时间是0.5ms,最大是2.5ms,然后你们也会计算了吧~





四、测试结果
先用示波器观察我们的输出波形是不是我们写的这个样子,示波器的表笔接P60,地线接上,然后观察是波形。
attach://3355.mp4
可以看到示波器自动测量的参数,在屏幕下方有显示,频率,周期和正脉宽的信息,频率基本稳定在50,周期约为20ms,正脉宽在500us-2.5ms之间变化,这就是我们的代码要实现的效果
   

最后的最后,我们直接上机测试。




好吧,没有这个舵机,只能到这里就全剧终了!附上代码!留给小伙伴们自行测试。



21cnxin 发表于 2023-2-4 15:52:59

不错,支持一下

hu_jia168 发表于 2023-2-6 17:11:38

大佬又出神作了

crcc_3c 发表于 2023-2-17 09:30:48

好资料,讲解很到位,谢谢大神

默默无铭 发表于 2023-2-21 00:01:55

感谢大佬的方案,但大佬你有什么方案可以解决因启动产生的电流。并且SG90在运动时又会产生感生电流,相当于一个小发电机,如何预防。使用在单片机IO接口与舵机之间的电路如何设计

电子DIY小家 发表于 2023-2-21 01:37:02

默默无铭 发表于 2023-2-21 00:01
感谢大佬的方案,但大佬你有什么方案可以解决因启动产生的电流。并且SG90在运动时又会产生感生电流,相当于 ...

最常见的方案如下:
1.电源部分:舵机和MCU两部分使用独立电源供电,或者MCU部分使用隔离电源+续流电路
2.控制部分:IO输出端口可以加光耦等隔离电路保护IO,做前后隔离
3.程序控制上尽量不要做瞬间变换角度,初始位置是0,一下输出个180度的脉冲出去的话那电流还是很恐怖的。

默默无铭 发表于 2023-2-21 10:18:56

电子DIY小家 发表于 2023-2-21 01:37
最常见的方案如下:
1.电源部分:舵机和MCU两部分使用独立电源供电,或者MCU部分使用隔离电源+续流电路
2 ...

感谢大佬的回复

梁工 发表于 2023-3-20 14:30:49

本帖最后由 梁工 于 2023-3-20 14:31 编辑

舵机使用PPM信号控制的,一般比较标准的PPM信号是1.0~2.0ms,大部分应用,步进1us就足够了,有1000步。
PPM信号最好使用PWM产生,硬件产生的波形稳定,不受程序影响,不要用软件循坏输出PPM信号,占用CPU带宽,波形受中断影响。

PPM信号周期大部分是4~20ms的,响应速度快就用短周期,比如高速比例遥控车(速度30km/h以上)、四轴飞行器等等,要求操作响应非常快,则一般用5~10ms的周期。固定翼飞机模型、动作缓慢的机械手等等,则可以用到20ms周期。

qjy822 发表于 2023-3-25 09:30:37

https://www.stcaimcu.com/data/attachment/forum/202302/03/112321wn12v2psvt02p145.png
   想问下能不能做到图片上低电平时间长,高电平时间短,再到高电平时间长,低电平时间短,就是这个波形倒过来一样,就是调节范围要宽,从最低从是0到最高全是F的变化范围可以做做到吗?我现在用例程都达不到这样的效果,请问程序要怎样处理?

qjy822 发表于 2023-3-25 09:32:06

我都想用定时器来写了,如果PWM能做到就太好了
页: [1] 2 3 4 5 6
查看完整版本: 驱动舵机, STC8H8K64U高级PWM, STC驱动教程系列