Automation applications generally require controlling multiple actuators (like many motors) at the same time. For concurrent and synchronous controlling of many actuators, they need to be controlled and operated independently. Engineers use as many controllers (microcontrollers or ASICs) as the number of actuators needs to be independently operated. However, when a controller is capable to operate multiple actuators (which is possible when it has high number of input/output pins) and has high clock speed (where due to high clock speed of the controller, actuators appear to be controlled independently at the same time), a single controller is preferred to be used to manage several actuators. This reduces the size and complexity on the hardware side, reducing the overall production cost for the specific embedded system. However, such a system requires a complex software sitting inside the controller.
In this project, a general purpose embedded application has been designed to control 32 servo motors (independently and concurrently) by popular ATMega32 AVR board. A complex C program is designed here to move 32 servos at predetermined speeds to pre-defined positions by the ATMega32.
The servos can move to 10 different positions (tracks) within an interval of 20 Milliseconds. They take up each position within a sub-period of 2 Milliseconds i.e. to take a predefined position, they take maximum 2 Milliseconds or less. The set of these predefined positions (10 positions defined individually for 32 servos) keeps on repeating in time interval of 20 Milliseconds till the system remains powered on. Within each sub-interval of 2 Milliseconds, servos move to the pre-defined position and rest there till the sub-interval of 2 Millisecond is completed. For this, they are stopped at specific time instants (which is obviously always 2 Milliseconds or less). Like, one servo may reach and rest at its pre-defined position at 1.2 Millisecond and other may reach and rest at its predefined position at 1.5 Millisecond. The servos keep receiving PWM pulse till they have to be moved and then when they have to be halted at a pre-defined position (within sub-interval of 2 Milliseconds), PWM pulse is stopped.
The servos can take up to 100 different predefined positions within each 2 Millisecond sub-interval. Suppose, a servo can rotate from 0 to 180 degrees on providing a PWM signal from 1 Millisecond to 2 Millisecond, then it can move to 100 positions having a difference of 1.8 degrees each.
While attaining each position (track) within 2-Millisecond sub-interval, servos can reach to predefined position at different speeds. For speed variation, temporary positions are defined in the code where the PWM pulse is stopped for 10 Microseconds. The greater will be the number of temporary positions (steps or dead bands of 10 Microseconds), less will be the speed of rotation. However, the servo will attain the predefined position within 2 millisecond sub-interval. If there is only one temporary position defined to reach the predefined position, servo will rotate at its maximum speed of rotation (as continuous PWM pulse will be provided to it) to reach that predefined position (angle) and will stop once it reach its predefined position (track) at a time instant within 2-Millisecond sub-interval. If there are 10 temporary positions defined to reach the predefined position (angle), there will be 10 steps or dead bands of 10 Microseconds, which will reduce the speed of rotation of the servo accordingly. This way, there is speed variation where servo rotates at its maximum speed to predefined position when there is 1 step (or dead band or temporary position) and rotates slowly as there are more steps (up to 10).
This is similar to industrial automation applications and can be used to control robotic arms or wings of a drone. The C program presented here is a general purpose program that can be modified by hard coding speed and position values of individual servos to suit any kind of automation application. The C program presented here has been developed and compiled using AVR studio.
A modification of the above project is also given where 30 servos are controlled (by 30 input/output pins of AVR ATMega32) and 2 input/output pins are utilized for serial communication with an external device. Here the external device connected through UART sends the values to the ATMega32 to control and change the sequence of servos in real-time. The external device (another controller or PC) can be connected through RS-232, Bluetooth or any interface using UART protocol.
Fig. 1: Prototype of AVR ATmega32 based Multiple Servo Controller
Components Required –
Fig. 2: Image of Components Required for AVR ATmega32 based Multiple Servo Controller
Circuit Diagram –
The servo motor controller for 32 servos has the following circuit diagram –
Fig. 3: Circuit Diagram of AVR ATmega32 based Multiple Servo Controller
The servo motor controller for controlling 30 servos with UART serial communication for remote operation by external device has the following circuit diagram –
Fig. 4: Circuit Diagram of AVR ATmega32 based Multiple Servo Controller
Circuit Connections –
The circuit used in this servo motor controller is very simple. A servo motor has three terminals – VCC, Ground and Signal. The VCC pin is connected to 5V supply and ground with the common ground. The signal pin of the servo is connected to GPIO of the controller to provide PWM signal. The servos used in this circuit are SG90 which can rotate 180 degrees (90 degrees in either direction). On provinding 1 ms High pulse, servo rotates to -90 degrees. On providing 1.5 ms High pulse, it moves to 0 degree and on providing 2 ms High pulse, it moves to 90 degrees. So, for a PWM input from 1 ms to 2 ms, servo rotates from -90 degrees to 90 degrees.
Fig. 5: Pin Diagram of Servo Motor
AVR ATMega32 has 32 general purpose input/output pins from which PWM signal can be provided to 32 servos. It comes in 40-pin PDIP or 44-pin TQFP/MLF package. The 40-pin PDIP package is used as the circuit is designed on a breadboard. AVR ATMega32 has the following pin diagram –
Fig. 6: Pin Diagram of AVR ATMega32
The servos are connected in the alphabetical order of the Ports as follow –
PORTA : S1-S8
PORTB : S9-S16
PORTC : S17-S24
PORTD : S25-S32
For high speed of operation, maximum frequency is selected for the AVR. A crystal oscillator of 16 MHz is connected between pins 12 and 13. Two capacitors of 22pF are used to resonate with the crystal inductance which are connected in series to the ground.
The servos as well as ATMega32 is supplied 5V DC for operation. The DC power supply to the circuit is chosen by taking into account the total current drawn by all the servos connected to the AVR. The current rating of the DC source should be higher to withstand the peak load currents of all the servos. A rule of thumb to find peak load current for standard servos is 1A for 3 to 4 Kg Servos. The SG90 servos used in this circuit weight 14.7 gm (Source – SG90 Servo Datasheet). So, each servo draws maximum current of around 15 Milliampere. The PDIP package of ATMega32 draws 200 Milliampere current. So, the total current requirement can be calculated by summing up the maximum current drawn by all the servos and the AVR controller as follow –
Total Current = Current drawn by AVR ATMega32 + Current drawn by Servos
Total Current = 32X15 mA + 200 mA
Total Current = 680 mA
So, the DC supply should be 5V and atleast 680 mA.
In the remote operated version of the circuit, two servos are removed from pins 14 and 15 and an external device (controller or PC) is connected to the ATMega32 via UART.
Fig. 7: Image showing Controller Circuit of AVR ATMega32 based Multiple Servo Control
How the circuit works –
The Servo motor controller designed here has predefined set of servo positions (10 positions per servo) in the firmware code. When the circuit is powered on, the controller reads an array where current position, final position and speed of rotation for each servo is predefined. Accordingly the time instants within first 2-Millisecond sub-interval for each servo to stop are determined. The time instants to stop the servos are sorted in an ascending order and the time instant with the least value is loaded to the compare match register of a timer. The PWM pulses are turned on for all the servos. The PWM signal for each servo is turned off as its respective timer interrupt is generated. The maximum width of PWM signal can be 2 Millisecond. The servos rotate to their predefined position (track) and stop to rest at that position. Each servo has its own final position (angle) and speed (determined by number of dead bands or temporary positions defined for that servo). Once the 2-Millisecond sub interval is complete, all servos had reached and stopped at their first predefined positions. The new current positions of the servos are updated to the controller. The final positions in first cycle now become current positions in second cycle.
Now the final positions and speeds for the second 2-Millisecond sub-interval are loaded to the array. Accordingly the time instants within second 2-Millisecond sub-interval for each servo to stop are determined. Again, the time instants to stop the servos are sorted in an ascending order and the time instant with the least value is loaded to the compare match register of the timer. The PWM pulses are turned on for all the servos. The PWM signal for each servo is turned off as its respective timer interrupt is generated. This repeats for each of the remaining eight 2-Millisecond sub interval.
Once the 10 cycles (2-Millisecond sub intervals) are completed, the loop of 20 Millisecond is completed. Now, again the loop begins from the first 2-Millisecond sub interval and continues to remaining cycles. The loop of 10 cycles for 10 predefined positions for each servo keeps on repeating till the system remains powered on. If the system is shut down and restarted, the loop of 10 cycles begins from the first cycle onwards.
Fig. 8: Image showing Working of AVR ATMega32 based Multiple Servo Control
Programming Guide –
The firmware for this servo motor controller has been developed in embedded C for AVR ATMega32. For writing and compiling the code, winavr and programmers notepad are used.
The working of this circuit is based on timer interrupts. AVR ATMega32 has three timers – Timer0 (which is 8-bit timer), Timer1 (which is 16-bit timer) and Timer2 (which is 8-bit timer). Timer1 and Timer2 are utilized in the coding of this system.
Timer1 is a 16-bit counter which is formed by two 8-bit registers – TCNT1H and TCNT1L. It is used to count the time and generate interrupts at the desired time instants to turn OFF the respective servo pulses. The Timer1 can operate in five modes –
1) Normal Mode
2) Clear Timer on Compare Match (CTC) Mode
3) Fast PWM Mode
4) Phase Correct PWM Mode
5) Phase and Frequency Correct PWM Mode
Here, Timer1 is operated in CTC (Clear Timer on Compare match) mode.
Fig. 9: Bit Values of TCNT1 Register in AVR ATMega32
The following registers are associated with Timer 1 –
1) TCCR1A (Timer/Counter 1 Control Register A)
2) TCCR1B (Timer/Counter 1 Control Register B)
3) TCNT1 (Timer/Counter Register 1, is 16-bit – TCNT1H and TCNT1L)
4) OCR1A (Output Compare Register 1 A , is 16-bit – OCR1AH and OCR1AL)
5) OCR1B (Output Compare Register 1 B , is 16-bit – OCR1BH and OCR1BL)
6) ICR1 (Input Capture Register 1, is 16-bit – ICR1H and ICR1L)
7) TIMSK (Timer/Counter Interrupt Mask Register)
8) TIFR (Timer Interrupt Flag Register)
The Timer 1 has two control registers – TCCR1A and TCCR1B. It has one Flag register – TIFR which tells controller the status of Timer 1. The TCCR1A is 8-bit register with following bit values –
Fig. 10: Bit Values of TCCR1A Register in AVR ATMega32
The TCCR1B is also 8-bit register with the following bit values –
Fig. 11: Bit Values of TCCR1B Register in AVR ATMega32
To set the Timer1 in CTC mode, WGM13 and WGM12 in the TCCR1B should be 0 and 1 respectively while WGM11 and WGM10 in TCCR1A should be 0.
Fig. 12: Table showing Conditions for CTC Mode Selection for Timer 1
Also, as a crystal oscillator of 16 MHz is connected to ATMega32, the time period of the crystal oscillator is as follow –
Time Period = 1/Frequency
= 1/(16X10^6)
= 0.0625 Microsecond
With this F_CPU in normal mode, Timer1 could only count for 4.096 Millisecond for full scale (0.0625 Microsecond X 2^16). So, it needs to be prescaled. The prescaling is determined by CS12, CS11 and CS10 bits of TCCR1B register according to the following table –
A value of 0x0A is written to TCCR1B while TCCR1A is left to 0x00. This set the Timer1 in CTC mode by making WGM13, WGM12 in the TCCR1B to 0 and 1 respectively while WGM11 and WGM10 in TCCR1A to 0. This also selects a prescaler of clkI/O/8. So, Timer1 counts at a clock of 2 MHz (16/8). Each count of the timer now corresponds to the time period as calculated below –
Time Period = 1/Frequency
= 1/(2X10^6)
= 0.5 Microsecond
Now Timer1 can count up to 32.768 Millisecond (0.5 Microsecond X 2^16). For generating interrupts within 2-Millisecond sub interval, it only needs to count upto 4000 (0.5 Microsecond X 4000 = 2 Millisecond). When TCCR1B register is written with non-zero clock pre-scaler bits, the timer/counter is started. So, this register is written after sorting and writing least position values into 16-Bit Output Compare Register (OCR1A).
ATMega32 has TIMSK register which has interrupt control bits for all its Timer/Counters including Timer 1. TIMSK has the following bit values –
Fig. 13: Bit Values of TIMSK Register in AVR ATMega32
The TICIE1, OCIE1A, OCIE1B and TOIE1 bits of TIMSK register are associated with Timer 1. The TIMSK is set to 0x90 which set the OCIE2 and OCIE1A to 1. By setting OCIE1A to High, it enables the Timer/Counter1 Output Compare A match interrupt. The time instant to stop PWM signal is loaded in OCR1A Register. When the count of TCNT1 matches with the value loaded in OCR1A, interrupt is generated. Everytime the interrupt is generated, PWM signal to the respective sevo is turned OFF.
Timer2 is 8-bit register (TCNT2) which is used to generate interrupts at every 2 Millisecond (to indicate end of 2-Millisecond sub intervals). It can operate in four modes –
1) Normal Mode
2) Clear Timer on Compare Match (CTC) mode
3) Fast PWM Mode
4) Phase Correct PWM Mode
In order to enable Timer 2, OCIE2 was set to 1 in TIMSK register. Timer 2 has the following registers associated with it –
1) TCCR2 (Timer/Counter Control Register 2)
2) TCNT2 (Timer/Counter Register 2)
3) OCR2 (Output Compare Register 2)
4) ASSR (Asynchronous Status Register)
5) TIMSK (Timer/Counter Interrupt Mask Register)
6) TIFR (Timer Interrupt Flag Register)
Timer 2 is controlled by TCCR2 register. TCCR2 has the following bit values –
Fig. 14: Bit Values of TCCR2 Register in AVR ATMega32
A value of 0x0D is written to TCCR2. The mode of operation of Timer 2 is selected by writing to bits WGM21 and WGM20 of TCCR2. The selection of mode is done according to the following table –
By writing 0x0D to TCCR2, WGM20 is set to 0 and WGM21 is set to 1, so Timer 2 operates in CTC mode. Like done for Timer 1, prescaling is also done for Timer 2. For Timer 2, the prescaling is determined by CS22, CS21 and CS20 bits of TCCR2 register according to the following table –
By writing 0x0D to TCCR2, CS22, CS21 and CS20 of TCCR2 are set to 1, 0 and 1 respectively. So, Timer2 counts at a clock of 125 KHz (16/128 MHz). Each count of the timer now corresponds to the time period as calculated below –
Time Period = 1/Frequency
= 1/(125X10^3)
= 8 Microsecond
For 2 Millisecond, Timer 2 now needs to count up to 250 (8 Microsecond X 25
0 = 2 Millisecond). So, a value of 250 is written to OCR2. When Timer 2 counts to 250 and matches with the value written in OCR2, interrupt is generated. This indicates end of one sub-interval (one of the ten cycles for servo positions).
The TIFR register has the following bit values –
Fig. 15: Bit Values of TIFR Register in AVR ATMega32
Timer 1 in CTC mode is comparing with OCR1A register. When it generates interrupt, OCF1A in TIFR is set High. By checking the status of OCF1A in TIFR, interrupt generated by Timer 1 is monitored. Timer 2 in CTC mode is comparing with OCR2 register. When that generates interrupt, OCF2 is set High. By checking the status of OCF2 in TIFR, interrupt generated by Timer 2 is monitored.
The C code developed for this AVR circuit follow an algorithm summarized by the following flowchart –
Fig. 16: Flowchart of Algorithm used by AVR Code for Multiple Servo Control
The C Code follows the following algorithm –
1) Load and sort the servo positions (Angles) for first 2-Millisecond sub interval –
For the first 2-Millisecond sub interval, the current position, final position and speed of the rotation for the servos are stored in a 2-dimensional array servo_positions[ns+1][3]. The array is sized one greater than the number of servos connected with AVR ATMega32. As there are 32 servos connected to the controller, the constant ns is initialized to 32.
The array stores three bytes for each servo. First byte is the speed value, second byte is the final position (final angle) of the servo and third byte is the current position (current angle) of the servo.
The final positions are mentioned as values that must be loaded in Timer 1 to stop the PWM signal when the Timer 1 interrupt for respective servo is generated. The servo rotates to -90 degree when 1 ms PWM pulse is given to it. It rotates to 0 degree when 1.5 ms PWM pulse is provided and rotates to 90 degree when 2 ms PWM pulse is provided. For at least 1 ms PWM signal, Timer 1 needs to count up to 2000 as 2000 counts of Timer 1 are equivalent to 1 Millisecond in the settings of its control registers. So, the final and current positions of the servos can be between 2000 and 4000 as PWM signal of minimum 1 Millisecond and maximum 2 Millisecond could be provided to rotate servo between -90 to 90 degrees. Any value, for current and final position of the servos, less than 2000 or greater than 4000, is therefore irrelevant. So, the final and current position of the servo must be always defined between 2000 and 4000.
The code is developed to rotate servo to 100 different positions (angles) between -90 and 90 degrees. All adjacent positions are gapped by 1.8 degrees of servo angle. Since, the Timer 1 count for each servo position can be between 2000 and 4000, with 100 possible positions (angles), Timer 1 count is incremented by a factor of 20 for any next servo position (angle). For example, for moving servo to final position of -90 degree, final position defined in code should be 2000. For moving servo to one position above i.e. -88.2 degrees (-90+1.8 degrees), final position defined in code should be 2020 and so on. The values of final or current position required to rotate servos to some standard angles are listed in the following table –
The speed is controlled by first byte of the servo_positions[ns+1][3] array. The value mentioned in this variable defines the number of temporary angles that are attained before reaching the final angle (final position). A dead band of 10 Microsecond (equivalent to Timer 1 count of 20) is inserted after every temporary position. For each servo that has to be stopped next, the value at respective index in speed_cycle[temp] array is reset to 0 and compared to speed value from servo_positions[ns+1][3] array in a while loop. The value of speed_cycle variable is incremented at every temporary position and compared with the speed value from servo_positions[ns+1][3] array. Once the value of at respective index in speed_cycle[temp] array becomes equal to speed value from servo_positions[ns+1][3] array, final position is reached. The speed value of servo_positions[ns+1][3] array is used to assign value to another variable steps. The value assigned to steps is equal to number of temporary positions. The difference between the final position and current position is calculated and divided by steps to insert temporary positions before reaching the final position (angle). If the value of step will be 1, continuous PWM signal will be provided to the servo without any dead band, so servo will rotate to maximum speed to reach final position from the current position. If the value of step will be more, there will be same number of temporary positions and dead bands before reaching the final position, so the speed of rotation to reach the final position will be reduced accordingly. The step variable is initialized 5 but for each servo position in every 2-Millisecond sub interval, it is assigned equal to respective speed value of servo_positions[ns+1][3] array. The speed value must be always defined a positive integer ranging between 1 to 10 to avoid any position errors like negative values or out of range position values. So, the value of speed equal to 1 indicates highest speed of rotation for the respective servo while the value of speed equal to 10 indicates lowest speed of rotation for the respective servo.
1) Identifying unique time instants when one or more servos have to be stopped within 2-Millisecond sub interval –
The final positions of all the servos are sorted in ascending order and the indices respective to the servos that must be stopped one after the other are stored in an array defined as order[ns+1]. The size of the order array is one greater than the number of servos. The 0 index of the order array is not used.
There can be situations where two or more servos may have same final position and they need to be stopped at the same time instant. So, difference between consecutive final positions after sorting is calculated in a loop and for every non-zero difference, a variable ‘instants’ is incremented by one. This gives the exact number when interrupt from Timer 1 should be generated. The difference between consecutive final positions after sorting is stored in a variable Temp2 and its value is loaded in the OCR1A register. The final positions at which the Timer 1 interrupts must be generated are stored in another array – timer_inst[].
2) For each unique time instant, generate Timer 1 interrupt and update port outputs –
For each instant, port values (PWM signals from GPIO pins) are stored in an array Port_values[][]. This way, if two or more servos have same final position and must be stopped at same time instant, a single interrupt is generated at that time instant by comparison to values in timer_inst[] array, and all servos with that final position are stopped at that interrupt (and at that time instant) by assigning ports the values stored for respective index in Port_values[][] array. The respective port_value is written using a ‘switch statement’ in the code and this is repeated for all the servos.
The BIT Wise OR operation is used to repeatedly update the values in ports_value array without changing the previous values. The Bit wise AND operation with inverted port_value is used to turn OFF the pins (bits) that are HIGH in port_values variable in order to stop respective servo(s). All the “port_values” are written and assigned to all the PORTs at all the timer instants. Even if there is no port_value (zero) at any instant for a particular port, the pins of the PORT are not changed because of the inverted BIT Wise AND operation.
Initially, all PWM signals are turned ON and at least 1 Millisecond of PWM signal is provided. Then, The servos are stopped one after the other as their respective Timer 1 interrupt is generated within 2-Millisecond sub interval. At each interrupt, Timer 1 is cleared and loaded with the difference value with next consecutive final position from the sorted array.
Fig. 17: Screenshot of AVR Code for Multiple Servo Motor Control
The final and current servo positions should be always between 2000 and 4000. That is why, for on being safe side, the code is developed such that, the user must define a value between 0 and 100 for final positions of the servos. The values entered for final positions of the servos by the user are multiplied by 20 (unit defined to increment Timer 1 count) and is added to 2000 in the code to derive actual Timer 1 interrupt instants. The values for current and final positions for standard servo angles that must be entered by the user in the servo_positions[ns+1][3] and servo_tracks[10][2][ns+1] arrays are listed in the following table –
4) Repeat 2-Millisecond sub intervals for 10 times and repeat the entire schedule infinitely –
The Timer 2 is generating interrupt after every 2 Milliseconds. When it generates an interrupt, it is an indication that current 2-Millisecond sub interval is over. All the servos are then turned off (by writing 0x00 to all the ports) as a precautionary measure. The number of 2-Millisecond sub intervals is tracked by a variable cycles. This variable is also incremented by one when Timer 2 generates the interrupt.
When 10 2-Millisecond sub intervals are over, a time period of 20 Milliseconds has passed. Since the servos are considered to operate at 50 Hz signal frequency (1/50 = 20 Milliseconds), now the servo positions are repeated from the position in the first 2-Millisecond sub interval. This keeps on repeating till the system remains powered on.
Note that in this circuit PWM signals for 10 sequences of servo angles for 32 servos are hard coded in the firmware code. The end user does not always have access to the firmware code. So, a modification of the above circuit is also presented where 2 servos are removed at pins 14 and 15 and an external device (another controller or PC) can be connected to the ATMega32 via UART.
The modified circuit can receive values from external source and operate servos in real-time by a human operator. An interrupt sub-routine ISR(USART_RXC_vect) is defined in the code to take input from the external source. This ISR is commented in the original code (where 32 servos are controlled). By proper layout of sequential positions, complex sequences can also be pre-programmed. This is limited only by the RAM of the microcontroller to store the data variables. The program presented here contains a 10-step sequential program, which swings the arms within pre-defined limits. This sequence is named as track and the position along with speed are defined in the track array (3-Dimensional). An external microcontroller can be programmed to pass the sequences in the predefined format as per real-time human input or real-time human input can be passed through a terminal application on a PC in the predefined format.
UART is used to update the servo positions in Real Time from the outside world. This will reduce the 32 channels to 30 channels, as two pins TX and RX are used by UART for data processing. This is useful in multiple processor systems. The main processor issues data to this servo controller depending on the real-time inputs or feedbacks. This is also useful for wireless control of servo motors using Bluetooth or any other wireless serial transmission.
You may also like:
Project Source Code
###
//Program to #include#include #include #define ns 32 #define nt 8 unsigned char servo_positions[ns+1][3]={{0,0,0}, //ns=no.of servos //Speed Final Position Present Position for initialization only {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, {1,1,100}, }; unsigned char temp_servo_positions[ns+1],temp_inst=0,*temp_inst_addr=&temp_inst,step=5; unsigned char temp,temp1,temp2,temp3,swap,order[ns+1]; unsigned char sorted_positions[ns+1],instants=ns,port_values[ns+1][5], temp_port=1,cycles=0,speed=10,uart=0,track=0; unsigned int timer_inst[ns+1],speed_cycle[ns+1],sort=0; unsigned char servo_tracks[nt][2][ns+1]={ {//Speed //Position {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100}, }, { {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,1,1,1,1,1,1,1,1, 100,100,100,100,100,100,100,100, 100,100,100,100,100, 100,100,100, 100,100,100,100,100,100,100,100}, }, { {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 100,100,100,100,100,100,100,100, 100, 100,100,100,100,100,100,100}, }, { {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 100,100,100,100,100,100,100,100}, }, { {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1}, }, { {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,100,100,100,100,100,100,100,100, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1}, }, { {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1}, }, { {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8}, {0,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100, 1,1,1,1,1,1,1,1}, } }; void sort_positions(void); void instants_port_values(void); int main(void) { PORTA=0; DDRA=0XFF; PORTB=0; DDRB=0XFF; PORTC=0; DDRC=0XFF; PORTD=0; DDRD=0XFF; sort_positions(); OCR2=250; OCR1AH=timer_inst[1]/256; OCR1AL=timer_inst[1]%256; temp_inst=1; PORTA=255; //Turn ON All servo pulses PORTB=255; PORTC=255; PORTD=255; //Turn ON timers TCCR2=0X0D; //F/128,CTC Mode TCCR1B=0X0A; TIMSK|=0X90; //OCIE 2,1A SREG|=0X80; while(1); } ISR(TIMER2_COMP_vect) { cycles++; if(cycles==1) { //Precautionary:turn Off all pulses(servos) PORTA=0X00; PORTB=0X00; PORTC=0X00; PORTD=0X00; //Servo data update inquiry //uart=0; //do{}while((UCSRA&0X20)==0); //UCSRA|=0X40; //UDR=0; //do{}while((UCSRA&0X40)==0); } else if(cycles==3) { //Servo data update inquiry //uart=0; //do{}while((UCSRA&0X20)==0); //UCSRA|=0X40; //UDR=1; //do{}while((UCSRA&0X40)==0); } else if(cycles==5) { //<2mS sort=ns; for(temp=1;temp<=ns;temp++) { if(servo_positions[temp][2]>servo_positions[temp][1]) { //1.5uS for one servo speed_cycle[temp]++; if(speed_cycle[temp]>=servo_positions[temp][0]) { servo_positions[temp][2]-=step; if(servo_positions[temp][2] =servo_positions[temp][0]) { servo_positions[temp][2]+=step; if(servo_positions[temp][2]>servo_positions[temp][1]) { servo_positions[temp][2]=servo_positions[temp][1]; } speed_cycle[temp]=0; } } else { sort--; //to count no.of unequal servo positions } } //Sorting-Ascending Order according to servo_positions[1] if(sort!=0) { sort_positions(); } else { track++; track%=nt; for(temp=1;temp<=ns;temp++) { servo_positions[temp][0]=servo_tracks[track][0][temp]; servo_positions[temp][1]=servo_tracks[track][1][temp]; } sort_positions(); } } else if(cycles==10) { PORTA=0XFF; PORTB=0XFF; PORTC=0XFF; PORTD=0XFF; temp_inst=1; OCR1AH=timer_inst[temp_inst]/256; OCR1AL=timer_inst[temp_inst]%256; TCCR1B=0X0A; cycles=0; } } ISR(TIMER1_COMPA_vect) { TCCR1B=0X00; PORTA&=~port_values[temp_inst][1]; PORTB&=~port_values[temp_inst][2]; PORTC&=~port_values[temp_inst][3]; PORTD&=~port_values[temp_inst][4]; if(temp_inst =1;temp2--) { swap=0; for(temp1=ns;temp1>=1;temp1--) { if(swap ###
Circuit Diagrams
Project Video
Filed Under: Electronic Projects
Filed Under: Electronic Projects
Questions related to this article?
👉Ask and discuss on EDAboard.com and Electro-Tech-Online.com forums.
Tell Us What You Think!!
You must be logged in to post a comment.