[PATCH 1/1] Adding QEP driver to Beaglebone bsp

James Fitzsimons james.fitzsimons at gmail.com
Sun Aug 23 09:51:24 UTC 2020


Hi Chris,

On Sat, 22 Aug 2020 at 20:41, Chris Johns <chrisj at rtems.org> wrote:

> On 21/8/20 8:01 pm, James Fitzsimons wrote:
> ...
> > +
> > +
> > +typedef void (*bbb_eqep_timer_callback)(BBB_PWMSS);
>
> What is BBB_PWMSS used for as an argument?
>

The BBB_PWMSS argument is provided to the user call back function so that
the user knows which QEP module raised the interrupt. It is quite likely
that a user would have multiple QEP modules configured. Thinking about this
now, the call back could also have the count provided as an argument so the
user doesn't need to call the "int32_t beagle_qep_get_position(BBB_PWMSS
pwmss_id);" function from their call back handler.

>
> Could a `void* user` be passed back? It helps if the interrupt provides a
> user
> specific data pointer.
>

I'm sorry I don't understand what you mean here. Do you mean the argument
to the call back should be "void* user" which presumably would be a pointer
to an arbitrary data structure?


> > +
> > +
> > +/**
> > + * @brief This structure represents an eQEP module instance.
> > + *
> > + * The members are closely modelled on the FDT structure. *
> > + */
> > +typedef struct {
> > +  const BBB_PWMSS pwmss_id;
> > +  const uint32_t mmio_base;
> > +  const rtems_vector_number irq;
> > +  bbb_eqep_timer_callback timer_callback;
>
> Is this part of the FDT? I think some extra documentation here would be
> good to
> have.
>

Good catch - that comment needs updating. When I first added the bbb_eqep
structure it did closely resemble the structure used in the FDT
configuration (at least for Linux). However, I then added additional
members such as the rtems_vector_number and the bbb_eqep_timer_callback
which have no relationship to the FDT. I can update the comment to more
accurately reflect the current state.

>
> Can we have a `void* user;`?
>

Are you suggesting replacing "bbb_eqep_timer_callback timer_callback;" with
"void* user;"? If so yes I can do that. Is there a convention for passing
arguments to that function from the interrupt handler?

Many thanks for your review and comments!

Cheers,
James

>
> Thanks
> Chris
>
> > +  BBB_QEP_COUNT_MODE count_mode;
> > +  BBB_QEP_QUADRATURE_MODE quadrature_mode;
> > +  uint32_t invert_qa;
> > +  uint32_t invert_qb;
> > +  uint32_t invert_qi;
> > +  uint32_t invert_qs;
> > +  uint32_t swap_inputs;
> > +} bbb_eqep;
> > +
> > +
> > +/**
> > + * @brief Initialises the eQEP module of the specified PWMSS unit. This
> > + * configures the clocks, sets up the interrupt handler and unit timer,
> > + * The module is configured in Quadrature decode mode using
> > + * absolute position by default.
> > + *
> > + * @param pwmss_id The PWMSS module to configure the eQEP for.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_qep_init(BBB_PWMSS pwmss_id);
> > +
> > +/**
> > + * @brief Enables the eQEP module of the specified PWMSS unit.
> > + *
> > + * @param pwmss_id The PWMSS module which will have the eQEP function
> enabled.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_qep_enable(BBB_PWMSS pwmss_id);
> > +
> > +/**
> > + * @brief Disables the eQEP module of the specified PWMSS unit.
> > + *
> > + * @param pwmss_id The PWMSS module which will have the eQEP function
> disabled.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_qep_disable(BBB_PWMSS pwmss_id);
> > +
> > +/**
> > + * @brief Configures a given pin for use with the eQEP function of the
> supplied
> > + * PWMSS module.
> > + *
> > + * @param pin_no The P9 or P8 header pin to be configured for the eQEP
> function.
> > + * @param pwmss_id The PWMSS module which will have the eQEP function
> enabled.
> > + * @param pullup_enable If true then the internal pull up resistor on
> the
> > + * specified pin will be enabled, if false the pull down will be
> enabled.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_qep_pinmux_setup(
> > +  bbb_qep_pin pin_no,
> > +  BBB_PWMSS pwmss_id,
> > +  bool pullup_enable
> > +);
> > +
> > +/**
> > + * @brief Returns the current position value of the eQEP function for
> the
> > + * specified PWMSS module.
> > + *
> > + * @param pwmss_id Identifies which PWMSS module to return the eQEP
> position for
> > + * @return int32_t The current position value.
> > + */
> > +int32_t beagle_qep_get_position(BBB_PWMSS pwmss_id);
> > +
> > +/**
> > + * @brief Sets the initial position value of the eQEP function for the
> > + * specified PWMSS module.
> > + *
> > + * @param pwmss_id Identifies which PWMSS module to set the eQEP
> position for
> > + * @param position The value to initialise the position register with.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_qep_set_position(
> > +    BBB_PWMSS pwmss_id,
> > +    uint32_t position
> > +);
> > +
> > +/**
> > + * @brief Sets the count mode for the eQEP module.
> > + * @param pwmss_id Identifies which PWMSS module to set the eQEP count
> mode for.
> > + * @param mode One of the above modes to configure the eQEP module for.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_qep_set_count_mode(
> > +  BBB_PWMSS pwmss_id,
> > +  BBB_QEP_COUNT_MODE mode
> > +);
> > +
> > +/**
> > + * @brief Gets the currently configured count mode for the eQEP module.
> > + * @param pwmss_id Identifies which PWMSS module to set the eQEP count
> mode for.
> > + * @return An enum value representing the current count mode.
> > + */
> > +BBB_QEP_COUNT_MODE beagle_qep_get_count_mode(BBB_PWMSS pwmss_id);
> > +
> > +/**
> > + * @brief Returns the currently configured quadrature mode - either
> absolute,
> > + * or relative.
> > + * @param pwmss_id Identifies which PWMSS module to get the eQEP
> quadrature
> > + * mode for.
> > + * @return BBB_QEP_QUADRATURE_MODE The currently configured quadrature
> mode.
> > + */
> > +BBB_QEP_QUADRATURE_MODE beagle_qep_get_quadrature_mode(BBB_PWMSS
> pwmss_id);
> > +
> > +/**
> > + * @brief Sets the quadrature mode to either absolute or relative.
> > + * @param pwmss_id Identifies which PWMSS module to set the eQEP
> quadrature
> > + * mode for.
> > + * @param mode BBB_QEP_QUADRATURE_MODE Set the mode of the eQEP to
> either
> > + * absolute or relative.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_qep_set_quadrature_mode(
> > +    BBB_PWMSS pwmss_id,
> > +    BBB_QEP_QUADRATURE_MODE mode
> > +);
> > +
> > +/**
> > + * @brief Returns the the currently configured unit timer period.
> > + * @param pwmss_id Identifies which PWMSS module to get the eQEP timer
> value for
> > + * @return uint32_t The current unit timer value in nanoseconds
> > + */
> > +uint32_t beagle_eqep_get_timer_period(BBB_PWMSS pwmss_id);
> > +
> > +/**
> > + * @brief Sets the unit timer period for the eQEP module.
> > + * 0 = off, greater than zero sets the period.
> > + * @param pwmss_id Identifies which PWMSS module to set the eQEP unit
> timer for.
> > + * @param period The value in nanoseconds to set the unit timer period
> to.
> > + * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid
> pwmss_id is
> > + * supplied.
> > + */
> > +rtems_status_code beagle_eqep_set_timer_period(
> > +    BBB_PWMSS pwmss_id,
> > +    uint64_t period,
> > +    bbb_eqep_timer_callback timer_callback
> > +);
> > +
> > +#ifdef __cplusplus
> > +}
> > +#endif /* __cplusplus */
> > +
> > +#endif /* LIBBSP_ARM_BEAGLE_QEP_H */
> > diff --git a/bsps/arm/beagle/pwm/pwm.c b/bsps/arm/beagle/pwm/pwm.c
> > index 0bc5d125bf..4bd09293e8 100644
> > --- a/bsps/arm/beagle/pwm/pwm.c
> > +++ b/bsps/arm/beagle/pwm/pwm.c
> > @@ -23,6 +23,7 @@
> >  #include <bsp/gpio.h>
> >  #include <bsp/bbb-gpio.h>
> >  #include <bsp.h>
> > +#include <bsp/pwmss.h>
> >  #include <bsp/bbb-pwm.h>
> >  #include <bsp/beagleboneblack.h>
> >
> > @@ -38,7 +39,7 @@
> >   * @param pwm_id It is the instance number of EPWM of pwm sub system.
> >   *
> >   * @return Base Address of respective pwm instant.
> > -*/
> > + */
> >  static uint32_t select_pwm(BBB_PWMSS pwm_id)
> >  {
> >    uint32_t baseAddr=0;
> > @@ -102,9 +103,9 @@ bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no,
> BBB_PWMSS pwm_id)
> >       } else if (pin_no == BBB_P8_36_1A) {
> >         REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(10)) =
> BBB_MUXMODE(BBB_P8_36_MUX_PWM);
> >       } else if (pin_no == BBB_P9_14_1A) {
> > -       REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(2)) =
> BBB_MUXMODE(BBB_P9_14_MUX_PWM);
> > +       REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A2) =
> BBB_MUXMODE(BBB_P9_14_MUX_PWM);
> >       } else if (pin_no == BBB_P9_16_1B) {
> > -       REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(3)) =
> BBB_MUXMODE(BBB_P9_16_MUX_PWM);
> > +       REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A3) =
> BBB_MUXMODE(BBB_P9_16_MUX_PWM);
> >       } else {
> >         is_valid = false;
> >          }
> > @@ -181,63 +182,6 @@ static bool pwm_clock_enable(BBB_PWMSS pwm_id)
> >    return status;
> >  }
> >
> > -/**
> > - * @brief   This function configures the L3 and L4_PER system clocks.
> > - *          It also configures the system clocks for the specified
> ePWMSS
> > - *          instance.
> > - *
> > - * @param   pwmss_id    The instance number of ePWMSS whose system
> clocks
> > - *                         have to be configured.
> > - *
> > - * 'pwmss_id' can take one of the following values:
> > - * (0 <= pwmss_id <= 2)
> > - *
> > - * @return  True if successful
> > - *          False if Unsuccessful
> > - */
> > -static bool pwmss_module_clk_config(BBB_PWMSS pwmss_id)
> > -{
> > -  bool is_valid = true;
> > -
> > -  if(pwmss_id == BBB_PWMSS0) {
> > -    const uint32_t is_functional =
> AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST_FUNC <<
> > -                         AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST_SHIFT;
> > -    const uint32_t clkctrl = AM335X_CM_PER_ADDR +
> AM335X_CM_PER_EPWMSS0_CLKCTRL;
> > -    const uint32_t idle_bits = AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST;
> > -    const uint32_t is_enable =
> AM335X_CM_PER_EPWMSS0_CLKCTRL_MODULEMODE_ENABLE;
> > -    const uint32_t module_mode =
> AM335X_CM_PER_EPWMSS0_CLKCTRL_MODULEMODE;
> > -
> > -    REG(clkctrl) |= is_enable;
> > -    while((REG(clkctrl) & module_mode) != is_enable);
> > -    while((REG(clkctrl) & idle_bits) != is_functional);
> > -    }
> > -    else if(pwmss_id == BBB_PWMSS1) {
> > -    const uint32_t is_functional =
> AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST_FUNC <<
> > -                         AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST_SHIFT;
> > -    const uint32_t clkctrl = AM335X_CM_PER_ADDR +
> AM335X_CM_PER_EPWMSS1_CLKCTRL;
> > -    const uint32_t idle_bits = AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST;
> > -    const uint32_t is_enable =
> AM335X_CM_PER_EPWMSS1_CLKCTRL_MODULEMODE_ENABLE;
> > -    const uint32_t module_mode =
> AM335X_CM_PER_EPWMSS1_CLKCTRL_MODULEMODE;
> > -
> > -    REG(clkctrl) |= is_enable;
> > -    while((REG(clkctrl) & module_mode) != is_enable);
> > -    while((REG(clkctrl) & idle_bits) != is_functional);
> > -    } else if(pwmss_id == BBB_PWMSS2) {
> > -    const uint32_t is_functional =
> AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST_FUNC <<
> > -                         AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST_SHIFT;
> > -    const uint32_t clkctrl = AM335X_CM_PER_ADDR +
> AM335X_CM_PER_EPWMSS2_CLKCTRL;
> > -    const uint32_t idle_bits = AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST;
> > -    const uint32_t is_enable =
> AM335X_CM_PER_EPWMSS2_CLKCTRL_MODULEMODE_ENABLE;
> > -    const uint32_t module_mode =
> AM335X_CM_PER_EPWMSS2_CLKCTRL_MODULEMODE;
> > -
> > -    REG(clkctrl) |= is_enable;
> > -    while((REG(clkctrl) & module_mode) != is_enable);
> > -    while((REG(clkctrl) & idle_bits) != is_functional);
> > -    } else
> > -     is_valid = false;
> > -  return is_valid;
> > -}
> > -
> >  bool beagle_pwm_init(BBB_PWMSS pwmss_id)
> >  {
> >    const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT;
> > diff --git a/bsps/arm/beagle/pwmss/pwmss.c
> b/bsps/arm/beagle/pwmss/pwmss.c
> > new file mode 100644
> > index 0000000000..c4d36d8ae1
> > --- /dev/null
> > +++ b/bsps/arm/beagle/pwmss/pwmss.c
> > @@ -0,0 +1,64 @@
> > +/**
> > + * @file
> > + *
> > + * @ingroup arm_beagle
> > + *
> > + * @brief Support for eQEP for the BeagleBone Black.
> > + */
> > +
> > +/**
> > + * Copyright (c) 2020 James Fitzsimons <james.fitzsimons at gmail.com>
> > + *
> > + * The license and distribution terms for this file may be
> > + * found in the file LICENSE in this distribution or at
> > + * http://www.rtems.org/license/LICENSE.
> > + */
> > +
> > +#include <libcpu/am335x.h>
> > +#include <stdio.h>
> > +#include <bsp.h>
> > +#include <bsp/pwmss.h>
> > +#include <bsp/beagleboneblack.h>
> > +
> > +
> > +/**
> > + * @brief   This function configures the L3 and L4_PER system clocks.
> > + *          It also configures the system clocks for the specified
> ePWMSS
> > + *          instance.
> > + *
> > + * @param   pwmss_id    The instance number of ePWMSS whose system
> clocks
> > + *                         have to be configured.
> > + *
> > + * 'pwmss_id' can take one of the following values:
> > + * (0 <= pwmss_id <= 2)
> > + *
> > + * @return  True if successful
> > + *          False if Unsuccessful
> > + */
> > +rtems_status_code pwmss_module_clk_config(BBB_PWMSS pwmss_id)
> > +{
> > +    uint32_t clkctrl;
> > +
> > +    /* calculate the address of the clock control register for the PWMSS
> > +     * module we are configuring */
> > +    if(pwmss_id == BBB_PWMSS0) {
> > +        clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS0_CLKCTRL;
> > +    } else if(pwmss_id == BBB_PWMSS1) {
> > +        clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS1_CLKCTRL;
> > +    } else if(pwmss_id == BBB_PWMSS2) {
> > +        clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS2_CLKCTRL;
> > +    }
> > +
> > +    /* when the module is functional the IDLEST bits (16 -17) of the
> > +     * CM_PER_EPWMSSx_CLKCTRL register will be 0x0. */
> > +    const uint32_t is_functional = 0x0;
> > +    const uint32_t idle_bits = AM335X_CM_PER_EPWMSSx_CLKCTRL_IDLEST;
> > +    const uint32_t is_enable =
> AM335X_CM_PER_EPWMSSx_CLKCTRL_MODULEMODE_ENABLE;
> > +    const uint32_t module_mode =
> AM335X_CM_PER_EPWMSSx_CLKCTRL_MODULEMODE;
> > +
> > +    REG(clkctrl) |= is_enable;
> > +    while((REG(clkctrl) & module_mode) != is_enable);
> > +    while((REG(clkctrl) & idle_bits) != is_functional);
> > +
> > +    return RTEMS_SUCCESSFUL;
> > +}
> > diff --git a/bsps/arm/beagle/qep/qep.c b/bsps/arm/beagle/qep/qep.c
> > new file mode 100644
> > index 0000000000..d0fc9fc6ba
> > --- /dev/null
> > +++ b/bsps/arm/beagle/qep/qep.c
> > @@ -0,0 +1,435 @@
> > +/**
> > + * @file
> > + *
> > + * @ingroup arm_beagle
> > + *
> > + * @brief Support for eQEP for the BeagleBone Black.
> > + */
> > +
> > +/**
> > + * Copyright (c) 2020 James Fitzsimons <james.fitzsimons at gmail.com>
> > + *
> > + * The license and distribution terms for this file may be
> > + * found in the file LICENSE in this distribution or at
> > + * http://www.rtems.org/license/LICENSE.
> > + */
> > +
> > +#include <libcpu/am335x.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <bsp/gpio.h>
> > +#include <bsp/bbb-gpio.h>
> > +#include <bsp.h>
> > +#include <bsp/pwmss.h>
> > +#include <bsp/qep.h>
> > +#include <bsp/beagleboneblack.h>
> > +
> > +
> > +/**
> > + * @brief Represents all the PWMSS QEP modules and their default values.
> > + */
> > +static bbb_eqep bbb_eqep_table[ BBB_PWMSS_COUNT ] =
> > +{
> > + {
> > +  .pwmss_id = BBB_PWMSS0,
> > +  .mmio_base = AM335X_EQEP_0_REGS,
> > +  .irq = AM335X_INT_eQEP0INT,
> > +  .timer_callback = NULL,
> > +  .count_mode = QUADRATURE_COUNT,
> > +  .quadrature_mode = ABSOLUTE,
> > +  .invert_qa = 0,
> > +  .invert_qb = 0,
> > +  .invert_qi = 0,
> > +  .invert_qs = 0,
> > +  .swap_inputs = 0
> > + },
> > + {
> > +  .pwmss_id = BBB_PWMSS1,
> > +  .mmio_base = AM335X_EQEP_1_REGS,
> > +  .irq = AM335X_INT_eQEP1INT,
> > +  .timer_callback = NULL,
> > +  .count_mode = QUADRATURE_COUNT,
> > +  .quadrature_mode = ABSOLUTE,
> > +  .invert_qa = 0,
> > +  .invert_qb = 0,
> > +  .invert_qi = 0,
> > +  .invert_qs = 0,
> > +  .swap_inputs = 0
> > + },
> > + {
> > +  .pwmss_id = BBB_PWMSS2,
> > +  .mmio_base = AM335X_EQEP_2_REGS,
> > +  .irq = AM335X_INT_eQEP2INT,
> > +  .timer_callback = NULL,
> > +  .count_mode = QUADRATURE_COUNT,
> > +  .quadrature_mode = ABSOLUTE,
> > +  .invert_qa = 0,
> > +  .invert_qb = 0,
> > +  .invert_qi = 0,
> > +  .invert_qs = 0,
> > +  .swap_inputs = 0
> > + }
> > +};
> > +
> > +/* eQEP Interrupt handler */
> > +static void beagle_eqep_irq_handler(void *arg)
> > +{
> > +  uint16_t flags;
> > +  bbb_eqep* eqep = arg;
> > +
> > +  /* Use the interrupt register (QFLG) mask to determine what caused the
> > +   * interrupt. */
> > +  flags = REG16(eqep->mmio_base + AM335x_EQEP_QFLG) &
> AM335x_EQEP_QFLG_MASK;
> > +  /* Check the interrupt source to see if it was a unit timer overflow
> */
> > +  if (flags & AM335x_EQEP_QFLG_UTO && eqep->timer_callback != NULL) {
> > +    /* Handle the unit timer overflow interrupt */
> > +    eqep->timer_callback(eqep->pwmss_id);
> > +  }
> > +
> > +  /* Clear interrupt flags (write back triggered flags to the clear
> register) */
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QCLR) = flags;
> > +}
> > +
> > +rtems_status_code beagle_qep_init(BBB_PWMSS pwmss_id)
> > +{
> > +  rtems_status_code sc;
> > +  uint16_t qdecctl;
> > +
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +
> > +  sc = pwmss_module_clk_config(eqep->pwmss_id);
> > +  if (sc != RTEMS_SUCCESSFUL) {
> > +    /* failed to successfully configure the PWMSS module clocks */
> > +    return sc;
> > +  }
> > +
> > +  /* This enables clock for EQEP module in PWMSS subsystem. */
> > +  REG(eqep->mmio_base + AM335X_PWMSS_CLKCONFIG) |= AM335x_EQEP_CLK_EN;
> > +
> > +  /* Setup interrupt handler */
> > +  sc = rtems_interrupt_handler_install(
> > +      eqep->irq,
> > +      NULL,
> > +      RTEMS_INTERRUPT_UNIQUE,
> > +      (rtems_interrupt_handler)beagle_eqep_irq_handler,
> > +      (void*)eqep
> > +  );
> > +
> > +  /* The QDECCTL register configures the QEP Decoder module. We use it
> to set */
> > +  /* the count mode, input inversion, channel swaps, unit timer
> interrupt etc. */
> > +  qdecctl = 0;
> > +  if (eqep->count_mode <= 3) {
> > +    qdecctl |= eqep->count_mode << 14;
> > +
> > +    /* If the count mode is UP_COUNT or DOWN_COUNT then only count on
> > +     * the rising edge. QUADRATURE_COUNT and DIRECTION_COUNT count on
> > +     * both edges.  */
> > +    if (eqep->count_mode >= 2) {
> > +      qdecctl |= AM335x_EQEP_QDECCTL_XCR;
> > +    }
> > +  }
> > +
> > +  /* Should we swap the cha and chb inputs */
> > +  if (eqep->swap_inputs == 1) {
> > +    qdecctl |= AM335x_EQEP_QDECCTL_SWAP;
> > +  }
> > +  /* Should we invert the qa input */
> > +  if (eqep->invert_qa == 1) {
> > +    qdecctl |= AM335x_EQEP_QDECCTL_QAP;
> > +  }
> > +  /* Should we invert the qb input */
> > +  if (eqep->invert_qb == 1) {
> > +    qdecctl |= AM335x_EQEP_QDECCTL_QBP;
> > +  }
> > +  /* Should we invert the index input */
> > +  if (eqep->invert_qi == 1) {
> > +    qdecctl |= AM335x_EQEP_QDECCTL_QIP;
> > +
> > +  }
> > +  /* Should we invert the strobe input */
> > +  if (eqep->invert_qs == 1) {
> > +    qdecctl |= AM335x_EQEP_QDECCTL_QSP;
> > +  }
> > +
> > +  /* Write the configured decoder control settings to the QDECCTL
> register */
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QDECCTL) = qdecctl;
> > +  /* Set the position counter initialisation register */
> > +  REG(eqep->mmio_base + AM335x_EQEP_QPOSINIT) = 0;
> > +  /* initialise the maximum position counter value */
> > +  REG(eqep->mmio_base + AM335x_EQEP_QPOSMAX) = ~0;
> > +  /* initialise the position counter register */
> > +  REG(eqep->mmio_base + AM335x_EQEP_QPOSCNT) = 0;
> > +  /* Enable Unit Time Period interrupt. */
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QEINT) |= AM335x_EQEP_QEINT_UTO;
> > +
> > +  /* The following bitmasks enable the eQEP module with:
> > +   * - the unit timer disabled
> > +   * - will latch the value in QPOSLAT to QPOSCNT upon unit timer
> overflow
> > +   * - will latch QPOSILAT on index signal.
> > +   * - Software initialisation of position counter (will be set to 0
> because
> > +   *   QPOSINIT = 0).
> > +   */
> > +  uint32_t value = AM335x_EQEP_QEPCTL_QCLM | AM335x_EQEP_QEPCTL_IEL |
> > +      AM335x_EQEP_QEPCTL_PHEN | AM335x_EQEP_QEPCTL_SWI;
> > +
> > +  /* set the enable bit of the control register */
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = value;
> > +
> > +  return RTEMS_SUCCESSFUL;
> > +}
> > +
> > +rtems_status_code beagle_qep_enable(BBB_PWMSS pwmss_id)
> > +{
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +  /* set the enable bit of the control register */
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) |=
> AM335x_EQEP_QEPCTL_PHEN;
> > +
> > +  return RTEMS_SUCCESSFUL;
> > +}
> > +
> > +rtems_status_code beagle_qep_disable(BBB_PWMSS pwmss_id)
> > +{
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +  /* clear the enable bit of the control register */
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) &=
> ~AM335x_EQEP_QEPCTL_PHEN;
> > +
> > +  return RTEMS_SUCCESSFUL;
> > +}
> > +
> > +rtems_status_code beagle_qep_pinmux_setup(
> > +    bbb_qep_pin pin_no,
> > +    BBB_PWMSS pwmss_id,
> > +    bool pullup_enable
> > +)
> > +{
> > +  rtems_status_code result = RTEMS_SUCCESSFUL;
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  /* enable internal pull up / pull down resistor in pull up mode, and
> set the
> > +   * pin as an input. */
> > +  uint32_t pin_mode =  BBB_RXACTIVE;
> > +  if ( pullup_enable ) {
> > +    pin_mode |= BBB_PU_EN;
> > +  }
> > +  // The offsets from AM335X_PADCONF_BASE (44e10000) are named after
> the mode0 mux for that pin.
> > +  if(pwmss_id == BBB_PWMSS0) {
> > +    if (pin_no == BBB_P9_25_0_STROBE) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_AHCLKX) = pin_mode |
> BBB_MUXMODE(BBB_P9_25_MUX_QEP);
> > +    } else if (pin_no == BBB_P9_27_0B_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_FSR) = pin_mode |
> BBB_MUXMODE(BBB_P9_27_MUX_QEP);
> > +    } else if (pin_no == BBB_P9_41_0_IDX) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_AXR1) = pin_mode |
> BBB_MUXMODE(BBB_P9_41_MUX_QEP);
> > +    } else if (pin_no == BBB_P9_42_0A_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_ACLKR) = pin_mode |
> BBB_MUXMODE(BBB_P9_42_MUX_QEP);
> > +    } else {
> > +      result = RTEMS_INTERNAL_ERROR;
> > +    }
> > +  } else if (pwmss_id == BBB_PWMSS1) {
> > +    if (pin_no == BBB_P8_31_1_IDX) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA14) = pin_mode |
> BBB_MUXMODE(BBB_P8_31_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_32_1_STROBE) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA15) = pin_mode |
> BBB_MUXMODE(BBB_P8_32_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_33_1B_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA13) = pin_mode |
> BBB_MUXMODE(BBB_P8_33_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_35_1A_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA12) = pin_mode |
> BBB_MUXMODE(BBB_P8_35_MUX_QEP);
> > +    } else {
> > +      result = RTEMS_INTERNAL_ERROR;
> > +    }
> > +  } else if (pwmss_id == BBB_PWMSS2) {
> > +    if (pin_no == BBB_P8_11_2B_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD13) = pin_mode |
> BBB_MUXMODE(BBB_P8_11_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_12_2A_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD12) = pin_mode |
> BBB_MUXMODE(BBB_P8_12_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_15_2_STROBE) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD15) = pin_mode |
> BBB_MUXMODE(BBB_P8_15_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_16_2_IDX) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD14) = pin_mode |
> BBB_MUXMODE(BBB_P8_16_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_39_2_IDX) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA6) = pin_mode |
> BBB_MUXMODE(BBB_P8_39_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_40_2_STROBE) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA7) = pin_mode |
> BBB_MUXMODE(BBB_P8_40_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_41_2A_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA4) = pin_mode |
> BBB_MUXMODE(BBB_P8_41_MUX_QEP);
> > +    } else if (pin_no == BBB_P8_42_2B_IN) {
> > +      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA5) = pin_mode |
> BBB_MUXMODE(BBB_P8_42_MUX_QEP);
> > +    } else {
> > +      result = RTEMS_INTERNAL_ERROR;
> > +    }
> > +  } else {
> > +    result = RTEMS_INTERNAL_ERROR;
> > +  }
> > +  return result;
> > +}
> > +
> > +int32_t beagle_qep_get_position(BBB_PWMSS pwmss_id)
> > +{
> > +  int32_t position = 0;
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return -1;
> > +  }
> > +  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +
> > +  if (eqep->quadrature_mode == ABSOLUTE) {
> > +    /* return the current value of the QPOSCNT register */
> > +    position = REG(eqep->mmio_base + AM335x_EQEP_QPOSCNT);
> > +  } else if (eqep->quadrature_mode == RELATIVE) {
> > +    /* return the latched value from the last unit timer interrupt */
> > +    position = REG(eqep->mmio_base + AM335x_EQEP_QPOSLAT);
> > +  }
> > +
> > +  return position;
> > +}
> > +
> > +rtems_status_code beagle_qep_set_position(BBB_PWMSS pwmss_id, uint32_t
> position)
> > +{
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +  /* setting the position only really makes sense in ABSOLUTE mode. */
> > +  if (eqep->quadrature_mode == ABSOLUTE) {
> > +    REG(eqep->mmio_base + AM335x_EQEP_QPOSCNT) = position;
> > +  }
> > +
> > +  return RTEMS_SUCCESSFUL;
> > +}
> > +
> > +rtems_status_code beagle_qep_set_count_mode(
> > +    BBB_PWMSS pwmss_id,
> > +    BBB_QEP_COUNT_MODE mode
> > +)
> > +{
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +  eqep->count_mode = mode;
> > +
> > +  return RTEMS_SUCCESSFUL;
> > +}
> > +
> > +BBB_QEP_COUNT_MODE beagle_qep_get_count_mode(BBB_PWMSS pwmss_id)
> > +{
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +
> > +  return eqep->count_mode;
> > +}
> > +
> > +rtems_status_code beagle_qep_set_quadrature_mode(
> > +    BBB_PWMSS pwmss_id,
> > +    BBB_QEP_QUADRATURE_MODE mode
> > +)
> > +{
> > +  uint16_t qepctl;
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +
> > +  qepctl = REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL);
> > +
> > +  if (mode == ABSOLUTE) {
> > +    /*
> > +     * Disable the unit timer position reset
> > +     */
> > +    qepctl &= ~AM335x_EQEP_QEPCTL_PCRM;
> > +
> > +    eqep->quadrature_mode = ABSOLUTE;
> > +  } else if (mode == RELATIVE) {
> > +    /*
> > +     *  enable the unit timer position reset
> > +     */
> > +    qepctl |= AM335x_EQEP_QEPCTL_PCRM;
> > +
> > +    eqep->quadrature_mode = RELATIVE;
> > +  }
> > +
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = qepctl;
> > +
> > +  return RTEMS_SUCCESSFUL;
> > +}
> > +
> > +BBB_QEP_QUADRATURE_MODE beagle_qep_get_quadrature_mode(BBB_PWMSS
> pwmss_id)
> > +{
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return -1;
> > +  }
> > +  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +
> > +  return eqep->quadrature_mode;
> > +}
> > +
> > +
> > +/* Function to read the period of the unit time event timer */
> > +uint32_t beagle_eqep_get_timer_period(BBB_PWMSS pwmss_id)
> > +{
> > +  uint64_t period;
> > +  uint32_t timer_period;
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return -1;
> > +  }
> > +  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +
> > +  /* Convert from counts per interrupt back into period_ns */
> > +  period = REG(eqep->mmio_base + AM335x_EQEP_QUPRD);
> > +  period = period * NANO_SEC_PER_SEC;
> > +  timer_period = (uint32_t)(period / SYSCLKOUT);
> > +
> > +  return timer_period;
> > +}
> > +
> > +rtems_status_code beagle_eqep_set_timer_period(
> > +    BBB_PWMSS pwmss_id,
> > +    uint64_t period,
> > +    bbb_eqep_timer_callback timer_callback
> > +)
> > +{
> > +  uint16_t qepctl;
> > +  uint64_t tmp_period;
> > +  uint32_t timer_period;
> > +  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
> > +    return RTEMS_INVALID_ID;
> > +  }
> > +  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
> > +
> > +  /* Disable the unit timer before modifying its period register */
> > +  qepctl = readw(eqep->mmio_base + AM335x_EQEP_QEPCTL);
> > +  qepctl &= ~(AM335x_EQEP_QEPCTL_UTE | AM335x_EQEP_QEPCTL_QCLM);
> > +  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = qepctl;
> > +
> > +  /* Zero the unit timer counter register */
> > +  REG(eqep->mmio_base + AM335x_EQEP_QUTMR) = 0;
> > +
> > +  /* If the timer is enabled (a non-zero period has been passed) */
> > +  if (period) {
> > +    /* update the period */
> > +    tmp_period = period * SYSCLKOUT;
> > +    timer_period = (uint32_t)(tmp_period / NANO_SEC_PER_SEC);
> > +    REG(eqep->mmio_base + AM335x_EQEP_QUPRD) = timer_period;
> > +
> > +    /* Enable unit timer, and latch QPOSLAT to QPOSCNT on timer
> expiration */
> > +    qepctl |= AM335x_EQEP_QEPCTL_UTE | AM335x_EQEP_QEPCTL_QCLM;
> > +    REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = qepctl;
> > +
> > +    /* attach the unit timer interrupt handler if one has been supplied
> */
> > +    if (timer_callback != NULL) {
> > +      eqep->timer_callback = timer_callback;
> > +    }
> > +  }
> > +
> > +  return RTEMS_SUCCESSFUL;
> > +}
> > diff --git a/c/src/lib/libbsp/arm/beagle/Makefile.am
> b/c/src/lib/libbsp/arm/beagle/Makefile.am
> > index e37472373c..c9492f41a1 100644
> > --- a/c/src/lib/libbsp/arm/beagle/Makefile.am
> > +++ b/c/src/lib/libbsp/arm/beagle/Makefile.am
> > @@ -76,9 +76,15 @@ librtemsbsp_a_SOURCES +=
> ../../../../../../bsps/arm/beagle/spi/spi.c
> >  # GPIO
> >  librtemsbsp_a_SOURCES +=
> ../../../../../../bsps/arm/beagle/gpio/bbb-gpio.c
> >
> > +#pwmss shared
> > +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/pwmss/pwmss.c
> > +
> >  #pwm
> >  librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/pwm/pwm.c
> >
> > +#qep
> > +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/qep/qep.c
> > +
> >  #RTC
> >  librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/rtc/rtc.c
> >  librtemsbsp_a_SOURCES +=
> ../../../../../../bsps/shared/dev/rtc/rtc-support.c
> >
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20200823/5111a33b/attachment-0001.html>


More information about the devel mailing list