/****************************************************************************
Module
MyPWMLib.c
Description
This module contains functions that allow motors to be driven via PWM
****************************************************************************/
/*----------------------------- 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 "MyPWMLib.h"
// the headers to access the GPIO subsystem
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"
#include "hw_nvic.h"
#include "hw_pwm.h"
#include "hw_timer.h"
// the headers to access the TivaWare Library
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "driverlib/pwm.h"
#include "BITDEFS.H"
/*----------------------------- Module Defines ----------------------------*/
/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
relevant to the behavior of this state machine
*/
static void My_Set_100DC(uint8_t Channel);
static void My_RestoreDC(uint8_t Channel);
static void My_Set_0DC(uint8_t Channel);
/*---------------------------- 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
#define ONE_SEC 976
#define HUNDRED_MSEC (ONE_SEC / 10)
#define PWM_DIV 32
#define CLOCK_SPEED 40E6 //why not SYSCTLCLOCKGET??
#define BitsPerNibble 4
#define MAX_NUM_CHANNELS 16
#define GENA_NORMAL PWM_0_GENA_ACTCMPAU_ONE | PWM_0_GENA_ACTCMPAD_ZERO
#define GENB_NORMAL PWM_0_GENB_ACTCMPBU_ONE | PWM_0_GENB_ACTCMPBD_ZERO
#define BASE_FREQ 500
static uint8_t LocalDuty[MAX_NUM_CHANNELS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0 };
static const uint32_t ChannelToPWM_MOD[MAX_NUM_CHANNELS] = {
PWM0_BASE, PWM0_BASE, PWM0_BASE, PWM0_BASE,
PWM0_BASE, PWM0_BASE, PWM0_BASE, PWM0_BASE,
PWM1_BASE, PWM1_BASE, PWM1_BASE, PWM1_BASE,
PWM1_BASE, PWM1_BASE, PWM1_BASE, PWM1_BASE
};
static const uint32_t ChannelToCTLOffset[MAX_NUM_CHANNELS >> 1] = {
PWM_O_0_CTL, PWM_O_1_CTL, PWM_O_2_CTL, PWM_O_3_CTL,
PWM_O_0_CTL, PWM_O_1_CTL, PWM_O_2_CTL, PWM_O_3_CTL
};
static const uint32_t ChannelToGENOffset[MAX_NUM_CHANNELS] = {
PWM_O_0_GENA, PWM_O_0_GENB, PWM_O_1_GENA, PWM_O_1_GENB,
PWM_O_2_GENA, PWM_O_2_GENB, PWM_O_3_GENA, PWM_O_3_GENB,
PWM_O_0_GENA, PWM_O_0_GENB, PWM_O_1_GENA, PWM_O_1_GENB,
PWM_O_2_GENA, PWM_O_2_GENB, PWM_O_3_GENA, PWM_O_3_GENB
};
static const uint32_t ChannelToLOADOffset[MAX_NUM_CHANNELS >> 1] = {
PWM_O_0_LOAD, PWM_O_1_LOAD, PWM_O_2_LOAD, PWM_O_3_LOAD,
PWM_O_0_LOAD, PWM_O_1_LOAD, PWM_O_2_LOAD, PWM_O_3_LOAD
};
static const uint32_t ChannelToCMPOffset[MAX_NUM_CHANNELS] = {
PWM_O_0_CMPA, PWM_O_0_CMPB, PWM_O_1_CMPA, PWM_O_1_CMPB,
PWM_O_2_CMPA, PWM_O_2_CMPB, PWM_O_3_CMPA, PWM_O_3_CMPB,
PWM_O_0_CMPA, PWM_O_0_CMPB, PWM_O_1_CMPA, PWM_O_1_CMPB,
PWM_O_2_CMPA, PWM_O_2_CMPB, PWM_O_3_CMPA, PWM_O_3_CMPB
};
static const uint32_t ChannelToGENConfig[MAX_NUM_CHANNELS] = {
GENA_NORMAL, GENB_NORMAL, GENA_NORMAL, GENB_NORMAL,
GENA_NORMAL, GENB_NORMAL, GENA_NORMAL, GENB_NORMAL,
GENA_NORMAL, GENB_NORMAL, GENA_NORMAL, GENB_NORMAL,
GENA_NORMAL, GENB_NORMAL, GENA_NORMAL, GENB_NORMAL
};
static const uint32_t ChannelToPORTOffset[MAX_NUM_CHANNELS] = {
GPIO_PORTB_BASE, GPIO_PORTB_BASE, GPIO_PORTB_BASE, GPIO_PORTB_BASE,
GPIO_PORTE_BASE, GPIO_PORTE_BASE, GPIO_PORTC_BASE, GPIO_PORTC_BASE,
GPIO_PORTD_BASE, GPIO_PORTD_BASE, GPIO_PORTA_BASE, GPIO_PORTA_BASE,
GPIO_PORTF_BASE, GPIO_PORTF_BASE, GPIO_PORTF_BASE, GPIO_PORTF_BASE
};
static const uint32_t ChanneltoPINOffset[MAX_NUM_CHANNELS] = { 6, 7, 4, 5, 4, 5, 4,
5, 0, 1, 6, 7, 0, 1, 2, 3 };
static uint8_t PWMDuty;
/*------------------------------ Module Code ------------------------------*/
// Performs all operations to initialize PWM0 signal
void My_PWM_Init(uint16_t Frequency)
{
// enable the clock to the PWM0 Module
HWREG(SYSCTL_RCGCPWM) |= SYSCTL_RCGCPWM_R0;
// enable the clock to port B (where this module outputs PWM)
HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R1;
// select PWM clock as system clock (why do I want to divide this?)
HWREG(SYSCTL_RCC) = (HWREG(SYSCTL_RCC) & ~SYSCTL_RCC_PWMDIV_M) |
(SYSCTL_RCC_USEPWMDIV | SYSCTL_RCC_PWMDIV_32);
// check to see if PWM module ready
while ((HWREG(SYSCTL_PRPWM) & SYSCTL_PRPWM_R0) != SYSCTL_PRPWM_R0)
{
;
}
// disable PWM while init
HWREG(PWM0_BASE + PWM_O_0_CTL) = 0;
// program 1 during rising and 0 during falling
HWREG(PWM0_BASE + PWM_O_0_GENA) = GENA_NORMAL;
// set the PWM period (1/2 the desired high time)
uint16_t PWMInterval = (uint16_t)((SysCtlClockGet() / PWM_DIV) / Frequency);
HWREG(PWM0_BASE + PWM_O_0_LOAD) = ((PWMInterval) >> 1);
// set the initial duty cycle to 0% (Period/2 - DesiredHighTime/2)
HWREG(PWM0_BASE + PWM_O_0_CMPA) = HWREG(PWM0_BASE + PWM_O_0_LOAD);
// enable the PWM output
HWREG(PWM0_BASE + PWM_O_ENABLE) = PWM_ENABLE_PWM0EN;
// configure Port B pin as output for PWM0
HWREG(GPIO_PORTB_BASE + GPIO_O_AFSEL) |= BIT6HI;
// now need to map PWM to PB6 alternate function MUX
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 digital output
HWREG(GPIO_PORTB_BASE + GPIO_O_DIR) |= BIT6HI;
// set the up/down count mode, enable the PWM generator, generator update locally
synchronized to zero
HWREG(PWM0_BASE + PWM_O_0_CTL) = (PWM_0_CTL_MODE | PWM_0_CTL_ENABLE |
PWM_0_CTL_GENAUPD_LS);
}
// Initialize up to 8 PWM channels on Module0 (1 module, 4 generators, 8 PWM lines)
void My_PWM_Init_Many(uint8_t HowMany)
{
static uint8_t i;
// enable the clock to the PWM0 Module
HWREG(SYSCTL_RCGCPWM) |= SYSCTL_RCGCPWM_R0;
// enable the minimal ports for the number of PWM lines needed
// always enable the clock to port B (channels 0-3)
HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R1;
// enable port E if needed (channels 4,5)
if (HowMany > 4)
{
HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R4;
}
// enable port C if needed (channels 6,7)
if (HowMany > 6)
{
HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R2;
}
// select PWM clock as system clock with /32 divisor
HWREG(SYSCTL_RCC) = (HWREG(SYSCTL_RCC) & ~SYSCTL_RCC_PWMDIV_M) |
(SYSCTL_RCC_USEPWMDIV | SYSCTL_RCC_PWMDIV_32);
// check to see if PWM0 module ready
while ((HWREG(SYSCTL_PRPWM) & SYSCTL_PRPWM_R0) != SYSCTL_PRPWM_R0)
{
;
}
// set generator options (PWM0-3) in PWM0 module
// disable generators while configuring, set all generators to 500Hz initially
for (i = 0; i < HowMany; i += 2)
{
HWREG(PWM0_BASE + ChannelToCTLOffset[i >> 1]) = 0;
HWREG(PWM0_BASE + ChannelToLOADOffset[i >> 1]) = (SysCtlClockGet() / PWM_DIV /
BASE_FREQ) >> 1; //SYSCLK with 32 divisor for 500Hz frequency
}
// configure GenA or GenB for center-aligned up-down counting
for (i = 0; i < HowMany; i++)
{
HWREG(PWM0_BASE + ChannelToGENOffset[i]) = ChannelToGENConfig[i];
}
// set all duty cycles to 0% initially
for (i = 0; i < HowMany; i++)
{
My_PWM_SetDuty(50, i);
}
// enable the generator outputs
for (i = 0; i < HowMany; i++)
{
HWREG(PWM0_BASE + PWM_O_ENABLE) |= PWM_ENABLE_PWM0EN << i;
}
// configure pins for alternate function, mux in a value of 4 for PWM alt function,
enable as digital output
for (i = 0; i < HowMany; i++)
{
HWREG(ChannelToPORTOffset[i] + GPIO_O_AFSEL) |= GPIO_PIN_0 <<
ChanneltoPINOffset[i];
HWREG(ChannelToPORTOffset[i] + GPIO_O_PCTL) = (HWREG(ChannelToPORTOffset[i] +
GPIO_O_PCTL) & ~(0x0000000f << (BitsPerNibble * ChanneltoPINOffset[i]))) + (4 <<
(ChanneltoPINOffset[i] * BitsPerNibble)); //THIS IS PROBABLY WHAT'S WRONG
HWREG(ChannelToPORTOffset[i] + GPIO_O_DEN) |= GPIO_PIN_0 <<
ChanneltoPINOffset[i];
HWREG(ChannelToPORTOffset[i] + GPIO_O_DIR) |= GPIO_PIN_0 <<
ChanneltoPINOffset[i];
}
// enable the generator and update locally synchronized to zero
for (i = 0; i < HowMany; i += 2)
{
HWREG(PWM0_BASE + ChannelToCTLOffset[i >> 1]) = (PWM_0_CTL_MODE |
PWM_0_CTL_ENABLE | PWM_0_CTL_GENAUPD_LS);
}
printf("My_PWM_Init_Many successful\n\r");
}
//init PWM just for channel 10 (PA6) for reload IR out (M1,PWM1,GenA)
void My_PWM_Init_Channel10(void)
{
// channel 10 for PA6
uint8_t i = 10;
// enable the clock to the PWM1 Module
HWREG(SYSCTL_RCGCPWM) |= SYSCTL_RCGCPWM_R1;
// enable the clock to port A
HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R0;
// select PWM clock as system clock with /32 divisor
HWREG(SYSCTL_RCC) = (HWREG(SYSCTL_RCC) & ~SYSCTL_RCC_PWMDIV_M) |
(SYSCTL_RCC_USEPWMDIV | SYSCTL_RCC_PWMDIV_32);
// check to see if PWM1 module ready
while ((HWREG(SYSCTL_PRPWM) & SYSCTL_PRPWM_R1) != SYSCTL_PRPWM_R1)
{
;
}
// set generator options M1 in PWM1 module
// disable generators while configuring, set generator to 500Hz initially
HWREG(ChannelToPWM_MOD[i] + ChannelToCTLOffset[i >> 1]) = 0;
HWREG(ChannelToPWM_MOD[i] + ChannelToLOADOffset[i >> 1]) = (SysCtlClockGet() /
PWM_DIV / 500) >> 1; //SYSCLK with 32 divisor for 500Hz frequency
// configure GenA for center-aligned up-down counting
HWREG(ChannelToPWM_MOD[i] + ChannelToGENOffset[i]) = ChannelToGENConfig[i];
// set duty cycle to 0% initially
My_PWM_SetDuty(0, i);
// enable the generator outputs
HWREG(ChannelToPWM_MOD[i] + PWM_O_ENABLE) |= PWM_ENABLE_PWM0EN << (i - 8);
// configure pins for alternate function, mux in a value of 4 for PWM alt function,
enable as digital output
HWREG(ChannelToPORTOffset[i] + GPIO_O_AFSEL) |= GPIO_PIN_0 <<
ChanneltoPINOffset[i];
HWREG(ChannelToPORTOffset[i] + GPIO_O_PCTL) = (HWREG(ChannelToPORTOffset[i] +
GPIO_O_PCTL) & ~(0x0000000f << (BitsPerNibble * ChanneltoPINOffset[i]))) + (5 <<
(ChanneltoPINOffset[i] * BitsPerNibble)); //THIS IS PROBABLY WHAT'S WRONG
HWREG(ChannelToPORTOffset[i] + GPIO_O_DEN) |= GPIO_PIN_0 <<
ChanneltoPINOffset[i];
HWREG(ChannelToPORTOffset[i] + GPIO_O_DIR) |= GPIO_PIN_0 <<
ChanneltoPINOffset[i];
// enable the generator and update locally synchronized to zero
HWREG(ChannelToPWM_MOD[i] + ChannelToCTLOffset[i >> 1]) = (PWM_1_CTL_MODE |
PWM_1_CTL_ENABLE | PWM_1_CTL_GENAUPD_LS);
printf("My_PWM_Init_Channel10 successful\n\r");
}
//update the PWM frequency by group
void My_PWM_SetFreq(uint16_t Frequency, uint8_t Group)
{
uint16_t PWMInterval = (uint16_t)((SysCtlClockGet() / PWM_DIV) / Frequency);
if (Group < 4)
{
HWREG(PWM0_BASE + ChannelToLOADOffset[Group]) = (PWMInterval >> 1);
}
else
{
HWREG(PWM1_BASE + ChannelToLOADOffset[Group]) = (PWMInterval >> 1);
}
uint8_t ChannelA = Group * 2;
uint8_t ChannelB = Group * 2 + 1;
My_PWM_SetDuty( LocalDuty[ChannelA], ChannelA); // need to reset the CMPA value
My_PWM_SetDuty( LocalDuty[ChannelB], ChannelB); // need to reset the CMPB value
}
//update the PWM period by group (ONLY FOR SERVOS ON CHANNELS 2,3,4)
void My_PWM_SetPeriod(uint16_t Period, uint8_t Group)
{
HWREG(PWM0_BASE + ChannelToLOADOffset[Group]) = (Period >> 1);
}
//update the PWM pulse width by channel (ONLY FOR SERVOS ON CHANNELS 2,3,4)
void My_PWM_SetPulseWidth(uint16_t PulseWidth, uint8_t Channel)
{
uint32_t CurrentLoadValue = HWREG(PWM0_BASE + ChannelToLOADOffset[Channel >> 1]);
HWREG(PWM0_BASE + ChannelToCMPOffset[Channel]) = (CurrentLoadValue - (PulseWidth
>> 1));
}
// Set the duty cycle (0-100%) for a specific PWM channel (0-15)
void My_PWM_SetDuty(uint8_t DutyCycle, uint8_t Channel)
{
LocalDuty[Channel] = DutyCycle;
if (DutyCycle == 100)
{
My_Set_100DC(Channel);
}
else if (DutyCycle == 0)
{
My_Set_0DC(Channel);
}
else
{
My_RestoreDC(Channel);
if (Channel < 8)
{
HWREG(PWM0_BASE + ChannelToCMPOffset[Channel]) = 1 + HWREG(PWM0_BASE +
ChannelToLOADOffset[Channel >> 1]) * (100 - DutyCycle) / 100;
}
else
{
HWREG(PWM1_BASE + ChannelToCMPOffset[Channel]) = 1 + HWREG(PWM1_BASE +
ChannelToLOADOffset[Channel >> 1]) * (100 - DutyCycle) / 100;
}
}
}
// set to 100% duty
static void My_Set_100DC(uint8_t Channel)
{
if (Channel < 8)
{
HWREG(PWM0_BASE + ChannelToGENOffset[Channel]) = PWM_0_GENA_ACTZERO_ONE;
}
else
{
HWREG(PWM1_BASE + ChannelToGENOffset[Channel]) = PWM_1_GENA_ACTZERO_ONE;
}
}
// set to 0% duty
static void My_Set_0DC(uint8_t Channel)
{
if (Channel < 8)
{
HWREG(PWM0_BASE + ChannelToGENOffset[Channel]) = PWM_0_GENA_ACTZERO_ZERO;
}
else
{
HWREG(PWM1_BASE + ChannelToGENOffset[Channel]) = PWM_1_GENA_ACTZERO_ZERO;
}
}
// restore the normal actions
static void My_RestoreDC(uint8_t Channel)
{
if (Channel < 8)
{
HWREG(PWM0_BASE + ChannelToGENOffset[Channel]) = ChannelToGENConfig[Channel];
}
else
{
HWREG(PWM1_BASE + ChannelToGENOffset[Channel]) = ChannelToGENConfig[Channel];
}
}