/**************************************************************************** Module PWM_Module.c Revision 1.0.1 Description This is a PWM file for ME218B Lab 8 for Team 3. Notes History When Who What/Why -------------- --- -------- 02/04/2019 bibit converted from lab 7 to lab 8 module 01/../2019 robbie lab 7 module 01/15/12 11:12 jec revisions for Gen2 framework 11/07/11 11:26 jec made the queue static 10/30/11 17:59 jec fixed references to CurrentEvent in RunTemplateSM() 10/23/11 18:20 jec began conversion from SMTemplate.c (02/20/07 rev) ****************************************************************************/ /*----------------------------- Include Files -----------------------------*/ /* include header files for this state machine as well as any machines at the next lower level in the hierarchy that are sub-machines to this machine */ #include "ES_Configure.h" #include "ES_Framework.h" #include "PWM_Module.h" #include "ES_Types.h" #include "termio.h" #include "BITDEFS.H" #include <stdint.h> #include <stdbool.h> #include "inc/hw_pwm.h" #include "inc/hw_gpio.h" #include "inc/hw_sysctl.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_nvic.h" #include "inc/hw_Timer.h" #include "driverlib/gpio.h" #include "driverlib/sysctl.h" #include "driverlib/pin_map.h" #include "driverlib/pwm.h" #include "GameplayHSM.h" /*----------------------------- Module Defines ----------------------------*/ // Clock is 40MHz..with 32x prescaling that is 125 KHz // 125 KHz is 1250000 ticks per second or 1250 ticks per milisecond #define PWMTicksPerMS 1250 #define BitsPerNibble 4 #define MsPerSecond 1000 #define PercentToDec 100 #define PWM0_GenA_Normal (PWM_0_GENA_ACTCMPAU_ONE | PWM_0_GENA_ACTCMPAD_ZERO) #define PWM0_GenB_Normal (PWM_0_GENB_ACTCMPBU_ONE | PWM_0_GENB_ACTCMPBD_ZERO) #define PWM1_GenA_Normal (PWM_1_GENA_ACTCMPAU_ONE | PWM_1_GENA_ACTCMPAD_ZERO) #define PWM1_GenB_Normal (PWM_1_GENB_ACTCMPBU_ONE | PWM_1_GENB_ACTCMPBD_ZERO) /*---------------------------- Module Functions ---------------------------*/ /* prototypes for private functions for this machine.They should be functions relevant to the behavior of this state machine */ static void Set0_DC(uint8_t PinNum); static void Set100_DC(uint8_t PinNum); static void RestoreDC(uint8_t PinNum); /*---------------------------- Module Variables ---------------------------*/ // everybody needs a state variable, you may need others as well. // type of state variable should match htat of enum in header file // Startup freq. of 200 Hz...(1/StartupFreq) * MsPerSecond; static uint32_t Startup_Period_ms = 5; static uint32_t Period_0; // ticks static uint32_t Duty_0; // percent static uint32_t CompareA; static uint32_t CompareB; // with the introduction of Gen2, we need a module level Priority var as well /*------------------------------ Module Code ------------------------------*/ //**************************************************************************/ /**************************************************************************** Function PWM_Init Parameters uint8_t (HowMany) Returns None Description Initializes PWM ports. Capable of doing 1 or 2 pins at PB6 and PB7. Notes PWM PORT SEQUENCE: NUM. 0 ----> PB6 M0PWM0 NUM. 1 ----> PB7 M0PWM1 Author - ****************************************************************************/ void PWM_Init(uint8_t NumPWM) { switch (NumPWM) { // We only want one PWM port. This corresponds to PB6 case 1: { // start by enabling the clock to the PWM Module (PWM0) HWREG(SYSCTL_RCGCPWM) |= SYSCTL_RCGCPWM_R0; // enable the clock to Port B HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R1; // make sure that Port B is initialized while ((HWREG(SYSCTL_PRGPIO) & SYSCTL_PRGPIO_R1) != BIT1HI) {} // Select the PWM clock as System Clock/32 HWREG(SYSCTL_RCC) = (HWREG(SYSCTL_RCC) & ~SYSCTL_RCC_PWMDIV_M) | (SYSCTL_RCC_USEPWMDIV | SYSCTL_RCC_PWMDIV_32); // make sure that the PWM module clock has gotten going while ((HWREG(SYSCTL_PRPWM) & SYSCTL_PRPWM_R0) != SYSCTL_PRPWM_R0) {} // disable the PWM while initializing HWREG(PWM0_BASE + PWM_O_0_CTL) = 0; // program generators to go to 1 at rising compare A/B, 0 on falling // compare A/B HWREG(PWM0_BASE + PWM_O_0_GENA) = PWM0_GenA_Normal; // Set the PWM period. Since we are counting both up & down, we // initialize the load register to 1/2 the desired total period. We // will also program the match compare registers to 1/2 the desired // high time Period_0 = Startup_Period_ms * PWMTicksPerMS; HWREG(PWM0_BASE + PWM_O_0_LOAD) = ((Period_0)) >> 1; // Set the initial Duty cycle on A to 50% by programming the compare // value to 1/2 the period to count up (or down). Technically, the // value to program should be Period/2 - DesiredHighTime/2, but since // the desired high time is 1/2 the period, we can skip the subtract Duty_0 = 50; HWREG(PWM0_BASE + PWM_O_0_CMPA) = HWREG(PWM0_BASE + PWM_O_0_LOAD) >> 1; HWREG(PWM0_BASE + PWM_O_ENABLE) |= (PWM_ENABLE_PWM0EN); // now configure the Port B pin to be PWM outputs // start by selecting the alternate function for PB6 HWREG(GPIO_PORTB_BASE + GPIO_O_AFSEL) |= (BIT6HI); // now choose to map PWM to those pins, this is a mux value of 4 that we // want to use for specifying the function on bits 6 HWREG(GPIO_PORTB_BASE + GPIO_O_PCTL) = (HWREG(GPIO_PORTB_BASE + GPIO_O_PCTL) & 0xf0ffffff) + (4 << (6 * BitsPerNibble)); // Enable pin 6 on Port B for digital I/O HWREG(GPIO_PORTB_BASE + GPIO_O_DEN) |= (BIT6HI); // make pin 6 on Port B an output HWREG(GPIO_PORTB_BASE + GPIO_O_DIR) |= (BIT6HI); // set the up/down count mode, enable the PWM generator and make // both generator updates locally synchronized to zero count HWREG(PWM0_BASE + PWM_O_0_CTL) = (PWM_0_CTL_MODE | PWM_0_CTL_ENABLE | PWM_0_CTL_GENAUPD_LS | PWM_0_CTL_GENBUPD_LS); } break; // We want 2 PWM ports. This corresponds to PB6 and PB7. case 2: { // start by enabling the clock to the PWM Module (PWM0) HWREG(SYSCTL_RCGCPWM) |= SYSCTL_RCGCPWM_R0; // enable the clock to Port B HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R1; // make sure that Port B is initialized while ((HWREG(SYSCTL_PRGPIO) & SYSCTL_PRGPIO_R1) != BIT1HI) {} // Select the PWM clock as System Clock/32 HWREG(SYSCTL_RCC) = (HWREG(SYSCTL_RCC) & ~SYSCTL_RCC_PWMDIV_M) | (SYSCTL_RCC_USEPWMDIV | SYSCTL_RCC_PWMDIV_32); // make sure that the PWM module clock has gotten going while ((HWREG(SYSCTL_PRPWM) & SYSCTL_PRPWM_R0) != SYSCTL_PRPWM_R0) {} // disable the PWM while initializing HWREG(PWM0_BASE + PWM_O_0_CTL) = 0; // program generators to go to 1 at rising compare A/B, 0 on falling // compare A/B HWREG(PWM0_BASE + PWM_O_0_GENA) = PWM0_GenA_Normal; HWREG(PWM0_BASE + PWM_O_0_GENB) = PWM0_GenB_Normal; // Set the PWM period. Since we are counting both up & down, we // initialize the load register to 1/2 the desired total period. We // will also program the match compare registers to 1/2 the desired // high time Period_0 = Startup_Period_ms * PWMTicksPerMS; HWREG(PWM0_BASE + PWM_O_0_LOAD) = ((Period_0)) >> 1; // Set the initial Duty cycle on A to 50% by programming the compare // value to 1/2 the period to count up (or down). Technically, the // value to program should be Period/2 - DesiredHighTime/2, but since // the desired high time is 1/2 the period, we can skip the subtract Duty_0 = 50; HWREG(PWM0_BASE + PWM_O_0_CMPA) = HWREG(PWM0_BASE + PWM_O_0_LOAD) >> 1; // Set the initial Duty cycle on B to 50% like A HWREG(PWM0_BASE + PWM_O_0_CMPB) = HWREG(PWM0_BASE + PWM_O_0_LOAD) >> 1; // enable the PWM outputs HWREG(PWM0_BASE + PWM_O_ENABLE) |= (PWM_ENABLE_PWM0EN | PWM_ENABLE_PWM1EN); // now configure the Port B pins to be PWM outputs // start by selecting the alternate function for PB6 and PB7 HWREG(GPIO_PORTB_BASE + GPIO_O_AFSEL) |= (BIT6HI | BIT7HI); // now choose to map PWM to those pins, this is a mux value of 4 that // we want to use for specifying the function on bits 6 and 7 HWREG(GPIO_PORTB_BASE + GPIO_O_PCTL) = (HWREG(GPIO_PORTB_BASE + GPIO_O_PCTL) & 0x00ffffff) + (4 << (6 * BitsPerNibble)) + (4 << (7 * BitsPerNibble)); // Enable pins 6 and 7 on Port B for digital I/O HWREG(GPIO_PORTB_BASE + GPIO_O_DEN) |= (BIT6HI | BIT7HI); // make pins 6 and 7 on Port B an output HWREG(GPIO_PORTB_BASE + GPIO_O_DIR) |= (BIT6HI | BIT7HI); // set the up/down count mode, enable the PWM generator and make // both generator updates locally synchronized to zero count HWREG(PWM0_BASE + PWM_O_0_CTL) = (PWM_0_CTL_MODE | PWM_0_CTL_ENABLE | PWM_0_CTL_GENAUPD_LS | PWM_0_CTL_GENBUPD_LS); } break; } } /**************************************************************************** Function PWM_SetFrequency Parameters uint16_t (PWMFreq) uint8_t (Group) Returns None Description Sets the PWM frequency to desired value for designated group. Notes Author - ****************************************************************************/ void PWM_SetFrequency(uint16_t PWMFreq, uint8_t Group) { uint32_t CompVal = 0; uint32_t DesiredHighTime = 0; Period_0 = ((MsPerSecond * PWMTicksPerMS) / PWMFreq); // in ticks DesiredHighTime = (Period_0 * Duty_0) / 100; // ticks CompVal = Period_0 / 2 - DesiredHighTime / 2; switch (Group) { case DriveMotors: { // Set the PWM period. Since we are counting both up & down, we initialize // the load register to 1/2 the desired total period. We will also program // the match compare registers to 1/2 the desired high time HWREG(PWM0_BASE + PWM_O_0_LOAD) = ((Period_0) >> 1); // modify compare values HWREG(PWM0_BASE + PWM_O_0_CMPA) = CompVal; } break; case AllServos: { // Set the PWM period. Since we are counting both up & down, we initialize // the load register to 1/2 the desired total period. We will also program // the match compare registers to 1/2 the desired high time HWREG(PWM0_BASE + PWM_O_1_LOAD) = ((Period_0) >> 1); HWREG(PWM1_BASE + PWM_O_1_LOAD) = ((Period_0) >> 1); // modify compare values HWREG(PWM0_BASE + PWM_O_1_CMPA) = CompVal; HWREG(PWM0_BASE + PWM_O_1_CMPB) = CompVal; HWREG(PWM1_BASE + PWM_O_1_CMPA) = CompVal; HWREG(PWM1_BASE + PWM_O_1_CMPB) = CompVal; } break; case RecycleEmitter: { // Set the PWM period. Since we are counting both up & down, we // initialize the load register to 1/2 the desired total period. We // will also program the match compare registers to 1/2 the desired // high time HWREG(PWM1_BASE + PWM_O_0_LOAD) = (Period_0 >> 1); // modify compare values HWREG(PWM1_BASE + PWM_O_0_CMPA) = CompVal; } break; case RobotDetEmitter: { // Set the PWM period. Since we are counting both up & down, we // initialize the load register to 1/2 the desired total period. We // will also program the match compare registers to 1/2 the desired // high time HWREG(PWM0_BASE + PWM_O_2_LOAD) = (Period_0 >> 1); // modify compare values HWREG(PWM0_BASE + PWM_O_0_CMPA) = CompVal; } break; } } /**************************************************************************** Function PWM_SetDuty Parameters uint8_t: DutyCycle (0-100) uint8_t: PinNum (0 or 1 to correspond to PB6 or PB7) Returns none Description Sets cmpA or cmpB (specified by PinNum) to get the duty cycle of the PWM signal to the desired value. Notes PB6 - PWM M0PWM0 (Alt. No. 4) left drive motor PB7 - PWM M0PWM1 (Alt. No. 4) right drive motor PB4 - PWM M0PWM2 (Alt. No. 4) recycling bin servo PB5 - PWM M0PWM3 (Alt. No. 4) trash bin servo PA6 - PWM M1PWM2 (Alt. No. 5) sorter servo PA7 - PWM M1PWM3 (Alt. No. 5) team selection servo PD0 - PWM M1PWM0 (Alt. No. 5) IR recycling center Author Bibit Bianchini, 2/04/2019 ****************************************************************************/ void PWM_SetDuty(uint8_t DutyCycle, uint8_t PinNum) { // first take care of the duty cycles of 0 or 100 cases: if (DutyCycle == 0) { Set0_DC(PinNum); } else if (DutyCycle == 100) { Set100_DC(PinNum); } else { // make sure to reset the DC RestoreDC(PinNum); // now change the correct PWM pin based on PinNum switch (PinNum) { case MotorA: { // calculate what compare value should be for desired duty cycle CompareA = (HWREG(PWM0_BASE + PWM_O_0_LOAD) * (100 - DutyCycle)) / 100; // store that compare value into PWM port HWREG(PWM0_BASE + PWM_O_0_CMPA) = CompareA; } break; case MotorB: { // calculate what compare value should be for desired duty cycle CompareB = (HWREG(PWM0_BASE + PWM_O_0_LOAD) * (100 - DutyCycle)) / 100; // store that compare value into PWM port HWREG(PWM0_BASE + PWM_O_0_CMPB) = CompareB; } break; case RecycleServo: { // calculate what compare value should be for desired duty cycle CompareA = (HWREG(PWM0_BASE + PWM_O_1_LOAD) * (100 - DutyCycle)) / 100; // store that compare value into PWM port HWREG(PWM0_BASE + PWM_O_1_CMPA) = CompareA; } break; case TrashServo: { // calculate what compare value should be for desired duty cycle CompareB = (HWREG(PWM0_BASE + PWM_O_1_LOAD) * (100 - DutyCycle)) / 100; // store that compare value into PWM port HWREG(PWM0_BASE + PWM_O_1_CMPB) = CompareB; } break; case SortServo: { // calculate what compare value should be for desired duty cycle CompareA = (HWREG(PWM1_BASE + PWM_O_1_LOAD) * (100 - DutyCycle)) / 100; // store that compare value into PWM port HWREG(PWM1_BASE + PWM_O_1_CMPA) = CompareA; } break; case FlagServo: { // calculate what compare value should be for desired duty cycle CompareB = (HWREG(PWM1_BASE + PWM_O_1_LOAD) * (100 - DutyCycle)) / 100; // store that compare value into PWM port HWREG(PWM1_BASE + PWM_O_1_CMPB) = CompareB; } break; case RecycleEmitter: { // calculate what compare value should be for desired duty cycle CompareA = (HWREG(PWM1_BASE + PWM_O_0_LOAD) * (100 - DutyCycle)) / 100; // store that compare value into PWM port HWREG(PWM1_BASE + PWM_O_0_CMPA) = CompareA; } break; } } } /*************************************************************************** private functions ***************************************************************************/ /**************************************************************************** Function Set0_DC Parameters uint8_t: PinNum (0 or 1 to correspond to PB6 or PB7 respectively) Returns none Description Deals with the case of 0% duty cycle Notes Author Bibit Bianchini, 2/04/2019 ****************************************************************************/ static void Set0_DC(uint8_t PinNum) { // check for which pin we should be changing switch (PinNum) { // if we want to change PB6, then make changes to generator A case MotorA: { // to program 0% DC, set the action on Zero to set the output to 0 HWREG(PWM0_BASE + PWM_O_0_GENA) = PWM_0_GENA_ACTZERO_ZERO; } break; // if we want to change PB7, then make changes to generator B case MotorB: { // to program 0% DC, set the action on Zero to set the output to 0 HWREG(PWM0_BASE + PWM_O_0_GENB) = PWM_0_GENB_ACTZERO_ZERO; } break; case RecycleServo: { // to program 0% DC, set the action on Zero to set the output to 0 HWREG(PWM0_BASE + PWM_O_1_GENA) = PWM_0_GENA_ACTZERO_ZERO; } break; case TrashServo: { // to program 0% DC, set the action on Zero to set the output to 0 HWREG(PWM0_BASE + PWM_O_1_GENB) = PWM_0_GENB_ACTZERO_ZERO; } break; case SortServo: { // to program 0% DC, set the action on Zero to set the output to 0 HWREG(PWM1_BASE + PWM_O_1_GENA) = PWM_1_GENA_ACTZERO_ZERO; } break; case FlagServo: { // to program 0% DC, set the action on Zero to set the output to 0 HWREG(PWM1_BASE + PWM_O_1_GENB) = PWM_1_GENB_ACTZERO_ZERO; } break; case RecycleEmitter: { // to program 0% DC, set the action on Zero to set the output to 0 HWREG(PWM1_BASE + PWM_O_0_GENA) = PWM_0_GENA_ACTZERO_ZERO; } break; } } /**************************************************************************** Function Set100_DC Parameters uint8_t: PinNum (0 or 1 to correspond to PB6 or PB7 respectively) Returns none Description Deals with the case of 100% duty cycle Notes Author Bibit Bianchini, 2/04/2019 ****************************************************************************/ static void Set100_DC(uint8_t PinNum) { // check for which pin we should be changing switch (PinNum) { // if we want to change PB6, then make changes to generator A case MotorA: { // to program 100% DC, set the action on Zero to set the output to 1 HWREG(PWM0_BASE + PWM_O_0_GENA) = PWM_0_GENA_ACTZERO_ONE; } break; // if we want to change PB7, then make changes to generator B case MotorB: { // to program 100% DC, set the action on Zero to set the output to 1 HWREG(PWM0_BASE + PWM_O_0_GENB) = PWM_0_GENB_ACTZERO_ONE; } break; case RecycleServo: { // to program 100% DC, set the action on Zero to set the output to 1 HWREG(PWM0_BASE + PWM_O_1_GENA) = PWM_0_GENA_ACTZERO_ONE; } break; case TrashServo: { // to program 100% DC, set the action on Zero to set the output to 1 HWREG(PWM0_BASE + PWM_O_1_GENB) = PWM_0_GENB_ACTZERO_ONE; } break; case SortServo: { // to program 100% DC, set the action on Zero to set the output to 1 HWREG(PWM1_BASE + PWM_O_1_GENA) = PWM_1_GENA_ACTZERO_ONE; } break; case FlagServo: { // to program 100% DC, set the action on Zero to set the output to 1 HWREG(PWM1_BASE + PWM_O_1_GENB) = PWM_1_GENB_ACTZERO_ONE; } break; case RecycleEmitter: { // to program 100% DC, set the action on Zero to set the output to 1 HWREG(PWM1_BASE + PWM_O_0_GENA) = PWM_0_GENA_ACTZERO_ONE; } break; } } /**************************************************************************** Function RestoreDC Parameters uint8_t: PinNum Returns none Description Restores the original DC settings so PWM of duty cycles in between 0 and 100 work again. Notes Author Bibit Bianchini, 2/04/2019 ****************************************************************************/ static void RestoreDC(uint8_t PinNum) { // check for which pin we should be changing switch (PinNum) { // if we want to change PB6, then make changes to generator A case MotorA: { // to restore the previous DC, set the action back to the normal actions HWREG(PWM0_BASE + PWM_O_0_GENA) = PWM0_GenA_Normal; } break; // if we want to change PB7, then make changes to generator B case MotorB: { // to restore the previous DC, set the action back to the normal actions HWREG(PWM0_BASE + PWM_O_0_GENB) = PWM0_GenB_Normal; } break; case RecycleServo: { // to restore the previous DC, set the action back to the normal actions HWREG(PWM0_BASE + PWM_O_1_GENA) = PWM1_GenA_Normal; } break; case TrashServo: { // to restore the previous DC, set the action back to the normal actions HWREG(PWM0_BASE + PWM_O_1_GENB) = PWM1_GenB_Normal; } break; case SortServo: { // to restore the previous DC, set the action back to the normal actions HWREG(PWM1_BASE + PWM_O_1_GENA) = PWM1_GenA_Normal; } break; case FlagServo: { // to restore the previous DC, set the action back to the normal actions HWREG(PWM1_BASE + PWM_O_1_GENB) = PWM1_GenB_Normal; } break; case RecycleEmitter: { // to restore the previous DC, set the action back to the normal actions HWREG(PWM1_BASE + PWM_O_0_GENA) = PWM0_GenA_Normal; } break; } } /*------------------------------- Footnotes -------------------------------*/ /*------------------------------ End of file ------------------------------*/