giuliomoro
I was gonna clean it up and gener-ify it today, I would appreciate any help you can give
render:
/*
____ _____ _ _
| __ )| ____| | / \
| _ \| _| | | / _ \
| |_) | |___| |___ / ___ \
|____/|_____|_____/_/ \_\
The platform for ultra-low latency audio and sensor processing
http://bela.io
A project of the Augmented Instruments Laboratory within the
Centre for Digital Music at Queen Mary University of London.
http://www.eecs.qmul.ac.uk/~andrewm
(c) 2016 Augmented Instruments Laboratory: Andrew McPherson,
Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack,
Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved.
The Bela software is distributed under the GNU Lesser General Public License
(LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt
*/
#include <Bela.h>
#include <cmath>
#include "I2C_MPR121.h"
// How many pins there are
#define NUM_TOUCH_PINS 12
// Define this to print data to terminal
#define DEBUG_MPR121
//#undef DEBUG_MPR121
// Change this to change how often the MPR121 is read (in Hz)
int readInterval = 60; ///////changed from 50 to 9 - about 5 ms worth of samples
//80 hz brings it down to 23
//120 hz is about 8.3 ms latency and 29% cpu
//100 hz is 10 ms latency and 26%
//50 hz runs about 18-20% now. ///////about 8% cpu reduction
//at 175 hz now seems to start dropping below 30% cpu, everything above holds at right about 34% at least upto to 44100 hz.
//175 hz is about 252 frames. 5.7 ms, Not too bad.
//50 hz is 882 frames, 20 ms
// Change this threshold to set the minimum amount of touch
int threshold = 40; //already set on the chip using setThreshold function
uint8_t touchThreshold = 12; //uint8 type - can be 0 - 256. Original class defaulted to 12 and 6, plus int 40 on sensorValue, so would have been
uint8_t releaseThreshold = 6; //grand total of....52 and ....46. winning. winning. whats my prize
// This array holds the continuous sensor values
//int sensorValue[NUM_TOUCH_PINS];
// This array holds pointers to the continuous sensor calculations converted to floats at intervals of the chosen period.
//float * sensorValue[NUM_TOUCH_PINS];
int numPins{ NUM_TOUCH_PINS };
float * sensorAmplitude[NUM_TOUCH_PINS]; //and do only one conversion to amplitude per chosen cycle, sensor value only used for amplitude anyway. FUCK.
float calculatedSample = 0; //send a reference of this variable to the MPR class.
//trust me, you won't regret it. Actually the class will extract the reference, just send the var var.
// ---- test code stuff -- can be deleted for your example ----
// 12 notes of a C major scale... ////////////cut number of pins in half, cpu usage drops by 10%,
////////////from 36% to 26%.
//float gFrequencies[NUM_TOUCH_PINS] = {261.63, 293.66, 329.63, 349.23, 392.00, 440.00};
//float gFrequencies[NUM_TOUCH_PINS] = {261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25, 587.33, 659.25, 698.25, 783.99};
// This is internal stuff for the demo
//float gNormFrequencies[NUM_TOUCH_PINS];
//float gPhases[NUM_TOUCH_PINS] = {0};
// ---- internal stuff -- do not change -----
I2C_MPR121 mpr121(numPins, touchThreshold, releaseThreshold, sensorAmplitude, calculatedSample, threshold); // Object to handle MPR121 sensing
AuxiliaryTask i2cTask; // Auxiliary task to read I2C
int readCount = 0; // How long until we read again...
int readIntervalSamples = 0; // How many samples between reads
//float staticPi = 2.0f * (float)M_PI; //static cast of pi constant
void readMPR121(void*);
bool setup(BelaContext *context, void *userData)
{
if(!mpr121.begin(1, 0x5A)) {
rt_printf("Error initialising MPR121\n");
return false;
}
i2cTask = Bela_createAuxiliaryTask(readMPR121, 50, "bela-mpr121");
readIntervalSamples = context->audioSampleRate / readInterval;
return true;
}
void render(BelaContext *context, void *userData)
{
//cpu 36% usage un modified
for(unsigned int n = 0; n < context->audioFrames; n++) {
// Keep this code: it schedules the touch sensor readings
if(++readCount >= readIntervalSamples) {
readCount = 0;
Bela_scheduleAuxiliaryTask(i2cTask); //read the pins, do comparisons/conversions, point sensor and amplitude array pointers
//to the float results.
}
/////////LEAVE read period at 50 and comment sound generation out for
//////reduction from 37% to 17% cpu - so can be optimized.
/*
float sample = 0.0;
// This code can be replaced with your favourite audio code
for(int i = 0; i < NUM_TOUCH_PINS; i++) {
float amplitude = sensorValue[i] / 400.f; //this line is converting the sensor value in integer amount to a floating point
//number then dividing it...12 times every sample. The sennsor is only read 1 every 882 frames unless
//it's changed. Lets do the calculation to float in the sensor class once every time
//needed and convert to amplitude there. For a grand prize total of savings I bet.
//AND WHATS BEHIND DOOR NUMBER 5?????
//.....well shit, glad you asked. That means there's only one sample to be calculated
//every "INTERVAL" frames, so really you're just EFFIN yourself in the A OVER AND OVER
//again. like 881 extra times. times 50. per second. you little pervert.
//Ok, the class will calculate the sample as well. Once every INTERVAL, DUMMY
// Prevent clipping
if(amplitude > 0.5)
amplitude = 0.5;
//sample += amplitude * sinf_neon(gPhases[i]);
sample += amplitude * sinf(gPhases[i]); //-------------->Changed sinf to sinf_neon for 10% cpu reduction
gPhases[i] += gNormFrequencies[i];
if(gPhases[i] > M_PI)
gPhases[i] -= staticPi;
// gPhases[i] -= 2.0f * (float)M_PI; //---->static convert in setup instead of dynamic cast
//no change - probably compiler optimized already?
}
*/
for(unsigned int ch = 0; ch < context->audioInChannels; ch++)
context->audioOut[context->audioInChannels * n + ch] = calculatedSample;
}
}
void cleanup(BelaContext *context, void *userData)
{ }
// Auxiliary task to read the I2C board
void readMPR121(void*)
{
mpr121.read();
/*
for(int i = 0; i < NUM_TOUCH_PINS; i++) {
sensorValue[i] = -(*mpr121.filteredData(i) - *mpr121.baselineData(i));
//Extra step for each calculation - threshold added to touch and release in i2c_mpr121 class
//sensorValue[i] -= threshold;
if(sensorValue[i] < 0)
sensorValue[i] = 0;
#ifdef DEBUG_MPR121
rt_printf("%d ", sensorValue[i]);
#endif
}
#ifdef DEBUG_MPR121
rt_printf("\n");
#endif
// You can use this to read binary on/off touch state more easily
//rt_printf("Touched: %x\n", mpr121.touched());
*/
}
/**
\example MPR121/render.cpp
Capacitive touch sensing with MPR121
---------------------------
This sketch allows you to hook up an MPR121 capactive touch sensing device
to Bela, for example the SparkFun Capacitive Touch Sensor Breakout - MPR121.
The breakout board gives you 12 electrode connections.
To get this working with Bela you need to connect the breakout board to the I2C
terminal on the Bela board. See the Pin guide for details of which pin is which.
The sensor data will then be available for you to use in the array
`sensorValue[NUM_TOUCH_PINS]`.
*/
MRP121.h:
/*
* MPR121 Bela demo
*
* Andrew McPherson
* Based on Adafruit library by Limor Fried/Ladyada
*/
#ifndef I2CTK_H_
#define I2CTK_H_
#include <I2c.h>
#include <Utilities.h>
#include <stdint.h>
#include <libraries/math_neon/math_neon.h>
#include <cmath>
#include <iostream>
typedef bool boolean;
#define NUM_TOUCH_PINS 12
#define MPR121_I2CADDR_DEFAULT 0x5A
#define MPR121_TOUCHSTATUS_L 0x00
#define MPR121_TOUCHSTATUS_H 0x01
#define MPR121_FILTDATA_0L 0x04
#define MPR121_FILTDATA_0H 0x05
#define MPR121_BASELINE_0 0x1E
#define MPR121_MHDR 0x2B
#define MPR121_NHDR 0x2C
#define MPR121_NCLR 0x2D
#define MPR121_FDLR 0x2E
#define MPR121_MHDF 0x2F
#define MPR121_NHDF 0x30
#define MPR121_NCLF 0x31
#define MPR121_FDLF 0x32
#define MPR121_NHDT 0x33
#define MPR121_NCLT 0x34
#define MPR121_FDLT 0x35
#define MPR121_TOUCHTH_0 0x41
#define MPR121_RELEASETH_0 0x42
#define MPR121_DEBOUNCE 0x5B
#define MPR121_CONFIG1 0x5C
#define MPR121_CONFIG2 0x5D
#define MPR121_CHARGECURR_0 0x5F
#define MPR121_CHARGETIME_1 0x6C
#define MPR121_ECR 0x5E
#define MPR121_AUTOCONFIG0 0x7B
#define MPR121_AUTOCONFIG1 0x7C
#define MPR121_UPLIMIT 0x7D
#define MPR121_LOWLIMIT 0x7E
#define MPR121_TARGETLIMIT 0x7F
#define MPR121_GPIODIR 0x76
#define MPR121_GPIOEN 0x77
#define MPR121_GPIOSET 0x78
#define MPR121_GPIOCLR 0x79
#define MPR121_GPIOTOGGLE 0x7A
#define MPR121_SOFTRESET 0x80
//define for debugging messages
//#define DEBUG_MPR121
//#define DEBUG_READ16//prints the loop results for filtered data
//#define DEBUG_READ8//prints the loop results for baseline
//#define DEBUG_AMPARRAY
class I2C_MPR121 : public I2c
{
public:
// Hardware I2C
//I2C_MPR121();
I2C_MPR121(int numTouchPinsUsed = 12, uint8_t touchThreshold = 12, uint8_t releaseThreshold = 6, float * amplitude[12] = {}, float sample = 0, int threshold = 40 ) //added default initialize to 12 pins 10-22-21 - Zeke
: m_numTouchPinsUsed{ numTouchPinsUsed }, m_touchThreshold{ touchThreshold }, m_releaseThreshold{ releaseThreshold }, m_threshold{ threshold }
{
zeroArrays();
for(int i = 0; i < 12; i++) {
m_filteredAddress[i] = MPR121_FILTDATA_0L + i*2; //These two arrays store the addresses of respective data. not respectable data.
m_baselineAddress[i] = MPR121_BASELINE_0 + i;
}
amplitude = m_amplitudePtr; //point some gods dammmmmed pointers at some more mother freaking all that is unholy pointers.
//at least now it's on autopilot. just fill up m_amplitude, ok buddy? no one has to get hurt.
//Now this shit is all basically running behind the scenes, like vip and shit.56
m_samplePtr = &sample;
for(int i = 0; i < NUM_TOUCH_PINS; i++) {
gNormFrequencies[i] = 2.0 * M_PI * gFrequencies[i] / 44100;
}
}
bool zeroArrays (){
for(int i = 0; i<m_numTouchPinsUsed; i++) { //zero initialize i2c arrays
m_filteredData[i] = 0;
m_baselineData[i] = 0;
inbuf8[i] = (i2c_char_t)0 ;
outbuf8[i] = m_baselineAddress[i];
outbuf16[i*2] = m_filteredAddress[i];
m_amplitude[next][i] = 0;
}
return 1;
}
//mpr setup function
boolean begin(uint8_t bus = 1, uint8_t i2caddr = MPR121_I2CADDR_DEFAULT);
uint16_t* filteredData(int t); //not used- just readRegister16 //changed to take integer, return pointer
uint16_t* baselineData(int t); //not used- just readRegister8 //same for both, plop down the integer to get the pointer
bool readRegister16(); //change to void. read registers and store in array.
bool readRegister8(); //read registers and store in array.
uint16_t readRegister16(uint8_t); //overloaded to retain original functionality for begin()
uint8_t readRegister8(uint8_t reg); //overloaded to retain original functionality for begin()
void writeRegister(uint8_t reg, uint8_t value);
uint16_t touched(void);
void setThresholds(uint8_t touch, uint8_t release);
//read the registers, calculate amplitude and the sample, point like a huntin dog.
//call this in aux task in render
//it will grant you an updated audio sample pointer, amplitude array with a value for each sensor, and...
//unlimited health and mana. For whatever.
bool read() {
#ifdef DEBUG_MPR121 //debug
rt_printf("read8\n");
#endif
while((!readRegister8()));
#ifdef DEBUG_MPR121 //debug
rt_printf("read8 done read 16\n");
#endif
while((!readRegister16()));
#ifdef DEBUG_MPR121 //debug
rt_printf("read16 done calculateFloats\n");
#endif
while((!calculateFloats()));
//Everything is calculated and used in render. Now reset between reads.
#ifdef DEBUG_AMPARRAY
//a little street credit background check
for (int i = 0; i<m_numTouchPinsUsed; i++) {
//std::cout << m_amplitude[next][i] << " ";
rt_printf("%f ", m_amplitude[next][i]);
}
#endif
reset();
#ifdef DEBUG_AMPARRAY //prints the newline char
rt_printf("\n");
#endif
return 1;
}
bool calculateFloats() {
// This code can be replaced with your favourite audio code
for(int i = 0; i < m_numTouchPinsUsed; i++) {
//calculate the adjusted sensor reading
//----Don't do anything if nothing changed.------ first IF.
//if((sensorCalculated[i] != ((m_baselineData[i] - m_filteredData[i]) - m_threshold))) {
//FASTER FASTER!!..OK, you got it.
if((sensorCalculated[i] != (((uint16_t)inbuf8[i] - inbuf16[i]) - m_threshold))) {
sensorCalculated[i] = ((uint16_t)inbuf8[i]- inbuf16[i]) - m_threshold;
//if less than zero, don't do any extra consversions
if(sensorCalculated[i] <= 0) {
m_amplitude[next][i] = 0.f;
} else {
//m_amplitude[next][i] = ((m_baselineData[i] - m_filteredData[i]) - m_threshold) / 400.f;
//m_amplitude[next][i] = (-(m_filteredData[i]-m_baselineData[i])) + m_threshold; //this line is converting the sensor value in integer amount to a floating point
//number then dividing it...12 times every sample. The sennsor is only read 1 every 882 frames unless
//it's changed. Lets do the calculation to float in the sensor class once every time
//needed and convert to amplitude there. For a grand prize total of savings I bet.
//AND WHATS BEHIND DOOR NUMBER 5?????
//.....well shit, glad you asked. That means there's only one sample to be calculated
//every "INTERVAL" frames, so really you're just EFFIN yourself in the A OVER AND OVER
//again. like 881 extra times. times 50. per second. you little pervert.
//Ok, the class will calculate the sample as well. Once every INTERVAL, DUMMY
m_amplitude[next][i] = ((float)sensorCalculated[i]) / 400.f;
// Prevent clipping
if(m_amplitude[next][i] > 0.5) {
m_amplitude[next][i] = 0.5;
}
}
m_sample[next] += m_amplitude[next][i] * sinf_neon(gPhases[i]);
//m_sample[next] += m_amplitude[next][i] * sinf(gPhases[i]); //-------------->Changed sinf to sinf_neon for 10% cpu reduction
gPhases[i] += gNormFrequencies[i];
if(gPhases[i] > M_PI)
gPhases[i] -= staticPi;
// gPhases[i] -= 2.0f * (float)M_PI; //---->static convert in setup instead of dynamic cast
}//end don't calculate if nothing changed-IF
}//End sensor calculatin' for loop.
return 1;
}//end calculateFloats
bool changeCurrentPointer() { //always point to current, this also sets "next" variable.
//first, change the amplitude array and sample pointers to the most recently calculated result:
m_samplePtr = &m_sample[next]; //and we point to this new sample result. this makes us so, so so happy.
//almost like you're..a real friend. but better...almost
m_amplitudePtr[0] = &m_amplitude[next][0]; //pointers to these guys if you need them
//Then, change the numbers used by the ints.
if(current < 1) {
current++;
next--;
} else{
current--;
next++;
}
//Now, next should always point to the one I'm doing the calculations for. Current is the one "thing"
//currently being used for renders. And shit. Which means I can zero all the "next" shit now.
//and take my time doing calculations for it, like a long slow poop. on the clock. Excepty its off the clock(timer)
//the double dip pooper. EW SICK
return 1;
}
bool reset () { //reset all the arrays and pointer settings!
changeCurrentPointer();
zeroArrays(); //set i2c arrays back to zero.'
return 1;
}
int readI2C() { return 0; } // Unused
private:
int _i2c_address;
int m_numTouchPinsUsed{}; //added default initializer to set number of pins used - private variable
uint8_t m_filteredAddress[NUM_TOUCH_PINS]; //this array holds the filtered data addresses in order.
uint8_t m_baselineAddress[NUM_TOUCH_PINS]; //and baseline memory addresses, in order
uint8_t m_touchThreshold{}; //these change the sensitivity levels of touch and release- on the MPR chip
uint8_t m_releaseThreshold{}; //<------------release
uint16_t m_filteredData[NUM_TOUCH_PINS]; //pointers initialized to current value of the two arrays for speediness
uint16_t m_baselineData[NUM_TOUCH_PINS], inbuf16[NUM_TOUCH_PINS*2+1]; //inbuf needs to be uint16t type with one extra spot, for shufflin'
float * m_amplitudePtr[NUM_TOUCH_PINS]; //points to the currently useful values in the arrays of calculated amplitudes
float m_amplitude[2][NUM_TOUCH_PINS]; //array of sensor values filtered and converted to amplitude, waiting for use.
int current = 0, next = 1; //this number will ALWAYS be the current...something. AAAAAAAAAAALWAYYS
//int pointerCurrent = current; //we use enum to keep track of things now. we're sphistermated.
i2c_char_t outbuf16[NUM_TOUCH_PINS], outbuf8[NUM_TOUCH_PINS], inbuf8[NUM_TOUCH_PINS] ;
float * m_samplePtr; //take a reference from the initializer, and store the sample mrite mrhere.
float m_sample[2]{0,0}; //previous and current sample rotation. zero init'd...for his pleasure
//float gFrequencies[NUM_TOUCH_PINS] = {261.63, 293.66, 329.63, 349.23, 392.00, 440.00};
float gFrequencies[NUM_TOUCH_PINS] = {261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25, 587.33, 659.25, 698.25, 783.99};
// This is internal stuff for the demo
float gNormFrequencies[NUM_TOUCH_PINS];
float gPhases[NUM_TOUCH_PINS] = {0};
float staticPi = 2.0f * (float)M_PI; //static cast of pi constant
int m_threshold{};
int sensorCalculated[12]; //stores calculated sensors to save on cost if not needed to make another one.
};
#endif /* I2CTK_H_ */
MPR121.cpp:
/*
* I2C_MPR121.cpp
*
* Created on: Oct 14, 2013
* Author: Victor Zappi
*/
#include "I2C_MPR121.h"
#include <iostream>
/*
I2C_MPR121::I2C_MPR121(int num_touch_pins = 12) {
m_num_touch_pins = num_touch_pins;
}
*/
boolean I2C_MPR121::begin(uint8_t bus, uint8_t i2caddr) {
_i2c_address = i2caddr;
if(initI2C_RW(bus, i2caddr, 0) > 0)
return false;
// soft reset
writeRegister(MPR121_SOFTRESET, 0x63);
usleep(1000);
//delay(1);
for (uint8_t i=0; i<0x7F; i++) {
// Serial.print("$"); Serial.print(i, HEX);
// Serial.print(": 0x"); Serial.println(readRegister8(i));
}
writeRegister(MPR121_ECR, 0x0);
uint8_t c = readRegister8(MPR121_CONFIG2);
if (c != 0x24) {
rt_printf("MPR121 read 0x%x instead of 0x24\n", c);
return false;
}
setThresholds(m_touchThreshold, m_releaseThreshold);
writeRegister(MPR121_MHDR, 0x01);
writeRegister(MPR121_NHDR, 0x01);
writeRegister(MPR121_NCLR, 0x0E);
writeRegister(MPR121_FDLR, 0x00);
writeRegister(MPR121_MHDF, 0x01);
writeRegister(MPR121_NHDF, 0x05);
writeRegister(MPR121_NCLF, 0x01);
writeRegister(MPR121_FDLF, 0x00);
writeRegister(MPR121_NHDT, 0x00);
writeRegister(MPR121_NCLT, 0x00);
writeRegister(MPR121_FDLT, 0x00);
writeRegister(MPR121_DEBOUNCE, 0);
writeRegister(MPR121_CONFIG1, 0x10); // default, 16uA charge current
writeRegister(MPR121_CONFIG2, 0x20); // 0.5uS encoding, 1ms period
// writeRegister(MPR121_AUTOCONFIG0, 0x8F);
// writeRegister(MPR121_UPLIMIT, 150);
// writeRegister(MPR121_TARGETLIMIT, 100); // should be ~400 (100 shifted)
// writeRegister(MPR121_LOWLIMIT, 50);
// enable all electrodes
writeRegister(MPR121_ECR, 0x8F); // start with first 5 bits of baseline tracking
return true;
}
void I2C_MPR121::setThresholds(uint8_t touch, uint8_t release) {
//for (uint8_t i=0; i<12; i++) { //change from const of 12 to initializer to remove if statements - no cpu gain
for (uint8_t i=0; i<m_numTouchPinsUsed; i++) {
writeRegister(MPR121_TOUCHTH_0 + 2*i, touch);
writeRegister(MPR121_RELEASETH_0 + 2*i, release);
}
}
/*
uint16_t* I2C_MPR121::filteredData(int t) { ///////////////filtered data///////
//if (t > 12) return 0; //remove if statement, use initializer - no noticeable change in cpu.
if(t<1){
readRegister16(); //having multiplication here is unecessarily costly, use pre-configured.
} //map values to array corresponding to "t" input for pre-selection.
return &m_filteredData[t];
}
*/
/*
uint16_t* I2C_MPR121::baselineData(int t) { //////////////baseline data/
//if ( > 12) return 0; //remove if statement, use initializer
if(t<1) {
readRegister8(); //this uses read register8, note.
}
return &m_baselineData[t]; //shifing by two makes the 8 bit reading into 10 bit, from 256 to 1024 bit.
}
*/
/* ORIGINAL
uint16_t I2C_MPR121::filteredData(uint8_t t) { ///////////////filtered data///////
if (t > 12) return 0;
return readRegister16(MPR121_FILTDATA_0L + t*2);
}
uint16_t I2C_MPR121::baselineData(uint8_t t) { //////////////baseline data/
if (t > 12) return 0;
uint16_t bl = readRegister8(MPR121_BASELINE_0 + t);
return (bl << 2);
}
*/ // END ORIGINAL
uint16_t I2C_MPR121::touched(void) {
uint16_t t = readRegister16(MPR121_TOUCHSTATUS_L);
return t & 0x0FFF;
}
/*********************************************************************/
uint8_t I2C_MPR121::readRegister8(uint8_t reg) { //this is the original, needed for setup - new overloaded below
i2c_char_t inbuf, outbuf;
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
/*
* In order to read a register, we first do a "dummy write" by writing
* 0 bytes to the register we want to read from. This is similar to
* the packet in set_i2c_register, except it's 1 byte rather than 2.
*/
outbuf = reg;
messages[0].addr = 0x5A;
messages[0].flags = 0;
messages[0].len = sizeof(outbuf);
messages[0].buf = &outbuf;
/* The data will get returned in this structure */
messages[1].addr = 0x5A;
messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
messages[1].len = sizeof(inbuf);
messages[1].buf = &inbuf;
/* Send the request to the kernel and get the result back */
packets.msgs = messages;
packets.nmsgs = 2;
if(ioctl(i2C_file, I2C_RDWR, &packets) < 0) {
rt_printf("Unable to send data");
return 0;
}
return inbuf;
}
bool I2C_MPR121::readRegister8() { //These functions used in the library, need same names.
//#ifdef DEBUG_MPR121
//std::cout << "readRegister8\n";
//#endif
for(int n = 0; n < m_numTouchPinsUsed; n++) {
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
/*
* In order to read a register, we first do a "dummy write" by writing
* 0 bytes to the register we want to read from. This is similar to
* the packet in set_i2c_register, except it's 1 byte rather than 2.
*/
messages[0].addr = 0x5A;
messages[0].flags = 0;
messages[0].len = sizeof(&m_baselineAddress[n]);
messages[0].buf = &m_baselineAddress[n];
/* The data will get returned in this structure */
messages[1].addr = 0x5A;
messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
messages[1].len = sizeof(inbuf8[n]);
messages[1].buf = &inbuf8[n];
/* Send the request to the kernel and get the result back */
packets.msgs = messages;
packets.nmsgs = 2;
if(ioctl(i2C_file, I2C_RDWR, &packets) < 0) {
rt_printf("Unable to send data");
break;
}
inbuf8[n] <<= 2;
#ifdef DEBUG_READ8
rt_printf("%d ", inbuf8[n]);
//std::cout << m_baselineData[n] << " ";
#endif
} //end for loop
#ifdef DEBUG_READ8
rt_printf("\n");
#endif
return 1;
}
//10/22/21
//Original read method, comment out and modify for better efficiency
//replace with readFilteredRegisters() per Giuliomoro --
//see notes at the bottom comments
bool I2C_MPR121::readRegister16() { //just use the integer rep. of the filtered data array, ie, memory addresses
//1 is just 1. Npw no need to create arrays everytime
// bool done{1};
//#ifdef DEBUG_MPR121
// std::cout << "readRegister16\n";
//#endif
for(int n = 0; n < m_numTouchPinsUsed; ++n) {
//its copyping uint8_t to i2c char, maybe making a conversion, skip all that and just
//feed it an array of the addresses in uint8_t form. like a champion of codingzez
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
///
// In order to read a register, we first do a "dummy write" by writing
// 0 bytes to the register we want to read from. This is similar to
// the packet in set_i2c_register, except it's 1 byte rather than 2.
/// All this extra copying and shit is not necessary. we have to do something!
//outbuf = reg; //this is a copy of the register. Instead of copying, use circular arrays
// #ifdef DEBUG_MPR121
//std::cout << "readRegister16 loop\n";
//#endif
messages[0].addr = _i2c_address; //this message TELLs it which FUCKING ADDRESS TO REAADDDDDDD YUO DUM SHIT!!!
messages[0].flags = 0;
messages[0].len = sizeof(m_filteredAddress[n]);
messages[0].buf = &m_filteredAddress[n];
//messages[0].buf = &outbuf;//this just states the requested memory address to be read and size, I believe.
// The data will get returned in this structure //
messages[1].addr = _i2c_address; //re
messages[1].flags = I2C_M_RD;// | I2C_M_NOSTART//
messages[1].len = sizeof(inbuf16[n]);
// messages[1].buf = (i2c_char_t*)&inbuf16[n];
messages[1].buf = reinterpret_cast<i2c_char_t*>(&inbuf16[n]);
// Send the request to the kernel and get the result back //
packets.msgs = messages;
packets.nmsgs = 2;
if(ioctl(i2C_file, I2C_RDWR, &packets) < 0) {
rt_printf("Unable to send data");
break;
}
//m_filteredData[n] = (((uint16_t)inbuf16[n][0] | (uint16_t)inbuf16[n][1]) << 8);
//this shift 8 is like "move bitch, and hurry the fuck up!"
//it shifts the top half of the array straight into the abyss, and the
//middle is the new top now.
//make proper room for this shit
inbuf16[n] |= (inbuf16[n*2+1] << 16);
#ifdef DEBUG_READ16
rt_printf("%d ", inbuf16[n]);
#endif
//this also converts from i2c char to uint 16
// return (uint16_t)inbuf[0] | (((uint16_t)inbuf[1]) << 8); //the message is "or'ed" over from inbuf[0.5] to inbuf[0]- shifted 8 bits over in the
//16 bit register, cheap copy of 8 bits.
//Lets try having the zeroed out registers just sitting and waiting to be sent over,
//instead of doing it on the fly.
//create the memory locations on startup, return pointers, use circular buffer, shift
//right then zero immediately. Then, like chuck norris, we wait.
//recap: get the data once. Initialized twice as much space as needed, with pointers,
//in initializer. USE THE FUCKING POINTERS DUMMY
} //end for loop (read loop)
#ifdef DEBUG_READ16
rt_printf("\n");
#endif
return 1;
} //end function
uint16_t I2C_MPR121::readRegister16(uint8_t reg) {
i2c_char_t inbuf[2], outbuf;
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
///
// In order to read a register, we first do a "dummy write" by writing
// 0 bytes to the register we want to read from. This is similar to
// the packet in set_i2c_register, except it's 1 byte rather than 2.
///
outbuf = reg;
messages[0].addr = _i2c_address;
messages[0].flags = 0;
messages[0].len = sizeof(outbuf);
messages[0].buf = &outbuf;
// The data will get returned in this structure //
messages[1].addr = _i2c_address;
messages[1].flags = I2C_M_RD;// | I2C_M_NOSTART//
messages[1].len = sizeof(inbuf);
messages[1].buf = inbuf;
// Send the request to the kernel and get the result back //
packets.msgs = messages;
packets.nmsgs = 2;
if(ioctl(i2C_file, I2C_RDWR, &packets) < 0) {
rt_printf("Unable to send data");
return 0;
}
return (uint16_t)inbuf[0] | (((uint16_t)inbuf[1]) << 8);
}
/**************************************************************************/
/*!
@brief Writes 8-bits to the specified destination register
*/
/**************************************************************************/
void I2C_MPR121::writeRegister(uint8_t reg, uint8_t value) {
uint8_t buf[2] = { reg, value };
if(write(i2C_file, buf, 2) != 2)
{
std::cout << "Failed to write register " << (int)reg << " on MPR121\n";
return;
}
}
/* 10-22-21 - notes from Giuliomoro taken from forum comments: https://forum.bela.io/d/1870-mpr121-cpu-usage
it looks like that library is highly inefficient in the way it reads data from the device, by running several smaller transactions per each reading which may be the cause of the CPU hog. You can see in MPR121.cpp that each call to filteredData() or baselineData() involves a dedicated 2 or 1 byte (respectively) I2C transaction. You could add methods that read all the filtered data and all the baseline data at once, place the result in an array and then filteredData() and baselineData() could return the content of that array without each triggering an I2C transaction. I don't have an MPR121 at hand to test this (we would normally use a Trill Craft for this purpose), so I can only recommend some possible code changes. You could write a new method e.g.: readFilteredRegisters() which resembles readRegister16 but instead of inbuf[2] it uses inbuf[2 * numPads], then moves the retrieved data from inbuf into a object-owned vector, e.g.:
for(unsigned int n = 0; n < numPads; ++n)
lastFilteredData[n] = (uint16_t)inbuf[n * 2] | (((uint16_t)inbuf[n * 2 + 1]) << 8);
( you will want to pass numPads to the constructor or begin() method making sure you resize lastFilteredData accordingly before you start reading from it.
Then the filteredData method would be simplified to:
uint16_t I2C_MPR121::filteredData(uint8_t t) {
if (t > numPads) return 0;
return lastFilteredData[t];
}
You'd then do similar modifications for baselineData(). Then at the top of readMPR121(), before the for loop you'd call:
mpr121.readFilteredRegisters();
mpr121.readBaselineRegisters();
to ensure the for loop is operating on up-to-date data.
See if this:
works
reduces CPU load
*/