Exemplo n.º 1
0
/**
 * @brief Calibrate proximity sensor detection thresholds
 *
 * This function measures the proximity sensor output in 3 different steps,
 * one for each channel in the device.  This function should be called when
 * a sample object has been placed at the desired distance from the device
 * to define the threshold for near-proximity detection.  The measured
 * proximity sensor value for each channel is stored in non-volatile memory.
 * These thresholds will later be used to set the threshold during the
 * device initialization sequence.
 *
 * This routine must be called 3 times total, with the "step" parameter
 * indicating what stage of the calibration is being performed (i.e. which
 * channel of the proximity sensor).  This multi-step mechanism allows
 * the application to prompt for physical placement of the object to be
 * detected before this routine is called.
 *
 * @param sensor     Address of an initialized sensor device descriptor.
 * @param calib_type The address of a vector storing sensor axis data.
 * @param step       The calibration stage number [1,3].
 * @param info       Unimplemented (ignored) parameter.
 * @return bool     true if the call succeeds, else false is returned.
 */
static bool sfh7770_calibrate(sensor_t *sensor,
		sensor_calibration_t calib_type, int step, void *info)
{
	sensor_hal_t *const hal = sensor->hal;

	static uint8_t prox_data[3];
	uint8_t read_data[3];

	/* Validate the specified calibration type */
	if (calib_type != MANUAL_CALIBRATE) {
		sensor->err = SENSOR_ERR_PARAMS;
		return false;
	}

	/* Read proximity sensor for individual channel based on step number. */
	switch (step) {
	case 1:
		prox_data[0] = sensor_bus_get(hal, SFH7770_PS_DATA_LED1);
		break;

	case 2:
		prox_data[1] = sensor_bus_get(hal, SFH7770_PS_DATA_LED2);
		break;

	case 3:
		prox_data[2] = sensor_bus_get(hal, SFH7770_PS_DATA_LED3);

		/* Write data */
		nvram_write((SFH7770_NVRAM_OFFSET), prox_data,
				sizeof(prox_data));

		/* Read back data and confirm it was written correctly */
		nvram_read(SFH7770_NVRAM_OFFSET, read_data, sizeof(read_data));

		if (memcmp(prox_data, read_data, sizeof(prox_data))) {
			sensor->err = SENSOR_ERR_IO;
			return false;
		}

		/* Apply stored proximity thresholds from nvram */
		sensor_bus_write(hal, (SFH7770_PS_THR_LED1), read_data,
				sizeof(read_data));

		break;

	/* Any other step number is invalid */
	default:
		sensor->err = SENSOR_ERR_PARAMS;
		return false;
	}

	return true;
}
Exemplo n.º 2
0
/**
 * @brief Perform self-test function
 *
 * This function sets up and executes an built-in self test function
 * within the HMC5883L device.
 *
 * @param sensor    Address of an initialized sensor descriptor.
 * @param test_code Address of a numeric code of which test to perform
 *                  This location will contain the specific test result code
 *                  when the function returns.
 * @return bool     true if the call succeeds, else false is returned.
 */
bool hmc5883l_selftest(sensor_t *sensor, int *test_code, void *arg)
{
	int count;
	uint8_t meas_mode;          /* test measurement mode */
	vector3_t data;
	bool status = true;
	sensor_hal_t *const hal = sensor->hal;

	struct {
		uint8_t config_reg_a;
		uint8_t config_reg_b;
		uint8_t mode_reg;
	}
	reg_set;

	/* Initialize result code */
	*test_code = SENSOR_TEST_ERR_NONE;

	/* Save register values */
	count = sensor_bus_read(hal, HMC5883L_CONFIG_REG_A, (uint8_t *)&reg_set,
			sizeof(reg_set));

	if (count != sizeof(reg_set)) {
		*test_code = SENSOR_TEST_ERR_READ;
		return false;
	}

	/* Set range (sensitivity) */
	sensor_bus_put(hal, HMC5883L_CONFIG_REG_B, HMC5883L_TEST_GAIN);

	/* Set test mode */
	switch (*test_code) {          /* which test was specified */
	case SENSOR_TEST_DEFAULT:
	case SENSOR_TEST_BIAS_POS:
		meas_mode = MEAS_MODE_POS;  /* positive bias measurement mode */
		break;

	case SENSOR_TEST_BIAS_NEG:
		meas_mode = MEAS_MODE_NEG;  /* negative bias measurement mode */
		break;

	default:
		/* bad test code specified */
		sensor_bus_put(hal, HMC5883L_CONFIG_REG_B,
				reg_set.config_reg_b);
		/* restore reg */
		*test_code = SENSOR_TEST_ERR_FUNCTION;
		return false;
	}

	sensor_bus_put(hal, HMC5883L_CONFIG_REG_A,
			(DATA_RATE_15HZ | MEAS_AVG_1 | meas_mode));

	delay_ms(SELF_TEST_DELAY_MSEC);

	/* Perform test measurement & check results */
	sensor_bus_put(hal, HMC5883L_MODE_REG, MODE_SINGLE); /* single meas mode
	                                                      **/

	if (hmc5883l_get_data(hal, &data) != true) {
		*test_code = SENSOR_TEST_ERR_READ;
		status = false;    /* failed to read data registers */
	} else {
		if (arg != NULL) {
			((sensor_data_t *)arg)->scaled = false; /* only raw values */
			((sensor_data_t *)arg)->timestamp = 0;  /* no timestamp */

			((sensor_data_t *)arg)->axis.x = (int32_t)data.x; /* copy values */
			((sensor_data_t *)arg)->axis.y = (int32_t)data.y;
			((sensor_data_t *)arg)->axis.z = (int32_t)data.z;
		}

		/* Check range of readings */
		if ((HMC5883L_TEST_X_MIN > data.x) ||
				(data.x > HMC5883L_TEST_X_MAX) ||
				(HMC5883L_TEST_Y_MIN > data.y) ||
				(data.y > HMC5883L_TEST_Y_MAX) ||
				(HMC5883L_TEST_Z_MIN > data.z) ||
				(data.z > HMC5883L_TEST_Z_MAX)) {
			*test_code = SENSOR_TEST_ERR_RANGE;
			status = false; /* value out of range */
		}
	}

	/* Restore registers */
	count = sensor_bus_write(hal, HMC5883L_CONFIG_REG_A,
			(uint8_t *)&reg_set, sizeof(reg_set));

	if (count != sizeof(reg_set)) {
		*test_code = SENSOR_TEST_ERR_WRITE;
		status = false;
	}

	return status;
}
Exemplo n.º 3
0
/**
 * @brief InvenSense IMU-3000 gyroscope driver defaults.
 *
 * This is a convenience routine for setting the default device configuration
 * during initialization or after reset.
 *
 * @return bool     true if the sensor is ready for use, else false.
 */
static bool imu3000_default_init(sensor_hal_t *hal, sensor_hal_t *aux)
{
	bool status = false;

	/* Assuming the device is on an I2C bus, the device address and ID
	 * are one and the same.  Per the vendor data sheet, AD0 in the
	 * WHO_AM_I (device ID) register is software configurable s.t. two
	 * devices can be installed on the sensor bus.  The undefined AD0
	 * bit is set to zero to select the 110 1000 (AD0=0) address.
	 *
	 * Set the bus address here then verify the setting.
	 */
	sensor_bus_put(hal, IMU3000_WHO_AM_I, IMU3000_ID_VAL);

	if (IMU3000_ID_VAL == sensor_bus_get(hal, IMU3000_WHO_AM_I)) {
		/* Reset the motion processing unit to a known state then enable
		 * the gyroscope using the Z-axis for clock.
		 */
		sensor_bus_put(hal, IMU3000_PWR_MGM, PWR_MGM_H_RESET);
		sensor_bus_put(hal, IMU3000_WHO_AM_I, IMU3000_ID_VAL);
		sensor_bus_put(hal, IMU3000_PWR_MGM, PWR_MGM_CLK_SEL_Z);

		/* \todo Define a method to support I2C master mode.
		 *
		 * Determine whether it is reasonable to use the
		 * "sensor_desc.aux" field to support an external 3-axis accelerometer
		 * configured as an auxiliary device. For example, assume a user
		 * selects this as a build-time configuration option and the "aux"
		 * field points to a sensor_hal_t type describing the accelerometer.
		 */
		if (aux) {
			/* Configure and enable the IMU-3000 auxiliary device
			 * interface, including bus and burst read addresses.
			 */
			sensor_bus_put(hal, IMU3000_AUX_SLV_ADDR,
					AUX_ADDR_CLKOUTEN | aux->bus.addr);

			sensor_bus_put(hal, IMU3000_AUX_BURST_ADDR,
					aux->burst_addr);

			/* Enable the IMU as master to accelerometer (aux)
			 * interface via secondary I2C bus. For an external
			 * processor to communicate directly to an external accelerometer
			 * the AUX_IF_EN bit should be cleared and AUX_IF_RST bit
			 * should be set. The configuration below turns the accelerometer
			 * into a slave device that's not addressed by a separate
			 * sensor_t descriptor and API routines.
			 */
			sensor_bus_put(hal, IMU3000_USER_CTRL,
					USER_CTRL_AUX_IF_RST |
					USER_CTRL_AUX_IF_EN);
		} else {
			/* Reset the auxiliary interface in pass-through mode
			 * s.t. the external processor can communicate directly with
			 * an external accelerometer.
			 */
			sensor_bus_put(hal, IMU3000_USER_CTRL,
					USER_CTRL_AUX_IF_RST);
		}

		/* \todo Define a method to dynamically compute DC bias.
		 *
		 * The gyro signed offset registers remove DC bias from the
		 * sensor outputs by subtracting the offset values from gyro sensor
		 * values prior to moving sensor data to output registers. The
		 * device is initialized s.t. DC bias offsets are not applied to
		 * sensor data.
		 */
		const uint16_t *const gyro_offsets = (uint16_t [3]) {0};

		sensor_bus_write(hal, IMU3000_X_OFFS_USRH,
				gyro_offsets, 3 * sizeof(uint16_t));

		/* Set the sample rate divider, digital low-pass filter, and
		 * full-scale range.
		 *
		 * The digital low-pass filter also determines the internal
		 * sample rate used by the device.  The 256 Hz. and 2.1 kHz
		 * filter bandwidths result in an 8 kHz. internal sample rate, while
		 * all other settings result in a 1 kHz. internal sample rate.
		 *
		 * This internal sample rate is then further scaled by the
		 * sample rate divider (SMPLRT_DIV) value to produce the gyro
		 * sample rate according to the formula:
		 *
		 *          F_sample = F_internal / (divider + 1)
		 */
		uint8_t const dlpf_fs
			= range_table[sensor_fs_sel].reserved_val |
				band_table[sensor_dlpf_cfg].reserved_val;

		sensor_bus_put(hal, IMU3000_SMPLRT_DIV, 9);
		sensor_bus_put(hal, IMU3000_DLPF_FS, dlpf_fs);
	}

	if (STATUS_OK == hal->bus.status) {
		status = true;
	}

	return status;
}
Exemplo n.º 4
0
/**
 * @brief Set event threshold value
 *
 * @param hal       Address of an initialized sensor hardware descriptor.
 * @param channel   Channel (LED) number to set threshold
 * @param threshold Address of threshold descriptor.
 * @return bool     true if the call succeeds, else false is returned.
 */
static bool sfh7770_set_threshold(sensor_hal_t *hal, int16_t channel,
		sensor_threshold_desc_t *threshold)
{
	struct {
		uint8_t lsb;
		uint8_t msb;
	}
	reg_thresh;

	uint8_t led_count = 0;
	uint8_t index;
	uint8_t value = threshold->value;

	bool result = false;

	switch (threshold->type) {  /* check threshold type */
	case SENSOR_THRESHOLD_NEAR_PROXIMITY:   /* high (near) prox. threshold
		                                 **/

		/* Write to sensor register based on current channel (LED
		 * selection)
		 */
		switch (channel) {
		case SENSOR_CHANNEL_ALL:    /* "channel -1" = all 3 LEDs */
			led_count = 3;
			index = 0;
			break;

		case 1:
		case 2:
		case 3:
			led_count = 1;
			index = channel - 1;
			break;

		default:
			return false;   /* invalid channel selection */
		}

		while (led_count--) {
			sensor_bus_put(hal, (SFH7770_PS_THR_LED1 + index),
					(uint8_t)threshold->value);

			/* Write to nvram */
			nvram_write((SFH7770_NVRAM_OFFSET + index), &value, 1);
			++index;
		}

		result = true;
		break;

	case SENSOR_THRESHOLD_LOW_LIGHT:         /* lower light level threshold
		                                  **/
		reg_thresh.lsb = (uint8_t)(threshold->value & 0xFF);
		reg_thresh.msb = (uint8_t)((threshold->value >> 8) & 0xFF);
		low_light_threshold = (uint16_t)threshold->value;
		if (sensor_bus_write(hal, SFH7770_ALS_LO_THR_LSB, &reg_thresh,
				sizeof(reg_thresh)) == sizeof(reg_thresh)) {
			result = true;
		}

		break;

	case SENSOR_THRESHOLD_HIGH_LIGHT:        /* upper light level threshold
		                                  **/
		reg_thresh.lsb = (uint8_t)(threshold->value & 0xFF);
		reg_thresh.msb = (uint8_t)((threshold->value >> 8) & 0xFF);
		high_light_threshold = (uint16_t)threshold->value;
		if (sensor_bus_write(hal, SFH7770_ALS_UP_THR_LSB, &reg_thresh,
				sizeof(reg_thresh)) == sizeof(reg_thresh)) {
			result = true;
		}

		break;

	default:
		/* Invalid/unsupported threshold type - do nothing, return false */
		break;
	}

	return result;
}
Exemplo n.º 5
0
/**
 * @brief Osram SFH7770 light & proximity sensor driver initialization.
 *
 * This is the main initialization function for the SFH7770 device.
 *
 * @param sensor    Address of a sensor device descriptor.
 * @param resvd     Reserved value.
 * @return bool     true if the call succeeds, else false is returned.
 */
bool sfh7770_init(sensor_t *sensor, int resvd)
{
	bool status = false;
	sensor_hal_t *const hal = sensor->hal;

	/* Proximity threshold values from NVRAM */
	struct {
		uint8_t ps_thr_led1;
		uint8_t ps_thr_led2;
		uint8_t ps_thr_led3;
	} prox_thresholds;

	/* Read and check part ID register */
	uint8_t part_id = sensor_bus_get(hal, SFH7770_PART_ID);

	if (part_id == (SFH7770_PART_ID_VAL | SFH7770_PART_REV_VAL)) {
		/* Set the driver function table and capabilities pointer. */
		static const sensor_device_t sfh7770_device = {
			.func.read           = sfh7770_read,
			.func.ioctl          = sfh7770_ioctl,
			.func.calibrate      = sfh7770_calibrate,
			.func.event          = sfh7770_event,
#if 0
			.caps.feature        = XXX
#endif
			.caps.vendor         = SENSOR_VENDOR_OSRAM,
			.caps.units          = SENSOR_UNITS_lux,
			.caps.scale          = SENSOR_SCALE_one,
			.caps.name = "SFH7770 Ambient Light & Proximity Sensor"
		};

		sensor->drv = &sfh7770_device;

		hal->resolution = SFH7770_DATA_RESOLUTION;

		/* Set the device burst read starting register address. */
		hal->burst_addr = SFH7770_ALS_DATA_LSB;

		/* Reset device during first init call */
		if (!sfh7770_initialized) {
			sensor_bus_put(hal, SFH7770_ALS_CONTROL,
					ALS_CONTROL_SW_RESET);
		}

		/* Init light sensor functions if specified */

		if (sensor->type & SENSOR_TYPE_LIGHT) {
			/* Set light sensor mode & interval */

			sensor_bus_put(hal, SFH7770_ALS_CONTROL,
					ALS_MODE_FREE_RUNNING);
			sensor_bus_put(hal, SFH7770_ALS_INTERVAL,
					ALS_INTERVAL_500MS);
		}

		/* Init proximity sensor functions if specified */
		if (sensor->type & SENSOR_TYPE_PROXIMITY) {
			/* Set proximity sensor mode & interval */
			sensor_bus_put(hal, SFH7770_PS_CONTROL,
					PS_MODE_FREE_RUNNING);
			sensor_bus_put(hal, SFH7770_PS_INTERVAL,
					PS_INTERVAL_100MS);

			/* Specify which LEDs are active */
			uint8_t const active_leds = LED_ACTIVE_ALL;
			/* XXX one of: */
			/* XXX  LED_ACTIVE_1    - LED1 only (default) */
			/* XXX  LED_ACTIVE_1_2  - LED1 & LED2 */
			/* XXX  LED_ACTIVE_1_3  - LED1 & LED3 */
			/* XXX  LED_ACTIVE_ALL  - all LEDs */

			/* Set LED current levels */
			uint8_t const led1_curr = I_LED_50MA; /* LED1 current */
			uint8_t const led2_curr = I_LED_50MA; /* LED2 current */
			uint8_t const led3_curr = I_LED_50MA; /* LED3 current */

			sensor_bus_put(hal, SFH7770_I_LED_1_2,
					(active_leds |
					(led2_curr <<
					I_LED2_SHIFT) | led1_curr));

			sensor_bus_put(hal, SFH7770_I_LED_3, led3_curr);

			/* Apply stored proximity thresholds from nvram */
			nvram_read(SFH7770_NVRAM_OFFSET, &prox_thresholds,
					sizeof(prox_thresholds));

			sensor_bus_write(hal, (SFH7770_PS_THR_LED1),
					&prox_thresholds,
					sizeof(prox_thresholds));
		}

		if (!sfh7770_initialized) {
			/* Set interrupt output polarity & mode(active-low,
			 * latched).
			 */
			sensor_bus_put(hal, SFH7770_INT_SET, 0);

			/* Set up interrupt handler */
			if (STATUS_OK == hal->bus.status) {
				sensor_irq_connect(hal->mcu_sigint, sfh7770_isr, hal);
			}
		}

		sfh7770_initialized = true;
		status = true;
	}

	return status;
}

/**
 * @brief Osram SFH7770 driver interrupt service routine.
 *
 * This is the common interrupt service routine for all enabled SFH7770
 * interrupt events.  Three different types of interrupts can be programmed:
 * high light level, low light level, and near proximity.  All share the
 * same interrupt pin and therefore the same ISR entry.
 *
 * @param arg       The address of the driver sensor_hal_t descriptor.
 * @return Nothing.
 */
static void sfh7770_isr(volatile void *arg)
{
	sensor_hal_t *const hal = (sensor_hal_t *)arg;

	struct {                    /* Interrupt register data */
		uint8_t als_data_lsb;   /* light meas data - least signif byte */
		uint8_t als_data_msb;   /* light meas data - most signif byte */
		uint8_t als_ps_status;  /* light & prox sensor status */
		uint8_t ps_data_led1;   /* proximity meas data - LED 1 */
		uint8_t ps_data_led2;   /* proximity meas data - LED 2 */
		uint8_t ps_data_led3;   /* proximity meas data - LED 3 */
		uint8_t int_set;        /* interrupt status */
	}
	regs;

	/* Do not wait for a busy bus when reading data. */
	hal->bus.no_wait = true;
	sensor_bus_read(hal, hal->burst_addr, (uint8_t *)&regs, sizeof(regs));
	hal->bus.no_wait = false;

	if (STATUS_OK == hal->bus.status) {
		static sensor_event_data_t event_data = {.data.scaled = true};

		event_data.data.timestamp = sensor_timestamp();
		event_data.event = SENSOR_EVENT_UNKNOWN;

		/*
		 * Determine the interrupt source then combine measurement
		 * register values into a single 16-bit measurement value.
		 */
		uint8_t const int_source = (regs.int_set & INT_SOURCE_MASK);

		uint16_t const light_level
			= ((regs.als_data_msb << 8) | regs.als_data_lsb);

		switch (int_source) {
		case INT_SOURCE_ALS:

			/* Determine if low or high light interrupt */
			if (light_level >= high_light_threshold) {
				event_data.event = SENSOR_EVENT_HIGH_LIGHT;
				event_data.data.light.value = light_level;

				(event_cb[2].handler)(&event_data,
						event_cb[2].arg);
			} else if (light_level <= low_light_threshold) {
				event_data.event = SENSOR_EVENT_LOW_LIGHT;
				event_data.data.light.value = light_level;

				(event_cb[1].handler)(&event_data,
						event_cb[1].arg);
			}

			return;

		case INT_SOURCE_LED1:
		case INT_SOURCE_LED2:
		case INT_SOURCE_LED3:

			event_data.event = SENSOR_EVENT_NEAR_PROXIMITY;

			if (int_source == INT_SOURCE_LED1) {
				event_data.channel = 1;
			} else if (int_source == INT_SOURCE_LED2) {
				event_data.channel = 2;
			} else { /* INT_SOURCE_LED3 */
				event_data.channel = 3;
			}

			/* Use internal device threshold status to
			 * determine scaled values.
			 */
			event_data.data.proximity.value[0]
				= (regs.als_ps_status & PS_LED1_THRESH) ?
					PROXIMITY_NEAR : PROXIMITY_NONE;

			event_data.data.proximity.value[1]
				= (regs.als_ps_status & PS_LED2_THRESH) ?
					PROXIMITY_NEAR : PROXIMITY_NONE;

			event_data.data.proximity.value[2]
				= (regs.als_ps_status & PS_LED3_THRESH) ?
					PROXIMITY_NEAR : PROXIMITY_NONE;

			(event_cb[0].handler)(&event_data, event_cb[0].arg);
		}
	}
}

/**
 * @brief Read SFH7770 device ID and revision numbers.
 *
 * This function reads the sensor hardware identification registers
 * and returns these values in the specified data structure.
 *
 * @param hal       Address of an initialized sensor hardware descriptor.
 * @param data      Address of sensor_data_t structure to return values.
 * @return bool     true if the call succeeds, else false is returned.
 */
static bool sfh7770_device_id(sensor_hal_t *hal, sensor_data_t *data)
{
	uint8_t const part_id = sensor_bus_get(hal, SFH7770_PART_ID);

	data->device.id = (uint32_t)(part_id & PART_ID_MASK) >> PART_ID_SHIFT;
	data->device.version = (uint32_t)(part_id & PART_REV_MASK);

	return true;
}