Motorware Hal Tutorial
Motorware Hal Tutorial
Module of MotorWare
1. Introduction
The purpose of the HAL module is to allow a unified software base that can be ported across multiple
MCUs (i.e. f2802x, f2805x and f2806x) and hardware platforms (i.e. boostxldrv8301_revB,
drv8301kit_revD, drv8312kit_revD and hvkit_rev1p1).
2. Software Architecture
The following block diagram shows how the HAL object fits within the software architecture followed by
MotorWare:
USER INTERFACE
CTRL
PARK
PID
SVGEN
TRAJ
GPIO
OSC
FLASH
PIE
PLL
PWR
TIMER
WDOG
FILTER
OFFSET
CLARKE
IPARK
ADC
CLK
CPU
PWM
HAL
SW
HW
Platform
Motor
Certain guidelines are followed by the current software architecture, which allows a better use of a HAL
module:
HAL module is the only module that can interface with the hardware. In this context, hardware
includes MCU peripherals, and board functionality.
No peripheral configuration should be done at a users interface level, since this doesnt allow
portability of the code.
HAL functions which are time critical, should be inlined for faster execution.
Use of macros must be avoided, and use of inline functions should be used instead.
3. Object Definition
There are several object definitions of the HAL module, depending on the processor and board
combination. The object definition of the HAL module can be found within the MotorWare product tree
as shown in the following picture:
FLASH_Handle flashHandle;
GPIO_Handle gpioHandle;
OFFSET_Handle offsetHandle_I[3]; //!< the handles for the current offset estimators
OFFSET_Obj offset_I[3];
//!< the current offset objects
OFFSET_Handle offsetHandle_V[3]; //!< the handles for the voltage offset estimators
OFFSET_Obj offset_V[3];
OSC_Handle oscHandle;
PIE_Handle pieHandle;
PLL_Handle pllHandle;
PWM_Handle pwmHandle[3];
_iq
current_sf;
_iq
voltage_sf;
} HAL_Obj;
As can be seen, the HAL object definition is a combination of number of modules per peripheral, and
modules related to a particular platform. For example, there is only one ADC module that we have to
configure through the HAL module, so we only include one ADC module:
ADC_Handle adcHandle;
And there are three PWM pairs that we have to configure, so we add three of these:
PWM_Handle pwmHandle[3];
Also, related to the platform, the following HAL parameter will have the number of current sensors:
uint_least8_t numCurrentSensors; //!< the number of current sensors
The APIs are classified into different categories: Board management, Calibration management and
Peripheral management.
4.1.Board Management
A few examples of board management HAL APIs are the ones that do not configure a peripheral directly,
or manage variables related to a specific board, such as number of current sensors, number of
quadrature encode modules (QEP), etc. Some examples of these APIs are:
4.2.Calibration Management
A few examples of calibration management HAL APIs are the ones that run certain code to adjust a
calibration offset based on a device temperature sensor, or based on an ADC calibration subroutine.
Some examples of these APIs are:
HAL_cal()
HAL_AdcCalChanSelect()
HAL_AdcCalConversion()
HAL_AdcOffsetSelfCal()
HAL_getOscTrimValue()
HAL_OscTempComp()
HAL_osc1Comp()
HAL_osc2Comp()
4.3.Peripheral Management
These APIs are the ones that configure a device peripheral, or a set of peripherals. Some of these
functions are translated into a single function call to a peripheral configuration API, although they are
still needed to allow the portability of the software architecture to work. An example of the simplest
peripheral configuration is:
// enable global interrupts
HAL_enableGlobalInts(halHandle);
Which is implemented as follows:
void HAL_enableGlobalInts(HAL_Handle handle)
{
HAL_Obj *obj = (HAL_Obj *)handle;
CPU_enableGlobalInts(obj->cpuHandle);
return;
} // end of HAL_enableGlobalInts() function
As can be seen, from the top level, a HAL function is called. In the HAL module itself, a CPU bit is
changed. This HAL function might seem unnecessary, but it is actually needed to allow the top level to
be fully portable to other MCUs or boards, which might modify another bit or bits to enable global
interrupts.
Some of the Peripheral management functions are:
HAL_acqAdcInt()
HAL_acqPwmInt()
HAL_disableGlobalInts()
HAL_disableWdog()
HAL_disablePwm()
HAL_enableAdcInts()
HAL_enableDebugInt()
HAL_enableGlobalInts()
HAL_enablePwm()
HAL_enablePwmInt()
HAL_getAdcSocSampleDelay()
HAL_initIntVectorTable()
HAL_readTimerCnt()
HAL_reloadTimer()
HAL_startTimer()
HAL_stopTimer()
HAL_setTimerPeriod()
HAL_getTimerPeriod()
HAL_setAdcSocSampleDelay()
HAL_setGpioHigh()
HAL_toggleGpio()
HAL_setGpioLow()
HAL_setupAdcs()
HAL_setupTimers()
HAL_setupClks()
HAL_setupFlash()
HAL_setupGpios()
HAL_setupPeripheralClks()
HAL_setupPie()
HAL_setupPll()
HAL_setupPwms()
HAL_setupPwmDacs()
HAL_readPwmCmpA()
HAL_readPwmCmpB()
HAL_readPwmPeriod()
5. Usage in MotorWare
The following picture shows where the HAL object is added within a MotorWare project:
// the globals
HAL_Obj hal;
5.2.HAL Configuration
In a MotorWare project, there are several HAL functions that are called only once, related to the
configuration of the Hardware. As an example, consider proj_lab03a of MotorWare Release 13. All of
these functions deal with configuration of either a peripheral, or a driver IC (i.e. DRV8301).
// initialize the hardware abstraction layer
halHandle = HAL_init(&hal,sizeof(hal));
...
// set the hardware abstraction layer parameters
HAL_setParams(halHandle,&gUserParams);
...
// setup faults
HAL_setupFaults(halHandle);
// initialize the interrupt vector table
HAL_initIntVectorTable(halHandle);
// enable the ADC interrupts
HAL_enableAdcInts(halHandle);
// enable global interrupts
HAL_enableGlobalInts(halHandle);
// enable debug interrupts
HAL_enableDebugInt(halHandle);
Project Start
Setup faults
depending on board
used:
HAL_setupFaults()
Configure MCUs
peripherals:
HAL_initIntVectorTable()
HAL_enableAdcInts()
HAL_enableGlobalInts()
HAL_enableDebugInt()
HAL_disablePwm()
Configure DRV8301
(if applicable)
HAL_enableDrv()
HAL_setupDrvSpi()
HAL_disablePwm(halHandle);
}
else
{
...
if(ctrlState == CTRL_State_OffLine)
{
// enable the PWM
HAL_enablePwm(halHandle);
}
else if(ctrlState == CTRL_State_OnLine)
{
// update the ADC bias values
HAL_updateAdcBias(halHandle);
// Return the bias value for currents
gMotorVars.I_bias.value[0] = HAL_getBias(halHandle,HAL_SensorType_Current,0);
gMotorVars.I_bias.value[1] = HAL_getBias(halHandle,HAL_SensorType_Current,1);
gMotorVars.I_bias.value[2] = HAL_getBias(halHandle,HAL_SensorType_Current,2);
// Return the bias value for voltages
gMotorVars.V_bias.value[0] = HAL_getBias(halHandle,HAL_SensorType_Voltage,0);
gMotorVars.V_bias.value[1] = HAL_getBias(halHandle,HAL_SensorType_Voltage,1);
gMotorVars.V_bias.value[2] = HAL_getBias(halHandle,HAL_SensorType_Voltage,2);
// enable the PWM
HAL_enablePwm(halHandle);
}
else if(ctrlState == CTRL_State_Idle)
{
// disable the PWM
HAL_disablePwm(halHandle);
...
}
...
}
}
...
#ifdef DRV8301_SPI
HAL_writeDrvData(halHandle,&gDrvSpi8301Vars);
HAL_readDrvData(halHandle,&gDrvSpi8301Vars);
#endif
} // end of while(gFlag_enableSys) loop
HAL_disablePwm(halHandle);
...
} // end of for(;;) loop
The following state machine summarizes the HAL function calls in the forever loop (outside of the
interrupt):
Forever Loop Start
While
Flag_enableSys
is true
No
Disable PWM
Outputs:
HAL_disablePwm()
Yes
Disable PWM
Outputs:
HAL_disablePwm()
Yes
If CTRL has an
error
No
If CTRL state
changed to
Offline
Yes
Enable PWM
Outputs:
HAL_enablePwm()
Yes
Yes
Disable PWM
Outputs:
HAL_disablePwm()
No
If CTRL state
changed to
Online
No
If CTRL state
changed to Idle
No
Manage DRV8301
IC, read status:
HAL_writeDrvData()
HAL_readDrvData()
Enable PWM
Outputs:
HAL_enablePwm()
HAL_toggleLed(halHandle,(GPIO_Number_e)HAL_Gpio_LED2);
...
// acknowledge the ADC interrupt
HAL_acqAdcInt(halHandle,ADC_IntNumber_1);
// convert the ADC data
HAL_readAdcData(halHandle,&gAdcData);
...
// write the PWM compare values
HAL_writePwmData(halHandle,&gPwmData);
...
return;
} // end of mainISR() function
The following state machine summarizes the HAL function calls in the interrupt:
Interrupt Start
Time to toggle
LED?
No
Yes
Toggle LED:
HAL_toggleLed()
Acknowledge
Interrupt:
HAL_acqAdcInt()
Interrupt End
IA_FB
A1
IB_FB
B5
IC_FB
A5
f2806x and
VA_FB
B7
VB_FB
A7
drv8312kit_revD
Combination
VC_FB
B4
VBUS_FB
B2
Which is configured as shown in the following code snippet, taken from hal.c, function HAL_setupAdcs()
located in the following location: sw\modules\hal\boards\drv8312kit_revD\f28x\f2806x\src
// configure the SOCs for drv8312kit_revD
// EXT IA-FB
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_0,ADC_SocChanNumber_A1);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_0,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_0,ADC_SocSampleDelay_9_cycles);
// EXT IA-FB
// Duplicate conversion due to ADC Initial Conversion bug (SPRZ342)
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_1,ADC_SocChanNumber_A1);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_1,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_1,ADC_SocSampleDelay_9_cycles);
// EXT IB-FB
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_2,ADC_SocChanNumber_B5);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_2,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_2,ADC_SocSampleDelay_9_cycles);
// EXT IC-FB
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_3,ADC_SocChanNumber_A5);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_3,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_3,ADC_SocSampleDelay_9_cycles);
// ADC-Vhb1
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_4,ADC_SocChanNumber_B7);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_4,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_4,ADC_SocSampleDelay_9_cycles);
// ADC-Vhb2
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_5,ADC_SocChanNumber_A7);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_5,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_5,ADC_SocSampleDelay_9_cycles);
// ADC-Vhb3
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_6,ADC_SocChanNumber_B4);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_6,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_6,ADC_SocSampleDelay_9_cycles);
// VDCBUS
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_7,ADC_SocChanNumber_B2);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_7,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_7,ADC_SocSampleDelay_9_cycles);
Now lets change the connections, as if we had a customers board, which has a different ADC
configuration as in the following diagram:
IA_FB
A7
IB_FB
B1
IC_FB
B6
f2806x and
VA_FB
A2
VB_FB
B3
Customer Board
Combination
VC_FB
A5
VBUS_FB
A4
The only piece of code thats affected is in the HAL module. The configuration of the ADC, done in
function HAL_setupAdcs() would be like this (changes are highlighted):
// configure the SOCs for customer board
// EXT IA-FB
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_0,ADC_SocChanNumber_A7);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_0,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_0,ADC_SocSampleDelay_9_cycles);
// EXT IA-FB
// Duplicate conversion due to ADC Initial Conversion bug (SPRZ342)
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_1,ADC_SocChanNumber_A7);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_1,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_1,ADC_SocSampleDelay_9_cycles);
// EXT IB-FB
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_2,ADC_SocChanNumber_B1);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_2,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_2,ADC_SocSampleDelay_9_cycles);
// EXT IC-FB
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_3,ADC_SocChanNumber_B6);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_3,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_3,ADC_SocSampleDelay_9_cycles);
// ADC-Vhb1
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_4,ADC_SocChanNumber_A2);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_4,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_4,ADC_SocSampleDelay_9_cycles);
// ADC-Vhb2
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_5,ADC_SocChanNumber_B3);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_5,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_5,ADC_SocSampleDelay_9_cycles);
// ADC-Vhb3
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_6,ADC_SocChanNumber_A5);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_6,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_6,ADC_SocSampleDelay_9_cycles);
// VDCBUS
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_7,ADC_SocChanNumber_A4);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_7,ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_7,ADC_SocSampleDelay_9_cycles);
Keep in mind that the first two channels need to be repeated, so that the ADC initial conversion bug
(SPRZ342) is handled.
f2802x and
boostxldrv8301_revB
Combination
EPWM1A
PWM1H
EPWM1B
PWM1L
EPWM2A
PWM2H
EPWM2B
PWM2L
EPWM3A
PWM3H
EPWM3B
PWM3L
There are two functions that configure the PWM connections. One is in HAL_setupGpios() to setup the
GPIOs as PWM outputs:
// PWM1H
GPIO_setMode(obj->gpioHandle,GPIO_Number_0,GPIO_0_Mode_EPWM1A);
// PWM1L
GPIO_setMode(obj->gpioHandle,GPIO_Number_1,GPIO_1_Mode_EPWM1B);
// PWM2H
GPIO_setMode(obj->gpioHandle,GPIO_Number_2,GPIO_2_Mode_EPWM2A);
// PWM2L
GPIO_setMode(obj->gpioHandle,GPIO_Number_3,GPIO_3_Mode_EPWM2B);
// PWM3H
GPIO_setMode(obj->gpioHandle,GPIO_Number_4,GPIO_4_Mode_EPWM3A);
// PWM3L
GPIO_setMode(obj->gpioHandle,GPIO_Number_5,GPIO_5_Mode_EPWM3B);
And the second function that handles this configuration is HAL_init(), which assigns the address of the
PWM modules used:
// initialize PWM handle
obj->pwmHandle[0] = PWM_init((void *)PWM_ePWM1_BASE_ADDR,sizeof(PWM_Obj));
obj->pwmHandle[1] = PWM_init((void *)PWM_ePWM2_BASE_ADDR,sizeof(PWM_Obj));
obj->pwmHandle[2] = PWM_init((void *)PWM_ePWM3_BASE_ADDR,sizeof(PWM_Obj));
Now, consider the following customers board connection instead:
f2802x and
Customer Board
Combination
EPWM3A
PWM1H
EPWM3B
PWM1L
EPWM1A
PWM2H
EPWM1B
PWM2L
EPWM2A
PWM3H
EPWM2B
PWM3L
The following code snippet shows how to enable these GPIOs as outputs in HAL_setupGpios(). In fact,
the configuration is the same, although the comments are changed reflecting new order of connections.
// PWM2H
GPIO_setMode(obj->gpioHandle,GPIO_Number_0,GPIO_0_Mode_EPWM1A);
// PWM2L
GPIO_setMode(obj->gpioHandle,GPIO_Number_1,GPIO_1_Mode_EPWM1B);
// PWM3H
GPIO_setMode(obj->gpioHandle,GPIO_Number_2,GPIO_2_Mode_EPWM2A);
// PWM3L
GPIO_setMode(obj->gpioHandle,GPIO_Number_3,GPIO_3_Mode_EPWM2B);
// PWM1H
GPIO_setMode(obj->gpioHandle,GPIO_Number_4,GPIO_4_Mode_EPWM3A);
// PWM1L
GPIO_setMode(obj->gpioHandle,GPIO_Number_5,GPIO_5_Mode_EPWM3B);
A second function that handles this configuration is HAL_init(). The assigned addresses are changed
according to the PWM output connections:
// initialize PWM handle
obj->pwmHandle[0] = PWM_init((void *)PWM_ePWM3_BASE_ADDR,sizeof(PWM_Obj));
obj->pwmHandle[1] = PWM_init((void *)PWM_ePWM1_BASE_ADDR,sizeof(PWM_Obj));
obj->pwmHandle[2] = PWM_init((void *)PWM_ePWM2_BASE_ADDR,sizeof(PWM_Obj));
This way, every time we reference obj->pwmHandle[PWM_Number_1] we will be referencing outputs
PWM1H and PWM1L of the actual inverter, regardless of what EPWM it is assigned to. Similarly when
we reference obj->pwmHandle[PWM_Number_2] we will be referencing outputs PWM2H and
PWM2L, and when referencing obj->pwmHandle[PWM_Number_1] we will be referencing outputs
PWM3H and PWM3L. So in summary:
Finally, the start of conversion of the ADC samples needs to be changed in HAL_setupAdcs() function,
since now the conversions will be triggered by Inverter outputs PWM1H/PWM1L, which are now
assigned to f2802x: EPWM3A/EPWM3B. To change this, the following code snippet shows a highlighter
text, which changes from a value of ADC_SocTrigSrc_EPWM1_ADCSOCA to a value of
ADC_SocTrigSrc_EPWM3_ADCSOCA:
//configure the SOCs for customer board
// sample the first sample twice due to errata sprz342f
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_0,ADC_SocChanNumber_B1);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_0,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_0,ADC_SocSampleDelay_7_cycles);
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_1,ADC_SocChanNumber_B1);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_1,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_1,ADC_SocSampleDelay_7_cycles);
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_2,ADC_SocChanNumber_B3);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_2,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_2,ADC_SocSampleDelay_7_cycles);
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_3,ADC_SocChanNumber_B7);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_3,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_3,ADC_SocSampleDelay_7_cycles);
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_4,ADC_SocChanNumber_A3);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_4,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_4,ADC_SocSampleDelay_7_cycles);
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_5,ADC_SocChanNumber_A1);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_5,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_5,ADC_SocSampleDelay_7_cycles);
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_6,ADC_SocChanNumber_A0);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_6,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_6,ADC_SocSampleDelay_7_cycles);
ADC_setSocChanNumber(obj->adcHandle,ADC_SocNumber_7,ADC_SocChanNumber_A7);
ADC_setSocTrigSrc(obj->adcHandle,ADC_SocNumber_7,ADC_SocTrigSrc_EPWM3_ADCSOCA);
ADC_setSocSampleDelay(obj->adcHandle,ADC_SocNumber_7,ADC_SocSampleDelay_7_cycles);
sw\modules\hal\boards\drv8301kit_revD\f28x\f2806x\src\hal.h
sw\modules\hal\boards\drv8301kit_revD\f28x\f2806x\src\hal.c
sw\solutions\instaspin_foc\src\proj_lab03a.c
First of all, we need to configure a start of conversion channel. We will assign the start of conversion 8
to this potentiometer we want to read. The potentiometer we would like to read is connected to ADCB0 according to drv8301kit_revD schematics as shown in the following picture:
The second change is in hal.h file. A new function is created to read the potentiometer. This function
simply reads the ADC result register and scales the value to an IQ24 value. This function is listed here:
//! \brief Reads the Potentiometer
//! \param[in] handle The hardware abstraction layer (HAL) handle
//! \return The potentiometer value from _IQ(-1.0) to _IQ(1.0)
static inline _iq HAL_readPotentiometerData(HAL_Handle handle)
{
HAL_Obj *obj = (HAL_Obj *)handle;
_iq value;
// convert potentiometer from IQ12 to IQ24.
value = _IQ12toIQ((_iq)ADC_readResult(obj->adcHandle,ADC_ResultNumber_8));
return(value);
} // end of HAL_readPotentiometerData() function
The third change is in the project itself. Since we are working on proj_lab03a, we only need to declare a
global variable to store the converted value:
_iq gPotentiometer = _IQ(0.0);
And we call the new function we created in a background loop, outside of the interrupt to avoid any CPU
bandwidth hit.
gPotentiometer = HAL_readPotentiometerData(halHandle);
User can confirm that the value is being updated by adding the new global variable to the watch
window. The value type needs to be changed to IQ24 by right clicking the variable name as shown in the
following screen shot:
If we move the potentiometer all the way CCW, we see a value of 0.0 being displayed:
Our goal is to have a PWM with fixed frequency, and a global variable for duty cycle. The following files
are going to be affected based on proj_lab03a MotorWare release 13:
sw\modules\hal\boards\drv8301kit_revD\f28x\f2806x\src\hal.c
sw\modules\hal\boards\drv8301kit_revD\f28x\f2806x\src\hal.h
sw\modules\hal\boards\drv8301kit_revD\f28x\f2806x\src\hal_obj.h
sw\solutions\instaspin_foc\src\proj_lab03a.c
{
...
PWM_Handle pwmUserHandle;
} HAL_Obj;
6.4.2. Initializing the New Handle
The following code is needed to initialize the new handle, that is, to have a handle point to the
peripheral address, so every time we reference this handle, the peripheral itself is referenced.
HAL_Handle HAL_init(void *pMemory,const size_t numBytes)
{
...
// initialize PWM user handle
obj->pwmUserHandle = PWM_init((void *)PWM_ePWM8_BASE_ADDR,sizeof(PWM_Obj));
return(handle);
} // end of HAL_init() function
To verify, plot the PWM output in the scope. For example, in our test, we changed the duty cycle to 28%,
so:
And this is the PWM waveform, where it can be seen that the period is what we set it to, 100
microseconds, and the 28% of that is 28 microseconds as shown in the scope below:
As can be seen, SW2 is connected to GPIO-7 and SW1 is connected to GPIO-9. We need to configure the
GPIOs in HAL_setupGpios() function of hal.c file to allow those pins to be inputs:
// Push Button SW2
GPIO_setMode(obj->gpioHandle,GPIO_Number_7,GPIO_7_Mode_GeneralPurpose);
// Push Button SW1
GPIO_setMode(obj->gpioHandle,GPIO_Number_9,GPIO_9_Mode_GeneralPurpose);
We also need to define a function in hal.h that reads a GPIO as follows:
//! \brief Reads the specified GPIO pin
//! \details Takes in the enumeration GPIO_Number_e and reads that GPIO
//! \param[in] handle The hardware abstraction layer (HAL) handle
//! \param[in] gpioNumber The GPIO number
static inline bool HAL_readGpio(HAL_Handle handle,const GPIO_Number_e gpioNumber)
{
HAL_Obj *obj = (HAL_Obj *)handle;
// read GPIO
return(GPIO_read(obj->gpioHandle,gpioNumber));
} // end of HAL_readGpio() function
Then, we add two HAL_ definitions that link a switch with a specific GPIO. This is done so that in the top
level source file (in this case proj_lab03a.c) there is no reference to specific GPIO numbers. The reason
for this is to allow the top level code to be fully portable to other platforms, where the push buttons
might be connected to different GPIOs.
//! \brief Defines the GPIO pin number for drv8301kit_revD Switch 1
//!
#define HAL_GPIO_SW1 GPIO_Number_9
//! \brief Defines the GPIO pin number for drv8301kit_revD Switch 2
//!
#define HAL_GPIO_SW2 GPIO_Number_7
From the project main file, we need to add two global variables as follows, where we will store the state
of the push buttons:
bool gSw1;
bool gSw2;
The final step is to call the function we created, and assign the returned value in our new global
variables:
gSw1 = HAL_readGpio(halHandle, HAL_GPIO_SW1);
gSw2 = HAL_readGpio(halHandle, HAL_GPIO_SW2);
If we add these global variables to the watch window, we will see that when we press the buttons, the
state of the variables will change:
sw\drivers\pie\src\32b\f28x\f2806x\pie.c
sw\drivers\pie\src\32b\f28x\f2806x\pie.h
sw\modules\hal\boards\drv8301kit_revD\f28x\f2806x\src\hal.c
sw\modules\hal\boards\drv8301kit_revD\f28x\f2806x\src\hal.h
sw\solutions\instaspin_foc\src\proj_lab03a.c
PIE_enableTimer0Int(obj->pieHandle);
ENABLE_PROTECTED_REGISTER_WRITE_MODE;
pie->TINT0 = &timer0ISR;
pie->ADCINT1 = &mainISR;
DISABLE_PROTECTED_REGISTER_WRITE_MODE;
return;
} // end of HAL_initIntVectorTable() function
6.6.7. Implement the New Timer 0 Interrupt to Toggle an LED
Finally, we implement the interrupt service routine to toggle an LED. First of all we call the acknowledge
function to allow interrupts to happen again, and then we toggle the LED. In proj_lab03a.c we have:
return;
} // end of timer0ISR() function
Where the following definition is created in hal.h to allow portability to other platforms:
//! \brief Defines the GPIO pin number for control card LD3
//!
#define HAL_GPIO_LED3 GPIO_Number_34
When running the code, the LED labeled as LD3 in the control card will toggle every second.
7. References
MotorWare: www.ti.com/motorware
8. Revision History