int get_soc (void)
{
	float upper_voltage = 14.7f;
	float duty_cycle;
	
	if (cc_state == FULLY_CHARGED)
		return 100;
	else if (cc_state == PULSED_CURRENT)
		return 90;
	else if (cc_state == VOLTAGE_SETTLE)
		return 80;
	else
	{
		batt_voltage = get_adc_voltage(ADC_BATT_V);
		set_temperature_compensation( get_adc_voltage(ADC_TEMP), &upper_voltage, &duty_cycle );
		set_current_compensation(BATTERY_AHR*0.1f, batt_current, &upper_voltage );
		//Linear appoximation
		return (int)( 80.0f*(batt_voltage - v_lvdc) / (upper_voltage - v_lvdc) );
	}

}
Exemple #2
0
int
HMC5883::collect()
{
#pragma pack(push, 1)
	struct { /* status register and data as read back from the device */
		uint8_t		x[2];
		uint8_t		z[2];
		uint8_t		y[2];
	}	hmc_report;
#pragma pack(pop)
	struct {
		int16_t		x, y, z;
	} report;

	int	ret;
	uint8_t check_counter;

	perf_begin(_sample_perf);
	struct mag_report new_report;
	bool sensor_is_onboard = false;

	float xraw_f;
	float yraw_f;
	float zraw_f;

	/* this should be fairly close to the end of the measurement, so the best approximation of the time */
	new_report.timestamp = hrt_absolute_time();
        new_report.error_count = perf_event_count(_comms_errors);

	/*
	 * @note  We could read the status register here, which could tell us that
	 *        we were too early and that the output registers are still being
	 *        written.  In the common case that would just slow us down, and
	 *        we're better off just never being early.
	 */

	/* get measurements from the device */
	ret = _interface->read(ADDR_DATA_OUT_X_MSB, (uint8_t *)&hmc_report, sizeof(hmc_report));

	if (ret != OK) {
		perf_count(_comms_errors);
		debug("data/status read error");
		goto out;
	}

	/* swap the data we just received */
	report.x = (((int16_t)hmc_report.x[0]) << 8) + hmc_report.x[1];
	report.y = (((int16_t)hmc_report.y[0]) << 8) + hmc_report.y[1];
	report.z = (((int16_t)hmc_report.z[0]) << 8) + hmc_report.z[1];

	/*
	 * If any of the values are -4096, there was an internal math error in the sensor.
	 * Generalise this to a simple range check that will also catch some bit errors.
	 */
	if ((abs(report.x) > 2048) ||
	    (abs(report.y) > 2048) ||
	    (abs(report.z) > 2048)) {
		perf_count(_comms_errors);
		goto out;
	}

	/* get measurements from the device */
	new_report.temperature = 0;
	if (_conf_reg & HMC5983_TEMP_SENSOR_ENABLE) {
		/*
		  if temperature compensation is enabled read the
		  temperature too.

		  We read the temperature every 10 samples to avoid
		  excessive I2C traffic
		 */
		if (_temperature_counter++ == 10) {
			uint8_t raw_temperature[2];

			_temperature_counter = 0;

			ret = _interface->read(ADDR_TEMP_OUT_MSB, 
					       raw_temperature, sizeof(raw_temperature));
			if (ret == OK) {
				int16_t temp16 = (((int16_t)raw_temperature[0]) << 8) + 
					raw_temperature[1];
				new_report.temperature = 25 + (temp16 / (16*8.0f));
				_temperature_error_count = 0;
			} else {
				_temperature_error_count++;
				if (_temperature_error_count == 10) {
					/*
					  it probably really is an old HMC5883,
					  and can't do temperature. Disable it
					*/
					_temperature_error_count = 0;
					debug("disabling temperature compensation");
					set_temperature_compensation(0);
				}
			}
		} else {
			new_report.temperature = _last_report.temperature;
		}
	}

	/*
	 * RAW outputs
	 *
	 * to align the sensor axes with the board, x and y need to be flipped
	 * and y needs to be negated
	 */
	new_report.x_raw = report.y;
	new_report.y_raw = -report.x;
	/* z remains z */
	new_report.z_raw = report.z;

	/* scale values for output */

	// XXX revisit for SPI part, might require a bus type IOCTL
	unsigned dummy;
	sensor_is_onboard = !_interface->ioctl(MAGIOCGEXTERNAL, dummy);
	if (sensor_is_onboard) {
		// convert onboard so it matches offboard for the
		// scaling below
		report.y = -report.y;
		report.x = -report.x;
        }

        /* the standard external mag by 3DR has x pointing to the
	 * right, y pointing backwards, and z down, therefore switch x
	 * and y and invert y */
        xraw_f = -report.y;
	yraw_f = report.x;
	zraw_f = report.z;

	// apply user specified rotation
	rotate_3f(_rotation, xraw_f, yraw_f, zraw_f);

	new_report.x = ((xraw_f * _range_scale) - _scale.x_offset) * _scale.x_scale;
	/* flip axes and negate value for y */
	new_report.y = ((yraw_f * _range_scale) - _scale.y_offset) * _scale.y_scale;
	/* z remains z */
	new_report.z = ((zraw_f * _range_scale) - _scale.z_offset) * _scale.z_scale;

	if (!(_pub_blocked)) {

		if (_mag_topic != -1) {
			/* publish it */
			orb_publish(ORB_ID(sensor_mag), _mag_topic, &new_report);
		} else {
			_mag_topic = orb_advertise_multi(ORB_ID(sensor_mag), &new_report,
				&_orb_class_instance, (sensor_is_onboard) ? ORB_PRIO_HIGH : ORB_PRIO_MAX);

			if (_mag_topic < 0)
				debug("ADVERT FAIL");
		}
	}

	_last_report = new_report;

	/* post a report to the ring */
	if (_reports->force(&new_report)) {
		perf_count(_buffer_overflows);
	}

	/* notify anyone waiting for data */
	poll_notify(POLLIN);

	/*
	  periodically check the range register and configuration
	  registers. With a bad I2C cable it is possible for the
	  registers to become corrupt, leading to bad readings. It
	  doesn't happen often, but given the poor cables some
	  vehicles have it is worth checking for.
	 */
	check_counter = perf_event_count(_sample_perf) % 256;
	if (check_counter == 0) {
		check_range();
	}
	if (check_counter == 128) {
		check_conf();
	}

	ret = OK;

out:
	perf_end(_sample_perf);
	return ret;
}
Exemple #3
0
int
HMC5883::ioctl(struct file *filp, int cmd, unsigned long arg)
{
	unsigned dummy = arg;

	switch (cmd) {
	case SENSORIOCSPOLLRATE: {
		switch (arg) {

			/* switching to manual polling */
		case SENSOR_POLLRATE_MANUAL:
			stop();
			_measure_ticks = 0;
			return OK;

			/* external signalling (DRDY) not supported */
		case SENSOR_POLLRATE_EXTERNAL:

			/* zero would be bad */
		case 0:
			return -EINVAL;

			/* set default/max polling rate */
		case SENSOR_POLLRATE_MAX:
		case SENSOR_POLLRATE_DEFAULT: {
				/* do we need to start internal polling? */
				bool want_start = (_measure_ticks == 0);

				/* set interval for next measurement to minimum legal value */
				_measure_ticks = USEC2TICK(HMC5883_CONVERSION_INTERVAL);

				/* if we need to start the poll state machine, do it */
				if (want_start)
					start();

				return OK;
			}

			/* adjust to a legal polling interval in Hz */
		default: {
				/* do we need to start internal polling? */
				bool want_start = (_measure_ticks == 0);

				/* convert hz to tick interval via microseconds */
				unsigned ticks = USEC2TICK(1000000 / arg);

				/* check against maximum rate */
				if (ticks < USEC2TICK(HMC5883_CONVERSION_INTERVAL))
					return -EINVAL;

				/* update interval for next measurement */
				_measure_ticks = ticks;

				/* if we need to start the poll state machine, do it */
				if (want_start)
					start();

				return OK;
			}
		}
	}

	case SENSORIOCGPOLLRATE:
		if (_measure_ticks == 0)
			return SENSOR_POLLRATE_MANUAL;

		return 1000000/TICK2USEC(_measure_ticks);

	case SENSORIOCSQUEUEDEPTH: {
			/* lower bound is mandatory, upper bound is a sanity check */
			if ((arg < 1) || (arg > 100))
				return -EINVAL;

			irqstate_t flags = irqsave();
			if (!_reports->resize(arg)) {
				irqrestore(flags);
				return -ENOMEM;
			}
			irqrestore(flags);

			return OK;
		}

	case SENSORIOCGQUEUEDEPTH:
		return _reports->size();

	case SENSORIOCRESET:
		return reset();

	case MAGIOCSSAMPLERATE:
		/* same as pollrate because device is in single measurement mode*/
		return ioctl(filp, SENSORIOCSPOLLRATE, arg);

	case MAGIOCGSAMPLERATE:
		/* same as pollrate because device is in single measurement mode*/
		return 1000000/TICK2USEC(_measure_ticks);

	case MAGIOCSRANGE:
		return set_range(arg);

	case MAGIOCGRANGE:
		return _range_ga;

	case MAGIOCSLOWPASS:
	case MAGIOCGLOWPASS:
		/* not supported, no internal filtering */
		return -EINVAL;

	case MAGIOCSSCALE:
		/* set new scale factors */
		memcpy(&_scale, (mag_scale *)arg, sizeof(_scale));
		/* check calibration, but not actually return an error */
		(void)check_calibration();
		return 0;

	case MAGIOCGSCALE:
		/* copy out scale factors */
		memcpy((mag_scale *)arg, &_scale, sizeof(_scale));
		return 0;

	case MAGIOCCALIBRATE:
		return calibrate(filp, arg);

	case MAGIOCEXSTRAP:
		return set_excitement(arg);

	case MAGIOCSELFTEST:
		return check_calibration();

	case MAGIOCGEXTERNAL:
		debug("MAGIOCGEXTERNAL in main driver");
		return _interface->ioctl(cmd, dummy);

	case MAGIOCSTEMPCOMP:
		return set_temperature_compensation(arg);

	case DEVIOCGDEVICEID:
		return _interface->ioctl(cmd, dummy);

	default:
		/* give it to the superclass */
		return CDev::ioctl(filp, cmd, arg);
	}
}
__task void interrupted_charging (void)
{
	float sol_voltage = 0.0f, sol_current = 0.0f, sol_power = 0.0f;
	int pulse = 0;
	int counter = 0;
	
	//TODO: initialise hardware
	init_pwm(40000);
	init_adc();
	
	set_mppt();
		
	while (1)
	{
		batt_voltage = get_adc_voltage(ADC_BATT_V);
		batt_current = get_adc_voltage(ADC_BATT_I);
		sol_voltage = get_adc_voltage(ADC_SOL_V);
		sol_current = get_adc_voltage(ADC_SOL_I);
		sol_power = sol_voltage * sol_current;
		temp = get_adc_voltage(ADC_TEMP);
		
		set_temperature_compensation( temp, &v_high, &pulse_duty );
		
		TRACE_INFO("1,%.0f,%i,%.2f,%.2f,%.2f,%.3f,%.1f,%.2F\n",
					((double)get_time_t()), cc_state, batt_voltage, batt_current, sol_voltage, sol_current, duty_cycle, temp);
		
		//Check for LVDC voltage
		calc_lvdc(batt_current);
		if ( batt_voltage < v_lvdc )
		{
			//Turn off outputs and screen.
			//send os event to turn off
			os_evt_set( UI_LVDC, ui_t );
		}	
		
		switch (cc_state)
		{
			case BULK_CHARGING:
				//Start charging battery with 0.1C current or as high as possible if 0.1C cannot be met
				perturb_and_observe_cc_itter(BATTERY_AHR*0.1f);
				
				if (++counter > 10)
					set_current_compensation(BATTERY_AHR*0.1f, batt_current, &v_high);
			
				if (sol_power < P_NIGHT_MODE)
				{
					if (set_mppt() < (P_NIGHT_MODE*1.2))
					{
						cc_state = NIGHT_MODE;
						counter = 0;
						TRACE_DEBUG("Starting Night Mode State\n");
						break;
					}
					TRACE_DEBUG("Rescaned Power and Night mode not entered \n");
				}
		
						
				if (batt_voltage > v_high)
				{
					cc_state = VOLTAGE_SETTLE;
					counter = 0;
					TRACE_DEBUG("Starting Voltage Settle Charging State at V=%f \n", batt_voltage);
					break;
				}
				
				os_dly_wait( P_AND_O_PERIOD );

				break;
			
			case VOLTAGE_SETTLE:
				//Diable the MPPT Charging Circuit
				GPIO_ResetBits(GPIOB, GPIO_Pin_0);
			
				if (batt_voltage < v_low)
				{
					GPIO_SetBits(GPIOB, GPIO_Pin_0);
					cc_state = PULSED_CURRENT;
					TRACE_DEBUG("Starting Pulsed Current Charging State at V=%f \n", batt_voltage);
					break;
				}				
				//5s delay
				os_dly_wait(50);
				break;
				
			case PULSED_CURRENT:
				counter++;

				if (pulse)
				{
					//If greater than high period seconds, pulse is low. i.e sets up x% duty cycle
					if ( counter > (300 * pulse_duty) )
						pulse = 0;
					
					//Enable MPPT hardware
					GPIO_SetBits(GPIOB, GPIO_Pin_0);
					
					//Run p&o itteration
					perturb_and_observe_cc_itter(BATTERY_AHR*0.05f);
					
					
					if ( counter > 10 )
						{
						//set_current_compensation(BATTERY_AHR*0.05f, batt_current);
							
						if (sol_power < P_NIGHT_MODE)
						{
							if (set_mppt() < (P_NIGHT_MODE*1.2))
							{
								cc_state = NIGHT_MODE;
								counter = 0;
								TRACE_DEBUG("Starting Night Mode State\n");
								break;
							}
							TRACE_DEBUG("Rescaned Power and Night mode not entered \n");
						}
					}
				}
				else
				{
					//If greater than 30s, reset.
					if ( counter > 300)
					{
						counter = 0;
						pulse = 1;
					}
					//Diable the MPPT Charging Circuit
					GPIO_ResetBits(GPIOB, GPIO_Pin_0);
				}
				
								
				if (batt_voltage > v_high)
				{
					counter = 0;
					cc_state = FULLY_CHARGED;
					TRACE_DEBUG("Starting Fully Charged Charging State at V=%f \n", batt_voltage);
					break;
				}
				
				//100ms delay
				os_dly_wait(10);				
				break;
			case FULLY_CHARGED:
				//Regulate so that Battery current = 0
					//This means that the battery is no longer charged
					//But the panel is used to power any connected load
			
				//Diable the MPPT Charging Circuit
				GPIO_ResetBits(GPIOB, GPIO_Pin_0);				
			
				//Check when to start recharging again.
				if (batt_voltage < v_restart)
				{
					cc_state = BULK_CHARGING;
					TRACE_DEBUG("Restarting the Bulk Charging State \n");
					break;
				}
			
				//5s delay
				os_dly_wait(500);
				break;
			
			case NIGHT_MODE:
				//Check every 5 minutes
				//Counter used so that LVDC is still
				//checked every 5 seconds
				if (++counter > 2)
				{
					//Enable MPPT Circuit
					GPIO_SetBits(GPIOB, GPIO_Pin_0);
				
					if (set_mppt() > (P_NIGHT_MODE*1.2)) 
					{
						cc_state = BULK_CHARGING;
						TRACE_DEBUG("Exiting Night Mode\n");
						break;
					}
					
					//Diable the MPPT Charging Circuit
					//GPIO_ResetBits(GPIOB, GPIO_Pin_0);
					set_duty_cycle(100);
					
					counter = 0;
				}
				
				//5 second wait time
				os_dly_wait(500);			
				
				break;
			default:
				cc_state = BULK_CHARGING;
				TRACE_ERROR("Charging State machine entered Unknown State. Restarting with Bulk Charging \n");
		}
	}
}