view main.c @ 121:71430b08fe81

Force curve gauge is feature complete!! - Fixed no argument default (would skip the null in some cli commands) - Added free running force/distance measure - Most of the help information - Zeroing force and distance - Start/End marker setting
author Jacob Alexander <haata@kiibohd.com>
date Tue, 04 Feb 2014 00:27:33 -0800
parents b90e5316ffe7
children 2bb16439e6ca
line wrap: on
line source

/* Copyright (C) 2011-2014 by Jacob Alexander
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

// ----- Includes -----

// Compiler Includes
#include <Lib/MainLib.h>

// Project Includes
#include <macro.h>
#include <scan_loop.h>
#include <output_com.h>

#include <cli.h>
#include <led.h>
#include <print.h>



// ----- Defines -----

// Verified Keypress Defines
#define USB_TRANSFER_DIVIDER 10 // 1024 == 1 Send of keypresses per second, 1 == 1 Send of keypresses per ~1 millisecond



// ----- Macros -----
#if defined(_at90usb162_) || defined(_atmega32u4_) || defined(_at90usb646_) || defined(_at90usb1286_)
#define CPU_PRESCALE(n)	(CLKPR = 0x80, CLKPR = (n))
#endif



// ----- Function Declarations -----

void cliFunc_distRead    ( char* args );
void cliFunc_free        ( char* args );
void cliFunc_gaugeHelp   ( char* args );
void cliFunc_single      ( char* args );
void cliFunc_start       ( char* args );
void cliFunc_stop        ( char* args );
void cliFunc_zeroForce   ( char* args );
void cliFunc_zeroPosition( char* args );

char receiveUART0Char();

void transmitUART0String( char* str );

uint32_t readDistanceGauge();



// ----- Variables -----

// Timer Interrupt for flagging a send of the sampled key detection data to the USB host
uint16_t sendKeypressCounter = 0;

// Flag generated by the timer interrupt
volatile uint8_t sendKeypresses = 0;



// ----- Functions -----

// Initial Pin Setup, make sure they are sane
inline void pinSetup(void)
{

// AVR
#if defined(_at90usb162_) || defined(_atmega32u4_) || defined(_at90usb646_) || defined(_at90usb1286_)

	// For each pin, 0=input, 1=output
#if defined(__AVR_AT90USB1286__)
	DDRA = 0x00;
#endif
	DDRB = 0x00;
	DDRC = 0x00;
	DDRD = 0x00;
	DDRE = 0x00;
	DDRF = 0x00;


	// Setting pins to either high or pull-up resistor
#if defined(__AVR_AT90USB1286__)
	PORTA = 0x00;
#endif
	PORTB = 0x00;
	PORTC = 0x00;
	PORTD = 0x00;
	PORTE = 0x00;
	PORTF = 0x00;

// ARM
#elif defined(_mk20dx128_)
	// TODO - Should be cleared, but not that necessary due to the pin layout
#endif
}


inline void usbTimerSetup(void)
{
// AVR
#if defined(_at90usb162_) || defined(_atmega32u4_) || defined(_at90usb646_) || defined(_at90usb1286_)

	// Setup with 16 MHz clock
	CPU_PRESCALE( 0 );

	// Setup ISR Timer for flagging a kepress send to USB
	// Set to 256 * 1024 (8 bit timer with Clock/1024 prescalar) timer
	TCCR0A = 0x00;
	TCCR0B = 0x03;
	TIMSK0 = (1 << TOIE0);

// ARM
#elif defined(_mk20dx128_)
	// 48 MHz clock by default

	// System Clock Gating Register Disable
	SIM_SCGC6 |= SIM_SCGC6_PIT;

	// Enable Timers
	PIT_MCR = 0x00;

	// Setup ISR Timer for flagging a kepress send to USB
	// 1 ms / (1 / 48 MHz) - 1 = 47999 cycles -> 0xBB7F
	PIT_LDVAL0 = 0x0000BB7F;
	PIT_TCTRL0 = 0x3; // Enable Timer 0 interrupts, and Enable Timer 0

	// Insert the required vector for Timer 0
	NVIC_ENABLE_IRQ( IRQ_PIT_CH0 );
#endif
}


int main(void)
{
	// Configuring Pins
	pinSetup();
	init_errorLED();

	// Setup Output Module
	output_setup();

	// Enable CLI
	init_cli();

	// Setup ISR Timer for flagging a kepress send to USB
	usbTimerSetup();

	// Main Detection Loop
	uint8_t ledTimer = F_CPU / 1000000; // Enable LED for a short time
	while ( 1 )
	{
		// Setup the scanning module
		scan_setup();

		while ( 1 )
		{
			// Acquire Key Indices
			// Loop continuously until scan_loop returns 0
			cli();
			while ( scan_loop() );
			sei();

			// Run Macros over Key Indices and convert to USB Keys
			process_macros();

			// Send keypresses over USB if the ISR has signalled that it's time
			if ( !sendKeypresses )
				continue;

			// Send USB Data
			usb_send();

			// Clear sendKeypresses Flag
			sendKeypresses = 0;

			// Indicate Error, if valid
			errorLED( ledTimer );

			if ( ledTimer > 0 )
				ledTimer--;
		}

		// Loop should never get here (indicate error)
		ledTimer = 255;

		// HID Debug Error message
		erro_print("Detection loop error, this is very bad...bug report!");
	}
}


// ----- Interrupts -----

// USB Keyboard Data Send Counter Interrupt
#if defined(_at90usb162_) || defined(_atmega32u4_) || defined(_at90usb646_) || defined(_at90usb1286_) // AVR
ISR( TIMER0_OVF_vect )
#elif defined(_mk20dx128_) // ARM
void pit0_isr(void)
#endif
{
	sendKeypressCounter++;
	if ( sendKeypressCounter > USB_TRANSFER_DIVIDER ) {
		sendKeypressCounter = 0;
		sendKeypresses = 1;
	}

#if defined(_mk20dx128_) // ARM
	// Clear the interrupt flag
	PIT_TFLG0 = 1;
#endif
}


// ----- CLI Command Functions -----

uint32_t readDistanceGauge()
{
	// Setup distance read parameters for iGaging Distance Scale
	//       freq = 9kHz
	// duty_cycle = 20%
	// high_delay = (1/freq) *       (duty_cycle/100)
	//  low_delay = (1/freq) * ((100-duty_cycle)/100)
	uint8_t  bits       = 21; // 21 clock pulses, for 21 bits
	uint32_t high_delay = 22; // Clock high time per pulse
	uint32_t  low_delay = 89; // Clock low  time per pulse

	// Data
	uint32_t distInput = 0;

	// Make sure clock is low initially
	GPIOC_PCOR |= (1<<2); // Set Clock low

	// Scan each of the bits
	for ( uint8_t bit = 0; bit < bits; bit++ )
	{
		// Begin clock pulse
		GPIOC_PSOR |= (1<<2); // Set Clock high

		// Delay for duty cycle
		delayMicroseconds( high_delay );

		// End clock pulse
		GPIOC_PCOR |= (1<<2); // Set Clock low

		// Read Data Bit
		distInput |= GPIOC_PDIR & (1<<1) ? (1 << bit) : 0;

		// Delay for duty cycle
		delayMicroseconds( low_delay );
	}

	return distInput;
}

void cliFunc_distRead( char* args )
{
	// Parse number from argument
	//  NOTE: Only first argument is used
	char* arg1Ptr;
	char* arg2Ptr;
	argumentIsolation_cli( args, &arg1Ptr, &arg2Ptr );

	// Convert the argument into an int
	int read_count = decToInt( arg1Ptr ) + 1;

	// If no argument specified, default to 1 read
	if ( *arg1Ptr == '\0' )
	{
		read_count = 2;
	}

	// Repeat reading as many times as specified in the argument
	print( NL );
	while ( --read_count > 0 )
	{
		// Prepare to print output
		info_msg("Distance: ");

		// Data
		uint32_t distInput = readDistanceGauge() - distanceOffset;

		// Output result
		printInt32( distInput );

		// Convert to mm
		// As per http://www.shumatech.com/web/21bit_protocol?page=0,1
		// 21 bits is 2560 CPI (counts per inch) (C/inch)
		// 1 inch is 25.4 mm
		// 2560 / 25.4 = 100.7874016... CPMM (C/mm)
		// Or
		// 1 count is 1/2560 = 0.000390625... inches
		// 1 count is (1/2560) * 25.4 = 0.00992187500000000 mm = 9.92187500000000 um = 9921.87500000000 nm
		// Since there are 21 bits (2 097 152 positions) converting to um is possible by multiplying by 1000
		//    which is 2 097 152 000, and within 32 bits (4 294 967 295).
		// However, um is still not convenient, so 64 bits (18 446 744 073 709 551 615) is a more accurate alternative.
		// For each nm there are 2 097 152 000 000 positions.
		// And for shits:
		//    mm is 2 097 152                 :          0.009 921 875 000 mm : 32 bit
		//    um is 2 097 152 000             :          9.921 875 000     um : 32 bit (ideal acc. for 32 bit)
		//    nm is 2 097 152 000 000         :      9 921.875 000         nm : 64 bit
		//    pm is 2 097 152 000 000 000     :  9 921 875.000             pm : 64 bit (ideal acc. for 64 bit)

		// XXX Apparently shumatech was sorta wrong about the 21 bits of usage
		// Yes there are 21 bits, but the values only go from ~338 to ~30681 which is less than 16 bits...
		// This means that the conversion at NM can use 32 bits :D
		// It's been noted that the multiplier should be 100.6 (and that it could vary from scale to scale)
		uint32_t distNM = distInput * 9921;;
		uint32_t distUM = distNM / 1000;
		uint32_t distMM = distUM / 1000;

		print("  ");
		printInt32( distMM );
		print(" mm  ");
		printInt32( distUM );
		print(" um  ");
		printInt32( distNM );
		print(" nm  ");

		print( NL );

		// Only delay if still counting
		if ( read_count > 1 )
			delay( 50 );
	}
}


void cliFunc_free( char* args )
{
	// Set the forceDistanceRead to 1, which will read until start has passed twice
	forceDistanceRead = 1;
}


void cliFunc_gaugeHelp( char* args )
{
	print( NL
"\033[1;32mForce Curve Gauge Help\033[0m" NL
" \033[1;33mUsage Overview\033[0m" NL
"  TODO" NL
" \033[1;33mAdditional Command Details\033[0m" NL
"  \033[1;35mdistRead\033[0m" NL
"     Reads the current value from the distance gauge." NL
"     If specified it will N repeated reads with a delay after each read. Useful for testing the distance gauge." NL
"       e.g. \033[35mdistRead 250\033[0m" NL
"  \033[1;35mfree\033[0m" NL
"     Start free scanning force/distance reads." NL
"     Will continue until the [start] distance point has been past twice." NL
"  \033[1;35mimadaComm\033[0m" NL
"     Sends a command to the Imada force gauge." NL
"       e.g. \033[35mimadaComm D\033[0m" NL
"     The commands supported by the gauge depends on the model. Listed below is for the DS2." NL
"       K  Select g  units (default)" NL
"       N  Select N  units" NL
"       O  Select oz units" NL
"       P  Select peak mode" NL
"       T  Select real time mode (default)" NL
"       Z  Zero out display/reading" NL
"       Q  Turn off power" NL
"       E  Read high/low set points" NL
"       D  Read data from force gauge" NL
"       E\033[35mHHHHLLLL\033[0m" NL
"          Set the high/low setpoints, ignore decimals" NL
"          \033[35mHHHH\033[0m is 4 digit high, \033[35mLLLL\033[0m is 4 digit low" NL
"     Responses from the above commands." NL
"       R  Command successful" NL
"       E  Error/Invalid Command" NL
"       E\033[35mHHHHLLLL\033[0m" NL
"          Current high/low setpoints" NL
"          \033[35mHHHH\033[0m is 4 digit high, \033[35mLLLL\033[0m is 4 digit low" NL
"       \033[35m[value][units][mode]\033[0m" NL
"          Data read response" NL
"          \033[35m[value]\033[0m is force currently showing on the display (peak or realtime)" NL
"          \033[35m[units]\033[0m is the configured force units" NL
"          \033[35m[mode]\033[0m  is the current mode (peak or realtime)" NL
"  \033[1;35mread\033[0m" NL
"     Read the current force/distance value." NL
"     If specified it will N repeated reads with a delay after each read." NL
"       e.g. \033[35mread 125\033[0m" NL
"  \033[1;35mstart\033[0m" NL
"     Distance marker \033[35m[start]\033[0m for the start/end of a force curve measurement." NL
"     While in free running mode, a special message is displayed when reaching the \033[35m[start]\033[0m point." NL
"       \033[35m[start]\033[0m is defined by positioning the distance sensor at the position to start and running this command." NL
		);
}


void cliFunc_read( char* args )
{
	// Parse number from argument
	//  NOTE: Only first argument is used
	char* arg1Ptr;
	char* arg2Ptr;
	argumentIsolation_cli( args, &arg1Ptr, &arg2Ptr );

	// Convert the argument into an int
	int read_count = decToInt( arg1Ptr ) + 1;

	// If no argument specified, default to 1 read
	if ( *arg1Ptr == '\0' )
	{
		read_count = 2;
	}

	// Set the overall read count to read_count
	forceDistanceReadCount = read_count;
}


void cliFunc_start( char* args )
{
	// Read the current distance and set the new start/end position
	distanceStart = readDistanceGauge();

	print( NL );
	info_msg("New start/end position: ");
	printInt32( distanceStart - distanceOffset );
}


void cliFunc_stop( char* args )
{
	// Reset the forceDistanceRead and forceDistanceReadCount
	forceDistanceRead = 0;
	forceDistanceReadCount = 0;
}


void cliFunc_zeroForce( char* args )
{
	// Just use the imadaComm command sending the needed argument
	char* commandArg = "Z";
	imadaVerboseRead( commandArg );
}


void cliFunc_zeroPosition( char* args )
{
	// Read the current distance and set the new offset
	distanceOffset = readDistanceGauge();

	print( NL );
	info_msg("New distance offset: ");
	printInt32( distanceOffset );
}