HEX simplify program. How?

Started by Ed January 20, 2005

Hi

I'm writting a program to control an ICON H bridge with a PID
controller. I basically send data to the serial port and the board
moves the motor. The program looks like this:

Option Explicit
'-------------------------------
' ************* Serial Port Buffer and Pins Set Up ****************

Const InputPin as Byte = 6
Const OutputPin as byte = 5

Dim SerialOutputBuffer(1 to 10) as byte
Dim SerialInputBuffer(1 to 13) as byte

Dim A as byte ' Data output
Dim B as byte ' Data input

' ******************** Main program *****************************

Public Sub Main()

Call OpenQueue(SerialOutputBuffer, 10) ' Open buffers
Call OpenQueue(SerialInputBuffer, 13)
Call DefineCom3(InputPin, OutputPin, bx0000_1000) ' Port settings
Call OpenCom(3, 19200, SerialInputBuffer, SerialOutputBuffer) ' open
port

' ******************** Servo Data Output **************************

A=&HF0 ' Command
Call PutQueue (SerialOutputBuffer, A, 1)

A=&H01 ' Address
Call PutQueue (SerialOutputBuffer, A, 1)

A=&HD0 ' Position
Call PutQueue (SerialOutputBuffer, A, 1)

A=&H07 ' Position
Call PutQueue (SerialOutputBuffer, A, 1)

A=&H00 ' Position
Call PutQueue (SerialOutputBuffer, A, 1)

A=&H00 ' Position
Call PutQueue (SerialOutputBuffer, A, 1)

A=&HC8 ' Checksum
Call PutQueue (SerialOutputBuffer, A, 1) Call Delay (0.25)

' ************ Check for ACK PID controller signal **************

Call GetQueue (SerialInputBuffer, B, 1)
Debug.Print Cstr(B)

Call Sleep (0.0)

End Sub

As you can see, I'm sending byte by byte to the serial port. The PID
needs to receive the Least Significant byte before [LSB]. This motor
has to move thousands of time to different positions ans I don't
think it would be very practical to do it this way. Is there any way
to do something like this? (instead of changing the value of A
everytime):

Call PutQueue (SerialOutputBuffer, &HFF,1)

Can you think of any way to simplify the program? Do I really need
to sue the Buffers Queue?

Thanks




Ok, i got a way of simplify it: ------------------------------
Public Sub Movements()

Dim N as byte

For N=1 to 21

Command = &HF0 ' Command
Call PutQueue (SerialOutputBuffer, Command, 1)

Address = &H01 ' Address
Call PutQueue (SerialOutputBuffer, Address, 1)

D = 2000
Call PutQueue (SerialOutputBuffer, D, 4)

Check = &HC8 ' Checksum

Call PutQueue (SerialOutputBuffer, Check, 1)
-----------------------------

The problem is that I need to generate 256 Checksum summation. I
can easily add Command & Address but for D that now is a long
number, i need to split it in two. That is D(1) and D(2) so I
can add them with the rest of the variables (bytes) and generate the
Checksum.

What I wanted to do is to generate a loop that continuosly adds 250
to variable D (Displacement) so I don't have to write 250 variables.

Any ideas?


Hi Ed,
I looked at a datasheet at http://www.solutions-cubed.com
assuming that you are using that system, in (ref 4.5) "Direct Control Mode", I think the Protocol calls for 7 bytes to be sent to execute the (ref 5.2.18)"Write Desired Position" command. In thinking about this as a "Move To" command, you would know everything but the "checksum" prior to sending the 7 bytes. There are many different ways of proceeding, the following is just a suggestion. The "Position" is assigned to a 32 bit, 4 byte, "Long" variable. This is now in memory, stored LSB at the "address" of the variable, the next byte at "address + 1", the next byte at "address + 2" and the MSB at "address + 3". A subroutine would sequentially add the 4 bytes, add the "Command" and "DeviceADDR" bytes, do a "mod 256" to produce a checksum byte and then send all the known data.

TRIAL CODE IDEAS

'--------------------------------
'Declarations
'
'Const eCMD As Byte = &HF0
'Const eADDR As Byte = &H01
'
'Dim ePREAMBLE(1 to 2) As Byte 'is 2 bytes
'Dim ePOSITION As Long 'is 4 bytes
'Dim eCHKsumBYTE As Byte 'is 1 byte
'
'Dim eCHKsumINTEGER As Integer
'
'Dim SerialOutputBuffer(1 to 13) as byte
' must be at least 13 to send 4 bytes (+9 byte overhead)
'Dim SerialInputBuffer(1 to 13) as byte
'
'--------------------------------
'setup queues (do once)
'
' Call OpenQueue(SerialOutputBuffer, 13)
' Call OpenQueue(SerialInputBuffer, 13)
' Call DefineCom3(InputPin, OutputPin, bx0000_1000)
' Call OpenCom(3, 19200, SerialInputBuffer, SerialOutputBuffer)
'
'--------------------------------
'Build preamble (do once)
'
' ePREAMBLE(1) = eCMD
' ePREAMBLE(2) = eADDR
'
'--------------------------------
'Set the desired position (any time)
'
' ePOSITION = 2000 ' &H000007D0
' ' uses twos complement
'--------------------------------
'Compute checksum (prior to sending)
'
' eCHKsumINTEGER = 0 'clear checksum bytes
' eCHKsumINTEGER = eCHKsumINTEGER + RAMpeek( VarPtr(ePREAMBLE(1)) )
' eCHKsumINTEGER = eCHKsumINTEGER + RAMpeek( VarPtr(ePREAMBLE(2)) )
' eCHKsumINTEGER = eCHKsumINTEGER + RAMpeek( VarPtr(ePOSITION(1)) )
' eCHKsumINTEGER = eCHKsumINTEGER + RAMpeek( VarPtr(ePOSITION(1))+1 )
' eCHKsumINTEGER = eCHKsumINTEGER + RAMpeek( VarPtr(ePOSITION(1))+2 )
' eCHKsumINTEGER = eCHKsumINTEGER + RAMpeek( VarPtr(ePOSITION(1))+3 )
' eCHKsumBYTE = CByte(eCHKsumINTEGER MOD 256) 'the conversion may not be necessary
'
'--------------------------------
'Send 7 bytes of data (when you're ready)
'
' Call PutQueue (SerialOutputBuffer, ePREAMBLE, 2)
' Call PutQueue (SerialOutputBuffer, ePOSITION, 4)
' Call PutQueue (SerialOutputBuffer, eCHKsumBYTE, 1)
'
'--------------------------------

I hope this will be helpful

Best Regards, Eric

----- Original Message -----
From: Ed
To:
Sent: Thursday, January 20, 2005 4:56 AM
Subject: [BasicX] Re: HEX simplify program. How?

Ok, i got a way of simplify it:
------------------------------
Public Sub Movements()
Dim N as byte
For N=1 to 21
Command = &HF0 ' Command
Call PutQueue (SerialOutputBuffer, Command, 1)
Address = &H01 ' Address
Call PutQueue (SerialOutputBuffer, Address, 1)
D = 2000
Call PutQueue (SerialOutputBuffer, D, 4)
Check = &HC8 ' Checksum
Call PutQueue (SerialOutputBuffer, Check, 1)
-----------------------------
The problem is that I need to generate 256 Checksum summation. I
can easily add Command & Address but for D that now is a long
number, i need to split it in two. That is D(1) and D(2) so I
can add them with the rest of the variables (bytes) and generate the
Checksum.

What I wanted to do is to generate a loop that continuosly adds 250
to variable D (Displacement) so I don't have to write 250 variables.

Any ideas?



eserdahl@ wrote:

>Hi Ed,
>I looked at a datasheet at http://www.solutions-cubed.com
>assuming that you are using that system, in (ref 4.5) "Direct Control Mode", I think the Protocol calls for 7 bytes to be sent to execute the (ref 5.2.18)"Write Desired Position" command. In thinking about this as a "Move To" command, you would know everything but the "checksum" prior to sending the 7 bytes. There are many different ways of proceeding, the following is just a suggestion. The "Position" is assigned to a 32 bit, 4 byte, "Long" variable. This is now in memory, stored LSB at the "address" of the variable, the next byte at "address + 1", the next byte at "address + 2" and the MSB at "address + 3". A subroutine would sequentially add the 4 bytes, add the "Command" and "DeviceADDR" bytes, do a "mod 256" to produce a checksum byte and then send all the known data. >
>
Let me offer some improvements to Eric's suggested code.

1. Add constants for InputPort and OutputPort and fixup a few other
compile problems such as adding some CInt conversions.
2. When you add a byte to a byte in BasicX, the resultant byte is
automatically modulo 256. This fact can be used to eliminate the
eCHKsumINTEGER variable.
3. Adding the 6 bytes to create the checksum can be done all in one
statement. This saves on pushing and popping the variable eCHKsumBYTE
for each assignment.
4. Changed to output queue size to accommodate all 7 bytes of the message.

These changes reduce the code size down from 196 bytes to 144 bytes, The
checksum calculation is now only 52 bytes long. When I wrapped the
checksum calculation in a function, the mean time to execute was 0.55
ms including the overhead of the function call, return and a for/next
iteration loop for timing. This means you could execute at least 1800
checksums per second or probably more than 3000 if you inline the code.

Given that the two variables (ePREAMBLE and ePOSITION) are together in
memory, a For/Next loop could be used to cycle through the 6 bytes but
actually that turns out to be more code and slower than the simple
addition because of the loop overhead. Here is my revision to Eric's
example code:

Option Explicit

Public Sub Main()

'--------------------------------
'Declarations
'
Const eCMD As Byte = &HF0
Const eADDR As Byte = &H01

Const InputPin as Byte = 6
Const OutputPin as Byte = 5
'
Dim ePREAMBLE(1 to 2) As Byte 'is 2 bytes
Dim ePOSITION As Long 'is 4 bytes
Dim eCHKsumBYTE As Byte 'is 1 byte
'
Dim SerialOutputBuffer(1 to 16) as byte
' must be at least 16 to send 7 bytes (+9 byte overhead)
Dim SerialInputBuffer(1 to 13) as byte
'
'--------------------------------
'setup queues (do once)
'
Call OpenQueue(SerialOutputBuffer, 13)
Call OpenQueue(SerialInputBuffer, 13)
Call DefineCom3(InputPin, OutputPin, bx0000_1000)
Call OpenCom(3, 19200, SerialInputBuffer, SerialOutputBuffer)
'
'--------------------------------
'Build preamble (do once)
'
ePREAMBLE(1) = eCMD
ePREAMBLE(2) = eADDR
'
'--------------------------------
'Set the desired position (any time)
'
ePOSITION = 2000 ' &H000007D0
' ' uses twos complement
'--------------------------------
'Compute checksum (prior to sending)
'
eCHKsumBYTE = RAMpeek(VarPtr(ePREAMBLE(1))) +
RAMpeek(VarPtr(ePREAMBLE(2))) + RAMpeek(VarPtr(ePOSITION)) +
RAMpeek(VarPtr(ePOSITION)+1) + RAMpeek(VarPtr(ePOSITION)+2) +
RAMpeek(VarPtr(ePOSITION)+3)

'
'--------------------------------
'Send 7 bytes of data (when you're ready)
'
Call PutQueue (SerialOutputBuffer, ePREAMBLE, 2)
Call PutQueue (SerialOutputBuffer, ePOSITION, 4)
Call PutQueue (SerialOutputBuffer, eCHKsumBYTE, 1)

'--------------------------------

End Sub




I wrote: :

>
> These changes reduce the code size down from 196 bytes to 144 bytes,
The
> checksum calculation is now only 52 bytes long. When I wrapped the
> checksum calculation in a function, the mean time to execute was 0.55
> ms including the overhead of the function call, return and a for/next
> iteration loop for timing. This means you could execute at least 1800
> checksums per second or probably more than 3000 if you inline the code.

I inlined the code 100 times and found the average execution for the
checksum calculation to be 0.23 ms (230 microseconds) which should be
fast enough for what you want :)

Mike



Hi Mike,

Excellent improvements. Explaining that RAMpeek byte+byte=byte in the example addition results in an effective MOD 256 on the sum is SWEET!

Very good example of how to optimize, reduce code and speed up.

The line ... Call OpenQueue(SerialOutputBuffer, 13) should be ... Call OpenQueue(SerialOutputBuffer, 16). In my programs I use a "Const" to dimension the "size" of my buffers so that I'll have to think twice when I use OpenQueue.

Hopefully this will help Ed and he'll let us know how it worked.

Best Regards, Eric
----- Original Message -----
From: Mike Perks
To:
Sent: Thursday, January 20, 2005 7:00 PM
Subject: Re: [BasicX] Re: HEX simplify program. How? eserdahl@ wrote:

>Hi Ed,
>I looked at a datasheet at http://www.solutions-cubed.com
>assuming that you are using that system, in (ref 4.5) "Direct Control Mode", I think the Protocol calls for 7 bytes to be sent to execute the (ref 5.2.18)"Write Desired Position" command. In thinking about this as a "Move To" command, you would know everything but the "checksum" prior to sending the 7 bytes. There are many different ways of proceeding, the following is just a suggestion. The "Position" is assigned to a 32 bit, 4 byte, "Long" variable. This is now in memory, stored LSB at the "address" of the variable, the next byte at "address + 1", the next byte at "address + 2" and the MSB at "address + 3". A subroutine would sequentially add the 4 bytes, add the "Command" and "DeviceADDR" bytes, do a "mod 256" to produce a checksum byte and then send all the known data.
>
Let me offer some improvements to Eric's suggested code.

1. Add constants for InputPort and OutputPort and fixup a few other
compile problems such as adding some CInt conversions.
2. When you add a byte to a byte in BasicX, the resultant byte is
automatically modulo 256. This fact can be used to eliminate the
eCHKsumINTEGER variable.
3. Adding the 6 bytes to create the checksum can be done all in one
statement. This saves on pushing and popping the variable eCHKsumBYTE
for each assignment.
4. Changed to output queue size to accommodate all 7 bytes of the message.

These changes reduce the code size down from 196 bytes to 144 bytes, The
checksum calculation is now only 52 bytes long. When I wrapped the
checksum calculation in a function, the mean time to execute was 0.55
ms including the overhead of the function call, return and a for/next
iteration loop for timing. This means you could execute at least 1800
checksums per second or probably more than 3000 if you inline the code.

Given that the two variables (ePREAMBLE and ePOSITION) are together in
memory, a For/Next loop could be used to cycle through the 6 bytes but
actually that turns out to be more code and slower than the simple
addition because of the loop overhead. Here is my revision to Eric's
example code:

Option Explicit

Public Sub Main()

'--------------------------------
'Declarations
'
Const eCMD As Byte = &HF0
Const eADDR As Byte = &H01

Const InputPin as Byte = 6
Const OutputPin as Byte = 5
'
Dim ePREAMBLE(1 to 2) As Byte 'is 2 bytes
Dim ePOSITION As Long 'is 4 bytes
Dim eCHKsumBYTE As Byte 'is 1 byte
'
Dim SerialOutputBuffer(1 to 16) as byte
' must be at least 16 to send 7 bytes (+9 byte overhead)
Dim SerialInputBuffer(1 to 13) as byte
'
'--------------------------------
'setup queues (do once)
'
Call OpenQueue(SerialOutputBuffer, 13)
Call OpenQueue(SerialInputBuffer, 13)
Call DefineCom3(InputPin, OutputPin, bx0000_1000)
Call OpenCom(3, 19200, SerialInputBuffer, SerialOutputBuffer)
'
'--------------------------------
'Build preamble (do once)
'
ePREAMBLE(1) = eCMD
ePREAMBLE(2) = eADDR
'
'--------------------------------
'Set the desired position (any time)
'
ePOSITION = 2000 ' &H000007D0
' ' uses twos complement
'--------------------------------
'Compute checksum (prior to sending)
'
eCHKsumBYTE = RAMpeek(VarPtr(ePREAMBLE(1))) +
RAMpeek(VarPtr(ePREAMBLE(2))) + RAMpeek(VarPtr(ePOSITION)) +
RAMpeek(VarPtr(ePOSITION)+1) + RAMpeek(VarPtr(ePOSITION)+2) +
RAMpeek(VarPtr(ePOSITION)+3)

'
'--------------------------------
'Send 7 bytes of data (when you're ready)
'
Call PutQueue (SerialOutputBuffer, ePREAMBLE, 2)
Call PutQueue (SerialOutputBuffer, ePOSITION, 4)
Call PutQueue (SerialOutputBuffer, eCHKsumBYTE, 1)

'--------------------------------

End Sub



eserdahl@ wrote:

>Hi Mike,
>
>Excellent improvements. Explaining that RAMpeek byte+byte=byte in the example addition results in an effective MOD 256 on the sum is SWEET!
>
>Very good example of how to optimize, reduce code and speed up.
>
>The line ... Call OpenQueue(SerialOutputBuffer, 13) should be ... Call OpenQueue(SerialOutputBuffer, 16). In my programs I use a "Const" to dimension the "size" of my buffers so that I'll have to think twice when I use OpenQueue.
>
>Hopefully this will help Ed and he'll let us know how it worked.
>
>Best Regards, Eric

Thanks. You are right of course - I was more focused on the problem at
hand. Constants should be used for almost everything with suitable
comments. In fact it could even be something like:

Const MandatoryQueueSize as Byte = 9
Const HBridgeSerialOutputSize as Byte = 7 + MandatoryQueueSize

See message 17545 for an explanation of what is in the 9 byte header for
every queue.

Mike



WOW!! You're great guys!!
Thanks a lot for the info!!

Yesterday afternoon I got it working by using GetBit and PutBit so
basically I built my own four 4 bytes variables to add them together
with a few FOR NEXT loops. A quite complex and long way to do it
compared to your solution.

Just for reference purposes only I post the code of how I got it:

' ********* CALCULATE CHECKSUM **************************
For I = 0 to 7

C1(I) = GetBit (D,I)
Call PutBit (T1, I, C1(I))
'Debug.Print CStr(C1(I)); " ";Cstr
(T1); " "; Cstr(I)
Next

Checksum = T1

For I = 0 to 7
C2(I+8) = GetBit (D,I+8)
Call Putbit (T2, (I), C2(I+8))
'Debug.Print CStr(C2(I)); " ";Cstr
(T2); " "; Cstr(I+8)
Next

Checksum = Checksum + T2 + Command + Address
'Checksum = &H04
'Debug.Print Cstr(Checksum)
' ******* END CALCULATE CHECKSUM **************************** I won't be able to test your code until Monday, as the Motor has
been taken to a workshop to do a few modifications to the support
where it is mounted on. I will let you know as soon as I get some
results.

- Eric, yes the system I'm using is the one you mention and yes:
Direct Mode.

- Mike, how you find out how fast the calculations are? (just out of
curiosity) Thanks a lot again, you're been of great help!!

Ed --- In , "eserdahl@" <eserdahl@p...> wrote:
> Hi Mike,
>
> Excellent improvements. Explaining that RAMpeek byte+byte=byte in
the example addition results in an effective MOD 256 on the sum is
SWEET!
>
> Very good example of how to optimize, reduce code and speed up.
>
> The line ... Call OpenQueue(SerialOutputBuffer, 13) should be ...
Call OpenQueue(SerialOutputBuffer, 16). In my programs I use
a "Const" to dimension the "size" of my buffers so that I'll have to
think twice when I use OpenQueue.
>
> Hopefully this will help Ed and he'll let us know how it worked.
>
> Best Regards, Eric
> ----- Original Message -----
> From: Mike Perks
> To:
> Sent: Thursday, January 20, 2005 7:00 PM
> Subject: Re: [BasicX] Re: HEX simplify program. How? > eserdahl@ wrote:
>
> >Hi Ed,
> >I looked at a datasheet at http://www.solutions-cubed.com
> >assuming that you are using that system, in (ref 4.5) "Direct
Control Mode", I think the Protocol calls for 7 bytes to be sent to
execute the (ref 5.2.18)"Write Desired Position" command. In
thinking about this as a "Move To" command, you would know
everything but the "checksum" prior to sending the 7 bytes. There
are many different ways of proceeding, the following is just a
suggestion. The "Position" is assigned to a 32 bit, 4 byte, "Long"
variable. This is now in memory, stored LSB at the "address" of the
variable, the next byte at "address + 1", the next byte at "address
+ 2" and the MSB at "address + 3". A subroutine would sequentially
add the 4 bytes, add the "Command" and "DeviceADDR" bytes, do a "mod
256" to produce a checksum byte and then send all the known data.
> >
> Let me offer some improvements to Eric's suggested code.
>
> 1. Add constants for InputPort and OutputPort and fixup a few
other
> compile problems such as adding some CInt conversions.
> 2. When you add a byte to a byte in BasicX, the resultant byte
is
> automatically modulo 256. This fact can be used to eliminate the
> eCHKsumINTEGER variable.
> 3. Adding the 6 bytes to create the checksum can be done all in
one
> statement. This saves on pushing and popping the variable
eCHKsumBYTE
> for each assignment.
> 4. Changed to output queue size to accommodate all 7 bytes of
the message.
>
> These changes reduce the code size down from 196 bytes to 144
bytes, The
> checksum calculation is now only 52 bytes long. When I wrapped
the
> checksum calculation in a function, the mean time to execute was
0.55
> ms including the overhead of the function call, return and a
for/next
> iteration loop for timing. This means you could execute at least
1800
> checksums per second or probably more than 3000 if you inline
the code.
>
> Given that the two variables (ePREAMBLE and ePOSITION) are
together in
> memory, a For/Next loop could be used to cycle through the 6
bytes but
> actually that turns out to be more code and slower than the
simple
> addition because of the loop overhead. Here is my revision to
Eric's
> example code:
>
> Option Explicit
>
> Public Sub Main()
>
> '--------------------------------
> 'Declarations
> '
> Const eCMD As Byte = &HF0
> Const eADDR As Byte = &H01
>
> Const InputPin as Byte = 6
> Const OutputPin as Byte = 5
> '
> Dim ePREAMBLE(1 to 2) As Byte 'is 2 bytes
> Dim ePOSITION As Long 'is 4 bytes
> Dim eCHKsumBYTE As Byte 'is 1 byte
> '
> Dim SerialOutputBuffer(1 to 16) as byte
> ' must be at least 16 to send 7 bytes (+9 byte
overhead)
> Dim SerialInputBuffer(1 to 13) as byte
> '
> '--------------------------------
> 'setup queues (do once)
> '
> Call OpenQueue(SerialOutputBuffer, 13)
> Call OpenQueue(SerialInputBuffer, 13)
> Call DefineCom3(InputPin, OutputPin, bx0000_1000)
> Call OpenCom(3, 19200, SerialInputBuffer, SerialOutputBuffer)
> '
> '--------------------------------
> 'Build preamble (do once)
> '
> ePREAMBLE(1) = eCMD
> ePREAMBLE(2) = eADDR
> '
> '--------------------------------
> 'Set the desired position (any time)
> '
> ePOSITION = 2000 ' &H000007D0
> ' ' uses twos complement
> '--------------------------------
> 'Compute checksum (prior to sending)
> '
> eCHKsumBYTE = RAMpeek(VarPtr(ePREAMBLE(1))) +
> RAMpeek(VarPtr(ePREAMBLE(2))) + RAMpeek(VarPtr(ePOSITION)) +
> RAMpeek(VarPtr(ePOSITION)+1) + RAMpeek(VarPtr(ePOSITION)+2) +
> RAMpeek(VarPtr(ePOSITION)+3)
>
> '
> '--------------------------------
> 'Send 7 bytes of data (when you're ready)
> '
> Call PutQueue (SerialOutputBuffer, ePREAMBLE, 2)
> Call PutQueue (SerialOutputBuffer, ePOSITION, 4)
> Call PutQueue (SerialOutputBuffer, eCHKsumBYTE, 1)
>
> '--------------------------------
>
> End Sub >





I haven't forgot about it, I just haven't got the motor back from
the workshop but as soon as I get it I will post my results.

Thanks for the help