PIC32 vs dsPIC vs ARM vs AVR, ¿la arquitectura importa cuando programamos en lenguaje C de todos modos? [cerrado]

7

Actualmente estamos utilizando un microcontrolador PIC32 de 32 bits. Está funcionando bien para nuestras necesidades, pero también estamos explorando otros microcontroladores que pueden combinarse mejor con nosotros + tenemos otros proyectos para los cuales estamos seleccionando MCU. Para ello, hemos seleccionado DA DA basado en ARM microcontrolador que es el mismo de 32 bits, pero está basado en ARM (más popular que PIC32, de la industria).

Ahora para PIC32 usamos MPLAB pero para ARM cortex-M0, usaremos Atmel Studio. Usaremos lenguaje C en ambas plataformas. Lo que me preocupa es que usaremos dos microcontroladores de 32 bits (de la misma compañía) pero con arquitecturas diferentes. Esto requerirá que aprendamos dos dispositivos diferentes y aumentará nuestra "curva de aprendizaje" + tiempo de entrega. Pero, por otro lado, también creo que ya que usaremos C-Language en ambos casos, la curva de aprendizaje para ARM no debería ser tan escuchada y también vale la pena explorar ese procesador.

Mi pregunta principal es, cuán importante es la diferencia que hace la arquitectura cuando programamos en C-Language, ya que proporciona una abstracción de los componentes internos del microcontrolador. Y cuáles son los principales diferencias en MPLAP y Atmel Studio , considerando la programación en lenguaje C.

    
pregunta engineer

5 respuestas

18

Este es un tema bastante criticado. Puedo hablar por mí mismo (AVR, ARM, MSP430).

La diferencia 1 (la más significativa) está en los periféricos. Cada uno de los MCU tiene UART, SPI, temporizadores, etc. similares. Solo registre nombres y los bits son diferentes. La mayor parte del tiempo fue el problema principal con el que tuve que lidiar al mover el código entre chips. Solución: escriba sus controladores con una API común, para que su aplicación pueda ser portátil.

La diferencia 2 es la arquitectura de la memoria. Si desea colocar constantes en flash en un AVR, tiene que usar atributos especiales y funciones especiales para leerlos. En el mundo de ARM, simplemente elimina la referencia a un puntero porque hay un espacio de direcciones único (no sé cómo lo manejan los PIC pequeños, pero supondría que están más cerca del AVR).

La diferencia 3 es la declaración de interrupción y el manejo. avr-gcc tiene la macro ISR() . ARM solo tiene un nombre de función (como someUART_Handler () - si usa encabezados CMSIS y código de inicio). Los vectores de interrupción ARM pueden colocarse en cualquier lugar (incluida la RAM) y modificarse en tiempo de ejecución (muy útil si tiene, por ejemplo, dos protocolos UART diferentes que se pueden cambiar). AVR solo tiene la opción de usar vectores en "flash principal" o en la "sección del cargador de arranque" (por lo tanto, si desea manejar las interrupciones de manera diferente, debe usar una declaración if ).

Diferencia 4 - modos de suspensión y control de potencia. Si necesita reducir el consumo de energía, debe cumplir con todas las funciones de la MCU. Esto puede diferir mucho entre los MCU: algunos tienen modos de ahorro de energía más generales, otros pueden habilitar / deshabilitar periféricos individuales. Algunas MCU tienen reguladores ajustables para que pueda ejecutarlas con un voltaje más bajo a una velocidad más lenta, etc. No veo una manera fácil de lograr la misma eficiencia en una MCU (digamos) con 3 modos de potencia global y otra con 7 modos de potencia y Control de reloj periférico individual.

Lo más importante cuando se preocupa por la portabilidad es dividir claramente su código en partes dependientes de hardware (controladores) e independientes de hardware (aplicación). Puede desarrollar y probar esta última en una PC normal con un controlador simulado (por ejemplo, consola en lugar de un UART). Esto me salvó muchas veces, ya que el 90% del código de la aplicación estaba completo antes de que el prototipo de hardware saliera del horno de reflujo :)

En mi opinión, lo bueno de ARM es el "monocultivo": la disponibilidad de muchos compiladores (gcc, Keil, IAR ... por nombrar algunos), muchos IDE gratuitos y con soporte oficial ( al menos para NXP, STM32, Silicon Labs, Nordic), muchas herramientas de depuración (SEGGER, especialmente Ozone, ULINK, OpenOCD ...) y muchos proveedores de chips (ni siquiera empezaré a nombrarlos). El PIC32 se limita principalmente a Microchip (pero solo importa si no te gustan sus herramientas).

Cuando se trata de código C Es el 99% igual, una instrucción if es lo mismo, un bucle funciona de la misma manera. Sin embargo, debe preocuparse por el tamaño de la palabra nativa. Por ejemplo, un bucle for en un AVR es más rápido si usa uint8_t para el contador, mientras que en ARM uint32_t es el tipo más rápido (o int32_t ). ARM tendría que verificar el desbordamiento de 8 bits cada vez que use un tipo más pequeño.

La selección de una MCU y / o proveedor en general se trata principalmente de política y logística (a menos que tenga restricciones de ingeniería muy claras, por ejemplo: alta temperatura: use MSP430 o Vorago). Incluso si la aplicación puede ejecutarse en cualquier cosa y solo el 5% del código (controladores) debe desarrollarse y admitirse durante la vida útil del producto , sigue siendo un costo adicional para la empresa. Todos los lugares en los que he trabajado tienen un proveedor favorito y una línea de MCU (como "elige cualquier Kinetis que quieras a menos que haya una muy buena razón para elegir algo diferente"). También ayuda si tiene otras personas para pedir ayuda, así que como gerente evitaría tener un departamento de desarrollo de 5 personas donde todos usaban un chip totalmente diferente.

    
respondido por el filo
11

He usado varios MCU de cuatro fabricantes diferentes. El trabajo principal cada vez más es familiarizarse con los periféricos.

Por ejemplo, un UART en sí no es demasiado complejo y encuentro el puerto de mis controladores fácilmente. Pero la última vez que tardé casi un día en hacer que los relojes, los pines de E / S se interrumpieran, se habilitaran, etc.

El GPIO puede ser muy complejo. Configuración de bit, bit clear, bit-toggle, funciones especiales habilitar / deshabilitar, tri-estado. A continuación, obtiene interrupciones: cualquier borde, subida, caída, nivel bajo, nivel alto, limpieza automática o no.

Luego están I2C, SPI, PWM, temporizadores y dos docenas de tipos de periféricos, cada uno con su propio reloj habilitado y cada vez que los registros son diferentes con los nuevos bits. Para todos esos, lleva muchas horas leer la hoja de datos cómo establecer qué bit en qué circunstancias.

El último fabricante tenía muchos ejemplos de código que encontré inutilizables. Todo fue abstraído. Pero cuando lo localicé, el código pasó por seis! niveles de llamadas a funciones para establecer un bit GPIO. Agradable si tiene un procesador de 3GHz pero no en una MCU de 48MHz. Mi código al final era una sola línea:

GPIO->set_output = bit.

He intentado usar más controladores genéricos, pero me he rendido. En una MCU siempre estás luchando con los ciclos de espacio y reloj. Descubrí que la capa de abstracción es la primera en salir por la ventana si genera una forma de onda específica en una rutina de interrupción llamada a 10 KHz.

Así que ahora tengo todo funcionando y planeo NO cambiar de nuevo a menos que sea por una muy, muy buena razón.

Todo lo anterior debe amortizarse según la cantidad de productos que vende y lo que ahorra. Vender un millón: ahorrar 0,10 para cambiar a un tipo diferente significa que puede gastar 100.000 en horas de trabajo de software. Vendiendo 1000 solo tienes 100 para gastar.

    
respondido por el Oldfart
8

Esto es más una opinión / comentario que una respuesta.

No desea y no debería estar programando en C. C ++, cuando se usa de la manera correcta , es muy superior. (De acuerdo, debo admitir que, cuando se usa de manera incorrecta, es mucho peor que C.) Eso lo limita a los chips que tienen un compilador de C ++ (moderno), que es casi todo lo que soporta GCC, incluyendo AVR (con algunas limitaciones, filo menciona los problemas de un espacio de direcciones no uniforme), pero excluyendo casi todos los PIC (PIC32 podría ser compatible, pero todavía no he visto ningún puerto decente).

Cuando estás programando algoritmos en C / C ++, la diferencia entre las opciones que mencionas es pequeña (excepto que un chip de 8 o 16 bits tendrá una gran desventaja cuando hagas mucha aritmética de bits de 16, 32 o más) . Cuando necesite la última onza de rendimiento, probablemente necesitará usar el ensamblador (ya sea el suyo propio o el código proporcionado por el proveedor o un tercero). En ese caso, es posible que desee volver a considerar el chip que seleccionó.

Cuando esté codificando para el hardware, puede usar una capa de abstracción (a menudo proporcionada por el fabricante) o escribir la suya propia (según la hoja de datos y / o el código de ejemplo). Las abstracciones de C existentes de IME (mbed, cmsis, ...) a menudo son funcionales (casi) correctas, pero fallan horriblemente en el rendimiento (verifique los viejos arreglos y desplace alrededor de 6 capas de direccionamiento indirecto para una operación de conjunto de pin), usabilidad y portabilidad. Quieren exponerle a usted la funcionalidad todo del chip en particular, que en casi todos los casos no necesitará y no le importará, y bloquea su código a ese proveedor en particular (y probablemente a ese proveedor en particular). chip).

Esto es donde C ++ puede hacerlo mucho mejor: cuando se hace correctamente, un conjunto de pines puede pasar por 6 o más capas de abstracción (porque eso hace posible una mejor interfaz (¡portátil!) y un código más corto), pero proporciona una la interfaz que es independiente del destino para los casos simples , y sigue generando el mismo código de máquina que escribiría en el ensamblador .

Un fragmento del estilo de codificación que utilizo, que puede entusiasmarte o apartarte del horror:

// GPIO part of a HAL for atsam3xa
enum class _port { a = 0x400E0E00U, . . . };

template< _port P, uint32_t pin >
struct _pin_in_out_base : _pin_in_out_root {

   static void direction_set_direct( pin_direction d ){
      ( ( d == pin_direction::input )
         ? ((Pio*)P)->PIO_ODR : ((Pio*)P)->PIO_OER )  = ( 0x1U << pin );
   }

   static void set_direct( bool v ){
      ( v ? ((Pio*)P)->PIO_SODR : ((Pio*)P)->PIO_CODR )  = ( 0x1U << pin );    
   }
};

// a general GPIO needs some boilerplate functionality
template< _port P, uint32_t pin >
using _pin_in_out = _box_creator< _pin_in_out_base< P, pin > >;

// an Arduino Due has an on-board led, and (suppose) it is active low
using _led = _pin_in_out< _port::b, 27 >;
using led  = invert< pin_out< _led > >;

En realidad hay algunas capas más de abstracción. Sin embargo, el uso final del led, digamos que para encenderlo, no muestra la complejidad ni los detalles del objetivo (para un arduin uno o una pastilla azul ST32, el código sería idéntico).

target::led::init();
target::led::set( 1 );

El compilador no se siente intimidado por todas esas capas, y como no hay funciones virtuales involucradas, el optimizador ve todo (algunos detalles, omitidos, como habilitar el reloj periférico):

 mov.w  r2, #134217728  ; 0x8000000
 ldr    r3, [pc, #24]   
 str    r2, [r3, #16]
 str    r2, [r3, #48]   

Así es como lo habría escrito en el ensamblador - SI me hubiera dado cuenta de que los registros PIO se pueden usar con desplazamientos de una base común. En este caso, probablemente lo haría, pero el compilador es mucho mejor optimizando esas cosas que yo.

Por lo que tengo una respuesta, es: escriba una capa de abstracción para su hardware, pero hágalo en C ++ moderno (conceptos, plantillas) para que no dañe su rendimiento. Con eso en su lugar, puedes cambiar fácilmente a otro chip. Incluso puedes comenzar a desarrollar en un chip aleatorio que tengas, con el que estés familiarizado, con buenas herramientas de depuración, etc. y posponer la elección final hasta más tarde (cuando tengas más información sobre la memoria requerida, la velocidad de la CPU, etc.).

En mi opinión, una de las falacias del desarrollo integrado es elegir el chip primero (es una pregunta que se hace a menudo en este foro: en qué chip debería elegir ... La mejor respuesta es generalmente: no importa).

(edición: respuesta a "¿En cuanto al rendimiento, C o C ++ estarían al mismo nivel?")

Para las mismas construcciones, C y C ++ son iguales. C ++ tiene muchas más construcciones para la abstracción (solo algunas: clases, plantillas, constexpr) que, como cualquier herramienta, pueden usarse para lo bueno o para lo malo. Para que las discusiones sean más interesantes: no todos están de acuerdo en lo que es bueno o malo ...

    
respondido por el Wouter van Ooijen
4

Si entiendo correctamente, desea saber qué características específicas de la arquitectura de la plataforma "aparecen" en su entorno de lenguaje C, lo que hace que sea más difícil escribir código portátil y mantenible en ambas plataformas.

C ya es bastante flexible ya que es un "ensamblador portátil". Todas las plataformas que ha seleccionado tienen GCC / compiladores comerciales disponibles que son compatibles con los estándares de idioma C89 y C99, lo que significa que puede ejecutar un código similar en todas las plataformas.

Hay algunas consideraciones:

  • Algunas arquitecturas son Von Neumann (ARM, MIPS), otras son Harvard. Las principales limitaciones surgen cuando su programa de C necesita leer datos de la ROM, por ejemplo. para imprimir cadenas, tenga los datos definidos como "const" o similar.

Algunas plataformas / compiladores pueden ocultar esta "limitación" mejor que otras. P.ej. en AVR necesitas usar macros específicas para leer datos de ROM. En PIC24 / dsPIC también hay disponibles instalaciones tblrd dedicadas. Sin embargo, además, algunas partes también tienen la característica "visibilidad del espacio del programa" (PSVPAG) disponible que permite mapear una página del FLASH en RAM, haciendo que el direccionamiento inmediato de datos esté disponible sin tblrd. El compilador puede hacer esto con bastante eficacia.

ARM y MIPS son Von Neumann, por lo tanto tienen regiones de memoria para ROM, RAM y periféricos empaquetados en 1 bus. No notará ninguna diferencia entre la lectura de datos de RAM o "ROM".

  • Si bucea por debajo de C y observa las instrucciones generadas para ciertas operaciones, encontrará algunas grandes diferencias en torno a la E / S. ARM y MIPS son RISC arquitectura de registro de almacenamiento de carga . Esto significa que el acceso a los datos en el bus de memoria debe pasar por las instrucciones MOV. Esto también significa que cualquier modificación de un valor periférico conducirá a una operación de lectura-modificación-escritura (RMW). Hay algunas partes ARM que admiten Bit-Banding, que mapean los registros de conjunto / clr-bit en el espacio periférico de E / S. Sin embargo, debe codificar este acceso usted mismo.

Por otra parte, un PIC24 permite que las operaciones de ALU lean y escriban datos directamente a través de direccionamiento indirecto (incluso con modificaciones de puntero ...). Esto tiene algunas características de una arquitectura similar a CISC, por lo que 1 instrucción puede hacer más trabajo. Este diseño puede llevar a núcleos de CPU más complejos, relojes más bajos, mayor consumo de energía, etc. Afortunadamente para usted, la pieza ya está diseñada. ;-)

Estas diferencias pueden significar que un PIC24 puede ser "más contundente" w.r.t. Operaciones de E / S que un chip ARM o MIPS cronometrado de manera similar. Sin embargo, es posible que obtenga una pieza ARM / MIPS de temporizador mucho más alta para las mismas restricciones de precio / paquete / diseño. Supongo que, en términos prácticos, creo que una gran cantidad de "aprender la plataforma" consiste en comprender lo que la arquitectura puede y no puede hacer, la rapidez con la que se realizarán algunas operaciones, etc.

  • Los periféricos, la gestión del reloj, etc. difieren según la familia de piezas. Estrictamente hablando, esto también cambiará dentro del ecosistema ARM entre los proveedores, a excepción de algunos periféricos unidos a Cortex m como NVIC y SysTick.

Estas diferencias pueden encapsularse de alguna manera por los controladores de dispositivo, pero al final el firmware incorporado tiene un alto nivel de acoplamiento con el hardware, por lo que a veces no se puede evitar el trabajo personalizado.

Además, si está abandonando los ecosistemas de Microchip / antiguo Atmel, es posible que las piezas ARM requieran más configuración para que funcionen. Me refiero en términos de; habilitando los relojes a los periféricos, luego configurando los periféricos y "habilitándolos", configurando el NVIC por separado, etc. Esto es solo una parte de la curva de aprendizaje. Una vez que recuerde hacer todas estas cosas, en el orden correcto, escribir controladores de dispositivo para todos estos microcontroladores se sentirá bastante similar en algún momento.

  • También, intente usar bibliotecas como stdint.h, stdbool.h, etc. si aún no lo está. Estos tipos de enteros hacen explícitos los anchos, lo que hace que el comportamiento del código sea más predecible entre las plataformas. Esto puede significar el uso de enteros de 32 bits en un AVR de 8 bits; pero si su código lo necesita, que así sea.
respondido por el Hans
3

Sí y no. Desde la perspectiva de los programadores, lo ideal es que ocultes los detalles del conjunto de instrucciones. Pero hasta cierto punto, eso ya no es relevante, ya que los periféricos, que es el objetivo principal de escribir el programa, no forman parte del conjunto de instrucciones. Ahora, al mismo tiempo, no puede comparar 4096Byte flash parts en esos conjuntos de instrucciones, especialmente si usa C, la cantidad de consumo del flash / memoria está muy determinada por el conjunto de instrucciones y el compilador, algunos nunca deben ver un compilador tos) debido a la cantidad de desperdicio de esos recursos que se consumen por compilación. Otros consumo de flash es una sobrecarga más pequeña. El rendimiento también es un problema al utilizar un lenguaje de alto nivel y los asuntos de rendimiento en las aplicaciones de MCU, por lo que puede marcar la diferencia entre gastar $ 3 por placa para el mcu o $ 1. Construye 1 millón de unidades, eso es 2Millones de dólares que simplemente regalas en relación con unas pocas decenas a cien mil dólares en tiempo de desarrollo de software.

Si se trata de facilitar la programación (al costo general del producto), debería poder descargar un paquete de desarrolladores para el mcu de tal manera que la arquitectura del conjunto de instrucciones nunca se vea, por lo que si ese es su principal preocupación, no es una preocupación. Todavía le cuesta dinero en cuanto al costo del producto para usar estas bibliotecas, pero, el tiempo de comercialización podría ser más corto, creo que las bibliotecas requieren más tiempo / trabajo para usar, en lugar de hablar directamente con los periféricos .

En pocas palabras, los conjuntos de instrucciones son la menor de tus preocupaciones, pasa a los problemas reales.

    
respondido por el old_timer

Lea otras preguntas en las etiquetas

Comentarios Recientes

millerstein5 $ PEZ Más horas de capacitación en backend nuevo para 12k personas, todas con dispositivos PIN únicos y confiando en la multitud de relojes especializados conectados a través de Zigbee y PDU ... (mensajes con barricada para preguntas de 1aa RPC) Algo así como efectivo gratis. [abierto] Ritmo razonable y no se le paga diez veces por las dificultades diarias. y http: //pelvryani.somavnu.com / ... Andrew Lang: ... BE de appas y ACM Research quieren que extraiga pezcoins. PUBLICACIONES EN ESPAÑOL oncon... Lees verder