添加底层
This commit is contained in:
parent
23ae0c3fa9
commit
7d868bf32a
11
.clangd
Normal file
11
.clangd
Normal file
@ -0,0 +1,11 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
- '-ferror-limit=0'
|
||||
- '-Wno-implicit-int'
|
||||
CompilationDatabase: build/Debug
|
||||
Diagnostics:
|
||||
Suppress:
|
||||
- unused-includes
|
||||
- unknown_typename
|
||||
- unknown_typename_suggest
|
||||
- typename_requires_specqual
|
||||
17
.mxproject
17
.mxproject
File diff suppressed because one or more lines are too long
91
CMakeLists.txt
Normal file
91
CMakeLists.txt
Normal file
@ -0,0 +1,91 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
#
|
||||
# This file is generated only once,
|
||||
# and is not re-generated if converter is called multiple times.
|
||||
#
|
||||
# User is free to modify the file as much as necessary
|
||||
#
|
||||
|
||||
# Setup compiler settings
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS ON)
|
||||
|
||||
|
||||
# Define the build type
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
endif()
|
||||
|
||||
# Set the project name
|
||||
set(CMAKE_PROJECT_NAME DevC)
|
||||
|
||||
# Enable compile command to ease indexing with e.g. clangd
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
# Core project settings
|
||||
project(${CMAKE_PROJECT_NAME})
|
||||
message("Build type: " ${CMAKE_BUILD_TYPE})
|
||||
|
||||
# Enable CMake support for ASM and C languages
|
||||
enable_language(C ASM)
|
||||
|
||||
# Create an executable object type
|
||||
add_executable(${CMAKE_PROJECT_NAME})
|
||||
|
||||
# Add STM32CubeMX generated sources
|
||||
add_subdirectory(cmake/stm32cubemx)
|
||||
|
||||
# Link directories setup
|
||||
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
# Add user defined library search paths
|
||||
)
|
||||
|
||||
# Add sources to executable
|
||||
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
# Add user sources here
|
||||
# User/bsp sources
|
||||
User/bsp/can.c
|
||||
User/bsp/mm.c
|
||||
User/bsp/time.c
|
||||
|
||||
# User/component sources
|
||||
User/component/ahrs.c
|
||||
User/component/user_math.c
|
||||
|
||||
# User/device sources
|
||||
User/device/dm_imu.c
|
||||
User/device/motor.c
|
||||
User/device/motor_lk.c
|
||||
User/device/motor_lz.c
|
||||
|
||||
# User/task sources
|
||||
User/task/Task4.c
|
||||
User/task/blink.c
|
||||
User/task/ctrl_lz.c
|
||||
User/task/imu.c
|
||||
User/task/init.c
|
||||
User/task/user_task.c
|
||||
)
|
||||
|
||||
# Add include paths
|
||||
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
# Add user defined include paths
|
||||
User
|
||||
)
|
||||
|
||||
# Add project symbols (macros)
|
||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
# Add user defined symbols
|
||||
)
|
||||
|
||||
# Remove wrong libob.a library dependency when using cpp files
|
||||
list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_LIBRARIES ob)
|
||||
|
||||
# Add linked libraries
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
stm32cubemx
|
||||
|
||||
# Add user defined libraries
|
||||
)
|
||||
38
CMakePresets.json
Normal file
38
CMakePresets.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"hidden": true,
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"toolchainFile": "${sourceDir}/cmake/gcc-arm-none-eabi.cmake",
|
||||
"cacheVariables": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Release",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "Debug",
|
||||
"configurePreset": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "Release",
|
||||
"configurePreset": "Release"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -59,6 +59,7 @@ void EXTI3_IRQHandler(void);
|
||||
void EXTI4_IRQHandler(void);
|
||||
void DMA1_Stream1_IRQHandler(void);
|
||||
void DMA1_Stream2_IRQHandler(void);
|
||||
void CAN1_TX_IRQHandler(void);
|
||||
void CAN1_RX0_IRQHandler(void);
|
||||
void CAN1_RX1_IRQHandler(void);
|
||||
void EXTI9_5_IRQHandler(void);
|
||||
@ -69,6 +70,7 @@ void TIM7_IRQHandler(void);
|
||||
void DMA2_Stream1_IRQHandler(void);
|
||||
void DMA2_Stream2_IRQHandler(void);
|
||||
void DMA2_Stream3_IRQHandler(void);
|
||||
void CAN2_TX_IRQHandler(void);
|
||||
void CAN2_RX0_IRQHandler(void);
|
||||
void CAN2_RX1_IRQHandler(void);
|
||||
void OTG_FS_IRQHandler(void);
|
||||
|
||||
@ -122,6 +122,8 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
|
||||
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
||||
|
||||
/* CAN1 interrupt Init */
|
||||
HAL_NVIC_SetPriority(CAN1_TX_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);
|
||||
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
|
||||
HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 5, 0);
|
||||
@ -155,6 +157,8 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
|
||||
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
||||
|
||||
/* CAN2 interrupt Init */
|
||||
HAL_NVIC_SetPriority(CAN2_TX_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN2_TX_IRQn);
|
||||
HAL_NVIC_SetPriority(CAN2_RX0_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN2_RX0_IRQn);
|
||||
HAL_NVIC_SetPriority(CAN2_RX1_IRQn, 5, 0);
|
||||
@ -186,6 +190,7 @@ void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
|
||||
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_0|GPIO_PIN_1);
|
||||
|
||||
/* CAN1 interrupt Deinit */
|
||||
HAL_NVIC_DisableIRQ(CAN1_TX_IRQn);
|
||||
HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
|
||||
HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn);
|
||||
/* USER CODE BEGIN CAN1_MspDeInit 1 */
|
||||
@ -211,6 +216,7 @@ void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
|
||||
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_5|GPIO_PIN_6);
|
||||
|
||||
/* CAN2 interrupt Deinit */
|
||||
HAL_NVIC_DisableIRQ(CAN2_TX_IRQn);
|
||||
HAL_NVIC_DisableIRQ(CAN2_RX0_IRQn);
|
||||
HAL_NVIC_DisableIRQ(CAN2_RX1_IRQn);
|
||||
/* USER CODE BEGIN CAN2_MspDeInit 1 */
|
||||
|
||||
@ -1,161 +1,159 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : freertos.c
|
||||
* Description : Code for freertos applications
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under Ultimate Liberty license
|
||||
* SLA0044, the "License"; You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at:
|
||||
* www.st.com/SLA0044
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "main.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
/* Private includes ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PTD */
|
||||
|
||||
/* USER CODE END PTD */
|
||||
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PD */
|
||||
|
||||
/* USER CODE END PD */
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PM */
|
||||
|
||||
/* USER CODE END PM */
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Variables */
|
||||
|
||||
/* USER CODE END Variables */
|
||||
/* Definitions for defaultTask */
|
||||
osThreadId_t defaultTaskHandle;
|
||||
const osThreadAttr_t defaultTask_attributes = {
|
||||
.name = "defaultTask",
|
||||
.stack_size = 128 * 4,
|
||||
.priority = (osPriority_t) osPriorityNormal,
|
||||
};
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* USER CODE BEGIN FunctionPrototypes */
|
||||
|
||||
/* USER CODE END FunctionPrototypes */
|
||||
|
||||
void StartDefaultTask(void *argument);
|
||||
|
||||
extern void MX_USB_DEVICE_Init(void);
|
||||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||||
|
||||
/* Hook prototypes */
|
||||
void configureTimerForRunTimeStats(void);
|
||||
unsigned long getRunTimeCounterValue(void);
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName);
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* USER CODE BEGIN 4 */
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
|
||||
{
|
||||
/* Run time stack overflow checking is performed if
|
||||
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
|
||||
called if a stack overflow is detected. */
|
||||
}
|
||||
/* USER CODE END 4 */
|
||||
|
||||
/**
|
||||
* @brief FreeRTOS initialization
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void MX_FREERTOS_Init(void) {
|
||||
/* USER CODE BEGIN Init */
|
||||
|
||||
/* USER CODE END Init */
|
||||
|
||||
/* USER CODE BEGIN RTOS_MUTEX */
|
||||
/* add mutexes, ... */
|
||||
/* USER CODE END RTOS_MUTEX */
|
||||
|
||||
/* USER CODE BEGIN RTOS_SEMAPHORES */
|
||||
/* add semaphores, ... */
|
||||
/* USER CODE END RTOS_SEMAPHORES */
|
||||
|
||||
/* USER CODE BEGIN RTOS_TIMERS */
|
||||
/* start timers, add new ones, ... */
|
||||
/* USER CODE END RTOS_TIMERS */
|
||||
|
||||
/* USER CODE BEGIN RTOS_QUEUES */
|
||||
/* add queues, ... */
|
||||
/* USER CODE END RTOS_QUEUES */
|
||||
|
||||
/* Create the thread(s) */
|
||||
/* creation of defaultTask */
|
||||
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
/* add threads, ... */
|
||||
/* USER CODE END RTOS_THREADS */
|
||||
|
||||
/* USER CODE BEGIN RTOS_EVENTS */
|
||||
/* add events, ... */
|
||||
/* USER CODE END RTOS_EVENTS */
|
||||
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN Header_StartDefaultTask */
|
||||
/**
|
||||
* @brief Function implementing the defaultTask thread.
|
||||
* @param argument: Not used
|
||||
* @retval None
|
||||
*/
|
||||
/* USER CODE END Header_StartDefaultTask */
|
||||
void StartDefaultTask(void *argument)
|
||||
{
|
||||
/* init code for USB_DEVICE */
|
||||
MX_USB_DEVICE_Init();
|
||||
/* USER CODE BEGIN StartDefaultTask */
|
||||
/* Infinite loop */
|
||||
for(;;)
|
||||
{
|
||||
osDelay(1);
|
||||
}
|
||||
/* USER CODE END StartDefaultTask */
|
||||
}
|
||||
|
||||
/* Private application code --------------------------------------------------*/
|
||||
/* USER CODE BEGIN Application */
|
||||
|
||||
/* USER CODE END Application */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : freertos.c
|
||||
* Description : Code for freertos applications
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under Ultimate Liberty license
|
||||
* SLA0044, the "License"; You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at:
|
||||
* www.st.com/SLA0044
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "main.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
/* Private includes ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
|
||||
#include "task/user_task.h"
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PTD */
|
||||
|
||||
/* USER CODE END PTD */
|
||||
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PD */
|
||||
|
||||
/* USER CODE END PD */
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PM */
|
||||
|
||||
/* USER CODE END PM */
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Variables */
|
||||
|
||||
/* USER CODE END Variables */
|
||||
/* Definitions for defaultTask */
|
||||
osThreadId_t defaultTaskHandle;
|
||||
const osThreadAttr_t defaultTask_attributes = {
|
||||
.name = "defaultTask",
|
||||
.stack_size = 128 * 4,
|
||||
.priority = (osPriority_t) osPriorityNormal,
|
||||
};
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* USER CODE BEGIN FunctionPrototypes */
|
||||
|
||||
/* USER CODE END FunctionPrototypes */
|
||||
|
||||
void StartDefaultTask(void *argument);
|
||||
|
||||
extern void MX_USB_DEVICE_Init(void);
|
||||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||||
|
||||
/* Hook prototypes */
|
||||
void configureTimerForRunTimeStats(void);
|
||||
unsigned long getRunTimeCounterValue(void);
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName);
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* USER CODE BEGIN 4 */
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
|
||||
{
|
||||
/* Run time stack overflow checking is performed if
|
||||
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
|
||||
called if a stack overflow is detected. */
|
||||
}
|
||||
/* USER CODE END 4 */
|
||||
|
||||
/**
|
||||
* @brief FreeRTOS initialization
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void MX_FREERTOS_Init(void) {
|
||||
/* USER CODE BEGIN Init */
|
||||
|
||||
/* USER CODE END Init */
|
||||
|
||||
/* USER CODE BEGIN RTOS_MUTEX */
|
||||
/* add mutexes, ... */
|
||||
/* USER CODE END RTOS_MUTEX */
|
||||
|
||||
/* USER CODE BEGIN RTOS_SEMAPHORES */
|
||||
/* add semaphores, ... */
|
||||
/* USER CODE END RTOS_SEMAPHORES */
|
||||
|
||||
/* USER CODE BEGIN RTOS_TIMERS */
|
||||
/* start timers, add new ones, ... */
|
||||
/* USER CODE END RTOS_TIMERS */
|
||||
|
||||
/* USER CODE BEGIN RTOS_QUEUES */
|
||||
/* add queues, ... */
|
||||
/* USER CODE END RTOS_QUEUES */
|
||||
|
||||
/* Create the thread(s) */
|
||||
/* creation of defaultTask */
|
||||
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
/* add threads, ... */
|
||||
osThreadNew(Task_Init, NULL, &attr_init); // 创建初始化任务
|
||||
/* USER CODE END RTOS_THREADS */
|
||||
|
||||
/* USER CODE BEGIN RTOS_EVENTS */
|
||||
/* add events, ... */
|
||||
/* USER CODE END RTOS_EVENTS */
|
||||
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN Header_StartDefaultTask */
|
||||
/**
|
||||
* @brief Function implementing the defaultTask thread.
|
||||
* @param argument: Not used
|
||||
* @retval None
|
||||
*/
|
||||
/* USER CODE END Header_StartDefaultTask */
|
||||
void StartDefaultTask(void *argument)
|
||||
{
|
||||
/* init code for USB_DEVICE */
|
||||
MX_USB_DEVICE_Init();
|
||||
/* USER CODE BEGIN StartDefaultTask */
|
||||
osThreadTerminate(osThreadGetId());
|
||||
/* USER CODE END StartDefaultTask */
|
||||
}
|
||||
|
||||
/* Private application code --------------------------------------------------*/
|
||||
/* USER CODE BEGIN Application */
|
||||
|
||||
/* USER CODE END Application */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
|
||||
@ -268,6 +268,20 @@ void DMA1_Stream2_IRQHandler(void)
|
||||
/* USER CODE END DMA1_Stream2_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles CAN1 TX interrupts.
|
||||
*/
|
||||
void CAN1_TX_IRQHandler(void)
|
||||
{
|
||||
/* USER CODE BEGIN CAN1_TX_IRQn 0 */
|
||||
|
||||
/* USER CODE END CAN1_TX_IRQn 0 */
|
||||
HAL_CAN_IRQHandler(&hcan1);
|
||||
/* USER CODE BEGIN CAN1_TX_IRQn 1 */
|
||||
|
||||
/* USER CODE END CAN1_TX_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles CAN1 RX0 interrupts.
|
||||
*/
|
||||
@ -408,6 +422,20 @@ void DMA2_Stream3_IRQHandler(void)
|
||||
/* USER CODE END DMA2_Stream3_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles CAN2 TX interrupts.
|
||||
*/
|
||||
void CAN2_TX_IRQHandler(void)
|
||||
{
|
||||
/* USER CODE BEGIN CAN2_TX_IRQn 0 */
|
||||
|
||||
/* USER CODE END CAN2_TX_IRQn 0 */
|
||||
HAL_CAN_IRQHandler(&hcan2);
|
||||
/* USER CODE BEGIN CAN2_TX_IRQn 1 */
|
||||
|
||||
/* USER CODE END CAN2_TX_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles CAN2 RX0 interrupts.
|
||||
*/
|
||||
|
||||
244
Core/Src/syscalls.c
Normal file
244
Core/Src/syscalls.c
Normal file
@ -0,0 +1,244 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file syscalls.c
|
||||
* @author Auto-generated by STM32CubeMX
|
||||
* @brief Minimal System calls file
|
||||
*
|
||||
* For more information about which c-functions
|
||||
* need which of these lowlevel functions
|
||||
* please consult the Newlib or Picolibc libc-manual
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2020-2025 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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes */
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
|
||||
|
||||
/* Variables */
|
||||
extern int __io_putchar(int ch) __attribute__((weak));
|
||||
extern int __io_getchar(void) __attribute__((weak));
|
||||
|
||||
|
||||
char *__env[1] = { 0 };
|
||||
char **environ = __env;
|
||||
|
||||
|
||||
/* Functions */
|
||||
void initialise_monitor_handles()
|
||||
{
|
||||
}
|
||||
|
||||
int _getpid(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _kill(int pid, int sig)
|
||||
{
|
||||
(void)pid;
|
||||
(void)sig;
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void _exit (int status)
|
||||
{
|
||||
_kill(status, -1);
|
||||
while (1) {} /* Make sure we hang here */
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _read(int file, char *ptr, int len)
|
||||
{
|
||||
(void)file;
|
||||
int DataIdx;
|
||||
|
||||
for (DataIdx = 0; DataIdx < len; DataIdx++)
|
||||
{
|
||||
*ptr++ = __io_getchar();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _write(int file, char *ptr, int len)
|
||||
{
|
||||
(void)file;
|
||||
int DataIdx;
|
||||
|
||||
for (DataIdx = 0; DataIdx < len; DataIdx++)
|
||||
{
|
||||
__io_putchar(*ptr++);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int _close(int file)
|
||||
{
|
||||
(void)file;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int _fstat(int file, struct stat *st)
|
||||
{
|
||||
(void)file;
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _isatty(int file)
|
||||
{
|
||||
(void)file;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _lseek(int file, int ptr, int dir)
|
||||
{
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _open(char *path, int flags, ...)
|
||||
{
|
||||
(void)path;
|
||||
(void)flags;
|
||||
/* Pretend like we always fail */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _wait(int *status)
|
||||
{
|
||||
(void)status;
|
||||
errno = ECHILD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _unlink(char *name)
|
||||
{
|
||||
(void)name;
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
clock_t _times(struct tms *buf)
|
||||
{
|
||||
(void)buf;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _stat(const char *file, struct stat *st)
|
||||
{
|
||||
(void)file;
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _link(char *old, char *new)
|
||||
{
|
||||
(void)old;
|
||||
(void)new;
|
||||
errno = EMLINK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _fork(void)
|
||||
{
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _execve(char *name, char **argv, char **env)
|
||||
{
|
||||
(void)name;
|
||||
(void)argv;
|
||||
(void)env;
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --- Picolibc Specific Section ---
|
||||
#if defined(__PICOLIBC__)
|
||||
|
||||
/**
|
||||
* @brief Picolibc helper function to output a character to a FILE stream.
|
||||
* This redirects the output to the low-level __io_putchar function.
|
||||
* @param c Character to write.
|
||||
* @param file FILE stream pointer (ignored).
|
||||
* @retval int The character written.
|
||||
*/
|
||||
static int starm_putc(char c, FILE *file)
|
||||
{
|
||||
(void) file;
|
||||
__io_putchar(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Picolibc helper function to input a character from a FILE stream.
|
||||
* This redirects the input from the low-level __io_getchar function.
|
||||
* @param file FILE stream pointer (ignored).
|
||||
* @retval int The character read, cast to an unsigned char then int.
|
||||
*/
|
||||
static int starm_getc(FILE *file)
|
||||
{
|
||||
unsigned char c;
|
||||
(void) file;
|
||||
c = __io_getchar();
|
||||
return c;
|
||||
}
|
||||
|
||||
// Define and initialize the standard I/O streams for Picolibc.
|
||||
// FDEV_SETUP_STREAM connects the starm_putc and starm_getc helper functions to a FILE structure.
|
||||
// _FDEV_SETUP_RW indicates the stream is for reading and writing.
|
||||
static FILE __stdio = FDEV_SETUP_STREAM(starm_putc,
|
||||
starm_getc,
|
||||
NULL,
|
||||
_FDEV_SETUP_RW);
|
||||
|
||||
// Assign the standard stream pointers (stdin, stdout, stderr) to the initialized stream.
|
||||
// Picolibc uses these pointers for standard I/O operations (printf, scanf, etc.).
|
||||
FILE *const stdin = &__stdio;
|
||||
__strong_reference(stdin, stdout);
|
||||
__strong_reference(stdin, stderr);
|
||||
|
||||
// Create strong aliases mapping standard C library function names (without underscore)
|
||||
// to the implemented system call stubs (with underscore). Picolibc uses these
|
||||
// standard names internally, so this linking is required.
|
||||
__strong_reference(_read, read);
|
||||
__strong_reference(_write, write);
|
||||
__strong_reference(_times, times);
|
||||
__strong_reference(_execve, execve);
|
||||
__strong_reference(_fork, fork);
|
||||
__strong_reference(_link, link);
|
||||
__strong_reference(_unlink, unlink);
|
||||
__strong_reference(_stat, stat);
|
||||
__strong_reference(_wait, wait);
|
||||
__strong_reference(_open, open);
|
||||
__strong_reference(_close, close);
|
||||
__strong_reference(_lseek, lseek);
|
||||
__strong_reference(_isatty, isatty);
|
||||
__strong_reference(_fstat, fstat);
|
||||
__strong_reference(_exit, exit);
|
||||
__strong_reference(_kill, kill);
|
||||
__strong_reference(_getpid, getpid);
|
||||
|
||||
#endif //__PICOLIBC__
|
||||
87
Core/Src/sysmem.c
Normal file
87
Core/Src/sysmem.c
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file sysmem.c
|
||||
* @author Generated by STM32CubeMX
|
||||
* @brief System Memory calls file
|
||||
*
|
||||
* For more information about which C functions
|
||||
* need which of these lowlevel functions
|
||||
* please consult the Newlib or Picolibc libc manual
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes */
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Pointer to the current high watermark of the heap usage
|
||||
*/
|
||||
static uint8_t *__sbrk_heap_end = NULL;
|
||||
|
||||
/**
|
||||
* @brief _sbrk() allocates memory to the newlib heap and is used by malloc
|
||||
* and others from the C library
|
||||
*
|
||||
* @verbatim
|
||||
* ############################################################################
|
||||
* # .data # .bss # newlib heap # MSP stack #
|
||||
* # # # # Reserved by _Min_Stack_Size #
|
||||
* ############################################################################
|
||||
* ^-- RAM start ^-- _end _estack, RAM end --^
|
||||
* @endverbatim
|
||||
*
|
||||
* This implementation starts allocating at the '_end' linker symbol
|
||||
* The '_Min_Stack_Size' linker symbol reserves a memory for the MSP stack
|
||||
* The implementation considers '_estack' linker symbol to be RAM end
|
||||
* NOTE: If the MSP stack, at any point during execution, grows larger than the
|
||||
* reserved size, please increase the '_Min_Stack_Size'.
|
||||
*
|
||||
* @param incr Memory size
|
||||
* @return Pointer to allocated memory
|
||||
*/
|
||||
void *_sbrk(ptrdiff_t incr)
|
||||
{
|
||||
extern uint8_t _end; /* Symbol defined in the linker script */
|
||||
extern uint8_t _estack; /* Symbol defined in the linker script */
|
||||
extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */
|
||||
const uint32_t stack_limit = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size;
|
||||
const uint8_t *max_heap = (uint8_t *)stack_limit;
|
||||
uint8_t *prev_heap_end;
|
||||
|
||||
/* Initialize heap end at first call */
|
||||
if (NULL == __sbrk_heap_end)
|
||||
{
|
||||
__sbrk_heap_end = &_end;
|
||||
}
|
||||
|
||||
/* Protect heap from growing into the reserved MSP stack */
|
||||
if (__sbrk_heap_end + incr > max_heap)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return (void *)-1;
|
||||
}
|
||||
|
||||
prev_heap_end = __sbrk_heap_end;
|
||||
__sbrk_heap_end += incr;
|
||||
|
||||
return (void *)prev_heap_end;
|
||||
}
|
||||
|
||||
#if defined(__PICOLIBC__)
|
||||
// Picolibc expects syscalls without the leading underscore.
|
||||
// This creates a strong alias so that
|
||||
// calls to `sbrk()` are resolved to our `_sbrk()` implementation.
|
||||
__strong_reference(_sbrk, sbrk);
|
||||
#endif
|
||||
4
DevC.ioc
4
DevC.ioc
@ -267,8 +267,10 @@ MxDb.Version=DB.6.0.20
|
||||
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
|
||||
NVIC.CAN1_RX0_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN1_RX1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN1_TX_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN2_RX0_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN2_RX1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN2_TX_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
|
||||
NVIC.DMA1_Stream1_IRQn=true\:5\:0\:true\:false\:true\:true\:false\:true\:true
|
||||
NVIC.DMA1_Stream2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
|
||||
NVIC.DMA1_Stream7_IRQn=true\:5\:0\:true\:false\:true\:true\:false\:true\:true
|
||||
@ -552,7 +554,7 @@ ProjectManager.ProjectName=DevC
|
||||
ProjectManager.ProjectStructure=
|
||||
ProjectManager.RegisterCallBack=
|
||||
ProjectManager.StackSize=0x1000
|
||||
ProjectManager.TargetToolchain=MDK-ARM V5.32
|
||||
ProjectManager.TargetToolchain=CMake
|
||||
ProjectManager.ToolChainLocation=
|
||||
ProjectManager.UAScriptAfterPath=
|
||||
ProjectManager.UAScriptBeforePath=
|
||||
|
||||
775
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c
vendored
Normal file
775
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c
vendored
Normal file
@ -0,0 +1,775 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V10.3.1
|
||||
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* http://www.FreeRTOS.org
|
||||
* http://aws.amazon.com/freertos
|
||||
*
|
||||
* 1 tab == 4 spaces!
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Implementation of functions defined in portable.h for the ARM CM4F port.
|
||||
*----------------------------------------------------------*/
|
||||
|
||||
/* Scheduler includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#ifndef __VFP_FP__
|
||||
#error This port can only be used when the project options are configured to enable hardware floating point support.
|
||||
#endif
|
||||
|
||||
#ifndef configSYSTICK_CLOCK_HZ
|
||||
#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
|
||||
/* Ensure the SysTick is clocked at the same frequency as the core. */
|
||||
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
|
||||
#else
|
||||
/* The way the SysTick is clocked is not modified in case it is not the same
|
||||
as the core. */
|
||||
#define portNVIC_SYSTICK_CLK_BIT ( 0 )
|
||||
#endif
|
||||
|
||||
/* Constants required to manipulate the core. Registers first... */
|
||||
#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) )
|
||||
#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) )
|
||||
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) )
|
||||
#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
|
||||
/* ...then bits in the registers. */
|
||||
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
|
||||
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
|
||||
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
|
||||
#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL )
|
||||
#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )
|
||||
|
||||
/* Constants used to detect a Cortex-M7 r0p1 core, which should use the ARM_CM7
|
||||
r0p1 port. */
|
||||
#define portCPUID ( * ( ( volatile uint32_t * ) 0xE000ed00 ) )
|
||||
#define portCORTEX_M7_r0p1_ID ( 0x410FC271UL )
|
||||
#define portCORTEX_M7_r0p0_ID ( 0x410FC270UL )
|
||||
|
||||
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
|
||||
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
|
||||
|
||||
/* Constants required to check the validity of an interrupt priority. */
|
||||
#define portFIRST_USER_INTERRUPT_NUMBER ( 16 )
|
||||
#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 )
|
||||
#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) )
|
||||
#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff )
|
||||
#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 )
|
||||
#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 )
|
||||
#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL )
|
||||
#define portPRIGROUP_SHIFT ( 8UL )
|
||||
|
||||
/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */
|
||||
#define portVECTACTIVE_MASK ( 0xFFUL )
|
||||
|
||||
/* Constants required to manipulate the VFP. */
|
||||
#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */
|
||||
#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL )
|
||||
|
||||
/* Constants required to set up the initial stack. */
|
||||
#define portINITIAL_XPSR ( 0x01000000 )
|
||||
#define portINITIAL_EXC_RETURN ( 0xfffffffd )
|
||||
|
||||
/* The systick is a 24-bit counter. */
|
||||
#define portMAX_24_BIT_NUMBER ( 0xffffffUL )
|
||||
|
||||
/* For strict compliance with the Cortex-M spec the task start address should
|
||||
have bit-0 clear, as it is loaded into the PC on exit from an ISR. */
|
||||
#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )
|
||||
|
||||
/* A fiddle factor to estimate the number of SysTick counts that would have
|
||||
occurred while the SysTick counter is stopped during tickless idle
|
||||
calculations. */
|
||||
#define portMISSED_COUNTS_FACTOR ( 45UL )
|
||||
|
||||
/* Let the user override the pre-loading of the initial LR with the address of
|
||||
prvTaskExitError() in case it messes up unwinding of the stack in the
|
||||
debugger. */
|
||||
#ifdef configTASK_RETURN_ADDRESS
|
||||
#define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
|
||||
#else
|
||||
#define portTASK_RETURN_ADDRESS prvTaskExitError
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Setup the timer to generate the tick interrupts. The implementation in this
|
||||
* file is weak to allow application writers to change the timer used to
|
||||
* generate the tick interrupt.
|
||||
*/
|
||||
void vPortSetupTimerInterrupt( void );
|
||||
|
||||
/*
|
||||
* Exception handlers.
|
||||
*/
|
||||
void xPortPendSVHandler( void ) __attribute__ (( naked ));
|
||||
void xPortSysTickHandler( void );
|
||||
void vPortSVCHandler( void ) __attribute__ (( naked ));
|
||||
|
||||
/*
|
||||
* Start first task is a separate function so it can be tested in isolation.
|
||||
*/
|
||||
static void prvPortStartFirstTask( void ) __attribute__ (( naked ));
|
||||
|
||||
/*
|
||||
* Function to enable the VFP.
|
||||
*/
|
||||
static void vPortEnableVFP( void ) __attribute__ (( naked ));
|
||||
|
||||
/*
|
||||
* Used to catch tasks that attempt to return from their implementing function.
|
||||
*/
|
||||
static void prvTaskExitError( void );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Each task maintains its own interrupt status in the critical nesting
|
||||
variable. */
|
||||
static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
|
||||
|
||||
/*
|
||||
* The number of SysTick increments that make up one tick period.
|
||||
*/
|
||||
#if( configUSE_TICKLESS_IDLE == 1 )
|
||||
static uint32_t ulTimerCountsForOneTick = 0;
|
||||
#endif /* configUSE_TICKLESS_IDLE */
|
||||
|
||||
/*
|
||||
* The maximum number of tick periods that can be suppressed is limited by the
|
||||
* 24 bit resolution of the SysTick timer.
|
||||
*/
|
||||
#if( configUSE_TICKLESS_IDLE == 1 )
|
||||
static uint32_t xMaximumPossibleSuppressedTicks = 0;
|
||||
#endif /* configUSE_TICKLESS_IDLE */
|
||||
|
||||
/*
|
||||
* Compensate for the CPU cycles that pass while the SysTick is stopped (low
|
||||
* power functionality only.
|
||||
*/
|
||||
#if( configUSE_TICKLESS_IDLE == 1 )
|
||||
static uint32_t ulStoppedTimerCompensation = 0;
|
||||
#endif /* configUSE_TICKLESS_IDLE */
|
||||
|
||||
/*
|
||||
* Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure
|
||||
* FreeRTOS API functions are not called from interrupts that have been assigned
|
||||
* a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY.
|
||||
*/
|
||||
#if( configASSERT_DEFINED == 1 )
|
||||
static uint8_t ucMaxSysCallPriority = 0;
|
||||
static uint32_t ulMaxPRIGROUPValue = 0;
|
||||
static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16;
|
||||
#endif /* configASSERT_DEFINED */
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* See header file for description.
|
||||
*/
|
||||
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
|
||||
{
|
||||
/* Simulate the stack frame as it would be created by a context switch
|
||||
interrupt. */
|
||||
|
||||
/* Offset added to account for the way the MCU uses the stack on entry/exit
|
||||
of interrupts, and to ensure alignment. */
|
||||
pxTopOfStack--;
|
||||
|
||||
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
|
||||
pxTopOfStack--;
|
||||
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
|
||||
pxTopOfStack--;
|
||||
*pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */
|
||||
|
||||
/* Save code space by skipping register initialisation. */
|
||||
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
|
||||
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
|
||||
|
||||
/* A save method is being used that requires each task to maintain its
|
||||
own exec return value. */
|
||||
pxTopOfStack--;
|
||||
*pxTopOfStack = portINITIAL_EXC_RETURN;
|
||||
|
||||
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
|
||||
|
||||
return pxTopOfStack;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvTaskExitError( void )
|
||||
{
|
||||
volatile uint32_t ulDummy = 0;
|
||||
|
||||
/* A function that implements a task must not exit or attempt to return to
|
||||
its caller as there is nothing to return to. If a task wants to exit it
|
||||
should instead call vTaskDelete( NULL ).
|
||||
|
||||
Artificially force an assert() to be triggered if configASSERT() is
|
||||
defined, then stop here so application writers can catch the error. */
|
||||
configASSERT( uxCriticalNesting == ~0UL );
|
||||
portDISABLE_INTERRUPTS();
|
||||
while( ulDummy == 0 )
|
||||
{
|
||||
/* This file calls prvTaskExitError() after the scheduler has been
|
||||
started to remove a compiler warning about the function being defined
|
||||
but never called. ulDummy is used purely to quieten other warnings
|
||||
about code appearing after this function is called - making ulDummy
|
||||
volatile makes the compiler think the function could return and
|
||||
therefore not output an 'unreachable code' warning for code that appears
|
||||
after it. */
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortSVCHandler( void )
|
||||
{
|
||||
__asm volatile (
|
||||
" ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */
|
||||
" ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
|
||||
" ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */
|
||||
" ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
|
||||
" msr psp, r0 \n" /* Restore the task stack pointer. */
|
||||
" isb \n"
|
||||
" mov r0, #0 \n"
|
||||
" msr basepri, r0 \n"
|
||||
" bx r14 \n"
|
||||
" \n"
|
||||
" .align 4 \n"
|
||||
"pxCurrentTCBConst2: .word pxCurrentTCB \n"
|
||||
);
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvPortStartFirstTask( void )
|
||||
{
|
||||
/* Start the first task. This also clears the bit that indicates the FPU is
|
||||
in use in case the FPU was used before the scheduler was started - which
|
||||
would otherwise result in the unnecessary leaving of space in the SVC stack
|
||||
for lazy saving of FPU registers. */
|
||||
__asm volatile(
|
||||
" ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */
|
||||
" ldr r0, [r0] \n"
|
||||
" ldr r0, [r0] \n"
|
||||
" msr msp, r0 \n" /* Set the msp back to the start of the stack. */
|
||||
" mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */
|
||||
" msr control, r0 \n"
|
||||
" cpsie i \n" /* Globally enable interrupts. */
|
||||
" cpsie f \n"
|
||||
" dsb \n"
|
||||
" isb \n"
|
||||
" svc 0 \n" /* System call to start first task. */
|
||||
" nop \n"
|
||||
);
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* See header file for description.
|
||||
*/
|
||||
BaseType_t xPortStartScheduler( void )
|
||||
{
|
||||
/* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.
|
||||
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
|
||||
configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY );
|
||||
|
||||
/* This port can be used on all revisions of the Cortex-M7 core other than
|
||||
the r0p1 parts. r0p1 parts should use the port from the
|
||||
/source/portable/GCC/ARM_CM7/r0p1 directory. */
|
||||
configASSERT( portCPUID != portCORTEX_M7_r0p1_ID );
|
||||
configASSERT( portCPUID != portCORTEX_M7_r0p0_ID );
|
||||
|
||||
#if( configASSERT_DEFINED == 1 )
|
||||
{
|
||||
volatile uint32_t ulOriginalPriority;
|
||||
volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
|
||||
volatile uint8_t ucMaxPriorityValue;
|
||||
|
||||
/* Determine the maximum priority from which ISR safe FreeRTOS API
|
||||
functions can be called. ISR safe functions are those that end in
|
||||
"FromISR". FreeRTOS maintains separate thread and ISR API functions to
|
||||
ensure interrupt entry is as fast and simple as possible.
|
||||
|
||||
Save the interrupt priority value that is about to be clobbered. */
|
||||
ulOriginalPriority = *pucFirstUserPriorityRegister;
|
||||
|
||||
/* Determine the number of priority bits available. First write to all
|
||||
possible bits. */
|
||||
*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
|
||||
|
||||
/* Read the value back to see how many bits stuck. */
|
||||
ucMaxPriorityValue = *pucFirstUserPriorityRegister;
|
||||
|
||||
/* Use the same mask on the maximum system call priority. */
|
||||
ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;
|
||||
|
||||
/* Calculate the maximum acceptable priority group value for the number
|
||||
of bits read back. */
|
||||
ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
|
||||
while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
|
||||
{
|
||||
ulMaxPRIGROUPValue--;
|
||||
ucMaxPriorityValue <<= ( uint8_t ) 0x01;
|
||||
}
|
||||
|
||||
#ifdef __NVIC_PRIO_BITS
|
||||
{
|
||||
/* Check the CMSIS configuration that defines the number of
|
||||
priority bits matches the number of priority bits actually queried
|
||||
from the hardware. */
|
||||
configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef configPRIO_BITS
|
||||
{
|
||||
/* Check the FreeRTOS configuration that defines the number of
|
||||
priority bits matches the number of priority bits actually queried
|
||||
from the hardware. */
|
||||
configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS );
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Shift the priority group value back to its position within the AIRCR
|
||||
register. */
|
||||
ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
|
||||
ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
|
||||
|
||||
/* Restore the clobbered interrupt priority register to its original
|
||||
value. */
|
||||
*pucFirstUserPriorityRegister = ulOriginalPriority;
|
||||
}
|
||||
#endif /* conifgASSERT_DEFINED */
|
||||
|
||||
/* Make PendSV and SysTick the lowest priority interrupts. */
|
||||
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
|
||||
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
|
||||
|
||||
/* Start the timer that generates the tick ISR. Interrupts are disabled
|
||||
here already. */
|
||||
vPortSetupTimerInterrupt();
|
||||
|
||||
/* Initialise the critical nesting count ready for the first task. */
|
||||
uxCriticalNesting = 0;
|
||||
|
||||
/* Ensure the VFP is enabled - it should be anyway. */
|
||||
vPortEnableVFP();
|
||||
|
||||
/* Lazy save always. */
|
||||
*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;
|
||||
|
||||
/* Start the first task. */
|
||||
prvPortStartFirstTask();
|
||||
|
||||
/* Should never get here as the tasks will now be executing! Call the task
|
||||
exit error function to prevent compiler warnings about a static function
|
||||
not being called in the case that the application writer overrides this
|
||||
functionality by defining configTASK_RETURN_ADDRESS. Call
|
||||
vTaskSwitchContext() so link time optimisation does not remove the
|
||||
symbol. */
|
||||
vTaskSwitchContext();
|
||||
prvTaskExitError();
|
||||
|
||||
/* Should not get here! */
|
||||
return 0;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortEndScheduler( void )
|
||||
{
|
||||
/* Not implemented in ports where there is nothing to return to.
|
||||
Artificially force an assert. */
|
||||
configASSERT( uxCriticalNesting == 1000UL );
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortEnterCritical( void )
|
||||
{
|
||||
portDISABLE_INTERRUPTS();
|
||||
uxCriticalNesting++;
|
||||
|
||||
/* This is not the interrupt safe version of the enter critical function so
|
||||
assert() if it is being called from an interrupt context. Only API
|
||||
functions that end in "FromISR" can be used in an interrupt. Only assert if
|
||||
the critical nesting count is 1 to protect against recursive calls if the
|
||||
assert function also uses a critical section. */
|
||||
if( uxCriticalNesting == 1 )
|
||||
{
|
||||
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortExitCritical( void )
|
||||
{
|
||||
configASSERT( uxCriticalNesting );
|
||||
uxCriticalNesting--;
|
||||
if( uxCriticalNesting == 0 )
|
||||
{
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void xPortPendSVHandler( void )
|
||||
{
|
||||
/* This is a naked function. */
|
||||
|
||||
__asm volatile
|
||||
(
|
||||
" mrs r0, psp \n"
|
||||
" isb \n"
|
||||
" \n"
|
||||
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
|
||||
" ldr r2, [r3] \n"
|
||||
" \n"
|
||||
" tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */
|
||||
" it eq \n"
|
||||
" vstmdbeq r0!, {s16-s31} \n"
|
||||
" \n"
|
||||
" stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */
|
||||
" str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */
|
||||
" \n"
|
||||
" stmdb sp!, {r0, r3} \n"
|
||||
" mov r0, %0 \n"
|
||||
" msr basepri, r0 \n"
|
||||
" dsb \n"
|
||||
" isb \n"
|
||||
" bl vTaskSwitchContext \n"
|
||||
" mov r0, #0 \n"
|
||||
" msr basepri, r0 \n"
|
||||
" ldmia sp!, {r0, r3} \n"
|
||||
" \n"
|
||||
" ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */
|
||||
" ldr r0, [r1] \n"
|
||||
" \n"
|
||||
" ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */
|
||||
" \n"
|
||||
" tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */
|
||||
" it eq \n"
|
||||
" vldmiaeq r0!, {s16-s31} \n"
|
||||
" \n"
|
||||
" msr psp, r0 \n"
|
||||
" isb \n"
|
||||
" \n"
|
||||
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
|
||||
#if WORKAROUND_PMU_CM001 == 1
|
||||
" push { r14 } \n"
|
||||
" pop { pc } \n"
|
||||
#endif
|
||||
#endif
|
||||
" \n"
|
||||
" bx r14 \n"
|
||||
" \n"
|
||||
" .align 4 \n"
|
||||
"pxCurrentTCBConst: .word pxCurrentTCB \n"
|
||||
::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
|
||||
);
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void xPortSysTickHandler( void )
|
||||
{
|
||||
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
|
||||
executes all interrupts must be unmasked. There is therefore no need to
|
||||
save and then restore the interrupt mask value as its value is already
|
||||
known. */
|
||||
portDISABLE_INTERRUPTS();
|
||||
{
|
||||
/* Increment the RTOS tick. */
|
||||
if( xTaskIncrementTick() != pdFALSE )
|
||||
{
|
||||
/* A context switch is required. Context switching is performed in
|
||||
the PendSV interrupt. Pend the PendSV interrupt. */
|
||||
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
|
||||
}
|
||||
}
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if( configUSE_TICKLESS_IDLE == 1 )
|
||||
|
||||
__attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
|
||||
{
|
||||
uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements;
|
||||
TickType_t xModifiableIdleTime;
|
||||
|
||||
/* Make sure the SysTick reload value does not overflow the counter. */
|
||||
if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
|
||||
{
|
||||
xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
|
||||
}
|
||||
|
||||
/* Stop the SysTick momentarily. The time the SysTick is stopped for
|
||||
is accounted for as best it can be, but using the tickless mode will
|
||||
inevitably result in some tiny drift of the time maintained by the
|
||||
kernel with respect to calendar time. */
|
||||
portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;
|
||||
|
||||
/* Calculate the reload value required to wait xExpectedIdleTime
|
||||
tick periods. -1 is used because this code will execute part way
|
||||
through one of the tick periods. */
|
||||
ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
|
||||
if( ulReloadValue > ulStoppedTimerCompensation )
|
||||
{
|
||||
ulReloadValue -= ulStoppedTimerCompensation;
|
||||
}
|
||||
|
||||
/* Enter a critical section but don't use the taskENTER_CRITICAL()
|
||||
method as that will mask interrupts that should exit sleep mode. */
|
||||
__asm volatile( "cpsid i" ::: "memory" );
|
||||
__asm volatile( "dsb" );
|
||||
__asm volatile( "isb" );
|
||||
|
||||
/* If a context switch is pending or a task is waiting for the scheduler
|
||||
to be unsuspended then abandon the low power entry. */
|
||||
if( eTaskConfirmSleepModeStatus() == eAbortSleep )
|
||||
{
|
||||
/* Restart from whatever is left in the count register to complete
|
||||
this tick period. */
|
||||
portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;
|
||||
|
||||
/* Restart SysTick. */
|
||||
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
|
||||
|
||||
/* Reset the reload register to the value required for normal tick
|
||||
periods. */
|
||||
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
|
||||
|
||||
/* Re-enable interrupts - see comments above the cpsid instruction()
|
||||
above. */
|
||||
__asm volatile( "cpsie i" ::: "memory" );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the new reload value. */
|
||||
portNVIC_SYSTICK_LOAD_REG = ulReloadValue;
|
||||
|
||||
/* Clear the SysTick count flag and set the count value back to
|
||||
zero. */
|
||||
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
|
||||
|
||||
/* Restart SysTick. */
|
||||
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
|
||||
|
||||
/* Sleep until something happens. configPRE_SLEEP_PROCESSING() can
|
||||
set its parameter to 0 to indicate that its implementation contains
|
||||
its own wait for interrupt or wait for event instruction, and so wfi
|
||||
should not be executed again. However, the original expected idle
|
||||
time variable must remain unmodified, so a copy is taken. */
|
||||
xModifiableIdleTime = xExpectedIdleTime;
|
||||
configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
|
||||
if( xModifiableIdleTime > 0 )
|
||||
{
|
||||
__asm volatile( "dsb" ::: "memory" );
|
||||
__asm volatile( "wfi" );
|
||||
__asm volatile( "isb" );
|
||||
}
|
||||
configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
|
||||
|
||||
/* Re-enable interrupts to allow the interrupt that brought the MCU
|
||||
out of sleep mode to execute immediately. see comments above
|
||||
__disable_interrupt() call above. */
|
||||
__asm volatile( "cpsie i" ::: "memory" );
|
||||
__asm volatile( "dsb" );
|
||||
__asm volatile( "isb" );
|
||||
|
||||
/* Disable interrupts again because the clock is about to be stopped
|
||||
and interrupts that execute while the clock is stopped will increase
|
||||
any slippage between the time maintained by the RTOS and calendar
|
||||
time. */
|
||||
__asm volatile( "cpsid i" ::: "memory" );
|
||||
__asm volatile( "dsb" );
|
||||
__asm volatile( "isb" );
|
||||
|
||||
/* Disable the SysTick clock without reading the
|
||||
portNVIC_SYSTICK_CTRL_REG register to ensure the
|
||||
portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again,
|
||||
the time the SysTick is stopped for is accounted for as best it can
|
||||
be, but using the tickless mode will inevitably result in some tiny
|
||||
drift of the time maintained by the kernel with respect to calendar
|
||||
time*/
|
||||
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );
|
||||
|
||||
/* Determine if the SysTick clock has already counted to zero and
|
||||
been set back to the current reload value (the reload back being
|
||||
correct for the entire expected idle time) or if the SysTick is yet
|
||||
to count to zero (in which case an interrupt other than the SysTick
|
||||
must have brought the system out of sleep mode). */
|
||||
if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
|
||||
{
|
||||
uint32_t ulCalculatedLoadValue;
|
||||
|
||||
/* The tick interrupt is already pending, and the SysTick count
|
||||
reloaded with ulReloadValue. Reset the
|
||||
portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick
|
||||
period. */
|
||||
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );
|
||||
|
||||
/* Don't allow a tiny value, or values that have somehow
|
||||
underflowed because the post sleep hook did something
|
||||
that took too long. */
|
||||
if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
|
||||
{
|
||||
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
|
||||
}
|
||||
|
||||
portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;
|
||||
|
||||
/* As the pending tick will be processed as soon as this
|
||||
function exits, the tick value maintained by the tick is stepped
|
||||
forward by one less than the time spent waiting. */
|
||||
ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Something other than the tick interrupt ended the sleep.
|
||||
Work out how long the sleep lasted rounded to complete tick
|
||||
periods (not the ulReload value which accounted for part
|
||||
ticks). */
|
||||
ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;
|
||||
|
||||
/* How many complete tick periods passed while the processor
|
||||
was waiting? */
|
||||
ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;
|
||||
|
||||
/* The reload value is set to whatever fraction of a single tick
|
||||
period remains. */
|
||||
portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
|
||||
}
|
||||
|
||||
/* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
|
||||
again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
|
||||
value. */
|
||||
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
|
||||
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
|
||||
vTaskStepTick( ulCompleteTickPeriods );
|
||||
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
|
||||
|
||||
/* Exit with interrupts enabled. */
|
||||
__asm volatile( "cpsie i" ::: "memory" );
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if configUSE_TICKLESS_IDLE */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Setup the systick timer to generate the tick interrupts at the required
|
||||
* frequency.
|
||||
*/
|
||||
__attribute__(( weak )) void vPortSetupTimerInterrupt( void )
|
||||
{
|
||||
/* Calculate the constants required to configure the tick interrupt. */
|
||||
#if( configUSE_TICKLESS_IDLE == 1 )
|
||||
{
|
||||
ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
|
||||
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
|
||||
ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
|
||||
}
|
||||
#endif /* configUSE_TICKLESS_IDLE */
|
||||
|
||||
/* Stop and clear the SysTick. */
|
||||
portNVIC_SYSTICK_CTRL_REG = 0UL;
|
||||
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
|
||||
|
||||
/* Configure SysTick to interrupt at the requested rate. */
|
||||
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
|
||||
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* This is a naked function. */
|
||||
static void vPortEnableVFP( void )
|
||||
{
|
||||
__asm volatile
|
||||
(
|
||||
" ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */
|
||||
" ldr r1, [r0] \n"
|
||||
" \n"
|
||||
" orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */
|
||||
" str r1, [r0] \n"
|
||||
" bx r14 "
|
||||
);
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if( configASSERT_DEFINED == 1 )
|
||||
|
||||
void vPortValidateInterruptPriority( void )
|
||||
{
|
||||
uint32_t ulCurrentInterrupt;
|
||||
uint8_t ucCurrentPriority;
|
||||
|
||||
/* Obtain the number of the currently executing interrupt. */
|
||||
__asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" );
|
||||
|
||||
/* Is the interrupt number a user defined interrupt? */
|
||||
if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
|
||||
{
|
||||
/* Look up the interrupt's priority. */
|
||||
ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];
|
||||
|
||||
/* The following assertion will fail if a service routine (ISR) for
|
||||
an interrupt that has been assigned a priority above
|
||||
configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API
|
||||
function. ISR safe FreeRTOS API functions must *only* be called
|
||||
from interrupts that have been assigned a priority at or below
|
||||
configMAX_SYSCALL_INTERRUPT_PRIORITY.
|
||||
|
||||
Numerically low interrupt priority numbers represent logically high
|
||||
interrupt priorities, therefore the priority of the interrupt must
|
||||
be set to a value equal to or numerically *higher* than
|
||||
configMAX_SYSCALL_INTERRUPT_PRIORITY.
|
||||
|
||||
Interrupts that use the FreeRTOS API must not be left at their
|
||||
default priority of zero as that is the highest possible priority,
|
||||
which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY,
|
||||
and therefore also guaranteed to be invalid.
|
||||
|
||||
FreeRTOS maintains separate thread and ISR API functions to ensure
|
||||
interrupt entry is as fast and simple as possible.
|
||||
|
||||
The following links provide detailed information:
|
||||
http://www.freertos.org/RTOS-Cortex-M3-M4.html
|
||||
http://www.freertos.org/FAQHelp.html */
|
||||
configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
|
||||
}
|
||||
|
||||
/* Priority grouping: The interrupt controller (NVIC) allows the bits
|
||||
that define each interrupt's priority to be split between bits that
|
||||
define the interrupt's pre-emption priority bits and bits that define
|
||||
the interrupt's sub-priority. For simplicity all bits must be defined
|
||||
to be pre-emption priority bits. The following assertion will fail if
|
||||
this is not the case (if some bits represent a sub-priority).
|
||||
|
||||
If the application only uses CMSIS libraries for interrupt
|
||||
configuration then the correct setting can be achieved on all Cortex-M
|
||||
devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the
|
||||
scheduler. Note however that some vendor specific peripheral libraries
|
||||
assume a non-zero priority group setting, in which cases using a value
|
||||
of zero will result in unpredictable behaviour. */
|
||||
configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue );
|
||||
}
|
||||
|
||||
#endif /* configASSERT_DEFINED */
|
||||
|
||||
|
||||
243
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h
vendored
Normal file
243
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V10.3.1
|
||||
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* http://www.FreeRTOS.org
|
||||
* http://aws.amazon.com/freertos
|
||||
*
|
||||
* 1 tab == 4 spaces!
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Port specific definitions.
|
||||
*
|
||||
* The settings in this file configure FreeRTOS correctly for the
|
||||
* given hardware and compiler.
|
||||
*
|
||||
* These settings should not be altered.
|
||||
*-----------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Type definitions. */
|
||||
#define portCHAR char
|
||||
#define portFLOAT float
|
||||
#define portDOUBLE double
|
||||
#define portLONG long
|
||||
#define portSHORT short
|
||||
#define portSTACK_TYPE uint32_t
|
||||
#define portBASE_TYPE long
|
||||
|
||||
typedef portSTACK_TYPE StackType_t;
|
||||
typedef long BaseType_t;
|
||||
typedef unsigned long UBaseType_t;
|
||||
|
||||
#if( configUSE_16_BIT_TICKS == 1 )
|
||||
typedef uint16_t TickType_t;
|
||||
#define portMAX_DELAY ( TickType_t ) 0xffff
|
||||
#else
|
||||
typedef uint32_t TickType_t;
|
||||
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
|
||||
|
||||
/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
|
||||
not need to be guarded with a critical section. */
|
||||
#define portTICK_TYPE_IS_ATOMIC 1
|
||||
#endif
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Architecture specifics. */
|
||||
#define portSTACK_GROWTH ( -1 )
|
||||
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
|
||||
#define portBYTE_ALIGNMENT 8
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Scheduler utilities. */
|
||||
#define portYIELD() \
|
||||
{ \
|
||||
/* Set a PendSV to request a context switch. */ \
|
||||
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
|
||||
\
|
||||
/* Barriers are normally not required but do ensure the code is completely \
|
||||
within the specified behaviour for the architecture. */ \
|
||||
__asm volatile( "dsb" ::: "memory" ); \
|
||||
__asm volatile( "isb" ); \
|
||||
}
|
||||
|
||||
#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )
|
||||
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
|
||||
#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
|
||||
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Critical section management. */
|
||||
extern void vPortEnterCritical( void );
|
||||
extern void vPortExitCritical( void );
|
||||
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
|
||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)
|
||||
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
|
||||
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0)
|
||||
#define portENTER_CRITICAL() vPortEnterCritical()
|
||||
#define portEXIT_CRITICAL() vPortExitCritical()
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Task function macros as described on the FreeRTOS.org WEB site. These are
|
||||
not necessary for to use this port. They are defined so the common demo files
|
||||
(which build with all the ports) will build. */
|
||||
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters )
|
||||
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Tickless idle/low power functionality. */
|
||||
#ifndef portSUPPRESS_TICKS_AND_SLEEP
|
||||
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
|
||||
#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime )
|
||||
#endif
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Architecture specific optimisations. */
|
||||
#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
|
||||
#endif
|
||||
|
||||
#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
|
||||
|
||||
/* Generic helper function. */
|
||||
__attribute__( ( always_inline ) ) static inline uint8_t ucPortCountLeadingZeros( uint32_t ulBitmap )
|
||||
{
|
||||
uint8_t ucReturn;
|
||||
|
||||
__asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) : "memory" );
|
||||
return ucReturn;
|
||||
}
|
||||
|
||||
/* Check the configuration. */
|
||||
#if( configMAX_PRIORITIES > 32 )
|
||||
#error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
|
||||
#endif
|
||||
|
||||
/* Store/clear the ready priorities in a bit map. */
|
||||
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
|
||||
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) ucPortCountLeadingZeros( ( uxReadyPriorities ) ) )
|
||||
|
||||
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#ifdef configASSERT
|
||||
void vPortValidateInterruptPriority( void );
|
||||
#define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority()
|
||||
#endif
|
||||
|
||||
/* portNOP() is not required by this port. */
|
||||
#define portNOP()
|
||||
|
||||
#define portINLINE __inline
|
||||
|
||||
#ifndef portFORCE_INLINE
|
||||
#define portFORCE_INLINE inline __attribute__(( always_inline))
|
||||
#endif
|
||||
|
||||
portFORCE_INLINE static BaseType_t xPortIsInsideInterrupt( void )
|
||||
{
|
||||
uint32_t ulCurrentInterrupt;
|
||||
BaseType_t xReturn;
|
||||
|
||||
/* Obtain the number of the currently executing interrupt. */
|
||||
__asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" );
|
||||
|
||||
if( ulCurrentInterrupt == 0 )
|
||||
{
|
||||
xReturn = pdFALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
portFORCE_INLINE static void vPortRaiseBASEPRI( void )
|
||||
{
|
||||
uint32_t ulNewBASEPRI;
|
||||
|
||||
__asm volatile
|
||||
(
|
||||
" mov %0, %1 \n" \
|
||||
" msr basepri, %0 \n" \
|
||||
" isb \n" \
|
||||
" dsb \n" \
|
||||
:"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI( void )
|
||||
{
|
||||
uint32_t ulOriginalBASEPRI, ulNewBASEPRI;
|
||||
|
||||
__asm volatile
|
||||
(
|
||||
" mrs %0, basepri \n" \
|
||||
" mov %1, %2 \n" \
|
||||
" msr basepri, %1 \n" \
|
||||
" isb \n" \
|
||||
" dsb \n" \
|
||||
:"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory"
|
||||
);
|
||||
|
||||
/* This return will not be reached but is necessary to prevent compiler
|
||||
warnings. */
|
||||
return ulOriginalBASEPRI;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue )
|
||||
{
|
||||
__asm volatile
|
||||
(
|
||||
" msr basepri, %0 " :: "r" ( ulNewMaskValue ) : "memory"
|
||||
);
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" )
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
|
||||
269
STM32F407XX_FLASH.ld
Normal file
269
STM32F407XX_FLASH.ld
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Author : STM32CubeMX
|
||||
**
|
||||
** Abstract : Linker script for STM32F407IGHx series
|
||||
** 1024Kbytes FLASH and 192Kbytes RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
** Distribution: The file is distributed “as is,” without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
*****************************************************************************
|
||||
** @attention
|
||||
**
|
||||
** <h2><center>© COPYRIGHT(c) 2025 STMicroelectronics</center></h2>
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
** 1. Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
** 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
** may be used to endorse or promote products derived from this software
|
||||
** without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
|
||||
}
|
||||
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM */
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x1000; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x1000; /* required amount of stack */
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
_siccmram = LOADADDR(.ccmram);
|
||||
|
||||
/* CCM-RAM section
|
||||
*
|
||||
* IMPORTANT NOTE!
|
||||
* If initialized variables will be placed in this section,
|
||||
* the startup code needs to be modified to copy the init-values.
|
||||
*/
|
||||
.ccmram :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sccmram = .; /* create a global symbol at ccmram start */
|
||||
*(.ccmram)
|
||||
*(.ccmram*)
|
||||
|
||||
. = ALIGN(4);
|
||||
_eccmram = .; /* create a global symbol at ccmram end */
|
||||
} >CCMRAM AT> FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
*(.RamFunc) /* .RamFunc sections */
|
||||
*(.RamFunc*) /* .RamFunc* sections */
|
||||
|
||||
. = ALIGN(4);
|
||||
} >RAM AT> FLASH
|
||||
|
||||
/* Initialized TLS data section */
|
||||
.tdata : ALIGN(4)
|
||||
{
|
||||
*(.tdata .tdata.* .gnu.linkonce.td.*)
|
||||
. = ALIGN(4);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
PROVIDE(__data_end = .);
|
||||
PROVIDE(__tdata_end = .);
|
||||
} >RAM AT> FLASH
|
||||
|
||||
PROVIDE( __tdata_start = ADDR(.tdata) );
|
||||
PROVIDE( __tdata_size = __tdata_end - __tdata_start );
|
||||
|
||||
PROVIDE( __data_start = ADDR(.data) );
|
||||
PROVIDE( __data_size = __data_end - __data_start );
|
||||
|
||||
PROVIDE( __tdata_source = LOADADDR(.tdata) );
|
||||
PROVIDE( __tdata_source_end = LOADADDR(.tdata) + SIZEOF(.tdata) );
|
||||
PROVIDE( __tdata_source_size = __tdata_source_end - __tdata_source );
|
||||
|
||||
PROVIDE( __data_source = LOADADDR(.data) );
|
||||
PROVIDE( __data_source_end = __tdata_source_end );
|
||||
PROVIDE( __data_source_size = __data_source_end - __data_source );
|
||||
/* Uninitialized data section */
|
||||
.tbss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.tbss .tbss.*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( __tbss_end = . );
|
||||
} >RAM
|
||||
|
||||
PROVIDE( __tbss_start = ADDR(.tbss) );
|
||||
PROVIDE( __tbss_size = __tbss_end - __tbss_start );
|
||||
PROVIDE( __tbss_offset = ADDR(.tbss) - ADDR(.tdata) );
|
||||
|
||||
PROVIDE( __tls_base = __tdata_start );
|
||||
PROVIDE( __tls_end = __tbss_end );
|
||||
PROVIDE( __tls_size = __tls_end - __tls_base );
|
||||
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
|
||||
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1) );
|
||||
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
|
||||
PROVIDE( __arm64_tls_tcb_offset = MAX(16, __tls_align) );
|
||||
|
||||
.bss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
PROVIDE( __bss_end = .);
|
||||
} >RAM
|
||||
PROVIDE( __non_tls_bss_start = ADDR(.bss) );
|
||||
|
||||
PROVIDE( __bss_start = __tbss_start );
|
||||
PROVIDE( __bss_size = __bss_end - __bss_start );
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} >RAM
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a:* ( * )
|
||||
libm.a:* ( * )
|
||||
libgcc.a:* ( * )
|
||||
}
|
||||
|
||||
}
|
||||
28
User/bsp/bsp.h
Normal file
28
User/bsp/bsp.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
#define BSP_OK (0)
|
||||
#define BSP_ERR (-1)
|
||||
#define BSP_ERR_NULL (-2)
|
||||
#define BSP_ERR_INITED (-3)
|
||||
#define BSP_ERR_NO_DEV (-4)
|
||||
#define BSP_ERR_TIMEOUT (-5)
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
11
User/bsp/bsp_config.yaml
Normal file
11
User/bsp/bsp_config.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
can:
|
||||
devices:
|
||||
- instance: CAN1
|
||||
name: '1'
|
||||
- instance: CAN2
|
||||
name: '2'
|
||||
enabled: true
|
||||
mm:
|
||||
enabled: true
|
||||
time:
|
||||
enabled: true
|
||||
708
User/bsp/can.c
Normal file
708
User/bsp/can.c
Normal file
@ -0,0 +1,708 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/can.h"
|
||||
#include "bsp/bsp.h"
|
||||
#include <can.h>
|
||||
#include <cmsis_os2.h>
|
||||
#include <string.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define CAN_QUEUE_MUTEX_TIMEOUT 100 /* 队列互斥锁超时时间(ms) */
|
||||
#define CAN_TX_MAILBOX_NUM 3 /* CAN发送邮箱数量 */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
typedef struct BSP_CAN_QueueNode {
|
||||
BSP_CAN_t can; /* CAN通道 */
|
||||
uint32_t can_id; /* 解析后的CAN ID */
|
||||
osMessageQueueId_t queue; /* 消息队列ID */
|
||||
uint8_t queue_size; /* 队列大小 */
|
||||
struct BSP_CAN_QueueNode *next; /* 指向下一个节点的指针 */
|
||||
} BSP_CAN_QueueNode_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static BSP_CAN_QueueNode_t *queue_list = NULL;
|
||||
static osMutexId_t queue_mutex = NULL;
|
||||
static void (*CAN_Callback[BSP_CAN_NUM][BSP_CAN_CB_NUM])(void);
|
||||
static bool inited = false;
|
||||
static BSP_CAN_IdParser_t id_parser = NULL; /* ID解析器 */
|
||||
static BSP_CAN_TxQueue_t tx_queues[BSP_CAN_NUM]; /* 每个CAN的发送队列 */
|
||||
|
||||
/* Private function prototypes ---------------------------------------------- */
|
||||
static BSP_CAN_t CAN_Get(CAN_HandleTypeDef *hcan);
|
||||
static osMessageQueueId_t BSP_CAN_FindQueue(BSP_CAN_t can, uint32_t can_id);
|
||||
static int8_t BSP_CAN_CreateIdQueue(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size);
|
||||
static void BSP_CAN_RxFifo0Callback(void);
|
||||
static void BSP_CAN_RxFifo1Callback(void);
|
||||
static void BSP_CAN_TxCompleteCallback(void);
|
||||
static BSP_CAN_FrameType_t BSP_CAN_GetFrameType(CAN_RxHeaderTypeDef *header);
|
||||
static uint32_t BSP_CAN_DefaultIdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
|
||||
static void BSP_CAN_TxQueueInit(BSP_CAN_t can);
|
||||
static bool BSP_CAN_TxQueuePush(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg);
|
||||
static bool BSP_CAN_TxQueuePop(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg);
|
||||
static bool BSP_CAN_TxQueueIsEmpty(BSP_CAN_t can);
|
||||
|
||||
/* Private functions -------------------------------------------------------- */
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
/**
|
||||
* @brief 根据CAN句柄获取BSP_CAN实例
|
||||
*/
|
||||
static BSP_CAN_t CAN_Get(CAN_HandleTypeDef *hcan) {
|
||||
if (hcan == NULL) return BSP_CAN_ERR;
|
||||
|
||||
if (hcan->Instance == CAN1)
|
||||
return BSP_CAN_1;
|
||||
else if (hcan->Instance == CAN2)
|
||||
return BSP_CAN_2;
|
||||
else
|
||||
return BSP_CAN_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找指定CAN ID的消息队列
|
||||
* @note 调用前需要获取互斥锁
|
||||
*/
|
||||
static osMessageQueueId_t BSP_CAN_FindQueue(BSP_CAN_t can, uint32_t can_id) {
|
||||
BSP_CAN_QueueNode_t *node = queue_list;
|
||||
while (node != NULL) {
|
||||
if (node->can == can && node->can_id == can_id) {
|
||||
return node->queue;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建指定CAN ID的消息队列
|
||||
* @note 内部函数,已包含互斥锁保护
|
||||
*/
|
||||
static int8_t BSP_CAN_CreateIdQueue(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size) {
|
||||
if (queue_size == 0) {
|
||||
queue_size = BSP_CAN_DEFAULT_QUEUE_SIZE;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
BSP_CAN_QueueNode_t *node = queue_list;
|
||||
while (node != NULL) {
|
||||
if (node->can == can && node->can_id == can_id) {
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_ERR; // 已存在
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
BSP_CAN_QueueNode_t *new_node = (BSP_CAN_QueueNode_t *)BSP_Malloc(sizeof(BSP_CAN_QueueNode_t));
|
||||
if (new_node == NULL) {
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
new_node->queue = osMessageQueueNew(queue_size, sizeof(BSP_CAN_Message_t), NULL);
|
||||
if (new_node->queue == NULL) {
|
||||
BSP_Free(new_node);
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_ERR;
|
||||
}
|
||||
new_node->can = can;
|
||||
new_node->can_id = can_id;
|
||||
new_node->queue_size = queue_size;
|
||||
new_node->next = queue_list;
|
||||
queue_list = new_node;
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取帧类型
|
||||
*/
|
||||
static BSP_CAN_FrameType_t BSP_CAN_GetFrameType(CAN_RxHeaderTypeDef *header) {
|
||||
if (header->RTR == CAN_RTR_REMOTE) {
|
||||
return (header->IDE == CAN_ID_EXT) ? BSP_CAN_FRAME_EXT_REMOTE : BSP_CAN_FRAME_STD_REMOTE;
|
||||
} else {
|
||||
return (header->IDE == CAN_ID_EXT) ? BSP_CAN_FRAME_EXT_DATA : BSP_CAN_FRAME_STD_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 默认ID解析器(直接返回原始ID)
|
||||
*/
|
||||
static uint32_t BSP_CAN_DefaultIdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type) {
|
||||
(void)frame_type; // 避免未使用参数警告
|
||||
return original_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化发送队列
|
||||
*/
|
||||
static void BSP_CAN_TxQueueInit(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return;
|
||||
|
||||
tx_queues[can].head = 0;
|
||||
tx_queues[can].tail = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向发送队列添加消息(无锁)
|
||||
*/
|
||||
static bool BSP_CAN_TxQueuePush(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg) {
|
||||
if (can >= BSP_CAN_NUM || msg == NULL) return false;
|
||||
|
||||
BSP_CAN_TxQueue_t *queue = &tx_queues[can];
|
||||
uint32_t next_head = (queue->head + 1) % BSP_CAN_TX_QUEUE_SIZE;
|
||||
|
||||
// 队列满
|
||||
if (next_head == queue->tail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 复制消息
|
||||
queue->buffer[queue->head] = *msg;
|
||||
|
||||
// 更新头指针(原子操作)
|
||||
queue->head = next_head;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 从发送队列取出消息(无锁)
|
||||
*/
|
||||
static bool BSP_CAN_TxQueuePop(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg) {
|
||||
if (can >= BSP_CAN_NUM || msg == NULL) return false;
|
||||
|
||||
BSP_CAN_TxQueue_t *queue = &tx_queues[can];
|
||||
|
||||
// 队列空
|
||||
if (queue->head == queue->tail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 复制消息
|
||||
*msg = queue->buffer[queue->tail];
|
||||
|
||||
// 更新尾指针(原子操作)
|
||||
queue->tail = (queue->tail + 1) % BSP_CAN_TX_QUEUE_SIZE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查发送队列是否为空
|
||||
*/
|
||||
static bool BSP_CAN_TxQueueIsEmpty(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return true;
|
||||
|
||||
return tx_queues[can].head == tx_queues[can].tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理所有CAN实例的发送队列
|
||||
*/
|
||||
static void BSP_CAN_TxCompleteCallback(void) {
|
||||
// 处理所有CAN实例的发送队列
|
||||
for (int i = 0; i < BSP_CAN_NUM; i++) {
|
||||
BSP_CAN_t can = (BSP_CAN_t)i;
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle(can);
|
||||
if (hcan == NULL) continue;
|
||||
|
||||
BSP_CAN_TxMessage_t msg;
|
||||
uint32_t mailbox;
|
||||
|
||||
// 尝试发送队列中的消息
|
||||
while (!BSP_CAN_TxQueueIsEmpty(can)) {
|
||||
// 检查是否有空闲邮箱
|
||||
if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) == 0) {
|
||||
break; // 没有空闲邮箱,等待下次中断
|
||||
}
|
||||
|
||||
// 从队列中取出消息
|
||||
if (!BSP_CAN_TxQueuePop(can, &msg)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
if (HAL_CAN_AddTxMessage(hcan, &msg.header, msg.data, &mailbox) != HAL_OK) {
|
||||
// 发送失败,消息已经从队列中移除,直接丢弃
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief FIFO0接收处理函数
|
||||
*/
|
||||
static void BSP_CAN_RxFifo0Callback(void) {
|
||||
CAN_RxHeaderTypeDef rx_header;
|
||||
uint8_t rx_data[BSP_CAN_MAX_DLC];
|
||||
for (int can_idx = 0; can_idx < BSP_CAN_NUM; can_idx++) {
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle((BSP_CAN_t)can_idx);
|
||||
if (hcan == NULL) continue;
|
||||
while (HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO0) > 0) {
|
||||
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK) {
|
||||
uint32_t original_id = (rx_header.IDE == CAN_ID_STD) ? rx_header.StdId : rx_header.ExtId;
|
||||
BSP_CAN_FrameType_t frame_type = BSP_CAN_GetFrameType(&rx_header);
|
||||
uint32_t parsed_id = BSP_CAN_ParseId(original_id, frame_type);
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue((BSP_CAN_t)can_idx, parsed_id);
|
||||
if (queue != NULL) {
|
||||
BSP_CAN_Message_t msg = {0};
|
||||
msg.frame_type = frame_type;
|
||||
msg.original_id = original_id;
|
||||
msg.parsed_id = parsed_id;
|
||||
msg.dlc = rx_header.DLC;
|
||||
if (rx_header.RTR == CAN_RTR_DATA) {
|
||||
memcpy(msg.data, rx_data, rx_header.DLC);
|
||||
}
|
||||
msg.timestamp = HAL_GetTick();
|
||||
osMessageQueuePut(queue, &msg, 0, BSP_CAN_TIMEOUT_IMMEDIATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FIFO1接收处理函数
|
||||
*/
|
||||
static void BSP_CAN_RxFifo1Callback(void) {
|
||||
CAN_RxHeaderTypeDef rx_header;
|
||||
uint8_t rx_data[BSP_CAN_MAX_DLC];
|
||||
for (int can_idx = 0; can_idx < BSP_CAN_NUM; can_idx++) {
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle((BSP_CAN_t)can_idx);
|
||||
if (hcan == NULL) continue;
|
||||
while (HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO1) > 0) {
|
||||
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO1, &rx_header, rx_data) == HAL_OK) {
|
||||
uint32_t original_id = (rx_header.IDE == CAN_ID_STD) ? rx_header.StdId : rx_header.ExtId;
|
||||
BSP_CAN_FrameType_t frame_type = BSP_CAN_GetFrameType(&rx_header);
|
||||
uint32_t parsed_id = BSP_CAN_ParseId(original_id, frame_type);
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue((BSP_CAN_t)can_idx, parsed_id);
|
||||
if (queue != NULL) {
|
||||
BSP_CAN_Message_t msg = {0};
|
||||
msg.frame_type = frame_type;
|
||||
msg.original_id = original_id;
|
||||
msg.parsed_id = parsed_id;
|
||||
msg.dlc = rx_header.DLC;
|
||||
if (rx_header.RTR == CAN_RTR_DATA) {
|
||||
memcpy(msg.data, rx_data, rx_header.DLC);
|
||||
}
|
||||
msg.timestamp = HAL_GetTick();
|
||||
osMessageQueuePut(queue, &msg, 0, BSP_CAN_TIMEOUT_IMMEDIATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* HAL Callback Functions --------------------------------------------------- */
|
||||
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_MSG_PENDING_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_MSG_PENDING_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_FULL_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_FULL_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_MSG_PENDING_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_MSG_PENDING_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_FULL_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_FULL_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_SLEEP_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_SLEEP_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_WAKEUP_FROM_RX_MSG_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_WAKEUP_FROM_RX_MSG_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_ERROR_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_ERROR_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
int8_t BSP_CAN_Init(void) {
|
||||
if (inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
|
||||
// 清零回调函数数组
|
||||
memset(CAN_Callback, 0, sizeof(CAN_Callback));
|
||||
|
||||
// 初始化发送队列
|
||||
for (int i = 0; i < BSP_CAN_NUM; i++) {
|
||||
BSP_CAN_TxQueueInit((BSP_CAN_t)i);
|
||||
}
|
||||
|
||||
// 初始化ID解析器为默认解析器
|
||||
id_parser = BSP_CAN_DefaultIdParser;
|
||||
|
||||
// 创建互斥锁
|
||||
queue_mutex = osMutexNew(NULL);
|
||||
if (queue_mutex == NULL) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
// 先设置初始化标志,以便后续回调注册能通过检查
|
||||
inited = true;
|
||||
|
||||
// 初始化 CAN1 - 使用 FIFO0
|
||||
CAN_FilterTypeDef can1_filter = {0};
|
||||
can1_filter.FilterBank = 0;
|
||||
can1_filter.FilterIdHigh = 0;
|
||||
can1_filter.FilterIdLow = 0;
|
||||
can1_filter.FilterMode = CAN_FILTERMODE_IDMASK;
|
||||
can1_filter.FilterScale = CAN_FILTERSCALE_32BIT;
|
||||
can1_filter.FilterMaskIdHigh = 0;
|
||||
can1_filter.FilterMaskIdLow = 0;
|
||||
can1_filter.FilterActivation = ENABLE;
|
||||
can1_filter.SlaveStartFilterBank = 14;
|
||||
can1_filter.FilterFIFOAssignment = CAN_RX_FIFO0;
|
||||
HAL_CAN_ConfigFilter(&hcan1, &can1_filter);
|
||||
HAL_CAN_Start(&hcan1);
|
||||
|
||||
// 自动注册CAN1接收回调函数
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_1, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifo0Callback);
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_1, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_1, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_1, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);
|
||||
|
||||
// 激活CAN1中断
|
||||
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING |
|
||||
CAN_IT_TX_MAILBOX_EMPTY); // 激活发送邮箱空中断
|
||||
|
||||
// 初始化 CAN2 - 使用 FIFO1
|
||||
can1_filter.FilterBank = 14;
|
||||
can1_filter.FilterFIFOAssignment = CAN_RX_FIFO1;
|
||||
HAL_CAN_ConfigFilter(&hcan2, &can1_filter); // 通过 CAN1 配置
|
||||
HAL_CAN_Start(&hcan2);
|
||||
|
||||
// 自动注册CAN2接收回调函数
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_2, HAL_CAN_RX_FIFO1_MSG_PENDING_CB, BSP_CAN_RxFifo1Callback);
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_2, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_2, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);
|
||||
BSP_CAN_RegisterCallback(BSP_CAN_2, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);
|
||||
|
||||
// 激活CAN2中断
|
||||
HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO1_MSG_PENDING |
|
||||
CAN_IT_TX_MAILBOX_EMPTY); // 激活发送邮箱空中断
|
||||
|
||||
|
||||
inited = true;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
|
||||
CAN_HandleTypeDef *BSP_CAN_GetHandle(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (can) {
|
||||
case BSP_CAN_1:
|
||||
return &hcan1;
|
||||
case BSP_CAN_2:
|
||||
return &hcan2;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_RegisterCallback(BSP_CAN_t can, BSP_CAN_Callback_t type,
|
||||
void (*callback)(void)) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (callback == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
if (can >= BSP_CAN_NUM) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
if (type >= BSP_CAN_CB_NUM) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
CAN_Callback[can][type] = callback;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_Transmit(BSP_CAN_t can, BSP_CAN_Format_t format,
|
||||
uint32_t id, uint8_t *data, uint8_t dlc) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (can >= BSP_CAN_NUM) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
if (data == NULL && format != BSP_CAN_FORMAT_STD_REMOTE && format != BSP_CAN_FORMAT_EXT_REMOTE) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
if (dlc > BSP_CAN_MAX_DLC) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle(can);
|
||||
if (hcan == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
|
||||
// 准备发送消息
|
||||
BSP_CAN_TxMessage_t tx_msg = {0};
|
||||
|
||||
switch (format) {
|
||||
case BSP_CAN_FORMAT_STD_DATA:
|
||||
tx_msg.header.StdId = id;
|
||||
tx_msg.header.IDE = CAN_ID_STD;
|
||||
tx_msg.header.RTR = CAN_RTR_DATA;
|
||||
break;
|
||||
case BSP_CAN_FORMAT_EXT_DATA:
|
||||
tx_msg.header.ExtId = id;
|
||||
tx_msg.header.IDE = CAN_ID_EXT;
|
||||
tx_msg.header.RTR = CAN_RTR_DATA;
|
||||
break;
|
||||
case BSP_CAN_FORMAT_STD_REMOTE:
|
||||
tx_msg.header.StdId = id;
|
||||
tx_msg.header.IDE = CAN_ID_STD;
|
||||
tx_msg.header.RTR = CAN_RTR_REMOTE;
|
||||
break;
|
||||
case BSP_CAN_FORMAT_EXT_REMOTE:
|
||||
tx_msg.header.ExtId = id;
|
||||
tx_msg.header.IDE = CAN_ID_EXT;
|
||||
tx_msg.header.RTR = CAN_RTR_REMOTE;
|
||||
break;
|
||||
default:
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
tx_msg.header.DLC = dlc;
|
||||
tx_msg.header.TransmitGlobalTime = DISABLE;
|
||||
|
||||
// 复制数据
|
||||
if (data != NULL && dlc > 0) {
|
||||
memcpy(tx_msg.data, data, dlc);
|
||||
}
|
||||
|
||||
// 尝试直接发送到邮箱
|
||||
uint32_t mailbox;
|
||||
if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) > 0) {
|
||||
HAL_StatusTypeDef result = HAL_CAN_AddTxMessage(hcan, &tx_msg.header, tx_msg.data, &mailbox);
|
||||
if (result == HAL_OK) {
|
||||
return BSP_OK; // 发送成功
|
||||
}
|
||||
}
|
||||
|
||||
// 邮箱满,尝试放入队列
|
||||
if (BSP_CAN_TxQueuePush(can, &tx_msg)) {
|
||||
return BSP_OK; // 成功放入队列
|
||||
}
|
||||
|
||||
// 队列也满,丢弃数据
|
||||
return BSP_ERR; // 数据丢弃
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_TransmitStdDataFrame(BSP_CAN_t can, BSP_CAN_StdDataFrame_t *frame) {
|
||||
if (frame == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
return BSP_CAN_Transmit(can, BSP_CAN_FORMAT_STD_DATA, frame->id, frame->data, frame->dlc);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_TransmitExtDataFrame(BSP_CAN_t can, BSP_CAN_ExtDataFrame_t *frame) {
|
||||
if (frame == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
return BSP_CAN_Transmit(can, BSP_CAN_FORMAT_EXT_DATA, frame->id, frame->data, frame->dlc);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_TransmitRemoteFrame(BSP_CAN_t can, BSP_CAN_RemoteFrame_t *frame) {
|
||||
if (frame == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
BSP_CAN_Format_t format = frame->is_extended ? BSP_CAN_FORMAT_EXT_REMOTE : BSP_CAN_FORMAT_STD_REMOTE;
|
||||
return BSP_CAN_Transmit(can, format, frame->id, NULL, frame->dlc);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_RegisterId(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
return BSP_CAN_CreateIdQueue(can, can_id, queue_size);
|
||||
}
|
||||
|
||||
|
||||
int8_t BSP_CAN_GetMessage(BSP_CAN_t can, uint32_t can_id, BSP_CAN_Message_t *msg, uint32_t timeout) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (msg == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue(can, can_id);
|
||||
osMutexRelease(queue_mutex);
|
||||
if (queue == NULL) {
|
||||
return BSP_ERR_NO_DEV;
|
||||
}
|
||||
osStatus_t result = osMessageQueueGet(queue, msg, NULL, timeout);
|
||||
return (result == osOK) ? BSP_OK : BSP_ERR;
|
||||
}
|
||||
|
||||
int32_t BSP_CAN_GetQueueCount(BSP_CAN_t can, uint32_t can_id) {
|
||||
if (!inited) {
|
||||
return -1;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return -1;
|
||||
}
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue(can, can_id);
|
||||
osMutexRelease(queue_mutex);
|
||||
if (queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return (int32_t)osMessageQueueGetCount(queue);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_FlushQueue(BSP_CAN_t can, uint32_t can_id) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue(can, can_id);
|
||||
osMutexRelease(queue_mutex);
|
||||
if (queue == NULL) {
|
||||
return BSP_ERR_NO_DEV;
|
||||
}
|
||||
BSP_CAN_Message_t temp_msg;
|
||||
while (osMessageQueueGet(queue, &temp_msg, NULL, BSP_CAN_TIMEOUT_IMMEDIATE) == osOK) {
|
||||
// 清空
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_RegisterIdParser(BSP_CAN_IdParser_t parser) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (parser == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
|
||||
id_parser = parser;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
uint32_t BSP_CAN_ParseId(uint32_t original_id, BSP_CAN_FrameType_t frame_type) {
|
||||
if (id_parser != NULL) {
|
||||
return id_parser(original_id, frame_type);
|
||||
}
|
||||
return BSP_CAN_DefaultIdParser(original_id, frame_type);
|
||||
}
|
||||
|
||||
|
||||
259
User/bsp/can.h
Normal file
259
User/bsp/can.h
Normal file
@ -0,0 +1,259 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <can.h>
|
||||
#include "bsp/bsp.h"
|
||||
#include "bsp/mm.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <cmsis_os.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
#define BSP_CAN_MAX_DLC 8
|
||||
#define BSP_CAN_DEFAULT_QUEUE_SIZE 10
|
||||
#define BSP_CAN_TIMEOUT_IMMEDIATE 0
|
||||
#define BSP_CAN_TIMEOUT_FOREVER osWaitForever
|
||||
#define BSP_CAN_TX_QUEUE_SIZE 32 /* 发送队列大小 */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef enum {
|
||||
BSP_CAN_1,
|
||||
BSP_CAN_2,
|
||||
BSP_CAN_NUM,
|
||||
BSP_CAN_ERR,
|
||||
} BSP_CAN_t;
|
||||
|
||||
typedef enum {
|
||||
HAL_CAN_TX_MAILBOX0_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX1_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX2_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX0_ABORT_CB,
|
||||
HAL_CAN_TX_MAILBOX1_ABORT_CB,
|
||||
HAL_CAN_TX_MAILBOX2_ABORT_CB,
|
||||
HAL_CAN_RX_FIFO0_MSG_PENDING_CB,
|
||||
HAL_CAN_RX_FIFO0_FULL_CB,
|
||||
HAL_CAN_RX_FIFO1_MSG_PENDING_CB,
|
||||
HAL_CAN_RX_FIFO1_FULL_CB,
|
||||
HAL_CAN_SLEEP_CB,
|
||||
HAL_CAN_WAKEUP_FROM_RX_MSG_CB,
|
||||
HAL_CAN_ERROR_CB,
|
||||
BSP_CAN_CB_NUM,
|
||||
} BSP_CAN_Callback_t;
|
||||
|
||||
/* CAN消息格式枚举 - 用于发送和接收消息时指定格式 */
|
||||
typedef enum {
|
||||
BSP_CAN_FORMAT_STD_DATA, /* 标准数据帧 */
|
||||
BSP_CAN_FORMAT_EXT_DATA, /* 扩展数据帧 */
|
||||
BSP_CAN_FORMAT_STD_REMOTE, /* 标准远程帧 */
|
||||
BSP_CAN_FORMAT_EXT_REMOTE, /* 扩展远程帧 */
|
||||
} BSP_CAN_Format_t;
|
||||
|
||||
/* CAN帧类型枚举 - 用于区分不同类型的CAN帧 */
|
||||
typedef enum {
|
||||
BSP_CAN_FRAME_STD_DATA, /* 标准数据帧 */
|
||||
BSP_CAN_FRAME_EXT_DATA, /* 扩展数据帧 */
|
||||
BSP_CAN_FRAME_STD_REMOTE, /* 标准远程帧 */
|
||||
BSP_CAN_FRAME_EXT_REMOTE, /* 扩展远程帧 */
|
||||
} BSP_CAN_FrameType_t;
|
||||
|
||||
/* CAN消息结构体 - 支持不同类型帧 */
|
||||
typedef struct {
|
||||
BSP_CAN_FrameType_t frame_type; /* 帧类型 */
|
||||
uint32_t original_id; /* 原始ID(未解析) */
|
||||
uint32_t parsed_id; /* 解析后的实际ID */
|
||||
uint8_t dlc; /* 数据长度 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
uint32_t timestamp; /* 时间戳(可选) */
|
||||
} BSP_CAN_Message_t;
|
||||
|
||||
/* 标准数据帧结构 */
|
||||
typedef struct {
|
||||
uint32_t id; /* CAN ID */
|
||||
uint8_t dlc; /* 数据长度 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
} BSP_CAN_StdDataFrame_t;
|
||||
|
||||
/* 扩展数据帧结构 */
|
||||
typedef struct {
|
||||
uint32_t id; /* 扩展CAN ID */
|
||||
uint8_t dlc; /* 数据长度 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
} BSP_CAN_ExtDataFrame_t;
|
||||
|
||||
/* 远程帧结构 */
|
||||
typedef struct {
|
||||
uint32_t id; /* CAN ID */
|
||||
uint8_t dlc; /* 请求的数据长度 */
|
||||
bool is_extended; /* 是否为扩展帧 */
|
||||
} BSP_CAN_RemoteFrame_t;
|
||||
|
||||
/* ID解析回调函数类型 */
|
||||
typedef uint32_t (*BSP_CAN_IdParser_t)(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
|
||||
|
||||
/* CAN发送消息结构体 */
|
||||
typedef struct {
|
||||
CAN_TxHeaderTypeDef header; /* 发送头 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
} BSP_CAN_TxMessage_t;
|
||||
|
||||
/* 无锁环形队列结构体 */
|
||||
typedef struct {
|
||||
BSP_CAN_TxMessage_t buffer[BSP_CAN_TX_QUEUE_SIZE]; /* 缓冲区 */
|
||||
volatile uint32_t head; /* 队列头 */
|
||||
volatile uint32_t tail; /* 队列尾 */
|
||||
} BSP_CAN_TxQueue_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化 CAN 模块
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 获取 CAN 句柄
|
||||
* @param can CAN 枚举
|
||||
* @return CAN_HandleTypeDef 指针,失败返回 NULL
|
||||
*/
|
||||
CAN_HandleTypeDef *BSP_CAN_GetHandle(BSP_CAN_t can);
|
||||
|
||||
/**
|
||||
* @brief 注册 CAN 回调函数
|
||||
* @param can CAN 枚举
|
||||
* @param type 回调类型
|
||||
* @param callback 回调函数指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_RegisterCallback(BSP_CAN_t can, BSP_CAN_Callback_t type,
|
||||
void (*callback)(void));
|
||||
|
||||
/**
|
||||
* @brief 发送 CAN 消息
|
||||
* @param can CAN 枚举
|
||||
* @param format 消息格式
|
||||
* @param id CAN ID
|
||||
* @param data 数据指针
|
||||
* @param dlc 数据长度
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_Transmit(BSP_CAN_t can, BSP_CAN_Format_t format,
|
||||
uint32_t id, uint8_t *data, uint8_t dlc);
|
||||
|
||||
/**
|
||||
* @brief 发送标准数据帧
|
||||
* @param can CAN 枚举
|
||||
* @param frame 标准数据帧指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_TransmitStdDataFrame(BSP_CAN_t can, BSP_CAN_StdDataFrame_t *frame);
|
||||
|
||||
/**
|
||||
* @brief 发送扩展数据帧
|
||||
* @param can CAN 枚举
|
||||
* @param frame 扩展数据帧指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_TransmitExtDataFrame(BSP_CAN_t can, BSP_CAN_ExtDataFrame_t *frame);
|
||||
|
||||
/**
|
||||
* @brief 发送远程帧
|
||||
* @param can CAN 枚举
|
||||
* @param frame 远程帧指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_TransmitRemoteFrame(BSP_CAN_t can, BSP_CAN_RemoteFrame_t *frame);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取发送队列中待发送消息数量
|
||||
* @param can CAN 枚举
|
||||
* @return 队列中消息数量,-1表示错误
|
||||
*/
|
||||
int32_t BSP_CAN_GetTxQueueCount(BSP_CAN_t can);
|
||||
|
||||
/**
|
||||
* @brief 清空发送队列
|
||||
* @param can CAN 枚举
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_FlushTxQueue(BSP_CAN_t can);
|
||||
|
||||
/**
|
||||
* @brief 注册 CAN ID 接收队列
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @param queue_size 队列大小,0使用默认值
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_RegisterId(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取 CAN 消息
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @param msg 存储消息的结构体指针
|
||||
* @param timeout 超时时间(毫秒),0为立即返回,osWaitForever为永久等待
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_GetMessage(BSP_CAN_t can, uint32_t can_id, BSP_CAN_Message_t *msg, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief 获取指定ID队列中的消息数量
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @return 消息数量,-1表示队列不存在
|
||||
*/
|
||||
int32_t BSP_CAN_GetQueueCount(BSP_CAN_t can, uint32_t can_id);
|
||||
|
||||
/**
|
||||
* @brief 清空指定ID队列中的所有消息
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_FlushQueue(BSP_CAN_t can, uint32_t can_id);
|
||||
|
||||
/**
|
||||
* @brief 注册ID解析器
|
||||
* @param parser ID解析回调函数
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_RegisterIdParser(BSP_CAN_IdParser_t parser);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 解析CAN ID
|
||||
* @param original_id 原始ID
|
||||
* @param frame_type 帧类型
|
||||
* @return 解析后的ID
|
||||
*/
|
||||
uint32_t BSP_CAN_ParseId(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
30
User/bsp/mm.c
Normal file
30
User/bsp/mm.c
Normal file
@ -0,0 +1,30 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/mm.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
inline void *BSP_Malloc(size_t size) { return pvPortMalloc(size); }
|
||||
|
||||
inline void BSP_Free(void *pv) { vPortFree(pv); }
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
32
User/bsp/mm.h
Normal file
32
User/bsp/mm.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
void *BSP_Malloc(size_t size);
|
||||
void BSP_Free(void *pv);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
81
User/bsp/time.c
Normal file
81
User/bsp/time.c
Normal file
@ -0,0 +1,81 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/time.h"
|
||||
#include "bsp.h"
|
||||
|
||||
#include <cmsis_os2.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "main.h"
|
||||
#include "task.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
uint32_t BSP_TIME_Get_ms() { return xTaskGetTickCount(); }
|
||||
|
||||
uint64_t BSP_TIME_Get_us() {
|
||||
uint32_t tick_freq = osKernelGetTickFreq();
|
||||
uint32_t ticks_old = xTaskGetTickCount()*(1000/tick_freq);
|
||||
uint32_t tick_value_old = SysTick->VAL;
|
||||
uint32_t ticks_new = xTaskGetTickCount()*(1000/tick_freq);
|
||||
uint32_t tick_value_new = SysTick->VAL;
|
||||
if (ticks_old == ticks_new) {
|
||||
return ticks_new * 1000 + 1000 - tick_value_old * 1000 / (SysTick->LOAD + 1);
|
||||
} else {
|
||||
return ticks_new * 1000 + 1000 - tick_value_new * 1000 / (SysTick->LOAD + 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t BSP_TIME_Get() __attribute__((alias("BSP_TIME_Get_us")));
|
||||
|
||||
int8_t BSP_TIME_Delay_ms(uint32_t ms) {
|
||||
uint32_t tick_period = 1000u / osKernelGetTickFreq();
|
||||
uint32_t ticks = ms / tick_period;
|
||||
|
||||
switch (osKernelGetState()) {
|
||||
case osKernelError:
|
||||
case osKernelReserved:
|
||||
case osKernelLocked:
|
||||
case osKernelSuspended:
|
||||
return BSP_ERR;
|
||||
|
||||
case osKernelRunning:
|
||||
osDelay(ticks ? ticks : 1);
|
||||
break;
|
||||
|
||||
case osKernelInactive:
|
||||
case osKernelReady:
|
||||
HAL_Delay(ms);
|
||||
break;
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*阻塞us延迟*/
|
||||
int8_t BSP_TIME_Delay_us(uint32_t us) {
|
||||
uint64_t start = BSP_TIME_Get_us();
|
||||
while (BSP_TIME_Get_us() - start < us) {
|
||||
// 等待us时间
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_TIME_Delay(uint32_t ms) __attribute__((alias("BSP_TIME_Delay_ms")));
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
43
User/bsp/time.h
Normal file
43
User/bsp/time.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
uint32_t BSP_TIME_Get_ms();
|
||||
|
||||
uint64_t BSP_TIME_Get_us();
|
||||
|
||||
uint64_t BSP_TIME_Get();
|
||||
|
||||
int8_t BSP_TIME_Delay_ms(uint32_t ms);
|
||||
|
||||
/*微秒阻塞延时,一般别用*/
|
||||
int8_t BSP_TIME_Delay_us(uint32_t us);
|
||||
|
||||
int8_t BSP_TIME_Delay(uint32_t ms);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
417
User/component/ahrs.c
Normal file
417
User/component/ahrs.c
Normal file
@ -0,0 +1,417 @@
|
||||
/*
|
||||
开源的AHRS算法。
|
||||
MadgwickAHRS
|
||||
*/
|
||||
|
||||
#include "ahrs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
#define BETA_IMU (0.033f)
|
||||
#define BETA_AHRS (0.041f)
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* 2 * proportional gain (Kp) */
|
||||
static float beta = BETA_IMU;
|
||||
|
||||
/**
|
||||
* @brief 不使用磁力计计算姿态
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param accl 加速度计数据
|
||||
* @param gyro 陀螺仪数据
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
static int8_t AHRS_UpdateIMU(AHRS_t *ahrs, const AHRS_Accl_t *accl,
|
||||
const AHRS_Gyro_t *gyro) {
|
||||
if (ahrs == NULL) return -1;
|
||||
if (accl == NULL) return -1;
|
||||
if (gyro == NULL) return -1;
|
||||
|
||||
beta = BETA_IMU;
|
||||
|
||||
float ax = accl->x;
|
||||
float ay = accl->y;
|
||||
float az = accl->z;
|
||||
|
||||
float gx = gyro->x;
|
||||
float gy = gyro->y;
|
||||
float gz = gyro->z;
|
||||
|
||||
float recip_norm;
|
||||
float s0, s1, s2, s3;
|
||||
float q_dot1, q_dot2, q_dot3, q_dot4;
|
||||
float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2,
|
||||
q3q3;
|
||||
|
||||
/* Rate of change of quaternion from gyroscope */
|
||||
q_dot1 = 0.5f * (-ahrs->quat.q1 * gx - ahrs->quat.q2 * gy -
|
||||
ahrs->quat.q3 * gz);
|
||||
q_dot2 = 0.5f * (ahrs->quat.q0 * gx + ahrs->quat.q2 * gz -
|
||||
ahrs->quat.q3 * gy);
|
||||
q_dot3 = 0.5f * (ahrs->quat.q0 * gy - ahrs->quat.q1 * gz +
|
||||
ahrs->quat.q3 * gx);
|
||||
q_dot4 = 0.5f * (ahrs->quat.q0 * gz + ahrs->quat.q1 * gy -
|
||||
ahrs->quat.q2 * gx);
|
||||
|
||||
/* Compute feedback only if accelerometer measurement valid (avoids NaN in
|
||||
* accelerometer normalisation) */
|
||||
if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
|
||||
/* Normalise accelerometer measurement */
|
||||
recip_norm = InvSqrt(ax * ax + ay * ay + az * az);
|
||||
ax *= recip_norm;
|
||||
ay *= recip_norm;
|
||||
az *= recip_norm;
|
||||
|
||||
/* Auxiliary variables to avoid repeated arithmetic */
|
||||
_2q0 = 2.0f * ahrs->quat.q0;
|
||||
_2q1 = 2.0f * ahrs->quat.q1;
|
||||
_2q2 = 2.0f * ahrs->quat.q2;
|
||||
_2q3 = 2.0f * ahrs->quat.q3;
|
||||
_4q0 = 4.0f * ahrs->quat.q0;
|
||||
_4q1 = 4.0f * ahrs->quat.q1;
|
||||
_4q2 = 4.0f * ahrs->quat.q2;
|
||||
_8q1 = 8.0f * ahrs->quat.q1;
|
||||
_8q2 = 8.0f * ahrs->quat.q2;
|
||||
q0q0 = ahrs->quat.q0 * ahrs->quat.q0;
|
||||
q1q1 = ahrs->quat.q1 * ahrs->quat.q1;
|
||||
q2q2 = ahrs->quat.q2 * ahrs->quat.q2;
|
||||
q3q3 = ahrs->quat.q3 * ahrs->quat.q3;
|
||||
|
||||
/* Gradient decent algorithm corrective step */
|
||||
s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
|
||||
s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * ahrs->quat.q1 -
|
||||
_2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
|
||||
s2 = 4.0f * q0q0 * ahrs->quat.q2 + _2q0 * ax + _4q2 * q3q3 -
|
||||
_2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
|
||||
s3 = 4.0f * q1q1 * ahrs->quat.q3 - _2q1 * ax +
|
||||
4.0f * q2q2 * ahrs->quat.q3 - _2q2 * ay;
|
||||
|
||||
/* normalise step magnitude */
|
||||
recip_norm = InvSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3);
|
||||
|
||||
s0 *= recip_norm;
|
||||
s1 *= recip_norm;
|
||||
s2 *= recip_norm;
|
||||
s3 *= recip_norm;
|
||||
|
||||
/* Apply feedback step */
|
||||
q_dot1 -= beta * s0;
|
||||
q_dot2 -= beta * s1;
|
||||
q_dot3 -= beta * s2;
|
||||
q_dot4 -= beta * s3;
|
||||
}
|
||||
|
||||
/* Integrate rate of change of quaternion to yield quaternion */
|
||||
ahrs->quat.q0 += q_dot1 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q1 += q_dot2 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q2 += q_dot3 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q3 += q_dot4 * ahrs->inv_sample_freq;
|
||||
|
||||
/* Normalise quaternion */
|
||||
recip_norm = InvSqrt(ahrs->quat.q0 * ahrs->quat.q0 +
|
||||
ahrs->quat.q1 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q2 +
|
||||
ahrs->quat.q3 * ahrs->quat.q3);
|
||||
ahrs->quat.q0 *= recip_norm;
|
||||
ahrs->quat.q1 *= recip_norm;
|
||||
ahrs->quat.q2 *= recip_norm;
|
||||
ahrs->quat.q3 *= recip_norm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化姿态解算
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param magn 磁力计数据
|
||||
* @param sample_freq 采样频率
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Init(AHRS_t *ahrs, const AHRS_Magn_t *magn, float sample_freq) {
|
||||
if (ahrs == NULL) return -1;
|
||||
|
||||
ahrs->inv_sample_freq = 1.0f / sample_freq;
|
||||
|
||||
ahrs->quat.q0 = 1.0f;
|
||||
ahrs->quat.q1 = 0.0f;
|
||||
ahrs->quat.q2 = 0.0f;
|
||||
ahrs->quat.q3 = 0.0f;
|
||||
|
||||
if (magn) {
|
||||
float yaw = -atan2(magn->y, magn->x);
|
||||
|
||||
if ((magn->x == 0.0f) && (magn->y == 0.0f) && (magn->z == 0.0f)) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
|
||||
} else if ((yaw < (M_PI / 2.0f)) || (yaw > 0.0f)) {
|
||||
ahrs->quat.q0 = 0.997458339f;
|
||||
ahrs->quat.q1 = 0.000336312107f;
|
||||
ahrs->quat.q2 = -0.0057230792f;
|
||||
ahrs->quat.q3 = 0.0740156546;
|
||||
|
||||
} else if ((yaw < M_PI) || (yaw > (M_PI / 2.0f))) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
|
||||
} else if ((yaw < 90.0f) || (yaw > M_PI)) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
|
||||
} else if ((yaw < 90.0f) || (yaw > 0.0f)) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 姿态运算更新一次
|
||||
* @note 输入数据必须是NED(North East Down) 参考坐标系
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param accl 加速度计数据
|
||||
* @param gyro 陀螺仪数据
|
||||
* @param magn 磁力计数据
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Update(AHRS_t *ahrs, const AHRS_Accl_t *accl,
|
||||
const AHRS_Gyro_t *gyro, const AHRS_Magn_t *magn) {
|
||||
if (ahrs == NULL) return -1;
|
||||
if (accl == NULL) return -1;
|
||||
if (gyro == NULL) return -1;
|
||||
|
||||
beta = BETA_AHRS;
|
||||
|
||||
float recip_norm;
|
||||
float s0, s1, s2, s3;
|
||||
float q_dot1, q_dot2, q_dot3, q_dot4;
|
||||
float hx, hy;
|
||||
float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1,
|
||||
_2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3,
|
||||
q2q2, q2q3, q3q3;
|
||||
|
||||
if (magn == NULL) return AHRS_UpdateIMU(ahrs, accl, gyro);
|
||||
|
||||
float mx = magn->x;
|
||||
float my = magn->y;
|
||||
float mz = magn->z;
|
||||
|
||||
/* Use IMU algorithm if magnetometer measurement invalid (avoids NaN in */
|
||||
/* magnetometer normalisation) */
|
||||
if ((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) {
|
||||
return AHRS_UpdateIMU(ahrs, accl, gyro);
|
||||
}
|
||||
|
||||
float ax = accl->x;
|
||||
float ay = accl->y;
|
||||
float az = accl->z;
|
||||
|
||||
float gx = gyro->x;
|
||||
float gy = gyro->y;
|
||||
float gz = gyro->z;
|
||||
|
||||
/* Rate of change of quaternion from gyroscope */
|
||||
q_dot1 = 0.5f * (-ahrs->quat.q1 * gx - ahrs->quat.q2 * gy -
|
||||
ahrs->quat.q3 * gz);
|
||||
q_dot2 = 0.5f * (ahrs->quat.q0 * gx + ahrs->quat.q2 * gz -
|
||||
ahrs->quat.q3 * gy);
|
||||
q_dot3 = 0.5f * (ahrs->quat.q0 * gy - ahrs->quat.q1 * gz +
|
||||
ahrs->quat.q3 * gx);
|
||||
q_dot4 = 0.5f * (ahrs->quat.q0 * gz + ahrs->quat.q1 * gy -
|
||||
ahrs->quat.q2 * gx);
|
||||
|
||||
/* Compute feedback only if accelerometer measurement valid (avoids NaN in
|
||||
* accelerometer normalisation) */
|
||||
if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
|
||||
/* Normalise accelerometer measurement */
|
||||
recip_norm = InvSqrt(ax * ax + ay * ay + az * az);
|
||||
ax *= recip_norm;
|
||||
ay *= recip_norm;
|
||||
az *= recip_norm;
|
||||
|
||||
/* Normalise magnetometer measurement */
|
||||
recip_norm = InvSqrt(mx * mx + my * my + mz * mz);
|
||||
mx *= recip_norm;
|
||||
my *= recip_norm;
|
||||
mz *= recip_norm;
|
||||
|
||||
/* Auxiliary variables to avoid repeated arithmetic */
|
||||
_2q0mx = 2.0f * ahrs->quat.q0 * mx;
|
||||
_2q0my = 2.0f * ahrs->quat.q0 * my;
|
||||
_2q0mz = 2.0f * ahrs->quat.q0 * mz;
|
||||
_2q1mx = 2.0f * ahrs->quat.q1 * mx;
|
||||
_2q0 = 2.0f * ahrs->quat.q0;
|
||||
_2q1 = 2.0f * ahrs->quat.q1;
|
||||
_2q2 = 2.0f * ahrs->quat.q2;
|
||||
_2q3 = 2.0f * ahrs->quat.q3;
|
||||
_2q0q2 = 2.0f * ahrs->quat.q0 * ahrs->quat.q2;
|
||||
_2q2q3 = 2.0f * ahrs->quat.q2 * ahrs->quat.q3;
|
||||
q0q0 = ahrs->quat.q0 * ahrs->quat.q0;
|
||||
q0q1 = ahrs->quat.q0 * ahrs->quat.q1;
|
||||
q0q2 = ahrs->quat.q0 * ahrs->quat.q2;
|
||||
q0q3 = ahrs->quat.q0 * ahrs->quat.q3;
|
||||
q1q1 = ahrs->quat.q1 * ahrs->quat.q1;
|
||||
q1q2 = ahrs->quat.q1 * ahrs->quat.q2;
|
||||
q1q3 = ahrs->quat.q1 * ahrs->quat.q3;
|
||||
q2q2 = ahrs->quat.q2 * ahrs->quat.q2;
|
||||
q2q3 = ahrs->quat.q2 * ahrs->quat.q3;
|
||||
q3q3 = ahrs->quat.q3 * ahrs->quat.q3;
|
||||
|
||||
/* Reference direction of Earth's magnetic field */
|
||||
hx = mx * q0q0 - _2q0my * ahrs->quat.q3 +
|
||||
_2q0mz * ahrs->quat.q2 + mx * q1q1 +
|
||||
_2q1 * my * ahrs->quat.q2 + _2q1 * mz * ahrs->quat.q3 -
|
||||
mx * q2q2 - mx * q3q3;
|
||||
hy = _2q0mx * ahrs->quat.q3 + my * q0q0 -
|
||||
_2q0mz * ahrs->quat.q1 + _2q1mx * ahrs->quat.q2 -
|
||||
my * q1q1 + my * q2q2 + _2q2 * mz * ahrs->quat.q3 - my * q3q3;
|
||||
// _2bx = sqrtf(hx * hx + hy * hy);
|
||||
// 改为invsqrt
|
||||
_2bx = 1.f / InvSqrt(hx * hx + hy * hy);
|
||||
_2bz = -_2q0mx * ahrs->quat.q2 + _2q0my * ahrs->quat.q1 +
|
||||
mz * q0q0 + _2q1mx * ahrs->quat.q3 - mz * q1q1 +
|
||||
_2q2 * my * ahrs->quat.q3 - mz * q2q2 + mz * q3q3;
|
||||
_4bx = 2.0f * _2bx;
|
||||
_4bz = 2.0f * _2bz;
|
||||
|
||||
/* Gradient decent algorithm corrective step */
|
||||
s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q1 * (2.0f * q0q1 + _2q2q3 - ay) -
|
||||
_2bz * ahrs->quat.q2 *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(-_2bx * ahrs->quat.q3 + _2bz * ahrs->quat.q1) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
_2bx * ahrs->quat.q2 *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q0 * (2.0f * q0q1 + _2q2q3 - ay) -
|
||||
4.0f * ahrs->quat.q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) +
|
||||
_2bz * ahrs->quat.q3 *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(_2bx * ahrs->quat.q2 + _2bz * ahrs->quat.q0) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
(_2bx * ahrs->quat.q3 - _4bz * ahrs->quat.q1) *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q3 * (2.0f * q0q1 + _2q2q3 - ay) -
|
||||
4.0f * ahrs->quat.q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) +
|
||||
(-_4bx * ahrs->quat.q2 - _2bz * ahrs->quat.q0) *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(_2bx * ahrs->quat.q1 + _2bz * ahrs->quat.q3) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
(_2bx * ahrs->quat.q0 - _4bz * ahrs->quat.q2) *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q2 * (2.0f * q0q1 + _2q2q3 - ay) +
|
||||
(-_4bx * ahrs->quat.q3 + _2bz * ahrs->quat.q1) *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(-_2bx * ahrs->quat.q0 + _2bz * ahrs->quat.q2) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
_2bx * ahrs->quat.q1 *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
/* normalise step magnitude */
|
||||
recip_norm = InvSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3);
|
||||
s0 *= recip_norm;
|
||||
s1 *= recip_norm;
|
||||
s2 *= recip_norm;
|
||||
s3 *= recip_norm;
|
||||
|
||||
/* Apply feedback step */
|
||||
q_dot1 -= beta * s0;
|
||||
q_dot2 -= beta * s1;
|
||||
q_dot3 -= beta * s2;
|
||||
q_dot4 -= beta * s3;
|
||||
}
|
||||
|
||||
/* Integrate rate of change of quaternion to yield quaternion */
|
||||
ahrs->quat.q0 += q_dot1 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q1 += q_dot2 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q2 += q_dot3 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q3 += q_dot4 * ahrs->inv_sample_freq;
|
||||
|
||||
/* Normalise quaternion */
|
||||
recip_norm = InvSqrt(ahrs->quat.q0 * ahrs->quat.q0 +
|
||||
ahrs->quat.q1 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q2 +
|
||||
ahrs->quat.q3 * ahrs->quat.q3);
|
||||
ahrs->quat.q0 *= recip_norm;
|
||||
ahrs->quat.q1 *= recip_norm;
|
||||
ahrs->quat.q2 *= recip_norm;
|
||||
ahrs->quat.q3 *= recip_norm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通过姿态解算主结构体中的四元数计算欧拉角
|
||||
*
|
||||
* @param eulr 欧拉角
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_GetEulr(AHRS_Eulr_t *eulr, const AHRS_t *ahrs) {
|
||||
if (eulr == NULL) return -1;
|
||||
if (ahrs == NULL) return -1;
|
||||
|
||||
const float sinr_cosp = 2.0f * (ahrs->quat.q0 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q3);
|
||||
const float cosr_cosp =
|
||||
1.0f - 2.0f * (ahrs->quat.q1 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q2);
|
||||
eulr->pit = atan2f(sinr_cosp, cosr_cosp);
|
||||
|
||||
const float sinp = 2.0f * (ahrs->quat.q0 * ahrs->quat.q2 -
|
||||
ahrs->quat.q3 * ahrs->quat.q1);
|
||||
|
||||
if (fabsf(sinp) >= 1.0f)
|
||||
eulr->rol = copysignf(M_PI / 2.0f, sinp);
|
||||
else
|
||||
eulr->rol = asinf(sinp);
|
||||
|
||||
const float siny_cosp = 2.0f * (ahrs->quat.q0 * ahrs->quat.q3 +
|
||||
ahrs->quat.q1 * ahrs->quat.q2);
|
||||
const float cosy_cosp =
|
||||
1.0f - 2.0f * (ahrs->quat.q2 * ahrs->quat.q2 +
|
||||
ahrs->quat.q3 * ahrs->quat.q3);
|
||||
eulr->yaw = atan2f(siny_cosp, cosy_cosp);
|
||||
|
||||
#if 0
|
||||
eulr->yaw *= M_RAD2DEG_MULT;
|
||||
eulr->rol *= M_RAD2DEG_MULT;
|
||||
eulr->pit *= M_RAD2DEG_MULT;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief 将对应数据置零
|
||||
*
|
||||
* \param eulr 被操作的数据
|
||||
*/
|
||||
void AHRS_ResetEulr(AHRS_Eulr_t *eulr) { memset(eulr, 0, sizeof(*eulr)); }
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
114
User/component/ahrs.h
Normal file
114
User/component/ahrs.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
开源的AHRS算法。
|
||||
MadgwickAHRS
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* 欧拉角(Euler angle) */
|
||||
typedef struct {
|
||||
float yaw; /* 偏航角(Yaw angle) */
|
||||
float pit; /* 俯仰角(Pitch angle) */
|
||||
float rol; /* 翻滚角(Roll angle) */
|
||||
} AHRS_Eulr_t;
|
||||
|
||||
/* 加速度计 Accelerometer */
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} AHRS_Accl_t;
|
||||
|
||||
/* 陀螺仪 Gyroscope */
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} AHRS_Gyro_t;
|
||||
|
||||
/* 磁力计 Magnetometer */
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} AHRS_Magn_t;
|
||||
|
||||
/* 四元数 */
|
||||
typedef struct {
|
||||
float q0;
|
||||
float q1;
|
||||
float q2;
|
||||
float q3;
|
||||
} AHRS_Quaternion_t;
|
||||
|
||||
/* 姿态解算算法主结构体 */
|
||||
typedef struct {
|
||||
/* 四元数 */
|
||||
AHRS_Quaternion_t quat;
|
||||
|
||||
float inv_sample_freq; /* 采样频率的的倒数 */
|
||||
} AHRS_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/**
|
||||
* @brief 初始化姿态解算
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param magn 磁力计数据
|
||||
* @param sample_freq 采样频率
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Init(AHRS_t *ahrs, const AHRS_Magn_t *magn, float sample_freq);
|
||||
|
||||
/**
|
||||
* @brief 姿态运算更新一次
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param accl 加速度计数据
|
||||
* @param gyro 陀螺仪数据
|
||||
* @param magn 磁力计数据
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Update(AHRS_t *ahrs, const AHRS_Accl_t *accl,
|
||||
const AHRS_Gyro_t *gyro, const AHRS_Magn_t *magn);
|
||||
|
||||
/**
|
||||
* @brief 通过姿态解算主结构体中的四元数计算欧拉角
|
||||
*
|
||||
* @param eulr 欧拉角
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_GetEulr(AHRS_Eulr_t *eulr, const AHRS_t *ahrs);
|
||||
|
||||
/**
|
||||
* \brief 将对应数据置零
|
||||
*
|
||||
* \param eulr 被操作的数据
|
||||
*/
|
||||
void AHRS_ResetEulr(AHRS_Eulr_t *eulr);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
7
User/component/component_config.yaml
Normal file
7
User/component/component_config.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
ahrs:
|
||||
dependencies:
|
||||
- component/user_math.h
|
||||
enabled: true
|
||||
user_math:
|
||||
dependencies: []
|
||||
enabled: true
|
||||
138
User/component/user_math.c
Normal file
138
User/component/user_math.c
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
自定义的数学运算。
|
||||
*/
|
||||
|
||||
#include "user_math.h"
|
||||
#include <string.h>
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
inline float InvSqrt(float x) {
|
||||
//#if 0
|
||||
/* Fast inverse square-root */
|
||||
/* See: http://en.wikipedia.org/wiki/Fast_inverse_square_root */
|
||||
float halfx = 0.5f * x;
|
||||
float y = x;
|
||||
long i = *(long*)&y;
|
||||
i = 0x5f3759df - (i>>1);
|
||||
y = *(float*)&i;
|
||||
y = y * (1.5f - (halfx * y * y));
|
||||
y = y * (1.5f - (halfx * y * y));
|
||||
return y;
|
||||
//#else
|
||||
// return 1.0f / sqrtf(x);
|
||||
//#endif
|
||||
}
|
||||
|
||||
inline float AbsClip(float in, float limit) {
|
||||
return (in < -limit) ? -limit : ((in > limit) ? limit : in);
|
||||
}
|
||||
|
||||
float fAbs(float in){
|
||||
return (in > 0) ? in : -in;
|
||||
}
|
||||
|
||||
inline void Clip(float *origin, float min, float max) {
|
||||
if (*origin > max) *origin = max;
|
||||
if (*origin < min) *origin = min;
|
||||
}
|
||||
|
||||
inline float Sign(float in) { return (in > 0) ? 1.0f : 0.0f; }
|
||||
|
||||
/**
|
||||
* \brief 将运动向量置零
|
||||
*
|
||||
* \param mv 被操作的值
|
||||
*/
|
||||
inline void ResetMoveVector(MoveVector_t *mv) { memset(mv, 0, sizeof(*mv)); }
|
||||
|
||||
/**
|
||||
* \brief 计算循环值的误差,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器:相差1.5PI其实等于相差-0.5PI
|
||||
*
|
||||
* \param sp 被操作的值
|
||||
* \param fb 变化量
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
*
|
||||
* \return 函数运行结果
|
||||
*/
|
||||
inline float CircleError(float sp, float fb, float range) {
|
||||
float error = sp - fb;
|
||||
if (range > 0.0f) {
|
||||
float half_range = range / 2.0f;
|
||||
|
||||
if (error > half_range)
|
||||
error -= range;
|
||||
else if (error < -half_range)
|
||||
error += range;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief 循环加法,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器,在0-2PI内变化,1.5PI + 1.5PI = 1PI
|
||||
*
|
||||
* \param origin 被操作的值
|
||||
* \param delta 变化量
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
*/
|
||||
inline void CircleAdd(float *origin, float delta, float range) {
|
||||
float out = *origin + delta;
|
||||
if (range > 0.0f) {
|
||||
if (out >= range)
|
||||
out -= range;
|
||||
else if (out < 0.0f)
|
||||
out += range;
|
||||
}
|
||||
*origin = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 循环值取反
|
||||
*
|
||||
* @param origin 被操作的值
|
||||
*/
|
||||
inline void CircleReverse(float *origin) { *origin = -(*origin) + M_2PI; }
|
||||
|
||||
/**
|
||||
* @brief 根据目标弹丸速度计算摩擦轮转速
|
||||
*
|
||||
* @param bullet_speed 弹丸速度
|
||||
* @param fric_radius 摩擦轮半径
|
||||
* @param is17mm 是否为17mm
|
||||
* @return 摩擦轮转速
|
||||
*/
|
||||
inline float CalculateRpm(float bullet_speed, float fric_radius, bool is17mm) {
|
||||
if (bullet_speed == 0.0f) return 0.f;
|
||||
if (is17mm) {
|
||||
if (bullet_speed == 15.0f) return 4670.f;
|
||||
if (bullet_speed == 18.0f) return 5200.f;
|
||||
if (bullet_speed == 30.0f) return 7350.f;
|
||||
} else {
|
||||
if (bullet_speed == 10.0f) return 4450.f;
|
||||
if (bullet_speed == 16.0f) return 5800.f;
|
||||
}
|
||||
|
||||
/* 不为裁判系统设定值时,计算转速 */
|
||||
return 60.0f * (float)bullet_speed / (M_2PI * fric_radius);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @brief 断言失败处理
|
||||
// *
|
||||
// * @param file 文件名
|
||||
// * @param line 行号
|
||||
// */
|
||||
// void VerifyFailed(const char *file, uint32_t line) {
|
||||
// UNUSED(file);
|
||||
// UNUSED(line);
|
||||
// while (1) {
|
||||
// __NOP();
|
||||
// }
|
||||
// }
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
179
User/component/user_math.h
Normal file
179
User/component/user_math.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
自定义的数学运算。
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
#define M_DEG2RAD_MULT (0.01745329251f)
|
||||
#define M_RAD2DEG_MULT (57.2957795131f)
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
#endif
|
||||
|
||||
#ifndef M_2PI
|
||||
#define M_2PI 6.28318530717958647692f
|
||||
#endif
|
||||
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((__packed__))
|
||||
#endif /* __packed */
|
||||
|
||||
#define max(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#define min(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
|
||||
|
||||
/* 移动向量 */
|
||||
typedef struct {
|
||||
float vx; /* 前后平移 */
|
||||
float vy; /* 左右平移 */
|
||||
float wz; /* 转动 */
|
||||
} MoveVector_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
float InvSqrt(float x);
|
||||
|
||||
float AbsClip(float in, float limit);
|
||||
|
||||
float fAbs(float in);
|
||||
|
||||
void Clip(float *origin, float min, float max);
|
||||
|
||||
float Sign(float in);
|
||||
|
||||
/**
|
||||
* \brief 将运动向量置零
|
||||
*
|
||||
* \param mv 被操作的值
|
||||
*/
|
||||
void ResetMoveVector(MoveVector_t *mv);
|
||||
|
||||
/**
|
||||
* \brief 计算循环值的误差,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器:相差1.5PI其实等于相差-0.5PI
|
||||
*
|
||||
* \param sp 被操作的值
|
||||
* \param fb 变化量
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
*
|
||||
* \return 函数运行结果
|
||||
*/
|
||||
float CircleError(float sp, float fb, float range);
|
||||
|
||||
/**
|
||||
* \brief 循环加法,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器,在0-2PI内变化,1.5PI + 1.5PI = 1PI
|
||||
*
|
||||
* \param origin 被操作的值
|
||||
* \param delta 变化量
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
*/
|
||||
void CircleAdd(float *origin, float delta, float range);
|
||||
|
||||
/**
|
||||
* @brief 循环值取反
|
||||
*
|
||||
* @param origin 被操作的值
|
||||
*/
|
||||
void CircleReverse(float *origin);
|
||||
|
||||
/**
|
||||
* @brief 根据目标弹丸速度计算摩擦轮转速
|
||||
*
|
||||
* @param bullet_speed 弹丸速度
|
||||
* @param fric_radius 摩擦轮半径
|
||||
* @param is17mm 是否为17mm
|
||||
* @return 摩擦轮转速
|
||||
*/
|
||||
float CalculateRpm(float bullet_speed, float fric_radius, bool is17mm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
/**
|
||||
* @brief 如果表达式的值为假则运行处理函数
|
||||
*
|
||||
*/
|
||||
#define ASSERT(expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
VerifyFailed(__FILE__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
|
||||
/**
|
||||
* @brief 未定DEBUG,表达式不会运行,断言被忽略
|
||||
*
|
||||
*/
|
||||
#define ASSERT(expr) ((void)(0))
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
/**
|
||||
* @brief 如果表达式的值为假则运行处理函数
|
||||
*
|
||||
*/
|
||||
#define VERIFY(expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
VerifyFailed(__FILE__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
|
||||
/**
|
||||
* @brief 表达式会运行,忽略表达式结果
|
||||
*
|
||||
*/
|
||||
#define VERIFY(expr) ((void)(expr))
|
||||
#endif
|
||||
|
||||
// /**
|
||||
// * @brief 断言失败处理
|
||||
// *
|
||||
// * @param file 文件名
|
||||
// * @param line 行号
|
||||
// */
|
||||
// void VerifyFailed(const char *file, uint32_t line);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
47
User/device/device.h
Normal file
47
User/device/device.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
#define DEVICE_OK (0)
|
||||
#define DEVICE_ERR (-1)
|
||||
#define DEVICE_ERR_NULL (-2)
|
||||
#define DEVICE_ERR_INITED (-3)
|
||||
#define DEVICE_ERR_NO_DEV (-4)
|
||||
|
||||
/* AUTO GENERATED SIGNALS BEGIN */
|
||||
/* No signals defined */
|
||||
/* AUTO GENERATED SIGNALS END */
|
||||
|
||||
/* USER SIGNALS BEGIN */
|
||||
|
||||
/* USER SIGNALS END */
|
||||
/*设备层通用Header*/
|
||||
typedef struct {
|
||||
bool online;
|
||||
uint64_t last_online_time;
|
||||
} DEVICE_Header_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
12
User/device/device_config.yaml
Normal file
12
User/device/device_config.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
dm_imu:
|
||||
bsp_config: {}
|
||||
enabled: true
|
||||
motor:
|
||||
bsp_config: {}
|
||||
enabled: true
|
||||
motor_lk:
|
||||
bsp_config: {}
|
||||
enabled: true
|
||||
motor_lz:
|
||||
bsp_config: {}
|
||||
enabled: true
|
||||
270
User/device/dm_imu.c
Normal file
270
User/device/dm_imu.c
Normal file
@ -0,0 +1,270 @@
|
||||
/*
|
||||
DM_IMU数据获取(CAN)
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "dm_imu.h"
|
||||
|
||||
#include "bsp/can.h"
|
||||
#include "bsp/time.h"
|
||||
#include "component/user_math.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define DM_IMU_OFFLINE_TIMEOUT 1000 // 设备离线判定时间1000ms
|
||||
|
||||
#define ACCEL_CAN_MAX (58.8f)
|
||||
#define ACCEL_CAN_MIN (-58.8f)
|
||||
#define GYRO_CAN_MAX (34.88f)
|
||||
#define GYRO_CAN_MIN (-34.88f)
|
||||
#define PITCH_CAN_MAX (90.0f)
|
||||
#define PITCH_CAN_MIN (-90.0f)
|
||||
#define ROLL_CAN_MAX (180.0f)
|
||||
#define ROLL_CAN_MIN (-180.0f)
|
||||
#define YAW_CAN_MAX (180.0f)
|
||||
#define YAW_CAN_MIN (-180.0f)
|
||||
#define TEMP_MIN (0.0f)
|
||||
#define TEMP_MAX (60.0f)
|
||||
#define Quaternion_MIN (-1.0f)
|
||||
#define Quaternion_MAX (1.0f)
|
||||
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function --------------------------------------------------------- */
|
||||
|
||||
static uint8_t count = 0; // 计数器,用于判断设备是否离线
|
||||
/**
|
||||
* @brief: 无符号整数转换为浮点数函数
|
||||
*/
|
||||
static float uint_to_float(int x_int, float x_min, float x_max, int bits)
|
||||
{
|
||||
float span = x_max - x_min;
|
||||
float offset = x_min;
|
||||
return ((float)x_int)*span/((float)((1<<bits)-1)) + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析加速度计数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseAccelData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
int8_t temp = data[1];
|
||||
uint16_t acc_x_raw = (data[3] << 8) | data[2];
|
||||
uint16_t acc_y_raw = (data[5] << 8) | data[4];
|
||||
uint16_t acc_z_raw = (data[7] << 8) | data[6];
|
||||
imu->data.temp = (float)temp;
|
||||
imu->data.accl.x = uint_to_float(acc_x_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
|
||||
imu->data.accl.y = uint_to_float(acc_y_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
|
||||
imu->data.accl.z = uint_to_float(acc_z_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
/**
|
||||
* @brief 解析陀螺仪数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseGyroData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
uint16_t gyro_x_raw = (data[3] << 8) | data[2];
|
||||
uint16_t gyro_y_raw = (data[5] << 8) | data[4];
|
||||
uint16_t gyro_z_raw = (data[7] << 8) | data[6];
|
||||
imu->data.gyro.x = uint_to_float(gyro_x_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
|
||||
imu->data.gyro.y = uint_to_float(gyro_y_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
|
||||
imu->data.gyro.z = uint_to_float(gyro_z_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
/**
|
||||
* @brief 解析欧拉角数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseEulerData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
uint16_t pit_raw = (data[3] << 8) | data[2];
|
||||
uint16_t yaw_raw = (data[5] << 8) | data[4];
|
||||
uint16_t rol_raw = (data[7] << 8) | data[6];
|
||||
imu->data.euler.pit = uint_to_float(pit_raw, PITCH_CAN_MIN, PITCH_CAN_MAX, 16) * M_DEG2RAD_MULT;
|
||||
imu->data.euler.yaw = uint_to_float(yaw_raw, YAW_CAN_MIN, YAW_CAN_MAX, 16) * M_DEG2RAD_MULT;
|
||||
imu->data.euler.rol = uint_to_float(rol_raw, ROLL_CAN_MIN, ROLL_CAN_MAX, 16) * M_DEG2RAD_MULT;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析四元数数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseQuaternionData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
int w = (data[1] << 6) | ((data[2] & 0xF8) >> 2);
|
||||
int x = ((data[2] & 0x03) << 12) | (data[3] << 4) | ((data[4] & 0xF0) >> 4);
|
||||
int y = ((data[4] & 0x0F) << 10) | (data[5] << 2) | ((data[6] & 0xC0) >> 6);
|
||||
int z = ((data[6] & 0x3F) << 8) | data[7];
|
||||
imu->data.quat.q0 = uint_to_float(w, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
imu->data.quat.q1 = uint_to_float(x, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
imu->data.quat.q2 = uint_to_float(y, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
imu->data.quat.q3 = uint_to_float(z, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化DM IMU设备
|
||||
*/
|
||||
int8_t DM_IMU_Init(DM_IMU_t *imu, DM_IMU_Param_t *param) {
|
||||
if (imu == NULL || param == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
|
||||
// 初始化设备头部
|
||||
imu->header.online = false;
|
||||
imu->header.last_online_time = 0;
|
||||
|
||||
// 配置参数
|
||||
imu->param.can = param->can;
|
||||
imu->param.can_id = param->can_id;
|
||||
imu->param.device_id = param->device_id;
|
||||
imu->param.master_id = param->master_id;
|
||||
|
||||
// 清零数据
|
||||
memset(&imu->data, 0, sizeof(DM_IMU_Data_t));
|
||||
|
||||
// 注册CAN接收队列,用于接收回复报文
|
||||
int8_t result = BSP_CAN_RegisterId(imu->param.can, imu->param.master_id, 10);
|
||||
if (result != BSP_OK) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 请求IMU数据
|
||||
*/
|
||||
int8_t DM_IMU_Request(DM_IMU_t *imu, DM_IMU_RID_t rid) {
|
||||
if (imu == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
|
||||
// 构造发送数据:id_L, id_H(DM_IMU_ID), RID, 0xcc
|
||||
uint8_t tx_data[4] = {
|
||||
imu->param.device_id & 0xFF, // id_L
|
||||
(imu->param.device_id >> 8) & 0xFF, // id_H
|
||||
(uint8_t)rid, // RID
|
||||
0xCC // 固定值
|
||||
};
|
||||
|
||||
// 发送标准数据帧
|
||||
BSP_CAN_StdDataFrame_t frame = {
|
||||
.id = imu->param.can_id,
|
||||
.dlc = 4,
|
||||
};
|
||||
memcpy(frame.data, tx_data, 4);
|
||||
int8_t result = BSP_CAN_TransmitStdDataFrame(imu->param.can, &frame);
|
||||
return (result == BSP_OK) ? DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新IMU数据(从CAN中获取所有数据并解析)
|
||||
*/
|
||||
int8_t DM_IMU_Update(DM_IMU_t *imu) {
|
||||
if (imu == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
|
||||
BSP_CAN_Message_t msg;
|
||||
int8_t result;
|
||||
bool data_received = false;
|
||||
|
||||
// 持续接收所有可用消息
|
||||
while ((result = BSP_CAN_GetMessage(imu->param.can, imu->param.master_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE)) == BSP_OK) {
|
||||
// 验证回复数据格式(至少检查数据长度)
|
||||
if (msg.dlc < 3) {
|
||||
continue; // 跳过无效消息
|
||||
}
|
||||
|
||||
// 根据数据位的第0位确定反馈报文类型
|
||||
uint8_t rid = msg.data[0] & 0x0F; // 取第0位的低4位作为RID
|
||||
|
||||
// 根据RID类型解析数据
|
||||
int8_t parse_result = DEVICE_ERR;
|
||||
switch (rid) {
|
||||
case 0x01: // RID_ACCL
|
||||
parse_result = DM_IMU_ParseAccelData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
case 0x02: // RID_GYRO
|
||||
parse_result = DM_IMU_ParseGyroData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
case 0x03: // RID_EULER
|
||||
parse_result = DM_IMU_ParseEulerData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
case 0x04: // RID_QUATERNION
|
||||
parse_result = DM_IMU_ParseQuaternionData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
default:
|
||||
continue; // 跳过未知类型的消息
|
||||
}
|
||||
|
||||
// 如果解析成功,标记为收到数据
|
||||
if (parse_result == DEVICE_OK) {
|
||||
data_received = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果收到任何有效数据,更新设备状态
|
||||
if (data_received) {
|
||||
imu->header.online = true;
|
||||
imu->header.last_online_time = BSP_TIME_Get_ms();
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动更新IMU所有数据(包括加速度计、陀螺仪、欧拉角和四元数,最高1khz)
|
||||
*/
|
||||
int8_t DM_IMU_AutoUpdateAll(DM_IMU_t *imu){
|
||||
if (imu == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
switch (count) {
|
||||
case 0:
|
||||
DM_IMU_Request(imu, RID_ACCL);
|
||||
break;
|
||||
case 1:
|
||||
DM_IMU_Request(imu, RID_GYRO);
|
||||
break;
|
||||
case 2:
|
||||
DM_IMU_Request(imu, RID_EULER);
|
||||
break;
|
||||
case 3:
|
||||
DM_IMU_Request(imu, RID_QUATERNION);
|
||||
DM_IMU_Update(imu); // 更新所有数据
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
if (count >= 4) {
|
||||
count = 0; // 重置计数器
|
||||
}
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查设备是否在线
|
||||
*/
|
||||
bool DM_IMU_IsOnline(DM_IMU_t *imu) {
|
||||
if (imu == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t current_time = BSP_TIME_Get_ms();
|
||||
return imu->header.online &&
|
||||
(current_time - imu->header.last_online_time < DM_IMU_OFFLINE_TIMEOUT);
|
||||
}
|
||||
90
User/device/dm_imu.h
Normal file
90
User/device/dm_imu.h
Normal file
@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device/device.h"
|
||||
#include "component/ahrs.h"
|
||||
#include "bsp/can.h"
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
|
||||
#define DM_IMU_CAN_ID_DEFAULT 0x6FF
|
||||
#define DM_IMU_ID_DEFAULT 0x42
|
||||
#define DM_IMU_MST_ID_DEFAULT 0x43
|
||||
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
typedef struct {
|
||||
BSP_CAN_t can; // CAN总线句柄
|
||||
uint16_t can_id; // CAN通信ID
|
||||
uint8_t device_id; // 设备ID
|
||||
uint8_t master_id; // 主机ID
|
||||
} DM_IMU_Param_t;
|
||||
typedef enum {
|
||||
RID_ACCL = 0x01, // 加速度计
|
||||
RID_GYRO = 0x02, // 陀螺仪
|
||||
RID_EULER = 0x03, // 欧拉角
|
||||
RID_QUATERNION = 0x04, // 四元数
|
||||
} DM_IMU_RID_t;
|
||||
|
||||
typedef struct {
|
||||
AHRS_Accl_t accl; // 加速度计
|
||||
AHRS_Gyro_t gyro; // 陀螺仪
|
||||
AHRS_Eulr_t euler; // 欧拉角
|
||||
AHRS_Quaternion_t quat; // 四元数
|
||||
float temp; // 温度
|
||||
} DM_IMU_Data_t;
|
||||
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
DM_IMU_Param_t param; // IMU参数配置
|
||||
DM_IMU_Data_t data; // IMU数据
|
||||
} DM_IMU_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化DM IMU设备
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @param param IMU参数配置指针
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_Init(DM_IMU_t *imu, DM_IMU_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 请求IMU数据
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @param rid 请求的数据类型
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_Request(DM_IMU_t *imu, DM_IMU_RID_t rid);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 更新IMU数据(从CAN中获取所有数据并解析)
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_Update(DM_IMU_t *imu);
|
||||
|
||||
/**
|
||||
* @brief 自动更新IMU所有数据(包括加速度计、陀螺仪、欧拉角和四元数,最高1khz,运行4次才有完整数据)
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_AutoUpdateAll(DM_IMU_t *imu);
|
||||
|
||||
/**
|
||||
* @brief 检查设备是否在线
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @return true 在线,false 离线
|
||||
*/
|
||||
bool DM_IMU_IsOnline(DM_IMU_t *imu);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
52
User/device/motor.c
Normal file
52
User/device/motor.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
电机通用函数
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "motor.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
float MOTOR_GetRotorAbsAngle(const MOTOR_t *motor) {
|
||||
if (motor == NULL) return DEVICE_ERR_NULL;
|
||||
return motor->feedback.rotor_abs_angle;
|
||||
}
|
||||
|
||||
float MOTOR_GetRotorSpeed(const MOTOR_t *motor) {
|
||||
if (motor == NULL) return DEVICE_ERR_NULL;
|
||||
return motor->feedback.rotor_speed;
|
||||
}
|
||||
|
||||
float MOTOR_GetTorqueCurrent(const MOTOR_t *motor) {
|
||||
if (motor == NULL) return DEVICE_ERR_NULL;
|
||||
return motor->feedback.torque_current;
|
||||
}
|
||||
|
||||
float MOTOR_GetTemp(const MOTOR_t *motor) {
|
||||
if (motor == NULL) return DEVICE_ERR_NULL;
|
||||
return motor->feedback.temp;
|
||||
}
|
||||
68
User/device/motor.h
Normal file
68
User/device/motor.h
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device/device.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef struct {
|
||||
float rotor_abs_angle; /* 转子绝对角度 */
|
||||
float rotor_speed; /* 实际转子转速 */
|
||||
float torque_current; /* 转矩电流 */
|
||||
float temp; /* 温度 */
|
||||
} MOTOR_Feedback_t;
|
||||
|
||||
/**
|
||||
* @brief mit电机输出参数结构体
|
||||
*/
|
||||
typedef struct {
|
||||
float torque; /* 目标力矩 */
|
||||
float velocity; /* 目标速度 */
|
||||
float angle; /* 目标位置 */
|
||||
float kp; /* 位置环增益 */
|
||||
float kd; /* 速度环增益 */
|
||||
} MOTOR_MIT_Output_t;
|
||||
|
||||
/**
|
||||
* @brief 转矩电流控制模式参数结构体
|
||||
*/
|
||||
typedef struct {
|
||||
float current; /* 目标电流 */
|
||||
} MOTOR_Current_Output_t;
|
||||
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
bool reverse; /* 是否反装 true表示反装 */
|
||||
MOTOR_Feedback_t feedback;
|
||||
} MOTOR_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
float MOTOR_GetRotorAbsAngle(const MOTOR_t *motor);
|
||||
float MOTOR_GetRotorSpeed(const MOTOR_t *motor);
|
||||
float MOTOR_GetTorqueCurrent(const MOTOR_t *motor);
|
||||
float MOTOR_GetTemp(const MOTOR_t *motor);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
329
User/device/motor_lk.c
Normal file
329
User/device/motor_lk.c
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
LK电机驱动
|
||||
*/
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "motor_lk.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "bsp/can.h"
|
||||
#include "bsp/mm.h"
|
||||
#include "bsp/time.h"
|
||||
#include "component/user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define LK_CTRL_ID_BASE (0x140)
|
||||
#define LK_FB_ID_BASE (0x240)
|
||||
|
||||
// LK电机命令字节定义
|
||||
#define LK_CMD_FEEDBACK (0x9C) // 反馈命令字节
|
||||
#define LK_CMD_MOTOR_OFF (0x80) // 电机关闭命令
|
||||
#define LK_CMD_MOTOR_ON (0x88) // 电机运行命令
|
||||
#define LK_CMD_TORQUE_CTRL (0xA1) // 转矩闭环控制命令
|
||||
|
||||
// LK电机参数定义
|
||||
#define LK_CURR_LSB_MF (33.0f / 4096.0f) // MF电机转矩电流分辨率 A/LSB
|
||||
#define LK_CURR_LSB_MG (66.0f / 4096.0f) // MG电机转矩电流分辨率 A/LSB
|
||||
#define LK_POWER_RANGE_MS (1000) // MS电机功率范围 ±1000
|
||||
#define LK_TORQUE_RANGE (2048) // 转矩控制值范围 ±2048
|
||||
#define LK_TORQUE_CURRENT_MF (16.5f) // MF电机最大转矩电流 A
|
||||
#define LK_TORQUE_CURRENT_MG (33.0f) // MG电机最大转矩电流 A
|
||||
|
||||
#define MOTOR_TX_BUF_SIZE (8)
|
||||
#define MOTOR_RX_BUF_SIZE (8)
|
||||
|
||||
// 编码器分辨率定义
|
||||
#define LK_ENC_14BIT_MAX (16383) // 14位编码器最大值
|
||||
#define LK_ENC_15BIT_MAX (32767) // 15位编码器最大值
|
||||
#define LK_ENC_16BIT_MAX (65535) // 16位编码器最大值
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static MOTOR_LK_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
|
||||
|
||||
/* Private functions -------------------------------------------------------- */
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
static float MOTOR_LK_GetCurrentLSB(MOTOR_LK_Module_t module) {
|
||||
switch (module) {
|
||||
case MOTOR_LK_MF9025:
|
||||
case MOTOR_LK_MF9035:
|
||||
return LK_CURR_LSB_MF;
|
||||
default:
|
||||
return LK_CURR_LSB_MG; // 默认使用MG的分辨率
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t MOTOR_LK_GetEncoderMax(MOTOR_LK_Module_t module) {
|
||||
// 根据电机型号返回编码器最大值,这里假设都使用16位编码器
|
||||
// 实际使用时需要根据具体电机型号配置
|
||||
return LK_ENC_16BIT_MAX;
|
||||
}
|
||||
|
||||
static MOTOR_LK_CANManager_t* MOTOR_LK_GetCANManager(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return NULL;
|
||||
return can_managers[can];
|
||||
}
|
||||
|
||||
static int8_t MOTOR_LK_CreateCANManager(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
|
||||
if (can_managers[can] != NULL) return DEVICE_OK;
|
||||
|
||||
can_managers[can] = (MOTOR_LK_CANManager_t*)BSP_Malloc(sizeof(MOTOR_LK_CANManager_t));
|
||||
if (can_managers[can] == NULL) return DEVICE_ERR;
|
||||
|
||||
memset(can_managers[can], 0, sizeof(MOTOR_LK_CANManager_t));
|
||||
can_managers[can]->can = can;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
static void MOTOR_LK_Decode(MOTOR_LK_t *motor, BSP_CAN_Message_t *msg) {
|
||||
|
||||
// 检查命令字节是否为反馈命令
|
||||
if (msg->data[0] != LK_CMD_FEEDBACK) {
|
||||
// 如果不是标准反馈命令,可能是其他格式的数据
|
||||
// 临时跳过命令字节检查,直接解析数据
|
||||
// return;
|
||||
}
|
||||
|
||||
// 解析温度 (DATA[1])
|
||||
motor->motor.feedback.temp = (int8_t)msg->data[1];
|
||||
|
||||
// 解析转矩电流值或功率值 (DATA[2], DATA[3])
|
||||
int16_t raw_current_or_power = (int16_t)((msg->data[3] << 8) | msg->data[2]);
|
||||
|
||||
// 根据电机类型解析电流或功率
|
||||
switch (motor->param.module) {
|
||||
case MOTOR_LK_MF9025:
|
||||
case MOTOR_LK_MF9035:
|
||||
motor->motor.feedback.torque_current = raw_current_or_power * MOTOR_LK_GetCurrentLSB(motor->param.module);
|
||||
break;
|
||||
default:
|
||||
motor->motor.feedback.torque_current = (float)raw_current_or_power;
|
||||
break;
|
||||
}
|
||||
|
||||
// 解析转速 (DATA[4], DATA[5]) - 单位:1dps/LSB
|
||||
int16_t raw_speed = (int16_t)((msg->data[5] << 8) | msg->data[4]);
|
||||
motor->motor.feedback.rotor_speed = motor->param.reverse ? -raw_speed : raw_speed;
|
||||
|
||||
// 解析编码器值 (DATA[6], DATA[7])
|
||||
uint16_t raw_encoder = (uint16_t)((msg->data[7] << 8) | msg->data[6]);
|
||||
uint16_t encoder_max = MOTOR_LK_GetEncoderMax(motor->param.module);
|
||||
|
||||
// 将编码器值转换为弧度 (0 ~ 2π)
|
||||
float angle = (float)raw_encoder / (float)encoder_max * M_2PI;
|
||||
motor->motor.feedback.rotor_abs_angle = motor->param.reverse ? (M_2PI - angle) : angle;
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
int8_t MOTOR_LK_Register(MOTOR_LK_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
if (MOTOR_LK_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
|
||||
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
|
||||
if (manager == NULL) return DEVICE_ERR;
|
||||
|
||||
// 检查是否已注册
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
|
||||
return DEVICE_ERR_INITED;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查数量
|
||||
if (manager->motor_count >= MOTOR_LK_MAX_MOTORS) return DEVICE_ERR;
|
||||
|
||||
// 创建新电机实例
|
||||
MOTOR_LK_t *new_motor = (MOTOR_LK_t*)BSP_Malloc(sizeof(MOTOR_LK_t));
|
||||
if (new_motor == NULL) return DEVICE_ERR;
|
||||
|
||||
memcpy(&new_motor->param, param, sizeof(MOTOR_LK_Param_t));
|
||||
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
|
||||
new_motor->motor.reverse = param->reverse;
|
||||
|
||||
// 对于某些LK电机,反馈数据可能通过命令ID发送
|
||||
// 根据实际测试,使用命令ID接收反馈数据
|
||||
uint16_t feedback_id = param->id; // 使用命令ID作为反馈ID
|
||||
|
||||
// 注册CAN接收ID
|
||||
if (BSP_CAN_RegisterId(param->can, feedback_id, 3) != BSP_OK) {
|
||||
BSP_Free(new_motor);
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
manager->motors[manager->motor_count] = new_motor;
|
||||
manager->motor_count++;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_Update(MOTOR_LK_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
|
||||
if (manager == NULL) return DEVICE_ERR_NO_DEV;
|
||||
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
MOTOR_LK_t *motor = manager->motors[i];
|
||||
if (motor && motor->param.id == param->id) {
|
||||
// 对于某些LK电机,反馈数据通过命令ID发送
|
||||
uint16_t feedback_id = param->id;
|
||||
|
||||
BSP_CAN_Message_t rx_msg;
|
||||
if (BSP_CAN_GetMessage(param->can, feedback_id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
|
||||
uint64_t now_time = BSP_TIME_Get();
|
||||
if (now_time - motor->motor.header.last_online_time > 1000) {
|
||||
motor->motor.header.online = false;
|
||||
return DEVICE_ERR_NO_DEV;
|
||||
}
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
motor->motor.header.online = true;
|
||||
motor->motor.header.last_online_time = BSP_TIME_Get();
|
||||
MOTOR_LK_Decode(motor, &rx_msg);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
}
|
||||
return DEVICE_ERR_NO_DEV;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_UpdateAll(void) {
|
||||
int8_t ret = DEVICE_OK;
|
||||
for (int can = 0; can < BSP_CAN_NUM; can++) {
|
||||
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager((BSP_CAN_t)can);
|
||||
if (manager == NULL) continue;
|
||||
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
MOTOR_LK_t *motor = manager->motors[i];
|
||||
if (motor != NULL) {
|
||||
if (MOTOR_LK_Update(&motor->param) != DEVICE_OK) {
|
||||
ret = DEVICE_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_SetOutput(MOTOR_LK_Param_t *param, float value) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
|
||||
if (manager == NULL) return DEVICE_ERR_NO_DEV;
|
||||
|
||||
// 限制输出值范围
|
||||
if (value > 1.0f) value = 1.0f;
|
||||
if (value < -1.0f) value = -1.0f;
|
||||
|
||||
MOTOR_LK_t *motor = MOTOR_LK_GetMotor(param);
|
||||
if (motor == NULL) return DEVICE_ERR_NO_DEV;
|
||||
|
||||
// 根据反转参数调整输出
|
||||
float output = param->reverse ? -value : value;
|
||||
|
||||
// 转矩闭环控制命令 - 将输出值转换为转矩控制值
|
||||
int16_t torque_control = (int16_t)(output * (float)LK_TORQUE_RANGE);
|
||||
|
||||
// 构建CAN帧
|
||||
BSP_CAN_StdDataFrame_t tx_frame;
|
||||
tx_frame.id = param->id;
|
||||
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
|
||||
|
||||
tx_frame.data[0] = LK_CMD_TORQUE_CTRL;
|
||||
tx_frame.data[1] = 0x00;
|
||||
tx_frame.data[2] = 0x00;
|
||||
tx_frame.data[3] = 0x00;
|
||||
tx_frame.data[4] = (uint8_t)(torque_control & 0xFF);
|
||||
tx_frame.data[5] = (uint8_t)((torque_control >> 8) & 0xFF);
|
||||
tx_frame.data[6] = 0x00;
|
||||
tx_frame.data[7] = 0x00;
|
||||
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_Ctrl(MOTOR_LK_Param_t *param) {
|
||||
// 对于LK电机,每次设置输出时就直接发送控制命令
|
||||
// 这个函数可以用于发送其他控制命令,如电机开启/关闭
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_MotorOn(MOTOR_LK_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
BSP_CAN_StdDataFrame_t tx_frame;
|
||||
tx_frame.id = param->id;
|
||||
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
|
||||
|
||||
// 电机运行命令
|
||||
tx_frame.data[0] = LK_CMD_MOTOR_ON; // 命令字节
|
||||
tx_frame.data[1] = 0x00;
|
||||
tx_frame.data[2] = 0x00;
|
||||
tx_frame.data[3] = 0x00;
|
||||
tx_frame.data[4] = 0x00;
|
||||
tx_frame.data[5] = 0x00;
|
||||
tx_frame.data[6] = 0x00;
|
||||
tx_frame.data[7] = 0x00;
|
||||
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_MotorOff(MOTOR_LK_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
BSP_CAN_StdDataFrame_t tx_frame;
|
||||
tx_frame.id = param->id;
|
||||
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
|
||||
|
||||
// 电机关闭命令
|
||||
tx_frame.data[0] = LK_CMD_MOTOR_OFF; // 命令字节
|
||||
tx_frame.data[1] = 0x00;
|
||||
tx_frame.data[2] = 0x00;
|
||||
tx_frame.data[3] = 0x00;
|
||||
tx_frame.data[4] = 0x00;
|
||||
tx_frame.data[5] = 0x00;
|
||||
tx_frame.data[6] = 0x00;
|
||||
tx_frame.data[7] = 0x00;
|
||||
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
MOTOR_LK_t* MOTOR_LK_GetMotor(MOTOR_LK_Param_t *param) {
|
||||
if (param == NULL) return NULL;
|
||||
|
||||
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
|
||||
if (manager == NULL) return NULL;
|
||||
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
MOTOR_LK_t *motor = manager->motors[i];
|
||||
if (motor && motor->param.id == param->id) {
|
||||
return motor;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_Relax(MOTOR_LK_Param_t *param) {
|
||||
return MOTOR_LK_SetOutput(param, 0.0f);
|
||||
}
|
||||
|
||||
int8_t MOTOR_LK_Offine(MOTOR_LK_Param_t *param) {
|
||||
MOTOR_LK_t *motor = MOTOR_LK_GetMotor(param);
|
||||
if (motor) {
|
||||
motor->motor.header.online = false;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
return DEVICE_ERR_NO_DEV;
|
||||
}
|
||||
119
User/device/motor_lk.h
Normal file
119
User/device/motor_lk.h
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device/device.h"
|
||||
#include "device/motor.h"
|
||||
#include "bsp/can.h"
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
#define MOTOR_LK_MAX_MOTORS 32
|
||||
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef enum {
|
||||
MOTOR_LK_MF9025,
|
||||
MOTOR_LK_MF9035,
|
||||
} MOTOR_LK_Module_t;
|
||||
|
||||
|
||||
/*每个电机需要的参数*/
|
||||
typedef struct {
|
||||
BSP_CAN_t can;
|
||||
uint16_t id;
|
||||
MOTOR_LK_Module_t module;
|
||||
bool reverse;
|
||||
} MOTOR_LK_Param_t;
|
||||
|
||||
/*电机实例*/
|
||||
typedef struct{
|
||||
MOTOR_LK_Param_t param;
|
||||
MOTOR_t motor;
|
||||
} MOTOR_LK_t;
|
||||
|
||||
/*CAN管理器,管理一个CAN总线上所有的电机*/
|
||||
typedef struct {
|
||||
BSP_CAN_t can;
|
||||
MOTOR_LK_t *motors[MOTOR_LK_MAX_MOTORS];
|
||||
uint8_t motor_count;
|
||||
} MOTOR_LK_CANManager_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 注册一个LK电机
|
||||
* @param param 电机参数
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_Register(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 更新指定电机数据
|
||||
* @param param 电机参数
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_Update(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 设置一个电机的输出
|
||||
* @param param 电机参数
|
||||
* @param value 输出值,范围[-1.0, 1.0]
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_SetOutput(MOTOR_LK_Param_t *param, float value);
|
||||
|
||||
/**
|
||||
* @brief 发送控制命令到电机,注意一个CAN可以控制多个电机,所以只需要发送一次即可
|
||||
* @param param 电机参数
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_Ctrl(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 发送电机开启命令
|
||||
* @param param 电机参数
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_MotorOn(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 发送电机关闭命令
|
||||
* @param param 电机参数
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_MotorOff(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 获取指定电机的实例指针
|
||||
* @param param 电机参数
|
||||
* @return
|
||||
*/
|
||||
MOTOR_LK_t* MOTOR_LK_GetMotor(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 使电机松弛(设置输出为0)
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_Relax(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 使电机离线(设置在线状态为false)
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_Offine(MOTOR_LK_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
int8_t MOTOR_LK_UpdateAll(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
440
User/device/motor_lz.c
Normal file
440
User/device/motor_lz.c
Normal file
@ -0,0 +1,440 @@
|
||||
/*
|
||||
灵足电机驱动
|
||||
|
||||
灵足电机通信协议:
|
||||
- CAN 2.0通信接口,波特率1Mbps
|
||||
- 采用扩展帧格式(29位ID)
|
||||
- ID格式:Bit28~24(通信类型) + Bit23~8(数据区2) + Bit7~0(目标地址)
|
||||
*/
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "motor_lz.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "bsp/can.h"
|
||||
#include "bsp/mm.h"
|
||||
#include "bsp/time.h"
|
||||
#include "component/user_math.h"
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
// 灵足电机协议参数
|
||||
#define LZ_ANGLE_RANGE_RAD (12.57f) /* 角度范围 ±12.57 rad */
|
||||
#define LZ_VELOCITY_RANGE_RAD_S (20.0f) /* 角速度范围 ±20 rad/s */
|
||||
#define LZ_TORQUE_RANGE_NM (60.0f) /* 力矩范围 ±60 Nm */
|
||||
#define LZ_KP_MAX (5000.0f) /* Kp最大值 */
|
||||
#define LZ_KD_MAX (100.0f) /* Kd最大值 */
|
||||
|
||||
#define LZ_RAW_VALUE_MAX (65535) /* 16位原始值最大值 */
|
||||
#define LZ_TEMP_SCALE (10.0f) /* 温度缩放因子 */
|
||||
|
||||
#define LZ_MAX_RECOVER_DIFF_RAD (0.28f)
|
||||
#define MOTOR_TX_BUF_SIZE (8)
|
||||
#define MOTOR_RX_BUF_SIZE (8)
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
|
||||
MOTOR_LZ_MotionParam_t lz_relax_param = {
|
||||
.target_angle = 0.0f,
|
||||
.target_velocity = 0.0f,
|
||||
.kp = 0.0f,
|
||||
.kd = 0.0f,
|
||||
.torque = 0.0f,
|
||||
};
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static MOTOR_LZ_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
|
||||
|
||||
/* Private function prototypes ---------------------------------------------- */
|
||||
static MOTOR_LZ_CANManager_t* MOTOR_LZ_GetCANManager(BSP_CAN_t can);
|
||||
static int8_t MOTOR_LZ_CreateCANManager(BSP_CAN_t can);
|
||||
static void MOTOR_LZ_Decode(MOTOR_LZ_t *motor, BSP_CAN_Message_t *msg);
|
||||
static uint32_t MOTOR_LZ_BuildExtID(MOTOR_LZ_CmdType_t cmd_type, uint16_t data2, uint8_t target_id);
|
||||
static uint16_t MOTOR_LZ_FloatToRaw(float value, float max_value);
|
||||
static float MOTOR_LZ_RawToFloat(uint16_t raw_value, float max_value);
|
||||
static int8_t MOTOR_LZ_SendExtFrame(BSP_CAN_t can, uint32_t ext_id, uint8_t *data, uint8_t dlc);
|
||||
static uint32_t MOTOR_LZ_IdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
|
||||
|
||||
/* Private functions -------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 获取CAN管理器
|
||||
*/
|
||||
static MOTOR_LZ_CANManager_t* MOTOR_LZ_GetCANManager(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return NULL;
|
||||
return can_managers[can];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建CAN管理器
|
||||
*/
|
||||
static int8_t MOTOR_LZ_CreateCANManager(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
|
||||
if (can_managers[can] != NULL) return DEVICE_OK;
|
||||
|
||||
can_managers[can] = (MOTOR_LZ_CANManager_t*)BSP_Malloc(sizeof(MOTOR_LZ_CANManager_t));
|
||||
if (can_managers[can] == NULL) return DEVICE_ERR;
|
||||
|
||||
memset(can_managers[can], 0, sizeof(MOTOR_LZ_CANManager_t));
|
||||
can_managers[can]->can = can;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 构建扩展ID
|
||||
*/
|
||||
static uint32_t MOTOR_LZ_BuildExtID(MOTOR_LZ_CmdType_t cmd_type, uint16_t data2, uint8_t target_id) {
|
||||
uint32_t ext_id = 0;
|
||||
ext_id |= ((uint32_t)cmd_type & 0x1F) << 24; // Bit28~24: 通信类型
|
||||
ext_id |= ((uint32_t)data2 & 0xFFFF) << 8; // Bit23~8: 数据区2
|
||||
ext_id |= ((uint32_t)target_id & 0xFF); // Bit7~0: 目标地址
|
||||
return ext_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 浮点值转换为原始值(对称范围:-max_value ~ +max_value)
|
||||
*/
|
||||
static uint16_t MOTOR_LZ_FloatToRaw(float value, float max_value) {
|
||||
// 限制范围
|
||||
if (value > max_value) value = max_value;
|
||||
if (value < -max_value) value = -max_value;
|
||||
|
||||
// 转换为0~65535范围,对应-max_value~max_value
|
||||
return (uint16_t)((value + max_value) / (2.0f * max_value) * (float)LZ_RAW_VALUE_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 浮点值转换为原始值(单向范围:0 ~ +max_value)
|
||||
*/
|
||||
static uint16_t MOTOR_LZ_FloatToRawPositive(float value, float max_value) {
|
||||
// 限制范围
|
||||
if (value > max_value) value = max_value;
|
||||
if (value < 0.0f) value = 0.0f;
|
||||
|
||||
// 转换为0~65535范围,对应0~max_value
|
||||
return (uint16_t)(value / max_value * (float)LZ_RAW_VALUE_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 原始值转换为浮点值
|
||||
*/
|
||||
static float MOTOR_LZ_RawToFloat(uint16_t raw_value, float max_value) {
|
||||
// 将0~65535范围转换为-max_value~max_value
|
||||
return ((float)raw_value / (float)LZ_RAW_VALUE_MAX) * (2.0f * max_value) - max_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送扩展帧
|
||||
*/
|
||||
static int8_t MOTOR_LZ_SendExtFrame(BSP_CAN_t can, uint32_t ext_id, uint8_t *data, uint8_t dlc) {
|
||||
BSP_CAN_ExtDataFrame_t tx_frame;
|
||||
tx_frame.id = ext_id;
|
||||
tx_frame.dlc = dlc;
|
||||
if (data != NULL) {
|
||||
memcpy(tx_frame.data, data, dlc);
|
||||
} else {
|
||||
memset(tx_frame.data, 0, dlc);
|
||||
}
|
||||
return BSP_CAN_TransmitExtDataFrame(can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 灵足电机ID解析器
|
||||
* @param original_id 原始CAN ID(29位扩展帧)
|
||||
* @param frame_type 帧类型
|
||||
* @return 解析后的ID(用于队列匹配)
|
||||
*
|
||||
* 灵足电机扩展ID格式:
|
||||
* Bit28~24: 通信类型 (0x1=运控控制, 0x2=反馈数据, 0x3=使能, 0x4=停止, 0x6=设零位)
|
||||
* Bit23~8: 数据区2 (根据通信类型而定)
|
||||
* Bit7~0: 目标地址 (目标电机CAN ID)
|
||||
*/
|
||||
static uint32_t MOTOR_LZ_IdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type) {
|
||||
// 只处理扩展数据帧
|
||||
if (frame_type != BSP_CAN_FRAME_EXT_DATA) {
|
||||
return original_id; // 非扩展帧直接返回原始ID
|
||||
}
|
||||
|
||||
// 解析扩展ID各个字段
|
||||
uint8_t cmd_type = (original_id >> 24) & 0x1F; // Bit28~24: 通信类型
|
||||
uint16_t data2 = (original_id >> 8) & 0xFFFF; // Bit23~8: 数据区2
|
||||
uint8_t host_id = (uint8_t)(original_id & 0xFF); // Bit7~0: 主机CAN ID
|
||||
|
||||
// 对于反馈数据帧,我们使用特殊的解析规则
|
||||
if (cmd_type == MOTOR_LZ_CMD_FEEDBACK) {
|
||||
// 反馈数据的data2字段包含:
|
||||
// Bit8~15: 当前电机CAN ID
|
||||
// Bit16~21: 故障信息
|
||||
// Bit22~23: 模式状态
|
||||
uint8_t motor_can_id = data2 & 0xFF; // bit8~15: 当前电机CAN ID
|
||||
|
||||
// 返回格式化的ID,便于匹配
|
||||
// 格式:0x02HHMMTT (02=反馈命令, HH=主机ID, MM=电机ID, TT=主机ID)
|
||||
return (0x02000000) | (host_id << 16) | (motor_can_id << 8) | host_id;
|
||||
}
|
||||
|
||||
// 对于其他命令类型,直接返回原始ID
|
||||
return original_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解码灵足电机反馈数据
|
||||
*/
|
||||
static void MOTOR_LZ_Decode(MOTOR_LZ_t *motor, BSP_CAN_Message_t *msg) {
|
||||
if (motor == NULL || msg == NULL) return;
|
||||
uint8_t cmd_type = (msg->original_id >> 24) & 0x1F;
|
||||
if (cmd_type != MOTOR_LZ_CMD_FEEDBACK) return;
|
||||
uint16_t id_data2 = (msg->original_id >> 8) & 0xFFFF;
|
||||
uint8_t motor_can_id = id_data2 & 0xFF;
|
||||
uint8_t fault_info = (id_data2 >> 8) & 0x3F;
|
||||
uint8_t mode_state = (id_data2 >> 14) & 0x03;
|
||||
motor->lz_feedback.motor_can_id = motor_can_id;
|
||||
motor->lz_feedback.fault.under_voltage = (fault_info & 0x01) != 0;
|
||||
motor->lz_feedback.fault.driver_fault = (fault_info & 0x02) != 0;
|
||||
motor->lz_feedback.fault.over_temp = (fault_info & 0x04) != 0;
|
||||
motor->lz_feedback.fault.encoder_fault = (fault_info & 0x08) != 0;
|
||||
motor->lz_feedback.fault.stall_overload = (fault_info & 0x10) != 0;
|
||||
motor->lz_feedback.fault.uncalibrated = (fault_info & 0x20) != 0;
|
||||
motor->lz_feedback.state = (MOTOR_LZ_State_t)mode_state;
|
||||
|
||||
// 反馈解码并自动反向
|
||||
uint16_t raw_angle = (uint16_t)((msg->data[0] << 8) | msg->data[1]);
|
||||
float angle = MOTOR_LZ_RawToFloat(raw_angle, LZ_ANGLE_RANGE_RAD);
|
||||
uint16_t raw_velocity = (uint16_t)((msg->data[2] << 8) | msg->data[3]);
|
||||
float velocity = MOTOR_LZ_RawToFloat(raw_velocity, LZ_VELOCITY_RANGE_RAD_S);
|
||||
uint16_t raw_torque = (uint16_t)((msg->data[4] << 8) | msg->data[5]);
|
||||
float torque = MOTOR_LZ_RawToFloat(raw_torque, LZ_TORQUE_RANGE_NM);
|
||||
|
||||
while (angle <0){
|
||||
angle += M_2PI;
|
||||
}
|
||||
while (angle > M_2PI){
|
||||
angle -= M_2PI;
|
||||
}
|
||||
// 自动反向
|
||||
if (motor->param.reverse) {
|
||||
angle = M_2PI - angle;
|
||||
velocity = -velocity;
|
||||
torque = -torque;
|
||||
}
|
||||
|
||||
motor->lz_feedback.current_angle = angle;
|
||||
motor->lz_feedback.current_velocity = velocity;
|
||||
motor->lz_feedback.current_torque = torque;
|
||||
|
||||
uint16_t raw_temp = (uint16_t)((msg->data[6] << 8) | msg->data[7]);
|
||||
motor->lz_feedback.temperature = (float)raw_temp / LZ_TEMP_SCALE;
|
||||
|
||||
motor->motor.feedback.rotor_abs_angle = angle;
|
||||
motor->motor.feedback.rotor_speed = velocity;
|
||||
motor->motor.feedback.torque_current = torque;
|
||||
motor->motor.feedback.temp = (int8_t)motor->lz_feedback.temperature;
|
||||
motor->motor.header.online = true;
|
||||
motor->motor.header.last_online_time = BSP_TIME_Get();
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化灵足电机驱动系统
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Init(void) {
|
||||
// 注册灵足电机专用的ID解析器
|
||||
return BSP_CAN_RegisterIdParser(MOTOR_LZ_IdParser) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
|
||||
int8_t MOTOR_LZ_Register(MOTOR_LZ_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
if (MOTOR_LZ_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
|
||||
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager(param->can);
|
||||
if (manager == NULL) return DEVICE_ERR;
|
||||
|
||||
// 检查是否已注册
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
if (manager->motors[i] && manager->motors[i]->param.motor_id == param->motor_id) {
|
||||
return DEVICE_ERR; // 已注册
|
||||
}
|
||||
}
|
||||
|
||||
// 检查数量
|
||||
if (manager->motor_count >= MOTOR_LZ_MAX_MOTORS) return DEVICE_ERR;
|
||||
|
||||
// 创建新电机实例
|
||||
MOTOR_LZ_t *new_motor = (MOTOR_LZ_t*)BSP_Malloc(sizeof(MOTOR_LZ_t));
|
||||
if (new_motor == NULL) return DEVICE_ERR;
|
||||
|
||||
memcpy(&new_motor->param, param, sizeof(MOTOR_LZ_Param_t));
|
||||
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
|
||||
memset(&new_motor->lz_feedback, 0, sizeof(MOTOR_LZ_Feedback_t));
|
||||
memset(&new_motor->motion_param, 0, sizeof(MOTOR_LZ_MotionParam_t));
|
||||
|
||||
new_motor->motor.reverse = param->reverse;
|
||||
|
||||
// 注册CAN接收ID - 使用解析后的反馈数据ID
|
||||
// 构建反馈数据的原始扩展ID
|
||||
// 反馈数据:data2包含电机ID(bit8~15),target_id是主机ID
|
||||
uint32_t original_feedback_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_FEEDBACK, param->motor_id, param->host_id);
|
||||
// 通过ID解析器得到解析后的ID
|
||||
uint32_t parsed_feedback_id = MOTOR_LZ_IdParser(original_feedback_id, BSP_CAN_FRAME_EXT_DATA);
|
||||
|
||||
if (BSP_CAN_RegisterId(param->can, parsed_feedback_id, 3) != BSP_OK) {
|
||||
BSP_Free(new_motor);
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
manager->motors[manager->motor_count] = new_motor;
|
||||
manager->motor_count++;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LZ_Update(MOTOR_LZ_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager(param->can);
|
||||
if (manager == NULL) return DEVICE_ERR_NO_DEV;
|
||||
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
MOTOR_LZ_t *motor = manager->motors[i];
|
||||
if (motor && motor->param.motor_id == param->motor_id) {
|
||||
// 获取反馈数据 - 使用解析后的ID
|
||||
uint32_t original_feedback_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_FEEDBACK, param->motor_id, param->host_id);
|
||||
uint32_t parsed_feedback_id = MOTOR_LZ_IdParser(original_feedback_id, BSP_CAN_FRAME_EXT_DATA);
|
||||
BSP_CAN_Message_t msg;
|
||||
|
||||
while (BSP_CAN_GetMessage(param->can, parsed_feedback_id, &msg, 0) == BSP_OK) {
|
||||
MOTOR_LZ_Decode(motor, &msg);
|
||||
}
|
||||
return DEVICE_OK;
|
||||
}
|
||||
}
|
||||
return DEVICE_ERR_NO_DEV;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LZ_UpdateAll(void) {
|
||||
int8_t ret = DEVICE_OK;
|
||||
for (int can = 0; can < BSP_CAN_NUM; can++) {
|
||||
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager((BSP_CAN_t)can);
|
||||
if (manager == NULL) continue;
|
||||
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
MOTOR_LZ_t *motor = manager->motors[i];
|
||||
if (motor) {
|
||||
if (MOTOR_LZ_Update(&motor->param) != DEVICE_OK) {
|
||||
ret = DEVICE_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LZ_MotionControl(MOTOR_LZ_Param_t *param, MOTOR_LZ_MotionParam_t *motion_param) {
|
||||
if (param == NULL || motion_param == NULL) return DEVICE_ERR_NULL;
|
||||
MOTOR_LZ_t *motor = MOTOR_LZ_GetMotor(param);
|
||||
if (motor == NULL) return DEVICE_ERR_NO_DEV;
|
||||
|
||||
// 自动反向控制
|
||||
MOTOR_LZ_MotionParam_t send_param = *motion_param;
|
||||
if (param->reverse) {
|
||||
send_param.target_angle = -send_param.target_angle;
|
||||
send_param.target_velocity = -send_param.target_velocity;
|
||||
send_param.torque = -send_param.torque;
|
||||
}
|
||||
|
||||
memcpy(&motor->motion_param, motion_param, sizeof(MOTOR_LZ_MotionParam_t));
|
||||
|
||||
uint16_t raw_torque = MOTOR_LZ_FloatToRaw(send_param.torque, LZ_TORQUE_RANGE_NM);
|
||||
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_MOTION, raw_torque, param->motor_id);
|
||||
uint8_t data[8];
|
||||
uint16_t raw_angle = MOTOR_LZ_FloatToRaw(send_param.target_angle, LZ_ANGLE_RANGE_RAD);
|
||||
data[0] = (raw_angle >> 8) & 0xFF;
|
||||
data[1] = raw_angle & 0xFF;
|
||||
uint16_t raw_velocity = MOTOR_LZ_FloatToRaw(send_param.target_velocity, LZ_VELOCITY_RANGE_RAD_S);
|
||||
data[2] = (raw_velocity >> 8) & 0xFF;
|
||||
data[3] = raw_velocity & 0xFF;
|
||||
uint16_t raw_kp = MOTOR_LZ_FloatToRawPositive(send_param.kp, LZ_KP_MAX);
|
||||
data[4] = (raw_kp >> 8) & 0xFF;
|
||||
data[5] = raw_kp & 0xFF;
|
||||
uint16_t raw_kd = MOTOR_LZ_FloatToRawPositive(send_param.kd, LZ_KD_MAX);
|
||||
data[6] = (raw_kd >> 8) & 0xFF;
|
||||
data[7] = raw_kd & 0xFF;
|
||||
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
|
||||
}
|
||||
|
||||
|
||||
int8_t MOTOR_LZ_Enable(MOTOR_LZ_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
// 构建扩展ID - 使能命令
|
||||
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_ENABLE, param->host_id, param->motor_id);
|
||||
|
||||
// 数据区清零
|
||||
uint8_t data[8] = {0};
|
||||
|
||||
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
|
||||
}
|
||||
|
||||
int8_t MOTOR_LZ_Disable(MOTOR_LZ_Param_t *param, bool clear_fault) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
// 构建扩展ID - 停止命令
|
||||
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_DISABLE, param->host_id, param->motor_id);
|
||||
|
||||
// 数据区
|
||||
uint8_t data[8] = {0};
|
||||
if (clear_fault) {
|
||||
data[0] = 1; // Byte[0]=1时清故障
|
||||
}
|
||||
|
||||
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
|
||||
}
|
||||
|
||||
int8_t MOTOR_LZ_SetZero(MOTOR_LZ_Param_t *param) {
|
||||
if (param == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
// 构建扩展ID - 设置零位命令
|
||||
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_SET_ZERO, param->host_id, param->motor_id);
|
||||
|
||||
// 数据区 - Byte[0]=1
|
||||
uint8_t data[8] = {1, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
|
||||
}
|
||||
|
||||
MOTOR_LZ_t* MOTOR_LZ_GetMotor(MOTOR_LZ_Param_t *param) {
|
||||
if (param == NULL) return NULL;
|
||||
|
||||
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager(param->can);
|
||||
if (manager == NULL) return NULL;
|
||||
|
||||
for (int i = 0; i < manager->motor_count; i++) {
|
||||
MOTOR_LZ_t *motor = manager->motors[i];
|
||||
if (motor && motor->param.motor_id == param->motor_id) {
|
||||
return motor;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int8_t MOTOR_LZ_Relax(MOTOR_LZ_Param_t *param) {
|
||||
return MOTOR_LZ_MotionControl(param, &lz_relax_param);
|
||||
}
|
||||
|
||||
int8_t MOTOR_LZ_Offline(MOTOR_LZ_Param_t *param) {
|
||||
MOTOR_LZ_t *motor = MOTOR_LZ_GetMotor(param);
|
||||
if (motor) {
|
||||
motor->motor.header.online = false;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
return DEVICE_ERR_NO_DEV;
|
||||
}
|
||||
|
||||
static MOTOR_LZ_Feedback_t* MOTOR_LZ_GetFeedback(MOTOR_LZ_Param_t *param) {
|
||||
MOTOR_LZ_t *motor = MOTOR_LZ_GetMotor(param);
|
||||
if (motor && motor->motor.header.online) {
|
||||
return &motor->lz_feedback;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
212
User/device/motor_lz.h
Normal file
212
User/device/motor_lz.h
Normal file
@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device/device.h"
|
||||
#include "device/motor.h"
|
||||
#include "bsp/can.h"
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
#define MOTOR_LZ_MAX_MOTORS 32
|
||||
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef enum {
|
||||
MOTOR_LZ_RSO0,
|
||||
MOTOR_LZ_RSO1,
|
||||
MOTOR_LZ_RSO2,
|
||||
MOTOR_LZ_RSO3,
|
||||
MOTOR_LZ_RSO4,
|
||||
MOTOR_LZ_RSO5,
|
||||
MOTOR_LZ_RSO6,
|
||||
} MOTOR_LZ_Module_t;
|
||||
|
||||
/* 灵足电机控制模式 */
|
||||
typedef enum {
|
||||
MOTOR_LZ_MODE_MOTION = 0x1, /* 运控模式 */
|
||||
MOTOR_LZ_MODE_CURRENT = 0x2, /* 电流模式 */
|
||||
MOTOR_LZ_MODE_VELOCITY = 0x3, /* 速度模式 */
|
||||
MOTOR_LZ_MODE_POSITION = 0x4, /* 位置模式 */
|
||||
} MOTOR_LZ_ControlMode_t;
|
||||
|
||||
/* 灵足电机通信类型 */
|
||||
typedef enum {
|
||||
MOTOR_LZ_CMD_MOTION = 0x1, /* 运控模式控制 */
|
||||
MOTOR_LZ_CMD_FEEDBACK = 0x2, /* 电机反馈数据 */
|
||||
MOTOR_LZ_CMD_ENABLE = 0x3, /* 电机使能运行 */
|
||||
MOTOR_LZ_CMD_DISABLE = 0x4, /* 电机停止运行 */
|
||||
MOTOR_LZ_CMD_SET_ZERO = 0x6, /* 设置电机机械零位 */
|
||||
} MOTOR_LZ_CmdType_t;
|
||||
|
||||
/* 灵足电机运行状态 */
|
||||
typedef enum {
|
||||
MOTOR_LZ_STATE_RESET = 0, /* Reset模式[复位] */
|
||||
MOTOR_LZ_STATE_CALI = 1, /* Cali模式[标定] */
|
||||
MOTOR_LZ_STATE_MOTOR = 2, /* Motor模式[运行] */
|
||||
} MOTOR_LZ_State_t;
|
||||
|
||||
/* 灵足电机故障信息 */
|
||||
typedef struct {
|
||||
bool uncalibrated; /* bit21: 未标定 */
|
||||
bool stall_overload; /* bit20: 堵转过载故障 */
|
||||
bool encoder_fault; /* bit19: 磁编码故障 */
|
||||
bool over_temp; /* bit18: 过温 */
|
||||
bool driver_fault; /* bit17: 驱动故障 */
|
||||
bool under_voltage; /* bit16: 欠压故障 */
|
||||
} MOTOR_LZ_Fault_t;
|
||||
|
||||
/* 灵足电机运控参数 */
|
||||
typedef struct {
|
||||
float target_angle; /* 目标角度 (-12.57f~12.57f rad) */
|
||||
float target_velocity; /* 目标角速度 (-20~20 rad/s) */
|
||||
float kp; /* 位置增益 (0.0~5000.0) */
|
||||
float kd; /* 微分增益 (0.0~100.0) */
|
||||
float torque; /* 力矩 (-60~60 Nm) */
|
||||
} MOTOR_LZ_MotionParam_t;
|
||||
|
||||
/*每个电机需要的参数*/
|
||||
typedef struct {
|
||||
BSP_CAN_t can; /* CAN总线 */
|
||||
uint8_t motor_id; /* 电机CAN ID */
|
||||
uint8_t host_id; /* 主机CAN ID */
|
||||
MOTOR_LZ_Module_t module; /* 电机型号 */
|
||||
bool reverse; /* 是否反向 */
|
||||
MOTOR_LZ_ControlMode_t mode; /* 控制模式 */
|
||||
} MOTOR_LZ_Param_t;
|
||||
|
||||
/*电机反馈信息扩展*/
|
||||
typedef struct {
|
||||
float current_angle; /* 当前角度 (-12.57f~12.57f rad) */
|
||||
float current_velocity; /* 当前角速度 (-20~20 rad/s) */
|
||||
float current_torque; /* 当前力矩 (-60~60 Nm) */
|
||||
float temperature; /* 当前温度 (摄氏度) */
|
||||
MOTOR_LZ_State_t state; /* 运行状态 */
|
||||
MOTOR_LZ_Fault_t fault; /* 故障信息 */
|
||||
uint8_t motor_can_id; /* 当前电机CAN ID */
|
||||
} MOTOR_LZ_Feedback_t;
|
||||
|
||||
/*电机实例*/
|
||||
typedef struct {
|
||||
MOTOR_LZ_Param_t param;
|
||||
MOTOR_t motor;
|
||||
MOTOR_LZ_Feedback_t lz_feedback; /* 灵足电机特有反馈信息 */
|
||||
MOTOR_LZ_MotionParam_t motion_param; /* 运控模式参数 */
|
||||
} MOTOR_LZ_t;
|
||||
|
||||
/*CAN管理器,管理一个CAN总线上所有的电机*/
|
||||
typedef struct {
|
||||
BSP_CAN_t can;
|
||||
MOTOR_LZ_t *motors[MOTOR_LZ_MAX_MOTORS];
|
||||
uint8_t motor_count;
|
||||
} MOTOR_LZ_CANManager_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化灵足电机驱动系统
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 注册一个灵足电机
|
||||
* @param param 电机参数
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Register(MOTOR_LZ_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 更新指定电机数据
|
||||
* @param param 电机参数
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Update(MOTOR_LZ_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 更新所有电机数据
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_UpdateAll(void);
|
||||
|
||||
/**
|
||||
* @brief 运控模式控制电机
|
||||
* @param param 电机参数
|
||||
* @param motion_param 运控参数
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_MotionControl(MOTOR_LZ_Param_t *param, MOTOR_LZ_MotionParam_t *motion_param);
|
||||
|
||||
/**
|
||||
* @brief 电流(力矩)模式控制电机
|
||||
* @param param 电机参数
|
||||
* @param torque 目标力矩 (-60~60 Nm)
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_TorqueControl(MOTOR_LZ_Param_t *param, float torque);
|
||||
|
||||
/**
|
||||
* @brief 位置模式控制电机
|
||||
* @param param 电机参数
|
||||
* @param target_angle 目标角度 (-12.57~12.57 rad)
|
||||
* @param max_velocity 最大速度 (0~20 rad/s)
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_PositionControl(MOTOR_LZ_Param_t *param, float target_angle, float max_velocity);
|
||||
|
||||
/**
|
||||
* @brief 速度模式控制电机
|
||||
* @param param 电机参数
|
||||
* @param target_velocity 目标速度 (-20~20 rad/s)
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_VelocityControl(MOTOR_LZ_Param_t *param, float target_velocity);
|
||||
|
||||
/**
|
||||
* @brief 电机使能运行
|
||||
* @param param 电机参数
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Enable(MOTOR_LZ_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 电机停止运行
|
||||
* @param param 电机参数
|
||||
* @param clear_fault 是否清除故障
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Disable(MOTOR_LZ_Param_t *param, bool clear_fault);
|
||||
|
||||
/**
|
||||
* @brief 设置电机机械零位
|
||||
* @param param 电机参数
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_SetZero(MOTOR_LZ_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 获取指定电机的实例指针
|
||||
* @param param 电机参数
|
||||
* @return 电机实例指针,失败返回NULL
|
||||
*/
|
||||
MOTOR_LZ_t* MOTOR_LZ_GetMotor(MOTOR_LZ_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 使电机松弛(发送停止命令)
|
||||
* @param param 电机参数
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Relax(MOTOR_LZ_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 使电机离线(设置在线状态为false)
|
||||
* @param param 电机参数
|
||||
* @return 设备状态码
|
||||
*/
|
||||
int8_t MOTOR_LZ_Offline(MOTOR_LZ_Param_t *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
44
User/task/Task4.c
Normal file
44
User/task/Task4.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Task4 Task
|
||||
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "task/user_task.h"
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private function --------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
void Task_Task4(void *argument) {
|
||||
(void)argument; /* 未使用argument,消除警告 */
|
||||
|
||||
|
||||
/* 计算任务运行到指定频率需要等待的tick数 */
|
||||
const uint32_t delay_tick = osKernelGetTickFreq() / TASK4_FREQ;
|
||||
|
||||
osDelay(TASK4_INIT_DELAY); /* 延时一段时间再开启任务 */
|
||||
|
||||
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
||||
/* USER CODE INIT BEGIN */
|
||||
|
||||
/* USER CODE INIT END */
|
||||
|
||||
while (1) {
|
||||
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
||||
/* USER CODE BEGIN */
|
||||
|
||||
/* USER CODE END */
|
||||
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
||||
}
|
||||
|
||||
}
|
||||
44
User/task/blink.c
Normal file
44
User/task/blink.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
blink Task
|
||||
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "task/user_task.h"
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private function --------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
void Task_blink(void *argument) {
|
||||
(void)argument; /* 未使用argument,消除警告 */
|
||||
|
||||
|
||||
/* 计算任务运行到指定频率需要等待的tick数 */
|
||||
const uint32_t delay_tick = osKernelGetTickFreq() / BLINK_FREQ;
|
||||
|
||||
osDelay(BLINK_INIT_DELAY); /* 延时一段时间再开启任务 */
|
||||
|
||||
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
||||
/* USER CODE INIT BEGIN */
|
||||
|
||||
/* USER CODE INIT END */
|
||||
|
||||
while (1) {
|
||||
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
||||
/* USER CODE BEGIN */
|
||||
|
||||
/* USER CODE END */
|
||||
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
||||
}
|
||||
|
||||
}
|
||||
28
User/task/config.yaml
Normal file
28
User/task/config.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
- delay: 0
|
||||
description: ''
|
||||
freq_control: true
|
||||
frequency: 500.0
|
||||
function: Task_blink
|
||||
name: blink
|
||||
stack: 256
|
||||
- delay: 0
|
||||
description: ''
|
||||
freq_control: true
|
||||
frequency: 1000.0
|
||||
function: Task_imu
|
||||
name: imu
|
||||
stack: 256
|
||||
- delay: 0
|
||||
description: ''
|
||||
freq_control: true
|
||||
frequency: 500.0
|
||||
function: Task_ctrl_lz
|
||||
name: ctrl_lz
|
||||
stack: 256
|
||||
- delay: 0
|
||||
description: ''
|
||||
freq_control: true
|
||||
frequency: 500.0
|
||||
function: Task_Task4
|
||||
name: Task4
|
||||
stack: 256
|
||||
44
User/task/ctrl_lz.c
Normal file
44
User/task/ctrl_lz.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
ctrl_lz Task
|
||||
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "task/user_task.h"
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private function --------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
void Task_ctrl_lz(void *argument) {
|
||||
(void)argument; /* 未使用argument,消除警告 */
|
||||
|
||||
|
||||
/* 计算任务运行到指定频率需要等待的tick数 */
|
||||
const uint32_t delay_tick = osKernelGetTickFreq() / CTRL_LZ_FREQ;
|
||||
|
||||
osDelay(CTRL_LZ_INIT_DELAY); /* 延时一段时间再开启任务 */
|
||||
|
||||
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
||||
/* USER CODE INIT BEGIN */
|
||||
|
||||
/* USER CODE INIT END */
|
||||
|
||||
while (1) {
|
||||
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
||||
/* USER CODE BEGIN */
|
||||
|
||||
/* USER CODE END */
|
||||
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
||||
}
|
||||
|
||||
}
|
||||
44
User/task/imu.c
Normal file
44
User/task/imu.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
imu Task
|
||||
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "task/user_task.h"
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private function --------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
void Task_imu(void *argument) {
|
||||
(void)argument; /* 未使用argument,消除警告 */
|
||||
|
||||
|
||||
/* 计算任务运行到指定频率需要等待的tick数 */
|
||||
const uint32_t delay_tick = osKernelGetTickFreq() / IMU_FREQ;
|
||||
|
||||
osDelay(IMU_INIT_DELAY); /* 延时一段时间再开启任务 */
|
||||
|
||||
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
||||
/* USER CODE INIT BEGIN */
|
||||
|
||||
/* USER CODE INIT END */
|
||||
|
||||
while (1) {
|
||||
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
||||
/* USER CODE BEGIN */
|
||||
|
||||
/* USER CODE END */
|
||||
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
||||
}
|
||||
|
||||
}
|
||||
45
User/task/init.c
Normal file
45
User/task/init.c
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Init Task
|
||||
任务初始化,创建各个线程任务和消息队列
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "task/user_task.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function --------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* \brief 初始化
|
||||
*
|
||||
* \param argument 未使用
|
||||
*/
|
||||
void Task_Init(void *argument) {
|
||||
(void)argument; /* 未使用argument,消除警告 */
|
||||
/* USER CODE INIT BEGIN */
|
||||
|
||||
/* USER CODE INIT END */
|
||||
osKernelLock(); /* 锁定内核,防止任务切换 */
|
||||
|
||||
/* 创建任务线程 */
|
||||
task_runtime.thread.blink = osThreadNew(Task_blink, NULL, &attr_blink);
|
||||
task_runtime.thread.imu = osThreadNew(Task_imu, NULL, &attr_imu);
|
||||
task_runtime.thread.ctrl_lz = osThreadNew(Task_ctrl_lz, NULL, &attr_ctrl_lz);
|
||||
task_runtime.thread.Task4 = osThreadNew(Task_Task4, NULL, &attr_Task4);
|
||||
|
||||
// 创建消息队列
|
||||
/* USER MESSAGE BEGIN */
|
||||
task_runtime.msgq.user_msg= osMessageQueueNew(2u, 10, NULL);
|
||||
/* USER MESSAGE END */
|
||||
|
||||
osKernelUnlock(); // 解锁内核
|
||||
osThreadTerminate(osThreadGetId()); // 任务完成后结束自身
|
||||
}
|
||||
31
User/task/user_task.c
Normal file
31
User/task/user_task.c
Normal file
@ -0,0 +1,31 @@
|
||||
#include "task/user_task.h"
|
||||
|
||||
Task_Runtime_t task_runtime;
|
||||
|
||||
const osThreadAttr_t attr_init = {
|
||||
.name = "Task_Init",
|
||||
.priority = osPriorityRealtime,
|
||||
.stack_size = 256 * 4,
|
||||
};
|
||||
|
||||
/* User_task */
|
||||
const osThreadAttr_t attr_blink = {
|
||||
.name = "blink",
|
||||
.priority = osPriorityNormal,
|
||||
.stack_size = 256 * 4,
|
||||
};
|
||||
const osThreadAttr_t attr_imu = {
|
||||
.name = "imu",
|
||||
.priority = osPriorityNormal,
|
||||
.stack_size = 256 * 4,
|
||||
};
|
||||
const osThreadAttr_t attr_ctrl_lz = {
|
||||
.name = "ctrl_lz",
|
||||
.priority = osPriorityNormal,
|
||||
.stack_size = 256 * 4,
|
||||
};
|
||||
const osThreadAttr_t attr_Task4 = {
|
||||
.name = "Task4",
|
||||
.priority = osPriorityNormal,
|
||||
.stack_size = 256 * 4,
|
||||
};
|
||||
104
User/task/user_task.h
Normal file
104
User/task/user_task.h
Normal file
@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <cmsis_os2.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* 任务运行频率 */
|
||||
#define BLINK_FREQ (500.0)
|
||||
#define IMU_FREQ (1000.0)
|
||||
#define CTRL_LZ_FREQ (500.0)
|
||||
#define TASK4_FREQ (500.0)
|
||||
|
||||
/* 任务初始化延时ms */
|
||||
#define TASK_INIT_DELAY (100u)
|
||||
#define BLINK_INIT_DELAY (0)
|
||||
#define IMU_INIT_DELAY (0)
|
||||
#define CTRL_LZ_INIT_DELAY (0)
|
||||
#define TASK4_INIT_DELAY (0)
|
||||
|
||||
/* Exported defines --------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
/* 任务运行时结构体 */
|
||||
typedef struct {
|
||||
/* 各任务,也可以叫做线程 */
|
||||
struct {
|
||||
osThreadId_t blink;
|
||||
osThreadId_t imu;
|
||||
osThreadId_t ctrl_lz;
|
||||
osThreadId_t Task4;
|
||||
} thread;
|
||||
|
||||
/* USER MESSAGE BEGIN */
|
||||
struct {
|
||||
osMessageQueueId_t user_msg; /* 用户自定义任务消息队列 */
|
||||
} msgq;
|
||||
/* USER MESSAGE END */
|
||||
|
||||
/* 机器人状态 */
|
||||
struct {
|
||||
float battery; /* 电池电量百分比 */
|
||||
float vbat; /* 电池电压 */
|
||||
float cpu_temp; /* CPU温度 */
|
||||
} status;
|
||||
|
||||
/* USER CONFIG BEGIN */
|
||||
|
||||
/* USER CONFIG END */
|
||||
|
||||
/* 各任务的stack使用 */
|
||||
struct {
|
||||
UBaseType_t blink;
|
||||
UBaseType_t imu;
|
||||
UBaseType_t ctrl_lz;
|
||||
UBaseType_t Task4;
|
||||
} stack_water_mark;
|
||||
|
||||
/* 各任务运行频率 */
|
||||
struct {
|
||||
float blink;
|
||||
float imu;
|
||||
float ctrl_lz;
|
||||
float Task4;
|
||||
} freq;
|
||||
|
||||
/* 任务最近运行时间 */
|
||||
struct {
|
||||
float blink;
|
||||
float imu;
|
||||
float ctrl_lz;
|
||||
float Task4;
|
||||
} last_up_time;
|
||||
|
||||
} Task_Runtime_t;
|
||||
|
||||
/* 任务运行时结构体 */
|
||||
extern Task_Runtime_t task_runtime;
|
||||
|
||||
/* 初始化任务句柄 */
|
||||
extern const osThreadAttr_t attr_init;
|
||||
extern const osThreadAttr_t attr_blink;
|
||||
extern const osThreadAttr_t attr_imu;
|
||||
extern const osThreadAttr_t attr_ctrl_lz;
|
||||
extern const osThreadAttr_t attr_Task4;
|
||||
|
||||
/* 任务函数声明 */
|
||||
void Task_Init(void *argument);
|
||||
void Task_blink(void *argument);
|
||||
void Task_imu(void *argument);
|
||||
void Task_ctrl_lz(void *argument);
|
||||
void Task_Task4(void *argument);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
43
cmake/gcc-arm-none-eabi.cmake
Normal file
43
cmake/gcc-arm-none-eabi.cmake
Normal file
@ -0,0 +1,43 @@
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
|
||||
set(CMAKE_C_COMPILER_ID GNU)
|
||||
set(CMAKE_CXX_COMPILER_ID GNU)
|
||||
|
||||
# Some default GCC settings
|
||||
# arm-none-eabi- must be part of path environment
|
||||
set(TOOLCHAIN_PREFIX arm-none-eabi-)
|
||||
|
||||
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
|
||||
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
|
||||
set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}g++)
|
||||
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
|
||||
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
|
||||
|
||||
set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
|
||||
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
|
||||
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")
|
||||
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
|
||||
# MCU specific flags
|
||||
set(TARGET_FLAGS "-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard ")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}")
|
||||
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fdata-sections -ffunction-sections")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-Os -g0")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-Os -g0")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${TARGET_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T \"${CMAKE_SOURCE_DIR}/STM32F407XX_FLASH.ld\"")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --specs=nano.specs")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--print-memory-usage")
|
||||
set(TOOLCHAIN_LINK_LIBRARIES "m")
|
||||
65
cmake/starm-clang.cmake
Normal file
65
cmake/starm-clang.cmake
Normal file
@ -0,0 +1,65 @@
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
|
||||
set(CMAKE_C_COMPILER_ID Clang)
|
||||
set(CMAKE_CXX_COMPILER_ID Clang)
|
||||
|
||||
# Some default llvm settings
|
||||
set(TOOLCHAIN_PREFIX starm-)
|
||||
|
||||
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}clang)
|
||||
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}clang++)
|
||||
set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}clang)
|
||||
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
|
||||
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
|
||||
|
||||
set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
|
||||
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
|
||||
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")
|
||||
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
|
||||
# STARM_TOOLCHAIN_CONFIG allows you to choose the toolchain configuration.
|
||||
# Possible values are:
|
||||
# "STARM_HYBRID" : Hybrid configuration using starm-clang Assemler and Compiler and GNU Linker
|
||||
# "STARM_NEWLIB" : starm-clang toolchain with NEWLIB C library
|
||||
# "STARM_PICOLIBC" : starm-clang toolchain with PICOLIBC C library
|
||||
set(STARM_TOOLCHAIN_CONFIG "STARM_HYBRID")
|
||||
|
||||
if(STARM_TOOLCHAIN_CONFIG STREQUAL "STARM_HYBRID")
|
||||
set(TOOLCHAIN_MULTILIBS "--multi-lib-config=\"$ENV{CLANG_GCC_CMSIS_COMPILER}/multilib.gnu_tools_for_stm32.yaml\" --gcc-toolchain=\"$ENV{GCC_TOOLCHAIN_ROOT}/..\"")
|
||||
elseif (STARM_TOOLCHAIN_CONFIG STREQUAL "STARM_NEWLIB")
|
||||
set(TOOLCHAIN_MULTILIBS "--config=newlib.cfg")
|
||||
endif()
|
||||
|
||||
# MCU specific flags
|
||||
set(TARGET_FLAGS "-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard ${TOOLCHAIN_MULTILIBS}")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}")
|
||||
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MP")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fdata-sections -ffunction-sections")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-Os -g0")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-Os -g0")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${TARGET_FLAGS}")
|
||||
|
||||
if (STARM_TOOLCHAIN_CONFIG STREQUAL "STARM_HYBRID")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --gcc-specs=nano.specs")
|
||||
set(TOOLCHAIN_LINK_LIBRARIES "m")
|
||||
elseif(STARM_TOOLCHAIN_CONFIG STREQUAL "STARM_NEWLIB")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lcrt0-nosys")
|
||||
elseif(STARM_TOOLCHAIN_CONFIG STREQUAL "STARM_PICOLIBC")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lcrt0-hosted")
|
||||
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T \"${CMAKE_SOURCE_DIR}/STM32F407XX_FLASH.ld\"")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -z noexecstack")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--print-memory-usage ")
|
||||
153
cmake/stm32cubemx/CMakeLists.txt
Normal file
153
cmake/stm32cubemx/CMakeLists.txt
Normal file
@ -0,0 +1,153 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
# Enable CMake support for ASM and C languages
|
||||
enable_language(C ASM)
|
||||
# STM32CubeMX generated symbols (macros)
|
||||
set(MX_Defines_Syms
|
||||
USE_HAL_DRIVER
|
||||
STM32F407xx
|
||||
$<$<CONFIG:Debug>:DEBUG>
|
||||
)
|
||||
|
||||
# STM32CubeMX generated include paths
|
||||
set(MX_Include_Dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Inc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../USB_DEVICE/App
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../USB_DEVICE/Target
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Inc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Inc/Legacy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/ST/STM32_USB_Device_Library/Core/Inc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Device/ST/STM32F4xx/Include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Include
|
||||
)
|
||||
|
||||
# STM32CubeMX generated application sources
|
||||
set(MX_Application_Src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../USB_DEVICE/Target/usbd_conf.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../USB_DEVICE/App/usb_device.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../USB_DEVICE/App/usbd_desc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../USB_DEVICE/App/usbd_cdc_if.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/main.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/gpio.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/freertos.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/adc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/can.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/crc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/dma.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/i2c.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/rng.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/spi.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/tim.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/usart.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/stm32f4xx_it.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/stm32f4xx_hal_msp.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/sysmem.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/syscalls.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../startup_stm32f407xx.s
|
||||
)
|
||||
|
||||
# STM32 HAL/LL Drivers
|
||||
set(STM32_Drivers_Src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/system_stm32f4xx.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_adc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_crc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rng.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c
|
||||
)
|
||||
|
||||
# Drivers Midllewares
|
||||
|
||||
|
||||
set(USB_Device_Library_Src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c
|
||||
)
|
||||
set(FreeRTOS_Src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/croutine.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/event_groups.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/list.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/queue.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/tasks.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/timers.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c
|
||||
)
|
||||
|
||||
# Link directories setup
|
||||
set(MX_LINK_DIRS
|
||||
|
||||
)
|
||||
# Project static libraries
|
||||
set(MX_LINK_LIBS
|
||||
STM32_Drivers
|
||||
${TOOLCHAIN_LINK_LIBRARIES}
|
||||
USB_Device_Library
FreeRTOS
|
||||
)
|
||||
# Interface library for includes and symbols
|
||||
add_library(stm32cubemx INTERFACE)
|
||||
target_include_directories(stm32cubemx INTERFACE ${MX_Include_Dirs})
|
||||
target_compile_definitions(stm32cubemx INTERFACE ${MX_Defines_Syms})
|
||||
|
||||
# Create STM32_Drivers static library
|
||||
add_library(STM32_Drivers OBJECT)
|
||||
target_sources(STM32_Drivers PRIVATE ${STM32_Drivers_Src})
|
||||
target_link_libraries(STM32_Drivers PUBLIC stm32cubemx)
|
||||
|
||||
|
||||
# Create USB_Device_Library static library
|
||||
add_library(USB_Device_Library OBJECT)
|
||||
target_sources(USB_Device_Library PRIVATE ${USB_Device_Library_Src})
|
||||
target_link_libraries(USB_Device_Library PUBLIC stm32cubemx)
|
||||
|
||||
# Create FreeRTOS static library
|
||||
add_library(FreeRTOS OBJECT)
|
||||
target_sources(FreeRTOS PRIVATE ${FreeRTOS_Src})
|
||||
target_link_libraries(FreeRTOS PUBLIC stm32cubemx)
|
||||
|
||||
# Add STM32CubeMX generated application sources to the project
|
||||
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${MX_Application_Src})
|
||||
|
||||
# Link directories setup
|
||||
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE ${MX_LINK_DIRS})
|
||||
|
||||
# Add libraries to the project
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} ${MX_LINK_LIBS})
|
||||
|
||||
# Add the map file to the list of files to be removed with 'clean' target
|
||||
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_PROJECT_NAME}.map)
|
||||
|
||||
# Validate that STM32CubeMX code is compatible with C standard
|
||||
if((CMAKE_C_STANDARD EQUAL 90) OR (CMAKE_C_STANDARD EQUAL 99))
|
||||
message(ERROR "Generated code requires C11 or higher")
|
||||
endif()
|
||||
508
startup_stm32f407xx.s
Normal file
508
startup_stm32f407xx.s
Normal file
@ -0,0 +1,508 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file startup_stm32f407xx.s
|
||||
* @author MCD Application Team
|
||||
* @brief STM32F407xx Devices vector table for GCC based toolchains.
|
||||
* This module performs:
|
||||
* - Set the initial SP
|
||||
* - Set the initial PC == Reset_Handler,
|
||||
* - Set the vector table entries with the exceptions ISR address
|
||||
* - Branches to main in the C library (which eventually
|
||||
* calls main()).
|
||||
* After Reset the Cortex-M4 processor is in Thread mode,
|
||||
* priority is Privileged, and the Stack is set to Main.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2017 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under BSD 3-Clause license,
|
||||
* the "License"; You may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at:
|
||||
* opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m4
|
||||
.fpu softvfp
|
||||
.thumb
|
||||
|
||||
.global g_pfnVectors
|
||||
.global Default_Handler
|
||||
|
||||
/* start address for the initialization values of the .data section.
|
||||
defined in linker script */
|
||||
.word _sidata
|
||||
/* start address for the .data section. defined in linker script */
|
||||
.word _sdata
|
||||
/* end address for the .data section. defined in linker script */
|
||||
.word _edata
|
||||
/* start address for the .bss section. defined in linker script */
|
||||
.word _sbss
|
||||
/* end address for the .bss section. defined in linker script */
|
||||
.word _ebss
|
||||
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
|
||||
|
||||
/**
|
||||
* @brief This is the code that gets called when the processor first
|
||||
* starts execution following a reset event. Only the absolutely
|
||||
* necessary set is performed, after which the application
|
||||
* supplied main() routine is called.
|
||||
* @param None
|
||||
* @retval : None
|
||||
*/
|
||||
|
||||
.section .text.Reset_Handler
|
||||
.weak Reset_Handler
|
||||
.type Reset_Handler, %function
|
||||
Reset_Handler:
|
||||
ldr sp, =_estack /* set stack pointer */
|
||||
|
||||
/* Copy the data segment initializers from flash to SRAM */
|
||||
ldr r0, =_sdata
|
||||
ldr r1, =_edata
|
||||
ldr r2, =_sidata
|
||||
movs r3, #0
|
||||
b LoopCopyDataInit
|
||||
|
||||
CopyDataInit:
|
||||
ldr r4, [r2, r3]
|
||||
str r4, [r0, r3]
|
||||
adds r3, r3, #4
|
||||
|
||||
LoopCopyDataInit:
|
||||
adds r4, r0, r3
|
||||
cmp r4, r1
|
||||
bcc CopyDataInit
|
||||
|
||||
/* Zero fill the bss segment. */
|
||||
ldr r2, =_sbss
|
||||
ldr r4, =_ebss
|
||||
movs r3, #0
|
||||
b LoopFillZerobss
|
||||
|
||||
FillZerobss:
|
||||
str r3, [r2]
|
||||
adds r2, r2, #4
|
||||
|
||||
LoopFillZerobss:
|
||||
cmp r2, r4
|
||||
bcc FillZerobss
|
||||
|
||||
/* Call the clock system intitialization function.*/
|
||||
bl SystemInit
|
||||
/* Call static constructors */
|
||||
bl __libc_init_array
|
||||
/* Call the application's entry point.*/
|
||||
bl main
|
||||
bx lr
|
||||
.size Reset_Handler, .-Reset_Handler
|
||||
|
||||
/**
|
||||
* @brief This is the code that gets called when the processor receives an
|
||||
* unexpected interrupt. This simply enters an infinite loop, preserving
|
||||
* the system state for examination by a debugger.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
.section .text.Default_Handler,"ax",%progbits
|
||||
Default_Handler:
|
||||
Infinite_Loop:
|
||||
b Infinite_Loop
|
||||
.size Default_Handler, .-Default_Handler
|
||||
/******************************************************************************
|
||||
*
|
||||
* The minimal vector table for a Cortex M3. Note that the proper constructs
|
||||
* must be placed on this to ensure that it ends up at physical address
|
||||
* 0x0000.0000.
|
||||
*
|
||||
*******************************************************************************/
|
||||
.section .isr_vector,"a",%progbits
|
||||
.type g_pfnVectors, %object
|
||||
.size g_pfnVectors, .-g_pfnVectors
|
||||
|
||||
|
||||
g_pfnVectors:
|
||||
.word _estack
|
||||
.word Reset_Handler
|
||||
.word NMI_Handler
|
||||
.word HardFault_Handler
|
||||
.word MemManage_Handler
|
||||
.word BusFault_Handler
|
||||
.word UsageFault_Handler
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word SVC_Handler
|
||||
.word DebugMon_Handler
|
||||
.word 0
|
||||
.word PendSV_Handler
|
||||
.word SysTick_Handler
|
||||
|
||||
/* External Interrupts */
|
||||
.word WWDG_IRQHandler /* Window WatchDog */
|
||||
.word PVD_IRQHandler /* PVD through EXTI Line detection */
|
||||
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
|
||||
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
|
||||
.word FLASH_IRQHandler /* FLASH */
|
||||
.word RCC_IRQHandler /* RCC */
|
||||
.word EXTI0_IRQHandler /* EXTI Line0 */
|
||||
.word EXTI1_IRQHandler /* EXTI Line1 */
|
||||
.word EXTI2_IRQHandler /* EXTI Line2 */
|
||||
.word EXTI3_IRQHandler /* EXTI Line3 */
|
||||
.word EXTI4_IRQHandler /* EXTI Line4 */
|
||||
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
|
||||
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
|
||||
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
|
||||
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
|
||||
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
|
||||
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
|
||||
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
|
||||
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
|
||||
.word CAN1_TX_IRQHandler /* CAN1 TX */
|
||||
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */
|
||||
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
|
||||
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
|
||||
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
|
||||
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
|
||||
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
|
||||
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
|
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
|
||||
.word TIM2_IRQHandler /* TIM2 */
|
||||
.word TIM3_IRQHandler /* TIM3 */
|
||||
.word TIM4_IRQHandler /* TIM4 */
|
||||
.word I2C1_EV_IRQHandler /* I2C1 Event */
|
||||
.word I2C1_ER_IRQHandler /* I2C1 Error */
|
||||
.word I2C2_EV_IRQHandler /* I2C2 Event */
|
||||
.word I2C2_ER_IRQHandler /* I2C2 Error */
|
||||
.word SPI1_IRQHandler /* SPI1 */
|
||||
.word SPI2_IRQHandler /* SPI2 */
|
||||
.word USART1_IRQHandler /* USART1 */
|
||||
.word USART2_IRQHandler /* USART2 */
|
||||
.word USART3_IRQHandler /* USART3 */
|
||||
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
|
||||
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
|
||||
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */
|
||||
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
|
||||
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
|
||||
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
|
||||
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
|
||||
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
|
||||
.word FSMC_IRQHandler /* FSMC */
|
||||
.word SDIO_IRQHandler /* SDIO */
|
||||
.word TIM5_IRQHandler /* TIM5 */
|
||||
.word SPI3_IRQHandler /* SPI3 */
|
||||
.word UART4_IRQHandler /* UART4 */
|
||||
.word UART5_IRQHandler /* UART5 */
|
||||
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */
|
||||
.word TIM7_IRQHandler /* TIM7 */
|
||||
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
|
||||
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
|
||||
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
|
||||
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
|
||||
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
|
||||
.word ETH_IRQHandler /* Ethernet */
|
||||
.word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */
|
||||
.word CAN2_TX_IRQHandler /* CAN2 TX */
|
||||
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */
|
||||
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */
|
||||
.word CAN2_SCE_IRQHandler /* CAN2 SCE */
|
||||
.word OTG_FS_IRQHandler /* USB OTG FS */
|
||||
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
|
||||
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
|
||||
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
|
||||
.word USART6_IRQHandler /* USART6 */
|
||||
.word I2C3_EV_IRQHandler /* I2C3 event */
|
||||
.word I2C3_ER_IRQHandler /* I2C3 error */
|
||||
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */
|
||||
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */
|
||||
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */
|
||||
.word OTG_HS_IRQHandler /* USB OTG HS */
|
||||
.word DCMI_IRQHandler /* DCMI */
|
||||
.word 0 /* CRYP crypto */
|
||||
.word HASH_RNG_IRQHandler /* Hash and Rng */
|
||||
.word FPU_IRQHandler /* FPU */
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Provide weak aliases for each Exception handler to the Default_Handler.
|
||||
* As they are weak aliases, any function with the same name will override
|
||||
* this definition.
|
||||
*
|
||||
*******************************************************************************/
|
||||
.weak NMI_Handler
|
||||
.thumb_set NMI_Handler,Default_Handler
|
||||
|
||||
.weak HardFault_Handler
|
||||
.thumb_set HardFault_Handler,Default_Handler
|
||||
|
||||
.weak MemManage_Handler
|
||||
.thumb_set MemManage_Handler,Default_Handler
|
||||
|
||||
.weak BusFault_Handler
|
||||
.thumb_set BusFault_Handler,Default_Handler
|
||||
|
||||
.weak UsageFault_Handler
|
||||
.thumb_set UsageFault_Handler,Default_Handler
|
||||
|
||||
.weak SVC_Handler
|
||||
.thumb_set SVC_Handler,Default_Handler
|
||||
|
||||
.weak DebugMon_Handler
|
||||
.thumb_set DebugMon_Handler,Default_Handler
|
||||
|
||||
.weak PendSV_Handler
|
||||
.thumb_set PendSV_Handler,Default_Handler
|
||||
|
||||
.weak SysTick_Handler
|
||||
.thumb_set SysTick_Handler,Default_Handler
|
||||
|
||||
.weak WWDG_IRQHandler
|
||||
.thumb_set WWDG_IRQHandler,Default_Handler
|
||||
|
||||
.weak PVD_IRQHandler
|
||||
.thumb_set PVD_IRQHandler,Default_Handler
|
||||
|
||||
.weak TAMP_STAMP_IRQHandler
|
||||
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
|
||||
|
||||
.weak RTC_WKUP_IRQHandler
|
||||
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
|
||||
|
||||
.weak FLASH_IRQHandler
|
||||
.thumb_set FLASH_IRQHandler,Default_Handler
|
||||
|
||||
.weak RCC_IRQHandler
|
||||
.thumb_set RCC_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI0_IRQHandler
|
||||
.thumb_set EXTI0_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI1_IRQHandler
|
||||
.thumb_set EXTI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI2_IRQHandler
|
||||
.thumb_set EXTI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI3_IRQHandler
|
||||
.thumb_set EXTI3_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI4_IRQHandler
|
||||
.thumb_set EXTI4_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream0_IRQHandler
|
||||
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream1_IRQHandler
|
||||
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream2_IRQHandler
|
||||
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream3_IRQHandler
|
||||
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream4_IRQHandler
|
||||
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream5_IRQHandler
|
||||
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream6_IRQHandler
|
||||
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler
|
||||
|
||||
.weak ADC_IRQHandler
|
||||
.thumb_set ADC_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_TX_IRQHandler
|
||||
.thumb_set CAN1_TX_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_RX0_IRQHandler
|
||||
.thumb_set CAN1_RX0_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_RX1_IRQHandler
|
||||
.thumb_set CAN1_RX1_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_SCE_IRQHandler
|
||||
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI9_5_IRQHandler
|
||||
.thumb_set EXTI9_5_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_BRK_TIM9_IRQHandler
|
||||
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_UP_TIM10_IRQHandler
|
||||
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_TRG_COM_TIM11_IRQHandler
|
||||
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_CC_IRQHandler
|
||||
.thumb_set TIM1_CC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM2_IRQHandler
|
||||
.thumb_set TIM2_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM3_IRQHandler
|
||||
.thumb_set TIM3_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM4_IRQHandler
|
||||
.thumb_set TIM4_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C1_EV_IRQHandler
|
||||
.thumb_set I2C1_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C1_ER_IRQHandler
|
||||
.thumb_set I2C1_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C2_EV_IRQHandler
|
||||
.thumb_set I2C2_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C2_ER_IRQHandler
|
||||
.thumb_set I2C2_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI1_IRQHandler
|
||||
.thumb_set SPI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI2_IRQHandler
|
||||
.thumb_set SPI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART1_IRQHandler
|
||||
.thumb_set USART1_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART2_IRQHandler
|
||||
.thumb_set USART2_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART3_IRQHandler
|
||||
.thumb_set USART3_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI15_10_IRQHandler
|
||||
.thumb_set EXTI15_10_IRQHandler,Default_Handler
|
||||
|
||||
.weak RTC_Alarm_IRQHandler
|
||||
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_FS_WKUP_IRQHandler
|
||||
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_BRK_TIM12_IRQHandler
|
||||
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_UP_TIM13_IRQHandler
|
||||
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_TRG_COM_TIM14_IRQHandler
|
||||
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_CC_IRQHandler
|
||||
.thumb_set TIM8_CC_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream7_IRQHandler
|
||||
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler
|
||||
|
||||
.weak FSMC_IRQHandler
|
||||
.thumb_set FSMC_IRQHandler,Default_Handler
|
||||
|
||||
.weak SDIO_IRQHandler
|
||||
.thumb_set SDIO_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM5_IRQHandler
|
||||
.thumb_set TIM5_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI3_IRQHandler
|
||||
.thumb_set SPI3_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART4_IRQHandler
|
||||
.thumb_set UART4_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART5_IRQHandler
|
||||
.thumb_set UART5_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM6_DAC_IRQHandler
|
||||
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM7_IRQHandler
|
||||
.thumb_set TIM7_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream0_IRQHandler
|
||||
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream1_IRQHandler
|
||||
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream2_IRQHandler
|
||||
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream3_IRQHandler
|
||||
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream4_IRQHandler
|
||||
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler
|
||||
|
||||
.weak ETH_IRQHandler
|
||||
.thumb_set ETH_IRQHandler,Default_Handler
|
||||
|
||||
.weak ETH_WKUP_IRQHandler
|
||||
.thumb_set ETH_WKUP_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_TX_IRQHandler
|
||||
.thumb_set CAN2_TX_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_RX0_IRQHandler
|
||||
.thumb_set CAN2_RX0_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_RX1_IRQHandler
|
||||
.thumb_set CAN2_RX1_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_SCE_IRQHandler
|
||||
.thumb_set CAN2_SCE_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_FS_IRQHandler
|
||||
.thumb_set OTG_FS_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream5_IRQHandler
|
||||
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream6_IRQHandler
|
||||
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream7_IRQHandler
|
||||
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART6_IRQHandler
|
||||
.thumb_set USART6_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C3_EV_IRQHandler
|
||||
.thumb_set I2C3_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C3_ER_IRQHandler
|
||||
.thumb_set I2C3_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_HS_EP1_OUT_IRQHandler
|
||||
.thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_HS_EP1_IN_IRQHandler
|
||||
.thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_HS_WKUP_IRQHandler
|
||||
.thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_HS_IRQHandler
|
||||
.thumb_set OTG_HS_IRQHandler,Default_Handler
|
||||
|
||||
.weak DCMI_IRQHandler
|
||||
.thumb_set DCMI_IRQHandler,Default_Handler
|
||||
|
||||
.weak HASH_RNG_IRQHandler
|
||||
.thumb_set HASH_RNG_IRQHandler,Default_Handler
|
||||
|
||||
.weak FPU_IRQHandler
|
||||
.thumb_set FPU_IRQHandler,Default_Handler
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
Loading…
Reference in New Issue
Block a user