MM7150 is 9-axis sensor fusion motion module from microchip (http://www.microchip.com/wwwproducts/en/MM7150 ).
It’s a complete self-contained solution comprising a 3-axis accelerometer, a gyroscope, a magnetometer, and a SSC7150 motion coprocessor.
3D Motion click is a module which carries MM7150 and has mikroBUS interface. With motion sensor, we can do as much as we can imagine.
This article shows library of MM7150 and example code to read data from this module.
The MM7150 responds to the standard HID protocol over I2C. It’s really complicated for beginner. So, I make it simple. This library works on both PHPoC blue and black.
Source Code Architecture
I2C driver library
It is official library from PHPoC and you can get it here https://github.com/phpoc/psp file sd_340.php
HID over I2C library
I make a simple version of it. It may be updated in the future.
vn_hid_i2c.php
PHP Code:
<?php
//HID descriptor fields offset
define("HID_DESC_LEN_OFFSET", 0x00);
define("HID_VERSION_OFFSET", 0x02);
define("HID_REPORT_DESC_LEN_OFFSET", 0x04);
define("HID_REPORT_DESC_REG_OFFSET", 0x06);
define("HID_INPUT_REG_OFFSET", 0x08);
define("HID_MAX_INPUT_LEN_OFFSET", 0x0A);
define("HID_OUPUT_REG_OFFSET", 0x0C);
define("HID_MAX_OUTPUT_LEN_OFFSET", 0x0E);
define("HID_CMD_REG_OFFSET", 0x10);
define("HID_DATA_REG_OFFSET", 0x12);
define("HID_VENDOR_ID_OFFSET", 0x14);
define("HID_PRODUCT_ID_OFFSET", 0x16);
define("HID_VERSION_ID_OFFSET", 0x18);
define("CMD_GET_HID_DESC", 1);
define("CMD_GET_RPT_DESC", 2);
define("CMD_RESET_DEV", 1);
define("CMD_POWER_ON", 2);
define("CMD_SLEEP", 3);
define("CMD_HID_GET_RPT_INPT", 4);
define("CMD_HID_GET_RPT_FEAT", 5);
define("CMD_HID_SET_RPT_OUTP", 6);
define("CMD_HID_SET_RPT_FEAT", 7);
define("HID_COLLECTION", 0xA1);
define("HID_PHYSICAL", 0x00);
define("HID_REPORT_ID", 0x85);
define("HID_END_COLLECTION", 0xC0);
// REPORT PARSE MACROS
define("HID_USG_SENS_PROPERTY_REPORTING_STATE_ALL_EVENTS_ENUM", 0x02);
define("HID_USG_SENS_TYPE", 0x09);
define("HID_USG_SENS_PROPERTY", 0x0A);
define("HID_USG_SENS_PROPERTY_CONN_TYPE", 0x09); //HID byte sequence : 0x0A,0x09,0x03
define("HID_USG_SENS_PROPERTY_RPT_STATE", 0x16); //HID byte sequence : 0x0A,0x16,0x03
define("HID_USG_SENS_PROPERTY_PWR_STATE", 0x19); //HID byte sequence : 0x0A,0x19,0x03
define("HID_USG_SENS_", 0x0A);
define("HID_USG_SENS_STATE_1", 0x01); //HID byte sequence : 0x0A,0x01,0x02
define("HID_USG_SENS_STATE_2", 0x02); //HID byte sequence : 0x0A,0x01,0x02
define("HID_USG_SENS_EVENT_1", 0x02); //HID byte sequence : 0x0A,0x02,0x02
define("HID_USG_SENS_EVENT_2", 0x02); //HID byte sequence : 0x0A,0x02,0x02
define("HID_USG_SENS_PROPERTY_RPT_INT", 0x0E); //HID byte sequence : 0x0A,0x0E,0x03
define("HID_USG_SENS_DATA_ACCU", 0x44); //the sensor data accuracy value is the //define("HID_USG_SENS_DATA(a,b) value (where (a|b)).
// For example, for the accelerometer: HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_ACCURACY)
// HID_USG_SENS_DATA_MOTION_ACCELERATION | HID_USG_SENS_DATA_MOD_ACCURACY = 0x04 | 0x40 = 0x44 this is accuracy value for Non-RAW sensors
define("HID_USG_SENS_DATA_RAW_ACCU", 0x45); //the sensor data accuracy value is the //define("HID_USG_SENS_DATA(a,b) value (where (a|b)).
// For example, for RAW sensor: HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_ACCURACY),
// HID_USG_SENS_DATA_CUSTOM_VALUE | HID_USG_SENS_DATA_MOD_ACCURACY = 0x05 | 0x40 = 0x45 this is accuracy value for RAW sensors
define("HID_USG_SENS_DATA_RES", 0x54); //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_RESOLUTION' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
// HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_RESOLUTION)
// where //define("HID_USG_SENS_DATA_MOD_RESOLUTION 0x50
define("HID_USG_SENS_DATA_RAW_RES", 0x55); // HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_RESOLUTION)
define("HID_USG_SENS_DATA_MOD_SENS", 0x14); //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
// HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS)
// where //define("HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS 0x10
define("HID_USG_SENS_DATA_RAW_MOD_SENS", 0x15); // HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS)
define("HID_USG_SENS_DATA_MOD_MAX", 0x24); //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_MAX' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
//HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MAX)
// where //define("HID_USG_SENS_DATA_MOD_MAX 0x20
define("HID_USG_SENS_DATA_RAW_MOD_MAX", 0x25); //HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_MAX)
define("HID_USG_SENS_DATA_MOD_MIN", 0x34); //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_MIN' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
//HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MIN)
// where //define("HID_USG_SENS_DATA_MOD_MIN 0x30
define("HID_USG_SENS_DATA_RAW_MOD_MIN", 0x35); //HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_MIN)
define("HID_UNIT_EXP", 0x55); // HID_UNIT_EXPONENT = 0x55
define("MAX_SENSOR_NUM", 12); // user can increase if need more
define("SENSOR_INFO_LEN", 7); // 7 byte for each sensor:
define("SENSOR_ID_OFFSET", 0);
define("SENSOR_TYPE_OFFSET", 1);
define("SENSOR_DATART_OFFSET", 2);
define("SENSOR_SENSOR_OFFSET", 3);
define("SENSOR_SENSOR_EXP_OFFSET",4);
define("SENSOR_DATA_OFFSET", 5);
define("SENSOR_DATA_EXP_OFFSET", 6);
define("ACCEL_SENSOR_TYPE", 0x73);
define("GYRO_SENSOR_TYPE", 0x76);
define("CMP_SENSOR_TYPE", 0x83);
define("ORI_SENSOR_TYPE", 0x8A);
define("INCL_SENSOR_TYPE", 0x86);
define("RAW_SENSOR_TYPE", 0xE1);
// Add more sensor type here
define("FLASH_UPDATE_RPT_ID", 0xE);
// SET REPORT command bytes
define("SET_RPT_FEAT", 0x30);
define("SET_OPCODE", 0x03);
// Report Feature Defines
define("RPT_SIZE_LSB", 0);
define("RPT_REPORT_STATE", 4);
define("RPT_PWR_STATE", 5);
define("RPT_SENS_STATE", 6);
define("RPT_REPORT_INTVAL_LSB", 7);
define("RPT_REPORT_INTVAL_MSB", 8);
define("RPT_CHG_SENS_LSB", 13);
define("RPT_CHG_SENS_MSB", 14);
define("GET_RPT_CMD_MSB", 0x02);// GET REPORT commmand bytes
define("GET_RRT_INPT", 0x10);
define("GET_RPT_FEAT", 0x30);
//HID descriptor table
$hid_descriptor_table = str_repeat("\x00", 30);
$hid_device_sensor_num = 0;
$hid_report_desc_table = str_repeat("\x00", MAX_SENSOR_NUM*SENSOR_INFO_LEN);
//Application sensors parameter in use
$app_sensor_info = array("", "", "", "", "", "", "", "");
$app_data = array("", "", "", "", "", "", "", "");
/*
This function is to get value of timer.
*/
function vn_hid_i2c_get_tick($div = "ms")
{
while(($pid = pid_open("/mmap/st9", O_NODIE)) == -EBUSY)
usleep(500);
if(!pid_ioctl($pid, "get state"))
{
pid_ioctl($pid, "set div us");
pid_ioctl($pid, "start");
}
$tick = pid_ioctl($pid, "get count");
pid_close($pid);
if($div == "ms")
{
return (int) $tick/1000;
}
return $tick;
}
function vn_hid_i2c_read($i2c_id, &$rbuf)
{
if($i2c_id != 0)
exit("i2c_read: i2c_id out of range $i2c_id\r\n");
$pid = pid_open_nodie("/mmap/i2c$i2c_id", "i2c_read");
pid_ioctl($pid, "req read 2");
while(pid_ioctl($pid, "get state"))
;
$len_buf = "";
pid_read($pid, $len_buf);
$len = bin2int($len_buf, 0, 2) - 2;
pid_ioctl($pid, "req read $len");
while(pid_ioctl($pid, "get state"))
;
$len = pid_read($pid, $rbuf);
$rbuf = $len_buf.$rbuf;
pid_close($pid);
return strlen($rbuf);
}
function vn_hid_i2c_write_read($i2c_id, $wbuf, &$rbuf)
{
i2c_write_read($i2c_id, $wbuf, $rbuf, 2);
$rlen = bin2int($rbuf, 0, 2);
i2c_write_read($i2c_id, $wbuf, $rbuf, $rlen);
}
function vn_hid_i2c_report_buf_val(&$buf, $count, $type = "int")
{
$low_bound = 0;
$upp_bound = 0;
$size = count($buf);
$index = 0;
while($index < $size)
{
$low_bound = $upp_bound;
$upp_bound += strlen($buf[$index]);
if($count < $upp_bound)
if($type == "int")
return bin2int($buf[$index], $count - $low_bound, 1);
else
return substr($buf[$index], $count - $low_bound, 1);
$index++;
}
return false;
}
function vn_hid_i2c_parse_report_dest(&$buf)
{
global $hid_device_sensor_num;
global $hid_report_desc_table;
global $hid_descriptor_table;
$tol_len = 0;
for($i = 0; $i < count($buf); $i++)
$tol_len += strlen($buf[$i]);
$count = 0;
$usage_offset = 0;
$reach_flag = false;
$hid_device_sensor_num = 0;
while($count < $tol_len)
{
if(vn_hid_i2c_report_buf_val($buf, $count) == HID_COLLECTION ) // Look for HID_COLLECTION(Physical) which should be start of REPORT ID (sensor device) info in report descriptor table
{
$count++; // Increment the pointer to look at the next byte
if(vn_hid_i2c_report_buf_val($buf, $count++) == HID_PHYSICAL && vn_hid_i2c_report_buf_val($buf, $count++) == HID_REPORT_ID ) // Check if the next two bytes meet the next required identifier needs
{
$usage_offset = 0; // clear offset of desired HID_USG_SENS_PROERTY field in report descriptor for later use in finding parameters within the GetReportFeature data
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_ID_OFFSET] = vn_hid_i2c_report_buf_val($buf, $count, "bin"); // Store first sensor device ID number in out temp struct
while(vn_hid_i2c_report_buf_val($buf, $count++) != HID_USG_SENS_TYPE); // Parse until sensor type indicator found (in HID table: HID_USG_SENS_TYPE_MOTION_ACCELEROMETER_3D)
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_TYPE_OFFSET] = vn_hid_i2c_report_buf_val($buf, $count, "bin"); // Store sensor type in struct
$reach_flag = true; // Set the flag to show we are in a field of data that we desire
}
}
if ( vn_hid_i2c_report_buf_val($buf, $count) == HID_END_COLLECTION ) // Look for end of this sensor's collection
{
$count++;
if( vn_hid_i2c_report_buf_val($buf, $count) == HID_END_COLLECTION )
break; // end of the report has occurred
else if(vn_hid_i2c_report_buf_val($buf, $count++) == HID_REPORT_ID) // Next report ID has been found
{
$usage_offset = 0; // clear offset of desired HID_USG_SENS_PROERTY field in report descriptor for later use in finding parameters within the GetReportFeature data
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_ID_OFFSET] = vn_hid_i2c_report_buf_val($buf, $count, "bin"); // Store location of sensor ID within the HID Report in struct
while(vn_hid_i2c_report_buf_val($buf, $count++) != HID_USG_SENS_TYPE); // Look for sensor type identifier (ie HID_USG_SENS_TYPE_MOTION_ACCELEROMETER_3D)
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_TYPE_OFFSET] = vn_hid_i2c_report_buf_val($buf, $count++, "bin"); // Store location of sensor type within the HID Report in struct
$reach_flag = true; // Set the flag to show we are in a field of data that we desire
}
}
if ($reach_flag) // Check if we should proceed parsing within the HID_USG_SENS_Properties or simply continue incrementing until a new ID is found
{
while($count < $tol_len) // Search for relevant features HID_USG_SENS_Properties
{
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_PROPERTY)
{
$count++;
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_PROPERTY_CONN_TYPE) // Seach for sensor connection type and increment offset variable because this field is undesired (in HID table:HID_USG_SENS_PROPERTY_SENSOR_CONNECTION_TYPE)
{
$count++;
if (vn_hid_i2c_report_buf_val($buf, $count++) == 0x03) // last parameter of HID_USG_SENS_PROPERTY_SENSOR_CONNECTION_TYPE (0x0A,0x09,0x03)
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
}
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_PROPERTY_RPT_STATE) // Search for reporting state and increment offset variable because this field is undesired (in HID table:HID_USG_SENS_PROPERTY_REPORTING_STATE)
{
$count++;
if (vn_hid_i2c_report_buf_val($buf, $count++) == 0x03) // last parameter of HID_USG_SENS_PROPERTY_REPORT_INTERVAL (0x0A,0x0E,0x03)
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
}
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_PROPERTY_PWR_STATE) // Search for power state and increment offset variable because this field is undesired (in HID table : HID_USG_SENS_PROPERTY_POWER_STATE)
{
$count++;
if (vn_hid_i2c_report_buf_val($buf, $count++) == 0x03) // last parameter of HID_USG_SENS_PROPERTY_POWER_STATE (0x0A,0x19,0x03)
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
}
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_STATE_1) // Search for sensor state and increment offset variable because this field is undesired (in HID table:HID_USG_SENS_STATE)
{
$count++;
if (vn_hid_i2c_report_buf_val($buf, $count++) == 0x02) // last parameter of HID_USG_SENS_STATE (0x0A,0x01,0x02)
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
}
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_PROPERTY_RPT_INT) // Search for reporting interval. We desire this value so store it in our struct and then increment the offset (in HID table:HID_USG_SENS_PROPERTY_REPORT_INTERVAL)
{
$count++;
if (vn_hid_i2c_report_buf_val($buf, $count++) == 0x03) // last parameter of HID_USG_SENS_PROPERTY_REPORT_INTERVAL (0x0A,0x0E,0x03)
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_DATART_OFFSET] = int2bin($usage_offset++, 1); //save & increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
}
$count++; //increment position ptr
// NOTE: There is a different identifier for RAW data and that is why two identifiers are checked for here
if ( (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_ACCU) || (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_RAW_ACCU) ) // Search for sensor accuracy & increment offset variable because this field is undesired (in HID table:HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_ACCURACY))
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
if ( (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_RES) || (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_RAW_RES) ) // Search for sensor resolution and increment offset variable because this field is undesired (in HID table: HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_RESOLUTION))
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
if ( (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_MOD_SENS) || (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_RAW_MOD_SENS) ) // Search for sensor sensitivity (HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS))
{
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_SENSOR_OFFSET] = int2bin($usage_offset++, 1); //save & increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
while(vn_hid_i2c_report_buf_val($buf, $count++) != HID_UNIT_EXP); // Increment until exponent value of the data is found
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_SENSOR_EXP_OFFSET] = vn_hid_i2c_report_buf_val($buf, $count, "bin"); // Store this value in the temp struct
break;
}
if ( (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_MOD_MAX) || (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_RAW_MOD_MAX) ) // Search for sensor MAX val and increment offset variable because this field is undesired HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MAX)
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
if ( (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_MOD_MIN) || (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_DATA_RAW_MOD_MIN) ) // Search for sensor MIN val and increment offset variable because this field is undesired HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MIN)
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
}
else $count++; // If a new identifier has not yet been reached, continue traversing report descriptor
}
$usage_offset = 0; //reset HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
while($count < $tol_len) // Search for relevant input features
{
if(vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_)
{
$count++; // Continue to next byte
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_STATE_1)
{
$count++;
if(vn_hid_i2c_report_buf_val($buf, $count++) == HID_USG_SENS_STATE_2) // Search for HID usage sensor state and increment offset variable because this field is undesired
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
}
if (vn_hid_i2c_report_buf_val($buf, $count) == HID_USG_SENS_EVENT_1)
{
$count++; // Continue to next byte
if (vn_hid_i2c_report_buf_val($buf, $count++) == HID_USG_SENS_EVENT_2) // Search for HID usage sensor event and increment the offset variable
{
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
while(vn_hid_i2c_report_buf_val($buf, $count++) != HID_END_COLLECTION); // end of the sensor event field signifies the start of desired input data
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_DATA_OFFSET] = int2bin($usage_offset + 3, 1); //save HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data) NOTE: offset an additional 3 for extra data received on GPIO interrupt
while(vn_hid_i2c_report_buf_val($buf, $count++) != HID_UNIT_EXP); // Search for, and store, the unit exponent value for the input data
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN + SENSOR_DATA_EXP_OFFSET] = vn_hid_i2c_report_buf_val($buf, $count, "bin");
break;
}
}
}
else $count++; // If desired identifiers haven't been reached, continue traversing HID Report Descriptor
}
$reach_flag = FALSE; // Reset the flag to 0 to show that we are done with descriptor data from this report ID
$hid_device_sensor_num++; // Increment to the next sensor in the struct array of sensors
}
else $count++;
}
}
function hid_i2c_get_index($sensor_type)
{
global $app_sensor_info;
$count = count($app_sensor_info);
$indx = 0;
while($indx < $count)
{
if($app_sensor_info[$indx] !== "")
{
$type = bin2int($app_sensor_info[$indx], SENSOR_TYPE_OFFSET, 1);
if($type == $sensor_type)
return $indx;
}
$indx++;
}
exit("hid: the sensor type $sensor_type not added, please add it before use\r\n");
}
function vn_hid_i2c_get_report_id($sensor_type)
{
global $app_sensor_info;
$indx = hid_i2c_get_index($sensor_type);
$id = bin2int($app_sensor_info[$indx], SENSOR_ID_OFFSET, 1);
return $id;
}
/*
After application get all infomation about sensor, delete this table to save memory
*/
function hid_i2c_del_report_desc()
{
global $hid_report_desc_table, $hid_device_sensor_num;
$hid_report_desc_table = "";
$hid_device_sensor_num = 0;
}
function hid_i2c_set_report($type, $report_buf, $size)
{
global $hid_descriptor_table;
$wbuf = "";
$vendor_cmd = false;
if (bin2int($report_buf, 0, 1) == FLASH_UPDATE_RPT_ID) // the flash update (Vendor) commands have a different format
$vendor_cmd = true;
$wbuf .= substr($hid_descriptor_table, HID_CMD_REG_OFFSET, 2); //command field bytes from HID config table
$wbuf .= "\x00";
if ($vendor_cmd)
$wbuf[2] = int2bin($type | bin2int($report_buf, 0, 1), 1);// HID Set command opcode low byte which includes the sensor's ReportID, high byte report type
else
$wbuf[2] = int2bin($type | bin2int($report_buf, 2, 1), 1);
$wbuf .= int2bin(SET_OPCODE, 1);//$wbuf[3] // HID SetReport command opcode high byte
$wbuf .= substr($hid_descriptor_table, HID_DATA_REG_OFFSET, 2); //$wbuf[4] and $wbuf[5] //data field bytes from HID config table
$index = 0;
if ($vendor_cmd)
$index++;//skips 1st byte of input buffer
//else $size++;
$wbuf .= substr($report_buf, $index, $size);// Append input to the command
$rlen = strlen($wbuf);
i2c_write(0, $wbuf);
}
/*
Request and Read input report from HID device
Parameters:
-$report_type: Report Type: 1-input, 3 feature
*/
function hid_i2c_get_report(&$report_buf, $report_type, $sensor_type)
{
global $hid_descriptor_table;
$wbuf = "";
$report_id = vn_hid_i2c_get_report_id($sensor_type);
$wbuf .= substr($hid_descriptor_table, HID_CMD_REG_OFFSET, 2);//command field bytes from HID config table
$wbuf .= int2bin(($report_type | $report_id) , 1); //$wbuf[2] // HID Get command opcode low byte which includes the sensor's ReportID, high byte report type
$wbuf .= int2bin(GET_RPT_CMD_MSB, 1); // HID GetReport command opcode high byte
$wbuf .= substr($hid_descriptor_table, HID_DATA_REG_OFFSET, 2); //data field bytes from HID config table
vn_hid_i2c_write_read(0, $wbuf, $report_buf);
}
/*
Note: if $cmd are CMD_POWER_ON, CMD_RESET_DEV, CMD_SLEEP, $sensor_type is any value.
*/
function hid_i2c_cmd_process(&$rbuf, $cmd, $sensor_type = 0)
{
global $hid_descriptor_table;
$cmd_buf = "";
switch($cmd)
{
case CMD_POWER_ON:
$cmd_buf .= substr($hid_descriptor_table, HID_CMD_REG_OFFSET, 2);
$cmd_buf .= "\x00\x08"; // HID Power on command opcode
i2c_write(0, $cmd_buf, 4);
break;
case CMD_RESET_DEV:
$cmd_buf .= substr($hid_descriptor_table, HID_CMD_REG_OFFSET, 2);
$cmd_buf .= "\x00\x01"; // HID Reset command opcode
i2c_write(0, $cmd_buf, 4);
$time_expire = vn_hid_i2c_get_tick() + 5000; //5 sec (as per HID spec) timeout for reset command
$data_avail = false;
while($time_expire > vn_hid_i2c_get_tick())
{
if(uio_in(0, INT_PIN) == INT_ASSERTED)
{
$data_avail = true;
}
}
if($data_avail)
{
$rbuf = "";
i2c_read(0, $rbuf, 2);
if(bin2int($rbuf, 0, 2) == 0)
echo "reset successfully\r\n";
else
echo "reset fail\r\n";
}
else
echo "reset fail\r\n";
break;
case CMD_SLEEP:
$cmd_buf .= substr($hid_descriptor_table, HID_CMD_REG_OFFSET, 2);
$cmd_buf .= "\x01\x08"; // HID sleep command opcode
i2c_write(0, $cmd_buf, 4);
break;
case CMD_HID_GET_RPT_INPT:
hid_i2c_get_report($rbuf, GET_RRT_INPT, $sensor_type);
break;
case CMD_HID_GET_RPT_FEAT:
hid_i2c_get_report($rbuf, GET_RPT_FEAT, $sensor_type);
break;
case CMD_HID_SET_RPT_FEAT:
//for non-Vendor commands, the sensor id is NOT passed in byte[0]
$max_size = bin2int($rbuf, RPT_SIZE_LSB, 1);//get size of Report Feature Packet
hid_i2c_set_report(SET_RPT_FEAT, $rbuf, $max_size);
break;
default:
break;
}
return true;
}
/*
Retrieve the HID descriptor table
*/
function hid_i2c_desc_handler()
{
global $hid_descriptor_table;
$cmd = int2bin(0x01, 2); // HID descriptor table request is 0x0001
i2c_write_read(0, $cmd, $hid_descriptor_table, 30); // HID descriptor length is 30
}
/*
Retrieve and parse report descriptor table
*/
function hid_i2c_rept_desc_handler()
{
global $hid_descriptor_table;
$cmd = substr($hid_descriptor_table, HID_REPORT_DESC_REG_OFFSET, 2); // Report table request is 0x0200
$rbuf_1 = "";
$rbuf_2 = "";
$rbuf_3 = "";
$rlen = bin2int($hid_descriptor_table, HID_REPORT_DESC_LEN_OFFSET, 2);
$pid = pid_open_nodie("/mmap/i2c0", "i2c_write_read");
if(pid_ioctl($pid, "get txfree") >= 2)
pid_write($pid, $cmd, 2);
else
exit("i2c_write_read: tx buffer full\r\n");
pid_ioctl($pid, "req write wait");
while(pid_ioctl($pid, "get state") && pid_ioctl($pid, "get txlen"))
;
if(pid_ioctl($pid, "get error"))
{
pid_ioctl($pid, "req stop");
pid_close($pid);
return 0;
}
else
{
pid_ioctl($pid, "req read $rlen");
while(pid_ioctl($pid, "get state"))
;
$rlen = 0;
$rlen += pid_read($pid, $rbuf_1);
$rlen += pid_read($pid, $rbuf_2);
$rlen += pid_read($pid, $rbuf_3);
pid_close($pid);
}
// parse report descriptor table
$rept_desc_buf = array($rbuf_1, $rbuf_2, $rbuf_3);
vn_hid_i2c_parse_report_dest($rept_desc_buf);
}
function hid_i2c_get_exponent($sensor_type)
{
global $app_sensor_info;
$indx = hid_i2c_get_index($sensor_type);
$data_expo = bin2int($app_sensor_info[$indx], SENSOR_DATA_EXP_OFFSET, 1);
if ($data_expo >= 0 && $data_expo <= 7) // These values are all positive exponents
$mult = pow(10, (float)$data_expo);
if($data_expo >= 8 && $data_expo <= 0x0F) // These values are all negative exponents (ie. to right of decimal place)
$mult = pow(10, (float)($data_expo - 16));
return $mult;
}
function hid_i2c_add_sensor($sensor_type)
{
global $app_sensor_info;
global $hid_report_desc_table, $hid_device_sensor_num;
$buf = "";
$max_sensor_num = count($app_sensor_info);
for($indx = 0; $indx < $max_sensor_num; $indx++)
{
if($app_sensor_info[$indx] === "")
{
for($count = 0; $count < $hid_device_sensor_num; $count++)
{
$offset = $count * SENSOR_INFO_LEN;
$type = bin2int($hid_report_desc_table, $offset + SENSOR_TYPE_OFFSET, 1);
if($type == $sensor_type)
{
$buf = substr($hid_report_desc_table, $offset, SENSOR_INFO_LEN);
$app_sensor_info[$indx++] = $buf;
return true;
}
}
exit("hid: sensor type $sensor_type not support by device\r\n");
}
}
echo "hid: excceed the allowed number of sensors by the api\r\n";
return false;
}
/*
Return: sensor type of report
*/
function vn_hid_i2c_update_data(&$report)
{
global $app_sensor_info, $app_data;
$sensor_id = bin2int($report, 2, 1);
$max_sensor_num = count($app_sensor_info);
for($indx = 0; $indx < $max_sensor_num; $indx++)
{
if($app_sensor_info !== "")
{
$id = bin2int($app_sensor_info[$indx], SENSOR_ID_OFFSET, 1);
if($sensor_id == $id)
{
$offset = bin2int($app_sensor_info[$indx], SENSOR_DATA_OFFSET, 1);
$app_data[$indx] = substr($report ,$offset);
$sensor_type = bin2int($app_sensor_info[$indx], SENSOR_TYPE_OFFSET, 1);
return $sensor_type;
}
}
}
return -1;
}
function vn_hid_i2c_get_data(&$data, $sensor_type)
{
global $app_sensor_info, $app_data;
$indx = hid_i2c_get_index($sensor_type);
$data = $app_data[$indx];
return true;
}
/*
This function is used when app want to request and read data from sensor.
*/
function hid_i2c_request_data(&$data_buf, $sensor_type)
{
$rbuf = "";
hid_i2c_get_report($rbuf, GET_RRT_INPT, $sensor_type);
vn_hid_i2c_update_data($rbuf);
vn_hid_i2c_get_data($data_buf, $sensor_type);
}
/*
This function is used to read data when an interrupt is asserted.
Return:
*/
function hid_i2c_read_data(&$data_buf)
{
$rbuf = "";
i2c_read(0, $rbuf, 13);
$sensor_type = vn_hid_i2c_update_data($rbuf);
vn_hid_i2c_get_data($data_buf, $sensor_type);
return $sensor_type;
}
?>
MM7150 Library
This library is to interact with MM7150 module.
Vd_MM7150.PHP
PHP Code:
<?php
define("MM7150_ADDR", 0x40);
define("STATE_WAKEUP_MASK", 1);
define("STATE_SLEEP_MASK", 1 << 1);
define("STATE_START_MASK", 1 << 2);
define("STATE_RESET_MASK", 1 << 3);
define("STATE_ACCEN_MASK", 1 << 4); //Accel enable
define("STATE_GYREN_MASK", 1 << 5);
define("STATE_CMPEN_MASK", 1 << 6);
define("STATE_ORIEN_MASK", 1 << 7);
define("STATE_INCEN_MASK", 1 << 8);
define("STATE_BAREN_MASK", 1 << 9);
define("STATE_ALSEN_MASK", 1 << 10);
define("STATE_RAW_ACCEN_MASK", 1 << 11);
define("STATE_RAW_MAG_MASK", 1 << 12);
define("STATE_RAW_GYR_MASK", 1 << 13);
define("MAX_RETRIES", 3 );
define("FULL_POWER", 0x02);
define("LOW_POWER", 0x03);
define("ON", TRUE);
define("OFF", FALSE);
$mm7150_power_state = OFF;
$mm7150_sensor_state = array(OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF);
function vd_mm7150_get_tick()
{
while(($pid = pid_open("/mmap/st8", O_NODIE)) == -EBUSY)
usleep(500);
if(!pid_ioctl($pid, "get state"))
{
pid_ioctl($pid, "set div us");
pid_ioctl($pid, "start");
}
$tick = pid_ioctl($pid, "get count");
pid_close($pid);
return $tick;
}
/** mm7150_set_state_data
* @note Set and confirm new features for a device
* @param GET_SET_PARAMS structure containing power state (=2: FULL, =3: LOW),sensor ID, New data rate,New sensitivity
* @return error status 0=SUCCESS, failcodes: 0x17=SET_FEAT_FAIL
*/
function mm7150_set_state_data($sensor_type, $power_state, $sensitivity = 0, $sensitivity_valid = false, $data_rate = 0, $data_rate_valid = false)
{
$get_feat_buf = str_repeat("\x00", 40);// GetFeature report buffer
$set_feat_buf = str_repeat("\x00", 40);// SetFeature report buffer
hid_i2c_cmd_process($get_feat_buf, CMD_HID_GET_RPT_FEAT, $sensor_type); // Issue a get report feature command and store the get features in $get_feat_buf
$set_feat_buf = $get_feat_buf; // Copy GetFeatBuff to bufs to use for set feature command
// Modify fields that we would like to set in bufs (sensor state, power state, data rate, sensitivity)
$set_feat_buf[RPT_REPORT_STATE] = int2bin(HID_USG_SENS_PROPERTY_REPORTING_STATE_ALL_EVENTS_ENUM, 1);
$set_feat_buf[RPT_PWR_STATE] = int2bin($power_state, 1); //update the Power state parameter
if ($data_rate_valid) // update data rate value?
{
$set_feat_buf[RPT_REPORT_INTVAL_LSB] = int2bin($data_rate, 2); //update 16 bit data rate value
}
if ($sensitivity_valid) // update sensitivity value?
{
$set_feat_buf[RPT_CHG_SENS_LSB] = int2bin($sensitivity, 2); //update 16 bit sensitivity value
}
for ($retry_count = 0; $retry_count < MAX_RETRIES; $retry_count++) // API spec requires 3 attempts at setting new features.
{
hid_i2c_cmd_process($set_feat_buf, CMD_HID_SET_RPT_FEAT, $sensor_type); // Send HID_SetFeature command to SSC7150 to update new features to the device
hid_i2c_cmd_process($get_feat_buf, CMD_HID_GET_RPT_FEAT, $sensor_type); // Send HID_GetFeature command to SSC7150 to check if new feature request to device were updated
$buf_size = bin2int($get_feat_buf, RPT_SIZE_LSB, 1); // size (in bytes) of packet is in 1st byte
for ($pointer = 0; $pointer < $buf_size; $pointer++) // Check to see if the features have been updated
{
if ( $get_feat_buf[$pointer] != $set_feat_buf[$pointer] )
break; //no they weren't, try again
}
if ( $pointer == $buf_size ) //that's the whole packet, ALL data matches
break; //we're done
}
if ($retry_count == MAX_RETRIES)
return;
}
function mm7150_enable_sensor($sensor_type)
{
global $mm7150_power_state;
global $mm7150_sensor_state;
global $app_sensor_info;
//WAKE COMMAND IF MM7150 IS SLEEPPING
if (!$mm7150_power_state)
{
wakeup_signal();//assert wake signal (1 ms toggle of RE9 signal to SSC150)
usleep(15000);//wait 12 ms (11 ms min per spec) after wake signal and before sending POWER_ON command to SSC7150
$abv_buf = "";
if (hid_i2c_cmd_process($abv_buf, CMD_POWER_ON)) // Issue the wake command (parameters 1 and 3 are not used)
{
$mm7150_power_state = ON;
//spec says must wait a minimum of 30 ms before next command to SSC7150
usleep(35000);
}
}
//ENABLE SENSOR COMMAND
mm7150_set_state_data($sensor_type, FULL_POWER);
$mm7150_sensor_state[hid_i2c_get_index($sensor_type)] = ON;
return true;
}
/*
$sensor_list: Array of sensors contains list of sensor type
*/
function mm7150_init($sensor_list)
{
global $mm7150_power_state;
global $app_sensor_info;
$rbuf = "";
if(uio_in(0, INT_PIN) == INT_ASSERTED)
vn_hid_i2c_read(0, $rbuf);
// Retrieve the HID descriptor table
hid_i2c_desc_handler();
// Wake the EC
hid_i2c_cmd_process($rbuf, CMD_POWER_ON);
//Reset the EC
hid_i2c_cmd_process($rbuf, CMD_RESET_DEV);
//Retrieve and parse report descriptor table
hid_i2c_rept_desc_handler();
$sensor_num = count($sensor_list);
for($i = 0; $i < $sensor_num; $i++)
{
hid_i2c_add_sensor($sensor_list[$i]);
}
// HID device is now awake and ready for operation
$mm7150_power_state = ON;
}
?>
Application
This code shows how to read value from all sensor of MM7150 module
Task0.php
PHP Code:
<?php
if(_SERVER("REQUEST_METHOD"))
exit; // avoid php execution via http request
include_once "/lib/sd_340.php";
include_once "/lib/vd_MM7150_pin_map.php";
include_once "/lib/vn_hid_i2c.php";
include_once "/lib/vd_MM7150.php";
function unsign2sign($val)
{
if($val&0x8000)
$val = ($val&0x7fff) - 0x8000;
return $val;
}
// Init user interface
i2c_setup(0, MM7150_ADDR);
uio_setup(0, INT_PIN, "in");
uio_setup(1, WAKEUP_PIN, "out");
uio_out(1, WAKEUP_PIN, HIGH);
//List of sensor in use
$sensor_list = array(
ACCEL_SENSOR_TYPE,
GYRO_SENSOR_TYPE,
CMP_SENSOR_TYPE,
ORI_SENSOR_TYPE,
INCL_SENSOR_TYPE);
// Init MM7150. Retrieve HID & report descriptors, and all device features
mm7150_init($sensor_list);
$sensor_type = ACCEL_SENSOR_TYPE;
//$sensor_type = GYRO_SENSOR_TYPE;
//$sensor_type = CMP_SENSOR_TYPE;
//$sensor_type = ORI_SENSOR_TYPE;
//$sensor_type = INCL_SENSOR_TYPE;
mm7150_enable_sensor($sensor_type);
$mult = hid_i2c_get_exponent($sensor_type);
$data = "";
while(1)
{
/*
//Polling Driven
hid_i2c_request_data($data, $sensor_type);
$x_val = unsign2sign(bin2int($data, 0, 2)) * $mult;
$y_val = unsign2sign(bin2int($data, 2, 2)) * $mult;
$z_val = unsign2sign(bin2int($data, 4, 2)) * $mult;
echo "x_val: ", $x_val , "\r\n";
echo "y_val: ", $y_val , "\r\n";
echo "z_val: ", $z_val , "\r\n";
if($sensor_type == ORI_SENSOR_TYPE)
{
$w_val = unsign2sign(bin2int($data, 6, 2)) * $mult;
echo "w_val: ", $w_val , "\r\n";
}
*/
// Event Driven
if(uio_in(0, INT_PIN) == INT_ASSERTED)
{
$data = "";
$in_sens_type = hid_i2c_read_data($data);
if($in_sens_type == $sensor_type)
{
$x_val = unsign2sign(bin2int($data, 0, 2)) * $mult;
$y_val = unsign2sign(bin2int($data, 2, 2)) * $mult;
$z_val = unsign2sign(bin2int($data, 4, 2)) * $mult;
echo "x_val: ", $x_val , "\r\n";
echo "y_val: ", $y_val , "\r\n";
echo "z_val: ", $z_val , "\r\n";
if($sensor_type == ORI_SENSOR_TYPE)
{
$w_val = unsign2sign(bin2int($data, 6, 2)) * $mult;
echo "w_val: ", $w_val , "\r\n";
}
}
}
}
?>
You can get full source code here: MM7150_full_source_code.zip