1

The project as follows: an attiny25 powers up a dht22 and an unltrasonic distance sensor with a MOSFET. The output of the MOSFET is also used to power a bus, there's a 3.3K pullup on it. The main program mostly sleeps, and when it's time, it executes this:

            if ((buff[1]<<8) + buff[0] <= powerCutoff) {
                generalStatus &= wipeMask; // Wipe lower 3 bits

                setOutput(signalPin);
                setLow(signalPin);

                MOSFETPowerOn(devPower)
                initSleep(1<<WDP2);

                measureDHT();
                measureDistance();

//                sendData();
                MOSFETPowerOff(devPower)
            }

You also need to know these:

#define MOSFETPowerOn(line) { line ## _port &= ~(1<<line); line ## _ddr |= (1<<line); asm("nop"); }
#define MOSFETPowerOff(line) { line ## _ddr &= ~(1<<line); line ## _port |= (1<<line); asm("nop"); }

#define ping(bit) { \
    bit ## _port &= ~(1<<bit); \
    bit ## _ddr |= (1<<bit); \
    bit ## _port |= (1<<bit); \
    asm("nop"); \
    bit ## _port &= ~(1<<bit); \
    asm("nop"); }

/** Devices power pin */
#define devPower PB2
#define devPower_port PORTB
#define devPower_ddr DDRB
#define devPower_pin PINB

/** Debug */
#define debug PB0
#define debug_port PORTB
#define debug_ddr DDRB
#define debug_pin PINB

void initSleep(uint8_t timing) {
    asm("WDR");
    WDTCR = 1<<WDIE | timing;

    MCUCR &= ~(1<<SM0);
    MCUCR |= 1<<SE | 1<<SM1;
    asm("sleep");
}
void measureDHT() {
    generalStatus |= 1<<fDHTMeasurementInProgress;

    initSleep(1<<WDP2); // 0.25s

    for (uint8_t i = 2; i < 7; i++) buff[i] = 0;
    dht22_status.status = dht22_inPresence;
    dht22_status.bitCounter = 0;
    dht22_status.byteCounter = 0;

    setOutput(devBus);
    _delay_us(1200); // 1ms initial low pulse
    setInput(devBus);
    _delay_us(50);
...

Clock is 8MHz. The power comes from a regulator, yes there is a small cap. I use parts of this code for many other projects, the initSleep() for example, and it works fine.

The problem is what happens between turning on the power for the dht and initiating the comms with it. As you can see in the datasheet, 1<<WDP2 fot the watchdog timer gives 0.25s sleep time. Then the measureDHT() kicks in and (almost) starts with the same 0.25s sleep, then I drive the bus low. So between power on and driving the bus low, one would expect a bit more than 500ms. Reality is 2349ms. :( The rest of the timing is fine. First row is power, second row is the data from the dht, third row is the distance. Bad timing

If I add one line after turning on the MOSFET like this:

...
                setOutput(signalPin);
                setLow(signalPin);

                MOSFETPowerOn(devPower)

                ping(debug)
                initSleep(1<<WDP2);

                measureDHT();
...

...this happens: Correct timing

In the bottom line you see the "ping" as a little spike. The timing is suddenly correct. I ran out of ideas what it could be. I'm using Linux, my avr-gcc is v5.4.0, the programmer is an AVR Dragon. Any ideas?

Edit: If I reduce the code to:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>

#include "macros.inc"
#include "pinConfig.h"

#define wakeCycle GPIOR0

#define MOSFETPowerOn(line) { line ## _port &= ~(1<<line); line ## _ddr |= (1<<line); asm("nop"); }
#define MOSFETPowerOff(line) { line ## _ddr &= ~(1<<line); line ## _port |= (1<<line); asm("nop"); }

void initSleep(uint8_t timing) {
    asm("WDR");
    WDTCR = 1<<WDIE | timing;

    MCUCR &= ~(1<<SM0);
    MCUCR |= 1<<SE | 1<<SM1;
    asm("sleep");
}

// Watchdog timeout ISR
ISR(WDT_vect) {
    wakeCycle = 12;
}

void measureDHT() {
    initSleep(1<<WDP2); // 0.25s

    setOutput(devBus);
    _delay_us(1200); // 1ms initial low pulse
    setInput(devBus);
    _delay_us(50);
}

int main(void) {
    MOSFETPowerOff(devPower) // MOSFET driven device turned off by pullup resistor
    GIMSK |= 1<<PCIE; // Enable pin change interrupt
    sei();

    while (1) {
        MOSFETPowerOn(devPower)
        initSleep(1<<WDP2);
        measureDHT();
        MOSFETPowerOff(devPower)

        initSleep(1<<WDP2 | 1<<WDP1 | 1<<WDP0); // 2 sec
    }
}

the problem is still the same. If I change the watchdog interrupr to this:

// Watchdog timeout ISR
ISR(WDT_vect) {
    asm("nop");
}

it works fine. I remove the DHT from the circuit both this reduced version and the original code work fine. So what kind of craziness is at work here? Is this an electronic or a coding/compiling issue?

  • 1
    Have you tried to create a minimal test case by removing all the irrelevant parts of your code until the problem goes away? – user253751 Jun 03 '22 at 11:16
  • What type is `buff`? Please post the declaration. And avoid global spaghetti programming... – Lundin Jun 03 '22 at 11:47
  • "If I add one line of random code my whole program suddenly changes its behavior" is almost always caused by stack overflow or similar memory corruption bugs. – Lundin Jun 03 '22 at 11:50
  • Minimal test case see in edit. buff is ```volatile uint8_t buff[12]``` Global spaghetti? I need a couple of vars to hold a bit of data. The while thing is about collecting this 12 bytes and sending it somewhere. Also, about adding random code: I was debugging if by adding things I can monitor, or use to trigger the scope. I have discovered that during this process that magically fixes the problem. Have a look at the example in my edit. There's no spaghetti or random code there, and the problem is still the same. A stack overflow is unlikely there. – Gambanishu Habbeba Jun 03 '22 at 11:53
  • What's this pin change interrupt? – user253751 Jun 03 '22 at 12:25
  • The pin change interrupt is not required for this example. In the original code there are pin change interrupts. But a PCIE on its own in the GIMSK is harmless. In the original program PCINT4 and PCINT3 turned on in PCMSK for a while, then turned off when it's not needed anymore. – Gambanishu Habbeba Jun 03 '22 at 12:45
  • It seems to be timing-related between switching the power and initializing the watchdog. Did you try different numbers of `nop`s in between? Have you checked the voltages with a good oscilloscope, as this could be triggered by a spike? – the busybee Jun 03 '22 at 13:14
  • I have tried up to 5 nop's with no luck. But here we are in the territory of trial-and-error programming again. The MOSFET is an NDP6020P by the way. According to some datasheets, the DHT's power consumption is around 1mA. I should be able to drive it directly with a uController pin but I'm being nice here. I have checked the voltages with a scope and it looks ok. But I have also inspected the MCUSR, and there's no evidence of PORF or BORF or any other reset. The extra time is always 1.82803 sec. I would like a solution with scientific explanation. Something I can write down for the future. – Gambanishu Habbeba Jun 03 '22 at 14:16
  • I must confess, there was no resistor between the controlling pin and the gate of the MOSFET (because it's something I never do, as it has never been a problem before.). It fixes the problem, but so does many other random things so I'm not convinced that this is the root of the problem. – Gambanishu Habbeba Jun 03 '22 at 14:59
  • I see `GIMSK |= 1< – AterLux Jun 04 '22 at 09:13
  • Does the problem only occur when the MOSFET is connected? – user253751 Jun 08 '22 at 09:51
  • I can't tell it now, because it's "fixed" by adding a resistor between the controlling pin and the gate of the MOSFET, but the question is excellent. I have also thought it was an electronic issue rather than a programming one. There were no (high) spikes in the VCC, but it went temporarily pretty low, about 0.5V lower to like 2.7V. BOD is not turned on and the controller did not reset. I guess the resistor at the gate is there to avoid the weird behaviour. Although, I would still like to understand what is actually happening. – Gambanishu Habbeba Jun 09 '22 at 10:19

0 Answers0