- A fork of Damien's program.

Dependencies:   WebSocketClient WiflyInterface mbed messages

Fork of BatteryModelTester by Damien Frost

Revision:
10:e8b66718a103
Child:
11:ce00133ab2f4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/pwm.cpp	Tue Nov 29 15:02:01 2016 +0000
@@ -0,0 +1,380 @@
+// **************
+// * 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;
+}
+
+