This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Can't get TI flag to be set when writing into SBUF

Hi,

I'm trying to simulate working of a UART.
When I write into SBUF, the TI flag doesn't
go high even though I have enabled interrupts.

Is there any way I can simulate it using SOUT VTREG. can't find example how to use SOUT.

Parents
  • Considering your example code, only two things come to mind:

    1) reitteration of the question Oleg posed: Are you waiting long enough?

    2) Make sure you don't have timer 1 overriden via inadvertently setting the RCLK and TCLK of a dormant Timer 2.

Reply
  • Considering your example code, only two things come to mind:

    1) reitteration of the question Oleg posed: Are you waiting long enough?

    2) Make sure you don't have timer 1 overriden via inadvertently setting the RCLK and TCLK of a dormant Timer 2.

Children
  • What is wrong in this below code? Why does the TI interrupt never fire. ie. why if something is written into SBUF, the hardware does not automatically set TI flag to fire the interrupt. I did single stepping and waited for too long and also did a continous execution using "F5" button still no luck. Any help would be very much appreciated and helpful to me.

    ///////////////////////////////////////////
    /* Serial Port Usage Example */
    #include <AT89S52.H>

    char keyPressed;
    void serial_int (void) interrupt 4 using 1
    {

    if(TI)
    {
    TI = 0;
    }
    else
    {
    keyPressed = SBUF;
    RI = 0;

    }
    }

    void main(void)
    {
    /* INITIALIZE THE UART*/
    TMOD=0x20; /* use timer1, mode 2 */
    TH1=0xFD; /* 9600 baud with a 11.059mHz clock */
    SCON=0x50; /* enable receive */
    TF1 = 0;
    TR1 = 1;
    EA = 1;
    ES = 1;
    TI = 1;
    RI = 1;
    while(1)
    {
    SBUF = 'Y';
    };
    } // end main

  • There are many errors in the code you have posted. I suggest you take a look at the interrupt driven serial comms example program available on this site.

  • I looked into the examples given by Keil where it uses some tbuf and rbuf. I want something very simple. Infact to my surpise the same code that I have pasted works if I introduce a delay using a for loop. (again the delay has to be changed for different baud rate). I am not sure what I am missing but it would be really very nice if you could send me a very simple serial example ie.in the same manner I have posted which I could use as a reference.
    Thanks.

  • The same code which I posted above, the same when modified as shown below works. The only difference between the two which brings the change is the for loop inside the main(). I am not sure why this delay is needed but then with this delay I am able to see 'Y' getting repeatedly displayed on the com port. Any idea as to why this delay is needed or what am I doing wrong? If I could get the same code modified with corrections then it would help in understanding.

    -------------------------------------------
    #include <AT89S52.H>
    void serial_int (void) interrupt 4
    {
    if (RI == 1) /* it was a receive interrupt */
    {
    RI = 0; /* clear the received interrupt flag */
    }
    else if (TI == 1) /* otherwise, assume it was a transmit interrupt */
    {
    TI = 0; /* clear the transmit interrupt flag */
    }
    }

    void main(void)
    {
    SCON = 0x50; /* mode 1, 8-bit uart, enable receiver */
    TMOD = 0x20; /* timer 1, mode 2, 8-bit reload */
    TH1 = 0xFD; /* reload value for 2400 baud */
    ET0 = 0; /* we don't want this timer to make interrupts */
    TR1 = 1; /* start the timer */
    TI = 1; /* clear the buffer */
    ES = 1; /* allow serial interrupts */
    EA = 1; /* enable interrupts */
    while (1)
    {
    unsigned int i;
    for (i = 0; i < 120; i++) {;} /* delay */
    SBUF = 'Y';
    }
    }
    ---------------------------------------

  • while(1)
    {
       SBUF = 'Y';
    };

    As has already been pointed out, TI gets set when the hardware finishes sending a character.

    Your code is continuously re-loading SBUF in a tight loop - therefore the UART never finishes sending a character, therefore TI is never set.

    QED?

  • You need to choose whether to use interrupts or a polled approach. You are trying to mix the two - this will only make your life difficult.

  • Hi Stefan, I would like to use the interrupt based approach. Could you please post the same code in the way you suggest me to write. It would help a lot. Thanks.

  • Rohit,

    If you're trying to use an interrupt-based methodology just to prove to yourself that you can send a string of 'H's or 'Y's out the port, just change your code as follows:

    Make the ISR look like this:

    void serial_int (void) interrupt 4 {
      if (RI)
        RI = 0;
      if (TI) {
        TI = 0;
        SBUF = 'Y';
      }
    }
    

    and then change your while loop to be very simply:

    while (1){}

    What will happen is this. The interrupt will fire after you set TI in your initialization code. At this point, it will clear the flag and load another character into SBUF to transmit. Then, when that character FINISHES, it will fire again and repeat the process. Since you don't appear to have anything else for your code to do in the meantime, the while(1) just keeps the program from resetting (assuming you have the watchdog disabled, that is).

    Hope that helps.

  • "I would like to use the interrupt based approach. Could you please post the same code in the way you suggest me to write."

    If I did that I'd be posting code nearly identical to Keil's example. If you are having trouble understanding the code in their example just post any questions you might have.

    If you want to use interrupt driven serial comms in your project you may as well bite the bullet and use a full implementation. The code is there, it's free and it (probably) works. Once you have compiled it into your project you can forget about SBUF, TI and RI, all you need to do is use the standard 'C' library functions.

  • Thanks Daniel for the code snippet. If I am to write to SBUF in the isr then I am facing an issue of reentrancy. My main interest is to say, write "Hello World" and I would like to use some_func(char *str) in the main(void) so I would give something like some_func("Hello World") in the main and our serial ISR is exected to print it and then wait for the next string whenever I send through some_func. How should do that?

  • Rohit,

    You are incorrect about the reentrancy problem. You need to learn a bit more about the way the 8051's interrupt system works. While you are servicing an interrupt, you WILL not be interrupted again by the same source. In other words, your ISR WILL finish running even if the character finished transmitting before you finished what you needed to do in the ISR. Look at the "RETI" assembly instruction for the 8051 to understand why a bit more.

    That being said, here is a way to do what you're trying to do, just to give you an idea how serial communications works. Also note, that this isn't the stylistically best way to do things, so just use this as a learning example.

    unsigned char xmitbuf[25];
    unsigned char outpos = 0;
    unsigned char outsize = 0;
    
    void serial_isr (void) interrupt 4 {
      if (RI)
        RI = 0;
      if (TI) {
        TI = 0;
        if (outpos < outsize) {
          SBUF = xmitbuf[outpos++];
        } else {
          outpos = 0;
          outsize = 0;
        }
      }
    }
    
    void some_func(char *str) {
      if (outsize == 0) {
        outsize = strlen(str);
        memcpy(xmitbuf, str, outsize);
        TI = 1;
      }
    }
    
    
    void main(void) {
      some_func("Hello World");
      while(1);
    }
    

    Note that this is STILL going to have problems until you resolve your difficulty with interrupt-based versus polled I/O. For instance, if you change main to be:

    void main(void) {
      some_func("Hello World");
      some_func("Next Message");
      while (1);
    }
    

    then only the FIRST message will be transmitted. This is because you'll be trying to overwrite the buffer that's being used to transmit and some_func will smartly not allow that. Again, I hope this helps you understand how this sort of this works with the 8051.

  • "If I am to write to SBUF in the isr then I am facing an issue of reentrancy."

    How do you figure that?

    "My main interest is to say, write "Hello World" and I would like to use some_func(char *str) in the main(void) so I would give something like some_func("Hello World") in the main and our serial ISR is exected to print it and then wait for the next string whenever I send through some_func. How should do that?"

    How about this:

    printf("Hello world");

    If you compiled Keil's serial interrupt code that is all you would have to do. Why are you determined to reinvent the wheel?

  • Stefan,

    I'm hoping that Rohit just wants to use this as a learning exercise since he's obviously new to the 8051. If that's the case, it's probably not a bad idea for him to hack around with code like this. Just my two cents.

  • Thanks Stefan and Daniel. I am able to understand with the examples provided by you.

  • "I'm hoping that Rohit just wants to use this as a learning exercise since he's obviously new to the 8051. If that's the case, it's probably not a bad idea for him to hack around with code like this. Just my two cents."

    Point taken, and I agree. However, if he were to thoroughly analyse Keil's code all his questions would be answered.