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);
}
loop()
is executed check to see if the bool has been set and if sodoStuff()
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 withmicros()
instead.