0
Multi-channel servo controller
So here's the first working version of multi-channel, USB (and serial) driven servo controller. It's by no means complete, and for some reason, my servos only move 45 degrees either side of the zero point but the theory behind it seems to work.
Here's what happens, in a nutshell:
Unlike other multiple servo controllers, this one does not activate the servo pins in sequence (which, incidentally on a 20Mhz crystal limits you to 8 servos, maximum).
This controller switches on all servo pins at the same time, and uses timer1 to decide when each servo pin should be switched off.
Since all pins are on for the first 1ms, and are turned off during the second millisecond, this keeps 3-20ms (9/10ths) of the processing time free for data processing and other tasks (give or take a few nanoseconds for some basic time-keeping checks).
Using the same layout as my earlier USB project you can get some results quite quickly. Set your board out according to the schematic and drop the code (below) onto the PIC18F2455.
Attaching your servo signal wire to pin RB0 should move the servo to the zero position. RB1 should move the servo to one extreme, pin RB2 to the other. (in fact, if you look at the code, you should see that pins RB3-7 also move the servo to the same angle as RB1.
I'm still working on fine-tuning the code and experimenting with other servos.
I'm using a Hextronic HXT900 servo which doesn't appear to move through the full 180 degrees. The half-way signal does appear to put the servo into the centre position. However, when sending signals to move the servo to its extremes, it only moves 45 degrees in each direction, rather than the full 90.
I'm not sure whether this is a software issue, or whether it's particular to this servo model. Results of any further investigations will be posted here in time!
Oshonsoft PICBasic listing for 18F2445
Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x3e
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x81
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40
'PORTA.0 is feature LED
'PORTA.1 is i/o LED
'these can be removed if necessary
declarations:
Dim ax As Byte
Dim i As Byte
Dim j As Byte
Dim t As Byte
Dim svh(16) As Byte
Dim svl(16) As Byte
usbcrap:
UsbSetVendorId 0x1220
UsbSetProductId 0x1234
UsbSetVersionNumber 0x1122
UsbSetManufacturerString "www.nerdclub.co.uk"
UsbSetProductString "Multiple servo USB controller"
UsbSetSerialNumberString "1111111111"
UsbOnIoInGosub usbonioin
UsbOnIoOutGosub usbonioout
UsbOnFtInGosub usbonftin
UsbOnFtOutGosub usbonftout
init:
AllDigital
Config PORTA = Output
Config PORTB = Output
T1CON = 0 'set all bits to zero
startusb:
PORTA.0 = 1 'turn on an output so we can see the pic is running
UsbStart
PORTA.0 = 0 'turn off output once USB has set up
startup:
'set the value for timer1 to count up to
'(count to 5000 = 1ms: highbyte=5000/256, lowbyte=5000 mod 256)
CCPR1H = 19
CCPR1L = 136
'to allow a 0-100% range, sv(0-15) can take the value 0-100
'(we multiply these up by 50 as the values are received, to give
'a range of 0-5000, which is 0ms to 1ms).
'these are some example values to test the middle and extreme
'ranges of the servo controller on pins 0,1,2
svh(0) = 9
svl(0) = 196
svh(1) = 0
svl(1) = 0
svh(2) = 19
svl(2) = 136
'1011 = special event interrupt, clear timer etc.
CCP1CON.CCP1M3 = 1
CCP1CON.CCP1M2 = 0
CCP1CON.CCP1M1 = 1
CCP1CON.CCP1M0 = 1
'PIR1.CCP1IF = 0 'clear any "bogus" reading
T1CON.TMR1ON = 1 'enable timer1
t = 0
loop:
If t = 1 Then
'turn on all servo outputs for 1ms
PORTB = 0xff
PORTA.0 = 1 'led to show timer is running
Else
If t = 2 Then
'this is where outputs get turned off. Because there may
'be a delay in processing this loop, we compare the actual
'value held in Timer2 and if it's greater than our servo-off
'value (0-100% multipled by 50) then turn that pin off
If TMR1H > svh(0) Then PORTB.0 = 0
If TMR1H = svh(0) And TMR1L >= svl(0) Then PORTB.0 = 0
If TMR1H > svh(1) Then PORTB.1 = 0
If TMR1H = svh(1) And TMR1L >= svl(1) Then PORTB.1 = 0
If TMR1H > svh(2) Then PORTB.2 = 0
If TMR1H = svh(2) And TMR1L >= svl(2) Then PORTB.2 = 0
Else
'turn off the servo outputs for the duration 3ms to 14ms (and 0ms-1ms)
PORTB = 0x00
PORTA.0 = 0 'turn off timer led
'service the USB port for data
UsbService
'check to see if we've had two bytes of serial data
If i = 0 Then
'Hserget i
Else
If j = 0 Then
'Hserget j
Else
'parse the two byte instruction and update the servo-off
'values as necessary, then reset i and j to zero
i = 0
j = 0
Endif
Endif
Endif
Endif
'check for CCP1 interrupt flag (even if interrupts are not
'on, you can poll this flag to see if an event has occurred)
'this really needs to be in an interrupt routine so that the timer
'is kept as up-to-date as possible
If PIR1.CCP1IF = 1 Then
t = t + 1
PIR1.CCP1IF = 0
If t > 14 Then
t = 0
'reset the timer to exactly zero
'(so if there's been a delay switching on, we're back in sync
'when it comes to switching the signal off, otherwise there
'might be a bit of judder as the time period between on and off
'may differ by a few thousandths of a millisecond)
TMR1L = 0
Endif
Endif
Goto loop
End
usbonftout:
Toggle PORTA.0
Return
usbonftin:
'we've received some data in the feature report
Return
usbonioout:
Toggle PORTA.1
Return
usbonioin:
'we've received some data on the data i/o, so
'update the necessary servo high and low byte values
'to get the selected servo to move to its new position
Return
Here's what happens, in a nutshell:
Unlike other multiple servo controllers, this one does not activate the servo pins in sequence (which, incidentally on a 20Mhz crystal limits you to 8 servos, maximum).
This controller switches on all servo pins at the same time, and uses timer1 to decide when each servo pin should be switched off.
Since all pins are on for the first 1ms, and are turned off during the second millisecond, this keeps 3-20ms (9/10ths) of the processing time free for data processing and other tasks (give or take a few nanoseconds for some basic time-keeping checks).
Using the same layout as my earlier USB project you can get some results quite quickly. Set your board out according to the schematic and drop the code (below) onto the PIC18F2455.
Attaching your servo signal wire to pin RB0 should move the servo to the zero position. RB1 should move the servo to one extreme, pin RB2 to the other. (in fact, if you look at the code, you should see that pins RB3-7 also move the servo to the same angle as RB1.
I'm still working on fine-tuning the code and experimenting with other servos.
I'm using a Hextronic HXT900 servo which doesn't appear to move through the full 180 degrees. The half-way signal does appear to put the servo into the centre position. However, when sending signals to move the servo to its extremes, it only moves 45 degrees in each direction, rather than the full 90.
I'm not sure whether this is a software issue, or whether it's particular to this servo model. Results of any further investigations will be posted here in time!
Oshonsoft PICBasic listing for 18F2445
Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x3e
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x81
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40
'PORTA.0 is feature LED
'PORTA.1 is i/o LED
'these can be removed if necessary
declarations:
Dim ax As Byte
Dim i As Byte
Dim j As Byte
Dim t As Byte
Dim svh(16) As Byte
Dim svl(16) As Byte
usbcrap:
UsbSetVendorId 0x1220
UsbSetProductId 0x1234
UsbSetVersionNumber 0x1122
UsbSetManufacturerString "www.nerdclub.co.uk"
UsbSetProductString "Multiple servo USB controller"
UsbSetSerialNumberString "1111111111"
UsbOnIoInGosub usbonioin
UsbOnIoOutGosub usbonioout
UsbOnFtInGosub usbonftin
UsbOnFtOutGosub usbonftout
init:
AllDigital
Config PORTA = Output
Config PORTB = Output
T1CON = 0 'set all bits to zero
startusb:
PORTA.0 = 1 'turn on an output so we can see the pic is running
UsbStart
PORTA.0 = 0 'turn off output once USB has set up
startup:
'set the value for timer1 to count up to
'(count to 5000 = 1ms: highbyte=5000/256, lowbyte=5000 mod 256)
CCPR1H = 19
CCPR1L = 136
'to allow a 0-100% range, sv(0-15) can take the value 0-100
'(we multiply these up by 50 as the values are received, to give
'a range of 0-5000, which is 0ms to 1ms).
'these are some example values to test the middle and extreme
'ranges of the servo controller on pins 0,1,2
svh(0) = 9
svl(0) = 196
svh(1) = 0
svl(1) = 0
svh(2) = 19
svl(2) = 136
'1011 = special event interrupt, clear timer etc.
CCP1CON.CCP1M3 = 1
CCP1CON.CCP1M2 = 0
CCP1CON.CCP1M1 = 1
CCP1CON.CCP1M0 = 1
'PIR1.CCP1IF = 0 'clear any "bogus" reading
T1CON.TMR1ON = 1 'enable timer1
t = 0
loop:
If t = 1 Then
'turn on all servo outputs for 1ms
PORTB = 0xff
PORTA.0 = 1 'led to show timer is running
Else
If t = 2 Then
'this is where outputs get turned off. Because there may
'be a delay in processing this loop, we compare the actual
'value held in Timer2 and if it's greater than our servo-off
'value (0-100% multipled by 50) then turn that pin off
If TMR1H > svh(0) Then PORTB.0 = 0
If TMR1H = svh(0) And TMR1L >= svl(0) Then PORTB.0 = 0
If TMR1H > svh(1) Then PORTB.1 = 0
If TMR1H = svh(1) And TMR1L >= svl(1) Then PORTB.1 = 0
If TMR1H > svh(2) Then PORTB.2 = 0
If TMR1H = svh(2) And TMR1L >= svl(2) Then PORTB.2 = 0
Else
'turn off the servo outputs for the duration 3ms to 14ms (and 0ms-1ms)
PORTB = 0x00
PORTA.0 = 0 'turn off timer led
'service the USB port for data
UsbService
'check to see if we've had two bytes of serial data
If i = 0 Then
'Hserget i
Else
If j = 0 Then
'Hserget j
Else
'parse the two byte instruction and update the servo-off
'values as necessary, then reset i and j to zero
i = 0
j = 0
Endif
Endif
Endif
Endif
'check for CCP1 interrupt flag (even if interrupts are not
'on, you can poll this flag to see if an event has occurred)
'this really needs to be in an interrupt routine so that the timer
'is kept as up-to-date as possible
If PIR1.CCP1IF = 1 Then
t = t + 1
PIR1.CCP1IF = 0
If t > 14 Then
t = 0
'reset the timer to exactly zero
'(so if there's been a delay switching on, we're back in sync
'when it comes to switching the signal off, otherwise there
'might be a bit of judder as the time period between on and off
'may differ by a few thousandths of a millisecond)
TMR1L = 0
Endif
Endif
Goto loop
End
usbonftout:
Toggle PORTA.0
Return
usbonftin:
'we've received some data in the feature report
Return
usbonioout:
Toggle PORTA.1
Return
usbonioin:
'we've received some data on the data i/o, so
'update the necessary servo high and low byte values
'to get the selected servo to move to its new position
Return
Post a Comment