Обновлена IRS
This commit is contained in:
@@ -1,11 +1,22 @@
|
||||
#include "IIMU.h"
|
||||
#include "stm32g4xx.h"
|
||||
#include "I2C.h"
|
||||
#include "ICM20948.h"
|
||||
#include "GPIO.h"
|
||||
|
||||
|
||||
IMU_Info IMUInfo;
|
||||
|
||||
IMU_Calib_Data CalibDataIMU;
|
||||
|
||||
void IMU_InitPower()
|
||||
{
|
||||
GPIO_InitPin(GPIO_PIN_13 | GPIO_PORT_C | GPIO_OUTPUT | GPIO_SET); // POWER ON 3V3 (nPC13)
|
||||
for (volatile int i = 0; i < 1000000; i ++) {};
|
||||
GPIOC->BSRR = GPIO_BSRR_BR13;
|
||||
for (volatile int i = 0; i < 1000000; i ++) {};
|
||||
}
|
||||
|
||||
void IMU_SetReg(unsigned char Addr, unsigned char Reg, unsigned char Value)
|
||||
{
|
||||
unsigned char reg[2];
|
||||
|
||||
@@ -47,6 +47,8 @@ typedef void (*IMU_Proc)(IMU_Data&);
|
||||
|
||||
extern IMU_Calib_Data CalibDataIMU;
|
||||
|
||||
void IMU_InitPower();
|
||||
|
||||
void IMU_SetReg(unsigned char Addr, unsigned char Reg, unsigned char Value);
|
||||
|
||||
unsigned char IMU_GetReg(unsigned char Addr, unsigned char Reg);
|
||||
|
||||
272
Source/INS/IRS.c
272
Source/INS/IRS.c
@@ -1,41 +1,76 @@
|
||||
#include "irs.h"
|
||||
#include "IRS.h"
|
||||
#include <math.h>
|
||||
|
||||
void IRS_init(IRS* irs)
|
||||
|
||||
static constexpr float PI = 3.14159265359f;
|
||||
static constexpr float TO_DEG = 180.0f / PI;
|
||||
static constexpr float TO_RAD = PI / 180.0f;
|
||||
static constexpr float G = 9.80665f; // m/c^2
|
||||
|
||||
IRS::IRS():
|
||||
RecordGyro(RecGyr.Rec),
|
||||
RecordSpeed(RecSpd.Rec),
|
||||
RecordPosit(RecPos.Rec),
|
||||
RecordQuat(RecQua.Rec)
|
||||
{
|
||||
irs->q.w = 1.0f;
|
||||
irs->q.x = 0.0f;
|
||||
irs->q.y = 0.0f;
|
||||
irs->q.z = 0.0f;
|
||||
RecordGyro.Init(RecGyr.Buffer, Freq, RecGyr.Past);
|
||||
RecordSpeed.Init(RecSpd.Buffer, Freq, RecSpd.Past);
|
||||
RecordPosit.Init(RecPos.Buffer, Freq, RecPos.Past);
|
||||
RecordQuat.Init(RecQua.Buffer, Freq, RecQua.Past);
|
||||
}
|
||||
|
||||
void IRS_update(IRS* irs, float dt)
|
||||
|
||||
// ======================Orientation======================
|
||||
void IRS::UpdateGyro(const Vector3& Gyr)
|
||||
{
|
||||
// gyro update
|
||||
Vector3 gyro =
|
||||
{
|
||||
irs->gyro.x * dt * DEG2RAD,
|
||||
irs->gyro.y * dt * DEG2RAD,
|
||||
irs->gyro.z * dt * DEG2RAD
|
||||
};
|
||||
if(!Gyr.IsFinite()) return;
|
||||
|
||||
Quaternion g = {gyro.x, gyro.y, gyro.z, 0};
|
||||
g = QuatProd(&irs->q, &g);
|
||||
g = QuatConstProd(&g, 0.5f);
|
||||
irs->q = QuatSum(&irs->q, &g);
|
||||
irs->q = QuatNormalize(&irs->q, 1.0f);
|
||||
IMU.Gyr = Gyr;
|
||||
|
||||
// /gyro update
|
||||
Vector3 gyr = Gyr * (Period * TO_RAD);
|
||||
|
||||
// accel update
|
||||
// restoreQuat(irs);
|
||||
RecordGyro.Add(Gyr * TO_RAD);
|
||||
|
||||
// /accel update
|
||||
Quaternion g = gyr;
|
||||
g = (OriQuat * g);
|
||||
|
||||
OriQuat += g * 0.5f;
|
||||
OriQuat = OriQuat.Norm();
|
||||
|
||||
RecordQuat.Add(OriQuat);
|
||||
|
||||
OriPRT = OriQuat.Conjugate() * Quaternion(0, 0, 1, 0) * OriQuat;
|
||||
}
|
||||
|
||||
void restoreQuat(IRS* irs)
|
||||
void IRS::UpdateAccel(const Vector3& Acc)
|
||||
{
|
||||
float len = lengthV3(&irs->accel);
|
||||
if(!Acc.IsFinite()) return;
|
||||
|
||||
Vector3 accel = ShiftAccelPRY * Acc * ShiftAccelPRY.Conjugate();
|
||||
|
||||
IMU.Acc = accel;
|
||||
|
||||
Quaternion ine = OriQuat * accel * OriQuat.Conjugate();
|
||||
|
||||
Vector3 acc = { ine.X, ine.Y, ine.Z - 1.0f }; // без гравитации
|
||||
|
||||
Inertial.Acc = acc;//OriQuat.RotateAroundZ(acc, true);
|
||||
|
||||
Inertial.Spd += acc * (G * Period); // интегрирование скорости
|
||||
|
||||
Vector3 spd = Inertial.Spd * Period;
|
||||
|
||||
Inertial.Pos += spd; // интегрирование позиции
|
||||
|
||||
RecordSpeed.Add(Inertial.Spd);
|
||||
RecordPosit.Add(Inertial.Pos);
|
||||
|
||||
RestoreQuat(accel);
|
||||
}
|
||||
|
||||
void IRS::RestoreQuat(const Vector3& Acc)
|
||||
{
|
||||
float len = Acc.Length();
|
||||
|
||||
static float quat_acc_alpha = 0.03f;
|
||||
static float quat_acc_max = 0.02f;
|
||||
@@ -47,65 +82,183 @@ void restoreQuat(IRS* irs)
|
||||
|
||||
if (gain < 0.0001f) return;
|
||||
|
||||
Vector3 acc = normalizeV3(&irs->accel, 1.0f);
|
||||
Vector3 acc = Acc.Norm();
|
||||
|
||||
Vector3 est = IRS_getGravity(&irs->q);
|
||||
Vector3 est;
|
||||
est.X = 2.0f * (OriQuat.X * OriQuat.Z - OriQuat.W * OriQuat.Y);
|
||||
est.Y = 2.0f * (OriQuat.W * OriQuat.X + OriQuat.Y * OriQuat.Z);
|
||||
est.Z = OriQuat.W * OriQuat.W - OriQuat.X * OriQuat.X - OriQuat.Y * OriQuat.Y + OriQuat.Z * OriQuat.Z;
|
||||
|
||||
Vector3 cross = Cross(&acc, &est);
|
||||
float dot = DotV3(&acc, &est);
|
||||
Vector3 cross = acc.Cross(est);
|
||||
float dot = acc.Dot(est);
|
||||
|
||||
if (dot < 0.0f)
|
||||
{
|
||||
float error_len = lengthV3(&cross);
|
||||
if (error_len < 0.001f) {
|
||||
cross.x = 1.0f;
|
||||
cross.y = 0.0f;
|
||||
cross.z = 0.0f;
|
||||
}
|
||||
else
|
||||
cross = constProdV3(&cross, 1.0f / error_len);
|
||||
float error_len = cross.Length();
|
||||
if (error_len < 0.001f) cross = {1.0f, 0.0f, 0.0f};
|
||||
else cross = cross * (1.0f / error_len);
|
||||
}
|
||||
|
||||
Vector3 axis = constProdV3(&cross, gain * 0.5f);
|
||||
Vector3 axis = cross * (gain * 0.5f);
|
||||
|
||||
Quaternion correction =
|
||||
Quaternion correction
|
||||
{
|
||||
axis.x,
|
||||
axis.y,
|
||||
axis.z,
|
||||
axis.X,
|
||||
axis.Y,
|
||||
axis.Z,
|
||||
1.0f // Для малых углов cos(0) = 1. При нормализации это сработает корректно.
|
||||
};
|
||||
|
||||
OriQuat = OriQuat * correction;
|
||||
|
||||
OriQuat = OriQuat.Norm();
|
||||
}
|
||||
|
||||
void IRS::UpdateMagnet(const Vector3& Mag)
|
||||
{
|
||||
if(!Mag.IsFinite()) return;
|
||||
|
||||
IMU.Mag = Mag; // Сохраняем сырые данные (если нужно для отладки)
|
||||
|
||||
static int init_count = 100;
|
||||
|
||||
float alpha = 0.001f;
|
||||
|
||||
|
||||
if(init_count > 0)
|
||||
{
|
||||
alpha = 0.05f;
|
||||
init_count--;
|
||||
}
|
||||
|
||||
Vector3 mag_norm = Mag.Norm();
|
||||
|
||||
Quaternion mW = OriQuat * mag_norm * OriQuat.Conjugate();
|
||||
|
||||
Vector3 mW_horizontal = { mW.X, mW.Y, 0.0f };
|
||||
|
||||
if (mW_horizontal.LengthSquared() < 0.0001f) return;
|
||||
mW_horizontal = mW_horizontal.Norm();
|
||||
|
||||
float error_z = mW_horizontal.Y * NorthDeclination.X - mW_horizontal.X * NorthDeclination.Y;
|
||||
|
||||
float half_error_step = error_z * alpha * 0.5f;
|
||||
|
||||
Quaternion corr
|
||||
{
|
||||
0.0f,
|
||||
0.0f,
|
||||
half_error_step,
|
||||
1.0f
|
||||
};
|
||||
|
||||
irs->q = QuatProd(&irs->q, &correction);
|
||||
irs->q = QuatNormalize(&irs->q, 1.0f);
|
||||
OriQuat = corr * OriQuat;
|
||||
OriQuat = OriQuat.Norm();
|
||||
}
|
||||
// ======================/Orientation======================
|
||||
|
||||
|
||||
void IRS::UpdatePositionSpeed(const Vector3& ShiftPosition, const Vector3& ShiftSpeed)
|
||||
{
|
||||
Shift.Pos += ShiftPosition;
|
||||
Shift.Spd += ShiftSpeed;
|
||||
}
|
||||
|
||||
void setAccelShift(IRS* irs, const float roll, const float pitch, const float yaw)
|
||||
void IRS::UpdateQuaternion(const Quaternion& ShiftQuaternion, float Alpha)
|
||||
{
|
||||
float h_roll = (roll * DEG2RAD) / 2.0f;
|
||||
float h_pitch = (pitch * DEG2RAD) / 2.0f;
|
||||
float h_yaw = (yaw * DEG2RAD) / 2.0f;
|
||||
Shift.Pos = Shift.Qua * (1.0f - Alpha) + ShiftQuaternion * Alpha;
|
||||
}
|
||||
|
||||
void IRS::RestoreAllShift(Vector3& ShiftPos)
|
||||
{
|
||||
Vector3 spd = Shift.Spd;
|
||||
Shift.Spd = 0;
|
||||
RecordSpeed.Mix(spd);
|
||||
Inertial.Spd += spd;
|
||||
|
||||
Vector3 pos = Shift.Pos;
|
||||
Shift.Pos = 0;
|
||||
|
||||
RecordPosit.Mix(pos);
|
||||
Inertial.Pos += pos;
|
||||
ShiftPos=pos;
|
||||
|
||||
Quaternion qua = Shift.Qua;
|
||||
Shift.Qua = 0;
|
||||
RecordQuat.Mix(qua);
|
||||
OriQuat += qua;
|
||||
OriQuat = OriQuat.Norm();
|
||||
}
|
||||
|
||||
void IRS::SetAccelShift(float Pitch, float Roll, float Yaw)
|
||||
{
|
||||
float h_roll = (Roll * TO_RAD) / 2.0f;
|
||||
float h_pitch = (Pitch * TO_RAD) / 2.0f;
|
||||
float h_yaw = (Yaw * TO_RAD) / 2.0f;
|
||||
|
||||
Quaternion q_roll = {0.0f, sinf(h_roll), 0.0f, cosf(h_roll)};
|
||||
Quaternion q_pitch = {sinf(h_pitch), 0.0f, 0.0f, cosf(h_pitch)};
|
||||
// Как найти поворот: arcsin(ось/макс_ось) * TO_GRAD
|
||||
Quaternion q_yaw = {0.0f, 0.0f, sinf(h_yaw), cosf(h_yaw)};
|
||||
|
||||
Quaternion prProd = QuatProd(&q_pitch, &q_roll);
|
||||
Quaternion pryProd = QuatProd(&prProd, &q_yaw);
|
||||
|
||||
irs->shiftAccelPRY = pryProd;
|
||||
ShiftAccelPRY = q_pitch * q_roll * q_yaw;
|
||||
}
|
||||
|
||||
Vector3 IRS_getGravity(const Quaternion* q)
|
||||
void IRS::SetNorthDeclination(const float Yaw)
|
||||
{
|
||||
Vector3 g =
|
||||
float dec = Yaw * TO_RAD; // Укажите ваше склонение
|
||||
Vector3 mag_north_target;
|
||||
NorthDeclination = { sinf(dec), cosf(dec) };
|
||||
}
|
||||
|
||||
bool IRS::SetGroundHeight(float* Height, float MaxHeight, float Alpha)
|
||||
{
|
||||
2.0f * (q->x*q->z - q->w*q->y),
|
||||
2.0f * (q->w*q->x + q->y*q->z),
|
||||
q->w*q->w - q->x*q->x - q->y*q->y + q->z*q->z
|
||||
if(Height)
|
||||
{
|
||||
float shift = Inertial.Pos.Z - *Height;
|
||||
GroundShift = GroundShift*(1.0f-Alpha) + shift*Alpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = Inertial.Pos.Z - GroundShift;
|
||||
|
||||
if(height<MaxHeight)
|
||||
{
|
||||
float shift = Inertial.Pos.Z - MaxHeight;
|
||||
GroundShift = GroundShift*(1.0f-Alpha) + shift*Alpha;
|
||||
}
|
||||
}
|
||||
|
||||
float height = Inertial.Pos.Z - GroundShift;
|
||||
if (height < 0.0f) GroundShift+=height;
|
||||
}
|
||||
|
||||
void IRS::GetSinXYCosZ(Vector3& SinXYCosZ)
|
||||
{
|
||||
SinXYCosZ = OriPRT;
|
||||
}
|
||||
|
||||
void IRS::GetPitchRollYaw(Vector3& PitchRollYaw, bool& Reversed)
|
||||
{
|
||||
Reversed = OriPRT.Z < 0;
|
||||
|
||||
float yaw = atan2f(2.0f * (OriQuat.X * OriQuat.Y - OriQuat.W * OriQuat.Z), 1.0f - 2.0f * (OriQuat.Y * OriQuat.Y + OriQuat.Z * OriQuat.Z)) * TO_DEG;
|
||||
if (yaw < 0.0f) yaw += 360.0f;
|
||||
|
||||
PitchRollYaw =
|
||||
{
|
||||
GetAngle(OriPRT.Y, OriPRT.X, OriPRT.Z), // Pitch
|
||||
GetAngle(-OriPRT.X, OriPRT.Y, OriPRT.Z), // Roll
|
||||
yaw // Yaw
|
||||
};
|
||||
}
|
||||
|
||||
return g;
|
||||
void IRS::GetInertial(Vector3* Pos, Vector3* Spd, Vector3* Acc, float* Gnd)
|
||||
{
|
||||
if (Acc) *Acc = Inertial.Acc;
|
||||
if (Spd) *Spd = Inertial.Spd;
|
||||
if (Pos) *Pos = Inertial.Pos;
|
||||
if (Gnd) *Gnd = GroundShift;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +267,3 @@ Vector3 IRS_getGravity(const Quaternion* q)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef IRS_H
|
||||
#define IRS_H
|
||||
|
||||
#include "Quaternion.h"
|
||||
#include "Vector.h"
|
||||
#include "util/Record.h"
|
||||
|
||||
#define PI 3.14159265359f
|
||||
#define DEG2RAD PI / 180.0f
|
||||
|
||||
typedef struct
|
||||
class IRS // Inertial Reference System
|
||||
{
|
||||
Quaternion q; // ориентация
|
||||
Vector3 oriPRT; // orientation pitch roll tilts
|
||||
Vector3 gyro; // deg/s
|
||||
Vector3 accel; // g
|
||||
public:
|
||||
IRS();
|
||||
|
||||
Quaternion shiftAccelPRY; // смещение акселерометра
|
||||
static constexpr float Freq = 100.0f;
|
||||
static constexpr float Period = 1.0f / Freq;
|
||||
|
||||
} IRS;
|
||||
Quaternion OriQuat; // Главный кватернион ориентации
|
||||
Vector3 OriPRT; // Локальные наклоны
|
||||
|
||||
void IRS_init(IRS* irs);
|
||||
struct { Vector3 Gyr, Acc, Mag; } IMU; // Последние значения датчиков
|
||||
struct { Vector3 Acc, Spd, Pos; } Inertial; // Инерциальные значения движения
|
||||
|
||||
void IRS_update(IRS* irs, float dt);
|
||||
void restoreQuat(IRS* irs);
|
||||
float GroundShift = 0.0f; // Разница между высотой и поверхностью
|
||||
|
||||
void setAccelShift(IRS* irs, const float pitch, const float roll, const float yaw);
|
||||
Quaternion ShiftAccelPRY; // Смещение акселерометра
|
||||
Vector2 NorthDeclination; // Смещение магнитометра
|
||||
|
||||
Vector3 IRS_getGravity(const Quaternion* q);
|
||||
private:
|
||||
// Запись значений для расчётов в прошлом
|
||||
struct StructRecGyr
|
||||
{
|
||||
static constexpr float Past = 0.1f; // Максимальная запись прошлых данных
|
||||
static constexpr unsigned long Count = Freq * Past + 1;
|
||||
Vector3 Buffer[Count];
|
||||
Record<Vector3> Rec;
|
||||
} RecGyr;
|
||||
|
||||
struct StructRecAccSpdPos
|
||||
{
|
||||
static constexpr float Past = 0.6f; // Максимальная запись прошлых данных
|
||||
static constexpr unsigned long Count = Freq * Past + 1;
|
||||
Vector3 Buffer[Count];
|
||||
Record<Vector3> Rec;
|
||||
} RecAcc, RecSpd, RecPos;
|
||||
|
||||
struct StructRecQua
|
||||
{
|
||||
static constexpr float Past = 0.6f; // Максимальная запись прошлых данных
|
||||
static constexpr unsigned long Count = Freq * Past + 1;
|
||||
Quaternion Buffer[Count];
|
||||
Record<Quaternion> Rec;
|
||||
} RecQua;
|
||||
|
||||
//---
|
||||
// Сдвиг данных
|
||||
struct { Vector3 Pos, Spd; Quaternion Qua = 0.0f; } Shift; // Значение смещения позиции, скорости и кватерниона
|
||||
//---
|
||||
|
||||
public:
|
||||
Record<Vector3>& RecordGyro;
|
||||
Record<Vector3>& RecordSpeed;
|
||||
Record<Vector3>& RecordPosit;
|
||||
Record<Quaternion>& RecordQuat;
|
||||
|
||||
private:
|
||||
void RestoreQuat(const Vector3& Acc);
|
||||
static float GetAngle(float X, float Y, float Z);
|
||||
|
||||
public:
|
||||
void UpdateGyro(const Vector3& Gyr); // deg/s
|
||||
void UpdateAccel(const Vector3& Acc); // g
|
||||
void UpdateMagnet(const Vector3& Mag); // gaus
|
||||
|
||||
// Коэффициенты восстановления позиции и скорости в секунду
|
||||
void SetShiftAlpha(const Vector3* Pos, const Vector3* Spd, const float* Qua);
|
||||
|
||||
void UpdatePositionSpeed(const Vector3& ShiftPosition, const Vector3& ShiftSpeed);
|
||||
void UpdateQuaternion(const Quaternion& ShiftQuaternion, float Alpha);
|
||||
|
||||
void RestoreAllShift(Vector3& ShiftPos);
|
||||
|
||||
void SetAccelShift(float Pitch, float Roll, float Yaw);
|
||||
void SetNorthDeclination(const float Yaw);
|
||||
|
||||
bool SetGroundHeight(float* Height, float MaxHeight, float Alpha);
|
||||
|
||||
void GetSinXYCosZ(Vector3& SinXYCos);
|
||||
void GetPitchRollYaw(Vector3& PitchRollYaw, bool& Reversed);
|
||||
void GetInertial(Vector3* Pos, Vector3* Spd, Vector3* Acc, float* Gnd);
|
||||
};
|
||||
|
||||
#endif
|
||||
47
Source/INS/util/Record.cpp
Normal file
47
Source/INS/util/Record.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "Record.h"
|
||||
#include <math.h>
|
||||
|
||||
template class Record<float>;
|
||||
template class Record<Vector2>;
|
||||
template class Record<Vector3>;
|
||||
template class Record<Quaternion>;
|
||||
|
||||
template <class T>
|
||||
Record<T>::Record(T* RecordBuffer, float Frequency, float Period)
|
||||
{
|
||||
Init(RecordBuffer, Frequency, Period);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Record<T>::Init(T* RecordBuffer, float Frequency, float Period)
|
||||
{
|
||||
Freq = Frequency;
|
||||
Buffer = RecordBuffer;
|
||||
Count = (float)(Frequency * Period + 1.0f);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Record<T>::Past(float Time) const
|
||||
{
|
||||
unsigned long move = (float)(Time * Freq);
|
||||
if (move >= Count) move = Count - 1;
|
||||
|
||||
if (Index >= move) move = Index - move;
|
||||
else move = Index + (Count - move);
|
||||
|
||||
return Buffer[move];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Record<T>::Add(const T& Value)
|
||||
{
|
||||
Index++;
|
||||
if (Index >= Count) Index = 0;
|
||||
Buffer[Index] = Value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Record<T>::Mix(const T& Shift)
|
||||
{
|
||||
for (unsigned int a = 0; a < Count; a++) Buffer[a] += Shift;
|
||||
}
|
||||
45
Source/INS/util/Record.h
Normal file
45
Source/INS/util/Record.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef RECORD_H
|
||||
#define RECORD_H
|
||||
|
||||
#include "Vector.h"
|
||||
#include "Quaternion.h"
|
||||
|
||||
template <typename T>
|
||||
class Record
|
||||
{
|
||||
public:
|
||||
Record() {};
|
||||
Record(T* RecordBuffer, float Frequency, float Period); // BufferSize = (Frequency * Period) + 1;
|
||||
|
||||
private:
|
||||
float Freq;
|
||||
unsigned long Count;
|
||||
T* Buffer;
|
||||
T Shift;
|
||||
unsigned long Index = 0;
|
||||
|
||||
public:
|
||||
void Init(T* RecordBuffer, float Frequency, float Period); // BufferSize = (Frequency * Period) + 1;
|
||||
|
||||
T Past(float Time) const;
|
||||
|
||||
void Add(const T& Value);
|
||||
|
||||
void Mix(const T& Shift);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Average
|
||||
{
|
||||
private:
|
||||
T Value;
|
||||
float Count = 0;
|
||||
|
||||
public:
|
||||
void Set(const T& Val) { Value += Val; Count++; }
|
||||
T Get() { if (Count == 0.0f) return T(); T ave = Value / Count; Count = 0; Value = 0.0f; return ave; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,12 @@
|
||||
#include "stm32g4xx.h"
|
||||
#include "ICM20948.h"
|
||||
|
||||
extern "C" void SystemClock_Config();
|
||||
|
||||
int main()
|
||||
{
|
||||
SystemClock_Config(); // 170MHz
|
||||
IMU_InitPower();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user