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
© Copyright 2024