找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2150|回复: 0
打印 上一主题 下一主题
收起左侧

STC12C5A60S2利用PCA时钟溢出做的PWM信号 0-255 资料共享 不对的地方望大家修改共享

[复制链接]
跳转到指定楼层
楼主
1.  程序里面想详细的算法 思路,
2. 在晶振和CPU满足计算的情况下,理论是你要多少你就通过编码器调节多少
3. 本程序任意频率调试是通过编码器来切换的,
4.关于精度问题 在100hz内非常准,在最大255HZ的时候相差20hz左右,  问题可能出现在 定时器计数这个位置, 我全部取整数了 所以误差比较大
  如果计数的出来本来机是整数的,那频率相对准确的, 在100hz的时候就是标准的100hz


单片机源程序如下:
/*------------------------------------------------------------------*/
/* --- 功能  pwm输出 频率可调 -------------------------- -----------*/
/* --- STC12C5Axx                                            -------*/
/* --- 对于定时器选择的时候,应该选择16位---------------------------*/
/* --- 不应该选择16位自动重装,因为16位自动重的工作原理是当TL0------*/
/* --- 溢出的时候,会直接把TH0的值填充到TL0里面计算这样就会导致-----*/
/* --- PWM计算频率出错     -----------------------------------------*/
/* --- 定时的工作方式为。定时器0的工作模式为16位--------------------*/
/* 计算方法 例如12MHZ需要转化为300HZ,那么根据上图,首先我们需要确定
PCA时钟输入频率,根据公式 300*256=76800HZ,这个值就是我们需要的PCA时
钟输入频率。现在问题就是 ,我们怎么把12MHZ,转化为76.8KHZ,
12000KHZ/76.8KHZ=156.25  ,这个156.25就是分频基数,而这个分频基数由
我们的定时器溢出参数来设定,意思就是当我们定时器如果计数156.25溢出就
可以做到分频基数为156.25, 所以我们在设置定时器0的计数起始值就
是65536-156=65380,对应TH0=0XFF,TL0=0X64。    0XFF=255   0X64=100
初始是如何分离的计算方法 65380/256=255 ps:取的整数,然后用255*256=65280
再用65380-65280=100这个就是 65380对256求于的个数放在TL0里面也就是0X64
那么TH0就应该放255将255转化为16进制数为0xff   */
/*整体计算公式(65536-12000/300*256)/256
/* article, please specify in which data and procedures from STC    */
/*------------------------------------------------------------------*/
/*按键k1  k2  分别接在 P0^6 P^7口  用的是独立键盘*/
/*实现的功能是,控制CR的开通和关断来实现混频效果*/
/*第二次修改时间2019-10-6*/
/*作者 Alan*/


#include <STC12C5A60S2.H>
#include <stdint.h>
//#include <intrins.h>
#define FOSC 18432000

/*分别定义了两个不高低位的变量用于保存 定时器初值,从而初值计算里面带变量计算以后保存到这个变量 这时候就能在中断正常重新装初值了,也不会出错*/
uint8_t  TH,TL;      

/*上一次的状态*/
uint8_t  Last_Amb_Status =  0;        

/*该变量用于保存按键值*/
uint8_t  Sd_Key_Value    =  0;

uint8_t  Sd_Key_Value2    =  0;        


/*编码器三个I/O的定义 分别为,A口 B口 sdKey */
sbit  Pin_Portry_A=P2^4 ;
sbit  Pin_Portry_B=P2^3 ;     
sbit  Pin_Portry_Sd=P2^2 ;

/*testing 测试端口 暂时不用*/
sbit  BUZ = P3^0;

sbit  LED = P3^7;

sbit  k1  = P0^6;

sbit  k2  = P0^7;

sbit  LED_1 = P0^5;


unsigned char flag=0;

uint8_t Data_key2(uint8_t *key_val2);


/*延时1ms级函数2*/

void delay(unsigned int z)
{
        unsigned int  x,y;
        for(x=z;x>0;x--)
                for(y=135;y>0;y--);

}


/*副频自动变化*/          //有缺陷 放弃  重新构思    但是这里取消了  不影响主频哈     

void fuping_bianhua(uint8_t XHZ)
{
       uint8_t i;
         for(i=0;i<XHZ;i++)
          {
            CR = 1;
                delay(25);   
            CR = 0;
                delay(25);
           }         
}

/*旋转编码器调整副频*/

uint8_t Data_key2(uint8_t *key_val2)             //*key_val这个是一个指针吗  
{
    //这个counter是为了防止没有键值动作时,程序一直在这里等待
    //具体的等待时间应该根据编码开关的脉冲周期和程序的执行周期来确定
    //可以根据实际情况进行调整,连续测试的时候,可以先去掉此限定条件
    uint16_t wait_counter = 100000;          //counter等待的时间

        uint8_t temp_key_val2 = *key_val2;  
                                                        //temp_key_val 这个是char型的变量 最大255 所以不需要设置最大值

        Last_Amb_Status = Pin_Portry_A;            //保存采样前的A口状态

        //开始采样端口A的跳变边沿,如果没有产生跳变
        //A口的当前采样值和之前保存的值是一样的,异或后值为0
        //while持续等待两个值不同时,跳过while执行下一步骤,如果等待到wait counter为0 的时候就跳出,然后执行下一步;


        while( !(Pin_Portry_A ^ Last_Amb_Status) && --wait_counter);



        if(!wait_counter)  //在while语句期间  如果A口发生变化 除了跳出 while 同时也跳过这里 这里不能打冒号
         

        
        return  0;           //跳过这里


        //此时采样B口的电平
        //如果B口的值和采样A口跳变沿之前的值相同,判断为顺时针旋转
        //如果B口的值和采样A口跳变沿之前的值不同,判断为逆时针旋转


        
        if(!(Pin_Portry_B ^ Last_Amb_Status))
        {
           //顺时针旋转
            temp_key_val2++;



                  if(temp_key_val2 < 2)
                     
                         temp_key_val2 = 2;

                  if(temp_key_val2 > 40);  //如果大于40就截止  限制最大
                     
                         temp_key_val2 = 2;         //重新设置为2

                        *key_val2 = temp_key_val2;  

               
                  return  1;
           }
            
        else if(Pin_Portry_B ^ Last_Amb_Status)          //和上面正转工作过程一样          zc注释
        {
           
            //逆时针旋转
            temp_key_val2--;

                if(temp_key_val2 > 40)  //如果逆时针大于40就截止
                     
                temp_key_val2 = 2;           //重新设置为2

                if(temp_key_val2 <= 2)          //如果小于2 就设置为最大40

                        temp_key_val2 = 40;

                        *key_val2 = temp_key_val2;

                  return  1;
        }

        return 0;
}



/* 定时器配置与初始化*/
void   Timer0Init(void)
{

    /*定义一个32位整形变量 temp 存放编码器初值*/
        uint32_t temp = 5;

        /*设置定时器0位1T模式*/
        /*先将tmod高4位保存为1 “TMOD=$=0XF0”,这样是防止多个定时器使用的时候,直接赋值导致其它定时器工作异常,或者是各定时器无法突出自己的作用*/
        /*通过上面保存了高4位后,接下来进行或运算"TMOD=|=0X01",从而只是将我们需要的位打开其它全部关闭,这样非常稳妥,互补相干*/
        AUXR |= 0x80;               
        TMOD &= 0xF0;               
        TMOD |= 0x01;
                        
        /*把要记的次数,通过下面的运算,算出来以后,保存到temp里面,供定时器装初值用*/
        temp = 0x10000-(18432000/(temp*256));

        /*temp内部保存的次数,分别装在定时器的高8位和低8位*/
        TH0  = TH = temp / 256;               
        TL0  = TL = temp % 256;                //设置定时器初值

        /*启动定时器相关功能,清楚标志位,启动定时器TR0,开总中断,开定时器中断ET0*/
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
        EA=1;
        ET0=1;



}

        /*对key函数声明*/
    uint8_t Data_key(uint8_t *key_val);

        /*对频率更新函数声明*/
    void pinglvgengxing(uint8_t key_val);


void main(void)

{



        Timer0Init();         //初始化定时器配置函数

    CCON = 0;
    CL = 0;
    CH = 0;

    /*将PCA fosc模式设置为 定时器0溢出率*/
    CMOD = 0x04;

        /*setting CCAP0H 脉宽为50%输出*/
    CCAP0H = CCAP0L = 0x80;   
              
        /*setting PCA模式为8bit自动重装模式*/
    CCAPM0 = 0x42;
                        
           /*setting CCAP1H 脉宽为50%输出*/
    CCAP1H = CCAP1L = 0x80;        

    PCA_PWM1 = 0x00;

        /*setting PCA模式为8bit自动重装模式*/
    CCAPM1 = 0x42;

        /*PCA时钟开始运行,计数, 输出pwm信号*/
    CR = 1;                        
        

  while (1)
        {        

            

          if(0 == flag)
         {
                LED=0;
               if(Data_key(&Sd_Key_Value));

                else if(Pin_Portry_Sd!=1)          // 判断按键是否被按下
               {
                        
                                
                           delay(5);           //消抖动

                       if(Pin_Portry_Sd!=1)  //再次判断

                              while(!Pin_Portry_Sd);

                                 LED=1;
                                    flag=1;
                          
                   }
          }
                        
                 else  if(1 == flag)
                   {
                                   LED_1=0;
                        if(Data_key2(&Sd_Key_Value2));
                                 
                        if(Pin_Portry_Sd!=1)  //再次判断
                                {
                                 delay(5);

                                while(!Pin_Portry_Sd);
                                  LED_1=1;
                                    flag=0;
                                 }
                        
                 }

         
                  
                        fuping_bianhua(Sd_Key_Value2);



                   // 如果要在1602上显示对应的频率,直接将“Sd_Key_Value2”变化写数据到1602
                   //记住要分离式数据哦   这个设计是0-255HZ  误差有点大 开到255的时候频率误差在20-30hz
                   //这个误差主要是来自PCA时钟运算这里,主要是计算的时候出现了小数点就省略了 如果从新
                   //将小数点保留 频率是很准的   在100hz内的频率基本就很准的  自己去调整吧

                 
                        pinglvgengxing(Sd_Key_Value);
               

   }

}


/*频率更新函数*/
void pinglvgengxing(uint8_t key_val)
{
        uint32_t temp;
        //关闭定时器中断
        //关闭全局中断
        //失能定时器
        //清除定时器溢出标志位
        ET0 = 0;
        EA  = 0;
        TR0 = 0;
        TF0 = 0;

        //重新初始化定时初值
        temp = 0x10000-18432000/(key_val*256) ;
        TH0 =TH= temp/256;                //设置定时初值
        TL0 =TL= temp%256;                //设置定时初值

        //开启全局中断
        //开启定时器中断
        //使能定时器
        EA  = 1;
        ET0 = 1;
        TR0 = 1;
}


/*主频按键函数*/
uint8_t Data_key(uint8_t *key_val)             //*key_val这个是一个指针吗  
{
    //这个counter是为了防止没有键值动作时,程序一直在这里等待
    //具体的等待时间应该根据编码开关的脉冲周期和程序的执行周期来确定
    //可以根据实际情况进行调整,连续测试的时候,可以先去掉此限定条件
    uint16_t wait_counter = 100000;          //counter等待的时间

        uint8_t temp_key_val = *key_val;  //这个是一个指针变量吗?  如果是  那它是指向 (Sd_Key_Value) 这里面的吗     //我还没有学过指针哈哈哈

        /*把A口的值保存到当前状态 变量里面*/  
        Last_Amb_Status = Pin_Portry_A;            //保存采样前的A口状态

        //开始采样端口A的跳变边沿,如果没有产生跳变
        //A口的当前采样值和之前保存的值是一样的,异或后值为0
        //while持续等待两个值不同时,跳过while执行下一步骤,如果等待到wait counter为0 的时候就跳出,然后执行下一步;


        while( !(Pin_Portry_A ^ Last_Amb_Status) && --wait_counter);



        if(!wait_counter)  //在while语句期间  如果A口发生变化 除了跳出 while 同时也跳过这里 这里不能打冒号
         


        return  0;           //跳过这里


        //此时采样B口的电平
        //如果B口的值和采样A口跳变沿之前的值相同,判断为顺时针旋转
        //如果B口的值和采样A口跳变沿之前的值不同,判断为逆时针旋转


        
        if(!(Pin_Portry_B ^ Last_Amb_Status))
        {
           //顺时针旋转
            temp_key_val++;


               
                  if(temp_key_val < 5)
                     
                         temp_key_val = 5;

                          //if(temp_key_val > 40)  //如果大于40就截止
                     
                        // temp_key_val = 2;         //重新设置为2

                 //我感觉这个是一个指针变量  将按键值传递给指针 然后指针指向Sd_Key_Value地址 然后就可以将 temp_key_val;结果传递给Sd_Key_Value
                        *key_val = temp_key_val;  

               
                  return  1;
           }
            
        else if(Pin_Portry_B ^ Last_Amb_Status)          //和上面正转工作过程一样          zc注释
        {
            //逆时针旋转

               
                 temp_key_val--;

            //        if(temp_key_val2 > 40)  //如果逆时针大于40就截止
                     
              //        temp_key_val2 = 2;           //重新设置为2

                     if(temp_key_val <= 5)   

                         temp_key_val =255;            //限制最小  

               
                        *key_val = temp_key_val;
               
                  return  1;
                 
        }
        
         
        return 0;
}




/*定时器0中断*/
void tm0_isr(void) interrupt 1 using 1
{
     TH0 =TH;                //设置定时初值
     TL0 =TL;                //设置定时初值
}

以上程序51hei下载地址:
通过编码器开关来控制 混频程序V1.0.rar (37.77 KB, 下载次数: 18)

评分

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

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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