0

I have a small problem with collision between handling serial transmission and executing things in timer interrupt.

I'm doing program which communicates with PC. It transmits and receives as well and received values are computed in timer interrupt and then transmitted to PC. I have also a LCD for simple debugging state of variables.

My question is generally about influancing uart transmision and timer interrupts each other, but I paste part of my code to better discribing my issue:

float Kr;  
float Tp=0.05;    
float Ti; 
float Td;
float yref;

void setup() {
lcd.begin(16,2);
 Serial.begin(115200);
lcd.clear();

 analogReference(DEFAULT);  

   Timer1.initialize(50000);  
   Timer1.attachInterrupt(Myfunction);  

 }

  void loop() {


  while(1)
  {

if(Serial.available() > 0)
{
 received = (char)Serial.read();
  if(received== 'a')
  {
    Serial.print(y);  // Here uC transmits to PC
    Serial.print('b');        
  }
  if(received == 'w')
  {
    yref_str = Serial.readStringUntil(';');  //uC receives datas from PC
   // yref = yref_str.toFloat();
  }
  if(received == 'x')
  {
    Kr_str = Serial.readStringUntil(';');  //uC receives datas from PC
    //Kr = Kr_str.toFloat();
  }
  if(received == 'y')
  {
    Ti_str = Serial.readStringUntil(';');  //uC receives datas from PC
    //Ti = Ti_str.toFloat();
  }
  if(received == 'z')
  {
    Td_str = Serial.readStringUntil(';');  //uC receives datas from PC
    //Td = Td_str.toFloat();
  }

}

 //Conversion string to float
 yref = yref_str.toFloat();  
 Kr = Kr_str.toFloat();
 Ti = Ti_str.toFloat();
 Td = Td_str.toFloat();


//Simple displaying some of  variables values
  lcd.home();
  lcd.print("Process ");
 lcd.setCursor(0,1);
 lcd.print("yref=");
 lcd.print(yref);
 lcd.setCursor(9,1);
 lcd.print(" ");
 lcd.setCursor(10,1);
 lcd.print("Kr=");
 lcd.print("    ");
 lcd.setCursor(12,1);
 lcd.print(Kr);
 }
 }

  void Myfunction()
  {
     //here the datas are computed
  }

Transmition bases on kind of frame. When it gets from PC proper char sign it sends datas or receives datas. The frame has ';' sign at the end.

The main thing is that when I set values of variables: float Kr, Ti, Td at the beginning of program, in declaration section (not by serial port) it works perfectly. problem appears when I don't set value at the beginning (just declare likes above) and try to set it via serial port the situation is following: I can see correct values of those variables on LCD (exactly what I send from PC to arduino) but they don't get to Myfunction (function which is executed within timer interrupt) so actually they are 0 continually.

I don't give whole code for "Myfunction" because this function works properly, for sure. I don't change anything since good working program version. Change is only adding serial transmission.

I tried to declare variables as volatile but it didn't solve the problem.

Anyone did have any experience with such problems??

Update:

I paste code of whole program, as you asked me for: It looks like below

#include<LiquidCrystal.h>
#include<TimerOne.h>

LiquidCrystal lcd(52, 50, 48, 46, 44, 42, 40);

#define Max 127    
#define Min -128   

/*float Kr;  
float Tp=0.05;    
float Ti; 
float Td; */
/*float Kr = 0.19; 
float Tp=0.05;    
float Ti = 0.91; 
float Td = 0.19;*/
volatile float Kr;  
volatile float Tp=0.05;    
volatile float Ti; 
volatile float Td;
float yref= -50;
float y;
float ep_p, ep_i, ep_d, u_b; 
float U_pr, U_int, U_intp, U_ip;  
float U_d, Ui_d, Ui_d_pop, U_d_pop;   
int Upwm;  
float u_b_OGR;  
float b=1; 
float c=0;  
float Tdf;  

float ep_a;  
float Tt;  
float U_a; 
int out=11;  
int inp=A7; 
int x;       
boolean state; 

 String Kr_str, Ti_str, Td_str, yref_str;
 char received;




void setup() {
lcd.begin(16,2);
Serial.begin(115200);
lcd.clear();
pinMode(out,OUTPUT);
pinMode(inp,INPUT);


analogReference(DEFAULT);  
Timer1.initialize(50000);  
Timer1.attachInterrupt(Myfunction);  



U_int=0;    
U_intp=0;    
Ui_d_pop=0;
U_d_pop=0;
y=0;          
U_a=0;        

 state=0;   


}

void loop() {  


if(Serial.available() > 0)
{
  received = (char)Serial.read();
  if(received == 'a')
  {
    Serial.print(y);
    Serial.print('b');        
  }
  if(received == 'w')
  {
    yref_str = Serial.readStringUntil(';');
   // yref = yref_str.toFloat();
  }
  if(received == 'x')
  {
    Kr_str = Serial.readStringUntil(';');
    //Kr = Kr_str.toFloat();
  }
  if(received == 'y')
  {
    Ti_str = Serial.readStringUntil(';');
    //Ti = Ti_str.toFloat();
  }
  if(received == 'z')
  {
    Td_str = Serial.readStringUntil(';');
    //Td = Td_str.toFloat();
  }

}
yref = yref_str.toFloat();
Kr = Kr_str.toFloat();
Ti = Ti_str.toFloat();
Td = Td_str.toFloat();


lcd.home();
lcd.print("Process");
lcd.setCursor(0,1);
lcd.print("yref=");
lcd.print(yref);
lcd.setCursor(9,1);
lcd.print(" ");
lcd.setCursor(10,1);
lcd.print("y=");
lcd.print("    ");
lcd.setCursor(12,1);
lcd.print(y);

}

void Myfunction()
{

Tt=0.3*Ti;
Tdf=Td/10;


y=analogRead(inp);
y=y/4; 
y=y-128;  


ep_p=b*yref-y;  
ep_i=yref-y; 
ep_d=c*yref-y;  
U_pr=Kr*ep_p;  
U_int=U_ip*Tp+U_intp;  
U_ip=ep_i*(Kr/Ti)+U_a;                     
U_intp=U_int; 
Ui_d=ep_d*Kr; 
U_d=(Td/Tdf)*Ui_d-(Td/Tdf)*Ui_d_pop-U_d_pop*exp(-Td/Tdf);  
Ui_d_pop=Ui_d; 
U_d_pop=U_d; 
u_b=U_pr+U_int+U_d;

if (u_b > Max)
 {
 u_b_OGR=Max;    
}
else if (u_b < Min)
{
  u_b_OGR=Min;
}
else if (u_b>=Min && u_b<=Max) 
{
u_b_OGR=u_b;
}



ep_a=u_b-u_b_OGR; 
U_a=ep_a*(1/Tt); 


Upwm=(int)u_b_OGR+128; 
analogWrite(out,Upwm); 

}
  • Could you please add to the question a Minimal, Complete, and Verifiable example showing your problem? As it stands, your question is unanswerable without a crystal ball. – Edgar Bonet Jun 21 '17 at 20:43
  • Interrupts are for short pieces of code that do not block or cause interrupts. You can't call Serial functions and you shouldn't be performing computations. What you should do it toggle a volatile global bool and then exit the interrupt. Next time loop() is executed check to see if the bool has been set and if so doStuff() – Code Gorilla Jun 22 '17 at 7:21
  • I've updated my question and gave whole code of my program. Please look at it and advise what can be wrong – M_K Jun 22 '17 at 20:18
  • I have added a 'Update' section to my answer. – Jot Jun 22 '17 at 22:20
  • You wrote: “analogRead(inp)”. This alone is a no-no in an ISR: it takes about 110 µs, enough to loose incoming serial data. And also: “exp(-Td/Tdf)”. Since Tdf=Td/10, this is exp(−10) ≈ 4.5399930e-5. You should declare it as a constant, rather than repeating the (very expensive) calculation each time. Seriously: forget about interrupts and manage your timings with micros() instead. – Edgar Bonet Jun 23 '17 at 7:53
1

When you say that something is working for sure, then there is a big chance that the problem is in that part. If the problem was somewhere else, then perhaps you could find the problem yourself.

Interrupts are executed one after another (with a few exceptions). Things run smooth when all interrupt routines are as short and as fast as possible. You have no control over the interrupt routine of the serial communication in the Serial library, but at least your own interrupt function should be as short as possible.

Please show us a sketch that has the problem. You have removed the most important part.
If for example your interrupt function calls a Serial function, then it can go wrong because the Serial library uses interrupts itself. If your interrupt function takes longer than the interrupts from receiving data at 115200 baud, then characters could be missed. The keyword 'volatile' should be used in a proper way. The Arduino Mega has a 8-bit microcontroller, that means that when handling an 16-bit or 32-bit variable an interrupt could occur while that variable is read or writting only halfway. And so on, and so on.

Update
You have shown the complete sketch, and the interrupt function 'myFunction' takes a very long time to do all those calculations.

The 'Kr', 'Ti' and 'Td' are written in the loop, and used in 'Myfunction' which is an interrupt routine. They are float variables that are 4 bytes long. That means that 'Myfunction' could be executed when one of those variables are written only halfway. It is better to disable interrupts when writing those variables. To keep disabling the interrupts short, don't disable interrupts when calling .toFloat(). Use temperary variables.

float yref_temporary = yref_str.toFloat();
float Kr_temporary = Kr_str.toFloat();
float Ti_temporary = Ti_str.toFloat();
float Td_temporary = Td_str.toFloat();

noInterrupts();
yref = yref_temporary;
Kr = Kr_temporary;
Ti = Ti_temporary;
Td = Td_temporary;
interrupts();

I think you have to make 'yref' volatile as well.

The Serial input is 115200 baud, that can be an interrupt every 87µs.
The Timer1 Myfunction runs every 50ms.
The Myfunction takes more than 600µs.

I did not measure the timing of 'Myfunction'. I calculated it for a rough number. 5 float divisions = 5*70µs. 11 float multiply = 11*7µs. 10 float substractions or additions = 10*7µs. Plus analogRead of 112µs. Plus a few more, that adds up to at least 600µs. It could be about 1ms.

There might be more conflicts.
The baudrate is high, and the Serial.readStringUntil is slow because it uses a String object.
There is no error checking for the received values.
The four .toFloat are always called, even when there was no serial data received.
Printing to the lcd display is also done every time the loop runs. There is no need to update the slow LCD display hundreds of times per second. That could be slowed down with millis.

Conclusion: Your 'Myfunction' is a real interrupt routine. When that is busy, the other interrupts have to wait. Since it takes more than 600µs, the Serial interrupts could be missed.
Perhaps a software timer with millis is accurate enough for the calculations. To be able to use millis, the Serial.readStringUntil can no longer be used.

  • Unless the function called from the ISR is itself producing serial output, the problem is most likely going to be the lack of a volatile declaration and atomic access. The simplest solution may be to disable the interrupt while updating the variables; another option is to write to a holding set, and set a flag requesting the ISR to copy them to the operational one. – Chris Stratton Jun 21 '17 at 21:06
  • @Jot, thank you for this answer, you suggest that timer interrupt routine takes too much time to the serial receiving interrupt can be called at all. But the situation is strange a bit because datas are being received and displayed on LCD so they really arrive to uC but they don't get to timer interrupt routine. – M_K Jun 24 '17 at 11:17
  • @M_K, I thought about that. But in the sketch that you show, only yref and y are printed, not the others. It is hard to tell what is going on in the interrupt routine and I don't know why you conclude that the variables don't reach the interrupt routine. The same global volatile variables are used, so they definitely are available in the interrupt routine, it must be something else. It would be better to declare locally used variables only locally to make the code better readable. The analogWrite to pin 11 uses Timer2, that should be okay. – Jot Jun 24 '17 at 12:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.