October 2009 Archives

Testing, Testing...

| No Comments
The AVR assembly language unit tests that I spoke of last week are going well. I decided to explore the idea of unit testing by writing tests for the easier to test aspects of the serial protocol code and then, as this went well, I decided to write tests for the serial protocol code in order rather that simply jumping to write tests for the code that I know is broken. I figure that I'm more likely to write the tests that I actually need (i.e. for all of the code) this way, rather than simply writing the tests that I think I need.

So far this is going well. The test harness program is now considerably bigger than the real servo controller program. I have around 50 tests at present and I'm around half way through the serial protocol code. I've found a couple of otherwise hard to find bugs; which is good. As usual the tests act as documentation for how the code is used and what the inputs and outputs and side effects are. This doesn't remove the need for actual documentation but it helps.
As I mentioned yesterday the servo controller project has got to the point where being able to unit test the code would be useful to me. In my day job as a C++ server developer I've been using unit tests for several years and most of the code that I write is written in a Test Driven Development style. This works well for me and was one of the first things that I missed when I started to develop in AVR assembler in AVR Studio. At the time I didn't know enough about how I would structure my code, or even how I'd write the code at all, and testing, though obviously missing, was something I managed to do without.

Now that my code has got pretty large I've begun to split it into separate asm files. These are broken down by purpose, in effect I'm creating different modules of functionality. Due to the way that AVR Studio works with assembler projects these files are then included into a master file which is then assembled. Since my code is already broken down into modules, if I'm careful with how I split the code up I've found that I can build separate test projects which include one block of real functionality from the servo controller project and which then include test harness code that provides the functionality that the real code needs to build; for example, I have a function called SerialEchoCommand which will echo a command back to the serial port, this is used once the command's arguments have been validated. By placing this function in one file and the code that uses it in another I can build a test which can replace SerialEchoCommand with a function that the test harness can use to determine if the real code would have done the right thing if it were linked with the real implementation of SerialEchoCommand. In testing terms this is called mocking. I've provided a mock implementation of an interface that the code under test uses and the test can interrogate the mock to determine if the code under test is behaving as expected. With most testing the challenge is to separate the code that you want to test from the code that you don't want to test, or the code that is too hard to manipulate within the test.

Once I realised that I could use relatively standard mocking techniques to separate the code under test from the underlying hardware it became obvious that I could build a test harness that would run in the AVR Studio Simulator and that could test various parts of my servo controller. Unit testing was within reach.

I've spent a few hours adding some tests to some of the simpler parts of the serial protocol handling code and it's going well. I seem to have a structure that works and so far it's been easy to provide data for the function under test and then to test that it produces the correct outcome and uses the correct services in the expected way. The next release of the servo controller source code will include my test harnesses.

I expect an example will help. Especially if you're not used to testing!

Suppose we have a function called SerialProcessCommandSetServoMinPosn which is called from the code that accumulates and executes serial commands and who's job it is to take a servo index and a position and to update the servo configuration data so that the supplied position is the minimum position that the servo can be moved to. This function might look something like this:

SerialProcessCommandSetServoMinPosn : 

    ld servoIndex, X+

    cpi servoIndex, NUM_SERVOS                  ; check the servo index is valid
    brlt PC+2
    rjmp SerialServoOutOfRange

    ld temp1, X                                 ; load new min posn

    rcall SerialSelectServoData

    adiw XL, MAX_POS_OFFSET

    ld temp2, X                                 ; read existing max position

    cp temp2, temp1                             ; new min must be less than 
    brsh PC+2                                   ; or equal to exisiting max
    rjmp SerialPosnOutOfRange 

    sbiw XL, MAX_POS_OFFSET
    adiw XL, MIN_POS_OFFSET

    st X, temp1
        
    rcall SerialEchoCommand
    
    rjmp SerialStart
This is generally how all of the serial command processing code is structured. The call into us is a rjmp from the serial command dispatch code. We validate our parameters, report errors or echo our command back to the guy on the end of the serial port and then either jump back to the serial data accumulation code or execute the command and then jump back to the serial data accumulation code.

It's probably clear from the code above that to be able to test it we need to set X to be pointing to some valid data; outside of the test this would be pointing into the serial data accumulation buffer at the point just after the command code that tells the dispatcher that this is the 'set min' command. In our test X can point anywhere that has two bytes of data available, our test harness will set this up and set the contents of the buffer that X is pointing to so that it contains a servo index and a position. Our test also needs to provide implementations of SerialServoOutOfRange, SerialPosnOutOfRange, SerialEchoCommand and SerialStart. As long as these labels exist in a different file we can replace the real code with mocks for our test simply by including the mock code rather than the real code. We'll use the real implemetation of SerialSelectServoData as that function is responsible for taking a servo index and pointing X to the right place in the servo position data. It looks like this:

SerialSelectServoData :

    push temp2

    ldi XL, LOW(POSITION_DATA_START)       
    ldi XH, HIGH(POSITION_DATA_START)

    ldi temp2, BYTES_PER_SERVO
    
    mul servoIndex, temp2

    add XL, resl
    adc XH, resh

    pop temp2

    ret
Our test just needs to provide a valid position data buffer (i.e. an equate that sets POSITION_DATA_START to something sensible) with some known values in it. 

The test might look something like this:

TestSerialProcessCommandSetServoMinPosn :

    ldi temp1, 15
    mov testIndex, temp1
    
    rcall InitialiseSerialOutputBuffer

    rcall InitialisePositionDataToKnownValues

    ldi XL, LOW(TEST_SERIAL_INPUT_BUFFER)     
    ldi XH, HIGH(TEST_SERIAL_INPUT_BUFFER)

    ldi temp1, 0                            
    st X+, temp1                            ; servo index to change

    ldi temp1, 20                            
    st X, temp1                             ; new min value

    ldi XL, LOW(TEST_SERIAL_INPUT_BUFFER)   ; reset X   
    ldi XH, HIGH(TEST_SERIAL_INPUT_BUFFER)

    rjmp SerialProcessCommandSetServoMinPosn

    rjmp TestsFailed
For now we can ignore the testIndex register. Here we set up an output buffer for our implementation of SerialEchoCommand and initialise POSITION_DATA_START and the data in the buffer to sensible values. We then set up the serial input buffer to contain a servo index and a position value and set X to point to the servo index in the buffer. This is how the serial dispatch code would leave the X pointer after examining the previous byte in the real serial accumulation buffer and switching on it depending on which command code it represents. We then jump to the code under test and, hopefully, never return. In case we DO return we then end the test by jumping to the TestsFailed label. 

The TestsFailed label is one place where the test code will end up after tests have been run. The other is the TestsSucceeded label. Both simply consist of a jump to themselves. By setting break points on each of these jumps we can run the tests and discover if there are any failures.

This serial code is slightly harder to test than it could be because it isn't structured as functions which return to their caller. Instead we jump into the functions and they jump back to the serial command accumulation loop when they're done. This makes error handling easier; failures result in the code at the level of the failure reporting the error back to the serial port and then jumping straight back to accumulate a new command. As such all of the code under test will eventually jump to SerialStart. To be able to determine if the test passed we need to be able to examine what the code under test did whilst it was running. This is where the testIndex register comes in. The mocked out code for SerialStart contains a jump table that jumps based on the contents of the testIndex register. Each test has a corresponding 'check results' function and the SerialStart code jumps to the check results code associated with the test that's currently running. 

TestSerialProcessCommandSetServoMinPosnCheckResults might look something like this:

TestSerialProcessCommandSetServoMinPosnCheckResults : 

    ; The call should leave X pointing at the config value that we have changed...

    cpi XL, LOW(POSITION_DATA_START + MIN_POS_OFFSET)
    breq PC+2
    rjmp TestsFailed

    cpi XH, HIGH(POSITION_DATA_START + MIN_POS_OFFSET)
    breq PC+2
    rjmp TestsFailed

    ld temp1, X                     ; validate that we changed the value we wanted to change
    cpi temp1, 20                   ; to the correct value
    breq PC+2
    rjmp TestsFailed        

    clr temp1                       ; reset the value to its starting value 
    st X, temp1

    rcall ValidatePositionDataIsUnchanged   ; and then make sure nothing else was changed

    ; now validate that the correct mock functions were called...

    ldi XL, LOW(TEST_SERIAL_OUTPUT_BUFFER)     
    ldi XH, HIGH(TEST_SERIAL_OUTPUT_BUFFER)

    ld temp1, X+
    cpi temp1, 1            ; there should have been 1 call
    breq PC+2
    rjmp TestsFailed

    ld temp1, X+
    cpi temp1, 0xFF         ; echo command
    breq PC+2
    rjmp TestsFailed

    inc testResult
    ret
Note that we can check that the X pointer has been left where we expect it to end up and that the data that should have been manipulated has been changed as expected. We can then check that the correct mock functions were called. In this case we check that one function, SerialEchoCommand, was called. The implementation of this could be something like this:

SerialEchoCommand : 

    ldi serialChar, 0xFF

    rcall SendSerial
    
    ret
Where SendSerial might be implemented like this:

SendSerial :
    push XL                                     ; save the registers that we use
    push XH
    push temp1
    push temp2

    ldi XL, LOW(TEST_SERIAL_OUTPUT_BUFFER)     
    ldi XH, HIGH(TEST_SERIAL_OUTPUT_BUFFER)

    ld temp1, X                                 ; load the number of bytes currently stored in the serial
    clr temp2                                   ; output buffer.
                                    
    inc temp1                                   ; increment the number of bytes as the offset from the
                                                ; start of the buffer is one greater than the number of bytes
                                                ; as the buffer also holds the count itself at offset 0
    add XL, temp1
    adc XH, temp2

    st X, serialChar                            ; store the data that would be written to the serial port in
                                                ; out buffer for later analysis in the test

    ldi XL, LOW(TEST_SERIAL_OUTPUT_BUFFER) 
    ldi XH, HIGH(TEST_SERIAL_OUTPUT_BUFFER)

    st X, temp1                                 ; save the number of bytes stored, note that we incremented
                                                ; this value above

    pop temp2                                   ; clean up the stack
    pop temp1
    pop XH
    pop XL

    ret
This makes it easy to check for data that the functions under test might write directly to the serial port, using SendSerial, or other mock functions that they may call, such as SerialEchoCommand. There's no need to test the functionality of SerialEchoCommand here, we can test that separately, so it's adequate that it simply writes a single well known token into the test output buffer.

Of course, things get more complex when we're testing for correct handling of invalid values (i.e. if we pass in a servo index that's too big, or if we try and set a min posn that's out of range) but most of the tests required can be built on the same framework.

Most functions, even PWM generating timer interrupt code, can be tested in a similar way. The complex part is always getting the granularity of the code packaging correct so that you can mock the appropriate layers. This often causes several simple functions, or macros, to be required rather than direct hardware access but it's often a good design decision to abstract these hardware access points away anyway. I've found in the past that allowing the tests to lead your design where appropriate (as is the way with TDD) usually results in a better design!

Now, off to fix those bugs in the multi-move command... 

Unit testing AVR assembly language

| No Comments
Way back at the beginning of this journey I mentioned the fact that I'd quite like to be able to use some of the development disciplines that I use in my day job during the development of the firmware for my hexapod. Now that I've actually written some non trivial assembly language for the AVR I find that I'm missing not having my usual unit tests to support my ongoing development and refactoring. This has been most noticeable just recently during the integration of the most complicated serial command of the new servo controller; the multiple move command.

I'm currently working on separating out the code that processes serial command messages from the code underlying support code. The idea being that I can then build the serial command processing code into a new AVR Studio project and 'mock' out the support code so that I can test the serial processing code. Once that's done I should be able to write a suite of tests that can be run on the serial code to make sure that the commands do what they're supposed to do. I'm lucky that the serial command code generally either calls other functions (which I can mock out by creating a testing version of the function and including the source for that rather than the source for the real function) or modifies data (which I can examine as part of the test to ensure that it has been modified in the correct way). 

So far it's going quite well. More once I have it working well enough to document and even more once I then use these new tests to fish out the bugs in the multiple move command!


Integrating the multi-move command

| No Comments
I'm in the process of integrating the stand alone code that implements my 'multi-servo move' command and the rest of the controller. It's harder than it should be, probably because I'm not experienced enough yet with assembly language not to have made some school boy errors. Once again I've run out of registers, mainly because I'm trying not to have to push stuff on the stack that often. I've been juggling with the limited number of registers and up until now it's worked but...

The first problem is that I need two pointers to be able to do the data sorting for the multi-move command. Up until now all of the serial code has only ever had to use one pointer. I've been using X in the serial code and Y and Z in the PWM code. My usage of both Y and Z precludes me sharing them with the non interrupt code, which is a shame. The reason I can't share Z is because I'm using it to switch between modes in the timer interrupt; I use ijmp which is an indirect jump using Z to select which interrupt handler I need to call next time around. This is cute but hardly necessary... Removing this use of Z means that I can use it in the serial code as well, now that it's just a normal register pair I can simply push it onto the stack during the PWM setup code that also needs to use it for sorting... 

My second problem is that I should always have simply been pushing registers onto the stack when I entered the PWM setup routine. Instead I tried very hard to simply not share registers between the PWM code and the serial code. Unfortunately I've no pretty much run out and so have to go back into the code and adjust the register usage which is a pain and error prone...

Anyway, that's almost done now and, hopefully, the rest of the integration will be straight forward.

After the servo controller

| No Comments
The work on turning my excel spreadsheet into AVR assembler code which can move multiple servos to arrive at their target locations at the same time is proceeding well. I have the required code operating in a stand alone environment in the simulator and all I need to do now is merge that in with the rest of the code... Once that's done my servo controller is complete and whilst I already know that there are at least two further versions in the pipeline I expect I'll move onto something more before working on them. 

This weekend I sketched out some ideas for the next programming phase; the servo sequencer or 'gait controller'. This will initially be written in C++ and run on a PC but the design sketches that I did this weekend were for the microprocessor version. The plan is that the servo sequencer is a state machine which can be triggered off of servo move completions and timers. The idea being that it can be programmed to know about a sequence of moves (such as all of the moves required to move a 6 legged robot in a forward direction) and then be told to execute those moves. The sequencer would then manage this and other processors would deal with sensors and other decisions (such as exactly where the feet should land or exactly how the body should be angled, etc.). The sequencer can be isolated from these things a little by allowing the other controllers to tell it the detail (i.e. where exactly each three servos for each leg should be) but the sequencer itself would know that it moves this leg after that leg etc. Step length, step depth and body orientation can then be adjusted by tuning parameters that the overall sequence of steps uses without changing the state machine that makes the middle left leg move after the front right, or whatever.

My current ideas for the microprocessor version of the sequencer are quite ambitious; but then that's the point really, these micro projects should be pushing me forward and shouldn't be easy. The sequencer, since it's essentially a programmable state machine, needs to be able to allocate manipulate and free various blocks of memory; so it seems that I'll be building a dynamic memory allocator at some point. It will use interrupt driven serial I/O; and once that's done that work can be used for the next version of the servo controller. It will need to speak to the PC so that a PC program can be used to controller the sequencer (until the next level of robot controller is conceived and then designed) and it needs to talk to the servo controller to actually move the legs (this may be two UARTS; potentially meaning I have to create the second without hardware support and bit-bang the comms out, or perhaps serial to the PC and TWI/I2C to the servo controller). The sequencer's state machine is driven by the completion of delayed moves from the servo controller; so we can kick of a sequence by actioning the first state and then wait for the completion to trigger the state transition. Additionally a state transition can set a timer which can later trigger a new state transition. The programmable timer code will need to be developed... 

Deliberately the sequencer will not be limited to controlling blocks of 3 servos. This wont be a hexapod sequencer but a generic servo sequencer that will work with the servo controller that I'm currently finishing. So, theoretically when I want to develop l'arachnid once l'hexapod is complete I can 'simply' switch to using 8 x 4 servo legs and the same firmware...

However, the fact that I've now got to the point where I've almost completed a 'non-trivial' assembler project means that I feel I'm able to move the hardware side of the project forward. So I expect that once the servo controller is complete and whilst the sequencer is being designed I'll try and get to a point where I actually have the required number of servos and a 6 legged robot to control...

Moving multiple servos at once

| No Comments
The final command for my serial servo controller is the most complex. The idea behind it is that with a hexapod leg you will want to be able to move the leg to a new position where the new position requires all three of the servos that manage the leg to move to potentially new locations. Ideally you want the "foot" to arrive at the final resting place in such a way that all of the servos complete their moves at the same time. Since each servo will likely have to move a different distance from where it is now to where you want it to be and since I don't want to have to burden the code that determines how the legs themselves move with this kind of knowledge I decided to build the functionality into the servo controller. My design decision was validated somewhat when I discovered that the SSC-32 servo controller can do this.

I have all of the pieces in place to be able to do this, but as always, the devil is in the detail. To be able to move several servos from one place to another so that they arrive at the same time I first implemented commands that allow me to move servos at less than their maximum speed. This resulted in the two "delayed move" commands that are present in the current version of the servo controller. The idea is that you can have the servo controller take a target position and a step size and a step frequency and it will deal with moving the servo gradually from its current position to the target position (and then notify you when the servo has completed the move). This works well and the functionality is essential for getting the multiple servo "move as one" command to work. 

With the multiple servo command I need to take several new target positions and then calculate the required step sizes and step speeds for each of the servos so that they can all arrive at their target positions at the same time. For example, if we have two servos, both currently at position 0, and we wish to move servo A to position 100 and servo B to position 50 it's fairly obvious that B has to move half the distance that A has to move and as such if A were stepping 1 step every cycle then B needs to step 1 step every other cycle for them to arrive at their target positions at the same time. It's less easy to see that if servo B needs to move to position 77 then it needs to step with a step size of 3 every 4 cycles and that we need to fudge its start point by one step (of 3) to ensure that it actually arrives at the target (rather than at position 75).

Essentially we're interested in calculating the smallest common factors between the maximum distance that needs to be moved (by the servo that needs to move farthest) and the distance that this particular servo needs to be moved. I've currently got a spreadsheet that can calculate the required step size and frequency values for given distance pairs, which is a start. Translating that into assembly shouldn't be too hard now that I understand what's needed.

Almost there....

| No Comments
Work on the latest version of the serial servo controller is going well. I'd accumulated a pile of random nice to have ideas, some of which then necessitated some other ideas, and then there was the one must have command (the one which moves several servos to new positions over time and ensures that they all arrive at their final resting places at the same time). Of course I've worked on some of the nice to haves rather than dealing with the final 'must have' command that I need before I can move on to writing some gait controlling software to move multiple legs in sequence...

The current software includes the ability to turn off the PWM generation completely and to turn it on again; this is needed so that the 'save settings to eeprom' code can work as that requires that we don't have interrupts enabled. I've added the ability to set minimum, maximum and initial positions for all servos. These values can be saved to eeprom so the controller remembers them. I've also added the ability to adjust the 'centre' position for a servo; you can say that a particular servo is centred at 100 for example, rather than 127 and then talk to it as if you're moving relative to a 127 centre and the controller will adjust accordingly. There are also three controller configuration options that can adjusted via serial commands and saved to eeprom. The first determines if the PWM generation is turned on after a reboot. If not then you have to turn it on yourself with the appropriate command. The next determines if the controller sends out a 'get info' response when it starts after a reset (I figure that this will be useful for downstream processors, if the servo controller is reset these other processors might need to know!). Finally there's a setting which determines what happens if you try and move a servo beyond the min or max position. By default such a command is considered to be in error and you get an error response. You can change this so that such a command is OK but the command echo indicates that although you tried to move to, say, 201 the limit is 200... I've also added a command which resets the controller; mainly for testing at the moment. The get info command for this version returns more information so that you can tell what the state of the various programmable options are.

Now, I really should get on with the 'move multiple and arrive together' command...

New design please...

| No Comments
Having discussed my serial communication issues on AVRFreaks I've decided that I need a new design for the servo controller.

My current design is very successful in doing what I set out to do, which was to give priority to the PWM generation aspect of the code. We're generating a rock solid PWM signal. Unfortunately this is at the expense of the correctness of the serial communications and that just isn't right (although the original code had these same serial comms problems too!). 

I don't often experience comms corruption problems right now, but the system is relying on luck to some extent and that, IMHO, is a bad design. However, I don't expect for this new design sketch to see the light of day as actual code until after I've finished the final new commands using the old design and, perhaps, after I've actually moved on to building the 'gait controller' and I have something that walks (or perhaps have something that could make something walk if I had that mechanical part of the equation finished...).

Anyway; I figure that the only way for me to get the serial communications to be robust is to use interrupt driven comms. This needs to have a buffer that is bigger than a single command and it needs to use some form of flow control (and still expect to get more data arriving after signalling that the sender should stop to allow for latency in processing the flow control signal) and it should also deal with the buffer filling... Once I have that, I expect the comms will be as rock solid as the PWM generation as they can't be affected by the fact that we're processing a command that has already arrived. The serial comms will have priority over the processing of complete commands.

Unfortunately, getting to that point with the serial comms means that the PWM generation will no longer be rock solid as the serial interrupts could occur at just the point when the timer interrupt should occur for a pulse switch off and that will mean that the PWM signal lasts fractionally longer than it should and therefore has the potential to have a jitter... Although the timer interrupt will have priority over the serial interrupt a timer interrupt can still be delayed by the time it takes for the serial interrupt to complete its work. So, the PWM code will also need adjusting to work in a world where there are more interrupts occurring than just the timer interrupt that controls the PWM signals.

What I figure I should do is this... Firstly I need to factor into the design the fact that there are two kinds of timer interrupt. The first kind, the setup interrupt, is not time critical. The second kind, the pulse switch off interrupt, is time critical. The first kind of timer interrupt can turn interrupts back on as the first thing that it does; it will allow a nested serial interrupt to occur whilst it's processing. That way it doesn't block serial interrupts at all and therefore it doesn't affect the serial comms at all. There's plenty of time between the setup interrupt handler starting and the point when it needs to set the first 'switch off' timer and I'm happy (at this stage at least) to assume that this will work fine even in we're being bombarded with inbound serial data at max speed for the 9600 link. If I up the baud rate then we may need to get a bit more measurey with this part and work out if we can still assume that we have enough time to do all the required work.

The time critical 'switch off' timer interrupts could be protected by a new kind of non time critical timer interrupt... Basically we set the timer for X before the critical moment (where X is more than the length of time that it takes for a serial comms interrupt to process completely in the worst case scenario) and have the timer that goes off at that point turn off serial interrupts and set the new (time critical) timer. We then know that the time critical interrupt can't be delayed by anything (just like now). Once this interrupt is complete we can turn on serial interrupts again, process any serial data that arrived whilst interrupts were off and all is well. The devil is in the detail (as ever) and there will be some complexity in working out how to deal with time critical interrupts that occur very close together...

Once we have a design where there are only key periods when the timer interrupt can't be delayed we open the door on being able to disable interrupts completely at some points during the programs execution (i.e. at any point when a time critical interrupt is not pending). Obviously this would be just for short periods of time, but we can then update multi-byte data values by turning off interrupts globally (when we're allowed). This would allow us to let the serial protocol work in terms of actual pulse times rather than 0-254 values as we would be able to update current and target positions that take more than one byte to represent. A single bit which is set when the non time critical timer interrupt is configuring things for the time critical interrupt can then be used to build a spin lock to prevent global interrupts being disabled at the wrong time...

There's also one other point where serial interrupts must be disabled and that is when we're working with the new style serial command buffer that can contain more than just a single command. We will need to turn off serial interrupts to be enable us to remove a processed command from the buffer and shuffle any extra data to the front.

This design ramble was brought to you by several hours driving on the M25, followed by Wells Bombardier English Premium Bitter, some pasta, and an episode of House, so I reserve the right to throw it away when I'm sober... And now back to our scheduled development...

Flow control is the key?

| No Comments
I posted a question about my serial communications issues over on AVRFreaks and so far the answers have been pointing in the direction of including some form of formalised flow control. This makes sense. I've yet to decide if hardware flow control in a RTS/CTS form or software flow control such as Xon/Xoff would be best... 

Serial communications issues

| No Comments
I've just spent a while tracking down a but which ended up being in my PC based control software rather than in the serial servo controller firmware.

The symptoms of the problem were that my servo controller would suddenly to process random, poorly formed commands. My control software was sending commands correctly but it wasn't waiting for a command echo from the servo controller before sending the next command. Due to the design of the controller (i.e. the fact that we don't use interrupts for the serial communication code) if new commands are sent whilst the controller is processing the current command and before it has echoed it then serial data might be lost. This throws the serial command processor out of sync and hence my problems.

Changing the PC control software to wait for command responses before sending new commands has solved the immediate issue but I think I need to have a bit of a rethink about the serial communication design. Right now commands such as the various stop and query commands all do more work after they've echoed the command back. Theoretically the PC software could send a new command at that point and this new command might get garbled due to the fact that the servo controller is still processing the previous command and is not processing new inbound serial data. What's more the whole asynchronous movement complete response system also delays the serial read code.

I guess the best solution is to process the serial data input using interrupts, but that will potentially throw off my PWM generation code as a serial interrupt could be being processed whilst timer interrupt should be running... 
I was looking for information to help me decide which servos to buy for the legs; wondering about torque and cost and whatever when I followed a link to the Lynxmotion site and came across the documentation for their SSC 32 servo controller. Although I'd looked at their PC based control software that drives this servo controller I hadn't seen the docs for the controller itself. It seems that it has most of the features that my controller has, including the 'move multiple servos so that they get to the end at the same time' command that I'm working on now. They don't have the asynchronous move completion notifications that I have and I can see those being really useful for sequencing moves based without the need for timing loops (the completion of one set of moves triggering a state change which causes a new set of moves). They also don't have a 'stop!'  command that can stop a 'delayed move' mid stride and that's also something that I think will be really useful; especially once I have sensors working...

Their docs include a schematic which shows that they're doing things in a pretty similar style to the way that I decided to do things; multiple mux chips and an ATMega168 at the heart of it all. I guess that their mux chips are active low, as they seem to have pull up resistors on them...

It's quite nice to come across this now and to see that I've managed to get this far on my own :)

About this Archive

This page is an archive of entries from October 2009 listed from newest to oldest.

September 2009 is the previous archive.

November 2009 is the next archive.

Find recent content on the main index or look in the archives to find all content.