# PDI-2023: Practice 5: SPI Sensor
The objective of this practice is to use the SPI interface in a real system.
For this purpose, an already coded eclipse project is provided for the students to study and understand.
:::info
:information_source: First of all download the project and import it to your eclipse environment as is described below.
:::
## Download and import P5 eclipse project
Download de P5 project ZIP form the following link: [Project P5](https://drive.google.com/file/d/1EU1BKbKJq8HpfLnzPT9yTesqO7DA5i7f/view?usp=sharing)
Once unzipped the Downloads folder should contain a folder named P5_SPI as is shown in the following figure.

Open eclipse environment en select the option ```import``` in the ```File``` menu. Select ```Existing Projects into Workspace``` and press ```Next```.

Select the folder ```P5_SPI``` located in ```Downloads``` folder and press ```Finish```.

Now the P5_SPI project is added to the Eclipse Projets Explorer and the project can be built.
:::info
:information_source: Take a look to the code and try to understand how it works.
:::
:::success
:raising_hand: Read the ```void config_spi_nav(void)``` function and understand the SPI configuration:
* REV
* SPI clock
* SPI transfer length
```c=
void config_spi_nav(void)
{
pNAV_SPI_REGS->Mode = 0; //Reset
pNAV_SPI_REGS->Mode &=~(1<<30); //LOOP mode disabled
// pNAV_SPI_REGS->Mode |= (1<<30); //LOOP mode enabled
pNAV_SPI_REGS->Mode |=(1<<25); //Master enabled
pNAV_SPI_REGS->Mode |=(1<<26); //REV enabled: MSB transmited first, see TX/RX regs
// pNAV_SPI_REGS->Mode |=(10<<16); //PM:3 40Mhz/16 => 2,5 Mhz (SCK) (ARTY)
pNAV_SPI_REGS->Mode |=(4<<16); //PM:4 40Mhz/20 => 2 Mhz (SCK) (ARTY)
// pNAV_SPI_REGS->Mode |=(15<<16); //PM:15 250Mhz/64 => 3,9 Mhz (SCK)
pNAV_SPI_REGS->Mode |=(1<<2); //Ignore SPISEL
// pNAV_SPI_REGS->Mode &=~(0xF<<20); //LEN = 0 (WLEN = 32 bits)
pNAV_SPI_REGS->Mode |= (0xF<<20); //LEN = 15 (WLEN = 16 bits)
// pNAV_SPI_REGS->Mode |= (0x7<<20); //LEN = 7 (WLEN = 8 bits)
pNAV_SPI_REGS->Mode |=(1<<24); //Core enabled
printREGS();
}
```
:::
:::success
:raising_hand: Read the ```uint8_t read_nav(uint8_t slave, uint8_t reg_id)``` function and understand how does it works:
* Draw the sequence of SPI signals involved in reading a register. How would a write sequence looks like?
* Where is the device selected?
```c=
uint8_t read_nav(uint8_t slave, uint8_t reg_id)
{
uint32_t write_timeout=0;
uint32_t word;
pNAV_SPI_REGS->Event |= (0x3<<11); //Clean flags UN and OV
while(((pNAV_SPI_REGS->Event & (1<<8))==0) && (write_timeout < 0xAAAAA))
{
write_timeout++; //Wait until NF=1
}
if(write_timeout < 0xAAAAA)
{
// RW: 1 -> Read, 0 -> Write
// TX request: <RW:1bit><REG_ID:7bits> : <0>
// RX data: <REG Value:8bits>
word=0;
word |= ((0x80 | reg_id) << 24); // REG_ID, Read
pNAV_SPI_REGS->SlaveSelect &= ~(1 << slave); // Slave select goes down
pNAV_SPI_REGS->Transmit=word; //Transmit word
while(pNAV_SPI_REGS->Event & (1<<31))
; // Wait while transfer in progress
pNAV_SPI_REGS->SlaveSelect |= (1 << slave); // Slave select goes up
// Read received data
if (pNAV_SPI_REGS->Event & (1<<9))
{
// RX register 16 MSB bits contains REG value and REG+1 value
// printf("DATA: %08X\n", pNAV_SPI_REGS->Receive );
return (pNAV_SPI_REGS->Receive >> 16);
}
else
{
printf("Error: no data received...\n");
}
}
else
{
printf("Read reg NF Timeout...\n");
}
return 0xFF;
}
```
:::
:::success
:raising_hand: Read the ```getACEL_X``` function and describe how it works:
```c=
uint16_t getACEL_X()
{
uint8_t AclX_L, AclX_H;
AclX_L=read_nav( ACCELEROMETER_SLAVE, OUT_X_L_XL );
AclX_H=read_nav( ACCELEROMETER_SLAVE, OUT_X_H_XL );
return ( (int16_t)(AclX_H << 8) | AclX_L );
}
```
* What kind of operation is done with ```return ( (int16_t)(AclX_H << 8) | AclX_L );```
:::
:::success
:raising_hand: Read the following code located in main function:
```c=
AclX = getACEL_X();
AclY = getACEL_Y();
AclZ = getACEL_Z();
// Normalizar los datos
double AclX_norm = (double)AclX / 16384.0;
double AclY_norm = (double)AclY / 16384.0;
double AclZ_norm = (double)AclZ / 16384.0;
printf("AclX: %f ", AclX_norm);
printf("AclY: %f ", AclY_norm);
printf("AclZ: %f ", AclZ_norm);
float pitch = atan2(AclX_norm, sqrt(AclY_norm*AclY_norm + AclZ_norm*AclZ_norm)) * (180 / M_PI);
float roll = atan2(AclY_norm, sqrt(AclX_norm*AclX_norm + AclZ_norm*AclZ_norm)) * (180 / M_PI);
```
* What kind of operation is done with ```double AclX_norm = (double)AclX / 16384.0;```
* What do ```pitch``` and ```roll``` values mean?
* Draw a graphical representation of the vectors involved in the calculation.
:::
## Test P5 on a real board
A real board is going to be attached to development environment by means of the USB interface. When the new USB device is detected, it must be connected to the virtual machine.

### Board monitor
In order to verify that the board is properly attached to the system, open an eclipse terminal on the P5_SPI project and type the following command ```grmon -digilent```, as is shown in the following below. The monitor must print the RISCV system components present in the board:
```
atcsol@atcsol:~/Downloads/P5_SPI$ grmon -digilent
GRMON debug monitor v3.3.4.1 64-bit eval version
Copyright (C) 2023 Frontgrade Gaisler - All rights reserved.
For latest updates, go to https://www.gaisler.com/
Comments or bug-reports to support@gaisler.com
This eval version will expire on 15/02/2024
JTAG chain (1): xc7a100t
Device ID: 0x330
GRLIB build version: 4267
Detected frequency: 40.0 MHz
Component Vendor
NOEL-V RISC-V Processor Frontgrade Gaisler
GR Ethernet MAC Frontgrade Gaisler
AHB Debug UART Frontgrade Gaisler
JTAG Debug Link Frontgrade Gaisler
EDCL master interface Frontgrade Gaisler
Xilinx MIG Controller Frontgrade Gaisler
Generic AHB ROM Frontgrade Gaisler
AHB/APB Bridge Frontgrade Gaisler
RISC-V CLINT Frontgrade Gaisler
RISC-V PLIC Frontgrade Gaisler
RISC-V Debug Module Frontgrade Gaisler
AMBA Trace Buffer Frontgrade Gaisler
Version and Revision Register Frontgrade Gaisler
AHB Status Register Frontgrade Gaisler
General Purpose I/O port Frontgrade Gaisler
SPI Controller Frontgrade Gaisler
Modular Timer Unit Frontgrade Gaisler
Generic UART Frontgrade Gaisler
Use command 'info sys' to print a detailed report of attached cores
grmon3>
```
### Program load
The binary of the ```P5_SPI``` project can be load in the board using the ```load``` command.
```
grmon3> load Debug/P5_SPI
0 .text 33.4kB / 33.4kB [===============>] 100%
8570 .srodata 4B [===============>] 100%
8574 .eh_frame 60B [===============>] 100%
85b0 .srodata.cst8 224B [===============>] 100%
8690 .data 1.8kB / 1.8kB [===============>] 100%
Total size: 35.46kB (376.78kbit/s)
Entry point 0x00000000
Image /home/atcsol/Downloads/P5_SPI/Debug/P5_SPI loaded
grmon3>
```
### Program run
The output messages of the program are displayed by ```cutecom``` program. It is important to configure ```cutecom``` with the following settings. The virtual terminal is ```/dev/ttyUSB1```. In addition and since the connection is to be made with a real system the ```Baudrate``` parameter must match the one used by the board, in this case ```38400```.

Once ```cutecom``` is started the code project is executed with the following command:
```
grmon3> run
```
The program start, print some configuration messages and finally prints every second the 3 axes acceleration and the ```pitch``` and ```roll``` angles.

:::success
:raising_hand: Tilt the board on its axes and observe the printed values.
:::
## Student work
### Basic visual indication
Complete the code to add an excessive Pitch/Roll visual indication according to the following requirements:

* If roll is higher than 30º turn on the rightmost led, otherwise it must be off
* If pitch is higher than 30º turn on the leftmost led , otherwise it must be off
:::info
:information_source: Only ```main``` function file must be modified.
:::
### Advanced visual indication
Complete the code to add an excessive Pitch/Roll visual indication according to the following requirements:
* If pitch or roll is higher than 30º the four leds must blink, otherwise they must be off
:::info
:information_source: In order to implement the ```blinking``` efect a timer ISR must be used.
:::
:::spoiler
```c=
// Código de encendido/apagado de los leds, sin parpadeo
uint32_t pines = gpio_read();
if ( pitch > 30 )
{
pines |= LED_0_MASK;
}
else
{
pines &= ~LED_0_MASK;
}
if ( roll > 30 )
{
pines |= LED_3_MASK;
}
else
{
pines &= ~LED_3_MASK;
}
gpio_write( pines );
}
```
:::