Logo
CapTIvate™ Technology Guide  v1.00.20.00
Software Library

Introduction

The CapTIvate™ Capacitive Touch Software Library provides everything needed to support self and mutual capacitive sensors to help shorten the application development process.

Overview

The software library is a comprehensive collection of functions for touch, communications and sensor management features. Touch functions range from advanced sensor processing for buttons, sliders and wheels to "bare-metal" functions allowing direct access to the CapTIvate™ peripheral. Communication functions for I2C and UART serial drivers, and a communications protocol, enable the MSP430FR26xx\25xx microcontroller to send sensor data to the CapTIvate™ Design Center during the sensor development process. A library manager simplifies sensor measurement, calibration and communication activities.

lib_lib.png
Capacitive Touch Library

Benefits of using the CapTIvate™ Library

  • Simplifies sensor configuration, measurement, processing and communications
  • Shortens application development cycle

Features

  • Available in MSP430FR26xx\25xx ROM
  • Simple API
    • Requires only three API calls to use the library
      • CAPT_initUI()
      • CAPT_calibrateUI()
      • CAPT_updateUI()
      • Application notified of sensor updates through callback mechanism
  • Touch
    • Measurement and reporting process is automatic
    • Supports CapTIvate™ hardware "wake on touch" state machine
    • Proximity and Touch detect with de-bounce
    • IIR data filters for both fast averaging and high resolution
    • IQ Math using built-in 32-bit hardware multiplier
    • Button processing
      • Provides single dominant key
      • Supports up to 64 buttons
    • Slider/Wheel processing
      • Supports from 3 to 12 elements
      • Up to 16-bit resolution (application specific)
      • Wheel position accuracy is (+/-1) degree, 0 to 360 degrees
  • Communications protocol
    • Supports sensor data and commands
    • Use with CapTIvat™ Design Center or host processor
  • Serial Drivers
    • I2C and UART supported
  • Library Manager
    • Manages sensor scanning and communications operations
  • Leverages CapTIvate™ features for better performance and lower power
    • Wake-on-touch state machine
    • Dedicated oscillator
    • Dedicated timer

Pre-Compiled Library Components

The components illustrated below are provided pre-compiled as captivate.lib for Texas Instruments Code Composer Studio (CCS) and captivate.r43 for IAR Embedded Workbench. The CapTIvate™ Software Library API provides the interface to the library functions. Source code for these pre-compiled components are not provided.

  • Advanced
  • Touch
  • HAL
  • Communications
  • Protocol
lib_lib_precompiled.png
Pre-compiled library components (.lib and .r43)

Compile Time Configurable Components

Some library components are provided as source code, allowing flexibility in the system configuration during the application development cycle.

  • Library Manager
  • Communication Interface
  • I2C and UART serial drivers
lib_lib_user.png
User configurable components

ROM Library

The library components illustrated below are pre-programmed into ROM on MSP430FR26xx\25xx microcontrollers with CapTIvate™ Technology. Library components in ROM, rather than FRAM, allows more program memory space for the application.

  • Advanced
  • Touch
  • HAL
  • Communications
  • Protocol
  • MSP430 EUSCI I2C and UART driver-lib
lib_lib_rom.png
ROM Contents

ROM Library Size

The CapTIvate™ Library utilizes functions in ROM when using the MSP430FR26xx/25xx family. The ROM size is10.2kB.

CapTIvateā„¢ Touch Application Code Memory Size

All applications using CapTIvate™ Touch Technology require a small base amount of FRAM code memory to call into the ROM library. This code size is not dependent on the type or number of sensors in the system. The code size does vary when using the UART or I2C drivers that are needed during the development process to communicate with the CapTIvate™ Design Center.

CapTIvateā„¢ Touch Application Data Memory Size

Data memory is variable and grows based on the number and type of sensor in the application.

Relocating Sensor Data memory to FRAM

With FRAM technology, data RAM memory can easily be relocated to FRAM. This may be a consideration in applications with larger number of sensors that require more data space than the available amount of device RAM memory. Below is a simple modification to the MCU's linker command file that will place the .data section for the sensor objects (defined in CAPT_UserConfig.c) into FRAM.

.data : sensors:{CAPT_UserConfig.obj(.data)} > FRAM

Memory Model

The pre-compiled library components are compiled using small code and small data memory.

Libarary File Structure

The CapTIvate™ file structure is shown below. Note, Bold Files indicate those components that are not provided in the pre-compiled library, but are provided as library source code.

  • Advanced
    • CAPT_Buttons.h
    • CAPT_Slider.h
    • CAPT_EMC.h
    • CAPT_Manger.c
    • CAPT_Manger.h
  • Base
    • CAPT_HAL.h
    • CAPT_ISR.c
    • CAPT_Touch.h
    • CAPT_Type.h
    • rom_captivate.h
    • rom_map_captivate.h
  • COMM
    • CAPT_ByteQueue.h
    • CAPT_CommConfig.h
    • CAPT_Interface.c
    • CAPT_Interface.h
    • CAPT_PingPongBuffer.h
    • CAPT_Protocol.h
    • Serial Drivers
      • FunctionTimer.c
      • FunctionTimer.h
      • FunctionTimer_Definitions.h
      • I2CSlave.c
      • I2CSlave.h
      • I2CSlave_Definitions.h
      • UART.c
      • UART.h
      • UART.Definitions.h
  • captivate.h
  • captivate.lib
  • captivate.r43

Software Libary API

The MSP430FR2xx family software library API guide is provided as a separate document.
Jump to the Software Library API Guide...


Software Stack

The software library is designed to provide a bridge between the user's application and the CapTIvate™ peripheral. At a minimum, an application only needs to interface with the upper most Advanced layer in the library.

lib_stack.png
Software Stack

Advanced Layer

The Advanced layer functions provide button, slider and wheel functionality. Results from the touch layer are processed for each sensor type, depending on the type of sensors in the application. The advanced layer updates the application layer through user defined callback functions. See Callback section for details.

  • Buttons
    • Provides the most dominate button in a group of one or more buttons
      • If more than one button touched in a multi-button sensor, the application is responsible to check the other buttons
    • Up to 256 buttons (depending on the device)
      • MSP430FR26xx/25xx (32-pin device) supports 16 buttons in self capacitance mode and 64 buttons in mutual mode
  • Slider / Wheel
    • Provides filtered position
    • Resolution up to 13bit
    • Supports from 3 to 12 element sensors
    • (+/-) 1 degree accuracy, 360 degrees (wheel)

Touch Layer

The Touch layer functions perform processing and filtering of raw sensor data, as well as debounce and threshold detection. Results are provided to the components in the advanced layer.

  • Touch and proximity detection
  • Touch and proximity de-bounce
  • Long term baseline averaging "LTA"
  • IIR and fast IIR measurement and LTA filtering
  • Sensor calibration

HAL Layer

The HAL (hardware abstraction layer) software functions can directly access and manipulate the CapTIvate™ analog and hardware state machine. In a typical touch application, it is not necessary to call the HAL functions directly from the application. Rather, HAL functions are called from the higher level layers in the library or from the library manager during initialization.

Library Sensor Manager

To help abstract the sensor management and timing, three high level functions, typically called from within an application's main() function, are part of the library's "library sensor manager" which is responsible for managing the execution of the lower level library functions. These functions are:

  • CAPT_initUI()
  • CAPT_calibrateUI()
  • CAPT_updateUI()

Using only these three functions, it is possible to assemble a basic sensor measurement framework that provides sensor initialization, sensor calibration and sensor updates. The CapTIvate™ Design Center generates example code projects that show the use of these functions.


Using the Library

The CapTIvate™ software library is provided with every MSP430FR26xx\25xx source code project created by the CapTIvate™ Design Center. The recommended process for creating new projects is described in the capacitive touch design process. It is also recommended to use ROM library functions vs. the pre-compilied library functions to minimize the FRAM memory footprint.

Calling functions in ROM

Calling library functions directly from the MSP430FRxx microcontroller ROM requires the use of two header files that are provided with the library, rom_captivate.h and rom_map_captivate.h. These header files provide a mapping between functions in the software library function and the same function located in device ROM. Use the function call technique described in the following example to simplify the procedure.

Example:

Assume the captivate.lib has been added to the project and header files rom_captivate.h and map_rom_captivate.h are included in the source file where the library function call will be made. To call the library function "foo()", it is recommended to make an implicit ROM call MAP_foo(). The complier parses through the rom_map_captivate.h file to determine if the function resides in ROM, and if it does, will make the ROM call. If this function does not exist in ROM then the compiler will make a call to the pre-compiled library version.

It is possible to explicitly call the function "foo()" from the pre-compiled library using foo(). The linker will pull this function from the pre-compiled library during the link process, adding the function to FRAM program memory. To explicitly call the function "foo()" from ROM, use ROM_foo(). This will force the compiler to make a call to ROM, with no impact on FRAM program memory.

Building projects with CapTIvate™ library

The CapTIvate™ library is provided as a pre-compiled library in addition to some user configurable source files. The pre-compiled library file and source files can be added to the IDE project or used with a command line make file. During the link process, any non-ROM functions called by the application will be extracted from the library.

Example:

If the application references a library function foo(), then this function is extracted from the pre-compiled library and placed into FRAM program memory by the linker.

If, however, the application references the library function as MAP_foo() or ROM_foo(), the function is resolved by the linker to ROM using the map_rom_captivate.h file.

Callbacks

Callbacks provide a mechanism for the application to be notified when a sensor has been updated. The application must first register its "callback" function for each sensor before it can receive updates. When the callback is executed, the application can query the sensor's data structure to determine the status of the sensor.

A library function CAPT_registerCallback() provides the registration.

Example

Registering the application's callback function named "my_button_callback" to the sensor BTN0000.

MAP_CAPT_registerCallback((tSensor*)&BTN0000, (void (*)(tSensor*))&my_button_callback);

Once an application's callback function is registered, the callback is executed each time the corresponding sensor is scanned and processed, regardless if a proximity or touch detection has occurred. Inside the user callback, the application can perform any required sensor data and status post-processing. In a typical button application, this is where the application will check for a proximity, touch detection or slider/wheel position.

Example

Here is a typical callback example for a button that checks both when a touch is detected and a release.

void my_button_callback(tSensor* pSensor)
{
if((pSensor->bSensorTouch == true) && (pSensor->bSensorPrevTouch == false))
{
// BUTTON PRESSED
// DO SOMETHING ...
}
else
if((pSensor->bSensorTouch == false) && (pSensor->bSensorPrevTouch == true))
{
// BUTTON RELEASED
// DO SOMETHING ...
}
}

Here is a callback example for a proximity sensor.

void my_proximity_callback(tSensor* pSensor)
{
if(pSensor->bSensorProx == true)
{
// PROXIMITY DETECTED
// DO SOMETHING ...
}
}

In addition to proximity and touch status, sliders and wheels also provide position status. Here is a typical callback example for a slider or wheel that checks a sensor's position.

void my_slider_callback(tSensor* pSensor)
{
uint16_t ui16Position;
// FIRST CHECK IF THERE IS VALID TOUCH
if(pSensor->bSensorTouch == true)
{
// THEN GET THE CURRENT TOUCH POSITION ON THE SLIDER/WHEEL
ui16Position = (uint16_t)((tSliderSensorParams*)pSensor->pSensorParams)->SliderPosition.ui16Natural;
// DO SOMETHING WITH POSITION ...
}
}

Communications Module

The CapTIvate™ Touch Library includes a communications module for connecting CapTIvate™ MCUs to the outside world. This section discusses the architecture, features, and specification for that communications module, as well as how it may be used in a variety of applications from development to production.

This section assumes that the reader is familiar with the following:

  • Capacitive sensing as a user interface technology
  • The Captivate ecosystem and touch library
  • Basic MSP microcontroller architecture

Background

Designing a capacitive touch interface is an iterative process. Every sensor in the system must be individually tuned and optimized to achieve the desired sensitivity and "feel." Having the ability to communicate in real-time between a PC GUI and the target MCU drastically reduces the amount of time needed to tune an interface, and can provide better tuning results as the features incorporated into the CapTIvate™ peripheral can quickly and easily be exercised to determine the best configuration.

Following the design and tuning phase, a capacitive touch microcontroller takes on one of two roles in a system. It may be a dedicated human-machine interface (HMI) that only serves to resolve a capacitive touch panel into usable information (like touch/no touch, or a slider position), or it may double as a host processor, integrating other functionality such as monitoring sensors or controlling other functions in the system. In the first case (the dedicated HMI case), the controller will almost always require a way to communicate the status of the interface to some other host, which may be another MCU or an MPU. This interface could be as simple as a GPIO that gets set when a button is pressed, or as complex as a full I2C protocol with addressable parameters.

Overview

The CapTIvate™ Software Library communications module provides a single solution to the two needs above. The communications module is a layered set of firmware with a simple top-level API, designed to link a CapTIvate™ MCU to the CapTIvate™ Design Center PC GUI or to a host processor of some kind via a standard, common serial interface.

In a capacitive touch application the MCU is responsible for measuring capacitive sensors, processing the measurement to interpret some kind of result, and transmitting that result either to the application locally or to a host processor. The communications layer provides the "transmission" part of the equation. The diagram below portrays how the communications module fits in with the rest of the firmware in a typical CapTIvate™ application.

The CapTIvate™ communications module is layered and contains several standalone features that are interlinked together. These features are introduced below. To use the communications module, it is only necessary to set up the configuration file and call the top-level APIs.

The communications module contains 4 layers. In order of decreasing abstraction, they are:

  • Interface Layer [COMM/CAPT_Interface.c/.h]
    • The interface layer implements the top-level API.
  • Protocol Layer [COMM/CAPT_Protocol.c/.h]
    • The protocol layer implements Captivate protocol packet generation and packet interpretation.
  • Serial Driver Layer [COMM/Serial_Drivers/*.c/.h]
    • The serial driver layer contains several interchangeable serial driver options.
    • One and only one option may be selected at any given time.
    • Selection and configuration is handled in the configuration file.
    • The serial drivers are built on top of the MSP430 DriverLib API.
    • UART and I2C Slave drivers are provided.
  • Data Structure Layer [COMM/CAPT_ByteQueue.c/.h, COMM/CAPT_PingPongBuffer.c/.h]
    • The data structure layer implements basic abstract data types, such as a FIFO queue and a ping pong buffer to aid serial communication.

Interface Layer

The CapTIvate™ interface layer is the top-level communication layer. It provides the top-level function calls that are used by the application, and serves to marry together the protocol layer (which handles packet generation and interpretation) with the serial driver (which actually moves the data in and out of the microcontroller). The functionality provided is covered in this section below. All application access to the communications module should be through the interface layer.

Using the Communications Module: Initializing the Interface

The communications module must be initialized at startup by the application via a call to CAPT_initCommInterface(). This top-level init function handles opening the selected serial driver, as well as initializing any queues/buffers that are needed for communication.

DescriptionDeclaration
Init the Communications Moduleextern void CAPT_initCommInterface(tCaptivateApplication *pApp)

Using the Communications Module: Handling Incoming Data

Incoming raw data from a host is buffered by the serial driver to be serviced when the application is available to do so. The application must periodically call CAPT_checkForInboundPacket() to check to see if any packets have been received from the host. This top-level function will check for packets in the receive queue of the serial driver, and if any packets are found, they will be processed according to their type. Typically, this function is called in a background loop when the application is available. Note that the serial drivers will exit active from sleep if a data is arriving from the host, which can be used as a mechanism to wake up the background loop to call this function.

The CAPT_checkForRecalibrationRequest() function should also be called periodically to see if any of the packets received and handled by CAPT_checkForInboundPacket() require the application to re-calibrate the user interface. An example of this would be if a packet was received that changed the conversion count, requiring a re-calibration of the sensors in the system.

DescriptionDeclaration
Check for an Inbound Packet, and Process Itextern bool CAPT_checkForInboundPacket(void)
Check for a Re-calibration Requestextern bool CAPT_checkForRecalibrationRequest(void)

Using the Communications Module: Writing Out Data

The interface layer provides 3 top-level constructs for transmitting data to the host. The three functions below handle generation of the appropriate packet, management of the transmit ping/pong buffers, and transmission over the serial interface. If the serial peripheral is available (not busy) these calls are non-blocking, and the packets that are generated are transmitted to the host via interrupt service routines in the serial driver.

DescriptionDeclaration
Write Element Dataextern bool CAPT_writeElementData(uint8_t ui8SensorID)
Write Sensor Dataextern bool CAPT_writeSensorData(uint8_t ui8SensorID)
Write General Purpose Dataextern bool CAPT_writeGeneralPurposeData(uint16_t *pData, uint8_t ui8Cnt)

Compile-Time Configuration

The compile-time configuration options are set in the CAPT_CommConfig.h file. The available compile-time options are described below.

Interface Selection Definition

ParameterFileValid Values
CAPT_INTERFACECAPT_UserConfig.h__CAPT_NO_INTERFACE__, CAPT_UART_INTERFACE, CAPT_BULKI2C_INTERFACE, CAPT_REGISTERI2C_INTERFACE

CAPT_INTERFACE, unlike the remaining definitions, is located in the User Config file (CAPT_UserConfig.h). It selects the interface that the communications module should be built for. If the communication module should be excluded from the build, then CAPT_NO_INTERFACE should be set. Otherwise, the desired communication mode should be set.

NOTE: This value is automatically populated in the CAPT_UserConfig.h file by the Design Center during source code generation.

Transmit Buffer Size Definition

ParameterFileValid Values
CAPT_TRANSMIT_BUFFER_SIZECAPT_CommConfig.hUnsigned Integer

CAPT_TRANSMIT_BUFFER_SIZE defines the size of the transmit buffer. Note that 2x this size will be allocated, since ping-pong buffering is used. This size should also be at least 2x the size of the largest packet, to allow for byte stuffing.

Receive Queue Buffer Size Definition

ParameterFileValid Values
CAPT_QUEUE_BUFFER_SIZECAPT_CommConfig.hUnsigned Integer

CAPT_QUEUE_BUFFER_SIZE defines the size of the receive queue. This is the queue that the serial driver uses to buffer received data until the data is processed by a call to CAPT_checkForInboundPacket(). If it seems like packets are being dropped, a good first step is to increase the size of this buffer.

I2C Slave Serial Driver Receive Buffer Size Definition

ParameterFileValid Values
CAPT_I2C_RECEIVE_BUFFER_SIZECAPT_CommConfig.hUnsigned Integer

CAPT_I2C_RECEIVE_BUFFER_SIZE defines the size of the receive buffer used by the I2C Slave driver, if that driver is selected. This buffer size should be at least as large as the maximum length I2C bus write transaction that is expected.

I2C Slave Serial Driver Buffer Size in Register Mode Definition

ParameterFileValid Values
CAPT_I2C_REGISTER_RW_BUFFER_SIZECAPT_CommConfig.hUnsigned Integer

CAPT_I2C_REGISTER_RW_BUFFER_SIZE defines the size of the buffer used by the I2C Slave driver when the communication interface is configured in register I2C mode. This buffer size should be at least as large as the maximum length I2C bus transaction that is expected.

Protocol Layer

The CapTIvate™ protocol is a communications specification for sending capacitive touch specific data. It enables MSP430 Captivate-equipped microcontrollers to communicate with design, debug, and tuning tools on host PCs. In addition to this function, it can also provide a mechanism for interfacing a Captivate MCU to another host MCU or SoC in the context of a larger system. This guide discusses the details of the protocol itself: packet types and packet structure.

The Captivate protocol is a packet-based serial messaging protocol. It includes provisions for passing real-time capacitive measurement data from a Captivate target MCU to another processor, as well as provisions for tuning parameter read and write.

Use Cases

The various use cases for the protocol are described below.

  1. Capacitive Touch Development and Tuning Capacitive touch development and tuning involves looking at real-time data from a touch panel and adjusting software parameters to achieve the desired response and feel from the sensors on that panel. For example: tuning a capacitive button involves looking at the raw data coming back from the microcontroller about that button, and adjusting thresholds, de-bounce, and filters accordingly to create a robust user interface. Having the ability to adjust all of these software parameters in real-time while looking at sensor data, without re-compiling code, is extremely powerful and reduces development time. The Captivate protocol was designed with the Captivate Design Center specifically to meet this need.
  2. Interface to Host Processor Most capacitive touch user interfaces involve a dedicated microcontroller driving the touch panel, which communicates up to a host processor of some kind. The flexibility of the Captivate protocol allows for it to be re-used as an interface to a host processor; it can stream touch status, proximity status, and slider/wheel position up to a host.
  3. In-Field / In-System Debug Interface and Tuning Since the Captivate protocol supports reading and writing of capacitive touch tuning parameters, as well as the streaming of real-time data, it could potentially be utilized as a diagnostic tool in the field when coupled with the Captivate Design Center PC tool.

Introduction to Packet Types

The Captivate protocol supports five packet types: sensor packets, cycle packets, parameter packets, general purpose packets, and trackpad packets. In a capacitive touch system, there are two endpoints in the communication link: the target MCU itself, and the host. The host might be a PC tool or some kind of embedded processor. Sensor packets, cycle packets, general purpose packets, and trackpad packets carry information about the current state of the touch panel being driven by the target MCU. These packets are UNIDIRECTIONAL, and only travel from the target to the host. Parameter packets are BIDIRECTIONAL, and may travel from the target to the host or from the host to the target.

lib_comm_protocol_packet_direction.png
Packet Directionality

Sensor Packets

Sensor packets are unidirectional packets from the Captivate MCU to the host. They provide information about the current state of a sensor. Sensor state information includes things like dominant button, slider or wheel position, sensor global proximity state, and sensor global touch/previous touch state.

Cycle Packets

Cycle packets are unidirectional packets from the Captivate MCU to the host. They provide low level element information, such as element touch status, element proximity status, element count, and element long term average for all of the elements within a cycle. These packets are typically used in the tuning phase, where it is desirable to have real-time views of count and long term average for setting thresholds and tuning filters.

Trackpad Packets

Trackpad packets are unidirectional packets from trackpad MCUs to the host. They provide the X and Y coordinates of touches on the trackpad.

General Purpose Packets

General purpose packets are unidirectional packets from a Captivate MCU to the host. They serve as a generic container to send any information that can be formatted as a 16-bit unsigned integer. This channel can serve as a debug tool for sending any kind of information that doesn't fit into any of the other packet types. Up to 29 integers (58 bytes) may be sent in a single packet.

Parameter Packets

Parameter packets are bi-directional packets between a host and the Captivate MCU. Parameter packets allow for the host to adjust a tuning parameter on the target at runtime. For example, the touch threshold for an element or the resolution of a slider could be adjusted by sending the appropriate parameter command. Parameters can be read or written. Parameter reads and writes from a host to a target MCU always result in a read-back of the most current value (the value after the write, in the case of a write).

Transmission Rules

The packet types discussed above are transmitted via a serial interface of some kind between the target and the host. Full-duplex UART is the typical interface. To provide reliable and accurate packet transmission, a set of transmission rules is applied to all packets when being transmitted. These rules are discussed in the HID Bridge Packet Mode section.

The following functions aid in applying transmission rules:

DescriptionDeclaration
Stuff Sync Bytes in a Packetextern uint16_t CAPT_stuffSyncBytes(uint8_t *pBuffer, uint16_t ui16Length)
Verify a Checksumextern bool CAPT_verifyChecksum(const uint8_t *pBuffer, const uint16_t ui16Length, const uint16_t ui16Checksum)
Get a Checksumextern uint16_t CAPT_getChecksum(const uint8_t *pBuffer, const uint16_t ui16Length)
Identify and Frame a Packet in a Receive Data Queueextern bool CAPT_processReceivedData(tByteQueue *pReceiveQueue, tParameterPacket *pPacket, tTLProtocolProcessingVariables *pVariables)

Format: Sensor Packets

Sensor packets have a fixed length of 6 bytes. There are two control bytes and four data payload bytes.

lib_comm_protocol_sensorpacket.png
Sensor Packet Format
  1. Command Byte [0] The command byte for a sensor packet is always 00h.
  2. Sensor ID Byte [1] The sensor ID byte contains an unsigned 8-bit integer that specifies the ID of the sensor whose data is being transmitted. Sensor ID on the target side is typically established by the order of sensor pointers in the global sensor pointer array. Sensors are sorted alphabetically by the Captivate Design Center PC GUI.
  3. Data Payload Bytes [2-5] The data payload bytes contain the sensor data that is being sent. The information contained in the payload is dependent upon the sensor type. The table below describes the payload on a sensor type basis.
Sensor TypeByte 0Byte 1Byte 2Byte 3
Button GroupDominant Element (256 elements max)Previous Dominant Element (256 elements max)ReservedSensor Status
SliderSlider Position (Lower 8 bits of 16 bits)Slider Position (Upper 8 bits of 16 bits)ReservedSensor Status
WheelWheel Position (Lower 8 bits of 16 bits)Wheel Position (Upper 8 bits of 16 bits)ReservedSensor Status
ProximityReservedReservedReservedSensor Status
TrackpadReservedReservedReservedSensor Status

The sensor status byte, included in button group, slider, and wheel sensor packets, provides additional data about the state of the sensor that is often meaningful. The status flags are all boolean flags, and are assigned to bit positions as follows:

Bit Mask (Position)Sensor Status Flag
Bit 0 (01h)Global Sensor Touch Flag
Bit 1 (02h)Global Sensor Previous Touch Flag
Bit 2 (04h)Global Sensor Proximity Flag
Bit 3 (08h)Global Sensor Detect Flag (Prox Detect Pre-Debounce)
Bit 4 (10h)Global Sensor Negative Touch Flag (Reverse Touch)
Bit 5 (20h)Global Sensor Noise State
Bit 6 (40h)Global Sensor Max Count Error Flag
Bit 7 (80h)Global Sensor Calibration Error Flag

NOTE: For slider / wheel sensors, the 16 position bits in the data payload will contain the valid slider or wheel position when the global sensor touch flag is true on that sensor. If there is not a global touch detection, the 16 position bits will all be set high (0xFFFF for the slider/wheel position value).

NOTE: Reserved fields are still transmitted, but do not contain any meaningful data. Do not use or rely on any data transmitted in a reserved field.

Sensor packets are generated via a call into the protocol layer. The CAPT_getSensorPacket() function looks up the sensor at index ui8SensorID in the array sensorArray, and stores the generated packet in the buffer space pointed to by pBuffer. The length of the packet is returned by the function.

DescriptionDeclaration
Get a Sensor Packetextern uint16_t CAPT_getSensorPacket(tSensor **sensorArray, uint8_t ui8SensorID, uint8_t *pBuffer)

Format: Cycle Packets

Cycle packets have a variable length which is dependent upon the number of elements within the cycle. There are always 3 control bytes and 3 state bytes. In addition to those 6 bytes, there are 4 bytes per element in the cycle.

lib_comm_protocol_cyclepacket.png
Cycle Packet Format
  1. Command Byte [0] The command byte for a cycle packet is always 01h.
  2. Sensor ID Byte [1] The sensor ID byte contains an unsigned 8-bit integer that specifies the ID of the sensor whose data is being transmitted. Sensor ID on the target side is typically established by the order of sensor pointers in the global sensor pointer array. Sensors are sorted alphabetically by the Captivate Design Center PC GUI.
  3. Cycle ID Byte [2] The cycle ID byte contains an unsigned 8-bit integer that specifies the ID of the cycle whose data is being transmitted, relative to the sensor. For example, in a sensor with 3 cycles, the first cycle would have an ID of 0, the second, an ID of 1, and the third, an ID of 2. The cycle ID should correspond to the index of the cycle in the sensor's cycle pointer array.
  4. Cycle State Bytes [3-5] The cycle state bytes specify the touch and proximity detection flags for each element in the cycle. Up to 12 elements per cycle are supported. Bytes 3 through 5 of the cycle packet comprise a 24 bit cycle state section, where the lower 12 bits represent the proximity state for each element in a bitwise fashion, and the upper 12 bits represent the touch state for each element in a bitwise fashion.
  5. Element LTA and Element Count Bytes [6-n], n=5+4(number of elements) The remainder of the cycle packet is comprised of element LTA and count values. LTA and count are represented as 16 bit unsigned integers. As such, 32 bits are required for each element in the LTA/Count section. The packet shall increase in size in 32 bit (4 byte) increments for every additional element in the cycle, up to a maximum of 12 elements. The LTA is sent first, and the count second. Per the protocol, when sending values greater than one byte, the transmission sequence is lowest order byte to highest order byte.

Cycle packets are generated via a call into the protocol layer. The CAPT_getCyclePacket() function looks up the cycle at index ui8Cycle in the sensor at index ui8SensorID in the array sensorArray, and stores the generated packet in the buffer space pointed to by pBuffer. The length of the packet is returned by the function.

DescriptionDeclaration
Get a Cycle Packetextern uint16_t CAPT_getCyclePacket(tSensor **sensorArray, uint8_t ui8SensorID, uint8_t ui8Cycle, uint8_t *pBuffer);

Format: Trackpad Packets

Trackpad packets have a variable length which is dependent upon the maximum number of simultaneous touches supported by the trackpad device. There are 3 control bytes. Following the control bytes, there is a trackpad gesture byte for indicating the detection of a gesture and what the gesture was. For each simultaneous touch the trackpad device supports, 4 additional payload bytes are added to the packet. The packet format can be seen below.

lib_comm_protocol_trackpadpacket.png
Trackpad Packet Format
  1. Command Byte [0] The command byte for trackpad packets is always 02h.
  2. Sensor ID Byte [1] The sensor ID byte contains an unsigned 8-bit integer that specifies the ID of the sensor whose data is being transmitted. Sensor ID on the target side is typically established by the order of sensor pointers in the global sensor pointer array. Sensors are sorted alphabetically by the Captivate Design Center PC GUI.
  3. Max Touches [2] The max touches byte contains an unsigned 8-bit integer that specifies the number of simultaneous touches supported by the trackpad device. This also provides insight into the length of the remainder of the packet. Following the max touches byte, there will be 4 bytes for each simultaneous touch supported by the trackpad device. Trackpad devices must support at least one touch.
  4. Gesture Status [3] The trackpad gesture byte indicates whether a gesture was just detected, and what that gesture was. The value/gesture pairs are listed below. If no gesture was detected, the gesture byte will be set to FFh.
**Value**Gesture
00hWake on Proximity Detection
01hReserved
02hSingle Touch Single Tap
03hSingle Touch Double Tap
04hTwo Touch Single Tap
05hTwo Touch Double Tap
06hTap and Hold
07hTwo Touch Tap and Hold
08hSwipe Left
09hSwipe Right
0AhSwipe Up
0BhSwipe Down
0Ch-FEhReserved
FFhNo Gesture Detected
  1. Touch Coordinates [4-n], n=4+4(Max Touches) The touch coordinates indicate the presence of and location of a touch on the trackpad. Touch locations are communicated via two 16-bit unsigned integers per touch: one for the coordinate on the X axis, and one for the coordinate on the Y axis. 16 bits is the maximum resolution in either the X or Y direction for the trackpad. Most applications will have a working resolution that is less than 16 bits per coordinate, but 16 bits are always sent regardless. If no touch is present, both the X and the Y coordinates will read as FFFFh.

NOTE: Trackpad packets only pertain to dedicated trackpad devices.

Format: General Purpose Packets

General purpose packets are unique in that they simply serve as a data streaming mechanism for any data an application wishes to send. Data is sent as an array of 16-bit unsigned integers. General purpose packets have variable length that is dependent upon the number of integers being sent. There are always 2 control bytes. Following the control bytes is the data payload (up to 29 entries), with 2 bytes (16 bits) per entry.

lib_comm_protocol_gppacket.png
General Purpose Packet Format
  1. Command Byte [0] The command byte for general purpose packets is always 10h.
  2. Valid Data Count Byte [1] The valid data count byte indicates how many valid data entries exist in the packet. This also implies the length of the packet: each valid entry implies two bytes of payload (each entry is a 16-bit unsigned integer). Valid values for this field are 1-29.
  3. Data Payload [2-n] The data payload section contains the generic data to be transmitted, formatted as unsigned 16-bit integers. Up to 29 payload items (58 bytes) are allowed.

General purpose packets are generated via a call into the protocol layer. The CAPT_getGeneralPurposePacket() function generates a packet for the data array of length ui8Cnt pointed to by pData, and stores the generated packet in the buffer space pointed to by pBuffer.

DescriptionDeclaration
Get a General Purpose Packetextern uint16_t CAPT_getGeneralPurposePacket(uint16_t *pData, uint8_t ui8Cnt, uint8_t *pBuffer)

Format: Parameter Packets

Parameter packets have a fixed length of 7 bytes. There are 3 control bytes and 4 data payload bytes. The packet format can be seen below.

lib_comm_protocol_parameterpacket.png
Parameter Packet Format
  1. Command Byte [0] The command byte for parameter packets is variable, and indicates the ID of the parameter that is to be read or written to.
  2. Read/Write Byte [1] The read/write byte is a 1-bit Boolean value that indicates whether the operation is a parameter read or a parameter write. 1 = write, 0 = read.
  3. Sensor ID Byte [2] The sensor ID byte contains an unsigned 8-bit integer that specifies the ID of the sensor whose parameter is being read from or written to. Sensor ID on the target side is typically established by the order of sensor pointers in the global sensor pointer array. Sensors are sorted alphabetically by the Captivate Design Center PC tool. Note that parameters for controller commands (C-h commands) may not use the Sensor ID field.
  4. Data Bytes [3-6] The data bytes contain the information that is to be read or written to. In some cases, 1 or more of the data bytes are used for further ID (such as cycle ID or element ID).

The protocol layer provides several functions for framing and interpreting parameter packets. Placing a call into CAPT_processReceivedData() causes the protocol layer to search for potential packets in a datastream that has been queued up by a serial driver. Once a valid packed has been framed, the parameter may be accessed and updated/read via calls to CAPT_accessSensorParameter() and CAPT_accessSpecialSensorParameter().

DescriptionDeclaration
Access a Sensor Parameterextern tTLParameterAccessResult CAPT_accessSensorParameter(tSensor **sensorArray, tParameterPacket *pPacket)
Access a Special Sensor Parameterextern tTLParameterAccessResult CAPT_accessSpecialSensorParameter(tSensor **sensorArray, tParameterPacket *pPacket)
Identify and Frame a Packet in a Receive Data Queueextern bool CAPT_processReceivedData(tByteQueue *pReceiveQueue, tParameterPacket *pPacket, tTLProtocolProcessingVariables *pVariables)
Verify a Checksumextern bool CAPT_verifyChecksum(const uint8_t *pBuffer, const uint16_t ui16Length, const uint16_t ui16Checksum)

The available parameters that can be adjusted through the use of parameter packets are listed below.

Sensor Parameters

Parameter NameByte 0: CMDByte 1: RWByte 2: IDByte 3Byte 4Byte 5Byte 6MCU TaskSW Containing StructureSW Containing Variable
Conversion Gain80RWSensor IDDon't CareDon't CareATI Base Lower ByteATI Base Upper ByteRe-CaltSensorui16ConversionGain
Conversion Count81RWSensor IDDon't CareDon't CareATI Target Lower ByteATI Target Upper ByteRe-CaltSensorui16ConversionCount
Prox Threshold82RWSensor IDDon't CareDon't CareProx Threshold Lower ByteProx Threshold Upper ByteNAtSensorui16ProxThreshold
Prox Debounce-In Threshold84RWSensor IDDon't CareDon't CareProx Db InDon't CareNAtSensorProxDbThreshold .DbUp
Prox Debounce-Out Threshold85RWSensor IDDon't CareDon't CareProx Db OutDon't CareNAtSensorProxDbThreshold .DbDown
Touch Debounce-In Threshold86RWSensor IDDon't CareDon't CareTouch Db InDon't CareNAtSensorTouchDbThreshold .DbUp
Touch Debounce-Out Threshold87RWSensor IDDon't CareDon't CareTouch Db OutDon't CareNAtSensorTouchDbThreshold .DbDown
Sensor Timeout Threshold88RWSensor IDDon't CareDon't CareSensor Timeout Lower ByteSensor Timeout Upper ByteNAtSensorui16TimeoutThreshold
Count Filter Enable89RWSensor IDDon't CareDon't CareCount Filter Enable bitDon't CareNAtSensorbCountFilterSelect
Count Filter Beta8ARWSensor IDDon't CareDon't CareCount Filter BetaDon't CareNAtSensorui8CntBeta
LTA Filter Beta8BRWSensor IDDon't CareDon't CareLTA Filter BetaDon't CareNAtSensorui8LTABeta
Halt LTA Filter Immediately8CRWSensor IDDon't CareDon't CareLTA Filter HaltDon't CareNAtSensorbSensorHalt
Runtime Re-Calibration Enable8DRWSensor IDDon't CareDon't CareRuntime Re-Cal EnableDon't CareNAtSensorbReCalibrateEnable
Force Re-Calibrate8EN/ASensor IDDon't CareDon't CareDon't CareDon't CareRe-CaltSensorN/A
Bias Current8FRWSensor IDDon't CareDon't CareBias CurrentDon't CareRe-CaltSensorui8BiasControl
Sample Capacitor Discharge95RWSensor IDDon't CareDon't CareCs DischargeDon't CareRe-CaltSensorbCsDischarge
Modulation Enable96RWSensor IDDon't CareDon't CareMod EnableDon't CareRe-CaltSensorbModEnable
Frequency Divider97RWSensor IDDon't CareDon't CareFreq DivDon't CareRe-CaltSensorui8FreqDiv
Charge/Hold Phase Length98RWSensor IDDon't CareDon't CareCharge LengthDon't CareRe-CaltSensorui8ChargeLength
Transfer/Sample Phase Length99RWSensor IDDon't CareDon't CareTransfer LengthDon't CareRe-CaltSensorui8TransferLength
Error Threshold9ARWSensor IDDon't CareDon't CareError Threshold Lower ByteError Threshold Upper ByteNAtSensorui16ErrorThreshold
Negative Touch Threshold9BRWSensor IDDon't CareDon't CareNegative Touch Threshold Lower ByteNegative Touch Threshold Upper ByteNAtSensorui16NegativeTouchThreshold
Idle State9CRWSensor IDDon't CareDon't CareIdle StateDon't CareNAtSensorbIdleState
Input Sync9DRWSensor IDDon't CareDon't CareInput SyncDon't CareNAtSensorui8InputSyncControl
Timer Sync9ERWSensor IDDon't CareDon't CareTimer SyncDon't CareNAtSensorbTimerSyncControl
Automatic Power-Down Enable9FRWSensor IDDon't CareDon't CarePower Down ControlDon't CareNAtSensorbLpmControl
Halt LTA on Sensor Prox or TouchA0RWSensor IDDon't CareDon't CareSensor Prox/Touch HaltDon't CareNAtSensorbPTSensorHalt
Halt LTA on Element Prox or TouchA1RWSensor IDDon't CareDon't CareElement Prox/Touch HaltDon't CareNAtSensorbPTElementHalt

Element Parameters

Parameter NameByte 0: CMDByte 1: RWByte 2: IDByte 3Byte 4Byte 5Byte 6MCU TaskSW Containing StructureSW Containing Variable
Touch Threshold83RWSensor IDCycle # (relative to sensor)Lower 4 Bits: Element # (relative to cycle)Touch ThresholdDon't CareNAtElementui8TouchThreshold [Element #]
Coarse Gain RatioA2RSensor IDCycle # (relative to sensor)[UPPER 4 Bits: Frequency #] [ LOWER 4 Bits: Element # relative to cycle] Coarse GainDon't CareNAtCaptivateElementTuningui8GainRatioCoarse
Fine Gain RatioA3RSensor IDCycle # (relative to sensor)[UPPER 4 Bits: Frequency #] [ LOWER 4 Bits: Element # relative to cycle] Fine GainDon't CareNAtCaptivateElementTuningui8GainRatioFine
Parasitic Offset ScaleD0RSensor IDCycle # (relative to sensor)[UPPER 4 Bits: Frequency #] [ LOWER 4 Bits: Element # relative to cycle] Offset Scale (2 bit selection)Don't CareNAtCaptivateElementTuningui16OffsetTap Upper Byte
Parasitic Offset LevelD1RSensor IDCycle # (relative to sensor)[UPPER 4 Bits: Frequency #] [ LOWER 4 Bits: Element # relative to cycle] Offset Level (8 bit selection)Don't CareNAtCaptivateElementTuningui16OffsetTap Lower Byte

Slider/Wheel Parameters

Parameter NameByte 0: CMDByte 1: RWByte 2: IDByte 3Byte 4Byte 5Byte 6MCU TaskSW Containing StructureSW Containing Variable
Slider/Wheel Position Filter Enable90RWSensor IDDon't CareDon't CareSlider Filter Enable bitDon't CareNAtSliderSensorParamsSliderFilterEnable
Slider/Wheel Position Filter Beta91RWSensor IDDon't CareDon't CareSlider Filter BetaDon't CareNAtSliderSensorParamsSliderBeta
Desired Slider/Wheel Resolution92RWSensor IDDon't CareDon't CareSlider Resolution Lower ByteSlider Resolution Upper ByteNAtSliderSensorParamsui16Resolution
Slider Lower Trim93RWSensor IDDon't CareDon't CareSlider Lower Trim Lower ByteSlider Lower Trim Upper ByteNAtSliderSensorParamsSliderLower
Slider Upper Trim94RWSensor IDDon't CareDon't CareSlider Upper Trim Lower ByteSlider Upper Trim Upper ByteNAtSliderSensorParamsSliderUpper

Controller/Management Parameters

Parameter NameByte 0: CMDByte 1: RWByte 2: IDByte 3Byte 4Byte 5Byte 6MCU TaskSW Containing StructureSW Containing Variable
Element Data Transmit EnableC0RWDon't CareDon't CareDon't CareElement Transmit EnableDon't CareNAtCaptivateApplicationbElementDataTxEnable
Sensor Data Transmit EnableC1RWDon't CareDon't CareDon't CareSensor Transmit EnableDon't CareNAtCaptivateApplicationbSensorDataTxEnable
Active Mode Scan Rate (ms)C2RWDon't CareDon't CareDon't CareActive Report Period (ms) Lower ByteActive Report Period (ms) Upper Byte NAtCaptivateApplicationui16ActiveModeScanPeriod
Wake-on-Prox Mode Scan Rate (ms)C3RWDon't CareDon't CareDon't CareWoP Report Period (ms) Lower ByteWoP Report Period (ms) Upper Byte NAtCaptivateApplicationui16WakeOnProxModeScanPeriod
Wakeup IntervalC4RWDon't CareDon't CareDon't CareWakeup IntervalDon't CareNAtCaptivateApplicationui8WakeupInterval
Inactivity TimeoutC5RWDon't CareDon't CareDon't CareTimeout Lower ByteTimeout Upper ByteNAtCaptivateApplicationui16InactivityTimeout

UART Driver

The MSP430 eUSCI_A UART Driver provides a simple UART API to MSP430 applications, enabling interrupt-driven transmit and receive operations as well as error handling. This section provides an overview of the driver's features, architecture, and API. This document assumes that the reader is familiar with the MSP430 MCU architecture, as well as embedded C programming concepts.

Related Documents

This document should be used with the following additional supporting documentation. The MSP430 Driver Library API is not specific to this driver, and has its own documentation.

  • MSP430 Driver Library (DriverLib) User's Guide
  • The relevant MSP430 Family User's Guide

Purpose of the Driver

The eUSCI_A UART Driver enables developers to quickly get up and running with UART communication via an API that is similar to one used on a PC. The API provides simple functions such as UART_openPort() and UART_transmitBuffer(), and it allows the developer to register event handlers for received data and error conditions. To enable portability, the driver is built upon the MSP430 DriverLib register abstraction. This driver has been designed to work with MSP430 DriverLib build 1.90.00.00 and greater. Like any other serial interface, UART has benefits and drawbacks. Whether or not it is the best choice for an embedded interface depends on a number of factors. The benefits and drawbacks of UART are discussed below.

UART Benefits

  • Ease of implementation. UART is one of the most common serial communication protocols around. While interfaces with higher speed and higher reliability exist, such as I2C and SPI, none match UART for ease of implementation. UART is easily interfaced to a host PC or another microcontroller, and as such it is well suited for use as a debug console.
  • Point-to-point simplicity. Since only two nodes exist, the interface is not a bus with multiple devices attached. This removes the need for addressing or chip-select overhead.
  • Full duplex communication. In this UART driver implementation, transmit and receive operations are decoupled and may occur concurrently. Each of the two connected nodes may consider itself a master, and may begin transmission to the other node at any time.

UART Drawbacks

  • Critical bit timing. Because UART transmission is asynchronous (there is no bit clock), accurate timing must be guaranteed on both sides of the interface.
  • Bit rate limitation. Due to the critical bit timing above, UART is typically more limited in maximum bit clock then when compared with a synchronous interface, such as SPI or I2C.
  • Critical ISR timing. This driver does not employ the use of a DMA channel for moving data from transmit and receive buffers into RAM, as not all devices are equipped with a DMA engine. Instead of DMA, all data transfers are handled via interrupt service routines. This means that the CPU must be available to service receive interrupts at least every byte period. A byte period is defined as the transmission time for one byte, which is equal to 10 times the bit period. Note that the byte transmission time increases if parity or more than one stop bit is used. For example, if the bit clock is 250 kHz, the transmission time of one byte is 4us times 10 bits, or 40us. With an 8MHz CPU clock, there are only 320 instruction cycles available every 40us. If the CPU is not available to service receive interrupts, received bytes may be lost. The driver provides an error detection mechanism to alert the application if and when data is being lost.
  • Asynchronous overhead. UART as configured in this driver requires at least one start bit and one stop bit. As such, each 8-bit byte requires that 10 bits be sent. This overhead would not be present in a SPI interface, although I2C does impose an ACK/NACK bit as well as addressing overhead.

Driver Features

The key features implemented in the UART driver are listed below.

  • Full duplex bi-directional communication
  • No flow control required
  • Fully interrupt-driven
  • Non-blocking transmit function
  • Receive event callback
  • Error event callback
  • Compile-time selection of which eUSCI_A instance to use

Driver Overview

The UART driver is provided in source code. The driver consists of three source files:

  • UART_Definitions.h (Configuration header file)
  • UART.h (API Header file)
  • UART.c (API Implementation file) The core driver is implemented in UART.h and UART.c. The UART_Definitions.h file allows the developer to adjust the driver's compile-time options.

The UART driver requires the following hardware resources:

  • One eUSCI_A peripheral instantiation
  • Two device pins (UCAxTXD and UCAxRXD), where 'x' represents the selected eUSCI_A instance

The UART driver API is composed of functions and data types. The sections below describe how to configure the UART driver and use it to perform transmit and receive operations. The UART driver employs a basic software state machine to manage operations. The three driver states and their interconnection are shown in the diagram below.

lib_comm_uartdriverstatemachine.png
UART Driver State Machine

Compile-time Driver Configuration Options

The UART driver has compile-time options as well as run-time configurable options. Certain things must be known at compile-time, such as which eUSCI_A peripheral instance is associated with the driver. Other options, such as baud rate, may be controlled at runtime.

The compile-time configuration options are set in the UART_Definitions.h file. The available compile-time options are described below.

UART Enable Definition

ParameterFileValid Values
UART__ENABLEUART_Definitions.htrue, false

The UART Enable compile-time option selects whether the UART Driver is enabled or disabled. This provides a mechanism to exclude the driver from the compilation process. To include the driver, define UART__ENABLE as true. Else, define it as false.

UART eUSCI_A Peripheral Selection Definition

ParameterFileValid Values
UART__EUSCI_A_PERIPHERALUART_Definitions.hEUSCI_A0_BASE, EUSCI_A1_BASE

The UART eUSCI_A peripheral selection allows easy selection of which eUSCI_A instance to associate with the UART driver. This provides flexibility during design if a pin-mux change is necessary. A valid base address must be provided.When a eUSCI_A peripheral selection is made, the UART driver ISR address is linked to the appropriate eUSCI interrupt vector automatically. If an invalid address is selected, a compiler error is thrown.

UART Low Power Mode (LPMx)

ParameterFileValid Values
UART__LPMx_bitsUART_Definitions.h0, LPM0_bits, LPM1_bits, LPM2_bits, LPM3_bits

UART communications is often performed in a low power mode. The UART LPM Mode configuration is used in three ways, as described below.

  • The receive callback and error callback functions may trigger an active exit from a low power mode to wake the CPU for further action. If a callback function returns a Boolean true, the UART driver will wake the CPU after returning from the callback. It does this by clearing the status register bits indicated in the UART LPM Mode configuration upon exit from the UART ISR.
  • A transmit operation will exit the low power mode specified by the UART LPM Mode configuration once the full transmission is complete, to allow the application to know that the transmission was completed.
  • The transmit function is normally a non-blocking function. Once transmission begins, the function returns. However, in the event that a previous transmission was still in progress when the transmit function was called, there is an option to wait for the previous transmission to complete inside of a low power mode. If that option is selected, the low power mode specified by the UART LPM Mode configuration will be entered while waiting. The transmit function will know to begin transmission when the previous transmission exits active (per number 2 above).

Run-time Driver Configuration Options

The UART driver has compile-time options as well as run-time configurable options. The runtime configuration options are specified by populating a tUARTPort structure in the application, and passing this structure to the driver when opening the driver. A tUARTPort structure is required when opening the UART driver. The tUARTPort structure must be available in memory whenever the driver is open. This is a result of the fact that the UART driver references this structure at runtime to find the callback functions for receive handling and error handling. However, the driver does not modify the data in the structure at any time, and as such, the structure may be placed in a read-only memory section (such as C const memory). These parameters are considered runtime adjustable because the parameters may be modified by the application if the UART driver is closed first, then re-opened. For example, the application may change the UART baud rate by closing the port, changing the baud rate options on the tUARTPort structure, and re-opening the port. Note that the .peripheralParameters member of the tUARTPort structure is a EUSCI_A_UART_initParam structure from the MSP430 Driver Library.

MemberDescriptionValid Values
bool (*pbReceiveCallback)(uint8_t)pbReceiveCallback is a function pointer that may point to a receive event handler. If no receive handling is required, initialize this member to 0 (null).Null, or a valid function address.
bool (*pbErrorCallback)(uint8_t)pbErrorCallback is a function pointer that may point to an error event handler. If no error handling is required, initialize this member to 0 (null).Null, or a valid function address.
.peripheralParameters.selectClockSourceThis member specifies the clock source for the eUSCI_A peripheral.EUSCI_A_UART_CLOCKSOURCE_SMCLK, EUSCI_A_UART_CLOCKSOURCE_ACLK
.peripheralParameters.clockPrescalarThis member specifies the eUSCI_A clock prescalar. This affects the baud rate.0-65535
.peripheralParameters.firstModRegThis member specifies the eUSCI_A first stage modulation. This affects the baud rate.0-15
.peripheralParameters.secondModRegThis member specifies the eUSCI_A second stage modulation. This affects the baud rate.0-255
.peripheralParameters.parityThis member specifies the UART parity mode.EUSCI_A_UART_NO_PARITY, EUSCI_A_UART_ODD_PARITY, EUSCI_A_UART_EVEN_PARITY
.peripheralParameters.msborLsbFirstThis member specifies the transmission bit order.EUSCI_A_UART_LSB_FIRST, EUSCI_A_UART_MSB_FIRST
.peripheralParameters.numberofStopBitsThis member specifies the number of stop bits.EUSCI_A_UART_ONE_STOP_BIT, EUSCI_A_UART_TWO_STOP_BITS
.peripheralParameters.uartModeThis member specifies the UART mode.EUSCI_A_UART_MODE, EUSCI_A_UART_IDLE_LINE_MULTI_PROCESSOR_MODE, EUSCI_A_UART_ADDRESS_BIT_MULTI_PROCESSOR_MODE, EUSCI_A_UART_AUTOMATIC_BAUDRATE_DETECTION_MODE
.peripheralParameters.overSamplingThis member specifies whether UART oversampling is enabled.EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION, EUSCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION

Using the Driver: Opening and Closing the Driver

Opening and closing of the UART driver is accomplished through the following function calls:

DescriptionDeclaration
Open the UART Portextern void UART_openPort(const tUARTPort *pPort)
Close the UART Portextern void UART_closePort(void)

The UART driver is opened and initialized by a call to UART_openPort(), which is passed a completed tUARTPort structure. The tUARTPort structure must be populated by the application. It may be placed in read-only memory, as the UART API does not modify the structure at any time; rather, it only references it. It is important that the structure be left in memory at the address given when UART_openPort() is called, as the UART API will reference this structure to access callback functions when the port is open. If the memory must be freed, first close the UART port with a call to UART_closePort().

A call to UART_closePort() will disable the UART port and its associated eUSCI_A peripheral, halting all Rx/Tx interrupt activity. After the port is closed, the event handlers will no longer be called, and the tUARTPort structure memory may be released to the application.

Using the Driver: Transmitting Data

Data transmission is accomplished through the following function calls:

DescriptionDeclaration
Transmit a Bufferextern void UART_transmitBuffer(const uint8_t *pBuffer, uint16_t ui16Length)
Transmit a Byteextern void UART_transmitByteImmediately(uint8_t ui8Data)
Get Port Statusextern uint8_t UART_getPortStatus(void)

Transmit operations are interrupt driven. To initiate a transmission after opening the UART port, make a call to UART_transmitBuffer(). This function is simply passed a pointer to the buffer to transmit, and the length of the buffer to transmit (specified in bytes). If the eUSCI_A peripheral is available, transmission will begin immediately and the function will return. If the peripheral is still busy sending a previous transmission, it will block (in a low power mode, if specified in UART_Definitions.h) until the previous transmission is complete.

lib_comm_uartdrivertxflow.png
UART Transmit Flow Diagram

The transmit operation uses the buffer memory that was pointed to in the UART_transmitBuffer() function - it does not perform a data copy in the name of efficiency. As such, that memory must not be modified during the transmission, or the transmission will be invalid. To detect when transmission has completed and the memory is again available, it is possible to check the UART port status via a call to UART_getPortStatus(). If an application will be re-using buffer space, it is best to employ a ping-pong buffer strategy so that a new packet may be assembled while a previous packet is being sent.

A call to UART_getPortStatus() returns one of the values enumerated by tUARTStates. The options are described below.

Port Status OptionDescription
eUARTIsClosedThe UART driver is not currently open, and is neither receiving nor transmitting data.
eUARTIsIdleThe UART driver is currently open, but is not in the process of transmitting a buffer.
eUARTIsTransmittingThe UART driver is currently open, and is in the process of transmitting a buffer.

If only a single byte is going to be sent, it is possible to send it immediately via the UART_transmitByteImmediately() function. This function sends a single byte as soon as the eUSCI_A peripheral transmit buffer is available. It will block until the buffer is available. If a transmission started by a call to UART_transmitBuffer() is in progress, this transfer may finish first as it is interrupt-driven. The UART_transmitByteImmediately() function is mainly available to be used in terminal emulator applications, where sent ASCII characters are echoed back to be visible to the user.

Using the Driver: Receiving Data

Received data is passed to the application through the use of the receive callback. The UART driver will call a user-defined function every time a new byte is received from the UART peripheral, passing that byte to the receive callback. It is then up to the application to decide how to handle the data. It is recommended to use a ring-buffer FIFO queue to handle buffering incoming data. The data may then be extracted and processed in a background process. The receive event handler is called from the UART ISR in the UART driver, and as such, the event handler should be kept as short as possible. It is recommended to follow the same practice used to write ISR's when writing event handlers.

The receive callback function is linked to the driver by placing its address in the pbReceiveCallback member of the tUARTPort structure. If the application does not wish to listen for receive events, the receive callback pointer in the tUARTPort structure may be initialized to null. Receive operations are performed entirely out of the ISR. The receive operation ISR flow is shown below. Errors are checked for whenever a receive interrupt is serviced.

lib_comm_uartdriverrxflow.png
UART Receive Flow Diagram

Using the Driver: Error Handling

The UART driver employs basic UART error detection to alert the application when something has gone wrong. The two errors detected by the driver are:

  1. UART Receive Buffer Overrun. This occurs if a new byte is placed into the eUSCI_A UART receive buffer before a previous byte was read out. This occurs when there is not enough CPU bandwidth to read out data at the rate data is coming into the peripheral. To resolve this error during development, it is necessary to either: slow down the rate at which data is received, whether through delays between bytes or a lower baud rate, or to increase the CPU availability by shortening interrupt handlers or increasing the CPU clock frequency. The enumerated error flag is eUARTOverrunError.
  2. UART Framing Error. This typically occurs if there is a baud rate mismatch between devices. The enumerated error flag is eUARTFramingError. The driver alerts the application via the error callback function. This works similar to the receive callback, except the error code is passed rather than the received byte. The error callback function is called from the UART ISR, and as such, it should be kept as short as possible. The error callback function is linked to the driver by placing its address in the pbErrorCallback member of the tUARTPort structure. If the application does not wish to listen for error events, the error callback pointer in the tUARTPort structure may be initialized to null.

I2C Slave Driver

The MSP430 eUSCI_B I2C slave driver provides a simple I2C slave API to MSP430 applications, enabling interrupt-driven I2C read/write operations as well as bus timeout detection and driver error handling. This User's Guide provides an overview of the driver's features, architecture, and API. This document assumes that the reader is familiar with MSP430 MCU architecture, as well as embedded C programming concepts and basic I2C principles. Note: From this point forward, the I2C slave driver will simply be referred to as "the driver."

Related Documents

This guide should be used with the following additional supporting documentation. The MSP430 Driver Library API is not specific to this driver, and has its own documentation.

  • MSP430 Driver Library (DriverLib) User's Guide
  • The relevant MSP430 Family User's Guide

Purpose of the Driver

The driver enables quick development of MSP430 applications where the MSP430 itself is a slave device to some other master in a larger embedded system. This is a common application, as MSP430 microcontrollers are often a secondary MCU to a larger host MCU or host MPU.

The driver is structured to be flexible, enabling many different applications. It is capable of providing a register-file interface to a host processor, similar to a sensor or an EEPROM. It may also be used as a bulk-transfer interface to a host.

Driver Features

The key features provided by the driver are listed below.

  • Interrupt-driven I2C slave software state machine
  • I2C Write (master transmitter, slave receiver) event callback
  • I2C Read (master receiver, slave transmitter) buffer assignment
  • Support for I2C start/stop/start as well as I2C start/repeated-start sequences
  • I2C error/warning reporting via an error callback function
  • Operation in LPM3 between I2C interrupts when timeout feature is used
  • Operation in LPM4 between I2C interrupts when timeout feature is not used
  • Open drain slave request signal (to alert the master to service the slave)
  • Slave request timeout capability to prevent an application stall
  • I2C bus transaction timeout capability to prevent an application stall

Driver Overview

The driver is provided in C source code. It consists of 3 base driver source files, plus an additional 3 source files related to the I2C timeout detection feature (which is a compile-time option).

Base Driver:

  • I2CSlave_Definitions.h (Configuration header file)
  • I2CSlave.h (API header file)
  • I2CSlave.c (API implementation file)

Function Timer (used for I2C timeout detection):

  • FunctionTimer_Definitions.h (Configuration header file)
  • FunctionTimer.h (API header file)
  • FunctionTimer.c (API source file)

The I2CSlave_Definitions.h and FunctionTimer_Definitions.h files contain the compile-time options for each component.

The driver requires the following MCU hardware resources:

Base Driver:

  • One eUSCI_B peripheral instantiation
  • Two device pins (UCBxSDA and UCBxSCL), where 'x' represents the selected eUSCI_B instance

Slave Request Feature (Optional):

  • One additional device pin (Any digital IO for the slave request line feature).

Timeout Feature (Optional):

  • One TIMER_A peripheral instantiation with at least 2 capture-compare units (CCR0 and CCR1)

The driver operation is based upon a software state machine that keeps track of the current driver state. There are four possible states: closed, idle, read, and write. Since the driver implements an I2C slave, it is important to be clear on the naming conventions for read and write. An I2C bus write is a receive operation from the perspective of an I2C slave, as the bus master is writing to the slave. Similarly, an I2C bus read is a transmit operation from the perspective of the slave. The state machine implemented by the driver is depicted below.

lib_comm_i2cslavestatemachine.png
I2C Slave Driver State Machine

As shown, state changes between idle, read, and write are controlled solely by the I2C bus master. It is possible for the slave to close the driver at any time, however.

Driver Overview: I2C Receive Operations (I2C Bus Write)

The driver enters the "write" state (eI2CSlaveIsBeingWrittenTo) whenever the bus master sends a start or restart condition to the driver's 7-bit I2C address with the R/_W bit cleared. In this state, the driver is receiving data. Data is received into the buffer memory that was specified by the user when the driver was opened. If the bus master attempts to write data beyond the length of the receive buffer, the data is ignored and an error callback alerts the application. The write state is cleared when a stop condition or a restart condition is issued. When this happens, the driver calls a user-specified callback function for the received data to be handled.

Driver Overview: I2C Transmit Operations (I2C Bus Read)

The driver enters the "read" state (eI2CSlaveIsBeingRead) whenever the bus master sends a start or restart condition to the driver's 7-bit I2C address with the R/_W bit set. In this state, the driver is transmitting data to the master by loading data from the latest transmit buffer memory that was linked to the driver. Note that the buffer memory must have been pre-loaded before the read state was entered. If the bus master attempts to read data before any buffer was specified or if it attempts to read out more data than was made available in the buffer, the driver will clock out an invalid byte, which may be specified as a compile-time option. The read state is cleared when a stop condition or a restart condition is issued. When this happens, the driver wakes the CPU from any low power modes in case it was pending on the completion of a read operation.

Driver Overview: Slave Request Feature

The driver provides a mechanism for alerting the bus master that it wishes to communicate. This is helpful in many applications, as the slave has no way to initiate communication on the I2C bus. The slave request feature is implemented as an open-drain request line. The request line may be any digital IO on the MCU. The request line should be pulled up to VCC via a pull-up resistor, just like an I2C bus line. The driver controls whether the request line IO is left tri-stated (Hi-Z), or whether it is pulled to a logic low (sinking current to ground through the pullup resistor and the digital IO). The driver API contains a function for "pulling" the request line, which waits for a I2C bus response from the master (or a timeout) before returning.

Driver Overview: Timeout Feature (Function Timer)

The driver provides a timeout mechanism for preventing application lock-ups. To enable timeouts in a low-power way, a dedicated hardware timer is used to set limits on how long specific driver operations may take. The operations that may have timeouts placed on them are:

  1. I2C Slave Request Timeout. The slave request feature described in section 2.3.3 signals the bust master to communicate with the slave. That slave request API function is designed to block until the master starts communication (via a I2C bus read start condition). If the timeout feature is enabled, the slave request function will time out automatically if the master does not start a read within the specified timeout period.
  2. I2C Transaction Timeout. The transaction timeout places a limit on how long any I2C bus transaction may take. An I2C transaction time is defined between the time a start or restart condition is issued until a restart or stop condition is issued. The goal of the transaction timeout is to detect scenarios where the bus master went down in the middle of transmission, or an I2C bus disconnection.

The timeout feature is implemented by the Function Timer module, which is a completely independent software module. As transaction monitoring may be a part of the application layer (perhaps with a standard watchdog timer), the timeout feature may be excluded completely at compile time. The Function Timer module essentially calls a predefined function in the foreground after a defined delay in timer cycles. That function can then take actions such as cancelling a slave request or resetting the driver.

Compile-time Driver Configuration Options

The compile-time configuration options are set in the I2CSlave_Definitions.h and FunctionTimer_Definitions.h files. These options are described below.

I2C Slave Enable Definition

ParameterFileValid Values
I2CSLAVE__ENABLEI2CSlave_Definitions.htrue, false

The I2C Slave Enable compile-time option selects whether the driver is enabled or disabled. This provides a mechanism to exclude the driver from the compilation process. To include the driver, define I2CSlave__ENABLE as true. Else, define it as false.

I2C eUSCI_B Peripheral Selection Definition

ParameterFileValid Values
I2CSLAVE__EUSCI_B_PERIPHERALI2CSlave_Definitions.hEUSCI_B0_BASE

The I2C eUSCI_B peripheral selection allows easy selection of which eUSCI_B instance to associate with the I2C driver. This provides flexibility during design if a pin-mux change is necessary. A valid base address must be provided.

I2C Slave Address Definition

ParameterFileValid Values
I2CSLAVE__ADDRESSI2CSlave_Definitions.h0x00 to 0x7F

The I2C slave address specifies the 7-bit I2C bus address associated with this device.

I2C Slave LPM Mode Definition

ParameterFileValid Values
I2CSLAVE__LPMx_bitsI2CSlave_Definitions.hLPM0_bits, LPM1_bits, LPM2_bits, LPM3_bits, LPM4_bits

I2C communication may be performed in a low power mode. No clocks need to be enabled on the MSP430 to send or receive bytes on a eUSCI_B peripheral when in I2C slave mode. This is a benefit of the bit clock being provided by the bus master. The I2C Slave LPM mode control is used in the following ways.

  1. Receive callback functions may trigger an active exit from the I2C ISR after the completion of a receive event (triggered by either a re-start or a stop condition). If a receive callback returns true to the driver, the status register bits specified by the I2C slave LPM mode will be cleared when exiting.
  2. A call to I2CSlave_setTransmitBuffer() will block if a previous read is requested or in progress. If a low power mode is specified via the I2C Slave LPM Mode definition, the block will take place in that low power mode- otherwise, it will block in active mode.

I2C Slave Invalid Byte Definition

ParameterFileValid Values
I2CSLAVE__INVALID_BYTEI2CSlave_Definitions.h0x00 to 0xFF

The invalid byte specifies the byte that is transmitted to the master (during an I2C read) if the master attempts to read beyond the length of the transmit buffer provided to the driver.

I2C Slave Timeout Enable Definition

ParameterFileValid Values
I2CSLAVE__TIMEOUT_ENABLEI2CSlave_Definitions.htrue, false

The slave timeout enable controls whether the timeout feature of the driver is included. The timeout feature provides the ability to set a maximum amount of time a certain I2C slave task may take before it fails. The two tasks that may be monitored with a timeout is the I2C slave request and any I2C transaction. Note that enabling the timeout feature requires the inclusion of the FunctionTimer source files, a Timer_A instance, and additional memory.

I2C Slave Request Timeout Cycles Definition

ParameterFileValid Values
I2CSLAVE__REQ_TIMEOUT_CYCLESI2CSlave_Definitions.h0x0000 - 0xFFFF

The I2C slave request timeout cycles specifies the timeout period for the I2C request timeout, in units of the function timer clock period. This is the amount of time the driver will wait after pulling the slave request line low before the transaction fails out. The bus master must respond to the slave request within this amount of time.

I2C Slave Transfer Timeout Cycles Definition

ParameterFileValid Values
I2CSLAVE__TXFR_TIMEOUT_CYCLESI2CSlave_Definitions.h0x0000 - 0xFFFF

The I2C slave transfer timeout cycles specifies the timeout period for any I2C transaction, in units of the function timer clock period. I2C transactions are timed from start condition to stop condition, or start condition to re-start condition.

I2C Slave Request Enable Definition

ParameterFileValid Values
I2CSLAVE__REQ_ENABLEI2CSlave_Definitions.htrue, false

The request enable controls whether the I2C request line feature is included in the driver build. The I2C request line provides a mechanism for the slave to signal the master that it would like to communicate.

I2C Slave Request Line Definition

ParameterFileValid Values
I2CSLAVE__REQ_POUTI2CSlave_Definitions.hPxOUT
I2CSLAVE__REQ_PDIRI2CSlave_Definitions.hPxDIR
I2CSLAVE__REQ_MASKI2CSlave_Definitions.hBIT0 - BIT7

The I2C slave request line is defined by three values- the port output register, the port direction register, and the pin mask. These do not need to be defined if the slave request enable is set to false.

Function Timer Enable Definition

ParameterFileValid Values
FUNCTIONTIMER__ENABLEFunctionTimer_Definitions.htrue, false

The function timer enable controls whether the function timer is included in the driver build. If the I2C slave timeout feature is not used, memory is conserved when the function timer is disabled (set to false).

Function Timer Peripheral Definition

ParameterFileValid Values
FUNCTIONTIMER__PERIPHERALFunctionTimer_Definitions.hTIMER_A0_BASE, TIMER_A1_BASE, TIMER_A2_BASE, TIMER_A3_BASE

The function timer peripheral stores the base address of the Timer_A instance that should be associated with the function timer. This instance must have at least two capture compare units (CCR0 an CCR1).

Function Timer Clock Source Definition

ParameterFileValid Values
FUNCTIONTIMER__CLOCKFunctionTimer_Definitions.hTASSEL__SMCLK, TASSEL__ACLK

The function timer clock determines the resolution of the function timer, as well as the maximum delay. Sources include SMCLK and ACLK. Note that if the clock source is changed, the effective function timer delay may change.

Function Timer Clock Divider Definition

ParameterFileValid Values
FUNCTIONTIMER__DIVIDERFunctionTimer_Definitions.hID__1, ID__2, ID__4, ID__8

The function timer clock divider divides down the source clock (which was specified above). Note that if the clock divider is changed, the effective function timer delay may change.

Function Timer Extended Clock Divider Definition

ParameterFileValid Values
FUNCTIONTIMER__EXDIVIDERFunctionTimer_Definitions.hTAIDEX_0 to TAIDEX_7

The function timer extended divider allows an additional second divider to be added in series with the standard divider. Note that if the extended clock divider is changed, the effective function timer delay may change.

Function Timer LPM Clear Definition

ParameterFileValid Values
FUNCTIONTIMER__LPM_CLEARFunctionTimer_Definitions.hLPM0_bits, LPM1_bits, LPM2_bits, LPM3_bits, LPM4_bits

The function timer LPM clear controls which LPM bits are cleared upon exit from a function called in the foreground by the function timer. This should be set to enable CPU wake-up after a timeout event. The function timer feature will never use these bits to enter into a low power mode- it will only use them to exit.

Run-time Driver Configuration Options

The driver's runtime configuration options are specified by populating a tI2CSlavePort structure in the application, and passing this structure to the driver when opening the driver. The tI2CSlavePort structures are discussed below.

MemberDescriptionValid Values
bool (*pbReceiveCallback)(uint16_t)pbReceiveCallback is a function pointer that may point to a receive event handler in the application. If no receive handling is required, initialize this member to 0 (null).Null, or a valid function address.
void (*pvErrorCallback)(uint8_t)pvErrorCallback is a function pointer that may point to an error event handler in the application. If no error handling is required, initialize this member to 0 (null).Null, or a valid function address
ui16ReceiveBufferSizeThis member stores the size of the receive buffer pointed to by pReceiveBuffer.0x00 to 0xFF
pReceiveBufferThis member is a pointer to the I2C receive buffer.A valid pointer
bSendReadLengthFirstWhen set to true, this flag configures the driver to always load the length of the transmit buffer as the first data byte read by the bus master. This is useful when variable length bulk packets are being read out by the master, and the master needs to know how many bytes to read from the slave.true, false

If the timeout feature is included in the driver build, a function timer run-time configuration also happens- but it is handled automatically by the driver when the I2C port is opened. The function timer runtime configuration structure (tFunctionTimer) is outlined below for completeness, but application does not need to know any of the function timer details.

MemberDescriptionValid Values
ui16FunctionDelay_AThis member specifies the length of the delay (in timer clock cycles) before function A is called.0x0000 to 0xFFFF
bool (*pbFunction_A)(void)pbFunction_A is a function pointer to function A.A valid function pointer, else null.
ui16FunctionDelay_BThis member specifies the length of the delay (in timer clock cycles) before function B is called.0x0000 to 0xFFFF
bool (*pbFunction_B)(void)pbFunction_B is a function pointer to function B.A valid function pointer, else null.

Using the Driver: Opening and Closing the Driver

Opening and closing of the I2C slave driver is accomplished through the following function calls:

DescriptionDeclaration
Open the I2C Slave Portextern void I2CSlave_openPort(const tI2CSlavePort *pPort)
Close the I2C Slave Portextern void I2CSlave_closePort(void)

The driver is initialized and made ready for use by placing a call to I2CSlave_openPort(), which is passed a completed tI2CSlavePort structure. The tI2CSlavePort structure must be populated by the application. This structure is not modified by the driver at any time, and as such, it may be placed in read-only memory such as a C const memory section. It is important that the structure be left in memory at the original address that is passed to I2CSlave_openPort(), as the driver will reference this structure to access callback functions when the port is open. If the memory must be freed, first close the driver with a call to I2CSlave_closePort(). A call to I2CSlave_closePort() will disable the driver and its associated eUSCI_B peripheral, halting all I2C slave activity. If the timeout feature was included in the build, I2C_closePort() disables the function timer module that drives the timeout feature. After the port is closed, the event handlers will no longer be called and the tI2CSlave structure memory may be released to the application.

Using the Driver: Transmitting Data (I2C Read Operation)

Data transmission is accomplished through the following function calls:

DescriptionDeclaration
Set Transmit Bufferextern void I2CSlave_setTransmitBuffer(uint8_t *pBuffer, uint16_t ui16Length)
Set Request Flagextern void I2CSlave_setRequestFlag(void)
Get Port Statusextern uint8_t I2CSlave_getPortStatus(void)

An I2C slave cannot push data out onto the bus on its own- rather, a master must address the slave and clock out the data. Therefore, the data buffer must be made available to the driver before the bus master attempts to read it. The application may pass a pointer and a length (in bytes) to the I2CSlave_setTransmitBuffer() function. This function will block if a current transaction is in progress, and will return when the transmit buffer pointer and length have been updated. If no transmit buffer is provided and the bus master attempts to read from the slave, it will read out the invalid character (a compile-time option).

The I2CSlave_setTransmitBuffer() function does not perform a copy of the buffer- rather, it just stores its location and length. This is very valuable for "register" applications, where the application just updates the buffer, and I2CSlave_setTransmitBuffer() only needs to be called once during initialization. Repeated reads from the bus master will re-read the same buffer until I2CSlave_setTransmitBuffer() is called again. On the flipside, it is important that the application does not overwrite the transmit buffer space until transmission is complete.

If the slave request feature is enabled, the master may be signaled by calling the I2CSlave_setRequestFlag() function. This function immediately pulls the request line, then waits for the master to begin performing a read operation before it returns (or times out).

The driver state may be obtained by the application at any time by calling I2CSlave_getPortStatus(). This function returns the current I2C Slave state. The possible states are enumerated by tI2CSlaveStates. The possible enumerations are listed below.

Port Status OptionDescription
eI2CSlaveIsClosedThe driver is closed. All functions besides I2CSlave_openPort() and I2CSlave_getPortStatus() are invalid.
eI2CSlaveIsIdleThe driver is open, but no I2C transactions are currently in progress.
eI2CSlaveIsBeingReadThe driver is open and the bus master is reading data. The driver loads data from the transmit buffer until all data has been sent, then it loads the invalid byte.
eI2CSlaveIsBeingWrittenToThe driver is open and the bus master is writing data. The written data is placed into the receive buffer if there is space. At the end of this state, the receive callback in the application is called.

Using the Driver: Receiving Data (I2C Write Operation)

The bus master may start a write operation at any time. The driver will buffer incoming data into the receive buffer. When the transaction is complete, the application receive callback is called. The callback is passed the size of the data received from the master (in bytes). Since it is called from the driver interrupt service routine, no other interrupts will be processed and the eUSCI_B may be stretching the bus clock line until the driver returns from the receive callback. This provides the callback function the opportunity to process any received data and update the transmit buffer before the master may continue with communication.

Using the Driver: Error Handling

The driver provides an error callback to alert the application if there is a problem with the driver. The error callback, when called, passes a value indicating the error that occurred. The possible errors are listed below.

Error CodeDescription
eI2CSlaveTransmitRequestTimeoutThis code indicates that a slave request to the bus master was not serviced within the timeout window, and the request timed out.
eI2CSlaveTransactionTimeoutThis code indicates that an I2C transaction was taking longer than the timeout window, and the transaction timed out. This error also results in a driver reset.
eI2CSlaveReceiveBufferFullThis code indicates that the receive buffer was full during the last read transaction, and data from the bus master was lost.
eI2CSlaveWasReadBeyondTransmitBufferThis code indicates that the bus master attempted to read data from the slave beyond the valid transmit buffer length (indicating the master was reading invalid bytes).

EMC Module

The CapTIvate™ Touch Library includes an EMC module in the advanced layer. This module provides processing plug-ins to the touch layer to enhance the robustness of a system in the presence of noise.

EMC Module Background

While the CapTIvate™ peripheral provides a significant feature set for dealing with electromagnetic compatibility issues on its own, some amount of digital signal processing is still required to process the raw data into usable values. The EMC module in the CapTIvate™ Software Library fills that need.

EMC Module Overview

The EMC module provides configurable algorithms to the base touch layer of the software library.

EMC Module Features

The EMC module provides the following feature set:

  1. Multi Frequency Processing (MFP) Algorithm for resolving a raw measurement set of 4 frequencies into a single, usable measurement result
  2. Multi Frequency Calibration Algorithm for ensuring that accurate, usable calibration values are obtained during the sensor calibration process, even in a noisy environment
  3. Element Filtered Noise Level Tracking to keep a record of the noise level observed on each element
  4. Global Filtered Noise Level Tracking to keep a global value of the noise level observed on all self-capacitance elements in the system for use in global dynamic threshold adjustment
  5. Dynamic Threshold Adjustment Algorithm for calculating a threshold adjustment factor to compensate for increased sensitivity of self-capacitance sensors in the presence of noise

Using the EMC Module

When using the CapTIvate™ Software Library, the EMC module functions are not called by the application. Rather, noise immunity is enabled for a user configuration at a top level. When noise immunity is enabled in the library for a design, several base library functions for calibration and sensor measurement are replaced with EMC versions of the same functions. Those functions utilize the processing algorithms provided by this layer to add a level of robustness to a design.

Enabling Noise Immunity (EMC) Features

It is best to enable noise immunity via the CapTIvate™ Design Center. The controller customizer has a compile-time option for noise immunity. Selecting this option sets the CAPT_CONDUCTED_NOISE_IMMUNITY_ENABLE compile-time definition in the CAPT_UserConfig.h file to true when source code is generated.

Function Replacements with Noise Immunity Enabled

When the compile-time option is set, the manager layer will make calls to EMC versions of functions rather than the standard versions. Below is a mapping of which functions are replaced:

Top Level Functions
DescriptionStandard FunctionEMC Function
Calibrate a SensorCAPT_calibrateSensor()CAPT_calibrateSensorWithEMC()
Update a SensorCAPT_updateSensor()CAPT_updateSensorWithEMC()

These top level functions are called by the application via abstractions in CAPT_Manager.

Supporting Functions
DescriptionStandard FunctionEMC Function
Process a CycleCAPT_processFSMCycle()CAPT_processCycleWithEMC()
Update Prox/Touch StatusCAPT_updateProx, CAPT_updateTouchCAPT_updateSelfElementProxTouchWithEMC(), CAPT_updateProjElementProxTouchWithEMC()

These functions are called inside the touch layer, and are not directly called by the application.

EMC Module Configuration

The EMC Module is configured through the tEMCConfig structure. The EMC layer only reads from this structure, so the configuration may be kept in non-volatile read-only memory if desired. A default configuration is provided in the CAPT_UserConfig module as shown below:

const tEMCConfig g_EMCConfig =
{
.bEnableDynamicThresholdAdjustment = true,
.bDynamicThresholdAdjustmentIsGlobal = true,
.ui16CalibrationNoiseLimit = 10,
.ui8CalibrationTestSampleSize = 8,
.ui8NoiseThreshold = 20,
.ui8MaxRelThreshAdj = 76,
.ui8NoiseLevelFilterEntryThresh = 32,
.ui8NoiseLevelFilterExitThresh = 0,
.ui8LocalNoiseLevelFilterDown = 5,
.ui8LocalNoiseLevelFilterUp = 1,
.ui8GlobalNoiseLevelFilterDown = 5,
.ui8GlobalNoiseLevelFilterUp = 1,
.coeffA = _IQ31(0.0065),
.coeffB = _IQ31(0.0100)
};

The default values were selected by bench characterization and have proven effective for several different sensing panels. However, for certain applications and/or certain noise environments, it may be necessary to adjust some of the parameters. To implement a custom configuration, simply create a new tEMCConfig structure with the desired values, and pass it's address to CAPT_loadEMCConfig(). Note that the CapTIvate™ starter project makes this call in CAPT_initUI() in CAPT_Manager. You may either override this call be calling the function again after CAPT_initUI() is called, or you may edit CAPT_initUI() directly.

CAPT_loadEMCConfig(&g_EMCConfig);

The configuration structure has the following parameters:

MemberDescriptionDefault ValueValid Values
bEnableDynamicThresholdAdjustmentEnable or disable dynamic threshold adjustment. Note that dynamic threshold adjustment only applies to self capacitance sensors in either case.truetrue, false
bDynamicThresholdAdjustmentIsGlobalEnable global dynamic threshold adjustment. When true, a global noise level that is representative of the highest noise level seen in the system is tracked. When the adjusted threshold is calculated for each element, if the global noise is greater than an individual element's noise, then the global value will be used to determine the threshold adjustment.truetrue, false
ui16CalibrationNoiseLimitDuring calibration, the noise limit is the maximum absolute noise allowed at a given frequency for any element in a sensor before the calibration values for that frequency are deemed invalid. Once a frequency is invalidated, its calibration values will be sourced from the closest valid frequency. If all 4 frequencies are noisy during calibration, the calibration error bit is set.108191
ui8CalibrationTestSampleSizeDuring calibration, the number of test samples that are taken for each element at each frequency. The peak-to-peak spread of the test set is compared against the ui16CalibrationNoiseLimit to determine if the calibration is valid.8255
ui8NoiseThresholdThe relative (% of the LTA with 0%=0 and 100%=128) value that specifies at what element noise level the bNoiseDetected bit in the element structure (and the bSensorNoiseState bit in the parent sensor structure) are set.200-128
ui8MaxRelThreshAdjThe relative (% of the LTA with 0%=0 and 100%=128) value that specifies the maximum threshold after adjustment. This may be used to place a cap on how much adjustment may be applied.760-128
ui8NoiseLevelFilterEntryThreshIf the noise level is increasing (low to high vector) and the new noise sample is below this value, the global and local value filters will be disabled to allow for rapid tracking. A value of '0' keeps the filters enabled at all times.320-128
ui8NoiseLevelFilterExitThreshIf the noise level is decreasing (high to low vector) and the new noise sample is below this value, the global and local value filters will be disabled to allow for rapid tracking. A value of '0' keeps the filters enabled at all times.00-128
ui8LocalNoiseLevelFilterDownThe filter beta applied to the local (element) filtered noise value when the new noise sample is lower than the filtered noise value.50-15
ui8LocalNoiseLevelFilterUpThe filter beta applied to the local (element) filtered noise value when the new noise sample is higher than the filtered noise value.10-15
ui8GlobalNoiseLevelFilterDownThe filter beta applied to the global filtered noise value when the new noise sample is lower than the filtered noise value.50-15
ui8GlobalNoiseLevelFilterUpThe filter beta applied to the global filtered noise value when the new noise sample is higher than the filtered noise value.10-15
coeffAThe 'A' coefficient in the dynamic threshold adjustment algorithm calculation.0.00650-0.999999999
coeffBThe 'B' coefficient in the dynamic threshold adjustment algorithm calculation.0.01000-0.999999999

EMC Module Algorithms

Multi Frequency Processing (MFP) Algorithm

The multi frequency processing algorithm is implemented in the CAPT_resolveMultiFreqSet() function. When noise immunity is enabled, each element is measured at four different conversion frequencies to gather more data in the presence of noise. The algorithm is then applied to the four raw measurements. The output of the algorithm is a single, composite measurement and a noise level. The composite measurement is then used by the higher levels of the library just like a raw sample normally would. The noise level is used to update each element's filtered noise level.

Dynamic Threshold Adjustment (DTA) Algorithm

The dynamic threshold adjustment (DTA) algorithm is implemented in the CAPT_computeRelativeNoiseComp() function. The algorithm calculates threshold adjustments based on the amount of noise seen in a history of measurements. It relies on a filtered relative noise value as an input, and it calculates the corresponding threshold adjustment to be applied for proximity and touch detection. The threshold adjustment is calculated based on a polynomial model as shown below, where 'x' is the relative noise value and 'y' is the corresponding relative threshold adjustment. The polynomial allows for greater adjustment at higher noise levels.

lib_dta_formula.png
DTA Formula

This formula with the default values provides the adjustment curve shown below. A linear (A=0; B=0.5) curve is also shown for reference.

lib_dta_formula_plot.png
DTA Default Value Response Curve