---
# System prepended metadata

title: PDI-PEI-PL1-2025

---

# 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.

![imagen](https://hackmd.io/_uploads/ByzWslWy-g.png)

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.

![kiss2](https://hackmd.io/_uploads/B16WQV9JZx.png)

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

![Screenshot from 2025-11-07 12-59-23](https://hackmd.io/_uploads/rJOKywoJ-g.png)

## Mensajes impresos en el terminal de eclipse

![Screenshot from 2025-11-07 12-58-32](https://hackmd.io/_uploads/SyS51PoyZl.png)

:::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;
}
```
:::