Software Hello World/es

From Qi-Hardware
Jump to: navigation, search

A continuación se realizará la documentación de una aplicación que despliega un mensaje por el puerto serial de la plataforma. Con esto pretendemos explicar como se configuran algunos periféricos del Nano como: El controlador de la SDRAM, el Puerto serie y El generador de reloj interno.

Para esto utilizamos la aplicación [1].

Esta aplicación permite descargar aplicaciones a la memoria interna del nano. Sus funciónes son:

  • Configurar e inicializar el PLL.
  • Inicializar la memoria SDRAM (32 MBytes) para permitir el almacenamiento y ejecución de aplicaciones mayores a 16KBytes (Tamaño de la memoria Interna)
  • Inicializar el puerto serie como medio de depuración.
  • Inicialización de los GPIOs.

Contents

[edit] Código Fuente

Los archivos que forman parte de esta aplicación son:

jz4740/
|-- Makefile        // Archivo que define las reglas para la herramienta make
|-- jz_serial.c    // Rutinas de inicialización del puerto serial para el procesador JZ4740
|-- start.S          // Punto de entrada de la aplicación,
|-- main.c          // Rutina principal que además contiene la inicialización de la SDRAM, PLL y GPIO
`-- ld.script       // Script de enlazado.

[edit] C runtime Startup code 'start.S'

Este archivo es el punto de entrada de la aplicación, es el primer fragmento de código que se ejecutará y está encargado de:

  1. #include <jz4740.h> 
  2. #include <board.h> 
  3.  
  4. 	.text 
  5. 	.set noreorder 
  6. 	.global startup 
  7. startup: 
  8. 	/* 
  9. 	 * Disable all interrupts 
  10. 	 */ 
  11. 	la	$8, 0xB0001004		/* INTC_IMR */ 
  12. 	li	$9, 0xffffffff 
  13. 	sw	$9, 0($8) 
  14.  
  15. 	/* 
  16. 	 * CU0=UM=EXL=IE=0, BEV=ERL=1, IP2~7=1 
  17. 	 */ 
  18. 	li	$26, 0x0040FC04 
  19. 	mtc0	$26, $12		/* CP0_STATUS */ 
  20.  
  21. 	/* IV=1, use the specical interrupt vector (0x200) */ 
  22. 	li	$26, 0x00800000 
  23. 	mtc0	$26, $13		/* CP0_CAUSE */ 
  24.  
  25. 	/* Setup stack pointer */ 
  26. 	la	$29, 0x81000000 
  27.  
  28. 	/* Jump to the main routine */ 
  29. 	j	main_func 
  30. 	nop 
  31. 	.set reorder
  • [Inicializar el stack (Línea 26)]
  • [Hacer un llamado al código en C. (Línea 29)]

[edit] main.c

En este archivo se realiza la configuración de los siguientes periféricos:

[edit] Pines de Entrada/Salida de Propósito General (GPIOs)

Ver más en: SACK_GPIOs

La función gpio_init_4740 inicializa los pines del procesador asociados a los controladores de la memoria NAND (__gpio_as_nand), la memoria SDRAM (__gpio_as_sdram_32bit) y a los puertos seriales (__gpio_as_uart0, __gpio_as_uart1).

  1. #include "jz4740.h"
  2. #include "board.h"
  3.  
  4. void gpio_init_4740(void)
  5. {
  6. 	__gpio_as_sdram_32bit();
  7. 	__gpio_as_uart0();
  8. 	__gpio_as_nand();
  9. }

A manera de ejemplo miremos la función que configura los pines del puerto serie definido en el archivo jz_4740/include/jz4740.h

  1. #define __gpio_as_uart0()			\
  2. do {						\
  3. 	REG_GPIO_PXFUNS(3) = 0x06000000;	\
  4. 	REG_GPIO_PXSELS(3) = 0x06000000;	\
  5. 	REG_GPIO_PXPES(3) = 0x06000000;	\
  6. } while (0)

Para permitir que los pines del procesador sean utilizados por los periféricos asociados a ellos es necesario configurarlos para tal fín, esto se logra haciendo que el bit del registro PXFUN correspondiente a cada pin tenga el valor adecuado, si PXFUN = 0 el pin puede ser usado como GPIO o como entrada de interrupción, si PXFUN = 1 el pin es configurado para realizar una función alterna si está asociado a un periférico. En este caso se está escribiendo un 1 (Línea 3) en los bits 25 y 26 del puerto 3 (GPD25 y GPD26) del registro PXFUN del puerto D, con lo que asociamos las señales TXD y RXD a los puertos GPD25 y GPD26 respectivamente.

Cuando PXFUN = 0, si PXSELX = 0 se usa como GPIO, si PXSELX = 1 se usa como fuente de interrupción. Cuando PXFUN = 1, si PXSELX = 0 se asigna la función alterna 0, si PXSELX = 1 se asigna la función alterna1. Para este ejemplo, se asigna el valor 1 a los bits 25 y 26 del registro PXSEL del puerto 3 con lo que se asigna la función alterna 1 a estos pines ( UART ) (Este pin también tiene asociado las salidas de 2 PWMs ).

Finalmente se escribe un 1 en los bits 25 y 26 del registro PXPE, con lo que se deshabilitan los pull up/down internos en los pines GPD25 y GPD26.

Acciones similares se realizan para los pines asociados a las memorias NAND, SDRAM y a la UART1.

[edit] PLL

La funcion pll_init se encarga principalmente de 2 aspectos:

1.Inicializar el registro CPPCR, el cual almacena los parametros suficientes para configurar la frecuencia de salida del PLL.

2.Inicializar el registro CPCCR, encargado de almacenar los factores de division que generará posteriormente las frecuencias del PCLK, MCLK, LDCLK, HCLK y CCLK a partir de la señal PLL o CFG_EXTAL

  1. static void pll_init(void) 
  2. { 
  3.     register unsigned int cfcr, plcr1; 
  4.     int n2FR[33] = { 
  5.         0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, 
  6.         7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 
  7.         9 
  8.     }; 
  9.     int div[5] = { 0, 3, 3, 3, 3 };     /* divisors of I:S:P:M:L */ 
  10.     int nf, pllout2; 
  11.  
  12.     cfcr = CPM_CPCCR_CLKOEN | 
  13.         (n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) | 
  14.         (n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) | 
  15.         (n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) | 
  16.         (n2FR[div[3]] << CPM_CPCCR_MDIV_BIT) | 
  17.         (n2FR[div[4]] << CPM_CPCCR_LDIV_BIT); 
  18.  
  19.     pllout2 = (cfcr & CPM_CPCCR_PCS) ? CFG_CPU_SPEED : (CFG_CPU_SPEED / 2); 
  20.  
  21.     /* Init USB Host clock, pllout2 must be n*48MHz */ 
  22.     REG_CPM_UHCCDR = pllout2 / 48000000 - 1; 
  23.  
  24.     nf = CFG_CPU_SPEED * 2 / CFG_EXTAL; 
  25.     plcr1 = ((nf + 2) << CPM_CPPCR_PLLM_BIT) |  /* FD */ 
  26.         (0 << CPM_CPPCR_PLLN_BIT) |     /* RD=0, NR=2 */ 
  27.         (0 << CPM_CPPCR_PLLOD_BIT) |    /* OD=0, NO=1 */ 
  28.         (0x20 << CPM_CPPCR_PLLST_BIT) | /* PLL stable time */ 
  29.         CPM_CPPCR_PLLEN;        /* enable PLL */ 
  30.  
  31.     /* init PLL */ 
  32.     REG_CPM_CPCCR = cfcr; 
  33.     REG_CPM_CPPCR = plcr1; 
  34.  
  35. }


De manera adicional, analizamos la funcion que configura el PLL de salida incluida en el archivo /home/camilo/jz_xloader/include

Analizando la estructura del registroCPPCR y basandonos a la figura (ver PLL [2]); deducimos claramente los pasos necesarios para la configuracion interna del PLL: Inicialmente, se activa la salida del PLL asignando 1 al bit 8 del registro CPPCR (la constante CPM_CPPCR_PLLEN indica simplemente su posicion). Cuando este bit se iguala 0, el PLL es desconectado, asignando asi, el reloj principal (12 MHz) como unica fuente de entrada disponible al bloque BP de conmutacion(ver figura PLL).

Una vez activada la señal del PLL, modificamos los parámetros necesarios, ubicados dentro del registro CPPCR, para configurar sus respectivos divisores; los cuales se especifican a continuacion:

   * El divisor de feedback, en el intervalo de bits 23-31  (PLLM).
   * El divisor de entrada, ubicando en el intervalo de bits 18-20 (PLLN). 
 
   * El divisor de salida OD, determinado en los bits 16 y 17 (PLLOD).

Para el código anterior, siendo CFG_CPU_SPEED=12MHz, se inicializa el registro CPPCR a partir de los siguiente valores donde PLLM=0,PLLN=0, PLLOD=0 y PLLEN=1.

Posteriormente, observamos la definicion del registro CPCCR donde se modifican los parámetros necesarios para configurar las señales que alimentarán PCLK, MCLK, LDCLK, HCLK y CCLK:

1. El bit de habilitación del reloj de la SDRAM , ubicado en el bit 30 (CPM_CPCCR_CLKOEN)

2. El factor de división para el CPU Cloc, ubicado en los primeros 4 bits del registro (CPM_CPCCR_CDIV_BIT). El cual dependiendo del valor almacenado, arrojará según la tabla el verdadero factor de división.

3. Los factores de división para PCLK, MCLK, LDCLK, HCLK los cuales para este caso partiendo de la constante PHM_DIV=3 definida en ./main.c se inicializan en 2. luego, teniendo en cuenta la tabla especificada en [3] , dicha constante indica que el reloj de entrada (CFG_CPU_SPEED) se dividirá en 3 arrojando una frecuencia de salida de 84 MHz.


Finalmente, se modifica la frecuencia de entrada (pllout2) que generan los relojes MSC, I2S, LCD, USB a partir del registro CPM_CPCCR_PCS. Si CPM_CPCCR_PCS=0, la salida del pllout2= CFG_CPU_SPEED ; mientras que para PM_CPCCR_PCS=0, pllout2= CFG_CPU_SPEED/2. Una vez obtenido la frecuencia fuente se inicializa el factor de división del UHC clock cuyo valor es almacenado en el registro REG_CPM_UHCCDR.


Adicionalmente, notamos que la inicializacion del reloj de la SDRAM (mem_clk), se define a través del registro div[5] el cual se encargara de definir las constantes para determinar la frecuencia de salida ver sdram_init

[edit] SDRAM

  1. static void sdram_init(void) 
  2. { 
  3.     register unsigned int dmcr0, dmcr, sdmode, tmp, cpu_clk, mem_clk, ns; 
  4.  
  5.     unsigned int cas_latency_sdmr[2] = { 
  6.         EMC_SDMR_CAS_2, 
  7.         EMC_SDMR_CAS_3, 
  8.     }; 
  9.  
  10.     unsigned int cas_latency_dmcr[2] = { 
  11.         1 << EMC_DMCR_TCL_BIT,  /* CAS latency is 2 */ 
  12.         2 << EMC_DMCR_TCL_BIT   /* CAS latency is 3 */ 
  13.     }; 
  14.  
  15.     int div[] = { 1, 2, 3, 4, 6, 8, 12, 16, 24, 32 }; 
  16.  
  17.     cpu_clk = CFG_CPU_SPEED; 
  18.     mem_clk = cpu_clk * div[__cpm_get_cdiv()] / div[__cpm_get_mdiv()]; 
  19.  
  20.     //REG_EMC_BCR = 0;      /* Disable bus release */ 
  21.     REG_EMC_RTCSR = 0;          /* Disable clock for counting */ 
  22.     REG_EMC_RTCOR = 0; 
  23.     REG_EMC_RTCNT = 0; 
  24.  
  25.     /* Fault DMCR value for mode register setting */ 
  26. #define SDRAM_ROW0    11 
  27. #define SDRAM_COL0     8 
  28. #define SDRAM_BANK40   0 
  29.  
  30.     dmcr0 = ((SDRAM_ROW0 - 11) << EMC_DMCR_RA_BIT) | 
  31.         ((SDRAM_COL0 - 8) << EMC_DMCR_CA_BIT) | 
  32.         (SDRAM_BANK40 << EMC_DMCR_BA_BIT) | 
  33.         (CFG_SDRAM_BW16 << EMC_DMCR_BW_BIT) | 
  34.         EMC_DMCR_EPIN | cas_latency_dmcr[((CFG_SDRAM_CASL == 3) ? 1 : 0)]; 
  35.  
  36.     /* Basic DMCR value */ 
  37.     dmcr = ((CFG_SDRAM_ROW - 11) << EMC_DMCR_RA_BIT) | 
  38.         ((CFG_SDRAM_COL - 8) << EMC_DMCR_CA_BIT) | 
  39.         (CFG_SDRAM_BANK4 << EMC_DMCR_BA_BIT) | 
  40.         (CFG_SDRAM_BW16 << EMC_DMCR_BW_BIT) | 
  41.         EMC_DMCR_EPIN | cas_latency_dmcr[((CFG_SDRAM_CASL == 3) ? 1 : 0)]; 
  42.  
  43.     /* SDRAM timimg */ 
  44.     ns = 1000000000 / mem_clk; 
  45.     tmp = CFG_SDRAM_TRAS / ns; 
  46.     if (tmp < 4) 
  47.         tmp = 4; 
  48.     if (tmp > 11) 
  49.         tmp = 11; 
  50.     dmcr |= ((tmp - 4) << EMC_DMCR_TRAS_BIT); 
  51.     tmp = CFG_SDRAM_RCD / ns; 
  52.     if (tmp > 3) 
  53.         tmp = 3; 
  54.     dmcr |= (tmp << EMC_DMCR_RCD_BIT); 
  55.     tmp = CFG_SDRAM_TPC / ns; 
  56.     if (tmp > 7) 
  57.         tmp = 7; 
  58.     dmcr |= (tmp << EMC_DMCR_TPC_BIT); 
  59.     tmp = CFG_SDRAM_TRWL / ns; 
  60.     if (tmp > 3) 
  61.         tmp = 3; 
  62.     dmcr |= (tmp << EMC_DMCR_TRWL_BIT); 
  63.     tmp = (CFG_SDRAM_TRAS + CFG_SDRAM_TPC) / ns; 
  64.     if (tmp > 14) 
  65.         tmp = 14; 
  66.     dmcr |= (((tmp + 1) >> 1) << EMC_DMCR_TRC_BIT); 
  67.  
  68.     /* SDRAM mode value */ 
  69.     sdmode = EMC_SDMR_BT_SEQ | 
  70.         EMC_SDMR_OM_NORMAL | 
  71.         EMC_SDMR_BL_4 | cas_latency_sdmr[((CFG_SDRAM_CASL == 3) ? 1 : 0)]; 
  72.  
  73.     /* Stage 1. Precharge all banks by writing SDMR with DMCR.MRSET=0 */ 
  74.     REG_EMC_DMCR = dmcr; 
  75.     REG8(EMC_SDMR0 | sdmode) = 0; 
  76.  
  77.     /* Wait for precharge, > 200us */ 
  78.     tmp = (cpu_clk / 1000000) * 1000; 
  79.     while (tmp--); 
  80.  
  81.     /* Stage 2. Enable auto-refresh */ 
  82.     REG_EMC_DMCR = dmcr | EMC_DMCR_RFSH; 
  83.  
  84.     tmp = CFG_SDRAM_TREF / ns; 
  85.     tmp = tmp / 64 + 1; 
  86.     if (tmp > 0xff) 
  87.         tmp = 0xff; 
  88.     REG_EMC_RTCOR = tmp; 
  89.     REG_EMC_RTCNT = 0; 
  90.     REG_EMC_RTCSR = EMC_RTCSR_CKS_64;   /* Divisor is 64, CKO/64 */ 
  91.  
  92.     /* Wait for number of auto-refresh cycles */ 
  93.     tmp = (cpu_clk / 1000000) * 1000; 
  94.     while (tmp--); 
  95.  
  96.     /* Stage 3. Mode Register Set */ 
  97.     REG_EMC_DMCR = dmcr0 | EMC_DMCR_RFSH | EMC_DMCR_MRSET; 
  98.     REG8(EMC_SDMR0 | sdmode) = 0; 
  99.  
  100.     /* Set back to basic DMCR value */ 
  101.     REG_EMC_DMCR = dmcr | EMC_DMCR_RFSH | EMC_DMCR_MRSET; 
  102.  
  103.     /* everything is ok now */ 
  104. }

La funcion sdram_init se encarga de realizar la configuración inicial de la memoria SDRAM en donde se realiza básicamente:

  • Se configura por defecto el Registro de Modo (Mode Register) (lineas 74 a 91)

El arreglo cas_latency_sdram guarda las dos opciones para el retardo (en ciclos de reloj) entre la ejecución de un comando de lectura y la aparición de la primera parte del dato disponible. Para un CAS LATENCY igual a 2 son dos ciclos de reloj y cuando es igual a 3, tres ciclos.

  • Se definen entonces los tiempos para cada operación (Burst) realizada por la RAM (lineas 94 - 110)
  • Se inicializa la información en los 4 bancos de memoria (líneas 119 - 120)
  • Se

[edit] Explicación de código del External Memory Controller en la SDRAM

  • De la la línea 55 a 58 en el código anterior, se hacen definiciones del SDRAM Mode Register donde se almacenan dos valores, uno de cas latency igual a 2 y el otro igual a 3, en el que se especifica el retardo de lectura de datos del registro.
  • De la linea 60 a 63 se hace un procedimiento similar al hecho con el sdrm pero esta vez se almacenan dos valores de cas latency para el registro DMCR SDRAM Control Register, y se guarda el valor de 2 ciclos o 3 ciclos de retardo asignando 1 o 2 al par de bits asignados para definir los ciclos de retardo.
  • La línea 70 pone en 0 el bit BCR del Bus Control Register lo que hace que el Bus este disponible inmediatamente después que una transacción se haya hecho.
  • La línea 71 hace uso del REfresh Timer Control/Status Register, en el cual es deshabilitado en clock encargado del refresh.
  • en las líneas 78 a 91 se guardan dos valores de DMCR, que es el SDRAM Control Register, y se modifican los valores de cada bit para manipular

el ancho del bus de datos, el ancho de las direcciones en columnas y filas, si se activa o no y como va trabajar el refresh mode, si esta activado el modo registro, especifica si se usan 2 o 4 bancos, modo de bajo consumo o no y todos los retardos.

[edit] Puerto Serie

  1. static void serial_setbrg(void) 
  2. { 
  3.     volatile u8 *uart_lcr = (volatile u8 *) (UART_BASE + OFF_LCR); 
  4.     volatile u8 *uart_dlhr = (volatile u8 *) (UART_BASE + OFF_DLHR); 
  5.     volatile u8 *uart_dllr = (volatile u8 *) (UART_BASE + OFF_DLLR); 
  6.     u32 baud_div, tmp; 
  7.  
  8.     baud_div = CFG_EXTAL / 16 / CONFIG_BAUDRATE; 
  9.     tmp = *uart_lcr; 
  10.     tmp |= UART_LCR_DLAB; 
  11.     *uart_lcr = tmp; 
  12.  
  13.     *uart_dlhr = (baud_div >> 8) & 0xff; 
  14.     *uart_dllr = baud_div & 0xff; 
  15.  
  16.     tmp &= ~UART_LCR_DLAB; 
  17.     *uart_lcr = tmp; 
  18. }

Para realizar transmisión/recepción de datos por el puerto serial, es necesario configurar en la UART los siguientes registros :Líneas(157,158)

  • uart_dlhr(Divisor Latch Low) y uart_dllr (Divisor Latch High), estos registros componen el divisor para el generador programable de velocidad, este puede tomar el reloj de entrada de 3.6864 MHz y dividirlo por 1 a (216–1) (línea 161).La velocidad máxima es 230.4kbps.

uart_dlhr / uart_dllr guarda el 8-bit alto/bajo del divisor respectivamente (lineas 166,167). Si ambos registros están en cero, el reloj 16X se detiene.

     La relación de velocidad y valor de división  esta dado por la        
     siguiente fórmula:  Baud Rate = 3.6864 Mhz / (16 * Divisor).
  • El registro uart_lcr(Line Control Register) define el formato para la transmisión de datos del UART. EL bit DLAB (Divisor Latch Access Bit)permite habilitar a ciertos registros si :


     DLAB=0 permite habilitar a  uart_RBR, uart_THR o uart_ier
     DLAB=1 permite habilitar a  uart_dllr or uart_dlhr

[edit] jz_serial.c

En este archivo podremos encontrar las diferentes funciones para el manejo que necesita el puerto serial:

1. serial_putc // ... 2. serial_puts // ... 3. serial_getc // ... 4. serial_tstc // ... 5. serial_init // ... 6. serial_put_hex // ...

  1. void serial_putc (const char c)
  2. {
  3. 	volatile u8 *uart_lsr = (volatile u8 *)(UART_BASE + OFF_LSR);
  4. 	volatile u8 *uart_tdr = (volatile u8 *)(UART_BASE + OFF_TDR);
  5.  
  6. 	if (c == '\n') serial_putc ('\r');
  7.  
  8. 	/* Wait for fifo to shift out some bytes */
  9. 	while ( !((*uart_lsr & (UART_LSR_TDRQ | UART_LSR_TEMT)) == 0x60) );
  10.  
  11. 	*uart_tdr = (u8)c;
  12. }
  • uart_lsr (Line Status Register) este registro indica el estado de la información durante la transmisión de datos.
  • UART_TDRQ (Transmit Data Request)


  1. void serial_puts (const char *s)
  2. {
  3. 	while (*s) {
  4. 		serial_putc (*s++);
  5. 	}
  6. }


  1. int serial_getc (void)
  2. {
  3. 	volatile u8 *uart_rdr = (volatile u8 *)(UART_BASE + OFF_RDR);
  4.  
  5. 	while (!serial_tstc());
  6.  
  7. 	return *uart_rdr;
  8. }
  • RDR (Receive Data Ready Interrupt) recibir interrupción de los datos ( existen cinco tipos de interrupciones : recibir datos listos , tiempo de espera, estado de línea, transmisión de datos requeridos y el estado del módem)
  1. void serial_init(void)
  2. {
  3. 	volatile u8 *uart_fcr = (volatile u8 *)(UART_BASE + OFF_FCR);
  4. 	volatile u8 *uart_lcr = (volatile u8 *)(UART_BASE + OFF_LCR);
  5. 	volatile u8 *uart_ier = (volatile u8 *)(UART_BASE + OFF_IER);
  6. 	volatile u8 *uart_sircr = (volatile u8 *)(UART_BASE + OFF_SIRCR);
  7.  
  8. 	/* Disable port interrupts while changing hardware */
  9. 	*uart_ier = 0;
  10.  
  11. 	/* Disable UART unit function */
  12. 	*uart_fcr = ~UART_FCR_UUE;
  13.  
  14. 	/* Set both receiver and transmitter in UART mode (not SIR) */
  15. 	*uart_sircr = ~(SIRCR_RSIRE | SIRCR_TSIRE);
  16.  
  17. 	/* Set databits, stopbits and parity. (8-bit data, 1 stopbit, no parity) */
  18. 	*uart_lcr = UART_LCR_WLEN_8 | UART_LCR_STOP_1;
  19.  
  20. 	/* Set baud rate */
  21. 	serial_setbrg();
  22.  
  23. 	/* Enable UART unit, enable and clear FIFO */
  24. 	*uart_fcr = UART_FCR_UUE | UART_FCR_FE | UART_FCR_TFLS | UART_FCR_RFLS;
  25. }


  1. void serial_put_hex(unsigned int  d)
  2. {
  3. 	unsigned char c[12];
  4. 	char i;
  5. 	for(i = 0; i < 8;i++)
  6. 	{
  7. 		c[i] = (d >> ((7 - i) * 4)) & 0xf;
  8. 		if(c[i] < 10)
  9. 			c[i] += 0x30;
  10. 		else
  11. 			c[i] += (0x41 - 10);
  12. 	}
  13. 	c[8] = '\n';
  14. 	c[9] = 0;
  15. 	serial_puts(c);
  16.  
  17. }

Después de la configuración del UART está listo para la transferencia de datos (lineas 6-14). Para la transmisión de datos, es necesario realizar:

1. Lea ULSR.TDRQ o espere a que este habilitada la interrupción, si TDRQ = 1 o se generó una interrupción, entonces significa que hay espacio suficiente en UTHR para los nuevos datos.

2. Si ULSR.TDRQ = 1 o la interrupción esta habilitada se escriben los datos en UTHR para iniciar la transmisión.

3. Repita el punto 1 y 2 si hay más datos que esperan transmitirse.

4. Después de que todos los datos necesarios se escriban en UTHR, se espera que ULSR.TEMP = 1, lo cual indica que todos los datos han sido transmitidos totalmente

5. Si es necesario enviar un break, para lo cual se debe fijar ULCR.SBK y esperar un tiempo de 1-bit, luego se borra ULCR.SBK

6. Limpie UME (UART Module Enable) para acabar la transmisión del UART.

Del mismo modo para recepción de datos es necesario realizar: 1. Lea UART_LSR_DR o espere a que este habilitada la interrupción, si DR = 1 o se generó una interrupción, entonces significa que en URBR hay datos.

2. Si UART_LSR_DR = 1 o la interrupción esta habilitada, se lee UART_LSR_FIFOE, si es 1 entonces indica que ha ocurrido un error si no se continua con el punto 3 .

3. Lea los datos recibidos o el valor igual al del URBR (modo FIFO) .

4. Para comprobar que todos los datos fueron recibidos, compruebe que DRY =0,en modo FIFO y cuando la interrupción este permitida se puede generar una interrupción de tiempo de espera luego lea URBR hasta que DRY = 0 .

5. Limpie UME (UART Module Enable)para poner fin a la recepción de datos, cuando todos los datos son recibidos COLOQUE Uart_LSR.DRY = 0.


Se inicializan los registros uart_fcr, uart_lcr ,uart_ier ,uart_sircr (Líneas 42-57)

Interrupt Enable Register (UIER) FIFO Control Register (UFCR) Line control register (LCR)

configuración UART Transmisión y recepción de datos,Interrupciones

[edit] target.ls

  1. OUTPUT_ARCH(mips) 
  2. ENTRY(startup) 
  3. MEMORY 
  4. { 
  5. 	ram	: ORIGIN = 0x80000000 , LENGTH = 0x100000 
  6. } 
  7.  
  8. SECTIONS 
  9. { 
  10. 	. = ALIGN(4); 
  11. 	.text : { *(.text*) } > ram 
  12.  
  13. 	. = ALIGN(4); 
  14. 	.rodata : { *(.rodata*) } > ram 
  15.  
  16. 	. = ALIGN(4); 
  17. 	.sdata : { *(.sdata*) } > ram 
  18.  
  19. 	. = ALIGN(4); 
  20. 	.data : { *(.data*) *(.scommon*) *(.reginfo*) } > ram 
  21.  
  22. 	_gp = ALIGN(16); 
  23. 	.got : { *(.got*) } > ram 
  24.  
  25. 	. = ALIGN(4); 
  26. 	.sbss : { *(.sbss*) } > ram 
  27. 	.bss : { *(.bss*) } > ram 
  28. 	. = ALIGN (4); 
  29. }
  • Este código nos permite observar cómo está compuesto el mapeo de memoria y la arquitectura de la plataforma.
  • En la primera línea se define la característica de salida de la arquitectura del tipo de procesador a utilizar, en la cual se hace referencia al procesador MIPS-
  • En la segunda línea de código la instrucción Entry se encarga de definir la primera ejecución a realizar en este caso startup
  • De la línea 3 a la 6 se dimensiona el tamaño de la memoria RAM con un origen y una longitud determinada en este caso 0x80000000 , 0x100000 respectivamente. También se puede dimensionar otros tipos de memorias con la FLASH y ROM.
  • En las siguientes líneas ya se definen las secciones y el lugar donde serán almacenadas; En este código se puede ver las secciones .text (código ejecutable), .rodata (datos de solo lectura), .sdata, .data (variables inicializadas), .got, .sbss, .bss (variables no inicializadas) las cuales son almacenada en la memoria RAM conforme se muestra en el código, esto se hace para obtener una mayor rapidez en la ejecución del programa.
  • Nota: es de tener en cuenta que estás asignaciones no siempre se hacen en la memoria RAM sino que también se puede realizar para otro tipo de memorias previamente definidas,en su gran mayoría se almacena esta en la memoria RAM cuando el procesador no dispone de memoria no volátil.


[edit] Compilación y Transferencia al Nano

La siguiente Figura muestra los pasos que deben seguirse para poder ejecutar esta aplicación en la plataforma Nano.

SW design flow.png

[edit] Compilación

Para generar el archivo binario que será descargado a la plataforma se debe tener instalado el la cadena de herramientas GNU, y opcionalmente definir la variable crossmake a:

$ alias crossmake='make ARCH=mips CROSS_COMPILE=mipsel-openwrt-linux-'

Personal tools
Namespaces
Variants
Actions
Navigation
interactive
Toolbox
Print/export