178 lines
8.2 KiB
C++
178 lines
8.2 KiB
C++
#ifndef RPIPWM1_H
|
|
#define PRIPWM1_H
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
/***********************************************************************
|
|
* Author: Hussam al-Hertani (Hertaville.com)
|
|
* Others are free to modify and use this code as they see fit so long as they
|
|
* give credit to the author.
|
|
*
|
|
* The author is not liable for any kind of damage caused by this software.
|
|
*
|
|
* Acknowledgements: This 'C++' class is based on 'C' code available from :
|
|
* - code from http://elinux.org/RPi_Low-level_peripherals
|
|
* - http://www.raspberrypi.org/phpBB3/viewtopic.php?t=8467&p=124620 for PWM initialization
|
|
* - frank's code...http://www.frank-buss.de/raspberrypi/pwm.c
|
|
* - Gertboard's C source code
|
|
*
|
|
* The rpiPWM1 class provides a direct memory mapped (register-based)
|
|
* interface to the PWM1 hardware on the Raspberry Pi's BCM2835 SOC.
|
|
* The BCM2835 SOC was two PWM subsystems, PWM1 & PWM2. This code will
|
|
* enable access to the PWM1 subsystem which outputs the PWM signal on
|
|
* GPIO18 (ALT5).
|
|
*
|
|
* The class enables setting the Frequency (Max 19.2MHz), PWM resolution(4095),
|
|
* DutyCycle and PWM Mode to be used. The Duty Cycle can be set as a
|
|
* percentage (setDutyCycle() or setDutyCycleForce()) or as a function
|
|
* of the PWM Resoultion (setDutyCycleCount())
|
|
*
|
|
* Two PWM modes exist:
|
|
* - MSMODE - This is the traditional PWM Mode i.e. if PWM Resolution
|
|
* (counts) is 1024 and the dutyCycle (Pulse on time) is 512 (in counts or 50%)
|
|
* then the waveform would look like:
|
|
* |||||||||||||||||_________________
|
|
* MSMODE is ideal for servos and other applications that
|
|
* require classical PWM waveforms
|
|
* - PWMMODE - Is a slightly modified version of the traditional PWM Mode
|
|
* described above. The duty cycle or ON time is still unchanged
|
|
* within the period but is distributed across the entire period instead
|
|
* on being concentrated in the first part of the period..i.e.if PWM Resolution
|
|
* (counts) is 1024 and the dutyCycle (Pulse on time) is 512 (in counts or 50%)
|
|
* then the waveform would look like:
|
|
* |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_
|
|
* This mode is ideal if you want to pass the signal through
|
|
* a low pass filter to obtain an analog signal equivalent to the . The even
|
|
* distibution of the ON time through the entire period
|
|
* significantly reduces ripple caused by using a simple RC
|
|
* low pass filter
|
|
*
|
|
* When setting the frequency via the constructor or 'setFrequency' method, one is strictly
|
|
* requesting a particular frequency. The code will do its best to get as close as possible to
|
|
* the requested frequency but it is likely to not create a PWM at the exact requested Frequency.
|
|
* As an example say that we want to create a PWM waveform with a resolution of 256 counts (8-bit)
|
|
* and a frequency of 8KHz....We get the divisor using the following algorithm
|
|
*
|
|
* Waveform period = 1 / 8KHz = 0.125ms
|
|
* Duration of a single count = period/256 = 0.125ms / 256 = 0.488us
|
|
* Frequency of a single count = 1 / Duration of a single count = 1 / 0.488us = 2.048MHz
|
|
* Divisor value = floor (PWM Clock Frequency / Frequency of a single count) = floor (19.2MHz / 2.048MHz) = floor(9.375) = 9
|
|
*
|
|
* With a Divisor of 9 Actual Waveform Frequency = 1/((1/(19.2MHz/9))*256) = 8.333 KHz
|
|
*
|
|
* The actual Frequency will generally deviate further from the desired frequency as the count value (PWM resolution)
|
|
* increases i.e. As an example say that we want to create a PWM waveform with a resolution of 1024 counts (10-bit)
|
|
* and the same frequency as the above example:
|
|
*
|
|
* Waveform period = 1 / 8KHz = 0.125ms
|
|
* Duration of a single count = period/1024 = 0.125ms / 1024 = 122.070ns
|
|
* Frequency of a single count = 1 / Duration of a single count = 1 / 122.070ns = 8.192MHz
|
|
* Divisor value = floor (PWM Clock Frequency / Frequency of a single count)
|
|
* = floor (19.2MHz / 8.192MHz) = floor(2.34) = 2
|
|
*
|
|
* With a Divisor of 2, Actual Waveform Frequency = 1/((1/(19.2MHz/2))*1024) = 9.375KHz
|
|
*
|
|
* DIVISOR MUST BE AT LEAST 2....SO PICK YOUR COUNT AND DESIRED FREQUENCY VALUES CAREFULLY!!!!!
|
|
* i.e MAXIMUM FREQUENCY FOR 10-BIT RESOLUTION (COUNT=1024) IS 9.375KHz
|
|
* & MAXIMUM FREQUENCY FOR 8-BIT RESOLUTION (COUNT=256) IS 37.5KHz
|
|
*
|
|
* WARNING: The RPI uses the PWM1 subsystem to produce audio. As such
|
|
* please refrain from playing audio on the RPI while this code
|
|
* is running.
|
|
* *********************************************************************/
|
|
class rpiPWM1 {
|
|
|
|
public:
|
|
rpiPWM1();
|
|
// default constructor configures GPIO18 for PWM and Frequency 1000Hz,
|
|
// PWM resolution (counts) 256, Duty Cycle 50% and PWM mode is 'PWMMODE'
|
|
rpiPWM1(double Hz, unsigned int cnts, double duty, int m);
|
|
//overloaded constructor..allows user to set initial values for Frequency,
|
|
//PWM resolution, Duty Cycle and PWM mode.
|
|
~rpiPWM1();
|
|
// Destructor....safely releases all mapped memory and puts all used peripherals
|
|
// (PWM clock, PWM peripheral and GPIO peripheral in their initial states
|
|
unsigned int setFrequency(const double &hz);
|
|
// Sets Frequency and reinitializes PWM peripheral
|
|
unsigned int setCounts(const unsigned int &cnts);
|
|
// Sets PWM resolution (counts) and reinitializes PWM peripheral
|
|
unsigned int setDutyCycle(const double &duty);
|
|
// Sets Duty Cycle as a Percentage (Fast)
|
|
unsigned int setDutyCycleCount(const unsigned int &cnts );
|
|
// Sets Duty Cycle as a count value (Fast) i.e. if counts is 1024
|
|
// and 'duty' is set to 512, a 50% duty cycle is achieved
|
|
unsigned int setDutyCycleForce(const double &duty, const int &m);
|
|
// disables PWM peripheral first,
|
|
//Sets Duty Cycle as a Percentage and PWM mode...
|
|
// then enables PWM peripheral
|
|
unsigned int setMode(const int &m);
|
|
// sets PWM mode...calls 'setDutyCycleForce()'
|
|
|
|
|
|
double getFrequency() const;
|
|
// returns current Frequency of PWM waveform
|
|
double getDutyCycle() const;
|
|
// returns current DutyCycle (as a %) of PWM waveform
|
|
int getCounts() const;
|
|
// returns PWM resolution
|
|
int getDivisor() const;
|
|
//returns Divisor value used to set the period per count
|
|
//as a function of the default PWM clock Frequency of 19.2MHz
|
|
int getMode() const;
|
|
//returns (1) if current PWM mode is 'PWMMODE' or (2) if current PWM mode
|
|
//is 'MSMODE'
|
|
|
|
//Public constants
|
|
static const int PWMMODE = 1;
|
|
static const int MSMODE = 2;
|
|
//Two PWM modes
|
|
static const int ERRFREQ = 1;
|
|
static const int ERRCOUNT = 2;
|
|
static const int ERRDUTY = 3;
|
|
static const int ERRMODE = 4;
|
|
//Error Codes
|
|
|
|
private:
|
|
//Private constants
|
|
static const int BCM2708_PERI_BASE = 0x20000000;
|
|
static const int PWM_BASE = (BCM2708_PERI_BASE + 0x20C000); /* PWM controller */
|
|
static const int CLOCK_BASE = (BCM2708_PERI_BASE + 0x101000); /* Clock controller */
|
|
static const int GPIO_BASE = (BCM2708_PERI_BASE + 0x200000); /* GPIO controller */
|
|
//Base register addresses
|
|
static const int PWM_CTL = 0;
|
|
static const int PWM_RNG1 = 4;
|
|
static const int PWM_DAT1 = 5;
|
|
static const int PWMCLK_CNTL= 40;
|
|
static const int PWMCLK_DIV = 41;
|
|
// Register addresses offsets divided by 4 (register addresses are word (32-bit) aligned
|
|
static const int BLOCK_SIZE = 4096;
|
|
// Block size.....every time mmap() is called a 4KB
|
|
//section of real physical memory is mapped into the memory of
|
|
//the process
|
|
|
|
|
|
volatile unsigned *mapRegAddr(unsigned long baseAddr);
|
|
// this function is used to map physical memory
|
|
void configPWM1Pin();
|
|
//this function sets GPIO18 to the alternat function 5 (ALT5)
|
|
// to enable the pin to output the PWM waveforms generated by PWM1
|
|
void configPWM1();
|
|
//This function is responsible for the global configuration and initialixation
|
|
//of the the PWM1 peripheral
|
|
|
|
double frequency; // PWM frequency
|
|
double dutyCycle; //PWM duty Cycle (%)
|
|
unsigned int counts; // PWM resolution
|
|
unsigned int divisor; // divisor value
|
|
int mode; // PWM mode
|
|
volatile unsigned *clk, *pwm, *gpio; // pointers to the memory mapped sections
|
|
//of our process memory
|
|
|
|
};
|
|
#endif
|