¿Es posible interrumpir el proceso de copia de una estructura por una interrupción en C incrustada?

7

Dentro del controlador, tengo una función para copiar los datos de la estructura interna en una estructura desde 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;
}
    
pregunta Stani

7 respuestas

24

Sí. Casi todo en una MCU puede ser interrumpido por una solicitud de interrupción. Cuando el controlador de interrupciones complete, 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 prioridades más altas (interrupciones anidadas).

Si una serie de instrucciones no deben interrumpirse, entonces necesita implementar una sección crítica (básicamente, deshabilitar globalmente las interrupciones, hacer el trabajo, habilitar nuevamente).

Recuerde que, dependiendo de la arquitectura de la CPU de destino, se puede compilar una sola línea de C con muchas instrucciones de ensamblaje. Un simple i++ en un AVR se compila a varias instrucciones si i es, por ejemplo, uint32_t .

    
respondido por el filo
11

Cualquier operación que no sea atomic se puede interferir con una interrupción. Este tipo de programación a menudo es muy diferente a la mayoría de la programación y puede ser confuso para las personas que no han estudiado el diseño del procesador o la arquitectura de la computadora.

Puedes pensar "Esto nunca sucederá realmente, ¿cuánto tiempo tarda en copiarse este código y qué tan probable es una interrupción?" Pero con la mayoría de las aplicaciones integradas de producción, esto sucederá porque el producto lleva años sin actualizaciones.

El otro problema con las copias de estructura como esta es cuando ocurren, son extremadamente difíciles de depurar porque solo ocurren cuando la interrupción ocurre en el momento justo (lo que puede ser tan solo un ciclo).

    
respondido por el Sam
10

El punto central de las interrupciones es que pueden ocurrir (y suceden) todo el tiempo, y están diseñadas para tener un impacto cero en cualquier código que se ejecute cuando ocurren. Todos los registros se guardan y, dependiendo de la arquitectura de la CPU, se puede intercambiar un conjunto de registros completamente diferente, la interrupción hace su trabajo, y luego los registros originales se restauran y el código continúa funcionando normalmente.

Pueden ocurrir problemas cuando la rutina del servicio de interrupción intenta acceder a la memoria a la que se accede mediante el código interrumpido en ejecución. Incluso pueden ocurrir errores más sutiles cuando se interrumpe un proceso de E / S crítico para el tiempo. Estos problemas son comunes con las arquitecturas más antiguas, más sencillas y menos seguras, donde puede haber poca separación entre el código de modo "usuario" y "supervisor / kernel".

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 mediante programación defensiva, mutex / semáforos o simplemente deshabilitando las interrupciones en secciones críticas de código.

La clase general de problemas se ha estudiado ampliamente, y las CPU modernas de múltiples núcleos e incluso los sistemas operativos multitarea no serían posibles si no se hubieran probado y probado múltiples soluciones.

    
respondido por el Echelon
4

Voy a seguir adelante y asumiré que me lo has pedido por una buena razón.

*Data_external = Data_internal;

Se puede dividir (salvo algunos casos de borde que probablemente no estén en juego aquí).

No conozco tu 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 o no una buena idea depende de muchas cosas que simplemente no estoy calificado para evaluar.

Si eres multi-core (y finalmente recordé que hay una CPU integrada multi-core en el mercado) no hagas esto. No vale nada. Necesitarías desarrollar un bloqueo adecuado.

    
respondido por el Joshua
3

El código tal como lo presentaste puede ser interrumpido. Sin embargo, antes de comenzar a crear secciones críticas en todo el lugar, debes verificar algunas cosas:

  • Usted dice 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 ha accedido a Data_internal dentro de un controlador de interrupciones? Si no, no hay daño, incluso si la operación puede ser interrumpida.

respondido por el Dmitry Grigoryev
1

[No hay suficiente representante para comentar]

Otro gotchya con ese tipo de estructura es que es una copia superficial . Es posible que necesite una copia profunda en su lugar.

Una copia superficial podría ser atómica, pero probablemente no, dependiendo de la arquitectura de la máquina. Una copia profunda es casi seguro que no es atómica en ninguna arquitectura.

    
respondido por el studog
0

Una implementación de calidad adecuada para el uso de sistemas embebidos documentará cómo se realizarán las lecturas calificadas de volatile de varios tipos con suficiente detalle para indicar si se pueden "dividir" por interrupciones, y también si las lecturas y escrituras no volátiles se secuencian con respecto a las lecturas y escrituras no calificadas. Si bien algunas implementaciones pueden comportarse como si todas las lecturas y escrituras estuvieran calificadas como volatile , 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 haya volatile interviniente. accesos.

En un microcontrolador de 32 bits típico, lee y escribe tipos de enteros calificados volatile que son de 32 bits y más pequeños; 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 copia atómicamente, colóquela en una unión con un 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 embebidos permitirán que las uniones se empleen de tal manera sin tener en cuenta si la Norma requeriría implementaciones que no estén destinadas a tal uso para hacer 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.

    
respondido por el supercat

Lea otras preguntas en las etiquetas