References
[1] Honeywell International Inc., "Speech synthesis and voice recognition in metrologic equipment", 20100070273 A1, 2016.
[2] B. Land, “Cornell University ECE4760 Adafruit TFT LCD Display Model 1480 and Keypad
PIC32MX250F128B,” ECE 4760, 2016. [Online]. Available: https://people.ece.cornell.edu/land/courses/ece4760/PIC32/index_TFT_display.html. [Accessed: 13-Sep-2016]
[3] B. Land, "Charge Time Measurement Unit (CTMU) PIC32MX250F128B", ECE 4760, 2015. [Online]. Available: http://people.ece.cornell.edu/land/courses/ece4760/PIC32/index_CTMU.html. [Accessed: 08- Dec- 2016].
[4] B. Land, "Charge Time Measurement Unit (CTMU) PIC32MX250F128B", ECE 4760, 2015. [Online]. Available: http://people.ece.cornell.edu/land/courses/ece4760/PIC32/index_CTMU.html. [Accessed: 08- Dec- 2016].
[5] B. Land, "Cornell University ECE4760 Digital Signal Processing PIC32MX250F128B.", Cornell University ECE 4760, 2015. [Online]. Available: https://people.ece.cornell.edu/land/courses/ece4760/PIC32/index_DSP.html. [Accessed: 04- Oct- 2016].
Appendices
Appendix A
The group approves this report for inclusion on the course website.
The group approves the video for inclusion on the course youtube channel.
Appendix B - Raw Code
/*
* lab5.c
*
* Final Project: Speech Multimeter
*
* Rachel Dipirro (rdd49)
* Jonathan Lo (hl887)
*/
// Autorange = do it with overflow bit.
#include "config.h"
#include "tft_master.h"
#include "tft_gfx.h"
#include <stdlib.h>
#include <math.h>
#include <plib.h>
#include "pt_cornell_1_2_1.h"
#include "Mike_digits_8khz_packed.h"
//========================================================================
// DAC Macros
//========================================================================
#define DAC_config_chan_A 0b0011000000000000
#define DAC_config_chan_B 0b1011000000000000
//========================================================================
// ADC Setup Macros
//========================================================================
// ADC Setup for R Meter
#define PARAM1_R ADC_FORMAT_INTG16 | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_OFF //
#define PARAM2_R ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
#define PARAM3_R ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_31 | ADC_CONV_CLK_Tcy //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy
#define PARAM4_R ENABLE_AN11_ANA
#define PARAM5_R SKIP_SCAN_ALL
// ADC Setup for C Meter
#define PARAM1_C ADC_FORMAT_INTG16 | ADC_CLK_MANUAL | ADC_AUTO_SAMPLING_OFF //
#define PARAM2_C ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
#define PARAM3_C ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_15 | ADC_CONV_CLK_Tcy //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy
#define PARAM4_C ENABLE_AN9_ANA
#define PARAM5_C SKIP_SCAN_ALL
//========================================================================
// Keypad Pull Up/Pull Down Macros
//========================================================================
//Port B
#define EnablePullDownB(bits) CNPUBCLR=bits; CNPDBSET=bits;
#define DisablePullDownB(bits) CNPDBCLR=bits;
#define EnablePullUpB(bits) CNPDBCLR=bits; CNPUBSET=bits;
#define DisablePullUpB(bits) CNPUBCLR=bits;
//PORT A
#define EnablePullDownA(bits) CNPUACLR=bits; CNPDASET=bits;
#define DisablePullDownA(bits) CNPDACLR=bits;
#define EnablePullUpA(bits) CNPDACLR=bits; CNPUASET=bits;
#define DisablePullUpA(bits) CNPUACLR=bits;
//========================================================================
// 16:16 Fixed Point Macros
//========================================================================
typedef signed int fix16 ;
#define multfix16(a,b) ((fix16)(((( signed long long)(a))*(( signed long long)(b)))>>16)) //multiply two fixed 16:16
#define float2fix16(a) ((fix16)((a)*65536.0)) // 2^16
#define fix2float16(a) ((float)(a)/65536.0)
#define fix2int16(a) ((int)((a)>>16))
#define int2fix16(a) ((fix16)((a)<<16))
#define divfix16(a,b) ((fix16)((((signed long long)(a)<<16)/(b))))
#define sqrtfix16(a) (float2fix16(sqrt(fix2float16(a))))
#define absfix16(a) abs(a)
#define onefix16 0x00010000 // int2fix16(1)
#define sustain_constant float2fix16(256.0/20000.0) ; // seconds per decay update
//========================================================================
// some precise, fixed, short delays
// to use for cap charging time
#define NOP asm("nop");
// 1/2 microsec
#define wait20 NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;
// one microsec
#define wait40 wait20;wait20;
//========================================================================
//========================================================================
// KEYS
//========================================================================
int resConfirm = 0;
int rMode = 0;
int R_new, R_old;
int dontgocrazy;
char ohmBuffer[20];
int rSpeechMode;
int capConfirm = 0;
int cMode = 0;
float C_new, C_old, C_nano;
int C_nanoint, C_decimal;
char capBuffer[20];
float fractpart, intpart;
int cSpeechMode;
int voltConfirm = 0;
int vMode = 0;
int showKey_pressed = 1;
float divVoltage;
float V_new, V_old;
int V_int;
char voltBuffer[20];
int vSpeechMode;
//========================================================================
// DAC and SPI
//========================================================================
volatile unsigned int DAC_data;
volatile SpiChannel spiChn = SPI_CHANNEL2 ; // the SPI channel to use
volatile int spiClkDiv = 2 ; // 20 MHz max speed for this DAC
int sys_time_seconds ;
//========================================================================
// Voice
//========================================================================
int key_pressed;
volatile int end_index;
volatile int begin_index;
volatile unsigned int voice_i, voice_j, voice_packed, voice_value;
int playFlag;
char reset_voice = 0;
int desiredKey; // This is for testing only!!! WARNING.
int len, digitLength;
int digitVar = 0;
int stop = 0;
int stableFlag;
int endFlag;
//========================================================================
// Protothreads
//========================================================================
char buffer[60]; // String buffer
char timerbuff[60]; // String buffer for timer
static struct pt pt_timer, pt_key, pt_ctmu, pt_voice;
//========================================================================
// ISR
//========================================================================
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void){
mT2ClearIntFlag();
// Voice
if (playFlag){
voice_j = voice_i >> 1;
if(~(voice_i & 1)) voice_packed = AllDigits[voice_j];
// If odd, get upper nibble
if(voice_i & 1) voice_value = voice_packed >> 4;
// If even, get lower nibble
else voice_value = voice_packed & 0x0f;
voice_i++;
voice_j = voice_i >> 1;
if(voice_j > end_index){
playFlag = 0; // Stop playing the word
//reset_voice = 0; // Prevent more voice creation
if (!rSpeechMode && !vSpeechMode && !cSpeechMode) digitVar++; // Go to the next digit if available
dontgocrazy = 0; // Prevents ohmBuffer from being constantly written, which makes things go crazy
rSpeechMode = 0;
cSpeechMode = 0;
vSpeechMode = 0;
}
}
else{
voice_value = 0; // Nothing sends to DAC
}
//SPI for Voice
mPORTBClearBits(BIT_4); // Start transaction
DAC_data = DAC_config_chan_B | (voice_value<<5) ; // Shift for volume control
WriteSPI2(DAC_data);
while (SPI2STATbits.SPIBUSY); // Wait for end of transaction
mPORTBSetBits(BIT_4); // End transaction
} //End ISR
//========================================================================
//======================= Timer Thread ===================================
//========================================================================
static PT_THREAD (protothread_timer(struct pt *pt))
{
PT_BEGIN(pt);
//tft_setCursor(0, 0);
//tft_setTextColor(ILI9340_WHITE); tft_setTextSize(1);
//tft_writeString("Secs since boot\n");
while(1) {
// yield time 1 second
PT_YIELD_TIME_msec(1000) ;
// =================================================================== //
// Putting the play because it will be slower, and stop speech correspondingly.
if (rMode){
if ((stableFlag) && (digitLength>=digitVar)){ // Stop at digit+unit, good for R mode
playFlag = 1;
}
if (digitLength<digitVar+1 && !rSpeechMode) endFlag = 1;
}
if (cMode){
if ((stableFlag) && (digitLength+3>=digitVar)){ // Stop at digit+unit+2 decimal places, good for C mode
playFlag = 1;
}
if (digitLength+3<digitVar+1 && !cSpeechMode) endFlag = 1;
}
if (vMode){
if ((stableFlag) && (digitLength+3>=digitVar)){ // Stop at digit+unit, good for R mode
playFlag = 1;
}
if (digitLength+3<digitVar+1 && !vSpeechMode) endFlag = 1;
}
if(rSpeechMode) playFlag = 1;
if(cSpeechMode) playFlag = 1;
if(vSpeechMode) playFlag = 1;
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // timer thread
//========================================================================
//======================= CTMU Thread ====================================
//========================================================================
// Mess with resistance measurement
static int pressedKey ;
static PT_THREAD (protothread_ctmu(struct pt *pt))
#define Vdd 3.3
#define ADC_max 1023.0
{
PT_BEGIN(pt);
static int raw_adc;
static int tempvalR, k;
static float I[4]={550e-6, 0.55e-6, 5.5e-6, 55e-6} ; // current settings in amps
static int adc_zero[4]={113, 0, 0, 11}; // ADC reading with zero ohms at each current
static float I_set, tempvalC, newFractpart, tempvalV;
// static float fractpart, intpart;
// CTMU Setup
// Current Range IRNG 0x3 => 100x, approx 55 microamps;
// IRNG 0x2 => 10x, approx 5.5 microamps
// IRNG 0x1 => 1x, approx 0.55 microamps
// IRNG 0x00 => 1000x, approx 550 microamps
CTMUCONbits.ON = 1; // Turn on CTMU
//CTMUCONbits.IRNG = 3; // set this in loop
CTMUCONbits.IDISSEN = 0; // End drain of circuit
CTMUCONbits.EDG1STAT = 1; // Begin charging the circuit
while(1) {
PT_YIELD_TIME_msec(30);
//===================================================================
//=================== RESISTANCE MEASUREMENT ========================
//===================================================================
while(rMode && (!rSpeechMode)){ // Check if resistance mode
//CTMUCONbits.IRNG = 3; // set this in loop
CTMUCONbits.IDISSEN = 0; // End drain of circuit
CTMUCONbits.EDG1STAT = 1; // Begin charging the circuit
PT_YIELD_TIME_msec(30);
OpenADC10( PARAM1_R, PARAM2_R, PARAM3_R, PARAM4_R, PARAM5_R ); // configure ADC using the parameters defined above
EnableADC10();
tft_fillRoundRect(0,107, 10, 15, 0, ILI9340_BLACK);
tft_setCursor(0, 107);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
tft_writeString("R: ");
int currentSET = 2;
I_set = I[currentSET];
CTMUCONbits.IRNG = currentSET;
CTMUCONbits.EDG1STAT = 1;
AcquireADC10(); // start ADC sampling
while (!AD1CON1bits.DONE){}; // Wait for ADC conversion
// read the result of channel AN11 from the idle buffer
raw_adc = ReadADC10(0) ;
// turn off current source
CTMUCONbits.EDG1STAT = 0;
// convert raw to resistance ADC reads 11 at zero resistance
// Vref = Vdd = 3.3 ; current source=55 microamps
R_old = R_new; // For stabilization purposes
R_new = (float)(raw_adc-adc_zero[currentSET])/ADC_max * Vdd / I_set ; // V/I = R
// Brute force calibration
if (((int)R_new < 600) && (int)R_new>200) R_new = R_new - 250; // Works with 330 measurement
if (((int)R_new < 1500) && (int)R_new>1000) R_new = R_new - 170; // Works with 1k measurement
if (((int)R_new < 2500) && (int)R_new>2000) R_new = R_new - 340; // Works with 2k measurement
// draw resistance results
tft_fillRoundRect(40,107, 220, 20, 0, ILI9340_BLACK);// x,y,w,h,radius,color
tft_setCursor(40, 107);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
sprintf(buffer,"%d Ohm", (int)R_new );
tft_writeString(buffer);
//}
if (R_new>0 && !stableFlag){ // If there is a reading on resistance, necessary, otherwise will crash if R_new = 0 (not reading anything).
tempvalR = R_new;
if(tempvalR>=10) len = 2; // Find the length of the value
if(tempvalR>=100) len = 3;
if(tempvalR>=1000) len = 4;
if(tempvalR>=10000) len = 5;
if(tempvalR>=100000) len = 6;
if(tempvalR>=1000000) len = 7;
if(tempvalR>=10000000) len = 8;
if(tempvalR>=100000000) len = 9;
if(tempvalR>=1000000000) len = 10;
digitLength = len; // digitLength is used to stop speech, variable len gets "--"ed, will get overwritten in following for loop, need to preserve it
ohmBuffer[len] = 11; // Assign the last digit to be "ohm"
for(k=tempvalR;len--; k=(int)(k/10)) ohmBuffer[len] = (k%10);
// E.g. R = 9852, then ohmBuffer[0] = 9, ohmBuffer[1]=8, etc.
}
}
//tft_fillRoundRect(0,130, 220, 50, 0, ILI9340_BLACK);// When another mode is selected, erase the ADC, R thing.
//===================================================================
//================== CAPACITANCE MEASUREMENT ========================
//===================================================================
while(cMode && (!cSpeechMode)){
tft_fillRoundRect(0,107, 10, 15, 0, ILI9340_BLACK);
tft_setCursor(0, 107);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
tft_writeString("C: ");
//I_set = I[key_pressed];
I_set = I[0];
CTMUCONbits.IRNG = 0;
PT_YIELD_TIME_msec(100);
OpenADC10( PARAM1_C, PARAM2_C, PARAM3_C, PARAM4_C, PARAM5_C ); // configure ADC using the parameters defined above
EnableADC10();
// dischrge the cap
AcquireADC10(); // start ADC sampling -- connects ADC sample cap to circuit
// and discharge
CTMUCONbits.IDISSEN = 1; // start drain of circuit
PT_YIELD_TIME_msec(1); // wait for discharge
CTMUCONbits.IDISSEN = 0; // End drain of circuit
// start charging and wait 2 microsecs
CTMUCONbits.EDG1STAT = 1;
wait40;wait40;
// end charging
CTMUCONbits.EDG1STAT = 0;
// Keep in between 0.1 - 1 V for each decade
// stop samping and start conversion
// note that in:
//#define PARAM1 ADC_FORMAT_INTG16 | ADC_CLK_MANUAL | ADC_AUTO_SAMPLING_OFF
// clock is manual and auto sampling is off
ConvertADC10(); // end sampling & start conversion
// wait for complete
while (!AD1CON1bits.DONE){}; // Wait for ADC conversion
// read the result of channel from the idle buffer
raw_adc = ReadADC10(0) ;
// convert raw to resistance ADC reads 11 at zero resistance
// Vref = Vdd = 3.3 ; 2 microsec charge pulse
C_old = C_new; // For stabilization purposes
C_new = (I_set * 2e-6) / ((float)(raw_adc)/ADC_max * Vdd) ; // c = q/v
//if ((((int)C_new > 45) && ((int)C_new<55))||(((int)C_new > 25) && ((int)C_new<30))) C_new = 99.12;
C_nano = C_new/(1e-9);
/*int C_temp = (int)C_nano;
if (C_temp == 341) C_new = (I_set * 2e-4) / ((float)(raw_adc)/ADC_max * Vdd) ; // c = q/v*/
// draw capacitance results
// erase
tft_fillRoundRect(40,107, 220, 20, 0, ILI9340_BLACK);// x,y,w,h,radius,color
tft_setCursor(40, 107);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
//sprintf(buffer,"%3.4e nF", C_nano);
sprintf(buffer,"%.2f nF", C_nano);
tft_writeString(buffer);
if (C_nano>10 && !stableFlag){ // If there is a reading on resistance, necessary, otherwise will crash if R_new = 0 (not reading anything).
C_nanoint = (int)C_nano;
tempvalC = C_nanoint;
if(tempvalC>=10) len = 2; // Find the length of the value
if(tempvalC>=100) len = 3;
if(tempvalC>=1000) len = 4;
if(tempvalC>=10000) len = 5;
if(tempvalC>=100000) len = 6;
if(tempvalC>=1000000) len = 7;
if(tempvalC>=10000000) len = 8;
if(tempvalC>=100000000) len = 9;
if(tempvalC>=1000000000) len = 10;
digitLength = len; // digitLength is used to stop speech, variable len gets "--"ed, will get overwritten in following for loop, need to preserve it
capBuffer[len] = 10; // Assign the next digit to be "point"
fractpart = modf(C_nano, &intpart);
newFractpart = (fractpart*100); // To get the 2 decimal values
capBuffer[len+1] = (((int)newFractpart/10)%10); // First decimal
capBuffer[len+2] = ((int)newFractpart%10); // Second decimal
capBuffer[len+3] = 13; // Assign the last digit to be "nano-farad"
// For loop goes after the above before len gets overwriten.
for(k=tempvalC;len--; k=(int)(k/10)) capBuffer[len] = (k%10);
// Force to always pronounce 2 decimals
}
else if (C_nano<10 && !stableFlag){
C_nanoint = (int)C_nano;
tempvalC = C_nanoint;
len = 1;
digitLength = len;
capBuffer[len-1] = tempvalC;
capBuffer[len] = 10;
fractpart = modf(C_nano, &intpart);
newFractpart = (fractpart*100); // To get the 2 decimal values
capBuffer[len+1] = (((int)newFractpart/10)%10); // First decimal
capBuffer[len+2] = ((int)newFractpart%10); // Second decimal
capBuffer[len+3] = 13; // Assign the last digit to be "nano-farad"
}
}
//tft_fillRoundRect(0,80, 220, 50, 0, ILI9340_BLACK);// When another mode is selected, erase the Capacitance: thing.
//===================================================================
//================== VOLTMETER MEASUREMENT ==========================
//===================================================================
while(vMode && (!rSpeechMode)){
PT_YIELD_TIME_msec(100); // To be modified depending on load
//CTMUCONbits.IRNG = 3; // set this in loop
CTMUCONbits.IDISSEN = 0; // End drain of circuit
CTMUCONbits.EDG1STAT = 1; // Begin charging the circuit
OpenADC10( PARAM1_R, PARAM2_R, PARAM3_R, PARAM4_R, PARAM5_R ); // configure ADC using the parameters defined above
EnableADC10();
I_set = I[3];
CTMUCONbits.IRNG = 3;
CTMUCONbits.EDG1STAT = 1;
AcquireADC10(); // start ADC sampling
while (!AD1CON1bits.DONE){}; // Wait for ADC conversion
// read the result of channel AN11 from the idle buffer
raw_adc = ReadADC10(0) ;
// turn off current source
CTMUCONbits.EDG1STAT = 0;
// convert raw to resistance ADC reads 11 at zero resistance
// Vref = Vdd = 3.3 ; current source=55 microamps
//tft_fillRoundRect(0,80, 220, 50, 1, ILI9340_BLACK);// When another mode is selected, erase previous words:
// 0V = raw_adc @ 70
// 1.33V = raw_adc @ 214
// 2.33V = raw_adc @ 322
// 3.34V = raw_adc @ 431
// 4.34V = raw_adc @ 536
// 5.34V = raw_adc @ 644
// 6.34V = raw_adc @
// Max @ raw_adc = 1024 (9.84V)
OpenADC10( PARAM1_R, PARAM2_R, PARAM3_R, PARAM4_R, PARAM5_R ); // configure ADC using the parameters defined above
EnableADC10();
AcquireADC10(); // start ADC sampling
while (!AD1CON1bits.DONE){}; // Wait for ADC conversion
// read the result of channel AN11 from the idle buffer
raw_adc = ReadADC10(0) ;
// Equation created using Excel y = 111x - 20.143
V_old = V_new;
divVoltage = (raw_adc+3.5714)/107.89 + 0.05;
V_new = divVoltage;
tft_fillRoundRect(0,107, 10, 15, 0, ILI9340_BLACK);
tft_setCursor(0, 107);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
tft_writeString("V: ");
tft_fillRoundRect(40,107, 220, 20, 0, ILI9340_BLACK);
tft_setCursor(40, 107);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
//sprintf(buffer,"%d V", endFlag);
sprintf(buffer,"%.2f V", V_new);
tft_writeString(buffer);
if (V_new!=0 && !stableFlag){ // If there is a reading on resistance, necessary, otherwise will crash if R_new = 0 (not reading anything).
V_int = (int)V_new;
tempvalV = V_int;
len = 1;
digitLength = len; // digitLength is used to stop speech, variable len gets "--"ed, will get overwritten in following for loop, need to preserve it
voltBuffer[len-1] = V_int;
voltBuffer[len] = 10; // Assign the next digit to be "point"
fractpart = modf(V_new, &intpart);
newFractpart = (fractpart*100); // To get the 2 decimal values
voltBuffer[len+1] = (((int)newFractpart/10)%10); // First decimal
voltBuffer[len+2] = ((int)newFractpart%10); // Second decimal
voltBuffer[len+3] = 14; // Assign the last digit to be "volts"
}
}
//tft_fillRoundRect(0,80, 220, 50, 0, ILI9340_BLACK);// When another mode is selected, erase the Voltage: thing.
//====================================================================
PT_YIELD_TIME_msec(30);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // ctmu thread
//========================================================================
//============================ Key Thread ================================
//========================================================================
static PT_THREAD (protothread_key(struct pt *pt))
{
PT_BEGIN(pt);
static int keypad, pattern, stableCount;
static int keytable[12]={0x108, 0x81, 0x101, 0x201, 0x82, 0x102, 0x202, 0x84, 0x104, 0x204, 0x88, 0x208};
mPORTASetPinsDigitalOut(BIT_0 | BIT_1 | BIT_2 | BIT_3); //Set port as output
mPORTBSetPinsDigitalIn(BIT_7 | BIT_8 | BIT_9); //Set port as input
/*// For debug purposes only
tft_setCursor(0, 180);
tft_setTextColor(ILI9340_WHITE);
tft_setTextSize(1);
tft_writeString("Current Value of key_pressed: ");*/
while(1) {
mPORTAClearBits(BIT_0 | BIT_1 | BIT_2 | BIT_3);
pattern = 1; mPORTASetBits(pattern);
PT_YIELD_TIME_msec(30);
for (key_pressed=0; key_pressed<4; key_pressed++) {
keypad = mPORTBReadBits(BIT_7 | BIT_8 | BIT_9);
if(keypad!=0) {keypad |= pattern ; break;}
mPORTAClearBits(pattern);
pattern <<= 1;
mPORTASetBits(pattern);
}
// search for keycode
if (keypad > 0){ // then button is pushed
for (key_pressed=0; key_pressed<12; key_pressed++){
if (keytable[key_pressed]==keypad) break;
}
}
else key_pressed = -1; // no button pushed
/*// For debug purposes only // draw key number
tft_fillRoundRect(190,180, 30, 15, 0, ILI9340_BLACK);// x,y,w,h,radius,color
tft_setCursor(190, 180);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
sprintf(buffer,"%d", showKey_pressed);*/
pressedKey = key_pressed;
if (stableFlag && !dontgocrazy && !endFlag){
if(rMode) desiredKey = ohmBuffer[digitVar];
if(cMode) desiredKey = capBuffer[digitVar];
if(vMode) desiredKey = voltBuffer[digitVar];
dontgocrazy = 1;
}
else if (rSpeechMode && !dontgocrazy){
desiredKey = 15;
dontgocrazy = 1;
}
else if (cSpeechMode && !dontgocrazy){
desiredKey = 16;
dontgocrazy = 1;
}
else if (vSpeechMode && !dontgocrazy){
desiredKey = 17;
dontgocrazy = 1;
}
else
/* Some big number that cannot be matched in the switch function
* within the voice thread. This number will never be reached to stop.*/
desiredKey = 1000;
// For debug purposes only
// tft_writeString(buffer);
// ====================================================================
// ==================== Mode selection begins here=====================
// ====================================================================
if (key_pressed == 7){
resConfirm = 1;
capConfirm = 0;
voltConfirm = 0;
tft_fillRoundRect(0,50, 240, 30, 0, ILI9340_BLACK);
tft_setCursor(18, 50);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
tft_writeString("Ohmmeter Selected");
tft_setCursor(12, 70);
tft_writeString("Press # to Confirm");
}
else if (key_pressed == 8){
resConfirm = 0;
capConfirm = 1;
voltConfirm = 0;
tft_fillRoundRect(0,50, 240, 30, 0, ILI9340_BLACK);
tft_setCursor(12, 50);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
tft_writeString("Cap Meter Selected");
tft_setCursor(12, 70);
tft_writeString("Press # to Confirm");
}
else if (key_pressed == 9){
resConfirm = 0;
capConfirm = 0;
voltConfirm = 1;
tft_fillRoundRect(0,50, 240, 30, 0, ILI9340_BLACK);
tft_setCursor(12, 50);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
tft_writeString("Voltmeter Selected");
tft_setCursor(12, 70);
tft_writeString("Press # to Confirm");
}
if (key_pressed==11 && resConfirm==1){
tft_fillRoundRect(0,50, 240, 40, 0, ILI9340_BLACK);
tft_fillRoundRect(0,130, 240, 14, 0, ILI9340_BLACK);
tft_setCursor(33, 55);
tft_setTextColor(ILI9340_GREEN); tft_setTextSize(3);
tft_writeString("Resistance");
rSpeechMode = 1;
// Then Toggle flag for R meter mode
rMode = 1; // Resistance meter is officially selected
cMode = 0; // Turn off
vMode = 0; // Turn off
stableCount = 0; // Reset stable counter upon switching to different mode
stableFlag = 0;
digitVar = 0;
endFlag = 0;
}
else if (key_pressed==11 && capConfirm==1){
tft_fillRoundRect(0,50, 240, 40, 0, ILI9340_BLACK);
tft_fillRoundRect(0,130, 240, 14, 0, ILI9340_BLACK);
tft_setCursor(20, 55);
tft_setTextColor(ILI9340_GREEN); tft_setTextSize(3);
tft_writeString("Capacitance");
cSpeechMode = 1;
// Then Toggle flag for C meter mode
rMode = 0; // Turn off
cMode = 1; // Capacitance meter is officially selected
vMode = 0; // Turn off
stableCount = 0; // Reset stable counter upon switching to different mode
stableFlag = 0;
digitVar = 0;
}
else if (key_pressed==11 && voltConfirm==1){
tft_fillRoundRect(0,50, 240, 40, 0, ILI9340_BLACK);
tft_fillRoundRect(0,130, 240, 14, 0, ILI9340_BLACK);
tft_setCursor(0, 55);
tft_setTextColor(ILI9340_GREEN); tft_setTextSize(3);
tft_writeString(" Voltage");
vSpeechMode = 1;
// Then Toggle flag for V meter mode
rMode = 0; // Turn off
cMode = 0; // Turn off
vMode = 1; // Voltmeter is officially selected
stableCount = 0; // Reset stable counter upon switching to different mode
stableFlag = 0;
digitVar = 0;
}
// ==================================================================
if (rMode){
if (R_new > 0 && !stableFlag){ // If R has begun measuring, then activate stabilization code
if (R_old == R_new){
stableCount++; // Increment until stable value reached (Handled in another thread)
}
else
stableCount = 0; // Reset
if (stableCount >= 7) { // 12 obtained from trial and error, how long it takes to stabilize value?
tft_fillRect(0, 130, 240, 20, ILI9340_GREEN);
tft_setCursor(1, 132);
tft_setTextColor(ILI9340_BLACK); tft_setTextSize(2);
tft_writeString("Stabilized at ");
stableFlag = 1;// Set a stable flag
sprintf(buffer,"%d", R_old);
tft_writeString(buffer);
}
else{
tft_fillRect(0, 130, 240, 20, ILI9340_RED);
tft_setCursor(1, 132);
tft_setTextColor(ILI9340_BLACK); tft_setTextSize(2);
tft_writeString("Waiting to stabilize...");
}
}
}
if (cMode){
if (C_new > 0 && !stableFlag){
if (C_old == C_new){
stableCount++; // Increment until stable value reached (Handled in another thread)
}
else
stableCount = 0; // Reset
if (stableCount >= 4) { // 12 obtained from trial and error, how long it takes to stabilize value?
tft_fillRect(0, 130, 240, 20, ILI9340_GREEN);
tft_setCursor(1, 132);
tft_setTextColor(ILI9340_BLACK); tft_setTextSize(2);
tft_writeString("Stabilized at ");
stableFlag = 1;// Set a stable flag
sprintf(buffer,"%.2f", C_nano);
tft_writeString(buffer);
}
else{
tft_fillRect(0, 130, 240, 20, ILI9340_RED);
tft_setCursor(1, 132);
tft_setTextColor(ILI9340_BLACK); tft_setTextSize(2);
tft_writeString("Waiting to stabilize...");
}
}
}
if (vMode){
if (V_new > 0 && !stableFlag){
if (V_old == V_new){
stableCount++; // Increment until stable value reached (Handled in another thread)
}
else
stableCount = 0; // Reset
if (stableCount >= 10) { // 12 obtained from trial and error, how long it takes to stabilize value?
tft_fillRect(0, 130, 240, 20, ILI9340_GREEN);
tft_setCursor(1, 132);
tft_setTextColor(ILI9340_BLACK); tft_setTextSize(2);
tft_writeString("Stabilized at ");
stableFlag = 1;// Set a stable flag
sprintf(buffer,"%.2f", V_old);
tft_writeString(buffer);
}
else{
tft_fillRect(0, 130, 240, 20, ILI9340_RED);
tft_setCursor(1, 132);
tft_setTextColor(ILI9340_BLACK); tft_setTextSize(2);
tft_writeString("Waiting to stabilize...");
}
}
}
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // keypad thread
// ==============================================================
// ==================== Voice Thread ============================
// ==============================================================
static PT_THREAD (protothread_voice(struct pt *pt))
{
PT_BEGIN(pt);
static int pattern;
// Init the keypad pins A0-A3 and B7-B9, and PortA ports as digital outputs
mPORTASetPinsDigitalIn(BIT_0 | BIT_1 | BIT_2 | BIT_3); //Set port as input
mPORTBSetPinsDigitalIn(BIT_7 | BIT_8 | BIT_9); //Set port as input
while(1) {
// read each row sequentially
mPORTASetPinsDigitalOut(BIT_0); //Set A0 as output
mPORTAClearBits(BIT_0 | BIT_1 | BIT_2 | BIT_3);
pattern = 1; mPORTASetBits(pattern);
//=== Keypad Code=================================================
// yield time
PT_YIELD_TIME_msec(10);
//====Set voice and test mode=====================================
switch(desiredKey){
// The following are indexes for the digits.
case 0:
reset_voice = 1; // Only toggle reset voice when a digit is to be pronounced.
begin_index = 0;
end_index = 2400;
showKey_pressed = 0;
break;
case 1:
reset_voice = 1;
begin_index = 2461;
end_index = 3787;
showKey_pressed = 1;
break;
case 2:
reset_voice = 1;
begin_index = 4912;
end_index = 6656;
showKey_pressed = 2;
break;
case 3:
reset_voice = 1;
begin_index = 7088;
end_index = 8864;
showKey_pressed = 3;
break;
case 4:
reset_voice = 1;
begin_index = 9344;
end_index = 11232;
showKey_pressed = 4;
break;
case 5:
reset_voice = 1;
begin_index = 11712;
end_index = 14080;
showKey_pressed = 5;
break;
case 6:
reset_voice = 1;
begin_index = 14192;
end_index = 16496;
showKey_pressed = 6;
break;
case 7:
reset_voice = 1;
begin_index = 16816;
end_index = 19344;
showKey_pressed = 7;
break;
case 8:
reset_voice = 1;
begin_index = 19648;
end_index = 20848;
showKey_pressed = 8;
break;
case 9:
reset_voice = 1;
begin_index = 21184;
end_index = 23719;
showKey_pressed = 9;
break;
// The following are correct indexes for the words
case 10: // Point
reset_voice = 1;
begin_index = 23719;
end_index = 24519;
break;
case 11: // Ohm
reset_voice = 1;
begin_index = 24520;
end_index = 25500;
break;
case 12: // Kilo-ohm
reset_voice = 1;
begin_index = 25501;
end_index = 28200;
break;
case 13: // Nano-Farad
reset_voice = 1;
begin_index = 28201;
end_index = 31000;
break;
case 14: // Volt
reset_voice = 1;
begin_index = 31000;
end_index = 31700;
break;
case 15: // Resistance
reset_voice = 1;
begin_index = 31701;
end_index = 34200;
break;
case 16: // Capacitance
reset_voice = 1;
begin_index = 34201;
end_index = 37201;
break;
case 17: // Voltage
reset_voice = 1;
begin_index = 37202;
end_index = 41284;
break;
case 1000:
reset_voice = 0;
}
// Set new voice, reset_voice is toggled back to 0 after a digit
// has finished producing. Only when a case is matched
if(reset_voice){
voice_i = begin_index << 1;
reset_voice = 0;
}
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // keypad thread
void main(void) {
SYSTEMConfigPerformance(PBCLK);
ANSELA = 0; ANSELB = 0; CM1CON = 0; CM2CON = 0;
PT_setup();
INTEnableSystemMultiVectoredInt(); // Setup system wide interrupts
EnablePullDownB( BIT_7 | BIT_8 | BIT_9); // Keypad Config
SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN11 ); // configure to sample AN11
// Initialize Threads
PT_INIT(&pt_timer);
PT_INIT(&pt_key);
PT_INIT(&pt_ctmu);
PT_INIT(&pt_voice);
// Initialize TFT
tft_init_hw();
tft_begin();
tft_fillScreen(ILI9340_BLACK);
tft_setRotation(0); // Use tft_setRotation(1) for 320x240
// Timer Interrupt
// Set up timer2 on, interrupts, internal clock, prescalar 1, toggle rate
// at 40 MHz PB clock 60 counts is two microsec
// 5000 is 8 ksamples/sec
OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_1, 5000);
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2);
mT2ClearIntFlag();
PPSOutput(2, RPB5, SDO2); // SDO2 (MOSI) is in PPS output group 2, could be connected to RB5 which is pin 14
// DAC Chip Select
mPORTBSetPinsDigitalOut(BIT_4);
mPORTBSetBits(BIT_4);
SpiChnOpen(spiChn, SPI_OPEN_ON | SPI_OPEN_MODE16 | SPI_OPEN_MSTEN | SPI_OPEN_CKE_REV , spiClkDiv);
tft_fillRoundRect(0,240, 100, 14, 0, ILI9340_BLACK);
tft_setCursor(40, 245);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
tft_writeString("Instructions");
tft_drawLine(0, 267, 240, 267, ILI9340_YELLOW);
tft_drawLine(0, 95, 240, 95, ILI9340_YELLOW);
tft_setCursor(10, 275);
tft_setTextSize(1);
tft_writeString("Press 7 to Select Resistance Meter");
tft_setCursor(10, 290);
tft_writeString("Press 8 to Select Capacitance Meter");
tft_setCursor(10, 305);
tft_writeString("Press 9 to Select Voltage Meter");
//Press 7 for R Meter \nPress 8 for C Meter \nPress 9 for V Meter\n
tft_setCursor(70, 0);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
tft_writeString("ECE 4760\n Talking Multimeter\n");
tft_drawLine(0, 40, 240, 40, ILI9340_YELLOW);
while (1){
PT_SCHEDULE(protothread_timer(&pt_timer));
PT_SCHEDULE(protothread_voice(&pt_voice));
PT_SCHEDULE(protothread_ctmu(&pt_ctmu));
PT_SCHEDULE(protothread_key(&pt_key));
}
} // main
// === end ==============================================================
Appendix C
Schematics
Appendix D
Budget Considerations
We worked to develop a low-cost design that could be easily replicated. We used exclusively parts from lab so we wouldn't have to wait on shipping. Most of our budget goes to the hardware lab rentals, and even with the exorbitant amount of headers, we remained well under the $100 budget. The cost of wires, resistors, solder, lab space and equipment, and labor are not included in this budget.
Name |
Quantity |
Price |
PIC32MX250F128B |
1 |
5 |
MicroStickII |
1 |
10 |
TFT LCD |
1 |
10 |
Keypad |
1 |
6 |
Solder board |
1 |
2.50 |
Headers |
61 |
3.05 |
Jumper Cables |
4 |
0.40 |
Speaker (w/ AUX adapter) |
1 |
2 |
MCP4822 SPI DAC |
1 |
1.48 |
|
40.43 |
Appendix E
Task Division
Rachel specified the design for the project proposal, reasearched potential methods of speech generation and CTMU usage, organized the pin allocation, designed and debugged software, and soldered the final circuit. Jonathan designed and implemented most of the software, maintained the neatness of the whiteboard circuit, and debugged both hardware and software problems. Both participated in developing the website and writing the final report.