zcllom 发表于 2020-9-15 11:22:54

求助STC15W401AS的PWM功能,帮忙设置频率为100Hz。

求助STC15W401AS的PWM功能,帮忙设置频率为100Hz。
这个型号资源有限,最好不用定时器,直接自带的PCA功能。我看了下分频,做到100Hz似乎不可能。所以发帖求助。如果实在用PCA做不了,那就还是设置定时器,怎么弄也请帮忙。。

古调独弹 发表于 2020-9-26 00:49:56

STC-ISP中带的例程改改应该就能用了

/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966-------------------------*/
/* --- Web: www.STCMCU.com --------------------------------------------*/
/* --- Web: www.GXWMCU.com --------------------------------------------*/
/* 如果要在文章中应用此代码,请在文章中注明使用了STC的资料及程序      */
/*---------------------------------------------------------------------*/


#include    <reg52.h>


/*************功能说明    **************

输出3路9~16位PWM信号。

PWM频率 = MAIN_Fosc / PWM_DUTY, 假设 MAIN_Fosc = 24MHZ, PWM_DUTY = 6000, 则输出PWM频率为4000HZ.

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

/***************************用户宏定义*******************************************************/
#define   MAIN_Fosc       24000000UL//定义主时钟

#define   PWM_DUTY      6000      //定义PWM的周期,数值为PCA所选择的时钟脉冲个数。
#define   PWM_HIGH_MIN    80          //限制PWM输出的最小占空比, 避免中断里重装参数时间不够。
#define   PWM_HIGH_MAX    (PWM_DUTY - PWM_HIGH_MIN)       //限制PWM输出的最大占空比。

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

#define PCA0            0
#define PCA1            1
#define PCA2            2
#define PCA_Counter   3
#define PCA_P12_P11_P10_P37 (0<<4)
#define PCA_P34_P35_P36_P37 (1<<4)
#define PCA_P24_P25_P26_P27 (2<<4)
#define PCA_Mode_PWM                0x42
#define PCA_Mode_Capture            0
#define PCA_Mode_SoftTimer          0x48
#define PCA_Mode_HighPulseOutput    0x4c
#define PCA_Clock_1T    (4<<1)
#define PCA_Clock_2T    (1<<1)
#define PCA_Clock_4T    (5<<1)
#define PCA_Clock_6T    (6<<1)
#define PCA_Clock_8T    (7<<1)
#define PCA_Clock_12T   (0<<1)
#define PCA_Clock_Timer0_OF (2<<1)
#define PCA_Clock_ECI   (3<<1)
#define PCA_Rise_Active (1<<5)
#define PCA_Fall_Active (1<<4)
#define PCA_PWM_8bit    (0<<6)
#define PCA_PWM_7bit    (1<<6)
#define PCA_PWM_6bit    (2<<6)

#define   ENABLE      1
#define   DISABLE   0

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

sfr AUXR1 = 0xA2;
sfr CCON = 0xD8;
sfr CMOD = 0xD9;
sfr CCAPM0 = 0xDA;      //PCA模块0的工作模式寄存器。
sfr CCAPM1 = 0xDB;      //PCA模块1的工作模式寄存器。
sfr CCAPM2 = 0xDC;      //PCA模块2的工作模式寄存器。

sfr CL   = 0xE9;
sfr CCAP0L = 0xEA;      //PCA模块0的捕捉/比较寄存器低8位。
sfr CCAP1L = 0xEB;      //PCA模块1的捕捉/比较寄存器低8位。
sfr CCAP2L = 0xEC;      //PCA模块2的捕捉/比较寄存器低8位。

sfr CH   = 0xF9;
sfr CCAP0H = 0xFA;      //PCA模块0的捕捉/比较寄存器高8位。
sfr CCAP1H = 0xFB;      //PCA模块1的捕捉/比较寄存器高8位。
sfr CCAP2H = 0xFC;      //PCA模块2的捕捉/比较寄存器高8位。

sbit CCF0= CCON^0;    //PCA 模块0中断标志,由硬件置位,必须由软件清0。
sbit CCF1= CCON^1;    //PCA 模块1中断标志,由硬件置位,必须由软件清0。
sbit CCF2= CCON^2;    //PCA 模块2中断标志,由硬件置位,必须由软件清0。
sbit CR    = CCON^6;    //1: 允许PCA计数器计数,0: 禁止计数。
sbit CF    = CCON^7;    //PCA计数器溢出(CH,CL由FFFFH变为0000H)标志。
                        //PCA计数器溢出后由硬件置位,必须由软件清0。
sbit PPCA= IP^7;      //PCA 中断 优先级设定位

sfr P2M1 = 0x95;    //P2M1.n,P2M0.n   =00--->Standard,    01--->push-pull
sfr P2M0 = 0x96;    //                  =10--->pure input,11--->open drain

//================================================================

sbit    P25 = P2^5;
sbit    P26 = P2^6;
sbit    P27 = P2^7;

u16   CCAP0_tmp,PWM0_high,PWM0_low;
u16   CCAP1_tmp,PWM1_high,PWM1_low;
u16   CCAP2_tmp,PWM2_high,PWM2_low;

u16 pwm0,pwm1,pwm2;

void    PWMn_Update(u8 PCA_id, u16 pwm);
void    PCA_Init(void);
void    delay_ms(u8 ms);

/******************** 主函数 **************************/
void main(void)
{
   
    PCA_Init(); //PCA初始化
    EA = 1;
    P2M1 &= ~(0xe0);    //P2.7 P2.6 P2.5 设置为推挽输出
    P2M0 |=(0xe0);
   
    while (1)
    {
      delay_ms(2);

      if(++pwm0 >= PWM_HIGH_MAX)pwm0 = PWM_HIGH_MIN;
      PWMn_Update(PCA0,pwm0);

      if(++pwm1 >= PWM_HIGH_MAX)pwm1 = PWM_HIGH_MIN;
      PWMn_Update(PCA1,pwm1);

      if(++pwm2 >= PWM_HIGH_MAX)pwm2 = PWM_HIGH_MIN;
      PWMn_Update(PCA2,pwm2);
    }
}


//========================================================================
// 函数: voiddelay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2013-4-1
// 备注:
//========================================================================
voiddelay_ms(u8 ms)
{
   unsigned int i;
   do{
          i = MAIN_Fosc / 13000;
          while(--i)    ;
   }while(--ms);
}


//========================================================================
// 函数: void PWMn_SetHighReg(unsigned int high)
// 描述: 更新占空比数据。
// 参数: high:占空比数据,即PWM输出高电平的PCA时钟脉冲个数。
// 返回: 无
// 版本: VER1.0
// 日期: 2013-5-15
// 备注:
//========================================================================
void PWMn_Update(u8 PCA_id, u16 pwm)
{
    if(pwm > PWM_HIGH_MAX)pwm = PWM_HIGH_MAX; //如果写入大于最大占空比数据,强制为最大占空比。
    if(pwm < PWM_HIGH_MIN)pwm = PWM_HIGH_MIN; //如果写入小于最小占空比数据,强制为最小占空比。

    if(PCA_id == PCA0)
    {
      CR = 0;                     //停止PCA一会, 一般不会影响PWM。
      PWM0_high = pwm;            //数据在正确范围,则装入占空比寄存器。
      PWM0_low = PWM_DUTY - pwm;//计算并保存PWM输出低电平的PCA时钟脉冲个数。
      CR = 1;                     //启动PCA。
    }
    else if(PCA_id == PCA1)
    {
      CR = 0;                     //停止PCA。
      PWM1_high = pwm;            //数据在正确范围,则装入占空比寄存器。
      PWM1_low = PWM_DUTY - pwm;//计算并保存PWM输出低电平的PCA时钟脉冲个数。
      CR = 1;                     //启动PCA。
    }
    else if(PCA_id == PCA2)
    {
      CR = 0;                     //停止PCA。
      PWM2_high = pwm;            //数据在正确范围,则装入占空比寄存器。
      PWM2_low = PWM_DUTY - pwm;//计算并保存PWM输出低电平的PCA时钟脉冲个数。
      CR = 1;                     //启动PCA。
    }
}

//========================================================================
// 函数: void   PCA_Init(void)
// 描述: PCA初始化程序.
// 参数: none
// 返回: none.
// 版本: V1.0, 2013-11-22
//========================================================================
void    PCA_Init(void)
{
    CR = 0;
    AUXR1 = (AUXR1 & ~(3<<4)) | PCA_P24_P25_P26_P27;    //切换IO口
    CCAPM0 = (PCA_Mode_HighPulseOutput | ENABLE);   //16位软件定时、高速脉冲输出、中断模式
    CCAPM1 = (PCA_Mode_HighPulseOutput | ENABLE);
    CCAPM2 = (PCA_Mode_HighPulseOutput | ENABLE);

    CH = 0;
    CL = 0;
    CMOD= (CMOD& ~(7<<1)) | PCA_Clock_1T;         //选择时钟源
    PPCA= 1;// 高优先级中断

    pwm0 = (PWM_DUTY / 4 * 1);//给PWM一个初值
    pwm1 = (PWM_DUTY / 4 * 2);
    pwm2 = (PWM_DUTY / 4 * 3);

    PWMn_Update(PCA0,pwm0);
    PWMn_Update(PCA1,pwm1);
    PWMn_Update(PCA2,pwm2);

    CR    = 1;// 运行PCA定时器
}
//======================================================================

//========================================================================
// 函数: void   PCA_Handler (void) interrupt 7
// 描述: PCA中断处理程序.
// 参数: None
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void    PCA_Handler (void) interrupt 7
{
    if(CCF0)      //PCA模块0中断
    {
      CCF0 = 0;       //清PCA模块0中断标志
      if(P25) CCAP0_tmp += PWM0_high; //输出为高电平,则给影射寄存器装载高电平时间长度
      else    CCAP0_tmp += PWM0_low;//输出为低电平,则给影射寄存器装载低电平时间长度
      CCAP0L = (u8)CCAP0_tmp;         //将影射寄存器写入捕获寄存器,先写CCAP0L
      CCAP0H = (u8)(CCAP0_tmp >> 8);//后写CCAP0H
    }

    if(CCF1)    //PCA模块1中断
    {
      CCF1 = 0;       //清PCA模块1中断标志
      if(P26) CCAP1_tmp += PWM1_high; //输出为高电平,则给影射寄存器装载高电平时间长度
      else    CCAP1_tmp += PWM1_low;//输出为低电平,则给影射寄存器装载低电平时间长度
      CCAP1L = (u8)CCAP1_tmp;         //将影射寄存器写入捕获寄存器,先写CCAP0L
      CCAP1H = (u8)(CCAP1_tmp >> 8);//后写CCAP0H
    }

    if(CCF2)    //PCA模块2中断
    {
      CCF2 = 0;       //清PCA模块1中断标志
      if(P27) CCAP2_tmp += PWM2_high; //输出为高电平,则给影射寄存器装载高电平时间长度
      else    CCAP2_tmp += PWM2_low;//输出为低电平,则给影射寄存器装载低电平时间长度
      CCAP2L = (u8)CCAP2_tmp;         //将影射寄存器写入捕获寄存器,先写CCAP0L
      CCAP2H = (u8)(CCAP2_tmp >> 8);//后写CCAP0H
    }
}

zcllom 发表于 2020-9-27 15:23:32

古调独弹 发表于 2020-9-26 00:49
STC-ISP中带的例程改改应该就能用了

谢谢回复。
打赏50汤圆。

小李非刀 发表于 2020-9-27 16:17:04

这么低的频率,只能用定时器0的溢出做时钟了,你没说主频多少。
比如,使用22.1184MHZ的晶振,则Timer0重装值= 256 - 22118400/12/256/100 = 256-72=184.

古调独弹 发表于 2020-9-28 15:49:56

zcllom 发表于 2020-9-27 15:23
谢谢回复。
打赏50汤圆。

感谢打赏的汤圆!
如果要用定时器0实现软件PWM可以用下面的代码
/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC15F4K60S4 系列 定时器软件模拟PWM举例-------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966-------------------------*/
/* --- Web: www.STCMCU.com --------------------------------------------*/
/* --- Web: www.GXWMCU.com --------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序      */
/* 如果要在文章中应用此代码,请在文章中注明使用了STC的资料及程序      */
/*---------------------------------------------------------------------*/

//本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
//若无特别说明,工作频率一般为11.0592MHz


#include "reg51.h"

//#define PWM6BIT   64            //6-bit PWM 周期数
#define PWM8BIT   256             //8-bit PWM 周期数
//#define PWM10BIT1024            //10-bit PWM 周期数
//#define PWM16BIT65536         //16-bit PWM 周期数

#define HIGHDUTY 128               //高电平周期数(占空比128/256=50%)占空比50%的方波
#define LOWDUTY(PWM8BIT-HIGHDUTY) //低电平周期数

sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P1M1 = 0x91;
sfr P1M0 = 0x92;
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;

sfr AUXR      = 0x8e;               //辅助寄存器
sfr INT_CLKO= 0x8f;               //时钟输出控制寄存器
sbit T0CLKO   = P3^5;               //定时器0的时钟输出口

bit flag;

//定时器0中断服务程序
void tm0() interrupt 1
{
    flag = !flag;                   //反转PWM的输出标志
    if (flag)
    {
      TL0 = (65536-HIGHDUTY);   //准备高电平的重载值
      TH0 = (65536-HIGHDUTY) >> 8;
    }
    else
    {
      TL0 = (65536-LOWDUTY);      //准备低电平的重载值
      TH0 = (65536-LOWDUTY) >> 8;
    }
}

void main()
{
    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    P3M0 = 0x00;
    P3M1 = 0x00;
    P4M0 = 0x00;
    P4M1 = 0x00;
    P5M0 = 0x00;
    P5M1 = 0x00;
    P6M0 = 0x00;
    P6M1 = 0x00;
    P7M0 = 0x00;
    P7M1 = 0x00;

    AUXR = 0x80;                  //定时器0为1T模式
    INT_CLKO = 0x01;                //使能定时器0的时钟输出功能
    TMOD &= 0xf0;                   //设置定时器0为模式0(16位自动重装载)
    TL0 = (65536-LOWDUTY);          //初始化定时器初值和重装值
    TH0 = (65536-LOWDUTY) >> 8;
    T0CLKO = 1;                     //初始化时钟输出脚(软PWM口)
    flag = 0;                     //初始化标志位
    TR0 = 1;                        //定时器0开始计时
    ET0 = 1;                        //使能定时器0中断
    EA = 1;
    while (1);
}

古调独弹 发表于 2020-9-28 16:24:51

本帖最后由 古调独弹 于 2020-9-28 16:32 编辑

如果确实定时器资源紧张,也可以设置定时器为39微秒一个周期,39*256(8位PWM)约等于10MS,也就是100HZ,然后在定时器中断中再通过判断计数来改变高低电平的占空比,同时定时器中断也还能通过其他计数完成本来需要用的功能,伪代码见下

unsigned char pwm计数=0;
unsigned char 高电平占空比=128; //占空比50%的方波
unsigned int 其他计数=0;

void Timer0Init(void)                //39微秒@22.1184MHz
{
        AUXR |= 0x80;                //定时器时钟1T模式
        TMOD &= 0xF0;                //设置定时器模式 16位自动重载
        TL0 = 0xA1;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
}

//定时器0中断服务程序
void tm0() interrupt 1
{
    pwm计数++;
    if (pwm计数<高电平占空比)
    {
      pwm脚输出高电平;
    }
    else
    {
      pwm脚输出低电平;
    }
    其他计数++;
    if (其他计数>25600)
    {
      其他计数=0;
      1秒旗标=1;
    }
}

chengtina 发表于 2020-10-13 16:26:54

古调独弹 发表于 2020-9-26 00:49
STC-ISP中带的例程改改应该就能用了


这是哪个版本的STC-ISP附带的?我用6.87N找不到这个范例程序。或者您这个是选的哪个单片机什么功能的?在考虑用一个小项目,是否用这个功能 能实现{:handshake:}

古调独弹 发表于 2020-10-14 00:24:25

chengtina 发表于 2020-10-13 16:26
这是哪个版本的STC-ISP附带的?我用6.87N找不到这个范例程序。或者您这个是选的哪个单片机什么功能的? ...

是v6.87O版本附带的,范例程序中【STC15Fxx/STC15Lxx/STC15Wxx 系列 - 使用3路PCA实现3路9-16位PWM - C】

chengtina 发表于 2020-10-14 10:19:55

古调独弹 发表于 2020-10-14 00:24
是v6.87O版本附带的,范例程序中【STC15Fxx/STC15Lxx/STC15Wxx 系列 - 使用3路PCA实现3路9-16位PWM - C】 ...

谢谢。我没下载对版本
页: [1]
查看完整版本: 求助STC15W401AS的PWM功能,帮忙设置频率为100Hz。