Writing to the console window

While a variant of the helloBlinky recipe is usually the first program introduced in most embedded tutorials, the first program found most C textbooks usually outputs the string "Hello World" to the screen. To run such a program on our evaluation board, we'll need to install a terminal emulation program on our PC host. PuTTY® http://www.chiark.greenend.org.uk/~sgtatham/putty/, an open source terminal emulation program is a good choice. We also need to connect the evaluation board to the PC's (COM) serial port. Most PCs and laptops are no longer fitted with 9-pin D-type (COM) ports, so you may need to purchase a USB to Serial Adaptor cable.

Getting ready

Follow these steps to install PuTTY, and connect the evaluation board to the PC's COM port:

  1. If you're using a USB Serial Adaptor, then plug it into the laptop, and wait for the driver to be installed.
  2. Open the Control Panel, and make a note of the COM port that has been allocated (you will need this later to configure PuTTY).
    Getting ready
  3. Connect the 9-Pin D-type UART1/3/4 connector on the evaluation board to the PC USB port, and ensure that the jumpers J13 and J14 are set to short pins 1 and 2 thereby selecting USART4. Pin 1 can be easily be identified by its square solder pad, easily visible on the underside of the board. Install PuTTY, and configure the serial connection to use the COM port you previously identified in Control Panel, configured to 115200 Baud, 8 data bits, 1 stop-bit, no parity or flow control.
    Getting ready

How to do it…

  1. Create a new folder named helloWorld; invoke uVision5, and create a new project. Using the RTE manager, select the MCBSTM32F400 board, but don't check any of the board support tick boxes. Check CMSISCORE, RTOS (API)KeilRTX, DeviceStartup, and DeviceSTM32Cube Framework (API)Classic. Click Resolve to automatically load any additional software components needed. Then exit by clicking on OK.
    How to do it…
  2. The source code for this project is divided between three source code files. Create a new file (FileNew…), and enter the source code shown. Save the file (FileSaveAs) as helloWorld.c. The source file named helloWorld.c contains the main function in the project, illustrated using the folding editor feature to hide the boilerplate.
    /***************************************************
     * Recipe:   helloWorld_c2v0
     * File:     helloWorld.c 
     * Purpose:  Serial I/O Example                            
     ***************************************************
     *	  			                     
     * Modification History				         
     * 2014 Created							             
     * 03.12.15 Updated for uVision_5.17 & DFP_2.6.0                                
     * 									   
     * Dr Mark Fisher, CMP, UEA, Norwich, UK.          
     ***************************************************/
    
    #include "stm32F4xx_hal.h"
    #include "cmsis_os.h"
    #include <stdio.h>
    #include "Serial.h" 
    
    /* Function prototypes */
    void wait(unsigned long delay);
    extern void init_serial(void);
    extern int sendchar(int c);
    extern int getkey(void);
    
    #ifdef __RTX
    /* Function prototypes */
    void wait(unsigned long delay);
    extern void init_serial(void);
    extern int sendchar(int c);
    extern int getkey(void);

    #ifdef __RTX
    _____________________________________________________
    /*-------------------------------------------------
      System Clock Configuration
     *-------------------------------------------------*/
    _____________________________________________________
    void SystemClock_Config(void) {
    
    /*-------------------------------------------------
    *	wait
    *--------------------------------------------------*/
    void wait (unsigned long delay){
    unsigned long i;
    
       for (i = 0; i < delay; i++)
         ;
    }
    
    int main (void) {
      HAL_Init ();  /* Init Hardware Abstraction Layer */
      SystemClock_Config ();          /* Config Clocks */
      SER_Init();
      
      for (;;) {                       /* Loop forever */
        wait(1000000);
        printf("Hello World!
    ");
      }
    }
  3. In the project window, right-click on the Source Group 1 folder, and add the source file helloWorld.c to the project.
    How to do it…
  4. Create a new file, enter the following code, name the new file Retarget.c, and add it in the project. This source file redefines some functions used by C's standard input output library, <stdio.h>.
    /*-------------------------------------------------
     * Name:    Retarget.c
     * Purpose: 'Retarget' layer for target-
     *          dependent low level functions
     * Note(s):
     *-------------------------------------------------
     * This file is part of the uVision/ARM 
     * development tools.
     *-------------------------------------------------*/
    
    #include <stdio.h>
    #include <rt_misc.h>
    #include "Serial.h"
    
    #pragma import(__use_no_semihosting_swi)
    
    struct __FILE { 
      int handle; 
      /* Add whatever you need here */ 
    };
    FILE __stdout;
    FILE __stdin;
    
    int fputc(int c, FILE *f) {
      return (SER_PutChar(c));
    }
    
    int fgetc(FILE *f) {
      return (SER_GetChar());
    }
    
    int ferror(FILE *f) {
      /* Your implementation of ferror */
      return EOF;
    }
    
    void _ttywrch(int c) {
      SER_PutChar(c);
    }
    
    void _sys_exit(int return_code) {
    label:  goto label;  /* endless loop */
    }
  5. Create a new file, enter the SER_Init() function, name the new file Serial.c, and add it in the project.
    /*-------------------------------------------------
     * Name:    Serial.c
     * Purpose: Low level serial routines
     * Note(s):
     *-------------------------------------------------
     * This file is part of the uVision/ARM 
     * development tools.
     *-------------------------------------------------*/
    #include "stm32f4xx.h"           /* STM32F4xx Defs */
    #include "Serial.h"
    
    #ifdef __DBG_ITM
    volatile int32_t ITM_RxBuffer;
    #endif
    /*-------------------------------------------------
     * SER_Init:  Initialize Serial Interface
     *-------------------------------------------------*/
    void SER_Init (void) {
    #ifdef __DBG_ITM
      ITM_RxBuffer = ITM_RXBUFFER_EMPTY;
    
    #else  
      RCC->APB1ENR  |= (1UL << 19); /* Enable 
                                          USART4 clock */
      RCC->APB2ENR  |= (1UL <<  0); /* Enable 
                                            AFIO clock */
      RCC->AHB1ENR  |= (1UL <<  2); /* Enable 
                                           GPIOC clock */
      GPIOC->MODER  &= 0xFF0FFFFF;
      GPIOC->MODER  |= 0x00A00000;
      GPIOC->AFR[1] |= 0x00008800;  /* PC10 UART4_Tx, 
                                   PC11 UART4_Rx (AF8) */
    
      /* Configure UART4: 115200 baud @ 42MHz, 8 bits, 
                                 1 stop bit, no parity */
      UART4->BRR = (22 << 4) | 12;  
      UART4->CR2 = 0x0000;
      UART4->CR1 = 0x200C;
    #endif
    }
  6. Add the functions SER_getc() and SER_putc() to Serial.c
    /*-------------------------------------------------
     * SER_PutChar:  Write a char to Serial Port
     *-------------------------------------------------*/
    int32_t SER_PutChar (int32_t ch) {
    #ifdef __DBG_ITM
      int i;
      ITM_SendChar (ch & 0xFF);
      for (i = 10000; i; i--)
        ;
    #else
      while (!(UART4->SR & 0x0080));
        UART4->DR = (ch & 0xFF);
    #endif  
    
      return (ch);
    }
    
    /*-------------------------------------------------
     * SER_GetChar:  Read a char from Serial Port
     *-------------------------------------------------*/
    int32_t SER_GetChar (void) {
    #ifdef __DBG_ITM
      if (ITM_CheckChar())
        return ITM_ReceiveChar();
    #else
      if (UART4->SR & 0x0020)
        return (UART4->DR);
    #endif
      return (-1);
    }
  7. Create a new file, enter the following code, name the file Serial.h, and add it to the project. This is the header file that declares the function prototypes for Serial.c
    /*-------------------------------------------------
     * Name:    Serial.h
     * Purpose: Low level serial definitions
     * Note(s):
     *-------------------------------------------------*/
    #ifndef __SERIAL_H
    #define __SERIAL_H
    
    extern void SER_Init      (void);
    extern int  SER_GetChar   (void);
    extern int  SER_PutChar   (int c);
    
    #endif
  8. Configure PuTTY as shown in part a) of the following image. Build, download, and run the program to achieve the output shown in b)
    How to do it…

How it works…

The evaluation board and PC communicate by exchanging data using an RS232 serial Input/Output (I/O) connection (http://en.wikipedia.org/wiki/RS-232). RS232 is a 2-wire full-duplex communications standard. PuTTY manages the protocol at the PC, but we are responsible for the evaluation board. To use serial I/O, we need to configure the microcontroller's Universal Synchronous/Asynchronous Receiver/Transmitter (USART). We can do this by including a peripheral driver applications interface (API) in our project. uVision5's RTE manager includes a suitable API, but this provides many more features than we need for our simple helloWorld recipe. So, for the time being, we'll use the simpler driver named Serial.c shown in step 4 and step 5 that ARM shipped with uVision4. File Serial.c comprises three functions SER_Init(), SER_PutChar(), and SER_GetChar(). The function SER_Init() is the first function called by main(). It initializes the USART peripheral by writing values to its registers so that it is configured to mirror the channel setup in PuTTY (that is, 115200 baud, 8 data-bits, 1 stop-bit). These parameters are critical. The baud rate is derived from the Peripheral Clock, and in turn the System Clock, so any change in the clock configuration will affect the baud rate. The baud rate is set by the value we write to the Baud Rate Register (BRR). Reference manual RM0090 (www.st.com) describes this as calculated by

How it works…

Rearranging the preceding formula, with OVER8 = 1 (since we're using 8 x oversampling) and fclk = 42 MHz we get:

How it works…

The other two functions read and write characters from/to the USART (these perform the low-level I/O ). We'll discuss this in more detail in Chapter 3, Assembly Language Programming.

Any program that wishes to use the services that Serial.c provides must include its function prototype. To facilitate this, the prototypes are declared in a so-called header file called Serial.h shown in step 6, and included in the program using a #include preprocessor directive (for example, see line 15 of main.c). If we look closely at Serial.h, we see the prototypes are preceded by the qualifier extern. This is a message to the compiler that the functions are defined in another file (that is, not main.c), and the function call reference must be resolved later by the linker. We can also see that the prototype declarations are enclosed within a conditional preprocessor statement, that is:

#ifndef __SERIAL_H
#define __SERIAL_H

/* function prototypes */

#endif

This ensures that the code enclosed within the conditional preprocessor statement is included in the project only once, even though both, main.c and Serial.c, include the statement:

#include "serial.h" 

The main() function calls printf() to output the string "Hello World ". The string "Hello World " is stored as a sequence of characters terminated by a NULL character. C interprets ' ' as a newline character, but the actual ASCII code ( http://en.wikipedia.org/wiki/ASCII) used to represent newline varies between operating systems; so to cover all eventualities, we can configure PuTTY as shown in step 7.

The function printf() is defined in C's standard input output library <stdio.h>. This function calls fputc(), which is also defined in <stdio.h>, but redefined in Retarget.c. So it calls SER_PutChar() to send the characters to the USART. Most microcontrollers use this technique to allow them to make use of the C library functions printf() and, as we'll see later, scanf() too.

File Retarget.c also uses the preprocessor directive #pragma, which is used to specify machine- or operating system-specific compiler features. In this case, the directive is used to disable semihosting. Semihosting is a mechanism that allows ARM targets to communicate with a host computer using the JTAG interface. Semihosting can be used with the function trace_printf(), to enable debug statements to write to the output window of the IDE. Obviously, we can achieve similar functionality using the COM port and PuTTY.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.185.170