I2C communication PCF8574AP

HomeForumsMonoBrick EV3 FirmwareI2C communication PCF8574AP

This topic contains 21 replies, has 5 voices, and was last updated by Author Image Anders Søborg 3 years, 1 month ago.

Viewing 15 posts - 1 through 15 (of 22 total)
  • Author
    Posts
  • #4867
    Author Image
    Bartłomiej Drozd
    Participant

    Hi, i have a problem with I2C communication. I need to control PCF8574AP with EV3, i used this, but i think, PCF8574AP doesn’t have register. How can i do it? If i use standard WriteRegister(byte register, byte data) it doesn’t work. Please, help me with this…

    #4868
    Author Image
    Tcm0
    Participant

    You probably don’t need the resistors on the breadboard. Can you read registers?

    #4869
    Author Image
    Bartłomiej Drozd
    Participant

    why i don’t need the resistors? In datasheet PCF8574AP doesn’t have any registers

    #4870
    Author Image
    Tcm0
    Participant

    The PCF8574AP must have registers because otherwise no I2C device can work with it. But the registers are programmed in the software.

    #4871
    Author Image
    Helmut Wunder
    Participant

    hi,
    you don’t need to address a register for R/W (it’s just register 0 then) -
    maybe this helps:

    I2C Interface

    I2C communication with this device is initiated by a master sending a start condition, a high-to-low transition on the SDA I/O while the SCL input is high.
    After the start condition, the device address byte is sent, most-significant bit (MSB) first, including the data direction bit (R/W).
    This device does not respond to the general call address.
    After receiving the valid address byte, this device responds with an acknowledge, a low on the SDA I/O during the high of the acknowledge-related clock pulse.
    The address inputs (A0–A2) of the slave device must not be changed between the start and the stop conditions.

    The data byte follows the address acknowledge. If the R/W bit is high, the data from this device are the values read from the P port. If the R/W bit is low, the data are from the master, to be output to the P port.
    The data byte is followed by an acknowledge sent from this device. If other data bytes are sent from the master, following the acknowledge, they are ignored by this device. Data are output only if complete bytes are received and acknowledged.

    this is the (quite simple) NXC Code you might have a look at (it reads single muxed sensors, passed by it’s number “input”; nevertheless, you might also read and return the whole bitpattern by 1 reading instead of course for quicker readings :

    
    int ReadPCF8574(char PCF8574Port, byte PCF8574ID, char input)
    {
        byte cnt = 0x02;
        byte I2CMsg[];
        byte inbuf[];
        byte Address = PCF8574ID;
        byte nByteReady = 0;
        int  result = -1;
        int loop;
    
        ArrayBuild(I2CMsg, Address);
        Wait(8);
        while (loop == STAT_COMM_PENDING)
        {
              loop = I2CStatus(PCF8574Port, nByteReady);
        }
    
        if (I2CBytes(PCF8574Port, I2CMsg, cnt, inbuf))
        {
            result = inbuf[1];
        }
    
        if( result == (result | (1<<input-1)) )
        {
           result = 0;
        }
        else
        {
           result = 1;
        }
    
        if(result==-1){
         TextOut(0, LCD_LINE1, "Error: Check the");
         TextOut(0, LCD_LINE2, "   connection!");
         Wait(500);
         ClearScreen();
        }
        return result;
    }
    
    • This reply was modified 3 years, 2 months ago by Author Image Helmut Wunder.
    #4873
    Author Image
    Bartłomiej Drozd
    Participant

    sorry, i don’t know how to read a register… in NXC i did it that:

    void writeReg(byte reg)
    {
     byte buff[] = {0x70, 0x00};
     buff[1] = reg;
     int nbytes;
     I2CWrite(I2C, 0, buff);
     while(I2CStatus(I2C, nbytes) == STAT_COMM_PENDING);
     Wait(100);
    }

    because i need only outputs and that is working fine, but i need it in C# on EV3. Maybe i’m doing something wrong? Here is my code…
    Main.cs

    using System;
    using System.Threading;
    using MonoBrickFirmware;
    using MonoBrickFirmware.Display.Dialogs;
    using MonoBrickFirmware.Display;
    using MonoBrickFirmware.Sensors;
    using MonoBrickFirmware.UserInput;
    
    namespace test
    {
    	class MainClass
    	{
    		public static void Main (string[] args)
    		{
    			EventWaitHandle stopped = new ManualResetEvent (false);
    			ButtonEvents btn = new ButtonEvents ();
    			byte address = 0x70;
    			byte[] data1 = new byte[8] {1, 1, 1, 1, 1, 1, 1, 1};
    			byte[] data0 = new byte[8] {0, 0, 0, 0, 0, 0, 0, 0};
    			LcdConsole.WriteLine ("hello world!");
    			pcf pcf = new pcf (SensorPort.In1, address);
    			btn.EscapePressed += () => {
    				LcdConsole.WriteLine("stopped");
    				stopped.Set ();
    			};
    			btn.LeftPressed += () => {
    				pcf.WriteReg(0, data1);
    				LcdConsole.WriteLine("write data1");
    			};
    			btn.RightPressed += () => {
    				pcf.WriteReg(0, data0);
    				LcdConsole.WriteLine("write data0");
    			};
    			stopped.WaitOne ();
    		}
    	}
    }

    pcf.cs

    using System;
    using MonoBrickFirmware;
    using MonoBrickFirmware.Sensors;
    
    namespace test
    {
    	public class pcf : I2CSensor
    	{
    		public pcf (SensorPort Port, byte adress) : base (Port, adress, I2CMode.LowSpeed)
    		{
    			base.Initialise();
    		}
    
    		public byte[] ReadReg(byte Register, byte Length)
    		{
    			return base.ReadRegister(Register, Length);
    		}
    
    		public void WriteReg(byte Register, byte[] Data)
    		{
    			base.WriteRegister(Register, Data);
    		}
    
    		public byte[] ReadAndWriteReg(byte Register, byte[] Data, int rxLength)
    		{
    			return base.WriteAndRead(Register, Data, rxLength);
    		}
    
    		public override string ReadAsString()
    		{
    			throw new NotImplementedException();
    		}
    
    		public override void SelectNextMode()
    		{
    			throw new NotImplementedException ();
    		}
    
    		public override string GetSensorName()
    		{
    			return "PCF8574AP";
    		}
    
    		public override void SelectPreviousMode()
    		{
    			throw new NotImplementedException ();
    		}
    
    		public override int NumberOfModes()
    		{
    			return 0;
    		}
    
    		public override string SelectedMode()
    		{
    			return "0";
    		}
    	}
    }
    #4874
    Author Image
    Tcm0
    Participant

    You can try to use “0×70″ as the register.

    #4875
    Author Image
    Bartłomiej Drozd
    Participant

    unfortunetly, it is not workking… i found source of sending function

                   /// <summary>
    		/// Write and read an array of bytes to the sensor
    		/// </summary>
    		/// <returns>The bytes that was read</returns>
    		/// <param name="register">Register to write to.</param>
    		/// <param name="data">Byte array to write</param>
    		/// <param name="rxLength">Length of the expected reply</param>
            protected byte[] WriteAndRead (byte register, byte[] data, int rxLength)
    		{
    			if (rxLength > BufferSize)
    				throw new ArgumentOutOfRangeException("I2C Receive Buffer only holds " + BufferSize + " bytes");
    			if (data.Length > BufferSize) {
    				throw new ArgumentOutOfRangeException("I2C Write Buffer only holds " + BufferSize + " bytes");
    			}
    			bool dataReady = false;
    			int replyIndex = 0;
    			byte[] writeData = new byte[BufferSize];//30
    			Array.Copy (data, 0, writeData, 0, data.Length);
    			ByteArrayCreator command = new ByteArrayCreator ();
    			command.Append ((int)-1);
    			command.Append ((byte)this.port);
    			command.Append ((byte)1);//repeat
    			command.Append ((short)0);//time
    			command.Append ((byte)(data.Length + 2));//length of write data
    			command.Append ((byte)((byte)I2CAddress >> 1));
    			command.Append (register);
    			command.Append(writeData);
    			command.Append ((byte)-rxLength);
    			replyIndex = command.Data.Length;
    			command.Append (new byte[BufferSize]);//make room for reply
    			byte[] i2cData = command.Data;
    			while (!dataReady) {
    				unchecked {
    					I2CDevice.IoCtl ((Int32)I2CIOSetup, i2cData);
    				}
    				int status = BitConverter.ToInt32 (i2cData, 0);
    				if (status < 0) {
    					throw new Exception ("I2C I/O error");
    				}
    				if (status == 0) {
    					byte[] reply = new byte[rxLength];
    					if (rxLength > 0) {
    						Array.Copy(i2cData,replyIndex, reply,0, rxLength);
    					}
    					return reply;
    				}
    			}
            	throw new TimeoutException("I2C timeout");
        	}

    maybe this should help…

    #4876
    Author Image
    Helmut Wunder
    Participant

    I actually doubt that 0×70 is a valid register number.
    It’s more supposed to be the device address, but there are several different PCF8574s out there with different device addresses like, e.g., 0x4E, 0×73 or something (CMIIW).
    The registers are quite a different thing.
    If you read a i2c byte the reading starts at 0×00 and then the read pointer is incremented automatically after each follow-up-reading. So if a i2c device has 10 registers starting at 0×00 you will reach the register number 0×05 by reading 5 bytes one after the other.

    For the PCF8574 simply start reading 2bytes starting at register 0×00;
    the 1st byte contains the ack byte, the 2nd byte contains the bit I/O pattern.

    Anyway, f your code works by addr 0×70 using NXC it’s surely valid. Then it’s just a Mono source code issue.

    • This reply was modified 3 years, 2 months ago by Author Image Helmut Wunder.
    #4878
    Author Image
    Bartłomiej Drozd
    Participant

    in datasheet for PCF8574A address is
    0 1 1 1 A2 A1 A0 0
    and my A2, A1, A0 is 0, so b01110000 = 0×70. i should read bytes, but i need only write bits… i don’t understand too much…

    #4879
    Author Image
    Helmut Wunder
    Participant

    you first have to write bytes to initiate, and then read the values.

    #4880
    Author Image
    Bartłomiej Drozd
    Participant

    ok, i did that:
    LcdConsole.WriteLine(BitConverter.ToString(pcf.ReadReg(2, 1)));
    and i got “03″ on lcd when i connect P0 to + and “02″ when i connect P0 to -. So, register is just a number of port to read/write?

    #4881
    Author Image
    Helmut Wunder
    Participant

    about the technical details of i2c I don’t know more than I already told you, and the Mono code is far too complicated for me to understand, so what you are actually doing in your C# source is far beyond me.
    My skills just fit to use NXC and Sketch C.
    In Sketch C I just have to use wire.write() and wire.read(),
    and for display output I just use Serial.write() or TextOut() -
    that’s it, and that’s why I can use both NXC and Sketch (and no Mono at all).
    So for Mono you would need professional Mono helpers ;)

    #4882
    Author Image
    Tcm0
    Participant

    I’m not sure that “LcdConsole.WriteLine(BitConverter.ToString(pcf.ReadReg(2, 1)));” works.
    Try it with “LcdConsole.WriteLine(Convert.ToString(pcf.ReadReg(2, 1)));

    #4883
    Author Image
    Bartłomiej Drozd
    Participant

    unfortunetly, that is not working… it display only “System.Byte[]”. When i use my version, it dislpay hex byte. When i change to pcf.ReadReg(16, 1) it is reading all 8 bits

Viewing 15 posts - 1 through 15 (of 22 total)

You must be logged in to reply to this topic.

Posted in

Make a donation