Los enchufes son inevitables al desarrollar cualquier software. En una incrustación, sus generosos cinco centavos también pueden provocar problemas de hardware, pero esta es una canción separada. Pero emboscadas puramente programadas, cuando te quedas atrapado en un lugar aparentemente vacío ... Para mí hay tres tipos de ellas.
La forma más fácil es cuando el manual, el estándar o, por ejemplo, el procedimiento para configurar la biblioteca para el hierro no se entiende completamente. Aquí está claro: no todos los movimientos se han agotado, la paciencia y el trabajo, otros cinco o dos experimentos, y cobrarán vida. Osciloscopio y tyk científico para ayudar.
Elegir un divisor de frecuencia para configurar el bus CANPeor aún, cuando el problema es un error tipográfico o un error en la lógica, que no puede ver en blanco hasta que pase por este lugar veinte veces con los ojos y la depuración paso a paso. Luego amanece, un golpe sonoro en la frente, un grito: "¡Bueno, eres una especie de babai!", Edición. Funciona
Y una tercera visión sombría: un problema técnico arraigado en una biblioteca extranjera y que se arrastra en el cruce con hierro. Las pasiones de Shakespeare dan lugar a la luz constante de un monitor. “¡Por qué no puede, el sistema no puede comportarse de esta manera, porque nunca puede! Bueno, de verdad! ¡Ah! No Recibe, firma.
Como resultado, la realidad es más amplia, más amplia y más amplia de lo esperado. Un par de ejemplos:
Historia No. 1. Unidad flash MicroSD y trabajo DMA
Anamnesis
Necesita volcar los datos en un archivo en la tarjeta SD. Por supuesto, no tengo tiempo ni deseo de escribir el sistema de archivos y el controlador SDIO, así que tomo la biblioteca terminada. Lo configuré para hierro, y todo funciona bien. Al principio Y luego resulta que los datos se graban salvajemente: los volúmenes son precisos, pero en los archivos mismos, se duplican pares separados de bytes triples, luego desaparecen, sin ninguna regularidad. No esta bien!
Los experimentos comienzan. Estoy escribiendo datos de prueba, todo está bien. Estoy escribiendo combate, una especie de demonio. Cambio el tamaño de los búferes de datos, la frecuencia de su descarga, las plantillas de datos son inútiles. En las memorias intermedias, todo es siempre excelente, los datos en la memoria están en todas partes lo que necesita. Y, sin embargo, fallas en una unidad flash, aquí están.
Le llevó un par de días cavar al perro.
El diagnostico
El problema estaba en la interacción de la biblioteca con el equipo
DMA .
Las tarjetas SD tienen una peculiaridad: están escritas solo en bloques de 512 bytes. Para hacer esto, la biblioteca almacena los datos en una matriz de 512 bytes, y al llenarlos se descarga desde allí a través de DMA para flashear. Pero!
Si transfiero al registro un fragmento mayor que <512xN + espacio vacío en el búfer de la biblioteca> bytes, entonces la biblioteca (obviamente, para no empujar la memoria de un lado a otro) hace esto: repone su búfer, lo escribe para flashear , y los siguientes 512xN bytes se lanzan directamente a mi DMA desde mi búfer. Bueno, si algo queda sin terminar, nuevamente se copia a sí mismo, hasta la próxima vez.
Y todo estaría bien, pero el controlador DMA requiere que los datos se coloquen en la memoria alineada en un límite de 4 bytes. El búfer de la biblioteca siempre está tan alineado que el lenguaje lo garantiza. Pero con qué dirección, después de copiar una parte de los datos, los restantes 512xN con un pequeño byte comienzan conmigo: Dios sabe. Y la biblioteca no verifica esto en absoluto: la dirección, tal como está, se pasa al controlador DMA.
"Enviaron algo torpe ... Un perro con él". El controlador restablece silenciosamente los 2 bits inferiores de la dirección transmitida. Y comienza la transferencia.

La dirección, inicialmente no un múltiplo de 4, se reemplaza por un múltiplo - voila, hasta los últimos tres bytes del búfer de la biblioteca se reescriben en el archivo desde el mío, y el mismo número de bytes de mi búfer se pierde sin dejar rastro. Como resultado, la cantidad total de datos es correcta, las operaciones se realizan sin problemas, pero el disco no tiene sentido.
Tratamiento
Tuve que agregar otro búfer inmediatamente antes de llamar a la función de grabación de hardware. Si la dirección de escritura no es un múltiplo de 4, primero se copian los datos. Al mismo tiempo, la velocidad promedio aumentó debido a una elección razonable del tamaño del búfer. Por supuesto, tomó memoria, pero lo que son 4 kilobytes por una buena causa, cuando tienes a tu disposición: ¡192 ilimitados!
Historia No. 2. Rantime y un montón
Prologo
Después del siguiente cambio, el programa comenzó a caer, y de alguna manera cayó muy duro, arrojando el procesador al controlador de
Falla Difícil . Y lo arrojó allí justo después del inicio, incluso antes de que la ejecución llegara a main (), es decir, ni una sola línea de mi código tuvo tiempo de ejecutarse.
La primera impresión es "el castor está muerto, el chip es para reemplazarlo". Y luego el programador le dio el roble. Pero no, la versión anterior del firmware funciona de manera estable, pero la nueva cae constantemente en algunas oscuras profundidades de ensamblaje entre el lanzamiento y mi código. No tenía suposiciones de qué tipo de herejía se trataba.
Capitulo 1
Ayudé a Internet a ver cómo obtener al menos información adicional. Se buscó en Google el procedimiento para analizar las consecuencias de un incumplimiento por defecto: estado de los registros, volcado de la pila. Dopilil Lo usé
Resultó que se bloquea debido a un error de operación en el bus. Decidí que este era nuevamente el acceso desequilibrado, un problema del mismo tipo que en la primera historia, pero desde una perspectiva diferente. Pero lo más opuesto es dónde ocurrió el error. Y surgió dentro de la biblioteca de tiempo de ejecución, es decir, en el código, que, en teoría, se lamió como los moretones del gato en un día soleado.
La continuación del análisis mostró que la falla es una consecuencia de un intento de inicializar variables estáticas locales.
Digresión líricaPor cierto, considerando el código desensamblado, simultáneamente encontré la respuesta a una pregunta que a veces me hacía, pero fui demasiado flojo para googlear de inmediato: ¿cómo se resuelve la situación cuando 2 o más hilos pueden intentar inicializar dicha variable al mismo tiempo? Resultó que en este caso, el compilador organiza la inicialización con semáforos, garantizando que solo un hilo a la vez pasará por todo el procedimiento, y el resto esperará hasta que termine el primero.
Este comportamiento se ha estandarizado desde C ++ 11. Sabias Yo no
Capítulo 2
Una vez que el tiempo de ejecución se dedica a la construcción de variables, también le corresponde llamar a los destructores al finalizar el programa (incluso si el programa nunca completa realmente el trabajo, que es la norma absoluta para los microcontroladores). Para hacer esto, necesita un lugar para almacenar información sobre todas las variables que logró inicializar.
Eso es justo en el lugar donde dicha información se almacena en algún tipo de lista interna, el tiempo de ejecución también cayó. Debido a que la función malloc (), a través de la cual se asignó memoria para los elementos de esta lista y que, según el estándar, produce bloques garantizados para estar alineados
al menos en el límite de 8 bytes , después de un enésimo número de llamadas exitosas, produce una pieza que no está alineada en este límite.

Los cambios en el nuevo código de firmware rompieron malloc?! Pero, ¿cómo es esto posible? No redefiní exactamente malloc; ¡yo mismo no lo necesito en ningún otro lado!
Útil en las opciones del compilador, para buscar algunas palabras clave, ayuda, pero se dijo claramente en todas partes:
malloc () garantiza la salida de memoria alineada a lo largo del límite fundamental. O puntero nulo en caso de que no haya suficiente memoria .
Capitulo 3
Durante mucho tiempo me quedé sin sentido en el código, establecí los puntos de interrupción, sufrí y no entendí nada, hasta que en algún momento no me tocó y miré las direcciones devueltas por Malloc con cuidado. Antes de esto, todo el análisis era para ver si el último dígito de la dirección es 0x4. Y ahora comenzó a comparar completamente entre sí las direcciones emitidas por llamadas sucesivas a Malloc.
Y oh, un milagro!
Todas las llamadas exitosas emitieron direcciones desde el espacio RAM (0x20000000 y anteriores para esta piedra), aumentando secuencialmente de llamada a llamada. Y el primero que no tuvo éxito devolvió 0x00000036. Es decir, la dirección no es solo que no estaba alineada, ¡sino que tampoco estaba en el espacio de direcciones de la RAM! El procesador intentó escribir algo allí y, naturalmente, cayó.
Y, sorprendentemente, incluso si malloc () actuó de acuerdo con el estándar y devolvió 0 si no hubiera suficiente espacio, esto no habría cambiado nada en el sentido de un bloqueo del programa (a menos que la causa del error se hubiera aclarado antes). El valor devuelto por malloc todavía no se verifica de ninguna manera, pero inmediatamente entra en acción. Esto es en tiempo de ejecución.
Epílogo
Aumentó el tamaño de almacenamiento dinámico en el archivo de configuración, y todo se solucionó.
Pero antes de ese momento ni siquiera pensaba en su volumen. Si el infierno se rindió ante mí, pensé. De todos modos, tengo todas las variables y objetos estáticos o en la pila. Entonces, solo por inercia, dejé 0x300 bytes debajo, ya que se asigna un volumen bajo el montón en todos los proyectos de plantilla. Pero no, el tiempo de ejecución de C ++ todavía necesita memoria asignada dinámicamente, y en cantidades bastante notables, según los estándares de los controladores.
Vive y aprende.