Arduino External Interrupt Not Fast Enough - arduino

I've been trying to measure the time that the line is high on the Arduino. It goes high, then stays high for a couple of milliseconds. Then it gets pulled low for 1us and then floats back to high. My code doesn't seem to recognise the line getting pulled low. Is 1us too fast for the interrupt? How can I slow it down?
Thank you
EDIT
My thoughts are to use an RC filter in conjunction with a diode to slow the rise time enough for the Arduino to recognise the change, but only when the change occurs from the receiving line. Is this viable? Or can I use a pulse extender chip with a diode in the same way?
#define ESC 2 //the digital pin the esc signal line is attached to
int throttlePos = 0;
volatile unsigned long timer_start;
volatile int last_interrupt_time;
volatile int pulse_time;
void setup() {
// put your setup code here, to run once:
pinMode(ESC, OUTPUT); //originally set the ESC's line to output to keep line high ready for throttle armature
digitalWrite(ESC, HIGH); //keep the pulse high due to inverted throttle pulse
Serial.begin(115200); //opens the serial port for use when testing
timer_start = 0;
attachInterrupt(digitalPinToInterrupt(ESC), calcSignal, CHANGE);
for(throttlePos = 0; throttlePos <= 1000; throttlePos += 1) //these for loops arm the ESC by emulating an inverted PWM pulse, process takes two seconds
{
digitalWrite(ESC, LOW);
delayMicroseconds(1500);
digitalWrite(ESC, HIGH);
delayMicroseconds(100);
}
for(throttlePos = 1000; throttlePos <= 2000; throttlePos += 1)
{
digitalWrite(ESC, LOW);
delayMicroseconds(1000);
digitalWrite(ESC, HIGH);
delayMicroseconds(100);
}
}
void loop() {
digitalWrite(ESC, LOW);
delayMicroseconds(1200);
digitalWrite(ESC, HIGH);
delayMicroseconds(100);
delay(19);
Serial.println(pulse_time);
}
void calcSignal()
{
//record the interrupt time so that we can tell if the receiver has a signal from the transmitter
last_interrupt_time = micros();
//if the pin has gone HIGH, record the microseconds since the Arduino started up
if(digitalRead(ESC) == HIGH)
{
timer_start = micros();
}
//otherwise, the pin has gone LOW
else
{
//only worry about this if the timer has actually started
if(timer_start != 0)
{
//record the pulse time
pulse_time = ((volatile int)micros() - timer_start);
//restart the timer
timer_start = 0;
}
}
}

1/1us is 1MHz. Given that your Arduino executes instructions at 16MHz (at best, some instructions take longer), your interrupt handler could easily be missing things. However, the interrupt should still execute, even if the interrupt condition is cleared before the interrupt finishes.
Are you using the Arduino libraries for interrupts or configuring pin change interrupts directly at the register level?
Have you verified that the pulse into the Arduino is electrically sound? Without knowing more about your circuit, it's difficult to suggest a fix.

Related

how do i read and output pwm with a black pill board?

I am making an stm32 based motor speed controller for a DC motor. But I can't read PWM off my receiver and I can't make my MOSFET vary the output! I need help because now it's only on or off! I am using a RobotDyn BlackPill and an STP36NF06L Mosfet. And also I use Arduino ide with the STM board.
Code:
int Motor = PA15;
int rc = PB1;
int s;
void setup() {
pinMode(Motor, OUTPUT);
pinMode(rc, INPUT);
digitalWrite(Motor, LOW);
}
void loop() {
if(s = (map(pulseIn(rc, HIGH), 1100, 1900, 0, 255)) > 200) {
digitalWrite(Motor, HIGH);
}
else{
digitalWrite(Motor, LOW);
}
delay(10);
}
If you want to read an external PWM signal you can use a timer in input capture mode, such that one channel triggers on the rising edge and the other triggers on the falling edge then compute the period and as such the frequency and duty cycle of the incoming signal. It is well documented in the reference manual and there are various examples and even YouTube tutorials of input capture mode.

Atmega328P Wake up from Power Down mode using a edge Triggered Interrupt

Snapshot from the ATmega328P datasheet:
According to the above section of the ATmega328P datasheet, only a Level or Pin change interrupt should wake up the CPU from Power Down Sleep Mode.
However, in the following code, a rising edge is being used to wake up the CPU from Power Down Mode.
#include <LowPower.h>
const byte led_pin = 8;
const byte interrupt_pin = 2;
volatile byte state = LOW;
void setup() {
Serial.begin(9600);
pinMode(led_pin,OUTPUT);
}
void loop() {
// the interrupt must be attached each loop
attachInterrupt(digitalPinToInterrupt(interrupt_pin), interrupt_routine, RISING);
LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
detachInterrupt(digitalPinToInterrupt(interrupt_pin)); // remove interrupt
// the usual wake routine that turns on the LED
if (state == HIGH) {
digitalWrite(led_pin, HIGH);
delay(500);
}
if (state == HIGH){
state = LOW;
digitalWrite(led_pin,LOW);
}
}
void interrupt_routine() {
state = HIGH;
}
Code taken from "Arduino Interrupts with PIR Motion Sensor".
I don't understand how this code is working.
The Atmel datasheet make people believe that you can only use LOW interrupts to wake up the MCU when it is in sleep mode. It has however been long confirmed that you can use any type of interrupt (Rising edge / Falling edge / Low level / any logical change) to wake up the ATmega328P from sleep mode. There was a mistake on Atmel's datasheet. This was confirmed and documented by Nick Gammon on his post on Interrupts.

ESP8266 GPIO expander missing interrupts

I have a program that lets an LED pulse. I also connected the PC8574 GPIO expander with a push button. I want to evaluate the keypress. However, I can only read the status of the INT (interrupt) while the program is in the part between making the LED brighter and making it darker again (between the two for loops)
I know that the problem is the delays withing the for loops but I have no idea how to avoid that.
Would it be possible to evaluate the interrupt related code more often or like a real interrupt - always when the actual key is pressed? And if so, how?
I use this library: https://github.com/WereCatf/PCF8574_ESP
/*LED_Breathing.ino Arduining.com 20 AUG 2015
Using NodeMCU Development Kit V1.0
Going beyond Blink sketch to see the blue LED breathing.
A PWM modulation is made in software because GPIO16 can't
be used with analogWrite().
*/
#include <pcf8574_esp.h>
#include <Wire.h>
TwoWire testWire;
// Initialize a PCF8574 at I2C-address 0x20, using GPIO5, GPIO4 and testWire for the I2C-bus
PCF857x pcf8574(0x20, &testWire);
#define LED D1 // Led in NodeMCU at pin GPIO16 (D0).
#define BRIGHT 300 //max led intensity (1-500)
#define INHALE 1250 //Inhalation time in milliseconds.
#define PULSE INHALE*1000/BRIGHT
#define REST 1000 //Rest Between Inhalations.
#define PIN_INT D5
#define PIN_SDA D7
#define PIN_SCL D8
//----- Setup function. ------------------------
void setup() {
Serial.begin(115200);
Wire.pins(PIN_SDA, PIN_SCL);//SDA - D1, SCL - D2
Wire.begin();
pinMode(PIN_INT, INPUT_PULLUP);
pcf8574.begin( 0xFF);
pcf8574.resetInterruptPin();
pinMode(LED, OUTPUT); // LED pin as output.
}
bool CheckKey(byte key, byte num){ //0, 1, 2, 3
return key & (1 << num);
}
//----- Loop routine. --------------------------
void loop() {
//ramp increasing intensity, Inhalation:
for (int i=1;i<BRIGHT;i++){
digitalWrite(LED, LOW); // turn the LED on.
delayMicroseconds(i*10); // wait
digitalWrite(LED, HIGH); // turn the LED off.
delayMicroseconds(PULSE-i*10); // wait
delay(0); //to prevent watchdog firing.
}
if( digitalRead(PIN_INT)==LOW ){
delay(50);
byte b = pcf8574.read8();
Serial.println( "INT: " + String(b));
byte keys = ((~b)) & 0x0F;
if( CheckKey(keys, 8) ){
Serial.println( "KEY 7");
delay(2000);
}
}
//ramp decreasing intensity, Exhalation (half time):
for (int i=BRIGHT-1;i>0;i--){
digitalWrite(LED, LOW); // turn the LED on.
delayMicroseconds(i*10); // wait
digitalWrite(LED, HIGH); // turn the LED off.
delayMicroseconds(PULSE-i*10); // wait
i--;
delay(0); //to prevent watchdog firing.
}
delay(REST); //take a rest...
}
You could use the PCF8574 INT pin as an interrupt to the ESP8266 via Arduino's attachInterrupt() function, but you wouldn't gain much from that, since in order to detect which key was pressed you need to call pcf8574.read8(), and you can't do that from the interrupt handler.
The ESP8266 Arduino core is based on the Espressif NONOS SDK, so you can't have a separate thread to monitor key presses. I would suggest defining a helper function that checks if a key is currently being pressed, and then calling that function as often as you can in your main loop, e.g. at every iteration of each of your two for loops. The LED brightness ramps would be slightly disrupted when there is a key press, but I think it wouldn't be noticeable to the human eye.
So the helper function could be defined as:
byte GetKeyPress(void) {
if (digitalRead(PIN_INT) == LOW) {
return ~pcf8574.read8();
}
else {
return 0;
}
}
Then, at the beginning of the loop() function declare a static variable static byte keyPress;, and call the above function in each of the two for loops:
if (keyPress == 0) {
keyPress = GetKeyPress();
}
When you want to process a key press (e.g. between the two for loops like in your code), you can do like that:
if (keyPress) {
/* Do your stuff. */
keyPress = 0;
}

External digital interrupt and dht11

I have an Arduino Pro Mini 5v, 16 mhz and it is connected to a digital switch on pin 2. This switch is used to wake the Arduino from sleep using a external digital interrupt. I also have a DHT11 temperature sensor connected to pin 9. What I want to achieve is the when the Arduino is awake for 5 seconds and also when the switch on pin 2 is HIGH, I want to read the temperature sensor and return the temperature. I am using the DHT11 library by Tillart and when I do this, it returns a TIME_OUT error. The only possible explanation I have for this is that somehow the voltage is changed when both the DHT11 and the switch on pin 2 is being read together? Any pointers to a solution will be greatly appreciated. Thank you.
Edit 1: Added code
#include <LowPower.h>
#include <dht.h>
int pin2 = 2;
dht DHT;
#define DHT11_PIN 9
void pin2interrupt(void)
{
// Function called when awoken from sleep
// Detach interrupt to stop it from continuosly firing when in normal mode
}
void enterSleep(void)
{
attachInterrupt(0, pin2interrupt, HIGH);
Serial.println("Sleeping");
delay(100);
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
Serial.println("Awake!");
}
void setup()
{
Serial.begin(115200);
pinMode(pin2, INPUT);
pinMode(DHT11_PIN, INPUT);
}
int seconds = 0;
void loop()
{
delay(1000);
seconds++;
Serial.println("Awake in the loop!");
Serial.println(seconds);
if (digitalRead(pin2) == LOW && seconds == 5)
{
seconds = 0;
Serial.println("No child detected, so going to sleep!");
delay(200);
enterSleep();
}
else if (seconds == 5)
{
Serial.print("DHT11, \t");
int chk = DHT.read11(DHT11_PIN);
switch (chk)
{
case DHTLIB_OK:
Serial.print("OK,\t");
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.print("Checksum error,\t");
break;
case DHTLIB_ERROR_TIMEOUT:
Serial.print("Time out error,\t");
break;
default:
Serial.print("Unknown error,\t");
break;
}
// DISPLAY DATA
Serial.println(DHT.temperature, 1);
delay(2000);
seconds = 0;
}
}
Edit 2: I also forgot to to mention that I am using the LowPower library by RocketScream to put the Arduino to sleep. The library can be found here: https://github.com/rocketscream/Low-Power
As discussed in the issues section on the official Github page of the DHT11 library by Rob Tillart, the problem is caused because some DHT11 sensors take longer to transfer data back to the board then the 50ms or so specified on the datasheet. Therefore, if you are encountering this problem try increasing the DHTLIB_TIMEOUT on the dht header file by reducing the the value dividing the F_CPU value to around 400 and try again. This allows the board to wait longer than 50ms for the board to receive data back from the sensor. If this fix doesn't work, you might want to try measuring the response time using an oscilloscope as it seems some DHT11 are built differently.

Arduino - Using interrupts freezes processing and serial output?

So, the interrupts seem to work insofar as "interrupting" when an event happens. My only problem is that I the interrupts will occur 2-3 times and everything essentially stops (Serial out, everything).
I was programming the board to output serially a calculated distance based on the output of the HC-SR04 distance IC. The distances are calculated accurately but, like I said earlier, everything seems to freeze. Below is both the code and an image of the serial monitor.
#define TRIGPIN 4
#define ECHOPIN 3
#define RED 2
#define GREEN 13
#define INTNUM 1 //interrupt pin 1 is digital pin 3 on the duemilanove
#define PULSE 10 //microseconds
#define CYCLETIME 50 //milliseconds
void ledWrite(int), trigPulse(), getTime();
int millisNow, millisPrev = 0;
int microsPrev;
boolean isHigh = false;
void setup() {
Serial.begin (9600);
pinMode(TRIGPIN, OUTPUT);
pinMode(ECHOPIN, INPUT);
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
attachInterrupt(INTNUM, getTime, CHANGE);
}
void loop() {
trigPulse();
// some other code while waiting on HC-SR04 to interrupt us when echo goes HIGH
}
void trigPulse(){
if( (millisNow = millis()) - millisPrev >= CYCLETIME){ //sufficient cycle time
digitalWrite(TRIGPIN, HIGH);
delayMicroseconds(PULSE);
digitalWrite(TRIGPIN, LOW);
millisPrev = millisNow; //reset clock
}
return;
}
void ledWrite(int dTime){
int distance = dTime/58.2;
if (distance < 4) {
digitalWrite(RED,HIGH);
digitalWrite(GREEN,LOW);
}
else {
digitalWrite(RED,LOW);
digitalWrite(GREEN,HIGH);
}
if (distance >= 200 || distance <= 0){
Serial.println("Out of range");
}
else {
Serial.print(distance);
Serial.println(" cm");
}
}
void getTime(){
int timeNow = micros();
Serial.println("Interrupted");
if(isHigh == false){
microsPrev = timeNow; //get time now, pin LOW->HIGH
isHigh = true;
Serial.println("Returning ..");
return;
}
else { //pin HIGH->lOW
ledWrite(timeNow - microsPrev);
isHigh = false;
microsPrev = micros();
Serial.println("Returning ..");
return;
}
return;
}
I know this is an old thread, but I just came by it having my own problems. The problem here is that you cannot use:
Serial.Print()
Within an interrupt service routine. The reason that the Serial.Print() doesn't work within an ISR is that it uses interrupts to pull the characters out of the serial buffer, but interrupts of a certain level are masked within the ISR. What basically happens is that the arduino throws out all other interrupts that are of a lower priority, which Serial.Read() falls into.
It is documented in a number of places: link1, link2, link3
I think you are getting interrupt while you are already processing interrupt. You should try disabling the interrupt as soon as you are in interrupt function and re-enable it again when you are done processing just before return. Hence I would advice to have just one return so that you don't have to repeat code of enabling interrupt. Also make sure the function which you are calling inside your interrupt code do not re-enable the interrupt. It may happen that the function micros() or any of the Serial function are re-enabling the interrupt.
I would suggest instead of calling function directly in you interrupt code try using some flags and set/reset in interrupt and use these flags in main loop to call your regular function.

Resources