I previously decided that a CAN interface would be the best used in my robots. But this was a couple of years ago, when I restarted my hobby it took me a while to understand the complexity of the solution I developed. It was working perfectly, but the main reason for selecting this was that each processor could directly communicate with each other processor. In practice this was not used, the typical architecture remains to be a brain (master) and many limbs (slaves). So a more simple solution like using an RS485 network can do the job, this allows me to use many other, smaller controllers as well who do not have a CAN interface and the software solution is much more simple.
While doing some basic experiments for the charger I also connected the RS-1501 servo to the AT90CAN. Driving the servo is simple using a PWM output that pulses every 20ms (50Hz). If the high time of the pulse is 1.5ms, the servo is in it’s center position. If decreased to 1.0ms is turns 45 degrees clockwise, if increased to 2.0ms it turns 45 degrees counter clockwise.
Although this works fine and the servo is very powerful, I came to the conclusion this will not work for SnakeBot. The main reason for this is that the speed can not be controlled, only the position. In SnakeBot there will be multiple segments connected together, while moving each segment makes a different angle compared to the others. When moving forward, each servo needs to make a different “delta-angle” in the same amount of time depending it’s position on the path to follow. For this, the speed must be varied since the time available for each segment reaching it’s new angle is exactly the same. Segments that must travel a larger angle must move faster than segments that must travel a small angle (or no angle at all).
This changes the whole mechanical, electrical and software concept of a segment…. Instead of a servo now a simple DC engine can be used with a large gearbox. This can than be driven with a H-bridge using a PWM signal so the speed can be varied. Conrad has a nice power 6V engine engine with a 50:1 gearbox attached, part number is 233130-89.
This engine delivers a torque of 588Ncm at 116RPM of the 6mm shaft while consuming a maximum of 1.8A. 116 turns per minute is about 2 turns per second, based on the servo selection a speed of 0.75sec per 60 degrees would be fast enough allowing a further reduction in speed by a factor of 9 and so increase the torque by a factor of 9 to 5292Ncm! The size of the engine is relative large, it’s length is about 9cm and the shaft is not at a 90 degrees angle. This means the engine should be placed vertical make the segment at least 10cm in diameter making SnakeBot look like an Anaconda! It might be an option to place it horizontal and use two 45 degrees angled gears, to be investigated later. Position can be measured using a potentiometer and an analogue input, there are types available with a metal shaft of 6mm so this can also be used for the axis of the last gear.
Since this setup will allow the speed of the engine to be varied, the question is how to inform the segment that it should move at what speed. In nature, the brains will make the decision to move forward to a target position with a target speed. The Primary Motor Cortex (PMC) will translate this decision into several signals to each individual muscle that is involved, this is done by sending an electrical signal called action potential (spike) through the nerve. When it reaches the end of the nerve it releases a neurotransmitter that triggers a series of events that finally result in contraction of the muscle. For skeletal muscles the force exerted by the muscle is controlled by varying the frequency at which action potentials are sent to muscle fibers. When the frequency changes, also the force changes.
This same principle can be used for the segments, however driving each segment with a frequency modulated signal using the RoboCAN interface would not be very practical. But if we take a look on how the pulses vary during a contraction we can define three phases:
- An increase in pulses from 0Hz to “x”Hz resulting in an increase of force
- A constant frequency of “x”Hz resulting in a constant force being applied
- A decrease in pulses from “x”Hz to 0Hz resulting in a decrease of force
These phases combined result in moving a part of the skeleton (for example a finger) from one location to another location, so frequency does not translate into position, it translates into force applied and so finally speed depending how much resistance is encountered. When resistance is encountered, the speed decreases and might totally stop. The brain monitors the speed and determines that the speed is not what was required and as a response it will increase the frequency of action potentials being fired resulting in a larger force applied until the speed is at the right level. With me so far?
If you are than you will agree that frequency is equal to torque applied by the engine in the segment. And torque translates into current. So basically the PMC determines what the speed must be, the segment starts driving current trough the engine that normally matches the speed. If resistance is encountered, the speed will decrease and the PMC should give the command to increase the current.
Here it might be more practical to have the feedback loop implemented on the segment itself. In practice the PMC has the target to “go there” no matter what resistance is encountered. Only when too much resistance is encountered it might be better to inform the brain it should try something else. Based on this concept the PMC sends the command to increase the speed to “x” in time “y”. The segments starts driving the engine by increasing slowly the current and continiously checks if the speed is reached, if not more current is applied. When it overshoots the current is reduced and so on. When the speed is still not met while the current is at it’s maximum than it should inform the PMC that the command can not be executed but continuous to hold the current until the PMC gets the instruction from the brain to release. When it overheats in the mean time than the segment can protect itself by releasing, same as in nature. Your brains can tell you to hold your fingers firm when hanging over a cliff but when they overload they will release no matter what the brain tells them to do.
What would happen if the segments reaches the speed and no new command is received from the PMC? In nature that would mean that the PMC did not determine what to do next so it continues to fire action potentials. The force applied only changes when the PMC decides to change the frequency, so in theory this would mean the speed is maintained until the PMC gives a new command. That would not work for SnakeBot since it can be that the PMC is occupied with something else. In nature this will not happen since than the PMC is truly multitasking while the micro controller implementation can not. As a result the absolute position of the segments angle is no longer guaranteed. If the PMC calculates the distance in angle to travel and splits this in three parts as mentioned above and sends it to the segment in one command it might work since at that time the segment can compensate by itself the actual speed required versus the given target speed. For example, if the ramp up takes a bit more time since more resistance was detected, the time at constant speed can be reduced a bit so the segment is back on track when it is time to decrease the speed.
Another option is that the PMC only gives command: go to that position “x” within “y” seconds which is in fact force since Force is mass times acceleration (Newtons law). If the delta in position is large, the acceleration required to be there in time “y” is high. Since the mass of SnakeBot is constant, the force is in direct relation to the acceleration and so the required delta distance in time. The segment than executes this acceleration using the same feedback algorithm to increase (or decrease) the speed until it reaches position x. If no new command is given, now it should continue to move at that speed. If the new position is than given with a small delay the segment can compensate the acceleration so it still reaches the newly required position on time.
This option might work the best since I feel this will make the most natural movement possible. The brain decides to move forward ”x” cm in ”y” seconds of time and informs the PMC. Based on the distance and the allowed time the PMC defines three time periods:
- t0-t1, increase the speed until position p1 is reached
- t1-t2, maintain speed until position p2 is reached
- t2-t3, decrease speed until position p3 is reached
This can than be translated into actual angles for each segment at times t1, t2 and t3 since it knows the current position of each segment and the final position of each segment based on the vector models for movement as defined before. Than it is a matter of sending combinations of (p1, t1) to each segment and when t1 is reached sending combinations of (p2, t2). When the time t2 is reached it becomes interesting, if the brain gives the command to move further forward at the same speed than the PMC should re-calculate above mentioned combinations and skip sending (p3, t3). At constant speed the combinations (p1, t1) are equal to (p2, t2) since no ramp up is required but that is still fine for the segments. (p3, t3) will be of a different value (slowing down) since the PMC was told to go to a specific location. If the brain than tells again before reaching t2 to move forward this will repeat itself over and over making the movement constant in speed.
Changing speed works similar, the brain simply changes it’s mind and informs the PMC at any given time of the new position and time. The PMC calculates again the combinations and the segments will adjust the speed accordingly based on the speed and position they have when receiving the new instruction. Simple, right? In all fairness, I’m not sure I’m right here but it seems logic enough to give it a try.
It makes sense to use units that available in the segment for defining position and time, this will prevent complex conversion calculations. For example, the angle is measured using a potentiometer connected to the reference voltage of the analogue to digital converter, the variable voltage from the potentiometer is than measured with a 10-bit resolution resulting in a value of $000 to $3FF. By defining the required angle in an value between $000 and $3FF instead of degrees the segment does not need to convert anything. The potentiometer has a rotation angle of 300 degrees, so $3FF/300=3.41 units per degree rotated. Since only 90 degrees are required the voltage from the potentiometer can also be multiplied by 3 giving a beter resolution of the measurement. This does however mean that the potentiometer must be adjusted in the segment so it is in it’s zero position when the angle is -45 degrees (0V, $000) and reaches 90 degrees turn when the segment is is at 45 degrees ((Vref/300)*90)*3=$3FF. With a bit of tuning the offset and multiplication factor this can reach 10 units per degree rotated.
For time, RoboCAN executes a maintenance routine every 25ms. Due to the resolution of the timer in the AT90CAN, this routine is called every 12.5ms and in the routine it is skipped every other call. When it is not executed, a call can be made to a function that is part of the segment that adjust the speed and checks for overload and if the target position is reached. As such, a time interval of 25ms is achieved and the CPU load is balanced: t=0ms: RoboCAN, t=12.5ms: the engine, t=25ms: RoboCAN, t-37.5ms: the engine and so on. By defining time in units of 25ms also here no calculations have to be made.
So, bottom line, the segment will only implement one command for movement. This will take two parameters: target position ranging from $000 to $3FF and target delta time in units of 25ms. The segment will try to execute this within its safety limits.
As mentioned, the engine will be driven using a PWM signal were the frequency is constant but the pulse varies in width. By experience, I find that most smaller engine like higher frequencies better than lower, using a 20kHz frequency has the additional benefit that you can not hear the engines making a high pitch noise.
A standard H-bridge uses 4 FET’s that each have a very low impedance, the lower the better since resistance means voltage drop which means heat being produced. Selecting a low resistance like 0.1E prevents the need to add heatsinks. I will use the FQP19N10 and the FQP27P06, in principle an overkill on allowed current but these are standard in my stock. Based on the the test schematic looks like this:
Q1 and Q2 are used for switching on and off the power supply, only one of them is open at the time and is kept constant and not driven by the PWM signal. Main reason for this is that it is not required, if a PWM signal is applied to Q5 or Q6 the engine connected to J1 can be varied in speed. Anything that switches causes electrical noise which should be prevented. Secondly, P-channel FET’s have different switching behaviors than N-channel FET’s, N-channel FET’s are typically faster, by keeping the P-channel always on the N-channel FET now controls the engine. Needless to say only the pair Q1/Q6 should be used or the pair Q2/Q5, so special care must be taken by software to prevent a short being created when reversing direction.
The coil in the engine tries to resist when power is applied, when the voltage is removed is resist again in the opposite direction. This results in voltages spikes of 20 to 30V on the power supply! To prevent these spikes, several counter measures are implemented. It starts with capacitor C1 which acts as a short for high frequencies, that is the best action, short it at the source. The diode D1..D4 are fast switching schottky diodes, they prevent that the reverse voltages generated by the engine coil destroy the FET’s. The filters created by R5/C2 and R6/C3 counteracts the coil, the values depend on the characteristics of the engine and are tuned by experiments. Measurements show that the spikes are almost eliminated by this setup, it is reduces to +/- 0.2V which is a job well done.
The 4 FET’s 2N7000 are used to drive the high voltage required for the engine FET’s from the low voltage AT90CAN outputs. When the AT90CAN is held in reset all pins are input, the gates of these 4 FET’s are all tied to ground using 10k resistors so that Q1/Q2 are closed and Q5/A6 are open. As a result both poles of the engine are tied to ground keeping it in brake mode and as a safe starting point until the software has bee initialized.
Resistor R11 will be used in a later stage to measure the current running through the engine.
Step one is to put this schematic on a piece of breadboard, step two is to write some basic code and step three is to build a mechanical setup for holding the engine and drive a larger gear that is connected to a potentiometer so position can be included to provide feedback.
As defined before, the each segment will have it’s own battery pack using NiMH cells. In order to define the best option for charging them, I will investigate the application note from ATMEL to see if this concept can be used.
When reading thought the application note I wrote down some items:
- NiMH batteries are charged with a constant current of 1*C
- To determine if the battery is full, dV/dt is used with temperature and absolute voltage as backup
- Trickle charging is done using a current of 0.025*C for maximum 90 minutes
- Charging is allowed between 5 and 40 dC
- Maximum voltage is 1.5V per cell
- Charging is stopped if dV/dt is 0 and/or dT/dt is 0
In any case the charger can not exceed 1A so that approx 15 segments can charge in parallel. This would already mean a power supply that can deliver 16A at roughly 10V so 160W. A current source is easy to build using a PNP transistor or even a LM317, however they both have a problem in dissipating the over voltage, specially when the battery is empty. In that case the regulator must dissipate 10-(5*1.1)=4.5V*1A=4.5W which will require some kind of heat sink.
A better option is to use some kind of PWM based current source, to prevent power being dissipated (and so wasted). This is indeed what the application note uses also in a very elementary form, it basically increases the voltage over the battery while measuring the current. If the current is too high, the PWM frequency is lowered so that a storage capacitor is charged less and in return the voltage is reduced.
This would mean a loop in software that continuously measure the current and adjusts the PWM. Since the current will not vary rapidly this does not have to be done extremely fast and non-interruptible. There are two micro controllers discussed in the application note which both use the same principle but with a different PWM frequency. The lower the frequency, this higher the storage capacity must be and the higher the switching coil must be. I tend to use so higher frequency, but this needs further investigation.
For measuring the current a series resistor is used 0.25E and the voltage drop over the resistors is measured. It might be an option to use this as well for measuring the discharge current, for this it will be needed to measure positive (charge goes in) and negative (charge goes out) voltages. This could be done using an amplifier that amplifies the voltage drop with an offset but perhaps a smarter one is to measure the voltage just before the resistor and directly after the resistor just before the battery and in software subtract the two measured voltages. This has the benefit that the exact voltage of the battery is known as well allowing a second option for cutting of the charger or the consumer in case the battery is almost empty.
- The AT90CAN has a 10-bit ADC, using the internal 2.56V reference this gives a resolution of 2.5mV per bit
- Using a 0.25E resistor and a voltage drop of 2.5mV this means a resultion of 10mA
With the resolution of 10mA per bit this means a current range of 0..10.24A, this is an overkill by a factor of at least 4. The 10mA will also not be 10mA due to noise and tolerances, typically the first bit should be ignored giving an resolution of 20mA. If the factor of 4 somehow can be used this can be decreased to 5mA which is a very nice resolution. Question is how to multiple the drop by a factor of 4?
One option is to increase the resistor to 1.0E. At the maximum discharge current of (estimated) 2.5A this will result in a voltage drop of 2.5V which is not nice… The other option is to use the amplifier again, in that case we still need to measure two voltages; one is the drop of the resistor and one is the actual battery voltage to see if it is almost empty.
I have to admit that analogue design is not my strongest point so designing this amplifier + offset circuit will go with some trial and error although the basics of OPAMPs are known to me. The most simple model I always use is that an OPAMP tries to match the voltage on the + input and on the – input. If they don’t match it will increase the output voltage if the + voltage is higher than the – voltage and reduce the output voltage if the – voltage is higher than the + voltage.
We need an difference amplifier as indicated on page 3 of the basics, in theory this is nothing else than an non-inverting amplifier with the difference that the ground to which Vout is increased is no longer the fixed ground but now a variable ground. Problem still is that Vout can only follow the positive voltage drops. Assume V1=V2=0V, than Vout is 0V. Assume V2=1V, than Vout is increase until the voltage on the – input is the same voltage as on the + input. Assume V2=-1V, than Vout should decrease the voltage but that is not possible with only a positive voltage supply. So the trick here is to generate a Vout of (2.56/2)=1.28V when V1=V2. Since Vout is increased this means that the voltage on the + input must be 1.28V in this case, so in short, we must add 1.28V to V2 or substract 1.28V from V1.
Assume R1=R2=R3=R4=100E, than when V1=V2=5V the output voltage should be 1.28V. As a result, V- is (((5-1.28)/(100+100))*100)+1.28=3.14V. That means that V+ is 3.14V as well (remember the amplifier tries to make them the same). The current through R3 is than (5-3.14)/100=0.0186A and the voltage drop over R4 must than be 100*0.0186=1.86V requiring the ground not to be 0V but 3.14-1.86=1.28V. That is funny, the 1.28V “virtual ground” matches exactly the offset of 1.28V…..
The amplification factor is given by R2/R1 if R1=R3 and R2=R4. Using a 0.1E resistor with a current of 2.5A results in a voltage drop of 0.25V. If we use 1.1V as the voltage to measure at 2.5A this means a multiplication factor of 1.1/0.25=4.4 times. The impedance “seen” by the 0.1E resistor should be very high to prevent influence of the voltage drop by the measurement circuit. Something in the range of 200 to 400k should be OK (gut feeling). When the resolution must be 10mA by not using the least significant bit this would mean a maximum measured current of (1024/2/2)=256*10mA=2.560A (+ and -). Using a resistor of 0.1E will cause a voltage drop of 0.256V at the maximum current. This should translate into 1.28V offset to “the virtual ground” at the output of the amplifier so a multiplication factor of 1.28/0.256=5.00 times.
Although above schematic looks simple there are some problems to solve:
- The 1.28V voltage supply should be able to sink current instead of supplying it
- The 1.28V voltage should be 50% of the internal 2.56V reference in the AT90CAN
An amplifier can source and sink current, so that can be used for the 1.28V power supply. The 50% can be created by using that same amplifier wired as the image below.
Since R3=R4 the V+ is 1.28V, the output is shorted to the V- and the rule dictates that the amplifier increases the output voltage until V- is V+ hence the 1.28V output. The 2.56V is available inside the AT90CAN but not outside so this can not be used. Instead an external reference diode needs to be used for generating this voltage in which case it can also be applied to the AT90CAN so that we are sure we the offset is always 50% of Vref.
One thing to keeo in mind is that most amplifiers can not generate an output voltage above Vdd-1.5V, some of them can (rail-to-rail amplifiers) but most of them don’t. They can also not work with voltages on V+ and V- that are above Vdd-1.5V, so if the 0.1E resistor was to be connected to the plus of the battery which produces 5*1.2=6.0V and the amplifier is running at 5V you can not work with the voltage drop over the 0.1E resistor since one side is 6.0V and the other side a little bit less; both more than 5-1.5=3.5V. Instead the 0.1E resistor should be connected to the minus of the battery which results in a “virtual” negative ground. This will become clear later.
The snake will consist of many segments that are in principle all the same as previously defined when investigating possible battery solutions. Below the block diagram which was defined at that time.
Each block needs to be investigated further, let’s start with the servo that will act as the muscles. I have no idea what kind of force the servo must be capable of producing. I can try to make some educated guess, but perhaps the most practical is to use the ones with the highest force and get hands-on experience of the actual torque generated during movements. Searching the internet shows the most simple one is T=Kt*I were the Kt stands for Torque Constant and I for the current but there is a lot of discussion on this.
Searching thought the many, many servo’s at Conrad, there are two main topic to decide upon. Since a servo works with a small DC-engine and a gearbox, the way to increase torque is to make the gearbox ratio larger. As a result, the possible torque is increased but the turning speed is decreased. Second topic is the material used for the gears and bearings. If it is plastic, at high torque the teeth of the gears will simply snap. Same for the bearing but than more wearing out of the bearings that finally leads to the problem that only the top of a tooth is used from the gear which again results in breaking the teeth.
For speed, there are special servo’s used for controlling the lines of a sailing boat. For example the SW1200 can supply a lot of torque (115Ncm) but are slow (1.6sec for 360 degrees or 0.26sec for 60 degrees). Since they can turn 360 degrees it seems there is no stop and they can spin around several times which makes sense if you are pulling a line. Improving speed will decrease torque to 72Ncm but increases the speed to 0.1sec for 60 degrees.
I worked with the AK-12digital servo’s in the past, they provide a torque of 160Ncm and a speed of 0.2sec for 60 degrees and I was not impressed with the torque they delivered. The most powerful servo that Conrad has available is the RS-1501 that provides 180Ncm of torque and a speed of 0.2sec for 60 degrees, I’m not sure this is enough.
Another option is to use a DC-motor and a gearbox in combination with some position measurement. Based on a snakes vertebrae, it can make an angle of + or – 20 degrees compare to the next. However, my vertabraes will be longer so this should be a bit bigger, assume + and – 40 degrees. Than moving forward using a sinus pattern were ones per second the servo would go from far left to far right would mean a minimum speed 1 sec per 80 degrees or 0.75 sec for 60 degrees.
While writing this (and that is exactly why I write this) it might also be an option to use the SW1200 and add an extra gear on to of it to reduce the speed by a factor of (0.75/0.26)=3. That would mean the circumference of the large gear would be 3 times larger than the small gear. Assume the small gear has a radius of 1 than the circumference is 2*PI*1=2*PI. Than the large one has a circumference of 6*PI so the radius is 2*PI*R=6*PI, so R=3…. Force = mass * arm length, so when the radius is 3 times longer than the force that can be applied is also 3 times longer. Normally the force is less when the arm is longer but here the gear reduction reduction increases the force. Not sure that all above is correct but this would mean that the than the force of 115Ncm can be increased to 345Ncm.
The other option is to use the RS-1501 in combination with an extra gear. The speed is 0.2 sec for 60 degrees so the reduction can be (0.75/0.2)=3.75 or 3 for short. Than using above assumption the force can be increased by 3 to a massive 540Ncm. It would require a modification of the servo since it can not turn 360 degrees but that should not be a problem. Most of them use a potentiometer connected to the axe of the final gear to give position feedback. It would be simple to use a new potentiometer for the larger gear and feed this back to the original control. Since this servo also uses metal gears and bearings I will order one to do some initial experiments.
I like LED’s that indicate activity, also here a 90 degrees bended version should be used. Next to this, the type to be used should be a low current version to reduce the amount of power consumed. There are various types available, in 3mm and in 5mm. They use 2mA of current compared to normally 10 or 20, they 5mm versions have an extra mechanical snap which I guess is good for robots that run around all the time, so I will uses the 5mm versions for now.
There are two signals to monitor; the CAN frames that are send by the PMC and are address to the PMC and the commands from and to the FOXBoard. For the first one, the ATDVK90CAN1 board has two LED’s tied directly to the TXD and the RXD lines. This is not what I want since in that case it shows any CAN frame while I’m only interested in RoboCAN commands that are processed by the PMC. This will require an extra out of the AT90CAN and a software modification in the RoboCAN library. The second one is already implemented.
For the micro controller, there are three AT90CAN’s available were the main difference is in memory and price:
- AT90CAN32, 32KB Flash, 2KB SRAM and 1KB EEPROM, 6.89 EUR at 10 units
- AT90CAN64, 64KB Flash, 4KB SRAM and 2KB EEPROM, 7.36 EUR at 10 units
- AT90CAN128, 128KB Flash, 4KB SRAM and 4KB EEPROM, 11.92 EUR at 10 units
The safest choice in life is always the middle one, I guess also here. Compiling the Primary Motor Cortex basic implementation that can interface with the FOXBoard and has the full RoboCAN protocol added uses 20KB of Flash and 1.6KB of SRAM. The AT90CAN32 can be used if not much additional application is added that uses variables as well. For only 0.47 EUR you can twice the amount of SRAM and FLASH, so I guess that is not worth taking the risk that the application just runs out of memory which would mean removing the part and soldering a larger on on the board. Time consuming and you risk damaging the board.
The cost adder for the AT90CAN128 is significant while only increasing the amount of FLASH. At the time of writing this, the number of code lines are:
- CAN.c, 576 lines
- RoboCAN.c, 885 lines
- FOXBoard.c, 340 lines
- Primary Motor Cortex, 213 lines
Lines of code do not directly translate to number of bytes, but for a rough estimation of how many lines the application can contain it will do: (20KB/(576+885+340+213))=10 bytes per line. 44KB/10=4500 lines of code. The largest application I wrote contained 3530 lines of code and there I was close to of-loading sub-systems to secondary micro controllers due to the complexity of the product. So using the AT90CAN64 is a safe choice for the PMC.
In order to connect the JTAG to the AT90CAN64 a 2×5 header needs to be used. Also here a 90 degrees bended version should be used so that any code can be updated later or any micro controller can be debugged later while no parts of SnakeBot have to be disassembled.
Combining all above results in the first version of the schematic. I will develop the PCB later, first the schematic should be on my desk for some days to review it several times.
Before the electrical design can start some items need to be considered.
I want to stack more add-on boards on top of the FOXBoard, this allows me to re-use some of the boards (or designs) when I start on another robot. The FOXBoard uses 2 headers of 2×20 pins, these are available as female sockets with variable pin length ranging from 3 to 17mm. Examples are the 962-60206-12 from EPT that offers a body height of 11.45mm and a pin length of 12.2mm. Using a PCB thickness of 1.5mm and an inserting of the pin in another female socket of 3 to 6mm this offers a maximum component height of 16.15 to 19.15mm.
If one board is stacked on top of another one connectors, LED’s, buttons and so on are not longer accessible, for this reason all these components should be placed on the side of the board and be a 90 degrees bended version.
For the CAN interface to the actuators, 2 signal wires are needed that must be twisted. Based on the basic electrical architecture of SnakeBot, there are 3 additional wires needed: power, ground and charge. It might be an option to use normal UTP cable, this consists of 4 pairs of 2 twisted wires. In this case one pair can be used for the ground, one for the power, one for the charge and one for the CAN interface.
A wire in an UTP cable is a 24 gauge copper wire, this limits the current per wire to 0.577A or 1.15A per pair. This is not so much, when using 10 or 15 segments for SnakeBot each charging with 250mA as a minimum but with more advanced charging up to 2.5A the total current through the charge wire can be in the range to 10 to 15A. This is indeed not very realistic since the charger must be capable of supplying this current, but nevertheless the 1A is by far to low.
I guess the best solution here is to use normal household appliance power cord cable for the three power lines and use a separate two wire cable (shielded and/or twisted) for the CAN interface. This also “feels” better since drawing high current spikes trough a conductor that is a millimeter away from a communication bus running at 1Mbps is probably begging for problems.
In order to connect the power cables, best option is to use terminal blocks with a screw. Reason for this is that there will be a rather thick core that needs to be inserted. When wiring SnakeBot the (for example) charge line need to go into a segment and leave the segment again to go to the next segment. You do not want to place a connector on the front of the PCB and route the charge line over the board to the other side and place a connector here as well to go to the next segment. If this was done than all the charge current of all segments combined will run though the PCB of the first segment causing serious layout problems. Instead, the charge line will be cut, both ends stripped, twisted together, soldered and than inserted in a single connector that only supplies the current to that segment.
A power cord that can handle 16A uses a core of 2.5mm2, twisting both of them together results in a 5mm2 surface that makes a circle of SQRT(5/3.14)*2=2.6mm in diameter. That would require a terminal block that can handle AWG 10 or less, these are available but the height is 21.5mm which is too high and they are not 90 degrees bended.
In principle this is the same for the CAN interface except that the cores are much thinner. Remains the problem that the screw can not be fastened since there might be another board on top. There are terminal blocks available that offer both a male and female part. The cable entry part is 2.5mm2, so inserting two wires will not work. As an alternative the power cable cut be cut, stripped and third small piece of about 5cm could be added to make a T-junction using shrinktube for isolation. Than the 5cm long wire could be inserted in the terminal block. For the CAN-wires this is not needed, two wires can be twisted, soldered and inserted without any problem.
For now, I will use the terminal connector using the male-female combination. For some reason this feels the best. I will split the connector in 2 times 3 pins connectors. For one that the PMC will only use the CAN signals and not the power lines but also giving me the option to buy a bit more of them and keep them on stock for other projects (2 and 3 pins connectors are used the most, 6 pins much less).
At this moment the only add-on defined is the Primary Motor Cortex, I can imagine there will be more added in a later phase. For this it is important that the software and the electrical design are defined so that more add-ons can be placed in parallel.
Below the impact on the electronics:
- Address lines, are all inputs. No special action other than that one add-on board should enable the pull-up resistors while the others don’t. If all add-ons would enable the pull-ups they would all be placed in parallel and as such reduce the value (and increase the current consumption).
- Data lines, all bi-directional but by default set to input. Also here the pull-ups must be enabled on one add-on only.
- /WR, /RD lines, are all inputs. Again the action on the pull-ups.
- /INT line, is an input that each add-on can activate. Should be an open collector in that case . In the test software the output is pulled low. Using an open collector circuit would require a transistor, in this case the output on the AT90CAN128 should be inverted. Also a pull-up to the collector input of the transistor would be required, this should only be enabled on one add-on board again using a jumper.
- /ACK line, is an input that each add-on can activate so same as the /INT line.
Below the impact for the software:
- Use a define to enable or disable the pull-up on the address, data and control lines.
- Apply a filter in the read and write ISR’s to see if the command is for that specific add-on.
Oops. I said the FB was slow with it’s 10us time required for an IO call and that the AT90CAN128 would always be waiting for it to change states. Well, I stand corrected.
I was testing the code with a dummy command that takes 16 bytes that the FB sends to the AT. I noticed that about 3 to 4 out of the 16 bytes were well received and the rest got lost. It also did not receive 0..3 and than stopped, not the AT received 0, 4, 8 or something like that. Stepping trough the code on both sides showed everything was working and all 16 bytes were well received, this typically means there is a timing problem (stepping is working at low speed, executing is non-working at high speed).
Monitoring /WR and the LED output using a scope revealed that when the first byte is received on the AT it takes 17us to process the falling edge on /WR. The next ones go faster but it already takes 3us from a level change to start of ISR. With an 8MHz crystal each cycle takes 0.125us, so apparently there are 24 instructions needed to push everything on stack and execute the ISR. A 16Mhz crystal would reduce everything by 2 but the 17us would than still take 8.5us. True, this is in O0 mode for the compiler, so non-optimized, but it still worries me. Later the RoboCAN interface will be running as well, there is a big chance now that bytes will be missed if one of the CAN ISR’s is executed which are even more complex than the ones for interfacing with the FOXBoard. Also the optimizer can be enabled, a small test with -O2 shows a small us improvement but nothing drastic. I also don’t like to work with the optimizer during development since it makes debugging complex.
That leaves only one option, data transfer must use a handshake method. An extra input on the FB is required for this called /ACK were 1 means the AT is ready and 0 means it is busy.
For writing from the FB to the AT this means:
- Set the data bus to output (if not done already)
- Set the 1’s of the command (address) and data pins
- Set the 0’s of the command and data pins including the low level of the /WR pin
- Wait until ACK is low
- Raise /WR
- Wait until ACK is high
- Repeat this process for each data byte of the command
For reading by the FB from the AT this means:
- Set the data bus to input (if not done already)
- Set the 1’s of the command (address)
- Set the 0’s of the command including the low level of the /RD pin
- Wait until ACK is low
- Read the data pins and store them
- Raise /RD
- Wait until ACK is high
- Repeat this process for each data byte of the command
In both cases the rising flank of /WR or /RD will still trigger the interrupt in order to indicate the byte in finished.
Some testing later I have to conclude that this works perfectly. A write or read command that uses 16 bytes of data takes about 1.4ms, this is not very fast but I expect some improvements by increasing the crystal of the AT bring it to about 1.0ms.
The frequency of the AT90CAN128 can not go up to 16MHz since the voltage is 3V3, at this voltage the maximum frequency can be ((8/1.8)*(3.3-2.7))+8=10.6MHz. This is over the full temperature range, typically a 12MHz crystal should be OK, further testing at that moment.
Processing a command that writes data from the FB to the PMC is very straight forward:
- Set the data bus to output (if not done already)
- Set the 1′s of the command (address) and data pins
- Set the 0′s of the command and data pins including the low level of the /WR pin
- Repeat this process for each data byte of the command
Since the falling edge of the /WR pin on the PMC triggers an ISR and the write speed of the FB is limited to 10.8us (46kHz*2) per set or clear cycle there is no need to worry that data will be lost. The moment the ISR is triggered, the data is available on the data bus, so the ISR does not have to include any delay which will block the whole system (including the RoboCAN interface).
Since the command is received in an ISR and ISR’s should consume the least possible amount of time, processing the command received can only be part of the ISR if it sets or clears some variables. Any action that would require (for example) communication using the RoboCAN interface should be executed outside the ISR as part of the main application. Remember that a command over the RoboCAN interface might take several milli seconds in case the interface is busy or the received does not respond.
Processing a command that reading data is by definition more complex since there is a “slow” device (the FB) that needs to copy the data from the databus.
- Set the data bus to input (if not done already)
- Set the 1′s of the command (address)
- Set the 0′s of the command including the low level of the /RD pin
- Read the data pins and store them
- Repeat this process for each data byte of the command
Since it takes 10.8us for the FB to read the data (if not blocked by other processes) it means the PMC is stuck in the ISR linked to the falling edge of the /RD signal. One option is to leave the data on the bus and simply exit the ISR. This can (will) however create bus conflicts the moment the FB decides to write data.
In a normal bus system the slave places data on the bus at the falling edge of /RD and removes it on the rising edge of /RD. The best solution than is to trigger the ISR on each edge and decide what to do the moment the ISR is executed. The AT90CAN128 supports this behavior, it can trigger the interrupt on any logical change.
As a result, a falling edge on /RD will place the data on the bus and exit the ISR. At the rising edge of /RD the data is removed, final question to answer is when to generate the rising edge? If it is part of the read function on the FB it will require an additional IO call at the end of the read function (reading more bits will automatically raise /RD when the 1′s of the command are set). If this is not done, than the /RD will remain low until the next read or write command. This is not prefer ed since if a write follows the /RD pins is raised but the data lines are already reversed before this raise and so creating a bus conflict.
The big question is how much time the AT90CAN128 needs to prepare the data and set the first byte of this data on the bus. If this is not finished before the FB copies the data from the data lines than we have a problem. Based on the 10.8us for an IO call on the FB I don’t for see any problem as long as (again) filling the data to return does not include any calls on the AT90CAN128 other than copying existing known data into the buffer. Also the interrupt used is the second highest available interrupt source on the AT90CAN128. The highest is for the /WR signal, since the FB can not read and write at the same time there is no difference in the priorities, if /RD is lowered (or raised) the ISR will be called directly unless it is stuck into another ISR. This will be the first thing to check if strange data is read back.
For the /WR signal it is good enough to trigger only on the falling edge, but it makes sense to trigger on any level change as well to enable of disable the communication LED. To prevent reacting on undefined states when the FOXBoard is still booting or when the SnakeBot application is not started or starting the PMC needs to ignore any change until it detects a stable state (/RD is high and /WR is high).
While implementing the first command to set a power mode on the PMC I already struggle with processing this command inside the ISR. It will require commands send to the various actuators, so the command must be buffered and executed as part of the main program and not inside the ISR. Sending a command from the PMC to the FB is not interrupt based, so only received commands need to be buffered.
Claiming an array of command structures is not memory friendly, instead a simple circular buffer will do the trick in which the command (2 bytes), the amount if data bytes (1 byte) and the data (x) bytes can be stored. Two command pointers (one points to the next to read and one points to the first free position) do the maintenance of the buffer. This buffer is called CommandBuffer and made 128 bytes long. Time will tell if this is enough. Two functions are defined; StoreCommand and GetCommand. The first is now called when a full command is received in the /RD ISR. The application can call the function CommandReceived to check if a command is available in the buffer or simple call GetCommand which will fail if no command is available.
The ATDVK90CAN1 board requires a power supply of 6 to 15V, the FOXBoard requires 5V. When using two power supplies I fear problems with power leakage when one of the two is not powered up. To prevent this, I made a very simple 5V power supply as shown below based on this schematic that takes anything above 8V. Don’t forget to mount a small heat sink to the regulator to keep it nice and cool. Using this power supply both boards can be powered from my power supply using 8V, when something goes wrong a single switch will turn off all power at ones.
As mentioned, two pieces of software are required. The ATDVK90CAN board will be the Primary Motor Cortex (PMC for short), this link will download the latest version. It does not include the RoboCAN code, it you don’t have it, downloaded it here. For now, all it does is it initializes the RoboCAN interface, setup all IO pins and provide two interrupt service routines connected to a falling edge on /RD and a faling edge on /WR. Program this version in the ATDVK90CAN board before connecting it to the FB so you can be sure no IO pin conflicts will occur.
For the FB, a small test program is written that provides the first level of code to send and receive a command to the PMC. This code does not have to be compiled before the connection is made, although it does not hurt to verify if indeed you can compile it.
Connect every thing together and triple check all connections for shorts and verifying using a multimeter that each wire is indeed connected as listed in the schematic. Than switch on the power supply, the current at 8V should be around 250mA. If higher (even 280mA is higher…) than switch of the power supply because there is a problem somewhere. If it is indeed close to the 250mA it might be there is an IO conflict or both boards are not at the same ground level, if it is a lot higher than probably there is a mix somewhere in the plus and minuses of the power supply. In any case (even if the current is correct) verify each pin level with a voltage meter, they should all be 3.3V +-5% or 0V.
In AVR Studio you should be able to compile, download and execute the code step by step. Place a break point in the ISR (INT0_vect) procedure and run the code. On the FB, run the test program and step trough it in Kdbg until you reach the WriteByte command in the main function. Using F8 step into this function and when you reach the final command that clears the bits on the FB verify that the break point in AVR Studio is indeed triggered. If so there is a good change everything is working.