# PDI-PEI-PL1-2025
## PL1
Se desea medir el tiempo en milisegundos que transcurre entre la activación y desactivación de un pulsador (push-button). Para ello se deberá detectar el flanco de subida y de bajada del pulsador, obtener el número de ticks del sistema que han transcurrido entre ambos eventos, convertir este valor a su equivalente en milisegundos e imprimirlo por pantalla.
* Se podrán usar las rutinas implementadas en la práctica 1 para el acceso al GPIO
* uint32_t gpio_read();
* void gpio_write(uint32_t);
* Los Push buttons BT[0..3] están mapeados en las entradas GPIO [4..7]
* Para la obtención del valor actual de los ticks del sistema se usará la función:
* uint64_t get_ticks_from_reset();
Se pide: Completar el código de la función main mostrado a continuación para que se realice la funcionalidad pedida para el pulsador BT0:
```c=
int main()
{
uint32_t input, dir;
// Otras variables y constantes que sean necesarias para el programa
// Enable output on bits 19..16
dir = gpio_get_direction();
dir |= 0x000F0000;
gpio_set_direction( dir );
while( 1 ) // Completar el código del super-loop
{
} // while
} // main
```
:::spoiler
### Posible solución
```c=
#include "riscv_types.h"
#include "riscv_uart.h"
#include "gpio_drv.h"
#include "dispatch.h"
#include "clinc.h"
#include "riscv_monotonic_clock.h"
#include "log.h"
#define MS_TICKS 10000 // ticks por milisegundo
int main()
{
uint32_t dir, input_current, input_previous;
uint32_t button_current, button_previous;
uint64_t init_ticks, diff_ticks;
printf("Press and release BT0\n");
// Enable output on bits 19..16
dir = gpio_get_direction();
dir |= 0x000F0000;
gpio_set_direction( dir );
input_current = gpio_read();
while(1)
{
input_previous = input_current;
input_current = gpio_read();
button_previous = input_previous & BT_0_MASK;
button_current = input_current & BT_0_MASK;
if ( button_current && !button_previous ) // Rising edge
{
init_ticks = get_ticks_from_reset();
} // if
if ( !button_current && button_previous ) // Falling edge
{
diff_ticks = get_ticks_from_reset() - init_ticks;
uint32_t elapsed_mseconds = diff_ticks / MS_TICKS;
printf("Elapsed miliseconds: %d\n", elapsed_mseconds );
} // if
} // while
return 0;
}
```
:::
## Ampliaciones
### Indicación de timeout mediante el encendido de los leds
Si el tiempo transcurrido entre la activación y desactivación del pulsador BT0 supera el segundo se encenderán los 4 leds de la placa. Estos se mantendrán encendidos hasta que se pulse el BT1 momento en que se apagarán.
### Indicación de timeout mediante el parpadeo de los leds
Si el tiempo transcurrido entre la activación y desactivación del pulsador BT0 supera el segundo, los 4 leds de la placa comenzarán a parpadear con una cadencia de 1 segundo (500ms encendidos/500ms apagados).
#### Solución basada en ```get_ticks_from_reset```
:::spoiler
```c=
#include "riscv_types.h"
#include "riscv_uart.h"
#include "gpio_drv.h"
#include "dispatch.h"
#include "clinc.h"
#include "riscv_monotonic_clock.h"
#include "log.h"
#define SEG_TICKS 10000000 // ticks por segundo
#define MS_TICKS 10000 // ticks por milisegundo
#define TICKS_500MS 5000000 // 500 ms
#define LEDS_MASK 0x000F0000 // ticks por milisegundo
int main()
{
uint32_t dir, input_current, input_previous, input, on_blinking = 0;
uint32_t button_current, button_previous;
uint64_t init_ticks, diff_ticks, start_blinking_ticks, toggle_blinking_ticks;
printf("Press and release BT0\n");
// Enable output on bits 19..16
dir = gpio_get_direction();
dir |= LEDS_MASK;
gpio_set_direction( dir );
input_current = gpio_read();
while(1)
{
input_previous = input_current;
input_current = gpio_read();
button_previous = input_previous & BT_1_MASK;
button_current = input_current & BT_1_MASK;
if ( button_current && !button_previous ) // Rising edge BT1
{
on_blinking = 0;
printf("Turn blinking OFF\n");
input = gpio_read();
input &= ~LEDS_MASK;
gpio_write( input );
} // if
button_previous = input_previous & BT_0_MASK;
button_current = input_current & BT_0_MASK;
if ( button_current && !button_previous ) // Rising edge BT0
{
init_ticks = get_ticks_from_reset();
} // if
if ( !button_current && button_previous ) // Falling edge
{
diff_ticks = get_ticks_from_reset() - init_ticks;
uint32_t elapsed_mseconds = diff_ticks / MS_TICKS;
printf("Elapsed miliseconds: %d\n", elapsed_mseconds );
if ( diff_ticks > SEG_TICKS)
{
if ( !on_blinking )
{
on_blinking = 1;
printf("Turn blinking ON\n");
input = gpio_read();
input |= LEDS_MASK;
gpio_write( input );
start_blinking_ticks = get_ticks_from_reset();
}
}
} // if
if ( on_blinking )
{
toggle_blinking_ticks = get_ticks_from_reset() - start_blinking_ticks;
if ( toggle_blinking_ticks >= TICKS_500MS )
{
input = gpio_read();
if ( input & LEDS_MASK ) // Estan encendidos: apago
{
input &= ~LEDS_MASK;
gpio_write( input );
}
else // Estan apagados: enciendo
{
input |= LEDS_MASK;
gpio_write( input );
}
start_blinking_ticks += TICKS_500MS;
} // if ( toggle)
} // if (on_blinking)
} // while
return 0;
}
```
:::
El parpadeo se mantendrá hasta que se pulse el BT1, momento en que se apagarán los leds.
### Recodifique las soluciones anteriores para medir el tiempo que transcurre entre las pulsaciones de dos botones diferentes, por ejemplo BT2 y BT3.
---
## PEI
Se desea codificar un programa capaz de recibir y decodificar tramas de datos de acuerdo al formato de trama definido en el estándar KISS.
De forma muy esquemática el formato KISS define un carácter especial, *FEND*, que marca los límites, principio y fin, de la secuencia de bytes (octetos) a transmitir. A la hora de enviar un mensaje, este se encapsula con los caracteres FEND tal como se muestra en la figura. En este caso el tamaño del mensaje es variable, pero tiene un tamaño máximo de 256 bytes.

El mensaje, Data0 … DataN, estará siempre compuesto por una secuencia de caracteres imprimibles, esto garantiza que ***NUNCA*** habrá un carácter *FEND* formando parte del mensaje.
Se pide:
Complete las declaraciones y el código de la ISR para que el programa principal que se proporciona sea capaz de recibir una trama con el formato indicado de acuerdo al patrón de codificación top/button. Una vez recibida la trama se invoca la función procesa_y_respondeTRAMA() que NO hay que codificar.
```c=
#include "riscv_types.h"
#include "riscv_uart.h"
#include "dispatch.h"
#include "log.h"
// COMPLETAR CON LAS CONSTANTES
#define MAX_TAM_TRAMA 256
#define FEND 0xC0 // Valor del delimitador de una trama KISS
uint8_t flag_trama_completa = 0;
uint8_t buffer_trama[ MAX_TAM_TRAMA ];
uint32_t tam_trama;
// COMPLETAR EL RESTO DE VARIABLES GLOBALES
// UART RX IRQ handler
void uart_rx_irq_handler (void)
{
uint8_t car;
car = (uint8_t)riscv_getchar();
// COMPLETAR EL CÓDIGO DE LA ISR
} // ISR
// NO SE PIDE
void procesa_y_respondeTRAMA( uint8_t buffer_trama[], uint32_t lng )
{
printf("Process frame\n");
for( int i = 0;i < lng; i++ )
printf("%02X ", buffer_trama[i]);
printf("\n");
}
int main() // Función main: NO MODIFICAR
{
// TX/RX enable
riscv_uart_enable_TX();
riscv_uart_enable_RX();
// E/S por interrupción
install_irq_handler( UART_IRQ, uart_rx_irq_handler ); // Install uart handler
riscv_uart_enable_RI(); // Enable RX irq
plic_irq_unmask( UART_IRQ ); // PLIC UART irq unmask
enable_ext_irq(); // Enable MIE.MEI
enable_irq(); // Global interrupt enable
while(1)
{
if ( flag_trama_completa )
{
procesa_y_respondeTRAMA( buffer_trama, tam_trama );
flag_trama_completa = 0;
} // if
} // while
return 0;
}
```
En el buffer de recepción se deben almacenar solo los datos recibidos SIN los caracteres FEND.
Se asume que no se recibe una trama nueva hasta que se procesa y responde la anterior.
Asuma también que ya están incluidos todos los *“include”* necesarios.
Ejemplos de tramas:
```
0XC0 0x55 0xC0 -> Tamaño datos: 1, buffer_trama[] = {0x55}
0XC0 0x55 0x01 0x23 0xC0 -> Tamaño datos: 3, buffer_trama[] = {0x55, 0x01, 0x23}
```
:::spoiler
### Posible solución
```c=
#include "riscv_types.h"
#include "riscv_uart.h"
#include "dispatch.h"
#include "log.h"
// COMPLETAR CON LAS CONSTANTES
#define MAX_TAM_TRAMA 256
#define FEND 0xC0 // Valor del delimitador de una trama KISS
// COMPLETAR EL RESTO DE VARIABLES GLOBALES
uint8_t flag_trama_completa = 0;
uint8_t buffer_trama[ MAX_TAM_TRAMA ];
uint32_t tam_trama;
uint32_t idx = 0, in_frame = 0;
// UART RX IRQ handler
void uart_rx_irq_handler (void)
{
uint8_t car;
car = (uint8_t)riscv_getchar();
// COMPLETAR EL CÓDIGO DE LA ISR
if ( in_frame ) // Estoy dentro ?
{
if ( car == FEND ) // fin de trama, procesar el mensaje
{
flag_trama_completa = 1;
in_frame = 0;
tam_trama = idx; // Guardo la cantidad actual de los datos recibidos
idx = 0;
}
else
{
// Save received value
buffer_trama[ idx ] = car;
idx++;
} // else
} // if
else // Estoy fuera
{
if ( car == FEND ) // comienzo de trama, no guardo FEND
{
in_frame = 1;
}
else
{
printf("Not in frame -> discard data: %02X\n", car );
} // else
} // else
} // ISR
// NO SE PIDE
void procesa_y_respondeTRAMA( uint8_t buffer_trama[], uint32_t lng )
{
printf("Process frame\n");
for( int i = 0;i < lng; i++ )
printf("%02X ", buffer_trama[i]);
printf("\n");
}
int main() // Función main: NO MODIFICAR
{
// TX/RX enable
riscv_uart_enable_TX();
riscv_uart_enable_RX();
// E/S por interrupción
install_irq_handler( UART_IRQ, uart_rx_irq_handler ); // Install uart handler
riscv_uart_enable_RI(); // Enable RX irq
plic_irq_unmask( UART_IRQ ); // PLIC UART irq unmask
enable_ext_irq(); // Enable MIE.MEI
enable_irq(); // Global interrupt enable
while(1)
{
if ( flag_trama_completa )
{
procesa_y_respondeTRAMA( buffer_trama, tam_trama );
flag_trama_completa = 0;
} // if
} // while
return 0;
}
```
:::
## Ampliaciones
Se define una trama KISS con el siguiente formato.

En ella se envían dos datos que serán usados para la gestión de los leds.
* Data0: Encender (1) / Apagar (0)
* Data1: Led a gestionar (0-3)
Se pide modificar la función ```procesa_y_respondeTRAMA``` para que responda a la funcionalidad pedida.
El sistema deberá responder con otra trama KISS con un solo byte de datos en el que se codifica el resultado de la operación pedida.
* 0: Operación correcta
* 1: Número de parámetros incorrectos. Valor válido 2.
* 2: Valor de Data0 incorrecto. Valores válidos 0 y 1.
* 3: Valor de Data1 incorrecto. Valores válidos 0-3.
### Ejemplos
| Trama recibida | Respuesta | Explicación |
| -------- | -------- | -------- |
| <small>```0xC0 0x00 0xC0```</small> | <small>```0xC0 0x01 0xC0```</small> | <small>Número de parámetros incorrecto</small> |
| <small>```0xC0 0x00 0x02 0x03 0xC0```</small> | <small>```0xC0 0x01 0xC0```</small> | <small>Número de parámetros incorrecto</small> |
| <small>```0xC0 0x50 0x02 0xC0```</small> | <small>```0xC0 0x02 0xC0```</small> | <small>Valor de Data0 incorrecto</small> |
| <small>```0xC0 0x01 0x20 0xC0```</small> | <small>```0xC0 0x03 0xC0```</small> | <small>Valor de Data1 incorrecto</small> |
| <small>```0xC0 0x01 0x01 0xC0```</small> | <small>```0xC0 0x00 0xC0```</small> | <small>Operación correcta, se enciende el led1</small> |
| <small>```0xC0 0x00 0x01 0xC0```</small> | <small>```0xC0 0x00 0xC0```</small> | <small>Operación correcta, se apaga el led1</small> |
## Sesión de Cutecom donde se envían 4 secuencias (2 erroneas y dos correctas) y las respuestas recibidas

## Mensajes impresos en el terminal de eclipse

:::spoiler
```c=
#include "riscv_types.h"
#include "riscv_uart.h"
#include "gpio_drv.h"
#include "dispatch.h"
#include "log.h"
// COMPLETAR CON LAS CONSTANTES
#define MAX_TAM_TRAMA 256
#define FEND 0xC0 // Valor del delimitador de una trama KISS
// COMPLETAR EL RESTO DE VARIABLES GLOBALES
uint8_t flag_trama_completa = 0;
uint8_t buffer_trama[ MAX_TAM_TRAMA ];
uint32_t tam_trama;
uint32_t idx = 0, in_frame = 0;
// UART RX IRQ handler
void uart_rx_irq_handler (void)
{
uint8_t car;
car = (uint8_t)riscv_getchar();
// COMPLETAR EL CÓDIGO DE LA ISR
if ( in_frame ) // Estoy dentro ?
{
if ( car == FEND ) // fin de trama, procesar el mensaje
{
flag_trama_completa = 1;
in_frame = 0;
tam_trama = idx; // Guardo la cantidad actual de los datos recibidos
idx = 0;
}
else
{
// Save received value
buffer_trama[ idx ] = car;
idx++;
} // else
} // if
else // Estoy fuera
{
if ( car == FEND ) // comienzo de trama, no guardo FEND
{
in_frame = 1;
}
else
{
printf("Not in frame -> discard data: %02X\n", car );
} // else
} // else
} // ISR
void procesa_y_respondeTRAMA( uint8_t buffer_trama[], uint32_t lng )
{
int32_t leds_masks[4] = { LED_0_MASK, LED_1_MASK, LED_2_MASK, LED_3_MASK };
uint32_t input;
printf("Process frame: LNG %d: ", lng);
for( int i = 0;i < lng; i++ )
printf("%02X ", buffer_trama[i]);
printf("\n");
// Control leds: <ON/OFF: 1/0> <led num: 0-3>
if ( lng != 2 )
{
//riscv_print_string( "NOK: Num parameters wrong\n");
riscv_putchar( 0xC0 );
riscv_putchar( 0x01 );
riscv_putchar( 0xC0 );
}
else
{
if ( (buffer_trama[0] == 0) || (buffer_trama[0] == 1) )
{
if ( (buffer_trama[1] >= 0) && (buffer_trama[1] <= 3))
{
if ( buffer_trama[0] == 1 ) // ON led
{
input = gpio_read();
printf("Turn LED%d on\n", buffer_trama[1]);
input |= leds_masks[ buffer_trama[1] ];
gpio_write( input );
}
else // OFF led
{
input = gpio_read();
printf("Turn LED%d off\n", buffer_trama[1]);
input &= ~leds_masks[ buffer_trama[1] ];
gpio_write( input );
}
//riscv_print_string( "OK\n");
riscv_putchar( 0xC0 );
riscv_putchar( 0x00 );
riscv_putchar( 0xC0 );
}
else
{
// riscv_print_string( "NOK: Param 2 wrong value\n");
riscv_putchar( 0xC0 );
riscv_putchar( 0x03 );
riscv_putchar( 0xC0 );
}
}
else
{
// riscv_print_string( "NOK: Param 1 wrong value\n");
riscv_putchar( 0xC0 );
riscv_putchar( 0x02 );
riscv_putchar( 0xC0 );
}
}
}
int main() // Función main: NO MODIFICAR
{
uint32_t dir;
// Enable output on bits 19..16
dir = gpio_get_direction();
dir |= 0x000F0000;
gpio_set_direction( dir );
// TX/RX enable
riscv_uart_enable_TX();
riscv_uart_enable_RX();
// E/S por interrupción
install_irq_handler( UART_IRQ, uart_rx_irq_handler ); // Install uart handler
riscv_uart_enable_RI(); // Enable RX irq
plic_irq_unmask( UART_IRQ ); // PLIC UART irq unmask
enable_ext_irq(); // Enable MIE.MEI
enable_irq(); // Global interrupt enable
while(1)
{
if ( flag_trama_completa )
{
procesa_y_respondeTRAMA( buffer_trama, tam_trama );
flag_trama_completa = 0;
} // if
} // while
return 0;
}
```
:::