# Project 1 Interactive Hello World

This project picks up directly from the flashing instructions in the root `README.md`. In this project we will be manually creating all LVGL objects within the REPL. We do this to progressively learn how an LVGL interface is built from a set of parent<->child relationships.

The code within the example assumes an ESP32-S3 DevKitC-1, [pin out diagram is available here](https://docs.espressif.com/projects/esp-idf/en/v5.2.2/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html#pin-layout). 

The code also assumes an ili9341 TFT Display with an xpt2046 touch sensor. These boards are inexpensive and available on Amazon or Aliexpress from many distributors.

##  Our pin mapping

The ili9341/xpt2046 uses the Serial Peripheral Interface(SPI) for communication with our board. You should [review the terminology](https://docs.espressif.com/projects/esp-idf/en/v5.2.2/esp32s3/api-reference/peripherals/spi_master.html#terminology) section of the documentation before continuing. The ESP32-S3 has multiple SPI buses (or in Espressif terms 'hosts') some of which are already in use for flash and PSRAM on the devkit. SPI buses 1 and 2 will be used here for our example, each of which will be connected to the ili9341 and xpt2046, with different configured speeds.

### The GPIO Matrix and IO_MUX 

If you are combing through the ESP-IDF documentation you will find references to SPI buses connected to specific GPIO pins. The ESP32-S3 has a flexible I/O system that allows for mapping internal peripherals (e.g. SPI) to GPIO pins. This is achieved via the I/O Multiplexer(IO_MUX) and the GPIO Matrix.

The IO_MUX is a hardware-level multiplexer that connects the chip's internal peripherals to the physical pins. Each GPIO pin has a default peripheral function, which is directly connected through the IO_MUX. Using the IO_MUX provides the fastest and most efficient connection between a peripheral and a pin. This is why you will see in the pin out diagram reference SPI functions like FSPICS0(Chip select) to GPIO pin 10. 

The GPIO Matrix is a programmable routing system that sits between the IO_MUX and the chip's peripherals. It allows almost any internal signal to be routed to almost any GPIO pin. In short it allows us to map the Chip select functionality mentioned above to another pin.

This project makes generous use of the flexible pin assignment capability provided by the GPIO Matrix. Be aware that technically the IO_MUX default pins will provide better performance. Also in Micropython the SPI numbers do not directly map to the ESP-IDF SPI host numbers. 

See: `micropython/ports/esp32/machine_hw_spi.c`
```
// SPI mappings by device, naming used by IDF old/new
// upython   | ESP32     | ESP32S2   | ESP32S3 | ESP32C3
// ----------+-----------+-----------+---------+---------
// SPI(id=1) | HSPI/SPI2 | FSPI/SPI2 | SPI2    | SPI2
// SPI(id=2) | VSPI/SPI3 | HSPI/SPI3 | SPI3    | err
```

For our ESP32-S3 the micropython hardware SPI buses (1 and 2) are mapped to the ESP-IDF SPI2 and SPI3 of our chip.

### Pin Mapping

In my breadboard I have mapped the following ESP32-S3 GPIO pins to my `ili9341/xpt2046`. The pins chosen were simply for convenience sake, you can adjust the pin mapping as you see fit. Do note though mapping a pin to GPIO43 or GPIO44 can result in the loss of repl access. This is because UART0 TX/RX are IO_MUX'd to these pins. 

On the ili9341/xpt2046 I am numbering the J2 header pins from left to right.

| ESP32-S3 / Function | ili9341/xpt2046 J2 |
|---------------------|--------------------|
| 3.3V                | 1. VCC             | 
| GND                 | 2. GND             | 
| GPIO 6 / CS         | 3. CS              | 
| GPIO 5 / Reset      | 4. RESET           | 
| GPIO 4 / DC         | 5. DC              | 
| GPIO 14 / MOSI      | 6. SDI(MOSI)       | 
| GPIO 13 / SCK       | 7. SCK             | 
| 3.3V                | 8. LED             | 
| GPIO 41 / Unused    | 9. SDO(MISO)       | 
| GPIO 42             | 10.T_CLK           | 
| GPIO 35             | 11.T_CS            |
| GPIO 1              | 12.T_DIN           |
| GPIO 2              | 13.T_DO            |
| GPIO 42             | 14.T_IRQ           |

## Running the Code

Assuming no other Micropython serial devices are connected to your PC. Plug your board in and type the command `mpremote`. This should automatically connect to the first device found and you will be presented with  the repl input `>>>`.

Let's import all of the library code we will need:

```python
import lvgl as lv
import lcd_bus
import ili9341
from machine import SPI
```

Now we will create our SPI bus for the ili9341 display

```python
spi_bus = SPI.Bus(host=1, mosi=14, miso=41, sck=13)
```

Now we will instantiate our display object and set the data transfer rate of the bus to 40MHz.

```python
display_bus = lcd_bus.SPIBus(
    spi_bus=spi_bus,
    dc=4,
    cs=6,
    freq=40000000,
)

display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=240,
    display_height=320,
    reset_pin=5,
    reset_state=ili9341.STATE_LOW,
    backlight_pin=40,
    color_space=lv.COLOR_FORMAT.RGB565,
    color_byte_order=ili9341.BYTE_ORDER_BGR,
    rgb565_byte_swap=True,
)
```


```
display.set_power(True)
display.init()
display.set_rotation(lv.DISPLAY_ROTATION._90)

# setting this to anything but 100 or 0 when you don't have the
# backlight state set to PWM isn't going to do anything
display.set_backlight(100)

display.invert_colors()
```

