Touch controlled led light on LPCXPRESSO845-BRK

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Touch controlled led light on LPCXPRESSO845-BRK

Touch controlled led light on LPCXPRESSO845-BRK

Description

This tutorial shows how to create a touch-controlled LED light strip to place it over the kitchen counter, for example. For this tutorial, the LED strip is abstracted by the onboard LED instead of using MOSFET (with a proper MOSFET driver circuit) that would control a higher voltage LED strip. The touch control must be encoded due to the presence of only a single touch pad for multiple actions.

Used components

  1. LPCXpresso845-BRK breakout board
  2. SDK 2.8.0 for LPC845
  3. MCUXpresso 11.3

Used peripherals

CAPT

Capacitive touch (CAPT) control allows you to place the button under the product cover for keeping the design of the product consistent. You can cover touch pads with dielectric materials and hide them inside the product.

The CAPT peripheral in YH mode works by counting cycles until the charge is transferred from the small charging capacitor to the larger measuring one.  A human finger or capacitive stylus causes partial grounding of the charge that causes an increase or decrease of the cycles needed to fully charge the measuring capacitor.

CTIMER

A counter triggers a toggle of the output pin’s logic level that creates a PWM signal on that pin. The peripheral increases the counter with each peripheral clock cycle and when the counter matches the required number the output logic level is toggled.

MRT

A counter triggers interrupts after a defined number of peripheral clock cycles.  This peripheral can be programmed to create an interrupt repetitively or only after a specified number of cycles.

Ana_0-1631883681486.png

Figure 1 Block diagram

Touch control

Description

As mentioned previously, the board has only a single touch pad, and there are 3 different actions in this tutorial that must be controlled by it.

  1. Turn on/off the PWM output by tapping and removing the finger/stylus from the pad.
  2. Increase the PWM duty cycle by keeping the finger/stylus over the touch pad for a long time, keep touched for more increase.
  3. Decrease the PWM duty cycle by taping the touch pad, removing the finger, and taping again with keeping the finger/stylus over the touch pad, keep touched for more decrease.

The touch pad is periodically measured for touch or no touch events. Such events can store the state of the touch pad in a ring buffer. The ring buffer represents the history of the touches over a short time and can be analyzed for a long uninterrupted touch or double tap.

Ana_1-1631883681540.png

Figure 2 Encoding state diagram

Steps

IDE project

Create a project and select the lpc845 breakout from your installed SDK version.Ana_2-1631883681549.png

Figure 3 Creating a project in MCUXpresso IDE

In the project setup wizard, click the Drivers tab in the Components section in the lower half of the wizard. Select CAPT, CTIMER, and MRT in this table.

Ana_3-1631883681624.png

Figure 4 Adding required peripheral drivers to the project

To make active the newly created project (if it is not active yet), select it and open the Pins tool either in the context menu or by clicking the Config tools button.

Ana_4-1631883681630.png

Figure 5 The Config tools button

Pin routing

Open the Pins tool and create a functional group for our application. Set it to be called by the default initialization function and set other functional groups not to be called by the initialization function.

Ana_5-1631883681653.png

Figure 6  Properties of Pins tool functional groups

Ana_6-1631883681683.png

Figure 7 Routing details

Steps:

  1. Route CTIMER channel 0 to the pin that is connected to the onboard LED.
  2. Route X0 signal of CAPT to one side of the touch pad.
  3. Route signals YL and YH to the other side of the touch pad.
  4. Enable the invert function of the CTIMER channel 0 signal (see notes).
  5. Set the mode of all configured CAPT and CTIMER pins to Inactive (see notes).

NOTE:

The LED is connected to the MCU in a way that 90 % of the duty cycle would create only 10 % of the on-time.  To achieve the expected behavior, invert the signal. The CAPT peripheral requires the mode of the pins to be inactive to work properly. Activating a pull-up, pull-down resistor, or any other integrated circuit causes false measurements, and touch control does not work or causes false touch events.

Clock configuration

Open the functional group's dialog and select BOARD_BootClockFRO30M as the initialized functional group.

Ana_7-1631883681706.png

Figure 8 Properties of Clocks tool functional groups

Steps:

  1. Set SYSAHBCLKDIV to /3
  2. Activate the output of CAPTCLKSEL and choose fro as the input clock
Ana_8-1631883681711.png

Figure 9 System clock divider setting

Ana_9-1631883681719.png

Figure 10 Selection of clock source for CAPT peripheral

NOTE:

The system clock is slowed down to 10 MHz because it is used for the MRT peripheral as a source and the period with 30 MHz is too short for the purposes of this tutorial.

Peripheral configuration

MRT

This peripheral is used to delay the execution of events.

Steps:

  1. Add the MRT component (use the version that is part of your SDK version).
  2. Add 1 channel.
  3. Set Channel ID to Channel_wait.
  4. Set Time interval input to 400 ms.
  5. Set Start timer to false by unchecking the checkbox.
  6. Enable Interrupt request of the channel by checking the checkbox.
  7. Enable Interrupt vector by checking the checkbox.
  8. Copy the handler template by clicking the button.
  9. Paste the handler template to the application code.
Ana_10-1631883681752.png

Figure 11 Configuration of the MRT peripheral

NOTE:

For this application, the delay must be 400 ms. You can achieve it only by using a 10 MHz clock or by creating a counter in memory that is increased every time the interrupt handler is executed. Only when the counter has the required value, the delayed event executes, and the counter is reset. You need a counter because the MRT register, which has only 24 bits and overflows before reaching the number of ticks calculated from 400 ms.

CTIMER

For the PWM output, configure the CTIMER peripheral in the following way.

Ana_11-1631883681813.png

Figure 12 Configuration of the CTIMER peripheral

Steps:

  1. Add the CTIMER component (use the version that is part of your SDK version).
  2. Select the PWM mode of the CTIMER peripheral.
  3. Set the prescaler to 1 (see note for explanation).
  4. Check the checkbox Start timer in the initialization code if it is not checked already.
  5. Set the PWM frequency to 100 Hz (see note for explanation).
  6. Add 1 channel if it is not present already.
  7. Set the PWM duty of the channel to half of the value that is displayed below the PWM frequency setting notes:

NOTE:

The prescaler set to 1 (no prescaling) allows the most precise duty cycle (for example, if the resolution must be more than one percent).  The frequency of the output must be in the range between not noticing the flickering of the LED and potential unintentional overheating of the MOSFET that could be connected to the output due to high frequency. The frequency of 100 Hz is selected because unless the board is moving the LED looks dim and does not flicker, and the potential MOSFET does not overheat. The duty cycle is set in ticks, and at runtime is computed as a percentage.

CAPT

Steps:

  1. Create an instance of the CAPT component (use the version that is part of your SDK version).
  2. Set the Function clock divider setting to 15 to obtain a 2 MHz clock.
  3. Enable pin 0 by checking the checkbox in the X pins selection table.
  4. Select the value User calibration function (default name) in the setting Default function name.
  5. Copy the name and create the function in the application code.
  6. Select Counter higher than threshold could in the setting Touch activation.
  7. Set Time out count to the highest available value.
  8. Select Don’t wait for Measurement delay.
  9. Set Reset delay to the highest available value.
  10. Set Poll delay to 100 ms.
  11. Check the checkbox Initialize poll mode.
  12. Select Continuous in the setting Polling mode.
Ana_12-1631883681861.png

Figure 13 Configuration of the CAPT peripheral

NOTE:

The CAPT peripheral requires the input clock to be maximally 4 MHz. Therefore, a frequency of 2 MHz was chosen as it was easily achievable and easy to calculate the resulting period.

On the breakout board, only signal 0 is routed to the touch pad, and other signals can be routed to external connectors on the board.

For the CAPT to function properly, calibrate touch and no touch events to obtain the number of cycles required to transfer the charge. Enable the usage of the calibration method and either use the default name or provide your name of the function. The other method is to use a constant that was measured during development. However, keep in mind that the measured value might differ when the board is inside the final product and covered by a layer of material.

A touch event can happen either when the counter is higher or lower than the threshold. The selection depends on the final product design. A value higher than the threshold option was used in this tutorial because the finder/stylus grounds a part of the charge that is transferred and causes a higher cycles count required to fully charge the measured capacitor.

The timeout count is a counter of the number of ticks when a timeout interrupt is created. It is not used in this example, so set the highest value to avoid timeout instead of touch or no touch events.

The measurement delay setting represents the number of ticks until the measuring capacitor is sampled. When the YH method is used, it must be measured immediately.

The reset delay is a wait after a touch or no touch event and waiting discharges the capacitors before the next measurement cycle. The poll delay is a delay between repetitive measurement cycles and should be high since the physical movement of the finger/stylus is slow relative to the speed of the MCU and unnecessary measurements can be avoided.

Update code

The changes you made must be written on the disk before using them in the application code. Open the Update code dialog and confirm the changes.

Ana_13-1631883681872.png

Figure 14 Update code action on the main panel

Ana_14-1631883681902.png

Figure 15 Update code dialog

Application code

For the convenience of working with the code, create the following defines:

#define WAIT_MS 400U // How long to wait before making decisions

#define POLL_WAIT_MS 100U // How long it takes before next CAPT measurement

#define TOUCH_DATA_SIZE 32U // Size of ring buffer

Create the ring buffer, index of the current position, and functions that fill and analyze it. The ring buffer must hold 2-3 seconds of data and since the poll delay of the CAPT is 100 ms then 32 is sufficient.

Functions for analysis scan the ring buffer and let the application know whether a double tap was performed, for how many measurements the touch pad was held, and how many measurements ago the touch pad was touched. Create functions for storing and clearing data in the ring buffer.

unsigned int g_touchDataIndex = 0;

bool g_touchData[TOUCH_DATA_SIZE] = {false};

/**
 * Checks whether double tap was performed
 */
bool wasDoubleTap() {
       bool untouched = false;
       for (int i = 0; i < TOUCH_DATA_SIZE; i++) {
              if (!g_touchData[(g_touchDataIndex - i) % TOUCH_DATA_SIZE]) {
                     untouched = true;
              } else if (untouched) {
                     return true;
              }
       }
       return false;
}

/**
 * Returns number of data writes that represent continuous touch.
 */
int getContinuousTouchCount() {
       int count = 0;
       for (int i = 0; i < TOUCH_DATA_SIZE; i++) {
              if (g_touchData[(g_touchDataIndex - i) % TOUCH_DATA_SIZE]) {
                     count++;
              } else {
                     return count;
              }
       }
       return -1;
}

/**
 * Returns number that represents count of data writes until tap is found in history.
 * Returns -1 in case nothing is found.
 */
int getPreviousTouch() {
       for (int i = 0; i < TOUCH_DATA_SIZE; i++) {
              if (g_touchData[(g_touchDataIndex - i) % TOUCH_DATA_SIZE]) {
                     return i;
              }
       }
       return -1;
}

/**
 * Clears touch data in ring buffer
 */
void clearTouchData() {
       for (int i = 0; i < TOUCH_DATA_SIZE; i++) {
              g_touchData[(g_touchDataIndex - i) % TOUCH_DATA_SIZE] = false;
       }
}

/**
 * Stores touch data in ring buffer
 */
void storeTouchData(bool data) {
       ++g_touchDataIndex;
       g_touchDataIndex = g_touchDataIndex % TOUCH_DATA_SIZE;
       g_touchData[g_touchDataIndex] = data;
}

Create storage for the current state of the PWM signal and the state machine. Create a function to change the PWM output of the CTIMER channel.  The function must check the wanted duty cycle to be in a reasonable range.

int currentPWMDuty = 50;
bool PWMOutputEnabled = true;

enum
STATE {
       IDLE = 0,
       WAITING_FOR_TIMER = 100,
       TOGGLE = 200,
       INCREASE = 300,
       DECREASE = 400
};
enum STATE state = IDLE;
/**
 * Sets PWM output according to currently wanted duty cycle if output is enabled
 */
void setPWMOutput() {
       if (currentPWMDuty < 5) {
              currentPWMDuty = 5;
       }
       if (currentPWMDuty > 100) {
              currentPWMDuty = 100;
       }

       int ticks = PWMOutputEnabled ? ((CTIMER0_PWM_PERIOD / 100) * currentPWMDuty) : 0;

       CTIMER_SetupPwmPeriod(CTIMER0_PERIPHERAL, CTIMER0_PWM_0_CHANNEL, CTIMER0_PWM_PERIOD, ticks, false);

}

Calibration of the CAPT peripheral must be done in the CAPT_doCalibration function as it was set in the Peripherals tool.

/*
 * @brief   Calibration of the CAPT peripheral. Returns number of charge cycles in not activated state
 */
uint32_t CAPT_doCalibration(CAPT_Type *base) {
       // Here can be placed code that uses one time polling multiple times and the results would be averaged
       // or a constant returned
       return 70;
}

When a 400 ms delay passes,  the MRT peripheral runs this interrupt and after clearing the status flag the logic follows. After obtaining information on whether the touch pad is touched and for how many measurement cycles it is not held, the decision of what state follows is made.

  1. When the touch pad is not touched and it is more than or equal to 4 (400 ms/100 ms) measurement cycles, the PWM output will be disabled. If it is touched, the decision whether the INCREASE or DECREASE state should follow is made.
  2. To increase the PWM duty cycle, the touch pad must be currently held touched, it must be held for more than or equal to 4 (400 ms/100 ms) measurement cycles and the PWM output must be enabled.
  3. To decrease the PWM duty cycle, the touch pad must be held,  double-tap (not holding for at least one measurement cycle) is done and output is enabled.
  4. In all other cases, the state falls back to IDLE since it is not a valid encoding of the command.
/* MRT0_IRQn interrupt handler */
void MRT0_IRQHANDLER(void) {
  /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
     Store immediate overlapping exception return operation might vector to incorrect
interrupt. */
  #if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
  #endif
       uint32_t mrtWaitStatus = MRT_GetStatusFlags(MRT0, MRT0_CHANNEL_WAIT);
       MRT_ClearStatusFlags(MRT0, MRT0_CHANNEL_WAIT, mrtWaitStatus);
    if (mrtWaitStatus != 0) {
              int previousTouch = getPreviousTouch();
              bool currentlyTouched = g_touchData[g_touchDataIndex];
              if (!currentlyTouched && previousTouch >= (WAIT_MS / POLL_WAIT_MS) - 1)
{ // Tap, currently not pressed and no taps after the initial one
                     PWMOutputEnabled = !PWMOutputEnabled;
                     setPWMOutput();
                     clearTouchData();
                     state = IDLE;
          } else if (currentlyTouched && getContinuousTouchCount() >= (WAIT_MS /
POLL_WAIT_MS) && PWMOutputEnabled) { // Tap, currently pressed and touch pad held
until now
                     state = INCREASE;
       } else if (currentlyTouched && wasDoubleTap() && PWMOutputEnabled) { // Tap,
currently pressed and double tap was performed
                          state = DECREASE;
          } else {
                          state = IDLE; // Error -> reset
          }
    }
}

The interrupt from CAPT is performed every 100 ms and the data with interrupt status must be read and status cleared. When the touch pad is touched, the current state is checked and one of the following actions happens:

  1. When the current state is IDLE, a 400 ms delay is started and the state changes to WAITING_FOR_TIMER.
  2. When the INCREASE state is active, the PWM duty cycle is increased every 100 ms for as long as the touch pad is touched.
  3. The DECREASE state decreases the PWM duty cycle every 100 ms for as long as the touch pad is touched.

When the touch pad is not touched, the INCREASE or DECREASE state is changed to IDLE state and ring buffer is cleared. In other states nothing happens.

/* CMP_CAPT_IRQn interrupt handler */
void CAPT_IRQHANDLER(void) {
  /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
     Store immediate overlapping exception return operation might vector to incorrect
interrupt. */
  #if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
  #endif
    uint32_t mask;
    capt_touch_data_t g_data;
       CAPT_GetTouchData(CAPT, &g_data);
       mask = CAPT_GetInterruptStatusFlags(CAPT);
       CAPT_ClearInterruptStatusFlags(CAPT, mask);
       if (mask & kCAPT_InterruptOfYesTouchEnable && g_data.yesTouch)
       {
              PRINTF("Touch\n");
              storeTouchData(true);

              switch(state) {
                     case IDLE: {
                           MRT_StartTimer(MRT0, MRT0_CHANNEL_WAIT,
MRT0_CHANNEL_WAIT_TICKS);
                           state = WAITING_FOR_TIMER;
                           break;
                     }
                     case INCREASE: {
                           currentPWMDuty += 5;
                           setPWMOutput();
                           break;
                     }
                     case DECREASE: {
                           currentPWMDuty -= 5;
                           setPWMOutput();
                           break;
                     }
                     default: {
                           // Do not react - unexpected state
                     }
              }
       }

       if (mask & kCAPT_InterruptOfNoTouchEnable) {
              PRINTF("No touch\n");
              storeTouchData(false);

              switch(state) {
                     case INCREASE: {
                           state = IDLE;
                           clearTouchData();
                           break;
                     }
                     case DECREASE: {
                           state = IDLE;
                           clearTouchData();
                           break;
                     }
                     default: {
                           // Do not react - unexpected state
                     }
              }
       }
}

/*
 * @brief   Application entry point.
 */
int main(void) {
    /* Init board hardware. */
    BOARD_InitBootPins();
    BOARD_InitBootClocks();
    BOARD_InitBootPeripherals();
#ifndef BOARD_INIT_DEBUG_CONSOLE_PERIPHERAL
    /* Init FSL debug console. */
    BOARD_InitDebugConsole();
#endif
    while(1) {
          __asm__("nop");
    }
    return 0;
}

Summary

Now you can compile the application and upload it to the board. You do not require additional steps to make the application work.

No ratings
Version history
Last update:
‎09-17-2021 07:01 AM
Updated by:
NXP Employee