PDA

View Full Version : saving RCREG to word



Macgman2000
- 14th September 2008, 17:42
Hello,

I would appreciate your help and need a fresh pair of eyes on the issue I have.
I currently have a byte that is saved every time the UART interrupts with RCREG data ready. This only allows me at 4800bps 2ms between received bytes.

The main section of the code takes two consecutive bytes and merges them into a word in basic. Then drops through to send out a 1 ~ 2ms pulse width proportional to servo position.
The servo movement is a bit coarse which tells me that in the time I am sitting in the pauseus timing the pulse width, that I am dropping received UART bytes. This is because the part of the code that merges the bytes never runs since it iterrupts every 2ms in the middle of a pausus command.

If I handle the byte to word merging in the interrupt it will give me about 4ms of time to take care of servicing the servo (2ms max needed).
I would like to handle the merging of the two consecutive bytes in the interrupt assembly section to minimize PBP overhead. How can I do this with the least amount of clock cycles? see below snippet.







' Assembly language UART INTERRUPT handler
Asm
myint

; Uncomment the following if the device has less than 2k of code space
; movwf wsave ; Save W
; swapf STATUS, W ; Swap STATUS to W (swap avoids changing STATUS)
; clrf STATUS ; Clear STATUS
; movwf ssave ; Save swapped STATUS
; movf PCLATH, W ; Move PCLATH to W
; movwf psave ; Save PCLATH

; Save the FSR value for later
movf FSR, W ; Move FSR to W
movwf fsave ; Save FSR


; Read and store the character from the USART
movf RCREG, W ; Read the received character
movwf _tempbyte ; contains new UART received byte
incf _Btcounter,F ; passed to main to track 2 consecutive bytes





; Restore FSR, PCLATH, STATUS and W registers
finished
movf fsave, W ; retrieve FSR value
movwf FSR ; Restore it to FSR
movf psave, W ; Retrieve PCLATH value
movwf PCLATH ; Restore it to PCLATH
swapf ssave, W ; Retrieve the swapped STATUS value (swap to avoid changing STATUS)
movwf STATUS ; Restore it to STATUS
swapf wsave, F ; Swap the stored W value
swapf wsave, W ; Restore it to W (swap to avoid changing STATUS)
retfie ; Return from the interrupt



EndAsm

Darrel Taylor
- 15th September 2008, 00:40
It's more like 6ms.

There's the 2-byte buffer, and you can receive all but the last bit of the 3rd byte before it overflows. But in order to get that time, you'll have to disable the RCIE bit. Otherwise, since you are using ASM interrupts, it'll trigger the handler and interrupt the Pulsout timing on every byte received.

But anyway, the servo pulses should only be sent every 20ms (50hz). If you have continuous data coming in, and you're sending a pulse every time you get a new word ... the servo's not going to like it.
<br>

Macgman2000
- 15th September 2008, 01:58
Darrel,

thanks for confirming the ~ 3bytes buffer. I guess I need to change the part of the code that is in main and move it to the interrupt.

Please tell me if this is possible or if I completely missed it. I want to create a 4 word buffer, each word holds a manchester encoded servo position. Every interrupt will pack a byte into the high portion of the word and the next interrupt packs the lower byte of the word. It does this for the remaining 3 words. In main, the code looks at the byte number index and determines which servo to update out of the 4 servos.

The idea is to keep the bytes streaming in up to date even when in the pauseus pulse output portion of the code.

Can I in the inline assembly specify moving an 8bit register into the "high" or "low" byte of a variable? Like movf servo1.L, RCREG and movf servo1.H,RCREG ?

Thanks!

Darrel Taylor
- 15th September 2008, 02:23
... Can I in the inline assembly specify moving an 8bit register into the "high" or "low" byte of a variable? Like movf servo1.L, RCREG and movf servo1.H ...

No problem,

Let's say you have a WORD variable in BANK0 ... (It should be in BANK0).

TempWord VAR WORD BANK0

in ASM, the LowByte comes first, so that's at the address of the variable itself.
&nbsp; &nbsp; movwf &nbsp; &nbsp; _TempWord

The HighByte is 1 address more
&nbsp; &nbsp; movwf &nbsp; &nbsp; _TempWord + 1

BrianT
- 15th September 2008, 03:09
Not sure I understand what you are doing but I found for an RC aircraft/UAV that 256 servo positions gives plenty of resolution and very smooth flight. By pulling the servo data down to a byte variable I have heaps of time to send 8 channels of servo data every frame time for very smooth servo operation.

I have one PIC16F88 decoding the incoming radio PPM stream and building a fast packet of 8 single byte servo positions (plus start and checksum characters) at 115600 bps using Debug.

The radio decoder measures the width of the 8 incoming channel pulses. It has the 4 - 12 mSec gap at the end of the frame in which to send a fast packet. At 115600 bps this takes about 2100 uSecs to send. This is fast enough to complete before the 16F88 has to find the next incoming data frame.

This fast packet is massaged by a PIC16F887 where height hold, GPS and stability corrections are applied and a new past packet is built and sent to several 'servopods' that drive 1, 2 or 4 servos. The servopod reads the incoming packet and uses the new data if the checksum is good. Otherwise it uses last known good data for that channel.

It all runs under PBP and needs no assembler accept for setting the WDT.




Data @0,$0D, $0A, "P4 Servo ver 1.05 BDT 21/03/2006",$0D, $0A
data @40, 0, 0 'error counter
'************************************************* ***************
'* Name : P4_Servo_V105.BAS *
'* Author : Brian Taylor *
'* Notice : Copyright (c) 2006 Brian Taylor *
'* : All Rights Reserved *
'* Date : 21/03/2006 *
'* Version : 1.05 *
'* Notes : *
'* : *
'************************************************* ***************
' This is P4 - 16F88 - the servo driver
' test code to debug the hardware
'
'Background.
' A 'standard' JR, Futaba, HS, etc RC system uses on a nominal 50 Hz
' refresh rate to the servos. This gives a nominal 20 mSec frame time.
' Servos centre at 1.5 mSec and range from 1 to 2 mSec at the end stops.
' Bitter experience shows digital servos burn out outside 1-2 mS values.
' 8 channels all at maximum, highly unlikely, takes 8 x 2 mSec leaving
' 4 mSec for sync, etc. 8 channels all at minimum width, also highly
' unlikely, takes 8 mS leaving 12 mSec for sync, processing, etc.
' An 'average' frame has 8 channels at 1.5 mSec leaving 8 mSec for playtime.
' This code gets a 'fast packet' from the leveller PIC in about 2.1 mSec
' and drives all 8 servos regardless of whether they exist in hardware.
'
DEFINE LOADER_USED 1
DEFINE OSC 20
define no_clrwdt 1
define loader_used 1
' Set Debugin pin port
DEFINE DEBUGIN_REG PORTB
DEFINE DEBUG_BAUD 115200 '76800
' Set Debugin pin bit
DEFINE DEBUGIN_BIT 7
' Set Debugin mode: 0 = true, 1 = inverted
DEFINE DEBUGIN_MODE 1

'@ Device pic16F88, HS_OSC, BOD_OFF, PWRT_ON, WDT_ON, PROTECT_OFF

'************************** Hardware assignment *************************
'Port A all digital
Svo8 var porta.0 'p 17 digital drive to servo 8 digital out
Svo7 var porta.1 'p 18 servo 7 digital out
Svo6 var porta.2 'p 1 digital drive to servo 6 digital out
Svo5 var porta.3 'p 2 digital drive to servo 5 digital out
Svo4 var porta.4 'p 3 digital drive to servo 4 digital out

TRISA = %11100000
ANSEL = %00000000
CMCON = %00000111

'Port B
Svo3 var portb.0 'pin 6 servo 3 drive out
Svo2 var portb.1 'pin 7 servo 2 out
RxD var portb.2 'pin 8 BootLoader input in
Svo1 var portb.3 'pin 9 Servo 1 drive out
P4LED var portb.4 'pin 10 Diagnostic LED out
TxD var portb.5 'pin 11 BootLoader output - diagnostic out out
P3_P4 var portb.6 'p 12 Data in from P3 in
P2_P4 var portb.7 'p 13 Servo packet data from P2 in
TRISB = %11000100

'****************************** Software assignment **************************
A var byte
B var byte
C var byte
W var word
InCtr var byte
Ch var byte[8] ' the current channel data
LastCh var byte[8] ' last clean channel data
FailCh var byte[8] ' failsafe values invoked after N bad packets
Delta var byte ' maximum allowable servo step
CheckSum var byte
ErrorCtr var word ' counts checksum errors in servo data from P2

StepLimit con 8 ' max allowed servo change per frame
' damps out violent spikes

'--------------------------------- Initialise ------------------------
Initialise:

ShowRevision:
serout txd, 2, [$0D, $0A]
for a = 0 to 79
read a,b
serout txd, 2, [b]
clearwdt
next a
serout txd, 2, [$0D, $0A]

' WDTCON = %00001001 ' invoke s/w WDT, 58 mSec timeout, 110 mSec recovery
WDTCON = %00001010 ' longer WDT activate period
OPTION_REG = %10001010 ' No wpu, prescaler assigned to WDT & /4
' OPTION_REG xxxx00 gives 15 mS, 10 gives 64 mS
INTCON = %00000000 ' disable all interrupts
ANSEL = %00000000 ' all digital
CMCON = %00000111 ' Comparators OFF to allow inputs on pins
ADCON0 = %00000000
ADCON1 = %00000000

SetInitialServoPosition:
Ch[0] = 0
Ch[1] = 125
Ch[2] = 125
Ch[3] = 125
Ch[4] = 125
Ch[5] = 125
Ch[6] = 125
Ch[7] = 125

high p4led ' fires if WDT is invoked during preflight ground check.

Start:

GetServoData:
debugin 90, failsafe, [a, ch[0], ch[1], ch[2], ch[3], ch[4], ch[5], _
ch[6], ch[7], checksum]
'A = throw away sync byte - often corrupted
c = 0 ' checksum comparison
for b = 0 to 7 ' build local checksum
c = c + ch[b]
next b
if checksum = c then
goto goodpacket
endif


Failsafe: ' Use last clean packet if internal processor to processor
' error occurs
high p4led
for a = 0 to 7
ch[a] = lastch[a]
next a

GoodPacket:

DriveServos:
' PULSOUT gives 2 uS steps at 20 MHz.
' The servos need 1000 to 2000 uS at 50 Hz
' MUST MUST MUST define initial states as Pulsout is a toggle.
svo1 = 0
svo2 = 0
svo3 = 0
svo4 = 0
svo5 = 0
svo6 = 0
svo7 = 0
svo8 = 0

clearwdt

w = 500 + 2*ch[0] ' W is in 2 uS steps = 1000 to 2000 uS
pulsout svo1, w
w = 500 + 2*ch[1]
pulsout svo2, w
w = 500 + 2*ch[2]
pulsout svo3, w
w = 500 + 2*ch[3]
pulsout svo4, w
w = 500 + 2*ch[4]
pulsout svo5, w
w = 500 + 2*ch[5]
pulsout svo6, w
w = 500 + 2*ch[6]
pulsout svo7, w
w = 500 + 2*ch[7]
pulsout svo8, w

SaveLastValues:
for a = 0 to 7
lastch[a] = ch[a]
next a

low p4led
clearwdt
goto start 'getservodata



HTH
BrianT

Macgman2000
- 15th September 2008, 03:47
Darrel, thanks for the explanation. it makes sense.

Brian, I approached the RX code a bit differently. For example I use a manchester decoder that tests 01 or 10 transition if there is a 11 or 00 bit it throws out the packet. So I don't have a checksum. I also have framing error and over run detection enabled for the UART. I am streaming out data at 4800bps.

A modification since I am using a 900Mhz FSK radio, it does not need to have a balanced data stream so I can forgo the manchester. Instead I will send a word, high byte is true data and low byte is inverted data. The decoder then looks for identical payload once inverted between high and low byte, if not throw it out. All I am really controlling is 4 servos and 2 hardware PWM outputs which takes very little overhead since it is hardware unlike servos each 1 ~ 2ms.

I noticed you used pulseout instead of portb.0=1 pauseus (4*pos+500) portb.0=0. Does the pulseout save in code execution time?

BrianT
- 15th September 2008, 11:04
Hi Magman,
I did not optimise the code since I had plenty of time in the way I did it. It may turn out that your pauseus approach may save space and time. If I needed code space or clock cycles I would have checked that method out.

I reckon a single checksum across all channels uses less time and code than the 100% redundancy approach you are taking with the true/inverted signalling but I agree yours is a safe and simple approach. If I get a bad packet or a bad channel within a packet (out of range or delta too big) I use the last good value for that channel. Given that real data does not jump much from frame to frame this method seems to fly quite well.

My uplinks are 36 and 2400 MHz using the transmitter in PPM.

What are you building. Is it an aircraft?

Cheers
BrianT

Macgman2000
- 15th September 2008, 13:51
Looking over your code closely, I realized something. Do you pause in the TX output for 16 ~ 20ms before starting up your next frame?

I am not doing that, it may be why I am seeing the results I am seeing. using a continuous back to back output from the TX @ 4800bps I am able to get 4 servo + 2 hardware PWM control. BTW I figured out why my servo is a bit coarse. in your code you multiply with a 2 constant. I am not moving in 2us increments. I am doing the following: 2* (4*byte + 500), So for every crank on the potentiometer I am moving in chunks of 8us, so I am not loosing packets, its my scaling factor....ggeeeezzz.


yes, it is an aircraft. Actually VTOL.