ESP32 Interrupt jitter at 20kHz - arduino

I would like your help to solve the following problem.
I'm using an ESP32 Dev Kit V1 connected as follows:
Pin 4 (input) is connected to a 20kHz and 3.3V signal and to channel 1 of the oscilloscope; and
Pin 2 (output) is connected to channel 2 of the oscilloscope.
My goal is to use interrupts and generate a signal on pin 2 (output) that follows the variations of the signal on pin 4 (input).
The image below illustrates two behaviors. The most frequent and the other one that happens occasionally.
In the first behavior, the latency is around 3 us, but sometimes there is a variation (jitter) and the rise of the output signal takes 15 us or even more to keep up with the input.
I would like to know how to remove this occasional behavior and keep the system stable.
The code that I'm using is below. To compile and upload I am using the latest Arduino IDE version 1.8.13.
I'm using GPIO.out_w1ts and GPIO.out_w1tc because I believe it will be faster than using digitalWrite(pin, state).
Also notice that I'm not reading the state of pin 4 (input) to be faster, because of this, sometimes the output signal gets inverted, but that's not a problem at this point.
#define OUTPUT_PIN 2
#define INPUT_PIN 4
volatile bool state = false;
void IRAM_ATTR interruptFunction() {
if (state)
GPIO.out_w1ts = 0b100;
else
GPIO.out_w1tc = 0b100;
state = !state;
}
void setup() {
pinMode(INPUT_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(INPUT_PIN), interruptFunction, CHANGE);
gpio_config_t io_conf;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = 0b100;
gpio_config(&io_conf);
}
void loop() {
}
The settings used in the Arduino IDE are illustrated in the next image.
Thanks in advance for all the help!

Related

ARDUINO UNO- Need explains about program

I am begginer on Arduino. I am student and this is my first homework.
Please, could someone explain to me how to understand the following codes. Which code is better? How do operators affect the executive speed? Below is the content.
"The task was to measure the speed of code execution depending on the programming technique used. The program code was designed to expose the high and low states to the D10 (PB2) port respectively without entering delays – as a result, we obtained a rectangular waveform with the maximum frequency for a given program recording method. Programs were written by putting all statements in void setup() omitting the void loop().
Preparation for the exercise consisted in connecting the Arduino Uno board to the USB port, starting the Arduino IDE environment and turning on the oscilloscope. The oscilloscope was used to observe the rectangular waveform generated from the D10 pin (PB2) and to measure its frequency."
Code of the first program:
const byte outPin = 10;
void setup() {
pinMode(outPin,OUTPUT);
while (1)
{
digitalWrite(outPin, HIGH);
digitalWrite(outPin, LOW);
}
Code of the second program:
const byte outPin = B00000100;
void setup() {
DDRB | = outPin;
while (1)
{
PORTB = B00000100;
PORTB = B11111011;
}
Third program code:
const byte outPin =10;
byte state =0;
void setup() {
pinMode(outPin,OUTPUT);
while (1)
{
digitalWrite(outPin, state);
state = !state;
}
Code of the fourth program:
#define _BV(n) (1<<n)
const byte outPin= B00000100;
byte state = _BV(2);
void setup()
{
DDRB|=outPin;
}
while (1){
PORTB |=state;
PORTB &=~state;
}
Code of the fifth program:
const byte outPin= B00000100;
void setup()
{
DDRB|=outPin;
while (1){
PORTB |=B00000100;
PORTB &=B11111011;}
}
As I said I am begginer on ARDUINO UNO. I cannot find any solution to explain exactly my homework.
Which code is better?
The first one, of course. Reasons:
Usually, it's fast enough.
The other ones are hardware specific and do not run on any Arduino, but just on atmega328P microcontrollers.
How do operators affect the executive speed?
It's the call to digitalWrite, which makes the difference, not some operators like ~ . You should look for an similarly hardware independent replacement to digitalWrite, which does the translation between Arduino pin number and hardware register only once at compile time.
BTW: your question is off topic here :)

cannot turn on relay indefinitely on an arduino

I want to make a device like Knocki(https://knocki.com), which essentially is a relay control using a vibration sensor. i can detect vibrations rn but the problem is, once i knock the relay blinks on and then turns off. i understand this is a lack of programming that is causing this. could someone help me write code which makes it so that when i knock the relay is turned on indefinitely; until I knock again to turn relay off.
And yes u can probably tell that this code is copied from somewhere(https://wiki.keyestudio.com/Ks0068_keyestudio_37_in_1_Sensor_Kit_for_Arduino_Starters#Project_21:_Vibration_Sensor).I took it from the home page of the vibration sensor. the code was initially so that every time i knocked, the onboard Arduino led lit up. Also, right now the relay is blinking faintly every time i knock(Although correctly,in sync with my knocks)
#define SensorLED 13
#define SensorINPUT 3 //Connect the sensor to digital Pin 3 which is Interrupts 1.
unsigned char state = 0;
int Relay = 5;
void setup()
{
pinMode(SensorLED, OUTPUT);
pinMode(SensorINPUT, INPUT);
attachInterrupt(1, blink, FALLING);// Trigger the blink function when the falling edge is detected
}
void loop()
{ if(state!=0)
{
state = 0;
digitalWrite(SensorLED,HIGH);
delay(500);
digitalWrite(Relay,HIGH);
}
else
digitalWrite(SensorLED,LOW);
digitalWrite(Relay,lOW);
}
void blink()//Interrupts function
{ state++;
Yes its in your code: The (bad) example works only because there is a
digitalWrite(SensorLED,HIGH);
->>> delay(500);
a delay for 1/2 sec to keep the led on.So as a check put an other delay after the relay line and it should go on for 1/2 sec too (so the led is lit 1 sec in total)
digitalWrite(SensorLED,HIGH);
delay(500);
digitalWrite(Relay,HIGH);
delay(500);
Thats just for checking -> NEXT STEP:
Get rid of the delays (see blinkwithoutdelay example in
Arduino->File->Examples->2.Digital -> blinkwithoutdelay
and introduce a second state variable e.g.
bool relayStateOn = false;
to get an independent on/off of the relay and the led.(If thats - what I understand -what you want to do)
If you feed your relay from the board, that is not the problem. Please, check the voltage in your relay when you try to set it on, if your voltage falls down, it means that this output to your relay does not supply the necessary current.

arduino uno with PWM driven motor and 433mhz wireless receiver don't work

I am fairly new to the arduino topic and try to get a few things to work together.
First i tried setting up a DC motor that can be controlled via PWM, which works perfectly when used standalone. I can start/stop the motor and change speed depending on the value i send to the PWM pin.
Second i tried to use an RF-5V wireless receiver to work with a remote control from remote switched power outlets. For this one i followed the instructions on how to build a 433mhz sniffer.
This all by itself works as well. I can receive diffent codes depending on which keys on the remote i am pressing.
Now the fun part started: I wanted to integrate both of the projects into one, so i could use the remote to start/stop the motor.
So i came up with the following circuit:
(Thanks for some of you pointing out that the circuit does not match the sketch. I made an error when drawing, but even with the cables attached to the right pins, it works as described)
and the following code (which is partly from the instructions mentioned above):
#include <RCSwitch.h>
// init 433MHz lib
RCSwitch mySwitch = RCSwitch();
unsigned long lOldValue=0; // to check for consecutive reads on 433MHz
int motorPin = 5; // PWM-Pin to use for motor
void setup()
{
pinMode(motorPin, OUTPUT);
Serial.begin(9600);
// set-up rf receiver
mySwitch.enableReceive(0); // 433MHz Receiver on interrupt 0 => that is pin #2
}
void loop()
{
if (mySwitch.available())
{
int value = mySwitch.getReceivedValue();
// only react, if at least two times same value received
if (value == lOldValue)
{
if (value == 0)
{
Serial.print("Unknown encoding");
}
else
{
Serial.print("Received ");
Serial.print( mySwitch.getReceivedValue() );
Serial.print(" / ");
Serial.print( mySwitch.getReceivedBitlength() );
Serial.print("bit ");
Serial.print("Protocol: ");
Serial.println( mySwitch.getReceivedProtocol() );
// One of the keys on the remote
if (value == 274393) {
Serial.println("got start code, starting motor");
analogWrite(motorPin, 100); // start the motor
}
// another key on the remote
if (value == 270384) {
Serial.println("got stop code, stopping motor");
analogWrite(motorPin, 0); // stop the motor
}
}
}
lOldValue = value;
mySwitch.resetAvailable();
}
}
when i run the code and click on the remote, i get different values shown depending on the key i press. So the wireless receiver works as expected.
When i receive the right value for starting the motor, the motor really begins to turn, so this works as well.
And here the fun part starts:
As soon as i use the analogWrite function to send data to the PWM port the motor is connected to, the wireless receiver stops working (or at least I do not get any more values when pressing a key on the remote).
I found a few similar posts/problem descriptions on the net which said to try the following:
Use another pin for PWM (due to possible interrupt conflicts). I tried that as well, same behaviour
Use external power supply instead of USB-Cable, which helped somebody resolve this issue. Not here. Does not work either
So the question is:
Does anybody know how to combine those two things together so a can use the wireless receiver to get commands and switch on/off the motor with it?
I have the same problem in the past. The problem is the ability of arduino to supply them both. I recommend to use external power supply for the receiver or for the motor (it's best to do that for the motor but according to your circuit it's impossible) like the 545043YwRobot and supply the other from the arduino (I hope this is not what you try already, if so i'm sorry).
Hope it's help.
Yoav

Only receiving "<0><0><0><0><0><0><0>" on USART signal

When I try to receive the USART signal with my Silicon Labs CP210x USB to UART Bridge. The only thing I receive is:
<0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0><0> etc
I got the right settings selected: baud rate: 9600 data bits: 8 parity: none stop bits: 1
I use a ATmega128A3U. The test_LED (see code below) on port E pin 0 is working. I used a oscilloscope to check the uart signal. See picture here: http://imgur.com/dPxkdZ6
Does someone know a solution for this?
Does anyone know how to fix a framing error? (My UART software is giving this error)
CODE:
#define F_CPU (32000000UL) // CPU clock speed
#include <avr/io.h>
#include <util/delay.h>
void USARTF0_init() {
USARTF0_BAUDCTRLB = 0; //BSCALE is 0
USARTF0_BAUDCTRLA = 0xCF; //BSEL is 207
USARTF0_CTRLA = 0; //Disable interrupts
USARTF0_CTRLC = USART_CHSIZE_8BIT_gc; //8 data bits, no parity and 1 stop bit
USARTF0_CTRLB = USART_TXEN_bm | USART_RXEN_bm; // //Enable receive,transmit and high speed mode
PORTF_OUTSET = PIN3_bm; // Port F pin 3 as TX
PORTF_DIRSET = PIN3_bm; // TX pin as output
PORTF_OUTCLR = PIN2_bm; // Port F pin 2 as RX
PORTF_DIRCLR = PIN2_bm; // RX pin as input
}
void sendChar(char c) {
while( !(USARTF0_STATUS & USART_DREIF_bm) ); //Wait until DATA buffer is empty
USARTF0_DATA = c;
}
void sendString(char *text) {
while(*text) {
sendChar(*text++);
}
}
int main(void) {
USARTF0_init();
PORTE.DIRSET = PIN0_bm; // make port E pin 0 output (test_LED)
while(1)
{
_delay_ms(10);
sendString("Hello World!\n\r");
PORTE.OUTTGL = PIN0_bm; // test_LED Toggle
}
}
Code source: http://morf.lv/modules.php?name=tutorials&lasit=29
To recap the discussion, so it can useful to someone later.
If you have an oscilloscope, capture a trace for a simple test case, as #DrOctorooi has done with "#~00":
He has marked start and stop bits, and also the data bits (for serial/UART they're least-significant first)
This confirms that UART somehow works.
One should also confirm the voltage levels. 0-3.2V looks about right.
And the time base. On the captured trace it appears 1 bit takes 1.625ms, which means a baudrate of around 615. That's quite far from expected 9600. In fact, that's around 16 times slower.
Since the error has to do with time, the immediate questions are the MCU clock and UART clock dividers (and on more complex devices, and intermediate dividers and clocks).
It turns out the dividers were meant for the 32MHz clock, but this MCU has a clock of 2MHz when it comes out of reset (16 times slower, as we have seen above). Solution was to recalculate the dividers.

Are interrupts the right thing to use for my robot?

So I have this old motorized wheelchair that I am trying to convert into a robot. I replaced the original motor driver with a sabertooth 2x12 and I’m using an Arduino micro to talk to it. The motors shaft goes all the way threw so I attached a magnet and a Hall Effect sensor to the back side to act as a rotary encoder. My current goal is to be able to tell the robot to move forward a certain amount of feet then stop. I wrote some code to do this linearly however this didn't work so well. Then I learned about interrupts and that sounded like exactly what I needed. So I tried my hand at that and things went wrong on several different levels.
LEVEL ONE: I have never seemed to be able to properly drive the motors it seems like any time I put the command to turn them on inside of a loop or if statement they decide to do what they want and move sporadically and unpredictably
LEVEL TWO: I feel like the interrupts are interrupting themselves and the thing I set in place to stop the wheels from moving forward because I can tell it to move 14 rotary encoder clicks forward and one wheel will continue moving way past 1000 clicks while the other stops
LEVEL THREE: couple times I guess I placed my interrupts wrong because when I uploaded the code windows would stop recognizing the Arduino and my driver would break until I uploaded the blink sketch after pressing the reset button which also reloaded and fixed my drivers. Then if I deleted one of my interrupts it would upload normally.
LEVEL FOUR: my Hall Effect sensors seem to not work right when the motors are on. They tend to jump from 1 to 200 clicks in a matter of seconds. This in turn floods my serial port and crashes the Arduino ide.
So as you can see there are several flaws somewhere in the system whether it’s hardware or software I don't know. Am I approaching this the right way or is there some Arduino secret I don’t know about that would make my life easier? If I am approaching this right could you take a look at my code below and see what I’m doing wrong.
#include <Servo.h>//the motor driver uses this library
Servo LEFT, RIGHT;//left wheel right wheel
int RclickNum=0;//used for the rotory encoder
int LclickNum=0;//these are the number of "clicks" each wheel has moved
int D =115;//Drive
int R =70;//Reverse
int B =90;//Break
int Linterrupt = 1;//these are the interrupt numbers. 0 = pin 3 and 1 = pin 2
int Rinterrupt = 0;
int clickConvert = 7;// how many rotery encoder clicks equal a foot
void setup()
{
Serial.begin(9600); //starting serial communication
LEFT.attach( 9, 1000, 2000);//attaching the motor controller that is acting like a servo
RIGHT.attach(10, 1000, 2000);
attachInterrupt(Linterrupt, LclickCounter, FALLING);//attaching the rotory encoders as interrupts that will
attachInterrupt(Rinterrupt, RclickCounter, FALLING);//trip when the encoder pins go from high to low
}
void loop()
{//This is for controling the robot using the standard wasd format
int input= Serial.read();
if(input == 'a')
left(2);
if(input == 'd')
right(2);
if(input == 'w')
forward(2);
if(input == 's')
backward(2);
if(input == 'e')
STOP();
}
void forward(int feet)//this is called when w is sent threw the serial port and is where i am testing all of my code.
{
interrupts(); //turn on the interrupts
while(RclickNum < feet * clickConvert || LclickNum < feet * clickConvert)// while either the left or right wheel hasnt made it to the desired distance
{
if(RclickNum < feet * clickConvert)//check if the right wheel has gone the distance
RIGHT.write(D); //make the right wheel move
else
RIGHT.write(B);//stop the right wheel
if(LclickNum < feet * clickConvert)
LEFT.write(D);
else
LEFT.write(B);
}
noInterrupts();//stop the interrupts
resetCount();//set the click counters back to zero
}
//once i have the forward function working i will implament it through out the other functions
//----------------------------------------------------------------------
void backward(int feet)
{
RIGHT.write(R);
LEFT.write(R);
}
void left(int feet)
{
RIGHT.write(D);
LEFT.write(R);
}
void right(int feet)
{
RIGHT.write(R);
LEFT.write(D);
}
void STOP()
{
resetCount();
RIGHT.write(B);
LEFT.write(B);
}
void LclickCounter()//this is called by the left encoder interrupt
{
LclickNum++;
Serial.print("L");
Serial.println(LclickNum);
}
void RclickCounter()//this is called by the right encoder interrupt
{
RclickNum++;
M Serial.print("R");
Serial.println(RclickNum);
}
void resetCount()
{
RclickNum=0;
LclickNum=0;
}
don't use interrupt() and nointerrupt() (or cli() and sei()) as they will stop timer and serial interrupt, breaking a lot of things. Just set to 0 the counting variable OR use detachInterrupt and attachInterrupt.
variable used inside interrupt AND normal execution flow should be declared as volatile, or their value my be unsyncornized. So declare them like volatile int RclickNum=0;
interrupt should be fast to execute, as by default other interrupt will NOT execute while inside an interrupt.
NEVER use Serial inside interrupt; if Serial buffer is full, it will call Serial.flush(), that will wait for Serial interrupt of byte written, but because you are alreadi inside an interrupt will never happen...dead lock aka you code hangs forever!
because your "moving" function use quite a long time to execute, if multiple command arrive to the serial, thay will remain isnode the buffer until readed. So if in the terminal you write "asd" and then "e", you will see robot go left, backward, right, stop (yes, actually the stop function is not usefull as it does nothing because your "moving" function are "blocking", that mean they won't return until they ended, so the loop() code (and the read of "e") will not execute until the buffer of serial has been processed.

Resources