ASIF AHMAD
/
cortexm0sleepmodes
sleep library for cortex m0
pmu.c@0:e8a124690cda, 2013-09-27 (annotated)
- Committer:
- asifahmad
- Date:
- Fri Sep 27 18:13:20 2013 +0000
- Revision:
- 0:e8a124690cda
sleep
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
asifahmad | 0:e8a124690cda | 1 | |
asifahmad | 0:e8a124690cda | 2 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 3 | /*! |
asifahmad | 0:e8a124690cda | 4 | @file pca9685.c |
asifahmad | 0:e8a124690cda | 5 | @author K. Townsend (microBuilder.eu) |
asifahmad | 0:e8a124690cda | 6 | |
asifahmad | 0:e8a124690cda | 7 | @brief Drivers for NXP's PCA9685 12-bit 16-channel PWM Driver |
asifahmad | 0:e8a124690cda | 8 | |
asifahmad | 0:e8a124690cda | 9 | @section DESCRIPTION |
asifahmad | 0:e8a124690cda | 10 | |
asifahmad | 0:e8a124690cda | 11 | The PCA9685 is an I2C-bus controlled 16-channel LED controller |
asifahmad | 0:e8a124690cda | 12 | optimized for LCD Red/Green/Blue/Amber (RGBA) color backlighting |
asifahmad | 0:e8a124690cda | 13 | applications. Each LED output has its own 12-bit resolution (4096 |
asifahmad | 0:e8a124690cda | 14 | steps) fixed frequency individual PWM controller that operates at a |
asifahmad | 0:e8a124690cda | 15 | programmable frequency from 40 Hz to 1000 Hz with a duty cycle that |
asifahmad | 0:e8a124690cda | 16 | is adjustable from 0 % to 100 % to allow the LED to be set to a |
asifahmad | 0:e8a124690cda | 17 | specific brightness value. All outputs are set to the same PWM |
asifahmad | 0:e8a124690cda | 18 | frequency. |
asifahmad | 0:e8a124690cda | 19 | |
asifahmad | 0:e8a124690cda | 20 | @section LICENSE |
asifahmad | 0:e8a124690cda | 21 | |
asifahmad | 0:e8a124690cda | 22 | Software License Agreement (BSD License) |
asifahmad | 0:e8a124690cda | 23 | |
asifahmad | 0:e8a124690cda | 24 | Copyright (c) 2012, K. Townsend |
asifahmad | 0:e8a124690cda | 25 | All rights reserved. |
asifahmad | 0:e8a124690cda | 26 | |
asifahmad | 0:e8a124690cda | 27 | Redistribution and use in source and binary forms, with or without |
asifahmad | 0:e8a124690cda | 28 | modification, are permitted provided that the following conditions are met: |
asifahmad | 0:e8a124690cda | 29 | 1. Redistributions of source code must retain the above copyright |
asifahmad | 0:e8a124690cda | 30 | notice, this list of conditions and the following disclaimer. |
asifahmad | 0:e8a124690cda | 31 | 2. Redistributions in binary form must reproduce the above copyright |
asifahmad | 0:e8a124690cda | 32 | notice, this list of conditions and the following disclaimer in the |
asifahmad | 0:e8a124690cda | 33 | documentation and/or other materials provided with the distribution. |
asifahmad | 0:e8a124690cda | 34 | 3. Neither the name of the copyright holders nor the |
asifahmad | 0:e8a124690cda | 35 | names of its contributors may be used to endorse or promote products |
asifahmad | 0:e8a124690cda | 36 | derived from this software without specific prior written permission. |
asifahmad | 0:e8a124690cda | 37 | |
asifahmad | 0:e8a124690cda | 38 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY |
asifahmad | 0:e8a124690cda | 39 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
asifahmad | 0:e8a124690cda | 40 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
asifahmad | 0:e8a124690cda | 41 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY |
asifahmad | 0:e8a124690cda | 42 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
asifahmad | 0:e8a124690cda | 43 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
asifahmad | 0:e8a124690cda | 44 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
asifahmad | 0:e8a124690cda | 45 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
asifahmad | 0:e8a124690cda | 46 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
asifahmad | 0:e8a124690cda | 47 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
asifahmad | 0:e8a124690cda | 48 | */ |
asifahmad | 0:e8a124690cda | 49 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 50 | #include <string.h> |
asifahmad | 0:e8a124690cda | 51 | #include "pca9685.h" |
asifahmad | 0:e8a124690cda | 52 | #include "core/gpio/gpio.h" |
asifahmad | 0:e8a124690cda | 53 | #include "core/delay/delay.h" |
asifahmad | 0:e8a124690cda | 54 | |
asifahmad | 0:e8a124690cda | 55 | extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; |
asifahmad | 0:e8a124690cda | 56 | extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; |
asifahmad | 0:e8a124690cda | 57 | extern volatile uint32_t I2CReadLength, I2CWriteLength; |
asifahmad | 0:e8a124690cda | 58 | |
asifahmad | 0:e8a124690cda | 59 | static bool _pca9685Initialised = false; |
asifahmad | 0:e8a124690cda | 60 | static uint8_t _pca9685Address = PCA9685_ADDRESS; |
asifahmad | 0:e8a124690cda | 61 | |
asifahmad | 0:e8a124690cda | 62 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 63 | /*! |
asifahmad | 0:e8a124690cda | 64 | @brief Writes the specified number of bytes over I2C |
asifahmad | 0:e8a124690cda | 65 | */ |
asifahmad | 0:e8a124690cda | 66 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 67 | error_t pca9685WriteBytes(uint8_t reg, uint8_t *buffer, size_t length) |
asifahmad | 0:e8a124690cda | 68 | { |
asifahmad | 0:e8a124690cda | 69 | uint32_t i; |
asifahmad | 0:e8a124690cda | 70 | |
asifahmad | 0:e8a124690cda | 71 | /* Try to avoid buffer overflow */ |
asifahmad | 0:e8a124690cda | 72 | ASSERT(length <= I2C_BUFSIZE - 2, ERROR_BUFFEROVERFLOW); |
asifahmad | 0:e8a124690cda | 73 | |
asifahmad | 0:e8a124690cda | 74 | /* Fill write buffer */ |
asifahmad | 0:e8a124690cda | 75 | for ( i = 2; i < length+2; i++ ) |
asifahmad | 0:e8a124690cda | 76 | { |
asifahmad | 0:e8a124690cda | 77 | I2CMasterBuffer[i] = buffer[i-2]; |
asifahmad | 0:e8a124690cda | 78 | } |
asifahmad | 0:e8a124690cda | 79 | |
asifahmad | 0:e8a124690cda | 80 | /* Write transaction */ |
asifahmad | 0:e8a124690cda | 81 | I2CWriteLength = 2+length; |
asifahmad | 0:e8a124690cda | 82 | I2CReadLength = 0; |
asifahmad | 0:e8a124690cda | 83 | I2CMasterBuffer[0] = _pca9685Address; |
asifahmad | 0:e8a124690cda | 84 | I2CMasterBuffer[1] = reg; |
asifahmad | 0:e8a124690cda | 85 | /* Check if we got an ACK or TIMEOUT error */ |
asifahmad | 0:e8a124690cda | 86 | ASSERT_I2C_STATUS(i2cEngine()); |
asifahmad | 0:e8a124690cda | 87 | |
asifahmad | 0:e8a124690cda | 88 | return ERROR_NONE; |
asifahmad | 0:e8a124690cda | 89 | } |
asifahmad | 0:e8a124690cda | 90 | |
asifahmad | 0:e8a124690cda | 91 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 92 | /*! |
asifahmad | 0:e8a124690cda | 93 | @brief Reads the specified number of bytes over I2C |
asifahmad | 0:e8a124690cda | 94 | */ |
asifahmad | 0:e8a124690cda | 95 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 96 | error_t pca9685ReadBytes(uint8_t reg, uint8_t *buffer, size_t length) |
asifahmad | 0:e8a124690cda | 97 | { |
asifahmad | 0:e8a124690cda | 98 | uint32_t i; |
asifahmad | 0:e8a124690cda | 99 | |
asifahmad | 0:e8a124690cda | 100 | /* Try to avoid buffer overflow */ |
asifahmad | 0:e8a124690cda | 101 | ASSERT(length <= I2C_BUFSIZE, ERROR_BUFFEROVERFLOW); |
asifahmad | 0:e8a124690cda | 102 | |
asifahmad | 0:e8a124690cda | 103 | /* Read and write need to be handled in separate transactions or the |
asifahmad | 0:e8a124690cda | 104 | PCA9685 increments the current register one step ahead of where |
asifahmad | 0:e8a124690cda | 105 | we should be. */ |
asifahmad | 0:e8a124690cda | 106 | |
asifahmad | 0:e8a124690cda | 107 | /* Write transaction */ |
asifahmad | 0:e8a124690cda | 108 | I2CWriteLength = 2; |
asifahmad | 0:e8a124690cda | 109 | I2CReadLength = 0; |
asifahmad | 0:e8a124690cda | 110 | I2CMasterBuffer[0] = _pca9685Address; |
asifahmad | 0:e8a124690cda | 111 | I2CMasterBuffer[1] = reg; |
asifahmad | 0:e8a124690cda | 112 | i2cEngine(); |
asifahmad | 0:e8a124690cda | 113 | |
asifahmad | 0:e8a124690cda | 114 | /* Read transaction */ |
asifahmad | 0:e8a124690cda | 115 | I2CWriteLength = 0; |
asifahmad | 0:e8a124690cda | 116 | I2CReadLength = length; |
asifahmad | 0:e8a124690cda | 117 | I2CMasterBuffer[0] = _pca9685Address | PCA9685_READBIT; |
asifahmad | 0:e8a124690cda | 118 | /* Check if we got an ACK or TIMEOUT error */ |
asifahmad | 0:e8a124690cda | 119 | ASSERT_I2C_STATUS(i2cEngine()); |
asifahmad | 0:e8a124690cda | 120 | |
asifahmad | 0:e8a124690cda | 121 | /* Fill the buffer with the I2C response */ |
asifahmad | 0:e8a124690cda | 122 | for ( i = 0; i < length; i++ ) |
asifahmad | 0:e8a124690cda | 123 | { |
asifahmad | 0:e8a124690cda | 124 | buffer[i] = I2CSlaveBuffer[i]; |
asifahmad | 0:e8a124690cda | 125 | } |
asifahmad | 0:e8a124690cda | 126 | |
asifahmad | 0:e8a124690cda | 127 | return ERROR_NONE; |
asifahmad | 0:e8a124690cda | 128 | } |
asifahmad | 0:e8a124690cda | 129 | |
asifahmad | 0:e8a124690cda | 130 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 131 | /*! |
asifahmad | 0:e8a124690cda | 132 | @brief Writes an 8 bit value over I2C |
asifahmad | 0:e8a124690cda | 133 | */ |
asifahmad | 0:e8a124690cda | 134 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 135 | error_t pca9685Write8 (uint8_t reg, uint8_t value) |
asifahmad | 0:e8a124690cda | 136 | { |
asifahmad | 0:e8a124690cda | 137 | uint8_t buffer = value; |
asifahmad | 0:e8a124690cda | 138 | return pca9685WriteBytes(reg, &buffer, 1); |
asifahmad | 0:e8a124690cda | 139 | } |
asifahmad | 0:e8a124690cda | 140 | |
asifahmad | 0:e8a124690cda | 141 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 142 | /*! |
asifahmad | 0:e8a124690cda | 143 | @brief Reads a single byte over I2C |
asifahmad | 0:e8a124690cda | 144 | */ |
asifahmad | 0:e8a124690cda | 145 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 146 | error_t pca9685Read8(uint8_t reg, uint8_t *result) |
asifahmad | 0:e8a124690cda | 147 | { |
asifahmad | 0:e8a124690cda | 148 | return pca9685ReadBytes(reg, result, 1); |
asifahmad | 0:e8a124690cda | 149 | } |
asifahmad | 0:e8a124690cda | 150 | |
asifahmad | 0:e8a124690cda | 151 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 152 | /*! |
asifahmad | 0:e8a124690cda | 153 | @brief Initialises the I2C block |
asifahmad | 0:e8a124690cda | 154 | |
asifahmad | 0:e8a124690cda | 155 | @param address The device I2C address (left-shifted 1 bit) |
asifahmad | 0:e8a124690cda | 156 | */ |
asifahmad | 0:e8a124690cda | 157 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 158 | error_t pca9685Init(uint8_t address) |
asifahmad | 0:e8a124690cda | 159 | { |
asifahmad | 0:e8a124690cda | 160 | // Initialise I2C |
asifahmad | 0:e8a124690cda | 161 | i2cInit(I2CMASTER); |
asifahmad | 0:e8a124690cda | 162 | |
asifahmad | 0:e8a124690cda | 163 | /* Ping the I2C device first to see if it exists! */ |
asifahmad | 0:e8a124690cda | 164 | ASSERT(!(i2cCheckAddress(_pca9685Address)), ERROR_I2C_DEVICENOTFOUND); |
asifahmad | 0:e8a124690cda | 165 | |
asifahmad | 0:e8a124690cda | 166 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, 0x00)); |
asifahmad | 0:e8a124690cda | 167 | |
asifahmad | 0:e8a124690cda | 168 | _pca9685Initialised = true; |
asifahmad | 0:e8a124690cda | 169 | |
asifahmad | 0:e8a124690cda | 170 | return ERROR_NONE; |
asifahmad | 0:e8a124690cda | 171 | } |
asifahmad | 0:e8a124690cda | 172 | |
asifahmad | 0:e8a124690cda | 173 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 174 | /*! |
asifahmad | 0:e8a124690cda | 175 | @brief Sets the PWM clock frequency (40-1000Hz) |
asifahmad | 0:e8a124690cda | 176 | |
asifahmad | 0:e8a124690cda | 177 | @param freqHz Approximate frequency in Hz (40-1000) |
asifahmad | 0:e8a124690cda | 178 | */ |
asifahmad | 0:e8a124690cda | 179 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 180 | error_t pca9685SetFrequency(uint16_t freqHz) |
asifahmad | 0:e8a124690cda | 181 | { |
asifahmad | 0:e8a124690cda | 182 | uint32_t prescaleValue; |
asifahmad | 0:e8a124690cda | 183 | uint8_t oldMode, newMode; |
asifahmad | 0:e8a124690cda | 184 | |
asifahmad | 0:e8a124690cda | 185 | ASSERT(_pca9685Initialised, ERROR_DEVICENOTINITIALISED); |
asifahmad | 0:e8a124690cda | 186 | |
asifahmad | 0:e8a124690cda | 187 | if (freqHz < 40) |
asifahmad | 0:e8a124690cda | 188 | { |
asifahmad | 0:e8a124690cda | 189 | freqHz = 40; |
asifahmad | 0:e8a124690cda | 190 | } |
asifahmad | 0:e8a124690cda | 191 | |
asifahmad | 0:e8a124690cda | 192 | if (freqHz > 1000) |
asifahmad | 0:e8a124690cda | 193 | { |
asifahmad | 0:e8a124690cda | 194 | freqHz = 1000; |
asifahmad | 0:e8a124690cda | 195 | } |
asifahmad | 0:e8a124690cda | 196 | |
asifahmad | 0:e8a124690cda | 197 | // prescaleValue = round(25MHz / (4096*updateRate)) - 1 |
asifahmad | 0:e8a124690cda | 198 | prescaleValue = 25000000; // 25 MHz |
asifahmad | 0:e8a124690cda | 199 | prescaleValue /= 4096; // 12-bit |
asifahmad | 0:e8a124690cda | 200 | prescaleValue /= freqHz; |
asifahmad | 0:e8a124690cda | 201 | prescaleValue -= 1; |
asifahmad | 0:e8a124690cda | 202 | |
asifahmad | 0:e8a124690cda | 203 | ASSERT_STATUS(pca9685Read8(PCA9685_REG_MODE1, &oldMode)); |
asifahmad | 0:e8a124690cda | 204 | newMode = (oldMode & 0x7F) | 0x10; |
asifahmad | 0:e8a124690cda | 205 | |
asifahmad | 0:e8a124690cda | 206 | // Go to sleep |
asifahmad | 0:e8a124690cda | 207 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, newMode)); |
asifahmad | 0:e8a124690cda | 208 | |
asifahmad | 0:e8a124690cda | 209 | // Set prescale |
asifahmad | 0:e8a124690cda | 210 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_PRESCALE, prescaleValue & 0xFF)); |
asifahmad | 0:e8a124690cda | 211 | |
asifahmad | 0:e8a124690cda | 212 | // Wakeup |
asifahmad | 0:e8a124690cda | 213 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, oldMode)); |
asifahmad | 0:e8a124690cda | 214 | delay(5); |
asifahmad | 0:e8a124690cda | 215 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_MODE1, oldMode | 0x80)); |
asifahmad | 0:e8a124690cda | 216 | |
asifahmad | 0:e8a124690cda | 217 | return ERROR_NONE; |
asifahmad | 0:e8a124690cda | 218 | } |
asifahmad | 0:e8a124690cda | 219 | |
asifahmad | 0:e8a124690cda | 220 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 221 | /*! |
asifahmad | 0:e8a124690cda | 222 | @brief Sets the PWM output of the specified channel |
asifahmad | 0:e8a124690cda | 223 | |
asifahmad | 0:e8a124690cda | 224 | @param channel The channel number [0..15] |
asifahmad | 0:e8a124690cda | 225 | @param on The 12-bit start point (low to high transition) |
asifahmad | 0:e8a124690cda | 226 | @param off The 12-bit stop point (high to low transition) |
asifahmad | 0:e8a124690cda | 227 | */ |
asifahmad | 0:e8a124690cda | 228 | /**************************************************************************/ |
asifahmad | 0:e8a124690cda | 229 | error_t pca9685SetPWM(uint16_t channel, uint16_t on, uint16_t off) |
asifahmad | 0:e8a124690cda | 230 | { |
asifahmad | 0:e8a124690cda | 231 | ASSERT(_pca9685Initialised, ERROR_DEVICENOTINITIALISED); |
asifahmad | 0:e8a124690cda | 232 | |
asifahmad | 0:e8a124690cda | 233 | if (on > 0xFFF) |
asifahmad | 0:e8a124690cda | 234 | { |
asifahmad | 0:e8a124690cda | 235 | on = 0xFFF; |
asifahmad | 0:e8a124690cda | 236 | } |
asifahmad | 0:e8a124690cda | 237 | |
asifahmad | 0:e8a124690cda | 238 | if (off < on) |
asifahmad | 0:e8a124690cda | 239 | { |
asifahmad | 0:e8a124690cda | 240 | off = on; |
asifahmad | 0:e8a124690cda | 241 | } |
asifahmad | 0:e8a124690cda | 242 | |
asifahmad | 0:e8a124690cda | 243 | if (off > 0xFFF) |
asifahmad | 0:e8a124690cda | 244 | { |
asifahmad | 0:e8a124690cda | 245 | off = 0xFFF; |
asifahmad | 0:e8a124690cda | 246 | } |
asifahmad | 0:e8a124690cda | 247 | |
asifahmad | 0:e8a124690cda | 248 | /* Set the on and off values */ |
asifahmad | 0:e8a124690cda | 249 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_ON_L+4*channel, on & 0xFF)); |
asifahmad | 0:e8a124690cda | 250 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_ON_H+4*channel, on >> 8)); |
asifahmad | 0:e8a124690cda | 251 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_OFF_L+4*channel, off & 0xFF)); |
asifahmad | 0:e8a124690cda | 252 | ASSERT_STATUS(pca9685Write8(PCA9685_REG_LED0_OFF_H+4*channel, off >> 8)); |
asifahmad | 0:e8a124690cda | 253 | |
asifahmad | 0:e8a124690cda | 254 | return ERROR_NONE; |
asifahmad | 0:e8a124690cda | 255 | } |