# 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. ![Screenshot from 2023-11-29 09-43-52](https://hackmd.io/_uploads/Hy-GqdNrT.png) Open eclipse environment en select the option ```import``` in the ```File``` menu. Select ```Existing Projects into Workspace``` and press ```Next```. ![Screenshot from 2023-11-29 09-45-30](https://hackmd.io/_uploads/B1s49uVr6.png) Select the folder ```P5_SPI``` located in ```Downloads``` folder and press ```Finish```. ![Screenshot from 2023-11-29 09-46-24](https://hackmd.io/_uploads/B1EOq_4S6.png) 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. ![image](https://hackmd.io/_uploads/HysLi_4ra.png) ### 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```. ![Screenshot from 2023-11-29 09-56-59](https://hackmd.io/_uploads/BkOlauNS6.png) 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. ![Screenshot from 2023-12-12 11-37-43](https://hackmd.io/_uploads/H1qEOnrIa.png) :::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: ![](https://hackmd.io/_uploads/rJ6pLWW6n.png) * 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 ); } ``` :::