Skeleton program for Federico's 4YP project.

Dependencies:   WebSocketClient WiflyInterface mbed messages

Fork of IoT_Ex by Damien Frost

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pwm.cpp Source File

pwm.cpp

00001 // **************
00002 // * iQ_PWM.cpp *
00003 // **************
00004 //
00005 // Created: 2015/03/19
00006 // By: Damien Frost
00007 
00008 #include "math.h"
00009 #include "mbed.h"
00010 #include "globals.h "
00011 #include "pwm.h"
00012 #include "ADC.h "
00013 #include "Commands.h "
00014 
00015 #define DEBUG
00016 #define INFOMESSAGES
00017 #define WARNMESSAGES
00018 #define ERRMESSAGES
00019 #define FUNCNAME "PWM"
00020 #include "messages.h"
00021 
00022 
00023 // Configures the PWM for use, using an initial duty cycle and period in us.
00024 void ConfigurePWM(float duty_us, float period_us){
00025     unsigned int value;
00026     float newVal;
00027     
00028     // Ensure power is turned on
00029     // Grabbed from lines 54-57 of analogin_api.h, modified for PWM
00030     // This turns on the clock to Ports A, B, and C
00031     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
00032     // This turns on the clock to the Time 1:
00033     RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
00034     
00035     // Set the GPIO Ports properly:
00036     // PWM1 is connected to  PA_8
00037     // PWM1N is connected to PA_7
00038     
00039     // Set the PWM outputs to general output pins:
00040     // This sets the PA_7 and PA_8 pins to Alternate Function Pins
00041     value = 0x8000 + 0x20000;
00042     GPIOA->MODER |= value;
00043     
00044     // Set the PWM outputs to high speed:
00045     value = 0xC000 + 0x30000;
00046     GPIOA->OSPEEDR |= value;
00047     
00048     // Set PWM as outputs to the pins:
00049     value = GPIOA->AFR[1];
00050     // Reset the lowest four bits:
00051     value &= 0xFFFFFFF0;
00052     // Configure PA_8 to AF:
00053     value |= 0x1;
00054     GPIOA->AFR[1] = value;
00055     
00056     value = GPIOA->AFR[0];
00057     // Reset the the 4 MSB:
00058     value &= 0x0FFFFFFF;
00059     // Configure PA_7 to AF:
00060     value |= 0x10000000;
00061     GPIOA->AFR[0] = value;
00062     
00063     // Set pull down resistors to PWM outputs:
00064     value = GPIOA->PUPDR;
00065     // Clear the bits:
00066     value &= ~(GPIO_PUPDR_PUPDR7 | GPIO_PUPDR_PUPDR8);
00067     // Set to pull down:
00068     value |= GPIO_PUPDR_PUPDR7_1 | GPIO_PUPDR_PUPDR8_1;
00069     // Set the register:
00070     GPIOA ->PUPDR = value;
00071     
00072     // Set the prescale value to 1:
00073     TIM1->PSC = 0;
00074     
00075     // *** TIM1 control register 1: TIMx_CR1 ***
00076     value = 0;
00077     // [9:8] Set CKD bits to zero for clock division of 1
00078     // [7] TIMx_ARR register is buffered, set the ARPE bit to 1:
00079     value |= TIM_CR1_ARPE;
00080     // [6:5] Set CMS bits to zero for edge aligned mode
00081     // [6:5] Set CMS bits to 10 for Center Aligned mode 2, up down mode with flags set when counter reaches the top.
00082     //value |= TIM_CR1_CMS_1;
00083     // [4] Set DIR bit to zero for upcounting
00084     // [3] Set OPM bit to zero so that the counter is not stopped at update event
00085     // [2] Set URS bit to zero so that anything can create an interrupt
00086     // [1] Set UDIS bit to zero to generate an update event
00087     // [0] Set the CEN bit to zero to disable the counter
00088     // * Set the TIMx_CR1 Register: *
00089     TIM1->CR1 |= value;
00090     
00091     // *** TIM1 control register 2: TIMx_CR2 ***
00092     value  = 0;
00093     // [14] Set OIS4 bit to zero, the idle state of OC4 output
00094     // [13] Set OIS3N bit to zero, the idle state of OC3N output
00095     // [12] Set OIS3 bit to zero, the idle state of OC3 output
00096     // [11] Set OIS2N bit to zero, the idle state of OC2N output
00097     // [10] Set OIS2 bit to zero, the idle state of OC2 output
00098     // [9] Set OIS1N bit to zero, the idle state of OC1N output
00099     // [8] Set OIS1 bit to zero, the idle state of OC1 output
00100     // [7] Set TI1S bit to zero, connecting only CH1 pin to TI1 input
00101     // [6:4] Set to 111: The OC4REF signal is used as trigger output (TRGO)
00102     // value |= TIM_CR2_MMS_2 | TIM_CR2_MMS_1 | TIM_CR2_MMS_0;
00103     //value |= TIM_CR2_MMS_1 | TIM_CR2_MMS_0;
00104     // [3] Set CCDS bit to zero, request sent when CCx event occurs
00105     // [2] Set CCUS bit to 1, capture/compare control bits are updated by setting the COMG bit or when a rising edge occurs on TRGI
00106     //value |= 0x4;
00107     // [0] Set CCPC bit to 1, CCxE, CCxNE and OCxM are update on a commutation event, or rising edge on TRGI
00108     //value |= 0x1;
00109     // * Set the TIMx_CR2 Register: *
00110     TIM1->CR2 = value;
00111     
00112     // *** TIM1 Auto Reload Register: ARR ***
00113     value = 0;
00114     // [15:0] Set ARR bits to the frequency to be loaded in:
00115     newVal = ceil(period_us/PWMSTEP_US);
00116     value = (unsigned int) newVal;
00117     // * Set the TIMx_ARR Register:
00118     TIM1->ARR = value;
00119     
00120     // *** TIM1 capture/compare register 1: CCR1 ***
00121     value = 0;
00122     // [15:0] Set the capture compare value to the duty cycle:
00123     newVal = ceil(duty_us/PWMSTEP_US);
00124     value = (unsigned int) newVal;
00125     // * Set the TIMx_CCR1 Register:
00126     TIM1->CCR1 = value;
00127     
00128     // *** TIM1 capture/compare register 4: CCR4 ***
00129     value = 0;
00130     // [15:0] Set the capture compare value to the value at which the PWM interrupt should be triggered:
00131     newVal = 0;
00132     value = (unsigned int) newVal;
00133     // * Set the TIMx_CCR4 Register:
00134     TIM1->CCR4 = 0;
00135     
00136     // *** TIM1 capture/compare mode register 2: CCMR2
00137     value = 0;
00138     // [15] Set OC4CE bit to 0, OC4Ref is not affected by the ETRF input
00139     // [14-12] Set the OC4M bits to '110', PWM mode 1, which is what we want I think.
00140     value |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1;
00141     // [11] Set the OC4PE bit to 1, meaning read/write operations to the preload event require an update event.
00142     value |= TIM_CCMR2_OC4PE;
00143     // [10] Set the OC4FE bit to 0, the output compare fast enable is disabled
00144     // [9:8] Set the CC4S bits to 0, the channel is configured as an output.
00145     // * Set the TIMx_CCMR2 Register: *
00146     TIM1->CCMR2 = value;
00147     
00148     // *** TIM1 capture/compare mode register 1: CCMR1
00149     value = 0;
00150     // [7] Set OC1CE bit to 0, OC1Ref is not affected by the ETRF input
00151     // [6-4] Set the OC1M bits to '110', PWM mode 1, which is what we want I think.
00152     value |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;
00153     // [3] Set the OC1PE bit to 1, meaning read/write operations to the preload event require an update event.
00154     value |= TIM_CCMR1_OC1PE;
00155     // [2] Set the OC1FE bit to 0, the output compare fast enable is disabled
00156     // [1:0] Set the CC1S bits to 0, the channel is configured as an output.
00157     // * Set the TIMx_CCMR1 Register: *
00158     TIM1->CCMR1 = value;
00159     
00160     // *** TIM1 capture/compare enable register: CCER
00161     value = 0;
00162     // [15:4] - Don't care:
00163     // [3] Set CC1NP bit to 1 for active low. (
00164     value |= TIM_CCER_CC1NP;
00165     // [2] Set CC1NE bit to 0, to de-activate the OC1N signal
00166     // value |= 0x4;
00167     // [1] Set the CC1P bit to 1 for active low.
00168     value |= TIM_CCER_CC1P;
00169     // [0] Set the CC1E bit to 1, to de-activate the OC1 signal
00170     // value |= 0x1;
00171     // * Set the TIM1_CCER Register: *
00172     TIM1->CCER = value;
00173     
00174     // *** TIM1 break and dead-time register: BDTR
00175     value = 0;
00176     // [15] Set MOE bit to 1 to enable the OC and OCN outputs
00177     value |= 0x8000;
00178     // [11] Set the OSSR bit such that the ouputs are forced to their idle mode when not running
00179     //value |= TIM_BDTR_OSSR;
00180     // [10] Set OSSI bit such that the outputs are forced to their idle mode when MOE = 0
00181     value |= TIM_BDTR_OSSI;
00182     // * Set the TIM1_BDTR register:
00183     TIM1->BDTR = value;
00184     
00185     // *** TIM1 DMA/Interrupt enable register: DIER
00186     value = 0;
00187     // [2] Set the CC1IE bit to 1, to trigger an interrupt when counter 1 has a match - which should be half way through the duty cycle.
00188     value |= TIM_DIER_CC4IE;
00189     // Set the TIM1_DIER register:
00190     TIM1->DIER |= value;
00191     
00192     // Set the UG bit in the EGR register to kick things off:
00193     value = 3;
00194     TIM1->EGR = value;
00195     
00196     // Print a message saying you are done:
00197     INFO("PWM configuration complete!");
00198     
00199     #ifdef DEBUG_PWM
00200         // Debugging Code:
00201         DBG("TIM1 Registers:");
00202         DBG("The CCMR1 Register reads: %d", TIM1->CCMR1);
00203         DBG("The CR1 Register reads: %d", TIM1->CR1);
00204         DBG("The CR2 Register reads: %d", TIM1->CR2);
00205         DBG("The ARR Register reads: %d", TIM1->ARR);
00206         DBG("The CCR1 Register reads: %d", TIM1->CCR1);
00207         DBG("The CCER Register reads: %d", TIM1->CCER);
00208         DBG("The BDTR Register reads: %d", TIM1->BDTR);
00209         DBG("The EGR Register reads: %d", TIM1->EGR);
00210         DBG("The SMCR Register reads: %d", TIM1->SMCR);
00211         DBG("The PSC Register reads: %d", TIM1->PSC);
00212         DBG("The GPIOA Registers:\n\r");
00213         DBG("The MODER Register reads: %d", GPIOA->MODER);
00214         DBG("The OSPEEDR Register reads: %d", GPIOA->OSPEEDR);
00215         DBG("The AFR[0] Register reads: %d", GPIOA->AFR[0]);
00216         DBG("The AFR[1] Register reads: %d", GPIOA->AFR[1]);
00217         DBG("Clock Registers:");
00218         DBG("The CFGR Register reads: %d", RCC->CFGR);
00219         
00220     #endif
00221     
00222     INFO("TIM1 Interrupt Priority: %d", NVIC_GetPriority(TIM1_CC_IRQn));
00223     INFO("UART1 Interrupt Priority: %d", NVIC_GetPriority(USART1_IRQn));
00224     INFO("UART2 Interrupt Priority: %d", NVIC_GetPriority(USART2_IRQn));
00225     INFO("UART6 Interrupt Priority: %d", NVIC_GetPriority(USART6_IRQn));
00226     
00227     // Configure the interrupt:
00228     NVIC_SetVector(TIM1_CC_IRQn, (uint32_t)&TIM1_CC_IRQHandler);
00229     NVIC_SetPriority(TIM1_CC_IRQn, PWMHIGHPRIORITY);
00230     NVIC_EnableIRQ(TIM1_CC_IRQn);
00231     
00232     
00233 
00234     return;
00235 }
00236 
00237 void TIM1_CC_IRQHandler(void){
00238     
00239     // States of the PWM module:
00240     /*
00241     #define PWMST_SETUPCONV 0
00242     #define PWMST_STARTCONV 1
00243     #define PWMST_RUNSCSKDC 2
00244     #define PWMST_CALCNEWPH 3
00245     #define PWMST_SETNEWPH  4
00246     #define PWMST_SHIFT     5
00247     #define PWMST_SAMPLECHTROUGH    6
00248     #define PWMST_WAITSAMPLETROUGH  7    
00249     #define PWMST_WAIT              8
00250     #define PWMST_SAMPLECHHILL      9
00251     #define PWMST_WAITSAMPLEHILL    10
00252     #define PWMST_MAXST             11
00253     */
00254     
00255     if(((TIM1->SR & TIM_SR_CC4IF) > 0)&&(IotStatus.CheckFlag(SS_PWMOVERRUNFLAG) == false)){
00256         // Block any other interrupts from occuring:
00257         IotStatus.SetFlag(SS_PWMOVERRUNFLAG);
00258         
00259         db = 1;
00260         
00261         // Sample the ADCs:
00262         VoltageMeasurement = VoltageSensor.read();
00263         CurrentMeasurement = CurrentSensor.read();
00264         db = 0;
00265         // Set battery model inputs:
00266         
00267         
00268         // Set a debug pin high:
00269         db = 1; // Probe this pin to see how long the battery model algorithm takes to run.
00270         
00271         // Run battery model here:
00272         
00273         
00274         // Clear the debug pin:
00275         db = 0;
00276         
00277         // Read battery model outputs:
00278         
00279         
00280         
00281         // Clear the flag:
00282         IotStatus.ClearFlag(SS_PWMOVERRUNFLAG);
00283         TIM1->SR &= (~TIM_SR_CC4IF);
00284     }
00285     
00286     
00287     return;
00288 }
00289 
00290     
00291 
00292 
00293 
00294 // Set a new duty cycle:
00295 float SetDuty_us(float duty_us){
00296     unsigned int value;
00297     float newVal;
00298     float temp = (duty_us/PwmPeriod_us);
00299         
00300     newVal = ceil(duty_us/PWMSTEP_US);
00301     value = (unsigned int) newVal;
00302     // Disable the Update Event:
00303     SETUDIS;
00304     // * Set the TIMx_CCR1 Register:
00305     TIM1->CCR1 = value;
00306     // Set the CCR4 Register to the maximum value minus CH4SHIFT. This ensures the high speed ADC is in sync with the PWM module.
00307     TIM1->CCR4 = 0;
00308     // Re-enable the update event
00309     CLEARUDIS;
00310     return temp*PwmPeriod_us;
00311 }
00312 
00313 
00314 // Turns on and off the PWM:
00315 void TurnOnPWM(bool trueForOn){
00316     if(trueForOn){ 
00317         // Enable the outputs:
00318         DBG("Enabling outputs...");
00319         TIM1->CCER |= TIM_CCER_CC1E | TIM_CCER_CC1NE;
00320         // Turn on the gating:
00321         DBG("Turning on gating...:");
00322         TIM1->CR1 |= TIM_CR1_CEN;
00323         DBG("PWM on.");
00324         
00325     }else{
00326         // Turn off the gating:
00327         TIM1->CR1 &= ~TIM_CR1_CEN;
00328         // Disable the outputs:
00329         TIM1->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC1NE);
00330     }
00331     return;
00332 }
00333         
00334 
00335         
00336 void SetPWMPeriodAndDuty(int pwmper){
00337     // This functions sets the PWM period by first disabling updates to the PWM module
00338     // It also scales the duty cycle register, so the duty cycle is the SAME.
00339     
00340     // Disable the UEV event in the PWM module:
00341     SETUDIS;
00342     // Set the new period:
00343     TIM1->ARR = (unsigned int)(PwmPeriod_us/PWMSTEP_US);
00344     // Set the new duty cycle:
00345     TIM1->CCR1 = ((unsigned int)(Duty_us/PwmPeriod_us * TIM1->ARR));
00346     // Set the CCR4 Register to the maximum value minus CH4SHIFT. This ensures the high speed ADC is in sync with the PWM module.
00347     TIM1->CCR4 = 0;
00348     // Re-enable the UEV event:
00349     CLEARUDIS;
00350     // Done!
00351     return;
00352 }
00353 
00354 void SetPWMPeriodAndDuty_us(float period){
00355     // This functions sets the PWM period by first disabling updates to the PWM module
00356     // It also scales the duty cycle register, so the duty cycle is the SAME.
00357     float PwmSteps = ((float)period/(float)PWMSTEP_US);
00358     
00359     // Disable the UEV event in the PWM module:
00360     SETUDIS;
00361     // Set the new period:
00362     if(PwmSteps > PWMARRMAX){
00363         PwmSteps = PWMARRMAX;
00364         WARN("Maximum PWM Period Reached.");
00365     }
00366     TIM1->ARR = (unsigned int)(PwmSteps);
00367     DBG("TIM1->ARR: %d", TIM1->ARR);
00368     DBG("period: %.3f", period);
00369     DBG("PWMSTEP: %.3f", (float) PWMSTEP_US);
00370     // Set the new duty cycle:
00371     TIM1->CCR1 = ((unsigned int)(Duty_us/period * TIM1->ARR));
00372     // Set the CCR4 Register to the maximum value minus CH4SHIFT. This ensures the high speed ADC is in sync with the PWM module.
00373     TIM1->CCR4 = 0;
00374     // Re-enable the UEV event:
00375     CLEARUDIS;
00376     // Done!
00377     return;
00378 }
00379 
00380