Una breve introducción al desarrollo de aplicaciones de microcontroladores stm32

Muy a menudo las personas recurren a mí con una solicitud para ayudarlos a comenzar con los microcontroladores de la familia stm32. Respondiendo a sus preguntas y ayudándoles con sus proyectos, me di cuenta de que sería mejor escribir un artículo que fuera útil para todos los que quieran comenzar a programar microcontroladores stm32. A pesar de todas sus muchas características, los controladores stm32 tienen un umbral de entrada bastante alto, que para muchos no está disponible. En este artículo, intentaré darle al lector una guía detallada sobre cómo crear proyectos en stm32 y cómo organizar su programa.

Usando el microcontrolador stm32f103c8t6 y el módulo de píldora azul como ejemplo, veremos la estructura de un proyecto típico para el entorno de desarrollo IAR y crearemos un firmware que funcione.

Todos los que estén interesados ​​en comenzar a trabajar con stm32, bienvenidos a cat.

¿Qué necesitamos para seguir trabajando?


Todos los ejemplos que siguen se realizaron en el entorno de trabajo IAR Embedded para el entorno ARM v7.30. El entorno está instalado en Windows XP, que está instalado en la máquina virtual VirtualBOX lanzada desde Mac OS X El capitán. También se utiliza el programador ST-LINK, que se conecta a la tableta de pastillas azul comprada en AliExpress por ~ 120₽.

Para crear un proyecto típico necesitarás:

  • Módulo de píldora azul o similar

    Se ubican fácilmente en AliExpress con la solicitud "placa stm32f103c8t6" y cuestan alrededor de 100 rublos.

  • Programador ST-Link

    También está fácilmente disponible en AliExpress a pedido de "stlink v2" y también cuesta alrededor de 100 rublos.


    Hay una versión más completa, que está diseñada para conectar un conector de cinta estándar IDC20

  • Entorno de desarrollo IAR 7.30 o posterior

    La versión limitada 8.10 se puede descargar desde el sitio web oficial.
  • Una plantilla de proyecto que contiene todos los componentes necesarios.

    La plantilla se puede descargar aquí.
  • STDPeripheralLibrary3.5.0 Referencia de funciones

    El proyecto incluye una biblioteca de funciones StdPeriph3.5.0. Un poco viejo para proyectos serios, pero para principiantes es bastante simple y evita un dolor de cabeza cuando se trabaja con la periferia del microcontrolador. Se puede descargar el manual en formato WinHelp (CHM)
    Desde aqui
  • RM0008 Manual de referencia

    Manual de referencia para microcontroladores de la familia stm32f103c8. El libro de referencia contiene una descripción del núcleo y la periferia del microcontrolador, su arquitectura y descripciones de registros. Es aconsejable, con el tiempo, leer a lo largo y ancho y saber cómo funciona cada componente periférico. Si eres partidario de CMSIS desnudo, entonces sin este PDF no puedes hacerlo. Si eres el novato que usa Perlib, entonces aún necesitas leer cómo funciona este o aquel módulo periférico. Puede descargar la referencia del sitio web de STMicroelectonics

Plantilla de proyecto


La plantilla de proyecto adjunta no se tomó completamente de algún lugar de Internet, sino que se creó con nuestras propias manos a partir de ejemplos que fueron a PeripheralLibrary, archivos del paquete CMSIS y nuestras propias mejoras. La plantilla del proyecto no contiene archivos que pertenecen a ningún entorno de desarrollo y se puede utilizar para cualquiera de ellos (no lo he probado, pero supongo que sí).

Una descripción detallada de la estructura del directorio puede parecer demasiado complicada para los principiantes, sin embargo, estudiarla también será útil.

Estructura del directorio del proyecto
  • config
    stm32f10x_conf.h

    Este archivo pertenece al paquete StdPeripheral y contiene la inclusión de archivos de encabezado utilizados en el proyecto periférico del microcontrolador. Se supone que el programador comentará los archivos no utilizados, dependiendo del proyecto. Por defecto, todos los archivos están incluidos.
  • núcleo

    Este directorio contiene archivos CMSIS que son específicos del núcleo del procesador ARM CortexM3.
  • perlib

    Este directorio contiene los archivos de encabezado y el código fuente de la biblioteca Perlib en los directorios inc y src .
  • inicio

    Estos son los archivos con el código de inicialización principal del controlador, que instalan los controladores de interrupción del núcleo ARM y llaman a las funciones de inicialización del sistema de reloj del núcleo y la inicialización PLL. Cada tipo de microcontrolador tiene su propio archivo separado.

    El código en este archivo se ejecuta ANTES de que se llame a la función main () de su programa.
    Los controladores de interrupción del núcleo ARM, así como las funciones de inicialización del reloj, no se almacenan en estos archivos, solo se llaman. Y se almacenan en los archivos del directorio del sistema, que se discutirán más adelante.

    Por ejemplo, para que el microcontrolador stm32f103c8t6 funcione a 72 MHz, y no a los 8 MHz predeterminados, debe conectar el archivo startup_stm32f10x_md.s en el proyecto.
  • sistema

    Este directorio contiene archivos que contienen todas las funciones de la inicialización inicial del sistema, los controladores de interrupción del núcleo ARM, así como descripciones de los nombres y campos de los registros del microcontrolador.

    stm32f10x.h

    Este es un archivo del paquete CMSIS que contiene direcciones, nombres de registros y sus campos para la periferia del microcontrolador. Cada módulo periférico se presenta aquí como una estructura, cuyos campos son registros. También define constantes y máscaras de bits para los campos de registro.

    stm32f10x_it

    Los controladores de interrupción del kernel ARM se escriben aquí. Con la excepción del controlador SysTick, que utilicé para calcular los intervalos de tiempo, estos controladores de interrupción están vacíos. Si, de repente, su proyecto requiere el manejo de interrupciones del núcleo, entonces este archivo es solo para usted.

    No confunda el manejo de interrupciones del núcleo del procesador y el manejo de interrupciones periféricas. Las interrupciones periféricas, como un temporizador o USART, deben realizarse en sus propios archivos de proyecto, que usted mismo escribe y agrega a la raíz del directorio del proyecto.

    system_stm32f10x

    Estos dos archivos contienen las mismas funciones de inicialización del reloj del núcleo y las funciones del reloj PLL que se invocan desde el archivo de inicialización en el directorio de inicio. A saber, las funciones SystemInit y SystemCoreClockUpdate.


Crear un proyecto


Para crear nuestro primer proyecto, primero debe crear una carpeta para él. Llamémoslo EX01.
Lanzamos IAR y en el menú Proyecto , seleccione Crear nuevo proyecto .

Figura 1. Crear un nuevo proyecto


El valor de la cadena de herramientas debe dejarse en ARM, y seleccionamos la plantilla de proyecto C → principal. Luego, haga clic en Aceptar.

Figura 2. Guardar un proyecto vacío


Aparecerá una ventana para guardar el proyecto. En él, debe seleccionar la carpeta creada anteriormente. Vamos a nombrar el proyecto ex01. Ahora puede hacer clic en Guardar .

Como resultado, se creará un archivo principal vacío y un proyecto vacío, que aún no contiene ninguna configuración. El siguiente paso es guardar el espacio de trabajo para que no interfiera con nuestras preguntas en el futuro. Haga clic en Archivo → Guardar espacio de trabajo . Llámelo ex01 y haga clic en Guardar .

Figura 3. Guardar el espacio de trabajo


Ahora estamos listos para usar la plantilla del proyecto. Cópielo del archivo a nuestra carpeta EX01.

Figura 4. Copiar archivos de una plantilla a un nuevo proyecto


Los archivos principales que crearon la plantilla del proyecto deben reemplazarse con archivos del proyecto.

Después de copiar la plantilla del proyecto, debe configurar el proyecto en sí.

Para hacer esto, seleccione el menú Proyecto → Opciones . Se abrirá una ventana que contendrá una lista de categorías de opciones a la izquierda, cada una de las cuales tendrá ciertos marcadores.

Seleccione la categoría Opciones generales y la pestaña Destino en ella. En el grupo Variante del procesador , seleccione la opción Dispositivo y haga clic en el botón para seleccionar un dispositivo específico al lado. En nuestro caso, será ST → STM32F103 → ST STM32F103x8.

Figura 5. Selección del dispositivo de destino para el proyecto


La siguiente categoría que requiere nuestra atención es la categoría del compilador C / C ++ y la pestaña Preprocesador .

Figura 6. Configuración de marcadores del preprocesador


El bloque de directorios de inclusión adicional debe completarse con enlaces a los directorios de la plantilla del proyecto.

PreIncluir archivo, debe seleccionar el archivo de configuración de la biblioteca PerLib, y los símbolos definidos deben especificarse con STM32F10X_MD para que los archivos de inicialización establezcan el reloj del núcleo correcto y configuren correctamente el PLL.

Como usamos ST-Link v2 como programador, debe elegir el controlador que usará el entorno de desarrollador. Seleccione la categoría Depuración y la pestaña Configuración , en la que seleccione Controlador ST-Link en la lista desplegable.

Figura 7. Elección de una herramienta de depuración


Ahora necesita configurar la carga del firmware al controlador. Puede hacerlo en la misma categoría, en la pestaña Descargar . Estamos interesados ​​en las opciones de Verify download and Use flash loader .

Figura 8. Configuración de parámetros de carga de firmware en el microcontrolador


Como elegimos ST-Link como un medio para cargar firmware al controlador y depurarlo, debe configurar su controlador para que funcione con nuestro controlador. La placa Blue Pill no tiene un conector JTAG completo que pueda funcionar utilizando el protocolo JTAG completo. En su lugar, utilizaremos su modo simplificado, que se llama SWD. Este modo JTAG usa solo tres líneas. Estos son GND, SWDCLK y SWDIO. De manera predeterminada, el modo JTAG completo está habilitado, por lo que debemos cambiarlo a SWD y configurar la frecuencia central a 72MHz.
En la lista de categorías, seleccione ST-LINK y cambie la opción en el grupo Interfaz a SWD.

Ahora puede hacer clic en Aceptar , nuestro proyecto está configurado.

Figura 9. Configuración del controlador ST-LINK


Ahora queda por hacer la última, pero muy importante acción, antes de comenzar a compilar nuestro primer programa. Debe agregar archivos de la plantilla al proyecto.

Los archivos se agregan al proyecto en el panel Espacio de trabajo. Ya hay un archivo principal allí, sin embargo, necesitamos agregar PerLib, así como uno de los archivos de inicialización del directorio de inicio y los archivos de inicialización del directorio del sistema. Podríamos lanzar un montón, pero luego tendríamos que sufrir y si el proyecto crece demasiado, entonces ese basurero será muy difícil.

El entorno de desarrollo IAR le permite crear grupos de archivos. Los grupos son un concepto puramente virtual. Solo le permiten organizar archivos en un proyecto. Los grupos no tienen nada que ver con los directorios del disco.

Crearemos grupos para cada directorio desde la plantilla del proyecto y colocaremos los archivos de la plantilla allí.

Primero, cree el grupo de configuración y coloque el archivo stm32f10x_conf.h del directorio de configuración en él.
Para hacer esto, haga clic derecho en el nombre del proyecto en la ventana del Espacio de trabajo y seleccione Agregar → Agregar grupo en el menú desplegable. Nombre del grupo Config.

Para agregar archivos a este grupo, haga clic derecho sobre él y seleccione Agregar → Agregar archivos. En la ventana que se abre, abra la carpeta de configuración y seleccione el archivo stm32f10x_conf.h.



Del mismo modo, agregamos el contenido de las carpetas Perlib, Startup y System. No necesita agregar la carpeta Core, solo hay archivos de encabezado que están disponibles en la biblioteca Perlib agregada.

Figura 11. Vista completa de un proyecto vacío y totalmente configurado


Ahora el proyecto está completamente listo para un mayor desarrollo.

Poco de teoría


Muchos programadores principiantes están acostumbrados al hecho de que el programa consiste en un ciclo en el que las funciones se llaman una tras otra. La siguiente función se llama solo después de que la anterior haya completado completamente su trabajo. Este paradigma lo ofrece Arduino o varios artículos con lecciones para principiantes. Sin embargo, los proyectos grandes rara vez tienen un solo subproceso. Como regla general, un firmware más o menos serio puede tener varios subprocesos.

Los microcontroladores, para organizar subprocesos múltiples, utilizan sistemas operativos en tiempo real (RTOS), como ThreadX o FreeRTOS. Todos ellos le permiten crear muchos de esos ciclos en los que las funciones se ejecutan una tras otra, solo los ciclos funcionan al mismo tiempo. Al igual que varios Arduins embistieron en un microcontrolador.

Con todo su poder, el RTOS introduce ciertas dificultades. Por ejemplo, cada hilo tiene su propia pila, su propia área de memoria. Si varios subprocesos necesitan acceder a la misma ubicación de memoria, deben sincronizar sus acciones utilizando mutexes o semáforos. El uso incorrecto de los objetos de sincronización puede conducir a puntos muertos o inversiones de prioridades de subprocesos. Además, el procesamiento de las interrupciones desde la periferia también requiere atención especial en un entorno de subprocesos múltiples, ya que surge el problema de guardar la pila y elegir las condiciones bajo las cuales la llamada de interrupción no destruye la pila de flujo interrumpido. Y el controlador de interrupciones en sí también debe funcionar hasta el final.

El RTOS asigna un pequeño intervalo de tiempo a cada subproceso. Después de este intervalo, el RTOS cambia a la siguiente secuencia (no importa si la anterior ha completado sus acciones o no) y así sucesivamente. Las diferentes transmisiones pueden recibir diferentes intervalos de tiempo, según su prioridad. Este tipo de subprocesamiento múltiple se denomina desplazamiento.

Para ordenar los flujos y transferirles el control durante un breve intervalo de tiempo, el componente RTOS, denominado "programador", es responsable.

Es difícil para los programadores novatos dominar de inmediato los enormes y complejos periféricos de stm32 y al mismo tiempo también aprender RTOS.

Afortunadamente, hay formas de hacer aplicaciones multiproceso sin ningún tipo de RTOS. Para esto, el "multihilo cooperativo" viene en nuestra ayuda. El multiproceso cooperativo le permite realizar proyectos multiproceso relativamente pequeños sin la participación de RTOS.

¿Cuál es la esencia del multihilo cooperativo? Con este subprocesamiento múltiple, cada subproceso requiere tanto tiempo de procesador como sea necesario, pero no lo suficiente para completar toda su tarea a la vez. Esto presenta requisitos muy estrictos para el estilo de escritura de las aplicaciones cooperativas de subprocesos múltiples.

El multihilo cooperativo tiene varias ventajas y desventajas. La elección del paradigma de subprocesos múltiples depende completamente del desarrollador y los requisitos de la tarea que realiza.
Las principales ventajas de los subprocesos múltiples cooperativos son la ausencia de un planificador, una sola pila para todos los subprocesos, la ausencia de la necesidad de sincronizar subprocesos y la simplicidad de las interrupciones de procesamiento desde la periferia.

Desafortunadamente, también hay desventajas. En particular, la congelación de uno de los hilos conducirá a la congelación de todo el programa en su conjunto. Además, la ortografía incorrecta de uno o más subprocesos puede provocar un retraso en la ejecución del resto. Y esta no es una lista completa.

Estructura cooperativa de aplicación multiproceso


Las aplicaciones cooperativas de subprocesos múltiples se basan en una máquina de estado. No lo describiré en detalle, ya que aquí ya se describe en detalle. Sin embargo, explicaré brevemente la esencia. Una máquina de estados es un objeto abstracto, cuyo número de estados es finito. Un objeto pasa de un estado a otro, ya sea bajo la influencia de factores externos o debido a procesos internos. En nuestro caso, un subproceso de aplicación cooperativa es una implementación de una máquina de estados.

Una secuencia tiene una lista de estados. En cada estado, se realiza una acción corta o nada. La conmutación de estados puede llevarse a cabo llamando a funciones externas o cuando surgen condiciones en las que ya no es posible encontrar el flujo en el estado actual y se requiere la conmutación a otro estado.

Durante varios años de trabajo con microcontroladores stm32, he desarrollado una estructura de una aplicación cooperativa de subprocesos múltiples, que quiero presentarles.

Cada secuencia es un módulo separado (archivo de encabezado y archivo de código).

El módulo tiene funciones públicas, cuyos prototipos están registrados en el archivo de encabezado y privados, cuya llamada es imposible desde el exterior. Cada módulo tiene al menos dos funciones públicas:

void XXX_Init(); void XXX_Control(); 

La función XXX_Init () se llama antes del bucle principal en la función main (), y la función XXX_Control () se llama en el bucle principal de la función principal.

 void main() { //   XXX XXX_Init(); //   YYY YYY_Init(); //   ZZZ ZZZ_Init(); while(true){ XXX_Control(); YYY_Control(); ZZZ_Control(); } } 

Un archivo de módulo XXX podría verse así:

xxx.c

 #include "xxx.h" #define XXX_WATER_MAX_THRESHOLD 100500 #define XXX_WATER_MIN_THRESHOLD 9000 typedef enum{ idle, state1, state2, : stateX, }XXX_States; static XXX_States xxxCurrentState = idle; static int xxxToiletWaterLevel=0; //---------    -------- void private_init1() { } void private_init2() { } void private_measureLevel() { } void private_flush() { } void private_superFlush() { } //--------    --------- void XXX_Init() { xxxCurrentState=idle; } void XXX_Reset() { private_superFlush(); xxxCurrentState=idle; } void XXX_Control() { switch(xxxCurrentState) { case idle: private_measureLevel(); if(xxxToiletWaterLevel>XXX_WATER_MIN_THRESHOLD) xxxCurrentState=state1; break; case state1: if(xxxToiletWaterLevel<XXX_WATER_MAX_THRESHOLD) { private_flush(); xxxCurrentState=idle; } else xxxCurrentState=state2; break; case state2: //   break; } } 

Ejemplo de aplicación cooperativa de subprocesos múltiples


Para no ser demasiado abstracto, imaginemos un verdadero desafío. Supongamos que tenemos un flujo que parpadea un LED (dos veces por segundo), que está conectado por un cátodo al puerto PC13. También tenemos una secuencia que recibe comandos a través del puerto serie. Si llega el carácter '0' (0x30), el parpadeo se detiene y se envía un signo '-' al cliente. Si llega el carácter '1' (0x31), el parpadeo se enciende y el carácter '*' se devuelve al cliente. Al presionar cualquier otra tecla, se devuelve el carácter 'E'.

Colocaremos el flujo de control del LED que parpadea en los archivos modLed.h y modLed.c. Este hilo está inicialmente en estado inactivo y no hace nada. Sin embargo, su función pública MODLED_command, cuando se recibe el argumento modled_on, cambia el estado de la secuencia a
modled_st_on. En este estado, el flujo enciende el LED, recuerda el valor inicial del contador global_count y entra en el estado de espera modled_st_wait1. En este estado, comprueba constantemente el valor actual del contador global_count, y cuando la diferencia entre la cuenta actual y la cuenta inicial es MODLED_BLINK_DELAY_ON, la secuencia cambia al estado modled_st_off. En este estado, la transmisión apaga el LED, recuerda el valor actual de la cuenta y cambia al estado modled_st_wait2. En este estado, el subproceso también compara el valor actual del contador global_count con el inicial, y cuando la diferencia es MODLED_BLINK_DELAY_OFF pasa al estado modled_st_on. Y así continuará hasta que alguien llame a la función MODLED_command con el argumento modled_off. Luego, la función cambiará el estado de la secuencia a modled_st_clamp. El hilo apagará el LED y pasará al estado modled_st_idle.

La inicialización del flujo modelado comienza en la función principal llamando a la función MODLED_init (). Esta función inicializa el puerto GPIOC y establece el estado inicial de la secuencia. Luego, en el bucle, se llama constantemente a la función MODLED_control (), que en una iteración realiza una verificación del estado actual y realiza pequeñas acciones para ello.

El flujo de control del puerto serie es idéntico.

Tiene funciones privadas de inicialización de puertos GPIO y módulo USART1. Además, dentro está oculto el controlador de interrupciones del módulo periférico USART1, en el que se almacena el byte recibido actual y el estado de la secuencia se establece en moduart_st_command.

Inicialmente, la secuencia de moduart está en el estado moduart_st_idle, en el que está esperando que se reciba un byte. Tan pronto como se recibe un byte y se almacena en una variable, el controlador de interrupción cambia el estado del flujo a moduart_st_command y el flujo verifica el byte recibido. Si el byte recibido es el comando '0', se llama a la función MODLED_command con el argumento modled_off y se devuelve el carácter '-'. Si el byte recibido es el comando '1', se llama a la función MODLED_command con el argumento modled_on y se devuelve el carácter '*'. En otros casos, el carácter 'E' simplemente se devuelve.

La inicialización de la secuencia MODUART también se produce en el archivo principal, llamando a la función MODUART_init (). Esta función inicializa el puerto y el módulo periférico USART1 y pone la transmisión en modo de espera. En el bucle principal, se llama a la función de control del flujo MODUART_control (), que verifica el estado actual y ejecuta un pequeño fragmento de código asociado con su procesamiento.

Todo el secreto de las aplicaciones cooperativas de subprocesos múltiples es precisamente crear pequeñas piezas de código para cada estado.

Global_count variable

Probablemente valga la pena una discusión por separado sobre esta variable global_count.

El archivo de inicialización startup \ startup_stm32f10x_md.s contiene una tabla de interrupción del microcontrolador. Contiene las direcciones de los manejadores para todas las interrupciones periféricas.
y granos. Sin embargo, las interrupciones en la periferia solo ocurren cuando la periferia se inicializa. Por lo tanto, inicialmente, los controladores apuntan a apéndices temporales. Pero los controladores de interrupción del núcleo Cortex M3 realmente existen y están contenidos en el archivo system \ stm32f10x_it . Una de esas interrupciones es la interrupción del temporizador del sistema SysTick. RTOS utiliza este temporizador para invocar el programador de tareas. Pero, lo uso para llamar a la función TimingDelay_Decrement, que está realmente definida en el archivo principal.

 //------------------------------------------------------------------- //   volatile unsigned long global_count=0; //       SysTick,  //    stm32f10x_it.c void TimingDelay_Decrement(void) { //    global_count++; if (TimingDelay != 0x00) { TimingDelay--; } } 

Al comienzo de la función principal está configurando la frecuencia del temporizador SysTick a 1 ms. Por lo tanto
cada milésima de segundo en el controlador de interrupción SysTick, el contador aumentará.

Basta recordar los valores de este contador y su diferencia dará el intervalo de tiempo entre comprobaciones en milisegundos. Por lo tanto, un contador puede usarse para soportar intervalos de tiempo en cualquier cantidad de hilos sin llamar a un Retraso de bloqueo.

Epílogo


Es posible que existan soluciones más simples para conectar PeripheralLib y los archivos de inicialización del reloj inicial. Por ejemplo, las opciones en la configuración del proyecto de otros entornos de desarrollo o constantes que obligan al entorno de desarrollo a cargarlas automáticamente al compilarlas. Sin embargo, este método, que cité aquí como ejemplo, es bastante visual en sí mismo y permite, si es necesario, cambiar los parámetros de inicialización del microcontrolador con bastante rapidez. Por ejemplo, rehaga el reloj desde el generador interno.

En comparación con otras "luces intermitentes de LED en stm32", que se encuentran en Internet, la mía resultó ser bastante engorrosa. Sin embargo, en una plantilla así, comienzo proyectos nuevos y complejos y pasar 2-5 minutos creándolo no me parece una pérdida tan terrible.

El código fuente del archivo modLed.h
Archivo ModLed.h
 #ifndef __MODLED_H #define __MODLED_H #include "stm32f10x.h" typedef enum{ modled_off, modled_on, }MODLED_Commands; void MODLED_init(); void MODLED_command(MODLED_Commands aCmd); void MODLED_control(); #endif 


El código fuente del archivo modLed.c
Archivo ModLed.c
 #include "modLed.h" #include "main.h" //      #define MODLED_BLINK_DELAY_ON 250 #define MODLED_BLINK_DELAY_OFF 250 //   typedef enum{ modled_st_idle, modled_st_on, modled_st_wait1, modled_st_off, modled_st_wait2, modled_st_clamp, }MODLED_States; //  ,    1  . extern unsigned long global_count; static MODLED_States modledState=modled_st_idle; static uint32_t modledStart, modledEnd; /* PC13 - led (Open drain) */ void modled_init_gpio() { //    GPIOC RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_DeInit(GPIOC); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); /*  Blue Pill    ,   PC13. :  PC13      (3 )           .    . */ //   ,  . gpio.GPIO_Mode=GPIO_Mode_Out_OD; gpio.GPIO_Speed=GPIO_Speed_2MHz; gpio.GPIO_Pin=GPIO_Pin_13; GPIO_Init(GPIOC, &gpio); //   GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); } void MODLED_init() { modled_init_gpio(); modledState=modled_st_idle; modledStart=global_count; } //   . void MODLED_command(MODLED_Commands aCmd) { switch(aCmd) { case modled_on: modledState=modled_st_on; break; case modled_off: modledState=modled_st_clamp; break; } } void MODLED_control() { switch(modledState) { case modled_st_idle: break; case modled_st_on: //     GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); //     modledStart=global_count; //       modledState=modled_st_wait1; break; case modled_st_wait1: //    modledEnd=global_count; //           if((modledEnd-modledStart)>=MODLED_BLINK_DELAY_ON) { //         modledState=modled_st_off; } break; case modled_st_off: //   GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); //     modledStart=global_count; //       modledState=modled_st_wait2; break; case modled_st_wait2: //    modledEnd=global_count; //           if((modledEnd-modledStart)>=MODLED_BLINK_DELAY_OFF) { //         modledState=modled_st_on; } break; case modled_st_clamp: //       GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); modledState=modled_st_idle; break; default: modledState=modled_st_idle; } } 


El código fuente del archivo modUart.h
Archivo ModUart.h

 #ifndef __MOD_UART_H #define __MOD_UART_H #include "stm32f10x.h" void MODUART_init(); void MODUART_control(); #endif 


El código fuente del archivo modUart.c
Archivo ModUart.c
 #include "modUart.h" #include "modLed.h" #define MODUART_BAUDRATE 115200 typedef enum{ moduart_st_idle, moduart_st_command, }MODUART_STATES; static MODUART_STATES moduartState=moduart_st_idle; static uint16_t moduartCmd=0; /* PA9 UART1_TX PA10 UART1_RX */ void moduart_init_gpio() { //RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //    GPIOA RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_Mode=GPIO_Mode_AF_PP; gpio.GPIO_Speed=GPIO_Speed_2MHz; gpio.GPIO_Pin=GPIO_Pin_9; GPIO_Init(GPIOA, &gpio); gpio.GPIO_Mode=GPIO_Mode_IN_FLOATING; gpio.GPIO_Pin=GPIO_Pin_10; GPIO_Init(GPIOA, &gpio); } void moduart_init_uart1() { //     UART1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); USART_InitTypeDef uart; USART_StructInit(&uart); uart.USART_BaudRate=MODUART_BAUDRATE; uart.USART_HardwareFlowControl=USART_HardwareFlowControl_None; uart.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; uart.USART_Parity=USART_Parity_No; uart.USART_StopBits=USART_StopBits_1; uart.USART_WordLength=USART_WordLength_8b; USART_Init(USART1, &uart); //     // --     USART1 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //         NVIC_EnableIRQ(USART1_IRQn); //   USART1. USART_Cmd(USART1, ENABLE); } //    USART1 void USART1_IRQHandler() { //    if(USART_GetITStatus(USART1, USART_IT_RXNE)!=RESET) { //    USART_ClearITPendingBit(USART1, USART_IT_RXNE); //    moduartCmd = USART_ReceiveData(USART1); moduartState = moduart_st_command; } } void moduart_processCmd() { //   uint16_t r = 'E'; switch(moduartCmd) { //    case '0': { MODLED_command(modled_off); r = '-'; } break; //    case '1': { MODLED_command(modled_on); r = '*'; } break; } //    USART_SendData(USART1, r); moduartCmd=0; } void MODUART_init() { moduartState=moduart_st_idle; moduart_init_gpio(); moduart_init_uart1(); } void MODUART_control() { switch(moduartState) { case moduart_st_idle: break; case moduart_st_command: moduart_processCmd(); moduartState=moduart_st_idle; break; default: moduartState=moduart_st_idle; } } 


El código fuente del archivo main.c
Archivo main.c
 #include "main.h" #include "modUart.h" #include "modLed.h" // Imported value static __IO uint32_t TimingDelay; RCC_ClocksTypeDef RCC_Clocks; int main() { RCC_GetClocksFreq(&RCC_Clocks); //   SysTick      SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000); //    MODLED_init(); //     MODUART_init(); do{ //      MODLED_control(); //      MODUART_control(); }while(1); #pragma diag_suppress=Pe111 return 0; } //------------------------------------------------------------------- void Delay(__IO uint32_t nCount) { TimingDelay = nCount; while(TimingDelay != 0); } //------------------------------------------------------------------- //   volatile unsigned long global_count=0; //       SysTick,  //    stm32f10x_it.c void TimingDelay_Decrement(void) { //    global_count++; if (TimingDelay != 0x00) { TimingDelay--; } } //------------------------------------------------------------------- #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { while(1){} } #endif 


El proyecto completo se puede descargar desde aquí .

Source: https://habr.com/ru/post/es407045/


All Articles