¿Cómo hacer la transición de microcontroladores "básicos" a ARM Cortex?

Tengo muchos años de experiencia con núcleos de 8 bits de varios fabricantes, a saber, 8051, PIC y AVR, y ahora tengo un Cortex M0 para averiguarlo. Específicamente este , pero espero que podamos ser más generales que eso.

Está resultando ser un poco más de lo que esperaba, con múltiples documentos que describen diferentes partes del sistema en diferentes niveles de detalle y ninguno que haya visto realmente para conectarlo todo. Esto se compara con tener una hoja de datos que explica todo. Entiendo que tengo mucho más material para documentar en primer lugar, pero el cambio de formato me está dando vueltas.

El sitio web anterior tiene un documento que es una buena descripción general de cada subsistema y periférico de forma aislada, y otro que describe cada registro en detalle, y tengo todo el código fuente de su SDK, incluidos los archivos de encabezado y algunos ejemplos complejos, pero aún veo nada que describa cómo se conecta todo.

¿Hay un tutorial conciso de la arquitectura Cortex que explique la función de las cosas que los controladores más pequeños simplemente no tienen, como varias capas de buses desde la CPU hasta los periféricos, cada uno con su propio temporizador de vigilancia, y cómo se conectan entre sí?

No puedo decir por lo que describió si obtuvo la hoja de datos / manual de usuario real para el procesador del fabricante. Eso debería darle una buena imagen general y detalles. De una de las imágenes en el sitio que vinculó, parece un procesador NXP. Mire el número de pieza del fabricante y busque la documentación del procesador en su sitio. También está el sitio de ARM arm.com/products/processors/cortex-m/cortex-m0.php .
Perdón por la demora en volver a esto; He estado ocupado con otros proyectos. Gracias @Adam por la voz de la experiencia.
Y gracias a @Richard por la descripción general de cómo pensarlo y la nota sobre la impresión de la configuración de pines. Los voté a ambos.
Nunca volverá, excepto si encuentra algunos requisitos de precio extremos . E incluso entonces te sentirás tentado, ya que hay bastantes Cortex-M muy baratos.

Respuestas (6)

He trabajado en AVR y MCU basados ​​en ARM Cortex-M3/M4/R4. Creo que puedo ofrecer algunos consejos generales. Esto supondrá que está programando en C, no en ensamblaje.

La CPU es en realidad la parte fácil. Los tipos de datos básicos de C tendrán diferentes tamaños, pero de todos modos está usando uint8/16/32_t, ¿verdad? :-) Y ahora todos los tipos de enteros deberían ser razonablemente rápidos, siendo los de 32 bits (int) los más rápidos. Probablemente no tenga una FPU, así que continúe evitando flotantes y dobles.

Primero, trabaje en su comprensión de la arquitectura a nivel del sistema. Esto significa E/S, sincronización, memoria, reinicios e interrupciones. Además, debe acostumbrarse a la idea de los periféricos mapeados en memoria. En AVR puede evitar pensar en eso porque los registros tienen nombres únicos con variables globales únicas definidas para ellos. En sistemas más complejos, es común referirse a los registros por una dirección base y un desplazamiento. Todo se reduce a la aritmética de punteros. Si no se siente cómodo con los punteros, comience a aprender ahora.

Para los IO, descubra cómo se maneja el muxing periférico. ¿Hay un control mux central para seleccionar qué pines son señales periféricas y cuáles son GPIO? ¿O configura los pines en modo periférico usando los registros periféricos? Y, por supuesto, necesitará saber cómo configurar GPIO como entradas y salidas, y habilitar el modo de drenaje abierto y pull-ups/downs. Las interrupciones externas suelen caer también en esta categoría. Los GPIO son bastante genéricos, por lo que su experiencia debería serle útil aquí.

El reloj se reduce a algunas cosas. Comienza con una fuente de reloj, típicamente un cristal o un oscilador RC interno. Esto se utiliza para crear uno o más dominios de reloj a nivel de sistema. Los chips de mayor velocidad usarán un PLL, que puede considerar como un multiplicador de frecuencia. También habrá divisores de reloj en varios puntos. Las cosas clave a considerar son cuál debería ser la frecuencia de reloj de su CPU y qué tasas de bits necesita para sus periféricos de comunicación. Por lo general, esto es bastante flexible. Cuando esté más avanzado, puede aprender cosas como los modos de bajo consumo, que generalmente se basan en la sincronización del reloj.

Memoria significa flash y RAM. Si tiene suficiente RAM, a menudo es más rápido mantener su programa allí durante el desarrollo inicial para que no tenga que programar el flash una y otra vez. El gran problema aquí es la gestión de la memoria. Su proveedor debe proporcionar ejemplos de secuencias de comandos de vinculación, pero es posible que deba asignar más memoria al código, las constantes, las variables globales o la pila, según la naturaleza de su programa. Los temas más avanzados incluyen seguridad de código y programación flash en tiempo de ejecución.

Los reinicios son bastante sencillos. Por lo general, solo tiene que buscar el temporizador de vigilancia, que puede estar habilitado de forma predeterminada. Los reinicios son más importantes durante la depuración cuando ejecuta el mismo código una y otra vez. Es fácil pasar por alto un error debido a problemas de secuenciación de esa manera.

Hay dos cosas que debe saber acerca de las interrupciones: cómo habilitarlas y deshabilitarlas, y cómo configurar los vectores de interrupción. AVR-GCC hace esto último por usted con las macros ISR(), pero en otras arquitecturas es posible que deba escribir una dirección de función en un registro manualmente.

Los periféricos del microcontrolador suelen ser independientes entre sí, por lo que puede aprenderlos uno a la vez. Puede ser útil elegir un periférico y usarlo para aprender parte de las cosas a nivel del sistema. Los periféricos de comunicación y los PWM son buenos para el reloj y las E/S, y los temporizadores son buenos para las interrupciones.

No se deje intimidar por el nivel de complejidad. Esos microcontroladores "básicos" ya le han enseñado mucho de lo que necesita saber. Por favor, hágamelo saber si necesita que le aclare algo.

Buena respuesta. Otra cosa a tener en cuenta son los periféricos DMA, que tienden a tener interfaces complicadas y poco documentadas según mi experiencia.
"Y ahora todos los tipos de enteros deberían ser igualmente rápidos". En realidad, debido a las reglas de promoción de enteros en C, el uso de tipos de 8/16 bits puede resultar en una gran extensión de signo/cero, y puede ser un problema cuando la memoria Flash es baja. Entonces, si hay RAM de sobra, podría valer la pena usar más tipos de 32 bits o al menos preferir tipos int/ int_leastN_Tpara variables de pila.
Me equivoqué en mi comentario. Quise decir; usa int_fastN_ttipos, no int_leastN_ttipos.
@user694733: Desearía que el estándar C permitiera que el código solicite un número entero que ocupe un tamaño determinado en la memoria y pueda operar en valores dentro de un rango particular, pero con una semántica poco especificada cuando va más allá de ese rango. En algo como el ARM, an int16_tserá a menudo tan rápido como int32_tpara los valores almacenados en la memoria, pero el estándar requiere que en las plataformas donde intes de 17 bits o más, int16_t x=32767; x+=2;debe establecerse xen -32767, lo que requiere con frecuencia instrucciones de extensión de signo, incluso si el código nunca haga uso del comportamiento de envoltura.
El estándar @supercat C requiere un comportamiento de ajuste solo para tipos sin firmar. Para los tipos firmados, cualquier envoltorio es UB, debido a las diferentes representaciones posibles. Entonces, con x+=2, sería legal usar instrucciones para tipos de 16 bits, porque el compilador puede asumir que el valor no se ajustará y, por lo tanto, usarlo no cambiaría el comportamiento observable. Pero creo que ARM no tiene una instrucción ADD de 16 bits que lo haga posible. (Podría estar equivocado, mi conocimiento sobre el conjunto de instrucciones ARM no es tan bueno).
@user694733: conversiones y coerciones de valores con signo a un tipo con signo más pequeño que intel especificado como convertir el valor a un tipo sin signo del mismo tamaño y luego convertirlo a un entero con signo. Si el valor está dentro del rango especificado para el tipo de entero con signo, el resultado está definido por la implementación en lugar de indefinido. Creo que es una tontería que en la mayoría de las plataformas de 32 bits, dado int16_t i; uint16_t u;, la instrucción i*=i;se define para todos los valores de i, pero u*=u;no se define para todos los valores de u, pero no escribí las reglas.
@user694733: Me gustaría que C tuviera un tipo que se comportara en gran medida como int16_t, pero otorgando a los compiladores la libertad adicional de que si un valor fuera de rango se almacena en una variable de ese tipo, un compilador podría tener las lecturas de la variable arrojan arbitrariamente cualquier valor congruente con el valor almacenado mod 65536, aunque se requerirían conversiones para truncar al rango -32768..32767 ; por el contrario, requeriría que si el resultado de un operador aditivo, multiplicativo o de desplazamiento a la izquierda se convierte o se coacciona a un tipo sin signo más pequeño que int...
...el operador debe obligar a sus operandos a ese tipo más pequeño sin firmar y comportarse como si la operación se realizara en ese tipo. No veo ningún beneficio en permitir que el producto de dos uint16_tvalores desencadene un Comportamiento indefinido en los casos en que el resultado se volverá a asignar a uint16_t.

Es útil recordar que ARM posee la propiedad intelectual del microprocesador, pero en realidad no fabrica las piezas. En su lugar, los fabricantes obtienen licencias de las diversas versiones de procesadores ARM y producen sus propias piezas únicas con combinaciones individuales de características y periféricos.

Dicho esto, si es nuevo en la arquitectura, probablemente tenga sentido comenzar con la documentación de ARM que es, esencialmente, la documentación básica para todos esos microprocesadores.

Por ejemplo, el Cortex-M0 se describe en el sitio web de ARM .

También hay una lista de libros relacionados con ARM que satisface una amplia variedad de necesidades e intereses.

Finalmente, están las hojas de datos del fabricante específico. Para el M0, Cypress, NXP y STMicroelectronics son solo tres de los muchos fabricantes de piezas reales basadas en Cortex-M0.

(Y no, no trabajo para ARM y nunca lo he hecho).

Esta es una respuesta bastante genérica que no hace mucho más que un enlace a algunos documentos de Cortex-M0, que estoy seguro de que el OP puede encontrar por su cuenta.
Aborda directamente la pregunta que solicitaba ayuda para encontrar la documentación general. Esta respuesta responde directamente a esa necesidad y explica por qué las cosas son como son.

Una gran diferencia es el uso de bibliotecas proporcionadas por proveedores. Para los PIC, Atmels, etc., la mayoría de los desarrolladores no utilizaron mucho las bibliotecas básicas (para gpio, temporizadores, adc, etc.). En mi experiencia, la gente (como máximo) los usaría como guías al escribir su propio código.

Sin embargo, con ARM, casi siempre se usan las bibliotecas. Existe un estándar, "CMSIS", que se recomienda seguir a los fabricantes. Debe hacerse. Ayuda en la portabilidad del código (entre diferentes ARM y entre fabricantes) y brinda un método "estandarizado" para estructurar su código. La gente se acostumbra a ver y comprender las funciones de la biblioteca.

Seguro que hay algunos desarrolladores que acceden a los registros directamente, pero son los atípicos :)

Para responder a su pregunta, me resultó muy útil leer la documentación de la Biblioteca. ST tiene un código bien desarrollado, con un gran archivo de ayuda creado por Doxygen. Puede ver cuáles son todas las opciones para cada módulo de hardware.

Para usar GPIO como ejemplo, la función de inicialización maneja:

  • Dirección (dentro o fuera)
  • dominadas/tiradas
  • colector abierto/push-pull
  • velocidad de subida
  • etc.

Al mirar las opciones, puede ver lo que es posible. Y, por supuesto, ¡aprenderá cómo pasar estas opciones a la función Init!

Bien, ahora que dije eso, veo que su ARM específico no tiene bibliotecas compatibles con CMSIS. En cambio, tienen su SDK patentado disponible para descargar. Comenzaría a buscar a través de sus documentos SDK.

Si no está casado con este producto específico, podría recomendarle que busque un proveedor diferente con bibliotecas más compatibles. Vas a subir una curva de aprendizaje de todos modos, por lo que también podrías hacer que tu inversión sea más portátil...

¡Los ARM son divertidos! No he mirado atrás.

" Para los PIC, Atmels, etc., la mayoría de los desarrolladores no usaban mucho las bibliotecas". No estoy seguro de dónde viene eso. Solo he usado PIC, no AVR, pero ciertamente no me gustaría escribir mi propia biblioteca, por ejemplo, para la interfaz de host USB, la pila TCP o el sistema de archivos de la tarjeta SD. Las bibliotecas de Microchip para todos estos parecen bastante adecuadas.
Ah, @tcrosley, definitivamente tienes razón. Solo trataba de referirme a la funcionalidad que cubren los periféricos básicos: gpio, timers, adc, etc.
Acepto, generalmente accedo a GPIO, temporizadores, relojes y UART R/W directamente. A veces uso sus llamadas de biblioteca para la configuración de I2C, SPI, ADC y UART, pero no siempre. Muchos registros, ya sea que esté hablando de PIC (y en particular de PIC32) o ARM.
Creo que esta respuesta es la más pragmática enumerada para esta pregunta, aunque no se aplica al controlador específico del OP. Creo que se puede hacer bastante buena ingeniería integrada sin comprender el AHB o el NVIC.
@JayCarlson ¡Gracias! Su edición de esta respuesta fue rechazada porque se supone que no debemos cambiar las publicaciones de otras personas de manera tan significativa. ¡Pero fue muy buena información! Le sugiero que lo publique como su propia respuesta, por lo que ayudará a las personas y también será votado :)
@JayCarlson Por cierto, si no tiene acceso a la edición propuesta, puede encontrarla aquí . Si no, puedo enviártelo por correo electrónico o algo así para que no tengas que volver a escribirlo...
Oh esta bien. El formulario de edición dice: "las ediciones deben ser mejoras significativas, no cambios menores".

Buen momento para moverse; los 8 bits se están muriendo rápidamente; cuando puede comprar una placa de $ 5 con (por ejemplo) un STM32F103 que es un microcontrolador ARM de 32 bits bastante capaz (¡incluso con USB!), No hay duda de que los tiempos han cambiado.

Ya ha recibido algunas respuestas excelentes, pero principalmente diría "olvídese del ensamblaje" y casi "olvídese de preocuparse por cómo funciona la CPU a un nivel bajo". (una optimización específica o para la depuración), pero los núcleos ARM ejecutan el código C excepcionalmente bien (por diseño) y rara vez necesita aventurarse profundamente en las tripas.

Esto significa que pasará una cierta cantidad de tiempo golpeándose la cabeza contra los problemas con los compiladores (y especialmente los vinculadores y los archivos MAKE) vomitando errores oscuros, pero todos son superables.

Las entrañas de cómo funcionan los ARM (es decir, los libros de CPU ARM) son densos y no muy interesantes hasta el día en que realmente necesita optimizar (y se sorprenderá de la poca frecuencia que tiene cuando tiene registros de 32 bits y su PLL). d El reloj de la CPU está en la región de 100 MHz).

El conjunto de instrucciones ARM de la "vieja escuela" es mucho más fácil de leer y desensamblar que el mucho más nuevo "Thumb2", que es lo que se encuentra en la mayoría de los ARM de nivel de microcontrolador modernos (Cortex), pero nuevamente las entrañas de las instrucciones en lenguaje ensamblador en su mayoría se desvanecen en el fondo; si tiene el conjunto de herramientas correcto (especialmente un depurador de nivel de fuente decente con puntos de interrupción/paso único, etc.), simplemente no le importa demasiado que sea ARM en absoluto.

Una vez que esté en el mundo de los registros de 32 bits y los anchos de bus de datos de 32 bits y todo lo que siempre quiso disponible en el chip, nunca más querrá volver a una CPU de 8 bits; básicamente, a menudo no hay penalización por "tomarlo con calma" y escribir código para que sea legible más que eficiente.

Sin embargo... periféricos... sí y AHÍ ESTÁ el problema.

Seguro que obtienes un montón de cosas con las que jugar en los MCU modernos, y muchas de ellas son cosas bastante elegantes; a menudo encuentra un mundo de sofisticación mucho, mucho más allá de los periféricos en chip AVR, PIC y 8051.

¿Un temporizador programable? ¡No, toma ocho! DMA? ¿Qué tal 12 canales con prioridad programable y modo ráfaga y modo encadenado y recarga automática y... y... y...

¿I2C? ¿I2S? ¿Docenas de opciones de muxing de pines? ¿Quince formas diferentes de reprogramar el flash en chip? ¡Seguro!

A menudo se siente como si hubiera pasado de la hambruna a la fiesta con los periféricos y es común que haya trozos enteros de un chip que admirará pero que apenas usará (por lo tanto, sincronización del reloj).

La cantidad de hardware en el chip (y las variaciones en la línea de chips de un solo proveedor) es hoy en día bastante alucinante. Por supuesto, un proveedor de chips tenderá a reutilizar los bloques de IP, por lo que una vez que se familiarice con una determinada marca, se volverá más fácil, pero "la mierda se volvió loca hoy en día".

En todo caso, los periféricos y sus interacciones (y DMA e interrupciones y asignación de bus y y y...) son TAN complejos (y, en ocasiones, no exactamente como se describe en las hojas de datos) que los ingenieros con frecuencia tienen una gama favorita de MCU ARM y tienden a querer quedarse con él simplemente porque están familiarizados con los periféricos y las herramientas de desarrollo.

Buenas bibliotecas y herramientas de desarrollo (es decir, un ciclo rápido de compilación y depuración con un depurador adecuado) y un gran conjunto de proyectos de código de ejemplo en funcionamiento son absolutamente cruciales para su elección de ARM MCU hoy en día. Parece que la mayoría de los proveedores ahora tienen placas de evaluación extremadamente baratas (

Como estoy seguro de que habrás notado, una vez que superas el nivel del microcontrolador con ARM y llegas al nivel de SOC (por ejemplo, SOC de estilo Raspberry Pi/etc), las reglas cambian por completo y se trata de qué tipo de Linux estás usando. correr, porque, con muy pocas excepciones, estarías ladrando como un loco si intentaras cualquier otra cosa.

Básicamente; independientemente de la CPU que (puede) haber sido preseleccionada para usted en este concierto, cómprese un puñado de placas de evaluación basadas en Cortex súper baratas de algunos proveedores diferentes (TI, STM, Freescale y más vienen a la mente) y tenga un truco con el código de muestra proporcionado.

Último consejo; una vez que encuentre la página o tres en la hoja de datos que describe las opciones de pin-muxing para el chip de número de pieza exacto con el que está trabajando, es posible que desee imprimirlo y pegarlo en la pared. Descubrir tarde en un proyecto que cierta combinación de periféricos es imposible debido a la muxación de pines no es divertido y, a veces, esa información está tan oculta que juraría que están tratando de ocultarla :-)

una adición rápida: si su proyecto es mucho más que el controlador más simple, piense en usar un RTOS; hay algo así como una curva de aprendizaje con lo que elija, pero incluso los ARM más pequeños tienen mucho empuje hoy en día para ejecutar un sistema operativo multiproceso. Personalmente, descubrí que ChibiOS es una excelente combinación de tecnología esbelta pero capaz (especialmente si se ejecuta en STM32, donde viene con una buena biblioteca de periféricos), pero hay varias opciones.

También vengo de AVR y ahora generalmente me quedo con STM32 (Cortex-M). Esto es lo que recomiendo para empezar, y refleja mis propias dificultades cuando comencé:

  1. Obtenga una placa con un depurador, o al menos un conector JTAG (y luego compre un depurador JTAG). Hay muchos baratos, y ahorrarás mucho tiempo usándolos.

  2. Consigue un buen IDE con todo incluido. Solía ​​recomendar CooCox CoIDE hace mucho tiempo. Desde entonces, ha detenido y reiniciado el desarrollo, por lo que no estoy seguro de cómo está ahora. "Un buen IDE" le permite hacer que el LED Hello World básico parpadee en poco tiempo.

  3. "Un buen IDE" debería configurar los encabezados CMSIS del fabricante. Estos son básicamente los mapas de registro que permiten una escritura más fácil de los programas C/C++, con nombres de variables en lugar de números simples y punteros.

  4. Intente utilizar las bibliotecas de periféricos del fabricante, si no necesita el mejor rendimiento absoluto. En realidad no lo haces por ahora, ya que estás aprendiendo. Si luego descubre que necesita exprimir más, busque en el código de la biblioteca para ver cómo hace qué. Lo bueno de las bibliotecas también es que generalmente te permiten usar muchos chips diferentes del mismo fabricante con el mismo código.

  5. A diferencia de AVR, los chips ARM comienzan con los periféricos deshabilitados. Primero debe habilitarlos. Una buena biblioteca de periféricos tendrá ejemplos sobre cómo usar los periféricos correctamente, y puede obtener más información de la hoja de datos del dispositivo. Por lo tanto, recuerde habilitar los relojes y periféricos antes de usarlos. Sí, incluso los puertos de E/S se consideran periféricos.

  6. Codifica a medida que aprendes. No intentes asimilar todo a la vez, ya que es bastante complejo. Comenzaría aprendiendo el árbol del reloj (autobuses APB, AHB, etc.) y cómo interactúan los relojes y los divisores de reloj. Luego buscaría dónde almacena el IDE los scripts del enlazador y el código de inicio para su dispositivo. La secuencia de comandos del enlazador es más o menos cómo organiza la memoria (dónde está la RAM, la memoria flash, la tabla de vectores ISR, etc.). El script de inicio configura su programa (cosas como copiar inicializadores de variables globales de flash a RAM). Algunos IDE tienen scripts de inicio en ASM y otros en C. A veces puede buscar otro en Google, en el idioma que prefiera.

  7. Ponga en marcha el depurador lo antes posible. Es bastante común cometer un error al principio, al hacer algunas cosas (generalmente la inicialización del hardware) en un orden diferente al que debería. Esto a veces desencadena una excepción de ISR que lo coloca en while(1);un bucle infinito (implementación predeterminada para ese ISR) que detiene su programa y es difícil de rastrear incluso con un depurador. Imagínese sin un depurador.

  8. Hablando de un depurador, intente poner en marcha el UART también, luego use un adaptador serial-USB para leer eso. printf()la depuración siempre es útil :-)

No he trabajado mucho en 8051, AVR o PIC. Pero recientemente comencé a buscar en la línea de procesadores ARM Cortex MX. Por lo tanto, no puedo decirle mucho sobre la transición desde 8051, AVR o PIC, pero sobre todo desde el punto de vista de un principiante.

El procesador ARM®Cortex™-M4 se basa en la arquitectura Harvard, por lo que tiene buses de datos e instrucciones separados. A continuación se muestra una imagen de alto nivel.

ingrese la descripción de la imagen aquí

Esta semana, representantes de NXP visitarán nuestras instalaciones. Verificaré con ellos los recursos de NXP ARM-Cortex Mx y los publicaré aquí. Freescale tiene un microcontrolador (MCU) de 32 bits de bajo consumo Kinetis basado en núcleos ARM® Cortex®-M . Entiendo que también tienen guías similares para aprender a usar procesadores ARM. Desafortunadamente no los he investigado.


Referencias: