Reputation: 418
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 for 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.
If I add one line after turning on the MOSFET like this:
...
setOutput(signalPin);
setLow(signalPin);
MOSFETPowerOn(devPower)
ping(debug)
initSleep(1<<WDP2);
measureDHT();
...
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?
Upvotes: 1
Views: 79