capsense.c

Go to the documentation of this file.
00001 /**************************************************************************/
00036 /* EM header files */
00037 #include "em_device.h"
00038 
00039 /* Drivers */
00040 #include "capsense.h"
00041 #include "em_emu.h"
00042 #include "em_acmp.h"
00043 #include "capsenseconfig.h"
00044 
00046 static volatile uint8_t currentChannel;
00047 
00048 /**************************************************************************/
00052 static const bool channelsInUse[ACMP_CHANNELS] = CAPSENSE_CH_IN_USE;
00053 
00054 /**************************************************************************/
00058 static volatile uint32_t channelValues[ACMP_CHANNELS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
00059 
00060 /**************************************************************************/
00064 static volatile uint32_t channelMaxValues[ACMP_CHANNELS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
00065 
00066 
00067 /**************************************************************************/
00074 void TIMER0_IRQHandler(void)
00075 {
00076   uint32_t count;
00077 
00078   /* Stop timers */
00079   TIMER0->CMD = TIMER_CMD_STOP;
00080   TIMER1->CMD = TIMER_CMD_STOP;
00081 
00082   /* Clear interrupt flag */
00083   TIMER0->IFC = TIMER_IFC_OF;
00084 
00085   /* Read out value of TIMER1 */
00086   count = TIMER1->CNT;
00087 
00088   /* Store value in channelValues */
00089   channelValues[currentChannel] = count;
00090 
00091   /* Update channelMaxValues */
00092   if (count > channelMaxValues[currentChannel])
00093     channelMaxValues[currentChannel] = count;
00094 }
00095 
00096 /**************************************************************************/
00101 uint32_t CAPSENSE_getVal(uint8_t channel)
00102 {
00103   return channelValues[channel];
00104 }
00105 
00106 /**************************************************************************/
00111 uint8_t  CAPSENSE_getSegmentChannel(uint8_t capSegment)
00112 {
00113   uint8_t channel;
00114 
00115   switch (capSegment)
00116   {
00117   case(0):
00118     channel = SLIDER_PART0_CHANNEL;
00119     break;
00120   case(1):
00121     channel = SLIDER_PART1_CHANNEL;
00122     break;
00123   case(2):
00124     channel = SLIDER_PART2_CHANNEL;
00125     break;
00126   default:
00127     channel = SLIDER_PART3_CHANNEL;
00128     break;
00129   }
00130   return channel;
00131 
00132 }
00133 
00134 /**************************************************************************/
00138 uint8_t  CAPSENSE_getButtonChannel(void)
00139 {
00140   uint8_t channel = BUTTON_CHANNEL;
00141   return channel;
00142 
00143 }
00144 
00145 /**************************************************************************/
00150 uint32_t CAPSENSE_getNormalizedVal(uint8_t channel)
00151 {
00152   uint32_t max = channelMaxValues[channel];
00153   return (channelValues[channel] << 8) / max;
00154 }
00155 
00156 /**************************************************************************/
00162 bool CAPSENSE_getPressed(uint8_t channel)
00163 {
00164   uint32_t treshold;
00165   /* Treshold is set to 12.5% below the maximum value */
00166   /* This calculation is performed in two steps because channelMaxValues is
00167    * volatile. */
00168   treshold  = channelMaxValues[channel];
00169   treshold -= channelMaxValues[channel] >> 3;
00170 
00171   if (channelValues[channel] < treshold)
00172   {
00173     return true;
00174   }
00175   return false;
00176 }
00177 
00178 /**************************************************************************/
00183 int32_t CAPSENSE_getSliderPosition(void)
00184 {
00185   int      i;
00186   int      minPos = -1;
00187   uint32_t minVal = 224; /* 0.875 * 256 */
00188   /* Values used for interpolation. There is two more which represents the edges.
00189    * This makes the interpolation code a bit cleaner as we do not have to make special
00190    * cases for handling them */
00191   uint32_t interpol[6] = { 255, 255, 255, 255, 255, 255 };
00192 
00193   /* The calculated slider position. */
00194   int position;
00195 
00196   /* Iterate through the 4 slider bars and calculate the current value divided by
00197    * the maximum value multiplied by 256.
00198    * Note that there is an offset of 1 between channelValues and interpol.
00199    * This is done to make interpolation easier.
00200    */
00201   for (i = 1; i < 5; i++)
00202   {
00203     /* interpol[i] will be in the range 0-256 depending on channelMax */
00204     interpol[i]  = channelValues[i - 1] << 8;
00205     interpol[i] /= channelMaxValues[i - 1];
00206     /* Find the minimum value and position */
00207     if (interpol[i] < minVal)
00208     {
00209       minVal = interpol[i];
00210       minPos = i;
00211     }
00212   }
00213   /* Check if the slider has not been touched */
00214   if (minPos == -1)
00215     return -1;
00216 
00217   /* Start position. Shift by 4 to get additional resolution. */
00218   /* Because of the interpol trick earlier we have to substract one to offset that effect */
00219   position = (minPos - 1) << 4;
00220 
00221   /* Interpolate with pad to the left */
00222   position -= ((256 - interpol[minPos - 1]) << 3)
00223               / (256 - interpol[minPos]);
00224 
00225   /* Interpolate with pad to the right */
00226   position += ((256 - interpol[minPos + 1]) << 3)
00227               / (256 - interpol[minPos]);
00228 
00229   return position;
00230 }
00231 
00232 /**************************************************************************/
00237 void CAPSENSE_Sense(void)
00238 {
00239   /* Use the default STK capacative sensing setup and enable it */
00240   ACMP_Enable(ACMP_CAPSENSE);
00241 
00242   uint8_t ch;
00243   /* Iterate trough all channels */
00244   for (currentChannel = 0; currentChannel < ACMP_CHANNELS; currentChannel++)
00245   {
00246     /* If this channel is not in use, skip to the next one */
00247     if (!channelsInUse[currentChannel])
00248     {
00249       continue;
00250     }
00251 
00252     /* Set up this channel in the ACMP. */
00253     ch = currentChannel;
00254     ACMP_CapsenseChannelSet(ACMP_CAPSENSE, (ACMP_Channel_TypeDef) ch);
00255 
00256     /* Reset timers */
00257     TIMER0->CNT = 0;
00258     TIMER1->CNT = 0;
00259 
00260     /* Start timers */
00261     TIMER0->CMD = TIMER_CMD_START;
00262     TIMER1->CMD = TIMER_CMD_START;
00263 
00264     /* Wait for measurement to complete */
00265     EMU_EnterEM1();
00266   }
00267 
00268   /* Disable ACMP while not sensing to reduce power consumption */
00269   ACMP_Disable(ACMP_CAPSENSE);
00270 }
00271 
00272 /**************************************************************************/
00280 void CAPSENSE_Init(void)
00281 {
00282   /* Use the default STK capacative sensing setup */
00283   ACMP_CapsenseInit_TypeDef capsenseInit = ACMP_CAPSENSE_INIT_DEFAULT;
00284 
00285   /* Enable TIMER0, TIMER1, ACMP_CAPSENSE and PRS clock */
00286   CMU->HFPERCLKDIV |= CMU_HFPERCLKDIV_HFPERCLKEN;
00287   CMU->HFPERCLKEN0 |= CMU_HFPERCLKEN0_TIMER0
00288                       | CMU_HFPERCLKEN0_TIMER1
00289                       | ACMP_CAPSENSE_CLKEN
00290                       | CMU_HFPERCLKEN0_PRS;
00291 
00292   /* Initialize TIMER0 - Prescaler 2^9, top value 10, interrupt on overflow */
00293   TIMER0->CTRL = TIMER_CTRL_PRESC_DIV512;
00294   TIMER0->TOP  = 10;
00295   TIMER0->IEN  = TIMER_IEN_OF;
00296   TIMER0->CNT  = 0;
00297 
00298   /* Initialize TIMER1 - Prescaler 2^10, clock source CC1, top value 0xFFFF */
00299   TIMER1->CTRL = TIMER_CTRL_PRESC_DIV1024 | TIMER_CTRL_CLKSEL_CC1;
00300   TIMER1->TOP  = 0xFFFF;
00301 
00302   /*Set up TIMER1 CC1 to trigger on PRS channel 0 */
00303   TIMER1->CC[1].CTRL = TIMER_CC_CTRL_MODE_INPUTCAPTURE /* Input capture      */
00304                        | TIMER_CC_CTRL_PRSSEL_PRSCH0   /* PRS channel 0      */
00305                        | TIMER_CC_CTRL_INSEL_PRS       /* PRS input selected */
00306                        | TIMER_CC_CTRL_ICEVCTRL_RISING /* PRS on rising edge */
00307                        | TIMER_CC_CTRL_ICEDGE_BOTH;    /* PRS on rising edge */
00308 
00309   /*Set up PRS channel 0 to trigger on ACMP1 output*/
00310   PRS->CH[0].CTRL = PRS_CH_CTRL_EDSEL_POSEDGE      /* Posedge triggers action */
00311                     | PRS_CH_CTRL_SOURCESEL_ACMP_CAPSENSE      /* PRS source */
00312                     | PRS_CH_CTRL_SIGSEL_ACMPOUT_CAMPSENSE;    /* PRS source */
00313 
00314   /* Set up ACMP1 in capsense mode */
00315   ACMP_CapsenseInit(ACMP_CAPSENSE, &capsenseInit);
00316 
00317   /* Enable TIMER0 interrupt */
00318   NVIC_EnableIRQ(TIMER0_IRQn);
00319 }