Red de conocimiento informático - Material del sitio web - Cómo implementar programación modular en un microcontrolador

Cómo implementar programación modular en un microcontrolador

Descubramos el misterio de la modularidad y vislumbremos su verdadera cara. Archivos fuente en lenguaje C *.c Cuando se trata de archivos fuente en lenguaje C, todos están familiarizados con ellos. Porque casi todos los códigos de programa que escribimos habitualmente están en este archivo XX.C. El compilador también utiliza este archivo para compilar y generar el archivo objeto correspondiente. Como base de la programación modular, en este archivo se encuentra el código fuente de todas las funciones que queremos implementar. Lo ideal sería pensar en la modularidad como una caja negra. Es decir, solo nos preocupamos por las funciones proporcionadas por el módulo, independientemente de los detalles de implementación dentro del módulo. Es como cuando compramos un teléfono móvil, solo necesitamos poder utilizar las funciones que proporciona el teléfono móvil, no necesitamos saber cómo envía mensajes de texto o cómo responde a nuestras entradas clave para nosotros, los usuarios. Estos procesos son sólo una caja negra. En el desarrollo de programas a gran escala, un programa consta de muchos módulos y es probable que las tareas de redacción de estos módulos se asignen a diferentes personas. Al escribir este módulo, probablemente necesite usar la excusa de un módulo escrito por otros. En este momento, lo que nos importa es qué tipo de interfaz implementa su módulo, cómo debo llamarlo y cómo funciona dentro del mismo. módulo? Organizado, para mí, no requiere demasiada atención. Buscar la unidad de la interfaz y proteger los detalles innecesarios del exterior tanto como sea posible es a lo que debemos prestar atención. Archivo de encabezado en lenguaje C *.h Cuando se habla de programación modular, inevitablemente implicará una compilación de varios archivos, es decir, una compilación de proyectos. En un sistema de este tipo, suele haber varios archivos C y cada archivo C tiene una función diferente. En nuestro archivo C, dado que necesitamos proporcionar una interfaz para el mundo exterior, debe haber algunas funciones o variables que se puedan proporcionar a otros archivos externos para llamadas. Supongamos que tenemos un archivo LCD.C que proporciona la función de controlador LCD más básica LcdPutChar(char cNewValue); // Genera un carácter en la posición actual y necesitamos llamar a esta función en otro de nuestros archivos, entonces, ¿qué debemos hacer? ¿paño? Aquí es donde entra en juego el archivo de encabezado. Puedes llamarlo documento de descripción de la interfaz. El archivo no debe contener ningún código de función sustancial. Podemos entender este archivo de encabezado como un manual de instrucciones, que describe las funciones de interfaz o variables de interfaz proporcionadas por nuestro módulo. Al mismo tiempo, este archivo también contiene algunas definiciones de macros muy importantes y cierta información de estructura. Sin esta información, es probable que las funciones o variables de la interfaz no se puedan utilizar normalmente. Pero el principio general es: la información que el mundo exterior no debe conocer no debe aparecer en el archivo de encabezado, y la información necesaria para que el mundo exterior llame a las funciones de interfaz o variables de interfaz en el módulo debe aparecer en el archivo de encabezado. De lo contrario, el mundo exterior no podrá llamar correctamente a la función de interfaz que proporcionamos. Por lo tanto, para que las funciones o archivos externos llamen a las funciones de interfaz que proporcionamos, deben incluir el archivo de descripción de la interfaz que proporcionamos, es decir, el archivo de encabezado. Al mismo tiempo, nuestro propio módulo también debe incluir este archivo de encabezado del módulo (porque contiene las definiciones de macro o estructuras requeridas en el archivo fuente del módulo). Al igual que los archivos que usamos habitualmente están por triplicado, el módulo en sí también necesita Incluir. este archivo de encabezado. A continuación, definimos este archivo de encabezado. En términos generales, el nombre del archivo de encabezado debe ser coherente con el nombre del archivo fuente, para que podamos saber claramente qué archivo de encabezado describe qué archivo fuente. Entonces obtuvimos el archivo de encabezado LCD.h de LCD.C y su contenido es el siguiente. #ifndef _LCD_H_ #define _LCD_H_ extern LcdPutChar(char cNewValue); #endif Esto es algo similar a cuando definimos una función en el archivo fuente. La diferencia es que agregar el modificador externo delante indica que es una función externa y puede ser llamada por otros módulos externos. #ifndef _LCD_H_ #define _LCD_H_ #endif Estas definiciones de macros y compilación condicional tienen como objetivo evitar la inclusión repetida.

Si hay dos archivos fuente diferentes que necesitan llamar a la función LcdPutChar(char cNewValue), ambos incluyen este archivo de encabezado a través de #include "Lcd.h". Cuando se compila el primer archivo fuente, dado que _LCD_H_ no se ha definido, se establece la condición #ifndef _LCD_H_, por lo que se define _LCD_H_ y se incluye la siguiente declaración. Cuando se compila el segundo archivo, _LCD_H_ ya se ha definido desde que se incluyó el primer archivo. Por lo tanto, #ifndef _LCD_H_ no se mantiene y no se incluye todo el contenido del archivo de encabezado. Suponiendo que no existe tal declaración de compilación condicional, ambos archivos contienen LcdPutChar(char cNewValue) externo, lo que provocará un error de inclusión duplicada. Tengo que decir que muchos amigos parecen estar acostumbrados a usar las siguientes declaraciones para definir tipos de datos en programas: #define uint unsigned int #define uchar unsigned char y luego usa directamente uint g_nTimeCounter = 0 al definir variables. De hecho, este es el caso, es muy conveniente y también es conveniente para el trasplante. ¿Pero todavía lo pensarías considerando la siguiente situación? #define PINT unsigned int * //Definir el tipo de puntero int sin signo PINT g_npTimeCounter, g_npTimeState Entonces, ¿define dos variables de puntero de tipo int sin signo, o una variable de puntero y una variable entera? ¿Y cuál es su intención original? ¿Quiere definir dos variables de puntero de tipo int sin signo? Si este es el caso, probablemente pronto buscará errores en todas partes. Afortunadamente, el lenguaje C ha tenido esto en cuenta para nosotros. Esto es exactamente para lo que sirven los typedefs. Para darle un alias a una variable, podemos usar la siguiente declaración typedef unsigned int uint16; //Da un alias uint16 al puntero de la variable entera sin signo typedef unsigned int * puint16 //Dale un alias puint16 al puntero. a la variable entera sin signo En nuestro Al definir una variable, puede definirla de esta manera: uint16 g_nTimeCounter = 0; // Definir una variable entera sin signo puint16 g_npTimeCounter // Definir un puntero a una variable entera sin signo. Programación en lenguaje C del microcontrolador 51, el rango de variables enteras es de 16 bits, mientras que las variables enteras en microprocesadores basados ​​en 32 son de 32 bits. Si algún código que escribimos bajo un microcontrolador de 8 bits quiere ser trasplantado a un procesador de 32 bits, entonces es muy probable que necesitemos modificar las definiciones de tipo de variables en todas partes del archivo fuente. Esta es una tarea enorme. Para considerar la portabilidad del programa, debemos desarrollar buenos hábitos al principio y utilizar alias de variables para definirlos. Por ejemplo, en la plataforma del microcontrolador de 8 bits, existe la siguiente definición de variable uint16 g_nTimeCounter = 0; si se trasplanta a la plataforma del microcontrolador de 32 bits, aún se espera que el rango sea de 16 bits. Puede modificar directamente la definición de uint16, es decir, typedef unsigned short int uint16, esto es suficiente, sin tener que buscar y modificar el archivo fuente en todas partes. Todos los tipos de datos de uso común se definen de esta manera para formar un archivo de encabezado, al que nos resulta conveniente llamar directamente en futuras programaciones.

El nombre del archivo MacroAndConst.h tiene el siguiente contenido: #ifndef _MACRO_AND_CONST_H_#define _MACRO_AND_CONST_H_typedef unsigned int uint16; typedef unsigned int UINT; typedef unsigned int word; t int16; typedef int INT16; typedef unsigned long uint32; typedef unsigned long DWORD; typedef unsigned char byte; BYTE; typedef unsigned char uchar; typedef unsigned char UINT8; typedef unsigned char uint8; typedef unsigned char BOOL #endif En este punto, parece que tenemos una pequeña idea de la división del trabajo entre los archivos fuente y los archivos de encabezado. programación modular. Entonces, golpeemos mientras el hierro está caliente, dividamos la función de parpadeo del LED que escribimos en el capítulo anterior en módulos, reorganicémosla y compilémosla. En el capítulo anterior, la función principal que completamos fue que el LED impulsado por el puerto P0 parpadea a una frecuencia de 1Hz. Se utilizan un temporizador y un módulo controlador LED. Por lo tanto, podemos simplemente dividir todo el proyecto en tres módulos, el módulo del temporizador, el módulo LED y la relación de archivos correspondiente a la función principal de la siguiente manera: main.c Timer.c --?Timer.hLed.c --? Led.h En este punto, el Capítulo 3 termina aquí.