2023年12月22日 星期五

ASUS Tinker Board 2S - Android 11- Change I2C speed (clock-frequency)

  ASUS Tinker Board 2S - Android 11- Change I2C speed (clock-frequency)

這方法只適用於自行編譯Android 11 source code方式

如要使用官網發佈之Android 11 Image file, 請使用官網論壇之方法.

https://tinker-board.asus.com/forum/index.php?/topic/15458-i2c-speed/&tab=comments#comment-16920


Step 1:

 建議先從乾淨下載的 Android 11 source code 進行測試

下載方式如下

  https://snoopymemory.blogspot.com/2023/10/asus-tinker-board-2s-1.html

 repo sync 完成後, 進行下列修改

Step 2:

 開啟 /kernel/arch/arm64/boot/dts/rockchip/rk3399.dtsi

 取得 i2c6 及 i2c7 之記憶體位址, 如下  





Step 3:

  開啟 /kernel/arch/arm64/boot/dts/rockchip/rk3399-tinker-board-2.dtsi

 將原來的內容 修改為 

 clock-frequency = <400000>

   傳輸速率為 400 kbit/s

 clock-frequency = <100000>;  

   傳輸速率為 100 kbit/s


Step 3:

 從 docker-builder-run.sh 開始編譯程式

 https://snoopymemory.blogspot.com/2023/10/asus-tinker-board-2s-4.html


Step 4:

  使用 balenaEtcher 將 /rockdev/Image-WW_Tinker_Board_2/WW_Tinker_Board_2-raw.img 

  燒錄到SD Card中.

Step 5:

  使用燒錄好的 SD Card 於ASUS Tinker Board 2S開機
  使用內建的 Tinker Config APP 把 i2c6 及 i2c7 開啟

Step 6:

開啟windwos power shell

> adb devices

  220198250600582

> adb -s 220198250600582 shell

/* ff150000 在step 1 rk3399.dtsi 中查詢到的 */

$ od -bc /proc/device-tree/i2c\@ff150000/clock-frequency

0000000 000 006 032 200
         \0 006 032 200
0000004

$ od -bc /proc/device-tree/i2c\@ff160000/clock-frequency
0000000 000 001 206 240
         \0 001 206 240
0000004

I2C6 
/sys/devices/platform/ff150000.i2c/of_node/clock-frequency
BitRateHz dword: hex as 0x00061a80 => decimal 400000  400kHz
I2C7
/sys/devices/platform/ff160000.i2c/of_node/clock-frequency
BitRateHz dword: hex as 0x000186a0 => decimal 100000  100kHz  



下面是實際測試

先對 I2C6 讀取



再對 I2C7 讀取












2023年11月20日 星期一

關於 FT260Q 開發版 DS_UMFT260EV1A操作方法(Windows)

 關於 FT260Q 開發版 DS_UMFT260EV1A I2C操作方法(Windows)


FT260Q 對I2C的指令做的比較單純, 大部份FT260Q已包裹起來(類似Linux 的 I2C_dev )

如果想要自己下達I2C每一個步驟的指令, 可以考慮使用CH341A

CH341A優點是可以完全自行控制, 缺點是程式碼要增加百倍.

(CH341也有提供簡單的包裹式指令集)



SCL : GPIO0

SDL : GPIO1

開發版上面還多掛了一顆 EEPROM AT24C02D

Device Address: 0x50

如果要取消這顆EEPROM , 把開發版之 JP8 切斷




開發版 DS_UMFT260EV1A
https://ftdichip.com/products/umft260ev1a/

EEPROM AT24C02D
https://www.microchip.com/en-us/product/at24c02d

FT260Q 
https://ftdichip.com/products/ft260q/


LibFT260
Application Note AN_395 User Guide for LibFT260 
https://www.ftdichip.com/Support/Documents/AppNotes/AN_395_User_Guide_for_LibFT260.pdf
這是原廠提供的一組函式庫,可以將指量傳輸給FT260Q
https://ftdichip.com/software-examples/
最下面那個沒有說明的 Libft260
https://ftdichip.com/wp-content/uploads/2022/10/LibFT260-v1.1.6.zip

解壓縮後
\LibFT260-v1.1.6\imports\LibFT260\inc\LibFT260.h
\LibFT260-v1.1.6\imports\LibFT260\lib\i386\LibFT260.lib, LibFT260.dll 

範例程式 (須要使用 Visual Studio )
\LibFT260-v1.1.6\samples\I2C

原始範例程式可以使用 Device Address  0x50 對EEPROM AT24C02D直接讀寫.

下面是直接修改對EEPROM AT24C256 直接讀寫. 

// Open Device
    FT260_HANDLE mhandle1 = INVALID_HANDLE_VALUE;
    FT260_STATUS ftStatus = FT260_OK;
    DWORD devNum = 0;
    FT260_CreateDeviceList(&devNum);

    // #define VID 0x0403  #define PID 0x6030
    ftStatus = FT260_OpenByVidPid(VID ,PID, 0, &mhandle1) ;
    printf("FT260_OpenByVidPid = %d\n",ftStatus );

    ftStatus = FT260_I2CMaster_Init(mhandle1, 100);


// (5)Read EEPROM 
 unsigned long len;
 DWORD writeLength = 0;
 DWORD readLength = 0;
 char dataw[10], datar[10];

 gI2CAddr = 0x51;
 writeLength = 0;
 dataw[0] = 0x00;
 dataw[1] = 0x00;
 FT260_I2CMaster_Write(mhandle1, gI2CAddr, FT260_I2C_START, &dataw, 2, &writeLength);
 writeLength = 0;
 len = 8;
 FT260_I2CMaster_Read(mhandle1, gI2CAddr, (FT260_I2C_FLAG)(FT260_I2C_REPEATED_START | FT260_I2C_STOP),
     datar, len, &readLength, 5000);



// (6)Write EEPROM 

 unsigned long len;
 DWORD writeLength = 0;
 char data[10];
 char addr;
 FT260_STATUS ftStatusW1, ftStatusW2 = FT260_OK;

 gI2CAddr = 0x51;
 writeLength = 0;
 data[0] = 0x00; // Data Address MSB
 data[1] = 0x00; // Data Address LSB
 
 ftStatusW1 = FT260_I2CMaster_Write(mhandle1, gI2CAddr, FT260_I2C_START, &addr, 2, &writeLength);
 data[0] = 0xAA;  // Write Data 
 data[1] = 0x22;
 data[2] = 0x33;
 data[3] = 0x44;
 data[4] = 0x55;
 data[5] = 0x66;
 data[6] = 0x77;
 data[7] = 0x88;
 ftStatusW2 = FT260_I2CMaster_Write(mhandle1, gI2CAddr, FT260_I2C_STOP, &data[0], 8, &writeLength);

上圖和規格書的要求很類似




比較難的是  I2C_FLAG 的選擇
建議是各種指令都下一次, 使用分析儀來解析其不同之處

enum FT260_I2C_FLAG
{
FT260_I2C_NONE  = 0,
FT260_I2C_START = 0x02,
FT260_I2C_REPEATED_START = 0x03,
FT260_I2C_STOP  = 0x04,
FT260_I2C_START_AND_STOP = 0x06
};

相關解釋可以查看AN_395_User_Guide_for_LibFT260.pdf
Page 20  4.2 I2C Master Functions







2023年11月9日 星期四

ASUS Tinker Board 2S Debian 使用實體電源開關

當  Tinker Board 2S Debian中按了系統左上角的 shotdown後ㄉ

Tinker Board 2S除了拔電源開關讓其啟動, 

只能使用實體電源開關讓其再次啟動,

 

 注意

  這個會和 ASUS Tinker Board 2S 關閉睡眠功能 那邊有點衝突

  當 suspend 被關時, 會出現

  GDBus.Error:org.freedesktop.DBus.Error.AccessDenied: Permission denied

  所以要安裝實體電源開關,   不要加入 



Debian 系統設定


2.無段開關


3.

因PCB 沒有印刷, 所以要注意方向, 很容易搞錯.
MASKROM 是用來燒錄UBOOT或EMMC時使用

4.








ASUS Tinker Board 2S Debian 安裝 Teamviewer Host

 下載 https://www.teamviewer.com/hk/download/linux/

 TeamViewer Host  Debian arm64-64bit


$ sudo dpkg -i teamviewer-host_15.47.3_arm64.deb

ASUS Tinker Board 2S 設定 GPIO(Using the sysfs Interface)

 ASUS Tinker Board 2S 設定 GPIO(Using the sysfs Interface)


https://tinker-board.asus.com/forum/index.php?/topic/14984-gpio/


方法一:

 Using the sysfs Interface

就是直接對 Linux File System 的 /sys/class/gpio/ 文字檔進行讀寫

方法二:

使用 ASUS 提供的  GPIO WiringPi for C library

https://github.com/TinkerBoard/TinkerBoard/wiki/User-Guide#sample-code-for-tinker-board-2-series

http://dlcdnet.asus.com/pub/ASUS/mb/Linux/Tinker_Board_2GB/GPIO_API_for_C.ZIP


方法三:

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

方法一說明:

1. 先到wiki 查詢 2S GPIO pin對應到 Linux Debian之Device Path

  (Tinker Board 2S 每一個GPIO Pin 對應到Linux GPIO Index)

  GPIO Config Table for Tinker Board 2 series:

  https://github.com/TinkerBoard/TinkerBoard/wiki/User-Guide#gpio-config-table-for-tinker-board-2-series






  

 例如本次要試驗的是 Tinker Board 2S GPIO pin-18 

  對應到 Debian之Device Path是 GPIO: /sys/class/gpio/gpio87

  


2. 參考網站

  GPIO Programming: Using the sysfs Interface

    https://www.ics.com/blog/gpio-programming-using-sysfs-interface

  風火輪對 /sys/class/gpio 之解釋文

    https://wiki.youyeetoo.cn/tinker/page/DebianSystem/User_GPIO

3. 設定

  $ sudo bluefish /boot/config.txt

    下面這一行 GPIO pin-18 為 spi5 不可以打開

    #intf:spi5=off

4. 指令

    > Device Path是 GPIO: /sys/class/gpio/gpio87

    $ sudo su

    $ cd /sys/class/gpio

    $ echo 87 >/sys/class/gpio/export

    $ ls /sys/class/gpio/gpio87/

    $ echo out >/sys/class/gpio/gpio87/direction

    $ echo 0 >/sys/class/gpio/gpio87/value

     > 電錶量測 Tinker Board 2S GPIO pin-18 會是 0 

    $ echo 1 >/sys/class/gpio/gpio87/valu

     > 電錶量測 Tinker Board 2S  GPIO pin-18 會是 3.3v


5. C/C++ Example
   How to Control GPIO Hardware from C or C++
   https://www.ics.com/blog/how-control-gpio-hardware-c-or-c
   https://github.com/tranter/blogs/tree/master/gpio/part5


2023年11月8日 星期三

ASUS Tinker Board 2S - Debian I2C TCA9539

 

TI TCA9539

16-bit 1.65- to 5.5-V I2C/SMBus I/O expander with interrupt, reset & config registers


IO-EXPANDER-EVM: I2C and SMBus IO Expander Evaluation Module






1. 修改  /boot/config.txt 內容
intf:i2c6=on    /* 原始前面有個 # 要去除, 把 off 改為on */

2. 連接 I2C
  SCL: IO-EXPANDER J8 SCL
  SDA: IO-EXPANDER J8 SDA
  GND: IO-EXPANDER J7 GND
  VCC: IO-EXPANDER J9 VCC

3.Device Address
PS: IO Expander EVM User's Guide (Rev. A) 這一片是共用PCB.
TCA6424A  0100010 0x22 
TCA9539     1110111 0x77 



  


GPIO設定, 主要是在Registers 6 和7


 但這片 IO Expander EVM 另外拉了幾條線用於顯示LED燈
 可以直接改用J13 來測試GPIO


第一個是 Device Address 
    i2cmsg.addr  = nDevAddr;
第二個是  Registers 6或7
第三個就是 GPIO Pin 0到N 的 GPIO ON/OFF


4. Linux IOCTL 範例程式


#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"
// 0100010 0x22 TCA6424A
// 1110111 0x77 TCA9539
#define DEFAULT_TCA9539_ADDR 0x77

int TCA9539_write( int nFile, unsigned int nDevAddr, unsigned int nRegConfig,
                  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];

  msg_rdwr.msgs = &i2cmsg;
  msg_rdwr.nmsgs = 1;

  pI2cBuf[0] = (unsigned char)nRegConfig;
  pI2cBuf[1] = pBuf[0];
  i2cmsg.addr  = nDevAddr;
  i2cmsg.flags = 0;  // Write
  i2cmsg.len   = 2;
  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
   return nRecLen;
}

int TCA9539_read( int nFile, unsigned int nDevAddr, unsigned int nRegConfig,
                  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;

  msg_rdwr.msgs = i2cmsg;
  msg_rdwr.nmsgs = 2;

  pAdsBuf[0] = (unsigned char)nRegConfig;
  i2cmsg[0].addr  = nDevAddr;
  i2cmsg[0].flags = 0;  // Write
  i2cmsg[0].len   = 1;
  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-TCA9539_read ioctl failed");
   fprintf(stderr,"R6-TCA9539_read ioctl returned %d\n", nRecLen);
   return -1;
  }
  else
  {
   return nRecLen;
  } 
}

int main(void)
{
 int nDevAddr = DEFAULT_TCA9539_ADDR;
 int nFile, nWriteLen, nReadLen, nRecLen; 
 int nRc, nRegConfig;
 const char *pDevice = DEFAULT_I2C_BUS; //"/dev/i2c-6";
 unsigned char sBuf[0x10], sStrBuf[0x10];
 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");
 }

 
 // https://www.ti.com/product/zh-tw/TCA9539 // Pull High
 // Table 7. Registers 6 And 7 (Configuration Registers)
 nDevAddr = DEFAULT_TCA9539_ADDR;
 nRegConfig = 0x06; // Registers 6 Configuration
 pBuf[0] = 0x00;  // Set GPIO Pin-0 to Pin-7 all Pull Low
 nWriteLen = 1;
 nRecLen = TCA9539_write( nFile, nDevAddr, nRegConfig, pBuf, nWriteLen);
 if( nRecLen < 0)
 {
  printf( "W3A-TCA9539_write failed.\n");
  exit(0);
 }
 else
 {
  fprintf( stderr, "W6-Set gpio pin0-7: 0x%02x\n", pBuf[0]);
 } 
 nRegConfig = 0x07;  // Registers 7 Configuration
 pBuf[0] = 0x00;  // Set GPIO Pin-10 to Pin-17 all Pull Low
 nWriteLen = 1;
 nRecLen = TCA9539_write( nFile, nDevAddr, nRegConfig, pBuf, nWriteLen);
 if( nRecLen < 0)
 {
  printf( "W3A-TCA9539_write failed.\n");
  exit(0);
 }
 else
 {
  fprintf( stderr, "W7-Set gpio pin10-17: 0x%02x\n", pBuf[0]);
 } 

 // Read Process
 nDevAddr = DEFAULT_TCA9539_ADDR;
 nRegConfig = 0x06; // Registers 6 Configuration
 pBuf[0] = 0xFF;    // Read GPIO Pin-0 to Pin-7 
 nReadLen = 1;
 nRecLen = TCA9539_read( nFile, nDevAddr, nRegConfig, pBuf, nReadLen);
 if( nRecLen < 0)
 {
  printf("R1-TCA9539_read failed.\n");
  exit(1);
 }
 else
 {
  fprintf( stderr, "W6-Get gpio pin0-7: 0x%02x\n", pBuf[0]);
 }   
 nRegConfig = 0x07; // Registers 7 Configuration
 pBuf[0] = 0xFF;    // Read GPIO Pin-10 to Pin-17 
 nReadLen = 1;
 nRecLen = TCA9539_read( nFile, nDevAddr, nRegConfig, pBuf, nReadLen);
 if( nRecLen < 0)
 {
  printf("R1-TCA9539_read failed.\n");
  exit(1);
 }
 else
 {
  fprintf( stderr, "W6-Get gpio pin10-17: 0x%02x\n", pBuf[0]);
 }   
 close( nFile);
 return 0;
}




5.

















ASUS Tinker Board 2S 關閉睡眠功能

 Disable Suspend and Hibernation

 $ sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

 $ reboot

 $ systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target

 $ sudo vim /etc/systemd/logind.conf 

   [Login] 

   HandleLidSwitch=ignore 

   HandleLidSwitchDocked=ignore 



 Enable Suspend and Hibernation

 $ sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target


ASUS Tinker Board 2S 安裝 Eclipse IDE c++ Developers


  $ sudo apt update

  $ sudo apt list --upgradable

  $ sudo apt upgrade

  $ sudo apt install default-jre

  $ sudo apt-get install  gdb

  $ sudo gbd --version

  > TH2S 開啟 chromium (主畫面下方地球圖示)

   https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2023-09/R/eclipse-cpp-2023-09-R-linux-gtk-aarch64.tar.gz

  > 下載 Eclipse IDE c++ 選擇 AArch64版本

  > 畫面左邊 File System -> linaro -> Downloads

    滑鼠點擊2下 eclipse-cpp-2023-09-R-linux-gtk-aarch64.tar.gz

    開啟目錄 home/linaro/Downloads/eclipse-cpp-2023-09-R-linux-gtk-aarch64/eclipse-installer

    滑鼠點擊 eclipse-inst

    選擇 Eclipse IDE c++

    選擇目錄 home/linaro/eclipse

    滑鼠點擊 home/linaro/eclipse/eclipse

    或左上角 Application-> Other -> Eclipse IDE c++ Developers


    . File -> New -> c/c++ Project

      -> C++ Managed Build

      -> Project name: test

      -> Project type: Hello World C++ Project

      -> Toolchains: Cross GCC

      -> 點擊 Finsh

      -> menu Project->Build All

      -> menu Run->Run

         選擇 Local C/C++ Application


ASUS Tinker Board 2S 燒錄Debian到eMMC

 1. ASUS 官網下載 Debian Imagefile

   https://tinker-board.asus.com/tw/download-list.html?product=tinker-board-2s

   > Tinker Board 2 /2S Debian 11 (kernel 5.10) v3.0.6 023/07/31 1.31 GB




2. 把Debian燒錄到一片 SD Card中(開機用) 

   燒錄完成後把SD Card插入 ASUS Tinker Board 2S

3. 把ASUS Tinker Board 2S的 J3 切換為 MASKROM模式.



4. 把balenaEtcher 把 Debian Imagefile 燒錄到 ASUS Tinker Board 2S

https://etcher.balena.io/#download-etcher



5. 燒錄完成後, 把 ASUS Tinker Board 2 J3 切換為 Default模式

6. 把 ASUS Tinker Board 2 中 SD Card移除

7. 開啟電源

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;

}