Compare commits

..

16 Commits

Author SHA1 Message Date
6422b1e512 mrobot好了 2026-02-04 15:05:04 +08:00
8468c53092 差不多了 2026-02-04 14:42:14 +08:00
e056d5ecc7 功能化mrobot 2026-02-04 14:32:14 +08:00
268a132283 继续优化 2026-02-04 13:45:07 +08:00
20afc1a656 优化mr 2026-02-04 13:03:11 +08:00
8712efa6f2 好的 2026-02-04 12:43:11 +08:00
5f624a51b9 可以了 2026-02-04 04:38:24 +08:00
ff40b71c75 可以读imu了 2026-02-04 04:10:37 +08:00
a2c06026f3 有点意思了 2026-02-04 03:38:04 +08:00
00451057cf 加点东西 2026-02-04 02:48:44 +08:00
f0b9fdb003 加ekf 2026-02-02 20:39:50 +08:00
e914c4647c 添加ekf 2026-02-02 02:58:48 +08:00
be86c08ef4 添加卡尔曼滤波器 2026-02-02 02:46:09 +08:00
6d73319b54 添加卡尔曼滤波 2026-02-02 02:24:32 +08:00
796dbb2c16 vofa好了 2026-02-01 13:12:06 +08:00
b837b7f147 加vofa 2026-01-29 22:11:21 +08:00
50 changed files with 23503 additions and 10175 deletions

File diff suppressed because one or more lines are too long

View File

@ -56,12 +56,14 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
User/bsp/uart.c User/bsp/uart.c
# User/component sources # User/component sources
User/component/QuaternionEKF.c
User/component/ahrs.c User/component/ahrs.c
User/component/crc16.c User/component/crc16.c
User/component/crc8.c User/component/crc8.c
User/component/error_detect.c User/component/error_detect.c
User/component/filter.c User/component/filter.c
User/component/freertos_cli.c User/component/freertos_cli.c
User/component/kalman_filter.c
User/component/limiter.c User/component/limiter.c
User/component/lqr.c User/component/lqr.c
User/component/pid.c User/component/pid.c
@ -79,6 +81,8 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
User/device/motor_lz.c User/device/motor_lz.c
User/device/motor_rm.c User/device/motor_rm.c
User/device/vision_bridge.c User/device/vision_bridge.c
User/device/vofa.c
User/device/mrobot.c
# User/module sources # User/module sources
User/module/balance_chassis.c User/module/balance_chassis.c
@ -96,7 +100,9 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
User/task/init.c User/task/init.c
User/task/monitor.c User/task/monitor.c
User/task/rc.c User/task/rc.c
User/task/cli.c
User/task/user_task.c User/task/user_task.c
User/task/vofa.c
) )
# Add include paths # Add include paths
@ -116,6 +122,7 @@ list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_LIBRARIES ob)
# Add linked libraries # Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME} target_link_libraries(${CMAKE_PROJECT_NAME}
stm32cubemx stm32cubemx
${CMAKE_SOURCE_DIR}/Drivers/CMSIS/DSP/Lib/GCC/libarm_cortexM7lfsp_math.a
# Add user defined libraries # Add user defined libraries
) )

View File

@ -51,6 +51,10 @@
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h> #include <stdint.h>
extern uint32_t SystemCoreClock; extern uint32_t SystemCoreClock;
/* USER CODE BEGIN 0 */
extern void configureTimerForRunTimeStats(void);
extern unsigned long getRunTimeCounterValue(void);
/* USER CODE END 0 */
#endif #endif
#ifndef CMSIS_device_header #ifndef CMSIS_device_header
#define CMSIS_device_header "stm32h7xx.h" #define CMSIS_device_header "stm32h7xx.h"
@ -68,9 +72,11 @@
#define configTICK_RATE_HZ ((TickType_t)1000) #define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES ( 56 ) #define configMAX_PRIORITIES ( 56 )
#define configMINIMAL_STACK_SIZE ((uint16_t)128) #define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)0x10000) #define configTOTAL_HEAP_SIZE ((size_t)0x20000)
#define configMAX_TASK_NAME_LEN ( 16 ) #define configMAX_TASK_NAME_LEN ( 16 )
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY 1 #define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#define configUSE_16_BIT_TICKS 0 #define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1 #define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8 #define configQUEUE_REGISTRY_SIZE 8
@ -163,6 +169,12 @@ standard names. */
#define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 0 #define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 0
/* USER CODE BEGIN 2 */
/* Definitions needed when configGENERATE_RUN_TIME_STATS is on */
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS configureTimerForRunTimeStats
#define portGET_RUN_TIME_COUNTER_VALUE getRunTimeCounterValue
/* USER CODE END 2 */
/* USER CODE BEGIN Defines */ /* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */ /* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
/* USER CODE END Defines */ /* USER CODE END Defines */

View File

@ -34,11 +34,14 @@ extern "C" {
extern ADC_HandleTypeDef hadc1; extern ADC_HandleTypeDef hadc1;
extern ADC_HandleTypeDef hadc3;
/* USER CODE BEGIN Private defines */ /* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */ /* USER CODE END Private defines */
void MX_ADC1_Init(void); void MX_ADC1_Init(void);
void MX_ADC3_Init(void);
/* USER CODE BEGIN Prototypes */ /* USER CODE BEGIN Prototypes */

52
Core/Inc/bdma.h Normal file
View File

@ -0,0 +1,52 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file bdma.h
* @brief This file contains all the function prototypes for
* the bdma.c file
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __BDMA_H__
#define __BDMA_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* DMA memory to memory transfer handles -------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_BDMA_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __BDMA_H__ */

View File

@ -56,6 +56,8 @@ void DMA1_Stream0_IRQHandler(void);
void DMA1_Stream1_IRQHandler(void); void DMA1_Stream1_IRQHandler(void);
void DMA1_Stream2_IRQHandler(void); void DMA1_Stream2_IRQHandler(void);
void DMA1_Stream3_IRQHandler(void); void DMA1_Stream3_IRQHandler(void);
void DMA1_Stream4_IRQHandler(void);
void DMA1_Stream5_IRQHandler(void);
void FDCAN1_IT0_IRQHandler(void); void FDCAN1_IT0_IRQHandler(void);
void FDCAN2_IT0_IRQHandler(void); void FDCAN2_IT0_IRQHandler(void);
void FDCAN1_IT1_IRQHandler(void); void FDCAN1_IT1_IRQHandler(void);
@ -67,6 +69,8 @@ void USART3_IRQHandler(void);
void EXTI15_10_IRQHandler(void); void EXTI15_10_IRQHandler(void);
void UART5_IRQHandler(void); void UART5_IRQHandler(void);
void UART7_IRQHandler(void); void UART7_IRQHandler(void);
void ADC3_IRQHandler(void);
void BDMA_Channel0_IRQHandler(void);
void USART10_IRQHandler(void); void USART10_IRQHandler(void);
void FDCAN3_IT0_IRQHandler(void); void FDCAN3_IT0_IRQHandler(void);
void FDCAN3_IT1_IRQHandler(void); void FDCAN3_IT1_IRQHandler(void);

View File

@ -25,7 +25,9 @@
/* USER CODE END 0 */ /* USER CODE END 0 */
ADC_HandleTypeDef hadc1; ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc3;
DMA_HandleTypeDef hdma_adc1; DMA_HandleTypeDef hdma_adc1;
DMA_HandleTypeDef hdma_adc3;
/* ADC1 init function */ /* ADC1 init function */
void MX_ADC1_Init(void) void MX_ADC1_Init(void)
@ -99,36 +101,75 @@ void MX_ADC1_Init(void)
/* USER CODE END ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */
}
/* ADC3 init function */
void MX_ADC3_Init(void)
{
/* USER CODE BEGIN ADC3_Init 0 */
/* USER CODE END ADC3_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC3_Init 1 */
/* USER CODE END ADC3_Init 1 */
/** Common config
*/
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc3.Init.Resolution = ADC_RESOLUTION_12B;
hadc3.Init.DataAlign = ADC3_DATAALIGN_RIGHT;
hadc3.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc3.Init.LowPowerAutoWait = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc3.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
hadc3.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc3.Init.OversamplingMode = DISABLE;
hadc3.Init.Oversampling.Ratio = ADC3_OVERSAMPLING_RATIO_2;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VBAT;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC3_SAMPLETIME_2CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
sConfig.OffsetSign = ADC3_OFFSET_SIGN_NEGATIVE;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC3_Init 2 */
/* USER CODE END ADC3_Init 2 */
} }
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{ {
GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(adcHandle->Instance==ADC1) if(adcHandle->Instance==ADC1)
{ {
/* USER CODE BEGIN ADC1_MspInit 0 */ /* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */ /* USER CODE END ADC1_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.PLL2.PLL2M = 2;
PeriphClkInitStruct.PLL2.PLL2N = 16;
PeriphClkInitStruct.PLL2.PLL2P = 2;
PeriphClkInitStruct.PLL2.PLL2Q = 2;
PeriphClkInitStruct.PLL2.PLL2R = 2;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* ADC1 clock enable */ /* ADC1 clock enable */
__HAL_RCC_ADC12_CLK_ENABLE(); __HAL_RCC_ADC12_CLK_ENABLE();
@ -171,6 +212,39 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
/* USER CODE END ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */
} }
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspInit 0 */
/* USER CODE END ADC3_MspInit 0 */
/* ADC3 clock enable */
__HAL_RCC_ADC3_CLK_ENABLE();
/* ADC3 DMA Init */
/* ADC3 Init */
hdma_adc3.Instance = BDMA_Channel0;
hdma_adc3.Init.Request = BDMA_REQUEST_ADC3;
hdma_adc3.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc3.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc3.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc3.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc3.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc3.Init.Mode = DMA_NORMAL;
hdma_adc3.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_adc3) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc3);
/* ADC3 interrupt Init */
HAL_NVIC_SetPriority(ADC3_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(ADC3_IRQn);
/* USER CODE BEGIN ADC3_MspInit 1 */
/* USER CODE END ADC3_MspInit 1 */
}
} }
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
@ -198,6 +272,23 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
/* USER CODE END ADC1_MspDeInit 1 */ /* USER CODE END ADC1_MspDeInit 1 */
} }
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspDeInit 0 */
/* USER CODE END ADC3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC3_CLK_DISABLE();
/* ADC3 DMA DeInit */
HAL_DMA_DeInit(adcHandle->DMA_Handle);
/* ADC3 interrupt Deinit */
HAL_NVIC_DisableIRQ(ADC3_IRQn);
/* USER CODE BEGIN ADC3_MspDeInit 1 */
/* USER CODE END ADC3_MspDeInit 1 */
}
} }
/* USER CODE BEGIN 1 */ /* USER CODE BEGIN 1 */

55
Core/Src/bdma.c Normal file
View File

@ -0,0 +1,55 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file bdma.c
* @brief This file provides code for the configuration
* of all the requested memory to memory DMA transfers.
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "bdma.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure DMA */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* Enable DMA controller clock
*/
void MX_BDMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_BDMA_CLK_ENABLE();
/* DMA interrupt init */
/* BDMA_Channel0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn);
}
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */

View File

@ -55,6 +55,12 @@ void MX_DMA_Init(void)
/* DMA1_Stream3_IRQn interrupt configuration */ /* DMA1_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 5, 0); HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn); HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
/* DMA1_Stream4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
/* DMA1_Stream5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
} }

View File

@ -65,6 +65,23 @@ void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/* Hook prototypes */
void configureTimerForRunTimeStats(void);
unsigned long getRunTimeCounterValue(void);
/* USER CODE BEGIN 1 */
/* Functions needed when configGENERATE_RUN_TIME_STATS is on */
__weak void configureTimerForRunTimeStats(void)
{
}
__weak unsigned long getRunTimeCounterValue(void)
{
return 0;
}
/* USER CODE END 1 */
/** /**
* @brief FreeRTOS initialization * @brief FreeRTOS initialization
* @param None * @param None

View File

@ -20,6 +20,7 @@
#include "main.h" #include "main.h"
#include "cmsis_os.h" #include "cmsis_os.h"
#include "adc.h" #include "adc.h"
#include "bdma.h"
#include "dma.h" #include "dma.h"
#include "fdcan.h" #include "fdcan.h"
#include "octospi.h" #include "octospi.h"
@ -57,6 +58,7 @@
/* Private function prototypes -----------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void); void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
void MX_FREERTOS_Init(void); void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */ /* USER CODE BEGIN PFP */
@ -90,6 +92,9 @@ int main(void)
/* Configure the system clock */ /* Configure the system clock */
SystemClock_Config(); SystemClock_Config();
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
/* USER CODE BEGIN SysInit */ /* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */ /* USER CODE END SysInit */
@ -97,6 +102,7 @@ int main(void)
/* Initialize all configured peripherals */ /* Initialize all configured peripherals */
MX_GPIO_Init(); MX_GPIO_Init();
MX_DMA_Init(); MX_DMA_Init();
MX_BDMA_Init();
MX_ADC1_Init(); MX_ADC1_Init();
MX_TIM12_Init(); MX_TIM12_Init();
MX_SPI1_Init(); MX_SPI1_Init();
@ -113,8 +119,9 @@ int main(void)
MX_TIM1_Init(); MX_TIM1_Init();
MX_TIM2_Init(); MX_TIM2_Init();
MX_OCTOSPI1_Init(); MX_OCTOSPI1_Init();
MX_USB_OTG_HS_PCD_Init();
MX_UART5_Init(); MX_UART5_Init();
MX_ADC3_Init();
MX_USB_OTG_HS_PCD_Init();
/* USER CODE BEGIN 2 */ /* USER CODE BEGIN 2 */
/* USER CODE END 2 */ /* USER CODE END 2 */
@ -202,6 +209,32 @@ void SystemClock_Config(void)
HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSI, RCC_MCODIV_1); HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSI, RCC_MCODIV_1);
} }
/**
* @brief Peripherals Common Clock Configuration
* @retval None
*/
void PeriphCommonClock_Config(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.PLL2.PLL2M = 2;
PeriphClkInitStruct.PLL2.PLL2N = 16;
PeriphClkInitStruct.PLL2.PLL2P = 2;
PeriphClkInitStruct.PLL2.PLL2Q = 2;
PeriphClkInitStruct.PLL2.PLL2R = 2;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */ /* USER CODE BEGIN 4 */
/* USER CODE END 4 */ /* USER CODE END 4 */

View File

@ -22,6 +22,7 @@
#include "stm32h7xx_it.h" #include "stm32h7xx_it.h"
/* Private includes ----------------------------------------------------------*/ /* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes */
#include "bsp/uart.h"
/* USER CODE END Includes */ /* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/
@ -56,6 +57,8 @@
/* External variables --------------------------------------------------------*/ /* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_adc1; extern DMA_HandleTypeDef hdma_adc1;
extern DMA_HandleTypeDef hdma_adc3;
extern ADC_HandleTypeDef hadc3;
extern FDCAN_HandleTypeDef hfdcan1; extern FDCAN_HandleTypeDef hfdcan1;
extern FDCAN_HandleTypeDef hfdcan2; extern FDCAN_HandleTypeDef hfdcan2;
extern FDCAN_HandleTypeDef hfdcan3; extern FDCAN_HandleTypeDef hfdcan3;
@ -63,6 +66,8 @@ extern DMA_HandleTypeDef hdma_spi2_rx;
extern DMA_HandleTypeDef hdma_spi2_tx; extern DMA_HandleTypeDef hdma_spi2_tx;
extern SPI_HandleTypeDef hspi2; extern SPI_HandleTypeDef hspi2;
extern DMA_HandleTypeDef hdma_uart5_rx; extern DMA_HandleTypeDef hdma_uart5_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern UART_HandleTypeDef huart5; extern UART_HandleTypeDef huart5;
extern UART_HandleTypeDef huart7; extern UART_HandleTypeDef huart7;
extern UART_HandleTypeDef huart1; extern UART_HandleTypeDef huart1;
@ -229,6 +234,34 @@ void DMA1_Stream3_IRQHandler(void)
/* USER CODE END DMA1_Stream3_IRQn 1 */ /* USER CODE END DMA1_Stream3_IRQn 1 */
} }
/**
* @brief This function handles DMA1 stream4 global interrupt.
*/
void DMA1_Stream4_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream4_IRQn 0 */
/* USER CODE END DMA1_Stream4_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_tx);
/* USER CODE BEGIN DMA1_Stream4_IRQn 1 */
/* USER CODE END DMA1_Stream4_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream5 global interrupt.
*/
void DMA1_Stream5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream5_IRQn 0 */
/* USER CODE END DMA1_Stream5_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_rx);
/* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
/* USER CODE END DMA1_Stream5_IRQn 1 */
}
/** /**
* @brief This function handles FDCAN1 interrupt 0. * @brief This function handles FDCAN1 interrupt 0.
*/ */
@ -309,6 +342,7 @@ void USART1_IRQHandler(void)
/* USER CODE END USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1); HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE BEGIN USART1_IRQn 1 */
BSP_UART_IRQHandler(&huart1);
/* USER CODE END USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */
} }
@ -366,6 +400,7 @@ void UART5_IRQHandler(void)
/* USER CODE END UART5_IRQn 0 */ /* USER CODE END UART5_IRQn 0 */
HAL_UART_IRQHandler(&huart5); HAL_UART_IRQHandler(&huart5);
/* USER CODE BEGIN UART5_IRQn 1 */ /* USER CODE BEGIN UART5_IRQn 1 */
BSP_UART_IRQHandler(&huart5);
/* USER CODE END UART5_IRQn 1 */ /* USER CODE END UART5_IRQn 1 */
} }
@ -384,6 +419,34 @@ void UART7_IRQHandler(void)
/* USER CODE END UART7_IRQn 1 */ /* USER CODE END UART7_IRQn 1 */
} }
/**
* @brief This function handles ADC3 global interrupt.
*/
void ADC3_IRQHandler(void)
{
/* USER CODE BEGIN ADC3_IRQn 0 */
/* USER CODE END ADC3_IRQn 0 */
HAL_ADC_IRQHandler(&hadc3);
/* USER CODE BEGIN ADC3_IRQn 1 */
/* USER CODE END ADC3_IRQn 1 */
}
/**
* @brief This function handles BDMA channel0 global interrupt.
*/
void BDMA_Channel0_IRQHandler(void)
{
/* USER CODE BEGIN BDMA_Channel0_IRQn 0 */
/* USER CODE END BDMA_Channel0_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_adc3);
/* USER CODE BEGIN BDMA_Channel0_IRQn 1 */
/* USER CODE END BDMA_Channel0_IRQn 1 */
}
/** /**
* @brief This function handles USART10 global interrupt. * @brief This function handles USART10 global interrupt.
*/ */

View File

@ -31,6 +31,8 @@ UART_HandleTypeDef huart2;
UART_HandleTypeDef huart3; UART_HandleTypeDef huart3;
UART_HandleTypeDef huart10; UART_HandleTypeDef huart10;
DMA_HandleTypeDef hdma_uart5_rx; DMA_HandleTypeDef hdma_uart5_rx;
DMA_HandleTypeDef hdma_usart1_tx;
DMA_HandleTypeDef hdma_usart1_rx;
/* UART5 init function */ /* UART5 init function */
void MX_UART5_Init(void) void MX_UART5_Init(void)
@ -431,6 +433,43 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
GPIO_InitStruct.Alternate = GPIO_AF7_USART1; GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 DMA Init */
/* USART1_TX Init */
hdma_usart1_tx.Instance = DMA1_Stream4;
hdma_usart1_tx.Init.Request = DMA_REQUEST_USART1_TX;
hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_tx.Init.Mode = DMA_NORMAL;
hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);
/* USART1_RX Init */
hdma_usart1_rx.Instance = DMA1_Stream5;
hdma_usart1_rx.Init.Request = DMA_REQUEST_USART1_RX;
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_rx.Init.Mode = DMA_NORMAL;
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);
/* USART1 interrupt Init */ /* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn); HAL_NVIC_EnableIRQ(USART1_IRQn);
@ -630,6 +669,10 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
*/ */
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10); HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
/* USART1 DMA DeInit */
HAL_DMA_DeInit(uartHandle->hdmatx);
HAL_DMA_DeInit(uartHandle->hdmarx);
/* USART1 interrupt Deinit */ /* USART1 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART1_IRQn); HAL_NVIC_DisableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspDeInit 1 */ /* USER CODE BEGIN USART1_MspDeInit 1 */

View File

@ -16,6 +16,33 @@ ADC1.Rank-1\#ChannelRegularConversion=2
ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_32CYCLES_5 ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_32CYCLES_5
ADC1.SamplingTime-1\#ChannelRegularConversion=ADC_SAMPLETIME_32CYCLES_5 ADC1.SamplingTime-1\#ChannelRegularConversion=ADC_SAMPLETIME_32CYCLES_5
ADC1.master=1 ADC1.master=1
ADC3.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_VBAT
ADC3.ClockPrescaler=ADC_CLOCK_ASYNC_DIV64
ADC3.IPParameters=Rank-0\#ChannelRegularConversion,ClockPrescaler,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,OffsetNumber-0\#ChannelRegularConversion,OffsetSign-0\#ChannelRegularConversion,NbrOfConversionFlag
ADC3.NbrOfConversionFlag=1
ADC3.OffsetNumber-0\#ChannelRegularConversion=ADC_OFFSET_NONE
ADC3.OffsetSign-0\#ChannelRegularConversion=ADC3_OFFSET_SIGN_NEGATIVE
ADC3.Rank-0\#ChannelRegularConversion=1
ADC3.SamplingTime-0\#ChannelRegularConversion=ADC3_SAMPLETIME_2CYCLES_5
Bdma.ADC3.0.Direction=DMA_PERIPH_TO_MEMORY
Bdma.ADC3.0.EventEnable=DISABLE
Bdma.ADC3.0.Instance=BDMA_Channel0
Bdma.ADC3.0.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Bdma.ADC3.0.MemInc=DMA_MINC_ENABLE
Bdma.ADC3.0.Mode=DMA_NORMAL
Bdma.ADC3.0.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Bdma.ADC3.0.PeriphInc=DMA_PINC_DISABLE
Bdma.ADC3.0.Polarity=HAL_DMAMUX_REQ_GEN_RISING
Bdma.ADC3.0.Priority=DMA_PRIORITY_LOW
Bdma.ADC3.0.RequestNumber=1
Bdma.ADC3.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,SignalID,Polarity,RequestNumber,SyncSignalID,SyncPolarity,SyncEnable,EventEnable,SyncRequestNumber
Bdma.ADC3.0.SignalID=NONE
Bdma.ADC3.0.SyncEnable=DISABLE
Bdma.ADC3.0.SyncPolarity=HAL_DMAMUX_SYNC_NO_EVENT
Bdma.ADC3.0.SyncRequestNumber=1
Bdma.ADC3.0.SyncSignalID=NONE
Bdma.Request0=ADC3
Bdma.RequestsNb=1
CAD.formats= CAD.formats=
CAD.pinconfig= CAD.pinconfig=
CAD.provider= CAD.provider=
@ -51,7 +78,9 @@ Dma.Request0=ADC1
Dma.Request1=SPI2_RX Dma.Request1=SPI2_RX
Dma.Request2=SPI2_TX Dma.Request2=SPI2_TX
Dma.Request3=UART5_RX Dma.Request3=UART5_RX
Dma.RequestsNb=4 Dma.Request4=USART1_TX
Dma.Request5=USART1_RX
Dma.RequestsNb=6
Dma.SPI2_RX.1.Direction=DMA_PERIPH_TO_MEMORY Dma.SPI2_RX.1.Direction=DMA_PERIPH_TO_MEMORY
Dma.SPI2_RX.1.EventEnable=DISABLE Dma.SPI2_RX.1.EventEnable=DISABLE
Dma.SPI2_RX.1.FIFOMode=DMA_FIFOMODE_DISABLE Dma.SPI2_RX.1.FIFOMode=DMA_FIFOMODE_DISABLE
@ -106,6 +135,42 @@ Dma.UART5_RX.3.SyncEnable=DISABLE
Dma.UART5_RX.3.SyncPolarity=HAL_DMAMUX_SYNC_NO_EVENT Dma.UART5_RX.3.SyncPolarity=HAL_DMAMUX_SYNC_NO_EVENT
Dma.UART5_RX.3.SyncRequestNumber=1 Dma.UART5_RX.3.SyncRequestNumber=1
Dma.UART5_RX.3.SyncSignalID=NONE Dma.UART5_RX.3.SyncSignalID=NONE
Dma.USART1_RX.5.Direction=DMA_PERIPH_TO_MEMORY
Dma.USART1_RX.5.EventEnable=DISABLE
Dma.USART1_RX.5.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.USART1_RX.5.Instance=DMA1_Stream5
Dma.USART1_RX.5.MemDataAlignment=DMA_MDATAALIGN_BYTE
Dma.USART1_RX.5.MemInc=DMA_MINC_ENABLE
Dma.USART1_RX.5.Mode=DMA_NORMAL
Dma.USART1_RX.5.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
Dma.USART1_RX.5.PeriphInc=DMA_PINC_DISABLE
Dma.USART1_RX.5.Polarity=HAL_DMAMUX_REQ_GEN_RISING
Dma.USART1_RX.5.Priority=DMA_PRIORITY_LOW
Dma.USART1_RX.5.RequestNumber=1
Dma.USART1_RX.5.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode,SignalID,Polarity,RequestNumber,SyncSignalID,SyncPolarity,SyncEnable,EventEnable,SyncRequestNumber
Dma.USART1_RX.5.SignalID=NONE
Dma.USART1_RX.5.SyncEnable=DISABLE
Dma.USART1_RX.5.SyncPolarity=HAL_DMAMUX_SYNC_NO_EVENT
Dma.USART1_RX.5.SyncRequestNumber=1
Dma.USART1_RX.5.SyncSignalID=NONE
Dma.USART1_TX.4.Direction=DMA_MEMORY_TO_PERIPH
Dma.USART1_TX.4.EventEnable=DISABLE
Dma.USART1_TX.4.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.USART1_TX.4.Instance=DMA1_Stream4
Dma.USART1_TX.4.MemDataAlignment=DMA_MDATAALIGN_BYTE
Dma.USART1_TX.4.MemInc=DMA_MINC_ENABLE
Dma.USART1_TX.4.Mode=DMA_NORMAL
Dma.USART1_TX.4.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
Dma.USART1_TX.4.PeriphInc=DMA_PINC_DISABLE
Dma.USART1_TX.4.Polarity=HAL_DMAMUX_REQ_GEN_RISING
Dma.USART1_TX.4.Priority=DMA_PRIORITY_LOW
Dma.USART1_TX.4.RequestNumber=1
Dma.USART1_TX.4.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode,SignalID,Polarity,RequestNumber,SyncSignalID,SyncPolarity,SyncEnable,EventEnable,SyncRequestNumber
Dma.USART1_TX.4.SignalID=NONE
Dma.USART1_TX.4.SyncEnable=DISABLE
Dma.USART1_TX.4.SyncPolarity=HAL_DMAMUX_SYNC_NO_EVENT
Dma.USART1_TX.4.SyncRequestNumber=1
Dma.USART1_TX.4.SyncSignalID=NONE
FDCAN1.AutoRetransmission=ENABLE FDCAN1.AutoRetransmission=ENABLE
FDCAN1.CalculateBaudRateNominal=1000000 FDCAN1.CalculateBaudRateNominal=1000000
FDCAN1.CalculateTimeBitNominal=1000 FDCAN1.CalculateTimeBitNominal=1000
@ -141,9 +206,11 @@ FDCAN3.RxFifo0ElmtsNbr=32
FDCAN3.RxFifo1ElmtsNbr=32 FDCAN3.RxFifo1ElmtsNbr=32
FDCAN3.StdFiltersNbr=1 FDCAN3.StdFiltersNbr=1
FDCAN3.TxFifoQueueElmtsNbr=32 FDCAN3.TxFifoQueueElmtsNbr=32
FREERTOS.IPParameters=Tasks01,configTOTAL_HEAP_SIZE FREERTOS.IPParameters=Tasks01,configTOTAL_HEAP_SIZE,configUSE_STATS_FORMATTING_FUNCTIONS,configGENERATE_RUN_TIME_STATS
FREERTOS.Tasks01=defaultTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL FREERTOS.Tasks01=defaultTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL
FREERTOS.configGENERATE_RUN_TIME_STATS=1
FREERTOS.configTOTAL_HEAP_SIZE=0x10000 FREERTOS.configTOTAL_HEAP_SIZE=0x10000
FREERTOS.configUSE_STATS_FORMATTING_FUNCTIONS=1
File.Version=6 File.Version=6
GPIO.groupedBy=Group By Peripherals GPIO.groupedBy=Group By Peripherals
KeepUserPlacement=false KeepUserPlacement=false
@ -152,32 +219,34 @@ MMTConfigApplied=false
Mcu.CPN=STM32H723VGT6 Mcu.CPN=STM32H723VGT6
Mcu.Family=STM32H7 Mcu.Family=STM32H7
Mcu.IP0=ADC1 Mcu.IP0=ADC1
Mcu.IP1=CORTEX_M7 Mcu.IP1=ADC3
Mcu.IP10=OCTOSPI1 Mcu.IP10=MEMORYMAP
Mcu.IP11=RCC Mcu.IP11=NVIC
Mcu.IP12=SPI1 Mcu.IP12=OCTOSPI1
Mcu.IP13=SPI2 Mcu.IP13=RCC
Mcu.IP14=SYS Mcu.IP14=SPI1
Mcu.IP15=TIM1 Mcu.IP15=SPI2
Mcu.IP16=TIM2 Mcu.IP16=SYS
Mcu.IP17=TIM3 Mcu.IP17=TIM1
Mcu.IP18=TIM12 Mcu.IP18=TIM2
Mcu.IP19=UART5 Mcu.IP19=TIM3
Mcu.IP2=DEBUG Mcu.IP2=BDMA
Mcu.IP20=UART7 Mcu.IP20=TIM12
Mcu.IP21=USART1 Mcu.IP21=UART5
Mcu.IP22=USART2 Mcu.IP22=UART7
Mcu.IP23=USART3 Mcu.IP23=USART1
Mcu.IP24=USART10 Mcu.IP24=USART2
Mcu.IP25=USB_OTG_HS Mcu.IP25=USART3
Mcu.IP3=DMA Mcu.IP26=USART10
Mcu.IP4=FDCAN1 Mcu.IP27=USB_OTG_HS
Mcu.IP5=FDCAN2 Mcu.IP3=CORTEX_M7
Mcu.IP6=FDCAN3 Mcu.IP4=DEBUG
Mcu.IP7=FREERTOS Mcu.IP5=DMA
Mcu.IP8=MEMORYMAP Mcu.IP6=FDCAN1
Mcu.IP9=NVIC Mcu.IP7=FDCAN2
Mcu.IPNb=26 Mcu.IP8=FDCAN3
Mcu.IP9=FREERTOS
Mcu.IPNb=28
Mcu.Name=STM32H723VGTx Mcu.Name=STM32H723VGTx
Mcu.Package=LQFP100 Mcu.Package=LQFP100
Mcu.Pin0=PE2 Mcu.Pin0=PE2
@ -238,24 +307,33 @@ Mcu.Pin58=PB3(JTDO/TRACESWO)
Mcu.Pin59=PB5 Mcu.Pin59=PB5
Mcu.Pin6=PH1-OSC_OUT Mcu.Pin6=PH1-OSC_OUT
Mcu.Pin60=PB6 Mcu.Pin60=PB6
Mcu.Pin61=VP_FREERTOS_VS_CMSIS_V2 Mcu.Pin61=VP_ADC3_TempSens_Input
Mcu.Pin62=VP_OCTOSPI1_VS_octo Mcu.Pin62=VP_ADC3_Vref_Input
Mcu.Pin63=VP_SYS_VS_tim23 Mcu.Pin63=VP_ADC3_Vbat_Input
Mcu.Pin64=VP_MEMORYMAP_VS_MEMORYMAP Mcu.Pin64=VP_FREERTOS_VS_CMSIS_V2
Mcu.Pin65=VP_OCTOSPI1_VS_octo
Mcu.Pin66=VP_SYS_VS_tim23
Mcu.Pin67=VP_MEMORYMAP_VS_MEMORYMAP
Mcu.Pin68=VP_STMicroelectronics.X-CUBE-ALGOBUILD_VS_DSPOoLibraryJjLibrary_1.4.0_1.4.0
Mcu.Pin7=PC0 Mcu.Pin7=PC0
Mcu.Pin8=PC1 Mcu.Pin8=PC1
Mcu.Pin9=PC2_C Mcu.Pin9=PC2_C
Mcu.PinsNb=65 Mcu.PinsNb=69
Mcu.ThirdPartyNb=0 Mcu.ThirdParty0=STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0
Mcu.ThirdPartyNb=1
Mcu.UserConstants= Mcu.UserConstants=
Mcu.UserName=STM32H723VGTx Mcu.UserName=STM32H723VGTx
MxCube.Version=6.15.0 MxCube.Version=6.15.0
MxDb.Version=DB.6.0.150 MxDb.Version=DB.6.0.150
NVIC.ADC3_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
NVIC.BDMA_Channel0_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.DMA1_Stream0_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true NVIC.DMA1_Stream0_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Stream1_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true NVIC.DMA1_Stream1_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Stream2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true NVIC.DMA1_Stream2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Stream3_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true NVIC.DMA1_Stream3_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Stream4_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Stream5_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.EXTI15_10_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true NVIC.EXTI15_10_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
NVIC.FDCAN1_IT0_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true NVIC.FDCAN1_IT0_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
@ -509,12 +587,12 @@ ProjectManager.ProjectName=CtrBoard-H7_ALL
ProjectManager.ProjectStructure= ProjectManager.ProjectStructure=
ProjectManager.RegisterCallBack= ProjectManager.RegisterCallBack=
ProjectManager.StackSize=0x2000 ProjectManager.StackSize=0x2000
ProjectManager.TargetToolchain=MDK-ARM V5.32 ProjectManager.TargetToolchain=CMake
ProjectManager.ToolChainLocation= ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath= ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath= ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_TIM12_Init-TIM12-false-HAL-true,6-MX_SPI1_Init-SPI1-false-HAL-true,7-MX_SPI2_Init-SPI2-false-HAL-true,8-MX_TIM3_Init-TIM3-false-HAL-true,9-MX_USART1_UART_Init-USART1-false-HAL-true,10-MX_USART2_UART_Init-USART2-false-HAL-true,11-MX_USART3_UART_Init-USART3-false-HAL-true,12-MX_UART7_Init-UART7-false-HAL-true,13-MX_USART10_UART_Init-USART10-false-HAL-true,14-MX_FDCAN1_Init-FDCAN1-false-HAL-true,15-MX_FDCAN2_Init-FDCAN2-false-HAL-true,16-MX_FDCAN3_Init-FDCAN3-false-HAL-true,17-MX_TIM1_Init-TIM1-false-HAL-true,18-MX_TIM2_Init-TIM2-false-HAL-true,19-MX_OCTOSPI1_Init-OCTOSPI1-false-HAL-true,20-MX_USB_OTG_HS_PCD_Init-USB_OTG_HS-false-HAL-true,21-MX_UART5_Init-UART5-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,5-MX_BDMA_Init-BDMA-false-HAL-true,6-MX_ADC1_Init-ADC1-false-HAL-true,7-MX_TIM12_Init-TIM12-false-HAL-true,8-MX_SPI1_Init-SPI1-false-HAL-true,9-MX_SPI2_Init-SPI2-false-HAL-true,10-MX_TIM3_Init-TIM3-false-HAL-true,11-MX_USART1_UART_Init-USART1-false-HAL-true,12-MX_USART2_UART_Init-USART2-false-HAL-true,13-MX_USART3_UART_Init-USART3-false-HAL-true,14-MX_UART7_Init-UART7-false-HAL-true,15-MX_USART10_UART_Init-USART10-false-HAL-true,16-MX_FDCAN1_Init-FDCAN1-false-HAL-true,17-MX_FDCAN2_Init-FDCAN2-false-HAL-true,18-MX_FDCAN3_Init-FDCAN3-false-HAL-true,19-MX_TIM1_Init-TIM1-false-HAL-true,20-MX_TIM2_Init-TIM2-false-HAL-true,21-MX_OCTOSPI1_Init-OCTOSPI1-false-HAL-true,23-MX_UART5_Init-UART5-false-HAL-true,24-MX_ADC3_Init-ADC3-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true
RCC.ADCFreq_Value=96000000 RCC.ADCFreq_Value=96000000
RCC.AHB12Freq_Value=240000000 RCC.AHB12Freq_Value=240000000
RCC.AHB4Freq_Value=240000000 RCC.AHB4Freq_Value=240000000
@ -641,6 +719,10 @@ SPI2.Direction=SPI_DIRECTION_2LINES
SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,BaudRatePrescaler,CLKPolarity,CLKPhase SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,BaudRatePrescaler,CLKPolarity,CLKPhase
SPI2.Mode=SPI_MODE_MASTER SPI2.Mode=SPI_MODE_MASTER
SPI2.VirtualType=VM_MASTER SPI2.VirtualType=VM_MASTER
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.DSPOoLibraryJjLibrary_Checked=true
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.IPParameters=LibraryCcDSPOoLibraryJjDSPOoLibrary
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.LibraryCcDSPOoLibraryJjDSPOoLibrary=true
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0_SwParameter=LibraryCcDSPOoLibraryJjDSPOoLibrary\:true;
TIM1.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1 TIM1.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
TIM1.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3 TIM1.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
TIM1.IPParameters=Channel-PWM Generation3 CH3,Channel-PWM Generation1 CH1,Period,Pulse-PWM Generation1 CH1,Pulse-PWM Generation3 CH3,Prescaler TIM1.IPParameters=Channel-PWM Generation3 CH3,Channel-PWM Generation1 CH1,Period,Pulse-PWM Generation1 CH1,Pulse-PWM Generation3 CH3,Prescaler
@ -688,12 +770,20 @@ USART3.VirtualMode-Asynchronous=VM_ASYNC
USART3.VirtualMode-Hardware\ Flow\ Control\ (RS485)=VM_ASYNC USART3.VirtualMode-Hardware\ Flow\ Control\ (RS485)=VM_ASYNC
USB_OTG_HS.IPParameters=VirtualMode-Device_Only_FS USB_OTG_HS.IPParameters=VirtualMode-Device_Only_FS
USB_OTG_HS.VirtualMode-Device_Only_FS=Device_Only_FS USB_OTG_HS.VirtualMode-Device_Only_FS=Device_Only_FS
VP_ADC3_TempSens_Input.Mode=IN-TempSens
VP_ADC3_TempSens_Input.Signal=ADC3_TempSens_Input
VP_ADC3_Vbat_Input.Mode=IN-Vbat
VP_ADC3_Vbat_Input.Signal=ADC3_Vbat_Input
VP_ADC3_Vref_Input.Mode=IN-Vrefint
VP_ADC3_Vref_Input.Signal=ADC3_Vref_Input
VP_FREERTOS_VS_CMSIS_V2.Mode=CMSIS_V2 VP_FREERTOS_VS_CMSIS_V2.Mode=CMSIS_V2
VP_FREERTOS_VS_CMSIS_V2.Signal=FREERTOS_VS_CMSIS_V2 VP_FREERTOS_VS_CMSIS_V2.Signal=FREERTOS_VS_CMSIS_V2
VP_MEMORYMAP_VS_MEMORYMAP.Mode=CurAppReg VP_MEMORYMAP_VS_MEMORYMAP.Mode=CurAppReg
VP_MEMORYMAP_VS_MEMORYMAP.Signal=MEMORYMAP_VS_MEMORYMAP VP_MEMORYMAP_VS_MEMORYMAP.Signal=MEMORYMAP_VS_MEMORYMAP
VP_OCTOSPI1_VS_octo.Mode=octo_mode VP_OCTOSPI1_VS_octo.Mode=octo_mode
VP_OCTOSPI1_VS_octo.Signal=OCTOSPI1_VS_octo VP_OCTOSPI1_VS_octo.Signal=OCTOSPI1_VS_octo
VP_STMicroelectronics.X-CUBE-ALGOBUILD_VS_DSPOoLibraryJjLibrary_1.4.0_1.4.0.Mode=DSPOoLibraryJjLibrary
VP_STMicroelectronics.X-CUBE-ALGOBUILD_VS_DSPOoLibraryJjLibrary_1.4.0_1.4.0.Signal=STMicroelectronics.X-CUBE-ALGOBUILD_VS_DSPOoLibraryJjLibrary_1.4.0_1.4.0
VP_SYS_VS_tim23.Mode=TIM23 VP_SYS_VS_tim23.Mode=TIM23
VP_SYS_VS_tim23.Signal=SYS_VS_tim23 VP_SYS_VS_tim23.Signal=SYS_VS_tim23
board=custom board=custom

View File

@ -1323,6 +1323,18 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<bShared>0</bShared> <bShared>0</bShared>
</File> </File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>85</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\User\device\vofa.c</PathWithFileName>
<FilenameWithoutPath>vofa.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group> </Group>
<Group> <Group>
@ -1333,7 +1345,7 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<File> <File>
<GroupNumber>9</GroupNumber> <GroupNumber>9</GroupNumber>
<FileNumber>85</FileNumber> <FileNumber>86</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1345,7 +1357,7 @@
</File> </File>
<File> <File>
<GroupNumber>9</GroupNumber> <GroupNumber>9</GroupNumber>
<FileNumber>86</FileNumber> <FileNumber>87</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1357,7 +1369,7 @@
</File> </File>
<File> <File>
<GroupNumber>9</GroupNumber> <GroupNumber>9</GroupNumber>
<FileNumber>87</FileNumber> <FileNumber>88</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1369,7 +1381,7 @@
</File> </File>
<File> <File>
<GroupNumber>9</GroupNumber> <GroupNumber>9</GroupNumber>
<FileNumber>88</FileNumber> <FileNumber>89</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1389,7 +1401,7 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>89</FileNumber> <FileNumber>90</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1401,7 +1413,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>90</FileNumber> <FileNumber>91</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1413,7 +1425,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>91</FileNumber> <FileNumber>92</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1425,7 +1437,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>92</FileNumber> <FileNumber>93</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1437,7 +1449,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>93</FileNumber> <FileNumber>94</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1449,7 +1461,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>94</FileNumber> <FileNumber>95</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1461,7 +1473,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>95</FileNumber> <FileNumber>96</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1473,7 +1485,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>96</FileNumber> <FileNumber>97</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1485,7 +1497,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>97</FileNumber> <FileNumber>98</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1497,7 +1509,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>98</FileNumber> <FileNumber>99</FileNumber>
<FileType>5</FileType> <FileType>5</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1509,7 +1521,7 @@
</File> </File>
<File> <File>
<GroupNumber>10</GroupNumber> <GroupNumber>10</GroupNumber>
<FileNumber>99</FileNumber> <FileNumber>100</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@ -1519,6 +1531,18 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<bShared>0</bShared> <bShared>0</bShared>
</File> </File>
<File>
<GroupNumber>10</GroupNumber>
<FileNumber>101</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\User\task\vofa.c</PathWithFileName>
<FilenameWithoutPath>vofa.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group> </Group>
<Group> <Group>

View File

@ -839,6 +839,11 @@
<FileType>1</FileType> <FileType>1</FileType>
<FilePath>..\User\device\vision_bridge.c</FilePath> <FilePath>..\User\device\vision_bridge.c</FilePath>
</File> </File>
<File>
<FileName>vofa.c</FileName>
<FileType>1</FileType>
<FilePath>..\User\device\vofa.c</FilePath>
</File>
</Files> </Files>
</Group> </Group>
<Group> <Group>
@ -924,6 +929,11 @@
<FileType>1</FileType> <FileType>1</FileType>
<FilePath>..\User\task\ai.c</FilePath> <FilePath>..\User\task\ai.c</FilePath>
</File> </File>
<File>
<FileName>vofa.c</FileName>
<FileType>1</FileType>
<FilePath>..\User\task\vofa.c</FilePath>
</File>
</Files> </Files>
</Group> </Group>
<Group> <Group>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

BIN
User/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -124,4 +124,6 @@ uart:
devices: devices:
- instance: UART5 - instance: UART5
name: DR16 name: DR16
- instance: USART1
name: VOFA
enabled: true enabled: true

View File

@ -25,6 +25,8 @@ static void (*UART_Callback[BSP_UART_NUM][BSP_UART_CB_NUM])(void);
static BSP_UART_t UART_Get(UART_HandleTypeDef *huart) { static BSP_UART_t UART_Get(UART_HandleTypeDef *huart) {
if (huart->Instance == UART5) if (huart->Instance == UART5)
return BSP_UART_DR16; return BSP_UART_DR16;
else if (huart->Instance == USART1)
return BSP_UART_VOFA;
else else
return BSP_UART_ERR; return BSP_UART_ERR;
} }
@ -115,6 +117,8 @@ UART_HandleTypeDef *BSP_UART_GetHandle(BSP_UART_t uart) {
switch (uart) { switch (uart) {
case BSP_UART_DR16: case BSP_UART_DR16:
return &huart5; return &huart5;
case BSP_UART_VOFA:
return &huart1;
default: default:
return NULL; return NULL;
} }

View File

@ -28,6 +28,7 @@ extern "C" {
/* UART实体枚举与设备对应 */ /* UART实体枚举与设备对应 */
typedef enum { typedef enum {
BSP_UART_DR16, BSP_UART_DR16,
BSP_UART_VOFA,
BSP_UART_NUM, BSP_UART_NUM,
BSP_UART_ERR, BSP_UART_ERR,
} BSP_UART_t; } BSP_UART_t;

View File

@ -0,0 +1,527 @@
/*
QEKF
姿
:
1
as + 1
*/
#include "QuaternionEKF.h"
#include <string.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* 全局QEKF实例 */
QEKF_INS_t QEKF_INS;
/* 状态转移矩阵F初始值 */
static const float QEKF_F_Init[36] = {
1, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 1
};
/* 协方差矩阵P初始值 */
static float QEKF_P_Init[36] = {
1, 0.1, 0.1, 0.1, 0.1, 0.1,
0.1, 1, 0.1, 0.1, 0.1, 0.1,
0.1, 0.1, 1, 0.1, 0.1, 0.1,
0.1, 0.1, 0.1, 1, 0.1, 0.1,
0.1, 0.1, 0.1, 0.1, 1, 0.1,
0.1, 0.1, 0.1, 0.1, 0.1, 1
};
/* 用于调试的矩阵副本 */
static float QEKF_K[18];
static float QEKF_H[18];
/* 私有函数声明 */
static float QEKF_InvSqrt(float x);
static void QEKF_Observe(KF_t *kf);
static void QEKF_F_Linearization_P_Fading(KF_t *kf);
static void QEKF_SetH(KF_t *kf);
static void QEKF_xhatUpdate(KF_t *kf);
/**
* @brief EKF初始化
*
* @param process_noise1 (: 10)
* @param process_noise2 (: 0.001)
* @param measure_noise (: 1000000)
* @param lambda (: 0.9996)
* @param lpf (: 0)
*/
void QEKF_Init(float process_noise1, float process_noise2, float measure_noise,
float lambda, float lpf) {
QEKF_INS.initialized = 1;
QEKF_INS.Q1 = process_noise1;
QEKF_INS.Q2 = process_noise2;
QEKF_INS.R = measure_noise;
QEKF_INS.chi_square_threshold = 1e-8f;
QEKF_INS.converge_flag = 0;
QEKF_INS.error_count = 0;
QEKF_INS.update_count = 0;
if (lambda > 1) {
lambda = 1;
}
QEKF_INS.lambda = lambda;
QEKF_INS.acc_lpf_coef = lpf;
/* 初始化卡尔曼滤波器状态维度6控制维度0量测维度3 */
KF_Init(&QEKF_INS.qekf, 6, 0, 3);
KF_MatInit(&QEKF_INS.chi_square, 1, 1, QEKF_INS.chi_square_data);
/* 四元数初始化为单位四元数 */
QEKF_INS.qekf.xhat_data[0] = 1;
QEKF_INS.qekf.xhat_data[1] = 0;
QEKF_INS.qekf.xhat_data[2] = 0;
QEKF_INS.qekf.xhat_data[3] = 0;
/* 自定义函数初始化用于扩展或增加KF的基础功能 */
QEKF_INS.qekf.User_Func0_f = QEKF_Observe;
QEKF_INS.qekf.User_Func1_f = QEKF_F_Linearization_P_Fading;
QEKF_INS.qekf.User_Func2_f = QEKF_SetH;
QEKF_INS.qekf.User_Func3_f = QEKF_xhatUpdate;
/* 设定标志位用自定义函数替换KF标准步骤中的计算增益和后验估计 */
QEKF_INS.qekf.skip_eq3 = TRUE;
QEKF_INS.qekf.skip_eq4 = TRUE;
memcpy(QEKF_INS.qekf.F_data, QEKF_F_Init, sizeof(QEKF_F_Init));
memcpy(QEKF_INS.qekf.P_data, QEKF_P_Init, sizeof(QEKF_P_Init));
}
/**
* @brief EKF更新
*
* @param gx X轴角速度 (rad/s)
* @param gy Y轴角速度 (rad/s)
* @param gz Z轴角速度 (rad/s)
* @param ax X轴加速度 (m/s²)
* @param ay Y轴加速度 (m/s²)
* @param az Z轴加速度 (m/s²)
* @param dt (s)
*/
void QEKF_Update(float gx, float gy, float gz, float ax, float ay, float az,
float dt) {
/* 0.5*(Ohm-Ohm^bias)*deltaT用于更新状态转移F矩阵 */
float halfgxdt, halfgydt, halfgzdt;
float accel_inv_norm;
if (!QEKF_INS.initialized) {
QEKF_Init(10, 0.001f, 1000000 * 10, 0.9996f * 0 + 1, 0);
}
/* F矩阵带*号的位置需要设置
0 1* 2* 3* 4 5
6* 7 8* 9* 10 11
12* 13* 14 15* 16 17
18* 19* 20* 21 22 23
24 25 26 27 28 29
30 31 32 33 34 35
*/
QEKF_INS.dt = dt;
/* 去除零偏后的陀螺仪数据 */
QEKF_INS.gyro[0] = gx - QEKF_INS.gyro_bias[0];
QEKF_INS.gyro[1] = gy - QEKF_INS.gyro_bias[1];
QEKF_INS.gyro[2] = gz - QEKF_INS.gyro_bias[2];
/* 设置F矩阵 */
halfgxdt = 0.5f * QEKF_INS.gyro[0] * dt;
halfgydt = 0.5f * QEKF_INS.gyro[1] * dt;
halfgzdt = 0.5f * QEKF_INS.gyro[2] * dt;
/* 设定状态转移矩阵F的左上角4x4子矩阵即0.5*(Ohm-Ohm^bias)*deltaT
2x2单位阵已经初始化好了
predict步F的右上角是4x2的零矩阵predict都用单位阵覆盖前一轮线性化后的矩阵 */
memcpy(QEKF_INS.qekf.F_data, QEKF_F_Init, sizeof(QEKF_F_Init));
QEKF_INS.qekf.F_data[1] = -halfgxdt;
QEKF_INS.qekf.F_data[2] = -halfgydt;
QEKF_INS.qekf.F_data[3] = -halfgzdt;
QEKF_INS.qekf.F_data[6] = halfgxdt;
QEKF_INS.qekf.F_data[8] = halfgzdt;
QEKF_INS.qekf.F_data[9] = -halfgydt;
QEKF_INS.qekf.F_data[12] = halfgydt;
QEKF_INS.qekf.F_data[13] = -halfgzdt;
QEKF_INS.qekf.F_data[15] = halfgxdt;
QEKF_INS.qekf.F_data[18] = halfgzdt;
QEKF_INS.qekf.F_data[19] = halfgydt;
QEKF_INS.qekf.F_data[20] = -halfgxdt;
/* 加速度低通滤波,平滑数据,降低撞击和异常的影响 */
if (QEKF_INS.update_count == 0) {
/* 第一次进入,初始化低通滤波 */
QEKF_INS.accel[0] = ax;
QEKF_INS.accel[1] = ay;
QEKF_INS.accel[2] = az;
}
QEKF_INS.accel[0] = QEKF_INS.accel[0] * QEKF_INS.acc_lpf_coef /
(QEKF_INS.dt + QEKF_INS.acc_lpf_coef) +
ax * QEKF_INS.dt / (QEKF_INS.dt + QEKF_INS.acc_lpf_coef);
QEKF_INS.accel[1] = QEKF_INS.accel[1] * QEKF_INS.acc_lpf_coef /
(QEKF_INS.dt + QEKF_INS.acc_lpf_coef) +
ay * QEKF_INS.dt / (QEKF_INS.dt + QEKF_INS.acc_lpf_coef);
QEKF_INS.accel[2] = QEKF_INS.accel[2] * QEKF_INS.acc_lpf_coef /
(QEKF_INS.dt + QEKF_INS.acc_lpf_coef) +
az * QEKF_INS.dt / (QEKF_INS.dt + QEKF_INS.acc_lpf_coef);
/* 设置量测向量z单位化重力加速度向量 */
accel_inv_norm =
QEKF_InvSqrt(QEKF_INS.accel[0] * QEKF_INS.accel[0] +
QEKF_INS.accel[1] * QEKF_INS.accel[1] +
QEKF_INS.accel[2] * QEKF_INS.accel[2]);
for (uint8_t i = 0; i < 3; i++) {
QEKF_INS.qekf.measured_vector[i] = QEKF_INS.accel[i] * accel_inv_norm;
}
/* 获取机体状态 */
QEKF_INS.gyro_norm =
1.0f / QEKF_InvSqrt(QEKF_INS.gyro[0] * QEKF_INS.gyro[0] +
QEKF_INS.gyro[1] * QEKF_INS.gyro[1] +
QEKF_INS.gyro[2] * QEKF_INS.gyro[2]);
QEKF_INS.accl_norm = 1.0f / accel_inv_norm;
/* 如果角速度小于阈值且加速度处于设定范围内,认为运动稳定,加速度可以用于修正角速度 */
if (QEKF_INS.gyro_norm < 0.3f && QEKF_INS.accl_norm > 9.8f - 0.5f &&
QEKF_INS.accl_norm < 9.8f + 0.5f) {
QEKF_INS.stable_flag = 1;
} else {
QEKF_INS.stable_flag = 0;
}
/* 设置过程噪声Q和量测噪声R矩阵 */
QEKF_INS.qekf.Q_data[0] = QEKF_INS.Q1 * QEKF_INS.dt;
QEKF_INS.qekf.Q_data[7] = QEKF_INS.Q1 * QEKF_INS.dt;
QEKF_INS.qekf.Q_data[14] = QEKF_INS.Q1 * QEKF_INS.dt;
QEKF_INS.qekf.Q_data[21] = QEKF_INS.Q1 * QEKF_INS.dt;
QEKF_INS.qekf.Q_data[28] = QEKF_INS.Q2 * QEKF_INS.dt;
QEKF_INS.qekf.Q_data[35] = QEKF_INS.Q2 * QEKF_INS.dt;
QEKF_INS.qekf.R_data[0] = QEKF_INS.R;
QEKF_INS.qekf.R_data[4] = QEKF_INS.R;
QEKF_INS.qekf.R_data[8] = QEKF_INS.R;
/* 调用卡尔曼滤波更新注意几个User_Funcx_f的调用 */
KF_Update(&QEKF_INS.qekf);
/* 获取融合后的数据包括四元数和xy零偏值 */
QEKF_INS.q[0] = QEKF_INS.qekf.filtered_value[0];
QEKF_INS.q[1] = QEKF_INS.qekf.filtered_value[1];
QEKF_INS.q[2] = QEKF_INS.qekf.filtered_value[2];
QEKF_INS.q[3] = QEKF_INS.qekf.filtered_value[3];
QEKF_INS.gyro_bias[0] = QEKF_INS.qekf.filtered_value[4];
QEKF_INS.gyro_bias[1] = QEKF_INS.qekf.filtered_value[5];
QEKF_INS.gyro_bias[2] = 0; /* 大部分时候z轴通天无法观测yaw的漂移 */
/* 利用四元数反解欧拉角 */
QEKF_INS.yaw =
atan2f(2.0f * (QEKF_INS.q[0] * QEKF_INS.q[3] +
QEKF_INS.q[1] * QEKF_INS.q[2]),
2.0f * (QEKF_INS.q[0] * QEKF_INS.q[0] +
QEKF_INS.q[1] * QEKF_INS.q[1]) - 1.0f) * 57.295779513f;
QEKF_INS.pitch =
atan2f(2.0f * (QEKF_INS.q[0] * QEKF_INS.q[1] +
QEKF_INS.q[2] * QEKF_INS.q[3]),
2.0f * (QEKF_INS.q[0] * QEKF_INS.q[0] +
QEKF_INS.q[3] * QEKF_INS.q[3]) - 1.0f) * 57.295779513f;
QEKF_INS.roll =
asinf(-2.0f * (QEKF_INS.q[1] * QEKF_INS.q[3] -
QEKF_INS.q[0] * QEKF_INS.q[2])) * 57.295779513f;
/* 获取yaw总角度处理超过360度的情况如小陀螺 */
if (QEKF_INS.yaw - QEKF_INS.yaw_angle_last > 180.0f) {
QEKF_INS.yaw_round_count--;
} else if (QEKF_INS.yaw - QEKF_INS.yaw_angle_last < -180.0f) {
QEKF_INS.yaw_round_count++;
}
QEKF_INS.yaw_total_angle = 360.0f * QEKF_INS.yaw_round_count + QEKF_INS.yaw;
QEKF_INS.yaw_angle_last = QEKF_INS.yaw;
QEKF_INS.update_count++;
}
/**
* @brief 线F右上角的4x2分块矩阵P的更新
*
*
* @param kf
*/
static void QEKF_F_Linearization_P_Fading(KF_t *kf) {
float q0, q1, q2, q3;
float q_inv_norm;
q0 = kf->xhatminus_data[0];
q1 = kf->xhatminus_data[1];
q2 = kf->xhatminus_data[2];
q3 = kf->xhatminus_data[3];
/* 四元数归一化 */
q_inv_norm = QEKF_InvSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
for (uint8_t i = 0; i < 4; i++) {
kf->xhatminus_data[i] *= q_inv_norm;
}
/* F矩阵带*号的位置需要设置
0 1 2 3 4* 5*
6 7 8 9 10* 11*
12 13 14 15 16* 17*
18 19 20 21 22* 23*
24 25 26 27 28 29
30 31 32 33 34 35
*/
/* 设置F矩阵右上角4x2分块 */
kf->F_data[4] = q1 * QEKF_INS.dt / 2;
kf->F_data[5] = q2 * QEKF_INS.dt / 2;
kf->F_data[10] = -q0 * QEKF_INS.dt / 2;
kf->F_data[11] = q3 * QEKF_INS.dt / 2;
kf->F_data[16] = -q3 * QEKF_INS.dt / 2;
kf->F_data[17] = -q0 * QEKF_INS.dt / 2;
kf->F_data[22] = q2 * QEKF_INS.dt / 2;
kf->F_data[23] = -q1 * QEKF_INS.dt / 2;
/* 渐消滤波,防止零偏参数过度收敛 */
kf->P_data[28] /= QEKF_INS.lambda;
kf->P_data[35] /= QEKF_INS.lambda;
/* 限幅,防止发散 */
if (kf->P_data[28] > 10000) {
kf->P_data[28] = 10000;
}
if (kf->P_data[35] > 10000) {
kf->P_data[35] = 10000;
}
}
/**
* @brief h(x)Jacobi矩阵H
*
* @param kf
*/
static void QEKF_SetH(KF_t *kf) {
float doubleq0, doubleq1, doubleq2, doubleq3;
/* H矩阵布局
0 1 2 3 4 5
6 7 8 9 10 11
12 13 14 15 16 17
*/
doubleq0 = 2 * kf->xhatminus_data[0];
doubleq1 = 2 * kf->xhatminus_data[1];
doubleq2 = 2 * kf->xhatminus_data[2];
doubleq3 = 2 * kf->xhatminus_data[3];
memset(kf->H_data, 0, sizeof(float) * kf->z_size * kf->xhat_size);
kf->H_data[0] = -doubleq2;
kf->H_data[1] = doubleq3;
kf->H_data[2] = -doubleq0;
kf->H_data[3] = doubleq1;
kf->H_data[6] = doubleq1;
kf->H_data[7] = doubleq0;
kf->H_data[8] = doubleq3;
kf->H_data[9] = doubleq2;
kf->H_data[12] = doubleq0;
kf->H_data[13] = -doubleq1;
kf->H_data[14] = -doubleq2;
kf->H_data[15] = doubleq3;
}
/**
* @brief
*
*
*
* @param kf
*/
static void QEKF_xhatUpdate(KF_t *kf) {
float q0, q1, q2, q3;
kf->mat_status = KF_MatTrans(&kf->H, &kf->HT);
kf->temp_matrix.numRows = kf->H.numRows;
kf->temp_matrix.numCols = kf->Pminus.numCols;
/* temp_matrix = H·P'(k) */
kf->mat_status = KF_MatMult(&kf->H, &kf->Pminus, &kf->temp_matrix);
kf->temp_matrix1.numRows = kf->temp_matrix.numRows;
kf->temp_matrix1.numCols = kf->HT.numCols;
/* temp_matrix1 = H·P'(k)·HT */
kf->mat_status = KF_MatMult(&kf->temp_matrix, &kf->HT, &kf->temp_matrix1);
kf->S.numRows = kf->R.numRows;
kf->S.numCols = kf->R.numCols;
/* S = H·P'(k)·HT + R */
kf->mat_status = KF_MatAdd(&kf->temp_matrix1, &kf->R, &kf->S);
/* temp_matrix1 = inv(H·P'(k)·HT + R) */
kf->mat_status = KF_MatInv(&kf->S, &kf->temp_matrix1);
q0 = kf->xhatminus_data[0];
q1 = kf->xhatminus_data[1];
q2 = kf->xhatminus_data[2];
q3 = kf->xhatminus_data[3];
kf->temp_vector.numRows = kf->H.numRows;
kf->temp_vector.numCols = 1;
/* 计算预测得到的重力加速度方向(通过姿态获取) */
kf->temp_vector_data[0] = 2 * (q1 * q3 - q0 * q2);
kf->temp_vector_data[1] = 2 * (q0 * q1 + q2 * q3);
/* temp_vector = h(xhat'(k)) */
kf->temp_vector_data[2] = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;
/* 计算预测值和各个轴的方向余弦 */
for (uint8_t i = 0; i < 3; i++) {
QEKF_INS.orientation_cosine[i] = acosf(fabsf(kf->temp_vector_data[i]));
}
/* 利用加速度计数据修正 */
kf->temp_vector1.numRows = kf->z.numRows;
kf->temp_vector1.numCols = 1;
/* temp_vector1 = z(k) - h(xhat'(k)) */
kf->mat_status = KF_MatSub(&kf->z, &kf->temp_vector, &kf->temp_vector1);
/* 卡方检验 */
kf->temp_matrix.numRows = kf->temp_vector1.numRows;
kf->temp_matrix.numCols = 1;
/* temp_matrix = inv(H·P'(k)·HT + R)·(z(k) - h(xhat'(k))) */
kf->mat_status =
KF_MatMult(&kf->temp_matrix1, &kf->temp_vector1, &kf->temp_matrix);
kf->temp_vector.numRows = 1;
kf->temp_vector.numCols = kf->temp_vector1.numRows;
/* temp_vector = (z(k) - h(xhat'(k)))' */
kf->mat_status = KF_MatTrans(&kf->temp_vector1, &kf->temp_vector);
kf->mat_status =
KF_MatMult(&kf->temp_vector, &kf->temp_matrix, &QEKF_INS.chi_square);
/* rk较小滤波器已收敛/正在收敛 */
if (QEKF_INS.chi_square_data[0] < 0.5f * QEKF_INS.chi_square_threshold) {
QEKF_INS.converge_flag = 1;
}
/* rk大于阈值但曾经收敛过 */
if (QEKF_INS.chi_square_data[0] > QEKF_INS.chi_square_threshold &&
QEKF_INS.converge_flag) {
if (QEKF_INS.stable_flag) {
QEKF_INS.error_count++; /* 载体静止时仍无法通过卡方检验 */
} else {
QEKF_INS.error_count = 0;
}
if (QEKF_INS.error_count > 50) {
/* 滤波器发散 */
QEKF_INS.converge_flag = 0;
kf->skip_eq5 = FALSE; /* 步骤5是协方差矩阵P更新 */
} else {
/* 残差未通过卡方检验,仅预测 */
/* xhat(k) = xhat'(k) */
/* P(k) = P'(k) */
memcpy(kf->xhat_data, kf->xhatminus_data,
sizeof(float) * kf->xhat_size);
memcpy(kf->P_data, kf->Pminus_data,
sizeof(float) * kf->xhat_size * kf->xhat_size);
kf->skip_eq5 = TRUE; /* 跳过P更新 */
return;
}
} else {
/* 发散或rk不大/可接受,使用自适应增益 */
/* 自适应缩放rk越小则增益越大否则更相信预测值 */
if (QEKF_INS.chi_square_data[0] > 0.1f * QEKF_INS.chi_square_threshold &&
QEKF_INS.converge_flag) {
QEKF_INS.adaptive_gain_scale =
(QEKF_INS.chi_square_threshold - QEKF_INS.chi_square_data[0]) /
(0.9f * QEKF_INS.chi_square_threshold);
} else {
QEKF_INS.adaptive_gain_scale = 1;
}
QEKF_INS.error_count = 0;
kf->skip_eq5 = FALSE;
}
/* 计算卡尔曼增益K */
kf->temp_matrix.numRows = kf->Pminus.numRows;
kf->temp_matrix.numCols = kf->HT.numCols;
/* temp_matrix = P'(k)·HT */
kf->mat_status = KF_MatMult(&kf->Pminus, &kf->HT, &kf->temp_matrix);
kf->mat_status = KF_MatMult(&kf->temp_matrix, &kf->temp_matrix1, &kf->K);
/* 应用自适应增益 */
for (uint8_t i = 0; i < kf->K.numRows * kf->K.numCols; i++) {
kf->K_data[i] *= QEKF_INS.adaptive_gain_scale;
}
for (uint8_t i = 4; i < 6; i++) {
for (uint8_t j = 0; j < 3; j++) {
kf->K_data[i * 3 + j] *=
QEKF_INS.orientation_cosine[i - 4] / 1.5707963f; /* 1 rad */
}
}
kf->temp_vector.numRows = kf->K.numRows;
kf->temp_vector.numCols = 1;
/* temp_vector = K(k)·(z(k) - H·xhat'(k)) */
kf->mat_status = KF_MatMult(&kf->K, &kf->temp_vector1, &kf->temp_vector);
/* 零偏修正限幅,一般不会有过大的漂移 */
if (QEKF_INS.converge_flag) {
for (uint8_t i = 4; i < 6; i++) {
if (kf->temp_vector.pData[i] > 1e-2f * QEKF_INS.dt) {
kf->temp_vector.pData[i] = 1e-2f * QEKF_INS.dt;
}
if (kf->temp_vector.pData[i] < -1e-2f * QEKF_INS.dt) {
kf->temp_vector.pData[i] = -1e-2f * QEKF_INS.dt;
}
}
}
/* 不修正yaw轴数据 */
kf->temp_vector.pData[3] = 0;
kf->mat_status = KF_MatAdd(&kf->xhatminus, &kf->temp_vector, &kf->xhat);
}
/**
* @brief EKF观测环节
*
* @param kf
*/
static void QEKF_Observe(KF_t *kf) {
memcpy(QEKF_P_Init, kf->P_data, sizeof(QEKF_P_Init));
memcpy(QEKF_K, kf->K_data, sizeof(QEKF_K));
memcpy(QEKF_H, kf->H_data, sizeof(QEKF_H));
}
/**
* @brief 1/sqrt(x)
*
* @param x
* @return float 1/sqrt(x)
*/
static float QEKF_InvSqrt(float x) {
float halfx = 0.5f * x;
float y = x;
long i = *(long *)&y;
i = 0x5f375a86 - (i >> 1);
y = *(float *)&i;
y = y * (1.5f - (halfx * y * y));
return y;
}
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */

View File

@ -0,0 +1,111 @@
/*
QEKF
姿
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "kalman_filter.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* 布尔类型定义 */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* 四元数EKF惯性导航结构体 */
typedef struct {
uint8_t initialized; /* 初始化标志 */
KF_t qekf; /* 卡尔曼滤波器实例 */
uint8_t converge_flag; /* 收敛标志 */
uint8_t stable_flag; /* 稳定标志 */
uint64_t error_count; /* 错误计数 */
uint64_t update_count; /* 更新计数 */
float q[4]; /* 四元数估计值 */
float gyro_bias[3]; /* 陀螺仪零偏估计值 */
float gyro[3]; /* 陀螺仪数据 */
float accel[3]; /* 加速度计数据 */
float orientation_cosine[3]; /* 方向余弦 */
float acc_lpf_coef; /* 加速度低通滤波系数 */
float gyro_norm; /* 陀螺仪模值 */
float accl_norm; /* 加速度计模值 */
float adaptive_gain_scale; /* 自适应增益缩放 */
float roll; /* 横滚角 */
float pitch; /* 俯仰角 */
float yaw; /* 偏航角 */
float yaw_total_angle; /* 偏航总角度 */
float Q1; /* 四元数更新过程噪声 */
float Q2; /* 陀螺仪零偏过程噪声 */
float R; /* 加速度计量测噪声 */
float dt; /* 姿态更新周期 */
KF_Mat chi_square; /* 卡方检验矩阵 */
float chi_square_data[1]; /* 卡方检验数据 */
float chi_square_threshold; /* 卡方检验阈值 */
float lambda; /* 渐消因子 */
int16_t yaw_round_count; /* 偏航圈数计数 */
float yaw_angle_last; /* 上次偏航角 */
} QEKF_INS_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
extern QEKF_INS_t QEKF_INS;
/**
* @brief EKF初始化
*
* @param process_noise1 (: 10)
* @param process_noise2 (: 0.001)
* @param measure_noise (: 1000000)
* @param lambda (: 0.9996)
* @param lpf (: 0)
*/
void QEKF_Init(float process_noise1, float process_noise2, float measure_noise,
float lambda, float lpf);
/**
* @brief EKF更新
*
* @param gx X轴角速度 (rad/s)
* @param gy Y轴角速度 (rad/s)
* @param gz Z轴角速度 (rad/s)
* @param ax X轴加速度 (m/s²)
* @param ay Y轴加速度 (m/s²)
* @param az Z轴加速度 (m/s²)
* @param dt (s)
*/
void QEKF_Update(float gx, float gy, float gz, float ax, float ay, float az,
float dt);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@ -28,6 +28,9 @@
#ifndef COMMAND_INTERPRETER_H #ifndef COMMAND_INTERPRETER_H
#define COMMAND_INTERPRETER_H #define COMMAND_INTERPRETER_H
#include <stddef.h>
#include "FreeRTOS.h"
/* This config should be defined in FreeRTOSConfig.h. But due to the limition of CubeMX I put it here. */ /* This config should be defined in FreeRTOSConfig.h. But due to the limition of CubeMX I put it here. */
#define configCOMMAND_INT_MAX_OUTPUT_SIZE 512 #define configCOMMAND_INT_MAX_OUTPUT_SIZE 512

View File

@ -0,0 +1,591 @@
/*
modified from wang hongxi
使ARM CMSIS DSP优化矩阵运算
- HRK
-
- P
- ARM CMSIS DSP
- EKF/UKF/ESKF
使
1. PFQ 使
HR 使
2. (use_auto_adjustment = 1)
- measurement_map
- measurement_degreeH
- mat_r_diagonal_elements
3. (use_auto_adjustment = 0)
- P zHR
4.
- measured_vector
- 0
- KF 0
5.
- state_min_variance P
-
使
x =
| |
| |
| |
KF_t Height_KF;
void INS_Task_Init(void)
{
// 初始协方差矩阵 P
static float P_Init[9] =
{
10, 0, 0,
0, 30, 0,
0, 0, 10,
};
// 状态转移矩阵 F根据运动学模型
static float F_Init[9] =
{
1, dt, 0.5*dt*dt,
0, 1, dt,
0, 0, 1,
};
// 过程噪声协方差矩阵 Q
static float Q_Init[9] =
{
0.25*dt*dt*dt*dt, 0.5*dt*dt*dt, 0.5*dt*dt,
0.5*dt*dt*dt, dt*dt, dt,
0.5*dt*dt, dt, 1,
};
// 设置状态最小方差(防止过度收敛)
static float state_min_variance[3] = {0.03, 0.005, 0.1};
// 开启自动调整
Height_KF.use_auto_adjustment = 1;
// 量测映射:[气压高度对应状态1, GPS高度对应状态1, 加速度计对应状态3]
static uint8_t measurement_reference[3] = {1, 1, 3};
// 量测系数H矩阵元素值
static float measurement_degree[3] = {1, 1, 1};
// 根据 measurement_reference 与 measurement_degree 生成 H 矩阵如下
// (在当前周期全部量测数据有效的情况下)
// |1 0 0|
// |1 0 0|
// |0 0 1|
// 量测噪声方差R矩阵对角元素
static float mat_r_diagonal_elements[3] = {30, 25, 35};
// 根据 mat_r_diagonal_elements 生成 R 矩阵如下
// (在当前周期全部量测数据有效的情况下)
// |30 0 0|
// | 0 25 0|
// | 0 0 35|
// 初始化卡尔曼滤波器状态维数3控制维数0量测维数3
KF_Init(&Height_KF, 3, 0, 3);
// 设置矩阵初值
memcpy(Height_KF.P_data, P_Init, sizeof(P_Init));
memcpy(Height_KF.F_data, F_Init, sizeof(F_Init));
memcpy(Height_KF.Q_data, Q_Init, sizeof(Q_Init));
memcpy(Height_KF.measurement_map, measurement_reference,
sizeof(measurement_reference));
memcpy(Height_KF.measurement_degree, measurement_degree,
sizeof(measurement_degree));
memcpy(Height_KF.mat_r_diagonal_elements, mat_r_diagonal_elements,
sizeof(mat_r_diagonal_elements));
memcpy(Height_KF.state_min_variance, state_min_variance,
sizeof(state_min_variance));
}
void INS_Task(void const *pvParameters)
{
// 循环更新卡尔曼滤波器
KF_Update(&Height_KF);
vTaskDelay(ts);
}
// 传感器回调函数示例:在数据就绪时更新 measured_vector
void Barometer_Read_Over(void)
{
......
INS_KF.measured_vector[0] = baro_height; // 气压计高度
}
void GPS_Read_Over(void)
{
......
INS_KF.measured_vector[1] = GPS_height; // GPS高度
}
void Acc_Data_Process(void)
{
......
INS_KF.measured_vector[2] = acc.z; // Z轴加速度
}
*/
#include "kalman_filter.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* 私有函数声明 */
static void KF_AdjustHKR(KF_t *kf);
/**
* @brief
*
* @param kf
* @param xhat_size
* @param u_size 0
* @param z_size
* @return int8_t 0
*/
int8_t KF_Init(KF_t *kf, uint8_t xhat_size, uint8_t u_size, uint8_t z_size) {
if (kf == NULL) return -1;
kf->xhat_size = xhat_size;
kf->u_size = u_size;
kf->z_size = z_size;
kf->measurement_valid_num = 0;
/* 量测标志分配 */
kf->measurement_map = (uint8_t *)user_malloc(sizeof(uint8_t) * z_size);
memset(kf->measurement_map, 0, sizeof(uint8_t) * z_size);
kf->measurement_degree = (float *)user_malloc(sizeof(float) * z_size);
memset(kf->measurement_degree, 0, sizeof(float) * z_size);
kf->mat_r_diagonal_elements = (float *)user_malloc(sizeof(float) * z_size);
memset(kf->mat_r_diagonal_elements, 0, sizeof(float) * z_size);
kf->state_min_variance = (float *)user_malloc(sizeof(float) * xhat_size);
memset(kf->state_min_variance, 0, sizeof(float) * xhat_size);
kf->temp = (uint8_t *)user_malloc(sizeof(uint8_t) * z_size);
memset(kf->temp, 0, sizeof(uint8_t) * z_size);
/* 滤波数据分配 */
kf->filtered_value = (float *)user_malloc(sizeof(float) * xhat_size);
memset(kf->filtered_value, 0, sizeof(float) * xhat_size);
kf->measured_vector = (float *)user_malloc(sizeof(float) * z_size);
memset(kf->measured_vector, 0, sizeof(float) * z_size);
kf->control_vector = (float *)user_malloc(sizeof(float) * u_size);
memset(kf->control_vector, 0, sizeof(float) * u_size);
/* 状态估计 xhat x(k|k) */
kf->xhat_data = (float *)user_malloc(sizeof(float) * xhat_size);
memset(kf->xhat_data, 0, sizeof(float) * xhat_size);
KF_MatInit(&kf->xhat, kf->xhat_size, 1, kf->xhat_data);
/* 先验状态估计 xhatminus x(k|k-1) */
kf->xhatminus_data = (float *)user_malloc(sizeof(float) * xhat_size);
memset(kf->xhatminus_data, 0, sizeof(float) * xhat_size);
KF_MatInit(&kf->xhatminus, kf->xhat_size, 1, kf->xhatminus_data);
/* 控制向量 u */
if (u_size != 0) {
kf->u_data = (float *)user_malloc(sizeof(float) * u_size);
memset(kf->u_data, 0, sizeof(float) * u_size);
KF_MatInit(&kf->u, kf->u_size, 1, kf->u_data);
}
/* 量测向量 z */
kf->z_data = (float *)user_malloc(sizeof(float) * z_size);
memset(kf->z_data, 0, sizeof(float) * z_size);
KF_MatInit(&kf->z, kf->z_size, 1, kf->z_data);
/* 协方差矩阵 P(k|k) */
kf->P_data = (float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
memset(kf->P_data, 0, sizeof(float) * xhat_size * xhat_size);
KF_MatInit(&kf->P, kf->xhat_size, kf->xhat_size, kf->P_data);
/* 先验协方差矩阵 P(k|k-1) */
kf->Pminus_data = (float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
memset(kf->Pminus_data, 0, sizeof(float) * xhat_size * xhat_size);
KF_MatInit(&kf->Pminus, kf->xhat_size, kf->xhat_size, kf->Pminus_data);
/* 状态转移矩阵 F 及其转置 FT */
kf->F_data = (float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
kf->FT_data = (float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
memset(kf->F_data, 0, sizeof(float) * xhat_size * xhat_size);
memset(kf->FT_data, 0, sizeof(float) * xhat_size * xhat_size);
KF_MatInit(&kf->F, kf->xhat_size, kf->xhat_size, kf->F_data);
KF_MatInit(&kf->FT, kf->xhat_size, kf->xhat_size, kf->FT_data);
/* 控制矩阵 B */
if (u_size != 0) {
kf->B_data = (float *)user_malloc(sizeof(float) * xhat_size * u_size);
memset(kf->B_data, 0, sizeof(float) * xhat_size * u_size);
KF_MatInit(&kf->B, kf->xhat_size, kf->u_size, kf->B_data);
}
/* 量测矩阵 H 及其转置 HT */
kf->H_data = (float *)user_malloc(sizeof(float) * z_size * xhat_size);
kf->HT_data = (float *)user_malloc(sizeof(float) * xhat_size * z_size);
memset(kf->H_data, 0, sizeof(float) * z_size * xhat_size);
memset(kf->HT_data, 0, sizeof(float) * xhat_size * z_size);
KF_MatInit(&kf->H, kf->z_size, kf->xhat_size, kf->H_data);
KF_MatInit(&kf->HT, kf->xhat_size, kf->z_size, kf->HT_data);
/* 过程噪声协方差矩阵 Q */
kf->Q_data = (float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
memset(kf->Q_data, 0, sizeof(float) * xhat_size * xhat_size);
KF_MatInit(&kf->Q, kf->xhat_size, kf->xhat_size, kf->Q_data);
/* 量测噪声协方差矩阵 R */
kf->R_data = (float *)user_malloc(sizeof(float) * z_size * z_size);
memset(kf->R_data, 0, sizeof(float) * z_size * z_size);
KF_MatInit(&kf->R, kf->z_size, kf->z_size, kf->R_data);
/* 卡尔曼增益 K */
kf->K_data = (float *)user_malloc(sizeof(float) * xhat_size * z_size);
memset(kf->K_data, 0, sizeof(float) * xhat_size * z_size);
KF_MatInit(&kf->K, kf->xhat_size, kf->z_size, kf->K_data);
/* 临时矩阵分配 */
kf->S_data = (float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
kf->temp_matrix_data =
(float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
kf->temp_matrix_data1 =
(float *)user_malloc(sizeof(float) * xhat_size * xhat_size);
kf->temp_vector_data = (float *)user_malloc(sizeof(float) * xhat_size);
kf->temp_vector_data1 = (float *)user_malloc(sizeof(float) * xhat_size);
KF_MatInit(&kf->S, kf->xhat_size, kf->xhat_size, kf->S_data);
KF_MatInit(&kf->temp_matrix, kf->xhat_size, kf->xhat_size,
kf->temp_matrix_data);
KF_MatInit(&kf->temp_matrix1, kf->xhat_size, kf->xhat_size,
kf->temp_matrix_data1);
KF_MatInit(&kf->temp_vector, kf->xhat_size, 1, kf->temp_vector_data);
KF_MatInit(&kf->temp_vector1, kf->xhat_size, 1, kf->temp_vector_data1);
/* 初始化跳过标志 */
kf->skip_eq1 = 0;
kf->skip_eq2 = 0;
kf->skip_eq3 = 0;
kf->skip_eq4 = 0;
kf->skip_eq5 = 0;
/* 初始化用户函数指针 */
kf->User_Func0_f = NULL;
kf->User_Func1_f = NULL;
kf->User_Func2_f = NULL;
kf->User_Func3_f = NULL;
kf->User_Func4_f = NULL;
kf->User_Func5_f = NULL;
kf->User_Func6_f = NULL;
return 0;
}
/**
* @brief
*
* @param kf
* @return int8_t 0
*/
int8_t KF_Measure(KF_t *kf) {
if (kf == NULL) return -1;
/* 根据量测有效性自动调整 H, K, R 矩阵 */
if (kf->use_auto_adjustment != 0) {
KF_AdjustHKR(kf);
} else {
memcpy(kf->z_data, kf->measured_vector, sizeof(float) * kf->z_size);
memset(kf->measured_vector, 0, sizeof(float) * kf->z_size);
}
memcpy(kf->u_data, kf->control_vector, sizeof(float) * kf->u_size);
return 0;
}
/**
* @brief 1 - xhat'(k) = F·xhat(k-1) + B·u
*
* @param kf
* @return int8_t 0
*/
int8_t KF_PredictState(KF_t *kf) {
if (kf == NULL) return -1;
if (!kf->skip_eq1) {
if (kf->u_size > 0) {
/* 有控制输入: xhat'(k) = F·xhat(k-1) + B·u */
kf->temp_vector.numRows = kf->xhat_size;
kf->temp_vector.numCols = 1;
kf->mat_status = KF_MatMult(&kf->F, &kf->xhat, &kf->temp_vector);
kf->temp_vector1.numRows = kf->xhat_size;
kf->temp_vector1.numCols = 1;
kf->mat_status = KF_MatMult(&kf->B, &kf->u, &kf->temp_vector1);
kf->mat_status =
KF_MatAdd(&kf->temp_vector, &kf->temp_vector1, &kf->xhatminus);
} else {
/* 无控制输入: xhat'(k) = F·xhat(k-1) */
kf->mat_status = KF_MatMult(&kf->F, &kf->xhat, &kf->xhatminus);
}
}
return 0;
}
/**
* @brief 2 - P'(k) = F·P(k-1)·F^T + Q
*
* @param kf
* @return int8_t 0
*/
int8_t KF_PredictCovariance(KF_t *kf) {
if (kf == NULL) return -1;
if (!kf->skip_eq2) {
kf->mat_status = KF_MatTrans(&kf->F, &kf->FT);
kf->mat_status = KF_MatMult(&kf->F, &kf->P, &kf->Pminus);
kf->temp_matrix.numRows = kf->Pminus.numRows;
kf->temp_matrix.numCols = kf->FT.numCols;
/* F·P(k-1)·F^T */
kf->mat_status = KF_MatMult(&kf->Pminus, &kf->FT, &kf->temp_matrix);
kf->mat_status = KF_MatAdd(&kf->temp_matrix, &kf->Q, &kf->Pminus);
}
return 0;
}
/**
* @brief 3 - K = P'(k)·H^T / (H·P'(k)·H^T + R)
*
* @param kf
* @return int8_t 0
*/
int8_t KF_CalcGain(KF_t *kf) {
if (kf == NULL) return -1;
if (!kf->skip_eq3) {
kf->mat_status = KF_MatTrans(&kf->H, &kf->HT);
kf->temp_matrix.numRows = kf->H.numRows;
kf->temp_matrix.numCols = kf->Pminus.numCols;
/* H·P'(k) */
kf->mat_status = KF_MatMult(&kf->H, &kf->Pminus, &kf->temp_matrix);
kf->temp_matrix1.numRows = kf->temp_matrix.numRows;
kf->temp_matrix1.numCols = kf->HT.numCols;
/* H·P'(k)·H^T */
kf->mat_status = KF_MatMult(&kf->temp_matrix, &kf->HT, &kf->temp_matrix1);
kf->S.numRows = kf->R.numRows;
kf->S.numCols = kf->R.numCols;
/* S = H·P'(k)·H^T + R */
kf->mat_status = KF_MatAdd(&kf->temp_matrix1, &kf->R, &kf->S);
/* S^-1 */
kf->mat_status = KF_MatInv(&kf->S, &kf->temp_matrix1);
kf->temp_matrix.numRows = kf->Pminus.numRows;
kf->temp_matrix.numCols = kf->HT.numCols;
/* P'(k)·H^T */
kf->mat_status = KF_MatMult(&kf->Pminus, &kf->HT, &kf->temp_matrix);
/* K = P'(k)·H^T·S^-1 */
kf->mat_status = KF_MatMult(&kf->temp_matrix, &kf->temp_matrix1, &kf->K);
}
return 0;
}
/**
* @brief 4 - xhat(k) = xhat'(k) + K·(z - H·xhat'(k))
*
* @param kf
* @return int8_t 0
*/
int8_t KF_UpdateState(KF_t *kf) {
if (kf == NULL) return -1;
if (!kf->skip_eq4) {
kf->temp_vector.numRows = kf->H.numRows;
kf->temp_vector.numCols = 1;
/* H·xhat'(k) */
kf->mat_status = KF_MatMult(&kf->H, &kf->xhatminus, &kf->temp_vector);
kf->temp_vector1.numRows = kf->z.numRows;
kf->temp_vector1.numCols = 1;
/* 新息: z - H·xhat'(k) */
kf->mat_status = KF_MatSub(&kf->z, &kf->temp_vector, &kf->temp_vector1);
kf->temp_vector.numRows = kf->K.numRows;
kf->temp_vector.numCols = 1;
/* K·新息 */
kf->mat_status = KF_MatMult(&kf->K, &kf->temp_vector1, &kf->temp_vector);
/* xhat = xhat' + K·新息 */
kf->mat_status = KF_MatAdd(&kf->xhatminus, &kf->temp_vector, &kf->xhat);
}
return 0;
}
/**
* @brief 5 - P(k) = P'(k) - K·H·P'(k)
*
* @param kf
* @return int8_t 0
*/
int8_t KF_UpdateCovariance(KF_t *kf) {
if (kf == NULL) return -1;
if (!kf->skip_eq5) {
kf->temp_matrix.numRows = kf->K.numRows;
kf->temp_matrix.numCols = kf->H.numCols;
kf->temp_matrix1.numRows = kf->temp_matrix.numRows;
kf->temp_matrix1.numCols = kf->Pminus.numCols;
/* K·H */
kf->mat_status = KF_MatMult(&kf->K, &kf->H, &kf->temp_matrix);
/* K·H·P'(k) */
kf->mat_status = KF_MatMult(&kf->temp_matrix, &kf->Pminus, &kf->temp_matrix1);
/* P = P' - K·H·P' */
kf->mat_status = KF_MatSub(&kf->Pminus, &kf->temp_matrix1, &kf->P);
}
return 0;
}
/**
* @brief
*
* KFEKF/UKF/ESKF/AUKF
* User_Func
*
* @param kf
* @return float*
*/
float *KF_Update(KF_t *kf) {
if (kf == NULL) return NULL;
/* 步骤0: 量测获取和矩阵调整 */
KF_Measure(kf);
if (kf->User_Func0_f != NULL) kf->User_Func0_f(kf);
/* 步骤1: 先验状态估计 - xhat'(k) = F·xhat(k-1) + B·u */
KF_PredictState(kf);
if (kf->User_Func1_f != NULL) kf->User_Func1_f(kf);
/* 步骤2: 先验协方差 - P'(k) = F·P(k-1)·F^T + Q */
KF_PredictCovariance(kf);
if (kf->User_Func2_f != NULL) kf->User_Func2_f(kf);
/* 量测更新(仅在存在有效量测时执行) */
if (kf->measurement_valid_num != 0 || kf->use_auto_adjustment == 0) {
/* 步骤3: 卡尔曼增益 - K = P'(k)·H^T / (H·P'(k)·H^T + R) */
KF_CalcGain(kf);
if (kf->User_Func3_f != NULL) kf->User_Func3_f(kf);
/* 步骤4: 状态更新 - xhat(k) = xhat'(k) + K·(z - H·xhat'(k)) */
KF_UpdateState(kf);
if (kf->User_Func4_f != NULL) kf->User_Func4_f(kf);
/* 步骤5: 协方差更新 - P(k) = P'(k) - K·H·P'(k) */
KF_UpdateCovariance(kf);
} else {
/* 无有效量测 - 仅预测 */
memcpy(kf->xhat_data, kf->xhatminus_data, sizeof(float) * kf->xhat_size);
memcpy(kf->P_data, kf->Pminus_data,
sizeof(float) * kf->xhat_size * kf->xhat_size);
}
/* 后处理钩子 */
if (kf->User_Func5_f != NULL) kf->User_Func5_f(kf);
/* 防过度收敛:强制最小方差 */
for (uint8_t i = 0; i < kf->xhat_size; i++) {
if (kf->P_data[i * kf->xhat_size + i] < kf->state_min_variance[i])
kf->P_data[i * kf->xhat_size + i] = kf->state_min_variance[i];
}
/* 复制结果到输出 */
memcpy(kf->filtered_value, kf->xhat_data, sizeof(float) * kf->xhat_size);
/* 附加后处理钩子 */
if (kf->User_Func6_f != NULL) kf->User_Func6_f(kf);
return kf->filtered_value;
}
/**
* @brief
*
* @param kf
*/
void KF_Reset(KF_t *kf) {
if (kf == NULL) return;
memset(kf->xhat_data, 0, sizeof(float) * kf->xhat_size);
memset(kf->xhatminus_data, 0, sizeof(float) * kf->xhat_size);
memset(kf->filtered_value, 0, sizeof(float) * kf->xhat_size);
kf->measurement_valid_num = 0;
}
/**
* @brief H, R, K
*
*
*
*
* @param kf
*/
static void KF_AdjustHKR(KF_t *kf) {
kf->measurement_valid_num = 0;
/* 复制并重置量测向量 */
memcpy(kf->z_data, kf->measured_vector, sizeof(float) * kf->z_size);
memset(kf->measured_vector, 0, sizeof(float) * kf->z_size);
/* 清空 H 和 R 矩阵 */
memset(kf->R_data, 0, sizeof(float) * kf->z_size * kf->z_size);
memset(kf->H_data, 0, sizeof(float) * kf->xhat_size * kf->z_size);
/* 识别有效量测并重建 z, H */
for (uint8_t i = 0; i < kf->z_size; i++) {
if (kf->z_data[i] != 0) { /* 非零表示有效量测 */
/* 将有效量测压缩到 z */
kf->z_data[kf->measurement_valid_num] = kf->z_data[i];
kf->temp[kf->measurement_valid_num] = i;
/* 重建 H 矩阵行 */
kf->H_data[kf->xhat_size * kf->measurement_valid_num +
kf->measurement_map[i] - 1] = kf->measurement_degree[i];
kf->measurement_valid_num++;
}
}
/* 用有效量测方差重建 R 矩阵 */
for (uint8_t i = 0; i < kf->measurement_valid_num; i++) {
kf->R_data[i * kf->measurement_valid_num + i] =
kf->mat_r_diagonal_elements[kf->temp[i]];
}
/* 调整矩阵维度以匹配有效量测数量 */
kf->H.numRows = kf->measurement_valid_num;
kf->H.numCols = kf->xhat_size;
kf->HT.numRows = kf->xhat_size;
kf->HT.numCols = kf->measurement_valid_num;
kf->R.numRows = kf->measurement_valid_num;
kf->R.numCols = kf->measurement_valid_num;
kf->K.numRows = kf->xhat_size;
kf->K.numCols = kf->measurement_valid_num;
kf->z.numRows = kf->measurement_valid_num;
}
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */

View File

@ -0,0 +1,199 @@
/*
使ARM CMSIS DSP优化矩阵运算
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "arm_math.h"
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* 内存分配配置 */
#ifndef user_malloc
#ifdef _CMSIS_OS_H
#define user_malloc pvPortMalloc /* FreeRTOS堆分配 */
#else
#define user_malloc malloc /* 标准C库分配 */
#endif
#endif
/* ARM CMSIS DSP 矩阵运算别名 */
#define KF_Mat arm_matrix_instance_f32
#define KF_MatInit arm_mat_init_f32
#define KF_MatAdd arm_mat_add_f32
#define KF_MatSub arm_mat_sub_f32
#define KF_MatMult arm_mat_mult_f32
#define KF_MatTrans arm_mat_trans_f32
#define KF_MatInv arm_mat_inverse_f32
/* 卡尔曼滤波器主结构体 */
typedef struct KF_s {
/* 输出和输入向量 */
float *filtered_value; /* 滤波后的状态估计输出 */
float *measured_vector; /* 量测输入向量 */
float *control_vector; /* 控制输入向量 */
/* 维度 */
uint8_t xhat_size; /* 状态向量维度 */
uint8_t u_size; /* 控制向量维度 */
uint8_t z_size; /* 量测向量维度 */
/* 自动调整参数 */
uint8_t use_auto_adjustment; /* 启用动态 H/R/K 调整 */
uint8_t measurement_valid_num; /* 有效量测数量 */
/* 量测配置 */
uint8_t *measurement_map; /* 量测到状态的映射 */
float *measurement_degree; /* 每个量测的H矩阵元素值 */
float *mat_r_diagonal_elements; /* 量测噪声方差R对角线 */
float *state_min_variance; /* 最小状态方差(防过度收敛) */
uint8_t *temp; /* 临时缓冲区 */
/* 方程跳过标志(用于自定义用户函数) */
uint8_t skip_eq1, skip_eq2, skip_eq3, skip_eq4, skip_eq5;
/* 卡尔曼滤波器矩阵 */
KF_Mat xhat; /* 状态估计 x(k|k) */
KF_Mat xhatminus; /* 先验状态估计 x(k|k-1) */
KF_Mat u; /* 控制向量 */
KF_Mat z; /* 量测向量 */
KF_Mat P; /* 后验误差协方差 P(k|k) */
KF_Mat Pminus; /* 先验误差协方差 P(k|k-1) */
KF_Mat F, FT; /* 状态转移矩阵及其转置 */
KF_Mat B; /* 控制矩阵 */
KF_Mat H, HT; /* 量测矩阵及其转置 */
KF_Mat Q; /* 过程噪声协方差 */
KF_Mat R; /* 量测噪声协方差 */
KF_Mat K; /* 卡尔曼增益 */
KF_Mat S; /* 临时矩阵 S */
KF_Mat temp_matrix, temp_matrix1; /* 临时矩阵 */
KF_Mat temp_vector, temp_vector1; /* 临时向量 */
int8_t mat_status; /* 矩阵运算状态 */
/* 用户自定义函数指针用于EKF/UKF/ESKF扩展 */
void (*User_Func0_f)(struct KF_s *kf); /* 自定义量测处理 */
void (*User_Func1_f)(struct KF_s *kf); /* 自定义状态预测 */
void (*User_Func2_f)(struct KF_s *kf); /* 自定义协方差预测 */
void (*User_Func3_f)(struct KF_s *kf); /* 自定义卡尔曼增益计算 */
void (*User_Func4_f)(struct KF_s *kf); /* 自定义状态更新 */
void (*User_Func5_f)(struct KF_s *kf); /* 自定义后处理 */
void (*User_Func6_f)(struct KF_s *kf); /* 附加自定义函数 */
/* 矩阵数据存储指针 */
float *xhat_data, *xhatminus_data;
float *u_data;
float *z_data;
float *P_data, *Pminus_data;
float *F_data, *FT_data;
float *B_data;
float *H_data, *HT_data;
float *Q_data;
float *R_data;
float *K_data;
float *S_data;
float *temp_matrix_data, *temp_matrix_data1;
float *temp_vector_data, *temp_vector_data1;
} KF_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/**
* @brief
*
* @param kf
* @param xhat_size
* @param u_size 0
* @param z_size
* @return int8_t 0
*/
int8_t KF_Init(KF_t *kf, uint8_t xhat_size, uint8_t u_size, uint8_t z_size);
/**
* @brief
*
* @param kf
* @return int8_t 0
*/
int8_t KF_Measure(KF_t *kf);
/**
* @brief 1 - xhat'(k) = F·xhat(k-1) + B·u
*
* @param kf
* @return int8_t 0
*/
int8_t KF_PredictState(KF_t *kf);
/**
* @brief 2 - P'(k) = F·P(k-1)·F^T + Q
*
* @param kf
* @return int8_t 0
*/
int8_t KF_PredictCovariance(KF_t *kf);
/**
* @brief 3 - K = P'(k)·H^T / (H·P'(k)·H^T + R)
*
* @param kf
* @return int8_t 0
*/
int8_t KF_CalcGain(KF_t *kf);
/**
* @brief 4 - xhat(k) = xhat'(k) + K·(z - H·xhat'(k))
*
* @param kf
* @return int8_t 0
*/
int8_t KF_UpdateState(KF_t *kf);
/**
* @brief 5 - P(k) = P'(k) - K·H·P'(k)
*
* @param kf
* @return int8_t 0
*/
int8_t KF_UpdateCovariance(KF_t *kf);
/**
* @brief
*
* @param kf
* @return float*
*/
float *KF_Update(KF_t *kf);
/**
* @brief
*
* @param kf
*/
void KF_Reset(KF_t *kf);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@ -61,6 +61,9 @@ int8_t VMC_Init(VMC_t *vmc, const VMC_Param_t *param, float sample_freq) {
// 重置状态 // 重置状态
VMC_Reset(vmc); VMC_Reset(vmc);
// 初始化dd_theta低通滤波器 (截止频率50Hz)
LowPassFilter2p_Init(&vmc->dd_theta_filter, sample_freq, 50.0f);
vmc->initialized = true; vmc->initialized = true;
return 0; return 0;
@ -124,8 +127,9 @@ int8_t VMC_ForwardSolve(VMC_t *vmc, float phi1, float phi4, float body_pitch, fl
leg->d_theta = body_pitch_rate + leg->d_phi0; leg->d_theta = body_pitch_rate + leg->d_phi0;
leg->d_L0 = VMC_ComputeNumericDerivative(leg->L0, leg->last_L0, vmc->dt); leg->d_L0 = VMC_ComputeNumericDerivative(leg->L0, leg->last_L0, vmc->dt);
// 计算角加速度 // 计算角加速度并滤波
leg->dd_theta = VMC_ComputeNumericDerivative(leg->d_theta, leg->last_d_theta, vmc->dt); float dd_theta_raw = VMC_ComputeNumericDerivative(leg->d_theta, leg->last_d_theta, vmc->dt);
leg->dd_theta = LowPassFilter2p_Apply(&vmc->dd_theta_filter, dd_theta_raw);
VMC_GroundContactDetection(vmc); // 更新地面接触状态 VMC_GroundContactDetection(vmc); // 更新地面接触状态
return 0; return 0;

View File

@ -8,6 +8,7 @@ extern "C" {
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <math.h> #include <math.h>
#include "filter.h"
/* Exported types ----------------------------------------------------------- */ /* Exported types ----------------------------------------------------------- */
/** /**
@ -83,6 +84,7 @@ typedef struct {
VMC_Leg_t leg; // 腿部状态 VMC_Leg_t leg; // 腿部状态
float dt; // 控制周期 float dt; // 控制周期
bool initialized; // 初始化标志 bool initialized; // 初始化标志
LowPassFilter2p_t dd_theta_filter; // dd_theta低通滤波器
} VMC_t; } VMC_t;
/* Exported constants ------------------------------------------------------- */ /* Exported constants ------------------------------------------------------- */

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <sys/_intsup.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -30,6 +31,7 @@ typedef struct {
} gyro_offset; /* 陀螺仪偏置 */ } gyro_offset; /* 陀螺仪偏置 */
} BMI088_Cali_t; /* BMI088校准数据 */ } BMI088_Cali_t; /* BMI088校准数据 */
typedef struct { typedef struct {
DEVICE_Header_t header; DEVICE_Header_t header;
AHRS_Accl_t accl; AHRS_Accl_t accl;

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <sys/_intsup.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@ -29,3 +29,7 @@ motor_lz:
motor_rm: motor_rm:
bsp_config: {} bsp_config: {}
enabled: true enabled: true
vofa:
bsp_config:
BSP_UART_VOFA: BSP_UART_VOFA
enabled: true

873
User/device/mrobot.c Normal file
View File

@ -0,0 +1,873 @@
/**
* @file mrobot.c
* @brief MRobot CLI
*/
/* Includes ----------------------------------------------------------------- */
#include "device/mrobot.h"
#include "component/freertos_cli.h"
#include "bsp/uart.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <FreeRTOS.h>
#include <task.h>
#include <semphr.h>
#include <cmsis_os2.h>
/* Private constants -------------------------------------------------------- */
static const char *const CLI_WELCOME_MESSAGE =
"\r\n"
" __ __ _____ _ _ \r\n"
" | \\/ | __ \\ | | | | \r\n"
" | \\ / | |__) |___ | |__ ___ | |_ \r\n"
" | |\\/| | _ // _ \\| '_ \\ / _ \\| __|\r\n"
" | | | | | \\ \\ (_) | |_) | (_) | |_ \r\n"
" |_| |_|_| \\_\\___/|_.__/ \\___/ \\__|\r\n"
" ------------------------------------\r\n"
" Welcome to use MRobot CLI. Type 'help' to view a list of registered commands.\r\n"
"\r\n";
/* ANSI 转义序列 */
#define ANSI_CLEAR_SCREEN "\033[2J\033[H"
#define ANSI_CURSOR_HOME "\033[H"
#define ANSI_BACKSPACE "\b \b"
/* Private types ------------------------------------------------------------ */
/* CLI 上下文结构体 - 封装所有状态 */
typedef struct {
/* 设备管理 */
MRobot_Device_t devices[MROBOT_MAX_DEVICES];
uint8_t device_count;
/* 自定义命令 */
CLI_Command_Definition_t *custom_cmds[MROBOT_MAX_CUSTOM_COMMANDS];
uint8_t custom_cmd_count;
/* CLI 状态 */
MRobot_State_t state;
char current_path[MROBOT_PATH_MAX_LEN];
/* 命令缓冲区 */
uint8_t cmd_buffer[MROBOT_CMD_BUFFER_SIZE];
volatile uint8_t cmd_index;
volatile bool cmd_ready;
/* UART 相关 */
uint8_t uart_rx_char;
volatile bool tx_complete;
volatile bool htop_exit;
/* 输出缓冲区 */
char output_buffer[MROBOT_OUTPUT_BUFFER_SIZE];
/* 初始化标志 */
bool initialized;
/* 互斥锁 */
SemaphoreHandle_t mutex;
} MRobot_Context_t;
/* Private variables -------------------------------------------------------- */
static MRobot_Context_t ctx = {
.device_count = 0,
.custom_cmd_count = 0,
.state = MROBOT_STATE_IDLE,
.current_path = "/",
.cmd_index = 0,
.cmd_ready = false,
.tx_complete = true,
.htop_exit = false,
.initialized = false,
.mutex = NULL
};
/* Private function prototypes ---------------------------------------------- */
/* 命令处理函数 */
static BaseType_t cmd_help(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static BaseType_t cmd_htop(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static BaseType_t cmd_cd(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static BaseType_t cmd_ls(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static BaseType_t cmd_show(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
/* 内部辅助函数 */
static void uart_tx_callback(void);
static void uart_rx_callback(void);
static void send_string(const char *str);
static void send_prompt(void);
static int format_float_va(char *buf, size_t size, const char *fmt, va_list args);
/* CLI 命令定义表 */
static const CLI_Command_Definition_t builtin_commands[] = {
{ "help", " --help: 显示所有可用命令\r\n", cmd_help, 0 },
{ "htop", " --htop: 动态显示 FreeRTOS 任务状态 (按 'q' 退出)\r\n", cmd_htop, 0 },
{ "cd", " --cd <path>: 切换目录\r\n", cmd_cd, 1 },
{ "ls", " --ls: 列出当前目录内容\r\n", cmd_ls, 0 },
{ "show", " --show [device] [count]: 显示设备信息\r\n", cmd_show, -1 },
};
#define BUILTIN_CMD_COUNT (sizeof(builtin_commands) / sizeof(builtin_commands[0]))
/* ========================================================================== */
/* 辅助函数实现 */
/* ========================================================================== */
/**
* @brief UART
*/
static void send_string(const char *str) {
if (str == NULL || *str == '\0') return;
ctx.tx_complete = false;
BSP_UART_Transmit(MROBOT_UART_PORT, (uint8_t *)str, strlen(str), true);
while (!ctx.tx_complete) { osDelay(1); }
}
/**
* @brief
*/
static void send_prompt(void) {
char prompt[MROBOT_PATH_MAX_LEN + 32];
snprintf(prompt, sizeof(prompt), MROBOT_USER_NAME "@" MROBOT_HOST_NAME ":%s$ ", ctx.current_path);
send_string(prompt);
}
/**
* @brief UART
*/
static void uart_tx_callback(void) {
ctx.tx_complete = true;
}
/**
* @brief UART
*/
static void uart_rx_callback(void) {
uint8_t ch = ctx.uart_rx_char;
/* htop 模式下检查退出键 */
if (ctx.state == MROBOT_STATE_HTOP) {
if (ch == 'q' || ch == 'Q' || ch == 27) {
ctx.htop_exit = true;
}
BSP_UART_Receive(MROBOT_UART_PORT, &ctx.uart_rx_char, 1, false);
return;
}
/* 正常命令输入处理 */
if (ch == '\r' || ch == '\n') {
if (ctx.cmd_index > 0) {
ctx.cmd_buffer[ctx.cmd_index] = '\0';
ctx.cmd_ready = true;
BSP_UART_Transmit(MROBOT_UART_PORT, (uint8_t *)"\r\n", 2, false);
}
} else if (ch == 127 || ch == 8) { /* 退格键 */
if (ctx.cmd_index > 0) {
ctx.cmd_index--;
BSP_UART_Transmit(MROBOT_UART_PORT, (uint8_t *)ANSI_BACKSPACE, 3, false);
}
} else if (ch >= 32 && ch < 127 && ctx.cmd_index < sizeof(ctx.cmd_buffer) - 1) {
ctx.cmd_buffer[ctx.cmd_index++] = ch;
BSP_UART_Transmit(MROBOT_UART_PORT, &ch, 1, false);
}
BSP_UART_Receive(MROBOT_UART_PORT, &ctx.uart_rx_char, 1, false);
}
/* ========================================================================== */
/* CLI 命令实现 */
/* ========================================================================== */
/**
* @brief help -
*/
static BaseType_t cmd_help(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) {
(void)pcCommandString;
int offset = snprintf(pcWriteBuffer, xWriteBufferLen,
"MRobot CLI v2.0\r\n"
"================\r\n"
"Built-in Commands:\r\n");
for (size_t i = 0; i < BUILTIN_CMD_COUNT && offset < (int)xWriteBufferLen - 50; i++) {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
" %s", builtin_commands[i].pcHelpString);
}
if (ctx.custom_cmd_count > 0) {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
"\r\nCustom Commands:\r\n");
for (uint8_t i = 0; i < ctx.custom_cmd_count && offset < (int)xWriteBufferLen - 50; i++) {
if (ctx.custom_cmds[i] != NULL) {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
" %s", ctx.custom_cmds[i]->pcHelpString);
}
}
}
return pdFALSE;
}
/**
* @brief htop - htop
*/
static BaseType_t cmd_htop(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) {
(void)pcCommandString;
(void)pcWriteBuffer;
(void)xWriteBufferLen;
/* htop 模式在 MRobot_Run 中处理 */
return pdFALSE;
}
/**
* @brief cd -
*/
static BaseType_t cmd_cd(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) {
const char *param;
BaseType_t param_len;
param = FreeRTOS_CLIGetParameter(pcCommandString, 1, &param_len);
if (param == NULL) {
/* 无参数时切换到根目录 */
strcpy(ctx.current_path, "/");
snprintf(pcWriteBuffer, xWriteBufferLen, "Changed to: %s\r\n", ctx.current_path);
return pdFALSE;
}
/* 安全复制路径参数 */
char path[MROBOT_PATH_MAX_LEN];
size_t copy_len = (size_t)param_len < sizeof(path) - 1 ? (size_t)param_len : sizeof(path) - 1;
strncpy(path, param, copy_len);
path[copy_len] = '\0';
/* 路径解析 */
if (strcmp(path, "/") == 0 || strcmp(path, "..") == 0 || strcmp(path, "~") == 0) {
strcpy(ctx.current_path, "/");
} else if (strcmp(path, "dev") == 0 || strcmp(path, "/dev") == 0) {
strcpy(ctx.current_path, "/dev");
} else if (strcmp(path, "modules") == 0 || strcmp(path, "/modules") == 0) {
strcpy(ctx.current_path, "/modules");
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Error: Directory '%s' not found\r\n", path);
return pdFALSE;
}
snprintf(pcWriteBuffer, xWriteBufferLen, "Changed to: %s\r\n", ctx.current_path);
return pdFALSE;
}
/**
* @brief ls -
*/
static BaseType_t cmd_ls(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) {
(void)pcCommandString;
int offset = 0;
if (strcmp(ctx.current_path, "/") == 0) {
snprintf(pcWriteBuffer, xWriteBufferLen,
"dev/\r\n"
"modules/\r\n");
} else if (strcmp(ctx.current_path, "/dev") == 0) {
offset = snprintf(pcWriteBuffer, xWriteBufferLen,
"Device List (%d devices)\r\n\r\n",
ctx.device_count);
if (ctx.device_count == 0) {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
" (No devices)\r\n");
} else {
/* 直接列出所有设备 */
for (uint8_t i = 0; i < ctx.device_count && offset < (int)xWriteBufferLen - 50; i++) {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
" - %s\r\n", ctx.devices[i].name);
}
}
} else if (strcmp(ctx.current_path, "/modules") == 0) {
snprintf(pcWriteBuffer, xWriteBufferLen,
"Module functions not yet implemented\r\n");
}
return pdFALSE;
}
/**
* @brief show -
*/
static BaseType_t cmd_show(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) {
const char *param;
const char *count_param;
BaseType_t param_len, count_param_len;
/* 使用局部静态变量跟踪多次打印状态 */
static uint32_t print_count = 0;
static uint32_t current_iter = 0;
static char target_device[MROBOT_DEVICE_NAME_LEN] = {0};
/* 首次调用时解析参数 */
if (current_iter == 0) {
/* 检查是否在 /dev 目录 */
if (strcmp(ctx.current_path, "/dev") != 0) {
snprintf(pcWriteBuffer, xWriteBufferLen,
"Error: 'show' command only works in /dev directory\r\n"
"Hint: Use 'cd /dev' to switch to device directory\r\n");
return pdFALSE;
}
param = FreeRTOS_CLIGetParameter(pcCommandString, 1, &param_len);
count_param = FreeRTOS_CLIGetParameter(pcCommandString, 2, &count_param_len);
/* 解析打印次数 */
print_count = 1;
if (count_param != NULL) {
char count_str[16];
size_t copy_len = (size_t)count_param_len < sizeof(count_str) - 1 ?
(size_t)count_param_len : sizeof(count_str) - 1;
strncpy(count_str, count_param, copy_len);
count_str[copy_len] = '\0';
int parsed = atoi(count_str);
if (parsed > 0 && parsed <= 1000) {
print_count = (uint32_t)parsed;
}
}
/* 保存目标设备名称 */
if (param != NULL) {
size_t copy_len = (size_t)param_len < sizeof(target_device) - 1 ?
(size_t)param_len : sizeof(target_device) - 1;
strncpy(target_device, param, copy_len);
target_device[copy_len] = '\0';
} else {
target_device[0] = '\0';
}
}
int offset = 0;
/* 连续打印模式:清屏 */
if (print_count > 1) {
offset = snprintf(pcWriteBuffer, xWriteBufferLen, "%s[%lu/%lu]\r\n",
ANSI_CLEAR_SCREEN,
(unsigned long)(current_iter + 1),
(unsigned long)print_count);
}
if (target_device[0] == '\0') {
/* 显示所有设备 */
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
"=== All Devices ===\r\n\r\n");
for (uint8_t i = 0; i < ctx.device_count && offset < (int)xWriteBufferLen - 100; i++) {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
"--- %s ---\r\n", ctx.devices[i].name);
if (ctx.devices[i].print_cb != NULL) {
int written = ctx.devices[i].print_cb(ctx.devices[i].data,
pcWriteBuffer + offset,
xWriteBufferLen - offset);
offset += (written > 0) ? written : 0;
} else {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
" (No print function)\r\n");
}
}
if (ctx.device_count == 0) {
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
" (No devices registered)\r\n");
}
} else {
/* 显示指定设备 */
const MRobot_Device_t *dev = MRobot_FindDevice(target_device);
if (dev == NULL) {
snprintf(pcWriteBuffer, xWriteBufferLen,
"Error: Device '%s' not found\r\n",
target_device);
current_iter = 0;
return pdFALSE;
}
offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
"=== %s ===\r\n", dev->name);
if (dev->print_cb != NULL) {
dev->print_cb(dev->data, pcWriteBuffer + offset, xWriteBufferLen - offset);
} else {
snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset,
" (No print function)\r\n");
}
}
/* 判断是否继续打印 */
current_iter++;
if (current_iter < print_count) {
osDelay(MROBOT_HTOP_REFRESH_MS);
return pdTRUE;
} else {
current_iter = 0;
return pdFALSE;
}
}
/* ============================================================================
* htop
* ========================================================================== */
static void handle_htop_mode(void) {
send_string(ANSI_CLEAR_SCREEN);
send_string("=== MRobot Task Monitor (Press 'q' to exit) ===\r\n\r\n");
/* 获取任务列表 */
char task_buffer[1024];
char display_line[128];
vTaskList(task_buffer);
/* 表头 */
send_string("Task Name State Prio Stack Num\r\n");
send_string("------------------------------------------------\r\n");
/* 解析并格式化任务列表 */
char *line = strtok(task_buffer, "\r\n");
while (line != NULL) {
char name[17] = {0};
char state_char = '?';
int prio = 0, stack = 0, num = 0;
if (sscanf(line, "%16s %c %d %d %d", name, &state_char, &prio, &stack, &num) == 5) {
const char *state_str;
switch (state_char) {
case 'R': state_str = "Running"; break;
case 'B': state_str = "Blocked"; break;
case 'S': state_str = "Suspend"; break;
case 'D': state_str = "Deleted"; break;
case 'X': state_str = "Ready "; break;
default: state_str = "Unknown"; break;
}
snprintf(display_line, sizeof(display_line),
"%-16s %-8s %-4d %-8d %-4d\r\n",
name, state_str, prio, stack, num);
send_string(display_line);
}
line = strtok(NULL, "\r\n");
}
/* 显示系统信息 */
snprintf(display_line, sizeof(display_line),
"------------------------------------------------\r\n"
"System Tick: %lu | Free Heap: %lu bytes\r\n",
(unsigned long)xTaskGetTickCount(),
(unsigned long)xPortGetFreeHeapSize());
send_string(display_line);
/* 检查退出 */
if (ctx.htop_exit) {
ctx.state = MROBOT_STATE_IDLE;
ctx.htop_exit = false;
send_string(ANSI_CLEAR_SCREEN);
send_prompt();
}
osDelay(MROBOT_HTOP_REFRESH_MS);
}
/* ========================================================================== */
/* 公共 API 实现 */
/* ========================================================================== */
void MRobot_Init(void) {
if (ctx.initialized) return;
/* 创建互斥锁 */
ctx.mutex = xSemaphoreCreateMutex();
/* 初始化状态 */
memset(ctx.devices, 0, sizeof(ctx.devices));
ctx.device_count = 0;
ctx.custom_cmd_count = 0;
ctx.state = MROBOT_STATE_IDLE;
strcpy(ctx.current_path, "/");
ctx.cmd_index = 0;
ctx.cmd_ready = false;
ctx.tx_complete = true;
ctx.htop_exit = false;
/* 注册内置命令 */
for (size_t i = 0; i < BUILTIN_CMD_COUNT; i++) {
FreeRTOS_CLIRegisterCommand(&builtin_commands[i]);
}
/* 注册 UART 回调 */
BSP_UART_RegisterCallback(MROBOT_UART_PORT, BSP_UART_RX_CPLT_CB, uart_rx_callback);
BSP_UART_RegisterCallback(MROBOT_UART_PORT, BSP_UART_TX_CPLT_CB, uart_tx_callback);
/* 启动 UART 接收 */
BSP_UART_Receive(MROBOT_UART_PORT, &ctx.uart_rx_char, 1, false);
/* 等待用户按下回车 */
while (ctx.uart_rx_char != '\r' && ctx.uart_rx_char != '\n') {
osDelay(10);
}
/* 发送欢迎消息和提示符 */
send_string(CLI_WELCOME_MESSAGE);
send_prompt();
ctx.initialized = true;
}
void MRobot_DeInit(void) {
if (!ctx.initialized) return;
/* 释放自定义命令内存 */
for (uint8_t i = 0; i < ctx.custom_cmd_count; i++) {
if (ctx.custom_cmds[i] != NULL) {
vPortFree(ctx.custom_cmds[i]);
ctx.custom_cmds[i] = NULL;
}
}
/* 删除互斥锁 */
if (ctx.mutex != NULL) {
vSemaphoreDelete(ctx.mutex);
ctx.mutex = NULL;
}
ctx.initialized = false;
}
MRobot_State_t MRobot_GetState(void) {
return ctx.state;
}
MRobot_Error_t MRobot_RegisterDevice(const char *name, void *data, MRobot_PrintCallback_t print_cb) {
if (name == NULL || data == NULL) {
return MROBOT_ERR_NULL_PTR;
}
if (ctx.device_count >= MROBOT_MAX_DEVICES) {
return MROBOT_ERR_FULL;
}
/* 检查重名 */
for (uint8_t i = 0; i < ctx.device_count; i++) {
if (strcmp(ctx.devices[i].name, name) == 0) {
return MROBOT_ERR_INVALID_ARG; /* 设备名已存在 */
}
}
/* 线程安全写入 */
if (ctx.mutex != NULL) {
xSemaphoreTake(ctx.mutex, portMAX_DELAY);
}
strncpy(ctx.devices[ctx.device_count].name, name, MROBOT_DEVICE_NAME_LEN - 1);
ctx.devices[ctx.device_count].name[MROBOT_DEVICE_NAME_LEN - 1] = '\0';
ctx.devices[ctx.device_count].data = data;
ctx.devices[ctx.device_count].print_cb = print_cb;
ctx.device_count++;
if (ctx.mutex != NULL) {
xSemaphoreGive(ctx.mutex);
}
return MROBOT_OK;
}
MRobot_Error_t MRobot_UnregisterDevice(const char *name) {
if (name == NULL) {
return MROBOT_ERR_NULL_PTR;
}
if (ctx.mutex != NULL) {
xSemaphoreTake(ctx.mutex, portMAX_DELAY);
}
for (uint8_t i = 0; i < ctx.device_count; i++) {
if (strcmp(ctx.devices[i].name, name) == 0) {
/* 移动后续设备 */
for (uint8_t j = i; j < ctx.device_count - 1; j++) {
ctx.devices[j] = ctx.devices[j + 1];
}
ctx.device_count--;
if (ctx.mutex != NULL) {
xSemaphoreGive(ctx.mutex);
}
return MROBOT_OK;
}
}
if (ctx.mutex != NULL) {
xSemaphoreGive(ctx.mutex);
}
return MROBOT_ERR_NOT_FOUND;
}
MRobot_Error_t MRobot_RegisterCommand(const char *command, const char *help_text,
MRobot_CommandCallback_t callback, int8_t param_count) {
if (command == NULL || help_text == NULL || callback == NULL) {
return MROBOT_ERR_NULL_PTR;
}
if (ctx.custom_cmd_count >= MROBOT_MAX_CUSTOM_COMMANDS) {
return MROBOT_ERR_FULL;
}
/* 动态分配命令结构体 */
CLI_Command_Definition_t *cmd_def = pvPortMalloc(sizeof(CLI_Command_Definition_t));
if (cmd_def == NULL) {
return MROBOT_ERR_ALLOC;
}
/* 初始化命令定义 */
*(const char **)&cmd_def->pcCommand = command;
*(const char **)&cmd_def->pcHelpString = help_text;
*(pdCOMMAND_LINE_CALLBACK *)&cmd_def->pxCommandInterpreter = (pdCOMMAND_LINE_CALLBACK)callback;
cmd_def->cExpectedNumberOfParameters = param_count;
/* 注册到 FreeRTOS CLI */
FreeRTOS_CLIRegisterCommand(cmd_def);
ctx.custom_cmds[ctx.custom_cmd_count] = cmd_def;
ctx.custom_cmd_count++;
return MROBOT_OK;
}
uint8_t MRobot_GetDeviceCount(void) {
return ctx.device_count;
}
const MRobot_Device_t *MRobot_FindDevice(const char *name) {
if (name == NULL) return NULL;
for (uint8_t i = 0; i < ctx.device_count; i++) {
if (strcmp(ctx.devices[i].name, name) == 0) {
return &ctx.devices[i];
}
}
return NULL;
}
int MRobot_Printf(const char *fmt, ...) {
if (fmt == NULL || !ctx.initialized) return -1;
char buffer[MROBOT_OUTPUT_BUFFER_SIZE];
va_list args;
va_start(args, fmt);
int len = format_float_va(buffer, sizeof(buffer), fmt, args);
va_end(args);
if (len > 0) {
send_string(buffer);
}
return len;
}
/**
* @brief va_list
*/
static int format_float_va(char *buf, size_t size, const char *fmt, va_list args) {
if (buf == NULL || size == 0 || fmt == NULL) return 0;
char *buf_ptr = buf;
size_t buf_remain = size - 1;
const char *p = fmt;
while (*p && buf_remain > 0) {
if (*p != '%') {
*buf_ptr++ = *p++;
buf_remain--;
continue;
}
p++; /* 跳过 '%' */
/* 处理 %% */
if (*p == '%') {
*buf_ptr++ = '%';
buf_remain--;
p++;
continue;
}
/* 解析精度 %.Nf */
int precision = 2; /* 默认精度 */
if (*p == '.') {
p++;
precision = 0;
while (*p >= '0' && *p <= '9') {
precision = precision * 10 + (*p - '0');
p++;
}
if (precision > 6) precision = 6;
}
int written = 0;
switch (*p) {
case 'f': { /* 浮点数 */
double val = va_arg(args, double);
written = MRobot_FormatFloat(buf_ptr, buf_remain, (float)val, precision);
break;
}
case 'd':
case 'i': {
int val = va_arg(args, int);
written = snprintf(buf_ptr, buf_remain, "%d", val);
break;
}
case 'u': {
unsigned int val = va_arg(args, unsigned int);
written = snprintf(buf_ptr, buf_remain, "%u", val);
break;
}
case 'x': {
unsigned int val = va_arg(args, unsigned int);
written = snprintf(buf_ptr, buf_remain, "%x", val);
break;
}
case 'X': {
unsigned int val = va_arg(args, unsigned int);
written = snprintf(buf_ptr, buf_remain, "%X", val);
break;
}
case 's': {
const char *str = va_arg(args, const char *);
if (str == NULL) str = "(null)";
written = snprintf(buf_ptr, buf_remain, "%s", str);
break;
}
case 'c': {
int ch = va_arg(args, int);
*buf_ptr = (char)ch;
written = 1;
break;
}
case 'l': {
p++;
if (*p == 'd' || *p == 'i') {
long val = va_arg(args, long);
written = snprintf(buf_ptr, buf_remain, "%ld", val);
} else if (*p == 'u') {
unsigned long val = va_arg(args, unsigned long);
written = snprintf(buf_ptr, buf_remain, "%lu", val);
} else if (*p == 'x' || *p == 'X') {
unsigned long val = va_arg(args, unsigned long);
written = snprintf(buf_ptr, buf_remain, *p == 'x' ? "%lx" : "%lX", val);
} else {
p--;
}
break;
}
default: {
*buf_ptr++ = '%';
buf_remain--;
if (buf_remain > 0) {
*buf_ptr++ = *p;
buf_remain--;
}
written = 0;
break;
}
}
if (written > 0) {
buf_ptr += written;
buf_remain -= (size_t)written;
}
p++;
}
*buf_ptr = '\0';
return (int)(buf_ptr - buf);
}
int MRobot_Snprintf(char *buf, size_t size, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int len = format_float_va(buf, size, fmt, args);
va_end(args);
return len;
}
int MRobot_FormatFloat(char *buf, size_t size, float val, int precision) {
if (buf == NULL || size == 0) return 0;
int offset = 0;
/* 处理负数 */
if (val < 0) {
if (offset < (int)size - 1) buf[offset++] = '-';
val = -val;
}
/* 限制精度范围 */
if (precision < 0) precision = 0;
if (precision > 6) precision = 6;
/* 计算乘数 */
int multiplier = 1;
for (int i = 0; i < precision; i++) multiplier *= 10;
int int_part = (int)val;
int frac_part = (int)((val - int_part) * multiplier + 0.5f);
/* 处理进位 */
if (frac_part >= multiplier) {
int_part++;
frac_part -= multiplier;
}
/* 格式化输出 */
int written;
if (precision > 0) {
written = snprintf(buf + offset, size - offset, "%d.%0*d", int_part, precision, frac_part);
} else {
written = snprintf(buf + offset, size - offset, "%d", int_part);
}
return (written > 0) ? (offset + written) : offset;
}
void MRobot_Run(void) {
if (!ctx.initialized) return;
/* htop 模式 */
if (ctx.state == MROBOT_STATE_HTOP) {
handle_htop_mode();
return;
}
/* 处理命令 */
if (ctx.cmd_ready) {
ctx.state = MROBOT_STATE_PROCESSING;
/* 检查是否是 htop 命令 */
if (strcmp((char *)ctx.cmd_buffer, "htop") == 0) {
ctx.state = MROBOT_STATE_HTOP;
ctx.htop_exit = false;
} else {
/* 处理其他命令 */
BaseType_t more;
do {
ctx.output_buffer[0] = '\0';
more = FreeRTOS_CLIProcessCommand((char *)ctx.cmd_buffer,
ctx.output_buffer,
sizeof(ctx.output_buffer));
if (ctx.output_buffer[0] != '\0') {
send_string(ctx.output_buffer);
}
} while (more != pdFALSE);
send_prompt();
ctx.state = MROBOT_STATE_IDLE;
}
ctx.cmd_index = 0;
ctx.cmd_ready = false;
}
osDelay(10);
}

325
User/device/mrobot.h Normal file
View File

@ -0,0 +1,325 @@
/**
* @file mrobot.h
* @brief MRobot CLI - FreeRTOS CLI
*
* :
* - IMU
* - Unix cd, ls, pwd
* - htop
* -
* - 线
*
* @example IMU
* @code
* // 1. 定义 IMU 数据结构
* typedef struct {
* bool online;
* float accl[3];
* float gyro[3];
* float euler[3]; // roll, pitch, yaw (deg)
* float temp;
* } MyIMU_t;
*
* MyIMU_t my_imu;
*
* // 2. 实现打印回调
* static int print_imu(const void *data, char *buf, size_t size) {
* const MyIMU_t *imu = (const MyIMU_t *)data;
* char ax[16], ay[16], az[16], r[16], p[16], y[16], t[16];
* MRobot_FormatFloat(ax, 16, imu->accl[0], 3);
* MRobot_FormatFloat(ay, 16, imu->accl[1], 3);
* MRobot_FormatFloat(az, 16, imu->accl[2], 3);
* MRobot_FormatFloat(r, 16, imu->euler[0], 2);
* MRobot_FormatFloat(p, 16, imu->euler[1], 2);
* MRobot_FormatFloat(y, 16, imu->euler[2], 2);
* MRobot_FormatFloat(t, 16, imu->temp, 1);
* return snprintf(buf, size,
* " Status: %s\r\n"
* " Accel : X=%s Y=%s Z=%s\r\n"
* " Euler : R=%s P=%s Y=%s\r\n"
* " Temp : %s C\r\n",
* imu->online ? "Online" : "Offline", ax, ay, az, r, p, y, t);
* }
*
* // 3. 注册设备
* MRobot_RegisterDevice("imu", &my_imu, print_imu);
* @endcode
*
* @example
* @code
* typedef struct {
* bool online;
* float angle; // deg
* float speed; // RPM
* float current; // A
* } MyMotor_t;
*
* MyMotor_t motor[4];
*
* static int print_motor(const void *data, char *buf, size_t size) {
* const MyMotor_t *m = (const MyMotor_t *)data;
* char ang[16], spd[16], cur[16];
* MRobot_FormatFloat(ang, 16, m->angle, 2);
* MRobot_FormatFloat(spd, 16, m->speed, 1);
* MRobot_FormatFloat(cur, 16, m->current, 3);
* return snprintf(buf, size,
* " Status : %s\r\n"
* " Angle : %s deg\r\n"
* " Speed : %s RPM\r\n"
* " Current: %s A\r\n",
* m->online ? "Online" : "Offline", ang, spd, cur);
* }
*
* // 注册 4 个电机
* MRobot_RegisterDevice("motor0", &motor[0], print_motor);
* MRobot_RegisterDevice("motor1", &motor[1], print_motor);
* MRobot_RegisterDevice("motor2", &motor[2], print_motor);
* MRobot_RegisterDevice("motor3", &motor[3], print_motor);
* @endcode
*
* @example
* @code
* // 校准数据
* static float gyro_offset[3] = {0};
*
* // 命令回调: cali gyro / cali accel / cali save
* static long cmd_cali(char *buf, size_t size, const char *cmd) {
* const char *param = FreeRTOS_CLIGetParameter(cmd, 1, NULL);
*
* if (param == NULL) {
* return snprintf(buf, size, "Usage: cali <gyro|accel|save>\r\n");
* }
* if (strncmp(param, "gyro", 4) == 0) {
* // 采集 1000 次陀螺仪数据求平均
* snprintf(buf, size, "Calibrating gyro... keep IMU still!\r\n");
* // ... 校准逻辑 ...
* return 0;
* }
* if (strncmp(param, "save", 4) == 0) {
* // 保存到 Flash
* snprintf(buf, size, "Calibration saved to flash.\r\n");
* return 0;
* }
* return snprintf(buf, size, "Unknown: %s\r\n", param);
* }
*
* // 注册命令
* MRobot_RegisterCommand("cali", "cali <gyro|accel|save>: IMU calibration\r\n", cmd_cali, -1);
* @endcode
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "bsp/uart.h"
/* Configuration ------------------------------------------------------------ */
/* 可在编译时通过 -D 选项覆盖这些默认值 */
#ifndef MROBOT_MAX_DEVICES
#define MROBOT_MAX_DEVICES 64 /* 最大设备数 */
#endif
#ifndef MROBOT_MAX_CUSTOM_COMMANDS
#define MROBOT_MAX_CUSTOM_COMMANDS 16 /* 最大自定义命令数 */
#endif
#ifndef MROBOT_CMD_BUFFER_SIZE
#define MROBOT_CMD_BUFFER_SIZE 128 /* 命令缓冲区大小 */
#endif
#ifndef MROBOT_OUTPUT_BUFFER_SIZE
#define MROBOT_OUTPUT_BUFFER_SIZE 512 /* 输出缓冲区大小 */
#endif
#ifndef MROBOT_DEVICE_NAME_LEN
#define MROBOT_DEVICE_NAME_LEN 32 /* 设备名最大长度 */
#endif
#ifndef MROBOT_PATH_MAX_LEN
#define MROBOT_PATH_MAX_LEN 64 /* 路径最大长度 */
#endif
#ifndef MROBOT_HTOP_REFRESH_MS
#define MROBOT_HTOP_REFRESH_MS 200 /* htop 刷新间隔 (ms) */
#endif
#ifndef MROBOT_UART_PORT
#define MROBOT_UART_PORT BSP_UART_VOFA /* 默认 UART 端口 */
#endif
#ifndef MROBOT_USER_NAME
#define MROBOT_USER_NAME "root" /* CLI 用户名 */
#endif
#ifndef MROBOT_HOST_NAME
#define MROBOT_HOST_NAME "MRobot" /* CLI 主机名 */
#endif
/* Error codes -------------------------------------------------------------- */
typedef enum {
MROBOT_OK = 0, /* 成功 */
MROBOT_ERR_FULL = -1, /* 容量已满 */
MROBOT_ERR_NULL_PTR = -2, /* 空指针 */
MROBOT_ERR_INVALID_ARG = -3, /* 无效参数 */
MROBOT_ERR_NOT_FOUND = -4, /* 未找到 */
MROBOT_ERR_ALLOC = -5, /* 内存分配失败 */
MROBOT_ERR_BUSY = -6, /* 设备忙 */
MROBOT_ERR_NOT_INIT = -7, /* 未初始化 */
} MRobot_Error_t;
/* CLI 运行状态 */
typedef enum {
MROBOT_STATE_IDLE, /* 空闲状态,等待输入 */
MROBOT_STATE_HTOP, /* htop 模式 */
MROBOT_STATE_PROCESSING, /* 正在处理命令 */
} MRobot_State_t;
/* Callback types ----------------------------------------------------------- */
/**
* @brief
* @param device_data
* @param buffer
* @param buffer_size
* @return
* @note
*/
typedef int (*MRobot_PrintCallback_t)(const void *device_data, char *buffer, size_t buffer_size);
/**
* @brief FreeRTOS CLI
*/
typedef long (*MRobot_CommandCallback_t)(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
/* Device structure --------------------------------------------------------- */
typedef struct {
char name[MROBOT_DEVICE_NAME_LEN]; /* 设备名称 */
void *data; /* 用户设备数据指针 */
MRobot_PrintCallback_t print_cb; /* 用户打印回调函数 */
} MRobot_Device_t;
/* Public API --------------------------------------------------------------- */
/**
* @brief MRobot CLI
* @note FreeRTOS
*/
void MRobot_Init(void);
/**
* @brief MRobot CLI
*/
void MRobot_DeInit(void);
/**
* @brief CLI
* @return MRobot_State_t
*/
MRobot_State_t MRobot_GetState(void);
/**
* @brief MRobot
* @param name MROBOT_DEVICE_NAME_LEN-1
* @param data NULL
* @param print_cb NULL show
* @return MRobot_Error_t
*/
MRobot_Error_t MRobot_RegisterDevice(const char *name, void *data, MRobot_PrintCallback_t print_cb);
/**
* @brief
* @param name
* @return MRobot_Error_t
*/
MRobot_Error_t MRobot_UnregisterDevice(const char *name);
/**
* @brief
* @param command
* @param help_text
* @param callback
* @param param_count -1
* @return MRobot_Error_t
*/
MRobot_Error_t MRobot_RegisterCommand(const char *command, const char *help_text,
MRobot_CommandCallback_t callback, int8_t param_count);
/**
* @brief
* @return
*/
uint8_t MRobot_GetDeviceCount(void);
/**
* @brief
* @param name
* @return NULL
*/
const MRobot_Device_t *MRobot_FindDevice(const char *name);
/**
* @brief MRobot CLI
* @note 10ms
*/
void MRobot_Run(void);
/**
* @brief CLI 线
* @param fmt
* @param ...
* @return
*
* @note :
* - %d, %i, %u, %x, %X, %ld, %lu, %lx :
* - %s, %c : /
* - %f : (2)
* - %.Nf : (N位小数, N=0-6)
* - %% :
*
* @example
* MRobot_Printf("Euler: R=%.2f P=%.2f Y=%.2f\\r\\n", roll, pitch, yaw);
*/
int MRobot_Printf(const char *fmt, ...);
/**
* @brief
* @note MRobot_Printf
*
* @example
* static int print_imu(const void *data, char *buf, size_t size) {
* const BMI088_t *imu = (const BMI088_t *)data;
* return MRobot_Snprintf(buf, size,
* " Accel: X=%.3f Y=%.3f Z=%.3f\\r\\n",
* imu->accl.x, imu->accl.y, imu->accl.z);
* }
*/
int MRobot_Snprintf(char *buf, size_t size, const char *fmt, ...);
/* Utility functions -------------------------------------------------------- */
/**
* @brief %f
* @param buf
* @param size
* @param val
* @param precision (0-6)
* @return
*
* @example
* char buf[16];
* MRobot_FormatFloat(buf, sizeof(buf), 3.14159f, 2); // "3.14"
* MRobot_FormatFloat(buf, sizeof(buf), -0.001f, 4); // "-0.0010"
*/
int MRobot_FormatFloat(char *buf, size_t size, float val, int precision);
#ifdef __cplusplus
}
#endif

106
User/device/vofa.c Normal file
View File

@ -0,0 +1,106 @@
/* Includes ----------------------------------------------------------------- */
#include <stdio.h>
#include <string.h>
#include "device/vofa.h"
#include "bsp/uart.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
#define MAX_CHANNEL 64u // 根据实际最大通道数调整
#define JUSTFLOAT_TAIL 0x7F800000
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
static uint8_t vofa_tx_buf[sizeof(float) * MAX_CHANNEL + sizeof(uint32_t)];
static VOFA_Protocol_t current_protocol = VOFA_PROTOCOL_FIREWATER; // 默认协议
/* Private function -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
/************************ RawData *************************/
void VOFA_RawData_Send(const char* data, bool dma) {
BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t*)data, strlen(data), dma);
}
/************************ FireWater *************************/
void VOFA_FireWater_Send(float *channels, uint8_t channel_count, bool dma)
{
if (channel_count == 0 || channel_count > MAX_CHANNEL)
return;
char *buf = (char *)vofa_tx_buf;
size_t len = 0;
for (uint8_t i = 0; i < channel_count; ++i) {
len += snprintf(buf + len,
sizeof(vofa_tx_buf) - len,
"%s%.2f",
(i ? "," : ""),
channels[i]);
}
snprintf(buf + len, sizeof(vofa_tx_buf) - len, "\n");
BSP_UART_Transmit(BSP_UART_VOFA, vofa_tx_buf, strlen(buf), dma);
}
/************************ JustFloat *************************/
void VOFA_JustFloat_Send(float *channels, uint8_t channel_count, bool dma)
{
if (channel_count == 0 || channel_count > MAX_CHANNEL)
return;
memcpy(vofa_tx_buf, channels, channel_count * sizeof(float));
uint32_t tail = JUSTFLOAT_TAIL; // 0x7F800000
memcpy(vofa_tx_buf + channel_count * sizeof(float), &tail, sizeof(tail));
BSP_UART_Transmit(BSP_UART_VOFA, vofa_tx_buf, channel_count * sizeof(float) + sizeof(tail), dma);
}
/* Exported functions ------------------------------------------------------- */
int8_t VOFA_init(VOFA_Protocol_t protocol) {
current_protocol = protocol;
return DEVICE_OK;
}
int8_t VOFA_Send(float* channels, uint8_t channel_count, bool dma) {
switch (current_protocol) {
case VOFA_PROTOCOL_RAWDATA:
{
char data[256];
if (channel_count >= 1) {
sprintf(data, "Channel1: %.2f", channels[0]);
if (channel_count >= 2) {
sprintf(data + strlen(data), ", Channel2: %.2f", channels[1]);
}
strcat(data, "\n");
VOFA_RawData_Send(data, dma);
}
}
break;
case VOFA_PROTOCOL_FIREWATER:
VOFA_FireWater_Send(channels, channel_count, dma);
break;
case VOFA_PROTOCOL_JUSTFLOAT:
VOFA_JustFloat_Send(channels, channel_count, dma);
break;
default:
return DEVICE_ERR;
}
return DEVICE_OK;
}

39
User/device/vofa.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "bsp/uart.h"
#include "device/device.h"
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* Exported functions prototypes -------------------------------------------- */
typedef enum {
VOFA_PROTOCOL_RAWDATA,
VOFA_PROTOCOL_FIREWATER,
VOFA_PROTOCOL_JUSTFLOAT,
} VOFA_Protocol_t;
/**
* @brief VOFA设备
* @param protocol
* @return
*/
int8_t VOFA_init(VOFA_Protocol_t protocol);
/**
* @brief VOFA
* @param channels
* @param channel_count
* @param dma 使DMA发送
* @return
*/
int8_t VOFA_Send(float* channels, uint8_t channel_count, bool dma);
#ifdef __cplusplus
}
#endif

View File

@ -134,7 +134,7 @@ int8_t Shoot_Init(Shoot_t *s, Shoot_Params_t *param, float target_freq)
memset(&s->shoot_Anglecalu,0,sizeof(s->shoot_Anglecalu)); memset(&s->shoot_Anglecalu,0,sizeof(s->shoot_Anglecalu));
memset(&s->output,0,sizeof(s->output)); memset(&s->output,0,sizeof(s->output));
s->target_variable.target_rpm=6000.0f; /* 归一化目标转速 */ s->target_variable.target_rpm=2000.0f; /* 归一化目标转速 */
return 0; return 0;
} }

View File

@ -4,7 +4,6 @@
*/ */
/* Includes ----------------------------------------------------------------- */ /* Includes ----------------------------------------------------------------- */
#include "cmsis_os2.h"
#include "task/user_task.h" #include "task/user_task.h"
/* USER INCLUDE BEGIN */ /* USER INCLUDE BEGIN */
#include "bsp/fdcan.h" #include "bsp/fdcan.h"

View File

@ -10,10 +10,14 @@
#include "bsp/mm.h" #include "bsp/mm.h"
#include "bsp/pwm.h" #include "bsp/pwm.h"
#include "component/ahrs.h" #include "component/ahrs.h"
#include "component/QuaternionEKF.h"
#include "component/pid.h" #include "component/pid.h"
#include "device/bmi088.h" #include "device/bmi088.h"
#include "device/mrobot.h"
#include "device/device.h"
#include "module/balance_chassis.h" #include "module/balance_chassis.h"
#include "task/user_task.h" #include "task/user_task.h"
#include <stdio.h>
/* USER INCLUDE END */ /* USER INCLUDE END */
/* Private typedef ---------------------------------------------------------- */ /* Private typedef ---------------------------------------------------------- */
@ -22,9 +26,10 @@
/* Private variables -------------------------------------------------------- */ /* Private variables -------------------------------------------------------- */
/* USER STRUCT BEGIN */ /* USER STRUCT BEGIN */
BMI088_t bmi088; BMI088_t bmi088;
#ifndef USE_QEKF
AHRS_t gimbal_ahrs; AHRS_t gimbal_ahrs;
AHRS_Magn_t magn; AHRS_Magn_t magn;
#endif
AHRS_Eulr_t eulr_to_send; AHRS_Eulr_t eulr_to_send;
Chassis_IMU_t imu_for_chassis; Chassis_IMU_t imu_for_chassis;
KPID_t imu_temp_ctrl_pid; KPID_t imu_temp_ctrl_pid;
@ -46,6 +51,27 @@ static const KPID_Params_t imu_temp_ctrl_pid_param = {
/* USER STRUCT END */ /* USER STRUCT END */
/* Private function --------------------------------------------------------- */ /* Private function --------------------------------------------------------- */
/**
* @brief IMU
*/
static int print_imu_callback(const void *data, char *buffer, size_t size) {
const BMI088_t *imu = (const BMI088_t *)data;
#define RAD2DEG 57.2957795131f
return MRobot_Snprintf(buffer, size,
" Status : %s\r\n"
" Accel : X=%.3f Y=%.3f Z=%.3f (m/s^2)\r\n"
" Gyro : X=%.3f Y=%.3f Z=%.3f (rad/s)\r\n"
" Euler : R=%.2f P=%.2f Y=%.2f (deg)\r\n"
" Temp : %.1f C\r\n",
imu->header.online ? "Online" : "Offline",
imu->accl.x, imu->accl.y, imu->accl.z,
imu->gyro.x, imu->gyro.y, imu->gyro.z,
eulr_to_send.rol * RAD2DEG, eulr_to_send.pit * RAD2DEG, eulr_to_send.yaw * RAD2DEG,
imu->temp);
}
/* Exported functions ------------------------------------------------------- */ /* Exported functions ------------------------------------------------------- */
void Task_atti_esit(void *argument) { void Task_atti_esit(void *argument) {
(void)argument; /* 未使用argument消除警告 */ (void)argument; /* 未使用argument消除警告 */
@ -55,10 +81,17 @@ void Task_atti_esit(void *argument) {
/* USER CODE INIT BEGIN */ /* USER CODE INIT BEGIN */
BMI088_Init(&bmi088, &cali_bmi088); BMI088_Init(&bmi088, &cali_bmi088);
#ifdef USE_QEKF
QEKF_Init(10.0f, 0.001f, 1000000.0f, 0.9996f, 0.0f);
#else
AHRS_Init(&gimbal_ahrs, &magn, BMI088_GetUpdateFreq(&bmi088)); AHRS_Init(&gimbal_ahrs, &magn, BMI088_GetUpdateFreq(&bmi088));
#endif
PID_Init(&imu_temp_ctrl_pid, KPID_MODE_NO_D, PID_Init(&imu_temp_ctrl_pid, KPID_MODE_NO_D,
1.0f / BMI088_GetUpdateFreq(&bmi088), &imu_temp_ctrl_pid_param); 1.0f / BMI088_GetUpdateFreq(&bmi088), &imu_temp_ctrl_pid_param);
BSP_PWM_Start(BSP_PWM_IMU_HEAT); BSP_PWM_Start(BSP_PWM_IMU_HEAT);
/* 注册 IMU 设备到 MRobot */
MRobot_RegisterDevice("bmi088", &bmi088, print_imu_callback);
/* USER CODE INIT END */ /* USER CODE INIT END */
while (1) { while (1) {
@ -78,10 +111,19 @@ void Task_atti_esit(void *argument) {
// IST8310_Parse(&ist8310); // IST8310_Parse(&ist8310);
/* 根据设备接收到的数据进行姿态解析 */ /* 根据设备接收到的数据进行姿态解析 */
#ifdef USE_QEKF
QEKF_Update(bmi088.gyro.x, bmi088.gyro.y, bmi088.gyro.z,
bmi088.accl.x, bmi088.accl.y, bmi088.accl.z,
1.0f / BMI088_GetUpdateFreq(&bmi088));
/* 从EKF获取欧拉角 */
eulr_to_send.rol = QEKF_INS.roll;
eulr_to_send.pit = QEKF_INS.pitch;
eulr_to_send.yaw = QEKF_INS.yaw;
#else
AHRS_Update(&gimbal_ahrs, &bmi088.accl, &bmi088.gyro, &magn); AHRS_Update(&gimbal_ahrs, &bmi088.accl, &bmi088.gyro, &magn);
/* 根据解析出来的四元数计算欧拉角 */ /* 根据解析出来的四元数计算欧拉角 */
AHRS_GetEulr(&eulr_to_send, &gimbal_ahrs); AHRS_GetEulr(&eulr_to_send, &gimbal_ahrs);
#endif
/* 加速度计数据绕z轴旋转270度: (x,y,z) -> (y,-x,z) */ /* 加速度计数据绕z轴旋转270度: (x,y,z) -> (y,-x,z) */
imu_for_chassis.accl.x = bmi088.accl.y; imu_for_chassis.accl.x = bmi088.accl.y;
imu_for_chassis.accl.y = -bmi088.accl.x; imu_for_chassis.accl.y = -bmi088.accl.x;

40
User/task/cli.c Normal file
View File

@ -0,0 +1,40 @@
/*
cli Task
*/
/* Includes ----------------------------------------------------------------- */
#include "task/user_task.h"
/* USER INCLUDE BEGIN */
#include "device/mrobot.h"
/* USER INCLUDE END */
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private function --------------------------------------------------------- */
/* Exported functions ------------------------------------------------------- */
void Task_cli(void *argument) {
(void)argument; /* 未使用argument消除警告 */
osDelay(CLI_INIT_DELAY); /* 延时一段时间再开启任务 */
/* USER CODE INIT BEGIN */
/* 初始化 MRobot CLI 系统 */
MRobot_Init();
/* USER CODE INIT END */
while (1) {
/* USER CODE BEGIN */
/* 运行 MRobot 主循环 */
MRobot_Run();
/* USER CODE END */
}
}

View File

@ -53,3 +53,16 @@
function: Task_ai function: Task_ai
name: ai name: ai
stack: 256 stack: 256
- delay: 0
description: ''
freq_control: true
frequency: 500.0
function: Task_vofa
name: vofa
stack: 256
- delay: 0
description: ''
freq_control: false
function: Task_cli
name: cli
stack: 1024

View File

@ -69,6 +69,8 @@ void Task_ctrl_chassis(void *argument) {
} }
Chassis_Control(&chassis, &chassis_cmd); Chassis_Control(&chassis, &chassis_cmd);
osMessageQueueReset(task_runtime.msgq.chassis.vofa); // 重置消息队列,防止阻塞
osMessageQueuePut(task_runtime.msgq.chassis.vofa, &chassis, 0, 0);
/* USER CODE END */ /* USER CODE END */
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */ osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
} }

View File

@ -24,7 +24,6 @@ Shoot_CMD_t shoot_cmd;
void Task_ctrl_shoot(void *argument) { void Task_ctrl_shoot(void *argument) {
(void)argument; /* 未使用argument消除警告 */ (void)argument; /* 未使用argument消除警告 */
/* 计算任务运行到指定频率需要等待的tick数 */ /* 计算任务运行到指定频率需要等待的tick数 */
const uint32_t delay_tick = osKernelGetTickFreq() / CTRL_SHOOT_FREQ; const uint32_t delay_tick = osKernelGetTickFreq() / CTRL_SHOOT_FREQ;

View File

@ -40,6 +40,8 @@ void Task_Init(void *argument) {
task_runtime.thread.blink = osThreadNew(Task_blink, NULL, &attr_blink); task_runtime.thread.blink = osThreadNew(Task_blink, NULL, &attr_blink);
task_runtime.thread.ctrl_shoot = osThreadNew(Task_ctrl_shoot, NULL, &attr_ctrl_shoot); task_runtime.thread.ctrl_shoot = osThreadNew(Task_ctrl_shoot, NULL, &attr_ctrl_shoot);
task_runtime.thread.ai = osThreadNew(Task_ai, NULL, &attr_ai); task_runtime.thread.ai = osThreadNew(Task_ai, NULL, &attr_ai);
// task_runtime.thread.vofa = osThreadNew(Task_vofa, NULL, &attr_vofa);
task_runtime.thread.cli = osThreadNew(Task_cli, NULL, &attr_cli);
// 创建消息队列 // 创建消息队列
/* USER MESSAGE BEGIN */ /* USER MESSAGE BEGIN */
@ -48,8 +50,10 @@ void Task_Init(void *argument) {
task_runtime.msgq.chassis.imu = osMessageQueueNew(2u, sizeof(Chassis_IMU_t), NULL); task_runtime.msgq.chassis.imu = osMessageQueueNew(2u, sizeof(Chassis_IMU_t), NULL);
task_runtime.msgq.chassis.cmd = osMessageQueueNew(2u, sizeof(Chassis_CMD_t), NULL); task_runtime.msgq.chassis.cmd = osMessageQueueNew(2u, sizeof(Chassis_CMD_t), NULL);
task_runtime.msgq.chassis.yaw = osMessageQueueNew(2u, sizeof(MOTOR_Feedback_t), NULL); task_runtime.msgq.chassis.yaw = osMessageQueueNew(2u, sizeof(MOTOR_Feedback_t), NULL);
task_runtime.msgq.chassis.vofa = osMessageQueueNew(2u, sizeof(Chassis_t), NULL);
task_runtime.msgq.gimbal.cmd = osMessageQueueNew(2u, sizeof(Gimbal_CMD_t), NULL); task_runtime.msgq.gimbal.cmd = osMessageQueueNew(2u, sizeof(Gimbal_CMD_t), NULL);
task_runtime.msgq.gimbal.ai_cmd = osMessageQueueNew(2u, sizeof(Gimbal_AI_t), NULL); task_runtime.msgq.gimbal.ai_cmd = osMessageQueueNew(2u, sizeof(Gimbal_AI_t), NULL);
/* USER MESSAGE END */ /* USER MESSAGE END */
osKernelUnlock(); // 解锁内核 osKernelUnlock(); // 解锁内核

View File

@ -49,3 +49,13 @@ const osThreadAttr_t attr_ai = {
.priority = osPriorityNormal, .priority = osPriorityNormal,
.stack_size = 256 * 4, .stack_size = 256 * 4,
}; };
const osThreadAttr_t attr_vofa = {
.name = "vofa",
.priority = osPriorityNormal,
.stack_size = 256 * 4,
};
const osThreadAttr_t attr_cli = {
.name = "cli",
.priority = osPriorityNormal,
.stack_size = 256 * 16,
};

View File

@ -20,6 +20,7 @@ extern "C" {
#define BLINK_FREQ (500.0) #define BLINK_FREQ (500.0)
#define CTRL_SHOOT_FREQ (500.0) #define CTRL_SHOOT_FREQ (500.0)
#define AI_FREQ (500.0) #define AI_FREQ (500.0)
#define VOFA_FREQ (500.0)
/* 任务初始化延时ms */ /* 任务初始化延时ms */
#define TASK_INIT_DELAY (100u) #define TASK_INIT_DELAY (100u)
@ -31,6 +32,8 @@ extern "C" {
#define BLINK_INIT_DELAY (0) #define BLINK_INIT_DELAY (0)
#define CTRL_SHOOT_INIT_DELAY (0) #define CTRL_SHOOT_INIT_DELAY (0)
#define AI_INIT_DELAY (0) #define AI_INIT_DELAY (0)
#define VOFA_INIT_DELAY (0)
#define CLI_INIT_DELAY (0)
/* Exported defines --------------------------------------------------------- */ /* Exported defines --------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */ /* Exported macro ----------------------------------------------------------- */
@ -48,6 +51,8 @@ typedef struct {
osThreadId_t blink; osThreadId_t blink;
osThreadId_t ctrl_shoot; osThreadId_t ctrl_shoot;
osThreadId_t ai; osThreadId_t ai;
osThreadId_t vofa;
osThreadId_t cli;
} thread; } thread;
/* USER MESSAGE BEGIN */ /* USER MESSAGE BEGIN */
@ -57,6 +62,7 @@ typedef struct {
osMessageQueueId_t imu; osMessageQueueId_t imu;
osMessageQueueId_t cmd; osMessageQueueId_t cmd;
osMessageQueueId_t yaw; osMessageQueueId_t yaw;
osMessageQueueId_t vofa;
}chassis; }chassis;
struct { struct {
osMessageQueueId_t imu; osMessageQueueId_t imu;
@ -101,6 +107,8 @@ typedef struct {
UBaseType_t blink; UBaseType_t blink;
UBaseType_t ctrl_shoot; UBaseType_t ctrl_shoot;
UBaseType_t ai; UBaseType_t ai;
UBaseType_t vofa;
UBaseType_t cli;
} stack_water_mark; } stack_water_mark;
/* 各任务运行频率 */ /* 各任务运行频率 */
@ -112,6 +120,7 @@ typedef struct {
float blink; float blink;
float ctrl_shoot; float ctrl_shoot;
float ai; float ai;
float vofa;
} freq; } freq;
/* 任务最近运行时间 */ /* 任务最近运行时间 */
@ -123,6 +132,7 @@ typedef struct {
float blink; float blink;
float ctrl_shoot; float ctrl_shoot;
float ai; float ai;
float vofa;
} last_up_time; } last_up_time;
} Task_Runtime_t; } Task_Runtime_t;
@ -140,6 +150,8 @@ extern const osThreadAttr_t attr_monitor;
extern const osThreadAttr_t attr_blink; extern const osThreadAttr_t attr_blink;
extern const osThreadAttr_t attr_ctrl_shoot; extern const osThreadAttr_t attr_ctrl_shoot;
extern const osThreadAttr_t attr_ai; extern const osThreadAttr_t attr_ai;
extern const osThreadAttr_t attr_vofa;
extern const osThreadAttr_t attr_cli;
/* 任务函数声明 */ /* 任务函数声明 */
void Task_Init(void *argument); void Task_Init(void *argument);
@ -151,6 +163,8 @@ void Task_monitor(void *argument);
void Task_blink(void *argument); void Task_blink(void *argument);
void Task_ctrl_shoot(void *argument); void Task_ctrl_shoot(void *argument);
void Task_ai(void *argument); void Task_ai(void *argument);
void Task_vofa(void *argument);
void Task_cli(void *argument);
#ifdef __cplusplus #ifdef __cplusplus
} }

84
User/task/vofa.c Normal file
View File

@ -0,0 +1,84 @@
/*
vofa Task
*/
/* Includes ----------------------------------------------------------------- */
#include "task/user_task.h"
/* USER INCLUDE BEGIN */
#include "module/balance_chassis.h"
#include "device/vofa.h"
/* USER INCLUDE END */
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
/* USER STRUCT BEGIN */
Chassis_t chassis_vofa;
/* USER STRUCT END */
/* Private function --------------------------------------------------------- */
/* Exported functions ------------------------------------------------------- */
void Task_vofa(void *argument) {
(void)argument; /* 未使用argument消除警告 */
/* 计算任务运行到指定频率需要等待的tick数 */
const uint32_t delay_tick = osKernelGetTickFreq() / VOFA_FREQ;
osDelay(VOFA_INIT_DELAY); /* 延时一段时间再开启任务 */
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
/* USER CODE INIT BEGIN */
VOFA_init(VOFA_PROTOCOL_JUSTFLOAT);
/* USER CODE INIT END */
while (1) {
tick += delay_tick; /* 计算下一个唤醒时刻 */
/* USER CODE BEGIN */
// 从消息队列获取chassis数据
if (osMessageQueueGet(task_runtime.msgq.chassis.vofa, &chassis_vofa, NULL, 0) == osOK) {
// 准备要发送的数据通道
float channels[16];
// 通道0-1: 左右腿的VMC腿长
channels[0] = chassis_vofa.vmc_[0].leg.L0;
channels[1] = chassis_vofa.vmc_[1].leg.L0;
// 通道2-3: 左右腿的VMC角度
channels[2] = chassis_vofa.vmc_[0].leg.theta;
channels[3] = chassis_vofa.vmc_[1].leg.theta;
// 通道4-6: IMU欧拉角 (pitch, roll, yaw)
channels[4] = chassis_vofa.feedback.imu.euler.pit;
channels[5] = chassis_vofa.feedback.imu.euler.rol;
channels[6] = chassis_vofa.feedback.imu.euler.yaw;
// 通道7-8: 左右轮速度
channels[7] = chassis_vofa.vmc_[0].leg.d_theta;
channels[8] = chassis_vofa.vmc_[0].leg.dd_theta;
// 通道9-10: 机体位置和速度
channels[9] = chassis_vofa.chassis_state.position_x;
channels[10] = chassis_vofa.chassis_state.velocity_x;
// 通道11-12: 左右腿的虚拟支撑力
channels[11] = chassis_vofa.vmc_[0].leg.F_virtual;
channels[12] = chassis_vofa.vmc_[1].leg.F_virtual;
// 通道13-14: 左右腿的虚拟摆力矩
channels[13] = chassis_vofa.vmc_[0].leg.T_virtual;
channels[14] = chassis_vofa.vmc_[1].leg.T_virtual;
// 通道15: yaw控制力矩
channels[15] = chassis_vofa.yaw_control.yaw_force;
// 使用JustFloat协议和DMA发送
VOFA_Send(channels, 16, true);
}
/* USER CODE END */
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
}
}

View File

@ -19,6 +19,7 @@ set(MX_Include_Dirs
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F ${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Device/ST/STM32H7xx/Include ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Device/ST/STM32H7xx/Include
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Include ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Include
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/ST/ARM/DSP/Inc
) )
# STM32CubeMX generated application sources # STM32CubeMX generated application sources
@ -27,6 +28,7 @@ set(MX_Application_Src
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/gpio.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/gpio.c
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/freertos.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/freertos.c
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/adc.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/adc.c
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/bdma.c
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/dma.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/dma.c
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/fdcan.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/fdcan.c
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/octospi.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/octospi.c