CD4067 best case channel select frequency

I want to display ‘real time’ temperature readings from 16 thermistors. For now forget about the display aspect; I’m just taking readings and printing to serial monitor.

Naturally I’m multiplexing using CD4067 and switching channel using port manipulation, which I gather is the fastest way in which to do this. As the multiplexer offers switching times in the nano second range I imagine that I ought to be able to approach that without cross-talking my signals.

When probing the MSB of PORTD I get 29Hz. This is up from earlier iterations of my code where I could see the digital outs flicker when grounded through LEDs, likely about 5Hz (Using digitalWrite()).

Obviously my display output (call it 7-segment display for now) will be using latched data to there won’t be any ‘flicker’, but what I’m really looking for is fast response to temperature changes and the ability to see (and ultimately graph) real time temperature shift.

So, is there a way to get ATmega 328p to write digital pins faster?

Suffice to say the data coming through is good and clean, But just slow to arrive.

**Note:
That the program pauses once per second to average 10 readings on each channel, convert to temperature and display the results of the calcs. Nonetheless eliminating these process doesn’t get my multiplexer switching faster that described above.

** Note:
Previously a delay was included in the channel switching cycle. I’ve eliminated that to increase the frequency of channel switching. I observe now 55Hz on the MSB of PORTD.

CODE:

///**************************CONSTANTS***********************//

const byte inputPin = A0;
byte controlPins[] = {B00000000,
                      B10000000,
                      B01000000,
                      B11000000,
                      B00100000,
                      B10100000,
                      B01100000,
                      B11100000,
                      B00010000,
                      B10010000,
                      B01010000,
                      B11010000,
                      B00110000,
                      B10110000,
                      B01110000,
                      B11110000};



#define BCOEFFICIENT 3950
#define TEMPERATURENOMINAL 25


//**ARRAY IN TWO DIMENSIONS**//

const byte ttlChannels = 16;

const byte numSamples = 10;

float valueStore[ttlChannels][numSamples];


//***DELAY FOR DISPLAY****//

const int period = 1000;


//***THERMISTOR V-DIVIDER CONSTANTS****//

const int fixedR = 10000;
const int thermNom = 10000;


//***APPLIED MUX DELAY***//

const int muxDelay = 1;

//**************************VARIABLES***********************//
unsigned long time_now = 0;

//**************************SETUP***********************//

void setup()
{

    Serial.begin(115200);
    DDRD = B11111111;
    analogReference(EXTERNAL);
}

//**************************LOOP***********************//

void loop()
{
    setChannel(); //THIS IS THE BOTTLE NECK

    if ((unsigned long)(millis() - time_now) > period)
    {
        time_now = millis();
        for (uint8_t i = 0; i < ttlChannels; i++)
        {
            Serial.print(i);
            Serial.print(": ");
            Serial.print(calcTemp(collateData(i)));
            Serial.println("*C");
            Serial.println("-------------");
        }
    }
}
//**************************FUNCTIONS***********************//

void setChannel()
{
    for (int i = 0; i < ttlChannels; i++)
    {
        PORTD = controlPins[i];
        for (uint8_t j = 0; j < numSamples; j++)
        {
            valueStore[i][j] = analogRead(inputPin);
        }
       // delay(muxDelay); //ELIMINATED TO TEST FULL SPEED. DATA IS GOOD WITH NO DELAY
    }
}

float calcTemp(float average)
{
    average = 1023 / average - 1;
    average = fixedR / average;

    float steinhart;
    steinhart = average / thermNom;                   // (R/Ro)
    steinhart = log(steinhart);                       // ln(R/Ro)
    steinhart /= BCOEFFICIENT;                        // 1/B * ln(R/Ro)
    steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
    steinhart = 1.0 / steinhart;                      // Invert
    steinhart -= 273.15;                              // convert to C

    return steinhart;
}

int collateData(int channel)
{
    float average = 0;
    for (uint8_t i = 0; i < numSamples; i++)
    {
        average += valueStore[channel][i];
    }
    average /= numSamples;
    return average;
}

As the multiplexer offers switching times in the nano second range I imagine that I ought to be able to approach that without cross-talking my signals.

No. Cross talk is caused by layout and components, nothing to do with the switching speed. Although sometimes fast switching increases cross talk.

So, is there a way to get ATmega 328p to write digital pins faster?

Yes look up Arduino port addressing. Or for a slower but easy to understand there is a fast write library.

Thanks Grumpy_Mike,

Yes look up Arduino port addressing.

but isn’t that exactly what I’m doing here:

    DDRD = B11111111;

and

PORTD=********

As I’ve just completed a test flipping bits in PORTD as fast as possible I can record 160kHz so long as there’s no writing to memory between states, is it possibly a memory write speed issue rather than a digital write issue?
ie in this code:

 for (int i = 0; i < ttlChannels; i++)
    {
        PORTD = controlPins[i];
        for (uint8_t j = 0; j < numSamples; j++)
        {
            valueStore[i][j] = analogRead(inputPin);
        }
    }

I appreciate any help you can offer!

const int muxDelay = 0.1; // an integer only holds integers

No sure what you're trying to do, but 10 analogue samples takes about 1ms.
A lot longer than switching the mux with normal code (microseconds).
Leo..

Leo, quite right. Thank you. That 0.1 is an error. So would you agree then that my bottleneck is the analog sampling not the channel switching?

I've see this great Instructables with a 'free-runnning' analog sampling approach. It seems smart and might assist my fast data acquisition.

Thoughts?

I've see this great Instructables

Somewhat of an oxymoron.

free-runnning' analog sampling approach

That will not speed things up as such, it will just allow you to do other things while you are waiting. To speed things up you can change the prescaller to the A/D. You can get the sample rate up to 56 KS/S without affecting accuracy or even faster if you need less accuracy.

but isn't that exactly what I'm doing here:

Yes but doing it in the setup doesn’t save any time because this is only done once. I can’t see where you access things in the main body with direct port addressing.

The whole thing will run a hell of a lot faster if you remove the print statements, they are a time killer.

but what I'm really looking for is fast response to temperature changes

Temperature doesn’t change very fast due to thermal time constants in the physical system. What are you measuring?

Thanks again Grumpy_Mike. As far as oxymoron go, well, perhaps. But when a good one shows up it's worth acknowledging I reckon.

Print statements; yes of course but we're still debugging here in the early stages and feedback is crucial.
I'm going to make changes to the A/D prescaler as you suggest.

Am I wrong in believing that I'm utilizing direct port access here:

PORTD = controlPins[i];

...as part of a method called in loop and accessing an array declared as constant up top??

I'm working on an automation for a large whisky still. Things escalate very quickly in pressure vessels when there's 10kW elements at play. The idea is to make a bunch of manual runs and log the data throughout to work up an automation algorithm. You be surprised how much can be missed in a 20 second period in this distilling caper.

Thanks again for all the input.

You be surprised how much can be missed in a 20 second period in this distilling caper

Ah 20 seconds yes temperature can change on that time scale, I was thinking more of the 20mS time scale.

Am I wrong in believing that I'm utilizing direct port access here:

No it is me sorry I failed to spot that in your code.

I am back on my laptop now so I have access to all my stuff ( iPad before ) so the line I have used on the prescaller is :-

  // set up fast sampling mode
  ADCSRA = (ADCSRA & 0xf8) | 0x04; // set 16 times division

In the setup function.