View previous topic :: View next topic |
Author |
Message |
bmete
Joined: 13 Jul 2023 Posts: 37
|
PCA9535PW I/O Expander with PIC16F883 |
Posted: Tue Jul 18, 2023 12:22 am |
|
|
Hello all,
I'm trying to use PCA9535PW with 16F883. First thing I need to do is set all P0 pins of PCA9535 to input and set all P1 pins to output. After this I need to check P0 pins and digital read and set low/high to P1 pins. I set PCA's address to 0x21(high to A0, low to A1,A2) I checked the i2c device with i2c scanner and it finds a device at 0x42 so there is no connection error.
Although I tried many things by looking at the datasheets, I could not set the pins. Unfortunately I don't have any serial port or a display so I can't debug and do any control from the serial port.
My code is as follows, I'm open to help.
Code: |
#include <main.h>
#include <16f883.h>
#fuses NOWDT,NOPROTECT,PUT,NOLVP,NOBROWNOUT
#use delay(clock=8000000)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#define PCA9535_ADDRESS 0x42 // 21
void main()
{
int8 outputData = 0x00; //
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS); // PCA9535 address
i2c_write(0x00); // P0 as input
i2c_write(outputData); //
i2c_stop(); //
outputData = 0xFF; //
i2c_start(); //
i2c_write(PCA9535_ADDRESS); //
i2c_write(0x03); // P1 as output
i2c_write(outputData); // set pins high low
i2c_stop(); //
while(1)
{
}
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Tue Jul 18, 2023 2:02 am |
|
|
First thing. Hurrah!...
You have covered so many things. You have understood the 7bit to 8bit
translation, have a 5v device. Great.
You have missed two things though. First you don't say what pull up's
you have on the bus?. However that it is working with the scanner code
suggests you have these OK.
Then key is understanding how the chip works. You have to send the chip
address, then a command, then data. Now, you don't have to 'Set P0 as
input'. It wakes up with both ports set as input. However you do have
to set P1 as output before you write to it, and you can't write to an input
port. Then on I2C you have to reverse the bus to read.
You have to start by writing to registers 6, and 7. These set the data
direction.
Code: |
//from here
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x06); // P0 direction register
i2c_write(0xFF); // set P0 as input (this is the default though)
i2c_stop();
//to here
//These are not needed since default is P0 set as input.
//Now P0 is set as input - so now read from it
i2c_start();
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x00); //select read register
i2c_start(); //This is a restart command
i2c_write(PCA9535_ADDRESS+1); //This is now saying we want to _read_
val=i2c_read(); //This now reads from input register 0 - so value of P0
i2c_stop();
|
Now in the case of P0, you can actually start by assuming it is set to
read, so selecting the input is not 'needed'. However you still have to
issue a select for the read register and then read it.
Now to write P1:
Code: |
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x07); // P1 direction register
i2c_write(0x0); // sets P1 as output
i2c_stop();
//Once this is done we can write to P1 output register
//Now P1 is set as output, so write to it
i2c_start();
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x03); //select write register P1
i2c_write(val); //This now writes val to P1
i2c_stop;
|
So you have to select the register, then direction, then perform the
I/O. |
|
|
bmete
Joined: 13 Jul 2023 Posts: 37
|
|
Posted: Tue Jul 18, 2023 2:43 am |
|
|
Hello Ttelmah thanks for your reply, I am just learning communication protocols and your messages are very helpful to me.
[img] https://ibb.co/7KXXz2Q [/img]
this is my circuit, from r48 to r53 are optional I set them to make address 0x21, also I connect pull-ups to SDA/SCL.
I programmed the pic with your code but still there is no output on the P1 port. All are low. What could be the problem? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Tue Jul 18, 2023 2:51 am |
|
|
Depends what you put in 'val'.... |
|
|
bmete
Joined: 13 Jul 2023 Posts: 37
|
|
Posted: Tue Jul 18, 2023 2:57 am |
|
|
First I tried 0x55 because I can understand which pin is low and high but no output, after I directly put the value that we equated to i2c_read above still no output.
What should be the format? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Tue Jul 18, 2023 3:23 am |
|
|
First thing. Are you sure your chip is running?.
You don't show a clock configuration. You say clock=8000000,
but there are no fuses to select what oscillator is being used to create
this.
So, start be telling the chip to use it's internal oscillator:
Code: |
#include <main.h>
#include <16f883.h>
#fuses NOWDT,NOPROTECT,PUT,NOLVP,NOBROWNOUT
#use delay(internal=8MHz)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
|
Compiling your code as posted selects the RC oscillator, which requires
an external resistor/capacitor to work.
|
|
|
bmete
Joined: 13 Jul 2023 Posts: 37
|
|
Posted: Tue Jul 18, 2023 3:41 am |
|
|
I am sure that my chip works because I tried to set pins high low and it worked without any issue and delay functions were worked correct. Also I have a external oscillator, this is my circuit around the chip:
[img] https://ibb.co/PDVfFTc [/img]
This is my code with last updates:
Code: |
#include <16F883.h>
#include <main.h>
#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP,NOBROWNOUT
#use delay(clock=8M)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#use FIXED_IO(B_outputs=PIN_B5,PIN_B4,PIN_B3)
#define PCA9535_ADDRESS 0x42 // 42?
void main()
{
int8 val;
while(TRUE)
{
//from here
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS | 0x01); // PCA9535 _write_ address
i2c_write(0x06); // P0 direction register
i2c_write(0xFF); // set P0 as input (this is the default though)
i2c_stop();
//to here
//These are not needed since default is P0 set as input.
//Now P0 is set as input - so now read from it
i2c_start();
i2c_write(PCA9535_ADDRESS | 0x01); // PCA9535 _write_ address
i2c_write(0x00); //select read register
i2c_start(); //This is a restart command
i2c_write(PCA9535_ADDRESS); //This is now saying we want to _read_
val=i2c_read(); //This now reads from input register 0 - so value of P0
i2c_stop();
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS | 0x01); // PCA9535 _write_ address
i2c_write(0x07); // P1 direction register
i2c_write(0x00); // sets P1 as output
i2c_stop();
//Once this is done we can write to P1 output register
//Now P1 is set as output, so write to it
i2c_start();
i2c_write(PCA9535_ADDRESS | 0x01); // PCA9535 _write_ address
i2c_write(0x03); //select write register P1
i2c_write(val); //This now writes val to P1
i2c_stop();
if (val == 0xE3)
{
output_high(PIN_B5);
output_low(PIN_B4);
}
else
{
output_high(PIN_B4);
output_low(PIN_B5);
}
}
}
|
Do you see any mistake? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Tue Jul 18, 2023 4:44 am |
|
|
You have not used what I posted. A _read_ address has | 1. The address
on it's own is a write address. You have the addresses reversed.
Code: |
#include <16f883.h>
#fuses NOWDT,NOPROTECT,PUT,NOLVP,NOBROWNOUT
#use delay(Crystal=8MHz)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#define PCA9535_ADDRESS 0x42 // 21
void main()
{
int8 count;
int 8 outputData; //
i2c_start(); // I2C iletisimini baslat
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x07); // P1 direction register
i2c_write(0x0); // sets P1 as output
i2c_stop();
//Once this is done we can write to P1 output register
while (TRUE)
{
output_data=1;
for (count=0;count<8;count++)
{
//Now P1 is set as output, so write to it
i2c_start();
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x03); //select write register P1
i2c_write(output_data; //This now writes val to P1
i2c_stop;
output_data*=2; //select next bit
delay_ms(1000);
}
}
}
|
This should turn on P1.0 for one second, then P1.1, and step through
all 8 pins and then go back to the start. |
|
|
bmete
Joined: 13 Jul 2023 Posts: 37
|
|
Posted: Tue Jul 18, 2023 5:58 am |
|
|
Thanks to you sir, I am finally able to run the expander now. I didn't know I had to do bit by bit processing, I thought it could take a byte of data I gave in a single message packet, but I was wrong. May I ask where did you find this information in the datasheet?
Similarly, I tried to read the P0 port, again assuming that the value is bit by bit, I saved these values in an array and wrote a function to convert this to the integer, but I think I'm making a mistake somewhere again.
Is this process correct?
Code: |
void main()
{
int8 count;
int8 outputData; //
int8 inputData; //
int16 val[8];
int8 result;
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x06); // P0 direction register
i2c_write(0xFF); // set P0 as input (this is the default though)
i2c_stop();
while (TRUE)
{
for (count=0;count<8;count++)
{
//These are not needed since default is P0 set as input.
//Now P0 is set as input - so now read from it
i2c_start();
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x00); //select read register
i2c_start(); //This is a restart command
i2c_write(PCA9535_ADDRESS+1); //This is now saying we want to _read_
val[count]=i2c_read(); //This now reads from input register 0 - so value of P0
i2c_stop();
delay_ms(500);
}
for (int i = 0; i < 8; i++)
{
result = (result << 1) | val[i];
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Tue Jul 18, 2023 6:00 am |
|
|
You don't.
I'm just showing you each bit in turn being set on. The routine is writing
a byte each time.
I just did it as a demo, showing how the I/O needs to be done. Point is
you only have to setup the direction register(s) once, and can then just
input or output bytes as wanted.
Your val array is getting 8 copies of the whole byte. You don't need to scan
it bit by bit. |
|
|
bmete
Joined: 13 Jul 2023 Posts: 37
|
|
Posted: Tue Jul 18, 2023 9:08 am |
|
|
Ttelmah,
I updated the code as you said, I don't know why, but no matter how I set the input ports, the value 0x02 comes to the "val" variable.
Code: |
void main()
{
int8 val;
int8 result;
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x06); // P0 direction register
i2c_write(0xFF); // set P0 as input (this is the default though)
i2c_stop();
while (TRUE)
{
i2c_start();
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x00); //select read register
i2c_start(); //This is a restart command
i2c_write(PCA9535_ADDRESS+1); //This is now saying we want to _read_
val=i2c_read(); //This now reads from input register 0 - so value of P0
i2c_stop();
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Tue Jul 18, 2023 9:43 am |
|
|
OK. One change needed:
Code: |
void main()
{
int8 val;
int8 result;
i2c_start(); // I2C iletişimini başlat
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x06); // P0 direction register
i2c_write(0xFF); // set P0 as input (this is the default though)
i2c_stop();
while (TRUE)
{
i2c_start();
i2c_write(PCA9535_ADDRESS); // PCA9535 _write_ address
i2c_write(0x00); //select read register
i2c_start(); //This is a restart command
i2c_write(PCA9535_ADDRESS+1); //This is now saying we want to _read_
val=i2c_read(0); //This now reads from input register 0 - so value of P0
i2c_stop();
}
}
|
Note the zero in the read. You have to NACK the last byte read. I'd
have expected the first byte to be right though.
This matches exactly what the data sheet shows in Fig 10. |
|
|
bmete
Joined: 13 Jul 2023 Posts: 37
|
|
Posted: Tue Jul 18, 2023 11:54 pm |
|
|
Yes that is it.
Its working properly. Now I can create an algorithm to operate system.
Thank you very very much sir for your patience and support. |
|
|
|