2
votes

Without going deep into the background of the project, we have created a BIN file from some standard text fields, through the use of some very creative coding. No issues there - it has worked perfectly. We then write this BIN file to an EEPROM chip using a programmer called an Aardvark. It has its own program to do this called FlashCenter, but also offers an API. Because programming this chip has been a hurdle for some of the employees, we would like to include this functionality into our BIN file generator (i.e. - the BIN file is generated, stored, and immediately written to EEPROM).

The only problem is that this is incredibly advanced for my limited knowledge and cannot figure out how to pass the BIN file data to the EEPROM using the VB code that the Aardvark API has provided. I hope you don't mind if I provide some sample code, and hopefully someone can help me figure this out.

This is the sample VB code provided in the Aardvark API: Code:

'==========================================================================
' (c) 2004-2009  Total Phase, Inc.
  '--------------------------------------------------------------------------
' Project : Aardvark Sample Code
' File    : aai2c_eeprom.bas
   '--------------------------------------------------------------------------
' Perform simple read and write operations to an I2C EEPROM device.
'--------------------------------------------------------------------------
' Redistribution and use of this file in source and binary forms, with
' or without modification, are permitted.
'
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
' "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
' LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
' FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
' COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
' INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
' BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
' CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
' LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
' ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
' POSSIBILITY OF SUCH DAMAGE.
'==========================================================================
Imports TotalPhase

Module aai2c_eeprom

    '==========================================================================
' CONSTANTS
'==========================================================================
Const I2C_BITRATE As Integer = 100  'kHz
Const I2C_BUS_TIMEOUT As UShort = 150  'ms
Const I2C_DEVICE As UShort = &H54


'==========================================================================
' MAIN PROGRAM
'==========================================================================
Sub aai2c_eeprom_run(ByRef sampletext As Windows.Forms.TextBox)
    Dim handle As Long

    handle = AardvarkApi.aa_open(0)
    If (handle <= 0) Then
        sampletext.Text &= "Unable to open Aardvark device on port 0" & vbCrLf
        sampletext.Text &= "Error code = " & handle & vbCrLf
        Exit Sub
    End If

    ' Ensure that the I2C subsystem is enabled
    Call AardvarkApi.aa_configure(handle, AardvarkConfig.AA_CONFIG_SPI_I2C)

    ' Enable the I2C bus pullup resistors (2.2k resistors).
    ' This command is only effective on v2.0 hardware or greater.
    ' The pullup resistors on the v1.02 hardware are enabled by default.
    Call AardvarkApi.aa_i2c_pullup(handle, AardvarkApi.AA_I2C_PULLUP_BOTH)

    ' Power the board using the Aardvark adapter's power supply.
    ' This command is only effective on v2.0 hardware or greater.
    ' The power pins on the v1.02 hardware are not enabled by default.
    Call AardvarkApi.aa_target_power(handle, AardvarkApi.AA_TARGET_POWER_BOTH)

    ' Set the bitrate
    Dim bitrate As Long
    bitrate = AardvarkApi.aa_i2c_bitrate(handle, I2C_BITRATE)
    sampletext.Text &= "Bitrate set to " & bitrate & " kHz" & vbCrLf

    ' Set the bus lock timeout
    Dim bus_timeout As Long
    bus_timeout = AardvarkApi.aa_i2c_bus_timeout(handle, I2C_BUS_TIMEOUT)
    sampletext.Text &= "Bus lock timeout set to " & bus_timeout & " ms" & vbCrLf

    ' Write the offset and read the data
    'Dim offset(15) As Byte
    Dim offset() As Byte = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}  'THIS IS JUST A TEST STRING, but doesn't seem to ever get written
    Dim data(128) As Byte
    Dim result As Long
    Dim k As String = String.Empty

    offset(0) = 1
    Call AardvarkApi.aa_i2c_write(handle, I2C_DEVICE, AardvarkI2cFlags.AA_I2C_NO_STOP, 128, offset)


    result = AardvarkApi.aa_i2c_read(handle, I2C_DEVICE, AardvarkI2cFlags.AA_I2C_NO_FLAGS, 128, data)

    If result <= 0 Then
        sampletext.Text &= "i2c read error" & vbCrLf
    Else
        Dim i As Integer
        sampletext.Text &= "Read data bytes:"
        For i = 0 To 127
            k &= System.Convert.ToChar(data(i)).ToString
        Next
        sampletext.Text &= k
        sampletext.Text &= vbCrLf
    End If

    ' Close the device and exit
    AardvarkApi.aa_close(handle)
End Sub
End Module

And specifically dealing with the "write" function:

Call AardvarkApi.aa_i2c_write(handle, I2C_DEVICE, AardvarkI2cFlags.AA_I2C_NO_STOP, 128, offset)

...here is what the support documents define:

Master Write (aa_i2c_write)

      int aa_i2c_write (Aardvark         aardvark,
                        aa_u16           slave_addr,
                        AardvarkI2cFlags flags,
                        aa_u16           num_bytes,
                        const aa_u08 *   data_out);

Write a stream of bytes to the I2C slave device. Arguments aardvark handle of an Aardvark adapter slave_addr the slave from which to read flags special operations as described in "Notes" section num_bytes the number of bytes to write (maximum 65535) data_out pointer to data Return Value Number of bytes written.

Specific Error Codes AA_I2C_WRITE_ERROR There was an error reading the acknowledgment from the Aardvark adapter. This is most likely a result of a communication error. Details For ordinary 7-bit addressing, the lower 7 bits of slave_addr should correspond to the slave address. The topmost bits are ignored. The Aardvark I2C subsystem will assemble the address along with the R/W bit after grabbing the bus. For 10-bit addressing, the lower 10 bits of addr should correspond to the slave address. The Aardvark adapter will then assemble the address into the proper format as described in the Philips specification. There is a limitation that a maximum of only 65534 bytes can be written in a single transaction if the 10-bit addressing mode is used.

The slave_addr 0x00 has been reserved in the I2C protocol specification for general call addressing. I2C slaves that are enabled to respond to a general call will acknowledge this address. The general call is not treated specially in the Aardvark I2C master. The user of this API can manually assemble the first data byte if the hardware address programming feature with general call is required.

It is actually possible to write 0 bytes to the slave. The slave will be addressed and then the stop condition will be immediately transmitted by the Aardvark adapter. No bytes are sent to the slave, so the data argument is ignored (i.e., it can be 0 or point to invalid memory).

If the number of bytes written is zero, the following conditions are possible.

The requested slave was not found. The requested slave is on the bus but refuses to acknowledge its address. The Aardvark adapter was unable to seize the bus due to the presence of another I2C master. Here, the arbitration was lost during the slave addressing phase results can be unpredictable. The slave was addressed and no bytes were written to it because num_bytes was set to 0. The number of bytes written can be less than the requested number of bytes in the transaction due to the following possibilities.

The Aardvark adapter loses the bus during the data transmission due to the presence of another I2C master. The slave refuses the reception of any more bytes.


So clearly I have to pass a byte array, but I'm not entirely sure how to encode the BIN file so that it can be passed. There's a lot of conversions at this low level that I'm cloudy about... converting things to strings, or to byte, or to char, etc. I have played around with them enough to know that in some cases you end up with ASCII, or hex, etc... but I'm not sure what is needed in this case to make it work.

To make this even more simple, I would love to pass ANYTHING to the EEPROM. No matter what I change in this code, I simply cannot get anything to write to the EEPROM. Or maybe it does and I'm just not seeing it. When you run this code the "read" function generates the output "12345678910111213141516". Currently there is 128 bytes of dummy content filling the EEPROM, and 1-16 is NOT in there.

If ANYONE can get me on the right track, it would be hugely appreciated!!!! Thank you.

3

3 Answers

0
votes

Correctly programming and reading your EEPROM is dependent upon the protocol your EEPROM uses and how it is physically configured on the board being programmed.

Most likely it uses the Phillips protocol discussed in the Aardvark docs here. The sample code has some built-in constants and assumptions. The first assumption is that your I2C network is working at 100KHz, the EEPROM is a slave and it is at address 0x54 (Const I2C_DEVICE As UShort = &H54 from the code sample).

Addressing

I2C can use either 7 bit or 10 bit device (bus) addressing. The most significant bits (MSBs) of are used to identify device type. EEPROMS have the device type 1010 (0x8). This takes up the 4 MSBs in the high address byte. Many devices have one or more pins which are used to control the I2C address that it uses. For instance, if the EEPROM is an 4K Atmel AT24C04C device with pin A1 low (grounded) and pin A2 pulled high (+Vcc) it would be addressed using (in binary) 101010XY. Where X=0 for page 0 on the device and 1 for page 1 and Y=1 for reading the device and Y=0 for writing to the device. The addresses of the device would be as follows:

  • 0xA8 = write page 0
  • 0xAC = write page 1
  • 0xA9 = read page 0
  • 0xAB = read page 1

What we're concerned with is the address lines of the chip (A2 and A1) combined with the addresses space within the EEPROM (0 to 512 for a 4Kbit EEPROM). Unfortunately, since the R/_W bit is bit 0 of the bus address byte and the page bit is bit 1 of the bus address byte. So, the address space of the EEPROM is not contiguous from an API point of view. So the physical bus address is always one of the above. When the bus address is combined with another byte of memory address, that specifies what we're actually reading or writing in a particular EEPROM on the I2C bus.

Device Memory

As noted above, EEPROMS can have different pages of memory. The page sizes are device specific. The AT24C04C has 8 pages of 16 bytes per page. In this example, the address within a page is specified by the 4 LSBs (least significant bits) of the address byte.

Reading Data

You can read data a byte at a time (Random Read) or sequentially. To get a byte of data:

  1. Write to the device what address you want to read from
  2. Read the byte from the device

The device has an internal address counter. If you want to do sequential reads of more than one byte, just keep reading from the device and it will keep sending the next byte in its memory. Most devices' address counters roll over, so you must be careful not to ask for more data than the device actually has or it may give you unexpected results.

Stop and Start Signals

At the beginning of communication from an I2C master to slave(s) the master asserts control of the bus by pulling the SDA (serial data A) line low while the SCL (clock) line is high. (During normal data transfers, SDA must not change while SCL is high, but may change while SCL is low). The master signals end of control when it raises SDA while SCL is high. This has implications for how you write the code and specify flags for reads and writes.

Example: Read first 32 bytes

  1. Write to address 0xA8: data=0x00
  2. Read from address 0xA9
  3. Repeat step 2 (31 more times)

In code this might look something like:

Const DEV0_READ__PAGE0 As Integer = &HA9
Const DEV0_WRITE_PAGE0 As Integer = &HA8
Dim startAddress() As Byte = New Byte() {0}
Dim data() as Byte = new Byte(32)
Dim writeResult As Integer = _
    AardvarkApi.aa_i2c_write( _
        handle, DEV0_WRITE_PAGE0, _
        AardvarkI2cFlags.AA_I2C_NO_STOP, 1, startAddress)
Dim readResult As Integer = _
    AardvarkApi.aa_i2c_read( _
        handle, DEV0_READ__PAGE0,
        AardvarkI2cFlags.AA_I2C_NO_FLAGS, 32, data)
If readResult <> 32 Then
    Console.WriteLine("Read Error {0} bytes requested, {1} bytes received", 32, readResult)
Else
    For i As Integer = 0 to 31
        Console.Write(data(i).ToString() & ", ")
    Next
End If

You have to read the manual of the EEPROM to understand the sequence of reads and writes involved for doing a given operation and how to specify the bus address of the device and the memory address within the EEPROM.

0
votes

The Nusbio device makes it easier to read/write EEPROM from C# or VB.NET. See their github samples

https://github.com/madeintheusb/Nusbio.Samples/tree/master/CS/MadeInTheUSB.Nusbio.SPI.EEPROM_25AA1024

https://github.com/madeintheusb/Nusbio.Samples/tree/master/CS/MadeInTheUSB.Nusbio.I2C.EEPROM_24LC256

using (var nusbio = new Nusbio(serialNumber))
{
var _eeprom = new EEPROM_25AA1024(
nusbio: nusbio, clockPin: NusbioGpio.Gpio0,mosiPin: NusbioGpio.Gpio1,
misoPin: NusbioGpio.Gpio2, selectPin: NusbioGpio.Gpio3
);
byte[] buf;

var r = _eeprom.ReadPage(p * _eeprom.PAGE_SIZE, _eeprom.PAGE_SIZE);
if (r.Succeeded)
{
    buf = r.Buffer;
}

}

0
votes

You can write a script which increment device address after writing into each quadrant i.e. 64KB. In your case 0x54 will be incremented to 0x55 and so on. Calculate number of quadrant based on total size of EEPROM. However byte address will be same for each quadrant i.e. 0x0000 to 0xFFFF