完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
一个概念:
每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在到达功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。这段延迟时间就是死区。 PWM:脉宽调制 可以有三种方式实现PWM,1采用ETMIER实现,2采用专门的模块,3GPIO模拟 1.采用ETMIER实现 例:采用ETIMER控制直流电机 LK32T的每个定时器有四个通道,在每个通道的捕获比较寄存器(CCRx)中放一个值,计数器从0开始计数,该通道的PWM输出为0,当计数器与这个寄存器的数相同之后,此PWM输出1(电平发生反转)。当计数器值(CNT)值到达ARR的值时,PWM开始输出下一个PWM波。 //主函数 int main( void ) { Device_Init(); TIM0_Init_PWM(1000,196);//TIM0 - CH2通道输出PWM TIM6_T0_Init();//定时器T6-T0初始化 TIM6_T1_Init(); //外部中断配置 //按键SB1--用于紧急停车 PA_INT_ENABLE(0);//开启PA0中断 PA_INT_EDGE(0);//配置为边沿中断 PA_INT_BE_DISABLE(0);//配置为单边沿触发 PA_INT_POL_LOW(0);//配置为下降沿触发 PA_INT_FLAG_CLR(0);//清除中断标志 //按键SB3--实际上是用于测转速,没有用于按键功能 PA_INT_ENABLE(10);//开启PA10中断 PA_INT_EDGE(10);//配置为边沿中断 PA_INT_BE_DISABLE(10);//配置为单边沿触发 PA_INT_POL_LOW(10);//配置为下降沿触发 PA_INT_FLAG_CLR(10);//清除中断标志 //按键SB4--切换液晶或是矩阵键盘功能 PA_INT_ENABLE(11);//开启PA11中断 PA_INT_EDGE(11);//配置为边沿中断 PA_INT_BE_DISABLE(11);//配置为单边沿触发 PA_INT_POL_LOW(11);//配置为下降沿触发 PA_INT_FLAG_CLR(11);//清除中断标志 PB -> OUTEN |= 0xf0fe;//PB输出使能,PB0作为PWM输出口PWM0A PB -> OUT = 0xffff; PA_OUT_ENABLE(12);//蜂鸣器端口输出使能,上电复位提示 SEG_GPIO_Init();//数码管显示端口初始化 delay_ms(500); BUZZER_OFF;//上电提示结束 IRQ_Enable();//开总中断 //LCD12864 初始化============================ LCD_GPIO_Init();//LCD12864液晶 GPIO口初始化 PSB_0; CS_1; lcd_init();//LCD12864初始化 delay_ms(100); lcd_clear();//清屏 delay_ms(100); lcd_wstr(1, 1, "恒温控制系统");//字符显示 lcd_wstr(2, 0, "当前温度");//字符显示 lcd_wstr(3, 0, "当前状态");//字符显示 lcd_wstr(4, 2, "@LUNTEK");//字符显示 write_figer(2,5,0); lcd_wstr(2,6,"℃"); lcd_wstr(3, 4, "NO CMD");//字符显示 //LCD12864 初始化============================ while(1) { if(KeyTrg & 0x02)//SC32主板 独立按键S3 { SB1_Pressed_Flg ^= 0x01;//按键翻转标志位 if(SB1_Pressed_Flg) { //蜂鸣器按键提示 PA_OUT_ENABLE(12); PA_OUT_LOW(12); delay_ms(100); PA_OUT_HIGH(12); PA_OUT_DISABLE(12); TIMER0 -> TIM_ARR = 500;// 自动重装载寄存器的值 printf("按键2启动 MOTOR RUNNINGn"); GPIO_AF_SEL(DIGITAL, PA, 7, 3);// T0CH2 TIMER0 -> TIM_CR1_b.CEN = 1; ST_delay_ms(5000); if(!Motor_Estop_flg) { TIMER0 -> TIM_CR1_b.CEN = 0; GPIO_AF_SEL(DIGITAL, PA, 7, 0);// T0CH2 PA_OUT_DISABLE(7); Motor_RPM = Motor_R * (60 / 5) / 4;//r/min printf("5S时间到 MOTOR SPEED %3.1f RPMnn",Motor_RPM); } else { Motor_Estop_flg = 0; printf("n警告:紧急停车!!!nn"); } Motor_R = 0;//电机旋转圈数清零 Motor_RPM = 0; } else { Motor_R = 0; printf("按键2关闭 MOTOR STOPn"); TIMER0 -> TIM_CR1_b.CEN = 0; GPIO_AF_SEL(DIGITAL, PA, 7, 0);// T0CH2 PA_OUT_DISABLE(7); } } PA_INT_ENABLE(10);//开启PA10中断 TIM6 -> CTC0_b.COUNT0INT_EN = 1; //矩阵键盘应用函数=========================== if(SB4_Pressed_Flg) { i = Key_Value;//读取键盘扫描函数返回值 if(1 == i) //按键1触发 { LED3_ON; lcd_wstr(3, 4, "LED3 ON");//字符显示 } else { LED3_OFF; } if(5 == i) //按键5触发 { LED4_ON; lcd_wstr(3, 4, "LED4 ON");//字符显示 } else { LED4_OFF; } if(9 == i ) //按键9触发 { LED5_ON; lcd_wstr(3, 4, "LED5 ON");//字符显示 } else { LED5_OFF; } if(13 == i ) //按键D触发 { PA_OUT_ENABLE(12); BUZZER_ON; lcd_wstr(3, 4, "BUZ ON");//字符显示 } else { BUZZER_OFF; } if(20 == i) lcd_wstr(3, 4, "NO CMD ");//字符显示 } //矩阵键盘应用函数============================ //工作状态指示/已经被tim0 PWM-CH3占用 // LED1_ON; // ST_delay_ms(500); // LED1_OFF; // ST_delay_ms(500); //DS18B20 液晶显示============================ //DS18B20_Temp = DS18B20_Get_Temp();//读取 DS18B20 温度采集数据 已在中断中实现 write_figer(2,4,DS18B20_Temp); // printf( "当前温度:%F℃rn", DS18B20_Temp ); // printf( "----------TestFinished----------rn" ); //DS18B20 液晶显示============================ //DS18B20 数码管显示========================== // SEG_Display[0] = (uint32_t)DS18B20_Temp % 10; // SEG_Display[1] = (uint32_t)DS18B20_Temp % 100 / 10; // SEG_Display[2] = (uint32_t)DS18B20_Temp % 1000 / 100; // SEG_Display[3] = (uint32_t)DS18B20_Temp % 10000 / 1000; // // // for( i = 0; i < 4; ++i) // { // Dispaly_Number(SEG_Display, i+1); // // ST_delay_ms(1); // PB -> OUT |= 0xe0; // Dispaly_Number(10, 2); // ST_delay_ms(1); // PB -> OUT |= 0xe0; // // } //DS18B20 数码管显示========================== //暂做保留==================================== // UartSendByte(65); // UartSendString("hello"); // UartSentUint32ToASCII(64); // ReadUartBuf(); // SendResponse(); // UartSendStart(); //暂做保留==================================== } } 这里写了很多代码,实际上跟PWM有关的关键代码只有寥寥数行,为便于理解在此详细解释 第6行: TIM0_Init_PWM(1000,196);//TIM0 - CH2通道输出PWM 从这行中我们可以看到这是定时器初始化,其主要设定了预分频1000,死区196,详细的下面再分析。系统的时钟频率为72Mhz,计数脉冲为1000,即计时脉冲为72Khz 第77-80行: TIMER0 -> TIM_ARR = 500;// 自动重装载寄存器的值 printf("按键2启动 MOTOR RUNNINGn"); GPIO_AF_SEL(DIGITAL, PA, 7, 3);// T0CH2 TIMER0 -> TIM_CR1_b.CEN = 1; 77(1)行:将TIM_ARR设定为500,在6行调用Tim0_Init_PWM(1000,196)中已经设置CCR2位250,所以我们可以计算出PWM的占空比为CCR2/ARR=50%,PWM的频率为72K/500=144Hz(?)。 79(3)行:设置PA07位PWM的输出口(T0CH2) 80(4)行:定时器启动,实际上就是开始输出PWM波,即电机开始转动! 85-89行: TIMER0 -> TIM_CR1_b.CEN = 0; GPIO_AF_SEL(DIGITAL, PA, 7, 0);// T0CH2 PA_OUT_DISABLE(7); Motor_RPM = Motor_R * (60 / 5) / 4;//r/min printf("5S时间到 MOTOR SPEED %3.1f RPMnn",Motor_RPM); 85(1)行:定时器停止计数--PWM停止--电机停止 86(2)行:改变PA7管脚功能为GPIO功能 87(3)行:PA7禁止输出 88行、89行:计算电机转速并显示 101~105行: Motor_R = 0; printf("按键2关闭 MOTOR STOPn"); TIMER0 -> TIM_CR1_b.CEN = 0; GPIO_AF_SEL(DIGITAL, PA, 7, 0);// T0CH2 PA_OUT_DISABLE(7); 与85~89行基本类似,电机停止转动并作出提示 PWM波: void TIM0_Init_PWM(uint32_t PRD, uint32_t DB_CFG) { TIMER0 -> TIM_ARR = PRD;// 自动重装载寄存器的值 TIMER0 -> TIM_PSC = 7200;//预分频值 TIMER0 -> TIM_CR1_b.CMS = CMS_EDGE_ALIGN;// 边沿对其模式 TIMER0 -> TIM_CR1_b.DIR = 0;//计数器增计数 TIMER0 -> TIM_CCMR1 |= OC2M_PWM_MODE2; // OC2M CH2选择PWM模式2 TIMER0 -> TIM_CCMR1 |= OC2PE_PRELOAD_ENABLE;// OC2PE 开启 TIM_CCR1 寄存器的预装载功能 TIM_ARPE_ENABLE; // 周期计数预装载允许位:TIM_ARR 寄存器有缓冲 TIMER0 -> TIM_CCER_b.CC2E = CC2E_ENABLE; // 捕捉/比较1输出使能 TIMER0 -> TIM_CCER_b.CC2NE = CC2NE_ENABLE; // 捕捉/比较 1 互补输出使能 TIMER0 -> TIM_CCER_b.CC2P = CC2P_OUTPUT_LOW; // 捕捉/比较1输出极性:OC1 高电平有效; TIMER0 -> TIM_CCER_b.CC2NP = CC2NP_OUTPUT_HIGH; // 捕捉/比较1互补输出极性:OC1N 高电平有效; TIMER0 -> TIM_CCR2 = PRD/4; // 占空比设置 TIMER0 -> TIM_BDTR_b.MOE = MOE_ENABLE;// 主输出使能 TIMER0 -> TIM_BDTR_b.AOE = AOE_SW_SET; TIMER0 -> TIM_BDTR_b.OSSR = 0; // 运行模式下“关闭状态”选择 TIMER0 -> TIM_BDTR_b.DTG = DB_CFG; // 196 死区设置 OC1上升沿后延时2us,具体设置参考寄存器说明 TIMER0 -> TIM_DTG1 = DB_CFG; // 196 死区设置 OC1N下降沿后延时2us,具体设置参考寄存器说明 //EGR_CNT_UPDATE; // TIMER0->TIM_EGR_b.UG = 1; 重新初始化计数器,并产生一个 ( 寄存器 ) 更新事件 //CNT_ENABLE; // TIMER0->TIM_CR1_b.CEN=1;使能计数器 /************************************************************************* 刹车输入 ************************************************************************/ // TIMER0 -> TIM_BDTR_b.BKE = BKE_BRAKE_ENABLE; // 刹车使能 // TIMER0 -> TIM_BDTR_b.BKP = BKP_BRAKE_POL_LOW; // 刹车极性为低电平有效 // TIMER0 -> TIM_BDTR_b.OSSI = 0; // 运行模式下“空闲状态”选择 // TIMER0 -> TIM_CR2_b.OIS1 = OIS1_OC1_AFTER_DT_LOW; // 输出空闲状态 1(OC1 输出 ) // TIMER0 -> TIM_CR2_b.OIS1N = OIS1N_OC1N_AFTER_DT_LOW; // 输出空闲状态 1(OC1N 输出 ) } PWM初始化的工作内容很多,暂时可以会用即可! TIM6的两个定时器设置略,与定时器一章相似,用于按键及数码管的扫描。 |
|
|
|
三个按键中断:
void GPIO0_IRQHandler() { if(!(PA -> PIN & (1 << 0)))//判断按键SB1是否按下 { PA_INT_DISABLE(0);//关闭PA10中断 Motor_Estop_flg = 1;//紧急停车标志位 TIMER0 -> TIM_CR1_b.CEN = 0; GPIO_AF_SEL(DIGITAL, PA, 7, 0);// T0CH2 PA_OUT_DISABLE(7); PA_INT_FLAG_CLR(0);//清除中断标志 PA_INT_ENABLE(0);//开启PA10中断 } if(!(PA -> PIN & (1 << 10)))//判断按键SB3是否按下 { PA_INT_DISABLE(10);//关闭PA10中断 Motor_R++; PA_INT_FLAG_CLR(10);//清除中断标志 PA_INT_ENABLE(10);//开启PA10中断 } if(!(PA -> PIN & (1 << 11)))//判断按键SB4是否按下 { //蜂鸣器按键提示 PA_OUT_ENABLE(12); PA_OUT_LOW(12); delay_ms(200); PA_OUT_HIGH(12); PA_OUT_DISABLE(12); SB4_Pressed_Flg ^= 1;//切换液晶或是矩阵键盘功能 if(SB4_Pressed_Flg)//模式选择位 { PA -> OUTEN &= ~0X70;//PA输出失能 数码管位选端 NVIC_DisableIRQ(TIM6_T1_IRQn); NVIC_EnableIRQ(TIM6_T0_IRQn);//选择矩阵键盘功能 } else { PA -> OUTEN |= 0X70;//PA输出使能 数码管位选端 PB -> OUTEN |= 0XFF00;//PB输出使能 PB -> OUT &= 0X00FF;//数据清零 PA -> OUT |= 0X70;//位选置位 关断 NVIC_DisableIRQ(TIM6_T0_IRQn); NVIC_EnableIRQ(TIM6_T1_IRQn);//选择数码管显示功能 } PA_INT_FLAG_CLR(11);//清除中断标志 } NVIC_ClearPendingIRQ(PA_IRQn);//清除中断 } 上述的代码洋洋洒洒,但是实际关键的就是几步: 1.设定PWM的频率 2.设定PWM波的占空比 3.开始输出PWM波 4.停止输出PWM TIMER0 -> TIM_ARR = 500;// 自动重装载寄存器的值 printf("按键2启动 MOTOR RUNNINGn"); GPIO_AF_SEL(DIGITAL, PA, 7, 3);// T0CH2 TIMER0 -> TIM_CR1_b.CEN = 1;//计数器开始计数,当然PWM就可以输出了 我们简单粗暴一点: 1.设定PWM的频率 Tim0_Init_PWM(1000,196)不要动了,另外设定ARR,可以设定PWM频率,72Mhz/1000/ARR=.... TIMER0 -> TIM_ARR = 500; .设定PWM波的占空比 设定CCRx可设定占空比,CCRx/(ARR+1)=...... TIMER0 -> TIM_CCR2 = PRD/4; // 占空比设置 3.开始输出PWM波 启动定时器可以开始输出PWM波 GPIO_AF_SEL(DIGITAL, PA, 7, 3);// T0CH2 TIMER0 -> TIM_CR1_b.CEN = 1; 4.停止输出PWM TIMER0 -> TIM_CR1_b.CEN = 0;//计数器不可以计数了,当然就停止输出PWM波了 GPIO_AF_SEL(DIGITAL, PA, 7, 0);// PA7管脚不再是T0CH2的输出了 PA_OUT_DISABLE(7); 2采用专门的模块 LK32T102采用专用的 16 位时间基准计数器,有三个通道PWM波输出PWM0,PWM1和PWM2,每个通道为一个模块,每个通道又分为A,B两路。 每个 PWM 模块由 6 个子模块组成,不同的模块间结构完全相同,既可以独立运行,又可以通过时钟同步信号,组成 一个系统运行。 1. 时基子模块: 它是一个专用的 16 位计数器,以 PWM 时钟(TBCLK)频 率运行,该时钟是由系统时钟(SYSCLKOUT)(通过 TBCTL[CLKDIV])分频得到的。因此,通过设置时基计数器 的计数周期[TBPRD]即可确定 PWM 的周期和频率。 2. 计数比较子模块: 计数器比较子模块中有两个比较值寄存器 CMPA 和 CMPB,通过持续的与时间基准比较器的值进行比较来产 生比较事件。例如:TBCTR=CMPA,TBCTR=CMPB 3. 动作限定子模块: 动作限定子模块的主要功能是决定响应何种比较事件,做出何种动作,从而得到所需的 PWM 波形。 4. 死区生成子模块 为防止 PWM 某一通道的上下桥臂同时导通导致短路,需要在一个开关管彻底关断和同通道另一个开关管导通 之间加入“死区”。死区生成子模块的作用就在于此。 5. 故障保护子模块 故障保护子模块通过对故障信号的检测,判断是否发生了故障,从而控制 PWM 输出做出相应的动作。 6. 事件触发子模块 事件触发子模块通过管理时间基准子模块(TB)和计数比较子模块(CC)所产生的事件,产生一个中断到 CPU 或者一个 SOC 信号到 ADC。 实例: 产生一个频率为10KHz,负占空比为 10%的PWM波 1)管脚功能选择 void GPIO_Init(){ GPIO_AF_SEL(DIGITAL,PA,8,2); // PWM1A // GPIO_AF_SEL(DIGITAL,PA,9,2); // PWM2B // GPIO_AF_SEL(DIGITAL,PA,10,2); // PWM2A // GPIO_AF_SEL(DIGITAL,PB,13,2); // PWM0B // GPIO_AF_SEL(DIGITAL,PB,14,2); // PWM0A // GPIO_AF_SEL(DIGITAL,PB,15,2); // PWM1B // .... } 管脚PA8的数字功能2(PWM1A)功能,具体设置哪一个管脚及哪一个通道,哪一路可查LK32T102编程手册。 2)设置PWM的频率 v void PWM_Init(void){ PWM_CFG(3600, 0); // 72MHz下,10kHzPWM,0us死区 //PWM0 -> CMPA = 500; //PB14 PWM1 -> CMPA = 360; //PA8 //PWM2 -> CMPA = 1500; //PA10 PWM_START; } 上述中1行调用了PWM_CFG(3600,0)对PWM进行配置,而4行设置了占空比。 因为采用增减计数的方式,计数脉冲也没有分频,所以,或者说PWM波的周期为0.1ms。 负占空比=360/3600=10%(?)或低电平时间为360*2/72=10us(?) 3)void PWM_CFG(uint32_t Period,uint32_t DB)关键代码分析 /************************************************************************ 初始化PWM0-2,设置参数为:PWM半周期计数值、死区计数值 ************************************************************************/ void PWM_CFG(uint32_t Period,uint32_t DB) { ...... /****************************************************************************** PWM1 *******************************************************************************/ PWM1->TBPRD = Period; // 设置PWM1的半周期 PWM1->TBPHS = 0; // Set Phase register to zero PWM1->TBCTL = 0; PWM1_CTRMODE_UPDOWN; // Symmetrical mode CTRMODE PWM1_PHASE_ENABLE; // Slave module PHSEN PWM1_PRD_SHADOWON; // TB_SHADOW PWM1->TBCTL_b.SYNCOSEL = SYNCOSEL_SYNC; // sync flow-through SYNCOSEL PWM1->CMPCTL_b.SHDWAMODE = SHADOWMODE_ON; // CC_SHADOW PWM1->CMPCTL_b.SHDWBMODE = SHADOWMODE_ON; // CC_SHADOW PWM1->CMPCTL_b.LOADAMODE = CMP_LOAD_ZERO; // load on CTR=Zero PWM1->CMPCTL_b.LOADBMODE = CMP_LOAD_ZERO; // load on CTR=Zero PWM1->AQCTLA_b.CAU = AQ_CAU_CLR; // 当计数器等于主CMPA寄存器并且计数递增时动作:清除:使PWMxA输出低 PWM1->AQCTLA_b.CAD = AQ_CAD_SET; // 当计数器等于主CMPA寄存器并且计数递减时动作:置位:使PWMxA输出高 PWM1->AQCTLB_b.CAU = AQ_CAU_SET; // 当计数器等于主CMPB寄存器并且计数递增时动作:置位:使PWMxB输出高 PWM1->AQCTLB_b.CAD = AQ_CAD_CLR; // 当计数器等于主CMPB寄存器并且计数递减时动作:清除:使PWMxB输出低 PWM1->DBCTL_b.IN_MODE = DB_INMODE_PWMB_BOTH_EDGE; // PWMxBIN作为上升沿和下降沿延迟的输入源 PWM1->DBCTL_b.OUT_MODE = DB_OUTMODE_BOTH_ENABLE; // 死区对于PWMXA输入的上升沿延迟和PWMxB输出的下降沿延迟完全使能。 PWM1->DBCTL_b.POLSEL = DB_POLSEL_AHC; // 高有效互补(AHC)模式。PWMxB反相 PWM1->DBFED = DB; // 下降沿死区 PWM1->DBRED = DB; // 上升沿死区 .... } 需要注意的是这段代码 中还有很多的配置,如PWM触发中断等,如果不需要的话最好将其注释掉,因为频繁的中断发生有可能使得其他部分的代码没有机会运行。 |
|
|
|
只有小组成员才能发言,加入小组>>
3314 浏览 9 评论
2995 浏览 16 评论
3494 浏览 1 评论
9059 浏览 16 评论
4088 浏览 18 评论
1178浏览 3评论
605浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
599浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2335浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1896浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 08:32 , Processed in 1.241161 second(s), Total 80, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号