PC to PIC communication: Part 2

Tips, Tricks and methods for programming, learn ways of making your programming life easier, and share your knowledge with others.

Moderators: Benj, Mods

Post Reply
Spanish_dude
Posts: 594
Joined: Thu Sep 17, 2009 7:52 am
Location: Belgium
Has thanked: 63 times
Been thanked: 102 times
Contact:

PC to PIC communication: Part 2

Post by Spanish_dude »

Hi everyone,

This is the second part of the article : PC to PIC communication
In this article we'll see how to write on the LCD screen from the PC.

In this article I'll talk about:
- Some preparations first
- Waiting for the start signal
- Parsing the received commands
- Executing commands received from the PC
- The VBA program

Requires:
- PIC programmer board
- 2x16 LCD E-Block
- COM Port or USB to Serial
- RS232 to TTL converter
- 16F877A

Some preparations first

First of all, you need to decide what commands you want to make.
As said before, we'll write on the LCD screen so:
- we'll need to be able to send a string to the microcontroller and write it on the LCD screen.
- we'll need to be able to move the cursor to the begin position of each line.
- we'll need to be able to clear the screen from the PC.

The second thing I decided to do is to use a command-acknowledge-like communication.
This means that when a command is sent to the microcontroller, it'll respond by giving a default answer.
This default answer will be the startbyte + the sent command + an acknowledge byte + endbyte.

The third thing I decided to do is to use a startbyte and endbyte so the microcontroller knows when it receives a command from the PC and when it needs to stop reading.
I used '<' as startbyte and '>' as endbyte.

The fourth thing to do is to decide the names of the commands sent by the PC.
As we will write on the display, I choose to use "WD" (Write Display) as the name of the command.

Here's an example of a command to send to the PIC to write on the LCD screen:
"<WDHello World>" (without quotes)


Waiting for the start signal:

Open up flowcode, create a new file, choose the 16F877A (or any other PIC with USART I/O) and set the oscillator to 19660800 Hz.
Add the RS232 and LCD component to your flowcode program.

Now we'll start with the code:
Note: The code you'll see isn't the code made by flowcode. It's my interpretation of the program. For the correct program see the attached files.

Code: Select all

int counter;
char command[2];
char data_buffer[32];
char cmd_type, x_pos, y_pos, RS232_input;

void main(void)
{
	delay_s(1);
	LCDDisplay_Start();
	LCDDisplay_Clear();

	while (1)
	{
		ReadStartByte();
		// rest of the code seen later
We start by declaring some global variables:
- a counter
- an array of 2 bytes to store the command
- an array of 32 bytes to store the received data
- a byte to store the type of the command
- a byte to store the x and another one to store the y position
- a byte to store the received char from the PC

The main function:
It contains a delay for the LCD display to start up correctly.
Then it initialize the display and clears it with two functions from the LCD component.

Next comes the main loop.
There we'll wait for the startbyte and then we will read the command.

Code: Select all

void ReadStartByte()
{
	do
	{
		RS232_input = ReceiveChar();
	} while (RS232_input != '<');
}
Here we see the ReadStartByte functions.
Nothing difficult here.
We stay in the loop while the RS232_input byte isn't the startbyte '<'.
To read a char we'll use the ReceiveChar function.

Code: Select all

char ReceiveChar()
{
	char input;

	do
	{
		input = RS232_ReceiveChar(10);
	} while (input == 255);

	return input;
}
This is the ReceiveChar function.
First we'll declare a byte to receive the char.
Next we'll loop while the received char is 255 (FF hex).

The RS232_ReceiveChar(char nTimeout) is a function from the RS232 component and the "10" is the timeout argument.

To end the function we'll return the received char.


Parsing the received commands:

Back to the main function, we'll add a new function:

Code: Select all

int counter;
char command[2];
char data_buffer[32];
char cmd_type, x_pos, y_pos, RS232_input;

void main(void)
{
	delay_s(1);
	LCDDisplay_Start();
	LCDDisplay_Clear();

	while (1)
	{
		ReadStartByte();
		ParseCommand();
		// rest of the code seen later
The ParseCommand function will tell the microcontroller which command it received from the PC.

This is how it looks like:

Code: Select all

void ParseCommand()
{
	cmd_type = 255;
	command[0] = 0;
	command[1] = 0;

	command[0] = ReceiveChar();
	command[1] = ReceiveChar();

	if (command[0] == 'W' && command[1] == 'D')
		cmd_type = 1;
}
The cmd_type variable is set to 255.
Then we reset the array command, and we read to bytes.

Next we'll check if the received command is "WD" which is done by the if statement.
Note: As we only have one command, there's only one if statement.

If the received command is unknown, the cmd_type variable will have the value 255, which is used to tell the microcontroller there's an unknown command received and thus sending an error string back to the PC.


Executing commands received from the PC:

Now we know which command has been sent, we can execute it.
Back to the main function to add the last function:

Code: Select all

int counter;
char command[2];
char data_buffer[32];
char cmd_type, x_pos, y_pos, RS232_input;

void main(void)
{
	delay_s(1);
	LCDDisplay_Start();
	LCDDisplay_Clear();

	while (1)
	{
		ReadStartByte();
		ParseCommand();
		ExecuteCommand();
	}
}
The ExecuteCommand, as it's name indicates, will execute the received command.
We can do three things with "WD" command.
- writing on the display
- setting the X and Y position
- clearing the display

This is how it looks like:

Code: Select all

void ExecuteCommand()
{
	counter = 0;
	
	if (cmd_type == 1)
	{
		ReceiveData();

		if (data_buffer[0] == 0)
		{
			LCDDisplay_Clear();
		}
		else if (data_buffer[0] >= 1 && data_buffer[0] <= 16 &&
				data_buffer[1] >= 1 && data_buffer[1] <= 2)
		{
			x_pos = data_buffer[0] - 1;
			y_pos = data_buffer[1] - 1;

			LCDDisplay_SetCursor(x_pos, y_pos);
		}
		else
		{
			counter = 0;

			while (data_buffer[counter] != '>')
			{
				LCDDisplay_WriteAscii(data_buffer[counter]);
				counter++;

				if (counter >= 32)
					break;
			}
		}

		SendAcknowledge();
	}
	else
		SendNoAcknowledge();
}
Here's how this function works.

First it sets the counter to 0.
Then it'll check which command has been received which is done by the first if statement.
If the cmd_type equals 1, then we'll start reading the received bytes with the ReceiveData function.

Next we'll check if the first byte of the received data is 0. (The data starts after "<WD")
If this byte is 0 then we'll clear the screen.

Else we'll check if the first byte is between 1 and 16 and if the second byte is between 1 and 2.
If thats the case we'll decrease by 1 the value of those two bytes and store them in x_pos and y_pos.
Then we'll set the cursor on the x_pos and y_pos which is done by the LCDDisplay_SetCursor functions from the LCD component.

Else we'll write the received data on the LCD screen.
This is done by setting the counter back to 0 and then loop while data_buffer[counter] isn't equal to the endbyte '>'.
In the while loop we'll write the data_buffer[counter] char on the LCD and we'll increase the value of counter by 1.

To avoid buffer overflow, I added an if statement to prevent the counter to be greater than or equal to 32.

Once we reach the end of the cmd_type if statement, we'll send an Acknowledge string which is done by the SendAcknowledge(), to tell the PC everything went ok.

If the cmd_type isn't known by the microcontroller, it will send a NoAcknowledge string which is done by the SendNoAcknowledge(), to tell the PC there's an error.

Code: Select all

void ReceiveData()
{
	do
	{
		data_buffer[counter] = ReceiveChar();
		counter++;
	} while (counter < 32 && data_buffer[counter] != '>');
}
This is the ReceiveData function.
It enters a while loop to receive the data and store it in the data_buffer array.

The loop is executed while the counter is less than 32 to avoid buffer overflow and while the data_buffer[counter] byte isn't equal to the endbyte '>'.

Code: Select all

void SendAcknowledge()
{
	RS232_SendChar('<');
	RS232_SendChar(command[0]);
	RS232_SendChar(command[1]);
	RS232_SendChar(6);
	RS232_SendChar('>');
}
This is the SendAcknowledge function.
It sends a string to the PC.
This string contains the startbyte, the previously received command, the acknowledge byte (see ASCII table, decimal value 6) and the endbyte.

Code: Select all

void SendNoAcknowledge()
{
	RS232_SendChar('<');
	RS232_SendChar(command[0]);
	RS232_SendChar(command[1]);
	RS232_SendChar(21);
	RS232_SendChar('>');
}
This is the SendNoAcknowledge function.
It sends a string to the PC.
This string contains the startbyte, the previously received command, the noacknowledge byte (see ASCII table, decimal value 21) and the endbyte.


Voilà, you have now made a firmware for your PIC microcontroller.

Now we need to do the PC program.

The VBA program:

Here we'll use the knowledge and functions from the Part 1 of this article.

Open up Excel, go to the Developer tab and click on the Visual Basic icon.

Insert a Userform and add 2 TextBoxes, 1 CommandButton and 1 MSComm.
Press F7 to see the code.

Now, we'll copy-paste some functions/sub seen in the previous article.

Code: Select all

Function ReceiveAnswer(end_char As String) As String
    Dim buffer As String
    Dim char As String
    Dim out_of_time As Boolean
    Dim time As Single
    
    buffer = ""
    time = Timer()
    
    Do
        char = MSComm1.Input
        buffer = buffer & char
        out_of_time = Timer() > time + 2
    Loop Until char = end_char Or out_of_time
    
    If out_of_time <> False Then
        ReceiveAnswer = buffer
    Else
        ReceiveAnswer = "Out Of Time"
    End If
End Function

Function InitializePort(COM As Integer)
    MSComm1.CommPort = COM
    MSComm1.Settings = "9600,N,8,1"
    MSComm1.InputLen = 1
    MSComm1.PortOpen = True
End Function

Private Sub UserForm_Initialize()
    Dim COM As Integer
    
    COM = InputBox("Set COM Port :")
    Call InitializePort(COM)
End Sub
Okay, now we'll add a SendData function.
This will simplify the way to send data to the microcontroller.

Code: Select all

Function SendData(data As String) As String
	Dim buffer As String
	Dim counter As Integer
	
	MSComm1.Output = data
	buffer = ReceiveAnswer(">")
	counter = 1
	
	Do While ((counter < 3) And (buffer = "Out Of Time"))
		MSComm1.Output = data
		buffer = ReceiveAnswer(">")
		counter = counter + 1
	Loop
	
	If counter >= 3 Then
		MsgBox("Error : Out of time")
		SendData = "Error"
	Else
		If buffer = Mid(data, 1, 3) & Chr(6) & ">" Then
			SendData = buffer
		Else
			MsgBox("Error : No Acknowledge received, " & buffer)
			SendData = "Error"
		EndIf
	EndIf	
End Function
First we'll declare a string buffer to receive the answer given by the PIC and a counter.

We send the data to through the COM port.
We receive the answer from the PIC and store it in the string buffer.
Then we initialize counter to 1.

If the buffer is equal to "Out Of Time", this means no answer has been read in 2 seconds, we'll send the data again.
If it fails again, it will try again.
This is done 3 times, so if the PIC doesn't answer within 6 seconds, then there's a problem...

If the counter is higher than or equal to 3 a window will open with the message "Error: Out of time" and then we'll return "Error".
Else if the buffer is equal to the 3 first chars of the data string + the acknowledge byte + endbyte then we'll return the buffer.
If it isn't, then a window will open with the message "Error: No Acknowledge received, " + the buffer string and then we'll return "Error".

Next we'll code the function to clear the screen when the CommandButton is pressed.

Code: Select all

Private Sub CommandButton1_Click()
    TextBox1.Text = ""
    TextBox2.Text = ""
    Call SendData("<WD" & Chr(0) & ">")
End Sub
When the CommandButton is pressed, we will clear the text written in the 2 TextBoxes and we'll send the string: "<WD" + nul char + ">" to the PIC.
This will be parsed by the microcontroller and it'll clear the screen.

To end this article, we'll add two more sub functions.
Those for the TextBoxes.

Code: Select all

Private Sub TextBox1_Change()
    Call SendData("<WD" & Chr(0) & ">")
    Call SendData("<WD" & Chr(1) & Chr(2) & ">")
    Call SendData("<WD" & TextBox2.Text & ">")
    Call SendData("<WD" & Chr(1) & Chr(1) & ">")
    Call SendData("<WD" & TextBox1.Text & ">")
End Sub

Private Sub TextBox2_Change()
    Call SendData("<WD" & Chr(0) & ">")
    Call SendData("<WD" & TextBox1.Text & ">")
    Call SendData("<WD" & Chr(1) & Chr(2) & ">")
    Call SendData("<WD" & TextBox2.Text & ">")
End Sub
Okay, so every time the text in the TextBox changes one of those functions will be executed.
Let's say the first TextBox changes from nothing to "Hello".
TextBox1_Change() will be executed.
This will send the command to clear the screen to the microcontroller.
Next it will set the x and y position to [0;1], that's the beginning of the second line.
Next it will send the command to write what's in the second TextBox on the LCD screen.
After that it sets the x and y position to the beginning of the first line.
And then he'll send the command to write what's in the first TextBox.
Now, there will be "Hello" written on the LCD screen, on the first line.

This method is used to prevent the text on the second line to be cleared.

A same method is used for the TextBox2_Change sub function.


Yes, it's finally done !

This is a video I made about my first test communicating with the PIC : http://www.youtube.com/watch?v=tpr_6DcKj9c

Hope you appreciated the 2 parts of the article.
Leave a comment if you liked it ;)

Best regards,

Nicolas L. F., aka Spanish_dude / pyroesp

PS: To run your VBA program you just need to click on the play-like button.
Attachments
PIC_Firmwarev0.1.fcf
PIC Firmware v0.1
(13.86 KiB) Downloaded 812 times
Last edited by Spanish_dude on Sun Mar 13, 2011 8:40 pm, edited 2 times in total.

User avatar
Steve
Matrix Staff
Posts: 3418
Joined: Tue Jan 03, 2006 3:59 pm
Has thanked: 114 times
Been thanked: 422 times
Contact:

Re: PC to PIC comunication: Part 2

Post by Steve »

Part 1 of this article can be found here:
http://www.matrixmultimedia.com/mmforum ... =26&t=7982

Spanish_dude
Posts: 594
Joined: Thu Sep 17, 2009 7:52 am
Location: Belgium
Has thanked: 63 times
Been thanked: 102 times
Contact:

Re: PC to PIC comunication: Part 2

Post by Spanish_dude »

Thanks Steve, I forgot to add the link :roll:

medelec35
Matrix Staff
Posts: 9520
Joined: Sat May 05, 2007 2:27 pm
Location: Northamptonshire, UK
Has thanked: 2585 times
Been thanked: 3815 times
Contact:

Re: PC to PIC communication: Part 2

Post by medelec35 »

Hi Nicolas,
What a great article!
You have clearly spent a lot of time and effort on explaining all the details.
I will defiantly be trying it out, as USB communications, is something I am started to get interested in.
Thanks a lot, and I hope you don't mind if I need to ask some questions :P
Martin

Spanish_dude
Posts: 594
Joined: Thu Sep 17, 2009 7:52 am
Location: Belgium
Has thanked: 63 times
Been thanked: 102 times
Contact:

Re: PC to PIC communication: Part 2

Post by Spanish_dude »

Thanks medelec35 :mrgreen:

The idea of this article came from what my school uses as devboard + VB. (An ADuC832 with LCD, LEDs and switches and RS232 connection).
I just took that idea and made it for PIC.

It was also something I wanted to try, PIC to PC communication through USB, as USB is now used most of the time.
So once I got the USB Serial set correctly, I just needed to do a program in VB to communicate with it and voilà ^^.

I made this article to help people getting started with USB and VB, if you (or anyone else) needs some help I'll gladly do whatever I can to help you out :P

medelec35
Matrix Staff
Posts: 9520
Joined: Sat May 05, 2007 2:27 pm
Location: Northamptonshire, UK
Has thanked: 2585 times
Been thanked: 3815 times
Contact:

Re: PC to PIC communication: Part 2

Post by medelec35 »

Spanish_dude wrote:I made this article to help people getting started with USB and VB, if you (or anyone else) needs some help I'll gladly do whatever I can to help you out :P
8) Thanks that's really appreciated!
I would also like to get into VB as well.
My 1st goal is to export via a graph with x axis as time, voltage data over a period of time.
I guess VB will have to play a big role in that?
Time for a lot of squatting up :P
If I am successful, then I will post programs on here.
Martin

Spanish_dude
Posts: 594
Joined: Thu Sep 17, 2009 7:52 am
Location: Belgium
Has thanked: 63 times
Been thanked: 102 times
Contact:

Re: PC to PIC communication: Part 2

Post by Spanish_dude »

You can do that really easily, I think.

In excel you could use the charts to make your graph.
Select a couple of A rows to use as Y-value, a couple of B rows to use as X-value.

Read data from PIC, write the received data in the A rows and the time when you received them in the B rows with a VB program.
Your chart should then automatically update and I think that would do the trick.

I'm not an expert in VB, I used it like 3 months in high-school.
I think Benj is the right person to go for more complex VB questions. I know the basic stuff. :P

Thein Lwin
Posts: 1
Joined: Wed Jun 01, 2011 12:50 pm
Contact:

Re: PC to PIC communication: Part 2

Post by Thein Lwin »

Thank U sir :mrgreen:

User avatar
achillis1
Posts: 347
Joined: Thu Oct 09, 2008 9:19 am
Has thanked: 91 times
Been thanked: 8 times
Contact:

Re: PC to PIC communication: Part 2

Post by achillis1 »

Hello Spanish dude,

I wasn't able to read the whole document( must went to a lot of trouble!) because it was like chinese to mee and my mind was going to blow!
But I have some questions, why do you use the RS232 TTL converter? Actually the question comes from the preparation you mention at the begining of the article, were you said something about USB serial. WIll you not use the USB serial component to establish the comms?

Never the less, it always good if a programmer uses its own code and it offers him more control and flexibility, but can I ask if this program coould be altered in the meaning of : use a USB serial component and then set the PIC to listen to incoming string values from the PC( via USB) and then when it gets DATA to print on LCD. Vice Versa to get a keypad value from PIC and then send it via the USB serial component to the PC.
(by the way my approach to flow icon programming derives from the fact that I do not know programming!)
A friend of me made a software written in C# and is simple as the aforementioned above. You write in the box the text and then the PC sends it over to PIC and the teh PIC through USB serial prints on screen.
Of course you will need usb driver and if someone goes wholesale must buy usb licenses.

I will attach in an other topic:
but the exe is more then 4 MB and I cannot upload!



Thank you.

Spanish_dude
Posts: 594
Joined: Thu Sep 17, 2009 7:52 am
Location: Belgium
Has thanked: 63 times
Been thanked: 102 times
Contact:

Re: PC to PIC communication: Part 2

Post by Spanish_dude »

Hi,

I'm using a RS232 to TTL converter because the PIC I used did not have a "USB port".
I'm using the USART of the PIC, converting TTL to RS232 and using a RS232 to USB to connect the PIC to the PC.

I don't think I mentioned it, but this article also applies to microcontrollers with USB port using the USB Serial component from flowcode.
Using the USB Serial component and installing the driver will create a virtual COM port on the PC.

The project I made for school has a 18F2455 connected, through USB, to the PC (using USB Serial) and I'm using a VB program to interface with the PIC.
http://www.youtube.com/watch?v=Nrz2ezQr3fY

User avatar
achillis1
Posts: 347
Joined: Thu Oct 09, 2008 9:19 am
Has thanked: 91 times
Been thanked: 8 times
Contact:

Re: PC to PIC communication: Part 2

Post by achillis1 »

Yes, it is clear now thank you very much!

jernelsa
Posts: 4
Joined: Sun Jul 03, 2011 12:58 pm
Contact:

Re: PC to PIC communication: Part 2

Post by jernelsa »

I hope it not too late, but im having problem every time I input in the textbox. it says "Out of time", i understand the code. but i dont know what wrong.

Spanish_dude
Posts: 594
Joined: Thu Sep 17, 2009 7:52 am
Location: Belgium
Has thanked: 63 times
Been thanked: 102 times
Contact:

Re: PC to PIC communication: Part 2

Post by Spanish_dude »

This error shows up when the microcontroller doesn't send data within a 3 sec period.

jernelsa
Posts: 4
Joined: Sun Jul 03, 2011 12:58 pm
Contact:

Re: PC to PIC communication: Part 2

Post by jernelsa »

I modified the vb code and I replaced 3 (thats the seconds in sending data) to 1. will that be ok? because it displays the data, but in 3 times. for example ke = kekeke, I know you know it because you made it :)

Spanish_dude
Posts: 594
Joined: Thu Sep 17, 2009 7:52 am
Location: Belgium
Has thanked: 63 times
Been thanked: 102 times
Contact:

Re: PC to PIC communication: Part 2

Post by Spanish_dude »

If it displays data 3 times it's because you send it 3 times.

The receive program has been made to receive data when a startbyte is received, until a stopbyte is sent.
With the function posted in the "tutorial" it can't receive 3 times the same string unless you send the same string 3 times and uses 3 times the receive function in vb.

Post Reply