2023年11月7日 星期二

ASUS Tinker Board 2S - Debian I2C AT24C256

  ASUS Tinker Board 2S - Debian I2C  Example

Tinker Board 2 - Wiki

https://tinker-board.asus.com/forum/index.php?/topic/15253-waveshare-tinker-board-2-wiki/


I2C 速度

低速模式, 傳輸速度 100KHz.

快速模式 (Fast-mode, Fm) 傳輸速度 400KHz

高速模式 (High-speed mode, Hs-mode) 3.4MHz

https://tinker-board.asus.com/forum/index.php?/topic/15458-i2c-speed/

Tinker Board 2S預設是 40k (400KHz)



參考網頁

/i2c/dev-interface

https://www.kernel.org/doc/Documentation/i2c/dev-interface

Understanding I2C Communication in Linux

https://www.linkedin.com/pulse/understanding-i2c-communication-linux-beginners-guide-soheil-nazari

Full IOCTL Example
https://stackoverflow.com/questions/9974592/i2c-slave-ioctl-purpose

也可以使用 ASUS API Programming方式(只有使用範例, 無原始碼)
https://tinker-board.asus.com/tw/documentation/ter.html#api/

使用WiringPi C library for Debian也是一個方式
https://github.com/TinkerBoard/TinkerBoard/wiki/Tinker-Board-2-&-2S#3341-wiringpi-c-library-for-debian

Mraa library for android
https://github.com/TinkerBoard/TinkerBoard/wiki/Tinker-Board-2-&-2S#3342-mraa-library-for-android


下面這方式是用Linux內含標準 i2c_dev (ioctl)方法

1. 修改  /boot/config.txt 內容

intf:fiq_debugger=on

#intf:uart0=off

#intf:uart4=off

intf:i2c6=on    /* 原始前面有個 # 要去除, 把 off 改為on */

#intf:i2c7=off

#intf:i2s0=off

#intf:spdif=off

#intf:spi1=off

#intf:spi5=off

#intf:pwm0=off

#intf:pwm1=off

#intf:pwm3a=off

#intf:test_clkout2=off

2. 把連接 I2C

  SCL: pin-3

  SDA:  pin-5

  GND: pin 5

  VCC: pin 1 (3.3V)

3. 蝦皮購買的 AT24C256 EEPROM 儲存模組


https://ww1.microchip.com/downloads/en/devicedoc/doc0670.pdf







 在上圖 AT24C256 EEPROM 儲存模組的 A0,A1,A2 都是 0

 注意: 這顆是AT24C256B 所以只能調整A0及A1 (A2怎調整都是0)
 所以 Device Address 就是  1010000  (不含R/W) 為 0x50
 在填入i2c_msg  中的addr 只要填入 0x50 
  i2cmsg.addr  = 0x50; 
 那個 R/W bit 在 ioctl()的Read或Write時會自動幫忙位移及填寫

 下圖是 A0 設定為 1 這樣Device Address就是 0b1010001 (0x51)




上圖是指 先送1Byte Device Address
再送 2Byte 要寫入的 EEPROM Data Address
(有些EEPROM AT24C02D是只要1個Byte的Data address, 請自行查閱規格書)
後面是實際要寫到 EEPROM 的 Data Buffer
(至於START/ACK/STOP這些旗標,在ioctl內層會幫忙回應硬體)
注意: 這個Device 單次最大64Byte, 還有這EEPROM讀寫速度並不快, 因此處理連續讀寫要慢點.



上圖是指 先送1Byte Device Address
再送 2Byte 要讀取的 EEPROM Data Address
Device會回應1Byte Device Address及要讀取 EEPROM 的 Data Buffer
但i2cmsg.buf    = pI2cBuf;  只會得到後面實際EEPROM 的 Data Buffer
那Device回應的1Byte Device Address  ioctl會自行處理
(至於START/ACK/STOP這些旗標,在ioctl內層會幫忙回應硬體)


下圖是AT24C02D的Page Write Command







4. 在Debian 終端機模式下 

$ mkdir testi2c

$ cd testi2c 

// download testi2c.c and save file

$ cc -version

$ cc -g testi2c.c -o testi2c


  


$ ./testi2c





5. Source Code

Write: 紅框區

Read: 藍框區

Write Command


Read Command




#include <stdio.h>

#include <stdint.h>

#include <stdlib.h>

#include <err.h>

#include <errno.h>

#include <string.h>

#include <linux/types.h>

#include <linux/i2c.h>

#include <linux/i2c-dev.h>


#include <sys/ioctl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>


#define DEFAULT_I2C_BUS        "/dev/i2c-6"

#define DEFAULT_EEPROM_ADDR  0x50            /* the 24C16 sits on i2c address 0x50 */

#define DEFAULT_NUM_PAGES     8                /* we default to a 24C16 eeprom which has 8 pages */

#define BYTES_PER_PAGE         256             /* one eeprom page is 256 byte */

#define MAX_BYTES                256             /* max number of bytes to write in one chunk */


int eeprom_write( int nFile, unsigned int nDevAddr, unsigned int nDataOffset,

                        unsigned char *pBuf, unsigned char nLen)

{

struct i2c_rdwr_ioctl_data msg_rdwr;

struct i2c_msg i2cmsg;

int nRecLen;

  unsigned char sI2cBuf[0xFF];

  unsigned char *pI2cBuf = &sI2cBuf[0];


if( nLen > MAX_BYTES)

{

fprintf(stderr,"W3-I can only write MAX_BYTES bytes at a time!\n");

return -1;

}


if( nLen + nDataOffset > 256)

{

fprintf(stderr,"W4-Sorry, len(%d)+offset(%d) > 256 (page boundary)\n", nLen, nDataOffset);

return -1;

}


msg_rdwr.msgs = &i2cmsg;

msg_rdwr.nmsgs = 1;


  pI2cBuf[0] = ((unsigned char *)&nDataOffset)[1];    

  pI2cBuf[1] = ((unsigned char *)&nDataOffset)[0];

  memcpy( pI2cBuf+2, pBuf, nLen);

  

i2cmsg.addr  = nDevAddr;

i2cmsg.flags = 0;  // Write

i2cmsg.len    = 2+nLen;

i2cmsg.buf    = pI2cBuf;

if( ( nRecLen = ioctl( nFile, I2C_RDWR, &msg_rdwr)) < 0)

{

perror("W5A-ioctl()");

fprintf( stderr,"W5B-ioctl %d\n%s\n", nRecLen, strerror(errno));

    return -1;

}

else

{

if( pBuf != NULL)

{

     fprintf( stderr,"W7-Write %d bytes to eeprom at 0x%02x, offset %08x, Data: 0x%02x\n",

                 nLen, nDevAddr, nDataOffset, pBuf[0]);

    }                  

    return nRecLen;

  } 

}


int eeprom_read( int nFile, unsigned int nDevAddr, unsigned int nDataOffset,

                        unsigned char *pBuf, unsigned char nLen)

{

struct i2c_rdwr_ioctl_data msg_rdwr;

struct i2c_msg i2cmsg[2];

  unsigned char sAdsBuf[3];

  unsigned char *pAdsBuf = &sAdsBuf[0];

int nRecLen;


if( nLen > MAX_BYTES)

{

fprintf(stderr,"R3-I can only write MAX_BYTES bytes at a time!\n");

return -1;

}


msg_rdwr.msgs = i2cmsg;

msg_rdwr.nmsgs = 2;


  pAdsBuf[0] = ((unsigned char *)&nDataOffset)[1];    

  pAdsBuf[1] = ((unsigned char *)&nDataOffset)[0];

i2cmsg[0].addr  = nDevAddr;

i2cmsg[0].flags = 0;  // Write

i2cmsg[0].len    = 2;

i2cmsg[0].buf    = pAdsBuf; 

  i2cmsg[1].addr  = nDevAddr;

  i2cmsg[1].flags = I2C_M_RD;  // read data, from slave to master

i2cmsg[1].len    = nLen;

i2cmsg[1].buf    = pBuf;


if( ( nRecLen = ioctl( nFile, I2C_RDWR, &msg_rdwr))<0)

{

perror("R5-eeprom_read ioctl failed");

fprintf(stderr,"R6-eeprom_read ioctl returned %d\n", nRecLen);

return -1;

}

else

{

   fprintf( stderr,"R7-Read %d bytes from eeprom at 0x%02x, offset %08x, Data: 0x%02x\n",

            nLen, nDevAddr, nDataOffset, pBuf[0]);

   return nRecLen;

  } 

}


int main(void)

{

 int nDevAddr = 0x50, nDataOffset, nRecLen;

 int nFile, nRc, nI, nH;

 int nRWTestCount = 1;


 const char *pDevice = DEFAULT_I2C_BUS; //"/dev/i2c-6";

 unsigned char sBuf[0xFF];

 unsigned char *pBuf= &sBuf[0];

 unsigned long nFuncs;


 printf("Hello! This is a test prgoram.\n");

  nFile = open( pDevice, O_RDWR);

  if( nFile < 0)

  err( errno, "O1-Tried to open '%s'", pDevice);

  else

  printf( "\nO2-Open dev i2c-6 success.\n");

  if (ioctl( nFile, I2C_FUNCS, &nFuncs) < 0)

  {

perror("W5A-ioctl()");

        fprintf(stderr, "Error: Could not get the adapter functionality matrix: %s\n",

      strerror(errno));

   close( nFile);

  exit(0);

}

ioctl( nFile, I2C_TIMEOUT,2);//  TIMEOUT

ioctl( nFile, I2C_RETRIES,1);// retry count

  nRc = ioctl( nFile, I2C_SLAVE, nDevAddr);

  if( nRc < 0)

  {

  err(errno, "O3-Tried to set device address '0x%02x'", nDevAddr);

  exit(0);

  }

  else

  {

    printf("O4-Dev i2c-6 Set Address 0x50 success.\n");

  }


 nDevAddr = 0x50;

 nDataOffset = 0;

 nWriteLen = 8;

 nRWTestCount = 8;

 for( nI = 0; nI < nRWTestCount; nI++)

 {

  sleep(1); // 2sec

  for( nH = 0; nH < 0xFF; nH++)

    pBuf[nH] = 20+nI+nH;

  nDataOffset = nI*nWriteLen;

  nRecLen = eeprom_write( nFile, nDevAddr, nDataOffset, pBuf, nWriteLen);

  if( nRecLen < 0)

  {

    printf( "W3A-eeprom_write failed.\n");

  exit(0);

  }

  else

  {

   fprintf( stderr, "W-%d: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",

            nDataOffset,

            pBuf[0], pBuf[1], pBuf[2], pBuf[3], pBuf[4], pBuf[5], pBuf[6], pBuf[7]);

  }

 } 


 // Read Process

 fprintf( stderr, "\n\nR0-eeprom_Read start...\n");

 nDevAddr = 0x50;

 nReadLen = 8;

 for( nI = 0; nI < nRWTestCount; nI++)

 {

  for( nH = 0; nH < 0xFF; nH++)

    sBuf[nH] = 0xFF;

  sleep(1); // 2sec

  nDataOffset = nI*nReadLen;

  nRecLen = eeprom_read( nFile, nDevAddr, nDataOffset, pBuf, nReadLen);

  if( nRecLen < 0)

  {

    printf("R1-eeprom_read failed. %d\n", nI);

    exit(1);

  }

  else

  {

  fprintf( stderr, "R-%d: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",

              nDataOffset,

              pBuf[0], pBuf[1], pBuf[2], pBuf[3], pBuf[4], pBuf[5], pBuf[6], pBuf[7]);

  }

 }    

 close( nFile);

 return 0;

}





沒有留言:

張貼留言