| 
	
	|  |  |  
	
		| View previous topic :: View next topic |  
		| Author | Message |  
		| SamMeredith 
 
 
 Joined: 13 Jul 2018
 Posts: 25
 
 
 
			    
 
 | 
			
				| dsPIC Buffered Serial TX - INT_TBE |  
				|  Posted: Wed Jul 07, 2021 2:57 am |   |  
				| 
 |  
				| I'm trying to set up buffered serial TX on a dsPIC33EP512MC806. 
 I initially used ex_STISR.c but found that the TBE interrupt only fires once.
 I found this post on the forum which suggests an alternative isr and indeed this works, but I don't understand why...
 
 
  	  | Code: |  	  | #include <33EP512MC806.h>
 
 #use delay( crystal=12Mhz, clock=120Mhz, AUX:clock=48Mhz )
 
 #pin_select U1TX=PIN_F2
 #pin_select U1RX=PIN_B12
 
 #use rs232( UART1, baud=115200, parity=N, bits=8, errors, TIMEOUT=1, stream=OUT )
 
 #pragma module
 
 #define TX_BUFFER_SIZE   39
 
 BYTE tx_buffer[TX_BUFFER_SIZE];
 BYTE tx_next_in = 0;
 volatile BYTE tx_next_out = 0;
 
 //------------------------------------------------------
 //
 //------------------------------------------------------
 void bputc(char c)
 {
 short restart;
 int ni;
 
 restart = tx_next_in == tx_next_out;
 tx_buffer[tx_next_in] = c;
 ni = (tx_next_in + 1) % TX_BUFFER_SIZE;
 // Hold while buffer full
 while (ni == tx_next_out);
 tx_next_in = ni;
 
 if (restart)
 // Re-trigger the interrupt if we've stopped sending
 enable_interrupts(INT_TBE);
 }
 
 //------------------------------------------------------
 //
 //------------------------------------------------------
 int main( int argc, char* argv[] )
 {
 delay_us(100); // Startup delay
 
 printf(bputc, "Hello World");
 
 while(TRUE) {};
 
 return 0;
 }
 
 //------------------------------------------------------
 //
 //------------------------------------------------------
 #int_tbe
 void tx_isr()
 {
 // ISR Copied from EX_STISR.c
 /*if (tx_next_in != tx_next_out)
 {
 fputc(tx_buffer[tx_next_out], OUT);
 tx_next_out = (tx_next_out + 1) % TX_BUFFER_SIZE;
 }*/
 
 // ISR by FvM
 // Taken from https://www.ccsinfo.com/forum/viewtopic.php?t=39315
 fputc(tx_buffer[tx_next_out], OUT);
 tx_next_out = (tx_next_out + 1) % TX_BUFFER_SIZE;
 if (tx_next_in == tx_next_out)
 disable_interrupts(INT_TBE);
 }
 
 | 
 
 Compiled with V5.101
 
 Could anyone explain why TX works as expected with the above ISR, but not with the (commented) ISR copied from EX_STISR.c?
 |  |  
		|  |  
		| Ttelmah 
 
 
 Joined: 11 Mar 2010
 Posts: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Wed Jul 07, 2021 6:18 am |   |  
				| 
 |  
				| That unfortunately, has the error, that it should only ever be used with binary buffer sizes. Otherwise if you use a byte sized division anywhere
 else in the code, you will get a warning that interrupts have been disabled
 to prevent re-entrancy.
 It also can have issues if the interrupt triggers while it is in the bputc
 routine. This can result in the counters getting miscalculated.
 What I use is:
 
  	  | Code: |  	  | #define T_BUFF_SIZE 128
 uint16_t ddata[T_BUFF_SIZE];
 int din=0;
 int dout=0;
 
 #bit U1TXIF = getenv("BIT:U1TXIF") //for the UART involved
 
 #INT_TBE //for whichever TX interrupt is involved
 void send_char(void)
 {
 fputc(ddata[dout++],DISPLAY);
 if (dout==T_BUFF_SIZE)
 dout=0;
 if(din==dout) //If same after transmission
 disable_interrupts(INT_TBE); //buffer empty
 }
 
 void bputc(uint16_t chr)
 {
 //interrupt driven TX
 short restart; //flag to restart TX
 disable_interrupts(INT_TBE);
 restart=(din==dout); //if buffer was empty must restart
 ddata[din++]=chr;
 if (din==T_BUFF_SIZE)
 din=0; //handle buffer wrap
 if(restart)
 U1TXIF = 1;  //force interrupt to trigger
 enable_interrupts(INT_TBE);
 }
 
 | 
 
 The difference is that on these PIC's, the interrupt only triggers when
 the buffer actually 'empties'. It can be cleared, and will not re-trigger
 unless the buffer empties again. Now on the PIC18/16, the interrupt
 cannot be cleared if the buffer is empty, so you have to disable this
 interrupt, and when you want to restart, simply enabling it will trigger
 immediately. On these 'senior' PIC's, you instead have to actually set
 the interrupt flag.
 CCS do an example for the 'PCD' PIC's ex_stisr_pcd.c
 This though retains the issue that exists in their PIC16/18 transmit ISR,
 that only binary buffer sizes should really be used.
 It also has an issue if the actual ISR triggers inside the maths for the
 counter update (I was caught by this on a high speed serial ISR on
 these chips).....
 My code above avoids both these issues and has proven totally reliable.
 It also (since it uses int, rather than byte for the sizes), can happily handle
 very large buffers. I have one system with a 2K serial buffer, running
 at 1MB/sec.
 |  |  
		|  |  
		| SamMeredith 
 
 
 Joined: 13 Jul 2018
 Posts: 25
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Wed Jul 07, 2021 9:10 am |   |  
				| 
 |  
				|  	  | Ttelmah wrote: |  	  | That unfortunately, has the error, that it should only ever be used with binary buffer sizes. Otherwise if you use a byte sized division anywhere
 else in the code, you will get a warning that interrupts have been disabled
 to prevent re-entrancy.
 | 
 I don't understand this one, if I have a non-binary-sized buffer and then perform a (entirely unrelated?) division operation elsewhere in the code, I get this warning?
 
 
  	  | Ttelmah wrote: |  	  | On these 'senior' PIC's, you instead have to actually set the interrupt flag.
 | 
 So we set U1TXIF = 1 inside bputc(), rather than just enabling the interrupt...
 
 
  	  | Ttelmah wrote: |  	  | It also has an issue if the actual ISR triggers inside the maths for the counter update
 | 
 ...and disable_interrupts() while updating the counter to avoid this issue.
 
 Understood, I'll make those changes.
 
 
  	  | Ttelmah wrote: |  	  | CCS do an example for the 'PCD' PIC's ex_stisr_pcd.c | 
 This doesn't seem to work at all. I think an additional enable_interrupts(INT_TBE) is required in bputc(), as you have in your code.
 
 The one thing still not clear to me is why the isr in my posted code (which does not manually set U1TXIF) seems to work?
 |  |  
		|  |  
		| Ttelmah 
 
 
 Joined: 11 Mar 2010
 Posts: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Wed Jul 07, 2021 9:25 am |   |  
				| 
 |  
				| If I remember correctly, you will find that it doesn't handle sending the 'first' character without the interrupt being explicitly triggered. What
 happens instead is after a few characters are loaded, the interrupt does
 trigger because the internal buffer is getting full. It then merrily starts.
 Because you are sending 'long' strings (several characters), this works.
 |  |  
		|  |  
		| Ttelmah 
 
 
 Joined: 11 Mar 2010
 Posts: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Wed Jul 07, 2021 9:26 am |   |  
				| 
 |  
				| I wrote to CCS pointing out that fault quite a time ago. They obviously still haven't updated the example!...
 |  |  
		|  |  
		| SamMeredith 
 
 
 Joined: 13 Jul 2018
 Posts: 25
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Wed Jul 07, 2021 10:38 am |   |  
				| 
 |  
				|  	  | Ttelmah wrote: |  	  | If I remember correctly, you will find that it doesn't handle sending the 'first' character without the interrupt being explicitly triggered. What
 happens instead is after a few characters are loaded, the interrupt does
 trigger because the internal buffer is getting full. It then merrily starts.
 Because you are sending 'long' strings (several characters), this works.
 | 
 I don't think this is the case here. It does work with a single character.
 
 I've spotted the difference in execution between the two ISRs:
 When (tx_next_out == tx_next_in) the ISR taken from the forums still calls fputc() before disable_interrupts(INT_TBE).
 The result being that after we've processed the entire buffer, U1TXIF is set.
 So when I call printf(bputc, "HELLO WORLD")
 
 
1. 'H' is buffered, enables TBE interrupt
 2. ISR fires (since U1TXIF is always set intially), disables TBE interrupt, clears U1TXIF
 3. 'H' is transmitted, buffer empty, sets U1TXIF
 4. 'E' is buffered, enables TBE interrupt
 5. Repeat
 
 Finally after 'D' is transmitted, U1TXIF is set but the TBE interrupt has already been disabled.
 
 On the other hand, the ISR from ex_stisr.c does nothing when (tx_next_out == tx_next_in).
 So when I call printf(bputc, "HELLO WORLD")
 
 
1. 'H' is buffered, enables TBE interrupt
 2. ISR fires (since U1TXIF is always set intially), clears U1TXIF
 3. 'H' is transmitted, buffer empty, sets U1TXIF
 4. ISR immediately fires again, buffer is empty so disables TBE interrupt
 5. 'E' is buffered, enables TBE interrupt but U1TXIF is not set so we stop here
 
 So you can get away without manually setting U1TXIF, provided it never gets cleared elsewhere.
 Doesn't seem very robust, I'll stick with your suggested code.
 |  |  
		|  |  
		|  |  
  
	| 
 
 | 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
 
 |