/******************************************************************************* * CTS_HAL.c - defines functions called by application as well as * functions for information processing * * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ /***************************************************************************//** * @file CTS_HAL.c * * @brief * * @par Project: * MSP430 Capacitive Touch Library * * @par Developed using: * IAR Version : 5.10.6 [Kickstart] (5.10.6.30180) * CCS Version : 4.2.1.00004, w/support for GCC extensions (--gcc) * * @version 1.0.0 Initial Release * * @par Supported API Calls: * - TI_CAPT_Init_Baseline() * - TI_CAPT_Update_Baseline() * - TI_CAPT_Reset_Tracking() * - TI_CAPT_Update_Tracking_DOI() * - TI_CAPT_Update_Tracking_Rate() * - TI_CAPT_Update_Baseline() * - TI_CAPT_Raw() * - TI_CAPT_Custom() * - TI_CAPT_Button() * - TI_CAPT_Buttons() * - TI_CAPT_Slider() * - TI_CAPT_Wheel() ******************************************************************************/ #include "CTS_Layer.h" #include // Global variables for sensing #ifdef TOTAL_NUMBER_OF_ELEMENTS uint16_t baseCnt[TOTAL_NUMBER_OF_ELEMENTS]; #ifdef RAM_FOR_FLASH uint16_t measCnt[MAXIMUM_NUMBER_OF_ELEMENTS_PER_SENSOR]; #endif uint16_t ctsStatusReg = (DOI_INC+TRADOI_FAST+TRIDOI_SLOW); #endif /***************************************************************************//** * @addtogroup CTS_API * @{ ******************************************************************************/ /***************************************************************************//** * @brief Measure the capacitance of each element within the Sensor * * This function selects the appropriate HAL to perform the capacitance * measurement based upon the halDefinition found in the sensor * structure. * The order of the elements within the Sensor structure is arbitrary * but must be consistent between the application and configuration. * The first element in the array (counts) corresponds to the first * element within the Sensor structure. * @param groupOfElements Pointer to Sensor structure to be measured * @param counts Address to where the measurements are to be written * @return none ******************************************************************************/ void TI_CAPT_Raw(const struct Sensor* groupOfElements, uint16_t * counts) { #ifdef RO_COMPAp_TA0_WDTp if(groupOfElements->halDefinition == RO_COMPAp_TA0_WDTp) { TI_CTS_RO_COMPAp_TA0_WDTp_HAL(groupOfElements, counts); } #endif #ifdef fRO_COMPAp_TA0_SW if(groupOfElements->halDefinition == fRO_COMPAp_TA0_SW) { TI_CTS_fRO_COMPAp_TA0_SW_HAL(groupOfElements, counts); } #endif #ifdef fRO_COMPAp_SW_TA0 if(groupOfElements->halDefinition == fRO_COMPAp_SW_TA0) { TI_CTS_fRO_COMPAp_SW_TA0_HAL(groupOfElements, counts); } #endif #ifdef RO_COMPAp_TA1_WDTp if(groupOfElements->halDefinition == RO_COMPAp_TA1_WDTp) { TI_CTS_RO_COMPAp_TA1_WDTp_HAL(groupOfElements, counts); } #endif #ifdef fRO_COMPAp_TA1_SW if(groupOfElements->halDefinition == fRO_COMPAp_TA1_SW) { TI_CTS_fRO_COMPAp_TA1_SW_HAL(groupOfElements, counts); } #endif #ifdef RC_PAIR_TA0 if(groupOfElements->halDefinition == RC_PAIR_TA0) { TI_CTS_RC_PAIR_TA0_HAL(groupOfElements, counts); // Measure all sensors } #endif #ifdef RO_PINOSC_TA0_WDTp if(groupOfElements->halDefinition == RO_PINOSC_TA0_WDTp) { TI_CTS_RO_PINOSC_TA0_WDTp_HAL(groupOfElements, counts); } #endif #ifdef RO_PINOSC_TA0 if(groupOfElements->halDefinition == RO_PINOSC_TA0) { TI_CTS_RO_PINOSC_TA0_HAL(groupOfElements, counts); } #endif #ifdef fRO_PINOSC_TA0_SW if(groupOfElements->halDefinition == fRO_PINOSC_TA0_SW) { TI_CTS_fRO_PINOSC_TA0_SW_HAL(groupOfElements, counts); // Measure all sensors } #endif #ifdef RO_COMPB_TA0_WDTA if(groupOfElements->halDefinition == RO_COMPB_TA0_WDTA) { TI_CTS_RO_COMPB_TA0_WDTA_HAL(groupOfElements, counts); } #endif #ifdef fRO_COMPB_TA0_SW if(groupOfElements->halDefinition == fRO_COMPB_TA0_SW) { TI_CTS_fRO_COMPB_TA0_SW_HAL(groupOfElements, counts); } #endif #ifdef RO_COMPB_TA1_WDTA if(groupOfElements->halDefinition == RO_COMPB_TA1_WDTA) { TI_CTS_RO_COMPB_TA1_WDTA_HAL(groupOfElements, counts); } #endif #ifdef fRO_COMPB_TA1_SW if(groupOfElements->halDefinition == fRO_COMPB_TA1_SW) { TI_CTS_fRO_COMPB_TA1_SW_HAL(groupOfElements, counts); } #endif } #ifdef TOTAL_NUMBER_OF_ELEMENTS /***************************************************************************//** * @brief Make a single capacitance meausrment to initialize baseline tracking * @param groupOfElements Pointer to Sensor structure to be measured * @return none ******************************************************************************/ void TI_CAPT_Init_Baseline(const struct Sensor* groupOfElements) { TI_CAPT_Raw(groupOfElements, &baseCnt[groupOfElements->baseOffset]); } /***************************************************************************//** * @brief Update baseline tracking by averaging several measurements * @param groupOfElements Pointer to Sensor structure to be measured * @param numberofAverages Number of measurements to be averaged * @return none ******************************************************************************/ void TI_CAPT_Update_Baseline(const struct Sensor* groupOfElements, uint8_t numberOfAverages) { uint8_t i,j; #ifndef RAM_FOR_FLASH uint16_t *measCnt; measCnt = (uint16_t *)malloc(groupOfElements->numElements * sizeof(uint16_t)); if(measCnt ==0) { while(1); } #endif for(j=0; j < numberOfAverages; j++) { for(i=0; i < groupOfElements->numElements; i++) { TI_CAPT_Raw(groupOfElements, measCnt); baseCnt[i+groupOfElements->baseOffset] = measCnt[i]/2 + baseCnt[i+groupOfElements->baseOffset]/2; } } #ifndef RAM_FOR_FLASH free(measCnt); #endif } /***************************************************************************//** * @brief Reset the Baseline Tracking algorithm to the default state * @param none * @return none ******************************************************************************/ void TI_CAPT_Reset_Tracking(void) { ctsStatusReg = (DOI_INC+TRADOI_FAST+TRIDOI_SLOW); } /***************************************************************************//** * @brief Update the Baseline Tracking algorithm Direction of Interest * @param direction Direction of increasing or decreasing capacitance * @return none ******************************************************************************/ void TI_CAPT_Update_Tracking_DOI(uint8_t direction) { if(direction) { ctsStatusReg |= DOI_INC; } else { ctsStatusReg &= ~DOI_INC; } } /***************************************************************************//** * @brief Update the baseling tracking algorithm tracking rates * @param rate Rate of tracking changes in and against direction of intrest * @return none ******************************************************************************/ void TI_CAPT_Update_Tracking_Rate(uint8_t rate) { ctsStatusReg &= ~(TRIDOI_FAST+TRADOI_VSLOW); // clear fields ctsStatusReg |= (rate & 0xF0); // update fields } /***************************************************************************//** * @brief Measure the change in capacitance of the Sensor * * This function measures the change in capacitance of each element * within a sensor and updates the baseline tracking in the event that * no change exceeds the detection threshold. * The order of the elements within the Sensor structure is arbitrary * but must be consistent between the application and configuration. * The first element in the array (deltaCnt) corresponds to the first * element within the Sensor structure. * @param groupOfElements Pointer to Sensor structure to be measured * @param deltaCnt Address to where the measurements are to be written * @return none ******************************************************************************/ void TI_CAPT_Custom(const struct Sensor* groupOfElements, uint16_t * deltaCnt) { uint8_t j; uint16_t tempCnt; ctsStatusReg &= ~ EVNT; // This section calculates the delta counts************************************* //****************************************************************************** TI_CAPT_Raw(groupOfElements, &deltaCnt[0]); // measure group of sensors for (j = 0; j < (groupOfElements->numElements); j++) { tempCnt = deltaCnt[j]; if(((ctsStatusReg & DOI_MASK) && (groupOfElements->halDefinition & RO_MASK)) || ((!(ctsStatusReg & DOI_MASK)) && (!(groupOfElements->halDefinition & RO_MASK)))) { // RO method, interested in an increase in capacitance if(baseCnt[j+groupOfElements->baseOffset] < deltaCnt[j]) { // If capacitance decreases, then measCnt is greater than base // , set delta to zero deltaCnt[j] = 0; // Limit the change in the opposite direction to the threshold if(((groupOfElements->arrayPtr[j])->threshold) && (baseCnt[j+groupOfElements->baseOffset]+(groupOfElements->arrayPtr[j])->threshold < tempCnt)) { tempCnt = baseCnt[j+groupOfElements->baseOffset]+(groupOfElements->arrayPtr[j])->threshold; } } else { // change occuring in our DOI, save result deltaCnt[j] = baseCnt[j+groupOfElements->baseOffset]-deltaCnt[j]; } } if(((!(ctsStatusReg & DOI_MASK)) && (groupOfElements->halDefinition & RO_MASK)) || ((ctsStatusReg & DOI_MASK) && (!(groupOfElements->halDefinition & RO_MASK)))) { // RO method: interested in a decrease in capactiance // measCnt is greater than baseCnt if(baseCnt[j+groupOfElements->baseOffset] > deltaCnt[j]) { // If capacitance increases, set delta to zero deltaCnt[j] = 0; // Limit the change in the opposite direction to the threshold if(((groupOfElements->arrayPtr[j])->threshold) && (baseCnt[j+groupOfElements->baseOffset] > tempCnt+(groupOfElements->arrayPtr[j])->threshold)) { tempCnt = baseCnt[j+groupOfElements->baseOffset]-(groupOfElements->arrayPtr[j])->threshold; } } else { // change occuring in our DOI deltaCnt[j] = deltaCnt[j] - baseCnt[j+groupOfElements->baseOffset]; } } // This section updates the baseline capacitance**************************** //************************************************************************** if (deltaCnt[j]==0) { // if delta counts is 0, then the change in capacitance was opposite the // direction of interest. The baseCnt[i] is updated with the saved // measCnt value for the current index value 'i'. switch ((ctsStatusReg & TRADOI_VSLOW)) { case TRADOI_FAST://Fast tempCnt = tempCnt/2; baseCnt[j+groupOfElements->baseOffset] = (baseCnt[j+groupOfElements->baseOffset]/2); break; case TRADOI_MED://Medium tempCnt = tempCnt/4; baseCnt[j+groupOfElements->baseOffset] = 3*(baseCnt[j+groupOfElements->baseOffset]/4); break; case TRADOI_SLOW://slow tempCnt = tempCnt/64; baseCnt[j+groupOfElements->baseOffset] = 63*(baseCnt[j+groupOfElements->baseOffset]/64); break; case TRADOI_VSLOW://very slow tempCnt = tempCnt/128; baseCnt[j+groupOfElements->baseOffset] = 127*(baseCnt[j+groupOfElements->baseOffset]/128); break; } // set X, Y & Z, then perform calculation for baseline tracking: // Base_Capacitance = X*(Measured_Capacitance/Z) + Y*(Base_Capacitance/Z) baseCnt[j+groupOfElements->baseOffset] = (tempCnt)+(baseCnt[j+groupOfElements->baseOffset]); } // delta counts are either 0, less than threshold, or greater than threshold // never negative else if(deltaCnt[j]<(groupOfElements->arrayPtr[j])->threshold && !(ctsStatusReg & PAST_EVNT)) { //if delta counts is positive but less than threshold, switch ((ctsStatusReg & TRIDOI_FAST)) { case TRIDOI_VSLOW://very slow if(deltaCnt[j] > 15) { if(tempCnt < baseCnt[j+groupOfElements->baseOffset]) { baseCnt[j+groupOfElements->baseOffset] = baseCnt[j+groupOfElements->baseOffset] - 1; } else { baseCnt[j+groupOfElements->baseOffset] = baseCnt[j+groupOfElements->baseOffset] + 1; } } tempCnt = 0; break; case TRIDOI_SLOW://slow if(tempCnt < baseCnt[j+groupOfElements->baseOffset]) { baseCnt[j+groupOfElements->baseOffset] = baseCnt[j+groupOfElements->baseOffset] - 1; } else { baseCnt[j+groupOfElements->baseOffset] = baseCnt[j+groupOfElements->baseOffset] + 1; } tempCnt = 0; break; case TRIDOI_MED://medium tempCnt = tempCnt/4; baseCnt[j+groupOfElements->baseOffset] = 3*(baseCnt[j+groupOfElements->baseOffset]/4); break; case TRIDOI_FAST://fast tempCnt = tempCnt/2; baseCnt[j+groupOfElements->baseOffset] = (baseCnt[j+groupOfElements->baseOffset]/2); break; } // set X, Y & Z, then perform calculation for baseline tracking: // Base_Capacitance = X*(Measured_Capacitance/Z) + Y*(Base_Capacitance/Z) baseCnt[j+groupOfElements->baseOffset] = (tempCnt)+(baseCnt[j+groupOfElements->baseOffset]); } //if delta counts above the threshold, event has occurred else if(deltaCnt[j]>=(groupOfElements->arrayPtr[j])->threshold) { ctsStatusReg |= EVNT; ctsStatusReg |= PAST_EVNT; } }// end of for-loop if(!(ctsStatusReg & EVNT)) { ctsStatusReg &= ~PAST_EVNT; } } /***************************************************************************//** * @brief Determine if a button is being pressed * @param groupOfElements Pointer to button to be scanned * @return result Indication if button is (1) or is not (0) being pressed ******************************************************************************/ uint8_t TI_CAPT_Button(const struct Sensor * groupOfElements) { uint8_t result = 0; #ifndef RAM_FOR_FLASH uint16_t *measCnt; measCnt = (uint16_t *)malloc(groupOfElements->numElements * sizeof(uint16_t)); if(measCnt ==0) { while(1); } #endif TI_CAPT_Custom(groupOfElements, measCnt); #ifndef RAM_FOR_FLASH free(measCnt); #endif if(ctsStatusReg & EVNT) { result = 1; } return result; } /***************************************************************************//** * @brief Determine which button if any is being pressed * @param groupOfElements Pointer to buttons to be scanned * @return result pointer to element (button) being pressed or 0 none ******************************************************************************/ const struct Element *TI_CAPT_Buttons(const struct Sensor *groupOfElements) { uint8_t index; #ifndef RAM_FOR_FLASH uint16_t *measCnt; measCnt = (uint16_t *)malloc(groupOfElements->numElements * sizeof(uint16_t)); if(measCnt ==0) { while(1); } #endif TI_CAPT_Custom(groupOfElements, measCnt); if(ctsStatusReg & EVNT) { index = Dominant_Element(groupOfElements, measCnt); //ctsStatusReg &= ~EVNT; index++; } else { index = 0; } #ifndef RAM_FOR_FLASH free(measCnt); #endif if(index) { return groupOfElements->arrayPtr[index-1]; } return 0; } #ifdef SLIDER /***************************************************************************//** * @brief Determine the position on a slider * @param groupOfElements Pointer to slider * @return result position on slider or illegal value if no touch ******************************************************************************/ uint16_t TI_CAPT_Slider(const struct Sensor* groupOfElements) { uint8_t index; int16_t position; // allocate memory for measurement #ifndef RAM_FOR_FLASH uint16_t *measCnt; measCnt = (uint16_t *)malloc(groupOfElements->numElements * sizeof(uint16_t)); if(measCnt ==0) { while(1); } #endif position = ILLEGAL_SLIDER_WHEEL_POSITION; //make measurement TI_CAPT_Custom(groupOfElements, measCnt); // Use EVNT flag to determine if slider was touched. // The EVNT flag is a global variable and managed within the TI_CAPT_Custom function. if(ctsStatusReg & EVNT) { index = Dominant_Element(groupOfElements, &measCnt[0]); // The index represents the element within the array with the highest return. if(index == 0) { // Special case of 1st element in slider, add 1st, last, and 2nd position = measCnt[0] + measCnt[1]; } else if(index == (groupOfElements->numElements -1)) { // Special case of Last element in slider, add last, 1st, and 2nd to last position = measCnt[groupOfElements->numElements -1] + measCnt[groupOfElements->numElements -2]; } else { position = measCnt[index] + measCnt[index+1] + measCnt[index-1]; } // Determine if sensor threshold criteria is met if(position > groupOfElements->sensorThreshold) { // calculate position position = index*(groupOfElements->points/groupOfElements->numElements); position += (groupOfElements->points/groupOfElements->numElements)/2; if(index == 0) { // Special case of 1st element in slider, which only has one // neighbor, measCnt[1]. measCnt is limited to maxResponse // within dominantElement function if(measCnt[1]) { position += (measCnt[1]*(groupOfElements->points/groupOfElements->numElements))/100; } else { position = (measCnt[0]*(groupOfElements->points/groupOfElements->numElements)/2)/100; } } else if(index == (groupOfElements->numElements -1)) { // Special case of Last element in slider, which only has one // neighbor, measCnt[x-1] or measCnt[numElements-1] if(measCnt[index-1]) { position -= (measCnt[index-1]*(groupOfElements->points/groupOfElements->numElements))/100; } else { position = groupOfElements->points; position -= (measCnt[index]*(groupOfElements->points/groupOfElements->numElements)/2)/100; } } else { position += (measCnt[index+1]*(groupOfElements->points/groupOfElements->numElements))/100; position -= (measCnt[index-1]*(groupOfElements->points/groupOfElements->numElements))/100; } if((position > groupOfElements->points) || (position < 0)) { position = ILLEGAL_SLIDER_WHEEL_POSITION; } } else { position = ILLEGAL_SLIDER_WHEEL_POSITION; } } #ifndef RAM_FOR_FLASH free(measCnt); #endif return position; } #endif #ifdef WHEEL /***************************************************************************//** * @brief Determine the position on a wheel * @param groupOfElements Pointer to wheel * @return result position on wheel or illegal value if no touch ******************************************************************************/ uint16_t TI_CAPT_Wheel(const struct Sensor* groupOfElements) { uint8_t index; int16_t position; // allocate memory for measurement #ifndef RAM_FOR_FLASH uint16_t *measCnt; measCnt = (uint16_t *)malloc(groupOfElements->numElements * sizeof(uint16_t)); if(measCnt ==0) { while(1); } #endif position = ILLEGAL_SLIDER_WHEEL_POSITION; //make measurement TI_CAPT_Custom(groupOfElements, measCnt); // Translate the EVNT flag from an element level EVNT to a sensor level EVNT. // The sensor must read at least 75% cumulative response before indicating a // touch. if(ctsStatusReg & EVNT) { index = Dominant_Element(groupOfElements, &measCnt[0]); // The index represents the element within the array with the highest return. // if(index == 0) { // Special case of 1st element in slider, add 1st, last, and 2nd position = measCnt[0] + measCnt[groupOfElements->numElements -1] + measCnt[1]; } else if(index == (groupOfElements->numElements -1)) { // Special case of Last element in slider, add last, 1st, and 2nd to last position = measCnt[index] + measCnt[0] + measCnt[index-1]; } else { position = measCnt[index] + measCnt[index+1] + measCnt[index-1]; } if(position > groupOfElements->sensorThreshold) { //index = Dominant_Element(groupOfElements, &measCnt[0]); // The index represents the element within the array with the highest return. // position = index*(groupOfElements->points/groupOfElements->numElements); position += (groupOfElements->points/groupOfElements->numElements)/2; if(index == 0) { // Special case of 1st element in slider, which only has one neighbor, measCnt[1] // measCnt is limited to maxResponse within dominantElement function position += (measCnt[1]*(groupOfElements->points/groupOfElements->numElements))/100; position -= (measCnt[groupOfElements->numElements -1]*(groupOfElements->points/groupOfElements->numElements))/100; if(position < 0) { position = position + (int16_t)groupOfElements->points; } } else if(index == (groupOfElements->numElements -1)) { // Special case of Last element in slider, which only has one neighbor, measCnt[x-1] or measCnt[numElements-1] // measCnt is limited to maxResponse within dominantElement function position += (measCnt[0]*(groupOfElements->points/groupOfElements->numElements))/100; position -= (measCnt[index-1]*(groupOfElements->points/groupOfElements->numElements))/100; if(position > (groupOfElements->points -1)) { position = position - (int16_t)groupOfElements->points; } } else { position += (measCnt[index+1]*(groupOfElements->points/groupOfElements->numElements))/100; position -= (measCnt[index-1]*(groupOfElements->points/groupOfElements->numElements))/100; } if((position > groupOfElements->points) || position < 0) { position = ILLEGAL_SLIDER_WHEEL_POSITION; } } else { position = ILLEGAL_SLIDER_WHEEL_POSITION; } } #ifndef RAM_FOR_FLASH free(measCnt); #endif return position; } #endif /***************************************************************************//** * @} ******************************************************************************/ /***************************************************************************//** * @defgroup CTS_support * @ingroup CTS_API ******************************************************************************/ /***************************************************************************//** * @ingroup CTS_support * @brief Determine which element within a sensor has the largest response * * This function compares and normalizes the change in capacitance to * determine which element has the dominant response. The deltaCnt * values for each element that exceed the threshold are also * converted from a 'raw' measurement to a percentage of the maximum * response. * @param groupOfElements Pointer to buttons to be scanned * @param deltaCnt Address to where the measurements are to be written * @return result index to the element which is dominant ******************************************************************************/ uint8_t Dominant_Element(const struct Sensor* groupOfElements, uint16_t* deltaCnt) { uint8_t i; uint16_t percentDelta=0; uint8_t dominantElement=0; for(i=0;inumElements;i++) { if(deltaCnt[i]>=(groupOfElements->arrayPtr[i])->threshold) { if(deltaCnt[i] > ((groupOfElements->arrayPtr[i])->maxResponse)) { deltaCnt[i] = (groupOfElements->arrayPtr[i])->maxResponse; // limit response to the maximum } // (maxResponse - threshold) cannot exceed 655 // 100*(delta - threshold) / (maxResponse - threshold) deltaCnt[i] = (100*(deltaCnt[i]-(groupOfElements->arrayPtr[i])->threshold))/((groupOfElements->arrayPtr[i])->maxResponse - (groupOfElements->arrayPtr[i])->threshold); if(deltaCnt[i] > percentDelta) { //update percentDelta percentDelta = deltaCnt[i]; dominantElement = i; } } else { deltaCnt[i] = 0; } } // end for loop return dominantElement; } #endif