Mozzi I2C problem

346 views
Skip to first unread message

Marian Keller

unread,
Jun 21, 2015, 11:43:36 AM6/21/15
to mozzi...@googlegroups.com
Hi,

I'm using Mozzi in HIFI-mode to generate a sound from the values measured by an ultrasonic sensor. The sensor is controlled via I2C, so I'm using the twi_nonblock library which comes with Mozzi. 
I've adapted the provided sample code for reading an ADXL345 accelerometer. It works as expected, except in the audio output there are "ticks" in the same rhythm the sensor gets read out. Already checked the power supply, the power is stable and has no glitches. So there must be something in my code that blocks the audio output.
#include <MozziGuts.h>             // Mozzi main library 
#include <Oscil.h>                 // Mozzi template for an oscillator
#include <tables/sin2048_int8.h>   // Mozzi wavetable holding a sine wave
#include <twi_nonblock.h>          // Mozzi non-blocking I2C Library
#include <Metronome.h>             // Mozzi metronome / task scheduler
#include <Smooth.h>                // Mozzi sound smoothening library
#include <avr/wdt.h>               // AVR Watchdog library

// --- Mozzi definitions ---
Oscil <2048, AUDIO_RATE> aSin(SIN2048_DATA); // defines an sine tone oscillator with the sin2048 wavetable. Structure: Oscil <table_size, update_rate> name(table_data);
#define CONTROL_RATE 1024          // number of control updates per second, only powers of 2
volatile int audioFrequency = 440; // stores present audio frequency
volatile byte volume = 100;        // stores present selected volume
volatile boolean mute = false;     // stores audio mute state

// --- Ultrasonic Sensor I2C definitions ---
#define ULT_ADDRESS 0x70       // I2CXL-Maxsonar-EZ device address
#define ULT_INIT_RANGING 0x51      // Initiate range reading

// --- Ultrasonic Sensor constants and variables ---
static volatile byte ult_status = 0; // Status byte
#define ULT_IDLE 0
#define ULT_READING 1
#define ULT_WRITING 2

#define AVERAGING_SIZE 3
byte ult_bytedata[2];                // Ultrasonic Sensor distance Information in two bytes
int ult_distance[AVERAGING_SIZE];    // Ultrasonic Sensor distance Information as Integer
int ult_clean_distance = 0;
byte averagingCounter = 0;

// --- Task schedulers ---
Metronome parameterScheduler(10);  // update input parameters all 10ms
Metronome sonarScheduler(50);      // read sonar every 50ms
Metronome frequencyScheduler(50*AVERAGING_SIZE); // change frequency every 150ms

// --- Smoothening ---
Smooth <int> smoothFreq(0.975f);
volatile int targetFrequency;


void setup() {
  delay(400);                           // wait for stable power supply
  wdt_enable(WDTO_2S);                  // enable watchdog timer, 2 second timeout
  Serial.begin(9600);                   // serial debug
  Serial.println("Gestartet");          // debug
  
  startMozzi(CONTROL_RATE);  // start sound library
  initialize_twi_nonblock(); // initialize I2C library and join I2C-Bus
}

void updateControl() {
  wdt_reset();  // reset watchdog
  int smoothedFrequency = smoothFreq.next(targetFrequency);
  aSin.setFreq(smoothedFrequency);
  if (parameterScheduler.ready() == true)
  {
    volume = 0; // debug
  }
  if (sonarScheduler.ready() == true)
  {
    ult_ranging();
    targetFrequency = audioFrequency; // debug
  }
  if (frequencyScheduler.ready() == true)
  {
      //targetFrequency = some mathematical function which uses ult_clean_distance
  }
}

int updateAudio() {
  return (aSin.next() * volume)>>8;
}

void initiate_read_maxbotix()
{
  // Reads num bytes starting from address register on device in to _buff array
  // indicate that we are transmitting
  //   transmitting = 1;
  // set address of targeted slave
  txAddress = ULT_ADDRESS;
  // reset tx buffer iterator vars
  txBufferIndex = 0;
  txBufferLength = 0;  

  // put byte in tx buffer
  txBuffer[txBufferIndex] = ULT_INIT_RANGING;
  ++txBufferIndex;
  // update amount in buffer   
  txBufferLength = txBufferIndex;

  twi_initiateWriteTo(txAddress, txBuffer, txBufferLength);
  ult_status = ULT_WRITING;
}

void initiate_request_maxbotix()
{
  // reset tx buffer iterator vars
  txBufferIndex = 0;
  txBufferLength = 0;
  // indicate that we are done transmitting
  //   transmitting = 0;

  byte read = twi_initiateReadFrom(ULT_ADDRESS, 2);
  ult_status = ULT_READING;
}

void finalise_request_maxbotix()
{
  byte read = twi_readMasterBuffer( rxBuffer, 2 );
  // set rx buffer iterator vars
  rxBufferIndex = 0;
  rxBufferLength = read;

  byte i = 0;
  while( rxBufferLength - rxBufferIndex > 0)         // device may send less than requested (abnormal)
  { 
    ult_bytedata[i] = rxBuffer[rxBufferIndex];
    ++rxBufferIndex;
    i++;
  }
  ult_status = ULT_IDLE;
}

void ult_ranging()
{
    switch( ult_status ){
    case ULT_IDLE:
      ult_distance[averagingCounter-1] = (int) (ult_bytedata[0]*256 + ult_bytedata[1]); // Ultrasonic Sensor distance reading
      initiate_read_maxbotix();
      if (averagingCounter < AVERAGING_SIZE)
      {
        averagingCounter++;
      }
      else 
      {
        averagingCounter = 0;
        filter_range();
      }
      break;
    case ULT_WRITING:
      if ( TWI_MTX != twi_state ){
        initiate_request_maxbotix();
      }
      break;
    case ULT_READING:
      if ( TWI_MRX != twi_state ){
        finalise_request_maxbotix();
      }
      break;
    }
}

void filter_range()
{
  isort(ult_distance, AVERAGING_SIZE);
  ult_clean_distance = mode(ult_distance, AVERAGING_SIZE);
}

//Sorting function
void isort(int *a, int n){
// *a is an array pointer function
  for (int i = 1; i < n; ++i)
  {
    int j = a[i];
    int k;
    for (k = i - 1; (k >= 0) && (j < a[k]); k--)
    {
      a[k + 1] = a[k];
    }
    a[k + 1] = j;
  }
}

//Mode function, returning the mode or median.
int mode(int *x,int n){
  int i = 0;
  int count = 0;
  int maxCount = 0;
  int mode = 0;
  int bimodal;
  int prevCount = 0;
  while(i<(n-1)){
    prevCount=count;
    count=0;
    while(x[i]==x[i+1]){
      count++;
      i++;
    }
    if(count>prevCount&count>maxCount){
      mode=x[i];
      maxCount=count;
      bimodal=0;
    }
    if(count==0){
      i++;
    }
    if(count==maxCount){//If the dataset has 2 or more modes.
      bimodal=1;
    }
    if(mode==0||bimodal==1){//Return the median if there is no mode.
      mode=x[(n/2)];
    }
    return mode;
  }
}

void loop() {
  audioHook();
}

But I can't figure out why the function initiate_read_maxbotix is disturbing the timing of mozzi in a different way than it does in the given sample code?

Thanks for your help,

Marian

Tim Barrass

unread,
Jun 21, 2015, 10:44:14 PM6/21/15
to mozzi...@googlegroups.com
Hi Marian,
after a quick look, I can't see anything obviously wrong with your code, but 1024 is a very fast CONTROL_RATE, which will take a lot of processing time in relation to the AUDIO_RATE of 16384 (only 16 audio sample generated per control update). 

Does the click occur when you use a lower CONTROL_RATE?  If a few milliseconds of latency is acceptable, it's best to set CONTROL_RATE as low as possible, ie. 64, which gives 15ms latency (in addition to 15ms audio buffer latency, though this can be halved by increasing AUDIO_RATE to 32768 in mozzi_config.h).

If that doesn't work, let me know and I'll look closer.

Tim

--
You received this message because you are subscribed to the Google Groups "Mozzi-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mozzi-users...@googlegroups.com.
To post to this group, send email to mozzi...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/mozzi-users/a4f092ee-c3d6-4b86-8561-78261681ba4c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Marian Keller

unread,
Jun 23, 2015, 12:06:44 PM6/23/15
to mozzi...@googlegroups.com
Hi Tim,

the CONTROL_RATE is at 1024 because I'm using Smooth to make a transition between an old and the new frequency, but setting it down didn't change something.
I also tried to use Arduino 1.0.5 instead of 1.6.4, as there seem to be performance problems on newer versions, but that didn't solve the problem either.

Marian

nescivi

unread,
Jun 23, 2015, 1:04:41 PM6/23/15
to mozzi...@googlegroups.com
Hi,

It might be possible that there are still some busy wait loops inside the twi_nonblock code, and you may need to split it up even further.

It's been a while since I worked out that code, and it could use more thorough testing, and further tinkering.

sincerely,
Marije

Tim Barrass

unread,
Jun 24, 2015, 5:48:01 AM6/24/15
to mozzi...@googlegroups.com
Hi Marian,

this might not be the whole cause of your problem, but are you sure this line is correct?
  if(count>prevCount&count>maxCount){

I would expect && instead of & :
  if(count>prevCount && count>maxCount){

Tim


Tim Barrass

unread,
Jun 27, 2015, 10:06:15 AM6/27/15
to mozzi...@googlegroups.com
Hi again,

mmm of course that line I wondered about is fine!

This might another useless answer, but now I think it could be possible that the sensor processing code might be unable to complete if it takes longer than the period of the control interrupt (1/CONTROL_RATE seconds).  This can happen because Metronome is just a counter which gets updated each time Metronome::ready() is called, not an interrupt of its own which would enable nested interrupt processing - which would be a better implementation, but I'm not sure how or if it would work with Mozzi, without looking at the Atmel manuals.

This problem has got me before!  Still, it might not be the issue here, since you wrote that the click happens even with low control rates.  If it did turn out that the processing/filtering code could complete at a control rate of 64, you could use Smooth or Line at audio rate to interpolate your frequency as in the 02.Control/Line_Gliss example.

Tim

Reply all
Reply to author
Forward
0 new messages