|
|
View previous topic :: View next topic |
Author |
Message |
biomo
Joined: 28 Oct 2010 Posts: 7
|
Problems with RS232. Strange behavior |
Posted: Thu Oct 28, 2010 9:26 am |
|
|
Hi, I have a question.
I am working with the PIC16F876A (4 MHz). The pic reads data from various analog sources through adc_read function () and send to PC via RS-232. So far so good.
The program also allows the PC to send commands to the PIC, using interrupt #int_rda and its associated function. Here's the problem. The function I use is (The PC sends a string, for example "1345 \ n". The first digit is the command and the rest is the value of-mult-):
This function does not work, get blocked:
Code: | void MyFunction
{
char inst;
float mult;
char s[20];
inst = getc (); // reads the first character is the command to execute (0-9)
get_string (s, 10); // read the string that indicates the value
mult = atof (s) // converts the string to float value corresponding
} |
But this function operates normally:
Code: | void MyFunction
{
char inst;
float mult;
char s[20];
inst = getc (); // reads the first character is the command to execute (0-9)
if (inst> 10) { //always TRUE in this case
get_string (s, 10); // read the string that indicates the value
mult = atof (s) // converts the string to float value corresponding
}
} |
It seems that by including the function get_string () inside a block-if-, or perhaps use the variable -inst- in that condition, makes it run the rest of the reading of the line. I do not know.
Although I have little time with microcontroller programming, I program (C, Java, VBasic, Pascal ..) for many years and this behavior does not respond to anything that I can understand
Please appreciate all the help you can provide, especially to understand the logic of the processes of this behavior. Maybe will be a compiler problem?
best regards |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Thu Oct 28, 2010 12:37 pm |
|
|
just a quick guess but I think you made the assumption that instr is always > 10.
Isn't /n actually two chars; 0x0d, 0x0a.
I don't know what get_string() is but CCS has a gets() which terminates on the RETURN (0x0d) character which would leave the 0x0a yet to be read.
Could that be the problem?
by saying
you would discard the extra character. |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Thu Oct 28, 2010 12:44 pm |
|
|
Well it matters little which language since all have to have a way of knowing when a string ends. In C strings are null terminated. get string expects this to be true. If your string isn't null terminated then it is terminated some other way maybe CR or LF or both. It that is true then you alone using getc will have to assemble your string and when you get to your terminator you will replace it with a null so you are in compliance with what C expects.
Now since broken clocks show the right time twice per day so broken code will will occasionally work in special circumstances. |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Thu Oct 28, 2010 1:07 pm |
|
|
char s[20]; may be initialized to zero by the compiler, that would guarantee a zero terminator when you load a shorter string into the array.
if you send "1345 \n" the quotes will zero terminate the string being sent also.
I have a digital clock, and it's never right. Even when it's broken!
(but some of my broken code has worked surprisingly well...for a while.) |
|
|
biomo
Joined: 28 Oct 2010 Posts: 7
|
|
Posted: Fri Oct 29, 2010 1:53 am |
|
|
Thank you for your interest.
I do not think the problem is the character string. Function get_string () is a function that comes with the examples of CCS in the file input.c. This function runs a reading loop with getc () until you find the ascii value '13 'or until the maximum number of characters to read. The function gets() have the same behavior in this case.
The problem is that if the process of reading is within a conditional block (always true in this case and only to force the execution of the instructions) is executed correctly and if it is outside of the conditional block is not executed, which is out of structured programming logic.
Could be a bug in the conversion from C to ASM ? In my short time in PIC programming, this is not the only gap that meeting between what does and what it should do, but I could go around solving the situations. But if this is very often maybe I'll have to change compiler. The problem is not that work differently, the problem is I do not know how it works, and I can not predict their behavior (at the programming level, of course). |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Fri Oct 29, 2010 5:13 am |
|
|
Perhaps the best thing to do is create the smallest program that has both functions in it,compile then dump the listing to see the assembler .
Now you can see ,line by line, what it's doing(or not), |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Fri Oct 29, 2010 5:21 am |
|
|
The compiler does not normally zero arrays or any vars for that matter.
gets and get_string will read input until a <cr> char(13) 0x0D is read, they will then discard the 0x0D and null terminate the string and return.
get_string should also only read upto the number of chars specified and then null terminate the string and return.
The problem possibly with this is when you are doing the check
if (inst > 10)
you are making sure that you have the start of your data before reading the string, when you are not doing the check any random char will be read and the program will move on to the get_string.
Taken from the help file
Code: |
atof
"Converts the string passed to the function into a floating point representation. If the result cannot be represented, the behavior is undefined."
|
If you do get other data comming in and atof fails then this may be why it is hannging.
"1345\n" the char '\n' is normally a NewLine char 0x0A, NOT a <cr>
but it could expand to \r\n depending on what is doing the outputting.
try
"1345\r" |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Fri Oct 29, 2010 6:10 am |
|
|
perhaps just add something to discard any potential garbage .
Code: |
while((inst = getch())<'0');
|
|
|
|
biomo
Joined: 28 Oct 2010 Posts: 7
|
|
Posted: Fri Oct 29, 2010 7:20 am |
|
|
Quote: | The problem possibly with this is when you are doing the check
if (inst > 10)
you are making sure that you have the start of your data before reading the string, when you are not doing the check any random char will be read and the program will move on to the get_string. |
I also thought that, but does not work as I show later.
The discusion about the end of the string, I say that I send in all posibilities with Java and ended in "\n", with Visual Basic append chr(13) and/or chr(10), etc.. In deed, the program works in the initial condition. I aswell send via wire, via bluetooth. I don´t think that the problem is in the send process.
To focus, the complete program, that work good, is as follows:
Code: |
#include <16F876A.h>
#include <ctype.h>
#include <stdlib.h>
#device adc=10 //propone una resolución de 10 bits (0-1023)
#FUSES NOWDT //No Watch Dog Timer
#FUSES LP //Low power osc < 200 khz
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES WRT_50% //Lower half of Program Memory is Write Protected
#fuses NOPROTECT,NOCPD,NOLVP,NOWDT,XT
#use fast_io (A) //no rehace el puerto cada vez que envía
#use fast_io (C) //no rehace el puerto cada vez que envía
#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
// FUNCTION GET_STRING OF INPUNT.C ++++++++++++++++++++++++
void get_string(char* s, int max) {
int len;
char c;
--max;
len=0;
do {
c=getc();
if(c==8) { // Backspace
if(len>0) {
len--;
}
} else if ((c>=' ')&&(c<='~'))
if(len<=max) {
s[len++]=c;
}
} while(c!=13);
s[len]=0;
}
// THE PROGRAM ++++++++++++++++++++++++++++++++++++++++++++++
float mult=5;
char s[20];
#int_rda //INTERUPTION OF UART
void MyIntFunction()
// THIS FUNCTION RUN. WITH THE STRING SEND VIA RS-232 BY THE PC, TAKE THE FIRST CARACTER AND PUT IT IN inst, THEN TAKE THE REST OF DE STRING AND PUT IT IN THE s VARIABLE.
{
char inst;
int k;
inst=0;
for (k=0;k<20;k++){s[k]=0;}
inst=getc(); //lee un caracter -> comando
if (inst>10) {
get_string(s,10); // lee un string y con una longitud máxima de 10
mult=atof(s); //convierte la cadena en su valor float correspondiente
delay_us(10); //espera 10 usegundos, no es necesario.
}
}
int16 ReadADC(int Puerto){
set_adc_channel (Puerto); //Selección del canal 0 (RA0)
delay_us (20); //Temporiza 0.01s
read_adc(ADC_START_ONLY); //Inicia la conversión
delay_ms(1); //Se asegura que se ha terminado la conversión AD
return read_adc (ADC_READ_ONLY); //Lee el resultado de la última conversión
}
int16 resultado0,resultado1; //Variable para el resultado de la conversión AD
float resultmV0=0.00;
float resultmV1=0.00;
void main()
{
set_tris_c (0b10111101); //RC7 -> Rx IN, RC6 -> Tx OUT, RC1-> ANOTHER OUT PIN (NO PROBLEM)
enable_interrupts(INT_RDA); //Activa interrupción en la recepción
enable_interrupts(global); //Habilita interrupciones
//setup_adc_ports
setup_adc (ADC_CLOCK_DIV_32); //Define la frecuencia del muestreo analógico
setup_adc_ports (ALL_ANALOG); //Todas entradas analógica
WHILE (1)
{
resultado0=LeerADC(0); //LEE EL PIN A0
resultado1=LeerADC(1); //LEE EL PIN A1
//RECALCULA Y LO ENVIA AL PUERTO SERIE
resultmV0 = ((float)resultado0) / 1023*mult;
resultmV1 = ((float)resultado1) / 1023*5;
printf ("s:%s A0: %04Lu, A1: %04Lu, A0(mV): %5.3f,A1(mV): %5.3f ,mult:%3.3f \n", s, resultado0, resultado1, resultmV0,resultmV1,mult);
}
}
|
functions MyIntFunction() like following are wrong (seem to get blocked waiting, but what?):
Code: | void MyIntFunction()
{
char inst;
int k;
inst=0;
for (k=0;k<20;k++){s[k]=0;}
inst=getc(); //lee un caracter -> comando
if (inst>10) {k=1;} //TO INITIALIZE THE inst VARIABLE
get_string(s,10); // lee un string y con una longitud máxima de 10
mult=atof(s); //convierte la cadena en su valor float correspondiente
delay_us(10); //espera 10 usegundos, no es necesario.
}
} |
Code: | void MyIntFunction() //SIMPLEST
{
get_string(s,10); // lee un string y con una longitud máxima de 10
}
} |
|
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Fri Oct 29, 2010 8:17 am |
|
|
Things to know
The rda interrupt is called every time for each and every character (byte) that is received.
An RS232 character inbound to your code can arrive at anytime in the middle of a calculation. middle of a line of code at anytime. The RDA routine must catch the character place it in memory and with as little extra code as is possible transfer control to the main routine so it is ready for the next character to arrive.
Things to never do
Never put delays in interrupt routines like #int rda
Never put floating point operations in an isr.
Never put printf in an isr.
Things to do
For a GPS interface always use a circular buffer. The GPS will send a sentence. Always parse the sentence in main
Ideas
Often only a specific sentence is needed so it is acceptable to select for the sentence as it comes in. Suppose its $abcde then set a start of sentence flag when the isr sees $ if the next char isn't 'a' then clear it
if still set and the next char isn't 'b' then clear it and so on. These are fast compares and even though there are many lines in the isr only a few are actually executed. If the sentence flag remains move the sentence into the circular buffer and set a sentence end flag. Main will see the end flag and start parsing while the circular buffer accepts the next sentence.
There are others way to do rs232 but few work as reliably as the circular buffer.
Many knowing an isr processes one byte at a time don't use get_string or any routine that will wait for more than one char.
Except for the simplest of situations RS232 needs the isr and the isr needs the circular buffer to be fully successful. |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Fri Oct 29, 2010 8:41 am |
|
|
Does the output from printf for mult mach what you are sending it ?
The only thing I can think of is that in the working code get_string is never called because inst is always <= 10.
This would mean the data you are sending is either wrong or corrupt.
If the printf matches then you know get_string is recieving the correct chars.
And what Douglas said stands about what you should not do in sir routines! |
|
|
biomo
Joined: 28 Oct 2010 Posts: 7
|
|
Posted: Sat Oct 30, 2010 2:42 am |
|
|
Thank for all,
Douglas, I will probe your recommendations.
if I undertand well, this maybe a program
Code: | byte s[20];
int cont=0;
# Int_rda / / INTERRUPCIÓN DE UART
void MyIntFunction ()
{
byte c;
c=getc();
if ((c=="$") || (s[0]=="$" ) {
s[cont]=c;
cont=cont+1;
}
}
|
And in Main
Code: | Comand=MyParseFunction(s); // My function that parse de string command
if (Command=1) then {........; MyClear(s);} //execute the comand 1 and clear de string command
if (Command=2) then {........; MyClear(s);} /execute the comand 2 and clear de string command
....
if (Command=-1) then {MyClear(s);} //The command don't exist and clear the string commad (MyClear(s) put all items of s to 0 and put the cont=0)
|
Or more simple alternate...
Code: | int1 Ismessage=0;
# Int_rda / / INTERRUPCIÓN DE UART
void MyIntFunction ()
{
Ismessage=1;
}
|
And In Main
Code: | If (Ismessage) {get_string(s,10);Ismessage=0}
Comand=MyParseFunction(s);
......
......
......
|
I'll probe this and I´ll say you the results |
|
|
biomo
Joined: 28 Oct 2010 Posts: 7
|
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|