您好,歡迎進入深圳市穎特新科技有限公司官方網(wǎng)站!
/**************************************************************************//** * @file main.c * @version V3.00 * $Revision: 2 $ * $Date: 15/09/02 10:03a $ * @brief Demonstrate how to set GPIO pin mode and use pin data input/output control. * @note * Copyright (C) 2013~2015 Nuvoton Technology Corp. All rights reserved. * ******************************************************************************/#include "stdio.h"#include "M451Series.h"#include "NuEdu-Basic01.h"
#define PLL_CLOCK 72000000
void SYS_Init(void){ /*---------------------------------------------------------------------------------------------------------*/ /* Init System Clock */ /*---------------------------------------------------------------------------------------------------------*/
/* Enable HIRC clock (Internal RC 22.1184MHz) */
CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);
/* Wait for HIRC clock ready */ CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);
/* Select HCLK clock source as HIRC and and HCLK clock divider as 1 */
CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));
/* Enable HXT clock (external XTAL 12MHz) */ CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk);
/* Wait for HXT clock ready */
CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk);
/* Set core clock as PLL_CLOCK from PLL */ CLK_SetCoreClock(PLL_CLOCK);
/* Enable UART module clock */
CLK_EnableModuleClock(UART0_MODULE);
/* Select UART module clock source as HXT and UART module clock divider as 1 */ CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HXT, CLK_CLKDIV0_UART(1));
/*---------------------------------------------------------------------------------------------------------*/
/* Init I/O Multi-function */
/*---------------------------------------------------------------------------------------------------------*/
/* Set PD multi-function pins for UART0 RXD(PD.6) and TXD(PD.1) */ SYS->GPD_MFPL &= ~(SYS_GPD_MFPL_PD6MFP_Msk | SYS_GPD_MFPL_PD1MFP_Msk); SYS->GPD_MFPL |= (SYS_GPD_MFPL_PD6MFP_UART0_RXD | SYS_GPD_MFPL_PD1MFP_UART0_TXD);
}
void UART0_Init(){ /*---------------------------------------------------------------------------------------------------------*/ /* Init UART */ /*---------------------------------------------------------------------------------------------------------*/ /* Reset UART module */ SYS_ResetModule(UART0_RST);
/* Configure UART0 and set UART0 baud rate */
UART_Open(UART0, 115200);
}
/*---------------------------------------------------------------------------------------------------------*//* Main Function *//*---------------------------------------------------------------------------------------------------------*/int32_t main(void){ uint32_t temp,temp1 = 0; /* Unlock protected registers */ SYS_UnlockReg();
/* Init System, peripheral clock and multi-function I/O */
SYS_Init();
/* Lock protected registers */ SYS_LockReg();
/* Init UART0 for printf */
UART0_Init();
printf("\nNuEdu-SDK-M451 PWM-DAC\n");
Initial_PWM_LED();
Initial_PWM_DAC();
Initial_LED();
Open_ADC_Knob();
Write_PWMDAC(1,temp1);
while(1)
{
//Get Volume Knob Data
temp = Get_ADC_PWMDAC(); //Volume Range: 0 ~ 4095
Write_LED_Bar((temp * (8 + 1) / 4096));
Write_PWMDAC(1,temp1++);
if(temp1>100)
temp1 = 0;
CLK_SysTickDelay(10000);
}
}
以上是PWM的例程,今天主要講講PWM的發(fā)生
M451提供了兩路PWM發(fā)生器。每路PWM支持6通道PWM輸出或輸入捕捉。有一個12位的預(yù)分頻器把時鐘源分頻后輸入給16位的計數(shù)器,另外還有一個16位的比較器。PWM計數(shù)器支持向上,向下,上下計數(shù)方式。PWM用比較器和計數(shù)器的比較來產(chǎn)生事件,這些事件用來產(chǎn)生PWM脈沖,中斷,EADC/DAC轉(zhuǎn)換觸發(fā)信號。
PWM發(fā)生器支持兩種標準PWM輸出模式:獨立模式和互補模式,它們的架構(gòu)不同。標準輸出模式又有兩種輸出功能:組功能和同步功能。組功能可以在獨立模式和互補模式下使能。同步功能只有在互補模式下才可以被使能。互補模式,有兩個比較器產(chǎn)生各種帶12位死區(qū)時間的PWM脈寬,另外還有一個自由觸發(fā)比較器來產(chǎn)生給EADC的觸發(fā)信號。PWM輸出控制單元,它支持極性輸出,獨立管腳屏蔽和剎車功能。
PWM也支持輸入捕捉功能,當輸入通道有向上跳變、向下跳變、或者兩者都有的跳變時,鎖存PWM計數(shù)器的值到相應(yīng)的寄存器中。捕捉功能也支持通過PDMA把捕捉到的數(shù)據(jù)搬移到內(nèi)存。
PWM功能特性 6.9.2.1
? 支持時鐘頻率最高達144MHz
? 支持兩個PWM模塊,每個模塊提供6個輸出通道
? 支持獨立模式的PWM輸出/輸入捕捉
? 支持3組互補通道的互補模式
? 12位解析度的死區(qū)插入
? 相控制的同步功能
? 每個周期兩個比較值
? 支持12位從1到4096的預(yù)分頻
? 支持16位解析度的PWM計數(shù)器
? 向上,向下和上下計數(shù)操作類型
? 支持one-shot或自動裝載計數(shù)器工作模式
? 支持組功能
? 支持同步功能
? 每個PWM管腳支持屏蔽功能和三態(tài)使能
? 支持剎車功能
? 剎車源來自管腳、模擬比較器和系統(tǒng)安全事件(時鐘故障、SRAM奇偶校驗錯誤、欠壓監(jiān)測和CPU鎖。
? 剎車源管腳噪聲濾波器
? 通過邊緣檢測剎車源來控制剎車狀態(tài)直到剎車中斷清除
? 剎車條件解除后電平檢測剎車源自動恢復(fù)功能
? 支持下列事件中斷:
? PWM計數(shù)器值為 0、周期值或比較值
? 發(fā)生剎車條件
? 支持下列事件觸發(fā)EADC/DAC :
? PWM計數(shù)器值為0、周期值或比較值
? PWM 計數(shù)器匹配自由觸發(fā)比較器比較值(僅EADC)
捕捉功能特性 6.9.2.2
? 支持12個16位解析度的輸入捕捉通道
? 支持上升/下降沿捕捉條件
? 支持輸入上升/下降沿 捕捉中斷
? 支持計數(shù)器重載選項的上升/下降沿 捕捉
? 支持PWM 的所有通道PDMA數(shù)據(jù)搬移功能
PWM系統(tǒng)時鐘可以設(shè)為等于或兩倍的HCLK頻率, 圖 6.9-2, 寄存器設(shè)置細節(jié)請參考表 6.9-1.
每個PWM發(fā)生器有三個輸入時鐘源,每個時鐘源可以從系統(tǒng)時鐘或者4組定時器觸發(fā)PWM輸出,圖
6.9-3,PWM_CLK0設(shè)置ECLKSRC0 (PWM_CLKSRC[2:0]),PWM_CLK2設(shè)置ECLKSRC2
(PWM_CLKSRC[10:8]),PWM_CLK4設(shè)置ECLKSRC4 (PWM_CLKSRC[18:16])
圖 6.9-4 和圖 6.9-5表示PWM獨立模式和互補模式的架構(gòu)。不管獨立模式還是比較模式,一對通道
組(PWM_CH0 和PWM_CH1, PWM_CH2 和 PWM_CH3, PWM_CH4 和 PWM_CH5)計數(shù)器來自相
同的時鐘源和預(yù)分頻。當計數(shù)器的值等于0,PERIOD(PWM_PERIODn[15:0])或比較器值,將產(chǎn)生
事件。這些事件通過相應(yīng)的發(fā)生器來產(chǎn)生PWM脈沖、中斷信號、EADC/DAC的轉(zhuǎn)換觸發(fā)信號。輸
出控制是用來改變PWM脈沖輸出狀態(tài)的。輸出控制的剎車功能也能產(chǎn)生中斷事件。在互補模式,
同步功能有效,偶數(shù)通道用奇數(shù)通道比較器產(chǎn)生事件,自由觸發(fā)比較器事件只用于產(chǎn)生觸發(fā)EADC
信號。
#include <stdio.h> #include "M451Series.h" #include "NuEdu-Basic01_PWMDAC.h" void Write_PWMDAC(unsigned char Enable, unsigned char ch0_dut) { /* set PWMB channel 0 output configuration */ PWM_ConfigOutputChannel(PWM1, 4, 1000, ch0_dut); // Start PWM COUNT PWM_Start(PWM1, 1 << 4); if(Enable == 0) /* Enable PWM Output path for PWMB channel 0 */ PWM_DisableOutput(PWM1, 1 << 4); else /* Diable PWM Output path for PWMB channel 0 */ PWM_EnableOutput(PWM1, 1 << 4); } void Initial_PWM_DAC(void) { GPIO_SetMode(PC, BIT13, GPIO_MODE_INPUT); //avoid to pwm dac out SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC13MFP_Msk ; SYS->GPC_MFPH |= SYS_GPC_MFPH_PC13MFP_PWM1_CH4; /* Enable PWM module clock */ CLK_EnableModuleClock(PWM1_MODULE); /* Select PWM module clock source */ CLK_SetModuleClock(PWM1_MODULE, CLK_CLKSEL2_PWM1SEL_PCLK1, 0); /* Reset PWM1 channel 0~5 */ SYS_ResetModule(PWM1_RST); }
/**************************************************************************//** * @file NuEdu-NuEdu-Basic01_RGBLED.c * @version V1.00 * $Revision: 5 $ * $Date: 15/09/02 10:02a $ * @brief NuEdu-Basic01_RGBLED driver source file for NuEdu-SDK-M451 * * @note * Copyright (C) 2014~2015 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/ #include <stdio.h> #include "M451Series.h" #include "NuEdu-Basic01_RGBLED.h" /** @addtogroup M451_Library M451 Library @{ */ /** @addtogroup NuEdu-SDK-M451_Basic01 M451_Basic01 Library @{ */ /** @addtogroup M451_Basic01_FUNCTIONS RGB LED Functions @{ */ /** * @brief Set multi-function pins for PWM1 channel 0,1,2 * @return None */ void Initial_PWM_LED(void) { /* Set PC9~PC11 multi-function pins for PWM1 Channel0~2 */ SYS->GPC_MFPH &= ~(SYS_GPC_MFPH_PC9MFP_Msk | SYS_GPC_MFPH_PC10MFP_Msk | SYS_GPC_MFPH_PC11MFP_Msk); SYS->GPC_MFPH |= SYS_GPC_MFPH_PC9MFP_PWM1_CH0 | SYS_GPC_MFPH_PC10MFP_PWM1_CH1 | SYS_GPC_MFPH_PC11MFP_PWM1_CH2; /* Enable PWM module clock */ CLK_EnableModuleClock(PWM1_MODULE); /* Select PWM module clock source */ CLK_SetModuleClock(PWM1_MODULE, CLK_CLKSEL2_PWM1SEL_PCLK1, 0); /* Reset PWM1 channel 0~5 */ SYS_ResetModule(PWM1_RST); } /** * @brief Set PWM clock enable and HCLK as PWM clock source, * * @param[in] ch Channel numbers that will be enabled. * * @param[in] ch0_fre Channel 0 frequency. * * @param[in] ch0_dut Channel 0 duty. * * @param[in] ch1_fre Channel 1 frequency. * * @param[in] ch1_dut Channel 1 duty. * * @param[in] ch2_fre Channel 2 frequency. * * @param[in] ch2_dut Channel 2 duty. * * @return None */ void PWM_LED(unsigned char ch, unsigned int ch0_fre, unsigned int ch0_dut, unsigned int ch1_fre, unsigned int ch1_dut, unsigned int ch2_fre, unsigned int ch2_dut) { /* set PWMA channel 1 output configuration */ PWM_ConfigOutputChannel(PWM1, 0, ch0_fre, ch0_dut); PWM_ConfigOutputChannel(PWM1, 1, ch1_fre, ch1_dut); PWM_ConfigOutputChannel(PWM1, 2, ch2_fre, ch2_dut); /* Enable PWM Output path for PWMA channel 0 */ PWM_EnableOutput(PWM1, ch); // Start PWM_Start(PWM1, ch); } /*@}*/ /* end of group M451_Basic01_FUNCTIONS */ /*@}*/ /* end of group NuEdu-SDK-M451_Basic01 */ /*@}*/ /* end of group M451_Library */ /*** (C) COPYRIGHT 2013~2015 Nuvoton Technology Corp. **/
uint32_t PWM_ConfigCaptureChannel(PWM_T *pwm, uint32_t u32ChannelNum, uint32_t u32UnitTimeNsec, uint32_t u32CaptureEdge) { uint32_t u32Src; uint32_t u32PWMClockSrc; uint32_t u32NearestUnitTimeNsec; uint16_t u16Prescale = 1, u16CNR = 0xFFFF; if(pwm == PWM0) u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM0SEL_Msk; else//(pwm == PWM1) u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM1SEL_Msk; if(u32Src == 0) { //clock source is from PLL clock u32PWMClockSrc = CLK_GetPLLClockFreq(); } else { //clock source is from PCLK SystemCoreClockUpdate(); u32PWMClockSrc = SystemCoreClock; } u32PWMClockSrc /= 1000; for(u16Prescale = 1; u16Prescale <= 0x1000; u16Prescale++) { u32NearestUnitTimeNsec = (1000000 * u16Prescale) / u32PWMClockSrc; if(u32NearestUnitTimeNsec < u32UnitTimeNsec) { if(u16Prescale == 0x1000) //limit to the maximum unit time(nano second) break; if(!((1000000 * (u16Prescale + 1) > (u32NearestUnitTimeNsec * u32PWMClockSrc)))) break; continue; } break; } // convert to real register value // every two channels share a prescaler PWM_SET_PRESCALER(pwm, u32ChannelNum, --u16Prescale); // set PWM to down count type(edge aligned) (pwm)->CTL1 = ((pwm)->CTL1 & ~(PWM_CTL1_CNTTYPE0_Msk << (2 * u32ChannelNum))) | (1UL << (2 * u32ChannelNum)); // set PWM to auto-reload mode (pwm)->CTL1 &= ~(PWM_CTL1_CNTMODE0_Msk << u32ChannelNum); PWM_SET_CNR(pwm, u32ChannelNum, u16CNR); return (u32NearestUnitTimeNsec); } /** * @brief This function Configure PWM generator and get the nearest frequency in edge aligned auto-reload mode * @param[in] pwm The pointer of the specified PWM module * - PWM0 : PWM Group 0 * - PWM1 : PWM Group 1 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5 * @param[in] u32Frequency Target generator frequency * @param[in] u32DutyCycle Target generator duty cycle percentage. Valid range are between 0 ~ 100. 10 means 10%, 20 means 20%... * @return Nearest frequency clock in nano second * @note Since every two channels, (0 & 1), (2 & 3), shares a prescaler. Call this API to configure PWM frequency may affect * existing frequency of other channel. */ uint32_t PWM_ConfigOutputChannel(PWM_T *pwm, uint32_t u32ChannelNum, uint32_t u32Frequency, uint32_t u32DutyCycle) { uint32_t u32Src; uint32_t u32PWMClockSrc; uint32_t i; uint16_t u16Prescale = 1, u16CNR = 0xFFFF; if(pwm == PWM0) u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM0SEL_Msk; else//(pwm == PWM1) u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM1SEL_Msk; if(u32Src == 0) { //clock source is from PLL clock u32PWMClockSrc = CLK_GetPLLClockFreq(); } else { //clock source is from PCLK SystemCoreClockUpdate(); u32PWMClockSrc = SystemCoreClock; } for(u16Prescale = 1; u16Prescale < 0xFFF; u16Prescale++)//prescale could be 0~0xFFF { i = (u32PWMClockSrc / u32Frequency) / u16Prescale; // If target value is larger than CNR, need to use a larger prescaler if(i > (0x10000)) continue; u16CNR = i; break; } // Store return value here 'cos we're gonna change u16Prescale & u16CNR to the real value to fill into register i = u32PWMClockSrc / (u16Prescale * u16CNR); // convert to real register value // every two channels share a prescaler PWM_SET_PRESCALER(pwm, u32ChannelNum, --u16Prescale); // set PWM to down count type(edge aligned) (pwm)->CTL1 = ((pwm)->CTL1 & ~(PWM_CTL1_CNTTYPE0_Msk << (2 * u32ChannelNum))) | (1UL << (2 * u32ChannelNum)); // set PWM to auto-reload mode (pwm)->CTL1 &= ~(PWM_CTL1_CNTMODE0_Msk << u32ChannelNum); PWM_SET_CNR(pwm, u32ChannelNum, --u16CNR); if(u32DutyCycle) { PWM_SET_CMR(pwm, u32ChannelNum, u32DutyCycle * (u16CNR + 1) / 100 - 1); (pwm)->WGCTL0 &= ~((PWM_WGCTL0_PRDPCTL0_Msk | PWM_WGCTL0_ZPCTL0_Msk) << (u32ChannelNum * 2)); (pwm)->WGCTL0 |= (PWM_OUTPUT_LOW << (u32ChannelNum * 2 + PWM_WGCTL0_PRDPCTL0_Pos)); (pwm)->WGCTL1 &= ~((PWM_WGCTL1_CMPDCTL0_Msk | PWM_WGCTL1_CMPUCTL0_Msk) << (u32ChannelNum * 2)); (pwm)->WGCTL1 |= (PWM_OUTPUT_HIGH << (u32ChannelNum * 2 + PWM_WGCTL1_CMPDCTL0_Pos)); } else { PWM_SET_CMR(pwm, u32ChannelNum, 0); (pwm)->WGCTL0 &= ~((PWM_WGCTL0_PRDPCTL0_Msk | PWM_WGCTL0_ZPCTL0_Msk) << (u32ChannelNum * 2)); (pwm)->WGCTL0 |= (PWM_OUTPUT_LOW << (u32ChannelNum * 2 + PWM_WGCTL0_ZPCTL0_Pos)); (pwm)->WGCTL1 &= ~((PWM_WGCTL1_CMPDCTL0_Msk | PWM_WGCTL1_CMPUCTL0_Msk) << (u32ChannelNum * 2)); (pwm)->WGCTL1 |= (PWM_OUTPUT_HIGH << (u32ChannelNum * 2 + PWM_WGCTL1_CMPDCTL0_Pos)); } return(i); } /** * @brief Start PWM module * @param[in] pwm The pointer of the specified PWM module * - PWM0 : PWM Group 0 * - PWM1 : PWM Group 1 * @param[in] u32ChannelMask Combination of enabled channels. Each bit corresponds to a channel. * Bit 0 is channel 0, bit 1 is channel 1... * @return None * @details This function is used to start PWM module. */ void PWM_Start(PWM_T *pwm, uint32_t u32ChannelMask) { (pwm)->CNTEN |= u32ChannelMask; }
PWM原理
隨著電子技術(shù)的發(fā)展,出現(xiàn)了多種PWM技術(shù),其中包括:相電壓控制PWM、脈寬PWM法、隨機PWM、SPWM法、線電壓控制PWM等,而在鎳氫電池智能充電器中采用的脈寬PWM法,它是把每一脈沖寬度均相等的脈沖列作為PWM波形,通過改變脈沖列的周期可以調(diào)頻,改變脈沖的寬度或占空比可以調(diào)壓,采用適當控制方法即可使電壓與頻率協(xié)調(diào)變化?梢酝ㄟ^調(diào)整PWM的周期、PWM的占空比而達到控制充電電流的目的。
模擬信號的值可以連續(xù)變化,其時間和幅度的分辨率都沒有限制。9V電池就是一種模擬器件,因為它的輸出電壓并不精確地等于9V,而是隨時間發(fā)生變化,并可取任何實數(shù)值。與此類似,從電池吸收的電流也不限定在一組可能的取值范圍之內(nèi)。模擬信號與數(shù)字信號的區(qū)別在于后者的取值通常只能屬于預(yù)先確定的可能取值集合之內(nèi),例如在{0V, 5V}這一集合中取值。
模擬電壓和電流可直接用來進行控制,如對汽車收音機的音量進行控制。在簡單的模擬收音機中,音量旋鈕被連接到一個可變電阻。擰動旋鈕時,電阻值變大或變小;流經(jīng)這個電阻的電流也隨之增加或減少,從而改變了驅(qū)動揚聲器的電流值,使音量相應(yīng)變大或變小。與收音機一樣,模擬電路的輸出與輸入成線性比例。
盡管模擬控制看起來可能直觀而簡單,但它并不總是非常經(jīng)濟或可行的。其中一點就是,模擬電路容易隨時間漂移,因而難以調(diào)節(jié)。能夠解決這個問題的精密模擬電路可能非常龐大、笨重(如老式的家庭立體聲設(shè)備)和昂貴。模擬電路還有可能嚴重發(fā)熱,其功耗相對于工作元件兩端電壓與電流的乘積成正比。模擬電路還可能對噪聲很敏感,任何擾動或噪聲都肯定會改變電流值的大小。
通過以數(shù)字方式控制模擬電路,可以大幅度降低系統(tǒng)的成本和功耗。此外,許多微控制器和DSP已經(jīng)在芯片上包含了PWM控制器,這使數(shù)字控制的實現(xiàn)變得更加容易了。
互補的意思就是當pwm1是高電平時,pwm2是低電平,如果pwm1是低電平時pwm2是高電平,總之是pwm1和pwm2不會同事變高或變低,總是不一樣的。 一般這樣的pwm輸出用于控制由兩個開關(guān)管組成的在電源和地之間的橋,兩個同時接通的話會導(dǎo)致橋臂短路電源和地引起燒毀,互補的波形可避免同時導(dǎo)通。
開關(guān)管可不是瞬間就能開啟或關(guān)閉的。 特別是推挽電路中,沒有死區(qū)的話,有可能造成直通短路。 即使是boost 電路,為了限制最大占空比,也得設(shè)計個死區(qū)時間啊, 具體設(shè)置多少要看所用開關(guān)管的開啟和關(guān)閉的延遲時間,
簡單的說,比如你有5V電源,要控制一臺燈的亮度,有一個傳統(tǒng)辦法,就是串聯(lián)一個可調(diào)電阻,改變電阻,燈的亮度就會改變。 還有一個辦法,就是PWM調(diào)節(jié)。不用串聯(lián)電阻,而是串聯(lián)一個開關(guān)。假設(shè)在1秒內(nèi),有0.5秒的時間開關(guān)是打開的,0.5秒關(guān)閉,那么燈就亮0.5秒,滅0.5秒。這樣持續(xù)下去,燈就會閃爍。如果把頻率調(diào)高一點,比如是1毫秒,0.5毫秒開,0.5毫秒滅,那么燈的閃爍頻率就很高。我們知道,閃爍頻率超過一定值,人眼就會感覺不到。所以,這時你看不到燈的閃爍,只看到燈的亮度只有原來的一半。 同理,如果1毫秒內(nèi),0.1毫秒開,0.9毫秒滅,那么,燈的亮度就只有原來的10分之一。 這就是PWM的基本原理。專業(yè)的說法百度一下就很多,我說了也不專業(yè)。但是道理就是這么簡單,具體PWM還分幾種,總的來說,都是保持一定的電壓或電流不變,但改變一定周期內(nèi)的導(dǎo)通和關(guān)斷時間。這樣等效于保持導(dǎo)通,但改變電壓或電流大小。 這樣的PWM控制方式,在數(shù)字控制電路上應(yīng)用很方便。因為讓電腦去控制一個可調(diào)電阻是比較困難的,而且可調(diào)電阻還有模擬電路固有的不穩(wěn)定問題。
掃碼關(guān)注我們
傳真:0755-82591176
郵箱:vicky@yingtexin.net
地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤豐創(chuàng)業(yè)園A棟2樓A08