Closed ITstreet1 closed 1 year ago
Hi Dejan, thanks for the question.
I do not have the intention at the moment to make a library for the PCA9553. It might be not too difficult to derive from this one or the PCA9634 or PCA9685.
As I did not read the datasheet yet it is hard to say if it is compatible and can be derived pretty easy. Or that it is made rather complete different.
Did you read the datasheet, and if so are these "close" like same registers etc? Do you need full functionality or a subset to start?
As I do not have the hardware I cannot test a library. Can you do that?
Datasheet - https://www.nxp.com/docs/en/data-sheet/PCA9553.pdf
4-bit I2C-bus LED driver with programmable blink rates Rev. 06 — 29 December 2008 26 pages
So 15 years old device.
Quick look at the datasheet shows it not a look alike. Good news is that it is not a complex one, so creating a library is expected to have little problems.
Hi,
Thank you for your fast response.
I need it only for driving some MOSFETs, and LED strips over the PWM. Old or not, I have them, so why not use them? :) If you have any suggestions, I am all ears.
I can have a look to make a quick and dirty first version of such library and clean it up based upon your tests?
Have an hour or two this evening (almost dinner time now, and I have a few other tasks to do)
Mate, I need you. :) You may need me, or you may not. :) Of course, I have as many hours as you need. I just connected everything, the I2C address is 0x62. The MCU I use is ATmega324p. Again, the chip I have, so it will go along the expander. :)
@ITstreet1
Hi Dejan,
created a develop branch and PR here - https://github.com/RobTillaart/PCA9553 Stripped a copy of the PCA9635 and throwed in the basic access to the registers with some "meaningfull" names.
I propose to move this issue to that repository to keep the discussions in the right repo.
(now I have to attend other tasks.
Almost got initial version build stable.
Merged and released the 0.1.0 initial version. Also published it for the Arduino Library Manager and platformIO.
Ok,
I installed the library from the library manager. There is an example Test01. After uploading, I get Lib Version and Done
What else to check?
There should be different signals on the 4 outputs.
FYI, the interface will probably change as the PCA9553 has a 8 and 16 LED variant. I need to check the datasheets if the output count is the only difference (from outside) If so I wan to give them all the same interface.
There should be different signals on the 4 outputs.
I am afraid I have none.
The PCA9553 I have is in the TSSOP8 package. The project I would like to use is to drive led strips over the MOSFETs. So the PCA9553 is soldered on a PCB. This is the schematic:
I can unsolder 220R resistors and check the output on pads if it would make any difference.
I believe for the start, the basic set HIGH/LOW logic would be better for testing, and later the PWM. But, I will try everything you suggest.
In the test sketch I connected every possible source to 1 pin, so there should be differences. Desoldering the device makes sense, Do not forget to add 4K7 pull up resistors to the SDA and SCL
// all channels a different source
leds.setLEDSource(0, 0); ==> LOW
leds.setLEDSource(1, 1); ==> HIGH
leds.setLEDSource(2, 2); ==> PWM duty cycle 50%
leds.setLEDSource(3, 3); ==> PWM duty cycle 12%
I have 10K. I will desolder the 220R, and change pull-up resistors to 4.7K, and let you know.
I created a new develop branch (and PR) as I want to have the new interface in place asap.
main change are a generic setPrescaler() and setPWM().
This allows to have the 8 and 16 outputs versions to have the same interface.
Get and set the pre-scaler of the PWM generator.
Get and set the duty cycle of the PWM generator.
Added two functions to the interface of the develop branch. Now you should be able to set the HIGH LOW as you asked for.
Added example using digitalWrite() and digitalRead()
I just soldered only the PCA9553, and two 4.7K resistors, and connect it to the Uno. Nothing on the R14-117 pads. Or pins of the PCA9553.
The sketch is Test02
PS. I2C scanner sees it on 0x62
Just tried basic:
void loop(){
leds.digitalWrite(0, HIGH);
delay(1000);
leds.digitalWrite(0, LOW);
delay(1000);
}
Nothing
And if you try
void loop
{
leds.digitalWrite(0, HIGH);
leds.digitalWrite(1, LOW);
leds.digitalWrite(2, HIGH);
leds.digitalWrite(3, LOW);
uint8_t val = leds.input();
Serial.println(val, HEX);
delay(100);
}
Can you change the private functions in the .cpp file, to see if the low level I2C gives errors.
(adding error handling is on the todo
uint8_t PCA9553::writeReg(uint8_t reg, uint8_t value)
{
Serial.println(__FUNCTION__);
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value);
_error = _wire->endTransmission();
Serial.println(_error, HEX);
if (_error == 0) _error = PCA9553_OK;
else _error = PCA9553_ERROR;
return _error;
}
uint8_t PCA9553::readReg(uint8_t reg)
{
Serial.println(__FUNCTION__);
_wire->beginTransmission(_address);
_wire->write(reg);
_error = _wire->endTransmission();
Serial.println(_error, HEX);
if (_wire->requestFrom(_address, (uint8_t)1) != 1)
{
_error = PCA9553_ERROR;
return 0;
}
_error = PCA9553_OK;
return _wire->read();
}
And if you try
void loop { leds.digitalWrite(0, HIGH); leds.digitalWrite(1, LOW); leds.digitalWrite(2, HIGH); leds.digitalWrite(3, LOW); uint8_t val = leds.input(); Serial.println(val, HEX); delay(100); }
'class PCA9553' has no member named 'input'; did you mean 'getInput'?
yes
Have to attend other work, so will be "offline" for several hours
Can you change the private functions in the .cpp file, to see if the low level I2C gives errors.
(adding error handling is on the todo
uint8_t PCA9553::writeReg(uint8_t reg, uint8_t value) { Serial.println(__FUNCTION__); _wire->beginTransmission(_address); _wire->write(reg); _wire->write(value); _error = _wire->endTransmission(); Serial.println(_error, HEX); if (_error == 0) _error = PCA9553_OK; else _error = PCA9553_ERROR; return _error; } uint8_t PCA9553::readReg(uint8_t reg) { Serial.println(__FUNCTION__); _wire->beginTransmission(_address); _wire->write(reg); _error = _wire->endTransmission(); Serial.println(_error, HEX); if (_wire->requestFrom(_address, (uint8_t)1) != 1) { _error = PCA9553_ERROR; return 0; } _error = PCA9553_OK; return _wire->read(); }
writeReg 0 readReg 0 writeReg 0 readReg 0 writeReg 0 readReg 0 writeReg 0 readReg 0 writeReg 0 readReg 0 writeReg 0 readReg 0
yes
With old CPP file:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
With the new CPP file, just add another 0 after redReg
So I2C looks like working.
Check if the source register is set and can be read back.
void loop()
{
for (uint8_t ch = 0; ch < 4; ch ++)
{
for (uint8_t src = 0; src < 4; src++)
{
leds.setLEDSource(ch, src);
Serial.print(leds.getLEDSource(ch));
}
Serial.println();
}
Serial.println();
}
0123 0123 0123 0123
0123 0123 0123 0123
0123 0123 0123 0123
old CPP file
So it looks like the register is written and read correctly.
Do I need to declare output to the pins in a setup()?
Do I need to declare output to the pins in a setup()?
Have you seen something in the datasheet about this? I may have missed it.
Just my guess.
So what have we got here? Uno can see the IC. It can write and read correctly.
There is nothing on the IO pins.
I'm going to read the datasheet again, this evening or tonight to see if I missed something.
In the mean time I have created initial experimental versions for the PCA9552 (16 lines) and PCA9551 (8 lines). Have to go through those datasheets as well. Advantage is that these have is 3 address lines so you can have 8 of those on one I2C bus (x 16 == 128!).
OK, I used PCA9553 as it has 4ch. All that I need.
Waiting for your suggestions. And thank you for your time.
new test sketch that reads back all registers, please run and check if output is as expected.
//
// FILE: PCA9553_test_registers.ino
// AUTHOR: Rob Tillaart
// PURPOSE: test PCA9553 device registers readback
// URL: https://github.com/RobTillaart/PCA9553
#include "Arduino.h"
#include "Wire.h"
#include "PCA9553.h"
PCA9553 leds(0x62);
void test_GPIO()
{
Serial.println(__FUNCTION__);
for (int i = 0; i < leds.channelCount(); i++)
{
leds.digitalWrite(i, LOW);
Serial.print(i);
Serial.print("\t");
Serial.print(leds.digitalRead(i));
Serial.print("\t");
leds.digitalWrite(i, HIGH);
Serial.print(leds.digitalRead(i));
Serial.print("\n");
}
Serial.println();
}
void test_prescaler()
{
Serial.println(__FUNCTION__);
for (uint8_t val = 0; val < 255; val += 5)
{
leds.setPrescaler(0, val);
leds.setPrescaler(1, val);
Serial.print(leds.getPrescaler(0));
Serial.print("\t");
Serial.println(leds.getPrescaler(1));
Serial.print("\n");
}
Serial.println();
}
void test_PWM()
{
Serial.println(__FUNCTION__);
for (uint8_t val = 0; val < 255; val += 5)
{
leds.setPWM(0, val);
leds.setPWM(1, val);
Serial.print(leds.getPWM(0));
Serial.print("\t");
Serial.println(leds.getPWM(1));
Serial.print("\n");
}
Serial.println();
}
void test_source()
{
Serial.println(__FUNCTION__);
for (uint8_t val = 0; val < 4; val++)
{
leds.setLEDSource(0, val);
leds.setLEDSource(1, val);
leds.setLEDSource(2, val);
leds.setLEDSource(3, val);
Serial.print(leds.getLEDSource(0));
Serial.print("\t");
Serial.print(leds.getLEDSource(1));
Serial.print("\t");
Serial.print(leds.getLEDSource(2));
Serial.print("\t");
Serial.print(leds.getLEDSource(3));
Serial.print("\n");
}
Serial.println();
}
void setup()
{
Serial.begin(115200);
Serial.print("PCA9553_LIB_VERSION: ");
Serial.println(PCA9553_LIB_VERSION);
Serial.println();
if (leds.begin() == false)
{
Serial.println("Could not connect.");
while(1);
}
Serial.println(leds.getAddress(), HEX);
Serial.println(leds.channelCount());
Serial.println();
test_GPIO();
test_prescaler();
test_PWM();
test_source();
Serial.println("\ndone...");
}
void loop()
{
}
// -- END OF FILE --
this might be needed to get digitalRead() working correctly Datasheet: 7.4 Pins used as general purpose I/Os
LED pins not used to control LEDs can be used as general purpose I/Os.
For use as input: Set LEDn to high-impedance (01) and then read the pin state via the Input register.
For use as output: Connect external pull-up resistor to the pin and size it according to the DC recommended operating characteristics. LED output pin is HIGH when the output is programmed as high-impedance, and LOW when the output is programmed LOW through the ‘LED selector’ register.
.cpp file
void PCA9553::pinMode(uint8_t led, uint8_t mode)
{
if (mode != OUTPUT) setLEDSource(led, 1);
}
added to develop branch, build runs.
new test sketch that reads back all registers, please run and check if output is as expected.
// // FILE: PCA9553_test_registers.ino // AUTHOR: Rob Tillaart // PURPOSE: test PCA9553 device registers readback // URL: https://github.com/RobTillaart/PCA9553 #include "Arduino.h" #include "Wire.h" #include "PCA9553.h" PCA9553 leds(0x62); void test_GPIO() { Serial.println(__FUNCTION__); for (int i = 0; i < leds.channelCount(); i++) { leds.digitalWrite(i, LOW); Serial.print(i); Serial.print("\t"); Serial.print(leds.digitalRead(i)); Serial.print("\t"); leds.digitalWrite(i, HIGH); Serial.print(leds.digitalRead(i)); Serial.print("\n"); } Serial.println(); } void test_prescaler() { Serial.println(__FUNCTION__); for (uint8_t val = 0; val < 255; val += 5) { leds.setPrescaler(0, val); leds.setPrescaler(1, val); Serial.print(leds.getPrescaler(0)); Serial.print("\t"); Serial.println(leds.getPrescaler(1)); Serial.print("\n"); } Serial.println(); } void test_PWM() { Serial.println(__FUNCTION__); for (uint8_t val = 0; val < 255; val += 5) { leds.setPWM(0, val); leds.setPWM(1, val); Serial.print(leds.getPWM(0)); Serial.print("\t"); Serial.println(leds.getPWM(1)); Serial.print("\n"); } Serial.println(); } void test_source() { Serial.println(__FUNCTION__); for (uint8_t val = 0; val < 4; val++) { leds.setLEDSource(0, val); leds.setLEDSource(1, val); leds.setLEDSource(2, val); leds.setLEDSource(3, val); Serial.print(leds.getLEDSource(0)); Serial.print("\t"); Serial.print(leds.getLEDSource(1)); Serial.print("\t"); Serial.print(leds.getLEDSource(2)); Serial.print("\t"); Serial.print(leds.getLEDSource(3)); Serial.print("\n"); } Serial.println(); } void setup() { Serial.begin(115200); Serial.print("PCA9553_LIB_VERSION: "); Serial.println(PCA9553_LIB_VERSION); Serial.println(); if (leds.begin() == false) { Serial.println("Could not connect."); while(1); } Serial.println(leds.getAddress(), HEX); Serial.println(leds.channelCount()); Serial.println(); test_GPIO(); test_prescaler(); test_PWM(); test_source(); Serial.println("\ndone..."); } void loop() { } // -- END OF FILE --
Downloaded lib again, with this example and get this on Serial monitor:
PCA9553_LIB_VERSION: 0.1.0
62 4
test_GPIO 0 0 0 PCA9553_LIB_VERSION: 0.1.0
62 4
test_GPIO 0 0 0 1 0 0 2 0 0 3 0 0
test_prescaler 0 0
5 5
PCA9553_LIB_VERSION: 0.1.0
62 4
test_GPIO 0 0 0 1 0 0 2 0 0 3 0 0
test_prescaler 0 0
5 5
10 10
15 15
20 20
25 25
30 30
35 35
40 40
45 45
50 50
55 55
60 60
65 65
70 70
75 75
80 80
85 85
90 90
95 95
100 100
105 105
110 110
115 115
120 120
125 125
130 130
135 135
140 140
145 145
150 150
155 155
160 160
165 165
170 170
175 175
180 180
185 185
190 190
195 195
200 200
205 205
210 210
215 215
220 220
225 225
230 230
235 235
240 240
245 245
250 250
test_PWM 0 0
5 5
10 10
15 15
20 20
25 25
30 30
35 35
40 40
45 45
50 50
55 55
60 60
65 65
70 70
75 75
80 80
85 85
90 90
95 95
100 100
105 105
110 110
115 115
120 120
125 125
130 130
135 135
140 140
145 145
150 150
155 155
160 160
165 165
170 170
175 175
180 180
185 185
190 190
195 195
200 200
205 205
210 210
215 215
220 220
225 225
230 230
235 235
240 240
245 245
250 250
test_source 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3
done...
But nothing on the pins. I am using a classic multimeter.
this might be needed to get digitalRead() working correctly Datasheet: 7.4 Pins used as general purpose I/Os
LED pins not used to control LEDs can be used as general purpose I/Os.
For use as input: Set LEDn to high-impedance (01) and then read the pin state via the Input register.
For use as output: Connect external pull-up resistor to the pin and size it according to the DC recommended operating characteristics. LED output pin is HIGH when the output is programmed as high-impedance, and LOW when the output is programmed LOW through the ‘LED selector’ register.
.cpp file
void PCA9553::pinMode(uint8_t led, uint8_t mode) { if (mode != OUTPUT) setLEDSource(led, 1); }
added to develop branch, build runs.
According to this, to use it as HIGH/LOW logic, I need a pull-up resistor on IO pins. PCA9553 can go LOW, but can not go HIGH. The only way is to use a resistor. I will try this.
But what about PWM? Do I need the same with a PWM? If I set 255 value, it should go "HIGH", it should have a 5V on the pin. Right now, as is, on the pins there is nothing, no matter I measure pins from the GND, or from the 5V.
The output looks good (except for the digitalWrite() and digitalRead()
What I learned from the datasheet is that it is not possible to switch runtime from INPUT to OUTPUT mode. Exactly because for the INPUT mode an external pull up is needed.
The only way is to use a resistor. I will try this.
Good action!
But what about PWM? Do I need the same with a PWM? If I set 255 value, it should go "HIGH", it should have a 5V on the pin. Right now, as is, on the pins there is nothing, no matter I measure pins from the GND, or from the 5V.
As far as I understand (cannot test it) is PWM an automatic OUTPUT mode so it needs no external PULLLUP. Only INPUT mode needs an internal PULLUP.
TODO: document this in the readme.md file.
Found no other things in the datasheet. To be continued tomorrow (other tasks are asking for attention)
As far as I understand (cannot test it) is PWM an automatic OUTPUT mode so it needs no external PULLLUP. Only INPUT mode needs an internal PULLUP.
In DS it says it needs resistors for output, not for input.
I soldered a resistor on ch0 and 5V rail. Nothing happened.
Didn't make it write a HIGH logic state...
In DS 9.1 - fig15 it says it can sink LEDs. Tomorrow I will solder one LED in sink to try it this way.
If any suggestions, I am all ears...
Good morning,
This is how I understand the working after a good night sleep.
What is missing in the picture is an internal pull up resistor between LEDn and 5V (VDD)
source mode must be set to 01 ==> the transistor blocks and the PULL UP set LEDn to HIGH
If the source mode == 00 the transistor should connect GND to LEDn resulting in LOW If the source mode == 01 the transistor blocks and the PULL UP set LEDn to HIGH
Connected a LED to 5V on one side and LEDn on the other, and you should be able to let it blink. An external PULL UP will minimize the RC time to get nicer "square wave" => faster and thus cleaner switching.
The MUX will select the signal from the PWM and alternatingly open/close the transistor giving a pulse on LEDn It makes sense to add an external PULL UP for the same reason as above.
Writing down the above made me aware that setting the source mode to 00 and putting 5V directly on LEDn is dangerous as there would flow an "unrestricted" current through the transistor. This can cause permanent damage, Also in PWM mode a direct connection to 5V would probably damage the transistor (in a pulsating way).
Does this story makes sense?
Can't help you much other than bare tests. If there is anything else you suggest me to try?
In a few minutes I will try an LED in a sink, according to the DS.
Now we can start all over again.
The LED BLINKS!!!!
#include "Arduino.h"
#include "Wire.h"
#include "PCA9553.h"
PCA9553 leds(0x62);
void setup() {
if (leds.begin() == false)
{
Serial.println("Could not connect.");
while(1);
}
}
void loop() {
leds.digitalWrite(1, LOW);
delay(1000);
leds.digitalWrite(1,HIGH);
delay(1000);
}
It is connected as on the DS, in sink on pin1. Shall we try barebone PWM?
#include "Arduino.h"
#include "Wire.h"
#include "PCA9553.h"
PCA9553 leds(0x62);
void setup() {
if (leds.begin() == false)
{
Serial.println("Could not connect.");
while(1);
}
}
void loop() {
for (uint8_t val = 0; val < 255; val += 5){
leds.setPWM(1, val);
delay(100);
}
}
There is nothing on the LED. The LED is connected as on the Blink sketch.
You must connect an output to the PWM.
Leds setLEDSource(0, 3);
0 is pin, 3 is pwm0. (4 is pwm1)
(From my phone, will be back later)
//
// FILE: PCA9553_PWM.ino
// AUTHOR: Rob Tillaart
// DATE: 2023-07-18
// PURPOSE: test PCA9553 library
// URL: https://github.com/RobTillaart/PCA9553
//
// Connect LEDs from pin 0 and pin 1 with a resistor to 5V
// See datasheet
#include "Arduino.h"
#include "Wire.h"
#include "PCA9553.h"
PCA9553 leds(0x62);
void setup()
{
Serial.begin(115200);
Serial.print("PCA9553_LIB_VERSION: ");
Serial.println(PCA9553_LIB_VERSION);
Serial.println();
if (leds.begin() == false)
{
Serial.println("Could not connect.");
while(1);
}
// not mandatory, just to make it explicit
leds.pinMode(0, OUTPUT);
leds.pinMode(1, OUTPUT);
leds.setPrescaler(0, 44);
leds.setPWM(0, 128);
leds.setPrescaler(1, 22);
leds.setPWM(1, 32);
}
void loop()
{
}
// -- END OF FILE --
Right now I have only one LED, I will add another one later. The one connected is lit. But...
Can you put little light here? What setPrescaler() function does here? Does it set the above limit? If so, what are the options? I noticed the LED is lit with the same brightness no matter what value I set for setPWM(). Do I need to adjust the setPrescaler, too?
This works in sink configuration. Can this work in source, too?
Although not related to this repo, I wonder do you intend to make support for PCA9553?