Zoltan Hudak / mbedPi
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers PwmOut.cpp Source File

PwmOut.cpp

00001 #include "mbed.h"
00002 #include "math.h"
00003 
00004 extern volatile uint32_t *bcm2835_pwm;
00005 extern volatile uint32_t *bcm2835_clk;
00006 
00007 /********************************************************************
00008  *
00009  *  PwmOut
00010  *
00011  ********************************************************************/
00012 /** Create a PwmOut connected to the specified pin
00013  *
00014  *  @param pin PwmOut pin to connect to
00015  */
00016 PwmOut::PwmOut(PinName pin) :
00017     _pwmPin(pin),
00018     _duty_cycle(0)
00019 {
00020     // Set the output pin to Alt Fun 5, to allow PWM channel 0 to be output there
00021     bcm2835_gpio_fsel(_pwmPin, BCM2835_GPIO_FSEL_ALT5);
00022     // Set default PWM period to 20ms (usually used by servos)
00023     period_ms(20);
00024 }
00025 
00026 /**
00027  * @brief
00028  * @note
00029  * @param
00030  * @retval
00031  */
00032 PwmOut::~PwmOut()
00033 {
00034     bcm2835_gpio_fsel(_pwmPin, BCM2835_GPIO_FSEL_INPT);
00035 }
00036 
00037 /** Set the output duty-cycle, specified as a percentage (float)
00038  *
00039  *  @param value A floating-point value representing the output duty-cycle,
00040  *    specified as a percentage. The value should lie between
00041  *    0.0f (representing on 0%) and 1.0f (representing on 100%).
00042  *    Values outside this range will be saturated to 0.0f or 1.0f.
00043  */
00044 void PwmOut::write(float value)
00045 {
00046     _duty_cycle = value;
00047 
00048     if (value < 0) {
00049         _duty_cycle = 0;
00050     }
00051 
00052     if (value > 1.0) {
00053         _duty_cycle = 1.0;
00054     }
00055 
00056     bcm2835_pwm_set_data(PWM_CHANNEL, _duty_cycle * _range);
00057     bcm2835_pwm_set_mode(PWM_CHANNEL, 1, 1);    // channel, MARKSPACE mode, active
00058 }
00059 
00060 /** Return the current output duty-cycle setting, measured as a percentage (float)
00061  *
00062  *  @returns
00063  *    A floating-point value representing the current duty-cycle being output on the pin,
00064  *    measured as a percentage. The returned value will lie between
00065  *    0.0f (representing on 0%) and 1.0f (representing on 100%).
00066  *
00067  *  @note
00068  *  This value may not match exactly the value set by a previous write().
00069  */
00070 float PwmOut::read()
00071 {
00072     return _duty_cycle;
00073 }
00074 
00075 /** Set the PWM period, specified in bcm2835PWMPulseWidth (micro/nano seconds), keeping the duty cycle the same.
00076  *  @note  Sets clock divider according to the required period.
00077  *  @param period Change the period of a PWM signal. The allowed values are:
00078  *         BCM2835_PWM_PERIOD_833_NS  ->  833.33 ns  = 1200.000  kHz
00079  */
00080 void PwmOut::period_ms(int period_ms)
00081 {
00082     _range = period_ms * 1200;
00083     bcm2835_pwm_set_clock(BCM2835_PWM_PERIOD_833_NS);   // clock pulse = 833.33 ns
00084     bcm2835_pwm_set_range(PWM_CHANNEL, _range);
00085 }
00086 
00087 /** Set the PWM period, specified in bcm2835PWMPulseWidth (micro/nano seconds), keeping the duty cycle the same.
00088  *  @note  Sets clock divider according to the required period.
00089  *  @param period Change the period of a PWM signal. The allowed values are:
00090  *         BCM2835_PWM_PERIOD_104_NS  -> 104.16 ns  = 9600.000  kHz
00091  */
00092 void PwmOut::period_us(int period_us)
00093 {
00094     _range = rintf(period_us * 9.600f);
00095     bcm2835_pwm_set_clock(BCM2835_PWM_PERIOD_104_NS);
00096     bcm2835_pwm_set_range(PWM_CHANNEL, _range);
00097 }
00098 
00099 /***********************************************************************
00100  *
00101  *  PWM
00102  *
00103  ***********************************************************************/
00104 
00105 /*! Sets the PWM clock divisor,
00106   to control the basic PWM pulse widths.
00107   \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common
00108   values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider
00109 */
00110 void bcm2835_pwm_set_clock(uint32_t divisor)
00111 {
00112     if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) {
00113         return;         /* bcm2835_init() failed or not root */
00114     }
00115 
00116     /* From Gerts code */
00117     divisor &= 0xfff;
00118 
00119     /* Stop PWM clock */
00120     bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01);
00121     bcm2835_delay(110); /* Prevents clock going slow */
00122 
00123     /* Wait for the clock to be not busy */
00124     while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0)
00125         bcm2835_delay(1);
00126 
00127     /* set the clock divider and enable PWM clock */
00128     bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12));
00129     bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11);  /* Source=osc and enable */
00130 }
00131 
00132 /*! Sets the mode of the given PWM channel,
00133   allowing you to control the PWM mode and enable/disable that channel
00134   \param[in] channel The PWM channel. 0 or 1.
00135   \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode.
00136   \param[in] enabled Set true to enable this channel and produce PWM pulses.
00137 */
00138 void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled)
00139 {
00140     if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) {
00141         return; /* bcm2835_init() failed or not root */
00142     }
00143 
00144     /* If you use the barrier here, wierd things happen, and the commands dont work */
00145 
00146     /*
00147     uint32_t    control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL);
00148 
00149     if (channel == 0) {
00150         if (markspace)
00151             control |= BCM2835_PWM0_MS_MODE;
00152         else
00153             control &= ~BCM2835_PWM0_MS_MODE;
00154         if (enabled)
00155             control |= BCM2835_PWM0_ENABLE;
00156         else
00157             control &= ~BCM2835_PWM0_ENABLE;
00158     }
00159     else
00160     if (channel == 1) {
00161         if (markspace)
00162             control |= BCM2835_PWM1_MS_MODE;
00163         else
00164             control &= ~BCM2835_PWM1_MS_MODE;
00165         if (enabled)
00166             control |= BCM2835_PWM1_ENABLE;
00167         else
00168             control &= ~BCM2835_PWM1_ENABLE;
00169     }
00170 
00171     bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control);
00172     */
00173 
00174     bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE);
00175 }
00176 
00177 /*! Sets the maximum range of the PWM output.
00178   The data value can vary between 0 and this range to control PWM output
00179   \param[in] channel The PWM channel. 0 or 1.
00180   \param[in] range The maximum value permitted for DATA.
00181 */
00182 void bcm2835_pwm_set_range(uint8_t channel, uint32_t range)
00183 {
00184     if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED)
00185         return; /* bcm2835_init() failed or not root */
00186 
00187     if (channel == 0)
00188         bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range);
00189     else
00190     if (channel == 1)
00191         bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range);
00192 }
00193 
00194 /*! Sets the PWM pulse ratio to emit to DATA/RANGE,
00195   where RANGE is set by bcm2835_pwm_set_range().
00196   \param[in] channel The PWM channel. 0 or 1.
00197   \param[in] data Controls the PWM output ratio as a fraction of the range.
00198   Can vary from 0 to RANGE.
00199 */
00200 void bcm2835_pwm_set_data(uint8_t channel, uint32_t data)
00201 {
00202     if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED)
00203         return; /* bcm2835_init() failed or not root */
00204 
00205     if (channel == 0)
00206         bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data);
00207     else
00208     if (channel == 1)
00209         bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data);
00210 }