Présentation
Pour cette première application, nous allons exploiter le port série, nous commencerons par réaliser un grand classique :
Un Hello World !
L'affichage du message se fera donc via le port série. Le port utilisé est l'USART1, le même qui est utilisé par le bootloader.
Dans un second temps, nous mettrons en place un interpréteur de commandes simple.
Hello world
Makefile
Pour personaliser le nom du projet, il faut remplacer "PROJECT_NAME=Base" par "PROJECT_NAME=Hello", cela permet de renommer le fichier de sortie
même si c'est purement symbolique.
Pour pouvoir utiliser le port série, il faut compiler les fonctions de l'API pour l'utiliser. Modifier le paramètre ST_LIB_SOURCE du Makefile pour
qu'il devienne :
De même modifier la liste des sources du projet pour un nouveau fichier :
# ST Library source files. ST_LIB_SOURCE= $(ARM_CMSIS_DIR)/CM3/CoreSupport/core_cm3.c \ $(ST_LIB_DIR)/src/misc.c \ $(ST_LIB_DIR)/src/stm32f10x_rcc.c \ $(ST_LIB_DIR)/src/stm32f10x_gpio.c \ $(ST_LIB_DIR)/src/stm32f10x_usart.c \
# Source files SOURCE= main.c \ stm32f10x_it.c \ stf_syscalls_minimal.c \ system_stm32f10x.c \ serial.c \
stm32f10x_conf.h
Le fichier stm32f10x_conf.h doit contenir un include vers les fonctions de l'usart, pour cela décommenter #include "stm32f10x_usart.h"
et #include "stm32f10x_gpio.h".
serial.c et serial.h
Les fonctions de gestion du port série sont ajoutés dans un nouveau fichier du projet : serial.c. Afin de comprendre le fonctionnement,
il s'avère nécéssaire
de lire le chapitre correspondant du manuel de référence. De plus le projet d'exemple de la standart peripheral library
"Project\STM32F10x_StdPeriph_Examples\USART\Polling" va nous inspirer pour le controle du port. Les différentes étapes de l'initialisation sont :
- Mise en service des bus d'horloges pour les périphériques. En effet celles-ci sont désactivées sur les périphériques non en fonctionnement afin de préserver la consommation
- Configuration des modes des broches d'entrées/sorties utilisées.
- Configuration des caractéristiques de communication du port.
- Activation de l'USART.
/** * @file serial.c * @author Florian MAZEN * @version V1.0 * @date 06/26/2010 * @brief Usart hight level manipulation functions */ #include <stm32f10x.h> #include "serial.h" /** * @brief Initialise the usart subsystem */ void usart_init() { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; /* Enable UART clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); /* Configure USART1 Rx (PA10) as input floating */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure USART1 Tx (PA9) as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); // Initialise USART USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* Configure USART1 */ USART_Init(USART1, &USART_InitStructure); /* Enable the USART1 */ USART_Cmd(USART1, ENABLE); } /** * @brief Send a char to the serial port * @param ch Byte to send on the serial port */ void noint_serial_putchar(int ch) { /* Loop until the end of transmission */ while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) { } USART_SendData(USART1, (uint8_t) ch); }
serial.c Télécharger
Le fichier serial.h contient les déclarations des fonctions correspondantes.
/** * @file serial.h * @author Florian MAZEN * @version V1.0 * @date 06/26/2010 * @brief Header for serial.h */ void usart_init(); void noint_serial_putchar(int ch);
serial.h Télécharger
stf_syscalls_minimal.c
Pour pouvoir utiliser les fonctions de sortie standard tel que printf, il faut indiquer à newlib (Voir chapitre précédent) comment écrire sur le port série.
La fonction générique utilisée est _write, celle ci prend en paramètre un pointeur vers les données, le nombre de caractère à écrire ainsi qu'un
paramètre "file" représentant un descripteur de fichier. Ici nous n'utilisons pas le descripteur de fichier, dans des cas plus complexe, nous pourrions
filtrer pour afficher sur le port série ce qui est destiné à STDOUT.
int _write(int file, char *buffer, unsigned int count) { register int i; for (i=0; i<count; ++i) { putChar(*buffer++); } return count; }
main.c
Voici le code du programme envoyant "Hello world".
/** * @file main.c * @author Florian MAZEN * @version V1.0 * @date 06/26/2010 * @brief Main */ #include <stm32f10x.h> #include <stdio.h> #include "serial.h" void RCC_Configuration(); /** * @brief Main program. * @retval None */ int main() { /* Configure the system clocks */ RCC_Configuration(); usart_init(); while (1) { printf("\r\nHello world\r\n"); } } /** * @brief Configures the different system clocks. */ void RCC_Configuration() { /* RCC system reset(for debug purpose) */ RCC_DeInit(); } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) { } } #endif
main.c Télécharger
Téléchargement
Le projet est disponible complet, près à être téléchargé puis compilé :
Télécharger
Télécharger
Programmation
Les STM32 intègrent un bootloader permettant de programmer la flash intégré à partir de l'USART1. La commutation du mode bootloder s'effectue par l'intéermédiaire
des broches boot0 et boot1.
Le logiciel de téléchargement sous Windows est "Flash loader demonstrator" disponible en téléchargement gratuitement sur le site
de ST microelectronics. Sous linux, le script perl stm32loader fonctionne parfaitement.
Boot1 | Boot0 | Mode |
---|---|---|
x | 0 | Démarrage sur la flash |
0 | 1 | Boot loader |
1 | 1 | Démarrage en sram |
Interpréteur de commandes
Dans ce deuxième exemple d'utilisation du port série, le projet hello world va être modifié pour supporter un interpréteur de commande. Pour le
moment seule une commande affichant le statut de la mémoire sera implémentée. Etant de nature à réutiliser un maximum le code déja éxistant, une librairie
correspondant aux besoins existe, il s'agit de Tiny Shell. L'archive du projet est à télécharger et à
décomprésser sur le disque dur du PC.
Dans les sources du projet, créer un dossier shell et copier les deux fichiers tinysh.h et tinysh.c.
Dans les sources du projet, créer un dossier shell et copier les deux fichiers tinysh.h et tinysh.c.
Makefile
Le Makefile est à modifier pour prendre en compte lors de la compilation le fichier que nous venons d'ajouter ainsi qu'un futur shell.c :
# Source files SOURCE= main.c \ stm32f10x_it.c \ stf_syscalls_minimal.c \ system_stm32f10x.c \ serial.c \ shell/tinysh.c \ shell/shell.c \
shell.c et shell.h
Le fichier shell.c contient tout le néscessaire pour l'initialisation de l'interpréteur de commande ainsi que la définition des commandes. La fonction
init_shell() definit le prompt puis ajoute notre fonction memstat qui indique le status de la mémoire. La déclaration se fait par
l'intermédiaire d'une structure tinysh_cmd_t contenant :
- tinysh_cmd_t *parent; : Utilisé pour les commandes contextuel. Cette fonction n'étant pas utilisée, elle est mise à 0.
- char *name; : Nom de la commande.
- char *help; : Message d'aide.
- char *usage; : Description des paramètres de la commande.
- tinysh_fnt_t function; : Pointeur vers la fonction appelée.
- void *arg; : Paramètres passés à l'initialisation.
- struct tinysh_cmd_t *next; : Usage interne, initialisé à 0.
- struct tinysh_cmd_t *child; : Usage interne, initialisé à 0.
/** * @file shell.c * @author Florian MAZEN * @version V1.0 * @date 01/08/2010 * @brief Shell management subfonctions */ #include <unistd.h> #include <stdio.h> #include "shell.h" #include "tinysh.h" #include "../serial.h" extern int _sheap; extern int _eheap; extern int _sflash; extern int _eflash; extern int _Stack_Size; extern int _estack; extern int _test; /** * @brief TinySH putchar function * @retval None */ void tinysh_char_out(unsigned char c) { noint_serial_putchar(c); } /** * @brief TinySH command: Display memory usage * @retval None */ static void cmd_memstat(int argc, char **argv) { unsigned int sflash = (unsigned int) &_sflash; unsigned int eflash = (unsigned int) &_eflash; unsigned int sheap = (unsigned int) &_sheap; unsigned int eheap = (unsigned int) &_eheap; unsigned int estask = (unsigned int) &_estack; unsigned int stack_size = (unsigned int) &_Stack_Size; printf("\n\rMemory usage\n\r============\n\r Flash\n\r -----"); printf("\n\r Start address: %x", sflash); printf("\n\r End address: %x", eflash); printf("\n\r Used flash: %d", eflash - sflash); printf("\n\r Ram\n\r ---"); printf("\n\r Heap start address: %x", sheap); printf("\n\r Heap end address: %x", eheap); printf("\n\r Total heap size: %d", eheap - sheap); printf("\n\r Allocated heap size: %d", getHeapAddess() - sheap); printf("\n\r Stack start address: %x", estask - stack_size); printf("\n\r Stack end address: %x", estask); printf("\n\r Total stack size: %d", stack_size); printf("\n\r"); } static tinysh_cmd_t memstat = { 0, "memstat", "Display memory usage", 0, cmd_memstat, 0, 0, 0 }; /** * @brief TinySH initialization function * @retval None */ void init_shell() { tinysh_set_prompt("stm32$ "); tinysh_add_command(&memstat); }
shell.c Télécharger
/** * @file shell.h * @author Florian MAZEN * @version V1.0 * @date 01/08/2010 * @brief Shell management subfonctions header file */ void init_shell();
shell.h Télécharger
main.c
Voici le nouveau fichier main qui se contente de lire les données recu sur le port série et de les transmettres à l'interpréteur de commande.
/** * @file main.c * @author Florian MAZEN * @version V1.0 * @date 06/26/2010 * @brief Main */ #include <stm32f10x.h> #include <stdio.h> #include "serial.h" #include "shell/shell.h" #include "shell/tinysh.h" void RCC_Configuration(); /** * @brief Main program. * @retval None */ int main() { /* Configure the system clocks */ RCC_Configuration(); usart_init(); init_shell(); printf("\r\nTiny Shell demo\r\n"); while (1) { // Check for characters on the serial port int c = noint_serial_getchar(); if (c != -1) { tinysh_char_in(c); } } } /** * @brief Configures the different system clocks. */ void RCC_Configuration() { /* RCC system reset(for debug purpose) */ RCC_DeInit(); } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) { } } #endif
main.c Télécharger
serial.c et serial.h
Il est ajouté au fichier serial.c une fonction de lecture des données recu sur le port série :
serial.h possède en plus la déclaration de cette fonction :
/** * @brief get the last received char on the serial port. * @return Received char or -1 if no char received */ int noint_serial_getchar() { if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) return -1; return USART_ReceiveData(USART1); }
int noint_serial_getchar();
stf_syscalls_minimal.c
Afin de connaitre la taille actuelement utilisée par le heap, il est néscéssaire d'ajouter une fonction au fichier stf_syscalls_minimal.c
unsigned int getHeapAddess() { return (unsigned int) heap; }
Téléchargement
Le projet est comme d'habitude disponible complet, près à être téléchargé puis compilé :
Télécharger
Télécharger