News:

Let's find out together what makes a PIC Tick!

Main Menu

UART with troubles

Started by midali, Nov 24, 2021, 12:49 PM

Previous topic - Next topic

midali

Hello all,
 I want to receive data for S-Bus decoding (Futaba protocol).  Data contain 25 bytes transmitted as serial UART , 100000bps, 8E2 (8bit, Even parity, 2 stop bits). A message is sent every 6 milliseconds, the lenght for each signal(25bytes) is 3 ms .


midali

#1
I have some troubles :
1. After I apply the Vcc to PIC , the values are displaied  after 15-20 sec , so here I have a big delay to start UART
2. The data received are oscilatting, impossible to reading the bytes values ( looks like  each byte oscillating from half-value to value)
 What is wrong in my code ?

Device = 16F1938

Config1 FCMEN_OFF, FOSC_INTOSC, WDTE_OFF, MCLRE_OFF, PWRTE_OFF, CP_ON, MCLRE_OFF, BOREN_OFF, CLKOUTEN_OFF, FCMEN_OFF
Config2 WRT_OFF, VCAPEN_OFF, PLLEN_OFF, STVREN_OFF, LVP_OFF

Declare  Xtal = 32

Declare Hserial_Clear  = On
Declare Hserial_Baud   = 100000
Declare Hserial_Parity = Even

Include "lcd.bas"

OSCCON  = %11110000 'set to 8 x 4PLL MHz

TRISA  = %00001111
ANSELA = %00001111
PORTA  = %00000000
TRISB  = %11111111
ANSELB = %00000000
PORTB  = %11111110
PORTC  = %00000000

Dim rx_buffer[25] As Byte
Dim rx            As Byte
Dim i             As Byte
Dim ch[7]         As Word
Dim A             As Word
Dim B             As Word

Cls
'--------------Main-------------
main:
    i = 0
    For i = 0 To 24   
       HSerIn 5,timeout ,[rx_buffer[i] ]
    Next i
   
timeout:
    High PORTC.4
    display()                   
GoTo main
'----------End-Main-------------


Proc display()
    Print At 1,1,  Dec rx_buffer[1] , " "
    Print At 1,5,  Dec rx_buffer[2] , " "
    Print At 1,9,  Dec rx_buffer[3] , " "
    Print At 1,13, Dec rx_buffer[4] , " "
    Print At 2,1,  Dec rx_buffer[5] , " "
    Print At 2,5,  Dec rx_buffer[6] , " "
    Print At 2,9,  Dec rx_buffer[7] , " "
    Print At 2,13, Dec rx_buffer[8] , " "
EndProc


joesaliba

Midali,

At least you are receiving something after 20 seconds, I never managed to receive something, although I tried everything.

I will test your code. However, I see some anomalies in your code that might affect your display.

1) You should wait for the Sbus header which is $0F. Therefore you can synchronise the incoming data.
2) Print is so slow to update characters on an LCD. So other characters might be skipped, and as you are not waiting for the header you are going to see garbage on display. I would recommend to send data through HRSout to a pc as this is faster.

Regards

Joe


trastikata

Quote from: midali on Nov 24, 2021, 01:01 PMWhat is wrong in my code ?

It looks like you have wrong oscillator set-up, check again the datasheets. The easiest way to verify oscillator is to set a loop with a LED blinking every second - if it doesn't then it is oscillator problem.

Also I don't see how do you setup the USART port?

See_Mos

Software delays before setting OSCCON can cause this

If you have any software delays in LCD.bas move the OSCcON setting to your LCD.bas before the delay.

trastikata

Quote from: trastikata on Nov 25, 2021, 08:30 AMIt looks like you have wrong oscillator set-up, check again the datasheets.

I just skimmed over the datasheet, read careful the HFINTOSC (5.2.2.1 ) section!

midali

Thank you to all for help and for time spend for me!

Quote from: joesaliba on Nov 25, 2021, 08:02 AMMidali,

1) You should wait for the Sbus header which is $0F. Therefore you can synchronise the incoming data.
2) Print is so slow to update characters on an LCD. So other characters might be skipped, and as you are not waiting for the header you are going to see garbage on display. I would recommend to send data through HRSout to a pc as this is faster.


1) Right , first byte (rx_buffer[0]) is %00001111 . If I wait for a $0F header, I receive nothing on serial, but I don't know if I'm correct :
HSerIn [ Wait ($0F) ,rx_buffer[i]]
2) I tried with HRSOUT , but is the same

Quote from: trastikata on Nov 25, 2021, 08:30 AMThe easiest way to verify oscillator is to set a loop with a LED blinking every second - if it doesn't then it is oscillator problem.
Also I don't see how do you setup the USART port?

Good point with oscillator test, I verified ,its ok .
With HSERIN is not necessary to set pin port for USART , only if PIC have a PPS . PORTC.7 is RX , set as default

Quote from: See_Mos on Nov 25, 2021, 10:05 AMSoftware delays before setting OSCCON can cause this

You have a eagle eyes , in lcd.bas I have a delay. I moved the OSCCON setting line and now the start is quicly !


   Now it start quickly, but I have the same problem with oscilating bytes readed . Maybe I have wait for the $0F header ?  I repeat, if I use  HSerIn [ Wait ($0F) ,rx_buffer] I receiving nothing !
   Any sugesstions are welcome !


Regards,
Midali




trastikata

QuoteWith HSERIN is not necessary to set pin port for USART

I meant setting up the port as input etc but I guess Positron does that automatically. 

Another possible problem spotted - your HSerIn timeout is 5ms, message is received every 6ms with a length of 3ms.
For i = 0 To 24   
    HSerIn 5,timeout ,[rx_buffer[i] ]
Next i

You start the program with the loop and there is no sync on the first byte. Otherwise said - how can you guarantee that the loop will begin watching for serial transfer exactly withing the frame when the slave devise is idling?

Even more - your timeout is shorter than the pause between the messages, so if you start looking for incoming message at relative pause time frame, say 0.8ms, then the HSerIn will timeout before the message arrives. Then there is no time to restart the loop within 0.2ms and the entire loop starts at random positions.   

midali

#8
Hi Trastikata,

 I believe the same thing, it's about not sync the first byte , but I don't know how I can solve the issue . For test, I tried a double Hserin , I hoped that I can sync the first reading, but the result is the same :
main:
i = 0
   Do   
       HSerIn 5,timeout ,[rx_buffer[i]]
        i = i +1
    Loop
     
timeout:
    GoSub display
    Do
        HSerIn 5,timeout2 ,[A]
    Loop
timeout2:
                       
GoTo main

In S-Bus protocol, the first byte is allways $0F , may be that is solution for 100% sync , but I don't know how I can use in my programm .

Also I tested with different timeout values , from 3ms to 14 ms and I saw more stability for some bytes, but the code is unusable .

trastikata

100000 baud is quite fast and by the time you verify the preamble, you are already missing bits from the second byte.

For that reason whenever I have to use 115200 baud with a preamble byte, I am writing interrupt driven code instead of the built-in commands.

Here's an example how I set-up hardware interrupt driven USART reception with a preamble and predetermined maximum message length for a GPS receiver at 115200 baud.

On_Interrupt GoTo Isr
GoTo Main

Isr:
    Context Save
   
    If OERR = 1 Then                                'Clear Overrun Error bit
        CREN = 0
        CREN = 1
    EndIf

    If RC1IF = 1 Then                               'Interrupt when a byte is received - USART RX Interrupt flag is set
        TempByte = RCREG1                           'Read the USART buffer to TempByte

        If TempByte = $A Or y > 84 Then             'If the end of message "$A" is found or the message exceeds the InBuffer size of 84 bytes
            If UsartRx = 1 Then                     'If "Ready to receive" bit is set           
                UsartRx = 0                         'Clear "Ready To Receive" Bit
                UsartRxReady = 1                    'And Set "Message Received/Message Read" bit
            EndIf
        EndIf

        If UsartRx = 1 Then                         'If "Ready To Receive" Bit is set
            InBuffer[y] = TempByte                  'Place the value of the received byte in the corresponding InBuffer position
            Inc y                                   'Increment the buffer position
        EndIf

        If TempByte = 36 And UsartRxReady = 0 Then  'If the preamble 36 is found and "Message Received/Message Read" Bit is cleared
            Clear InBuffer : y = 0                  'Reset InBuffer and buffer counter
            InBuffer[y] = TempByte                  'Start refilling the InBuffer
            UsartRx = 1                             'And "Ready to receive" Bit
            Inc y                                   'Increment the buffer counter from the first position
        EndIf

        RC1IF = 0                                   'Clear the USART RX interrupt flag
    EndIf

    Context Restore
   
Main:

'Do something and check frequently enough
'If the entire message has been received
'By verifying "Message Received" Bit
While 1 = 1
    'Loop and do something else
   
    If UsartRxReady = 1 Then
        GoSub DoSomething                           'Message Received and  go doing something with it
    EndIf
Wend   

DoSomething:
    'Do something with the message
     
    UsartRxReady = 0                                'Set the "Message Received/Message Read" bit allowing the interrupt       
Return
   

Basically in the Interrupt routine I start filling a message buffer as soon as a preamble byte is detected and as soon as an "end of message" is detected or the message exceeds the buffer size, I set a flag that the message has been received.

Then in the main program I pool that bit frequently enough (depending on timings) and when I see that the message has been received I do something, then I clear the bit and release the interrupt to reset the buffer and start refilling it. 

Hope this helps

tumbleweed

QuoteData contain 25 bytes transmitted as serial UART , 100000bps, 8E2 (8bit, Even parity, 2 stop bits)
According to the manual, the Hserial_Parity only works with 7E1 or 7O1 settings, where the parity bit is actually kept
as part of the 8-bit UART data. Most 8-bit pics don't have hardware parity support.

That data format (8E2) will require 11 total bit times, which the 8-bit pic hdw UART won't do without a little trickery.
I don't think you can use the built-in HSERIN/OUT routines either... you'll have to manage the UART directly.

To get 8E2 will require setting the pic UART to 9-bit mode, and then use the 9th bit as the parity bit.
You'll have to manage the parity bit manually in your code. You can ignore it on receive,
but you'll have to compute it and set the TXSTA.TX9D bit appropriately before you load the data byte into the TXREG.

The 2 STOP bits won't be a problem on receive (the 2nd one will be ignored), but to transmit "2 STOP bits" you'll have to
add code to the 'send byte' routine to wait at least one additional bit time after you load the TXREG and the TRMT bit
becomes set (about 10us @ 100K baud, but longer won't hurt anything). The extra idle time will look like a STOP bit.

joesaliba

Midali,

Are you aware that SBus signal is inverted?

midali

I am surprised by the kindness and patience of many members of this forum. I'll try what your suggestion .
Thank you all for your help!

midali

Quote from: joesaliba on Nov 26, 2021, 09:40 AMMidali,

Are you aware that SBus signal is inverted?

Inverted ? I don't know that..! Even parity can be inverted ?

This guy explain very well, may be I don't right understand :


joesaliba

This was taken from somewhere else, but if you search you can see that everyone mention that the signal is inverted.

S.Bus uses a standard Serial protocol (even parity, 2 stop bits, 100000 baud), however the polarity is inverted so you must use an external inverter (a transistor, FET, or an optoisolator). The data format is:

SBUS header (0x0F)
16 servos (11 bits each) packed together into 22 bytes.
Final byte showing if failsafe activated, plus channels 17,18
SBUS final byte (0x00)

joesaliba

I remember trying to wait for the header 0x0F received nothing.

But I have seen someone on the internet, when I was trying some years ago and then gave up, using an 18F26K22, like I use, and he was not only able to receive the data, but to inject telemetry using S-Bus2.

I tried converting the code but still had no success.

joesaliba

At 2:40 of the video you mentioned he states that the signal is inverted.

midali

Quote from: joesaliba on Nov 26, 2021, 10:22 AM... you must use an external inverter (a transistor, FET, or an optoisolator)...

Thank you for "small" remark :) . You are right, the signal must be inverted.  I made a inverter with a digital transistor, I verified the waves on osciloscope, now the signal is right.
   Of course, the bad data is further received , I have to test the ones proposed above .

Quote from: joesaliba on Nov 26, 2021, 10:25 AMinject telemetry using S-Bus2.


Very interesting ideea! I found nothing about this . Can you give me a link , please ? Its interesting to reading the subject...

tumbleweed

#18
QuoteS.Bus uses a standard Serial protocol (even parity, 2 stop bits, 100000 baud), however the polarity is inverted so you must use an external inverter (a transistor, FET, or an optoisolator)
If you have to invert the signal, some pics (like the 18F26K22) allow you to invert the TX and RX signals by setting a bit in one of the control registers.
Unfortunately, the 16F1938 only allows you to invert the TX data (BAUDCON.SCKP), so you need to invert the RX signal 'externally'.

If your pic has CLC (configurable Logic Cell) peripherals you can invert the signal using those. Otherwise, for chips like the 16F1938  you can use one of the Comparators to invert the signal. Connect the SBUS RX signal to the comparator input, and connect the comparator output to the UART RX input pin. The comparator output polarity is programmable (CxPOL bit), so it can be inverted in software. If your UART doesn't allow programmable TX polarity you can use this scheme with the TX data as well.


Looking back at my post #10, one thing I should clarify is the transmit STOP bit timing. The UART TRMT signal goes high half-way into the STOP bit being transmitted, so if you wish to generate 2 STOP bits, after TRMT==1 it would be best to wait 2 bit times (20us) before exiting the 'send byte' routine.

If you're only interested in receiving SBUS, then just set the UART format to 9-bit mode (RCSTA.RX9=1) and you can ignore the incoming parity bit which will be in RCSTA.RX9D. You should be able to use the standard HSERIN routines. If you wish to use parity then you'll have to do this manually as you must read RCSTA.RX9D before reading the RCREG to get the data byte.

midali

Very usefull explanations and example ! Tomorrow I'll start try with Trastikata's example and with Tumbleweed suggestions !
  Thank you very much!!