Working version
This commit is contained in:
		
						commit
						6727b4fdc9
					
				| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
# This code depends on make tool being used
 | 
			
		||||
DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES} ${TESTOBJECTFILES}))
 | 
			
		||||
ifneq (${DEPFILES},)
 | 
			
		||||
include ${DEPFILES}
 | 
			
		||||
endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
bt_car: main.o joystick.o rpiPWM1.o rpiServo.o
 | 
			
		||||
	g++ -o bt_car joystick.o main.o rpiPWM1.o rpiServo.o -lwiringPi -lpthread
 | 
			
		||||
 | 
			
		||||
main.o: main.cpp
 | 
			
		||||
	g++ main.cpp -c -o main.o
 | 
			
		||||
 | 
			
		||||
joystick.o: joystick.hh joystick.cc
 | 
			
		||||
	g++ joystick.cc -c -o joystick.o
 | 
			
		||||
 | 
			
		||||
rpiPWM1.o: rpiPWM1.h rpiPWM1.cpp
 | 
			
		||||
	g++ rpiPWM1.cpp -c -o rpiPWM1.o
 | 
			
		||||
 | 
			
		||||
rpiServo.o: rpiServo.h rpiServo.cpp
 | 
			
		||||
	g++ rpiServo.cpp -c -o rpiServo.o
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm *.o bt_car
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
//
 | 
			
		||||
// Copyright Drew Noakes 2013-2016
 | 
			
		||||
 | 
			
		||||
#include "joystick.hh"
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include "unistd.h"
 | 
			
		||||
 | 
			
		||||
Joystick::Joystick()
 | 
			
		||||
{
 | 
			
		||||
  openPath("/dev/input/js0");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Joystick::Joystick(int joystickNumber)
 | 
			
		||||
{
 | 
			
		||||
  std::stringstream sstm;
 | 
			
		||||
  sstm << "/dev/input/js" << joystickNumber;
 | 
			
		||||
  openPath(sstm.str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Joystick::Joystick(std::string devicePath)
 | 
			
		||||
{
 | 
			
		||||
  openPath(devicePath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Joystick::Joystick(std::string devicePath, bool blocking)
 | 
			
		||||
{
 | 
			
		||||
  openPath(devicePath, blocking);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Joystick::openPath(std::string devicePath, bool blocking)
 | 
			
		||||
{
 | 
			
		||||
  // Open the device using either blocking or non-blocking
 | 
			
		||||
  _fd = open(devicePath.c_str(), blocking ? O_RDONLY : O_RDONLY | O_NONBLOCK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Joystick::sample(JoystickEvent* event)
 | 
			
		||||
{
 | 
			
		||||
  int bytes = read(_fd, event, sizeof(*event)); 
 | 
			
		||||
 | 
			
		||||
  if (bytes == -1)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // NOTE if this condition is not met, we're probably out of sync and this
 | 
			
		||||
  // Joystick instance is likely unusable
 | 
			
		||||
  return bytes == sizeof(*event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Joystick::isFound()
 | 
			
		||||
{
 | 
			
		||||
  return _fd >= 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Joystick::~Joystick()
 | 
			
		||||
{
 | 
			
		||||
  close(_fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::ostream& operator<<(std::ostream& os, const JoystickEvent& e)
 | 
			
		||||
{
 | 
			
		||||
  os << "type=" << static_cast<int>(e.type)
 | 
			
		||||
     << " number=" << static_cast<int>(e.number)
 | 
			
		||||
     << " value=" << static_cast<int>(e.value);
 | 
			
		||||
  return os;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,154 @@
 | 
			
		|||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
//
 | 
			
		||||
// Copyright Drew Noakes 2013-2016
 | 
			
		||||
 | 
			
		||||
#ifndef __JOYSTICK_HH__
 | 
			
		||||
#define __JOYSTICK_HH__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
#define JS_EVENT_BUTTON 0x01 // button pressed/released
 | 
			
		||||
#define JS_EVENT_AXIS   0x02 // joystick moved
 | 
			
		||||
#define JS_EVENT_INIT   0x80 // initial state of device
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encapsulates all data relevant to a sampled joystick event.
 | 
			
		||||
 */
 | 
			
		||||
class JoystickEvent
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  /** Minimum value of axes range */
 | 
			
		||||
  static const short MIN_AXES_VALUE = -32768;
 | 
			
		||||
 | 
			
		||||
  /** Maximum value of axes range */
 | 
			
		||||
  static const short MAX_AXES_VALUE = 32767;
 | 
			
		||||
  
 | 
			
		||||
  /**
 | 
			
		||||
   * The timestamp of the event, in milliseconds.
 | 
			
		||||
   */
 | 
			
		||||
  unsigned int time;
 | 
			
		||||
  
 | 
			
		||||
  /**
 | 
			
		||||
   * The value associated with this joystick event.
 | 
			
		||||
   * For buttons this will be either 1 (down) or 0 (up).
 | 
			
		||||
   * For axes, this will range between MIN_AXES_VALUE and MAX_AXES_VALUE.
 | 
			
		||||
   */
 | 
			
		||||
  short value;
 | 
			
		||||
  
 | 
			
		||||
  /**
 | 
			
		||||
   * The event type.
 | 
			
		||||
   */
 | 
			
		||||
  unsigned char type;
 | 
			
		||||
  
 | 
			
		||||
  /**
 | 
			
		||||
   * The axis/button number.
 | 
			
		||||
   */
 | 
			
		||||
  unsigned char number;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns true if this event is the result of a button press.
 | 
			
		||||
   */
 | 
			
		||||
  bool isButton()
 | 
			
		||||
  {
 | 
			
		||||
    return (type & JS_EVENT_BUTTON) != 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns true if this event is the result of an axis movement.
 | 
			
		||||
   */
 | 
			
		||||
  bool isAxis()
 | 
			
		||||
  {
 | 
			
		||||
    return (type & JS_EVENT_AXIS) != 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns true if this event is part of the initial state obtained when
 | 
			
		||||
   * the joystick is first connected to.
 | 
			
		||||
   */
 | 
			
		||||
  bool isInitialState()
 | 
			
		||||
  {
 | 
			
		||||
    return (type & JS_EVENT_INIT) != 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The ostream inserter needs to be a friend so it can access the
 | 
			
		||||
   * internal data structures.
 | 
			
		||||
   */
 | 
			
		||||
  friend std::ostream& operator<<(std::ostream& os, const JoystickEvent& e);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stream insertion function so you can do this:
 | 
			
		||||
 *    cout << event << endl;
 | 
			
		||||
 */
 | 
			
		||||
std::ostream& operator<<(std::ostream& os, const JoystickEvent& e);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents a joystick device. Allows data to be sampled from it.
 | 
			
		||||
 */
 | 
			
		||||
class Joystick
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
  void openPath(std::string devicePath, bool blocking=false);
 | 
			
		||||
  
 | 
			
		||||
  int _fd;
 | 
			
		||||
  
 | 
			
		||||
public:
 | 
			
		||||
  ~Joystick();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Initialises an instance for the first joystick: /dev/input/js0
 | 
			
		||||
   */
 | 
			
		||||
  Joystick();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Initialises an instance for the joystick with the specified,
 | 
			
		||||
   * zero-indexed number.
 | 
			
		||||
   */
 | 
			
		||||
  Joystick(int joystickNumber);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Initialises an instance for the joystick device specified.
 | 
			
		||||
   */
 | 
			
		||||
  Joystick(std::string devicePath);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Joystick objects cannot be copied
 | 
			
		||||
   */
 | 
			
		||||
  Joystick(Joystick const&) = delete;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Joystick objects can be moved
 | 
			
		||||
   */
 | 
			
		||||
  Joystick(Joystick &&) = default;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Initialises an instance for the joystick device specified and provide
 | 
			
		||||
   * the option of blocking I/O.
 | 
			
		||||
   */
 | 
			
		||||
  Joystick(std::string devicePath, bool blocking);
 | 
			
		||||
 
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns true if the joystick was found and may be used, otherwise false.
 | 
			
		||||
   */
 | 
			
		||||
  bool isFound();
 | 
			
		||||
  
 | 
			
		||||
  /**
 | 
			
		||||
   * Attempts to populate the provided JoystickEvent instance with data
 | 
			
		||||
   * from the joystick. Returns true if data is available, otherwise false.
 | 
			
		||||
   */
 | 
			
		||||
  bool sample(JoystickEvent* event);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
/*
 | 
			
		||||
 * To change this license header, choose License Headers in Project Properties.
 | 
			
		||||
 * To change this template file, choose Tools | Templates
 | 
			
		||||
 * and open the template in the editor.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * File:   main.cpp
 | 
			
		||||
 * Author: pi
 | 
			
		||||
 *
 | 
			
		||||
 * Created on 2018. május 14., 10:10
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include "joystick.hh"
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <wiringPi.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
//#include <softPwm.h>
 | 
			
		||||
//#include "rpiServo.h"
 | 
			
		||||
 | 
			
		||||
#define PIN0 0
 | 
			
		||||
#define PIN1 2
 | 
			
		||||
#define PIN2 3
 | 
			
		||||
#define PWM_RANGE 100
 | 
			
		||||
#define SERVO_PWM_RANGE 50
 | 
			
		||||
#define PWM_INIT_VALUE 0
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
    wiringPiSetup();
 | 
			
		||||
    
 | 
			
		||||
    char cmd[100];
 | 
			
		||||
    
 | 
			
		||||
    //pinMode(0, PWM_OUTPUT);
 | 
			
		||||
    int pwm0, pwm1, pwm2;
 | 
			
		||||
    pwm0 = 0;
 | 
			
		||||
    pwm1 = 0;
 | 
			
		||||
    pwm2 = 0;
 | 
			
		||||
    
 | 
			
		||||
    //int pin0 = softPwmCreate(PIN0, PWM_INIT_VALUE, PWM_RANGE);
 | 
			
		||||
    //int pin1 = softPwmCreate(PIN1, PWM_INIT_VALUE, PWM_RANGE);
 | 
			
		||||
    //int pin2 = softPwmCreate(PIN2, PWM_INIT_VALUE, SERVO_PWM_RANGE);
 | 
			
		||||
    
 | 
			
		||||
    //softPwmWrite(0, 256);
 | 
			
		||||
    
 | 
			
		||||
    //pwmWrite(PIN0, 128);
 | 
			
		||||
    
 | 
			
		||||
    Joystick joy("/dev/input/js0");
 | 
			
		||||
    //rpiServo servo;
 | 
			
		||||
    //servo.setAngle(30);
 | 
			
		||||
    
 | 
			
		||||
    while (true)
 | 
			
		||||
    {
 | 
			
		||||
        // Restrict rate
 | 
			
		||||
        usleep(100);
 | 
			
		||||
        
 | 
			
		||||
        // Attempt to sample an event from the joystick
 | 
			
		||||
        JoystickEvent event;
 | 
			
		||||
        if (joy.sample(&event))
 | 
			
		||||
        {
 | 
			
		||||
            if (event.isButton())
 | 
			
		||||
            {
 | 
			
		||||
                printf("Button %u is %s\n", event.number, event.value == 0 ? "up" : "down");
 | 
			
		||||
            }
 | 
			
		||||
            else if (event.isAxis())
 | 
			
		||||
            {
 | 
			
		||||
                printf("Axis %u is at position %d\n", event.number, event.value);
 | 
			
		||||
                if(event.number == 4) //forward
 | 
			
		||||
                {
 | 
			
		||||
                    if(pwm1 == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        pwm0 = ((event.value+32768)/2620);
 | 
			
		||||
                        //softPwmWrite(PIN0, pwm0);
 | 
			
		||||
                        snprintf(cmd, 100, "echo 1=%d > /dev/servoblaster",-pwm0+150);
 | 
			
		||||
                        system(cmd);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if(event.number == 5) //backward
 | 
			
		||||
                {
 | 
			
		||||
                    if(pwm0 == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        pwm1 = ((event.value+32768)/2620);
 | 
			
		||||
                        //softPwmWrite(PIN2, pwm1);
 | 
			
		||||
                        snprintf(cmd, 100, "echo 1=%d > /dev/servoblaster",pwm1+150);
 | 
			
		||||
                        system(cmd);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if(event.number == 0) //servo
 | 
			
		||||
                {
 | 
			
		||||
                    pwm2 = (event.value/1092)+152;
 | 
			
		||||
                    //fprintf(fp, "2=%d",pwm2);
 | 
			
		||||
                    //fflush(fp);
 | 
			
		||||
                    snprintf(cmd, 100, "echo 2=%d > /dev/servoblaster",pwm2);
 | 
			
		||||
                    system(cmd);
 | 
			
		||||
                    std::cout << "PWM: " << pwm2 << std::endl;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,431 @@
 | 
			
		|||
#include "rpiPWM1.h"
 | 
			
		||||
 | 
			
		||||
//Need to do this to be able to access these constants
 | 
			
		||||
//Private constants
 | 
			
		||||
const int rpiPWM1::BCM2708_PERI_BASE;
 | 
			
		||||
const int rpiPWM1::PWM_BASE;/* PWM controller */
 | 
			
		||||
const int rpiPWM1::CLOCK_BASE; /* Clock controller */
 | 
			
		||||
const int rpiPWM1::GPIO_BASE; /* GPIO controller */
 | 
			
		||||
const int rpiPWM1::PWM_CTL ;
 | 
			
		||||
const int rpiPWM1::PWM_RNG1;
 | 
			
		||||
const int rpiPWM1::PWM_DAT1;
 | 
			
		||||
const int rpiPWM1::PWMCLK_CNTL;
 | 
			
		||||
const int rpiPWM1::PWMCLK_DIV;
 | 
			
		||||
const int rpiPWM1::BLOCK_SIZE;
 | 
			
		||||
//Public constants
 | 
			
		||||
const int rpiPWM1::PWMMODE;
 | 
			
		||||
const int rpiPWM1::MSMODE;
 | 
			
		||||
const int rpiPWM1::ERRFREQ;
 | 
			
		||||
const int rpiPWM1::ERRCOUNT;
 | 
			
		||||
const int rpiPWM1::ERRDUTY;
 | 
			
		||||
const int rpiPWM1::ERRMODE;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * rpiPWM1::rpiPWM1()
 | 
			
		||||
 * This is the Default constructor. First, it mmaps the registers in 
 | 
			
		||||
 * Physical memory responsible for configuring GPIO, PWM and the PWM clock.
 | 
			
		||||
 * It then sets the frequency to 1KHz, PWM resolution to 256, duty 
 | 
			
		||||
 * cycle to 50% & pwm mode to 'PWMMODE'
 | 
			
		||||
 * It then calls configPWM1Pin() to configure GPIO18 to ALT5 to allow it to
 | 
			
		||||
 * output PWM1 waveforms. 
 | 
			
		||||
 * Finally  configPWM1() is called to configure the PWM1 peripheral
 | 
			
		||||
 ***********************************************************************/
 | 
			
		||||
rpiPWM1::rpiPWM1()
 | 
			
		||||
{
 | 
			
		||||
  this->clk = mapRegAddr(CLOCK_BASE);// map PWM clock registers into memory
 | 
			
		||||
  this->pwm = mapRegAddr(PWM_BASE); //map PWM registers into memory
 | 
			
		||||
  this->gpio = mapRegAddr(GPIO_BASE);// map GPIO registers into memory 
 | 
			
		||||
  this->frequency = 1000.0; // set frequency
 | 
			
		||||
  this->counts = 256; //set PWM resolution
 | 
			
		||||
  this->dutyCycle = 50.0; //set duty cycle
 | 
			
		||||
  this->mode = PWMMODE; // set pwm mode
 | 
			
		||||
  configPWM1Pin(); //configure GPIO18 to ALT15 (PWM output)
 | 
			
		||||
  configPWM1();   // configure PWM1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * rpiPWM1::rpiPWM1(double Hz, unsigned int cnts, double duty, unsigned int m)
 | 
			
		||||
 * This is the overloaded constructor. First, it mmaps the registers in 
 | 
			
		||||
 * Physical memory responsible for configuring GPIO, PWM and the PWM clock.
 | 
			
		||||
 * It then sets the frequency, PWM resolution, duty cycle & pwm mode as 
 | 
			
		||||
 * per the parameters provided.
 | 
			
		||||
 * 
 | 
			
		||||
 * It then calls configPWM1Pin() to configure GPIO18 to ALT5 to allow it to
 | 
			
		||||
 * output PWM1 waveforms. 
 | 
			
		||||
 * Finally  configPWM1() is called to configure the PWM1 peripheral
 | 
			
		||||
 * Parameters: - Hz (double) - Frequency
 | 
			
		||||
 *             - cnts (unsigned int) - PWM resolution (counts)
 | 
			
		||||
 *             - duty (double) - Duty Cycle as a percentage
 | 
			
		||||
 *             - m     (int) - PWM mode (can be either 1 for PWMMODE (rpiPWM1::PWMMODE) 
 | 
			
		||||
 *               or 2 for MSMODE (rpiPWM1::MSMODE) 
 | 
			
		||||
 ***********************************************************************/
 | 
			
		||||
rpiPWM1::rpiPWM1(double Hz, unsigned int cnts, double duty, int m)
 | 
			
		||||
{
 | 
			
		||||
  this->clk = mapRegAddr(CLOCK_BASE);
 | 
			
		||||
  this->gpio = mapRegAddr(GPIO_BASE);
 | 
			
		||||
  this->pwm = mapRegAddr(PWM_BASE);
 | 
			
		||||
  
 | 
			
		||||
   if( (cnts < 0) || (cnts > UINT_MAX) ) {
 | 
			
		||||
   printf("counts value must be between 0-%d\n",UINT_MAX);
 | 
			
		||||
   exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if ((Hz < 1e-5) || (Hz > 19200000.0f)){
 | 
			
		||||
    printf("frequency value must be between 0-19200000\n");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if( (duty < 1e-5) || (duty> 99.99999) ) {
 | 
			
		||||
    printf("dutyCycle value must be between 0-99.99999\n");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if( (m != PWMMODE) && (m != MSMODE) ) {
 | 
			
		||||
    printf("mode must be either PWMMODE(1) or MSMODE(2)\n");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  this->frequency = Hz;
 | 
			
		||||
  this->counts = cnts;
 | 
			
		||||
  this->dutyCycle = duty;
 | 
			
		||||
  this->mode = m;
 | 
			
		||||
  configPWM1Pin();
 | 
			
		||||
  configPWM1();  
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * rpiPWM1::~rpiPWM1()
 | 
			
		||||
 * Destructor - Puts all Peripheral registers in their original (reset state)
 | 
			
		||||
 * and then unmaps the portions of memory containing to register addresses
 | 
			
		||||
 * for the PWM clock, PWM and GPIO peripherals
 | 
			
		||||
 ***********************************************************************/
 | 
			
		||||
rpiPWM1::~rpiPWM1(){
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	//lets put the PWM peripheral registers in their original state   
 | 
			
		||||
	*(pwm + PWM_CTL) = 0;
 | 
			
		||||
	*(pwm + PWM_RNG1) = 0x20;
 | 
			
		||||
	*(pwm + PWM_DAT1) = 0;
 | 
			
		||||
    // unmap the memory block containing PWM registers
 | 
			
		||||
    if(munmap((void*)pwm, BLOCK_SIZE) < 0){
 | 
			
		||||
		perror("munmap (pwm) failed");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
	//lets put the PWM Clock peripheral registers in their original state  
 | 
			
		||||
    //kill PWM clock
 | 
			
		||||
    *(clk + PWMCLK_CNTL) = 0x5A000000 | (1 << 5);
 | 
			
		||||
    usleep(10);
 | 
			
		||||
 | 
			
		||||
    // wait until busy flag is set
 | 
			
		||||
    while ( (*(clk + PWMCLK_CNTL)) & 0x00000080){}
 | 
			
		||||
 | 
			
		||||
    //reset divisor
 | 
			
		||||
    *(clk + PWMCLK_DIV) = 0x5A000000;
 | 
			
		||||
    usleep(10);
 | 
			
		||||
 | 
			
		||||
    // source=osc and enable clock
 | 
			
		||||
    *(clk + PWMCLK_CNTL) = 0x5A000011;
 | 
			
		||||
 | 
			
		||||
    // unmap the memory block containing PWM Clock registers
 | 
			
		||||
    if(munmap((void*)clk, BLOCK_SIZE) < 0){
 | 
			
		||||
		perror("munmap (clk) failed");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
  
 | 
			
		||||
   //lets put the GPIO peripheral registers in their original state
 | 
			
		||||
   //first put it in input mode (default)
 | 
			
		||||
   //taken from #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
 | 
			
		||||
   *(gpio+1) &= ~(7 << 24);
 | 
			
		||||
   //then munmap
 | 
			
		||||
    if(munmap((void*)gpio, BLOCK_SIZE) < 0){
 | 
			
		||||
		perror("munmap (gpio) failed");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * unsigned int rpiPWM1::setFrequency(const double &hz)
 | 
			
		||||
 * This function sets the PWM frequency and then reinitializes the PWM1
 | 
			
		||||
 * peripheral to update the frequency. The function performs a check to 
 | 
			
		||||
 * ensure that the PWM frequency is between 0 & 19.2MHz
 | 
			
		||||
 * Parameters: hz (double) - Frequency in Hz
 | 
			
		||||
 * Return Value: 0 if successful or rpiPWM1::ERRFREQ if frequency 
 | 
			
		||||
 *               parameter is invalid 
 | 
			
		||||
 ***********************************************************************/
 | 
			
		||||
 unsigned int rpiPWM1::setFrequency(const double &hz){
 | 
			
		||||
  unsigned int retVal = 0;
 | 
			
		||||
  if (hz < 1e-5 || hz > 19200000.0f){ // make sure that Frequency is valid 
 | 
			
		||||
	retVal = ERRFREQ; //if not return error code 
 | 
			
		||||
  }
 | 
			
		||||
  else{
 | 
			
		||||
	this->frequency = hz;
 | 
			
		||||
	configPWM1();
 | 
			
		||||
  }
 | 
			
		||||
  return retVal; // return 0 for success.....
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * unsigned int rpiPWM1::setCounts(const int &cnts)
 | 
			
		||||
 * This function sets the PWM resolution and then reinitializes the PWM1
 | 
			
		||||
 * peripheral to update the PWM resolution (counts). The function performs a check to 
 | 
			
		||||
 * ensure that the PWM resolution is between 0 &  UINT_MAX (its a 32-bit register)
 | 
			
		||||
 * Parameters: cnts (unsigned int) - counts
 | 
			
		||||
 * Return Value: 0 if successful or rpiPWM1::ERRCOUNT if count value is invalid
 | 
			
		||||
 ***********************************************************************/
 | 
			
		||||
unsigned int rpiPWM1::setCounts(const unsigned int &cnts){
 | 
			
		||||
	unsigned int retVal = 0;
 | 
			
		||||
   
 | 
			
		||||
	if( (cnts < 0) || (cnts > UINT_MAX) ) {
 | 
			
		||||
		retVal = ERRCOUNT;
 | 
			
		||||
	}
 | 
			
		||||
  else{
 | 
			
		||||
    this->counts = cnts;
 | 
			
		||||
    configPWM1();    
 | 
			
		||||
  }
 | 
			
		||||
  return retVal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * unsigned int rpiPWM1::setDutyCycle(const double &duty)
 | 
			
		||||
 * This function sets the PWM DutyCycle while the PWM peripheral is running.
 | 
			
		||||
 * The function performs a check to ensure that the PWM Duty Cycle is between 
 | 
			
		||||
 * 0 & 99.99999 %
 | 
			
		||||
 * Parameters: duty (double) - Duty Cycle in %
 | 
			
		||||
 * Return Value: 0 if successful or rpiPWM1::ERRDUTY if Duty cycle is invalid
 | 
			
		||||
 ****************************************************************************/
 | 
			
		||||
unsigned int rpiPWM1::setDutyCycle(const double &duty){
 | 
			
		||||
	unsigned int bitCount = 0;
 | 
			
		||||
	unsigned int retVal = 0;
 | 
			
		||||
   
 | 
			
		||||
	if( (duty < 1e-5) || (duty > 99.99999) ) {
 | 
			
		||||
		retVal = ERRDUTY;
 | 
			
		||||
	}
 | 
			
		||||
  else {
 | 
			
		||||
		this->dutyCycle = duty;
 | 
			
		||||
		bitCount = (int) ((this->dutyCycle/100.0) * this->counts);
 | 
			
		||||
		*(pwm + PWM_DAT1) = bitCount;  
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return retVal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * unsigned int rpiPWM1::setDutyCycleForce(const double &duty, unsigned int &m)
 | 
			
		||||
 * This function firsts stops the PWM1 peripheral, sets the PWM DutyCycle 
 | 
			
		||||
 * and the PWM mode and then re-enables the PWM1 peripheral in the new mode
 | 
			
		||||
 * The function performs a check to ensure that the PWM Duty Cycle is between 
 | 
			
		||||
 * 0 & 99.99999 % and that an appropriate mode is selected.
 | 
			
		||||
 *
 | 
			
		||||
 * Parameters: duty (double) - Duty Cycle in %
 | 
			
		||||
 *             m (int) - pwm mode (rpiPWM1::PWMMODE or rpiPWM1::MSMODE)
 | 
			
		||||
 * Return Value: 0 if successful or rpiPWM1::ERRDUTY if Duty cycle is invalid 
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
unsigned int rpiPWM1::setDutyCycleForce(const double &duty, const int &m){
 | 
			
		||||
	int retVal = 0;
 | 
			
		||||
	if( (m != PWMMODE) && (m != MSMODE) ) {
 | 
			
		||||
		retVal = ERRMODE;
 | 
			
		||||
	}
 | 
			
		||||
	else if( (duty < 1e-5) || (duty > 99.99999) ) {
 | 
			
		||||
		retVal = ERRDUTY;
 | 
			
		||||
	}
 | 
			
		||||
	else{
 | 
			
		||||
		this->mode = m;
 | 
			
		||||
		this->dutyCycle = duty;
 | 
			
		||||
		// disable PWM & start from a clean slate
 | 
			
		||||
		*(pwm + PWM_CTL) = 0;
 | 
			
		||||
		// needs some time until the PWM module gets disabled, without the delay the PWM module crashs
 | 
			
		||||
		usleep(10); 
 | 
			
		||||
		// set the number of counts that constitute a period 
 | 
			
		||||
		*(pwm + PWM_RNG1) = this->counts;
 | 
			
		||||
		//set  duty cycle
 | 
			
		||||
		*(pwm + PWM_DAT1) = (int) ((this->dutyCycle/100.0) * this->counts);
 | 
			
		||||
		// start PWM1 in
 | 
			
		||||
		if(this->mode == PWMMODE) //PWM mode
 | 
			
		||||
			*(pwm + PWM_CTL) |= (1 << 0);
 | 
			
		||||
		else // M/S Mode
 | 
			
		||||
			*(pwm + PWM_CTL) |= ( (1 << 7) | (1 << 0) );  
 | 
			
		||||
		}
 | 
			
		||||
	return retVal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * unsigned int rpiPWM1::setDutyCycleCount(unsigned int &dutyCycleCnts )
 | 
			
		||||
 * This function sets the PWM DutyCycle as a function of PWM resolution, 
 | 
			
		||||
 * while the PWM peripheral is running. The function performs a check to 
 | 
			
		||||
 * ensure that the PWM Duty Cycle count value is between 0 and count
 | 
			
		||||
 * Parameters: dutyCycleCnts (unsigned int) - Duty Cycle in counts
 | 
			
		||||
 * Return Value:0 if successful or rpiPWM1::ERRDUTY if Duty cycle is invalid 
 | 
			
		||||
 ***********************************************************************/
 | 
			
		||||
unsigned int rpiPWM1::setDutyCycleCount(const unsigned int &dutyCycleCnts ){
 | 
			
		||||
   unsigned int retVal = 0;
 | 
			
		||||
   if( (dutyCycleCnts < 0) || ( dutyCycleCnts > this->counts )) {
 | 
			
		||||
    retVal = ERRDUTY;
 | 
			
		||||
	}
 | 
			
		||||
	else{
 | 
			
		||||
		this->dutyCycle = ((dutyCycleCnts * 1.0)/ this->counts) * 100.0;
 | 
			
		||||
		*(pwm + PWM_DAT1) = dutyCycleCnts;
 | 
			
		||||
	}
 | 
			
		||||
	return retVal;  
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * unsigned int rpiPWM1::setMode(unsigned int &m)
 | 
			
		||||
 * This function sets the PWM mode. The function performs a check to 
 | 
			
		||||
 * ensure that a valid PWM mode is requested.
 | 
			
		||||
 * Parameters: m (int) - pwm mode (rpiPWM1::PWMMODE or rpiPWM1::MSMODE)
 | 
			
		||||
 * Return Value: 0 if successful or rpiPWM1::ERRMODE if MODE is invalid 
 | 
			
		||||
 ********************************************************************************/
 | 
			
		||||
unsigned int rpiPWM1::setMode(const int &m){
 | 
			
		||||
	unsigned int retVal = 0;
 | 
			
		||||
	if( (m != PWMMODE) && (m != MSMODE) ) {
 | 
			
		||||
		retVal = ERRMODE;
 | 
			
		||||
	}
 | 
			
		||||
	else{
 | 
			
		||||
		this->mode = m;
 | 
			
		||||
		setDutyCycleForce(this->dutyCycle, this->mode);
 | 
			
		||||
	 }
 | 
			
		||||
	 
 | 
			
		||||
 return retVal;
 | 
			
		||||
 }
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 *These are a bunch of 'getter' functions that enable the user
 | 
			
		||||
 * to access the PWM frequency, resolution, duty cycle and mode as well
 | 
			
		||||
 * as the PWM clock divisor value.
 | 
			
		||||
 *********************************************************************/
 | 
			
		||||
 double rpiPWM1::getFrequency() const{ return this->frequency;}
 | 
			
		||||
 int rpiPWM1::getCounts() const { return this->counts;}
 | 
			
		||||
 int rpiPWM1::getDivisor() const {return this->divisor;}
 | 
			
		||||
 double  rpiPWM1::getDutyCycle() const {return this->dutyCycle;}
 | 
			
		||||
 int rpiPWM1::getMode() const{return this->mode;}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 *	volatile unsigned *rpiPWM1::mapRegAddr(unsigned long baseAddr)
 | 
			
		||||
 * This function maps a block (4KB) of physical memory into the memory of 
 | 
			
		||||
 * the calling process. It enables a user space process to access registers
 | 
			
		||||
 * in physical memory directly without having to interact with in kernel side 
 | 
			
		||||
 * code i.e. device drivers
 | 
			
		||||
 * Parameter - baseAddr (unsigned long) - this is the base address of a 4KB
 | 
			
		||||
 *             block of physical memory that will be mapped into the user
 | 
			
		||||
 *             space process memory. 
 | 
			
		||||
 * Return Value - mapped pointer in process memory 
 | 
			
		||||
 ***********************************************************************/ 
 | 
			
		||||
volatile unsigned *rpiPWM1::mapRegAddr(unsigned long baseAddr){
 | 
			
		||||
  int mem_fd = 0;
 | 
			
		||||
  void *regAddrMap = MAP_FAILED;
 | 
			
		||||
 | 
			
		||||
  /* open /dev/mem.....need to run program as root i.e. use sudo or su */
 | 
			
		||||
  if (!mem_fd) {
 | 
			
		||||
    if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
 | 
			
		||||
     perror("can't open /dev/mem");
 | 
			
		||||
      exit (1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
   /* mmap IO */
 | 
			
		||||
  regAddrMap = mmap(
 | 
			
		||||
      NULL,             //Any adddress in our space will do
 | 
			
		||||
      BLOCK_SIZE,       //Map length
 | 
			
		||||
      PROT_READ|PROT_WRITE|PROT_EXEC,// Enable reading & writting to mapped memory
 | 
			
		||||
      MAP_SHARED|MAP_LOCKED,       //Shared with other processes
 | 
			
		||||
      mem_fd,           //File to map
 | 
			
		||||
      baseAddr         //Offset to base address
 | 
			
		||||
  );
 | 
			
		||||
    
 | 
			
		||||
  if (regAddrMap == MAP_FAILED) {
 | 
			
		||||
	  perror("mmap error");
 | 
			
		||||
	  close(mem_fd);
 | 
			
		||||
	  exit (1);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if(close(mem_fd) < 0){ //No need to keep mem_fd open after mmap
 | 
			
		||||
                         //i.e. we can close /dev/mem
 | 
			
		||||
	perror("couldn't close /dev/mem file descriptor");
 | 
			
		||||
    exit(1);
 | 
			
		||||
	}	
 | 
			
		||||
  return (volatile unsigned *)regAddrMap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * void rpiPWM1::configPWM1Pin()
 | 
			
		||||
 * This function is responsible for putting GPIO18 in PWM mode by setting 
 | 
			
		||||
 * its alternate function to ALT5. It firsts make the GPIO an input 
 | 
			
		||||
 * (default state) and then switches to alternate function 5 (PWM mode)
 | 
			
		||||
 ***********************************************************************/
 | 
			
		||||
void rpiPWM1::configPWM1Pin()
 | 
			
		||||
{
 | 
			
		||||
  
 | 
			
		||||
  /*GPIO 18 in ALT5 mode for PWM0 */
 | 
			
		||||
  // Let's first set pin 18 to input
 | 
			
		||||
  //taken from #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
 | 
			
		||||
  *(gpio+1) &= ~(7 << 24);
 | 
			
		||||
  //then set it to ALT5 function PWM0
 | 
			
		||||
  //taken from #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
 | 
			
		||||
  *(gpio+1) |= (2<<24);
 | 
			
		||||
  
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * void  rpiPWM1::configPWM1()
 | 
			
		||||
 * This function configures the PWM1 peripheral. 
 | 
			
		||||
 * - It stops the PWM clock
 | 
			
		||||
 * - Calculates an appropriate divisor value based on PWM Freq and resolution
 | 
			
		||||
 * - Writes this divisor value to the PWM clock
 | 
			
		||||
 * - Enables the PWM clock
 | 
			
		||||
 * - Disables the PWM peripheral
 | 
			
		||||
 * - Writes the PWM resolution and Duty Cycle to the appropriate registers
 | 
			
		||||
 * - Enables the PWM peripheral in the requested mode
 | 
			
		||||
 * *********************************************************************/
 | 
			
		||||
void  rpiPWM1::configPWM1(){
 | 
			
		||||
  
 | 
			
		||||
  double period;
 | 
			
		||||
  double countDuration;
 | 
			
		||||
  
 | 
			
		||||
  // stop clock and waiting for busy flag doesn't work, so kill clock
 | 
			
		||||
  *(clk + PWMCLK_CNTL) = 0x5A000000 | (1 << 5);
 | 
			
		||||
  usleep(10);  
 | 
			
		||||
  
 | 
			
		||||
  // wait until busy flag is set 
 | 
			
		||||
  while ( (*(clk + PWMCLK_CNTL)) & 0x00000080){}   
 | 
			
		||||
  
 | 
			
		||||
  //calculate divisor value for PWM1 clock...base frequency is 19.2MHz
 | 
			
		||||
  period = 1.0/this->frequency;
 | 
			
		||||
  countDuration = period/(counts*1.0f);
 | 
			
		||||
  this->divisor = (int)(19200000.0f / (1.0/countDuration));
 | 
			
		||||
  
 | 
			
		||||
  if( this->divisor < 0 || this->divisor > 4095 ) {
 | 
			
		||||
    printf("divisor value must be between 0-4095\n");
 | 
			
		||||
    exit(-1);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  //set divisor
 | 
			
		||||
  *(clk + PWMCLK_DIV) = 0x5A000000 | (this->divisor << 12);
 | 
			
		||||
  
 | 
			
		||||
  // source=osc and enable clock
 | 
			
		||||
  *(clk + PWMCLK_CNTL) = 0x5A000011;
 | 
			
		||||
  
 | 
			
		||||
  // disable PWM & start from a clean slate
 | 
			
		||||
  *(pwm + PWM_CTL) = 0;
 | 
			
		||||
  
 | 
			
		||||
  // needs some time until the PWM module gets disabled, without the delay the PWM module crashs
 | 
			
		||||
  usleep(10); 
 | 
			
		||||
  
 | 
			
		||||
  // set the number of counts that constitute a period with 0 for 20 milliseconds = 320 bits
 | 
			
		||||
  *(pwm + PWM_RNG1) = this->counts; usleep(10);
 | 
			
		||||
 | 
			
		||||
   //set  duty cycle
 | 
			
		||||
  *(pwm + PWM_DAT1) = (int) ((this->dutyCycle/100.0) * this->counts); usleep(10);
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
  // start PWM1 in
 | 
			
		||||
  if(this->mode == PWMMODE) //PWM mode 
 | 
			
		||||
	*(pwm + PWM_CTL) |= (1 << 0); 
 | 
			
		||||
   else // M/S Mode
 | 
			
		||||
	*(pwm + PWM_CTL) |= ( (1 << 7) | (1 << 0) ); 
 | 
			
		||||
}
 | 
			
		||||
  
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,177 @@
 | 
			
		|||
#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
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
#include "rpiServo.h"
 | 
			
		||||
 | 
			
		||||
const int rpiServo::ERRDEG;// Error code
 | 
			
		||||
 | 
			
		||||
/*********************************************************************************
 | 
			
		||||
 * rpiServo constructor - Calls rpiPWM1's overloaded constructor
 | 
			
		||||
 * which basically creates a 50Hz PWM waveform, wih a resolution count of 3600, 
 | 
			
		||||
 * a duty cycle of 7.5% (1.5ms ON pulse every 20ms to center servo shaft at 
 | 
			
		||||
 * 90 degree position) and traditional PWM mode (rpiPWM1::MSMODE)
 | 
			
		||||
 *
 | 
			
		||||
 * By setting count resolution to 3600 counts, one 20ms period is equivalent to
 | 
			
		||||
 * to 3600 counts, 2ms ON time (180 degree position)  is equivalent to 360 counts 
 | 
			
		||||
 * and 1ms ON time (0 degree position) is equivalent to 180 counts. This gives us
 | 
			
		||||
 * 180 counts between the 0 degree position and the 180 degree position hence we 
 | 
			
		||||
 * get 1 degree rotation resolution 
 | 
			
		||||
 *
 | 
			
		||||
 *********************************************************************************/
 | 
			
		||||
 | 
			
		||||
rpiServo::rpiServo():rpiPWM1(50.0,3600,7.5,rpiPWM1::MSMODE){
 | 
			
		||||
    // 20ms = 3600 counts (Period)
 | 
			
		||||
    // 2ms = 360 counts (10% duty cycle)  => angle 180
 | 
			
		||||
    // 1ms = 180 counts (5% duty cycle) => angle 0
 | 
			
		||||
    // 1.5ms = 180+90 = 270 (7.5%  duty cycle ) => angle = 90 //servo centered
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/************************************************************************
 | 
			
		||||
 *setAngle() - function sets the angle of the servo's shaft 
 | 
			
		||||
 *Parameters - degrees - new shaft angle. Can be any value between 0 and 180 only
 | 
			
		||||
 *
 | 
			
		||||
 *return Value - If parameter 'degrees' was between 0 and 180 (inclusive) then
 | 
			
		||||
                 return value is 0 else its spiServo::ERRDEG
 | 
			
		||||
 ***********************************************************************/	
 | 
			
		||||
 | 
			
		||||
unsigned int  rpiServo::setAngle(unsigned int degrees){
 | 
			
		||||
	unsigned int retVal = 0;
 | 
			
		||||
	
 | 
			
		||||
	if((degrees < 0) || (degrees > 180 ))
 | 
			
		||||
		retVal = ERRDEG;
 | 
			
		||||
	else	
 | 
			
		||||
	this->setDutyCycleCount(180 + degrees); // call the necessary rpiPWM1 method
 | 
			
		||||
	
 | 
			
		||||
	return retVal;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
#ifndef RPI_SERVO_H
 | 
			
		||||
#define RPI_SERVO_H
 | 
			
		||||
 | 
			
		||||
#include "rpiPWM1.h"
 | 
			
		||||
 | 
			
		||||
/******************************************************************
 | 
			
		||||
 * rpiServo - This tiny C++ class is able to generate waveforms necessary to 
 | 
			
		||||
 * control servos. This class is derived from the rpiPWM1 class. 
 | 
			
		||||
 * It consists of a constructor that creates a 50Hz PWM waveform with
 | 
			
		||||
 * a 1.5ms ON time pulse...causing the servo to center itself to the
 | 
			
		||||
 * 90 degree position.
 | 
			
		||||
 *
 | 
			
		||||
 * The class also consists on a setAngle method that sets the angle of 
 | 
			
		||||
 * rotation to anything between 0 & 180 degrees
 | 
			
		||||
 * ****************************************************************/
 | 
			
		||||
 | 
			
		||||
class rpiServo : public rpiPWM1
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
public: 
 | 
			
		||||
	rpiServo();
 | 
			
		||||
	unsigned int  setAngle(unsigned int degrees);
 | 
			
		||||
	static const int ERRDEG=1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
		Reference in New Issue