3.4 C implementation
Last updated
Last updated
Assuming you have successfully implemented a passthrough in the previous guide, we can simply copy and paste this project from within the SW4STM32 software. After pasting it, a pop-up will ask to give a name to the copied project. We recommend choosing a name with the current date and "alien_voice"
in it. Remember to delete the binary (ELF) file of the original project inside the copied project.
Open the CubeMX software to update the initialization code by opening the IOC file of the copied project.
For this exercise, we will only need to add the configuration of a timer (to benchmark our implementation) as the rest of the system is already up and running. In order to activate a timer, you need to set a "Clock Source". This is done from the "Pinout" tab on the left-hand column as shown below:
Figure: Set the "Clock Source" to "Internal Clock" in order to enable "TIM2".
Next, we need to configure the timer from the "Configuration" tab by pressing "TIM2" under "Control" (see below).
A pop-up similar to below should appear.
You can leave the rest of the parameters as is for "TIM2". Finally, you can update the initialization code by pressing the gear button in the toolbar. As before, do not open the project when prompted to do; select "Close".
You can now open the SW4STM32 software. In order to use the timer we configured, we will need to define a variable to keep track of the time and a macro to reset the timer. Between the USER CODE BEGIN PV
and USER CODE END PV
comments, add the following lines in the main.c
file.
To use this macro, just call it in your code, then the variable current_time_us
will be updated and the timer will be reset.
We want to assess if the processing time is longer or shorter than what our chosen buffer length and sampling frequency allows us. To do this, we will define some additional variables and constants (also between the USER CODE BEGIN PV
and USER CODE END PV
comments).
TASK 5: In the passthrough example, we set the buffer length (the macro called FRAME_PER_BUFFER
) to just 32. Increase it to 512 and use this value and FS
to calculate the maximum processing time allowed in microseconds. Replace the variable USING_FRAME_PER_BUFFER_AND_FS
in the code snippet below with this expression for the maximum processing time.
Note: keep in mind the points made about using float
or int
variables (see here).
In between the USER CODE BEGIN 2
and USER CODE END 2
comments, we propose adding the following lines to read the timer and print the result of the benchmarking tool to the console.
TASK 6: Using the current_time_us
and MAX_PROCESS_TIME_ALLOWED_us
, compute the value of processing_load
as a percentage in the code snippet above, i.e. replace USING_CURRENT_TIME_US_AND_MAX_PROCESS_TIME_ALLOWED_US
with the appropriate expression.
Note: keep in mind the points made about using float
or int
variables (see here).
You will notice that we used a printf
function in order to output text on the debug console. To enable this function you need to make the following changes to your project:
In the Project Properties ("right-click" project > Properties), navigate to "C/C++ Build > Settings" on the left-hand side (see the figure below). Under "MCU GCC Linker -> Miscellaneous", update the "Linker flags" field with:
Add the following function prototype above the main
function (e.g. between the USER CODE BEGIN PFP
and USER CODE END PFP
comments):
Add the following function call in the body of the main
function (before any printf
):
In Debug Configurations (dropdown from the bug icon) add the following option under the "Startup" tab:
After this setup, the printf
output will be shown in the debug console of Eclipse (precisely in the open ocd
console). Be careful, the modification made in the Debug Configuration will not stay if you copy and paste your project to make a new version!
We will now add our robot voice effect! As mentioned previously, we will also implement a simple high pass filter and add a gain to make the output more audible. Below is the final process
function we propose you to use. Insert it between the USER CODE BEGIN 4
and USER CODE END 4
comments.
TASK 7: Modify the code within the second for
loop in order to compute the alien voice output and update the state variables.
Note: normalize the sinusoid using the constant SINE_MAX
!
Copy the sinusoid lookup table below and place it between the USER CODE BEGIN PV
and USER CODE END PV
comments.
If you have some extra time, we propose to make a few improvements to the system!
TASK 8: Put the robot voice signal on both output channels.
Hint: edit the processing function, certainly near the end.
We will now program one of the on-board buttons - the blue button called "B1" - to toggle the alien voice effect. Copy the following code between the USER CODE BEGIN PV
and USER CODE END PV
comments.
In the above code snippet, you will find the state variable we propose for the FX (effects) state and the callback that is needed to react to the button. To activate the callback, you need to go into CubeMX and enable "EXTI line 4 to 15" from the Configuration tab under "System > NVIC". Then modify your process
function using a condition as proposed in the code snippet below.
Finally, you can try changing the modulation frequency and creating your lookup tables by running this Python script for modified values of f_sine
.
Congrats on implementing your (perhaps) first voice effect! In the next chapter, we will implement a more sophisticated high-pass filter than the one used here. To this end, we will come across fundamental theory and practical skills in digital filter design.
TASK 4: We ask you to set the "Prescaler" value (in the figure above) in order to achieve a period for "TIM2", i.e. we want our timer to have a resolution.
Hint: Go to the "Clock Configuration" tab (from the main window pane) to see what is the frequency of the input clock to "TIM2". From this calculate the prescaler value to increase the timer's period to .