Dentro del controlador tengo una función para copiar los datos de la estructura interna en una estructura de la aplicación.
¿Puede este proceso ser interrumpido por un disparador de interrupción del microcontrolador?
uint16_t getRawData(struct Data *Data_external)
{
if(Data_external == NULL)
{
return ERR_PARA;
}
else
{
*Data_external = Data_internal; // the copy process. Could this be interrupted?
}
return ERR_NONE;
}
Sí. Prácticamente todo en una MCU puede ser interrumpido por una solicitud de interrupción. Cuando el controlador de interrupciones se completa, el código anterior simplemente continuará, por lo que generalmente no es un problema.
En un caso especial, los manejadores de interrupciones pueden ser interrumpidos por interrupciones de mayor prioridad (interrupciones anidadas).
Si no se debe interrumpir una serie de instrucciones, debe implementar una sección crítica (básicamente, deshabilitar globalmente las interrupciones, hacer el trabajo, habilitar nuevamente).
Recuerde que, según la arquitectura de la CPU de destino, una sola línea de C se puede compilar en muchas instrucciones de ensamblaje. Un simple i++
en un AVR se compila en varias instrucciones i
, por ejemplo uint32_t
.
Cualquier operación que no sea atómica puede ser interferida por una interrupción. Este tipo de programación a menudo es muy diferente a la mayoría de la programación y puede resultar confuso para las personas que no han estudiado diseño de procesadores o arquitectura de computadoras.
Puede pensar para sí mismo "Esto nunca sucederá realmente, ¿cuánto tiempo lleva copiar este código y qué probabilidad hay de una interrupción?" Pero con la mayoría de las aplicaciones integradas de producción sucederá porque el producto está encendido durante años sin actualizaciones.
El otro problema con las copias de estructura como esta es que cuando ocurren, son extraordinariamente difíciles de depurar porque solo ocurren cuando la interrupción ocurre en el momento justo (que puede ser tan solo un ciclo).
El punto central de las interrupciones es que pueden ocurrir (y ocurren) todo el tiempo, y están diseñadas para tener un impacto cero en cualquier código que se esté ejecutando cuando ocurran. Todos los registros se guardan y, según la arquitectura de la CPU, se puede intercambiar un conjunto de registros completamente diferente, la interrupción hace lo suyo y luego se restauran los registros originales y el código continúa ejecutándose normalmente.
Pueden ocurrir problemas cuando la propia rutina de servicio de interrupción intenta acceder a la memoria a la que accede el código interrumpido en ejecución. Incluso pueden ocurrir errores más sutiles cuando se interrumpe un proceso de E/S de tiempo crítico. Estos problemas son comunes con arquitecturas más antiguas, más simples y menos seguras donde puede haber poca separación entre el código de modo "usuario" y "supervisor/núcleo".
Este tipo de problema puede ser difícil de identificar y, a menudo, difícil de reproducir, pero una vez identificados, a menudo son bastante triviales de solucionar utilizando programación defensiva, mutexes/semáforos o simplemente desactivando interrupciones en secciones críticas del código.
La clase general de problemas se ha estudiado ampliamente, y las CPU multinúcleo modernas e incluso los sistemas operativos multitarea no serían posibles si no se probaran y probaran varias soluciones.
Voy a seguir adelante y asumir que preguntaste esto por una muy buena razón.
*Data_external = Data_internal;
Se puede dividir (salvo algunos casos extremos que es poco probable que estén en juego aquí).
No conozco su CPU, pero aún no he visto una CPU que no pueda hacer el equivalente moral de:
cli(); /* mask all interrupts */
*Data_external = Data_internal;
sti(); /* restore interrupt mask */
Ahora no se puede dividir en ninguna CPU de un solo núcleo porque nada puede interrumpir mientras las interrupciones están desactivadas. Si esto es una buena idea o no depende de muchas cosas que simplemente no estoy calificado para evaluar.
Si tiene varios núcleos (y finalmente recordé que hay una CPU integrada de varios núcleos en el mercado), no haga esto. No tiene valor. Necesitaría desarrollar un bloqueo adecuado.
El código tal como lo presentaste de hecho puede ser interrumpido. Sin embargo, antes de comenzar a hacer secciones críticas por todas partes, debe verificar algunas cosas:
Dices que esta función está "dentro de un controlador". ¿Las interrupciones ya están deshabilitadas cuando se llama a esta función? ¿O se llama dentro de un controlador de interrupciones que evita que se activen otras interrupciones? En caso afirmativo, la operación no se puede interrumpir de hecho.
¿Se Data_internal
accede alguna vez dentro de un controlador de interrupción? Si no, no hay daño incluso si la operación puede interrumpirse.
[No hay suficiente representante para comentar]
Otro problema con ese tipo de copia de estructura es que es una copia superficial . Es posible que necesite una copia profunda en su lugar.
Una copia superficial podría, pero no probablemente, ser atómica, dependiendo de la arquitectura de la máquina. Es casi seguro que una copia profunda no es atómica en ninguna arquitectura.
Una implementación de calidad adecuada para el uso de sistemas integrados documentará cómo volatile
se realizarán las lecturas o escrituras calificadas de varios tipos con suficiente detalle para indicar si pueden ser "divididas" por interrupciones y cómo, y también si las lecturas y escrituras volátiles son o no. secuenciado con respecto a las lecturas y escrituras no calificadas. Si bien algunas implementaciones pueden comportarse como si todas las lecturas y escrituras estuvieran volatile
calificadas, generalmente se espera que las implementaciones sean libres para procesar secuencias de lecturas y escrituras no calificadas de la manera que sea más eficiente cuando no hay volatile
accesos intermedios.
En un microcontrolador típico de 32 bits, lee y escribe volatile
tipos enteros calificados de 32 bits o menos; una asignación consistirá en una lectura atómica seguida de una escritura atómica. Si desea asegurarse de que una estructura de 32 bits se copie atómicamente, colóquela en una unión con uint32_t
y lea o escriba ese miembro para leer o escribir la estructura como un todo. Las implementaciones de calidad que están configuradas para ser adecuadas para el uso de sistemas integrados permitirán que las uniones se empleen de esa manera sin tener en cuenta si el estándar exigiría que las implementaciones que no están destinadas para tal uso hagan lo mismo. Tenga en cuenta que gcc y clang no se comportarán de manera confiable como implementaciones de calidad adecuadas para el uso de sistemas integrados a menos que se deshabiliten varias optimizaciones.
PlasmaHH
broma
david tweed
broma
david tweed
broma
brigada
broma
broma
Rvdo
broma
Rvdo
broma