看了大概一个月的程序,基本理解了,根据自己的理解对程序做了详尽注释,希望能够帮助后来者。接下来要学习ppm信号,希望能够很快对接起来。
电路原理图如下:
单片机源程序如下:
- /***************************功能说明***************************
- 本程序试验使用STC15W408AS来驱动航模用的无感无刷三相直流马达.
- 本程序参考自网上的代码(作者: 瑞生), 改良而来.
- 电路图见文件 "BLDC-V10-实验电路.pdf".
- 控制信号由P3.2输入正脉冲信号, 间隔5~20ms, 脉冲宽度1.000~1.610ms.
- 1.160ms开始启动, 1.610ms为最高速度, 分辨率为2us.
- 本程序仅仅是简单控制, 软件没有处理过0延时30度切换和过流检测.
- 由于过0检测部分有RC滤波, 所以改变电容值可以大约的对应在最高速时延时30度的时间.
- 有意者可自行完善电路和程序.
- *********************************************************************************/
- #define MAIN_Fosc 24000000L //定义主时钟
- #include "STC15Fxxxx.H"
- //CMPCR1(比较器控制寄存器 1)
- #define CMPEN 0x80 //1: 允许比较器, 0: 禁止,关闭比较器电源
- #define CMPIF 0x40 //比较器中断标志, 包括上升沿或下降沿中断, 软件清0
- #define PIE 0x20 //1: 比较结果由0变1, 产生上升沿中断
- #define NIE 0x10 //1: 比较结果由1变0, 产生下降沿中断
- #define PIS 0x08 //输入正极性选择, 0: 选择外部P5.5做正输入, 1: 由ADCIS[2:0]所选择的ADC输入端做正输入.
- #define NIS 0x04 //输入负极性选择, 0: 选择内部BandGap电压BGv做负输入, 1: 选择外部P5.4做输入.
- #define CMPOE 0x02 //1: 允许比较结果输出到P1.2, 0: 禁止.
- #define CMPRES 0x01 //比较结果, 1: CMP+电平高于CMP-, 0: CMP+电平低于CMP-, 只读
- //CMPCR2(比较器控制寄存器 2)
- #define INVCMPO 0x80 //1: 比较器输出取反, 0: 不取反
- #define DISFLT 0x40 //1: 关闭0.1us滤波, 0: 允许
- #define LCDTY 0x00 //关闭数字滤波功能. 数字滤波功能即为数字信号去抖动功能.
- sbit PWM0_L = P3^4;
- sbit PWM1_L = P3^5;
- sbit PWM2_L = P3^6;
- u8 Step;
- u8 PWM_Value; // 决定PWM占空比的值
- u16 RxPulseWide;
- bit B_RxOk; //定义一个bool量.
- bit B_RUN;
- u8 PWW_Set;
- u8 cnt10ms;
- u8 Rx_cnt;
- u8 TimeOut; //堵转超时
- #define DISABLE_CMP_INT CMPCR1 &= ~0X40 // 比较器中断标志位清0.
- #define ENABLE_CMP_INT CMPCR1 |= 0X40 // 比较器中断标志位置1.
- /*********************************************************************/
- void Delay_n_ms(u8 dly)
- {
- u16 j;
- do
- {
- j = MAIN_Fosc / 13000; //延时1ms, 主程序在此节拍下运行
- while(--j) ;
- }
- while(--dly);
- }
- void delay_us(u8 us)
- {
- do
- {
- NOP(20); //@24MHz
- }
- while(--us);
- }
- void StepXL(void) // 换相序列函数
- {
- switch(Step)
- {
- case 0: // AB
- PWM0_L=0; PWM1_L = 1; PWM2_L=0; // Q4常开.
- CCAP0H = PWM_Value; CCAP1H=0; CCAP2H=0; // A相由占空比控制.
- ADC_CONTR = 0XCD; // ADC电源开,ADC转换开,选择P1.5作为ADC输入口
- CMPCR1 = 0x9C; // 比较器使能, 下降沿中断允许,通过ADC的P1.5口作为比较器的正极输入端,
- //选择外部端口P5.4作为比较器负极输入端.
- break;
- case 1: // AC
- PWM0_L=0; PWM1_L=0; PWM2_L = 1; // Q4常开
- CCAP0H = PWM_Value; CCAP1H=0; CCAP2H=0; // A相由占空比控制.
- ADC_CONTR = 0XCC; // ADC电源开,ADC转换开,选择P1.4作为ADC输入端
- CMPCR1 = 0xAC; // 比较器使能, 上升沿中断允许,通过ADC的P1.4口作为比较器的正极输入端
- //选择外部端口P5.4作为比较器负极输入端.
- break;
- case 2: // BC
- PWM0_L=0; PWM1_L=0; PWM2_L = 1; // Q2常开
- CCAP0H=0; CCAP2H=0; CCAP1H = PWM_Value; // B相由占空比控制.
- ADC_CONTR = 0XCB; // ADC电源开,ADC转换开,选择P1.3作为ADC输入口测量A点电压
- CMPCR1 = 0x9C; // 比较器使能, 下降沿中断允许,通过ADC的P1.3口作为比较器的正极输入端
- //选择外部端口P5.4作为比较器负极输入端.
- break;
- case 3: // BA
- PWM0_L = 1; PWM1_L=0; PWM2_L=0; // Q6常开
- CCAP0H=0; CCAP2H=0; CCAP1H = PWM_Value; // B相由占空比控制.
- ADC_CONTR = 0XCD; // ADC电源开,ADC转换开,选择P1.5作为ADC输入口测量C点电压
- CMPCR1 = 0xAC; // 比较器使能, 上升沿中断允许,通过ADC的P1.5口作为比较器的正极输入端
- //选择外部端口P5.4作为比较器负极输入端.
- break;
- case 4: // CA
- PWM0_L = 1; PWM1_L=0; PWM2_L=0; // Q6常开
- CCAP0H=0; CCAP1H=0; CCAP2H = PWM_Value; // C相由占空比控制.
- ADC_CONTR = 0XCC; // ADC电源开,ADC转换开,选择P1.4作为ADC输入口测量B点电压
- CMPCR1 = 0x9C; // 比较器使能, 下降沿中断允许,通过ADC的P1.4口作为比较器的正极输入端
- //选择外部端口P5.4作为比较器负极输入端.
- break;
- case 5: // CB
- PWM0_L=0; PWM2_L=0; PWM1_L = 1; // Q4常开
- CCAP0H=0; CCAP1H=0; CCAP2H = PWM_Value; // C相由占空比控制.
- ADC_CONTR = 0XCB; // ADC电源开,ADC转换开,选择P1.3作为ADC输入口测量A点电压
- CMPCR1 = 0xAC; // 比较器使能, 上升沿中断允许,通过ADC的P1.3口作为比较器的正极输入端
- //选择外部端口P5.4作为比较器负极输入端.
- break;
- default:
- break;
- }
- }
- /***************************************************************
- * PCA模块PWM输出设置 *
- ***************************************************************/
- void PWM_Init(void)
- {
- PWM0_L = 0; // 初始化P3^4为低电平.sbit PWM0_L = P3^4;
- PWM1_L = 0; // 初始化P3^5为低电平.sbit PWM1_L = P3^5;
- PWM2_L = 0; // 初始化P3^6为低电平.sbit PWM2_L = P3^6;
-
- P3n_push_pull(0x70); // 设置P3.4/3.5、3.6为推挽输出. P3M1 &= ~(bitn), P3M0 |= (bitn)
-
- CMOD = 5 << 1; //5(0101)左移1位,设置PCA的输入时钟源为系统时钟的1/4,即PWM频率=24M/4.
-
- CH=0; // PCA(Programmable Counter Array)清零
- CL=0; //
-
- PCA_PWM0 = 0X00; // 对PCA_PWM0模块0清0,并设置PWM为8位(EBS0[1:0]=00).
- CCAP0H = 0x00; // 重载值为{EPCnH,CCAPnH[7:0]}.
- CCAP0L = 0x00; // 比较值为{EPCnL,CCAPnL[7:0]}.
- CCAPM0 = 0x42; // 设置PCA模块0的控制模式(0100 0010).ECOM0=1,允许PCA模块0的比较功能.
- // PWM0=1,设置PCA模块0为脉宽调制输出功能.
- // 当PCA 模块工作在8位PWM模式时,{0,CL[7:0]}与{EPCnL,CCAPnL[7:0]}
- // 中的值进行比较,小于,输出低电平,大于等于输出高电平.
- PCA_PWM1 = 0X00; // 对PCA_PWM0模块0清0,并设置PWM为8位(EBS0[1:0]=00).
- CCAP1H = 0x00; // 重载值为{EPCnH,CCAPnH[7:0]}.
- CCAP1L = 0x00; // 比较值为{EPCnL,CCAPnL[7:0]}.
- CCAPM1 = 0x42; // 设置PCA模块1的控制模式(0100 0010).ECOM0=1,允许PCA模块1的比较功能.
- // PWM1=1,设置PCA模块1为脉宽调制输出功能.
- // 当PCA 模块工作在8位PWM模式时,{0,CL[7:0]}与{EPCnL,CCAPnL[7:0]}
- // 中的值进行比较,小于,输出低电平,大于等于输出高电平.
- PCA_PWM2 = 0X00; // 对PCA_PWM2模块进行设置.其中,EBS2[1:0]=00,PWM为8位.
- CCAP2H = 0x00; // 重载值为{EPCnH,CCAPnH[7:0]}.
- CCAP2L = 0x00; // 比较值为{EPCnL,CCAPnL[7:0]}.
- CCAPM2 = 0x42; // 设置PCA模块2的控制模式(0100 0010).ECOM0=1,允许PCA模块2的比较功能.
- // PWM2=1,设置PCA模块2为脉宽调制输出功能.
- // 当PCA 模块工作在8位PWM模式时,{0,CL[7:0]}与{EPCnL,CCAPnL[7:0]}
- // 中的值进行比较,小于,输出低电平,大于等于输出高电平.
- CR = 1; // PCA计数允许控制位,1:启动PCA计数.
- }
- void ADC_Init(void)
- {
- P1n_pure_input(0x38); // 设置P1.3/1.4/1.5口为高阻输入(电流既不能流入也不能流出).P1M1 |= (bitn), P1M0 &= ~(bitn)
- P1ASF = 0X38; // 开通P1.3 P1.4 P1.5的AD输入口(0011 1000)
- }
- /***************************************************************
- * 比较器中断服务函数 *
- ***************************************************************/
- void CMP_INT(void) interrupt 21
- {
- CMPCR1 &= ~0X40; // 软件清除中断标志位
- if(Step<5) Step++;
- else Step = 0;
- StepXL(); // 进入换相程序. 在换相程序中,已经对比较器的正负极输入端做了设置.
- // ADC的P1.4/P1.5/P1.6口作为比较器的正极输入端,选择外部端口P5.4作为比较器负极输入端.
- TimeOut = 10; // 在执行完比较器中断函数后,赋值TimeOut=10.
- }
- /***************************************************************
- * 比较器初始化设置 *
- ***************************************************************/
- void CMP_Init(void)
- {
- CMPCR1 = 0X8C; // 1000 1100 比较器使能,比较器正负极输入端设置, 通过ADC_CHS位选择比较器的正极输入端,选择P5.4作为负极.
- CMPCR2 = 60; // 60个时钟滤波
- P5n_pure_input(0x10); // 设置P5.4口为高阻输入(电流既不能流入也不能流出).P5M1 |= (bitn), P5M0 &= ~(bitn)
- }
- /***************************************************************
- * 电机启动函数 *
- ***************************************************************/
- u8 StartMotor(void)
- {
- u16 timer,i;
- DISABLE_CMP_INT; // 比较器中断标志位清0,不产生比较中断.
- PWM_Value = 30; // 设置占空比初值
- Step = 0;
- StepXL(); // ∵step=0, ∴ 进入AB相导通的初始位置
- Delay_n_ms(5); // 延时5ms
- timer = 300;
- while(1)
- {
- for(i=0; i<timer; i++) delay_us(50);
- timer -= timer /15 + 1;
- if(timer < 25) return(1);
- if( Step < 5) Step++;
- else Step = 0;
- StepXL();
- }
- }
- /***************************************************************
- * 定时器T0初始化函数 *
- ***************************************************************/
- void T0_Init(void) // 这里设置TH0/TL0=0,定时器T0为16位不自动重载,
- {
- Timer0_AsTimer(); // 设置定时器T0用做定时器. TMOD &= ~(1<<2)
- Timer0_12T(); // 定时器0 12分频. AUXR &= ~(1<<7)
- Timer0_16bit(); // 定时器0 16位不自动重载模式. TMOD = (TMOD & ~0x03) | 0x01
- Timer0_Gate_INT0_P32(); // T0_GATE置1, 只有在INT0脚为高及TR0=1时才可启动定时器T0.TMOD |= (1<<3).
- TH0 = 0;
- TL0 = 0;
- TR0 = 1; // 定时器 T0 准备工作.
- ET0 = 1; // 定时器 T0 的溢出中断允许位.
- }
- /***************************************************************
- * 定时器T0中断服务函数 *
- ***************************************************************/
- void T0_Interrupt(void) interrupt 1
- {
- Rx_cnt = 0; // 一旦出现溢出, 则开始的n个脉冲无效
- RxPulseWide = 1000; // 停止
- B_RxOk = 1; // 在执行完T0中断服务函数后,给B_RxOk赋值.
- }
- /***************************************************************
- * INT0(外部中断0)中断服务函数 *
- ***************************************************************/
- void INT0_int (void) interrupt 0
- {
- u16 j;
- TR0 = 0;
- TH0 = 0;
- TL0 = 0;
- j = ((u16)TH0 << 8) + TL0;
- TR0 = 1; // 定时器T0的运行控制位,允许T0计数, 这时候TH0、TL0就开始计数了.
- if(++Rx_cnt >= 5) Rx_cnt = 5;
- j >>= 1; //为了好处理, 转成单位为us.
- if((j >= 800) && (j <= 2000) && (Rx_cnt == 5))
- {
- RxPulseWide = j;
- B_RxOk = 1; //标志收到一个脉冲
- }
- }
- /**********************************************/
- void main(void)
- {
- u16 j; // unsigned int.
- PWM_Init(); // 设置P3.4/3.5、3.6为推挽输出. 当PCA 模块工作在8位PWM模式时,{0,CL[7:0]}与{EPCnL,CCAPnL[7:0]}
- // 中的值进行比较并决定输出高电平还是低电平.此电平作为
- ADC_Init(); // 开通P1.3 P1.4 P1.5的AD输入口.√
- CMP_Init(); // 比较器初始化设置. 对比较器正负极输入端进行设置, 通过ADC_CHS位选择比较器的正极输入端,选择P5.4作为负极.
- T0_Init(); // 定时器T0初始化设置, 定时器0 12分频. (AUXR &= ~(1<<7)).
- IE0 = 0; // 外部中断0(INT0/P3.2)中断请求标志位清零.
- EX0 = 1; // 外部中断0(INT0)中断允许.
- IT0 = 1; // 外部中断0(INT0)为下降沿触发.
-
- RxPulseWide = 1000;
- PWW_Set = 0;
- cnt10ms = 0;
- Rx_cnt = 0;
- TimeOut = 0;
- EA = 1; // 打开总中断
-
- while (1)
- {
- Delay_n_ms(1); // 延时1ms, 主程序在此节拍下运行
- if(TimeOut > 0) // 在执行完比较器中断函数后,进入此模块.
- {
- if(--TimeOut == 0)
- {
- CCAP0H=0; CCAP1H=0; CCAP2H=0; // PCA模块复原.
- PWM0_L=0; PWM1_L=0; PWM2_L=0; // P3.4/3.5、3.6脚复原.
- DISABLE_CMP_INT; // 关比较器中断
- Delay_n_ms(250); // 堵转时,延时1秒再启动
- Delay_n_ms(250);
- Delay_n_ms(250);
- Delay_n_ms(250);
- RxPulseWide = 1000;
- PWW_Set = 0;
- PWM_Value = 0;
- B_RxOk = 0;
- B_RUN = 0;
- Rx_cnt = 0;
- TimeOut = 0;
- }
- }
-
- if(B_RxOk) // 在执行完T0中断服务函数后,执行此模块.
- {
- B_RxOk = 0;
- j = RxPulseWide;
- if(j >= 1100) // 1100~1610对应PWM占空比值0~255
- {
- j = (j - 1100) >> 1; //2us对应PWM一个步进
- if(j > 256) j = 255;
- }
- else j = 0;
- PWW_Set = (u8)j;
- }
-
- if(!B_RUN && (PWW_Set >= 30)) // PWM_Set >= 30, 并且马达未运行, 则启动马达
- {
- StartMotor(); // 启动马达
- CMPCR1 &= ~0X40; // 比较器中断请求标志位清零
- ENABLE_CMP_INT; // 打开比较器中断
- B_RUN = 1; // B_RUN只是一个标志位,便于管理
- TimeOut = 0;
- }
-
-
- if(++cnt10ms >= 10) // 10ms时隙
- {
- cnt10ms = 0;
- if(B_RUN)
- {
- if(PWM_Value < PWW_Set) PWM_Value++;
- if(PWM_Value > PWW_Set) PWM_Value--;
- if(PWM_Value < 20)
- {
- PWM_Value = 0;
- B_RUN = 0;
- CCAP0H=0; CCAP1H=0; CCAP2H=0; // 占空比为0
- PWM0_L=0; PWM1_L=0; PWM2_L=0;
- DISABLE_CMP_INT; // 关比较器中断
- }
- }
- }
-
- }
- }
复制代码
上图Keil代码下载:
51-STC15W408AS-电调.rar
(111.84 KB, 下载次数: 434)
|