CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PCD: INT_TBE not firing off

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
xieliwei
Guest







PCD: INT_TBE not firing off
PostPosted: Thu Jun 18, 2009 5:26 am     Reply with quote

I have a dsPIC33FJ128GP802 that I'm testing out for some project I'm working on. I'm attempting to get both hardware UARTs working, interrupt based and buffered.

I translated the EX_STISR and EX_SISR examples to accommodate both TX and RX of both UARTs and did a little modification to use a ring buffer.

Everything works except for transmissions on both UARTs.

I've got my ICD3 connected up and set to break whenever any of the UART based interrupts got fired and set watches on the TX and RX buffers.

From what I observe:
- RX is okay on both sides, int_rda and int_rda2 fires off whenever a character is hit and characters are read and stored into their respective buffers
- TX does not work. Apparently int_tbe and int_tbe2 each fires off twice (why? when? more below) and never fires off again
- int_tbe and int_tbe2 does fire off when I manually call a fputc('x', STREAM); from main() and the buffers get flushed out
- I've set watches on the IEC0 register, specifically bits 11 and 12 (U1RXIE and U1TXIE) and bits 14 and 15 of the IEC1 register (U2RXIE and U2TXIE) and and they're set to 1 (interrupts enabled)
- enable/disable_interrupts(int_tbe); and enable/disable_interrupts(int_tbe2); work and the register bits change accordingly
- enable_interrupts(intr_global); was called before everything

Regarding the two times the int_tbe and int_tbe2 interrupts fires off, the first time is right at the start of execution (I'm not sure why, shouldn't all interrupts be disabled at start?), the second time is after calling enable_interrupts(intr_global); for the first time.

For the second time call to int_tbe and int_tbe2, the buffers are empty since its still at the initialisation stage. I've tried pre-filling the buffers before the call to enable_interrupts(intr_global); and that causes the buffer to be flushed correctly but again int_tbe and int_tbe2 stops firing off.

Anyone can help?

TLDR:
int_tbe and int_tbe2 stops firing after the first (or second, read second last paragraph above) time even though they are still enabled.
xieliwei
Guest







PostPosted: Thu Jun 18, 2009 5:33 am     Reply with quote

Oh yes, here's a stripped down version of my code, not sure if it compiles, but I think relevant code parts are there:

Code:
#include "main.h"
//UART1 (DEBUG)
#pin_select U1TX=PIN_B9
#pin_select U1RX=PIN_B8

//UART2 (GPS)
#pin_select U2TX=PIN_B11
#pin_select U2RX=PIN_B10

//Buffer size
#define BUFFER_SIZE 32

#use rs232(UART1,baud=115200,parity=N,bits=8,stream=DEBUG)
#use rs232(UART2,baud=115200,parity=N,bits=8,stream=GPS)

#define bkbhit_debug (rx_debug_next_in!=rx_debug_next_out)
#define bkbhit_gps (rx_gps_next_in!=rx_gps_next_out)

void bputc_debug(char c){
   int1 restart;
   restart=tx_debug_next_in==tx_debug_next_out;
   tx_debug_buffer[tx_debug_next_in]=c;
   tx_debug_next_in=(tx_debug_next_in+1) % BUFFER_SIZE;
   if(tx_debug_next_in==tx_debug_next_out)
      tx_debug_next_out=(tx_debug_next_out+1) % BUFFER_SIZE;
   if(restart)
      enable_interrupts(int_tbe);
   return;
}

void bputc_gps(char c){
   int1 restart;

   restart=tx_gps_next_in==tx_gps_next_out;
   tx_gps_buffer[tx_gps_next_in]=c;
   tx_gps_next_in=(tx_gps_next_in+1) % BUFFER_SIZE;
   if(tx_gps_next_in==tx_gps_next_out)
      tx_gps_next_out=(tx_gps_next_out+1) % BUFFER_SIZE;
   if(restart)
      enable_interrupts(int_tbe2);
   return;
}

//TX buffer empty interrupt functions for both UARTs
#int_tbe
void tbe_isr(){
   if(tx_debug_next_in!=tx_debug_next_out){
      fputc(tx_debug_buffer[tx_debug_next_out], DEBUG);
      tx_debug_next_out=(tx_debug_next_out+1) % BUFFER_SIZE;
   }else
      disable_interrupts(int_tbe);
   return;
}

#int_tbe2
void tbe2_isr(){
   if(tx_gps_next_in!=tx_gps_next_out){
      fputc(tx_gps_buffer[tx_gps_next_out], GPS);
      tx_gps_next_out=(tx_gps_next_out+1) % BUFFER_SIZE;
   }else
      disable_interrupts(int_tbe2);
   return;
}

//Buffered getc functions for both UARTs
char bgetc_debug(){
   char c;

   while(!bkbhit_debug);
   c=rx_debug_buffer[rx_debug_next_out];
   rx_debug_next_out=(rx_debug_next_out+1) % BUFFER_SIZE;
   return c;
}

char bgetc_gps(){
   char c;

   while(!bkbhit_gps) ;
   c=rx_gps_buffer[rx_gps_next_out];
   rx_gps_next_out=(rx_gps_next_out+1) % BUFFER_SIZE;
   return c;
}

//RX data available interrupt functions for both UARTs
#int_rda
void rda_isr(){
   rx_debug_buffer[rx_debug_next_in]=fgetc(DEBUG);
   rx_debug_next_in=(rx_debug_next_in+1) % BUFFER_SIZE;
   if(rx_debug_next_in==rx_debug_next_out)
      rx_debug_next_out=(rx_debug_next_out+1) % BUFFER_SIZE;
   return;
}

#int_rda2
void rda2_isr(){
   rx_gps_buffer[rx_gps_next_in]=fgetc(GPS);
   rx_gps_next_in=(rx_gps_next_in+1) % BUFFER_SIZE;
   if(rx_gps_next_in==rx_gps_next_out)
      rx_gps_next_out=(rx_gps_next_out+1) % BUFFER_SIZE;
   return;
}

//Initialisation for buffered serial
void bserial_init(){
   int8 i;
   for(i=0;i<BUFFER_SIZE;i++){
      tx_debug_buffer[i]='a';   //Prefilled buffer
      rx_debug_buffer[i]='\0';
      tx_gps_buffer[i]='\0';
      rx_gps_buffer[i]='\0';
   }

   enable_interrupts(INT_RDA);
   enable_interrupts(INT_TBE);
   enable_interrupts(INT_RDA2);
   enable_interrupts(INT_TBE2);

   return;
}

void main()
{
   //Start initialisation
   disable_interrupts(INTR_GLOBAL);
   bserial_init();
   enable_interrupts(INTR_GLOBAL);
 
   while(true){
      printf(bputc_debug, "abcd\r\n");
      printf(bputc_gps, "efgh\r\n");
   }

}
xieliwei
Guest







PostPosted: Thu Jun 18, 2009 5:39 am     Reply with quote

Again, I forgot something, main.h:

Code:
#include <33FJ128GP802.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES ICSP1                    //ICD uses PGC1/PGD1 pins
#FUSES NOJTAG                   //JTAG disabled
#FUSES NOCOE                    //Device will reset into operational mode
#FUSES DEBUG                  //Debug mode for ICD
#FUSES PUT128                   //Power On Reset Timer value 128ms
#FUSES WPOSTS16                 //Watch Dog Timer PostScalar 1:32768
#FUSES WPRES128                 //Watch Dog Timer PreScalar 1:128
#FUSES WINDIS                   //Watch Dog Timer in non-Window mode
#FUSES NOPR                     //Pimary oscillaotr disabled
#FUSES OSCIO                    //OSC2 is general purpose output
#FUSES NOCKSFSM                 //Clock Switching is disabled, fail Safe clock monitor is disabled
#FUSES FRC_PLL                  //Internal FAST RC oscillator with PLL
#FUSES NOIESO                   //Internal External Switch Over mode disabled
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOWRTSS                  //Secure segment not write protected
#FUSES NOSSS                    //No secure segment
#FUSES NORSS                    //No secure segment RAM
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOBSS                    //No boot segment
#FUSES NORBS                    //No Boot RAM defined
#FUSES RESERVED                 //Used to set the reserved FUSE bits
#FUSES NOALTI2C                 //I2C mapped to alternate pins
#FUSES NOIOL1WAY                //Allows multiple reconfigurations of peripheral pins

#use delay(clock=46000000)


I actually have another issue here. The clock setting is weird. I expected 80000000 (or 40MHz, I'm not very sure which should be which if two clock cycles are required to execute one instruction) but after measuring the output on OSC2 it was 46MHz. I'm using the internal FRC (at 7.37MHz) but there's no multiplier that gets 46MHz from that.

I suspect I've used a wrong fuse because the descriptions from the project wizard are known to be unreliable.
xieliwei
Guest







PostPosted: Thu Jun 18, 2009 6:13 am     Reply with quote

Here's the declaration for the buffers and their trackers:
Code:
//Buffer declarations for both UARTs
char tx_debug_buffer[BUFFER_SIZE];
unsigned int8 tx_debug_next_in = 0;
unsigned int8 tx_debug_next_out = 0;

char rx_debug_buffer[BUFFER_SIZE];
unsigned int8 rx_debug_next_in = 0;
unsigned int8 rx_debug_next_out = 0;

char tx_gps_buffer[BUFFER_SIZE];
unsigned int8 tx_gps_next_in = 0;
unsigned int8 tx_gps_next_out = 0;

char rx_gps_buffer[BUFFER_SIZE];
unsigned int8 rx_gps_next_in = 0;
unsigned int8 rx_gps_next_out = 0;
xieliwei
Guest







PostPosted: Thu Jun 18, 2009 7:07 am     Reply with quote

After more tests, it seems that manually calling fputc() for the relevant stream triggers int_tbe and int_tbe2, causing the buffers to be flushed.

From this, it seems like the interrupts only fire off once each time the buffers become empty, even if the interrupt is left on? This is different from expected, previous PICs I've used continuously fires off the interrupt as long as the buffer is empty and the interrupt is left on... this is the first time I'm working with a dsPIC though.
Ttelmah
Guest







PostPosted: Thu Jun 18, 2009 9:29 am     Reply with quote

If you look at the DsPIC data sheet, and the UART section, you will see, that it says:
"The transmitter generates an edge to set the UxTXIF bit". Note the 'edge' reference. The interrupt can also be set to trigger, when the whole transmit buffer is empty, or when there is just one 'space'. The DsPIC, is much tidier in this regard than the older PICs, but does therefore require slightly different handling.

Best Wishes
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Jun 18, 2009 10:01 am     Reply with quote

The problem is caused by the if..else construct in your ISR, I think. I have I slightly different ISR processing for PIC24F, that apparently solves the said problem, because the interrupt flag is always reset by writing TXREG. Also the buffer fill action is simpler then, the enable_interrupt(INT_TBE2) is e.g. unconditional.
Code:
#int_TBE2
void  TBE2_isr(void)
{
   U2TXREGL = STraBuf[STraNextOut];
   STraNextOut = (STraNextOut + 1) & (TRA_BUF_SIZE-1);
   if(STraNextIn == STraNextOut)
      disable_interrupts(INT_TBE2);
}
xieliwei
Guest







PostPosted: Thu Jun 18, 2009 11:46 am     Reply with quote

Okay, after more tinkering (which includes dealing with the SFR manually instead of using the ccs functions), I think I found the problem and an untested solution.

The problem seems to be that for the int_tbe and int_tbe2 interrupts to be repeatedly fired, UxTXIF must not be cleared, even on entry to their respective ISRs; it should only be cleared if actual data is to be transmitted.

I'm not sure if this is supposed to be the case and whether forcing UxTXIF to return to 1 in the ISR (since CCS automatically clears that bit on entry) actually causes the whole thing to act like it was polling (thus making things seem like its working).

So, err, any idea if this is the correct solution?
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Jun 18, 2009 11:56 am     Reply with quote

It can also work with PCD built-in functions, I think. In my code example, I access U2TXREGL directly, because PCD previously didn't support all PIC24F UARTs.

For clarity, can you please show your final int_tbex() and bputc_xxx() functions? I don't see a necessity to manipulate TXIF.
xieliwei
Guest







PostPosted: Thu Jun 18, 2009 12:17 pm     Reply with quote

Ttelmah wrote:
If you look at the DsPIC data sheet, and the UART section, you will see, that it says:
"The transmitter generates an edge to set the UxTXIF bit". Note the 'edge' reference. The interrupt can also be set to trigger, when the whole transmit buffer is empty, or when there is just one 'space'. The DsPIC, is much tidier in this regard than the older PICs, but does therefore require slightly different handling.

Best Wishes


Ttelmah, on which page is that quote from, I cannot find it. Do you mean that the interrupts will only be generated for the above conditions _only once_ according to the UTXISEL bits? I have no problems getting the interrupts to occur when the transmit buffers are free, but since these interrupts occur when my own buffers are empty, the ISR has nothing to send... but the interrupts never fires again after that.

FvM wrote:
The problem is caused by the if..else construct in your ISR, I think. I have I slightly different ISR processing for PIC24F, that apparently solves the said problem, because the interrupt flag is always reset by writing TXREG. Also the buffer fill action is simpler then, the enable_interrupt(INT_TBE2) is e.g. unconditional.
Code:
#int_TBE2
void  TBE2_isr(void)
{
   U2TXREGL = STraBuf[STraNextOut];
   STraNextOut = (STraNextOut + 1) & (TRA_BUF_SIZE-1);
   if(STraNextIn == STraNextOut)
      disable_interrupts(INT_TBE2);
}


Yes, FvM, you're right, there is something wrong with the ISR code in the CCS example that I used; it cannot differentiate between a filled or empty buffer without sacrificing one character space in the buffer. However, doesn't your version of the ISR send a character even when the buffer is empty (U2TXREGL = STraBuf[STraNextOut]; is always executed regardless of buffer status)?

I've tried not disabling interrupts in the ISR and always enabling interrupts when calling bputc();, but this did not solve the problem.


Come to think of it, Ttelmah, do you mean that the design of the TX interrupt is such that it will only occur once for each time the TX buffer state changes (full to free, or full to empty, or partially full to empty) so that instead of constantly being interrupted, the main routine can be notified once, take note of it, and next time when the internal buffer does have data, manually insert the first character to trigger off another change in TX buffer state and thus allow the internal buffer to automatically clear itself until it is empty again, where the process starts all over?
xieliwei
Guest







PostPosted: Thu Jun 18, 2009 12:22 pm     Reply with quote

FvM wrote:
For clarity, can you please show your final int_tbex() and bputc_xxx() functions? I don't see a necessity to manipulate TXIF.


Its already posted in the code snippet in this post:
http://ccsinfo.com/forum/viewtopic.php?p=117229
Ttelmah
Guest







PostPosted: Thu Jun 18, 2009 2:07 pm     Reply with quote

That to be what the data sheet says, by referring to generating an edge.
Remember you can add 'noclear' to the ISR declaration, and then the compiler won't clear the interrupt flag.

Best Wishes
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Jun 18, 2009 3:01 pm     Reply with quote

Quote:
Its already posted in the code snippet in this post

I was under the impression, that you have been talking of an edited code variant. If you were referring to the previously posted code, I don't understand to which code detail the TXIF considerations are related.

Quote:
However, doesn't your version of the ISR send a character even when the buffer is empty?

I doesn't, because the interrupt is disabled immediately, when the buffer has run empty (and reenabled with each new character inserted to the buffer).

I must confess, that I implemented a standard buffered TX handling without thinking much about the details. Because I didn't yet experience any problems, I had not reason to doubt.
xieliwei
Guest







PostPosted: Fri Jun 19, 2009 5:30 am     Reply with quote

FvM wrote:
Quote:
Its already posted in the code snippet in this post

I was under the impression, that you have been talking of an edited code variant. If you were referring to the previously posted code, I don't understand to which code detail the TXIF considerations are related.


Oh yes, the effects of staying up too late. Here's the modified ISR, I just reset the TXIF bit, or after reading Ttelmah's suggestion, I should just add a noclear to the ISR declaration:

Code:
//TX buffer empty interrupt functions for both UARTs
#int_tbe
void tbe_isr(){
   #bit U1TXIF = 0x84.12
   U1TXIF = 1;
   if(tx_debug_next_in!=tx_debug_next_out){
      fputc(tx_debug_buffer[tx_debug_next_out], DEBUG);
      U1TXIF = 0;
      tx_debug_next_out=(tx_debug_next_out+1) % BUFFER_SIZE;
      output_high(PIN_B6); //Some fancy thing to make LEDs light when sending data
   }else{
      disable_interrupts(int_tbe);
      output_low(PIN_B6);  //Some fancy thing to make LEDs light when sending data
   }
   return;
}


FvM wrote:
Quote:
However, doesn't your version of the ISR send a character even when the buffer is empty?

I doesn't, because the interrupt is disabled immediately, when the buffer has run empty (and reenabled with each new character inserted to the buffer).

I must confess, that I implemented a standard buffered TX handling without thinking much about the details. Because I didn't yet experience any problems, I had not reason to doubt.


Hmm, you're right, your ISR is a much cleaner implementation than the CCS example.


Well, I have to ask everyone again, is resetting the TXIF bit (or adding noclear to the ISR) the correct solution? Come to think of it, perhaps I should not enable the TBE interrupts at initialisation but only on adding characters, and disable the interrupts as soon as the last character is sent off; like what FvM explained.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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