我这里有个实例,用的STC8H1K16,两路pwm驱动两个NMOS管控制双色灯带- /************* 功能说明 **************
- 按键短按开灯,开灯后3秒内,短按开关切换色温(白光,黄光,自然光;切换色温功率不变);
- 长按调亮度;超过3秒短按关灯。
- 设置的参数掉电保存;
- 高级PWM定时器 PWM1P/PWM1N,PWM2P/PWM2N,PWM3P/PWM3N,PWM4P/PWM4N 每个通道都可独立实现PWM输出,
- 或者两两互补对称输出.
- MCU: STC8H1K16 LQFP-32封装
- P1.1 P1.2驱动两路LED
- 2023/4/14 调试成功
- ******************************************/
- //#include "reg51.h" //包含此头文件后,里面声明的寄存器不需要再手动输入,避免重复定义
- #include <stc8h.h>
- #include "intrins.h"
- #define MAIN_Fosc 11059200L //定义主时钟
- #define PWM_PSCR 3 //设置分频 频率计算公式MAIN_Fosc/(PWM_PSCR+1)*(PWM_ARR+1)边沿对齐
- #define PWM_ARR 1023 //设置周期值 频率2.7KHz
- //#define PWM_PSCR 15 //设置分频 频率计算公式MAIN_Fosc/(PWM_PSCR+1)*(PWM_ARR+1)
- //#define PWM_ARR 254 //设置周期值 频率2.7KHz
- #define uchar unsigned char
- #define uint unsigned int
- typedef unsigned char u8;
- typedef unsigned int u16;
- typedef unsigned long u32;
- /****************************** 用户定义宏 ***********************************/
- #define Timer0_Reload (65536UL -(MAIN_Fosc / 1000)) //Timer 0 中断频率, 1000次/秒
- /*****************************************************************************/
- #define PWM1_1 0x00 //P:P1.0 N:P1.1
- #define PWM1_2 0x01 //P:P2.0 N:P2.1
- #define PWM1_3 0x02 //P:P6.0 N:P6.1
- #define PWM2_1 0x00 //P:P1.2/P5.4 N:P1.3
- #define PWM2_2 0x04 //P:P2.2 N:P2.3
- #define PWM2_3 0x08 //P:P6.2 N:P6.3
- #define PWM3_1 0x00 //P:P1.4 N:P1.5
- #define PWM3_2 0x10 //P:P2.4 N:P2.5
- #define PWM3_3 0x20 //P:P6.4 N:P6.5
- #define PWM4_1 0x00 //P:P1.6 N:P1.7
- #define PWM4_2 0x40 //P:P2.6 N:P2.7
- #define PWM4_3 0x80 //P:P6.6 N:P6.7
- #define PWM4_4 0xC0 //P:P3.4 N: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
- /************* 本地变量声明 **************/
- bit B_1ms; //1ms标志
- u16 PWM1_Duty;
- u16 PWM2_Duty;
- u16 PWM2_Dutyv;
- u16 PWM1_Dutyv;
- //u16 PWM3_Duty;
- //u16 PWM4_Duty;
- uchar PWM2_Dutyv_L;
- uchar PWM2_Dutyv_M;
- uchar temp_PWM2_Dutyv_L;
- uchar temp_PWM2_Dutyv_M;
- bit PWM1_Flag;
- bit PWM2_Flag;
- bit PWM3_Flag;
- bit PWM4_Flag;
- void UpdatePwm(void);
- sbit k2=P3^2;//灯开关
- sbit led=P1^2;//灯
- uint k1_cont,k2_cont;//按键消抖计时
- uchar k1_lock,k2_lock;//按键锁
- uchar count1=10,count2=10;
- uint Long_cont=400;
- uint Long_cont1=200;
- uint Long_cont2=200;
- uchar PWM_Value; //占空比 变量
- bit PWM_UP_Down_Flag; //调光方向标志
- bit flag_short_k1,flag_short_k2;//按键短按标志
- bit flag_led_onoff;//led灯开关标志
- bit SaveFlag; //数据存储标志
- bit flag_qh,flag_qh1;//色温切换计时标志
- uint hcount,hcount1,hcount2;
- uchar led_qhcount;
- /////////////////////////
- void delay(uint ms)
- {
- uint i,j;
- for(i=0; i<ms; i++)
- for(j=0; j<930; j++);
- }
- //////////////////////
- /////////////////
- void IapIdle() //禁用IAP功能
- {
- IAP_CONTR = 0; //关闭IAP功能
- IAP_CMD = 0; //清除命令寄存器
- IAP_TRIG = 0; //清除触发寄存器
- IAP_ADDRH = 0x80; //将地址设置到非IAP区域
- IAP_ADDRL = 0;
- }
- char IapRead(int addr) //读EEPROM
- {
- char dat;
- IAP_CONTR = 0x80; //使能IAP
- IAP_TPS = 12; //设置等待参数12MHz
- IAP_CMD = 1; //设置IAP读命令
- IAP_ADDRL = addr; //设置IAP低地址
- IAP_ADDRH = addr >> 8; //设置IAP高地址
- IAP_TRIG = 0x5a; //写触发命令(0x5a)
- IAP_TRIG = 0xa5; //写触发命令(0xa5)
- _nop_();
- dat = IAP_DATA; //读IAP数据
- IapIdle(); //关闭IAP功能
- return dat;
- }
- void IapProgram(int addr, char dat) //写EEPROM
- {
- IAP_CONTR = 0x80; //使能IAP
- IAP_TPS = 12; //设置等待参数12MHz
- IAP_CMD = 2; //设置IAP写命令
- IAP_ADDRL = addr; //设置IAP低地址
- IAP_ADDRH = addr >> 8; //设置IAP高地址
- IAP_DATA = dat; //写IAP数据
- IAP_TRIG = 0x5a; //写触发命令(0x5a)
- IAP_TRIG = 0xa5; //写触发命令(0xa5)
- _nop_();
- IapIdle(); //关闭IAP功能
- }
- void IapErase(int addr) //擦除EEPROM
- {
- IAP_CONTR = 0x80; //使能IAP
- IAP_TPS = 12; //设置等待参数12MHz
- IAP_CMD = 3; //设置IAP擦除命令
- IAP_ADDRL = addr; //设置IAP低地址
- IAP_ADDRH = addr >> 8; //设置IAP高地址
- IAP_TRIG = 0x5a; //写触发命令(0x5a)
- IAP_TRIG = 0xa5; //写触发命令(0xa5)
- _nop_(); //
- IapIdle(); //关闭IAP功能
- }
- ///////////////////
- void keyscan()
- {
- // if(tes_flag==0) //非时钟设置状态
- {
-
- if(k2)
- {
- k2_lock=0;
- k2_cont=0;
- if(flag_short_k2)
- {
- // cmled2=!cmled2;//按键指示灯取反
- flag_short_k2=0;//清零k2短按标志
- // flag_led_onoff=!flag_led_onoff;//开关灯标志取反
- flag_qh1=1;
- if(flag_qh)
- {
- flag_led_onoff=1;
- led_qhcount++;
- if(led_qhcount==3)
- {
- led_qhcount=0;
- }
- // if(led_qhcount==0) //两路亮
- // {
- // // PWM2_Dutyv=PWM2_Dutyv>>1; //两路都亮的亮度是一路一半的亮度
- // PWM2_Dutyv=PWM2_Dutyv/2;
- // }
- // if(led_qhcount==1)
- // {
- // // PWM2_Dutyv=PWM2_Dutyv<<1; //单路亮度是双路亮度的2倍
- // PWM2_Dutyv=PWM2_Dutyv*2;
- // }
- // if(led_qhcount==2)
- // {
- // PWM2_Dutyv=PWM2_Dutyv;
- // }
-
- IapErase(0x1000); //擦除EEPROM
- IapProgram(0x1000, led_qhcount);//保存亮灯路数值
- hcount=0;
- hcount1=0;
- }
- else
- {
- flag_led_onoff=!flag_led_onoff;//开关灯标志取反
- }
- }
- if(SaveFlag) //长按松手后,保存pwm数据
- {
- PWM_UP_Down_Flag=!PWM_UP_Down_Flag;//调光方向标志取反
- PWM2_Dutyv_L=PWM2_Dutyv%256;//将大于255的占空比值拆分成两个uchar的数值再保存
- PWM2_Dutyv_M=PWM2_Dutyv/256;
- IapErase(0x0001); //擦除EEPROM
- IapProgram(0x0001, PWM2_Dutyv_L);//将PWM_Value写入EEPROM
- IapProgram(0x0002, PWM2_Dutyv_M);//将PWM_Value写入EEPROM
- SaveFlag = 0;//保存数据后,保存标志清零
- }
-
- }
- else if(k2_lock==0) //短按
- {
- k2_cont++;
- // flag_k2=1;
- if(k2_cont>count2)
- {
- k2_cont=0;
- k2_lock=1;
- flag_short_k2=1;
- // if(flag_qh)
- // {
- // led_qhcount++;
- // if(led_qhcount==3)
- // {
- // led_qhcount=0;
- // }
- // hcount=0;
- // hcount1=0;
- // }
- }
- }
- else if(k2_cont<Long_cont2) //长按
- {
- k2_cont++;
- if(k2_cont==Long_cont2)
- {
- k2_cont=196;
- flag_short_k2=0;//进入长按必须清短按标志,否则会触发一次短按
- SaveFlag = 1;//进入长按,保存标志置1
- if(flag_led_onoff)//开灯状态
- {
- if(PWM_UP_Down_Flag == 1) //亮度++
- {
-
-
- // if(PWM2_Dutyv < 1023)
- // {
- // // PWM2_Dutyv +=5;
- // if(led_qhcount==0)
- // { PWM2_Dutyv +=3;
- // if(PWM2_Dutyv > 511)
- // {
- // PWM2_Dutyv = 511;
- // }
- // }
- // else
- // {
- // PWM2_Dutyv +=6;
- // if(PWM2_Dutyv > 1022)
- // {
- // PWM2_Dutyv = 1022;
- // }
- // }
- // }
- //
- // }
- //
- // if(PWM_UP_Down_Flag == 0) //亮度--
- // {
- //
- //
- // if(PWM2_Dutyv > 0)
- // {
- // if(led_qhcount==0)
- // {PWM2_Dutyv -=3;}
- // else
- // {PWM2_Dutyv -=6;}
- // if(PWM2_Dutyv < 100)
- // {
- // PWM2_Dutyv = 100;
- // }
- //
- // }
- // }
- if(PWM2_Dutyv < 1023) //亮度++
- {
- PWM2_Dutyv +=5;
- if(PWM2_Dutyv > 1022)
- {
- PWM2_Dutyv = 1022;
- }
-
- }
-
- }
-
- if(PWM_UP_Down_Flag == 0) //亮度--
- {
-
-
- if(PWM2_Dutyv > 0)
- {
- PWM2_Dutyv -=5;
- if(PWM2_Dutyv < 100)
- {
- PWM2_Dutyv = 100;
- }
-
- }
- }
-
- }
- }
-
- }
- }
- }
- //////////////////////////
- void Timer0Init(void) //2毫秒@11.0592MHz
- {
- AUXR |= 0x80; //定时器时钟1T模式
- TMOD &= 0xF0; //设置定时器模式 16位自动重载
- TL0 = 0x9A; //设置定时初始值
- TH0 = 0xA9; //设置定时初始值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- ET0=1;
- EA=1;
- }
- //////////////////////////
- /******************** 主函数 **************************/
- void main(void)
- {
- Timer0Init();
- P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
- P1M1 = 0x00; P1M0 = 0x07; //设置为准双向口 p12设为推挽输出
- 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 = 512;
- // PWM2_Duty = 256;
- // PWM2_Dutyv=2046;
- // PWM3_Duty = 512;
- // PWM4_Duty = 1024;
-
- PWM2_Dutyv_L=IapRead(0x0001); //读出占空比值
- PWM2_Dutyv_M=IapRead(0x0002);
- if(PWM2_Dutyv_M==255)PWM2_Dutyv_M=3; //第一次上电读出的值是255,就给占空比赋初值
- if(PWM2_Dutyv_L==255)PWM2_Dutyv_L=255;
- //占空比最大为PWM_ARR的值
- PWM2_Dutyv=PWM2_Dutyv_M*256+PWM2_Dutyv_L;//两个uchar合并成uint,初始最大亮度值1024
- led_qhcount=IapRead(0x1000);
- if(led_qhcount==255)led_qhcount=2;
- P_SW2 |= 0x80;
-
- PWMA_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
- PWMA_CCER2 = 0x00;
- PWMA_CCMR1 = 0x60; //通道模式配置 设置CC1为PWMA输出模式,方向为输出
- PWMA_CCMR2 = 0x60;
- PWMA_CCMR3 = 0x60;
- PWMA_CCMR4 = 0x60;
- PWMA_CCER1 = 0x55; //配置通道输出使能和极性
- PWMA_CCER2 = 0x55;
- PWMA_PSCRH = (u8)(PWM_PSCR>>8); //设置分频
- PWMA_PSCRL = (u8)(PWM_PSCR);
- // PWMA_ARRH = (u8)(PWM_ARR >> 8); //设置周期时间
- // PWMA_ARRL = (u8)PWM_ARR;
- PWMA_ARRH = PWM_ARR/256; //设置周期时间 右移8位等同256求模
- PWMA_ARRL = PWM_ARR%256;
- /*
- 通俗的说,位移的实现是将数据转换成二进制后,进行左右移动的。如果左移,则右边补零,如果是右移,
- 则是左边补零,后边溢出的则去掉。因此,左移可以理解为整数的乘法,
- 而右移则是理解为整数的取整除法。
- 左移 (<<)
- 将第一个操作数向左移动第二个操作数指定的位数,空出的位置补0。
- 左移相当于乘. 左移一位相当于乘2;左移两位相当于乘4;左移三位相当于乘8。
- x<<1= x*2
- x<<2= x*4
- x<<3= x*8
- x<<4= x*16
- 同理, 右移即相反:
- 右移 (>>)
- 将第一个操作数向右移动第二个操作数所指定的位数,空出的位置补0。
- 右移相当于整除. 右移一位相当于除以2;右移两位相当于除以4;右移三位相当于除以8。
- 右移四位相当于除以16;右移8位相当于除以256.
- x>>1= x/2
- x>>2= x/4
- x>>3= x/8
- x>>4= x/16
- */
- // PWMA_ARRH = 0x03; //设置周期时间 自动重装载高8位
- // PWMA_ARRL = 0xff;//自动重装载低8位
- 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_1; //选择 PWM1_2 通道
- PWMA_PS |= PWM2_1; //选择 PWM2_1 通道 p12口
- // PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
- // PWMA_PS |= PWM4_3; //选择 PWM4_3 通道
- PWMA_BKR = 0x80; //使能主输出
- PWMA_CR1 |= 0x01; //开始计时
- P_SW2 &= 0x7f;
- while (1)
- {
- keyscan();
- delay(4);
-
- if(flag_led_onoff==1) //开灯
- {
- switch(led_qhcount)
- {
- // case 0: PWM2_Duty = PWM2_Dutyv; PWM1_Duty = PWM2_Dutyv; break;
- //
- // case 1: PWM2_Duty = 0; PWM1_Duty = PWM2_Dutyv; break;
- //
- // case 2: PWM2_Duty = PWM2_Dutyv; PWM1_Duty = 0; break;
- case 0: PWM2_Duty = PWM2_Dutyv/2; PWM1_Duty = PWM2_Dutyv/2; break;
- case 1: PWM2_Duty = 0; PWM1_Duty = PWM2_Dutyv; break;
- case 2: PWM2_Duty = PWM2_Dutyv; PWM1_Duty = 0; break;
- default: break;
- }
- }
- else //关灯
- {
- PWM2_Duty=0;
- PWM1_Duty = 0;
- flag_qh1=0; //关灯将flag_qh1,hcount2这两个变量清零
- hcount2=0; //否则关灯后再快速开灯会误切换色温
- }
-
- UpdatePwm();
-
- }
- }
- /********************** Timer0 1ms中断函数 ************************/
- void timer0(void) interrupt 1
- {
- if(flag_qh1)
- {
- hcount2++;
- if(hcount2==100)//0.2秒
- {
- hcount2=0;
- flag_qh1=0;
- flag_qh=1;
- }
- }
- if(flag_qh)
- {
- hcount++;
- if(hcount==500) //1秒
- {
- hcount=0;
- hcount1++;
- if(hcount1==3)
- {
- hcount1=0;
- flag_qh=0;
- }
- }
- }
- }
- //========================================================================
- // 函数: UpdatePwm(void)
- // 描述: 更新PWM占空比.
- // 参数: none.
- // 返回: none.
- // 版本: V1.0, 2012-11-22
- //========================================================================
- void UpdatePwm(void)
- {
- P_SW2 |= 0x80;
- 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);
- P_SW2 &= 0x7f;
- }
复制代码 |