![](/media/cache/profiles/c19a46c712c75b70a137f619a0916436.jpg.50x50_q85.png)
Skeleton program for Federico's 4YP project.
Dependencies: WebSocketClient WiflyInterface mbed messages
Fork of IoT_Ex by
source/pwm.cpp
- Committer:
- defrost
- Date:
- 2016-11-29
- Revision:
- 10:e8b66718a103
File content as of revision 10:e8b66718a103:
// ************** // * iQ_PWM.cpp * // ************** // // Created: 2015/03/19 // By: Damien Frost #include "math.h" #include "mbed.h" #include "globals.h" #include "pwm.h" #include "ADC.h" #include "Commands.h" #define DEBUG #define INFOMESSAGES #define WARNMESSAGES #define ERRMESSAGES #define FUNCNAME "PWM" #include "messages.h" // Configures the PWM for use, using an initial duty cycle and period in us. void ConfigurePWM(float duty_us, float period_us){ unsigned int value; float newVal; // Ensure power is turned on // Grabbed from lines 54-57 of analogin_api.h, modified for PWM // This turns on the clock to Ports A, B, and C RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN; // This turns on the clock to the Time 1: RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // Set the GPIO Ports properly: // PWM1 is connected to PA_8 // PWM1N is connected to PA_7 // Set the PWM outputs to general output pins: // This sets the PA_7 and PA_8 pins to Alternate Function Pins value = 0x8000 + 0x20000; GPIOA->MODER |= value; // Set the PWM outputs to high speed: value = 0xC000 + 0x30000; GPIOA->OSPEEDR |= value; // Set PWM as outputs to the pins: value = GPIOA->AFR[1]; // Reset the lowest four bits: value &= 0xFFFFFFF0; // Configure PA_8 to AF: value |= 0x1; GPIOA->AFR[1] = value; value = GPIOA->AFR[0]; // Reset the the 4 MSB: value &= 0x0FFFFFFF; // Configure PA_7 to AF: value |= 0x10000000; GPIOA->AFR[0] = value; // Set pull down resistors to PWM outputs: value = GPIOA->PUPDR; // Clear the bits: value &= ~(GPIO_PUPDR_PUPDR7 | GPIO_PUPDR_PUPDR8); // Set to pull down: value |= GPIO_PUPDR_PUPDR7_1 | GPIO_PUPDR_PUPDR8_1; // Set the register: GPIOA ->PUPDR = value; // Set the prescale value to 1: TIM1->PSC = 0; // *** TIM1 control register 1: TIMx_CR1 *** value = 0; // [9:8] Set CKD bits to zero for clock division of 1 // [7] TIMx_ARR register is buffered, set the ARPE bit to 1: value |= TIM_CR1_ARPE; // [6:5] Set CMS bits to zero for edge aligned mode // [6:5] Set CMS bits to 10 for Center Aligned mode 2, up down mode with flags set when counter reaches the top. //value |= TIM_CR1_CMS_1; // [4] Set DIR bit to zero for upcounting // [3] Set OPM bit to zero so that the counter is not stopped at update event // [2] Set URS bit to zero so that anything can create an interrupt // [1] Set UDIS bit to zero to generate an update event // [0] Set the CEN bit to zero to disable the counter // * Set the TIMx_CR1 Register: * TIM1->CR1 |= value; // *** TIM1 control register 2: TIMx_CR2 *** value = 0; // [14] Set OIS4 bit to zero, the idle state of OC4 output // [13] Set OIS3N bit to zero, the idle state of OC3N output // [12] Set OIS3 bit to zero, the idle state of OC3 output // [11] Set OIS2N bit to zero, the idle state of OC2N output // [10] Set OIS2 bit to zero, the idle state of OC2 output // [9] Set OIS1N bit to zero, the idle state of OC1N output // [8] Set OIS1 bit to zero, the idle state of OC1 output // [7] Set TI1S bit to zero, connecting only CH1 pin to TI1 input // [6:4] Set to 111: The OC4REF signal is used as trigger output (TRGO) // value |= TIM_CR2_MMS_2 | TIM_CR2_MMS_1 | TIM_CR2_MMS_0; //value |= TIM_CR2_MMS_1 | TIM_CR2_MMS_0; // [3] Set CCDS bit to zero, request sent when CCx event occurs // [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 //value |= 0x4; // [0] Set CCPC bit to 1, CCxE, CCxNE and OCxM are update on a commutation event, or rising edge on TRGI //value |= 0x1; // * Set the TIMx_CR2 Register: * TIM1->CR2 = value; // *** TIM1 Auto Reload Register: ARR *** value = 0; // [15:0] Set ARR bits to the frequency to be loaded in: newVal = ceil(period_us/PWMSTEP_US); value = (unsigned int) newVal; // * Set the TIMx_ARR Register: TIM1->ARR = value; // *** TIM1 capture/compare register 1: CCR1 *** value = 0; // [15:0] Set the capture compare value to the duty cycle: newVal = ceil(duty_us/PWMSTEP_US); value = (unsigned int) newVal; // * Set the TIMx_CCR1 Register: TIM1->CCR1 = value; // *** TIM1 capture/compare register 4: CCR4 *** value = 0; // [15:0] Set the capture compare value to the value at which the PWM interrupt should be triggered: newVal = 0; value = (unsigned int) newVal; // * Set the TIMx_CCR4 Register: TIM1->CCR4 = 0; // *** TIM1 capture/compare mode register 2: CCMR2 value = 0; // [15] Set OC4CE bit to 0, OC4Ref is not affected by the ETRF input // [14-12] Set the OC4M bits to '110', PWM mode 1, which is what we want I think. value |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1; // [11] Set the OC4PE bit to 1, meaning read/write operations to the preload event require an update event. value |= TIM_CCMR2_OC4PE; // [10] Set the OC4FE bit to 0, the output compare fast enable is disabled // [9:8] Set the CC4S bits to 0, the channel is configured as an output. // * Set the TIMx_CCMR2 Register: * TIM1->CCMR2 = value; // *** TIM1 capture/compare mode register 1: CCMR1 value = 0; // [7] Set OC1CE bit to 0, OC1Ref is not affected by the ETRF input // [6-4] Set the OC1M bits to '110', PWM mode 1, which is what we want I think. value |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1; // [3] Set the OC1PE bit to 1, meaning read/write operations to the preload event require an update event. value |= TIM_CCMR1_OC1PE; // [2] Set the OC1FE bit to 0, the output compare fast enable is disabled // [1:0] Set the CC1S bits to 0, the channel is configured as an output. // * Set the TIMx_CCMR1 Register: * TIM1->CCMR1 = value; // *** TIM1 capture/compare enable register: CCER value = 0; // [15:4] - Don't care: // [3] Set CC1NP bit to 1 for active low. ( value |= TIM_CCER_CC1NP; // [2] Set CC1NE bit to 0, to de-activate the OC1N signal // value |= 0x4; // [1] Set the CC1P bit to 1 for active low. value |= TIM_CCER_CC1P; // [0] Set the CC1E bit to 1, to de-activate the OC1 signal // value |= 0x1; // * Set the TIM1_CCER Register: * TIM1->CCER = value; // *** TIM1 break and dead-time register: BDTR value = 0; // [15] Set MOE bit to 1 to enable the OC and OCN outputs value |= 0x8000; // [11] Set the OSSR bit such that the ouputs are forced to their idle mode when not running //value |= TIM_BDTR_OSSR; // [10] Set OSSI bit such that the outputs are forced to their idle mode when MOE = 0 value |= TIM_BDTR_OSSI; // * Set the TIM1_BDTR register: TIM1->BDTR = value; // *** TIM1 DMA/Interrupt enable register: DIER value = 0; // [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. value |= TIM_DIER_CC4IE; // Set the TIM1_DIER register: TIM1->DIER |= value; // Set the UG bit in the EGR register to kick things off: value = 3; TIM1->EGR = value; // Print a message saying you are done: INFO("PWM configuration complete!"); #ifdef DEBUG_PWM // Debugging Code: DBG("TIM1 Registers:"); DBG("The CCMR1 Register reads: %d", TIM1->CCMR1); DBG("The CR1 Register reads: %d", TIM1->CR1); DBG("The CR2 Register reads: %d", TIM1->CR2); DBG("The ARR Register reads: %d", TIM1->ARR); DBG("The CCR1 Register reads: %d", TIM1->CCR1); DBG("The CCER Register reads: %d", TIM1->CCER); DBG("The BDTR Register reads: %d", TIM1->BDTR); DBG("The EGR Register reads: %d", TIM1->EGR); DBG("The SMCR Register reads: %d", TIM1->SMCR); DBG("The PSC Register reads: %d", TIM1->PSC); DBG("The GPIOA Registers:\n\r"); DBG("The MODER Register reads: %d", GPIOA->MODER); DBG("The OSPEEDR Register reads: %d", GPIOA->OSPEEDR); DBG("The AFR[0] Register reads: %d", GPIOA->AFR[0]); DBG("The AFR[1] Register reads: %d", GPIOA->AFR[1]); DBG("Clock Registers:"); DBG("The CFGR Register reads: %d", RCC->CFGR); #endif INFO("TIM1 Interrupt Priority: %d", NVIC_GetPriority(TIM1_CC_IRQn)); INFO("UART1 Interrupt Priority: %d", NVIC_GetPriority(USART1_IRQn)); INFO("UART2 Interrupt Priority: %d", NVIC_GetPriority(USART2_IRQn)); INFO("UART6 Interrupt Priority: %d", NVIC_GetPriority(USART6_IRQn)); // Configure the interrupt: NVIC_SetVector(TIM1_CC_IRQn, (uint32_t)&TIM1_CC_IRQHandler); NVIC_SetPriority(TIM1_CC_IRQn, PWMHIGHPRIORITY); NVIC_EnableIRQ(TIM1_CC_IRQn); return; } void TIM1_CC_IRQHandler(void){ // States of the PWM module: /* #define PWMST_SETUPCONV 0 #define PWMST_STARTCONV 1 #define PWMST_RUNSCSKDC 2 #define PWMST_CALCNEWPH 3 #define PWMST_SETNEWPH 4 #define PWMST_SHIFT 5 #define PWMST_SAMPLECHTROUGH 6 #define PWMST_WAITSAMPLETROUGH 7 #define PWMST_WAIT 8 #define PWMST_SAMPLECHHILL 9 #define PWMST_WAITSAMPLEHILL 10 #define PWMST_MAXST 11 */ if(((TIM1->SR & TIM_SR_CC4IF) > 0)&&(IotStatus.CheckFlag(SS_PWMOVERRUNFLAG) == false)){ // Block any other interrupts from occuring: IotStatus.SetFlag(SS_PWMOVERRUNFLAG); db = 1; // Sample the ADCs: VoltageMeasurement = VoltageSensor.read(); CurrentMeasurement = CurrentSensor.read(); db = 0; // Set battery model inputs: // Set a debug pin high: db = 1; // Probe this pin to see how long the battery model algorithm takes to run. // Run battery model here: // Clear the debug pin: db = 0; // Read battery model outputs: // Clear the flag: IotStatus.ClearFlag(SS_PWMOVERRUNFLAG); TIM1->SR &= (~TIM_SR_CC4IF); } return; } // Set a new duty cycle: float SetDuty_us(float duty_us){ unsigned int value; float newVal; float temp = (duty_us/PwmPeriod_us); newVal = ceil(duty_us/PWMSTEP_US); value = (unsigned int) newVal; // Disable the Update Event: SETUDIS; // * Set the TIMx_CCR1 Register: TIM1->CCR1 = value; // Set the CCR4 Register to the maximum value minus CH4SHIFT. This ensures the high speed ADC is in sync with the PWM module. TIM1->CCR4 = 0; // Re-enable the update event CLEARUDIS; return temp*PwmPeriod_us; } // Turns on and off the PWM: void TurnOnPWM(bool trueForOn){ if(trueForOn){ // Enable the outputs: DBG("Enabling outputs..."); TIM1->CCER |= TIM_CCER_CC1E | TIM_CCER_CC1NE; // Turn on the gating: DBG("Turning on gating...:"); TIM1->CR1 |= TIM_CR1_CEN; DBG("PWM on."); }else{ // Turn off the gating: TIM1->CR1 &= ~TIM_CR1_CEN; // Disable the outputs: TIM1->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC1NE); } return; } void SetPWMPeriodAndDuty(int pwmper){ // This functions sets the PWM period by first disabling updates to the PWM module // It also scales the duty cycle register, so the duty cycle is the SAME. // Disable the UEV event in the PWM module: SETUDIS; // Set the new period: TIM1->ARR = (unsigned int)(PwmPeriod_us/PWMSTEP_US); // Set the new duty cycle: TIM1->CCR1 = ((unsigned int)(Duty_us/PwmPeriod_us * TIM1->ARR)); // Set the CCR4 Register to the maximum value minus CH4SHIFT. This ensures the high speed ADC is in sync with the PWM module. TIM1->CCR4 = 0; // Re-enable the UEV event: CLEARUDIS; // Done! return; } void SetPWMPeriodAndDuty_us(float period){ // This functions sets the PWM period by first disabling updates to the PWM module // It also scales the duty cycle register, so the duty cycle is the SAME. float PwmSteps = ((float)period/(float)PWMSTEP_US); // Disable the UEV event in the PWM module: SETUDIS; // Set the new period: if(PwmSteps > PWMARRMAX){ PwmSteps = PWMARRMAX; WARN("Maximum PWM Period Reached."); } TIM1->ARR = (unsigned int)(PwmSteps); DBG("TIM1->ARR: %d", TIM1->ARR); DBG("period: %.3f", period); DBG("PWMSTEP: %.3f", (float) PWMSTEP_US); // Set the new duty cycle: TIM1->CCR1 = ((unsigned int)(Duty_us/period * TIM1->ARR)); // Set the CCR4 Register to the maximum value minus CH4SHIFT. This ensures the high speed ADC is in sync with the PWM module. TIM1->CCR4 = 0; // Re-enable the UEV event: CLEARUDIS; // Done! return; }