1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Module for general interaction with the specific systems installed on the RP6's robot base.

use super::Serial;
use crate::{avr::registers, interrupt, set_pins, Pin, Register};

/// Module binding pins to their device-specific function names.
pub mod port;
use port::*;

/// Module allowing for simple use of the robot's Anti-Collision System.
pub mod acs;

/// Struct managing all actions regarding the robot's base.
pub struct RobotBase;

impl RobotBase {
    pub fn init() {
        // Setup port directions and initial values.
        // THIS IS THE MOST IMPORTANT STEP!
        Self::init_ports();

        // Disable global interrupts
        interrupt::without_interrupts(|_| {
            // Make sure the Reset Button is enabled!
            // Do not disable it if you want to be able to
            // reset your robot! (Otherwise you can only
            // stop it by switching it off completely,
            // if it gets out of control ;) )
            Self::enable_reset_button();

            // Make sure that IRCOMM and ACS are turned OFF!
            Self::disable_ircomm();
            Self::set_acs_power_off();

            Serial::init();
            /*
            // Initialize ADC:
            ADMUX = 0; //external reference
            ADCSRA = (0<<ADIE) | (0<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADIF);
            SFIOR = 0;

            // Initialize External interrupts:
            MCUCR = (0 << ISC11) | (1 << ISC10) | (0 << ISC01) | (1 << ISC00);
            GICR = (1 << INT2) | (1 << INT1) | (1 << INT0);
            MCUCSR = (0 << ISC2);

            // Initialize Timer 0 -  100µs cycle for Delays/Stopwatches, RC5 reception etc.:
            TCCR0 =   (0 << WGM00) | (1 << WGM01)
                    | (0 << COM00) | (0 << COM01)
                    | (0 << CS02)  | (1 << CS01) | (0 << CS00);
            OCR0  = 99;

            // Initialize Timer1 - PWM:
            // PWM, phase correct with ICR1 as top value.
            TCCR1A = (0 << WGM10) | (1 << WGM11) | (1 << COM1A1) | (1 << COM1B1);
            TCCR1B =  (1 << WGM13) | (0 << WGM12) | (1 << CS10);
            ICR1 = 210; // Phase corret PWM top value - 210 results in
                        // about 19 kHz PWM.
                        // ICR1 is the maximum (=100% duty cycle) PWM value!
                        // This means that the PWM resolution is a bit lower, but
                        // if the frequency is lower than 19 kHz you may hear very
                        // annoying high pitch noises from the motors!
                        // 19 kHz is a bit over the maximum frequency most people can
                        // hear!
                        //
                        // ATTENTION: Max PWM value is 210 and NOT 255 !!!
            OCR1AL = 0;
            OCR1BL = 0;
            setMotorDir(FWD,FWD); 	// Direction Forwards

            // Initialize Timer2 - ACS:
            TCCR2 = (1 << WGM21) | (0 << COM20) | (1 << CS20);
            OCR2  = 0x6E; // 0x6E = 72kHz @8MHz

            // Initialize Timer Interrupts:
            TIMSK = (1 << OCIE0); //| (1 << OCIE2); // Fixed: Timer2 Interrupt is turned
                                  // off by default now! It is only active
                                  // when ACS/IRCOMM are transmitting.

            // Initialize ACS:
            sysStatACS.channel = ACS_CHANNEL_RIGHT;
            acs_state = ACS_STATE_IRCOMM_DELAY;
               */
        }); // Enable Global Interrupts
    }

    /// Initializes the IO ports of the robot.
    pub fn init_ports() {
        // init all ports to 0 (except `pd0` = RXD)
        registers::PORTA::write(0b00000000);
        registers::PORTB::write(0b00000000);
        registers::PORTC::write(0b00000000);
        registers::PORTD::write(0b00000001);
        // init input/output directions
        registers::DDRA::write(0b00000000);
        registers::DDRB::write(0b01011000);
        registers::DDRC::write(0b10001100);
        registers::DDRD::write(0b11110010);
    }

    /// Enable power on the `RobotBase`.
    pub fn power_on() {
        PowerOn::set_high();
    }

    /// Disable power on the `RobotBase`.
    pub fn power_off() {
        PowerOn::set_low();
    }

    /// Enable the hardware reset button on the robot.
    pub fn enable_reset_button() {
        ResetButton::set_low();
        ResetButton::set_input();
    }

    /// Disable the hardware reset button on the robot.
    pub fn disable_reset_button() {
        ResetButton::set_low();
        ResetButton::set_output();
    }

    /// Disable the IRCOMM of the robot.
    pub fn disable_ircomm() {
        IRComm::set_low();
    }

    /// Set the LEDs on the `RobotBase` to the least significant 6 bits of the provided value
    pub fn set_leds(value: u8) {
        // set LEDs SL1-SL3
        set_pins!([Led3, Led2, Led1], value);
        // set LEDs SL4-SL6
        set_pins!([Led6, Led5, Led4], value >> 3);
    }
}