Skip to content

Commit

Permalink
beagle: pwm polishing
Browse files Browse the repository at this point in the history
	. added a README to pwm
	. added select_pwmss() to select pwmss-generic registers, as opposed
	  to PWM-specific registers
	. added pwmss_clock_en_status(), beagle_pwmss_is_running() and pwmss_tb_clock_check()
	. other API improvements
	. style improvements
  • Loading branch information
punitvara authored and bengras committed Jul 17, 2016
1 parent 612297e commit 55bde66
Show file tree
Hide file tree
Showing 4 changed files with 568 additions and 309 deletions.
113 changes: 68 additions & 45 deletions c/src/lib/libbsp/arm/beagle/include/bbb-pwm.h
Expand Up @@ -3,11 +3,11 @@
*
* @ingroup arm_beagle
*
* @brief BeagleBone Black BSP definitions.
* @brief BeagleBone Black PWM support definitions.
*/

/**
* Copyright (c) 2016 Punit Vara <punitvara at gmail.com>
* Copyright (c) 2016 Punit Vara <punitvara@gmail.com>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
Expand All @@ -31,64 +31,74 @@ extern "C" {
#define BBB_CONTROL_CONF_GPMC_AD(n) (0x800 + (n * 4))
#define BBB_CONTROL_CONF_LCD_DATA(n) (0x8a0 + (n * 4))

#define BBB_PWMSS_COUNT 3
#define BBB_PWMSS0 0
#define BBB_PWMSS1 1
#define BBB_PWMSS2 2

#define BBB_P8_13_2B 3
#define BBB_P8_19_2A 4
#define BBB_P8_45_2A 5
#define BBB_P8_46_2B 6
#define BBB_P8_34_1B 7
#define BBB_P8_36_1A 8
#define BBB_P9_14_1A 9
#define BBB_P9_16_1B 10
#define BBB_P9_21_0B 11
#define BBB_P9_22_0A 12
#define BBB_P9_29_0B 13
#define BBB_P9_31_0A 14

#define BBB_MUX0 0
#define BBB_MUX1 1
#define BBB_MUX2 2
#define BBB_MUX3 3
#define BBB_MUX4 4
#define BBB_MUX5 5
#define BBB_MUX6 6
#define BBB_MUX7 7

#define BBB_EPWM1 1
#define BBB_EPWM2 2
#define BBB_EPWM0 0
/**
* @brief The set of possible PWM subsystem module
*
* Enumerated type to define various instance of pwm module.
*/
typedef enum{
BBB_PWMSS0 = 0,
BBB_PWMSS1,
BBB_PWMSS2,
BBB_PWMSS_COUNT
}BBB_PWMSS;

typedef enum{
BBB_P8_13_2B = 3,
BBB_P8_19_2A,
BBB_P8_45_2A,
BBB_P8_46_2B,
BBB_P8_34_1B,
BBB_P8_36_1A,
BBB_P9_14_1A,
BBB_P9_16_1B,
BBB_P9_21_0B,
BBB_P9_22_0A,
BBB_P9_29_0B,
BBB_P9_31_0A
}bbb_pwm_pin_t;

#define BBB_P8_13_MUX_PWM 4
#define BBB_P8_19_MUX_PWM 4
#define BBB_P8_45_MUX_PWM 3
#define BBB_P8_46_MUX_PWM 3
#define BBB_P8_34_MUX_PWM 2
#define BBB_P8_36_MUX_PWM 2
#define BBB_P9_14_MUX_PWM 6
#define BBB_P9_16_MUX_PWM 6
#define BBB_P9_21_MUX_PWM 3
#define BBB_P9_22_MUX_PWM 3
#define BBB_P9_29_MUX_PWM 1
#define BBB_P9_31_MUX_PWM 1
#define BBB_PWM_FREQ_THRESHOLD 0.5f

/**
* @brief BeagleBone Black PWM API.
*/

/**
* @brief This function intilize clock and pinmuxing for pwm sub system.
* @brief This function intilizes clock for pwm sub system.
*
* @param PWMSS_ID It is the instance number of EPWM of pwm sub system.
*
* @return true if successful
* @return false if not successful
*
**/
bool beagle_pwm_init(uint32_t pwmss_id);
bool beagle_pwm_init(BBB_PWMSS pwmss_id);

/* PWMSS setting
* set pulse argument of epwm module
*
* @param pwm_id : EPWMSS number , 0~2
* @param pwm_freq : frequency to be generated
* @param dutyA : Duty Cycle in ePWM A
* @param dutyB : Duty Cycle in ePWM B
* @param dutyA : Duty Cycle(in percentage) in PWM channel A
* @param dutyB : Duty Cycle(in percentage) in PWM channel B
*
* @return : 1 for success
* @return : 0 for failed
*
* @example : PWMSS_Setting(0 , 50.0f , 50.0f , 25.0f); // Generate 50HZ pwm in PWM0 ,
* @example : beagle_pwm_configure(0 , 50.0f , 50.0f , 25.0f); // Generate 50HZ pwm in PWM0 ,
* // duty cycle is 50% for ePWM0A , 25% for ePWM0B
*
* @Note :
Expand All @@ -105,12 +115,12 @@ bool beagle_pwm_init(uint32_t pwmss_id);
* Divisor = CLKDIV * HSPCLKDIV
* 1 TBPRD : 10 ns (default)
* 65535 TBPRD : 655350 ns
* 65535 TBPRD : 655350 * Divisor ns = X TBPRD : Cyclens
* 65535 TBPRD : 655350 * Divisor ns = X TBPRD : Cycle
*
* accrooding to that , we must find a Divisor value , let X nearest 65535 .
* so , Divisor must Nearest Cyclens/655350
* so , Divisor must Nearest Cycle/655350
*/
int beagle_pwmss_setting(uint32_t pwm_id, float pwm_freq, float dutyA, float dutyB);
int beagle_pwm_configure(BBB_PWMSS pwm_id, float pwm_freq, float duty_a, float duty_b);

/**
* @brief This API enables the particular PWM module.
Expand All @@ -121,21 +131,21 @@ int beagle_pwmss_setting(uint32_t pwm_id, float pwm_freq, float dutyA, float dut
* @return false if fail
*
**/
bool beagle_ehrpwm_enable(uint32_t pwmid);
bool beagle_pwm_enable(BBB_PWMSS pwmid);

/**
* @brief This API disables the HR sub-module.
* @brief This API disables the particular PWM module.
*
* @param pwmid It is the instance number of EPWM of pwm sub system.
*
* @return true if successful
* @return false if fail
*
**/
bool beagle_ehrpwm_disable(uint32_t pwmid);
bool beagle_pwm_disable(BBB_PWMSS pwmid);

/**
* @brief This function Enables pinmuxing for PWM module.
* @brief This function enables pinmuxing for PWM module.
*
* @param pin_no It is individual pin at which freuqency need to be generated.
* It should be according to pwm sub system.
Expand All @@ -146,7 +156,20 @@ bool beagle_ehrpwm_disable(uint32_t pwmid);
* @return false if fail
*
**/
bool beagle_epwm_pinmux_setup(uint32_t pin_no, uint32_t pwm_id);
bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no, BBB_PWMSS pwm_id);

/**
* @brief This function determines whether PWMSS-wide clocks enabled or not.
*
* @param pwmss_id It is the instance number of PWMSS which clocks need to be
* checked.
*
* @return true if successful
* @return false if fail
*
**/
bool beagle_pwmss_is_running(unsigned int pwmss_id);


#ifdef __cplusplus
}
Expand Down
4 changes: 4 additions & 0 deletions c/src/lib/libbsp/arm/beagle/preinstall.am
Expand Up @@ -118,6 +118,10 @@ $(PROJECT_INCLUDE)/bsp/bbb-gpio.h: include/bbb-gpio.h $(PROJECT_INCLUDE)/bsp/$(d
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/bbb-gpio.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/bbb-gpio.h

$(PROJECT_INCLUDE)/bsp/bbb-pwm.h: include/bbb-pwm.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/bbb-pwm.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/bbb-pwm.h

$(PROJECT_INCLUDE)/libcpu/arm-cp15.h: ../../../libcpu/arm/shared/include/arm-cp15.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/arm-cp15.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/arm-cp15.h
Expand Down
197 changes: 197 additions & 0 deletions c/src/lib/libbsp/arm/beagle/pwm/README
@@ -0,0 +1,197 @@
Pulse Width Modulation subsystem includes EPWM, ECAP , EQEP. There are
different instances available for each one. For PWM there are three
different individual EPWM module 0 , 1 and 2. So wherever pwmss word is
used that affects whole PWM sub system such as EPWM, ECAP and EQEP. This code
has only implementation Non high resolution PWM module. APIs for high
resolution PWM has been yet to develop.

For Each EPWM instance, has two PWM channels, e.g. EPWM0 has two channel
EPWM0A and EPWM0B. If you configure two PWM outputs(e.g. EPWM0A , EPWM0B)
in the same device, then they *must* be configured with the same frequency.
Changing frequency on one channel (e.g EPWMxA) will automatically change
frequency on another channel(e.g. EPWMxB). However, it is possible to set
different pulse-width/duty cycle to different channel at a time. So always
set the frequency first and then pulse-width/duty cycle.

For more you can refer :
http://www.ofitselfso.com/BBBCSIO/Source/PWMPortEnum.cs.html

Pulse Width Modulation uses the system frequency of Beagle Bone Black.

System frequency = SYSCLKOUT, that is, CPU clock. TBCLK = SYSCLKOUT(By Default)
SYCLKOUT = 100 MHz

Please visit following link to check why SYSCLKDIV = 100MHz:
https://groups.google.com/forum/#!topic/beagleboard/Ed2J9Txe_E4
(Refer Technical Reference Manual (TRM) Table 15-41 as well)

To generate different frequencies with the help of PWM module , SYSCLKOUT
need to be scaled down, which will act as TBCLK and TBCLK will be base clock
for the pwm subsystem.

TBCLK = SYSCLKOUT/(HSPCLKDIV * CLKDIV)

|----------------|
| clock |
SYSCLKOUT---> | |---> TBCLK
| prescale |
|----------------|
^ ^
| |
TBCTL[CLKDIV]----- ------TBCTL[HSPCLKDIV]


CLKDIV and HSPCLKDIV bits are part of the TBCTL register (Refer TRM).
CLKDIV - These bits determine part of the time-base clock prescale value.
Please use the following values of CLKDIV to scale down sysclk respectively.
0h (R/W) = /1
1h (R/W) = /2
2h (R/W) = /4
3h (R/W) = /8
4h (R/W) = /16
5h (R/W) = /32
6h (R/W) = /64
7h (R/W) = /128

These bits determine part of the time-base clock prescale value.
Please use following value of HSPCLKDIV to scale down sysclk respectively
0h (R/W) = /1
1h (R/W) = /2
2h (R/W) = /4
3h (R/W) = /6
4h (R/W) = /8
5h (R/W) = /10
6h (R/W) = /12
7h (R/W) = /14

For example, if you set CLKDIV = 3h and HSPCLKDIV= 2h Then
SYSCLKOUT will be divided by (1/8)(1/4). It means SYSCLKOUT/32

How to generate frequency ?

freq = 1/Period

TBPRD register is responsible to generate the frequency. These bits determine
the period of the time-base counter.

By default TBCLK = SYSCLKOUT = 100 MHz

Here by default period is 1/100MHz = 10 nsec

Following example shows value to be loaded into TBPRD

e.g. TBPRD = 1 = 1 count
count x Period = 1 x 1ns = 1ns
freq = 1/Period = 1 / 1ns = 100 MHz

For duty cycle CMPA and CMPB are the responsible registers.

To generate single with 50% Duty cycle & 100MHz freq.

CMPA = count x Duty Cycle
= TBPRD x Duty Cycle
= 1 x 50/100
= 0.2

The value in the active CMPA register is continuously compared to
the time-base counter (TBCNT). When the values are equal, the
counter-compare module generates a "time-base counter equal to
counter compare A" event. This event is sent to the action-qualifier
where it is qualified and converted it into one or more actions.
These actions can be applied to either the EPWMxA or the
EPWMxB output depending on the configuration of the AQCTLA and
AQCTLB registers.

List of pins for that can be used for different PWM instance :

------------------------------------------------
| EPWM2 | EPWM1 | EPWM0 |
------------------------------------------------
| BBB_P8_13_2B | BBB_P8_34_1B | BBB_P9_21_0B |
| BBB_P8_19_2A | BBB_P8_36_1A | BBB_P9_22_0A |
| BBB_P8_45_2A | BBB_P9_14_1A | BBB_P9_29_0B |
| BBB_P8_46_2B | BBB_P9_16_1B | BBB_P9_31_0A |
------------------------------------------------
BBB_P8_13_2B represents P8 Header , pin number 13 , 2nd PWM instance and B channel.

Following sample program can be used to generate 7 Hz frequency.

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/test.h>
#include <bsp.h>
#include <bsp/gpio.h>
#include <stdio.h>
#include <stdlib.h>
#include <bsp/bbb-pwm.h>

const char rtems_test_name[] = "Testing PWM driver";
rtems_printer rtems_test_printer;

static void inline delay_sec(int sec)
{
rtems_task_wake_after(sec*rtems_clock_get_ticks_per_second());
}

rtems_task Init(rtems_task_argument argument);

rtems_task Init(
rtems_task_argument ignored
)
{
rtems_test_begin();
printf("Starting PWM Testing");

/*Initialize GPIO pins in BBB*/
rtems_gpio_initialize();

/* Set P9 Header , 21 Pin number , PWM B channel and 0 PWM instance to generate frequency*/
beagle_epwm_pinmux_setup(BBB_P9_21_0B,BBB_PWMSS0);

/** Initialize clock for PWM sub system
* Turn on time base clock for PWM o instance
*/
beagle_pwm_init(BBB_PWMSS0);

float PWM_HZ = 7.0f ; /* 7 Hz */
float duty_A = 20.0f ; /* 20% Duty cycle for PWM 0_A output */
const float duty_B = 50.0f ; /* 50% Duty cycle for PWM 0_B output*/

/*Note: Always check whether pwmss clocks are enabled or not before configuring PWM*/
bool is_running = beagle_pwmss_is_running(BBB_PWMSS2);

if(is_running) {

/*To analyse the two different duty cycle Output should be observed at P8_45 and P8_46 pin number */
beagle_pwm_configure(BBB_PWMSS0, PWM_HZ ,duty_A , duty_B);
printf("PWM enable for 10s ....\n");

/*Set Up counter and enable pwm module */
beagle_pwm_enable(BBB_PWMSS0);
delay_sec(10);

/*freeze the counter and disable pwm module*/
beagle_epwm_disable(BBB_PWMSS0);
}
}

/* NOTICE: the clock driver is enabled */
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER

#define CONFIGURE_MAXIMUM_TASKS 1
#define CONFIGURE_USE_DEVFS_AS_BASE_FILESYSTEM

#define CONFIGURE_MAXIMUM_SEMAPHORES 1

#define CONFIGURE_RTEMS_INIT_TASKS_TABLE

#define CONFIGURE_EXTRA_TASK_STACKS (2 * RTEMS_MINIMUM_STACK_SIZE)

#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION

#define CONFIGURE_INIT
#include <rtems/confdefs.h>

0 comments on commit 55bde66

Please sign in to comment.