Matrix Operation
1. Basic Setup
Open STM32CubeMX and generate basic code for your board.
Setup
Makefile
orCMakeLists.txt
for ARM Math. See arm math tutorial.Update
Makefile
orCMakeLists.txt
to add matrix functions.C_SOURCES = \ ... \ ... \ Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_init_f32.c \ Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_add_f32.c \ Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_sub_f32.c \ Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_scale_f32.c \ Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_mult_f32.c \ Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_inverse_f32.c \ Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_trans_f32.c
# Add sources to executable target_sources(${CMAKE_PROJECT_NAME} PRIVATE # Add user sources here Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_init_f32.c Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_add_f32.c Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_sub_f32.c Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_scale_f32.c Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_mult_f32.c Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_inverse_f32.c Drivers/CMSIS/DSP/Source/MatrixFunctions/arm_mat_trans_f32.c )
Add matrix check definition in
Makefile
orCMakeLists.txt
.# C defines C_DEFS = \ ... \ -DARM_MATH_CM4 \ -DARM_MATH_MATRIX_CHECK
# Add project symbols (macros) target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE # Add user defined symbols ARM_MATH_CM4 ARM_MATH_MATRIX_CHECK )
2. Test Matrix Functions
Navigate to
Core > Src
and openmain.c
.Include headers.
/* USER CODE BEGIN Includes */ #include <stdio.h> #include "arm_math.h" /* USER CODE END Includes */
/* USER CODE BEGIN Includes */ #include <stdio.h> #include "arm_math.h" #include "usbd_cdc_if.h" /* USER CODE END Includes */
Warning
Do not forget to add compiler flag -u _printf_float
in Makefile
or CMakeLists.txt
to print floating point numbers otherwise it will not print the numbers.
Add to LDFLAGS
.
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
LDFLAGS += -u _printf_float
Create target_link_options at the bottom.
# Add compiler flags
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
-u _printf_float
)
Overwrite definition of
_write
forprintf
as:/* USER CODE BEGIN 0 */ int _write(int file, char *data, int len) { for (int i = 0; i < len; ++i) { ITM_SendChar(data[i]); } return len; } /* USER CODE END 0 */
/* USER CODE BEGIN 0 */ int _write(int file, char *data, int len) { CDC_Transmit_FS((uint8_t*)data, (uint16_t)len); return len; } /* USER CODE END 0 */
Write code to print matrix.
// ... void print_matrix(arm_matrix_instance_f32 *matrix) { for (int i = 0; i < matrix->numRows; i++) { for (int j = 0; j < matrix->numCols; j++) { printf("%f\t", matrix->pData[i * matrix->numCols + j]); } printf("\n"); } } /* USER CODE END 0 */
Write code to test matrix functions.
/* USER CODE BEGIN 2 */ arm_matrix_instance_f32 m1; float m1data[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; arm_mat_init_f32(&m1, 3, 3, m1data); arm_matrix_instance_f32 m2; float m2data[9] = {9, 8, 7, 6, 5, 4, 3, 2, 1}; arm_mat_init_f32(&m2, 3, 3, m2data); arm_matrix_instance_f32 result; float resultdata[9]; arm_mat_init_f32(&result, 3, 3, resultdata); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ printf("Adding m1 and m2, result:\n"); arm_mat_add_f32(&m1, &m2, &result); print_matrix(&result); HAL_Delay(1000); printf("Subtracting m1 and m2, result:\n"); arm_mat_sub_f32(&m1, &m2, &result); print_matrix(&result); HAL_Delay(1000); printf("Multiplying m1 and m2, result:\n"); arm_mat_mult_f32(&m1, &m2, &result); print_matrix(&result); HAL_Delay(1000); printf("Multiplying m1 by 2\n"); arm_mat_scale_f32(&m1, 2, &result); print_matrix(&result); HAL_Delay(1000); printf("Transposing m1\n"); arm_mat_trans_f32(&m1, &result); print_matrix(&result); HAL_Delay(1000); printf("Inverting m1\n"); if (arm_mat_inverse_f32(&m1, &result) == ARM_MATH_SUCCESS) { print_matrix(&result); } else { printf("Matrix is not invertible\n"); } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
3. Test Output:
Adding m1 and m2, result:
10.000000 10.000000 10.000000
10.000000 10.000000 10.000000
10.000000 10.000000 10.000000
Subtracting m1 and m2, result:
-8.000000 -6.000000 -4.000000
-2.000000 0.000000 2.000000
4.000000 6.000000 8.000000
Multiplying m1 and m2, result:
30.000000 24.000000 18.000000
84.000000 69.000000 54.000000
138.000000 114.000000 90.000000
Multiplying m1 by 2
2.000000 4.000000 6.000000
8.000000 10.000000 12.000000
14.000000 16.000000 18.000000
Transposing m1
1.000000 4.000000 7.000000
2.000000 5.000000 8.000000
3.000000 6.000000 9.000000
Inverting m1
Matrix is not invertible
4. Creating Matrix Class and Overloading Operators
Setup your
Makefile
orCMakeLists.txt``for ``C++
. See cpp setup.Create
matrix32.hpp
insideCore/Inc
. Copy these contents.matrix32.hpp1/* 2 Dynamic memory allocation is avoided. 3 Template struct is used, so wisely use only few sizes of matrix otherwise it will consume lots of memory. 4 No exeption is handled, instead used static assertion. 5 Assign or copy data carefully especialy from where array of data through pointer is used to do. 6 constructor = default is avoided, otherwise one class pointer will point other class data. 7*/ 8 9#ifndef MATRIX32_HPP 10#define MATRIX32_HPP 11 12#include <stdio.h> 13#include "arm_math.h" 14 15template <uint16_t numRows, uint16_t numCols> 16class Matrix32 17{ 18public: 19 float32_t data[numRows * numCols]; 20 arm_matrix_instance_f32 arm_mat; 21 22 Matrix32() noexcept 23 { 24 arm_mat_init_f32(&arm_mat, numRows, numCols, data); 25 } 26 27 Matrix32(const Matrix32 &other) noexcept 28 { 29 arm_mat_init_f32(&arm_mat, numRows, numCols, data); 30 memcpy(arm_mat.pData, other.arm_mat.pData, numRows * numCols * 4); 31 } 32 33 template <typename... Args> 34 Matrix32(Args... values) 35 : data{static_cast<float32_t>(values)...} 36 { 37 static_assert(sizeof...(values) == numRows * numCols, 38 "Incorrect number of values. If there is static_cast error just above this line, then it is probably be due to mismatched size of matrix passed to copy or assign."); 39 arm_mat_init_f32(&arm_mat, numRows, numCols, data); 40 } 41 42 Matrix32(const float32_t *pSrc) noexcept 43 { 44 arm_mat_init_f32(&arm_mat, numRows, numCols, data); 45 memcpy(arm_mat.pData, pSrc, numRows * numCols * 4); 46 } 47 48 Matrix32 &operator=(const Matrix32 &other) noexcept 49 { 50 memcpy(arm_mat.pData, other.arm_mat.pData, numRows * numCols * 4); 51 return *this; 52 } 53 54 Matrix32 &operator=(float32_t *pSrc) noexcept 55 { 56 memcpy(arm_mat.pData, pSrc, numRows * numCols * 4); 57 return *this; 58 } 59 60 Matrix32 operator+(const Matrix32 &rhs) const noexcept 61 { 62 Matrix32 dst; 63 arm_mat_add_f32(&this->arm_mat, &rhs.arm_mat, &dst.arm_mat); 64 return dst; 65 } 66 67 Matrix32 operator-(const Matrix32 &rhs) const noexcept 68 { 69 Matrix32 dst; 70 arm_mat_sub_f32(&this->arm_mat, &rhs.arm_mat, &dst.arm_mat); 71 return dst; 72 } 73 74 template <uint16_t numAny> 75 Matrix32<numRows, numAny> operator*(const Matrix32<numCols, numAny> &rhs) const noexcept 76 { 77 Matrix32<numRows, numAny> dst; 78 arm_mat_mult_f32(&this->arm_mat, &rhs.arm_mat, &dst.arm_mat); 79 return dst; 80 } 81 82 Matrix32<numCols, numRows> trans() const noexcept 83 { 84 Matrix32<numCols, numRows> dst; 85 arm_mat_trans_f32(&this->arm_mat, &dst.arm_mat); 86 return dst; 87 } 88 89 Matrix32 inverse() const noexcept 90 { 91 static_assert(numRows == numCols, 92 "Inverse of rectangular matrix does not exists. Matrix must be square to have inverse."); 93 Matrix32 dst; 94 arm_status status = arm_mat_inverse_f32(&this->arm_mat, &dst.arm_mat); 95 if (status == ARM_MATH_SINGULAR) 96 { 97 printf("EXCEPTION:: MATRIX INVERSION FAILED !!\n"); 98 } 99 100 return dst; 101 } 102 103 Matrix32 operator*(const float32_t &rhs) const noexcept 104 { 105 Matrix32 dst; 106 arm_mat_scale_f32(&this->arm_mat, rhs, &dst.arm_mat); 107 return dst; 108 } 109 110 Matrix32 scale(const float32_t &scaler) const noexcept 111 { 112 Matrix32 dst; 113 arm_mat_scale_f32(&this->arm_mat, scaler, &dst.arm_mat); 114 return dst; 115 } 116 117 void fill(const float32_t &value) noexcept 118 { 119 arm_fill_f32(value, arm_mat.pData, numRows * numCols); 120 } 121 122 void setIdentity() noexcept 123 { 124 static_assert(numRows == numCols, "Only square matrix can be identity matrix"); 125 for (uint16_t i = 0; i < numRows; ++i) 126 { 127 for (uint16_t j = 0; j < numCols; ++j) 128 { 129 arm_mat.pData[i * numCols + j] = (i == j) ? 1.0f : 0.0f; 130 } 131 } 132 } 133 134 void printData() const noexcept 135 { 136 for (uint16_t i = 0; i < numRows; ++i) 137 { 138 for (uint16_t j = 0; j < numCols; ++j) 139 { 140 printf("%f\t", arm_mat.pData[i * numCols + j]); 141 } 142 printf("\n"); 143 } 144 } 145 146 ~Matrix32() = default; 147}; 148 149#endif // MATRIXF32_HPP
Create
app.h
insideCore/Inc
andapp.cpp
insideCore/Src
. Copy these contents.app.h1#ifndef APP_H 2#define APP_H 3 4#ifdef __cplusplus 5extern "C" { 6#endif 7 8void init(); 9 10void run(); 11 12#ifdef __cplusplus 13} 14#endif 15 16#endif // APP_H
app.cpp1#include "main.h" 2#include "matrix32.hpp" 3 4#include "app.h" 5 6Matrix32<3, 3> m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 7Matrix32<3, 3> m2 = {9, 8, 7, 6, 5, 4, 3, 2, 1}; 8Matrix32<3, 3> result; 9 10void init() 11{ 12 // nothing to do here 13} 14 15void run() 16{ 17 printf("Adding m1 and m2, result:\n"); 18 result = m1 + m2; 19 result.printData(); 20 HAL_Delay(1000); 21 22 printf("Subtracting m1 and m2, result:\n"); 23 result = m1 - m2; 24 result.printData(); 25 HAL_Delay(1000); 26 27 printf("Multiplying m1 and m2, result:\n"); 28 result = m1 * m2; 29 result.printData(); 30 HAL_Delay(1000); 31 32 printf("Multiplying m1 by 2\n"); 33 result = m1.scale(2); 34 result.printData(); 35 HAL_Delay(1000); 36 37 printf("Transposing m1\n"); 38 result = m1.trans(); 39 result.printData(); 40 HAL_Delay(1000); 41 42 printf("Inverting m1\n"); 43 result = m1.inverse(); 44 result.printData(); 45 HAL_Delay(1000); 46}
Include
app.h
inmain.c
./* USER CODE BEGIN Includes */ // .. #include "app.h" /* USER CODE END Includes */
Call
init()
andrun()
functions. Also comment out previous codes from while loop./* USER CODE BEGIN 2 */ // arm_matrix_instance_f32 m1; // float m1data[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // arm_mat_init_f32(&m1, 3, 3, m1data); // arm_matrix_instance_f32 m2; // float m2data[9] = {9, 8, 7, 6, 5, 4, 3, 2, 1}; // arm_mat_init_f32(&m2, 3, 3, m2data); // arm_matrix_instance_f32 result; // float resultdata[9]; // arm_mat_init_f32(&result, 3, 3, resultdata); init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ run(); // printf("Adding m1 and m2, result:\n"); // arm_mat_add_f32(&m1, &m2, &result); // print_matrix(&result); // HAL_Delay(1000); // printf("Subtracting m1 and m2, result:\n"); // arm_mat_sub_f32(&m1, &m2, &result); // print_matrix(&result); // HAL_Delay(1000); // printf("Multiplying m1 and m2, result:\n"); // arm_mat_mult_f32(&m1, &m2, &result); // print_matrix(&result); // HAL_Delay(1000); // printf("Multiplying m1 by 2\n"); // arm_mat_scale_f32(&m1, 2, &result); // print_matrix(&result); // HAL_Delay(1000); // printf("Transposing m1\n"); // arm_mat_trans_f32(&m1, &result); // print_matrix(&result); // HAL_Delay(1000); // printf("Inverting m1\n"); // if (arm_mat_inverse_f32(&m1, &result) == ARM_MATH_SUCCESS) // { // print_matrix(&result); // } // else // { // printf("Matrix is not invertible\n"); // } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
Add sources to
Makefile
orCMakeLists.txt
.# C sources C_SOURCES = \ ... \ Drivers/CMSIS/DSP/Source/SupportFunctions/arm_fill_f32.c
# CXX sources CXX_SOURCES = \ Core/Src/app.cpp
# Add sources to executable target_sources(${CMAKE_PROJECT_NAME} PRIVATE # ... Drivers/CMSIS/DSP/Source/SupportFunctions/arm_fill_f32.c Core/Src/app.cpp )
5. Test Ouput of Matrix Class
Adding m1 and m2, result:
10.000000 10.000000 10.000000
10.000000 10.000000 10.000000
10.000000 10.000000 10.000000
Subtracting m1 and m2, result:
-8.000000 -6.000000 -4.000000
-2.000000 0.000000 2.000000
4.000000 6.000000 8.000000
Multiplying m1 and m2, result:
30.000000 24.000000 18.000000
84.000000 69.000000 54.000000
138.000000 114.000000 90.000000
Multiplying m1 by 2
2.000000 4.000000 6.000000
8.000000 10.000000 12.000000
14.000000 16.000000 18.000000
Transposing m1
1.000000 4.000000 7.000000
2.000000 5.000000 8.000000
3.000000 6.000000 9.000000
Inverting m1
EXCEPTION:: MATRIX INVERSION FAILED !!