views:

369

answers:

2

Hello everyone!

I'm trying to make a microcontroller communicate with a program on my desktop. I'm using serial port connections with Xbee radios on both ends.

The communication works fine when I send something from the microcontroller to the desktop and the program on the desktop then sends something back to the microcontroller.

However, when I require the information to be sent from the controller to the desktop program continuously until the desktop program sends a particular answer it doesn't work.

Here's the code for what I'm talking about:

    unsigned char ans = 'N';
    unsigned int count = 0;

    void main(void)
    {


        while(1)
        {
         if(count == 0)
         {
          Configure();
          count = 1;
         }

                  //there is some more code here but is irrelevant to the serial communication

         }

    }


void Configure()
{


    //Repeat this until the user accepts the sent string as correct
    while(ans == 'N')
    {

     BuildString();
     Send();
     Receive();
    }
}

void Send()
{
    unsigned int i;

    TMOD = 0x20;
    TH1 = 0xFD;
    SCON = 0x50;
    TR1 = 1;

    for(i=0; i<4; i++)
    {
     SBUF = toSend[i];
     while(TI == 0);
     TI = 0;
    } 

}

void Receive()
{
    unsigned int j;

    TMOD = 0x20;
    TH1 = 0xFD;
    SCON = 0x50;
    TR1 = 1;


    for(j=0; j<2; j++)
    {
     while(RI == 0);
     Received[j] = SBUF;
     RI = 0; 
    }


    if(count == 0)
     ans = Received[1];

    else
    { 
     RunType = Received[0];
     Move = Received[1];
    }


}

The BuildString() function simply constructs a string on the basis of some sensor inputs. The send and receive funtions work fine usually but when I need them to send and receive continuously, like in the Configure() funtion above, it doesn't work.

Any suggestions? I'd really appreciate them.

+4  A: 

The problem is that both your send and receive functions are polled and blocking. When you call the receive function, it will only return after a complete message is received. Dito for the send function, but in case of send the duration is propably shorter (your program will propably only call send when there is a message to be sent, while receive can wait for days before a message arrives.

If you require asynchronous commmunication, the best is to use interrupt based communications; at least for the receive and ideally for both send and receive.

It is also possible to impement this using polled communications, but then you need to write a function which checks if a character is available for receive (or if tx-empty), to read/write the next character from/to the buffer.

The advantages of interrupt based communication are:

  • full duplex
  • less cpu time is used (no wait loops necessary)
  • less power (it allows the cpu to go to a low power/standby mode, with wakeup on interrupt; polling always requires full power)

As a first step I advise you to implement (or get) a interrupt based receive; even when the transmit function is still blocked, it will allow full duplex operation with minimal effort. If you haven't got a os (rtos/scheduler) you'll have to think of a synchronisation mechanism. The simplest form is for your receive to handle a message if there is one available, and to return immediately if there isn't a (complete) message.

good luck.

Edit after comments On a message-by-message basis, things may seem to work if the desktop is reacting on messages sent by the controller. If your controller has a large FIFO buffer (i.e. 64 bytes) on the receive, this may work. Most controllers I know haven't got this. Many have only a single character buffer. You can detect this using an OVERFLOW bit in the registers; if this is set, then characters were lost on receive.

Some usecases: * you want to send 2 messages in one go (say: init + do_something). The pc responds to the first msg, but the controller is still sending and drops most of the data. * PC starts sending before controller is executing the receive() function. Data at the beginning of the packet may get lost * any drop in communication may cause a deadlock (i.e. controller and desktop are both waiting for the other end to send something.

So to diagnose: check the overflow bit. If it is set, you have lost data and you need to work on the interrupt functions. If possible, monitor both sides (at least the state; a blink a led for send, and one for receive for instance).

A rs232 monitor may help you (you'll need some additional ports for this. There are many (including freeware) applications which can monitor multiple rs232 ports and provide timestamps. So you can observe the order of communications. Google found me : link; I've used several similar utilities in the past years.

Adriaan
Thank you so much for your suggestion Adriaan. But I'm confused. I had a hunch that it may not be working because of the blocking aspect but I figured that the way I'd written my programs would have gotten around it. See I make the controller send a msg while the desktop program is waiting on it with a Receive()Then the desktop program checks the msg sees if it's acceptable (all this while the controller program is waiting to Receive) and then sends a msg signaling acceptance or rejection of the msg. The Receive() in the controller that was blocked is now unblocked isn't it?
CodeConfused
So shouldn't it just proceed with the rest of the code? That is check whether the received msg is for acceptance or rejection (During this time the desktop computer is waiting with a blocked Receive) and then send the msg again?I'm sure asynchronous communication would solve this probelem like you said, but I'm on a bit of a deadline with this project and I've never tried asynchronous comm. before so I would really really appreciate if you could help solve this with synchronous!
CodeConfused
see edit in answer.
Adriaan
Hmmm...I think it's very possible that is what's going on here. Although I don't fully comprehend how sending two messages from controller to desktop and two messages back from desktop to controller can work for a single time and not when I put the same code within a loop, I think I'll just have to assume the messages get lost in the continuous sending and receiving and in the end both end up waiting in blocked Receive functions. :(I'll try monitoring both sides like you suggested. Thanks!
CodeConfused
+1  A: 

Your program as written should send 4 bytes and then read 2 bytes.(assuming the registers you have are correct, but if you got it to work at all they are probably correct) then send 4 bytes again... It probably isn't hanging on the send part but the receiving side as it will always be waiting to read two bytes, and if for some reason two bytes don't arrive at the input register you will continue to wait for ever.

It maybe when you stress the system(send byte too quickly) you are overflowing the input buffer and end up loosing a byte? thus never getting two bytes. and will get stuck.

  1. can you debug where you are getting stuck? is it in fact in the receive loop?
  2. can you throttle the transmission from the pc to the micro so you can manually send one byte at a time?
  3. is there any handshaking between the micro and xbee interface? can that be throttled by the micro?
simon
Yes Simon you're right it should send 4 bytes then receive 2 and so on until a certain answer is received. I'm pretty sure the problem does lie in the receive part, simply because the Xbees light up when sending or recieving. So I can 'see' that the 2 bytes from the desktop program have left the desktop and can also 'see' them arriving at the microcontroller. The controller code just doesn't handle it after that. I did try to debug by manually sending the bytes one at a time like you said. But it still didn't work.
CodeConfused
I'm not sure about the handshaking between the micro and Xbee. But I'm pretty sure there isn't.
CodeConfused
@CudeConfused, Can you determine if/how many times the RI bit is being changed? some how when you are sending a byte some times the RI bit is not being set. Can some thing be triggering the RI before you clear it and thus miss the second byte?
simon
If the RI bit wasn't being set though, it wouldn't work for a single two message transmission and reception either, would it? That does work though. It only doesn't work when I put that same code into a loop.
CodeConfused
@CodeConfused (sorry about the typo in the last comment, can't edit comments ;)) I don't quite understand the working vs the non-working situations? I think you need to be more specific. I like Adriaan's suggestion to read the overflow bit in your receive loop. I think blinking the LED for each byte received, will confirm you only read one byte and then wait forever.
simon
@CodeConfused You could also change your protocol to handshake one byte at a time. Send one byte, receive one byte. then the micro and the pc can throttle/handshake each other to prevent any over runs. This is not a great way to do this in the long run, but is a quick and dirty fix. The interrupt asynchronous method would be a better long term approach.
simon