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.