Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Defining in-function Local Variables RS485

Status
Not open for further replies.

apsua

Member level 3
Joined
Feb 13, 2015
Messages
62
Helped
1
Reputation
2
Reaction score
1
Trophy points
8
Activity points
538
Hello,
I am having an RS485 communication problem between two pic microcontrollers for a long time, and I couldnt solve it. Actually, it is working, but after a while (10 minutes to more than one day) some unexpected data changes happen. There is no hardware problems. Previously it could hang after working similar periods. I have reduced these by disabling interrupts while eeprom writing processes.(I think this reduced).
I am still searching, I have a consideration about in-function defined local variables. Does these kept when an interrupt occur?
Btw, I dont have any problems when there is no serial communication.
Thanks.
Note: Compiler CCS C
 
Last edited:

Several things.....
What gets "kept when an interrupt occurs..." depends on how the ISR is written - it SHOULD be written to save every location that it changes (except "volatile" variables that are intended to be updated) so that it can restore the environment before it completes. Typically this refers to the devices registers more than variables.
By "in function defined local variables" I assume that you mean a variable whose scope is local to a function. Typically these are allocated on the processor's stack and so are automatically "saved" across an interrupt. However they are NOT saved between invocations of the function. If you need that they they must be declared as static. Static variables are declared in normal RAM and so might be a bit more susceptible to corruption by a rogue pointer. However I'm not sure why in-function variables would help your issue.
When you have unexpected data changes occurring, the causes are often stack corruptions, rogue pointers or array indexes that are going beyond the bounds of the array. Also passing pointers to character strings that are not NULL terminated and/or casting pointers and "pass by reference" parameters are good sources of problems.
From what you have said, I assume that the serial communications use interrupts and the ISRs are generated by the compiler (in which case the context saving and restoring will probably be correct).Therefore make sure that variables you update are declared to be volatile and that you strictly stay within ring buffers, arrays and the like, and that pointers are always correctly initialised.
Susan
 
  • Like
Reactions: apsua

    apsua

    Points: 2
    Helpful Answer Positive Rating
Hi,

In the main loop reading and writing of multibyte variables, that are manipulated in an ISR need to be acessed "atomic".

Klaus
 
  • Like
Reactions: apsua

    apsua

    Points: 2
    Helpful Answer Positive Rating
@Aussie Susan: The local variables are saved across the interrupts, and are not saved out of the function. What will happen, if in interrupt, two level function branching exists?
@KlausST: you mean very small by atomic, why?
 

Hi,

@KlausST: you mean very small by atomic, why?
No,

If you declare a variable as "atomic" then during read/write operations all interrupts are blocked to prevent data corruption.

***
Imagine a 16 bit variable. In reality this are two bytes.
If now the main loop reads the first byte...then an interrupt occurs and within the ISR the variable gets a new value...then it gets back to main loop
The main loop now reads the second byte... and here you have the problem.. high byte and low byte don't match..
The result is a wrong 16 bit value...

Klaus
 
  • Like
Reactions: apsua

    apsua

    Points: 2
    Helpful Answer Positive Rating
@KlausST:
Understood, but there is no atomic variable definition in CCS C as I see. What can I do?
 

Understood, but there is no atomic variable definition in CCS C as I see. What can I do?

In CCS, the smaller type that you can handle with a single instruction is the char or boolean.
 

So, you mean should I use 8-bit variables instead of all my 16-bit variables?
 

So i always use int8 or int16 or int32 as declaration so avoid such problems.
and i use make8, make16 and make32 functions to convert low bit variables to high bit values.
This is what CSS compiler says about variable declaration.
css.gif
 

Hi,

Using two 8 bit variables instead of one 16 bit variable doesn't solve the problem.

It's the "access" that solves the problem.

The access of an atomic 16 bit variable is like this:
* disable interrupt
* read first byte
* read second byte
* enable interrupts

Only if you can replace a 16 bit variable with one 8bit variable solves the problem.

Klaus
 

How about disabling interrupt when reading and enabling it again?
Code:
disable_interrupts(the interrupt that writes to global); 
temp=myglobalvar; 
enable_interrupts(the interrupt that writes to global);
 

@Aussie Susan:....What will happen, if in interrupt, two level function branching exists?
I'm sorry but I don't understand what you are referring to by "..two level function branching...".
If you are referring to priority interrupts where a high priority interrupt can interrupt an lower priority ISR, then there is still no problem as the hither priority ISR should save the processor state and restore it as it completes. What it interrupts does not matter as long as it is properly written.
As you are using the CCS C compiler (which is really rather old - try using the XC18 or XC16 compilers depending on what device you are actually using) then the compiler will take care of the saving and restoring for you.
I suggest that you read the User Guide for your compiler to find out how it handles interrupts. If there are special situations that you need to handle yourself, the compiler documentation will tell you.
Susan
 

Hello again,
I have realised that, while sending the data, between every fputc function there is 2ms delays, instead of waiting the txif.
Can this be the reason of my problem?
 

A delay in itself would not cause any data to change.
However using delays is probably the worst way of working out when to send the next character so changing that would only be for the better.
Can you show us your code - or more specifically a small but complete program that exhibits the problem.
Susan
 

Hello,

The related codes are below. I didn't attach the receive function and the rest, because even I cancel them with a return at the beginning, the problem goes on. If I turn off the receive interrupt, the problem doesnt occur.
Sorry for the mess in the code, changed too much to solve the problem.

Code:
interrupt:
#INT_RDA
void rsReceiveInt()
{
disable_interrupts(GLOBAL);
incomingData = fgetc(RS485);	// incomingData = RCREG;	(Both same, doesnt change)
enable_interrupts(GLOBAL);
}

in main.c:

#device HIGH_INTS = TRUE

in rs485_lib.c:

#define	RS485_RX_PIN				PIN_C7
#define	RS485_TX_PIN				PIN_C6
#define	RS485_ENABLE_PIN			PIN_C4
#bit		BIT_RS485_EN_PIN			= getenv("sfr:PORTB").0
#bit		RS485_CREN					= getenv("bit:CREN")
#define	RS485_BAUD					9600
#define	RS485_RX_BUFFER_SIZE		40

#ifndef	MULTI_ALL
#define	MULTI_ALL					0
#endif

#define	OFF							0
#define	ON							1

#use rs232(BAUD=RS485_BAUD, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, errors, stream=RS485)

#bit    rs485_collision = rs232_errors.6

void RcvOn()
{
	while(kbhit(RS485)) {getc();}
	if(addressMode == OFF) {
		setup_uart(UART_DATA);
	}
	else // addressMode == ON {
		setup_uart(UART_ADDRESS);
	}
	setup_uart(TRUE);
	enable_interrupts(INT_RDA);
	RS485_CREN = 1;
}
void RcvOff()
{
	RS485_CREN = 0;
	disable_interrupts(INT_RDA);
}
void Rs485Init(int1 addMode)
{
	addressMode = addMode;
	// RS485 veri alımını aç
	RcvOn();
	rs485IndexLast = 0;
	rs485IndexFirst = 0;
	enable_interrupts(INT_RDA);
	enable_interrupts(GLOBAL);
	BIT_RS485_EN_PIN = 0;
	RS485_CREN = 1;
}
void Rs485SetID(int ID)
{
	rs485ID = ID;
}
void Rs485SendData(int to, int length, int* data)
{
	int index;

	tempID = 0xE1; //0xE0 | rs485ID;
	tempTo = 0xF2; //0xF0 | to;
	tempAddress = 0;

	if(addressMode == 0)
	{
		tempAddress = 0x100;
	}

	// veri alımını kapat
	RcvOff();
	//output_high(RS485_ENABLE_PIN);
	//delay_ms(4);
	fputc((int16)tempAddress|tempID, RS485);
	while(!PIR1TXIF); //delay_ms(2);
	fputc((int16)tempAddress|tempTo, RS485);
	while(!PIR1TXIF); //delay_ms(2);
	fputc((int16)tempAddress|length, RS485);
	while(!PIR1TXIF); //delay_ms(2);
	checkSumToSend = tempTo^tempID^length;
	checkSumToSend ^= serialMessageTX[0];
	fputc((int16)tempAddress|serialMessageTX[0], RS485);
	while(!PIR1TXIF); //delay_ms(2);
	checkSumToSend ^= serialMessageTX[1];
	fputc((int16)tempAddress|serialMessageTX[1], RS485);
	while(!PIR1TXIF); //delay_ms(2);
	checkSumToSend ^= serialMessageTX[2];
	fputc((int16)tempAddress|serialMessageTX[2], RS485);
	while(!PIR1TXIF); //delay_ms(2);
	checkSumToSend ^= serialMessageTX[3];
	fputc((int16)tempAddress|serialMessageTX[3], RS485);
	while(!PIR1TXIF); //delay_ms(2);
	checkSumToSend ^= serialMessageTX[4];
	fputc((int16)tempAddress|serialMessageTX[4], RS485);
	while(!PIR1TXIF); //delay_ms(2);
	fputc((int16)tempAddress|checkSumToSend, RS485);
	RcvOn();
}
 

Which is the value that is changing unexpectedly?
Why are you turning off the reception interrupt and disabling the receiver side when you 'Rs485SendData'?
Also what device are you using?
Susan
 

Actually, I see now that the matter is not data change, I think the program is branching to wrong point of program when I enable int_rda. An example what happens for eg., is below:

Code:
int1 function();    // No data change in this function

if(function())
{
       if(function())
       {}
       else
       {
               // The software comes to this point for eg.
       }
}

I am using PIC18F452 & PIC18F628A. As for the disable_interrupt at the beginning of SendData, in the original lib file, it was the same, so I copied.
 

Unless you show where this happens in the actual code, I would expect it's normal operation, e.g. the interrupt or a side effect of the function itself call causes the result to be evaluated different in the subsequent calls.
 

In the code, there is no data change, also I have put a variable to indicate if there is a return 1 or return 0. It doesnt go into the "if section"(which is in the function) that returns 1, but as I said it returns 1.
Code:
if(function()){
     // the code enters here
}
After that I have put a second function call immediate after the first one for a second check to see what is happening. It returns 0.
Code:
if(function()){
     // the code enters here
     if(function()) {
         // But it doesnt enter here
     }
}
Also similar unexpected enternce to another function happens at one another point. Before, the software was hanging, which possibly occurs because of branching to an unwanted point. And all this happens when I enable the interrupt and read the rcreg, by directly;
Code:
void rsReceiveInt()
{
    disable_interrupts(GLOBAL);
    var = RCREG;
    enable_interrupts(GLOBAL);
}
or
Code:
void rsReceiveInt()
{
    disable_interrupts(GLOBAL);
    var = fgetc(RS485);
    enable_interrupts(GLOBAL);
}
What I will check today is, only enable the interrupt and only make a
Code:
void rsReceiveInt()
{
    disable_interrupts(GLOBAL);
    tempVar++;
    enable_interrupts(GLOBAL);
}
to see if reading the received data, or interrupt occurance causes the problem.
By the way, I also have another interrupt which is a timer interrupt.
Code:
#INT_TIMER1
void timerInt()
{
     disable_interrupts(GLOBAL);
     // Jobs done here..
     enable_interrupts(GLOBAL);
     clear_interrupt(int_timer1);
     return;
}
 

"Showing the actual code" was among others asking for the content of the dubious "function()".

I notice a problematic point in your code.
Code:
#INT_TIMER1
void timerInt()
{
     disable_interrupts(GLOBAL);
     // Jobs done here..
     enable_interrupts(GLOBAL);
     clear_interrupt(int_timer1);
     return;
}

In simple words, enable_interrupts(GLOBAL) must never appear in a PIC interrupt function. Neither disable_interrupts(GLOBAL) is required here. I presume you are not dealing with interrupts of different priority here, so you can completely rely on automatic disabling and re-enabling of interrupts.
 
  • Like
Reactions: apsua

    apsua

    Points: 2
    Helpful Answer Positive Rating
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top