STMicroelectronics: Cortex™-M4 Training STM32F407 Discovery

STMicroelectronics: Cortex™-M4 Training STM32F407 Discovery
STMicroelectronics: Cortexв„ў-M4 Training STM32F407
Discovery evaluation board using ARMВ® Keilв„ў MDK Toolkit
featuring Serial Wire Viewer
Summer 2012 Based on RB Version 1.1,
Original document by Robert Boys (ARM), [email protected]
Adapted for SHU Laboratories by Alan Holloway (SHU), [email protected]
AH version 1.1
The purpose of this lab is to introduce you to the STMicroelectronics Cortexв„ў-M4 processor using the ARMВ® Keilв„ў MDK
toolkit featuring the IDE ВµVisionВ®. We will use the Serial Wire Viewer (SWV) and the on-board ST-Link V2 Debug Adapter.
At the end of this tutorial, you will be able to confidently work with these processors and Keil MDK. See
Keil MDK supports and has examples for most ST ARM processors. Check the Keil Device DatabaseВ® on
for the complete list which is also included in MDK: in µVision, select Project/Select Device for target…
Keil MDK-Liteв„ў is a free evaluation version that limits code size to 32 Kbytes. Nearly all Keil examples will compile within
this 32K limit. The addition of a valid license number will turn it into the full commercial version.
RTX RTOS: All variants of MDK contain the full version of RTX with source code. See
Why Use Keil MDK ?
MDK provides these features particularly suited for Cortex-M users:
ВµVision IDE with Integrated Debugger, Flash programmer and
the ARMВ® Compiler toolchain. MDK is a turn-key product.
2. A full feature Keil RTOS called RTX is included with MDK.
RTX comes with a BSD type license. Source code is provided.
3. Serial Wire Viewer and ETM trace capability is included.
4. RTX Kernel Awareness window. It is updated in real-time.
5. Kernel Awareness is available for Keil RTX, CMX, Quadros,
Micrium and FreeRTOS. All RTOSs will compile with MDK.
6. Keil Technical Support is included for one year and is easily
renewable. This helps you get your project completed faster.
This document details these features:
1. Serial Wire Viewer (SWV) and ETM trace. Real-time tracing updated while the program is running.
Real-time Read and Write to memory locations for Watch, Memory and RTX Tasks windows. These are nonintrusive to your program. No CPU cycles are stolen. No instrumentation code is added to your source files.
Six Hardware Breakpoints (can be set/unset on-the-fly) and four Watchpoints (also known as Access Breaks).
RTX Viewer: a kernel awareness program for the Keil RTX RTOS that updates while your program is running.
A DSP example program using ARM CMSIS-DSP libraries.
Serial Wire Viewer (SWV):
Serial Wire Viewer (SWV) displays PC Samples, Exceptions (including interrupts), data reads and writes, ITM (printf), CPU
counters and a timestamp. This information comes from the ARM CoreSightв„ў debug module integrated into STM32 CPU.
SWV does not steal any CPU cycles and is completely non-intrusive. (except for the ITM Debug printf Viewer).
CoreSight displays memory contents and variable values in real-time and these can be modified on-the-fly.
Embedded Trace Macrocell (ETM):
ETM displays all the program counter values that were executed. This is very useful for debugging program flow problems
such as “going into the weeds” and “how did I get here?”. Keil µVision uses ETM to provide Code Coverage, Performance
Analysis and code execution times. ETM requires a special debug adapter such as a ULINKpro. The Discovery series do not
have the ETM connector even though the processor has ETM. Most other ST and Keil boards do have this connector.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
Discovery Board Debug Adapter Connections:
The STM32F407 Discovery board lacks the standard ARM debugger connections. This means it is not easy to connect a
ULINK2, ULINKpro or J-Link to these boards. In order to use features like ETM trace, it is easier to obtain a board such as
the Keil MCBSTM32 series or a STM32xxx-EVAL board. Versions are available with Cortex-M3 and Cortex-M4 processors.
Keil MDK has examples and labs for these boards. This document uses only the on-board ST-LINK. See
1. Keil Evaluation Software:
2. Keil Software Installation:
3. CoreSight Definitions:
4. CMSIS: Cortex Microcontroller Software Interface Standard
5. Configuring the ST-Link V2:
6. Blinky example:
7. Running and debugging your program:
8. Hardware Breakpoints:
9. Call Stack & Locals window:
10. Watch and Memory windows and how to use them:
11. How to view Local Variables in Watch and Memory windows:
12. View Variables Graphically with the Logic Analyzer (LA):
13. Watchpoints: Conditional Breakpoints
14. ITM (Instruction Trace Macrocell):
15. Serial Wire Viewer (SWV) Configuration window:
16. Creating your own project from scratch:
17. Serial Wire Viewer summary:
18. Useful Documents:
19. Keil Products and contact information:
Notes on using this document by Bob Boys:
The latest version of the original document (Robert Boys) and the necessary example source files are available here:
Caution! The example files are different from the SHU example
Configuring the ST-Link V2 debug adapter starts on page 4.
The on-board ST-Link V2 is used by default in this document. All you need install is the USB driver.
If you are using MDK 4.54 or earlier, a patch is available to enhance the performance of the ST-Link V2. It is
available on the website above. If you are using MDK 4.60 or later, you do not need this patch.
The original ST-Link (usually called V1) is supported by ВµVision but Serial Wire Viewer is not.
The first exercise starts at section 6. You can go directly there if using a ST-Link If you are using a ULINK2,
ULINK-ME or a ULINKpro, you will need to configure it appropriately as described on the following pages.
The ST-Link V2 interfaces very well with Keil ВµVision and its performance is quite good including SWV.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
1) Keil Evaluation Software:
Example Programs:
MDK contains many useful ready-to-run examples for boards using ST processors. See C:\Keil\ARM\Boards\ST and \Keil.
Keil has several labs for various STM32 processors including one using CAN. See for details.
STMicroelectronics has an entire suite of examples for various STM32 processors using Keil MDK. See
Keil Sales: In USA and Canada: [email protected] or 800-348-8051. Outside the US: [email protected]
2) Keil Software Installation:
This document was written using Keil MDK 4.54 or later which contains µVision 4. The evaluation copy of MDK (MDKLite) is available free on the Keil website. Do not confuse µVision 4 with MDK 4.0. The number “4” is a coincidence.
Nearly all example programs can be compiled within the 32K limit of MDK-Lite: the free evaluation version.
To obtain a copy of MDK go to and select the “Download” icon located on the right side.
You can use the evaluation version of MDK-Lite and a ULINK2, ULINK-ME, ULINKpro or J-Link (black case) for this lab.
If you are using MDK 4.54 or earlier, get the ST-Link V2 patch form the Keil website (URL is above) and extract the files to
C:\Keil\ARM\STLink\ If you are using MDK 4.60 or later you do not need this patch.
3) CoreSight Definitions: It is useful to have a basic understanding of these terms:
JTAG: Provides access to the CoreSight debugging module located on the Cortex processor. It uses 4 to 5 pins.
SWD: Serial Wire Debug is a two pin alternative to JTAG and has about the same capabilities except for no
Boundary Scan. SWD is referenced as SW in the ВµVision Cortex-M Target Driver Setup. See page 4, 2nd picture.
The SWJ box must be selected in ULINK2/ME or ULINKpro. SWV must use SWD because of the TDIO conflict
described in SWO below.
SWV: Serial Wire Viewer: A trace capability providing display of reads, writes, exceptions, PC Samples and printf.
DAP: Debug Access Port. A component of the ARM CoreSight debugging module that is accessed via the JTAG or
SWD port. One of the features of the DAP are the memory read and write accesses which provide on-the-fly memory
accesses without the need for processor core intervention. ВµVision uses the DAP to update memory, watch and
RTOS kernel awareness windows in real-time while the processor is running. You can also modify variable values
on the fly. No CPU cycles are used, the program can be running and no code stubs are needed in your sources.
You do not need to configure or activate DAP. ВµVision does this automatically when you select the function.
SWO: Serial Wire Output: SWV frames usually come out this one pin output. It shares the JTAG signal TDIO.
Trace Port: A 4 bit port that ULINKpro uses to collect ETM frames and optionally SWV (rather than SWO pin).
ETM: Embedded Trace Macrocell: Provides all the program counter values. Only the ULINKpro provides ETM.
4) CMSIS: Cortex Microcontroller Software Interface Standard
ARM CMSIS-DSP libraries are offered for all Cortex-M3 and Cortex-M4 processors.
CMSIS-RTOS provides standard APIs for RTOSs. RTX is a free RTOS available from ARM as part of CMSIS Version 3.0.
STMicroelectronics example software is CMSIS hardware abstraction layer compliant.
See and for more information.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
5) Configuring the ST-Link V2:
The configuration settings have already been completed in the SHU example and suitable
drivers installed on the SHU lab PC’s. If you are using a SHU PC in the embedded systems
lab go directly to section 6.
If you want to run this discovery board example on your own PC you will however be
required to install the ST-link driver. Follow the sections 1-5 below for details
It is easy to select a USB debugging adapter in ВµVision. You must configure the connection to both the target and to Flash
programming in two separate windows as described below. They are each selected using the Debug and Utilities tabs.
Using other Debug Adapters: This document will use the on-board ST-Link. You can use a ULINK2 or a ULINKpro with
suitable adjustments. You would need a suitable adapter to connect a different adapter to the SWD connector on the
Discovery board. Some step(s) to turn off the on-board ST-Link adapter will also be necessary to avoid conflicts. It is
reported that shorting solder bridge SB10 will hold the ST-Link processor in RESET allowing external adapter operation.
If your debugging sessions are unreliable, please check for additional conflicts or loading on the SWD pins. The SWD
connector provides the ability to use the Discovery board as a debug adapter on another board. Its main purpose is not to
connect an external tool such as a Keil ULINK2. Some adaptation is required.
The ST-Link is selected as the default debug adapter for the Keil examples for the Discovery board.
Serial Wire Viewer (SWV) is completely supported by ST-LINK Version 2. Firmware V2.16.S0.
Step 1) Installing the ST-Link USB Drivers: (you need to do this the first time only)
Do not have the Discovery board USB port connected to your PC at this time.
The USB drivers must be installed manually by executing ST-Link_V2_USBdriver.exe. This file is found in
C:\Keil\ARM\STLink\USBDriver. Find this file and double click on it.
Plug in the Discovery board to USB CN1. The USB drivers will now finish installing in the normal fashion.
Super TIP: The ST-Link V2 firmware update files are located here: C:\Keil\ARM\STLink. This updates the Discovery STLink firmware by executing ST-LinkUpgrade.exe. Find this file and double click on it. It will check and report the current
firmware version. It is important you are using firmware V2.J16.S0 or later for proper SWV operation. The patch for MDK
4.54 and earlier is here:
Continues on the next page……..
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
Step 2) Select the debug connection to the target: The following steps are already done by default in the example programs.
These instructions are provided for reference.
Connect your PC to the Discovery board with a USB cable. Start ВµVision. It must be in Edit mode (as it is when
first started – the alternative to Debug mode) and you have selected a valid project.
or ALT-F7 and select the Debug tab. In the drop-down menu box select ST-Link
Select Target Options
Debugger as shown here:
TIP: Do NOT select ST-Link (Deprecated Version).
Select Settings and the next window below opens up. This is the control
panel for the ULINKs and ST-Link. (they are the same).
In Port: select SW. JTAG is not a valid option for ST-Link and this board. SW is also known as SWD.
In the SW Device area: ARM CoreSight SW-DP MUST be displayed. This confirms you are connected to the target
processor. If there is an error displayed or it is blank this must be fixed before you can continue. Check the target
power supply. Cycle the power to the board.
TIP: To refresh this screen select Port: and change it or click
OK once to leave and then click on Settings again.
TIP: You can do everything with SW (SWD) as you can with
JTAG except for boundary scan.
Next: configure the Keil Flash programming tool:
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
Step 3) Configure the Keil Flash Programmer:
Click on OK once and select the Utilities tab.
Select the ULINK similar to Step 2 above.
Click Settings to select the programming algorithm if it is not visible or is the wrong one.
Select STM32F4xx Flash as shown here or the one for your processor:
10. Click on OK once.
TIP: To program the Flash every time you enter Debug mode,
check Update target before Debugging.
11. Click on OK to return to the ВµVision main screen.
12. Select File/Save All.
13. You have successfully connected to the STM32 target
processor and configured the ВµVision Flash
TIP: The Trace tab is where you configure the Serial Wire
Viewer (SWV). You will learn to do this later.
COM led LD1 indication:
LED is blinking RED: the first USB enumeration with the PC is taking place.
LED is RED: communication between the PC and ST-LINK/V2 is established (end of enumeration). ВµVision is not connected
to ST-Link (i.e. in Debug mode).
LED is GREEN: ВµVision is connected in Debug mode and the last communication was successful.
LED is blinking GREEN/RED: data is actively being exchanged between the target and ВµVision.
No Led: ST-LINK/V2 communication with the target or ВµVision has failed. Cycle the board power to restart.
Running programs in the internal STM32 RAM:
It is possible to run your program in the processor RAM rather than Flash. In this case, the Flash programming tool is not
used nor is the Load icon. After successfully compiling the source files, click on Debug icon
processor and loads your executable into RAM.
. An .ini file configures the
The Discovery Blinky project has a RAM setting. Select STM32F407 RAM as shown
here if you want to try this mode.
Loading and Running your program into RAM:
Select STM32F407 RAM as shown above.
Select Target Options
The ini file is located in the Initialization File: box as shown here:
Click on Edit… to view its contents.
Click on OK to return to the main ВµVision window.
Return to the STM32F407 Flash setting.
or ALT-F7 and select the Debug tab.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
6) Downloading the Blinky example program using the ST Discovery board:
We will connect a Keil MDK development system using real target hardware using the built-in ST-Link debug adapter.
Download the example file as instructed by your lecturer (from the SHU BB site) and unzip the contents to a suitable
location in your homespace.
Start ВµVision by clicking on its desktop icon.
Select Project/Open Project. Open the file Blinky.uvproj located in the folder you have just unzipped.
By default, the ST-Link is selected. If this is the first time you have run ВµVision and the Discovery board, you will
need to install the USB drivers. See the configuration instructions on the preceding page in this case.
Compile the source files by clicking on the Rebuild icon.
. You can also use the Build icon beside it.
Program the STM32 flash by clicking on the Load icon:
Progress will be indicated in the Output Window.
Select OK if the Evaluation Mode box appears.
Enter Debug mode by clicking on the Debug icon.
Note: You only need to use the Load icon to download to FLASH and not for RAM operation if it is chosen.
Click on the RUN icon.
Connect your PC to the board with a USB cable to CN1.
Note: you stop the program with the STOP icon.
The Green LED on the STM32F4 Discovery board will now blink.
Now you know how to compile a program, program it into the STM32 processor Flash, run it and stop it !
Note: The board will start Blinky stand-alone. Blinky is now permanently programmed in the Flash until reprogrammed.
7) Running and debugging the Blinky example program:
If you’re extremely lucky or exceptional good at embedded systems downloading your (perfect) program and letting it run as
previously described would be it. However it’s more than likely your that your program may contain errors or not operate
quite as you intended; in this instance the run and debug capabilities prove invaluable.
Similar to other programming environments you may be familiar with ВµVision has many features such as single stepping and
step over, step into (+ many more advanced features) which help you locate and fix bugs in your code. The following section
describes some of the basic debug features the remaining sections give details on some more advanced features.
Make sure you are currently in Debug mode and reset the chip.
Place your cursor at the line GPIOD->ODR = GPIOD->ODR | 0x00001000; and click Run to Cursor Line
your program should start execution and halt at the selected line with the
Now step
(single step) through the next line of code and notice what happens – hopefully one LED on your board
should now be lit.
If you not have already done so keep stepping until you reach the delay function and then single step one more time –
What happened? Your debugger has correctly gone to the next line code in your program (remember) the delay is a
You could now keep single stepping until the end function and or click step out
which would automatically run the
remaining function code and then return to main()at the line after the function call.
In many cases you may be confident your functions are working correctly and simply want to execute the complete
function call in a single mouse click. This is done by clicking the step over command.
Experiment with the different debug features and make sure you fully understand what each one does.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
showing the next executable
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
The table below gives a brief description of the some of the debug options available further details can be found in the help
sections within ВµVision.
Debug Menu
Start/Stop Debug Session
Starts or stops a debugging session.
Reset CPU
Sets the CPU to RESET state.
Continues executing the program until the next active
breakpoint is reached.
Stops the program execution immediately.
Executes a single-step into a function; Executes the
current instruction line.
Step Over
Executes a single-step over a function.
Step Out
Finishes executing the current function and stops
Run to Cursor Line
Executes the program until the current cursor line is
Show Next Statement
Shows the next executable statement/instruction.
Opens the dialog Breakpoints.
Toggles the breakpoint on the current line.
Enables/disables the breakpoint on the current line.
Disable All Breakpoints
Disables all breakpoints in the program.
Kill All Breakpoints
Removes all breakpoints in the program.
System Viewer
The system viewer windows display information about peripheral registers. In the Blinky example we have modified the
contents of a several registers to configure the I/O port (GPIOD) with the appropriate settings and to write to the port itself as
shown in the statements below.
&= ~((3UL << 2*12));
GPIOD->ODR |= (1UL <<
/* Configure PD.12 as output */
/* Set bit 12 high */
To open the System Viewer window and view the registers for GPIOD. From the taskbar select View – System Viewer >
GPIO > GPIOD. Alternatively Select the small arrow to the right hand side of the system viewer icon
GPIOD. Each of the registers associated with GPIOD can then be examined.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
and navigate to
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
The picture shows GPIOD in the System Viewer and the
Output Data Register (ODR) expanded. ODR bit 12 has been
set indicated by the tick in the designated field.
Use the System Viewer to:
View peripheral register property values.
View additional information about a property
Change property values at runtime. Click into the valuefield and enter a new value.
Search for specific properties using the search field.
8) Hardware Breakpoints:
The STM32F4 has six hardware breakpoints that can be set or unset on the fly while the program is running.
With Blinky running, in the main debug window, click on a darker block in the left margin on a line in main() in the
while loop.
A red circle will appear and the program will stop.
Note the breakpoint is displayed in both the disassembly and source windows as shown below:
You can a breakpoint in either the Disassembly or Source windows as long there is a gray rectangle indicating the
existence of an assembly instruction at that point.
Every time you click on the RUN icon
the program will
run until the breakpoint is again encountered.
You can also click on Single Step (Step In)
and Step Out
, Step Over
TIP: If single step (Step In) doesn’t work, click on the Disassembly
window to bring it into focus. If needed, click on a disassembly line.
This tells ВµVision you want to single step at the assembly level rather
than at the C source level.
TIP: A hardware breakpoint does not execute the instruction it is set
to. ARM CoreSight breakpoints are no-skid. This is a rather
important feature. Earlier versions of ВµVision required a double click
to set a breakpoint and displayed as a square.
Remove all the breakpoints when you are done for the
next exercise.
TIP: You can delete the breakpoints by clicking on them or selecting
Debug/Breakpoints (or Ctrl-B) and selecting Kill All. Click on Close
to return.
TIP: You can view the breakpoints set by selecting
Debug/Breakpoints or Ctrl-B.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
9) Call Stack + Locals Window:
Local Variables:
The Call Stack and Local windows are incorporated into one integrated window. Whenever the program is stopped, the Call
Stack + Locals window will display call stack contents as well as any local variables belonging to the active function.
If possible, the values of the local variables will be displayed and if not the message <not in scope> will be displayed. The
Call + Stack window presence or visibility can be toggled by selecting View/Call Stack window.
Run and Stop Blinky. Click on the Call Stack + Locals tab.
Shown is the Call Stack + Locals window.
The contents of the local variables are displayed as well as
names of active functions. Each function name will be
displayed as it is called from the function before it or from an
interrupt or exception.
When a function exits, it is removed from the list.
The first called function is at the bottom of this table.
This table is active only when the program is stopped.
Click on the Step In icon or F11:
Note the function different functions displayed as you step through them. If you get trapped in the Delay function,
Click numerous times on Step In and see other functions.
Right click on a function name and try the Show Callee
Code and Show Caller Code options as shown here:
Click on the StepOut icon
return to main().
use Step Out
or Ctrl-F11 to exit it faster.
to exit all functions to
TIP: If single step (Step In) doesn’t work, click on the Disassembly window to bring it into focus. If needed, click on a
disassembly line.
TIP: You can modify a variable value in the Call Stack & Locals window when the program is stopped.
TIP: This is standard “Stop and Go” debugging. ARM CoreSight debugging technology can do much better than this. You
can display global or static variables updated in real-time while the program is running. No additions or changes to your code
are required. Update while the program is running is not possible with local variables because they are usually stored in a
CPU register. They must be converted to global or static variables so they always remain in scope.
Changing a local variable to a static or global normally means it is moved from a CPU register to RAM. CoreSight can view
RAM but not CPU registers when the program is running.
Call Stack:
The list of stacked functions is displayed when the program is stopped as you have seen. This is useful when you need to
know which functions have been called and what return data is stored on the stack.
TIP: You can modify a variable value when the program is stopped.
TIP: You can access the Hardware Breakpoint table by clicking on Debug/Breakpoints or Ctrl-B. This is also where
Watchpoints (also called Access Points) are configured. You can temporarily disable entries in this table.
Selecting Debug/Kill All Breakpoints deletes Breakpoints but not Watchpoints.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
10) Watch and Memory Windows and how to use them:
The Watch and memory windows will display updated variable values in real-time. It does this through the ARM CoreSight
debugging technology that is part of Cortex-M processors. It is also possible to “put” or insert values into these memory
locations in real-time. It is possible to “drag and drop” variable names into windows or enter them manually.
Watch window:
Add a global variable: Recall the Watch and Memory windows can’t see local variables unless stopped in their function.
Stop the processor
and exit debug mode.
In the Blinky example program declare a global variable (I called it value) outside of main() like this:
unsigned int value = 0;
Add the statements below after the last Soft_Delay(); in
if (value > 0x10) value = 0;
3. Click on Rebuild.
. Click on Load
Click on RUN
to program the Flash.
. Recall you can set Watch and Memory windows while the program is
Enter Debug mode.
Open the Watch 1 window by clicking on the Watch 1 tab as shown or select View/Watch Windows/Watch 1.
In Blinky, block value, click and hold and drag it into Watch 1. Release it and value will be displayed as shown
value will increment until 0x10 in real-time.
TIP: You can also right click on the variable name and select
Add value to … and select Watch 1.
TIP: Make sure View/Periodic Window Update is selected.
You can also enter a variable manually by doubleclicking under Name or pressing F2 and using copy and paste or typing the variable.
TIP: To Drag �n Drop into a tab that is not active, pick up the variable and hold it over the tab you want to open; when it
opens, move your mouse into the window and release the variable.
Double click on the value for value in the Watch window. Enter the value 0 and press Enter. 0 will be inserted into
memory in real-time. If this variable is updated very quickly, it is possible to not see the result…but it happened.
Memory window:
Drag �n Drop value into the Memory 1 window or enter it manually. Select View/Memory Windows if necessary.
Note the value of value is displaying its address in Memory 1 as if it is a pointer. This is useful to see what address
a pointer is pointing to but this not what we want to see at this time.
Add an ampersand “&” in front of the variable name and press Enter. The physical address is shown (0x2000_0014).
Right click in the memory window and select Unsigned/Int.
The data contents of value is now displayed as a 32 bit value.
Both the Watch and Memory windows are
updated in real-time.
You can modify value in the Memory
window with a right-click with the mouse
cursor over the data field and select Modify
TIP: No CPU cycles are used to perform these
operations. See the next page for an explanation how this works.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
TIP: To view variables and their location use the Symbol window. Select View/Symbol Window while in Debug mode.
Serial Wire Viewer does not need to be configured in order for the Memory and Watch windows to operate as shown. This
mechanism uses a different feature of CoreSight than SWV. These Read and Write accesses are handled by the Serial Wire
Debug (SWD) or JTAG connection via the CoreSight Debug Access Port (DAP), which provides on-the-fly memory accesses.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
11) How to view Local Variables in the Watch or Memory windows:
Make sure Blinky is running. We will use the local variable �i’ from Soft_Delay(). Locate where the local
variable(s) are declared in the function Soft_Delay()
Drag and Drop each variable (1 in this example) into Watch 1 window. Note it says < not in scope > because
ВµVision cannot access the CPU registers while running
which is where the variable is probably located.
Set a breakpoint in the Soft_Delay() function while loop.
The problem will stop the program and the current
variable values will appear.
TIP: Remember: you can set and unset hardware breakpoints on-the-fly in the Cortex-M4 while the program is running !
ВµVision is unable to determine the value of these variables when the program is running because they exist only
when Soft_Delay() is running. They disappear in when not executing the local function that they are declared in.
They are a local or automatic variable and this means it is probably stored in a CPU register which ВµVision is unable
to access during run time.
Remove the breakpoint and make sure the program is not running
. Exit Debug mode.
How to view local variables updated in real-time:
All you need to do is to make the local variables global where it is declared in the Blinky source file!
Move the variable declarations outside of any your functions and main() - making them global variables:
TIP: You can edit files in edit or debug mode. However, you can compile them only in edit mode.
Compile the source files by clicking on the Rebuild icon. They will compile with no errors or warnings.
To program the Flash, click on the Load icon.
. A progress bar will be displayed at the bottom left.
TIP: To program the Flash automatically when you enter Debug mode select Target Options
select the “Update Target before Debugging” box.
, select the Utilities tab and
Enter Debug mode.
Click on RUN.
Now the variables are updated in real-time. This is ARM CoreSight technology working.
You can read (and write) global, static variables and structures. Anything that stays around in a variable from
function to function. This includes reads and writes to peripherals.
Stop the CPU and exit debug mode for the next step.
TIP: View/Periodic Window Update must be selected. Otherwise variables update only when the program is stopped.
How It Works:
ВµVision uses ARM CoreSight technology to read or write memory locations without stealing any CPU cycles. This is nearly
always non-intrusive and does not impact the program execution timings. Remember the Cortex-M4 is a Harvard architecture.
This means it has separate instruction and data buses. While the CPU is fetching instructions at full speed, there is plenty of
time for the CoreSight debug module to read or write values without stealing any CPU cycles.
This can be slightly intrusive in the unlikely event the CPU and ВµVision reads or writes to the same memory location at
exactly the same time. Then the CPU will be stalled for one clock cycle. In practice, this cycle stealing never happens.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
12) View Variables Graphically with the Logic Analyzer (LA):
We will display the global variable value you created earlier in the Logic Analyzer. No code stubs in the user code will be
used. This uses the Serial Wire Viewer and therefore
does not steal CPU cycles.
Stop the processor
and exit Debug mode.
Configure Serial Wire Viewer (SWV):
Select Target Options
or ALT-F7 and select
the Debug tab. Select Settings: on the right side
of this window. Confirm SW is selected. SW
selection is mandatory for SWV. ST-Link uses
only SWD. Select the Trace tab.
In the Trace tab, select Trace Enable. Unselect
Periodic and EXCTRC. Set Core Clock: to 168
MHz. Everything else is set as shown here:
Click OK once to return to the Debug tab.
in the Initialization File: box: select
STM32_SWO.ini. You can use the Browse icon to locate and enter it.
This file configures the STM32 SWV module and default is for SWV.
Click OK once to return to the main menu. Enter debug mode.
Configure Logic Analyzer:
Open View/Analysis Windows and select Logic Analyzer or select the LA window on the toolbar.
Click on the Blinky.c tab. Right click on value and select Add value to… and then select Logic Analyzer. You can
also Drag and Drop or enter manually.
Click on the Select box and the LA Setup window appears:
With value selected, set Display Range Max: to 0x15 as shown here:
Click on Close.
Run Program: Note: The LA can be configured while the program is running.
1) Click on Run.
Click on Zoom Out until Grid is about 1 second.
2) The variable value will increment to 0x10 (decimal 16) and then is set to 0.
TIP: You can show up to 4 variables in the Logic Analyzer. These variables must be
global, static or raw addresses such as *((unsigned long *)0x20000000).
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
13) Watchpoints: Conditional Breakpoints
Recall STM32 processors have 6 hardware breakpoints. These breakpoints can be set on-the-fly without stopping the CPU.
The STM32 also have four Watchpoints. Watchpoints can be thought of as conditional breakpoints. The Logic Analyzer uses
the same comparators as Watchpoints in its operations. This means in ВµVision you must have two variables free in the Logic
Analyzer to use Watchpoints. Watchpoints are also referred to as Access Breakpoints.
Use the same Blinky configuration as the previous page. Stop the program if necessary. Stay in debug mode.
2. We will use the global variable value you created in Blinky.c to explore Watchpoints.
The Trace does not need to be configured for Watchpoints. However, we will use it in this exercise.
The variable value should be still entered in the Logic Analyzer from the last exercise on the previous page.
Select Debug in the main ВµVision window and select Breakpoints or press Ctrl-B.
In the Expression box enter: “value = = 0x5” without the quotes. Select both the Read and Write Access.
Click on Define and it will be accepted as shown here: Click on Close.
Enter the variable value to the Watch 1 window
by dragging and dropping it if it is not already
Open Debug/Debug Settings and select the trace
tab. Check “on Data R/W sample”, uncheck
EXTRC and ITM 31 and 0.
10. Click on OK twice. Open the Trace Records
11. Click on RUN
12. You will see value change in the Logic Analyzer
as well as in the Watch window.
13. When value equals 0x5, the Watchpoint will
stop the program.
14. Note the data writes in the Trace Records window shown below. 0x5 is in the last Data column. Plus the address the
data written to and the PC of the write instruction. This is with the ST-Link. A ULINK2 will show the same
window. A ULINKpro or a J-Link (black case) will show a different display.
15. There are other types of expressions
you can enter and are detailed in the
Help button in the Breakpoints
16. To repeat this exercise, click on RUN.
17. When finished, stop the program, click on Debug and select Breakpoints (or Ctrl-B) and Kill the Watchpoint.
18. Leave Debug mode.
TIP: You cannot set Watchpoints on-the-fly while the program is running like you can with hardware breakpoints.
TIP: To edit a Watchpoint, double-click on it in the Breakpoints
window and its information will be dropped down into the
configuration area. Clicking on Define will create another
Watchpoint. You should delete the old one by highlighting it and
click on Kill Selected or try the next TIP:
TIP: The checkbox beside the expression allows you to
temporarily unselect or disable a Watchpoint without deleting it.
TIP: Raw addresses can also be entered into the Logic Analyzer.
An example is: *((unsigned long *)0x20000000)
Shown above right is the Logic Analyzer window displaying the variable value trigger point of 0x5.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
14) ITM (Instruction Trace Macrocell)
This example uses a ST-Link V2 or ULINK2.
ITM Port 0 is available for a printf type of instrumentation that requires minimal user code. After the write to the ITM port,
zero CPU cycles are required to get the data out of the processor and into ВµVision for display in its Debug (printf) Viewer
Completed in the Blinky_SHU example start at section
Note: Steps 1-10 of this section have already been completed in the example SHU Blinky example, the following material is
therefore for reference if required in a future example or project of your own.
Stop the program if it is running and exit Debug mode.
Open the project Blinky project (do not use RTX_Blinky).
Add this code to main source file in the Blinky project. A good place is near the top of your program,
just after the #includes.
#define ITM_Port8(n)
(*((volatile unsigned char *)(0xE0000000+4*n)))
In the main function in Blinky place the following code
while (ITM_Port8(0) == 0);
ITM_Port8(0) = 0x0D;
while (ITM_Port8(0) == 0);
ITM_Port8(0) = 0x0A;
Rebuild the source files, program the Flash memory and enter debug mode.
Open Select Target Options
Configure the Serial Wire Viewer as described on page 9. Use 168 MHz for the Core Clock.
Unselect On Data R/W Sample, EXCTRC and PC Sample. (this is to help not overload the SWO port)
Select ITM Port 0. ITM Stimulus Port “0” enables the Debug
(prinff) Viewer.
or ALT-F7 and select the Debug tab, and then the Trace tab.
10. Click OK twice. Enter Debug mode.
11. Click on View/Serial Windows and select Debug (printf) Viewer and click on
12. In the Debug (printf) Viewer you will see the ASCII of value appear.
Note: The SHU version has some additional code added which allows the use of traditional printf
statements in your code. If not already completed add the following sections of code to your
program as shown in the below.
After the end of the while(1)loop within main add the statements below and run your program. The message from the
printf statement should be output to the Debug (printf) Viewer.
while (ITM_Port8(0) == 0);
/* wait until ITM port is ready */
printf("\n The Green LED should be Flashing");
/* print your message here*/
In this instance the printf command only accepts ASCII characters if you need to print a variable such as the integer called
value in the example to the screen you will need to convert the value to an ASCII string using a suitable character buffer and
the sprint command, see details below.
Create a suitable storage space for your ASCII string, a global character array variable with enough places to hold the number
of characters required.
char char_buf[10]; // character buffer big enough for the number of digits required
To convert the variable to its ASCII character representation using the sprintf() function and then printing out the characters
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
using the printf function.
sprintf(char_buf, "%d", value);
//convert value to ASCII
printf("\n value = %s", char_buf);
Trace Records
Open the Trace Records if not already open. Double click on it to clear it.
You will see a window such as the one below with ITM and Exception frames.
What Is This ?
ITM 0 frames (Num column) are our ASCII characters from the text we have outputted. Each ASCII value is
displayed in the Data column.
All these are timestamped in both CPU cycles and time in seconds.
When you are done, stop the processor and exit debug mode.
0x4C = ASCII �L’
0x45 = ASCII �E’
0x44 = ASCII �D’
ITM Conclusion
The writes to ITM Stimulus Port 0 are intrusive and are usually one cycle. It takes no CPU cycles to get the data out the
processor and to your PC via the Serial Wire Output pin.
TIP: It is important to select as few options in the Trace configuration as possible to avoid overloading the SWO pin. Enter
only those features that you really need.
Super TIP: ITM_SendChar is a useful function you can use to send ITM characters. It is found in the header core.CM3.h.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
15) Serial Wire Viewer (SWV) Configuration window: (for reference)
The essential place to configure the trace is in the Trace tab as shown below. You cannot set SWV globally for ВµVision. You
must configure SWV for every project and additionally for every target settings within a project you want to use SWV. This
configuration information will be saved in the project. There are two ways to access this menu:
A. In Edit mode: Select Target Options
or ALT-F7 and select the Debug tab. Select Settings: on the right side of
this window and then the Trace tab. Edit mode is selected by default when you start ВµVision.
B. In Debug mode: Select Debug/Debug Settings and then select the Trace tab. Debug mode is selected with
1) Core Clock: The CPU clock speed for
SWV. The CPU speed can be found in
your startup code or in Abstract.txt. It is
usually called SYSCLK or Main Clock.
This must be set correctly for all
adapters except ULINKpro.
2) Trace Enable: Enables SWV and ITM.
It can only be changed in Edit mode.
This does not affect the Watch and
Memory window display updates.
3) Trace Port: This is preset for ST-Link.
4) Timestamps: Enables timestamps and
selects the Prescaler. 1 is the default.
5) PC Sampling: Samples the program
a. Prescaler 1024*16 (the default) means every 16,384th PC is displayed. The rest are not collected.
b. Periodic: Enables PC Sampling.
c. On Data R/W Sample: Displays the address of the instruction that caused a data read or write of a variable
listed in the Logic Analyzer. This is not connected with PC Sampling but rather with data tracing.
6) ITM Stimulus Ports: Enables the thirty-two 32 bit registers used to output data in a printf type statement to
ВµVision. Port 31 (a) is used for the Keil RTX Viewer which is a real-time kernel awareness window. Port 0 (b) is
used for the Debug (printf) Viewer. The rest are currently unused in ВµVision.
Enable: Displays a 32 bit hex number indicating which ports are enabled.
• Privilege: Privilege is used by an RTOS to specify which ITM ports can be used by a user program.
7) Trace Events: Enables various CPU counters. All except EXCTRC are 8 bit counters. Each counter is cumulative
and an event is created when this counter overflows every 256 cycles. These values are displayed in the Counter
window. The event created when a counter wraps around is displayed in the Instruction Trace window.
a. CPI: Cycles per Instruction: The cumulative number of extra cycles used by each instruction beyond the
first, one including any instruction fetch stalls.
b. Fold: Cumulative number of folded instructions. These results from a predicted branch instruction where
unused instructions are removed (flushed) from the pipeline giving a zero cycle execution time.
c. Sleep: Cumulative number of cycles the CPU is in sleep mode. Uses FCLK for timing.
d. EXC: Cumulative cycles CPU spent in exception overhead not including total time spent processing the
exception code. Includes stack operations and returns.
e. LSU: Cumulative number of cycles spent in load/store operations beyond the first cycle.
f. EXCTRC: Exception Trace. This is different than the other items in this section. This enables the display
of exceptions in the Instruction Trace and Exception windows. It is not a counter. This is a very useful
feature to display exception events and is often used in debugging.
TIP: Counters will increment while single stepping. This can provide some very useful information. You can read these
counters with your program as they are memory mapped.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
16) Creating your own project from scratch: Using the Blinky source files:
All examples provided by Keil are pre-configured. All you have to do is compile them. You can use them as a starting point
for your own projects. However, we will start this example project from the beginning to illustrate how easy this process is.
We will use the existing source code files so you will not have to type them in. Once you have the new project configured;
you can build, load and run a bare Blinky example. It has an empty main() function so it does not do much. However, the
processor startup sequences are present and you can easily add your own source code and/or files. You can use this process to
create any new project, including one using an RTOS. This is for a STM32 processor and can be used for a Cortex-M4.
Note you will not be able to save to the c:\ .. directory on SHU PC machines. This section assumed you are using your own PC
and have suitable admin rights to create the project in the directories described.
Create a new project called Mytest:
1. With µVision running and not in debug mode, select Project/New µVision Project…
2. In the window Create New Project that opens, go to the folder C:\Keil\ARM\Boards\ST\STM32F4-Discovery.
Create a new folder and name your project:
3. Right click inside this window and create a new folder by selecting New/Folder. I named this new folder FAE.
4. Double-click on the newly created folder “FAE” to enter this folder. It will be empty.
5. Name your project in the File name: box. I called mine Mytest. You can choose your own name but you will have to
keep track of it. This window is shown here:
6. Click on Save.
Select your processor:
7. The Select Device for “Target 1” opens up as
shown at the bottom of this page:
8. This is the Keil Device DatabaseВ® which lists all
the devices Keil supports. You can create your
own if desired for processors not released yet.
9. Locate the ST directory, open it and select a
STM32F415ZG (or the device you are using).
Note the device features are displayed.
10. Select OK.
ВµVision will configure itself to this device.
Select the startup file:
11. A window opens up asking if you want to insert
the default STM32 startup file to your project.
Click on “Yes”. This will save you some time.
12. In the Project Workspace in the upper left hand of ВµVision, expand the folders Target 1 and Source Group 1 by
clicking on the “+” beside each folder.
13. We have now created a project called Mytest with the target hardware called Target 1 with one source assembly file
startup_stm32f4xx.s and using the STM32F4 processor you chose.
TIP: You can create more target hardware configurations and easily select them. This can include multiple Options settings,
simulation and RAM operations. See
Rename the Project names for convenience:
14. Click once on the name “Target 1” (or twice if not
already highlighted) in the Project Workspace and
rename Target 1 to something else. I chose
STM32F4 Flash. Press Enter to accept this change.
Note the Target selector in the main ВµVision window
also changes to STM32F4 Flash. This is your first
15. Similarly, change Source Group 1 to Startup. This
will add some consistency to your project with the
Keil examples. You can name these or organize
them differently to suit yourself.
16. Select File/Save All.
Continued on the next page…
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
Select the source files and debug adapter:
Using MS Explore (right click on Windows Start icon), copy blinky.c and system_stm32f4xx.c from
C:\Keil\ARM\Boards\ST\STM32F4-Discovery\Blinky to the ..\ STM32F4-Discovery\FAE folder you created. Copy
these files from an example project that utilizes a similar STM32 processor as you are using.
Source Files:
2. In the Project Workspace in the upper left hand of µVision, right-click on “STM32F4 Flash” and select “Add Group”.
Name this new group “Source Files” and press Enter. You can name it anything. There are no restrictions from Keil.
3. Right-click on “Source Files” and select Add files to Group “Source
4. Select the file Blinky.c and click on Add (once!) and then Close.
System File:
5. Right-click on “Startup” and select Add files to Group “Source Files”.
6. Select system_stm32f4xx.c and click on Add (once!) and then Close.
7. Your Project window will look similar to the one shown here:
Select your Debug Adapter:
8. By default the simulator is selected when you create a new ВµVision project. You probably need to change this to a
Debug adapter such as ST-Link, ULINK2 or ULINKpro.
Select Target Options
or ALT-F7 and select the Debug tab. Select ULINK/ME Cortex Debugger as shown
below: If you are using another adapter such as ST-Link, J-Link or ULINKpro, select the appropriate adapter from
the pull-down list.
10. Select JTAG/SWD hardware debugging (as opposed to selecting the Simulator) by checking the circle just to the left
of the word “Use:” as shown in the window to the right:
11. Select Run to Main (unless you do not want this)
12. Select the Utilities tab and select the appropriate debug adapter and
the proper Flash algorithm for your processor.
13. Click on the Target tab and select MicroLIB for smaller programs.
See for details.
14. Click on OK.
Modify Blinky.c
15. Double-click the file Blinky.c in the Project window to open it in the editing window or click on its tab if open.
16. Delete everything in Blinky.c except the main () function to provide a basic platform to start with:
iinclude <stdio.h>
#include "STM32F4xx.h"
/*--------------------------------------------------------MAIN function
int main (void) {
while (1);
loop forever
17. Select File/Save All
Compile and run the program:
18. Compile the source files by clicking on the Rebuild icon.
. You can also use the Build icon beside it.
19. Program the STM32 flash by clicking on the Load icon:
Progress will be indicated in the Output Window.
20. Enter Debug mode by clicking on the Debug icon.
21. Click on the RUN icon.
Note: you stop the program with the STOP icon.
22. The program will run but since while (1) is empty – it does not do much. It consists of a NOP and Branch to itself.
You can set a breakpoint on any assembly or source lines that have a darker grey box signifying assembly code.
23. You are able to add your own source code to create a meaningful project.
This completes the exercise of creating your own project from scratch.
You can also configure a new RTX project from scratch using the RTX_Blinky project.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
17) Serial Wire Viewer Summary:
Serial Wire Viewer can see:
Global variables.
Static variables.
Peripheral registers – just read or write to them.
Can’t see local variables. (just make them global or static).
Can’t see DMA transfers – DMA bypasses CPU and SWV by definition.
Serial Wire Viewer displays in various ways:
PC Samples.
Data reads and writes.
Exception and interrupt events.
CPU counters.
18) Useful Documents:
The Definitive Guide to the ARM Cortex-M3 by Joseph Yiu. (he also has one for the Cortex-M0) Search the web.
MDK-ARM Compiler Optimizations: Appnote 202:
Lazy Stacking and Context Switching Appnote 298: and search for Lazy Stacking.
A list of resources is located at:
Click on the Resources tab. Or search for “Cortex-M3” on and click on the Resources tab.
ARM Infocenter: Look for Application Notes and Cortex-M4 listings.
19) Keil Products and Contact Information:
Keil Microcontroller Development Kit (MDK-ARMв„ў)
MDK-Lite (Evaluation version) $0
MDK-Basic (256K Compiler Limit, No debug Limit) - $2,695
MDK-Standard (unlimited compile and debug code and data
size) - $4,895
MDK-Professional (Includes Flash File, TCP/IP, CAN and USB
driver libraries) $9,995
USB-JTAG adapter (for Flash programming too)
ULINK2 - $395 (ULINK2 and ME - SWV only – no ETM)
ULINK-ME – sold only with a board by Keil or OEM.
ULINKpro - $1,395 – Cortex-Mx SWV & ETM trace.
MDK also supports ST-Link and Segger J-Link Debug adapters.
For special promotional pricing and offers, please contact Keil Sales.
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
The Keil RTX RTOS is now provided under a Berkeley BSD type license. This makes it free.
All versions, including MDK-Lite, includes Keil RTX RTOS with source code !
Keil provides free DSP libraries for the Cortex-M3 and Cortex-M4.
Call Keil Sales for details on current pricing, specials and quantity discounts.
Sales can also provide advice about the various tools options available to you.
They will help you find various labs and appnotes that are useful.
All products are available from stock.
All products include Technical Support for 1 year. This is easily renewed.
Call Keil Sales for special university pricing. Go to and search for
university to view various programs and resources.
MDK supports STM32 Cortex-M3 and Cortex-M4 processors. Keil supports
many other ST processors including 8051, ARM7, ARM9в„ў and ST10
processors. See the Keil Device DatabaseВ® on for the complete
list of STMicroelectronics support. This information is also included in MDK.
Note: USA prices. Contact [email protected] for pricing in other countries.
Prices are for reference only and are subject to change without notice.
For the entire Keil catalog see or contact Keil or your local distributor.
For Linux, Android and bare metal (no OS) support on ST processors such as SPEAr, please see DS-5
For more information:
Keil Sales In USA: [email protected] or 800-348-8051. Outside the US: [email protected]
Keil Technical Support in USA: [email protected] or 800-348-8051. Outside the US: [email protected]
For comments or corrections please email [email protected]
For the latest version of this document, go to and for more Atmel specific information.
CMSIS Version 3: or
STMicroelectronics Discovery STM32F407 Lab with ARMВ® Keilв„ў MDK toolkit
Copyright В© 2012 ARM Ltd. All rights reserved
AH V1.1 2013
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF