capsense.c

Go to the documentation of this file.
00001 /**************************************************************************/
00019 /* EM header files */
00020 #include "em_device.h"
00021 
00022 /* Drivers */
00023 #include "em_emu.h"
00024 #include "em_acmp.h"
00025 #include "capsense.h"
00026 
00030 static volatile uint8_t currentChannel;
00032 static volatile bool measurementComplete;
00033 
00034 /**************************************************************************/
00038 static const bool channelsInUse[ACMP_CHANNELS] = CAPSENSE_CH_IN_USE;
00039 
00040 /**************************************************************************/
00044 static volatile uint32_t channelValues[ACMP_CHANNELS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
00045 
00046 /**************************************************************************/
00050 static volatile uint32_t channelMaxValues[ACMP_CHANNELS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
00051 
00054 /**************************************************************************/
00061 void TIMER0_IRQHandler(void)
00062 {
00063   uint32_t count;
00064 
00065   /* Stop timers */
00066   TIMER0->CMD = TIMER_CMD_STOP;
00067   TIMER1->CMD = TIMER_CMD_STOP;
00068 
00069   /* Clear interrupt flag */
00070   TIMER0->IFC = TIMER_IFC_OF;
00071 
00072   /* Read out value of TIMER1 */
00073   count = TIMER1->CNT;
00074 
00075   /* Store value in channelValues */
00076   channelValues[currentChannel] = count;
00077 
00078   /* Update channelMaxValues */
00079   if (count > channelMaxValues[currentChannel])
00080     channelMaxValues[currentChannel] = count;
00081 
00082   measurementComplete = true;
00083 }
00084 
00085 /**************************************************************************/
00090 uint32_t CAPSENSE_getVal(uint8_t channel)
00091 {
00092   return channelValues[channel];
00093 }
00094 
00095 /**************************************************************************/
00100 uint32_t CAPSENSE_getNormalizedVal(uint8_t channel)
00101 {
00102   uint32_t max = channelMaxValues[channel];
00103   return (channelValues[channel] << 8) / max;
00104 }
00105 
00106 /**************************************************************************/
00112 bool CAPSENSE_getPressed(uint8_t channel)
00113 {
00114   uint32_t treshold;
00115   /* Treshold is set to 12.5% below the maximum value */
00116   /* This calculation is performed in two steps because channelMaxValues is
00117    * volatile. */
00118   treshold  = channelMaxValues[channel];
00119   treshold -= channelMaxValues[channel] >> 3;
00120 
00121   if (channelValues[channel] < treshold)
00122   {
00123     return true;
00124   }
00125   return false;
00126 }
00127 
00128 /**************************************************************************/
00133 int32_t CAPSENSE_getSliderPosition(void)
00134 {
00135   int      i;
00136   int      minPos = -1;
00137   uint32_t minVal = 224; /* 0.875 * 256 */
00138   /* Values used for interpolation. There is two more which represents the edges.
00139    * This makes the interpolation code a bit cleaner as we do not have to make special
00140    * cases for handling them */
00141   uint32_t interpol[6] = { 255, 255, 255, 255, 255, 255 };
00142 
00143   /* The calculated slider position. */
00144   int position;
00145 
00146   /* Iterate through the 4 slider bars and calculate the current value divided by
00147    * the maximum value multiplied by 256.
00148    * Note that there is an offset of 1 between channelValues and interpol.
00149    * This is done to make interpolation easier.
00150    */
00151   for (i = 1; i < 5; i++)
00152   {
00153     /* interpol[i] will be in the range 0-256 depending on channelMax */
00154     interpol[i]  = channelValues[i - 1] << 8;
00155     interpol[i] /= channelMaxValues[i - 1];
00156     /* Find the minimum value and position */
00157     if (interpol[i] < minVal)
00158     {
00159       minVal = interpol[i];
00160       minPos = i;
00161     }
00162   }
00163   /* Check if the slider has not been touched */
00164   if (minPos == -1)
00165     return -1;
00166 
00167   /* Start position. Shift by 4 to get additional resolution. */
00168   /* Because of the interpol trick earlier we have to substract one to offset that effect */
00169   position = (minPos - 1) << 4;
00170 
00171   /* Interpolate with pad to the left */
00172   position -= ((256 - interpol[minPos - 1]) << 3)
00173               / (256 - interpol[minPos]);
00174 
00175   /* Interpolate with pad to the right */
00176   position += ((256 - interpol[minPos + 1]) << 3)
00177               / (256 - interpol[minPos]);
00178 
00179   return position;
00180 }
00181 
00182 /**************************************************************************/
00187 void CAPSENSE_Sense(void)
00188 {
00189   /* Use the default STK capacative sensing setup and enable it */
00190   ACMP_Enable(ACMP_CAPSENSE);
00191 
00192   uint8_t ch;
00193   /* Iterate trough all channels */
00194   for (currentChannel = 0; currentChannel < ACMP_CHANNELS; currentChannel++)
00195   {
00196     /* If this channel is not in use, skip to the next one */
00197     if (!channelsInUse[currentChannel])
00198     {
00199       continue;
00200     }
00201 
00202     /* Set up this channel in the ACMP. */
00203     ch = currentChannel;
00204     ACMP_CapsenseChannelSet(ACMP_CAPSENSE, (ACMP_Channel_TypeDef) ch);
00205 
00206     /* Reset timers */
00207     TIMER0->CNT = 0;
00208     TIMER1->CNT = 0;
00209 
00210     measurementComplete = false;
00211 
00212     /* Start timers */
00213     TIMER0->CMD = TIMER_CMD_START;
00214     TIMER1->CMD = TIMER_CMD_START;
00215 
00216     /* Wait for measurement to complete */
00217     while ( measurementComplete == false )
00218     {
00219       EMU_EnterEM1();
00220     }
00221   }
00222 
00223   /* Disable ACMP while not sensing to reduce power consumption */
00224   ACMP_Disable(ACMP_CAPSENSE);
00225 }
00226 
00227 /**************************************************************************/
00235 void CAPSENSE_Init(void)
00236 {
00237   /* Use the default STK capacative sensing setup */
00238   ACMP_CapsenseInit_TypeDef capsenseInit = ACMP_CAPSENSE_INIT_DEFAULT;
00239 
00240   /* Enable TIMER0, TIMER1, ACMP_CAPSENSE and PRS clock */
00241   CMU->HFPERCLKDIV |= CMU_HFPERCLKDIV_HFPERCLKEN;
00242   CMU->HFPERCLKEN0 |= CMU_HFPERCLKEN0_TIMER0
00243                       | CMU_HFPERCLKEN0_TIMER1
00244                       | ACMP_CAPSENSE_CLKEN
00245                       | CMU_HFPERCLKEN0_PRS;
00246 
00247   /* Initialize TIMER0 - Prescaler 2^9, top value 10, interrupt on overflow */
00248   TIMER0->CTRL = TIMER_CTRL_PRESC_DIV512;
00249   TIMER0->TOP  = 10;
00250   TIMER0->IEN  = TIMER_IEN_OF;
00251   TIMER0->CNT  = 0;
00252 
00253   /* Initialize TIMER1 - Prescaler 2^10, clock source CC1, top value 0xFFFF */
00254   TIMER1->CTRL = TIMER_CTRL_PRESC_DIV1024 | TIMER_CTRL_CLKSEL_CC1;
00255   TIMER1->TOP  = 0xFFFF;
00256 
00257   /*Set up TIMER1 CC1 to trigger on PRS channel 0 */
00258   TIMER1->CC[1].CTRL = TIMER_CC_CTRL_MODE_INPUTCAPTURE /* Input capture      */
00259                        | TIMER_CC_CTRL_PRSSEL_PRSCH0   /* PRS channel 0      */
00260                        | TIMER_CC_CTRL_INSEL_PRS       /* PRS input selected */
00261                        | TIMER_CC_CTRL_ICEVCTRL_RISING /* PRS on rising edge */
00262                        | TIMER_CC_CTRL_ICEDGE_BOTH;    /* PRS on rising edge */
00263 
00264   /*Set up PRS channel 0 to trigger on ACMP1 output*/
00265   PRS->CH[0].CTRL = PRS_CH_CTRL_EDSEL_POSEDGE      /* Posedge triggers action */
00266                     | PRS_CH_CTRL_SOURCESEL_ACMP_CAPSENSE      /* PRS source */
00267                     | PRS_CH_CTRL_SIGSEL_ACMPOUT_CAMPSENSE;    /* PRS source */
00268 
00269   /* Set up ACMP1 in capsense mode */
00270   ACMP_CapsenseInit(ACMP_CAPSENSE, &capsenseInit);
00271 
00272   /* Enable TIMER0 interrupt */
00273   NVIC_EnableIRQ(TIMER0_IRQn);
00274 }