|
|
View previous topic :: View next topic |
Author |
Message |
karthickefy
Joined: 18 Jun 2015 Posts: 8
|
Need help, Send multiple characters through RS232 to ctrl IO |
Posted: Mon Jul 13, 2015 2:41 am |
|
|
Hi All,
I'm new in this forum and also new for CCS. I'm doing a project for DIO control using RS232 (In PC side LabVIEW will send the command).
Please see the below code which can support to send only one characters and control the port.
For Ex: If I send "A" PC1 is ON and if I send "a" PC1 is getting OFF. But, I would like to send more characters instead of single characters. Ex: PORTA1-ON or PORTA1-OFF. Please someone do the needful. Thanks.
I'm using CCS PCWHD 5.008 and MPLAB ICD3 with MCD-DEMO2 (pic16.com) development board.
Code: |
#include <16F877A.h>
#device ADC=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(crystal=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1, Errors)
void main()
{
char k;
while(TRUE)
{
k= getc();
if (k=='A')
output_high(pin_c0);
if (k=='a')
output_low(pin_c0);
if (k=='B')
output_high(pin_c1);
if (k=='b')
output_low(pin_c1);
if (k=='C')
output_high(pin_c2);
if (k=='c')
output_low(pin_c2);
if (k=='D')
output_high(pin_c3);
if (k=='d')
output_low(pin_c3);
if (k=='E')
output_high(pin_c4);
if (k=='e')
output_low(pin_c4);
if (k=='i')
printf(" PIC 16f877A based DIO");
}
}
|
Please Help.
Thanks.
Karthick |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Jul 13, 2015 3:41 am |
|
|
It already will.
If you send this code (for example) the text 'AB', then Pin C0 will go high and C1.
The only command there that would not do this, is the 'i', since this takes about 20mSeC to execute, and if more than one character the other 'way' comes in this time, it'll be lost. |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Mon Jul 13, 2015 10:19 am |
|
|
Hi,
In general, the best way to do what you want is to buffer the incoming serial data, and have a means to detect when a full 'command' has been received. You then need to interpret the command, and act on it. There are many ways to do this, and in most cases a 'linear buffering' scheme is sufficient. If the host is passing a lot of data to the PIC, a 'circular buffer may be warranted.
Here are some sample code snippets that would help you along to realize a buffered serial receive scheme:
First, some variable definitions and the serial interrupt handler.
Code: |
//-----< RS232 Packet definitions >-----
int RX_Command_Ready; // TRUE If a receive packet is ready
#define RX_SIZE 10 // RS232 buffer for serial reception
char RxBuffer[RX_SIZE]; // RS232 serial RX buffer
int Index = 0; // RS232 RX data IN index
#INT_RDA // Interrupt driven RS232 routine
void rs232_isr(void)
{
char temp; // local data storage
temp = fgetc(PC); // get rx data
// If we got a '#', then the command is beginning.
if (temp == '#')
{
Index = 0;
RxBuffer[Index] = temp;
Index++;
return;
}
// If we got a CR, then the command is completed.
if (temp == CR)
{
RxBuffer[Index] = temp;
RX_Command_Ready = TRUE;
return;
}
// Save the character to the receive buffer.
RxBuffer[Index]=temp;
// Check for buffer overflow.
if ( Index >= (RX_SIZE - 1) )
Index = 0;
else
Index++;
}
|
This code waits for a 'start' character, the '#', and then starts buffering incoming data until a 'CR' (carriage return) is received. At this point, the command is considered valid, a flag (RX_Command_Ready) is set, and the command is processed in Main().
Then some code in Main().
Code: |
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
while(1)
{
// Here we wait for a full command string to be received.
if ( RX_Command_Ready == TRUE )
{
// Just to be safe, first things first...
disable_interrupts(GLOBAL);
RX_Command_Ready = FALSE;
***** insert Command processing here *****
}
}
|
These bits-n-pieces should allow you to get the idea, and quickly code a multi-character serial command input.
John |
|
|
karthickefy
Joined: 18 Jun 2015 Posts: 8
|
|
Posted: Tue Jul 14, 2015 1:23 am |
|
|
Thank you ezflyr and Ttelmah for your help. I will modify the code and update you. Thanks again. |
|
|
karthickefy
Joined: 18 Jun 2015 Posts: 8
|
|
Posted: Tue Jul 14, 2015 3:01 am |
|
|
Dear John,
Please see my below code. Please guide me where I made the mistake.
Code: |
#include <16F877A.h>
#device ADC=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(crystal=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,errors)
#define BUFFER_SIZE 32
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
#INT_RDA
void serial_isr() {
int t;
buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
next_in=t; // Buffer full !!
}
#define bkbhit (next_in!=next_out)
BYTE bgetc() {
BYTE c;
while(!bkbhit) ;
c=buffer[next_out];
next_out=(next_out+1) % BUFFER_SIZE;
return(c);
}
void main() {
char cmd;
enable_interrupts(int_rda);
#if defined(__PCD__)
enable_interrupts(intr_global);
#else
enable_interrupts(global);
#endif
printf("\r\n\Running...\r\n");
// The program will delay for 10 seconds and then display
// any data that came in during the 10 second delay
do {
delay_ms(1000);
printf("\r\nBuffered data => ");
while(bkbhit)
putc( bgetc() );
}
while (TRUE)
{
cmd= bgetc();
if (cmd== RELAYA1)
output_high(pin_A0); // cH1 ON
if (cmd== RELAYA0)
output_low(pin_A0); // cH1 OFF
if (cmd==RELAYB1)
output_high(pin_A1); // cH2 ON
if (cmd==RELAYB0)
output_low(pin_A1); // cH2 OFF
}
}
|
I face the below error (5 errors) while building code.
Line51 (6,7) :Expect;
Line54 (14,20): Undefined identifier RELAYA1
Line58 (19,25): Undefined identifier RELAYA0
Line60 (18,24): Undefined identifier RELAYB1
Line62 (18,24): Undefined identifier RELAYB0
Please help.
Thanks,
Karthick |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Tue Jul 14, 2015 7:59 am |
|
|
Hello Karthick,
'Tough Love' mode = ON
You are clearly a beginner at 'C' programming, so I find it rather strange that you chose to ignore the code I already provided that was essentially complete 'out-of-the-box', and instead try to implement a rather advanced concept, the 'circular buffer', which is clearly beyond your capability/understanding as a beginner. Sorry, but based on what you have posted thus far, that is a simple fact.
If you want to continue with the circular buffer concept, then I suggest you set aside a couple of evenings to study the forum archives because much has been written about this technique. For the record, you need to actually *understand* what the circular buffer is doing - it is not sufficient to simply copy a piece of 'ex_sisr.c' into your code and hope it works.....
If you want to continue with the linear buffer concept, I suggest that you at least try to implement the code I've provided. Basically, learn to walk before you run, and once you get this experience, more advanced buffering techniques can come in later projects....
Learning an programming language is a step-by-step process, that takes time and dedication. Don't try to bypass the early steps as they are an important foundation on which the later, more advanced steps will be built!
'Tough Love' mode = OFF
To answer your specific questions, the compiler thinks you are missing a ';', and the four RELAY variables have never been defined in your code, so the compiler has no idea what to do with them....
John |
|
|
karthickefy
Joined: 18 Jun 2015 Posts: 8
|
|
Posted: Tue Jul 14, 2015 8:24 am |
|
|
Dear John,
Yes you are right. I'm beginner at C programming. Accually I have tried your example and I got some error while build the code. Compiler shows "PC" and "CR" has value not defined . That is what I chose"ex_sisr.c" example program.
Thanks.
Karthick. |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Tue Jul 14, 2015 8:42 am |
|
|
Hi Karthick,
It's OK, but rather than blasting off in a completely different direction, just ask for help on these issues. There are a lot of people here that can answer your specific questions.....
The 'PC' is a 'stream' specifier. The function 'fgetc' requires a stream specifier, which must be previously defined. Look in the CCS manual for the #use rs232 definition, and you will see how to solve this issue.
The 'CR' is just a constant for the 'Carriage Return' character. It makes the code easier to read 'at-a-glance' to see what's going on. Add this to the top of your program:
Code: |
#define CR 0x0d // ASCII Value for a Carriage Return
|
John |
|
|
karthickefy
Joined: 18 Jun 2015 Posts: 8
|
|
Posted: Wed Jul 15, 2015 1:45 am |
|
|
Dear John,
Below code is based on your example. Compiler does not accept the " RELAYA1 " (Alphanumerical characters) and it only accept numerical character. please guide me.
Code: | #include <16F877A.h>
#device ADC=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(crystal=20000000)
#use FIXED_IO( C_outputs=PIN_C5,PIN_C4,PIN_C3,PIN_C2,PIN_C1,PIN_C0 )
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PC,errors)
#include <Serial with buffer.h>
#define RELAYA1
#define RELAYA0
#define RELAYB1
#define RELAYB0
#define CR 0x0d // ASCII Value for a Carriage Return
//-----< RS232 Packet definitions >-----
int RX_Command_Ready; // TRUE If a receive packet is ready
#define RX_SIZE 10 // RS232 buffer for serial reception
char RxBuffer[RX_SIZE]; // RS232 serial RX buffer
int Index = 0; // RS232 RX data IN index
#INT_RDA // Interrupt driven RS232 routine
void rs232_isr(void)
{
char temp; // local data storage
temp = fgetc(PC); // get rx data
// If we got a '#', then the command is beginning.
if (temp == '#')
{
Index = 0;
RxBuffer[Index] = temp;
Index++;
return;
}
// If we got a CR, then the command is completed.
if (temp == CR)
{
RxBuffer[Index] = temp;
RX_Command_Ready = TRUE;
return;
}
// Save the character to the receive buffer.
RxBuffer[Index]=temp;
// Check for buffer overflow.
if ( Index >= (RX_SIZE - 1) )
Index = 0;
else
Index++;
}
void main()
{
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
while(1)
{
// Here we wait for a full command string to be received.
if ( RX_Command_Ready == TRUE )
// Just to be safe, first things first...
disable_interrupts(GLOBAL);
RX_Command_Ready = FALSE;
// ***** insert Command processing here *****
if (RX_Command_Ready== RELAYA1); //( NEED TO USE RELAYA1.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
{
output_high(pin_c3); // cH1 oN
}
if (RX_Command_Ready== RELAYA0) //( NEED TO USE RELAYA0.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
output_low(pin_c3); // cH1 oFF
if (RX_Command_Ready== RELAYB1) //( NEED TO USE RELAYB1.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
output_high(pin_c4); // cH2 oN
if (RX_Command_Ready== RELAYB0) //( NEED TO USE RELAYB0.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
output_low(pin_c4); // cH2 oN
}
}
|
Warning 203 Line 67(1,1); Condition always TRUE
Error: Line 78(32,40); A numerical expression must appear here
Error: Line 82(32,40); A numerical expression must appear here
Error: Line 84(32,40); A numerical expression must appear here
Error: Line 86(32,40); A numerical expression must appear here
Please guide me where I made wrong. Thanks
Last edited by karthickefy on Wed Jul 15, 2015 2:38 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed Jul 15, 2015 2:01 am |
|
|
The commands have to be defined to be something.
#DEFINE RELAYA1
Actually _clears_ the definition if it already existed. Doesn't set it.
#DEFINE RELAYA1 'Something'
Defines RELAYA1 to be 'Something'. You need to choose these and define the to be the commands you want.... |
|
|
karthickefy
Joined: 18 Jun 2015 Posts: 8
|
|
Posted: Wed Jul 15, 2015 2:31 am |
|
|
Dear Ttelmah,
Thanks for your reply. I have defined the command along with RELAYA1 as below. but still shows undefined identifier error for all 4 Relay commands.
#define RELAYA1 CH1ON
#define RELAYA0 CH1OFF
#define RELAYB1 CH2ON
#define RELAYB0 CH2OFF
But build error shows that Undefineed Identifier CH1ON, CH1OFF, CH2ON, CH2OFF
Can you please help me to correct my code.
Thanks,
Karthick |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Jul 15, 2015 3:45 am |
|
|
Code: | #define RELAYA1 CH1ON | Written like this the macro RELAYA1 is just a direct text replacement for another macro with the name CH1ON.
Think of it like this: the compiler is processing your program and comes along the name RELAYA1, it then looks up an internal table with #defined text replacements and applies this. After the compiler pre-process step your code looks like: Code: | if (RX_Command_Ready== RELAYA1);
is translated into:
if (RX_Command_Ready== CH1ON); | To the compiler this makes just as little sense as your earlier code.
Perhaps for now we should forget about the #defines, it is a great technique but your understanding of the C-language is not yet up to this level. I also would have left out the interrupt routine as it adds extra complexity.
Code: | if (RX_Command_Ready == RELAYA1) | What you want to do here is to compare the received command to your instruction name. A bit like Code: | if (RX_Command_Ready == "CH1ON") | Note the quote characters "", this tells the compiler you want to deal with the literal text, also known as a string.
Now, in more modern computer languages like C++, C# and Java this would be valid code, but in C it is not going to do what you want. For now, it is going to take me too much time to explain why it isn't working, if you are interested you can search the internet for the keywords 'string' and 'pointer'.
Comparing a string in C is done with the function strcmp(): Code: | if (strcmp(RX_Command_Ready, "CH1ON") == 0) |
To make this work you have to make two more changes to your program:
1) At the top, after the #fuses lines add Code: | #device PASS_STRINGS = IN_RAM | This is to make your program simpler. It works around a special PIC processor issue where ROM and RAM memory are separated. Without this line you first have to copy a string from ROM to RAM before you can use strcmp(). With this line added the compiler will do the copy for you in the background.
2) Change the part Code: | // If we got a CR, then the command is completed.
if (temp == CR)
{
RxBuffer[Index] = temp; | to Code: | // If we got a CR, then the command is completed.
if (temp == CR)
{
RxBuffer[Index] = 0; | In C a string should always be terminated by a zero. Without this end-marker the strcmp() function will fail. |
|
|
karthickefy
Joined: 18 Jun 2015 Posts: 8
|
|
Posted: Wed Jul 15, 2015 4:34 am |
|
|
Hi Ckielstra,
Thanks for your explanation. I try to use strcmp() cmd in
Code: |
if (RX_Command_Ready == "CH1ON")
|
but build failed.
Below code can build without any error but not working.
Code: |
#include <16F877A.h>
#device ADC=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#define PASS_STRINGS = IN_RAM
#use delay(crystal=20000000)
#use FIXED_IO( C_outputs=PIN_C5,PIN_C4,PIN_C3,PIN_C2,PIN_C1,PIN_C0 )
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PC,errors)
#define RELAYA1 CH1ON
#define RELAYA0 CH1OFF
#define RELAYB1 CH2ON
#define RELAYB0 CH2OFF
#define CR 0x0d // ASCII Value for a Carriage Return
//-----< RS232 Packet definitions >-----
int RX_Command_Ready; // TRUE If a receive packet is ready
#define RX_SIZE 10 // RS232 buffer for serial reception
char RxBuffer[RX_SIZE]; // RS232 serial RX buffer
int Index = 0; // RS232 RX data IN index
#INT_RDA // Interrupt driven RS232 routine
void rs232_isr(void)
{
char temp; // local data storage
temp = fgetc(PC); // get rx data
// If we got a '#', then the command is beginning.
if (temp == '#')
{
Index = 0;
RxBuffer[Index] = temp;
Index++;
return;
}
// If we got a CR, then the command is completed.
if (temp == CR)
{
RxBuffer[Index] = 0;
RX_Command_Ready = TRUE;
return;
}
// Save the character to the receive buffer.
RxBuffer[Index]=temp;
// Check for buffer overflow.
if ( Index >= (RX_SIZE - 1) )
Index = 0;
else
Index++;
}
void main()
{
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
while(1)
{
// Here we wait for a full command string to be received.
if (RX_Command_Ready == TRUE )
// Just to be safe, first things first...
disable_interrupts(GLOBAL);
RX_Command_Ready = FALSE;
// ***** insert Command processing here *****
if (RX_Command_Ready == "CH1ON") ; //( NEED TO USE RELAYA1.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
output_high(pin_c3); // cH1 oN
if (RX_Command_Ready== "CH1OFF") //( NEED TO USE RELAYA0.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
output_low(pin_c3); // cH1 oFF
if (RX_Command_Ready== "CH2ON") //( NEED TO USE RELAYB1.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
output_high(pin_c4); // cH2 oN
if (RX_Command_Ready== "CH2OFF") //( NEED TO USE RELAYB0.BUT COMPILER ACCECPT ONLY NUMERIACL EXPRESSIONS.
output_low(pin_c4); // cH2 oN
} } |
Compiler respond some warning: "if (RX_Command_Ready == "CH1ON") ;" has no effect.
Thanks,
Karthick. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Jul 15, 2015 6:35 am |
|
|
I'm not sure about the best way to help you. The more we help you, the more it becomes clear to me that you have no idea as to what you are doing.
We know this is a school project and you are supposed to learn from it by doing yourself. Right now you are mostly practising your English.
Quote: | Compiler respond some warning: "if (RX_Command_Ready == "CH1ON") ;" has no effect. | This line has several problems:
1) You are doing something I just tried to teach you not to do. You don't listen.
2) The ';' at the end of the line is an 'empty statement', i.e. a statement that is doing nothing and should be removed. A very common bug in C and it will cause the code on the next line to be always executed while you expected it to only happen in special situations.
Code: | if (strcmp(RX_Command_Ready, "CH1ON") == 0) | This line I posted contains another bug already present in your early code and accidentally copied by me. It is not a syntax error. I want you to try and find it yourself. |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Wed Jul 15, 2015 6:58 am |
|
|
Hello Karthick,
Whoa, there big guy, slow down a bit, and spend a bit more time trying to understand the code!! You've got a huge misunderstanding about how the code I provided is intended to work!!
Rx_Command_Ready is simply a 'flag' that tells the Main() part of the code that a valid command has been received, and is ready for processing. Note that I set this flag to 'True' or 'False' in the code. Those values are defined elsewhere as '1' and '0'. This flag contains NO data at all related to the received command, but rather that information is contained in the RxBuffer array. You need to look at the contents of the RxBuffer array to see what command has been received!
Decoding the contents of the RxBuffer once it contains a valid command (when Rx_Command_Ready == True) can be done a number of ways. If the number of 'commands' is small, and the command 'prefix' is the same, you can do it simply like this:
Code: |
if ( RX_Command_Ready == TRUE )
{
// Just to be safe, first things first...
disable_interrupts(GLOBAL);
RX_Command_Ready = FALSE;
//Check to see if we have '#RELAY' in the buffer.....
if (RxBuffer[0] == '#' && RxBuffer[1] == 'R' && RxBuffer[2] == 'E' && RxBuffer[3] == 'L' && ............. etc. )
{
if (RxBuffer[6] == 'A'
{
|
So, you can use conditional 'If' statements to 'parse' the received command, and then act on it..... This code is incomplete, but there is enough there that anyone can finish it given the strategy shown..... Like the 'linear' buffer, this is a simple technique, but it's sufficient for your needs, and it's a good starting point. More robust command parsers using string manipulation functions can come later!
Now, after you do all your command processing, you must remember to do this:
Code: |
// Serial command has been processed so we re-enable RS232
Index = 0;
enable_interrupts(GLOBAL);
|
Also, I noticed that my declaration of 'Rx_Command_Ready' should be like this:
Code: |
int1 RX_Command_Ready = 0; // TRUE If a receive packet is ready
|
This defines the variable explicitly as an boolean variable, which is the way I intended it to be!
One last thing, when you send data to your PIC, it must be in the format:
The command must use the '#' prefix, be all upper-case, and end with a carriage return.
Good Luck! _________________ John
If it's worth doing, it's worth doing in real hardware! |
|
|
|
|
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
|