PIC16F684 Soft UART Not able to recieve (RX)

General discussion on mikroC PRO for PIC.
Post Reply
Author
Message
tkj
Posts: 9
Joined: 24 Nov 2010 11:46

PIC16F684 Soft UART Not able to recieve (RX)

#1 Post by tkj » 24 Nov 2010 11:55

Hello all.
I'm an student from Denmark with a lot of experience with microcontrollers, microprocessors and FPGAs.
Currently I'm making a project in school where we have to use the PIC16F684, and I have to connect it to the Parallax RFID Reader.
As the PIC16F684 doesn't have any hardware UART, I have to use the software UART library in mikroC.

I've changed the device in the Soft UART Example to 16F684 and programmed the chip. In the terminal on my computer I can see the letters from small z to capital A (as supposed). Then the error LED turns on: error=1, but unfortunately when I write something to the chip, I isn't returned, as it should.

Any idea why it can send (TX) but doesn't recieve (RX)?
I'm using the internal 8MHz oscillator, and this is my configuration:

Code: Select all

OSCCON = 0b01110101;  // 16F684
TRISC = 0;
error = Soft_UART_Init(&PORTC, 4, 5, 2400, 0); // Initialize Soft UART at 2400 bps
Best Regards
Thomas Jespersen

User avatar
ranko.rankovic
Posts: 433
Joined: 11 Jun 2010 09:22

Re: PIC16F684 Soft UART Not able to recieve (RX)

#2 Post by ranko.rankovic » 26 Nov 2010 15:39

Hello tkj,

Since you didn't post your code and due to simptoms described, I can only suggest that you didn't disable comparators, which are located on PortC and disable A/D Convertor just in case.

Add next lines into main program or in initialization subrutine:

Code: Select all

  ANSEL = 0x00;
  CMCON0 = 0x07;
Best regards
Ranko Rankovic
mikroElektronika [Support Department]

tkj
Posts: 9
Joined: 24 Nov 2010 11:46

Re: PIC16F684 Soft UART Not able to recieve (RX)

#3 Post by tkj » 26 Nov 2010 16:46

Hi.
That kind of fixed the problem, but it's not functionel.
This is the code:

Code: Select all

char i, error, byte_read;                 // Auxiliary variables

void main(){
     OSCCON = 0b01110101;  // 16F684
     ANSEL = 0x00;
     CMCON0 = 0x07;

     TRISA = 0b00001000;
     TRISC = 0;
     PORTC.b2 = 0; // Enable the RFID Reader

  error = Soft_UART_Init(&PORTC, 0, 1, 2400, 0); // Initialize Soft UART at 2400 bps
  if (error > 0) {
    PORTA.b0 = 1;                        // Signalize Init error
    while(1) ;                            // Stop program
  }
  Delay_ms(100);

  for (i = 'z'; i >= 'A'; i--) {          // Send bytes from 'z' downto 'A'
    Soft_UART_Write(i);
    Delay_ms(100);
  }

  while(1) {                              // Endless loop
    byte_read = Soft_UART_Read(&error);   // Read byte, then test error flag
    if (error == 1)                            // If error was detected
        PORTA.b0 = 1;                        // Signalize Init error
    else if (error == 255)
         PORTA.b1 = 1;
    else
      Soft_UART_Write(byte_read);         // If error was not detected, return byte read
    }
}
The project settings is set to internal clock and 8MHz. In the computer I recieve the characters from z to A fine, and if I write a single character, I get it back correctly.
But if I write more than 1 character, I get some strange feedbacks, but there is some kind of pattern in what I recieve. I only recieve every second letter - fx:
Write: Thomas Jespersen
Recieve: Toa epre
Write: abcdef
Recieve: ace
Write: 123456
Recieve: 135
Why is this happening? In my project I have to store the characters I recieve in a buffer and then check for an RFID code in that buffer when 0x0D is recieved.
But I can't, as I can't recieve the RFID serial data stream correctly?

Best Regards
Thomas Jespersen

Sobrietytest
Posts: 619
Joined: 05 Jul 2008 06:05
Location: Thailand

Re: PIC16F684 Soft UART Not able to recieve (RX)

#4 Post by Sobrietytest » 27 Nov 2010 10:16

Hi Thomas, this is a common problem with the Soft_Uart functions. When you use a PIC with a hardware Uart, incoming data is buffered in a small FIFO memory space and (within reason) you can read the data at any time. Unfortunately the Soft_Uart functions do not have a FIFO buffer and each incoming character is overwritten by the next one. Also there is no provision for Uart_Data_Ready so there is no indication when new data has arrived.

The solution is to either implement some form of hardware handshaking (which can be quite difficult) or make sure your software reads the data before the next character arrives, the problem with the second solution is that it is entirely possible to read the same data twice if your software is reading too quickly! In essence, you need to make sure your software starts reading the data as soon as it arrives and continues reading until the end of the data, in practice this is almost impossible without implementing some kind of interrupt because the read cycles are not synchronised to the incoming baud rate.

Personally, I would avoid using Soft_Uart for reading incoming data because it can be very error prone and (as you have seen) it is very easy to miss data if your program is busy with other tasks.

Mince-n-Tatties
Posts: 2780
Joined: 25 Dec 2008 15:22
Location: Scotland

Re: PIC16F684 Soft UART Not able to recieve (RX)

#5 Post by Mince-n-Tatties » 27 Nov 2010 23:21

what about using RA2/INT as the RX pin.

enable INT with falling edge (Start bit detect) interrupt function and wrap your byte RX routine within the ISR.

This would give you the ability to place greater importance on the uart RX function.

It would be worth having a go at constructing your own software uart too, this way you will have control over which timer is used and sample rate/resolution.
Best Regards

Mince

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

Re: PIC16F684 Soft UART Not able to recieve (RX)

#6 Post by jpc » 28 Nov 2010 10:10

a major problem here is the soft_uart_write, this will take the full byte-time and during this time the soft_uart_receive is NOT receiving. Hardware Uart indeed would be the better option but you may try without the echo-back in the loop. Maybe there are silence's in the datastream that allow to tx without the risk of loosing incoming data. 2400 baud is so slow that you can build an interrupt driven softuart receiver, it is some effort but may work well if you have no alternative
Au royaume des aveugles, les borgnes sont rois.

tkj
Posts: 9
Joined: 24 Nov 2010 11:46

Re: PIC16F684 Soft UART Not able to recieve (RX)

#7 Post by tkj » 28 Nov 2010 13:22

Dear all.
Thanks for your responses.
As everything looks now, it doesn't look to good. The problem is that the project isn't that long, so we are starting to get time constraints.
As for me I don't have the time to start making an interrupt based Serial reciever - so we have to think of what excists and what don't...

So are there made any interrupt based serial recievers which can easily be ported to mikroC?

Best Regards
Thomas Jespersen

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

Re: PIC16F684 Soft UART Not able to recieve (RX)

#8 Post by jpc » 29 Nov 2010 10:20

i once did this in Pascal, should not be too difficult to translate.

Code: Select all

program irq_soft_uart;
{ irq-driven software uart with circular buffer

  this is special version for 12F683 , 8 mhz internal clock
  
  concept :
  
    detect startbit on int/ change portb interrupt;
    disable this interrupt
    enable timer interrupt
    wait half bit time
    sample 8 bits at bittime interval by timerinterrupt
    disable timer-interrupt
    store complete byte to circular buffer
    reenable change notification interrupt
  
  no risk of blocking or characters getting lost
  reception of 1 character consumes ~640 processor-cycles , at 8Mhz internal clock this is 320uS ,
  at lower baudrates significantly better then normal soft-uart which consumes all processor-time.
  in the given example i use 9600 baud , for lower baudrates prescaler and timer-values must be adjusted

  RX on GPIO.0 , TX on GPIO.2 in this example
  tested on EP3 , jumpered GPIO.0 ( PORTA.0) to PORTC.7 and GPIO.2(PORTA.2) to PORTC.6 in order to use max232
}




const halfbittime : byte = 255-(104-60); // might need tiny corrections
      fullbittime : byte = 255-(208-28);
      astate      : byte = 1;
      rxbufsize   : byte = 12;

var k,
    bit           : byte;
    rxbyte,
    RXhead,
    RXtail,
    irqstate      : byte;

    Rxbuf         : array[0..rxbufsize+1] of byte;
    RXbufempty,
    RXbfull       : boolean ;
    chars,
    value         : byte;
    counter : word;
    
procedure interrupt;

begin
  case irqstate of
  2 : begin // we get here for all 8 databits
        if intcon.t0if = 1 then
        begin
          tmr0:= fullbittime;
          if gpio.0 = astate then setbit(rxbyte,bit) else clearbit(rxbyte,bit);
          inc(bit); //
          if bit = 8 then // now we should have received a full byte
          begin
            inc(chars);
            // copy this byte to circular buffer
            Rxbuf[Rxtail] := rxbyte;
            Inc(Rxtail);
            If Rxtail > Rxbufsize Then Rxtail := 0;
            Rxbufempty := False;
            If Rxtail = Rxhead Then Rxbfull := True;

            clearbit(intcon,t0ie); //and reinitialize everything for next character
            clearbit(intcon,gpif);
            setbit(intcon,gpie);
            irqstate := 0;
          end;
          clearbit(intcon,t0if);
        end;
      end;
  0 : begin
        if intcon.gpif = 1 then
        begin
          tmr0 := halfbittime;
          //clearbit(intcon,t0if);
          if gpio.0 = 0  then
          begin
            // now startbit maybe is detected
            // rxbyte := 0; // this is the byte into which we will recieve the character
            //startfound := false;
            clearbit(intcon,gpie); // stop interrupting on portb-change now
            setbit(intcon,t0ie); // Respond to timer-interrupt from here
            irqstate := 1; // next fase
          end;
          clearbit(intcon,gpif); // should never happen
        end;
        clearbit(intcon,t0if);
      end;
  1 : begin  // we get here 1/2 bittime after detection of startbit
        if intcon.t0if = 1 then
        begin
          tmr0 := fullbittime;
          if gpio.0 = 0 then
          begin
            bit := 0;
            rxbyte := 0;
            irqstate := 2;
          end  else
          begin // if not stable startbit return to waitforstartbit
            clearbit(intcon,t0ie);
            clearbit(intcon,gpif);
            irqstate := 0;
            setbit(intcon,gpie);
          end;
          clearbit(intcon,t0if);
        end;
      end;
  end;
end;



function message_received : boolean;
begin
 if chars <> 0 then result := true else result := false;
end;

function testkey  : byte; // test byte in buffer , before using this first check with message_received if buffer not empty !
begin
  result := rxbuf[rxhead];
end;

Function Readkey :Byte; // Read A Byte From The Input-fifo
Var Rk : Byte;
Begin
  Rk := 0;
  If Rxbufempty = False Then
  Begin
    Rk := Rxbuf[Rxhead];
    Rxbuf[Rxhead] := 0; // Destructive Read
    Inc(Rxhead);
    dec(chars);
    If Rxhead >  Rxbufsize Then Rxhead := 0;
    If Rxhead = Rxtail Then Rxbufempty := True Else Rxbfull := False;
  End;
  Result := Rk;
End;

procedure reply;
var i : byte;
    tmp : byte;
begin
  tmp := readkey;
  soft_uart_write(tmp);
end;

procedure init_main;
begin
  rxhead := 0;
  rxtail := 0;
  // initialize global variables , peripherals, interrupts , timers etc.
  osccon := %01110000;  // internal osc , 8 mHz
  while osccon.hts = 0 do nop;  // wait until osc stable
  cmcon0 := 7;
  ansel  := 0;          // all digital inputs
  gpio  := 0;
  trisio  := %00001000;  // GPIO.3 =RX
  trisio  := %00000001;
  IOc := %00000001;
  option_reg := %10001000; // timer div 2 > 4800 baud
  intcon := 0;
  clearbit(intcon,gpif);
  setbit(intcon,gpie);  // enable interrupt on change portb
  bit := 0;
  irqstate := 0;
  chars := 0;
  setbit(intcon,gie);   // allow interrupts
  soft_uart_init(gpio,0,2,9600,0);

end;

begin
  init_main;
  trisio := 1;
  soft_uart_write(13);
  soft_uart_write(10);
  soft_uart_write('T');
  soft_uart_write('e');
  soft_uart_write('s');
  soft_uart_write('t');
  soft_uart_write(13);
  soft_uart_write(10);
  
  while true do
  begin
    clrwdt;
    if message_received then reply;
    inc(counter);
    case counter of
     3000 : gpio.1 := 0;
     30000 : begin
               gpio.1 := 1;
               counter := 0;
             end;
    end; // case
  end;
end.
Au royaume des aveugles, les borgnes sont rois.

Sobrietytest
Posts: 619
Joined: 05 Jul 2008 06:05
Location: Thailand

Re: PIC16F684 Soft UART Not able to recieve (RX)

#9 Post by Sobrietytest » 29 Nov 2010 10:49

Hi Thomas, if this is an educational assignment you should ask your teacher if you can use a different PIC. Personally I would be asking why your teacher specified a device which is completely impractical for the assignment; part of your education should include the ability to choose the right device for the job! Hitting a nail with a screwdriver is bad engineering practice.

There are many PIC's which would make this project very simple, it is far better to use the right tools rather than trying to hack a solution using the wrong ones.

tkj
Posts: 9
Joined: 24 Nov 2010 11:46

Re: PIC16F684 Soft UART Not able to recieve (RX)

#10 Post by tkj » 29 Nov 2010 11:10

Sobrietytest wrote:Hi Thomas, if this is an educational assignment you should ask your teacher if you can use a different PIC. Personally I would be asking why your teacher specified a device which is completely impractical for the assignment; part of your education should include the ability to choose the right device for the job! Hitting a nail with a screwdriver is bad engineering practice.

There are many PIC's which would make this project very simple, it is far better to use the right tools rather than trying to hack a solution using the wrong ones.
I know there are many PIC's which would fit this project a lot better. On our education we have PIC16F628 fx, which would be a lot better suited, as it has Hardware UART.
The reason why are trying to use the PIC16F684, is that our education already have made a board/PCB for this PIC. I don't understand why they did chose this PIC, but the board is already made.
We can of course chose another PIC, but then we will have to make a board, and that might be the only possibility too!

Thanks for the pascal code, jpc. I will take a look on that, and see if we can translate it.

Thomas

Mince-n-Tatties
Posts: 2780
Joined: 25 Dec 2008 15:22
Location: Scotland

Re: PIC16F684 Soft UART Not able to recieve (RX)

#11 Post by Mince-n-Tatties » 29 Nov 2010 13:43

Sobrietytest wrote:Hi Thomas, if this is an educational assignment you should ask your teacher if you can use a different PIC. Personally I would be asking why your teacher specified a device which is completely impractical for the assignment; part of your education should include the ability to choose the right device for the job! Hitting a nail with a screwdriver is bad engineering practice.

There are many PIC's which would make this project very simple, it is far better to use the right tools rather than trying to hack a solution using the wrong ones.
If would assume it was to force the student to learn how to deal with serial data hence why i suggested doing their own software uart rather than using the mikroC provided library. It could also just be the only pic the teacher knows inside out.
Best Regards

Mince

Mince-n-Tatties
Posts: 2780
Joined: 25 Dec 2008 15:22
Location: Scotland

Re: PIC16F684 Soft UART Not able to recieve (RX)

#12 Post by Mince-n-Tatties » 29 Nov 2010 13:47

how i use the hardware library with interrupts to ensure no data is missed

Code: Select all

// UART buffer parameters & constants
unsigned const char strbuffer_size = 50;
unsigned char strbuffer[strbuffer_size];
volatile unsigned char strbuffPtr = 0;
volatile unsigned char DisplayBuffer = 0; // used to control displaying of values



void interrupt(void)
      {
        if(PIR1.RCIF)
          {

           strbuffer[strbuffPtr] = UART1_Read();

                if(strbuffer[strbuffPtr] == 0x0A)    // New line found -
                  {                                  // DisplayBuffer tells main loop to
                   DisplayBuffer = 1;                // show buffer values.
                   

                  }
                strbuffPtr++;
                if((strbuffPtr > STRBUFFER_SIZE - 1)||(DisplayBuffer))strbuffPtr = 0;

          } // exit uart interrupt

      }// exit global interrupt
Best Regards

Mince

Mince-n-Tatties
Posts: 2780
Joined: 25 Dec 2008 15:22
Location: Scotland

Re: PIC16F684 Soft UART Not able to recieve (RX)

#13 Post by Mince-n-Tatties » 29 Nov 2010 13:56

now convert to use software library

Code: Select all

// UART buffer parameters & constants
unsigned const char strbuffer_size = 50;
unsigned char strbuffer[strbuffer_size];
volatile unsigned char strbuffPtr = 0;
volatile unsigned char DisplayBuffer = 0; // used to control displaying of values



void interrupt(void)
      {
        if(Replace with RA2/INT flag)
          {

           strbuffer[strbuffPtr] = (REPLACE with software uart read command);

                if(strbuffer[strbuffPtr] == 0x0A)    // New line found -
                  {                                  // DisplayBuffer tells main loop to
                   DisplayBuffer = 1;                // show buffer values.
                  }
                strbuffPtr++;
                if((strbuffPtr > STRBUFFER_SIZE - 1)||(DisplayBuffer))strbuffPtr = 0;

          } // exit RA2 falling edge interrupt

      }// exit global interrupt
all you need do is use RA2 for software uart RX and setup as falling edge interrupt. TBH if you cant fill in the blanks then you shouldnt really be asking other people for solutions to your educational projects.
Best Regards

Mince

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

Re: PIC16F684 Soft UART Not able to recieve (RX)

#14 Post by jpc » 29 Nov 2010 14:26

mince-n-tatties, i am afraid it is not that easy, softuart will consume all time inside the interrupt again, to make this work well the bit-timing should be done by a timer, if this is well done you may have real full duplex from soft-uart.The solution i suggested ( sorry it is in Pascal) in fact generates 8 interrupts for 1 byte, in between the cpu is free to do something else. At 4800 baud i bit is ~200 uS , the interrupt will use less than half of this , running a TX in software based upon the same technique ( use of timer for bittiming) is no problem.
Here the soft-TX i used :

Code: Select all

procedure my_soft_uart_write( pin ,k : byte);
begin
  tmr2 := 0;
  clearbit(pir1,tmr2if);
  clearbit(gpio,pin);               // startbit
  while pir1.tmr2if = 0 do nop;
  nop;
  clearbit(pir1,tmr2if);
  for bit := 0 to 7 do
  begin
     gpio.pin := k.0;
     k := k shr 1;
     while pir1.tmr2if = 0 do nop;
     clearbit(pir1,tmr2if);
  end;
  setbit(gpio,pin); // stopbit
  while pir1.tmr2if = 0 do nop;
  nop;
  clearbit(pir1,tmr2if);
  while pir1.tmr2if = 0 do nop;
end;

Au royaume des aveugles, les borgnes sont rois.

tkj
Posts: 9
Joined: 24 Nov 2010 11:46

Re: PIC16F684 Soft UART Not able to recieve (RX)

#15 Post by tkj » 29 Nov 2010 14:54

I totally agree with you guys, that the Soft UART library is a time consuming function, which should be avoided.
Also I understand how the UART protocol works, and if I had the time I would make an interrupt based function myself.
We will try to port your Pascal code, and see if it works. Thanks again JPC.

Mince-n-Tatties: I can definitely fill in the blanks myself, the only problem is the deadline.
Take a look at my company's website and blog, to see some of the projects I've done: http://www.tkjelectronics.dk

Post Reply

Return to “mikroC PRO for PIC General”