Add GPS/PARSE to 8xSERVO-MOTOR CODE in Oshonsoft

What happens if you extend it programatically?
I.E.
Code:
for i=1 to 20
    str=str&"A"
next i
What does it return for len(str) then?

Not that it matters, I'm just curious.

Mike.
Edit, I don't know how to concatenate strings.
The runtime just seems to ignore going over bounds for strings. It does not overflow to the next storage location. The LEN function returns the maximum length as defined as maximum string length.
So, in your example, with default string length at 16, it fills the string with "A" 16 times and that is all. LEN(str) returns 16. It seems there are some safeguards in place in the runtime code to prevent string boundary overflow.
 
Hi M,
I'm adding the components to the new PCB, unfortunately it's already out of date.

Anyway, here's an image. I will add a wire to CCP2 and any others needed, if you let me know.
Note the blue ones are spare.
EDIT: Am I correct that the GPS TX is connected to CCP2?
C
 

Attachments

  • PCB9.jpg
    441.9 KB · Views: 207
Last edited:
Hi,
finished the new PCB (9) I prgrammed it with the shift register CODe from a while back, for simplicity test.

The SERVOs moved backwards and forwards, , but most of them are jittery, although one (of the same type) seems fine. I haven't checked why yet!
Here's a pic.
EDIT:
There's a master reset RC3.
I use START UPT LEDS for analysis'
+++++++++++++++++++++++++++++++++++
TRISA = %11000000 '7=OSC, 6=OSC,
TRISC = %11110010 '6=1-slave4431_cs, 3=74HC164 MR, 2=74HC164 CLK, 0=74HC164 DATA'<<<<<<<<<<<<
TRISD = %00000000 '7=RLED, 6=YLED
LATC.0 = 0 'ensure data is low
LATC.2 = 0 'and clock
LATC.3 = 1 'Master reset HIGH for run.'<<<<<<<<<<<<<<

'Start up leds
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000
+++++++++++++++++++++++++++++++++
C
 

Attachments

  • REMOTE shift register.JPG
    1.5 MB · Views: 209
Last edited:
Hi M,
I tried the latest UART_GPS CODE and now all servos are moving but wrongly.

I assume you're working on new UART_GPS CODE, is this correct?
Perhaps it may be best to post that, than when done, I can check UART and GPS with detailled tests, with the oscilloscope etc.

C
 
Last edited:
Hi,
Here's a couple of videos of an Oscilloscope view showing the input into 2x SERVOs CH1 and CH2.. CH1 is at the top.

To me the timing looks ok, but it causes jitters on most of my SERVOs, but not 1x SERVO (Don't know why?) Left of video, and top of screen.

2xsettings 1ms and 10ms.

C.
 
Here's a version from the old thread that just does the servos without interrupts.
Code:
'18F4431 32MHz XTL REMOTE_SLAVE 164 389 030223 1200
Define CONFIG1L = 0x00
Define CONFIG1H = 0x06  '8mHz XTL x4 =32mHz
Define CONFIG2L = 0x0c
Define CONFIG2H = 0x20
Define CONFIG3L = 0x04
Define CONFIG3H = 0x80
Define CONFIG4L = 0x80  'Set for HVP
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

Define CLOCK_FREQUENCY = 32

'Define SIMULATION_WAITMS_VALUE = 1  'Comment in for SIM out for PIC

Dim servoCount as byte
Dim i as Word
Dim wordTemp As Word
Dim servoPos(8) as word
Dim frame as word

'need to ensure the 164 is reset at power on.

OSCCON = %01110000  '& h70
TRISA = %11000000   '7=OSC, 6=OSC,
TRISC = %11111010   '6=1-slave4431_cs, 2=74HC164 CLK, 0=74HC164 DATA
LATC.0=0            'ensure data is low
LATC.2=0            'snd clock
For i = 0 to 7
  LATC.2=1                  'send positive clock edge
  servoPos(i)=i*250+2000    '1ms(2000) to 1.875(1 7/8ths - 3750)ms in 1/8th mS steps
  LATC.2=0                  'send negative edge
Next i
TRISC=%11111010            'CCP0 (RC2) & RC0 output
servoCount=8                'cause it to reset
T1CON=%00100000            'prescaler = 4
T1CON.0=1                   'start timer
CCP1CON=%1000              'will go high on interrupt - will start a 0.5mS pulse
frame=1000                  'start everything in 4000 cycles
While 1
  If PIR1.CCP1IF Then       'has CCP1 triggered?
    wordtemp.HB = CCPR1H    'get value of CCPR1 into wordTemp
    wordtemp.LB = CCPR1L
    If CCP1CON = 0x08 Then  'have we started the 1000 cycle pulse
      CCP1CON = 0x09        'yes so end the pulse after 0.5mS
      wordtemp = wordtemp + 1000  'adding 1000 will make pulse 0.5mS long
    Else
      LATC.0=0              'clear the data pin
      CCP1CON = 0x08        'No so output the timed gap
      if servoCount<8 then
        'still doing the servos so add remainder of time
        wordtemp = wordtemp+servoPos(servoCount)
        wordTemp=wordTemp-1000    'knock of the 1000 already elapsed
        frame=frame-servoPos(servoCount)
        servoCount = servoCount + 1
      Else
        'done all the servos so just the frame gap to do
        wordtemp = wordtemp +frame
        frame=39000         '40,000(20mS) minus time of positive pulse(0.5mS)
        servoCount=0        'start all over again
        LATC.0=1            'will become first pulse
      Endif
    Endif
    CCPR1H = wordtemp.HB    'put value back into CCPR1
    CCPR1L = wordtemp.LB
    PIR1.CCP1IF = 0         'clear interrupt flag
  Endif
  'change any servo positions here
Wend
End

I think this worked ok.
Is this glitch free?


Mike.
 

Attachments

  • No_ISR.txt
    2.6 KB · Views: 216
Not trying to confuse things, but going back to the code in post 182 (the last version using interrupts before all the discussion about strings and midstr, etc) I have a few suggestions.

First, there seems to be a missing array index in the main code statement
Code:
            If servoPos < 2000 Then  'have we gone past the end?
I assume that should be:
Code:
            If servoPos(i) < 2000 Then  'have we gone past the end?

Now to the code itself-
Inside the main 'while 1' loop, the elapsed time comparison 'If (ms - tick) > 20' is the only place ms and tick are really used, so instead of declaring these as 'Long' they could could be 'Byte' type instead. That'll simplify the comparison and make it such that the GIE bit doesn't need disabling. If the time was > 255 then you'd need to use a word and so you'd have to add the disable/enable back in.

You should also be able to simplify the main loop by getting rid of all the array accesses and only disabling GIE when absolutely necessary...
Code:
' change Long to Byte since the largest elapased time is '> 20'
Dim ms As Byte
Dim tick As Byte
'add new declarations
Dim Dir as byte
Dim Pos as word

While 1
    'adjust servo positions here
    'INTCON.GIE = 0  'not required if ms and tick are bytes
    If (ms - tick) > 20 Then  '50 Then CHANGE<<<<<<<<<<<<<<<
        tick = ms
        For i = 0 To 7
            Dir = servoDir(i)
            Pos = servoPos(i)
          
            If Dir = 1 Then
                Pos = Pos + 40  'add 1/50th to the servo position
                If Pos > 4000 Then  'same for other end
                    Pos = 4000
                    Dir = 0
                Endif
            Else
                Pos = Pos - 40  'subtract it
                If Pos < 2000 Then  'have we gone past the end?
                    Pos = 2000  'yes so make it the end stop
                    Dir = 1  'and turn it around
                Endif
            Endif
            ' update global data
            servoDir(i) = Dir
            INTCON.GIE = 0
            servoPos(i) = Pos
            INTCON.GIE = 1
        Next i
    Endif
    'INTCON.GIE = 1         ' not required
    'other code here.
Wend

You might also consider moving the PIR2.CCP2IF, PIR1.TMR2IF, and PIR1.RCIF interrupts to the low-priority ISR so that the only high-priority intr is PIR1.CCP1IF.

I don't know what 'Save System' does in this language, so it's hard to say if that's really needed or not for the CCP1IF code. In other compilers that can be an expensive statement. I'd need to see an .asm or .lst output to tell.
 
Hi, M and T,
It's easy to get confused with this, as there are lots of small changes along the way, so I'm reluctant to go back to earlier CODE examples.
Having said that I did try the 'old' PCB8 https://www.electro-tech-online.com...c-oshonsoft-basic.163337/page-26#post-1433722, which is before PCB9 that has the 'shift register' on.
The only times, I get smooth SERVO running is with 2x of my box of SERVOs, while all of the rest jitter.

The latest CODE on the latest PCB9 gives the same results (2x smooth SERVOS), so I think we ought to go from here.
If I put a brake (shock absorb) on the jittering SERVOs, they move smoothly, but in steps, that I think is causing a kind of backlash?
Here is the latest CODE (I'm a bit lost what changes have been made):
The <<<<<<<<<<<<<, point to changes, but I now can't remember what they were?
Code:
'18F4431 32MHz XTL PCB9 REMOTE_SLAVE GPS_UART 040423 1700

Define CONFIG1L = 0x00
Define CONFIG1H = 0x06  '8mHz XTL x4 =32mHz
Define CONFIG2L = 0x0c
Define CONFIG2H = 0x20
Define CONFIG3L = 0x04
Define CONFIG3H = 0x80
Define CONFIG4L = 0x80  'Set for HVP
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

Define CLOCK_FREQUENCY = 32
Define SINGLE_DECIMAL_PLACES = 2
Define STRING_MAX_LENGTH = 20

'Define SIMULATION_WAITMS_VALUE = 1  'Comment in for SIM out for PIC

Dim wordTemp As Word
Dim ServoCount As Byte
Dim i As Word
Dim ServoPos(8) As Word
Dim servoDir(8) As Byte
Dim frame As Word
Dim Buff(80) As Byte
Dim strCount As Byte
Dim rec As Byte
Dim isDone As Bit
Symbol rled = PORTD.7

OSCCON = %01110000  '& h70
TRISA = %11000000  '7=OSC, 6=OSC,
TRISC = %11110010  '6=1-slave4431_cs, 3=74HC164 MR, 2=74HC164 CLK, 0=74HC164 DATA'<<<<<<<<<<<<
TRISD = %00000000
LATC.0 = 0  'ensure data is low
LATC.2 = 0  'and clock
LATC.3 = 1  'Master reset HIGH for run.'<<<<<<<<<<<<<<

'Start up led
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000

For i = 0 To 7
    LATC.2 = 1  'send positive clock edge
    ServoPos(i) = i * 250 + 2000  '1ms(2000) to 1.875(1 7/8ths - 3750)ms in 1/8th mS steps
    LATC.2 = 0  'send negative edge
    servoDir(i) = i And 1  '<<<<<<<added
Next i
'TRISC = %11111010  'CCP0 (RC2) & RC0 output'<<<<<<<<<<<<<<<<<<<<
ServoCount = 8  'cause it to reset
T1CON = %00100000  'prescaler = 4
T1CON.0 = 1  'start timer
CCP1CON = %1000  'will go high on interrupt - will start a 0.5mS pulse
frame = 1000  'start everything in 4000 cycles
strCount = 0
isDone = 0
'setup USART for 9600 baud receive
RCSTA = %10010000
TXSTA.BRGH = 1
BAUDCON.BRG16 = 1
SPBRG = 207
PIR1.RCIF = 0
PIE1.RCIE = 1
PIE1.CCP1IE = 1
INTCON.PEIE = 1
INTCON.GIE = 1

While 1
    While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend
    For i = 0 To 7
        INTCON.GIE = 0
        If servoDir(i) = 1 Then
            ServoPos(i) = ServoPos(i) + 40  'add 1/50th to the servo position
        Else
            ServoPos(i) = ServoPos(i) - 40  'subtract it
        Endif
        If ServoPos < 2000 Then  'have we gone past the end?
            ServoPos(i) = 2000  'yes so make it the end stop
            servoDir(i) = 1  'and turn it around
        Endif
        If ServoPos(i) > 4000 Then  'same for other end
            ServoPos(i) = 4000
            servoDir(i) = 0
        Endif
        INTCON.GIE = 1
    Next i
    'adjust servo positions here
Wend

End                                            

On High Interrupt  'go via location 0x0008
Save System

If PIR1.CCP1IF Then  'has CCP1 triggered?
    wordTemp.HB = CCPR1H  'get value of CCPR1 into wordTemp
    wordTemp.LB = CCPR1L
    If CCP1CON = 0x08 Then  'have we started the 1000 cycle pulse
        CCP1CON = 0x09  'yes so end the pulse after 0.5mS
        wordTemp = wordTemp + 1000  'adding 1000 will make pulse 0.5mS long
    Else
        LATC.0 = 0  'clear the data pin
        CCP1CON = 0x08  'No so output the timed gap
        If ServoCount < 8 Then
            'still doing the servos so add remainder of time
            wordTemp = wordTemp + ServoPos(ServoCount)
            wordTemp = wordTemp - 1000  'knock of the 1000 (0.5mS) already elapsed
            frame = frame - ServoPos(ServoCount)
            ServoCount = ServoCount + 1
        Else
            'done all the servos so just the frame gap to do
            wordTemp = wordTemp + frame
            frame = 39000  '40,000(20mS) minus time of positive pulse(0.5mS)
            ServoCount = 0  'start all over again
            LATC.0 = 1  'will become first pulse
        Endif
    Endif
    CCPR1H = wordTemp.HB  'put value back into CCPR1
    CCPR1L = wordTemp.LB
    PIR1.CCP1IF = 0  'clear interrupt flag
Endif

If PIR1.RCIF Then
    rec = RCREG  'get the received character
    If RCSTA.OERR Or RCSTA.FERR Then  'neither of these should ever occur.
        RCSTA.CREN = 0  'this is kinda wishful thinking
        If PIR1.RCIF Then
            rec = RCREG
        Endif
        RCSTA.CREN = 1  'as any data received is corrupt
        strCount = 0  'however, reset everything
        isDone = 0  'and hope for the best
    Else  'no errors so use the data
        If strCount = 0 And isDone = 0 Then  'are we already receiving
            'waiting for $                'no so wait
            If rec = "$" Then  'for $ to appear'<<<<<<<<<<<<<<<<<<<<<<<<
                Buff(strCount) = rec  'start receiving
                strCount = strCount + 1
            Endif
        Else
            If isDone = 0 And strCount < 79 Then  'have we collected a full string?
                Buff(strCount) = rec  'no so carry on storing
                strCount = strCount + 1
                If rec = "W" Then  'have we got the "endOfString" character'<<<<<<<<<<<<
                    isDone = 1  'yes, so set done true
                    'put break here
                Endif
            Else
                If isDone = 0 Then  '?????????<<<<<<<<<<<<<<<<<<<<<<<
                    'still waiting to start or buffer overflow
                    strCount = 0
                Endif
            Endif
        Endif  '<<<<<<<<<<<<<<<
    Endif
Endif  'end RS232 if
Resume

The oscilloscope readings I posted, kind of confuse me! I can see the on off times changing but as the screen is narrow, I don't see the full picture.
To me the FRAME looks like it's longer than the recommended 20ms. (I think I recall that this doesn't matter, but perhaps it should be 20ms for testing)

Tomorrow, I'll try some digital analyser tests, which log the output numbers, which may make things clearer?

Note: I haven't considered #211 yet!
EDIT: T, I had a quick look at the latest CODE, and
[ If servoPos(i) < 2000 Then 'have we gone past ] applies here too. Let's see what M thinks.

Regarding SAVE SYSTEM, this saves the system when returning from and INTERRUPT, so it carries on correctly.
C
 
Last edited:
Code:
If ServoPos < 2000 Then
That's just flat out wrong. It needs to have the 'ServoPos(i)' to use the correct index.

As far as SAVE SYSTEM, I know that it's associated with interrupt context. I just don't know exactly what it saves in this language.
 
Hi T,
I don't disagree, regarding 'ServoPos(i)', but I don't tend to change other peoples CODE without them agreeing.
C.
 
Here's a version with Tumbleweeds corrections and suggestion,
Code:
'18F4431 32MHz XTL PCB9 REMOTE_SLAVE GPS_UART 040423 1700

Define CONFIG1L = 0x00
Define CONFIG1H = 0x06  '8mHz XTL x4 =32mHz
Define CONFIG2L = 0x0c
Define CONFIG2H = 0x20
Define CONFIG3L = 0x04
Define CONFIG3H = 0x80
Define CONFIG4L = 0x80  'Set for HVP
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

Define CLOCK_FREQUENCY = 32
Define SINGLE_DECIMAL_PLACES = 2
Define STRING_MAX_LENGTH = 20

'Define SIMULATION_WAITMS_VALUE = 1  'Comment in for SIM out for PIC

Dim wordTemp As Word
Dim ServoCount As Byte
Dim i As Word
Dim ServoPos(8) As Word
Dim servoDir(8) As Byte
Dim frame As Word
Dim Buff(80) As Byte
Dim strCount As Byte
Dim rec As Byte
Dim isDone As Bit

Dim pos as word
Dim dir as byte

Symbol rled = PORTD.7

OSCCON = %01110000  '& h70
TRISA = %11000000  '7=OSC, 6=OSC,
TRISC = %11110010  '6=1-slave4431_cs, 3=74HC164 MR, 2=74HC164 CLK, 0=74HC164 DATA'<<<<<<<<<<<<
TRISD = %00000000
LATC.0 = 0  'ensure data is low
LATC.2 = 0  'and clock
LATC.3 = 1  'Master reset HIGH for run.'<<<<<<<<<<<<<<

For i = 0 To 7
    LATC.2 = 1              'send positive clock edge
    ServoPos(i) = i * 250 + 2000  '1ms(2000) to 1.875(1 7/8ths - 3750)ms in 1/8th mS steps
    LATC.2 = 0              'send negative edge
    servoDir(i) = i And 1  '<<<<<<<added
Next i

'Start up led
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000

'TRISC = %11111010  'CCP0 (RC2) & RC0 output'<<<<<<<<<<<<<<<<<<<<
ServoCount = 8  'cause it to reset
T1CON = %00100000  'prescaler = 4
T1CON.0 = 1  'start timer
CCP1CON = %1000  'will go high on interrupt - will start a 0.5mS pulse
frame = 1000  'start everything in 4000 cycles
strCount = 0
isDone = 0
'setup USART for 9600 baud receive
RCSTA = %10010000
TXSTA.BRGH = 1
BAUDCON.BRG16 = 1
SPBRG = 207
PIR1.RCIF = 0
PIE1.RCIE = 1
PIE1.CCP1IE = 1
INTCON.PEIE = 1
INTCON.GIE = 1
While 1
    While ServoCount = 0          'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend
    For i = 0 To 7
    dir=servoDir(i)                'doesn't matter if ISR happens as servoPos isn't changed by ISR
    pos=servoPos(i)
        If dir = 1 Then
            pos = pos + 40        'add 1/50th to the servo position
        Else
            pos = pos - 40     'subtract it
        Endif
        If pos < 2000 Then     'have we gone past the end?
            pos = 2000          'yes so make it the end stop
            dir = 1              'and turn it around
        Endif
        If pos > 4000 Then      'same for other end
            pos = 4000
            dir = 0
        Endif
        INTCON.GIE = 0
        servoPos(i)=pos
        INTCON.GIE = 1
        servoDir(i)=dir            'servoDir not used by ISR
    Next i
Wend

End                                            

On High Interrupt              'go via location 0x0008
Save System

If PIR1.CCP1IF Then          'has CCP1 triggered?
    wordTemp.HB = CCPR1H      'get value of CCPR1 into wordTemp
    wordTemp.LB = CCPR1L
    If CCP1CON = 0x08 Then 'have we started the 1000 cycle pulse
        CCP1CON = 0x09      'yes so end the pulse after 0.5mS
        wordTemp = wordTemp + 1000     'adding 1000 will make pulse 0.5mS long
    Else
        LATC.0 = 0              'clear the data pin
        CCP1CON = 0x08      'No so output the timed gap
        If ServoCount < 8 Then
            'still doing the servos so add remainder of time
            wordTemp = wordTemp + ServoPos(ServoCount)
            wordTemp = wordTemp - 1000  'knock of the 1000 (0.5mS) already elapsed
            frame = frame - ServoPos(ServoCount)
            ServoCount = ServoCount + 1
        Else
            'done all the servos so just the frame gap to do
            wordTemp = wordTemp + frame
            frame = 39000                  '40,000(20mS) minus time of positive pulse(0.5mS)
            ServoCount = 0                  'start all over again
            LATC.0 = 1                      'will become first pulse (is data pin of shift register)
        Endif
    Endif
    CCPR1H = wordTemp.HB                      'put value back into CCPR1
    CCPR1L = wordTemp.LB
    PIR1.CCP1IF = 0                          'clear interrupt flag
Endif

If PIR1.RCIF Then
    rec = RCREG  'get the received character
    If RCSTA.OERR Or RCSTA.FERR Then      'neither of these should ever occur.
        RCSTA.CREN = 0                      'this is kinda wishful thinking
        If PIR1.RCIF Then
            rec = RCREG
        Endif
        RCSTA.CREN = 1                  'as any data received is corrupt
        strCount = 0                      'however, reset everything
        isDone = 0                          'and hope for the best
    Else                                      'no errors so use the data
        If strCount = 0 And isDone = 0 Then  'are we already receiving
            'waiting for $           'no so wait
            If rec = "$" Then          'for $ to appear'<<<<<<<<<<<<<<<<<<<<<<<<
                Buff(strCount)=rec    'start receiving
                strCount = strCount + 1
            Endif
        Else
            If isDone = 0 And strCount < 79 Then  'have we collected a full string?
                Buff(strCount)=rec    'no so carry on storing
                strCount = strCount + 1
                If rec = "W" Then      'have we got the "endOfString" character'<<<<<<<<<<<<
                    isDone = 1          'yes, so set done true
                    'put break here
                Endif
            Else
                If isDone = 0 Then  '?????????<<<<<<<<<<<<<<<<<<<<<<<
                    'still waiting to start or buffer overflow
                    strCount = 0
                Endif
            Endif
        Endif  '<<<<<<<<<<<<<<<
    Endif
Endif  'end RS232 if
Resume

Note, it doesn't use ms or wait as it uses the servo control to time 20mS.

I also moved the for...next loop above the LED code so the shift register is immediately cleared. Didn't want random outputs for 4 seconds.

Mike.
 

Attachments

  • code 040423 1700.txt
    5.5 KB · Views: 214
I've just noticed,
LATC.3 = 1 'Master reset HIGH for run.'<<<<<<<<<<<<<<
to reset the shift register.
However, I can't see where it is cleared again.

Mike.
 
I've just noticed,
LATC.3 = 1 'Master reset HIGH for run.'<<<<<<<<<<<<<<
to reset the shift register.
However, I can't see where it is cleared again.

Mike.
Hi M,
I added LATC.3 to PCB9 as I had a spare PIC pin, but as the shift register is ON all of the time, I just left it HIGH=running.
It can be changed any time.
C
 
Hi M and T,
Smooooooth
C.
 
Does that mean it's glitchless?

Mike.
Hi M,
EDIT: I wrote that there are minor glitches, but I'm not sure now? Unless you can see why any glitches would be caused, perhaps wait till I've tested a bit more?

Here's a 5xServo video, with the oscilloscope set to 1ms, and looks as I hoped it would.
Well done.

C
 
Last edited:
Cookies are required to use this site. You must accept them to continue using the site. Learn more…