![](/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
Revision 10:e8b66718a103, committed 2016-11-29
- Comitter:
- defrost
- Date:
- Tue Nov 29 15:02:01 2016 +0000
- Parent:
- 9:9b6a54e63942
- Commit message:
- - Works
Changed in this revision
--- a/headers/Commands.h Tue Nov 29 12:22:14 2016 +0000 +++ b/headers/Commands.h Tue Nov 29 15:02:01 2016 +0000 @@ -74,13 +74,13 @@ #define SF_SERVERCONNECTED BIT0 #define SF_AUTOCONNECT BIT1 // Set this flag to automatically start connecting to the websocket server #define SF_WIRELESSCONNECTED BIT2 // Flag to indicate whether or not the wireless network is connected +#define SS_PWMOVERRUNFLAG BIT3 // Flag to indicate when the PWM interrutp is still running // Wifi Commands -#define NO_WIFI_CMD 0 -#define CV_LED_WIFI_CMD 1 +#define NO_WIFI_CMD 0 +#define CV_LED_WIFI_CMD 1 +#define CV_PWM_PERIOD_US_CMD 2 -// Change variable commands: -#define CV_LED 1 extern StatusReg IotStatus;
--- a/headers/globals.h Tue Nov 29 12:22:14 2016 +0000 +++ b/headers/globals.h Tue Nov 29 15:02:01 2016 +0000 @@ -48,6 +48,10 @@ #define WS_PORT 4444 #define SERVER_IP "192.168.1.99" +// Pin assignments +#define VOLTAGE_SENSOR_PIN PA_0 +#define CURRENT_SENSOR_PIN PA_1 + // Hardware: extern Serial pc; extern InterruptIn UIBut1; @@ -59,8 +63,15 @@ extern int SendCounter; extern int IoT_ID; extern float TempSensor; +extern float VoltageMeasurement; +extern float CurrentMeasurement; +extern float PwmPeriod_us; +extern float Duty_us; extern char* wifissid; extern char* wifipassword; +extern DigitalOut db; +extern AnalogIn VoltageSensor; +extern AnalogIn CurrentSensor; // Communication: extern WiflyInterface eth;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/headers/pwm.h Tue Nov 29 15:02:01 2016 +0000 @@ -0,0 +1,96 @@ +// ************ +// * iQ_PWM.h * +// ************ +// +// Created: 2015/03/19 +// By: Damien Frost +// +// Description: +// This file provides all of the functions used to configure the +// high speed PWM module of the STM32F401RE microcontroller. + +#ifndef PWWM_H +#define PWWM_H + +#define SETUDIS TIM1->CR1 |= TIM_CR1_UDIS +#define CLEARUDIS TIM1->CR1 &= ~TIM_CR1_UDIS +#define PWMHIGHPRIORITY 0 +#define PWMLOWPRIORITY 1 + +#define PS_RESET 0 +#define PS_SHIFT 1 + +#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_SKIPONECYCLE 11 +#define PWMST_MAXST 12 + +#define PWMSTEP_US (10.0f/839.0f) +#define TS (PWMPER_US/1000000) +#define PWMARRMAX (65535) + + + +// Global variables: +extern float CurrentPhaseShift; +extern unsigned int OneCycleSkipExitState; +extern void (*const pwmsm_StateFunction [PWMST_MAXST]) (void); + +// Function prototypes: + +// Configures the PWM for use, using an initial duty cycle of 'duty' +void ConfigurePWM(float duty_us, float period_us); + +// PWM ISR, used fo rdebugging purposes: +void TIM1_CC_IRQHandler(void); + +// Configures the Dead time of the PWM, using an initial dead time of 'dt': +void ConfigureDeadTime(float dt_ns); + +// Set duty by specifying the duty cycle in us: +float SetDuty_us(float duty_us); + +// Set a new duty cycle by specifying it as a number between 0 and 1: +float SetDuty(float duty); + +// This function checks to make sure the duty cycle is within the min and max constraints, it is called by the SetDuty functions. +float CheckMinMaxDuty(float duty); + +// Turn on the the PWM signal: +void TurnOnPWM(bool trueForOn); + +// Apply a phase shift: +int PhaseShift(unsigned int cmd, float phase); + +// Set a new PWM Period: +void SetPWMPeriodAndDuty(int pwmper); +void SetPWMPeriodAndDuty_us(float period); + +// Set the time at which the PWM isr fires: +void SetISRTime(unsigned int isrtime); + +// PWM State Machine functions: +void pwmsm_SetupConv(void); // PWMST_SETUPCONV 0 +void pwmsm_StartConv(void); // PWMST_STARTCONV 1 +void pwmsm_RunScskdc(void); // PWMST_RUNSCSKDC 2 +void pwmsm_CalcNewPh(void); // PWMST_CALCNEWPH 3 +void pwmsm_SetNewPh(void); // PWMST_SETNEWPH 4 +void pwmsm_Shift(void); // PWMST_SHIFT 5 +void pwmsm_SampleChTrough(void); // PWMST_SAMPLECHTROUGH 6 +void pwmsm_WaitSampleTrough(void); // PWMST_WAITSAMPLETROUGH 7 +void pwmsm_Wait(void); // PWMST_WAIT 8 +void pwmsm_SampleChHill(void); // PWMST_SAMPLECHHILL 9 +void pwmsm_WaitSampleHill(void); // PWMST_WAITSAMPLEHILL 10 +void pwmsm_SkipOneCycle(void); // PWMST_SKIPONECYCLE 11 +void SetupOneCycleSkip(unsigned int nextState); + +#endif /* PWWM_H */
--- a/main.cpp Tue Nov 29 12:22:14 2016 +0000 +++ b/main.cpp Tue Nov 29 15:02:01 2016 +0000 @@ -37,6 +37,7 @@ #include "Commands.h" #include "Websocket.h" #include "ADC.h" +#include "pwm.h" //#define DEBUG #define INFOMESSAGES @@ -73,12 +74,17 @@ // Connect to the wifi network. It will basically get stuck here until it // connects to the network. SetupNetwork(5000); - + // Configure the baud rate of the wifi shield: // This will make our wireless transmissions much faster. ws.setBaud(115200); wait(0.5f); + // Configure the PWM module: + ConfigurePWM(Duty_us, PwmPeriod_us); + // Turn on the PWM: + TurnOnPWM(true); + // Check to see we are connected to the network: if(IotStatus.CheckFlag(SF_WIRELESSCONNECTED)){ // Try to connect to the WebSocket server:
--- a/source/globals.cpp Tue Nov 29 12:22:14 2016 +0000 +++ b/source/globals.cpp Tue Nov 29 15:02:01 2016 +0000 @@ -32,8 +32,9 @@ #include "mbed.h" #include "globals.h" +#include "pwm.h" -//#define DEBUG +#define DEBUG #define INFOMESSAGES #define WARNMESSAGES #define ERRMESSAGES @@ -51,8 +52,16 @@ int SendCounter = 0; extern int IoT_ID = 0; float TempSensor = 0.0f; +float VoltageMeasurement = 0.0f; +float CurrentMeasurement = 0.0f; +float PwmPeriod_us = 20.0f; +float Duty_us = 10.0f; char* wifissid = "SC"; char* wifipassword = "smartcellshield"; +DigitalOut db(PC_10); +AnalogIn VoltageSensor(VOLTAGE_SENSOR_PIN); +AnalogIn CurrentSensor(CURRENT_SENSOR_PIN); + // Wifily interface declaration: WiflyInterface eth(D8, D2, D6, LED1, wifissid, wifipassword, WPA2); @@ -104,7 +113,7 @@ int intresult; if(IotStatus.CheckFlag(SF_SERVERCONNECTED)){ - sprintf(msg_buffer, "%d,%d,%.5f", IoT_ID, SendCounter,TempSensor); + sprintf(msg_buffer, "%d,%d,%.5f,%.5f,%.5f,%.5f", IoT_ID, SendCounter,TempSensor, VoltageMeasurement, CurrentMeasurement, PwmPeriod_us); INFO("Sending: %s", msg_buffer); // When this line is commented out, the mbed never tries to reconnect to the server after one try. SUPER. Keeping this here also uses precious CPU time intresult = ws.send(msg_buffer); }else{ @@ -170,6 +179,10 @@ // Get one more value: sscanf(msg_buffer2, "%f", value); } + if(*wifi_cmd == CV_PWM_PERIOD_US_CMD){ + // Get one more value: + sscanf(msg_buffer2, "%f", value); + } }else if(resp == -1){ // Connection to the server is lost: IotStatus.ClearFlag(SF_SERVERCONNECTED); @@ -185,13 +198,19 @@ void ModifyVariable(unsigned int wifi_var, float wifi_data){ // modifies something in the SCS Controller: switch(wifi_var){ - case CV_LED: + case CV_LED_WIFI_CMD: if(wifi_data > 0){ Led = 1; }else{ Led = 0; } break; + case CV_PWM_PERIOD_US_CMD: + DBG("wifi_data: %.3f", wifi_data); + PwmPeriod_us = wifi_data; + SetPWMPeriodAndDuty_us(PwmPeriod_us); + break; + default: break;
--- /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; +} + +