几句废话交代下这篇博客的用意。
首先,项目做多之后总是会忘记这类初始化代码该哪个项目复制,还有一些寄存器的定义和用法前面看明白了,下次用的时候又忘记了。于是就有了这篇博客。
E62D是有5路PWM的,并且功能说明里面有说PWW5是可以驱动半桥和全桥,有P1A,P1B,P1C,P1D四个输出口。
这篇文章介绍下其中几路的使用方法和半桥输出模式里面互补输出的使用方法。
开PWM4:
void timer4_init(void)
{
TM4RH=0x80;TMR4=0xD0;PWM4DUTY=0x00;T4CR2=0x03; // TM4RH--这个寄存器的第6、7位控制TMR4重载时的高两位
// TMR4--这个寄存器控制重载时的低八位,这里要注意一定要先写高位再写低位
// PWM4DUTY--这个寄存器控制占空比的低八位
// T4CR2--这个寄存器控制分频比、开启/关闭分频器、当选择外部时钟
// 时是上升沿触发-1还是下降沿触发-1、选择时钟输入
T4CR1 = C_PWM4_Active_Hi | C_TMR4_Reload | C_TMR4_En;
}
PWM4的占空比是根据TMR4定时器从初始值开始下数到跟PWM4DUTY值匹配,然后开始输出匹配点到TMR4发生下溢这段时间的有效电平。所以如果把PWM4DUTY写为0,同时高位也为0的话,PWM4将无法输出有效电平。
比如我将TMR4的重载值设定为255,PWM4DUTY设置为128,那么当定时器从255下数到128这段时间,PWM4不输出有效电平,当TMR4的值等于128了,PWM4开始输出有效电平,直到TMR4减小到下溢。
TMR4下溢之后,在T4CR1这个寄存器里面又可以设置模式,第七位(PWM4OEN)可以选择MCU的pin是作为PWM4输出口还是作为普通GPIO。第六位(PWM4OAL)是选择输出PWM4的有效电平。第五和第四是保留位。第三(TM4_HRC)是选择时钟源。第二(T4OS)可以选择当TMR4下溢之后,TMR4是停止等待重新赋值,还是自动重装。第一(T4RL)是选择重载时是从3FF还是从上面设定好的寄存器里面取数重新填到TMR4并继续下数。
上面这段函数是设置好了TMR4,但是还没有开启。下面这段是用来开启PWM4输出的。
T4CR1 = C_PWM4_En | C_PWM4_Active_Hi | C_TMR4_Reload | C_TMR4_En;
//其实就是T4CR1 = 0x8B;
//含义:PB3作为PWM4输出口、高电平有效、时钟源选内部高频振荡、连续下数模式、从TM4RH和TMR4两个寄存器读取重载值、开启TMR4定时器
重新赋值T4CR1之后,PWM4就能输出了。
如果要调整占空比,那就调整PWM4DUTY和占空比那高位的重载值,有一点要注意就是占空比的值不能大于TMR4的重载值,不然就是一直有效电平了。
如果要调整频率,那就是调整分频比和TMR4重载值。
PWM5也是同理,跟PWM4其实大同小异,初始化和开启PWM5输出如下。
void timer5_init(void)
{
TM5RH=0x20;TMR5=0xD0;PWM5DUTY=0x00;T5CR2=0x03;
T5CR1 = C_PWM5_Active_Hi | C_TMR5_Reload | C_TMR5_En;
}
//开启PWM5输出
T5CR1 = C_PWM5_En | C_PWM5_Active_Hi | C_TMR5_Reload | C_TMR5_En;
PWM5 输出互补的两路PWM波。
void main(void)
{
DISI();
// Initial GPIO
IOSTA = C_PA_Output; // Set PortA as intput port
IOSTB = C_PB0_Output; // Set PB0 as output port
PORTA = 0x00; // PortA Data Register = 0x00
PCON = C_WDT_En | C_LVR_En; // Enable WDT & LVR
TM5RH = C_TMR5_Data_b9 | C_TMR5_Data_b8 | C_PWM5_Duty_b9;
TMR5 = C_TMR5_Data;
PWM5DUTY = 0x00;
T5CR2 = C_PS5_Dis; //tmr5 source=instruction, prescaler off
T5CR1 = C_TMR5_Reload; //timer5 enable, reload.
PWMDB = 0x01; //deand band = 1 CPU cycle
CCPCON = C_CCP_PWM5_HalfOut | C_CCP_PWM5_All_ActHigh; // half-bridge ,P1A active High. P1B active Low.
INTE3 = C_INT_CCP; // enable ccp interrupt
ENI();
while(1)
{
CLRWDT(); // Clear WatchDog
}
}
//! interrupt service routine
void isr(void) __interrupt(0)
{
PORTB ^= 0x01; // PB0 Toggle
INTF3 = 0x00 ; //clear ccp interrupt
}
这段是官方例程,我做的时候用过发现不适合使用场景,于是就没用半桥输出模式。
大致解释一下这段代码。先是设置了整个PORTA作为输出,和一个PB0作为输出,然后设置PORTA各pin的初始值。开看门狗和LVR。设置TMR5重载的高两位和PWM5占空比的高两位–设置TMR5重载时的低八位–设置占空比的低八位。关闭分频器、选择内部时钟。设置TMR5为自动重载模式。设置死区时间(1个指令时钟)。设置CCP PWM模式为半桥输出模式,设置有效电平为高电平。最后开启了CCP捕获中断(每次中断之后就翻转一下PB0,没什么用意,应该是告诉用户完成了一次互补波形输出会中断一次。)
最后补充一个东西,这次我想要实现的效果是,两路相同占空比和频率的PWM5错相输出,中间死区大概几个指令周期。发现用半桥驱动模式不合适,因为半桥输出模式是输出两路互补的PWM波形,也就是一个口的占空比高,另一个就是低的。后面研究了一下发现,用硬件PWM也能实现,只不过是需要两路PWM。我这次就是用了PWM4加PWM5两路来实现一个错相的效果,用法简单粗暴。
T4CR1 = C_PWM4_En | C_PWM4_Active_Hi | C_TMR4_Reload | C_TMR4_En;
delay_us(400);
T5CR1 = C_PWM5_En | C_PWM5_Active_Hi | C_TMR5_Reload | C_TMR5_En;
开一路的时候,直接deley一个死区时间,这样两路开起来之后就是错相的。如果要得更好应该也有办法,比如开完之后搞一个PWM4下溢中断,中断服务函数里面再把PWM5开起来,同时把PWM4的下溢中断给关闭,这样应该也能实现一个错相的效果,不过也是要配合延时,不然进入中断之后PWM4是从初始值下数,PWM5也是从初始值下数,就达不到一个错相的效果。挺麻烦的,我这边干脆就直接在开的时候一个delay。最终实测错相没问题,不过如果中途要停一下占空比输出,最好使用:
TM4RH=0xC0;PWM4DUTY=0x00;TM5RH=0x30;PWM5DUTY=0x00;
来代替关闭PWM4和5,因为停PWM4、5之后,如果重新打开,错相的死区时间会变。我是直接采用占空比为0输出,这样也能实现一个停止输出的效果,只不过这时候定时器还没停,硬件PWM也还没停,只是输出的波形被我强制关掉了而已,重新把占空比赋值回去,错相的死区还是保持不变的。
以上就是全部内容了,有需要自取就好。