Download Curso introductorio a la arquitectura ARM con Cortex M4

Document related concepts
no text concepts found
Transcript
Curso introductorio a la
arquitectura ARM con
Cortex M4
Microcontrolador STM32F407VG
Firtec Capacitación Técnica
Realizado y editado por:
`
Calle San Lorenzo 2755 – Rosario (Santa Fe)
(S2000KPK) ARGENTINA
e-mail: [email protected]
[email protected]
Revisión: Julio 2015
2
Índice de contenido
Prologo.......................................................................................................................................................5
Capitulo I....................................................................................................................................................6
Historia de la Arquitectura ARM...........................................................................................................6
Que es Cortex M4..................................................................................................................................8
Algunos detalles del STM32F407VG...................................................................................................9
Características heredadas de RISC......................................................................................................11
Algunas ventajas de RISC...................................................................................................................11
Desventajas de RISC...........................................................................................................................11
Bus AMBA..........................................................................................................................................11
Pipeline................................................................................................................................................12
FPU......................................................................................................................................................14
ARM y Thumb.....................................................................................................................................14
Modos de Funcionamiento..................................................................................................................14
Modo usuario (Thread ).......................................................................................................................15
Modos de Privilegios (Handler)..........................................................................................................15
El sistema de memoria ARM...............................................................................................................16
Que es CMSIS.....................................................................................................................................18
Características de la placa entrenadora................................................................................................21
Configurando el entorno de trabajo.....................................................................................................26
Puedo programar el microcontrolador sin un programador específico?.............................................30
Mi Primer Programa en KEIL.............................................................................................................31
Capitulo II................................................................................................................................................41
Interrupciones......................................................................................................................................41
Temporizador del sistema (SysTick)...................................................................................................48
Funcionamiento de la USART.............................................................................................................57
Conversor Analógico con STM32F407VG.........................................................................................64
Capitulo III...............................................................................................................................................67
Pantalla LCD 16x2 con STM32..........................................................................................................67
Voltímetro con pantalla LCD 16x2.................................................................................................69
Midiendo la temperatura del Núcleo Cortex..................................................................................71
Canales DMA......................................................................................................................................77
Modo DMA de doble buffer................................................................................................................90
Emular memoria EEPROM en FLASH..............................................................................................90
Protocolo I2C......................................................................................................................................93
Puerto SDIO con STM32..................................................................................................................103
Memoria SD con FAT........................................................................................................................108
Ejemplo de manejo de FAT con STM32.......................................................................................115
Creando un disco extraíble a partir de una memoria SD..............................................................119
Control PID.......................................................................................................................................121
Funcionamiento general de un PID..............................................................................................122
Control PID con STM32F407......................................................................................................122
Datalogger de temperatura................................................................................................................127
Capitulo IV.............................................................................................................................................134
FSMC (Flexible Static Memory Controller)....................................................................................134
Pantallas TFT................................................................................................................................134
3
Sensor de imagen OV7670 con STM32F407...............................................................................141
Formato de la imagen...................................................................................................................142
RGB..............................................................................................................................................142
Señales de la camara.....................................................................................................................144
SCCB (Serial Camera Control Bus).............................................................................................146
Estructura del proyecto DCMI_OV7670......................................................................................148
Manejo del Touch-Screen.............................................................................................................151
Protocolo 1-wire................................................................................................................................157
Niveles eléctricos del bus.............................................................................................................157
Envío y recepción de datos...........................................................................................................158
Ejemplo con 1-Wire y el sensor DS18B20 & UART...................................................................158
Ejemplo con 1-Wire y el sensor DS18B20 & LCD TFT..............................................................162
Ethernet y MII (Media Independent Interface).................................................................................165
Señales del transmisor Ethernet....................................................................................................165
Señales del receptor Ethernet........................................................................................................165
Reducción Media Independent Interface (RMII).........................................................................166
El stackt LwIP controlando LED´s mediante CGI.......................................................................168
SSI (Server Side Include).............................................................................................................172
CAN BUS ( Controller Area Network).............................................................................................176
CAN BUS Loop Back (Sin la capa física)...................................................................................178
CAN NORMAL (Con la capa física)...........................................................................................186
Bibliografía............................................................................................................................................194
Carpetas con Ejemplos (Opcional).........................................................................................................195
Carpeta con Herramientas (Opcional)....................................................................................................196
4
Características heredadas de RISC.
La arquitectura ARM incorporó algunas características del diseño RISC de Berkeley, aunque no todas.
Las que se mantuvieron son:
• Arquitectura de carga y almacenamiento(load-store). Las instrucciones que acceden a memoria
están separadas de las instrucciones que procesan los datos, ya que en este último caso los datos
necesariamente están en registros.
• Instrucciones de longitud fija de 32 bits. Campos de instrucciones uniforme y de longitud fija
para simplificar la decodificación de las instrucciones.
• Formatos de instrucción de 3 direcciones. Consta de “f” bits para el código de operación, “n”
bits para especificar la dirección del 1er. operando, “n” bits para especificar la dirección del
2do. operando y “n” bits para especificar la dirección del resultado (el destino).
Algunas ventajas de RISC.
•
•
•
Menor desperdicio de área de silicio. Un procesador simple economiza transistores y área de
silicio. En consecuencia una CPU RISC deja mayor área libre para realizar mejoras de
rendimiento, tales como, memoria caché, funciones de manejo de memoria, punto flotante por
hardware,etc.
Menor tiempo de desarrollo. Un procesador simple tiene menor costo y lleva menos esfuerzo de
diseño, se adapta mejor a sistemas de tecnología de procesos.
Mayor rendimiento. Si se diseña un procesador simple y luego se le agregan instrucciones
complejas hará en forma más eficiente varias funciones de alto nivel pero también decaerá un
poco el reloj para el conjunto de las instrucciones. Midiendo los beneficios de esta técnica en
programas típicos se comprueba que todos los sets de instrucciones complejos hacen que el
programa corra a menor velocidad.
Desventajas de RISC.
•
•
No ejecuta códigos x86.Pero hay programas de emulación para varias plataformas RISCs.
Pobre densidad de código. Comparada con CISC. Ésto es consecuencia del set de instrucciones
de longitud fija. En ausencia de memoria caché, esta pobre densidad de código significa que la
búsqueda de la instrucción necesita un aumento del ancho de banda de la memoria principal,
dando por resultado un mayor consumo.
Bus AMBA.
El significado de esta sigla es Advanced Microcontroller Bus Architecture. La misma se refiere a un
standard de facto, abierto, que facilita la interconexión de bloques de propiedad intelectual para formar
“Sistemas On Chip”, es decir, circuitos integrados formados por varios procesadores y periféricos,
interconectados en un bus común. Los procesadores ARM utilizan esta arquitectura para interconexión
de los diferentes bloques internos que forman el chip.
Podemos ver en el gráfico anterior que en el procesador hay tres buses con tres velocidades distintas.
•
•
•
AHB1 corriendo a 168Mhz.
APB2 corriendo a 84Mhz.
APB1 corriendo a 42Mhz.
11
Esto último es muy importante porque marca una de las grandes diferencias con un microcontrolador
de 8 bits o de arquitectura “tradicional” en donde podemos tener la certeza de que cada operación dura
un determinado tiempo o ciclo de CPU lo que lleva a que los tiempos en juego se pueden determinar
con facilidad. En un micro de 32 bits hay varios buses y los tiempos ya no son tan fáciles de predecir ya
que otros periféricos pueden estar usando estos buses o si el micro tiene memoria cache esto altera los
tiempos en juego por lo que debe usted desterrar la idea que solo basta con contar las instrucciones y
multiplicar por la velocidad del bus.
Lo siguiente que puede resultar un poco confuso es que todo dentro del Cortex tiene su reloj individual
que por defecto esta desconectado.
Es decir entonces que para hacer uso de un módulo una de las configuraciones que debemos incluir es
activar y el reloj y determinar una frecuencia de operación dentro del rango que el bus admite.
Pipeline.
Se llama “pipeline” a la técnica que aprovecha un método para optimizar los recursos de hardware y
también el rendimiento del procesador.
Consiste en comenzar a procesar una instrucción antes de que se haya finalizado de procesar la actual.
En la siguiente figura se ilustra la ejecución de instrucciones con la técnica pipeline.
Tomando la secuencia de operaciones a partir de la instrucción “1”, el procesador se organiza de tal
manera que tan pronto como haya completado la primera etapa de esa instrucción, “fetch” y haya
avanzado hacia la segunda etapa, comenzará la primera etapa, “fetch”, de la próxima instrucción. En
principio, de esta manera el tiempo de ejecución debería ser hasta seis veces más veloz que el que
corresponde a instrucciones no superpuestas pero, como veremos luego, en la práctica no ocurre así.
Una de las características clave del alto desempeño de los microcontroladores ARM es el pipeline.
ARM7 tiene un pipeline de tres etapas que aumentan el flujo de instrucciones a través del procesador.
Así que cada instrucción se ejecuta en tres etapas:
1) Recoger: Se lee la instrucción de la memoria y se coloca en el pipeline
2) Decodificar: Se decodifica la instrucción.
3) Ejecutar: Se ejecuta la instrucción.
12
El pipeline se implementa en el nivel de hardware. Pipeline es lineal, lo que significa que el procesador
ejecuta una instrucción mientras está cargando otra para ser decodificada. Si bien esto suena interesante
presenta problemas con los saltos, por ejemplo cuando una instrucción necesita de un dato que todavía
no ha sido decodificado por lo que se ve obligado a esperar la ejecución de la instrucción que contiene
la información, esto se mejora con técnicas predictivas a nivel de hardware.
Registros del Procesador.
Existen trece registros de propósito general todos de 32 bits, otra gran diferencia con
microcontroladores menores donde solo existen un registro de trabajo y la interacción de nuestras
aplicaciones con este registro es fácilmente predecible, con trece registros la historia se complica y ya
no es tan claro como el procesador usará estos registros cuando programamos en lenguajes como C
donde la independencia con el hardware es importante.
Dos registros para el manejo del Stack, Main Stack Pointer (MSP) que es cargado con el valor
0x00000000 luego de un Reset, y el Procesador Pointer (PSP.
El Link Register (LR), registro R14 almacena la información de declaraciónes de subrutinas, llamadas a
funciones y excepciones, valores de retorno. Luego de un reset el valor LR es 0xFFFFFFFF.
El Program Status Register (PSR) se usa para para monitorear el estado del programa en ejecución, por
ejemplo si un número es negativo o cero entre otras cosas.
Luego de un RESET el PC se carga con el valor 0x00004 y el M4 puede dirección hasta un límite
teórico de 4GB.
13
FPU.
Este bloque opcional agrega un conjunto extra de treinta y dos registros para números en coma flotante
de simple precisión (32-bits), de S0 a S31. A su vez, cada par de estos registros puede verse como un
registro de 64-bits, es decir, una double-word.
Un registro adicional, el FPSCR (Floating-Point Status and Control Register) contiene los bits de
estado (N, Z, C, V) y las excepciones ocurridas (por ejemplo una división por cero).
El set de instrucciones reserva una serie de opcodes para ser utilizados por coprocesadores. En la
arquitectura, existen además espacios de registros asignados acorde al identificador del coprocesador.
En ARMv7-M se encuentran permitidos sólo dos coprocesadores, CP10 y CP11, mediante los cuales se
accede a la FPU. De este modo, accedemos a los registros mediante instrucciones adicionales.
La FPU provee entonces más de treinta instrucciones adicionales para soportar números en coma
flotante, entre las cuales encontramos suma y multiplicación en un ciclo de clock, división en catorce
ciclos, y multiplicación y acumulación en tres ciclos de clock.
La FPU puede ser habilitada o no, en las opciones del proyecto del KEIL se
puede configurar esto.
Internamente, la FPU opera con una pipeline de tres etapas totalmente
independiente
ARM y Thumb.
Se llama densidad de código a la medida de cuánta memoria necesita un sistema embebido para
contener instrucciones. Frecuentemente en los sistemas embebidos hay una limitación al tamaño de la
memoria. Esto es especialmente cierto para los sistemas con la memoria dentro del chip, en los cuales
la memoria normalmente ocupa más espacio en el chip que la propia CPU, por ejemplo la memoria
caché.
La arquitectura del procesador ARM se basa en los principios RISC aunque tiene aún mejor densidad
de código que la mayoría de los procesadores RISC. Sin embargo, su densidad de código todavía no
alcanza a ser tan buena como la de los procesadores CISC.
Para las aplicaciones en que es primordial la importancia de la densidad del código, ARM incorporó un
novedoso mecanismo llamado arquitectura Thumb. El set de instrucciones Thumb es una forma
comprimida a 16 bits del set de instrucciones de 32 bits original y emplea hardware de descompresión
dinámica en la instrucción pipeline, para descomprimir las instrucciones de 16 a 32 bits.
Por supuesto, la etapa extra requerida para manejar instrucciones de 16 bits afecta el rendimiento.
La densidad del código Thumb es mejor que la alcanzada por la mayoría de los procesadores CISC.
El set de instrucciones Thumb usado para obtener alta densidad de código en varios procesadores ARM
utiliza, predominantemente, una arquitectura de dos direcciones.
Se puede llevar a cabo un nuevo ajuste en el número de bits que se requiere para almacenar una
instrucción haciendo que el registro de destino coincida con alguno de los registros fuente.
Modos de Funcionamiento.
Tiene varios modos básicos de funcionamiento. El cambio de modo se puede hacer bajo control del
software o puede ser causado por interrupciones.
La mayoría de los programas de aplicaciones se ejecutan en modo usuario.
14
Mientras el procesador está en modo usuario, el programa que se está ejecutando no puede acceder a
recursos protegidos del sistema o cambiar de modo. Esto permite escribir un sistema operativo
adecuado para controlar el uso de los recursos del sistema.
Modo usuario (Thread ).
Sin privilegios, bajo el cual corren la mayoría de las aplicaciones. Modo de ejecución normal de los
programas. Los programadores a nivel de usuario deben tener en cuenta que solo pueden acceder a las
funciones a nivel del sistema a través de llamadas del supervisor. Generalmente estas funciones
incluyen cualquier acceso a los registros periféricos del hardware y operaciones frecuentes, tales como
entrada y salida de caracteres.
Los restantes modos son privilegiados y se llaman excepciones.
Modos de Privilegios (Handler).
•
•
•
•
•
•
•
•
Modo supervisor, SVC Supervisor Mode. Entra en este modo con el reset o cuando se ejecuta
una interrupción de software, SWI Software Interrupt. Modo protegido para sistema operativo.
El mecanismo de protección asegura que el código del usuario no pueda obtener los privilegios
del supervisor sin los controles apropiados a fin de asegurar que este código no intente
operaciones ilegales.
Modo instrucción indefinida, UND Undefined Mode. Se usa este modo para manejar
instrucciones indefinidas. Soporta emulación de software de coprocesadores de hardware.
Modo interrupciones de alta prioridad FIQ Fast Interrupt Request Mode. Entra en este modo
cuando aparece una interrupción de alta prioridad (fast). Soporta un proceso de transferencia de
datos o canal de alta velocidad.
Modo interrupciones de baja prioridad o normales,IRQ, Normal interrupt request Mode. Entra
en este modo cuando aparece una interrupción de baja prioridad (normal). Usado para manejar
interrupciones de propósitos generales.
Modo aborto, ABT, Abort Mode. Se usa para manejar violaciones de acceso a memoria. Aborto
por una fallo de memoria al leer una instrucción (instruction fetch memory fault) o al acceder a
un dato (data abort).
Modo sistema SYS, System Mode. Modo privilegiado que usa los mismos registros que el modo
usuario. Corre tareas privilegiadas del sistema operativo (arquitectura ARM versión 4 y
superiores).Se entra en ellos cuando aparece una excepción específica. Tienen acceso a todos
los recursos y pueden cambiar libremente de modo. Cada uno de ellos tiene registros
adicionales para evitar que cuando ocurre una excepción se arruinen los registros del modo
usuario. Al modo sistema, presente solamente a partir de la arquitectura ARM 4, no se entra con
cualquier excepción y participa de los mismos registros que el modo usuario. Sin embargo, es
un modo privilegiado y, por lo tanto, no está sujeto a las restricciones del modo usuario. Se
entra en él para usar tareas del sistema operativo que necesitan acceder a recursos de éste, pero
deseando evitar el uso de registros adicionales asociados con el modo de excepción. Así se evita
que el estadode la tarea no se corrompa por la aparición de cualquier excepción.
Normalmente y para simplificar, haremos referencia a dos modos para el Cortex M4.
•
•
Modo Thread: Es el modo normal de uso, entra en este modo luego de un RESET.
Modo Handler: Pasa a este modo cuando se ejecuta una interrupción o una excepción, luego de
finalizado regresa al modo Thread. Puede trabajar en modo “Con Privilegios” y “Sin
15
Privilegios”. El primero es la forma normal de uso teniendo acceso a puertos, interrupciones y
memoria en general. El segundo modo de trabajo no permite interrupciones y se restringe el
acceso a memoria y puertos.
El sistema de memoria ARM.
Además del estado de registros del procesador, un sistema ARM tiene estado de la memoria. La
memoria se puede ver como un arreglo lineal de bytes numerados desde cero hasta 232– 1. Los datos
pueden ser de bytes (8 bits), de medias palabras ( Half Word 16 bits) o palabras ( Word 32 bits). Las
palabras están siempre alineadas en bandas de 4 bytes (esto es, los 2 bits de direcciones menos
significativos son cero, porque son múltiplos de 4) y las medias palabras están alineadas en bandas de
pares de bytes (porque son múltiplos de 2).
Un byte puede ocupar cualquiera de estas posiciones de memoria. El dato de tamaño de una palabra
debe ocupar un grupo de posiciones de cuatro bytes que comienzan en una dirección que es un múltiplo
de cuatro y tiene sus cuatro bits menos significativos en 0. Esta es la organización de memoria littleendian usada por Intel y por ARM. Algunos ARM se puede configurar para trabajar como big-endian,
que es la configuración adoptada por Motorota y por los protocolos TCP, entre otros, donde los bytes se
escriben en el orden natural en que se los lee.
En la forma big endian, al tener primero el byte de mayor peso, se puede saber rápidamente si el
número es positivo o negativo sólo comprobando el estado del bit más significativo del primer byte
(recordemos que el signo se almacena en el bit más significativo) y sin necesidad de saber la longitud
del número. Esta forma de representación coincide con el orden en que se escriben los números, de
modo que las rutinas de conversión entre sistemas de numeración son más eficientes que si se
realizaran en little endian.
NOTA:
Como curiosidad, el nombre big endiany little endian se tomó irónicamente de "Los
viajes de Gulliver", de Jonathan Swift, novela en la que los habitantes de los
imperios de Lilliputy Blefuscu libran una encarnizada guerra por una disputa sobre
si los huevos hervidos debería empezar a comerse abriéndolo por su extremo
pequeño o por su extremo grande.
16
Aquí configuramos el entorno, las rutas de archivos y librerías, si en el paso anterior indicamos que
usaremos librerías provistas por STM en este paso indicamos donde están estas librerías.
IMPORTANTE: MicroLib es una biblioteca altamente optimizado para
aplicaciones embebidas escritas en C basados en ARM. Cuando se compara con la
biblioteca de C estándar que se incluye con el compilador de ARM, MicroLib
proporciona significativas ventajas en cuanto al tamaño de código requerido para
muchos sistemas embebidos.
Recordar: Todos los trabajos ejemplos están para
ser compilados con MICROLIB.
KEIL lo puede descargar directamente desde su pagina oficial.
28
Desde la lengüeta Debug siempre dentro de Utilities indicamos que el programador es del tipo SW y
no del tipo JTAG. (Si olvido este paso el programador NO FUNCIONARÁ.)
Puedo programar el microcontrolador sin un programador específico?
El controlador STM32F407VG y todos los
dispositivos de STMicroelectronics tienen
incorporado un cargador que funciona con el
protocolo UART y que permite bajar nuestro
código directamente a la memoria FLASH de
controlador.
Solo se necesita un conversor USB-RS232 para
nuestra notebook, elegir el COM en uso,
conectar el TX al pin PB10 y RX al pin PB11 en
el caso del STM32F407VG.
Si durante el arranque del controlador detecta un
nivel alto en el pin Boot 0 ejecuta el cargador
que espera recibir el programa que se guardará
en FLASH a través del puerto COM.
Luego se cambia el nivel de Boot 0, un RESET y
se ejecuta el programa grabado.
Es importante comentar que este cargador no
consume recursos del microcontrolador ni memoria de programa, es un módulo aparte agregado por
STM para simplificar el proceso de grabación de la FLASH con la aplicación programada.
30
Capitulo II.
Interrupciones.
El M4 soporta 240 fuentes de interrupción con 256 niveles de prioridad. Cada interrupción tiene un
vector (Interrupciones Vectorizadas) donde se aloja el código (ISR) que trata la interrupción.
Hay un controlador para las interrupciones llamado NVIC (Nested Vector Interrupt Controller).
Hay dos formas de tratar las prioridades: preemption priorities y sub priorities.
El que tiene mayor prioridad (preemption priorities) se ejecuta en primer lugar, cuando dos
interrupciones tienen la misma prioridad el que tiene mayor prioridad secundaria (sub priorities) se
ejecutará primero. Si ambas tienen igual prioridad y sub-prioridad la interrupción que ocurra primero se
ejecutará primero (orden de llegada).
Los procesadores Cortex M3 y M4 utilizan 8 bits para almacenar las prioridades y sub-prioridades
separados en cinco grupos para elegir la forma en que procesamos la prioridad de la interrupción.
Hay cinco grupos diferentes que podemos establecer.
•
•
•
•
•
Grupo0 - 0 bits para el sobreseimiento, 4 bits para sub prioridad .
Grupo1 - 1 bits para el sobreseimiento, 3 bits para sub prioridad .
Group2 - 2 bits para el sobreseimiento, 2 bits para sub prioridad .
Grupo3 - 3 bits para el sobreseimiento, 1 bits para sub prioridad .
Grupo4 - 4 bits para el sobreseimiento, 0 bits para sub prioridad .
La función NVIC_PriorityGroupConfig configura los grupos de prioridades.
Si elijo grupo 4 la sintaxis seria NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4) lo que me
dejará 4 bits para NVIC_IRQChannelPreemptionPriority y 0 bits para NVIC_IRQChannelSubPriority.
41
En la configuración de pines se especifica:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
Lo que indica que los pines han sido configurados como alternativos a un módulo de la unidad.
Se detalla a continuación el listado del programa ejemplo.
/*********************************************************
* Nombre
: USART.c
* Descripción : Funcionamiento de la USART2 en Cortex M4.
* Target
: STM32F407VG
* ToolChain
: MDK-ARM
* IDE
: uVision 4
*
www.firtec.com.ar
**********************************************************/
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#define USARTx
USART2 // Puerto que se usará
/* Esta función se utiliza para transmitir una cadena de caracteres a través
* El USART especificado en USARTx.
* La cadena tiene que ser pasado a la función como un puntero porque
* El compilador no conoce el tipo de datos string. En C una cadena es sólo
* un conjunto de caracteres.
**/
void Enviar_String(const char *s)
{
while(*s)
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, *s++);
}
}
void Config_USARTx(void){
// Esta función configura el periférico USART2
TX=GPIOA_Pin_2 (RX)
RX=GPIOA_Pin_3 (TX)
RTS=GPIOA_Pin_1 (CTS)
CTS=GPIOD_Pin_3 (RTS)
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Estructura para configurar las
// interrupciones NVIC
Activa APB1 reloj periférico para USART2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
Activa el reloj periférico para los pines utilizadas por la
USART2, PA3 para TX y RX para PA2
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
58
Len) es la encargada de transmitir donde Buf es un buffer donde se guardan los datos que serán
transmitidos.
Len es la cantidad de datos a transmitir y la función retorna USBD_OK si todo a salido bien, en caso
contrario retorna VCP_FAIL.
Para enviar datos la forma de implementar la función sería:
VCP_DataTx (0,dato);
Donde el 0 indica el EndPoint por donde se envía la información al USB, dato es el Byte enviado. Un
detalle es que esta función solo envía Bytes no se pueden enviar cadenas.
La función static int16_t VCP_DataRx (uint8_t* Buf, uint32_t Len) es la encargada de recibir los
datos que son almacenados en Buf siendo Len es la cantidad de datos recibidos.
Notará que las funciones están declaradas como static lo que significa que las funciones solo se pueden
acceder desde el módulo donde están declaradas. Si quiere tener acceso a estas funciones por ejemplo
desde el main() debe quitar el static en la función y desde donde las llame agregar el modificador
extern.
extern uint16_t VCP_DataTx (uint8_t* Buf, uint32_t Len);
Al final de este archivo encontrará la siguiente función:
void DISCOVERY_EXTI_IRQHandler(void)
{
VCP_DataTx (0,'F');
VCP_DataTx (0,'i');
VCP_DataTx (0,'r');
VCP_DataTx (0,'t');
VCP_DataTx (0,'e');
VCP_DataTx (0,'c');
}
Cada vez que se oprime el botón de usuario en la placa Discovery se envía por EndPoint 0 la palabra
Firtec carácter por carácter.
En el archivo usbd_desc.c encontramos todo lo referente al descriptor USB de la aplicación, si
observa el contenido de este archivo verá que desde el se definen varias cosas como el PID-VID y en
general la forma como la aplicación se identifica en el sistema, funciones clásicas de un descriptor
USB.
En usbd_usr.c encontrará cosas como estas:
//////////////////////////////////////////////////////////////////////////////////
// Función que se ejecuta cuando el dispositivo se desconecta del puerto USB.
//////////////////////////////////////////////////////////////////////////////////
void USBD_USR_DeviceSuspended(void)
{
STM32F4_Discovery_LEDOff(LED5); // Apaga LED rojo cuando desconecta
// Aquí se puede poner el código para hacer algo cuando se desconecta del USB
}
//////////////////////////////////////////////////////////////////////////////////
//
Función que se ejecuta cuando el dispositivo se conecta al puerto USB.
//////////////////////////////////////////////////////////////////////////////////
void USBD_USR_DeviceResumed(void)
{
STM32F4_Discovery_LEDOn(LED5); // Enciende LED rojo cuando se conecta al USB
// Aquí se puede poner el código para hacer algo cuando se conecta al puerto USB
}
62
float numero_2 = 0.897; // Valor float a mostrar en la pantalla LCD
int main(void){
HD44780_Init(16, 2);
HD44780_Puts(0, 0,"STM32F4x Cortex");
sprintf(numStr,"D1:%d",numero);
HD44780_Puts(0, 1,numStr);
sprintf(floatStr," D2:%2.3f",numero_2);
HD44780_Puts(7, 1,floatStr);
while(1);
}
/*** fin del archivo *************************************************************/
En los archivos que conforman el driver de la pantalla LCD se pueden configurar los pines que se
conectan al microcontrolador y cambiarlos según la necesidad.
La función
HD44780_Init(16, 2)
Configura la pantalla en 16 caracteres visibles por dos líneas pudiendo ser 40 caracteres a cuatro líneas
como máximo.
Trabajo practico
Se pide que escriba un programa contador de 0 a 999 y se muestre el estado del
contador en una pantalla LCD de dos líneas.
68
La idea es medir en cuatro canales analógicos (cuatro potenciómetros) y usar los canales DMA para
procesar los datos.
Usamos el módulo ADC3 que tiene asociado el canal 2 y el stream 0,
también podríamos usar el stream 1 que está asociado al módulo ADC3.
Los potenciómetros estarán conectados en GPC0, GPC1, GPC2 y GPC3.
En la pantalla LCD deberíamos ver lo siguiente:
Esto depende de las siguientes líneas de código:
HD44780_Puts(0, 0,"A1:");
HD44780_Puts(0, 1,"A2:");
HD44780_Puts(8, 0,"A3:");
HD44780_Puts(8, 1,"A4:");
while (1){
sprintf(datoStr,"%04d",ADC3ConvertedValue[0]);
HD44780_Puts(3, 0,datoStr);
sprintf(datoStr,"%04d",ADC3ConvertedValue[1]);
HD44780_Puts(3, 1,datoStr);
sprintf(datoStr,"%04d",ADC3ConvertedValue[2]);
HD44780_Puts(11, 0,datoStr);
sprintf(datoStr,"%04d",ADC3ConvertedValue[3]);
HD44780_Puts(11, 1,datoStr);
}
El módulo ADC está configurado para una resolución de 10 bits (0-1023). La configuración general
para el modulo ADC es como sigue:
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div6;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
Específicamente para el ADC3 es como sigue:
ADC_InitStructure.ADC_Resolution = ADC_Resolution_10b; // Resolución en 10bits
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 4; // Cuatro conversiones en ADC3
ADC_Init(ADC3, &ADC_InitStructure);
Las líneas siguiente determinan el orden en que los canales serán leídos y la velocidad con que leemos
los canales analógicos.
79
modo SD por defecto y no modo SPI, si se quiere trabajar en modo SPI hay algunos pasos para
configurar su funcionamiento.
En nuestro proyecto el driver para el manejo de la memoria se encuentra en los archivos SDIO.C y
SDIO.H.
Aquí vamos a ver algunos detalles del manejo de estas memorias.
•
•
Bloque: Número fijo de Bytes ya sea para escribir o leer, típicamente un bloque tine 512 Bytes.
Sector: Es el número de bloques a tratar.
Las memoria soportan dos modos de acceso, Modo Multiblock o Modo Simple Block, en este modo el
tamaño máximo de datos posible es de 512 bytes (un bloque) pero el mínimo puede ser un solo byte.
Para la lectura de varios bloques estos deben estar en el mismo sector.
La escritura se hace en bloques (512 Bytes). Para el modo Multiblock el proceso es parecido solo que
se procesan varios bloques siendo la dirección de inicio el inicio del sector, en este modo se habla de
sectores siendo el tamaño mínimo a borrar/escribir un sector.
Estas memorias típicamente soportan 100.000 ciclos de escritura/borrado.
El ejemplo que vamos a ver escribe dos bloques, el bloque 1000 y el bloque 3001 que son completados
con 512 bytes generados en el bucle:
for (i=0;i<512;i++) {
outbuf[i]= 'A';
}
Este bucle completa outbuf[] con 512 caracteres 'A'. Luego un segundo bloque con 'B'.
Observe que se han definido dos buffer de 512 bytes cada uno, el tamaño de un bloque.
En la segunda parte del programa se fija la dirección de inicio de dos sectores y se escriben varios
bloques con 'C', luego todo se lee desde la memoria y se envía en forma serial para ver los datos en la
computadora.
Los datos son enviados mediante la USART 2 utilizando los pines siguientes:
•
•
TX=GPIOA_Pin_2.
RX=GPIOA_Pin_3
112
modo SD por defecto y no modo SPI, si se quiere trabajar en modo SPI hay algunos pasos para
configurar su funcionamiento.
En nuestro proyecto el driver para el manejo de la memoria se encuentra en los archivos SDIO.C y
SDIO.H.
Aquí vamos a ver algunos detalles del manejo de estas memorias.
•
•
Bloque: Número fijo de Bytes ya sea para escribir o leer, típicamente un bloque tine 512 Bytes.
Sector: Es el número de bloques a tratar.
Las memoria soportan dos modos de acceso, Modo Multiblock o Modo Simple Block, en este modo el
tamaño máximo de datos posible es de 512 bytes (un bloque) pero el mínimo puede ser un solo byte.
Para la lectura de varios bloques estos deben estar en el mismo sector.
La escritura se hace en bloques (512 Bytes). Para el modo Multiblock el proceso es parecido solo que
se procesan varios bloques siendo la dirección de inicio el inicio del sector, en este modo se habla de
sectores siendo el tamaño mínimo a borrar/escribir un sector.
Estas memorias típicamente soportan 100.000 ciclos de escritura/borrado.
El ejemplo que vamos a ver escribe dos bloques, el bloque 1000 y el bloque 3001 que son completados
con 512 bytes generados en el bucle:
for (i=0;i<512;i++) {
outbuf[i]= 'A';
}
Este bucle completa outbuf[] con 512 caracteres 'A'. Luego un segundo bloque con 'B'.
Observe que se han definido dos buffer de 512 bytes cada uno, el tamaño de un bloque.
En la segunda parte del programa se fija la dirección de inicio de dos sectores y se escriben varios
bloques con 'C', luego todo se lee desde la memoria y se envía en forma serial para ver los datos en la
computadora.
Los datos son enviados mediante la USART 2 utilizando los pines siguientes:
•
•
TX=GPIOA_Pin_2.
RX=GPIOA_Pin_3
112
Funcionamiento general de un PID.
Para el correcto funcionamiento de un controlador PID que controle un proceso o sistema se necesita,
al menos:
1. Un sensor, que determine el estado del sistema.
2. Un controlador, que genere la señal que gobierna al actuador.
3. Un actuador, que modifique al sistema de manera controlada.
El sensor proporciona la información al controlador, la cual representa el punto actual en el que se
encuentra el proceso o sistema.
El controlador lee una señal externa que representa el valor que se desea alcanzar. Esta señal recibe el
nombre de punto de consigna (o punto de referencia), la cual es de la misma naturaleza y tiene el
mismo rango de valores que la señal que proporciona el sensor.
El controlador resta la señal de punto actual a la señal de punto de consigna, obteniendo así la señal de
error, que determina en cada instante la diferencia que hay entre el valor deseado (consigna) y el valor
medido. La señal de error es utilizada por cada uno de los 3 componentes del controlador PID. Las 3
señales sumadas, componen la señal de salida que el controlador va a utilizar para gobernar al actuador.
La señal resultante de la suma de estas tres se llama variable manipulada que será transformada para
ser compatible con el actuador utilizado en el sistema.
Las tres componentes de un controlador PID son: parte Proporcional, acción Integral y acción
Derivativa. El peso de la influencia que cada una de estas partes tiene en la suma final, viene dado por
la constante proporcional, el tiempo integral y el tiempo derivativo, respectivamente. Se pretenderá
lograr que el bucle de control corrija eficazmente y en el mínimo tiempo posible los efectos de las
perturbaciones.
Control PID con STM32F407.
CMSIS de ARM proporciona funciones matemáticas avanzadas para el diseño de un control PID.
También hay funciones PID en diferentes formatos para f32, q31 y Q7. Este proyecto implementa un
122
controlador PID con STM32F4xx usando funciones PID de ARM que ofrece tres funciones diferentes
para el PID. f32: float, q31: número entero, Q7: char.
Para cada uno de los tres tipos, tiene tres funciones:
f32
arm_pid_init_f32
arm_pid_reset_f32
arm_pid_f32
q31
arm_pid_init_q31
arm_pid_reset_q31
arm_pid_q31
q7
arm_pid_init_q7
arm_pid_reset_q7
arm_pid_q7
También hay una estructura donde se pasa parámetros al PID.
En el proyecto tratado se utilizan dos sensores de temperatura DS18B20. Se configuran en la
resolución de 12 bits por lo que demora unos ~ 750ms entre cada medición.
El proyecto completo se puede ver funcionado en este link.
123
Datalogger de temperatura.
Este proyecto lee la temperatura cada media hora mediante un sensor DS18B20 y guarda el
dato en una memoria SD con formato FAT32 compatible con una hoja de cálculo.
La aplicación almacena tanto la temperatura como la hora en que se tomó la muestra.
Al comenzar un nuevo día se genera un nuevo archivo que lleva como nombre de archivo la
fecha del día por ejemplo 19_06_15 y se completa con datos cada media hora.
Para leer los datos se puede extraerla memoria SD y conectarla en una notebook o
simplemente conectar un cable USB a la placa entrenadora Discovery (puerto de usuario) que
publicará en la notebook una unidad de disco (la memoria SD) donde podemos leer los datos
desde la misma placa sin sacar la memoria.
El calendario usado es un DS1307 por puerto I2C. El ejemplo es meramente demostrativo sin
embargo es fácilmente adaptable a cualquier proyecto que requiera de este tipo de aplicación
ya que todo los parámetros son configurables desde el firmware.
La aplicación se puede ver funcionando desde este link.
127
/*********************************************************************************
*
Target
: STM32F407VG
*
ToolChain
: MDK-ARM
*
IDE
: uVision 5
*
www.firtec.com.ar
**********************************************************************************/
#include <stdio.h>
#include "stm32f4xx.h"
#include "stm32f4_discovery_sdio_sd.h"
#include "stm32f4_hd44780.h"
#include "ff.h"
#include "usbd_msc_core.h"
#include "usbd_usr.h"
#include "usbd_desc.h"
#include "usb_conf.h"
#include "stm32f4_delay.h"
#include "stm32f4_ds1307.h"
#include "stm32f4_onewire.h"
#include "stm32f4_ds18b20.h"
#include <string.h>
void Archivo(void);
// Variables del sistema
SD_Error Status = SD_OK;
FRESULT ret;
FIL file;
DIR dir;
FILINFO fno;
UINT bw, br;
FATFS FatFs;
FATFS *fs;
DWORD fre_clust, fre_sect, tot_sect;
DWORD clust;
static char str1[20];
static char str2[20];
char ext[5]=".csv";
unsigned char usb_marca=0;
128
unsigned char bandera =0;
static char ArchNombre[13];
char temp[11];
char espacio[20];
uint8_t devices,count;
uint8_t device[EXPECTING_SENSORS][8];
float temps[EXPECTING_SENSORS];
unsigned char minuto_anterior = 0;
TM_DS1307_Time_t time;
static void fault_err (FRESULT rc);
#define EXPECTING_SENSORS
1
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined ( __ICCARM__ ) //!< IAR Compiler
#pragma data_alignment=4
#endif
#endif // USB_OTG_HS_INTERNAL_DMA_ENABLED
__ALIGN_BEGIN USB_OTG_CORE_HANDLE
USB_OTG_dev __ALIGN_END ;
FUNCION PRINCIPAL DEL PROGRAMA
int main(void){
TM_DS1307_Init();
/*
// Valores por defecto para el calendario
time.hours = 8;
time.minutes = 8;
time.seconds = 10;
time.date = 16;
time.day = 6;
time.month = 1;
time.year = 15;
TM_DS1307_SetDateTime(&time);
*/
HD44780_Init(16, 2);
// 16 col x 2 filas
DELAY_Init();
// Configura las rutinas de tiempo
OneWire_Init();
// OneWire en pin PC0
129
Sensor de imagen OV7670 con STM32F407
El OV7670 es un sensor de imagen de bajo costo con un DSP integrado que puede operar a un máximo
de 30 fps y 640 x 480 de resolución (VGA), equivalente a 0.3 Megapixels.
La imagen capturada puede ser pre-procesada por el DSP antes de enviarla al puerto DCMI. Este preprocesamiento se puede configurar a través de un registro de control (SCCE).
El módulo de la cámara tiene una doble fila de 8 pines por lado:
La interfaz se conecta a la placa entrenadora mediante 16 conexiones.
Conexiones del sensor a la placa entrenadora.
Sensor cableado a la placa entrenadora.
141
Y los píxeles reales son los siguientes:
Observe cada píxel es de 3 bytes de largo (por ejemplo, Y0, Cb0 y Cr0), como en el formato RGB.
Pero, en el formato YCbCr422, los canales Cb y Cr son compartidos entre dos píxeles consecutivos
(por ejemplo, píxeles 0 y 1 acción CB0 y cr0). Por lo tanto dos píxeles son "comprimidos" en 4 bytes o
32 bits de, esto significa que en promedio cada píxel se almacena como 2 bytes o 16 bits.
La ventaja adicional de YCbCr es que el canal Y es la imagen en escala de grises, mientras que en RGB
tendrá que promediar los 3 canales para obtener la imagen en escala de grises.
Señales de la cámara.
El OV7670 envía los datos en un formato sincrónico paralelo. En primer lugar, para obtener todos los
datos de la OV7670, es necesario suministrar una señal de reloj en el pin XCLK.
De acuerdo con la hoja de datos, este reloj debe tener una frecuencia de entre 10 y 48 MHz. Sin
embargo, funciona bien con relojes de hasta 8 MHz configurando el registro SCCE.
Si se utiliza un microcontrolador que tiene salida de reloj, se puede usar eso para el reloj OV7670, estos
pueden generalmente su salida de reloj del sistema interno de preescalado por algún factor. Si el
microcontrolador no tiene capacidad de salida de reloj, pero usted está usando un cristal externo,
entonces usted puede conectar el pin OSC_OUT al OV7670.
Después que la señal de reloj es puesta en XCLK, OV7670 comenzará a enviar señales por VSYNC,
HREF y D0-D7.
144