Results tagged “embedded assembly programming”

Redesigning the servo controller firmware

As I mentioned here, there's a fundamental design problem with the two versions of the ATTiny2313 servo controller firmware that I've presented so far (see the 8 channel source code and the 64 channel source code). The timing that determines the shape of the PWM signals that are generated relies on carefully crafted timing loops and the time taken by particular code sequences and this is affected by the interrupt driven serial I/O that is used to control the controller. Each time a command comes in on the serial port the PWM generation code is interrupted by the serial code and the timing of the generated pulses is affected; albeit slightly.

My proposed redesign moves the PWM generation code off to a timer interrupt and has the serial  I/O dealt with by the main loop of the program. This means that the PWM generation code always runs at a higher priority than the serial I/O code and the resulting pulse train should be rock solid. This will become more and more important as the code required to deal with the serial commands becomes more complex as more complex commands are added.

I've decided to split the work into two smaller projects. The first will deal with generating the PWM pulse train from the timer interrupt. The second will deal with the serial port processing code that will configure the data that is used to generate the PWM signals. Yesterday I started on the timer code... 

The structure of the new code is considerably different to the old code; this isn't too surprising. The old code operates on 8 banks of 8 channels. The signals for each bank of 8 channels are generated at the same time and generating the signals for 8 channels takes < 2.5ms. Generating the signals for all 8 banks takes around 20ms so that each signal is repeated 50 times per second. Ideally, each signal should be between 900us and 2.1ms long and the old code initially starts producing a bank of signals by setting all 8 channels on and then wasting time in a timer loop. We then enter a loop which executes 255 times and which checks the servo control data and turns off each signal as the corresponding control value is reached (see the 'PWM mark' section of the source code, here, for the detail). Due to the number of operations that we need to execute, the number of times that we need to run the loop and the speed of the processor's clock we end up with a range of possible pulse lengths between 579.25us and 2420.75us with a mid point of 1500us.

The new code also operates on 8 banks of 8 channels and still generates the 8 signals in < 2.5ms so that all 8 banks can be processed in 20ms to give the required 50hz refresh rate for each signal. However that's pretty much where the similarity ends. The new code does all of its work in response to timer interrupts. There are two distinct actions that occur during these timer interrupts. The first is the setup phase which is performed during the initial 900us 'on time' that all signals in all banks have. Once setup has completed we enter the 'pulse stop' phase during which the timer is used to tell us when to stop each signal. Once all signals have been stopped we wait for the setup phase for the next bank of channels. Rather than wasting time with a timer loop the first thing that we do once we've switched all channels on during the setup phase for a bank of channels is to set the timer to go off in 900us time and then to start calculating the off times for each of the channels and sorting them into ascending order. The result is a list of up to 8 sets of times and channel data; up to 8 rather than exactly 8 because multiple channels may stop at the same time and so we only need one time for all channels that stop at the same time Once the setup phase is complete we extend the timer, if need be, so that it will next fire at the first 'off time' for the first channel, or bunch of channels. The next time the timer fires we will be in signal termination mode. This mode is simple, we update the output ports to turn off the signals that are no longer being generated and set the timer to the next 'off time'. Once all signals are off we set the time one last time and switch back to setup mode. 

At least that's the plan. I have about half of the code working and the interrupt driven nature of the code means that I have much more time to do things than I did with the polled version. This is good as it means that there's time available to be processing serial configuration changes, etc. The most time that we gain from this new method is the big block of time that we were originally wasting in a timer loop; now we only use what we need to use during the setup phase of the signal generation and the rest of it is free for us to use for serial I/O handling.
Here's the source code to the 64 channel ATtiny2313 servo controller. Note that you'll need to use up to 8 CD74HCT238E, or equivalent, demultiplexor chips and that you can adjust the number of servos that you can control in steps of 8 using as many or as few CD74HCT238E chips as you want. If you only want 8 channels then you can do this without any demultiplexor chips; see here for source code to the 8 channel version.

The controller uses the 'standard' three byte serial "SSC" protocol of 0xFF <servo> <position>. Where <servo> is a value between 0 and 63 (or as many servos as you decide to support) and <position> is a value between 0 and 254 where 127 gives a pulse length of 1500us. At present the timing gives us pulses of 579.25us for 0 and 2420.75us for 254. This is a greater range than we really need (at least for the Hitec servos that I'm using) but the issue here is the number of instructions required to check each pin each time through the loop. A faster clock speed would allow us to perform the real work faster and add a delay into this loop to allow us to fine tune the range a little better... Perhaps... Anyway, I hope to move away from the current design shortly to one that gives a finer control over the pulse lengths.

Source code can be downloaded here.

64 channel servo controller...

I've been experimenting with the servo controller that I developed for the ATtiny2313 here and the demultiplexing chips that I mentioned here. The result is a 64 channel servo controller that seems to work pretty well. Right now I haven't breadboarded all 64 channels, I have two of the CD74HCT238E chips connected to the ATtiny but I/O pins and he firmware would drive 8 of them if they were connected to give 64 channels. Of course this is much more than I actually need for this project, but since there were enough I/O pins and since it was actually easier adjust the code from driving 8 channels to driving 8 x 8 channels I coded it up that way to see if it would work... It does, though I've had to adjust the timing loops considerably and I've switched from guaranteeing a min 900us pulse and a max 2300us pulse to simply making sure that the middle servo position is correct; this actually seems to be a better approach as I now have a clean 180 degree range of motion from the servo and the centre position is pretty much spot on where I would expect it to be...

Of course now I'm approaching the memory limits of the ATtiny. I only have 128 bytes of SRAM to play with and I need 64 of those bytes for the servo position data and a few bytes for the serial data input buffer. This means that my plans to have a mapping table that maps the servos from the physical location of the pins on the board to a nice sensible, easy to remember, logical sequence can't be implemented in this version as in itself it would require 64 bytes of memory to map the logical servo id to the physical  servo control pin. The reason this would be handy is that with 8 demultiplexing chips on a board and all the associated connections the chances of getting the servo control pins to all end up in a nice sequence somewhere is pretty slim. All's not lost, the firmware itself can be tailored to match the board layout but this isn't quite as elegant as providing a simple mapping table that can be updated easily to change the mappings.

Of course I only selected the ATtiny2313 by chance as it happened to be a available for a good price and was in stock at one of my suppliers so running out of resources on it isn't such a terrible thing for the project. However, ideally I'd like to squeeze as many of my ideas for the more advanced servo controller onto it if at all possible. This should be made somewhat easier as I only need 24 channels, which gives me around 5 bytes of storage per channel rather than the less than 2 that I have with the 64 channel controller... And if all else fails I could 'cheat' and switch to using one of the ATMega168's that I have laying around... 

Schematic and source will be available once I've worked out how Eagle works and once I've finished the firmware...

The jitters and general instability of the hacked together simple servo controller (see here) for the ATtiny2313 were, it seems, down to the fact that the internal clock wasn't stable enough and this caused enough timing issues to throw the PWM off enough to jiggle the servo position around rather than hold it steady. This morning my external 4MHz crystals arrived and after changing the processor's fuse bits to accommodate the change in clock source everything was fine and the code started behaving itself.

The source code for the servo controller follows; as mentioned before, it's a) pretty simple and b) heavily based on the code that I found here for the AT90S4414. It may be better to adjust it to work at an RS232 baud rate friendly clock speed, but I expect I'll move away from the hard coded timing loops as soon as possible if I can and this might make such clock speed adjustments slightly easier.

The next step is to expand this to use one (or more) of the demultiplexor chips so that I can expand the number of channels supported. 

Atmel ATtiny2313 Servo Controller v0.1

This morning I tested the first version of my custom servo controller. It's currently heavily based on the source for 16 channel servo controller that I've mentioned before. What I did was simply adjust that source for my target processor and the fact that I'm using it with a 4Mhz internal clock rather than the more standard "RS232 baud rate friendly" 3.6864 MHz that the original code used.

The main changes were to the reduce the controller from 16 channels to 8 since the ATtiny2313 doesn't have enough pins to run 16 channels and to adjust the general setup to account for the new processor. The timing loops also changed a bit due to the clock change. Since I'm working from the other code as a base my code generates the PWM signals manually in the same way that it does. Although we have custom hardware inside the ATtiny2313 that can generate PWM signals for us the manual approach seems to work at the moment. Things may well change as I get a better understanding of what I'm doing.

I initially had some problems because although I'd set the code up for a 4Mhz internal clock and simulated with that I hadn't changed the fuse bits on the processor so it was still running with the default 8Mhz internal oscillator with a divide by 8 to give a 1Mhz clock. The first hint that something was wrong with the timing was that I could see the test LED on one of the servo channels flashing rather than being solidly on; the frequency of the refresh of the pulse should be 50hz and that should look like the LED stays on rather than the flashing that I was getting at the 13hz or so that I was actually getting as a refresh. Once I calculated and set the new fuse bits (using this online calculator) the LED stabilised and plugging a servo in gave the expected results.

Since the servo controller uses the 'standard' three byte serial "SSC" protocol of 0xFF <servo> <position>, my existing control software works fine with it.

Right now the pulse frequency seems to be slightly off, which makes the servo vibrate in position a little. I'm not sure if this is down to my timing (which I expect it is) or if it's because I really should be using a more stable external clock.

Once I've tested this a little more and have something that's a little more stable I'll upload the source. What I have now is a good base for the larger capacity controller that I'll need. One of the next steps will be to reduce the number of I/O pins needed whilst increasing the number of channels supported by adding in the control of  the address lines of some 74xxx138 multiplexors which can be wired into the I/O pins that I'm using for the actual PWM output. Another thing on the list is to enhance the serial protocol, and then the controller itself, to support the various ideas I have:
  • Smooth blending between 'key frame' servo positions with the ability to stop the servo at any time before it reaches its final position.
  • Storing of multiple 'key frames' per servo so that the leg controller/gait sequencer can hand off a sequence of moves to the servo controller to blend.
  • Grouping of servos, so that we can have the blending apply across servos in the group (all 3 servos that make up a leg can arrive at their final 'key frames' at the same time).
  • etc...
Though the current servo controller design is very simplistic it seems to be a good base for these other requirements and, well, at least I now have AVR assembly code that I can build, simulate, program a device with and that actually seems to do what I expect it to do; that's a major step forward.

AVR Studio 4

I've been spending some time getting to know AVR Studio 4, especially the simulation and debugging functionality. It's a very functional and very useful free development suite that can be downloaded from Atmel from here.

As I mentioned a while back I have had a C compiler and development and programming tool chain set up for a while now but I hadn't got around to working out how to debug the code. AVR Studio allows me to simulate (or debug with an ICE if I had one) and is very powerful. So far I've only debugged assembly code but from the docs with WinAVR (my source of the C tool chain) imply that I can use AVR Studio to simulate and debug C code too as long as I get the debug info format correct.

The great thing about simulating the microcontroller is that I can simulate chips that I don't have (which is convenient as I have been stepping through the code for the 16 channel servo controller that I mentioned here (code available from this article, here)). At first I thought I'd need to port this code from the Atmel AT90S4414 to something that I have, but, of course, I can simply run this code in the simulator. The simulator is very powerful and shows the internal state of the microcontroller and lets you fiddle with 'virtual' input pins and watch the values of the outputs, etc. I've found that it's made it much easier to start to get a grip on AVR assembly language as I simply step through and watch the code as I would C++ on my PC. I'm currently wondering about how one goes about unit testing such code (I'm rather big into Test Driven Development and Unit Testing in my day job and would feel more comfortable writing code for my micros if I could bring some of those disciplines across with me). It seems that there's no standard solution for the AVR (at least none that the guys on AVRFreaks use) although these ideas look promising. Obviously it would be easier to test C code as there's a chance of building it on the host PC...

So far I haven't written any AVR assembly language myself yet, but I have several pages of notes and ideas as to how I can take the ideas that have been presented in the AT90S4414 servo controller code and write the servo controller that I feel I need for my robot. 
  1 2