ASIF AHMAD
/
cortexm0sleepmodes
sleep library for cortex m0
pmu.c
- Committer:
- asifahmad
- Date:
- 2013-09-27
- Revision:
- 0:e8a124690cda
File content as of revision 0:e8a124690cda:
/**************************************************************************/ /*! @file pca9685.c @author K. Townsend (microBuilder.eu) @brief Drivers for NXP's PCA9685 12-bit 16-channel PWM Driver @section DESCRIPTION The PCA9685 is an I2C-bus controlled 16-channel LED controller optimized for LCD Red/Green/Blue/Amber (RGBA) color backlighting applications. Each LED output has its own 12-bit resolution (4096 steps) fixed frequency individual PWM controller that operates at a programmable frequency from 40 Hz to 1000 Hz with a duty cycle that is adjustable from 0 % to 100 % to allow the LED to be set to a specific brightness value. All outputs are set to the same PWM frequency. @section LICENSE Software License Agreement (BSD License) Copyright (c) 2012, K. Townsend All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /**************************************************************************/ #include <string.h> #include "pca9685.h" #include "core/gpio/gpio.h" #include "core/delay/delay.h" extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; extern volatile uint32_t I2CReadLength, I2CWriteLength; static bool _pca9685Initialised = false; static uint8_t _pca9685Address = PCA9685_ADDRESS; /**************************************************************************/ /*! @brief Writes the specified number of bytes over I2C */ /**************************************************************************/ error_t pca9685WriteBytes(uint8_t reg, uint8_t *buffer, size_t length) { uint32_t i; /* Try to avoid buffer overflow */ ASSERT(length <= I2C_BUFSIZE - 2, ERROR_BUFFEROVERFLOW); /* Fill write buffer */ for ( i = 2; i < length+2; i++ ) { I2CMasterBuffer[i] = buffer[i-2]; } /* Write transaction */ I2CWriteLength = 2+length; I2CReadLength = 0; I2CMasterBuffer[0] = _pca9685Address; I2CMasterBuffer[1] = reg; /* Check if we got an ACK or TIMEOUT error */ ASSERT_I2C_STATUS(i2cEngine()); return ERROR_NONE; } /**************************************************************************/ /*! @brief Reads the specified number of bytes over I2C */ /**************************************************************************/ error_t pca9685ReadBytes(uint8_t reg, uint8_t *buffer, size_t length) { uint32_t i; /* Try to avoid buffer overflow */ ASSERT(length <= I2C_BUFSIZE, ERROR_BUFFEROVERFLOW); /* Read and write need to be handled in separate transactions or the PCA9685 increments the current register one step ahead of where we should be. */ /* Write transaction */ I2CWriteLength = 2; I2CReadLength = 0; I2CMasterBuffer[0] = _pca9685Address; I2CMasterBuffer[1] = reg; i2cEngine(); /* Read transaction */ I2CWriteLength = 0; I2CReadLength = length; I2CMasterBuffer[0] = _pca9685Address | PCA9685_READBIT; /* Check if we got an ACK or TIMEOUT error */ ASSERT_I2C_STATUS(i2cEngine()); /* Fill the buffer with the I2C response */ for ( i = 0; i < length; i++ ) { buffer[i] = I2CSlaveBuffer[i]; } return ERROR_NONE; } /**************************************************************************/ /*! @brief Writes an 8 bit value over I2C */ /**************************************************************************/ error_t pca9685Write8 (uint8_t reg, uint8_t value) { uint8_t buffer = value; return pca9685WriteBytes(reg, &buffer, 1); } /**************************************************************************/ /*! @brief Reads a single byte over I2C */ /**************************************************************************/ error_t pca9685Read8(uint8_t reg, uint8_t *result) { return pca9685ReadBytes(reg, result, 1); } /**************************************************************************/ /*! @brief Initialises the I2C block @param address The device I2C address (left-shifted 1 bit) */ /**************************************************************************/ error_t pca9685Init(uint8_t address) { // Initialise I2C i2cInit(I2CMASTER); /* Ping the I2C device first to see if it exists! */ ASSERT(!(i2cCheckAddress(_pca9685Address)), ERROR_I2C_DEVICENOTFOUND); ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, 0x00)); _pca9685Initialised = true; return ERROR_NONE; } /**************************************************************************/ /*! @brief Sets the PWM clock frequency (40-1000Hz) @param freqHz Approximate frequency in Hz (40-1000) */ /**************************************************************************/ error_t pca9685SetFrequency(uint16_t freqHz) { uint32_t prescaleValue; uint8_t oldMode, newMode; ASSERT(_pca9685Initialised, ERROR_DEVICENOTINITIALISED); if (freqHz < 40) { freqHz = 40; } if (freqHz > 1000) { freqHz = 1000; } // prescaleValue = round(25MHz / (4096*updateRate)) - 1 prescaleValue = 25000000; // 25 MHz prescaleValue /= 4096; // 12-bit prescaleValue /= freqHz; prescaleValue -= 1; ASSERT_STATUS(pca9685Read8(PCA9685_REG_MODE1, &oldMode)); newMode = (oldMode & 0x7F) | 0x10; // Go to sleep ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, newMode)); // Set prescale ASSERT_STATUS(pca9685Write8(PCA9685_REG_PRESCALE, prescaleValue & 0xFF)); // Wakeup ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, oldMode)); delay(5); ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, oldMode | 0x80)); return ERROR_NONE; } /**************************************************************************/ /*! @brief Sets the PWM output of the specified channel @param channel The channel number [0..15] @param on The 12-bit start point (low to high transition) @param off The 12-bit stop point (high to low transition) */ /**************************************************************************/ error_t pca9685SetPWM(uint16_t channel, uint16_t on, uint16_t off) { ASSERT(_pca9685Initialised, ERROR_DEVICENOTINITIALISED); if (on > 0xFFF) { on = 0xFFF; } if (off < on) { off = on; } if (off > 0xFFF) { off = 0xFFF; } /* Set the on and off values */ ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_ON_L+4*channel, on & 0xFF)); ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_ON_H+4*channel, on >> 8)); ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_OFF_L+4*channel, off & 0xFF)); ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_OFF_H+4*channel, off >> 8)); return ERROR_NONE; }