Estaba codificando para Atmel ATmega 32 chips. Ahora he cambiado a la familia ARM Cortex-M.
¿Hay alguna diferencia en la codificación entre AVR y ARM, o depende del compilador? ¿Cómo aprendería a programar para MCU de ARM?
Estaba codificando para Atmel ATmega 32 chips. Ahora he cambiado a la familia ARM Cortex-M.
¿Hay alguna diferencia en la codificación entre AVR y ARM, o depende del compilador? ¿Cómo aprendería a programar para MCU de ARM?
Hay cierta similitud (obviamente, ANSI C es bastante ANSI C) pero las diferencias en el hardware resultarán en un gran aumento de la complejidad en la mayoría de los casos cuando se usa un ARM u otro procesador de 32 bits. No es que usted no pueda hacer programas simples que accedan directamente al hardware en un ARM, pero generalmente hay capas de abstracción de hardware y middleware y tal vez un RTOS con el que lidiar para poder ejecutar un programa que era el La razón por la que querías usar un ARM en primer lugar.
Algunas cosas son probablemente un poco más simples, pero también hay problemas de alineación de la memoria y barrera de la memoria con los que lidiar. Y problemas de subprocesos si utiliza un RTOS (que es más o menos de lo que trata al usar interrupciones pero con una terminología diferente). La estructura del autobús es mucho más compleja. Es probable que desee considerar el uso de DMA u otros periféricos complejos.
Tendrá que adaptarse a las convenciones de denominación utilizadas por el proveedor de middleware si desea utilizar su pila USB, pila Ethernet, etc. Se dice que
"Solo hay dos cosas difíciles en Informática: la invalidación de la memoria caché, nombrar las cosas y los errores off-by-one"
Aquí hay un ejemplo de ASF (Atmel Software Framework) de escribir en un puerto (blinky):
en main.c:
ioport_set_pin_level(LED1_GPIO, IOPORT_PIN_LEVEL_HIGH);
en ioport.h
static inline void ioport_set_pin_level(ioport_pin_t pin, bool level)
{
arch_ioport_set_pin_level(pin, level);
}
en ioport_pio.h
__always_inline static void arch_ioport_set_pin_level(ioport_pin_t pin,
bool level)
{
Pio *base = arch_ioport_pin_to_base(pin);
if (level) {
base->PIO_SODR = arch_ioport_pin_to_mask(pin);
} else {
base->PIO_CODR = arch_ioport_pin_to_mask(pin);
}
}
También en ioport.h:
/** \brief IOPORT levels */
enum ioport_value {
IOPORT_PIN_LEVEL_LOW, /*!< IOPORT pin value low */
IOPORT_PIN_LEVEL_HIGH, /*!< IOPORT pin value high */
};
En resumen: sí y no.
El lenguaje C será casi el mismo independientemente de la arquitectura, aunque habrá algunas variaciones según el compilador específico que uses.
Sin embargo, el uso de una arquitectura de hardware diferente y su correspondiente cadena de herramientas le brindará inherentemente formas muy diferentes de usar los registros del hardware, los vectores de interrupción, etc. Sin embargo, no hay algunos tutoriales que no te ayuden. Buena suerte!
Un punto importante a tener en cuenta es que los diferentes compiladores intentarán "optimizar" el código con diferentes niveles de agresividad, en formas que serían permitidas bajo el Estándar C, pero harían a los compiladores inadecuados para procesar ciertos tipos de código a menos que incluye directivas específicas del compilador que no habrían sido necesarias al usar otros compiladores que "optimizan" de manera menos agresiva.
Por ejemplo, dado algo como:
// Simple polling function to acquire some data and store it, using the
// return value to indicate success. Note that there is no reason why
// this function should need to make "dest" volatile.
extern uint32_t volatile data_ready;
extern uint16_t volatile data_source;
int get_data(uint16_t *dest){
if (!data_ready) return 0;
*dest=data_source;
return 1;
}
// Set up and trigger outside interrupt, DMA, or other such process
// to output data and wait for that process to complete.
extern uint32_t volatile out_length;
extern uint16_t volatile * volatile out_ptr;
void write_data(uint16_t *buff, uint32_t len)
{
out_ptr = buff;
out_length = len;
do {} while(out_length);
}
// Sample code using the above. Note that if "buff" were volatile,
// its address could not be passed to "get_data" unless get_data took
// a volatile pointer, and making "buff" volatile would compel test()
// to do a couple of unnecessary extra stores to it.
uint16_t buff;
void test(void)
{
while (!get_data(&buff));
buff+=1;
write_data(&buff, 1);
while (!get_data(&buff));
buff+=1;
write_data(&buff, 1);
}
ARM gcc 6.3.0 ignorará el valor almacenado en "buff" en la primera llamada a get_data
si se invoca a -O2 o superior, ya que ve que se sobrescribirá por
la segunda llamada Lo hará incluso si se usa la opción -fno-strict-aliasing
. Para hacer que el código sea compatible con gcc, sería necesario hacer que buff
volatile (lo que a su vez requeriría que cualquier función que acepte punteros califique esos punteros como volatile
), use el nivel de optimización 0 o 1, o inserte una directiva __asm
para obligar al compilador a reconocer que las tiendas en ubicaciones volátiles pueden tener efectos secundarios en otros objetos.
Los autores de gcc adoptan la actitud de que el código tal como está escrito está roto, ya que la Norma no requiere que todos los compiladores, ni aquellos que no están diseñados para su uso en programas integrados o de sistemas, o sistemas de focalización donde no haya medios físicos existiría la posibilidad de que se produzcan efectos secundarios : tratar los accesos volátiles como posibles efectos secundarios en otros objetos. Personalmente creo que es la actitud de los autores de gcc la que está rota, pero dejaré esos juicios en manos del lector.
PD: si las funciones se escribieron como macros:
#define get_data(dest) (data_ready ? 0 : ((*dest=data_source),1))
#define write_data(buff,len) do {\
out_ptr=buff; out_length=len; do {} while(out_length); \
} while(0)
gcc rompería el código incluso en el nivel de optimización 1.
El código C correcto debería ser portátil si no depende de las especificaciones de la máquina o compilador. Por ejemplo, si desea un número entero de cierto tamaño, debe usar el encabezado stdint.h y no confiar en, por ejemplo. int
siendo 16 o 32 bits. Tampoco confíe en que la alineación se mantenga igual.
Por supuesto, los periféricos, las direcciones de memoria, etc., entre diferentes CPU pueden ser muy diferentes.
Lea otras preguntas en las etiquetas microcontroller embedded