Establezca el reloj y los pines de datos GPIO STM32 lo más rápido posible

4

Tengo un STM32 que alterna nueve pines GPIO repetidamente (un pin de reloj y ocho pines de datos para cargar una imagen FPGA usando SelectMap). Lo estoy haciendo usando la función de biblioteca estándar GPIO_WriteBit que modifica un bit GPIO y cambiando un pin a la vez.

Lamentablemente, esto resulta ser bastante lento. ¿Hay alguna manera de hacer que los "cambios de GPIO sin procesar" sean muy rápidos? ¿Hay algún parámetro de reloj que deba cambiar? ¿Puedo usar algún tipo de FIFO o método basado en interrupciones?

He configurado los pines GPIO de la siguiente manera:

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

(Nota: El manual de referencia (ver página 133) indica que El GPIO es capaz de:

Fast toggle capable of changing every two clock cycles

Pero no puedo ver cómo activar esta conmutación rápida.)

    
pregunta Randomblue

5 respuestas

1

Respondí a tu pregunta relacionada por qué solo podías cambiar a 4 MHz cuando esperabas 100 MHz:

  

Si los 4 MHz son sobre 4 MHz, y no son exactamente 100 MHz / 25, entonces el problema probablemente se deba a la función C GPIO_WriteBit .

     

Para operaciones de alta velocidad y operaciones que requieren una sincronización precisa, es mejor codificar en ensamblaje que en C. Si observa el código de ensamblaje creado por GPIO_WriteBit , puede tener media página, según el tipo de funciones de la función. tiene, y cuánto puede hacer el optimizador del compilador con él.

     

No dice qué cadena de herramientas de desarrollo está utilizando, pero muchos / la mayoría de los compiladores de C pueden manejar el ensamblaje en línea.

Entonces, escribe las funciones en ensamblador. Una función como

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;

no debe tomar más de 2 instrucciones en ensamblaje, mientras que el código C compilado puede tomar 20 veces más. O más.

    
respondido por el stevenvh
4

La configuración o eliminación de cualquier combinación de bits en un puerto de E / S (incluso la configuración de algunos y la eliminación de otros) debe requerir como máximo tres instrucciones; cuando se repitan, estas instrucciones deben tomar una instrucción cada una. Desafortunadamente, parece que muchos proveedores (ST entre ellos) tienden a definir bibliotecas de E / S que generan llamadas de subrutina incluso para operaciones comunes como esa.

Sugeriría definir sus propios métodos para establecer y borrar bits de E / S, e incluir el código para esos métodos, marcado con un calificador __inline , en su archivo .h [en lugar de simplemente incluir un prototipo en el .h y el código del método en un archivo .c separado].

Si dentro de un archivo .h, escribe:

__inline void SetPorts(GPIO_TypeDef* Addr, uint16_t data) { *Addr = data; }
__inline void ClearPorts(GPIO_TypeDef* Addr, uint16_t data) { *Addr = data; }

Luego el código:

setPorts(GPIOA, 1);
clearPorts(GPIOA, 2);
clearPorts(GPIOA, 1);

probablemente generará algo como (no estoy seguro acerca de algunos detalles):

    ldr  r0,=GPIOA
    mov  r1,#1
    strh r1,[r0+20]
    mov  r2,#2
    strh r2,[r0+22]
    strh r1,[r0+22]

Por el contrario, si uno usa GPIO_WriteBit , el código terminará siendo más como:

    ldr  r0,=GPIOA
    mov  r1,#1
    mov  r2,#1
    bl   _GPIO_WriteBit
    ldr  r0,=GPIOA
    mov  r1,#2
    mov  r2,#0
    bl   _GPIO_WriteBit
    ldr  r0,=GPIOA
    mov  r1,#2
    mov  r2,#0
    bl   _GPIO_WriteBit

_GPIO_WriteBit: ; Code below assumes pretty good compiler optimization 
    cmp   r2,#0
    itteq
    streq r1,[r0+20]
    strne r1,[r0+22]
    bx    lr

El primer ejemplo ejecuta seis instrucciones en total para las tres operaciones. El segundo ejemplo ejecuta doce en el código principal una vez, y aproximadamente cuatro instrucciones en la función WriteBits tres veces cada una, para un total de 24 [las instrucciones omitidas a veces se agregan al tiempo de ejecución y otras veces no]. Normalmente, el propósito de llamar a subrutinas es cambiar el tamaño del código por la velocidad de ejecución, pero en este caso la llamada de subrutina es un desastre desde el punto de vista tanto del espacio como del tiempo. El único trabajo útil realizado por la instrucción es una sola operación de almacenamiento; el código probablemente dejará los registros r0-r2 sin alterar, pero el código de llamada no tendrá forma de saberlo. En consecuencia, los tres parámetros deben establecerse explícitamente antes de cada llamada de método.

No sé por qué los proveedores de chips definen métodos que escriben bits GPIO pero no se molestan en hacerlos en línea, pero sugeriría que en la mayoría de los casos se debe evitar el uso de funciones proporcionadas por el proveedor de chips para escribir puertos GPIO si uno se preocupa por la eficiencia.

Por cierto, como otra nota general, tiendo a ser escéptico de las funciones de E / S proporcionadas por el proveedor en general. Aunque a veces pueden presentar a un programador un nivel de abstracción más alto que el hardware en bruto, lo que es útil, en muchos casos hacen que el código sea más difícil de escribir, más difícil de leer y menos eficiente. En ocasiones, también pueden tener efectos secundarios no deseados que pueden causar problemas en otros lugares. Por ejemplo, si un periférico necesita que el reloj se configure en uno de dos modos, y generalmente funciona mejor con uno de ellos, una biblioteca suministrada por el proveedor para el periférico puede configurar el reloj en el modo "mejor", incluso si ese reloj es Compartido con otro periférico que lo necesite para estar en el otro. Cuando los periféricos comparten recursos (como lo hacen a menudo), puede ser imposible usar las bibliotecas correctamente sin leer y entender todo el código que contiene. Si todo el propósito de la biblioteca era guardar un par de escrituras de registro, las consecuencias de la llamada a la biblioteca pueden ser mucho más difíciles de entender que las consecuencias del código que reemplaza.

    
respondido por el supercat
2

La velocidad que establece solo controla la velocidad de giro del pin. Cuanto más rápido sea, más rápido será el borde ascendente. No representa directamente qué tan rápido puede alternar los puertos.

La función le permite interactuar adecuadamente con otros dispositivos que requieren tiempos de subida / caída específicos.

    
respondido por el RobC
1

Estableces la velocidad de los GPIO a 100 MHz; Este es el límite de velocidad que el hardware puede soportar. Pero la velocidad de intercambio de datos final también puede estar limitada por la velocidad con la que se puede ejecutar su código, ya que el reloj de datos, etc. está controlado por su código ahora.

En mi opinión, no todos los miembros de la familia STM32 admiten la velocidad GPIO de 100 MHz, y la velocidad de MCU de algunas familias es inferior a esa velocidad, como 72 MHz. Si su MCU es compatible con GPIO de 100 MHz, entonces optimice su código. De lo contrario, elija una MCU con una velocidad de reloj más alta; STM32F429 admite una velocidad de reloj de 180 MHz.

Y por favor revise la hoja de datos de MCU. En la sección "Definición de características de CA de E / S", se proporcionan las características eléctricas del GPIO y cómo se define "100 MHz". Y debe tener en cuenta: " Para frecuencias máximas superiores a 50 MHz, se debe utilizar la celda de compensación. "

    
respondido por el diverger
0

Asegúrese de que también haya configurado correctamente los registros de su reloj; descargue la herramienta de configuración del reloj STM32F.

También el cubo STM32F tiene un generador de reloj automático. Debe establecerlos correctamente o no obtendrá buenos resultados (o ninguno).

Si no está preocupado por la desviación del reloj, escale el reloj externo a 1 MHz y luego use los otros registros para obtener los resultados que desea.

    
respondido por el Steve

Lea otras preguntas en las etiquetas