Uart Interrupt driven, almost working...

General discussion on mikroPascal for dsPIC30/33 and PIC24.
Post Reply
Author
Message
Skydec
Posts: 39
Joined: 30 Jan 2009 12:38

Uart Interrupt driven, almost working...

#1 Post by Skydec » 27 May 2009 17:54

I've written a Interrupt driven Uart, based on the one posted for the 8 bit family, but I have 2 very strange issues.

First one is when I use Uart1_Write_Char(val) to write a character it doesn't work, but if I use U1TXREG := val it works

Second of it, I get strange things from time to time, it's sending double characters, or sometimes it skips a few characters..

First I'll Include the unit, after that I'll include the Interrupts (note this is only done for Uart1, in my code I use a different unit to process Uart2)
The main program is only for a test, in the actual code I send large chunks to the Uart unit (aprox 200 chars each second on 38K4 Baud)

Can Somebody tell me why I get data errors from time to time?

Code: Select all

unit UartB;

uses DSPIC_aditional_string_library;

const
CL = #13+#10;

const rxbufsizeA : word = 100;
      rxbufsizeB : word = 10;
      txbufsizeA : word = 600;
      txbufsizeB : word = 10;

var

    RXheadA,RXheadB,
    RXtailA,RXtailB      : word;
    RxbufA       : string[rxbufsizeA];
    RxbufB         :string[rxbufsizeB];
    RXbufemptyA,RXbufemptyB,
    RXbfullA,RXbfullB     : boolean

    txbufA       : string[txbufsizeA];
    txbufB       : string[txbufsizeB];
    txheadA,txheadB,
    txtailA,txtailB      : word;

    crflagA,crflagB      : word;

    TXbuffullA,TXbuffullB,
    TXbufemptyA,TXbufemptyB  : boolean;
    
    CharCountA,CharCountB : word;
    Uart1Sending,Uart2Sending : Boolean;
    
implementation

function InbufA:word;
begin
  if txheadA = txtailA then result := 0;
  if txheadA < txtailA then result := txheadA-txtailA;
  if txheadA > txtailA then result := txtailA-txheadA;
end;

function InbufB:word;
begin
  if txheadB = txtailB then result := 0;
  if txheadB < txtailB then result := txheadB-txtailB;
  if txheadB > txtailB then result := txtailB-txheadB;
end;

procedure Uart1_TX_Byte();
begin
  // transmit everything in buffer
  if (txheadA <> txtailA)  then
  begin
    U1TXREG:=txbufA[txheadA];
    //Uart1_Write_Char(txbufA[txheadA]); // THIS WON'T WORK!
    inc(txheadA);
    if txheadA > txbufsizeA then txheadA := 0;
    if txheadA=txtailA then
    begin
      TXbufemptyA := true;
    end;
  end;
  Uart1Sending := False;

end;

procedure Uart2_TX_Byte();
begin
  if (InbufB = 0) then
  begin
    Uart2Sending := False;
    exit;
  end
  else
  begin
    Uart2Sending := True;
  end;
  // transmit everything in buffer
  U2TXREG:=txbufB[txheadB];
  inc(txheadB);
  TXbuffullB := false;
  if txheadB > txbufsizeB then txheadB := 0;
  if txheadB=txtailB then
  begin
    TXbufemptyB := true;
  end;
end;


procedure Uart1_RX_Byte();
begin
  RXbufA[RXtailA] := U1RXREG;
  if RXbufA[RXtailA] = 0 then exit; // Filter 0 chars
  inc(CharCountA);
  if RXbufA[RXtailA] = 10 then
  begin
    inc(crflagA);
    CharCountA := 0;
  end;
  inc(RXtailA);
  if rxtailA > rxbufsizeA then rxtailA := 0;
  RXbufemptyA := false;
  if RXtailA = RXheadA then RXbfullA := true;
end;

procedure Uart2_RX_Byte();
begin
  RXbufB[RXtailB] := U2RXREG;
  if RXbufB[RXtailB] = 0 then exit; // Filter 0 chars
  inc(CharCountB);
  if RXbufB[RXtailB] = 10 then
  begin
    inc(crflagB);
    CharCountB := 0;
  end;
  inc(RXtailB);
  if rxtailB > rxbufsizeB then rxtailB := 0;
  RXbufemptyB := false;
  if RXtailB = RXheadB then RXbfullB := true;
end;

function Uart1_Read_Char_ :byte; // read a byte from the input-FIFO
var k : byte;
begin
  k := 0;
  if RXbufemptyA = false then
  begin
    k := RXbufA[RXheadA];
    RXbufA[RXheadA] := 0; // destructive read
    inc(RXheadA);
    if RXheadA >  RXbufsizeA then RXheadA := 0;
    if RXheadA = RXtailA then RXbufemptyA := true else RXbfullA := false;
  end;
  result := k;
end;

function Uart2_Read_Char_ :byte; // read a byte from the input-FIFO
var k : byte;
begin
  k := 0;
  if RXbufemptyB = false then
  begin
    k := RXbufB[RXheadB];
    RXbufB[RXheadB] := 0; // destructive read
    inc(RXheadB);
    if RXheadB >  RXbufsizeB then RXheadB := 0;
    if RXheadB = RXtailB then RXbufemptyB := true else RXbfullB := false;
  end;
  result := k;
end;


procedure Uart1_Write_CharI_(k : Char); // write a byte to the TX-FIFO
begin
  TXbufA[TXtailA] := k;
  inc(TXtailA);
  if txtailA > txbufsizeA then txtailA := 0;
  if TXtailA=TXheadA then txbuffullA := true;  // notify buffer full
  txbufemptyA := false;
end;

procedure Uart1_Write_Char_(k : Char);
begin
  Uart1_Write_CharI_(k);
  if Uart1Sending = False then Uart1_TX_Byte(); // Start sending after the CRLF
end;

procedure Uart2_Write_CharI_(k : Char); // write a byte to the TX-FIFO
begin
  TXbufB[txtailB] := k;
  inc(txtailB);
  if txtailB > txbufsizeB then txtailB := 0;
  if TXtailB=TXheadB then txbuffullB := true;  // notify buffer full
  txbufemptyB := false;
end;


procedure Uart2_Write_Char_(k : Char);
begin
  Uart2_Write_CharI_(k);
  if Uart2Sending = False then Uart2_TX_Byte(); // Start sending after the CRLF
end;

procedure Uart1_Writeln_(var s : string[txbufsizeA]);
var i : word;
begin
  i := 0;
  while s[i] <> 0 do
  begin
    if (Com1TXInt = True) then
      Uart1_Write_CharI_(s[i])
    else
      Uart1_Write_Char(s[i]);
    inc(i);
  end;
  if (Uart2Sending = False) and (Com1TXInt = True) then
  begin
    ClearBit(IFS0,12); //Clear TXInt Uart 1
    Uart1_TX_Byte(); // Start sending after the CRLF
  end;
end;

procedure Uart1_Writeln(var s : string[txbufsizeA]);
begin
  str_cat(s,CL);
  Uart1_Writeln_(s);
end;

procedure Uart2_Writeln_(var s : string[txbufsizeB]);
var i : word;
begin
  i := 0;
  while s[i] <> 0 do
  begin
    if (Com2TXInt = True) then
      Uart2_Write_CharI_(s[i])
    else
      Uart2_Write_Char(s[i]);
    inc(i);
  end;
  //if (Uart1Sending = False) and (Com2TXInt = True) then
  if Testbit(U2STA,8) = 1 then
  begin
    ClearBit(IFS1,15); //Clear TXInt Uart 2
    Uart2_TX_Byte(); // Start sending after the CRLF
  end;
end;

procedure Uart2_Writeln(var s : string[txbufsizeB]);
begin
  str_cat(s,CL);
  Uart2_Writeln_(s);
end;


procedure Uart1_Readln(var s: string[txbufsizeA];var ok:boolean);
var i : word;
    dat : byte;
begin
  i:=0;
  dat:=0;
  if crflagA > 0 then
  begin
    while ((dat <> 10) and (RXbufemptyA = False)) do
    begin
      dat:=Uart1_Read_Char_();
      if dat > 0 then
      begin
        s[i]:=dat;
        inc(i);
      end;
    end;
    dec(crflagA);
    if RXbufemptyA = true then crflagA:=0;
    s[i]:=#0;
    ok:=True;
  end
  else
  begin
    s[0]:=#0;
    ok:=False;
  end;
end;


procedure Uart2_Readln(var s: string[txbufsizeB];var ok:boolean);
var i : word;
    dat: byte;
begin
  i:=0;
  dat:=0;
  if crflagB > 0 then
  begin
    while ((dat <> 10) and (RXbufemptyB = False)) do
    begin
      dat:=Uart2_Read_Char_();
      if dat > 0 then
      begin
        s[i]:=dat;
        inc(i);
      end;
    end;
    dec(crflagB);
    if RXbufemptyB = true then crflagB:=0;
    s[i]:=#0;
    ok:=True;
  end
  else
  begin
    s[0]:=#0;
    ok:=False;
  end;
end;


procedure Uart_Init_();
begin
  memset(@RXbufA,0,rxbufsizeA);
  memset(@TXbufA,0,txbufsizeA);
  memset(@RXbufB,0,rxbufsizeB);
  memset(@TXbufB,0,txbufsizeB);
  RXheadA := 0;
  RXtailA := 0;
  TXtailA := 0;
  TXheadA := 0;
  CharCountA := 0;
  CharCountB := 0;
  RXbfullA := false;
  RXbufemptyA := true;
  TXbuffullA := false;
  TXbufemptyA := true;
  crflagA := 0;
  RXheadB := 0;
  RXtailB := 0;
  TXtailB := 0;
  TXheadB := 0;
  RXbfullB := false;
  RXbufemptyB := true;
  TXbuffullB := false;
  TXbufemptyB := true;
  crflagB := 0;
  Uart1Sending := False;
  Uart2Sending := False;
  // Int register voor 24FJ
  IFS0.11 := 0; //Clear RXInt Uart 1
  IEC0.11 := 1; // Enable RXInt Uart 1
  IFS1.14 := 0; //Clear RXInt Uart 2
  IEC1.14 := 1; // Enable RXInt Uart 2
  IFS0.12 := 0; //Clear TXInt Uart 1
  if (Com1TXInt = True) then
    IEC0.12 := 1; // Enable TXInt Uart 1
  IFS1.15 := 0; //Clear TXInt Uart 2
  if (Com2TXInt = True) then
    IEC1.15 := 1; // Enable TXInt Uart 2

  ClearBit(U1STA,1); //Clear overflow
  ClearBit(U2STA,1); //Clear overflow
end;


end.
MAIN:

Code: Select all

procedure Uart1RXInt; org $2A;
begin
  ClearBit(IFS0,11); //Clear RXInt Uart 1
  Uart1_RX_Byte();
end;

procedure Uart1TXInt; org $2C;
begin
  ClearBit(IFS0,12); //Clear TXInt Uart 1
  Uart1_TX_Byte();
end;

begin

  // Int register voor 24FJ
  IFS0.11 := 0; //Clear RXInt Uart 1
  IEC0.11 := 1; // Enable RXInt Uart 1
  IFS0.12 := 0; //Clear TXInt Uart 1
  IEC0.12 := 1; // Enable TXInt Uart 1
  IFS1.14 := 0; //Clear RXInt Uart 2
  IEC1.14 := 1; // Enable RXInt Uart 2
  IFS1.15 := 0; //Clear TXInt Uart 2
  IEC1.15 := 1; // Enable TXInt Uart 2

  while True do 
  begin
     Uart1_Writeln('Test and a lot more');
     delay_ms(10);
  end;
end;

end.

jpc
Posts: 1986
Joined: 22 Apr 2005 17:40
Location: France 87

#2 Post by jpc » 28 May 2009 11:50

Skydec,

seems i never posted it , have some Uart library that provides circular buffered RX_ISR , for the TX i usually simply do without interrupt's.
It works quite reliable i think, receive and transmit unlimited size files at 115200 baud. I used it on P30, recently adapted it for P24 , main difference were the vector-addresses. I had some issue as you have seen with the Fifo but that seems solved.

Code: Select all

unit irq_uart_p24 ;

// @jpc 2009

implementation

const rxbuf1size      = 100;
      rxbuf2size      = 200;
      lf              : byte = 10;
      cr              : byte = 13;
      space           : byte = ' ';
      crlf            : array[3] of byte = (13,10,0);
      msecvalue       : word = (clock_khz div 2) - 32;

var rxhead1,
    rxtail1 ,
    termflag1         : word;
    mrxbuf1           : array[0..rxbuf1size] of byte;

    rxb1_empty,

    rxb1_full         : boolean;

    
    dummy : word;
    rxhead2,
    rxtail2 ,
    termflag2         : word;
    mrxbuf2           : array[0..rxbuf2size] of byte;

    rxb2_empty,

    rxb2_full         : boolean;

procedure uart1_rx_irq; org $2A;  // Uart1 receiver interrupt

begin
  if IFS0.11 = 1 then
  begin
   //uart1_write_text('ISR called');
    if  (U1STA and %000000001110) = 0 then    // no error , get character
    begin
      mrxbuf1[rxhead1]:=U1RXREG;               // store it in circular buffer
      rxb1_empty := false;
      inc(rxhead1);
      if rxhead1 = rxbuf1size then
      begin
        rxhead1 := 0;
      end;
      if rxhead1 = rxtail1 then
      begin
        rxb1_full :=  true;                // buffer full
      end;
    end else
    begin // there must be some error
     dummy:= Uart1_Read_Char;
      if U1STA.1 = 1 then  // overrun er3ror
      begin
        ClearBit(U1STA,1);
      end;
      if U1STA.2 = 1 then  // framing error
      begin
        ClearBit(U1STA,2);
      end;
      if U1STA.3 = 1 then  // parity error
      begin
        ClearBit(U1STA,3);
      end;
    end;
  end;
  IFS0.11 := 0;          // clear interrupt-flag
end;

procedure uart2_rx_irq; org $50;
begin

  if IFS1.14= 1 then
  begin

    if  (U2STA and %000000001110) = 0  then // no error , get character
    begin
      mrxbuf2[rxhead2]:= Uart2_Read_Char;         // store it in circular buffer
      rxb2_empty := false;
      inc(rxhead2);
      if rxhead2 = rxbuf2size then
      begin
        rxhead2 := 0;
      end;
      if rxhead2 = rxtail2 then  // buffer full
      begin
        rxb2_full :=  true;
      end;
    end else
    begin
      dummy:= Uart2_Read_Char;
      if U2STA.1 = 1 then  // overrun error
      begin
       uart1_write_text('overrun error');
        ClearBit(U2STA,1);
      end;
      if U2STA.2 = 1 then  // framing error
      begin   uart1_write_text('framing error');
        ClearBit(U2STA,2);
      end;
      if U2STA.3 = 1 then  // parity error
      begin  uart1_write_text('parity error');
        ClearBit(U2STA,3);
      end;
    end;
    IFS1.14 := 0;          // clear interrupt-flag
  end;
end;

procedure put1(k : byte); // write a byte to uart-out
begin
  Uart1_Write_Char(k);
end;


procedure write1( var s : string[40]); // write a string to uart1-out
var i : byte;
begin
  i := 0;
  while s[i] <> 0 do
  begin
    put1(s[i]);
    inc(i);
  end;
end;



function readkey1 : byte; // read a byte from the input-FIFO1
var k : byte;
begin
  k := 0;
  if rxb1_empty = false then
  begin
    k := mrxbuf1[rxtail1];
    // uart1_write_text('readkey'); // for debugging only
    mrxbuf1[rxtail1] := 0; // destructive read
    inc(rxtail1);
    if rxtail1 =  rxbuf1size then rxtail1 := 0;
    if rxtail1 = rxhead1  then rxb1_empty := true else rxb1_full := false;
  end ; //else termflag1 := 0;
  result := k;
end;



function lines_read1 : byte;  // function return nr cr's in FIFO
begin
  result := termflag1;
end;



function keypressed1 : boolean; // returns false if no byte available in RX-FIFO
begin

  result := not(rxb1_empty);
  
end;

function keypressed2 : boolean; // returns false if no byte available in RX-FIFO
begin
 // if rxb1_empty=true  then result := false  else result := true;
  result := not(rxb2_empty);
end;



procedure writeln1( var s : string[40]); // writeln a string to uart-out
begin
  write1(s);
  write1(crlf);
end;

procedure put2(k : byte); // write a byte to uart-out
begin
  Uart2_Write_Char(k);
end;

procedure write2( var s : string[40]); // write a string to uart2-out
var i : byte;
begin
  i := 0;
  while s[i] <> 0 do
  begin
    put2(s[i]);
    inc(i);
  end;
end;

procedure writeln2( var s : string[40]); // writeln a string to uart-out
begin
  write2(s);
  write2(crlf);
end;



function readkey2 : byte; // read a byte from the input-FIFO2
var k : byte;
begin
  k := 0;
  if rxb2_empty = false then
  begin
    k := mrxbuf2[rxtail2];

    mrxbuf2[rxtail2] := 0; // destructive read
    inc(rxtail2);
    if rxtail2 =  rxbuf2size then rxtail2 := 0;
    if rxtail2 = rxhead2  then rxb2_empty := true else rxb2_full := false;
  end else termflag2 := 0;
  result := k;
end;


procedure irq_usart2_init( baudrate : longint); // initialises interrupt's and variable's
begin
  memset(@mrxbuf2,0,sizeof(mrxbuf2));
  rxhead2 := 0;
  rxtail2 := 0;
  rxb2_full := false;
  rxb2_empty := true;
  termflag2 := 0;
  Uart2_Init(Baudrate);
  U2STA.7 := 0;  // interrupt on every char
  U2STA.6 := 0;
  U2STA.5 := 0;  // disable 9bit address detect
  
  IPC7 := IPC7 OR %0000011100000000; // set Uart2 RX interrupt priority to highest
  delay_ms(100);
  IEC1.14  := 1; // enable Uart2RX interrupts
end;

procedure irq_usart1_init( baudrate : longint); // initialises interrupt's and variable's
begin
  memset(@mrxbuf1,0,sizeof(mrxbuf1));

  termflag1 := 0;

  Uart1_Init(Baudrate);
   rxhead1 := 0;
  rxtail1 := 0;
  rxb1_full := false;
  rxb1_empty := true;
  U1STA.7 := 0;  // interrupt on every char
  U1STA.6 := 0;
  U1STA.5 := 0;  // disable 9bit address detect
  IPC2 := IPC2 OR %0111000000000000; // set Uart1 RX interrupt priority to highest
  delay_ms(100);
  IEC0.11  := 1; // enable Uart1RX interrupts
end;

end.
Au royaume des aveugles, les borgnes sont rois.

Skydec
Posts: 39
Joined: 30 Jan 2009 12:38

#3 Post by Skydec » 05 Jun 2009 09:57

Thanks !

I think I found the problem.
The pic processor has alot to do and there are 2 serial ports int driven with alot of data, and 2 spi ports also both interrupt driven.

The problem is that the interrupts conflict each other, even when you I priorize them.
The errors are less often when I dissable all other interrupts when the cpu is processing one.
So I think the cpu or compiler can't handle alot of ISR routines all together.

Even clearing the interrupt flag at the begining or the end of the code has a hudge impact on the stability.

jpc
Posts: 1986
Joined: 22 Apr 2005 17:40
Location: France 87

#4 Post by jpc » 05 Jun 2009 10:23

i actually have my doubts, these simple serial interrupts do not consume that much cpu-cycles , i run a 250uS timer based PID ISR in parallele with 2 uart-ISR , QEI-ISR , can still send and relay back textfile of kB at 115200 baud without any visible problem. Maybe you spend too much time inside interrupts somehwere.
Au royaume des aveugles, les borgnes sont rois.

Skydec
Posts: 39
Joined: 30 Jan 2009 12:38

#5 Post by Skydec » 05 Jun 2009 11:03

Yeah some routines have to do calculations on large chuncks of data.
But even if I should spend to much time, it shouldn't be solvable to dissable other interrupts.
With the dissabling, it works with the same load.

jpc
Posts: 1986
Joined: 22 Apr 2005 17:40
Location: France 87

#6 Post by jpc » 05 Jun 2009 11:09

as a strategy, i try to optimize my ISR's for speed , store data in buffer and let main-loop chew on it, this ensures interrupt-latency to be best and allow your main to consume all remaining cpu-cycles. For sure there comes a moment it gets overloaded but your interrupts should have no problem.
Actually i even regretted each interrupt to have its own context-store/restore , if at the moment of return from interrupt there is another one of same/lower priority pending, the compiler performs a restore context followed by a store context , all for nothing, just a waste. I have not found an efficient and elegant solution to deal with this.
Au royaume des aveugles, les borgnes sont rois.

Skydec
Posts: 39
Joined: 30 Jan 2009 12:38

#7 Post by Skydec » 05 Jun 2009 11:15

True about that.

But I saw you don't use interrupts for sending the data, problem for me is I must have my cpu available for reading the comport.
The application connected to the port send hudge blocks data and needs to get acknowledge on the right time, so I have to deal with it almost realtime, but all the other processes must be done in the spare time, but this is the same for both SPI busses (The controller is slave)
My first version was almost everything in the main routine, but that didn't work at all (to much time for checking if there is data to process etc. + no priority or interrupt of a proces possible)

jpc
Posts: 1986
Joined: 22 Apr 2005 17:40
Location: France 87

#8 Post by jpc » 05 Jun 2009 12:01

sending ACK is something else than processing , u have used a mix of these approaches where only RX is interrupt driven but this iosr is able to see when ACK has to be send and then takes care immediately. The majur data-processing still is waiting in that case for processing by main. I cannot judge if this appraoch would be possible in your application.
Au royaume des aveugles, les borgnes sont rois.

Skydec
Posts: 39
Joined: 30 Jan 2009 12:38

#9 Post by Skydec » 05 Jun 2009 12:13

No, that would be to easy :)
The data needs to be processed partly, calculate checksums etc and after that the application sends a ACK or NACK.

I think the Unit is working fine, but there is alot of overhead when using interrupts but that's what you also said in your previous post.


Only the funny thing is about when to clear the interrupt bit
Sometimes it would have been nice to have the source code of several functions so that you know how to tweak your code to run more optimized, but I don't think the guys from MikroE will make that available..

jpc
Posts: 1986
Joined: 22 Apr 2005 17:40
Location: France 87

#10 Post by jpc » 05 Jun 2009 12:24

ok, back to your first post here , i see you have

Code: Select all

if txheadA > txbufsizeA then txheadA := 0; 
, i think this is going beyond the buffer which you dimensioned string[txbufsizeA] , which has the elements string[0..txbufsizeA-1], same for the other buffer at first sight, maybe this explains your problems
Au royaume des aveugles, les borgnes sont rois.

kingGrey
Posts: 158
Joined: 08 Mar 2007 00:40
Location: portland, OR

#11 Post by kingGrey » 01 Sep 2009 22:45

Good discussion folks!
Thank You for your help
An expert is a man who has made all the mistakes which can be made, in a narrow field.
http://www.stevenswater.com/catalog/stevensProductdl3000.aspx?SKU=%2793750%27

Post Reply

Return to “mikroPascal for dsPIC30/33 and PIC24 General”