Instructions

ECE 486 DSP Configuration for the
STM32F-Discovery Demonstration Board
Don Hummels
Electrical and Computer Engineering
University of Maine
January 6, 2015
Contents
1
Background
2
2
Connecting to the Board
2.1 How to not break the board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Debugging Connections via Serial Port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
2
2
3
Writing code
2
4
Software interface
4
5
Compiling and Running
5.1 Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Flashing and running image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Running a debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
5
6
6
Pin descriptions
6.1 Analog Inputs/Outputs . . . . . . . .
6.2 Use of GPIO Pins . . . . . . . . . . .
6.3 Accessing the Discovery board LEDs
6.4 Accessing the USER Pushbutton . . .
6
6
6
8
8
7
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Sample Program
.
.
.
.
9
Abstract
This document describes the use of the STM32F4-Discovery evaluation board to support labs in ECE 486 Digital
Signal Processing.
1
1
Background
STMicroelectronics has produced a low-cost evaluation board to showcase their STM32F4xx line of processors. The
STM32F4-Discovery board features an STM32F407VGT6 microcontroller, which includes 1MB of Flash memory,
128 kB of user RAM, 64 kB of core-coupled RAM, and a floating point co-processor. The microcontroller includes
multiple ADC and DAC peripherals, as well as a rich host of other peripherals (DMAs, Timers, USART, and support
for I 2 C, SPI, USB-OTG, Ethernet, and a few other acronyms). An external USB interface allows debugging and
programming. The development board includes four user-controlled LEDs, an accelerometer, a digital MEMs microphone, a RESET push-button, a second USER push-button, and an audio CODEC with integrated class D speaker
driver.
In ECE 486, the board is configured to allow students to perform signal processing experiments in real-time.
Two 12-bit ADCs are configured to sample an input waveform. The sampled data is passed to a user for processing.
The calculated output waveform is then streamed to a pair of output DACs so that the results may be viewed on an
oscilloscope. All timing and DMA data transfers are handled via an ECE 486 library. This document provides the
hardware and software interface necessary to use this library.
2
Connecting to the Board
The ECE 486 functionality is obtained through the first 20 pins of the P1 connector on the STM32F4-Discovery board.
An interface cable is provided which includes a 50-pin connector, a 20-wire ribbon cable, and a 20-pin DIP wire-wrap
socket. The 50-pin connector should be installed on the back side of the P1 connector header, so that pins 1-20 are
connected through the ribbon cable. The DIP socket can then be plugged into a prototyping board to allow easy access
to the analog and digital inputs and outputs. Figure 1 shows a photo of correctly configured board under test. Pin
assignments for the 20-PIN DIP Socket are given in Figure 2, and discussed further in Section 6.
2.1
How to not break the board
The board may be safely powered by connecting the discovery board to a PC through the “type A to mini-B” USB
connector CN1. (This connector is also used to program and debug software on the board.)
Care should be taken when applying external signals to any of the pins of the board, since there is limited protection
on the development board. Signals outside of the 0-3 V power supply range can potentially damage the processor. All
GPIO pins being used are 5 V tolerant and do contain clamping diodes on the processor, but each pin can tolerate
at most 5 mA of current. Function generator connections are particularly problematic, since it’s easy to leave the
generator on when power is removed—and it’s easy to accidentally apply large (or negative) voltages. A 10 kΩ
protection resistor is recommended for all analog input signals.
2.2
Debugging Connections via Serial Port
(Optional connection. Use if needed when debugging a program.)
The ECE 486 libraries configure a UART to allow debugging information to be passed from the board to a serial
terminal running on a host computer. A USB-to-serial break-out cable is provided to access this information. Connect
the black (ground) lead of the serial cable to a ”GND” pin on the board. The white (Receive) lead of the cable should
be connected to pin PC10. The red (Power) and green (Transmit) leads of the cable can be left unconnected.
Plug the USB-to-serial break-out cable into a USB port on a host computer, and start a serial terminal emulator.
Configure the emulator to 9600 bps, 8 data-bits, 1 stop-bit, no start-bits, and no parity. The terminal emulator should
display strings sent to the UART on the development board.
3
Writing code
For ECE 486, a gcc cross-compiler is used to compile C code for the board. Writing code for this board is no different
from writing any other C code under Linux. Bring up your favorite editor (e.g. kate, vi, nedit, gedit, nano, etc.), and
just start writing code! No specific integrated development environment (IDE) is required.
2
Caution
To prevent damage to the board, it is recommend that all external function-generator
connections be made through a 10 kΩ resistor, limiting currents to a few milliamps (even !!!
for inadvertently large function generator settings)
Figure 1: STM32F4-Discovery Board connected for test. Pins 1-20 of the P1 connector provide the functionality
needed for ECE-486.
Figure 2: Pin assignments for the 20-Pin DIP socket. Red pin labels indicate that the pin is not available to the user,
and is allocated toward other functions on the STM32F4-Discovery board. The Analog input lines “Ain1” and “Ain2”
should be driven through a 10kΩ resistor to prevent damage to the board.
3
4
Software interface
Programs for real-time signal processing should initialize the processor using a single call to the initialize()
function before any other processing. This call configures the GPIO terminals of the processor, and then waits until
the blue USER push-button is pressed while the red and green LEDs blink. (This idle state is returned to when the
black RESET button is pressed, allowing the board to be reprogrammed without error.)
When the USER push-button is depressed, the initialize() function configures the on-chip ADC and DAC
so that blocks of memory are continually transferred from the ADC and written to the DAC (at a desired sample rate)
while the processor is working on other tasks (such as manipulating the signals). In a typical signal processing application, one block of data is requested from the ADC, processed by the processor, and the resulting output waveform is
transferred back to DAC output buffer. When a new block of data is requested from the ADC, the processor remains
idle until the ADC interface competes filling the requested block and the returns the data for further processing.
Two interface functions getblock() and putblock() are provided to enable a user to easily transfer blocks
of data to and from the DACs and ADCs. The user must allocate any required memory to store the ADC input
samples or the DAC output samples. (Similar stereo input/output routines are provided by getblockstereo()
and putblockstereo.)
Most real-time signal processing programs will access the following functions:
void setblocksize(int n samples) :
Optionally, the user may call setblocksize() to determine the size of the data block that will be used in later
calls to getblock() or putblock. If setblocksize() is not called, a default value “DEFAULT_BLOCKSIZE”
will be used. Using a larger block size may result in more efficient code, while using a smaller block size will
reduce the latency of the system.
void initialize(int sample rate select, int input mode, int output mode) :
The initialize() function is called once, at the beginning of program execution to configure the ADCs,
DACs, DMAs, processor clocks, etc.
Valid values for sample_rate_select are defined by including ece486.h and include FS_48K, FS_24K,
and FS_8K to request sample rates of 48, 24, and 8 ksps respectively. (Similar constants are available for 5, 6,
10, 12, 50, 96, 100, 200, 400, and 500 ksps.)
Similarly, valid values of input_mode are MONO_IN and STEREO_IN to configure whether a single ADC
(Ain1 Pin) or both ADCs are used. Valid values of output_mode are MONO_OUT or STEREO_OUT to
configure whether a single DAC (Aout1) or both DACs are used.
int getblocksize(void) :
getblocksize() returns the number of samples that getblock() will produce, and the number of samples that must be provided to putblock(). This is useful for allocating the any arrays needed to store or
manipulate the waveforms (especially when setblocksize was not called). In the interface description below, a working array (named “working”) is used to store data provided by getblock() and pass output data to
putblock(). Storage for this working array must be allocated by the interface user.
int getsamplingfrequency(void) :
getsamplingfrequency() returns the best guess (assuming the HFE oscillator is exactly at 8 MHz) at the
actual sampling rate being implemented (in samples/second). The actual sample rate may be slightly different
from the requested rate, particularly if the Microphone is used as the input device.
void getblock(float *working) :
getblock() waits until the ADC has complete acquiring a block of input samples. These samples are then
transferred into the caller’s working array and returned. The caller must allocate the memory required for the
returned result array.
Returned samples are stored as single-precision (type float) values ranging from −1.0 (corresponding to input
voltages near 0 V) to +1.0 (corresponding to the maximum input voltage, near 3 V). The actual input voltage
corresponding to the ith sample is approximately v(iTs ) ≈ 1.5 + (working[i] ∗ 1.5).
void putblock(float *working) :
putblock() transfers the caller’s working array for output via the DAC.
Output samples should range from −1.0 (for a minimum DAC output, near 0 V) to +1.0 (for the maximum
output value near 3.0 V).
4
In practice, initialize() and getblocksize() are called once, memory is allocated, and then the getblock()
and putblock() functions are repeatedly called as blocks of input samples are processed to form the output waveform.
If the board has been configured to use stereo inputs or outputs, modified data transfer functions are provided with
additional arrays for the two ADCs/DACs.
void getblockstereo(float *input1, float *input2)
void putblockstereo(float *output1, float *output2)
A few other utility functions are provided which may prove useful to debug or measure program performance.
The digital io pin provides one method of indicating program status, or timing events (via an oscilloscope) within the
program flow. The pin is configured as PC4 on the F4-Discovery board, or pin PF6 on the F429i-Discovery board.
Also, a UART is configured to transmit character strings from the processor to a serial terminal.
void DIGITAL IO SET(void)
Set the digital output pin.
void DIGITAL IO RESET(void)
Reset the digital output pin.
void UART putstr(const char *str)
Send a character string via a UART to a host serial terminal. The host terminal should have its receive pin
connected to PC10. The terminal should be configured at 9600 bps, 8 data-bits, no start-bits, one stop-bit, and
no parity. A DMA is configured to transmit the character string to the UART. The function call returns as soon
as the DMA is started, so that program execution may continue while the string is being transmitted to the
host. Modification of the string content while the string is being sent may produce unpredictable results. If
UART_putstr() is called while the DMA/UART are already busy (from a previous call), the function blocks
until the previous transmission is completed.
5
Compiling and Running
5.1
Makefile
Cross-compiling code with multiple libraries requires relatively complex gcc commands, so a Makefile is provided
that handles telling gcc how it should compile code, and where to find libraries. Edit the Makefile to include your
source code (following the comments in the Makefile). Running “make” should create two output files ”myexe” and
“myexe.bin”, where “myexe” is the executable name that you provide in the Makefile. Typing “make flash” will
additionally ”flash” the board by writing the ”myexe.bin” binary image to the development board.
5.2
Flashing and running image
1. Connect a USB cable from the host to the CN1 connector on the Discovery board.
2. Reset the STM32F4 board (hit the black push-button)
3. On the host, run:
make flash
4. Reset the STM32F4 board to restart the program back to the idle state. The green and red LEDs should be
flashing.
5. Continue program execution beyond the “initialize()” function by pressing the blue USER push-button. The
green LED should stay lit, indicating that the image is executing. Error conditions are indicated by lighting the
red LED.
5
5.3
Running a debugger
A symbolic debugger may be used to trace program execution, set breakpoints, and examine variable values. The
“gdb” debugger is supported as follows:
1. Recompile the code with debugging options, and flash the compiled image to the processor.
make clean
make debug
make flash
2. In a separate window on the host, run the st-util to establish the communications link between the development board and the host (Port :4242).
st-util
3. You may run gdb directly using the ELF file (called “myexe” in Section 5.1). After running the ARM compiled
gdb command (at the “(gdb)” prompt) specify the target development board port provided by st-util.
arm-none-eabi-gdb myexe
>>>>> (Output lines from gdb... wait for the (gdb) prompt) <<<<<
(gdb) target extended :4242
You may now continue using gdb commands to trace program execution.
4. As an alternative to using the raw gdb commands, the “ddd” debugger provides a graphical front-end to the gdb
debugger. To use ddd, you must specify the path to the ARM-compiled gdb debugger on the command line, and
then specify the target on the “(gdb)” prompt within the ddd window:
ddd --debugger arm-none-eabi-gdb myexe
>>>>>> Graphical debugger should start
<<<<<
>>>>>> Look for the gdb command window with the (gdb) prompt <<<<<
(gdb) target extended :4242
6
Pin descriptions
Pin assignments for the 20-pins accessed through the ECE 486 connector on the Discovery board P1 connector are
summarized in Table 1.
6.1
Analog Inputs/Outputs
The STM32F407VGT6 microcontroller includes two on-chip 12-bit ADCs, and two on-chip 12-bit DACs which are
accessed through the Ain1, Ain2, Aout1, and Aout2 terminals. In MONO modes, only the Ain1 or Aout1 terminals
are driven. The analog inputs and outputs should be in the 0 − 3 V range. These analog inputs and outputs are clamped
to the supply rails within the microcontroller using protection diodes, but currents in or out of these terminals must
be limited to no more than 5 mA. Inadvertent damage to the microcontroller may be avoided by making all function
generator connections to the Ain terminals through a 10 kΩ resistor.
6.2
Use of GPIO Pins
A digital output is configured on pin PC4. Users can set the values of these output using statements such as:
DIGITAL_IO_SET();
DIGITAL_IO_RESET();
DIGITAL_IO_TOGGLE();
The pins may be set/cleared to enable timing of segments of code using an oscilloscope.
Other GPIO pins (PA1, PC1, PC2, PC5) are not configured, and are available to the user. Example code illustrating
how to configure and drive the outputs is provided.
6
Table 1: Pin assignments for the ECE 486 interface. Pins shown in red are used by the STM32F4-Discovery
peripheral devices , and should be left with no connection.
DIP Pin
P1 Pin
P1 Label
ECE 486 Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
3
5
7
9
11
13
15
17
19
20
18
16
14
12
10
8
6
4
2
GND
VDD
GND
PC1
PC3
PA1
PA3
PA5
PA7
PC5
PC4
PA6
PA4
PA2
PA0
PC2
PC0
NRST
VDD
GND
Ground
Power (3 V)
Ground
Unconfigured GPIO Pin
Mic DOUT/AIN4x Interface
Unconfigured GPIO Pin
Ain1: Primary ADC Input (MONO and STEREO)
Aout1: Primary DAC Output(MONO and STEREO)
LIS302DL (Accelerometer) SDA/SDI/SDO
Unconfigured GPIO Pin
GPIO Digital Output
LIS302DL (Accelerometer) SDO
Aout2: Second DAC Output (STEREO only)
Ain2: Second ADC Input (STEREO only)
(Blue) User Push-button
Unconfigured GPIO Pin
USB PowerOn
(Black) Reset Push-button
Power (3 V)
Ground
In addition, pin PC10 (on the P2 connector) is used for the UART output.
DIP Header Pinout
7
6.3
Accessing the Discovery board LEDs
The F4-Discovery board LEDs are accessed through GPIO pins 12−15 of Port D: PD12 (LED4: Green), PD13 (LED3:
Amber), PD14 (LED5: Red), PD15 (LED6: Blue). By default, the Red and Green LEDs are initialized and used by
the library. These LEDs flash when the board is idle in the initialize() function to allow reprogramming. The
Green LED is lit when the program is executing normally, and the Red LED indicates an error condition.
Users may also drive the LEDs using the “board-specific” driver functions provided by ST. Use of the Amber or
Blue LEDs will require initialization by the user during program startup:
BSP_LED_Init(LED3);
BSP_LED_Init(LED6);
// Amber
// Blue
(LED4 and LED5 are pre-configured by initialize().) To control the state of an LED, use statements similar to:
BSP_LED_Off(LED5);
BSP_LED_On(LED6);
BSP_LED_Toggle(LED4);
6.4
// Turn Red LED off
// Turn Blue LED on
// Toggle Amber LED
Accessing the USER Pushbutton
The Blue USER pushbutton is also configured by the initialize() function. Button presses may be detected in
software by periodically monitoring the global variable UserButtonPressed. Normally UserButtonPressed
has a value of “Button_Ready”. The value changes to “Button_Pressed” on every button press, and remains
at this value until cleared by the user’s software. A typical code segment is given below:
#include "ece486.h"
...
if (UserButtonPressed == Button_Pressed) {
... // Take some action on button presses
UserButtonPressed = Button_Ready;
// Allows detection of the next press
}
...
8
7
1
2
3
4
5
6
7
8
9
10
11
12
Sample Program
/∗
∗ Example p r o g r a m t o i l l u s t r a t e t h e u s e o f t h e ECE 486 i n t e r f a c e .
∗
∗ An i n p u t waveform i s s q u a r e d , and s t r e a m e d t o t h e o u t p u t DAC. The
∗ waveform i s a l s o c o p i e d ( u n c h a n g e d ) t o t h e o t h e r DAC c h a n n e l . Each
∗ USER b u t t o n p r e s s i n v e r t s t h e s i g n a l on t h e s e c o n d DAC.
∗/
# include ” stm32f4xx hal . h”
# include ” stm32f4 discovery . h”
# i n c l u d e ” ece486 . h”
# i n c l u d e < s t d l i b . h>
# i n c l u d e < s t d i o . h>
13
14
15
16
17
18
19
20
i n t main ( v o i d )
{
i n t nsamp , i ;
f l o a t ∗ input , ∗output1 , ∗ output2 ;
s t a t i c f l o a t sign =1.0;
s t a t i c i n t button count = 0;
char o u t s t r [100];
21
i n i t i a l i z e ( FS 50K , MONO IN , STEREO OUT ) ;
22
/ / S e t up t h e DAC/ADC i n t e r f a c e
23
/∗
∗ A l l o c a t e R e q u i r e d Memory
∗/
nsamp = g e t b l o c k s i z e ( ) ;
i n p u t = ( f l o a t ∗ ) m a l l o c ( s i z e o f ( f l o a t ) ∗ nsamp ) ;
o u t p u t 1 = ( f l o a t ∗ ) m a l l o c ( s i z e o f ( f l o a t ) ∗ nsamp ) ;
o u t p u t 2 = ( f l o a t ∗ ) m a l l o c ( s i z e o f ( f l o a t ) ∗ nsamp ) ;
24
25
26
27
28
29
30
31
i f ( i n p u t ==NULL | | o u t p u t 1 ==NULL | | o u t p u t 2 ==NULL) {
f l a g e r r o r (MEMORY ALLOCATION ERROR ) ;
while ( 1 ) ;
}
32
33
34
35
36
/∗
∗ I n f i n i t e Loop t o p r o c e s s t h e d a t a s t r e a m , ” nsamp ” s a m p l e s a t a t i m e
∗/
while (1){
/∗
∗ Ask f o r a b l o c k o f ADC s a m p l e s t o be p u t i n t o t h e w o r k i n g b u f f e r
∗
g e t b l o c k ( ) w i l l w a i t u n t i l t h e i n p u t b u f f e r i s f i l l e d . . . On r e t u r n
∗
we work on t h e new d a t a b u f f e r .
∗/
getblock ( input ) ;
/ / Wait h e r e u n t i l t h e i n p u t b u f f e r i s f i l l e d . . . Then p r o c e s s
37
38
39
40
41
42
43
44
45
46
47
/ ∗ SIGNAL PROCESSING CODE TO CALCULATE THE REQUIRED OUTPUT BUFFERS ∗ /
DIGITAL IO SET ( ) ;
/ / Use a s c o p e on PC4 t o m e a s u r e e x e c u t i o n t i m e
f o r ( i = 0 ; i <nsamp ; i ++) {
output1 [ i ] = sign ∗ input [ i ] ;
output2 [ i ] = i n p u t [ i ]∗ i n p u t [ i ] ;
}
DIGITAL IO RESET ( ) ; / / ( f a l l i n g e d g e . . . .
done p r o c e s s i n g d a t a )
48
49
50
51
52
53
54
55
/∗
∗ p a s s t h e p r o c e s s e d w o r k i n g b u f f e r b a c k f o r DAC o u t p u t
∗/
p u t b l o c k s t e r e o ( output1 , output2 ) ;
56
57
58
59
60
i f ( U s e r B u t t o n P r e s s e d == B u t t o n P r e s s e d ) {
/ / Button Press ?
s i g n ∗= −1.0;
/ / Invert output2
UserButtonPressed = Button Ready ;
s p r i n t f ( o u t s t r , ” B u t t o n P r e s s e d ! c o u n t = %x\n ” , b u t t o n c o u n t + + ) ; / / %d , %f seem t o be buggy
UART putstr ( o u t s t r ) ;
}
61
62
63
64
65
66
}
67
68
}
9