<?php /*!*************************************************************************** *! FILE NAME : i2c.inc *! DESCRIPTION: Provides functions to read/write over i2c buses in the camera, *! low-level R/W and additionally: *! Setting per-slave protection mask, *! Synchronizing system clock with a "CMOS" one *! Reading from/writing to EEPROM memory *! Copyright (C) 2008-2016 Elphel, Inc *! -----------------------------------------------------------------------------** *! *! This program is free software: you can redistribute it and/or modify *! it under the terms of the GNU General Public License as published by *! the Free Software Foundation, either version 3 of the License, or *! (at your option) any later version. *! *! This program is distributed in the hope that it will be useful, *! but WITHOUT ANY WARRANTY; without even the implied warranty of *! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *! GNU General Public License for more details. *! *! You should have received a copy of the GNU General Public License *! along with this program. If not, see <http://www.gnu.org/licenses/>. *! -----------------------------------------------------------------------------** *! $Log: i2c.inc,v $ *! Revision 1.4 2012/01/12 19:09:28 elphel *! bug fix *! *! Revision 1.3 2011/12/22 05:35:11 elphel *! Added "slow" option (for slow i2c devices) and reading the sensor board temperature *! *! Revision 1.2 2010/08/10 21:11:40 elphel *! added EEPROM support of the 10359 and 10338D boards. *! *! Revision 1.1.1.1 2008/11/27 20:04:03 elphel *! *! *! Revision 1.3 2008/10/04 16:09:04 elphel *! updating FPGA time with system time from CMOS *! *! Revision 1.2 2008/06/04 20:26:23 elphel *! includes SMBus operations, needed to activate USB on the 10369A board *! *! Revision 1.2 2008/03/16 01:24:09 elphel *! Added i2c speed control interface *! *! Revision 1.1 2008/03/15 23:05:18 elphel *! split i2c.php into i2c.php and i2c.inc (to be used from other scripts) *! *! */ //$i2c_slow_safe=array(3,4,2,2,7,7); /// sensor i2c delays are 2,2,1,1,7,7 // In NC393: buses 0..3 - sensors(old bus=0), 4 - aux (old bus=1) /// NC393-fucnctions to read/write sensor ports /// Reusing NC53 parameters: $width->$name, $raw -> function i2c_send_sensor($name, ///< device class name (get all by "cat /sys/devices/soc0/elphel393-sensor-i2c@0/i2c_all") $sensor_port, ///< sensor port :0..3 $ra, ///< register address $data, ///< data to write $sa7_offset=0) ///< slave address offset when there are multiple devices (2*sub-channel in 10359) ///< return true on success, false on failure { //$a<0 - use raw read/write $path = '/sys/devices/soc0/elphel393-sensor-i2c@0/i2c'.strval($sensor_port); $rslt = file_put_contents ($path, $name.' ' . strval($sa7_offset) . ' ' . strval($ra) . ' ' . strval($data)); return $rslt; } function i2c_receive_sensor($name, ///< device class name (get all by "cat /sys/devices/soc0/elphel393-sensor-i2c@0/i2c_all") $sensor_port, ///< sensor port :0..3 $ra, ///< register address $sa7_offset=0) ///< slave address offset when there are multiple devices (2*sub-channel in 10359) ///< @return read data or -1 on error { // Initiate read $path = '/sys/devices/soc0/elphel393-sensor-i2c@0/i2c'.strval($sensor_port); $rslt = file_put_contents ($path, $name.' ' . strval($sa7_offset) . ' ' . strval($ra) ); if (!$rslt) return -1; $rslt = file_get_contents ($path); if ($rslt === false) return -1; return intval($rslt); } /** Read 256 bytes from device as a string */ function i2c_read256b_sensor($name, ///< device class name (get all by "cat /sys/devices/soc0/elphel393-sensor-i2c@0/i2c_all") $sensor_port, ///< sensor port :0..3 $sa7_offset=0) ///< slave address offset when there are multiple devices (2*sub-channel in 10359) ///< @return read data string or partial (empty) string on error { $rslt = ''; for ($ra=0;$ra<256;$ra++){ if (($d = i2c_receive_sensor($name,$sensor_port,$ra,$sa7_offset))<0) break; $rslt .= chr($d); } return $rslt; } /** Write string to device */ function i2c_write256b_sensor($data, ///< Sring to write $name, ///< device class name (get all by "cat /sys/devices/soc0/elphel393-sensor-i2c@0/i2c_all") $sensor_port, ///< sensor port :0..3 $sa7_offset=0) ///< slave address offset when there are multiple devices (2*sub-channel in 10359) ///< @return number of bytes written { $len = 0; if (strlen($data)<256) $data.=chr(0); for ($ra=0;$ra<strlen($data);$ra++){ $b = ord(substr($data,$ra,1)); if (($d = i2c_send_sensor($name,$sensor_port,$ra,$b,$sa7_offset))<0) break; usleep(10000); $len ++; } return $len; } ///sys/devices/soc0/elphel393-sensor-i2c@0# echo "mt9p006 8 9" > i2c3; cat i2c3 /* cy22393: 0x69 1 1 100 kHz sensor_temp: 0x18 1 2 100 kHz sensor_eeprom: 0x50 1 1 100 kHz pca9500_eeprom: 0x50 1 1 100 kHz el10359_32: 0x8 1 4 500 kHz el10359: 0x8 1 2 500 kHz mt9p006: 0x48 1 2 500 kHz mt9f002: 0x10 2 2 500 kHz */ function getSlowArray($usec=0){ if ($usec<0) $usec=0; $SCL_low=3+(int) ($usec*8); $SCL_high=4+(int) ($usec*8); if ($SCL_low>254) $SCL_low=254; if ($SCL_high>254) $SCL_high=254; $i2c_slow_safe=array($SCL_low ,$SCL_high,2,2,7,7); /// sensor i2c delays are 2,2,1,1,7,7 return $i2c_slow_safe; } ///Map i2c bus of 393 to "old" one function i2c_bus353($bus){ if ($bus<4) return 0; else return $bus-3; } function i2c_ctl($bus,$data="") { //!$data - string of decimal values separated by ":", empty - don't change $size=6; // bytes per bus $data=trim($data); if ($data!="") { $darr=explode(":",$data); $i2cctl = fopen('/dev/xi2cctl', 'w'); for ($i=0; ($i<$size) && ($i<count($darr)); $i++) if ($darr[$i]!=""){ fseek ($i2cctl, i2c_bus353($bus)*$size+$i) ; fwrite($i2cctl, chr($darr[$i]+0), 1); } fclose($i2cctl); } $i2cctl = fopen('/dev/xi2cctl', 'r'); fseek ($i2cctl, i2c_bus353($bus)*$size) ; $data = fread($i2cctl, $size); fclose($i2cctl); $xml = new SimpleXMLElement("<?xml version='1.0'?><i2cctl/>"); $xml->addChild ('scl_high',ord($data[0])); $xml->addChild ('scl_low', ord($data[1])); $xml->addChild ('slave2master', ord($data[2])); $xml->addChild ('master2slave', ord($data[3])); $xml->addChild ('filter_sda', ord($data[4])); $xml->addChild ('filter_scl', ord($data[5])); // $data=$xml->asXML(); return $xml; } // end of i2c_ctl() function i2c_ctl_arr($bus,$darr=array()) { //!$data - 6-element array $size=6; // bytes per bus if (count($darr)>00) { $i2cctl = fopen('/dev/xi2cctl', 'w'); for ($i=0; ($i<$size) && ($i<count($darr)); $i++) if ($darr[$i]!=""){ fseek ($i2cctl, i2c_bus353($bus)*$size+$i) ; fwrite($i2cctl, chr($darr[$i]+0), 1); } fclose($i2cctl); } $i2cctl = fopen('/dev/xi2cctl', 'r'); fseek ($i2cctl, i2c_bus353($bus)*$size) ; $data = fread($i2cctl, $size); fclose($i2cctl); for ($i=0;$i<$size; $i++) $darr[$i]=ord($data[$i]); return $darr; } // end of i2c_ctl() function i2c_send($width, $bus, $a, $d, $raw = 0) { // $a<0 - use raw read/write if ($bus < 4) return i2c_send_sensor ( $width, $bus, $a, $d, $raw ); else $bus = i2c_bus353 ( $bus ); $bus353 = i2c_bus353 ( $bus ); $w = ($width == 16) ? 2 : 1; $i2c_fn = '/dev/xi2c' . ($raw ? 'raw' : (($w == 2) ? '16' : '8')) . (($bus == 0) ? '' : '_aux'); $i2c = fopen ( $i2c_fn, 'w' ); fseek ( $i2c, $w * $a ); if ($w == 1) $res = fwrite ( $i2c, chr ( $d ) ); else $res = fwrite ( $i2c, chr ( floor ( $d / 256 ) ) . chr ( $d - 256 * floor ( $d / 256 ) ) ); fclose ( $i2c ); return $res; } // end of i2c_send() function i2c_send_slow($width, $bus, $a, $d, $raw = 0, $extrausec = -1) { // $a<0 - use raw read/write if ($bus < 4) return i2c_send_sensor ( $width, $bus, $a, $d, $raw ); else $bus = i2c_bus353 ( $bus ); if (($bus == 0) && ($extrausec >= 0)) { $i2c_old_ctrl = i2c_ctl_arr ( 0 ); i2c_ctl_arr ( 0, getSlowArray ( $extrausec ) ); } $w = ($width == 16) ? 2 : 1; $i2c_fn = '/dev/xi2c' . ($raw ? 'raw' : (($w == 2) ? '16' : '8')) . (($bus == 0) ? '' : '_aux'); $i2c = fopen ( $i2c_fn, 'w' ); fseek ( $i2c, $w * $a ); if ($w == 1) $res = fwrite ( $i2c, chr ( $d ) ); else $res = fwrite ( $i2c, chr ( floor ( $d / 256 ) ) . chr ( $d - 256 * floor ( $d / 256 ) ) ); fclose ( $i2c ); if (($bus == 0) && ($extrausec >= 0)) { i2c_ctl_arr ( 0, $i2c_old_ctrl ); // / restore old speed (not thread-safe) } return $res; } // end of i2c_send() function smbus_send($a, $d) { // d - array $i2c_fn = '/dev/xi2c8_aux'; $i2c = fopen ( $i2c_fn, 'w' ); fseek ( $i2c, $a ); $cmd = chr ( count ( $d ) ); foreach ( $d as $b ) $cmd .= chr ( $b ); // var_dump($cmd); $res = fwrite ( $i2c, $cmd ); fclose ( $i2c ); return $res; } // end of i2c_send() function i2c_receive($width, $bus, $a, $raw = 0) { if ($bus < 4) return i2c_receive_sensor ( $width, $bus, $a, $raw ); else $bus = i2c_bus353 ( $bus ); $w = ($width == 16) ? 2 : 1; $i2c_fn = '/dev/xi2c' . ($raw ? 'raw' : (($w == 2) ? '16' : '8')) . (($bus == 0) ? '' : '_aux'); $i2c = fopen ( $i2c_fn, 'r' ); fseek ( $i2c, $w * $a ); $data = fread ( $i2c, $w ); fclose ( $i2c ); if (strlen ( $data ) < $w) return - 1; $v = unpack ( ($w == 1) ? 'C' : 'n1', $data ); return $v [1]; } // end of i2c_receive() function i2c_receive_slow($width, $bus, $a, $raw = 0, $extrausec = -1) { if ($bus < 4) return i2c_receive_sensor ( $width, $bus, $a, $raw ); else $bus = i2c_bus353 ( $bus ); if (($bus == 0) && ($extrausec >= 0)) { $i2c_old_ctrl = i2c_ctl_arr ( 0 ); i2c_ctl_arr ( 0, getSlowArray ( $extrausec ) ); } $w = ($width == 16) ? 2 : 1; $i2c_fn = '/dev/xi2c' . ($raw ? 'raw' : (($w == 2) ? '16' : '8')) . (($bus == 0) ? '' : '_aux'); $i2c = fopen ( $i2c_fn, 'r' ); fseek ( $i2c, $w * $a ); $data = fread ( $i2c, $w ); fclose ( $i2c ); if (strlen ( $data ) < $w) return - 1; $v = unpack ( ($w == 1) ? 'C' : 'n1', $data ); if ($bus == 0) { i2c_ctl_arr ( 0, $i2c_old_ctrl ); // / restore old speed (not thread-safe) } return $v [1]; } // end of i2c_receive() function i2c_setprot($bus, $slave, $bit, $value) { // !slave is MSB aligned, LSB==0) $bus = i2c_bus353 ( $bus ); $i2cprot = fopen ( "/dev/xi2cenable", 'r+' ); fseek ( $i2cprot, ($bus * 128) + ($slave >> 1) ); $data = ord ( fread ( $i2cprot, 1 ) ); if ($value) $data |= (1 << $bit); else $data &= ~ (1 << $bit); fseek ( $i2cprot, ($bus * 128) + ($slave >> 1) ); fwrite ( $i2cprot, chr ( $data ) ); fclose ( $i2cprot ); } // end of i2c_setprot () function i2c_getCMOSClock() { echo "**** FUNCTION NOT SUPPORTED in NC393 *****"; return - 1; $i2c = fopen ( '/dev/xi2c8_aux', 'r' ); fseek ( $i2c, 0x5102 ); // seconds in clock on the 10369 board $data = fread ( $i2c, 7 ); // sec/min/hours/days/weekdays/century-months/years, BCD fclose ( $i2c ); if (strlen ( $data ) < 7) return "error"; $v = unpack ( 'C*', $data ); $d = array ( "sec" => ($v [1] & 0xf) + 10 * (($v [1] >> 4) & 0x7), "min" => ($v [2] & 0xf) + 10 * (($v [2] >> 4) & 0x7), "hrs" => ($v [3] & 0xf) + 10 * (($v [3] >> 4) & 0x3), "day" => ($v [4] & 0xf) + 10 * (($v [4] >> 4) & 0x3), "wday" => $v [5] & 0x7, "month" => ($v [6] & 0xf) + 10 * (($v [6] >> 4) & 0x1), "year" => ($v [7] & 0xf) + 10 * (($v [7] >> 4) & 0xf) + (($v [6] & 0x80) ? 2000 : 1900) ); exec ( "date -s " . sprintf ( "%02d%02d%02d%02d%04d.%02d", $d ["month"], $d ["day"], $d ["hrs"], $d ["min"], $d ["year"], $d ["sec"] ), $out, $ret ); if ($ret == 0) { elphel_set_fpga_time ( time () + 0.0 ); } return $ret; } // end of i2c_getCMOSClock() function i2c_bcd($v) { $d=intval($v,10); return $d + 6*floor($d/10); } // end of i2c_bcd($v) function i2c_setCMOSClock(){ $ts=time(); $data=chr(i2c_bcd(gmdate("s",$ts))). chr(i2c_bcd(gmdate("i",$ts))). chr(i2c_bcd(gmdate("H",$ts))). chr(i2c_bcd(gmdate("d",$ts))). chr(i2c_bcd(gmdate("w",$ts))). chr(i2c_bcd(gmdate("m",$ts))+ ((intval(gmdate("Y",$ts),10)>=2000)?0x80:0)). chr(i2c_bcd(gmdate("y",$ts))); $i2c = fopen('/dev/xi2c8_aux', 'w'); fseek ($i2c, 0x5102) ; //seconds in clock on the 10369 board $written= fwrite($i2c, $data); fclose($i2c); if ($written < strlen($data)) return "error"; return "OK"; } // end of i2c_setCMOSClock() function i2c_read256b($slave=0xa0,$bus=1,$extrausec=0) { //will read 256 bytes from slave (address is 8-bit, includes r/~w) if ($bus < 4) return i2c_read256b_sensor ( $slave, $bus, $extrausec); // ($name, $sensor_port, $sa7_offset) else $bus = i2c_bus353 ( $bus ); if (($bus==0) && ($extrausec>=0)){ $i2c_old_ctrl= i2c_ctl_arr(0); i2c_ctl_arr(0,getSlowArray($extrausec)); } $i2c_fn='/dev/xi2c8'.(($bus==0)?'':'_aux'); $i2c = fopen($i2c_fn, 'r'); fseek ($i2c, $slave*128) ; //256 per slave, but slave are only even $data = fread($i2c, 256); // full 256 bytes fclose($i2c); if (($bus==0) && ($extrausec>=0)){ i2c_ctl_arr(0,$i2c_old_ctrl); /// restore old speed (not thread-safe) } return $data; } // end of i2c_read256b () //this EEPROM writes only 4 bytes sequentionally (only 2 LSBs are incremented) function i2c_write256b($data, $slave=0xa0,$bus=1,$extrausec=0) { //will write up to 256 bytes $data to slave (address is 8-bit, includes r/~w). EEPROM should be un-protected if ($bus < 4) return i2c_write256b_sensor ($data, $slave, $bus, $extrausec); // ($data, $name, $sensor_port, $sa7_offset) else $bus = i2c_bus353 ( $bus ); if (($bus==0) && ($extrausec>=0)){ $i2c_old_ctrl= i2c_ctl_arr(0); i2c_ctl_arr(0,getSlowArray($extrausec)); } $maxretries=200; //measured - 19 if (!is_string($data)) return -1; if (strlen($data)>256) return -2; if (strlen($data)<256) $data.=chr(0); $i2c_fn='/dev/xi2c8'.(($bus==0)?'':'_aux'); $i2c = fopen($i2c_fn, 'w'); $len=0; for ($i=0;$i<strlen($data); $i+=4) { for ($retry=0; $retry< $maxretries; $retry++) { fseek ($i2c, $slave*128+$i) ; //256 per slave, but slave are only even $rslt=fwrite($i2c, substr($data,$i,4)); if ($rslt>0) break; } if ($rslt<=0) { $len=$rslt; break; } $len+=$rslt; } fclose($i2c); if (($bus==0) && ($extrausec>=0)){ i2c_ctl_arr(0,$i2c_old_ctrl); /// restore old speed (not thread-safe) } return $len; } // end of i2c_write256b () ?>