2mp.conae.gov.ar I sae.2mp@conae.gov.a Manual Misión SAE CIAA POSIX. VERSION 1.1 Tabla de contenidos 1. [ Introducción ].................................................................................................................................... 4 2. [POSIX ]................................................................................................................................................ 4 3. [CIAA POSIX ] ....................................................................................................................................... 4 1.1. ciaaPOSIX_open ........................................................................................................................... 4 1.1.1. Ejemplo de aplicación ............................................................................................................... 5 1.2. ciaaPOSIX_ioctl............................................................................................................................. 6 1.2.1. UART ......................................................................................................................................... 6 1.2.2. I2C ............................................................................................................................................. 7 1.2.3. Entradas y salidas digitales ....................................................................................................... 7 1.2.4. Ejemplo de aplicación ............................................................................................................... 7 1.3. ciaaPOSIX_read y ciaaPOSIX_write .............................................................................................. 8 1.3.1. Ejemplo de aplicación ............................................................................................................... 8 1.4. ciaaPOSIX_close ........................................................................................................................... 8 1.5. Ejemplos de aplicación................................................................................................................. 9 1.5.1. Ejemplo 1 - GPIO salidas ........................................................................................................... 9 1.5.2. Ejemplo 2 - GPIO entradas ...................................................................................................... 12 1.5.3. Ejemplo 3 - UART .................................................................................................................... 14 1. [ Introducción ] En los ejemplos de CIAA-Frimware se utilizan funciones con el prefijo “ciaaPOSIX_”, está guía pretende explicar el funcionamiento de cada una de esas instrucciones. 2. [POSIX ] El nombre POSIX es un acrónimo de “Portable Operating System Interface”, en donde la X proviene de UNIX, dando un marco de pertenencia. POSIX es una norma que especifica nomenclatura, funciones, servicios y demás recursos que sean necesarios para facilitar la portabilidad de las aplicaciones entre distintos sistemas operativos. 3. [CIAA POSIX ] El CIAA Firmware adopta algunos conceptos de POSIX para facilitar al programador de aplicaciones un acceso a los recursos de hardware fácil y principalmente portable. Por este motivo es posible compilar las aplicaciones tanto para arquitectura x86 y ejecutarlas en la PC como para Cortex M4 y ejecutarlas en la ECU-CIAA-NXP. Como no se adopto de forma completa el estándar POSIX se dice que CIAA- Firmware es “POSIX Like”. Dentro de las funcionalidad del entorno CIAA, podemos encontrar cinco funciones para realizar el control total sobre cualquier tipo de periférico incorporado al Firmware. Las funciones son: ciaaPOSIX_open ciaaPOSIX_ioctl ciaaPOISX_read ciaaPOSIX_write ciaaPOSIX_close La definición de los prototipos de las funciones se encuentra en el archivo “ciaaPOSIX_stdio.h” dentro de la carpeta “Firmware\modules\posix\inc\”. A continuación se detallarán cada una de ellas 1.1. ciaaPOSIX_open Prototipo de la función: int32_t ciaaPOSIX_open(char const * path, uint8_t oflag); Recibe como parámetro: path: la dirección del periférico. oflag: el modo de apertura del periférico. Retorna un número negativo si falla y en caso de éxito un numero que identifica al descriptor del periférico seleccionado, a este número se lo llama “file descriptor”. 4 Manual POSIX Versión 1.0 A continuación se listan los periféricos presentes en EDU-CIAA-NXP, sus paths y sus ubicaciónes. Periférico Path Ubicación entradas digitales /dev/dio/in/0 Teclas salidas digitales /dev/dio/out/0 LEDs UART 1 /dev/serial/uart/1 UART USB UART 2 /dev/serial/uart/2 UART slot expansión I2C * /dev/i2c/0 I2C slot expansión * Solo presente en el Firmware distribuido para MISIÓN SAE Las opciones de apertura y sus valores son: Solo lectura Solo Escritura Lectura y escritura Interfaz no bloqueante O_RDONLY O_WRONLY O_RDWR O_NONBLOCK 1.1.1. Ejemplo de aplicación Ejemplo de apertura de periféricos: int32_t teclado; int32_t display; int32_t uartUSB; int32_t i2cExpansion; teclado = ciaaPOSIX_open("/dev/dio/in/0", O_RDONLY); display = ciaaPOSIX_open("/dev/dio/out/0", O_RDWR); uartUSB = ciaaPOSIX_open("/dev/serial/uart/1", O_RDWR | O_NONBLOCK); i2cExpansion = ciaaPOSIX_open("/dev/i2c/0", O_RDWR); En el código presentado se abrieron los siguientes periféricos: Periférico Configuración Entrada digital Solo lectura Salidas digitales Lectura y escritura UART 1 Lectura y escritura no bloqueante I2C Lectura y escritura File descriptor teclado display uartUSB i2cExpansion 5 Manual POSIX Versión 1.0 1.2. ciaaPOSIX_ioctl Prototipo de la función: int32_t ciaaPOSIX_ioctl(int32_t fildes, int32_t request, void* param); Recibe como parámetro: fildes: el número de file descriptor devuelto por la función ciaaPOSIX_open. request: el código de configuración que se desea modificar (depende del dispositivo). param: el valor de configuración. Retorna un -1 si falla y en caso de éxito otro número. A continuación se listan los códigos “request” más utilizados para cada periférico y sus posibles valores. 1.2.1. UART Codigo ciaaPOSIX_IOCTL_SET_BAUDRATE Valores ciaaBAUDRATE_300 ciaaBAUDRATE_600 ciaaBAUDRATE_1200 ciaaBAUDRATE_1800 ciaaBAUDRATE_2400 ciaaBAUDRATE_4800 ciaaBAUDRATE_9600 ciaaBAUDRATE_14400 ciaaBAUDRATE_19200 ciaaBAUDRATE_38400 ciaaBAUDRATE_57600 ciaaBAUDRATE_115200 ciaaBAUDRATE_230400 ciaaBAUDRATE_460800 ciaaBAUDRATE_921600 ciaaPOSIX_IOCTL_SET_FIFO_TRIGGER_LEVEL ciaaFIFO_TRIGGER_LEVEL0 ciaaFIFO_TRIGGER_LEVEL1 ciaaFIFO_TRIGGER_LEVEL2 ciaaFIFO_TRIGGER_LEVEL3 Estas configuración están detalladas en el archivo “ciaaPOSIX_ioctl_serial.h” Detalle Selecciona la tasa de transferencia de bits de la UART. Selecciona la cantidad de bytes recibidos antes de producir una interrupción (depende del hardware) 6 Manual POSIX Versión 1.0 1.2.2. I2C Codigo ciaaPOSIX_IOCTL_SET_CLOCKRATE Valores ciaaCLOCKRATE_100000 ciaaCLOCKRATE_400000 <valor dirección del esclavo> ciaaPOSIX_IOCTL_SET_SLAVEADD ciaaPOSIX_IOCTL_SET_REGISTERADD ciaaPOSIX_IOCTL_SET_REGISTERADDWIDTH <valor dirección de registro del dispositivo esclavo> ciaaREGISTERADDWIDTH_0bits ciaaREGISTERADDWIDTH_8bits ciaaREGISTERADDWIDTH_16bits ciaaREGISTERADDWIDTH_24bits ciaaREGISTERADDWIDTH_32bits Detalle Selecciona la tasa de transferencia de bits del I2C. Configura la dirección del esclavo Configura el número de registro interno del esclavo. Cantidad de bits de dirección de los registros del dispositivo esclavo Estas configuración están detalladas en el archivo “ciaaPOSIX_ioctl_i2c.h” 1.2.3. Entradas y salidas digitales Entradas y salidas digitales: No poseen parámetros de configuración 1.2.4. Ejemplo de aplicación Ejemplo de configuración de UART: static int32_t fd_uart1; fd_uart1 = ciaaPOSIX_open("/dev/serial/uart/1", ciaaPOSIX_O_RDWR); ciaaPOSIX_ioctl(fd_uart1, *)ciaaBAUDRATE_115200); ciaaPOSIX_ioctl(fd_uart1, *)ciaaFIFO_TRIGGER_LEVEL3); ciaaPOSIX_IOCTL_SET_BAUDRATE, (void ciaaPOSIX_IOCTL_SET_FIFO_TRIGGER_LEVEL, (void 7 Manual POSIX Versión 1.0 1.3. ciaaPOSIX_read y ciaaPOSIX_write Prototipo de las funciones: ssize_t ciaaPOSIX_read(int32_t fildes, void * buf, size_t nbyte); ssize_t ciaaPOSIX_write(int32_t fildes, void const * buf, size_t nbyte); Recibe como parámetro: fildes: el número de file descriptor devuelto por la función ciaaPOSIX_open. buf: el puntero a la dirección de memoria en donde se almacenarán los datos recibidos (read) o los datos para ser enviados (write). nbyte: el numero de bytes a recibir (read) o enviar (write) Retorna un -1 si falla y en caso de éxito el valor de bytes recibidos o enviados. 1.3.1. Ejemplo de aplicación Un ejemplo de utilización de las funciones read y write static int32_t fd_in; static int32_t fd_out; uint8_t inputs = 0; fd_in = ciaaPOSIX_open("/dev/dio/in/0", ciaaPOSIX_O_RDONLY); fd_out = ciaaPOSIX_open("/dev/dio/out/0", ciaaPOSIX_O_RDWR); ciaaPOSIX_read(fd_in, &inputs, 1); ciaaPOSIX_write(fd_out, &inputs, 1); En este ejemplo se escriben (ciaaPOSIX_write) en los pines de salida del puerto 0 (fd_out) los mismos valores de los pines de entrada del puerto 0 (fd_in). 1.4. ciaaPOSIX_close Prototipo de las funciones: int32_t ciaaPOSIX_close(int32_t fildes); Recibe como parámetro: fildes: el número de file descriptor devuelto por la función ciaaPOSIX_open. Retorna un -1 si falla y en caso de éxito 0. 8 Manual POSIX Versión 1.0 1.5. Ejemplos de aplicación 1.5.1. Ejemplo 1 - GPIO salidas Nombre del proyecto: ejemplo_01_GPIO_out Esta aplicación se controla el estado de los LEDs provistos en la placa EDU-CIAA-NXP y se introducen conceptos de arquitetura de firmware. El ejemplo comienza en la función main, inicializando el sistema operativo y el ciaa kernel. La primer tarea que ejecuta el sistema operativo es “InitTask” en donde se cargan los drivers de GPIOs y UARTs. Además se configura la UART USB con un baudrate de 115200 bit/s. Por último se activa el ciclo de ejecución de la tarea “PeriodicTask” al ejecutar la línea: SetRelAlarm(ActivatePeriodicTask, BASETIEMPO, BASETIEMPO); En donde se indica que la tarea PeriodicTask tendrá un ciclo de ejecución permanente, con su base de tiempo definida en un valor de 10ms (en el archivo main.h se puede encontrar que el valor de BASETIEMPO). La tarea periódica tiene la siguiente estructura: TASK(PeriodicTask) { if ( Es_la_primera_vez_que_se_ejecuta ) { // hacer algo la primera vez que se ejecuta la tarea } else { // hace algo en en cada ciclo } TerminateTask(); } Es necesario avisarle al SO que termino el ciclo de ejecución, para eso se utiliza la función: TerminateTask(); 9 Manual POSIX Versión 1.0 Muchas veces es necesario inicializar valores de variables o dispositivos externos, para eso se utiliza una pequeña sección del programa que se ejecutará por única vez al inicio del programa. Por ejemplo se puede utilizar el siguiente: static sistema_inicializado = NO; if (sistema_inicializado == NO ) { // rutina de inicialización if ( finalizo_la_rutina_de_inicilizacion ) { sistema_inicializado = SI; } } El atributo static de la variable HW_inicializado significa que se guardara el valor de tal variable aún cuando se salga y se vuelva a entrar en la función (se guarda el valor como si fuera una variable global, pero se accede solamente dentro de la función). En el ciclo de ejecución permanente de la tarea se realiza acciones cada cierto periodo, se utilizan contadores y una estructura como la siguiente para ejecutar parte del código en momentos determinados: static int contador = 0; contador++; if ( !( contador % (500/BASETIEMPO)) ) { //accion que se ejecutara cada 500 ms } De debe resetear el contador cuando el mismo alcance su valor final, en este caso cada 1 segundo. contador++; if ( contador >= (1000/BASETIEMPO) ){ contador = 0; } 10 Manual POSIX Versión 1.0 Para facilitar las operaciones con los leds, se utilizan máscaras. En las mascaras cada bit representa la posición del bit de estado de un LED. Por ejemplo la máscara del LED 1 de la ED-CIAA-NXP (ver cuadro) es igual a 0x08, siendo que el estado del led se indica en el bit número 3 del byte. Pines de entrada bits 7 6 5 4 tecla 3 2 1 0 4 3 2 1 Pines de salida bits 7 LEDs 6 5 4 3 2 1 0 3 2 1 B G R Operaciones con máscaras: Poner en 1 el bit 0 del byte estado mascara = 0x01 estado |= mascara Poner en 0 el bit 1 del byte estado mascara = 0x02 estado &= mascara Invertir estado de los bits 1 y 0 del byte estado mascara = 0x03 estado ^= mascara 11 Manual POSIX Versión 1.0 1.5.2. Ejemplo 2 - GPIO entradas Nombre del proyecto: ejemplo_02_GPIO_in El ejemplos es idéntico al programa anterior, con algunas modificaciones orientadas a ordenar el código. Principalmente la diferencia está en implementar 3 etapas para resolver un problema, sean: leer los datos de entrada, procesarlos y generar las salidas correspondientes. Ahora en la tarea periódica están las funciones: TASK(PeriodicTask) { LeerEntradas(); Procesar(); EscribirSalidas(); TerminateTask(); } De esta forma solo existe un lugar en el programa principal que tiene acceso a leer el hardware y otro lugar con acceso a escribir el hardware. Esto hace que sea más fácil testearlo. Se detallan a continuación las funciones presentadas: int LeerEntradas(void) { ciaaPOSIX_read(fd_teclado, &estado_teclas, 1); ciaaPOSIX_read(fd_leds, &estado_leds, 1); return 1; } Hace una lectura del estado de los LEDs y de las teclas int EscribirSalidas(void) { ciaaPOSIX_write(fd_leds, &estado_leds, 1); return 1; } Escribe el estado de LEDs deseado 12 Manual POSIX Versión 1.0 int Procesar(void) { static int contador = 0; contador++; if ( GPIO_GET(estado_teclas, TECLA_2) ) GPIO_SET(estado_leds, LED_1); else GPIO_CLEAR(estado_leds, LED_1); return 1; } La función que proceso el estado de las entradas digitales actúa sobre los LEDs. 13 Manual POSIX Versión 1.0 1.5.3. Ejemplo 3 - UART Nombre del proyecto: ejemplo_03_UART En este caso se propone enviar un mensaje predefinido en el programa, y de forma repetitiva interceptar los caracteres recibidos por el puerto serie USB y retransmitirlos por el mismo, haciendo un eco de los datos recibidos. Envió de mensaje predefinido: char mensaje[] = "Mensaje enviado por UART\n"; ciaaPOSIX_write(fd_uart_usd, mensaje, ciaaPOSIX_strlen(mensaje)); Retransmisión del mensaje recibido: int8_t buf[20]; int32_t ret; while(1) { ret = ciaaPOSIX_read(fd_uart_usd, buf, 20); if(ret > 0) { ciaaPOSIX_write(fd_uart_usd, buf, ret); } } En este ejemplo se presentaron 2 tipos de tarea, una periódica de mayor prioridad y una de ejecución continua de menor prioridad. Los detalles y características de las mismas se explicarán en profundidad en el manual de FreeOSEK. 14 Manual POSIX Versión 1.0