Fix for hack that worked around iOS app

Haptics.cpp

Committer:
roysandberg
Date:
2016-03-03
Revision:
3:b64f3abb3c0b
Parent:
2:4f76784f8968
Child:
4:d3858592d3b5

File content as of revision 3:b64f3abb3c0b:

/******************************************************************************
 * Haptics.c
 *
 * Created on: Dec 16, 2011
 * Board: DRV2603EVM-CT RevD
 * Author: a0866685
 *
 * Description: This file contains the functions for sending haptics waveforms.
 * 		Edit this file when changing actuators.
 *
 * @TODO - Update this file when changing between actuators types
 * 			(ERM, LRA, Piezo)
 *
 ******************************************************************************/
 
#include "Actuator_Waveforms.h"

// private variables
//static uint8_t  j,k;

extern float lraEffectMultiplier; // defined in main.cpp

// public variables
uint16_t Haptics_dumbModeTick = DUMBTICK;		// Sets the LRA Auto-resonance off frequency (use DUMBTICK above to set frequency)
uint16_t Haptics_resonantModeTick;

DigitalOut LRA_EN(P0_12);
PwmOut LRA(P0_11);

extern DigitalOut led1;


// Used for off-mode haptics and LED signature
Timeout firstLEDOff;
Timeout secondBuzz;
Timeout secondLEDOff;
	
Ticker hapticsTicker;
volatile uint16_t lraPlaybackMode = 0;
volatile int lraCurrent = 0;
volatile int lraTarget = 0;
volatile uint16_t lraDelay = 0;
volatile uint16_t placeInWaveform = 0;
volatile uint16_t waveformDelayCounter = 0;
const Waveform* volatile waveform;
//const Waveform* volatile heartbeatWaveform = &lra_tick;
const Waveform* volatile heartbeatWaveform = &lra_rampdown;




// TODO: Support haptics modalities here: off, waveform, delayed waveform, ramp to target

// Called every 5ms to update LRA amplitude based on desired haptics response
void hapticsCallback() {
	switch (lraPlaybackMode) {
		case LRA_MODE_WAVEFORM:
			// play the current amplitude
			//led1 = 1;

			if(waveform->data[placeInWaveform] == 0x80) {
				LRA.pulsewidth_us(0);
				LRA_EN = 0;
			} else {
				LRA_EN = 1;
				float scaledWaveform = ((float)waveform->data[placeInWaveform]) * 0.375 * lraEffectMultiplier;  // 0.375 is 96/256
				LRA.pulsewidth_us((uint16_t)scaledWaveform); 
				//LRA.pulsewidth_us(((((uint16_t)waveform->data[placeInWaveform])*3) >> 3));  // mult by 3 shift right by 3 to get in range of 0..96
				//LRA.pulsewidth_us(90);   // used for fixed intensity output (for testing)				
			}	

			// run down the timer on this amplitude
			waveformDelayCounter++;
			if (waveformDelayCounter >= waveform->data[placeInWaveform+1]) {
				// move to the next waveform entry and reset the timer when necessary
				waveformDelayCounter=0;		
				placeInWaveform += 2;
				if (placeInWaveform >= waveform->length) {
					//led1 = 0;
					LRA.pulsewidth_us(0);
					LRA_EN = 0;
					placeInWaveform = 0;
					waveformDelayCounter = 0;
					lraPlaybackMode = LRA_MODE_OFF;
				}
			}	
			break;
		case LRA_MODE_DELAYED_WAVEFORM:
			waveformDelayCounter+=5; // every tick is 5ms
			if (waveformDelayCounter >= lraDelay) {
				waveformDelayCounter = 0;
				lraPlaybackMode = LRA_MODE_WAVEFORM;
			}
			break;
		case LRA_MODE_RAMP_TO_TARGET:
			// full ramp from 384 to 768 (threshold to full) takes 384*5 = 1920ms with step size = 1
			if (lraTarget > lraCurrent) {
				lraCurrent += 2;
			} else {
				lraCurrent -= 2;
			}
			LRA_EN = 1;
			//led1 = 1;
			LRA.pulsewidth_us((lraCurrent) >> 3);   //divide by 8 to get in range of 0..95
			break;
		case LRA_MODE_OFF:
		default: 
			// Do nothing
			LRA.pulsewidth_us(0);
			LRA_EN = 0;
			placeInWaveform = 0;
			waveformDelayCounter = 0;
			break;
	}
}
	
/**
 * Haptics_Init - initialize haptics variables and settings
 */
void Haptics_Init(void)
{
	// mbed uses 4uS timer precision, so keep this as large as possible for better granularity
	// 96 uS period means 96 is the maximum pulse width. So we need to scale from 256 to 96, or mult by 3, and shift right by 3.
	LRA.period_us(96); // 96 uS -> 10.4 Khz (needs to be between 10Khz and 250Khz)

	hapticsTicker.attach(&hapticsCallback, 0.005); // update haptics every 5 ms	    
	
	Haptics_resonantModeTick = (unsigned int) Haptics_dumbModeTick * 2;
}


/*
 * Haptics_OutputWaveform - control the PWM output pattern
 * @param struct Waveform - the waveform output type, length in bytes, and data
 */
void Haptics_OutputWaveform(const Waveform* newWaveform)
{

	if (lraPlaybackMode == LRA_MODE_OFF || lraPlaybackMode == LRA_MODE_RAMP_TO_TARGET) {
		waveform = newWaveform;
		lraPlaybackMode = LRA_MODE_WAVEFORM;
	} else {
		//printf("OutputWaveform: mode=%d, delay=%d, place=%d\n\r", lraPlaybackMode, waveformDelayCounter, placeInWaveform);
	}
}

// Override any currently active haptic effect
void Haptics_OverrideOutputWaveform(const Waveform* newWaveform)
{
	//led1 = !led1;
	placeInWaveform = 0;
	waveform = newWaveform;
	LRA_EN = 1;
	LRA.pulsewidth_us((((int)waveform->data[placeInWaveform])*3) >> 3);  // mult by 3 shift right by 2 to get in range of 0..95
	lraPlaybackMode = LRA_MODE_WAVEFORM;
}

void Haptics_OutputDelayedHeartbeat(uint16_t delay) {
	Haptics_OutputDelayedWaveform(heartbeatWaveform, delay); 
}

void Haptics_SetHeartbeatWaveform (int waveformNum) {
	printf("SetHeartbeatWaveform to %d.\n\r", waveformNum);
	
	switch (waveformNum) {
		case 1:
			heartbeatWaveform = &lra_click_nobrake;
			break;
		case 2:
			heartbeatWaveform = &lra_alert;
			break;
		case 3:
			heartbeatWaveform = &lra_rampup;
			break;
		case 4:
			heartbeatWaveform = &lra_rampdown;
			break;
		case 5:
			heartbeatWaveform = &lra_softclick;
			break;
		case 6:
			heartbeatWaveform = &lra_softbump;
			break;						
		case 7:
			heartbeatWaveform = &lra_softalert;
			break;
		case 8:
			heartbeatWaveform = &lra_rampupdoubleclick;
			break;			
		case 0:
		default:
			heartbeatWaveform = &lra_tick;
	}
}
	
void Haptics_OutputDelayedWaveform(const Waveform* newWaveform, uint16_t delay) {

	if (lraPlaybackMode == LRA_MODE_OFF || lraPlaybackMode == LRA_MODE_RAMP_TO_TARGET) {
		placeInWaveform = 0;
		waveformDelayCounter = 0;
		lraDelay = delay;
		waveform = newWaveform;
		lraPlaybackMode = LRA_MODE_DELAYED_WAVEFORM;
	} else {
		//printf("Haptics: mode=%d, delay=%d, until=%d, place=%d\n\r", lraPlaybackMode, waveformDelayCounter, lraDelay, placeInWaveform);
	}
}


void LEDOffCallback() {
   led1=0;
}

void BuzzCallback() {
    Haptics_OutputWaveform(&lra_alert);
    led1 = 1;
}

// generate off mode haptic response
// This is 2 buzzes separated by 300 ms, and 2 led blinks with the same timing.
void LeavingOffModeHapticResponse(int ledFinalState) {
	
	// Turn on LED and generate first buzz
    BuzzCallback();
    
	// Setup callback to turn off LED after 50ms
	firstLEDOff.attach(&LEDOffCallback,0.05);
	
	// Setup callback to generate second buzz after 300ms, and turn on LED second time
	secondBuzz.attach(&BuzzCallback,0.3);
	
	// Setup callback to turn off LED after 350mS
	if (ledFinalState == 0) {
        secondLEDOff.attach(&LEDOffCallback,0.35);
    }
}

void EnteringOffModeHapticResponse() {
	Haptics_OutputWaveform(&lra_rampdown);
	led1 = 0;
}
		
// Ignored while waveform is playing or queued. Overridden by waveforms.
void Haptics_SetRampTarget(float amplitude)
{
	if (lraPlaybackMode == LRA_MODE_OFF || lraPlaybackMode == LRA_MODE_RAMP_TO_TARGET) {
		if (lraPlaybackMode == LRA_MODE_OFF) {
			lraCurrent = 384; // reset to threshold off if this is the first ramp target
		}
		if (amplitude > 1.0) amplitude = 1.0;
		if (amplitude < 0.0) amplitude = 0.0;
		lraTarget = (int) (amplitude*768); // (96 * 8)
		lraPlaybackMode = LRA_MODE_RAMP_TO_TARGET;
		//printf("Ramp current=%d, target=%d\n\r", lraCurrent>>3, lraTarget>>3);
	}
}

// Buzz this many times
void Haptics_OutputCount (uint16_t count) {
	switch (count) {
		case 1:
			Haptics_OutputWaveform(&lra_click_nobrake);
			break;
		case 2:
			Haptics_OutputWaveform(&lra_doubleclick_nobrake);
			break;
		case 3:
			Haptics_OutputWaveform(&lra_tripleclick_nobrake);
			break;		
	}
}

// Stop haptics regardless of mode
void Haptics_Off()
{
	lraPlaybackMode = LRA_MODE_OFF;
}

bool isHapticsOff()
{
	return (lraPlaybackMode == LRA_MODE_OFF);
}