# Fish Light

## Code Explained

This section is more of the tutorial-like one to explain the meaning behind the code. The code is publically available on GitHub.

The project is not complex. All can be written using just one file - main.c. However, I will split it into sections in order to make things easier to explain.

### Initialization

The first step is to set up all the necessary registers of our microcontrollers. Once again, the explanation of each and every register is done in the datasheet. However, since it is often preferred to start up with an example, let us see exactly how the code was implemented.

First, let us place some global definitions. Here, the asf.h is the only library that we actually need. At least life it is much easier when having it, since then we can use the registers’ names. In other words, whenever we type for example TCNT2 the compiler with immediately recognize the register’s name. Late on, we assign the pins some specific names. It is a good practice. If we decide to change the function a pin, it is enough to edit this section. This way we can bring hardware into the software, which will make the code much easier to maintain.

Next, we define the an array containing the precomputed values needed to feed the R, G, B channels. I agree that it might not be the most exact way to compute the right color values as it does limit the color resolution. On the positive side, however, it is a simple and efficient approach that is easy to implement, debug and it does consume the minimum resources from the microcontroller’s side. As the table contains 3 times 256 values, let me skip it here. I will refer to it later as uint8_t color_temperature[256][3];. Let us move to the functions’ prototypes. In principle, we need to ensure three things:

• We need to control, which pins should act as inputs and which as outputs.
• We need to enable the interrupts and set the properly.
• We will also need timers in order to generate the PWM signal.

The choice of pins was not accidential. However, it enough at this point that you accept that the pins, whoose associated bit in the direction register DDRx is 0 will act as inputs and setting 1 will make them outputs. TOSC1 and TOSC2 are the two specific pins that are used to connect the external crystal oscillator. Obviously, they need to act as inputs.

Reading the keys with interrupts is much more efficient and at the same time simpler. ATmega88 allows to trigger a pin configured interrupt, whenever a pin changes its state.

The first line allows to generally use pin-controlled interrupts on pins from 0 to 7. The second line specifies, which are the pins. Finally, we need to allow the interrupts in general. As mentioned earlier, we need timer counters to allow us generating PWM signals. There exist three timer counters on ATmega88. Two of them are needed for the output channels. The remaining one will be used for tracking the time.

First, AS2 bit needs to be set in order to allow the TOSC1,2 pins to be used for an external crystal if the main CPU operates using the internal RC oscillator. Secondly, setting the TCCR2B as shown allows to prescale the external clock source. Having the oscillator operating on 32.768 kHz, prescalling it with 128 will make the 8-bit counter overflow exactly every second. Setting TOIE2 will trigger an interrupt when the overflow occurs. Finally, we reset the register and ensure the normal mode of operation. The next to counters will be used to generate the PWM signal on four channels (R, G, B and lamp).

Here, we are using the inverted PWM mode, which means that we have a positive input after the overflow occurs, not before. If the non-inverted output was used and we wanted a channel to be off, it would take at least one clock cycle to clear the OCRx flag. That would make enough time for the microcontroller to emit a short pulse. Since LEDs respond very fast to the electrical signal, these short repeating pulses would appear as constant glowing. The inverted mode, for a change, acts in the opposite way setting the flag instead of clearing it, which solves the problem.

In addition to that, since we need 3 channels (R, G, B) to act at the same time, it really makes no sense to use the 16-bit feature offered by the timer counter 1 (TC1) OCRIAH = 0x00; ensures that the upper 8 bits are not used.

Finally, we need to ensure that the global register PRR allows the timer counter to operate.

### Prototypes of the programme functions

This is a simple function that helps to track the time. I chose this way, since it allows to use only 8-bit numbers to store the time.

## Function loading the color values

As you can see, controlling of the channels is done by loading the correct values to the PWM prescalers OCR1AL, OCR0A, OCR0B and OCR1BL. Since we use the inverted PWM mode, we also invert the RGB_lvl_manual[...] value. In the automatic mode, the table used contains already inverted values.

### Main loop in the programme

After all settings are initialised, the main function simply alternates between updating the time and changing the output accordingly.

### Interrupts

Interrupt requests can be used for many things. Here we use them for allowing the microcontroller to respond to input signals. According to table 1 different keys are used to perform different functions.

The PCINT0_vect is associated with the pin-controlled interrupt that is used to sense the keys’ status. However, since the interrupt is triggered by the change of the state, every time we press (and release) a key the key, the interrupt will be executed twice. For this reason, the if statements check for the the zero value only. Finally, we allow the color channels to overflow (or underflow) when increasing (or decreasing) the value. This makes it possible to reach the full intensity faster.

The last interrupt occurs whenever timer-counter 2 overflows. Naturally, it is used to update the daytime buffer every second.