11//! This example demonstrates how to use the MCU's internal timer/counters
22//! to move a servo motor.
3- //!
3+ //!
44//! NOTE: using timer/counters will not put any load to the CPU, but will limit the amount of
55//! servo's you can control with a single board.
6- //!
6+ //!
77//! # Servo control
88//!
99//! * 20 ms PWM period required = 50 Hz
1414//! modification). With this we can check which PWM period we can achieve with an 8-bit timer/counter:
1515//! * With Prescale64: 1 clock tick of 256 total ticks = 4 us * 256 = +- 1 ms.
1616//! * With Prescale256: 1 clock tick = 16us * 256 = 4.1 ms.
17- //! * With Prescale1024: 1 clock tick = +- 0.1 us * 256 = 16.4 ms.
18- //!
17+ //! * With Prescale1024: 1 clock tick = +- 0.1 us * 256 = 16.4 ms.
18+ //!
1919//! If we use 16-bit timer/counters, we get a better control on the exact PWM period time.
2020//! The longest PWM cycle per prescaler is defined by MAX value (2^16) = 65536 (<-> 2^8 = 256).
2121//! Options:
2626//! This gives the following possible resolutions:
2727//! * Prescale8: 0.5 ms - 2.5 ms = 2 ms control range = 4000 clock ticks => 0.045 degrees per step.
2828//! * Prescale64: 0.5 - 2.5 ms = 500 clock ticks => 0.36 degrees per step.
29- //!
29+ //!
3030//! For most cases Prescale64 will be sufficient.
31- //!
31+ //!
3232//! Note: On the ATMega2560 TC1, TC3, TC4 and TC5 are 16-bit timer/counters.
3333//! TC0 and TC2 are 0-bit timer/counters.
3434//!
4040use panic_halt as _;
4141
4242#[ arduino_hal:: entry]
43- fn main ( ) -> ! {
44- let dp = arduino_hal:: Peripherals :: take ( ) . unwrap ( ) ;
43+ fn main ( ) -> ! {
44+ let dp = arduino_hal:: Peripherals :: take ( ) . unwrap ( ) ;
4545 let pins = arduino_hal:: pins!( dp) ;
4646
4747 // Take Timer/Counter3 (TC3) from Peripherals
@@ -56,31 +56,39 @@ fn main () -> ! {
5656 // // - bits 5:4 - COM1B1:0 = Output compare mode for OC1B (for channel B)
5757 // // - bits 3:2 - COM1C1:0 = Output compare mode for OC1C (for channel C)
5858 // Here will use pin D3, which is port PE5. PE5 uses TC3 with output compare channel A.
59-
59+
6060 // Wave generation mode 14: WGM bits 0:3 1110 => FastPWM with ICRn to define TOP and OCRNX to define compare output.
6161 // WGM bits 0:1 go into TCCR3A (0b10)
6262 // WGM bits 2:3 go into TCCR3B (0b11)
63-
64- tc3 // In TC3
63+
64+ tc3 // In TC3
6565 . tccr3a ( ) // Access register TCCR3A (TCCnx in the datasheet)
66- . write ( |w| unsafe { // write to the register. This is unsafe as there's no protection against concurrent writing.
67- w // Writer wrapper
68- . com3a ( ) . bits ( 0b10 ) // Write bits 0b10 to the com3a bits in register TCCR3A
69- . wgm3 ( ) . bits ( 0b10 ) } // Write WGM bits 2:3 into
66+ . write (
67+ |w| unsafe {
68+ // write to the register. This is unsafe as there's no protection against concurrent writing.
69+ w // Writer wrapper
70+ . com3a ( )
71+ . bits ( 0b10 ) // Write bits 0b10 to the com3a bits in register TCCR3A
72+ . wgm3 ( )
73+ . bits ( 0b10 )
74+ } , // Write WGM bits 2:3 into
7075 ) ;
7176 // Same operation for TCCR3B.
72- tc3. tccr3b ( ) . write ( |w| unsafe {
73- w
74- . wgm3 ( ) . bits ( 0b11 ) // Set WGM bits 2:3
75- . cs3 ( ) . prescale_64 ( ) } // Set clock select bits. Using the avr-hal pre-defined prescale bits.
77+ tc3. tccr3b ( ) . write (
78+ |w| unsafe {
79+ w. wgm3 ( )
80+ . bits ( 0b11 ) // Set WGM bits 2:3
81+ . cs3 ( )
82+ . prescale_64 ( )
83+ } , // Set clock select bits. Using the avr-hal pre-defined prescale bits.
7684 ) ;
7785 // We don't need TCCR3C for the servo.
7886
7987 // TOP will define the length of the total PWM cycle.
80- // Setting IRC3 to 4999 (now TOP for this TC) to achieve 50 Hz cycle.
88+ // Setting IRC3 to 4999 (now TOP for this TC) to achieve 50 Hz cycle.
8189 // IMPORTANT: This affects all channels (A, B and C) of Timer/Counter3!
8290 tc3. icr3 ( ) . write ( |w| w. set ( 4999u16 ) ) ;
83-
91+
8492 // Setting duty cycle on channel C using OCR3A. When the TC3 count register hits this value,
8593 // Output Compare 3 will be toggled. It will invert the pin from high to low (due to how we set the COM3A bits)
8694 // * 0.5 ms = 125 ticks (0 - 124)
@@ -89,12 +97,12 @@ fn main () -> ! {
8997 // Servo SG90 0 degrees = 0.5 ms -> with Prescale8 = 124
9098 // Servo SG90 180 degrees = 2.5 ms -> with Prescale8 = 624
9199 tc3. ocr3a ( ) . write ( |w| w. set ( 124u16 ) ) ; // Now we set it to 0 degrees.
92-
100+
93101 // Toggle pin D3 into an output pin connect it to the TC3 output.
94102 // Connect your servo with its PWM pin to your board's D3 pin.
95103 // VCC to 5V and GND to GND offcourse.
96- pins. d3 . into_output ( ) ;
97-
104+ pins. d3 . into_output ( ) ;
105+
98106 loop {
99107 for ticks in [ 124u16 , 374u16 , 624u16 , 374u16 ] {
100108 tc3. ocr3a ( ) . write ( |w| w. set ( ticks) ) ; // Now we set the angle using the 'duty'.
0 commit comments