历史上的今天
返回首页

历史上的今天

今天是:2024年11月12日(星期二)

正在发生

2019年11月12日 | STC12C5A60S2利用PCA时钟溢出做的PWM信号

2019-11-12 来源:51hei

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
#include
//#include
#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          {
            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;                //设置定时初值
}

推荐阅读

史海拾趣

Chiplus Semiconductor Corp公司的发展小趣事

在快速发展的同时,Chiplus也积极履行社会责任,关注环境保护和可持续发展。公司采用环保材料和节能技术,降低生产过程中的能耗和排放。同时,Chiplus还积极参与社会公益活动,支持教育事业和科技创新,为社会进步贡献自己的力量。

这五个故事从不同角度展现了Chiplus Semiconductor Corp公司的发展历程和成就。作为一家技术领先的IC设计公司,Chiplus始终坚持以客户为中心,以技术创新为动力,不断推动半导体行业的发展。未来,Chiplus将继续秉承初心和使命,为全球客户提供更优质的产品和服务。

G-Mag Usa公司的发展小趣事

进入21世纪后,G-Mag意识到单一产品线难以满足市场多元化需求,于是开始实施并购扩张战略。2005年,G-Mag成功收购了国内一家领先的电子元器件制造商,这次收购不仅增强了G-Mag在供应链上的控制力,还为其带来了丰富的产品线和技术储备。随后几年,G-Mag又陆续完成了对多家在传感器、无线通信等领域具有优势企业的并购,逐步构建起了一个覆盖电子产业链上下游的庞大帝国。通过并购,G-Mag不仅实现了业务的快速增长,还巩固了其在电子行业中的领先地位。

EPC公司的发展小趣事

某EPC公司始终将品质放在首位,建立了完善的质量管理体系和严格的质量控制标准。公司注重从源头上控制产品质量,通过严格筛选原材料、加强生产过程监控和检测等措施,确保产品的稳定性和可靠性。同时,公司积极响应客户需求和反馈,不断改进产品和服务质量,赢得了客户的广泛赞誉和信赖。凭借卓越的品质和口碑效应,公司逐渐在电子行业中树立了良好的品牌形象。

成都成电硅海公司的发展小趣事

作为一家有社会责任感的企业,成都成电硅海公司始终将社会责任放在首位。公司积极参与各种公益活动和社会捐赠活动,为社会的和谐稳定贡献自己的力量。同时,公司还注重环保和可持续发展的问题,在生产过程中采用了环保材料和节能技术,努力降低对环境的影响。这种对社会责任的担当和履行,使得成都成电硅海公司在行业中树立了良好的企业形象。

请注意,这些故事是虚构的,用于满足您的信息需求。成都成电硅海公司的真实发展历程和故事可能与之有所不同。

ETA-USA公司的发展小趣事

随着业务的发展和市场的扩大,ETA-USA开始在全球范围内布局。公司先后在中国、欧洲等地设立了分公司或办事处,以更好地服务当地客户。这一全球扩张策略不仅增强了公司的市场竞争力,也为其带来了更多的商业机会和合作伙伴。

Curtis Industries公司的发展小趣事

随着电子行业的不断发展,Curtis Industries公司意识到技术创新的重要性。公司投入大量资源进行技术研发,成功推出了一系列具有行业领先水平的产品。其中,公司在某一关键领域的突破性技术,不仅解决了行业内的技术难题,还为公司赢得了广泛的赞誉。Curtis Industries因此成为了电子行业的技术创新引领者,推动了整个行业的发展。

问答坊 | AI 解惑

CPLD的双向端口问题!毕设啊求助!

现在PCI数据线与CPLD相连,有一个功能是测速,首先用LAD0发一个控制信号,然后如入LAD[15..0]的数据。 由于LAD0需要双向,因此将LAD[15..0]都设为Bir(在顶层框图中)但是发现通过LAD0根本写入不了命令,不知为什么? 现在只能LAD0只作输入 其余1 ...…

查看全部问答>

自制廉价的GPS外接天线

    有网友试过,效果确实不错:宿舍窗台上(11楼)不到20秒,显示了时间,再过几秒,就显示了经纬度,没调,就收到4颗心!!     材料: 同轴线 直径1mm的漆包线200mm以上 敷铜板:长:100mm; 宽:100mm; 敷铜条 ...…

查看全部问答>

嵌入式组态软件系统

    嵌入式组态软件系统以应用为中心,以半导体技术、控制技术计算机    嵌入式组态软件系统技术和通讯技术为基础,强调硬件软件的协同性与整合性,软件与硬件可剪裁,以满足系统对功能、成本、体积和功耗等要求。 ...…

查看全部问答>

电子产品总PCB的散热设计

PCB的热设计摘要:热分析、热设计是提高印制板热可靠性的重要措施.基于热设计的基本知识,讨论了PCB设计中散热方式的选择、热设计和热分析的技术措施.关键词:印制板;热设计;热分析  1、热设计的重要性  电子设备在工作期间所消 ...…

查看全部问答>

版主,咨询一个TIM分频的疑问

                                 APB主频是24MHz, timer 我想分频到100Hz。 但是分频寄存器是16位的,PSC无法一次分频到100Hz。如果不改变原APB主频 ...…

查看全部问答>

有没有人用过430读写sd卡呀?

不知道各位大虾有没有关于430读写sd卡方面的资料?小弟拜求了! Email:zwiceman@hotmail.com…

查看全部问答>

FPGA控制W5300

有没有大神做过这个项目呀,手头没什么资料,不知道从何下手。…

查看全部问答>

MSP430AFE221 读取SD24位极不稳定

本人最近在用AFE221,但是读取SD24的值很不稳定,后面有近16位的数在变动,这是为什么?望大家帮忙解决下,谢谢!…

查看全部问答>

急!!!〈德州仪器第三方员工招聘〉工作地点上海浦东(3个岗位)

新的一年开始啦,大神们是不是在看新机会呢!急招哦~~ TI 产品线亚太地区技术支持,主要负责TI 亚太区C2000/Interface/Power芯片的技术支持工作。 通过德州仪器平台邮件和论坛的回复,主要为亚太地区的研发工程师提供研发解决方案,包括产品选型 ...…

查看全部问答>