A feature complete driver for the PCA9952/55 LED driver from NXP.
Dependents: PCA9955_HelloWorld
PCA9955.cpp
- Committer:
- neilt6
- Date:
- 2014-05-06
- Revision:
- 11:dbf20a128eb6
- Parent:
- 10:c8c96c894ec6
- Child:
- 12:2b8adb10c605
File content as of revision 11:dbf20a128eb6:
/* PCA9955 Driver Library * Copyright (c) 2014 Neil Thiessen * * 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. */ #include "PCA9955.h" PCA9955::PCA9955(PinName sda, PinName scl, Address addr, int hz) : m_I2C(sda, scl), m_ADDR((int)addr) { //Set the I2C bus frequency m_I2C.frequency(hz); } bool PCA9955::open() { //Probe for the PCA9952/55 using a Zero Length Transfer if (!m_I2C.write(m_ADDR, NULL, 0)) { //Return success return true; } else { //Return failure return false; } } void PCA9955::reset() { //The General Call Reset command char data = 0x06; //Issue the command to the General Call address m_I2C.write(0x00, &data, 1); } bool PCA9955::allCallEnabled() { //Read the 8-bit register value char value = read(REG_MODE1); //Return the status of the ALLCALL bit if (value & (1 << 0)) return true; else return false; } void PCA9955::allCallEnabled(bool enabled) { //Read the current 8-bit register value char value = read(REG_MODE1); //Set or clear the ALLCALL bit if (enabled) value |= (1 << 0); else value &= ~(1 << 0); //Write the value back out write(m_ADDR, REG_MODE1, value); } bool PCA9955::subCall3Enabled() { //Read the 8-bit register value char value = read(REG_MODE1); //Return the status of the SUB3 bit if (value & (1 << 1)) return true; else return false; } void PCA9955::subCall3Enabled(bool enabled) { //Read the current 8-bit register value char value = read(REG_MODE1); //Set or clear the SUB3 bit if (enabled) value |= (1 << 1); else value &= ~(1 << 1); //Write the value back out write(m_ADDR, REG_MODE1, value); } bool PCA9955::subCall2Enabled() { //Read the 8-bit register value char value = read(REG_MODE1); //Return the status of the SUB2 bit if (value & (1 << 2)) return true; else return false; } void PCA9955::subCall2Enabled(bool enabled) { //Read the current 8-bit register value char value = read(REG_MODE1); //Set or clear the SUB2 bit if (enabled) value |= (1 << 2); else value &= ~(1 << 2); //Write the value back out write(m_ADDR, REG_MODE1, value); } bool PCA9955::subCall1Enabled() { //Read the 8-bit register value char value = read(REG_MODE1); //Return the status of the SUB1 bit if (value & (1 << 3)) return true; else return false; } void PCA9955::subCall1Enabled(bool enabled) { //Read the current 8-bit register value char value = read(REG_MODE1); //Set or clear the SUB1 bit if (enabled) value |= (1 << 3); else value &= ~(1 << 3); //Write the value back out write(m_ADDR, REG_MODE1, value); } PCA9955::PowerMode PCA9955::powerMode() { //Read the 8-bit register value char value = read(REG_MODE1); //Return the status of the SLEEP bit if (value & (1 << 4)) return POWER_SHUTDOWN; else return POWER_NORMAL; } void PCA9955::powerMode(PowerMode mode) { //Read the current 8-bit register value char value = read(REG_MODE1); //Set or clear the SLEEP bit if (mode == POWER_SHUTDOWN) value |= (1 << 4); else value &= ~(1 << 4); //Write the value back out write(m_ADDR, REG_MODE1, value); } PCA9955::OutputChangeMode PCA9955::outputChangeMode() { //Read the 8-bit register value char value = read(REG_MODE2); //Return the status of the OCH bit if (value & (1 << 3)) return OUTPUT_CHANGE_ON_ACK; else return OUTPUT_CHANGE_ON_STOP; } void PCA9955::outputChangeMode(OutputChangeMode mode) { //Read the current 8-bit register value char value = read(REG_MODE2); //Set or clear the OCH bit if (mode == OUTPUT_CHANGE_ON_ACK) value |= (1 << 3); else value &= ~(1 << 3); //Write the value back out write(m_ADDR, REG_MODE2, value); } PCA9955::GroupMode PCA9955::groupMode() { //Read the 8-bit register value char value = read(REG_MODE2); //Return the status of the DMBLNK bit if (value & (1 << 5)) return GROUP_BLINKING; else return GROUP_DIMMING; } void PCA9955::groupMode(GroupMode mode) { //Read the current 8-bit register value char value = read(REG_MODE2); //Set or clear the DMBLNK bit if (mode == GROUP_BLINKING) value |= (1 << 5); else value &= ~(1 << 5); //Write the value back out write(m_ADDR, REG_MODE2, value); } bool PCA9955::overTemp() { //Read the current 8-bit register value char value = read(REG_MODE2); //Return the status of the OVERTEMP bit if (value & (1 << 7)) return true; else return false; } PCA9955::OutputState PCA9955::outputState(Output output) { char value; char reg; //Determine which register to read if (output < 4) { reg = REG_LEDOUT0; } else if (output < 8) { output = (Output)(output - 4); reg = REG_LEDOUT1; } else if (output < 12) { output = (Output)(output - 8); reg = REG_LEDOUT2; } else { output = (Output)(output - 12); reg = REG_LEDOUT3; } //Read the 8-bit register value value = read(reg); //Shift and mask the other output states value = (value >> (output * 2)) & 0x03; //Return the selected output's state if (value == 0) return OUTPUT_OFF; else if (value == 1) return OUTPUT_ON; else if (value == 2) return OUTPUT_PWM; else return OUTPUT_PWM_GRPPWM; } void PCA9955::outputState(Output output, OutputState state) { char value; char reg; //Determine which register to read if (output < 4) { reg = REG_LEDOUT0; } else if (output < 8) { output = (Output)(output - 4); reg = REG_LEDOUT1; } else if (output < 12) { output = (Output)(output - 8); reg = REG_LEDOUT2; } else { output = (Output)(output - 12); reg = REG_LEDOUT3; } //Read the current 8-bit register value value = read(reg); //Mask off the old output state (also turns the output off) value &= ~(0x03 << (output * 2)); //Add the new output state if (state == OUTPUT_ON) value |= (1 << (output * 2)); else if (state == OUTPUT_PWM) value |= (2 << (output * 2)); else if (state == OUTPUT_PWM_GRPPWM) value |= (3 << (output * 2)); //Write the value back out write(m_ADDR, reg, value); } float PCA9955::groupDuty() { //Return the value as a float return groupDuty_char() / 255.0; } void PCA9955::groupDuty(float duty, int altAddr) { //Range check the value if (duty < 0.0) duty = 0.0; if (duty > 1.0) duty = 1.0; //Convert the value to a char and write it groupDuty_char((char)(duty * 255.0), altAddr); } char PCA9955::groupDuty_char() { //Return the 8-bit register value return read(REG_GRPPWM); } void PCA9955::groupDuty_char(char duty, int altAddr) { //Write the new 8-bit register value write((altAddr == NULL) ? m_ADDR : altAddr, REG_GRPPWM, duty); } float PCA9955::groupBlinkPeriod() { //Read the 8-bit register value char value = groupBlinkPeriod_char(); //Return the period in seconds if (value == 0x00) return 0.067; else if (value == 0xFF) return 16.8; else return (value + 1) / 15.26; } void PCA9955::groupBlinkPeriod(float period, int altAddr) { char value = 0; //Do a smart conversion if (period > 0.067) { if (period < 16.8) value = (char)((period * 15.26) - 1); else value = 0xFF; } //Write the new 8-bit register value groupBlinkPeriod_char(value, altAddr); } char PCA9955::groupBlinkPeriod_char() { //Return the 8-bit register value return read(REG_GRPFREQ); } void PCA9955::groupBlinkPeriod_char(char period, int altAddr) { //Write the new 8-bit register value write((altAddr == NULL) ? m_ADDR : altAddr, REG_GRPFREQ, period); } float PCA9955::outputDuty(Output output) { //Return the value as a float return outputDuty_char(output) / 255.0; } void PCA9955::outputDuty(Output output, float duty, int altAddr) { //Range check the value if (duty < 0.0) duty = 0.0; if (duty > 1.0) duty = 1.0; //Convert the value to a char and write it outputDuty_char(output, (char)(duty * 255.0), altAddr); } char PCA9955::outputDuty_char(Output output) { //Return the 8-bit register value return read(REG_PWM0 + (char)output); } void PCA9955::outputDuty_char(Output output, char duty, int altAddr) { //Write the new 8-bit register value write((altAddr == NULL) ? m_ADDR : altAddr, REG_PWM0 + (char)output, duty); } float PCA9955::outputCurrent(Output output) { //Return the value as a float return outputCurrent_char(output) / 255.0; } void PCA9955::outputCurrent(Output output, float iref, int altAddr) { //Range check the value if (iref < 0.0) iref = 0.0; if (iref > 1.0) iref = 1.0; //Convert the value to a char and write it outputCurrent_char(output, (char)(iref * 255.0), altAddr); } char PCA9955::outputCurrent_char(Output output) { //Return the 8-bit register value return read(REG_IREF0 + (char)output); } void PCA9955::outputCurrent_char(Output output, char iref, int altAddr) { //Write the new 8-bit register value write((altAddr == NULL) ? m_ADDR : altAddr, REG_IREF0 + (char)output, iref); } char PCA9955::outputDelay() { //Return the 8-bit register value (minus the top 4 bits) return read(REG_OFFSET) & 0x0F; } void PCA9955::outputDelay(char clocks, int altAddr) { //Write the new 8-bit register value (minus the top 4 bits) write((altAddr == NULL) ? m_ADDR : altAddr, REG_OFFSET, clocks & 0x0F); } char PCA9955::subCall1Addr() { //Return the 8-bit address return read(REG_SUBADR1); } void PCA9955::subCall1Addr(char addr, int altAddr) { //Write the new 8-bit address write((altAddr == NULL) ? m_ADDR : altAddr, REG_SUBADR1, addr); } char PCA9955::subCall2Addr() { //Return the 8-bit address return read(REG_SUBADR2); } void PCA9955::subCall2Addr(char addr, int altAddr) { //Write the new 8-bit address write((altAddr == NULL) ? m_ADDR : altAddr, REG_SUBADR2, addr); } char PCA9955::subCall3Addr() { //Return the 8-bit address return read(REG_SUBADR3); } void PCA9955::subCall3Addr(char addr, int altAddr) { //Write the new 8-bit address write((altAddr == NULL) ? m_ADDR : altAddr, REG_SUBADR3, addr); } char PCA9955::allCallAddr() { //Return the 8-bit address return read(REG_ALLCALLADR); } void PCA9955::allCallAddr(char addr, int altAddr) { //Write the new 8-bit address write((altAddr == NULL) ? m_ADDR : altAddr, REG_ALLCALLADR, addr); } void PCA9955::allOutputStates(OutputState state, int altAddr) { char buff[5]; //Assemble the sending array buff[0] = REG_LEDOUT0 | REG_AUTO_INC; if (state == OUTPUT_OFF) { memset(buff + 1, 0x00, 4); } else if (state == OUTPUT_ON) { memset(buff + 1, 0x55, 4); } else if (state == OUTPUT_PWM) { memset(buff + 1, 0xAA, 4); } else { memset(buff + 1, 0xFF, 4); } //Send the array writeMulti((altAddr == NULL) ? m_ADDR : altAddr, buff, 5); } void PCA9955::getOutputDuties(float* duties) { char buff[16]; //Read all of the duty cycles as unsigned chars first getOutputDuties_char(buff); //Convert all of the duty cycles to percents for (int i = 0; i < 16; i++) { duties[i] = buff[i] / 255.0; } } void PCA9955::setOutputDuties(float* duties, int altAddr) { char buff[17]; //Assemble the sending array buff[0] = REG_PWM0 | REG_AUTO_INC; for (int i = 1; i < 17; i++) { //Range check the value if (duties[i - 1] < 0.0) duties[i - 1] = 0.0; if (duties[i - 1] > 1.0) duties[i - 1] = 1.0; //Convert the value to a char and write it buff[i] = duties[i - 1] * 255.0; } //Send the array writeMulti((altAddr == NULL) ? m_ADDR : altAddr, buff, 17); } void PCA9955::allOutputDuties(float duty, int altAddr) { //Range check the value if (duty < 0.0) duty = 0.0; if (duty > 1.0) duty = 1.0; //Convert the value to a char and write it allOutputDuties_char((char)(duty * 255.0), altAddr); } void PCA9955::getOutputDuties_char(char* duties) { //Read all of the duty cycles at once readMulti(REG_PWM0 | REG_AUTO_INC, duties, 16); } void PCA9955::setOutputDuties_char(char* duties, int altAddr) { char buff[17]; //Assemble the sending array buff[0] = REG_PWM0 | REG_AUTO_INC; memcpy(buff + 1, duties, 16); //Send the array writeMulti((altAddr == NULL) ? m_ADDR : altAddr, buff, 17); } void PCA9955::allOutputDuties_char(char duty, int altAddr) { //Write the new 8-bit register value write((altAddr == NULL) ? m_ADDR : altAddr, REG_PWMALL, duty); } void PCA9955::getOutputCurrents(float* irefs) { char buff[16]; //Read all of the current references as unsigned chars first getOutputCurrents_char(buff); //Convert all of the duty cycles to percents for (int i = 0; i < 16; i++) { irefs[i] = buff[i] / 255.0; } } void PCA9955::setOutputCurrents(float* irefs, int altAddr) { char buff[17]; //Assemble the sending array buff[0] = REG_IREF0 | REG_AUTO_INC; for (int i = 1; i < 17; i++) { //Range check the value if (irefs[i - 1] < 0.0) irefs[i - 1] = 0.0; if (irefs[i - 1] > 1.0) irefs[i - 1] = 1.0; //Convert the value to a char and write it buff[i] = irefs[i - 1] * 255.0; } //Send the array writeMulti((altAddr == NULL) ? m_ADDR : altAddr, buff, 17); } void PCA9955::allOutputCurrents(float iref, int altAddr) { //Range check the value if (iref < 0.0) iref = 0.0; if (iref > 1.0) iref = 1.0; //Convert the value to a char and write it allOutputCurrents_char((char)(iref * 255.0), altAddr); } void PCA9955::getOutputCurrents_char(char* irefs) { //Read all of the current references at once readMulti(REG_IREF0 | REG_AUTO_INC, irefs, 16); } void PCA9955::setOutputCurrents_char(char* irefs, int altAddr) { char buff[17]; //Assemble the sending array buff[0] = REG_IREF0 | REG_AUTO_INC; memcpy(buff + 1, irefs, 16); //Send the array writeMulti((altAddr == NULL) ? m_ADDR : altAddr, buff, 17); } void PCA9955::allOutputCurrents_char(char iref, int altAddr) { //Write the new 8-bit register value write((altAddr == NULL) ? m_ADDR : altAddr, REG_IREFALL, iref); } unsigned short PCA9955::faultTest() { //Read the current 8-bit register value char value = read(REG_MODE2); //Set the FAULTTEST bit value |= (1 << 6); //Write the value back out write(m_ADDR, REG_MODE2, value); //Wait for the fault test to complete while (read(REG_MODE2) & (1 << 6)); //Read the lower 8 flags unsigned short flags = read(REG_EFLAG0); //Add the upper 8 flags flags |= read(REG_EFLAG1) << 8; //Return the combined flags return flags; } PCA9955& PCA9955::operator=(float value) { //Set all of the output duties allOutputDuties(value); return *this; } char PCA9955::read(char reg) { //Select the register m_I2C.write(m_ADDR, ®, 1, true); //Read the 8-bit register m_I2C.read(m_ADDR, ®, 1); //Return the byte return reg; } void PCA9955::write(int addr, char reg, char data) { //Create a temporary buffer char buff[2]; //Load the register address and 8-bit data buff[0] = reg; buff[1] = data; //Write the data m_I2C.write(addr, buff, 2); } void PCA9955::readMulti(char startReg, char* data, int length) { //Select the starting register m_I2C.write(m_ADDR, &startReg, 1, true); //Read the specified number of bytes m_I2C.read(m_ADDR, data, length); } void PCA9955::writeMulti(int addr, char* data, int length) { //Write the data m_I2C.write(addr, data, length); }