移植适配新硬件ver3,做了一些优化喝结构调整

This commit is contained in:
mypx 2025-10-27 15:18:13 +08:00
parent 03497abc7f
commit 003e11c96a
62 changed files with 2310 additions and 3889 deletions

View File

@ -59,127 +59,16 @@ target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
-u _printf_float
)
# ETK module configuration options - based on etk CMakeLists.txt option naming
# Algorithm modules
# set(ETK_ALGORITHM ON CACHE BOOL "Enable algorithm module")
# set(ALG_DSP_UTILS ON CACHE BOOL "Enable dsp_utils submodule")
# set(ALG_FILTER ON CACHE BOOL "Enable filter submodule")
# set(ALG_GROOVE_TRACKER OFF CACHE BOOL "Enable groove_tracker submodule")
# set(ALG_MATH ON CACHE BOOL "Enable math submodule")
# set(ALG_PID ON CACHE BOOL "Enable pid submodule")
# set(ALG_TREND OFF CACHE BOOL "Enable trend submodule")
# # DSP Utils submodules
# set(ALG_FFT OFF CACHE BOOL "Enable FFT submodule")
# set(ALG_FREQ_ANALYZER OFF CACHE BOOL "Enable frequency analyzer submodule")
# set(ALG_GOERTZEL ON CACHE BOOL "Enable Goertzel submodule")
# set(ALG_INTERP OFF CACHE BOOL "Enable interpolation submodule")
# set(ALG_WAVES OFF CACHE BOOL "Enable waves submodule")
# set(ALG_WINDOWS OFF CACHE BOOL "Enable windows submodule")
# set(ETK_ZERO_CROSS ON CACHE BOOL "Enable zero_cross submodule")
# # Filter submodules
# set(ALG_BUTTERWORTH_FILTER OFF CACHE BOOL "Enable Butterworth filter submodule")
# set(ALG_CHEBYSHEV_FILTER OFF CACHE BOOL "Enable Chebyshev filter submodule")
# set(ALG_EMA_FILTER ON CACHE BOOL "Enable EMA filter submodule")
# set(ALG_FIR_FILTER OFF CACHE BOOL "Enable FIR filter submodule")
# set(ALG_GAUSSIAN_FILTER OFF CACHE BOOL "Enable Gaussian filter submodule")
# set(ALG_HIGH_PASS_FILTER OFF CACHE BOOL "Enable high pass filter submodule")
# set(ALG_IIR_FILTER OFF CACHE BOOL "Enable IIR filter submodule")
# set(ALG_KALMAN_FILTER OFF CACHE BOOL "Enable Kalman filter submodule")
# set(ALG_MEDIAN_FILTER OFF CACHE BOOL "Enable median filter submodule")
# set(ALG_SAVITZKY_FILTER OFF CACHE BOOL "Enable Savitzky filter submodule")
# set(ALG_SLIDING_FILTER OFF CACHE BOOL "Enable sliding filter submodule")
# # Math submodules
# set(ALG_WAVEFORM_ANALYSIS OFF CACHE BOOL "Enable waveform analysis submodule")
# set(ALG_LINEAR_SOLVER OFF CACHE BOOL "Enable linear solver submodule")
# set(ALG_NEWTON OFF CACHE BOOL "Enable Newton method submodule")
# set(ALG_DIFF OFF CACHE BOOL "Enable differentiation submodule")
# set(ALG_SLOPE ON CACHE BOOL "Enable slope calculation submodule")
# # Drivers modules
# set(ETK_DRIVERS ON CACHE BOOL "Enable drivers module")
# set(DRV_AD779X OFF CACHE BOOL "Enable ad779x driver submodule")
# set(DRV_ENCODER ON CACHE BOOL "Enable encoder driver submodule")
# # Utils modules
# set(ETK_UTILS ON CACHE BOOL "Enable utils module")
# set(UTIL_BYTE_CONV ON CACHE BOOL "Enable byte_conversion submodule")
# set(UTIL_FLEX_QUEUE OFF CACHE BOOL "Enable flex_queue submodule")
# set(UTIL_RINGBUFFER ON CACHE BOOL "Enable ringbuffer submodule")
# # System modules
# set(ETK_OS_ADAPT OFF CACHE BOOL "Enable os_adapt module")
# set(ETK_LOG_FRAMEWORK OFF CACHE BOOL "Enable log framework module")
# set(UTIL_LOG_SIMPLE ON CACHE BOOL "Enable logging module")
# # Communication modules
# set(ETK_COMMUNICATION OFF CACHE BOOL "Enable communication module")
# # Thirdparty modules
# set(THIRDPARTY ON CACHE BOOL "Enable thirdparty module")
# set(NANOMODBUS ON CACHE BOOL "Enable nanomodbus submodule")
# # Common modules (always enabled as base dependency)
# set(ETK_COMMON ON CACHE BOOL "Enable common module")
# set(ETK_ROOT_CMAKE ON CACHE BOOL "Build all modules into a single etk library")
# set(SYSTEM_TYPE 2 CACHE STRING "System type: 0(SYS_NONE_OS), 1(SYS_FREERTOS), 2(SYS_RTTHREAD), 3(SYS_LINUX), 4(SYS_POSIX)")
# RT-Thread specific configuration for ETK
#set(USE_EXTERNAL_RTTHREAD ON CACHE BOOL "Use external RT-Thread library")
#set(FETCH_RTTHREAD OFF CACHE BOOL "Download RT-Thread via FetchContent")
# Set RTT_ROOT to tell ETK where to find RT-Thread source code
#set(RTT_ROOT "${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/RealThread_RTOS_RT-Thread" CACHE PATH "RT-Thread source root directory for ETK")
# Set RTT_CONFIG_DIR for rtconfig.h location
#set(RTT_CONFIG_DIR "${CMAKE_SOURCE_DIR}/RT-Thread" CACHE PATH "RT-Thread config directory")
# if(NOT EXISTS "${RTT_CONFIG_DIR}/rtconfig.h")
# message(WARNING "RT-Thread config file rtconfig.h not found at: ${RTT_CONFIG_DIR}")
# endif()
# Additional configuration options for specific features
#set(ENABLE_ARM_CMSIS_DSP OFF CACHE BOOL "Enable ARM CMSIS-DSP library")
# Set RT-Thread include paths - include both source and config directories
# set(RTTHREAD_INCLUDE_DIRS
# ${RTT_ROOT}/include
# ${RTT_ROOT}/components/finsh
# ${RTT_ROOT}/components/finsh/inc
# ${RTT_ROOT}/components/drivers/include
# ${RTT_CONFIG_DIR} # Add rtconfig.h directory
# )
# # Set RTT_ROOT as environment variable for etk os_adapt module to find
# if(DEFINED RTT_ROOT)
# set(ENV{RTT_ROOT} "${RTT_ROOT}")
# message(STATUS "RTT_ROOT environment variable set to: $ENV{RTT_ROOT}")
# else()
# message(FATAL_ERROR "RTT_ROOT is not defined. Please set RTT_ROOT path.")
# endif()
# # Also set RTT_CONFIG_DIR as environment variable for rtconfig.h
# if(DEFINED RTT_CONFIG_DIR)
# set(ENV{RTT_CONFIG_DIR} "${RTT_CONFIG_DIR}")
# message(STATUS "RTT_CONFIG_DIR environment variable set to: $ENV{RTT_CONFIG_DIR}")
# endif()
# add_subdirectory(etk)
# # Link ETK library to main project to get access to all ETK headers
# if(ETK_ROOT_CMAKE)
# target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE etk)
# # Ensure etk library can get SYSTEM_TYPE value
# target_compile_definitions(etk PUBLIC SYSTEM_TYPE=${SYSTEM_TYPE})
# endif()
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
${CMAKE_SOURCE_DIR}/User/board/pm_board.c
${CMAKE_SOURCE_DIR}/User/board/bsp_misc.c
${CMAKE_SOURCE_DIR}/User/board/bsp_encoder.c
${CMAKE_SOURCE_DIR}/User/board/bsp_collect.c
${CMAKE_SOURCE_DIR}/User/board/bsp_tec.c
${CMAKE_SOURCE_DIR}/User/board/bsp_temper_sampling.c
${CMAKE_SOURCE_DIR}/User/board/bsp_hmi.c
${CMAKE_SOURCE_DIR}/User/board/bsp_motor.c
${CMAKE_SOURCE_DIR}/User/driver/at24cx/at24cx.c
${CMAKE_SOURCE_DIR}/User/driver/ad779x/ad7793.c
${CMAKE_SOURCE_DIR}/segger_rtt/RTT/SEGGER_RTT_printf.c
@ -208,10 +97,10 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/User/app/mb_hmi/hmi_server.c
${CMAKE_SOURCE_DIR}/User/app/pm_meas.c
${CMAKE_SOURCE_DIR}/User/app/pm_params.c
${CMAKE_SOURCE_DIR}/User/app/bsp_debug.c
${CMAKE_SOURCE_DIR}/nanoMODBUS/nanomodbus.c
${CMAKE_SOURCE_DIR}/etk/src/drivers/encoder/src/et_encoder.c
${CMAKE_SOURCE_DIR}/etk/src/drivers/encoder/src/et_encoder_utils.c
# ETK logging
${CMAKE_SOURCE_DIR}/etk/src/logging/et_log.c
${CMAKE_SOURCE_DIR}/etk/src/algorithm/filter/src/et_ema_filter.c
${CMAKE_SOURCE_DIR}/etk/src/algorithm/pid/src/etk_pid.c
@ -226,8 +115,6 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
# Note: ETK module include paths are now automatically included via the etk library
${CMAKE_SOURCE_DIR}/User/board
${CMAKE_SOURCE_DIR}/segger_rtt/Config/
${CMAKE_SOURCE_DIR}/segger_rtt/RTT/
@ -239,7 +126,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/User/
${CMAKE_SOURCE_DIR}/User/app/
${CMAKE_SOURCE_DIR}/User/app/mb_hmi/
# Add nanoMODBUS include path explicitly
${CMAKE_SOURCE_DIR}/nanoMODBUS
${CMAKE_SOURCE_DIR}/etk/src/logging
${CMAKE_SOURCE_DIR}/etk/src/algorithm/math/include
@ -260,7 +147,7 @@ target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
ARM_MATH
SYSTEM_TYPE=2
ET_SLIDING_FILTER_SIZE=4
NMBS_DEBUG
#NMBS_DEBUG
USING_RTT_AS_CONSOLE
RT_DEBUG
)

View File

@ -57,27 +57,25 @@ void Error_Handler(void);
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define TEC_DEAD_TIME 100
#define HMI_COM huart2
#define ENCODER_MAX_COUNT 65535
#define AD7793_SPI hspi3
#define US_TIM_PRE 71
#define RS485_COM huart5
#define ENCODER_TIM htim2
#define TEC_TIM_COUNTER 143
#define SAMPLING_TIM_PRE 71
#define US_TIM_PERIOD_COUNT 9999
#define TEC_TIM_PRESCALER 99
#define TEC_TIM htim1
#define HMI_COM huart2
#define SAMPLING_TIM htim3
#define US_TIM_PRE 71
#define TIMESTAMP_TIM htim4
#define ENCODER_TIM htim2
#define SAMPLING_TIM_PERIOD_COUNT 499//999
#define SERVO_COM huart3
#define LIGHT_ADC hadc1
#define ENCODER_MAX_COUNT 65535
#define TEC_TIM htim1
#define COLLECT_TIM htim3
#define ADS8866_SPI hspi2
#define PE5_LED1_Pin GPIO_PIN_5
#define PE5_LED1_GPIO_Port GPIOE
#define PE6_LED2_Pin GPIO_PIN_6
#define PE6_LED2_GPIO_Port GPIOE
#define RS485_DE_Pin GPIO_PIN_1
#define RS485_DE_GPIO_Port GPIOF
#define TIM2_ENCODER_B_Pin GPIO_PIN_0
#define TIM2_ENCODER_B_GPIO_Port GPIOA
#define TIM2_ENCODER_A_Pin GPIO_PIN_1
@ -86,16 +84,24 @@ void Error_Handler(void);
#define UART2_TX_LCD_GPIO_Port GPIOA
#define UART2_RX_LCD_Pin GPIO_PIN_3
#define UART2_RX_LCD_GPIO_Port GPIOA
#define TEC_LEFT_SD_Pin GPIO_PIN_6
#define TEC_LEFT_SD_GPIO_Port GPIOA
#define TEC_RIGHT_SD_Pin GPIO_PIN_7
#define TEC_RIGHT_SD_GPIO_Port GPIOA
#define ADS8866_CONVST_Pin GPIO_PIN_0
#define ADS8866_CONVST_GPIO_Port GPIOB
#define I2C2_EEPROM_SCL_Pin GPIO_PIN_10
#define I2C2_EEPROM_SCL_GPIO_Port GPIOB
#define I2C2_EEPROM_SDA_Pin GPIO_PIN_11
#define I2C2_EEPROM_SDA_GPIO_Port GPIOB
#define TIM1_CH1N_TEC_Pin GPIO_PIN_13
#define TIM1_CH1N_TEC_GPIO_Port GPIOB
#define FAN_CTRL_Pin GPIO_PIN_15
#define FAN_CTRL_GPIO_Port GPIOD
#define TIM1_CH1_TEC_Pin GPIO_PIN_8
#define TIM1_CH1_TEC_GPIO_Port GPIOA
#define SPI2_ADS8866_SCK_Pin GPIO_PIN_13
#define SPI2_ADS8866_SCK_GPIO_Port GPIOB
#define SPI2_ADS8866_MISO_Pin GPIO_PIN_14
#define SPI2_ADS8866_MISO_GPIO_Port GPIOB
#define SPI2_ADS8866_MOSI_Pin GPIO_PIN_15
#define SPI2_ADS8866_MOSI_GPIO_Port GPIOB
#define TEC_RIGHT_IN_Pin GPIO_PIN_8
#define TEC_RIGHT_IN_GPIO_Port GPIOA
#define UART1_LCD_TX_Pin GPIO_PIN_9
#define UART1_LCD_TX_GPIO_Port GPIOA
#define UART1_LCD_RX_Pin GPIO_PIN_10
@ -110,14 +116,14 @@ void Error_Handler(void);
#define UART5_RS485_RX_GPIO_Port GPIOD
#define FAN_CONTROL_Pin GPIO_PIN_5
#define FAN_CONTROL_GPIO_Port GPIOD
#define SPI3_SCK_AD7793_Pin GPIO_PIN_3
#define SPI3_SCK_AD7793_GPIO_Port GPIOB
#define SPI3_MISO_AD7793_Pin GPIO_PIN_4
#define SPI3_MISO_AD7793_GPIO_Port GPIOB
#define SPI3_MOSI_AD7793_Pin GPIO_PIN_5
#define SPI3_MOSI_AD7793_GPIO_Port GPIOB
#define SPI3_CS_AD7793_Pin GPIO_PIN_7
#define SPI3_CS_AD7793_GPIO_Port GPIOB
#define SPI3_AD7793_SCK_Pin GPIO_PIN_3
#define SPI3_AD7793_SCK_GPIO_Port GPIOB
#define SPI3_AD7793_MISO_Pin GPIO_PIN_4
#define SPI3_AD7793_MISO_GPIO_Port GPIOB
#define SPI3_AD7793_MOSI_Pin GPIO_PIN_5
#define SPI3_AD7793_MOSI_GPIO_Port GPIOB
#define SPI3_AD7793_CS_Pin GPIO_PIN_7
#define SPI3_AD7793_CS_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */

View File

@ -32,12 +32,15 @@ extern "C" {
/* USER CODE END Includes */
extern SPI_HandleTypeDef hspi2;
extern SPI_HandleTypeDef hspi3;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_SPI2_Init(void);
void MX_SPI3_Init(void);
/* USER CODE BEGIN Prototypes */

View File

@ -51,10 +51,10 @@ void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void DebugMon_Handler(void);
void DMA1_Channel1_IRQHandler(void);
void DMA1_Channel4_IRQHandler(void);
void DMA1_Channel5_IRQHandler(void);
void TIM2_IRQHandler(void);
void TIM3_IRQHandler(void);
void TIM4_IRQHandler(void);
void USART1_IRQHandler(void);
void USART2_IRQHandler(void);
void USART3_IRQHandler(void);

View File

@ -38,8 +38,6 @@ extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim3;
extern TIM_HandleTypeDef htim4;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
@ -47,7 +45,6 @@ extern TIM_HandleTypeDef htim4;
void MX_TIM1_Init(void);
void MX_TIM2_Init(void);
void MX_TIM3_Init(void);
void MX_TIM4_Init(void);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);

View File

@ -25,7 +25,6 @@
/* USER CODE END 0 */
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
/* ADC1 init function */
void MX_ADC1_Init(void)
@ -47,7 +46,7 @@ void MX_ADC1_Init(void)
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
@ -57,9 +56,9 @@ void MX_ADC1_Init(void)
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_10;
sConfig.Channel = ADC_CHANNEL_11;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
@ -84,29 +83,12 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
__HAL_RCC_GPIOC_CLK_ENABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
@ -125,12 +107,10 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0);
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1);
/* ADC1 DMA DeInit */
HAL_DMA_DeInit(adcHandle->DMA_Handle);
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */

View File

@ -43,9 +43,12 @@ void MX_DMA_Init(void)
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* DMA1_Channel4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
/* DMA1_Channel5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
}

View File

@ -47,6 +47,7 @@ void MX_GPIO_Init(void)
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
@ -55,10 +56,16 @@ void MX_GPIO_Init(void)
HAL_GPIO_WritePin(GPIOE, PE5_LED1_Pin|PE6_LED2_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, FAN_CTRL_Pin|FAN_CONTROL_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(SPI3_CS_AD7793_GPIO_Port, SPI3_CS_AD7793_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, TEC_LEFT_SD_Pin|TEC_RIGHT_SD_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, FAN_CONTROL_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(SPI3_AD7793_CS_GPIO_Port, SPI3_AD7793_CS_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PE5_LED1_Pin PE6_LED2_Pin */
GPIO_InitStruct.Pin = PE5_LED1_Pin|PE6_LED2_Pin;
@ -67,19 +74,33 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pin : RS485_DE_Pin */
GPIO_InitStruct.Pin = RS485_DE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(RS485_DE_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : TEC_LEFT_SD_Pin TEC_RIGHT_SD_Pin */
GPIO_InitStruct.Pin = TEC_LEFT_SD_Pin|TEC_RIGHT_SD_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : FAN_CTRL_Pin FAN_CONTROL_Pin */
GPIO_InitStruct.Pin = FAN_CTRL_Pin|FAN_CONTROL_Pin;
GPIO_InitStruct.Pin = FAN_CONTROL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/*Configure GPIO pin : SPI3_CS_AD7793_Pin */
GPIO_InitStruct.Pin = SPI3_CS_AD7793_Pin;
/*Configure GPIO pin : SPI3_AD7793_CS_Pin */
GPIO_InitStruct.Pin = SPI3_AD7793_CS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SPI3_CS_AD7793_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_Init(SPI3_AD7793_CS_GPIO_Port, &GPIO_InitStruct);
}

View File

@ -18,7 +18,6 @@
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
@ -28,6 +27,7 @@
#include "tim.h"
#include <rtthread.h>
#include "et_log.h"
#include "bsp_collect.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@ -68,46 +68,45 @@ void SystemClock_Config(void);
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* USER CODE BEGIN 1 */
/* MCU Configuration--------------------------------------------------------*/
/* USER CODE END 1 */
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* MCU Configuration--------------------------------------------------------*/
/* USER CODE BEGIN Init */
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE END Init */
/* USER CODE BEGIN Init */
/* USER CODE BEGIN SysInit */
/* USER CODE END Init */
/* USER CODE END SysInit */
/* USER CODE BEGIN SysInit */
/* Initialize all configured peripherals */
/* USER CODE END SysInit */
/* USER CODE BEGIN 2 */
/* Initialize all configured peripherals */
/* USER CODE BEGIN 2 */
et_log_init(DEBUG_LEVEL, false, true);
pm_device_init(&pm_device);
/* USER CODE END 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
pm_device_start(&pm_device);
/* Infinite loop */
/* USER CODE BEGIN WHILE */
//pm_device_start(&pm_device);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
rt_thread_mdelay(1000);
//pm_lcd_cmd_send("TEST\r\n", 6);
/* USER CODE BEGIN 3 */
rt_thread_mdelay(1000);
#if (TEST_ENCODER_PLUSE == 1)
rt_kprintf("count:%d\r\n", pm_encoder_get_count());
rt_kprintf("count:%d\r\n", pm_encoder_get_count());
#endif
//pm_system_led_toggle(); // Toggle the system LEDs
//bsp_system_led_toggle(); // Toggle the system LEDs
}
/* USER CODE END 3 */
/* USER CODE END 3 */
}
/**
@ -116,43 +115,44 @@ int main(void)
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
@ -169,21 +169,14 @@ void SystemClock_Config(void)
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM8)
{
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == SAMPLING_TIM.Instance)
{
pm_system_led_toggle();
#if (TEST_ENCODER_PLUSE == 0)
//data_sampling_once();
#endif
}
/* USER CODE END Callback 0 */
if (htim->Instance == TIM8)
{
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
#if (USING_SOFT_ENCODER == 0)
if (htim->Instance == ENCODER_TIM.Instance)
{
@ -194,14 +187,12 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
encoder_tim_overflow_count++;
rt_interrupt_leave(); // Enable interrupts
}
#endif // USING_SOFT_ENCODER
if (htim->Instance == TIMESTAMP_TIM.Instance)
#endif
if (htim->Instance == TIM3)
{
rt_interrupt_enter();
timestamp_tim_overflow_count++;
rt_interrupt_leave();
sampling_tim_elapsed_callback();
}
/* USER CODE END Callback 1 */
/* USER CODE END Callback 1 */
}
/**
@ -210,11 +201,12 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
ET_ERR("Error Handler Entered!\r\n");
while (1) {}
/* USER CODE END Error_Handler_Debug */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
@ -226,9 +218,9 @@ void Error_Handler(void)
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

View File

@ -24,8 +24,43 @@
/* USER CODE END 0 */
SPI_HandleTypeDef hspi2;
SPI_HandleTypeDef hspi3;
DMA_HandleTypeDef hdma_spi2_rx;
DMA_HandleTypeDef hdma_spi2_tx;
/* SPI2 init function */
void MX_SPI2_Init(void)
{
/* USER CODE BEGIN SPI2_Init 0 */
/* USER CODE END SPI2_Init 0 */
/* USER CODE BEGIN SPI2_Init 1 */
/* USER CODE END SPI2_Init 1 */
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI2_Init 2 */
/* USER CODE END SPI2_Init 2 */
}
/* SPI3 init function */
void MX_SPI3_Init(void)
{
@ -63,7 +98,68 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(spiHandle->Instance==SPI3)
if(spiHandle->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspInit 0 */
/* USER CODE END SPI2_MspInit 0 */
/* SPI2 clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB14 ------> SPI2_MISO
PB15 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = SPI2_ADS8866_SCK_Pin|SPI2_ADS8866_MOSI_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPI2_ADS8866_MISO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(SPI2_ADS8866_MISO_GPIO_Port, &GPIO_InitStruct);
/* SPI2 DMA Init */
/* SPI2_RX Init */
hdma_spi2_rx.Instance = DMA1_Channel4;
hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi2_rx.Init.Mode = DMA_NORMAL;
hdma_spi2_rx.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(spiHandle,hdmarx,hdma_spi2_rx);
/* SPI2_TX Init */
hdma_spi2_tx.Instance = DMA1_Channel5;
hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi2_tx.Init.Mode = DMA_NORMAL;
hdma_spi2_tx.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(spiHandle,hdmatx,hdma_spi2_tx);
/* USER CODE BEGIN SPI2_MspInit 1 */
/* USER CODE END SPI2_MspInit 1 */
}
else if(spiHandle->Instance==SPI3)
{
/* USER CODE BEGIN SPI3_MspInit 0 */
@ -77,15 +173,15 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
PB4 ------> SPI3_MISO
PB5 ------> SPI3_MOSI
*/
GPIO_InitStruct.Pin = SPI3_SCK_AD7793_Pin|SPI3_MOSI_AD7793_Pin;
GPIO_InitStruct.Pin = SPI3_AD7793_SCK_Pin|SPI3_AD7793_MOSI_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPI3_MISO_AD7793_Pin;
GPIO_InitStruct.Pin = SPI3_AD7793_MISO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(SPI3_MISO_AD7793_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_Init(SPI3_AD7793_MISO_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN SPI3_MspInit 1 */
@ -96,7 +192,29 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
if(spiHandle->Instance==SPI3)
if(spiHandle->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspDeInit 0 */
/* USER CODE END SPI2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI2_CLK_DISABLE();
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB14 ------> SPI2_MISO
PB15 ------> SPI2_MOSI
*/
HAL_GPIO_DeInit(GPIOB, SPI2_ADS8866_SCK_Pin|SPI2_ADS8866_MISO_Pin|SPI2_ADS8866_MOSI_Pin);
/* SPI2 DMA DeInit */
HAL_DMA_DeInit(spiHandle->hdmarx);
HAL_DMA_DeInit(spiHandle->hdmatx);
/* USER CODE BEGIN SPI2_MspDeInit 1 */
/* USER CODE END SPI2_MspDeInit 1 */
}
else if(spiHandle->Instance==SPI3)
{
/* USER CODE BEGIN SPI3_MspDeInit 0 */
@ -109,7 +227,7 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
PB4 ------> SPI3_MISO
PB5 ------> SPI3_MOSI
*/
HAL_GPIO_DeInit(GPIOB, SPI3_SCK_AD7793_Pin|SPI3_MISO_AD7793_Pin|SPI3_MOSI_AD7793_Pin);
HAL_GPIO_DeInit(GPIOB, SPI3_AD7793_SCK_Pin|SPI3_AD7793_MISO_Pin|SPI3_AD7793_MOSI_Pin);
/* USER CODE BEGIN SPI3_MspDeInit 1 */

View File

@ -22,11 +22,13 @@
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "pm_board.h"
#include "bsp_misc.h"
#include "pm_device.h"
#include "pm_common.h"
#include "rtthread.h"
#include "et_log.h"
#include "bsp_hmi.h"
#include "bsp_motor.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@ -60,10 +62,10 @@ extern pm_device_t pm_device;
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_adc1;
extern DMA_HandleTypeDef hdma_spi2_rx;
extern DMA_HandleTypeDef hdma_spi2_tx;
extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim3;
extern TIM_HandleTypeDef htim4;
extern UART_HandleTypeDef huart5;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
@ -158,17 +160,31 @@ void DebugMon_Handler(void)
/******************************************************************************/
/**
* @brief This function handles DMA1 channel1 global interrupt.
* @brief This function handles DMA1 channel4 global interrupt.
*/
void DMA1_Channel1_IRQHandler(void)
void DMA1_Channel4_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
/* USER CODE BEGIN DMA1_Channel4_IRQn 0 */
/* USER CODE END DMA1_Channel1_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_adc1);
/* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
/* USER CODE END DMA1_Channel4_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_rx);
/* USER CODE BEGIN DMA1_Channel4_IRQn 1 */
/* USER CODE END DMA1_Channel1_IRQn 1 */
/* USER CODE END DMA1_Channel4_IRQn 1 */
}
/**
* @brief This function handles DMA1 channel5 global interrupt.
*/
void DMA1_Channel5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel5_IRQn 0 */
/* USER CODE END DMA1_Channel5_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_tx);
/* USER CODE BEGIN DMA1_Channel5_IRQn 1 */
/* USER CODE END DMA1_Channel5_IRQn 1 */
}
/**
@ -199,20 +215,6 @@ void TIM3_IRQHandler(void)
/* USER CODE END TIM3_IRQn 1 */
}
/**
* @brief This function handles TIM4 global interrupt.
*/
void TIM4_IRQHandler(void)
{
/* USER CODE BEGIN TIM4_IRQn 0 */
/* USER CODE END TIM4_IRQn 0 */
HAL_TIM_IRQHandler(&htim4);
/* USER CODE BEGIN TIM4_IRQn 1 */
/* USER CODE END TIM4_IRQn 1 */
}
/**
* @brief This function handles USART1 global interrupt.
*/

View File

@ -27,7 +27,6 @@ uint32_t encoder_mode = 0;
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim4;
/* TIM1 init function */
void MX_TIM1_Init(void)
@ -45,12 +44,12 @@ void MX_TIM1_Init(void)
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = TEC_TIM_PRESCALER;
htim1.Init.Prescaler = 7;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = TEC_TIM_COUNTER;
htim1.Init.Period = 899;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
@ -62,7 +61,7 @@ void MX_TIM1_Init(void)
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.Pulse = 100;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
@ -75,7 +74,7 @@ void MX_TIM1_Init(void)
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = TEC_DEAD_TIME;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
@ -144,14 +143,15 @@ void MX_TIM3_Init(void)
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = SAMPLING_TIM_PRE;
htim3.Init.Prescaler = 71;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = SAMPLING_TIM_PERIOD_COUNT;
htim3.Init.Period = 499;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
@ -163,55 +163,28 @@ void MX_TIM3_Init(void)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 10;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
}
/* TIM4 init function */
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
__HAL_RCC_TIM4_CLK_ENABLE();
/* USER CODE END TIM4_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM4_Init 1 */
/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = US_TIM_PRE;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = US_TIM_PERIOD_COUNT;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
HAL_TIM_Base_Start(&htim4);
/* USER CODE END TIM4_Init 2 */
HAL_TIM_MspPostInit(&htim3);
}
@ -280,21 +253,6 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
/* USER CODE END TIM3_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
/* TIM4 interrupt Init */
HAL_NVIC_SetPriority(TIM4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM4_IRQn);
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
@ -305,27 +263,38 @@ void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
/* USER CODE BEGIN TIM1_MspPostInit 0 */
/* USER CODE END TIM1_MspPostInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM1 GPIO Configuration
PB13 ------> TIM1_CH1N
PA8 ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = TIM1_CH1N_TEC_Pin;
GPIO_InitStruct.Pin = TEC_RIGHT_IN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TIM1_CH1N_TEC_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = TIM1_CH1_TEC_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TIM1_CH1_TEC_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(TEC_RIGHT_IN_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN TIM1_MspPostInit 1 */
/* USER CODE END TIM1_MspPostInit 1 */
}
else if(timHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspPostInit 0 */
/* USER CODE END TIM3_MspPostInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**TIM3 GPIO Configuration
PB0 ------> TIM3_CH3
*/
GPIO_InitStruct.Pin = ADS8866_CONVST_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(ADS8866_CONVST_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN TIM3_MspPostInit 1 */
/* USER CODE END TIM3_MspPostInit 1 */
}
}
@ -387,20 +356,6 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
/* USER CODE END TIM3_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspDeInit 0 */
/* USER CODE END TIM4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM4_CLK_DISABLE();
/* TIM4 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM4_IRQn);
/* USER CODE BEGIN TIM4_MspDeInit 1 */
/* USER CODE END TIM4_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */

79
User/app/bsp_debug.c Normal file
View File

@ -0,0 +1,79 @@
/*
* @Author: mypx
* @Date: 2025-10-15 14:22:42
* @LastEditTime: 2025-10-24 15:07:15
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "bsp_tec.h"
#include "et_log.h"
#include <rtthread.h>
#include <stdint.h>
#include <stdlib.h>
static int test_tec_cooling_open(int argc, char **argv)
{
(void)argc;
(void)argv;
bsp_tec_cooling_open();
return 0;
}
MSH_CMD_EXPORT_ALIAS(test_tec_cooling_open, cool_open, "open cooling");
static int test_tec_heating_open(int argc, char **argv)
{
(void)argc;
(void)argv;
bsp_tec_heating_open();
return 0;
}
MSH_CMD_EXPORT_ALIAS(test_tec_heating_open, heat_open, "open heating");
static int test_adjust_tec_cooling_duty_cycle(int argc, char **argv)
{
uint32_t duty_cycle = 0;
if (argc != 2)
{
rt_kprintf("Usage: tldc <duty_cycle>\n");
return -1;
}
duty_cycle = atof(argv[1]);
ET_DBG("duty_cycle:%.2f", duty_cycle);
bsp_tec_cooling_adjust(duty_cycle);
return 0;
}
MSH_CMD_EXPORT_ALIAS(test_adjust_tec_cooling_duty_cycle, tldc, "adjust cool level");
static int test_adjust_tec_heating_duty_cycle(int argc, char **argv)
{
uint32_t duty_cycle = 0;
if (argc != 2)
{
rt_kprintf("Usage: trdc <duty_cycle>\n");
return -1;
}
duty_cycle = atof(argv[1]);
ET_DBG("duty_cycle:%.2f", duty_cycle);
bsp_tec_heating_adjust(duty_cycle);
return 0;
}
MSH_CMD_EXPORT_ALIAS(test_adjust_tec_heating_duty_cycle, trdc, "adjust heat level");
static int test_fan_start(int argc, char **argv)
{
(void)argc;
(void)argv;
bsp_fan_start();
return 0;
}
MSH_CMD_EXPORT_ALIAS(test_fan_start, fan_start, "start fan");
static int test_fan_stop(int argc, char **argv)
{
(void)argc;
(void)argv;
bsp_fan_stop();
return 0;
}
MSH_CMD_EXPORT_ALIAS(test_fan_stop, fan_stop, "stop fan");

View File

@ -2,12 +2,13 @@
* @Date: 2025-07-18 09:24:34
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-09-26 08:58:26
* @LastEditTime: 2025-10-27 10:42:07
* @FilePath: data_process.c
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
*/
#include "data_process.h"
#include "bsp_collect.h"
#include "data_sampling.h"
#include "et_log.h"
#include "pm_common.h"
@ -47,8 +48,8 @@ float adc_filter_buffer[DATA_PROCESS_UNIT_COUNT] = {0};
#define ANALYSIS_SIZE DATA_PROCESS_UNIT_COUNT
static bool cali_flag = false;
static bool rdy_flag = false;
// static bool cali_flag = false;
// static bool rdy_flag = false;
pm_meas_t pm_meas;
@ -66,7 +67,8 @@ void set_pm_stage(int argc, char **argv)
pm_meas.state = stage;
}
MSH_CMD_EXPORT_ALIAS(set_pm_stage, sst, set state);
uint32_t data_count = 0;
// uint32_t data_count = 0;
// 只考虑校准和测量状态,其他状态由采集线程 或者主线程管理
void data_process_thread_entry(void *parameter)
@ -76,14 +78,29 @@ void data_process_thread_entry(void *parameter)
int find_status = 0;
uint16_t *raw_buf;
uint16_t *used_buf;
pm_fsm_t *fsm = get_pm_fsm();
uint32_t times = 0;
bsp_light_data_sampling_start();
while (1)
{
result = rt_mq_recv(&adc_mq, &raw_buf, sizeof(adc_raw_buffer), RT_WAITING_FOREVER);
/* receive pointer to buffer (sender uses sizeof(ptr)) */
result = rt_mq_recv(&adc_mq, &raw_buf, sizeof(void *), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
data_sampling_stop();
if (get_find_zero_state(&pm_meas) == ZF_FAST_CLOSE_STAGE && fsm_state == false && pm_meas.p50 > 0.1f)
#if (DEBUG_LIGHT_DATA_ONLY == 1)
for (int i = 0; i < DATA_PROCESS_UNIT_COUNT; i++)
{
ET_DBG("[%d] r:%d", times++, raw_buf[i]);
if (times >= ANALYSIS_SIZE)
times = 0;
}
bsp_light_data_sampling_start();
continue;
#endif
if (get_find_zero_state(&pm_meas) == ZF_FAST_CLOSE_STAGE && fsm->fz_fsm_state == PM_FSM_STOP
&& pm_meas.p50 > 0.1f)
sv630p_speed_mode_stop(&sv630p_handle);
#if (ENABLE_IIR_FILTER == 1)
if (et_iir_filter_process(&iir_filter, (const void *)raw_buf, (void *)adc_filter_buffer) == IIR_OK)
@ -93,22 +110,15 @@ void data_process_thread_entry(void *parameter)
#else
used_buf = raw_buf;
#endif
#if (SAMPLING_RAW_DATA == 1)
for (int i = 0; i < DATA_PROCESS_UNIT_COUNT; i++)
{
ET_DBG("r:%d, f:%d", raw_buf[i], (uint16_t)adc_filter_buffer[i]);
}
#endif
#if (SAMPLING_RAW_DATA == 0)
pm_error_led_on(); // for timing
bsp_error_led_on(); // for timing
find_status = find_zero_process(&pm_meas, (const uint16_t *)used_buf, DATA_PROCESS_UNIT_COUNT);
pm_error_led_off();
#endif
bsp_error_led_off();
if (find_status == 0)
{
data_sampling_start();
if (get_find_zero_state(&pm_meas) == ZF_FAST_CLOSE_STAGE && fsm_state == false && pm_meas.p50 > 0.1f)
bsp_light_data_sampling_start();
if (get_find_zero_state(&pm_meas) == ZF_FAST_CLOSE_STAGE && fsm->fz_fsm_state == PM_FSM_STOP
&& pm_meas.p50 > 0.1f)
sv630p_speed_mode_resume(&sv630p_handle);
}
else
@ -138,7 +148,7 @@ int data_process_init(void)
return -1;
}
//valley_seek_init(&pm_meas.valley_seek);
working_set_state(PM_MEAS_TEST_Mode);
if (et_zero_cross_create(&pm_meas.zc, 100) != 0)
{
ET_ERR("zc create failed\n");
@ -177,13 +187,13 @@ int data_process_init(void)
#endif
etk_position_pid_init(&pm_meas.pid,
1.8f, // kp: 提高比例增益,加快响应速度
1.8f, // ki: 增加积分增益,加快接近目标时的收敛速度
0.25f, // kd: 进一步降低微分作用,减少阻尼
PID_TARGET_ZERO_POS_VAL, // 目标值设置为0.6
2.5f, // 积分限幅,允许更多积分累积加快收敛
-FZ_FAST_CLOSE_STAGE_RPM, // 最小输出(限制最大反转速度)
FZ_FAST_CLOSE_STAGE_RPM, -1);
1.8f, // kp: 提高比例增益,加快响应速度
1.8f, // ki: 增加积分增益,加快接近目标时的收敛速度
0.25f, // kd: 进一步降低微分作用,减少阻尼
PID_TARGET_ZERO_POS_VAL, // 目标值设置为0.6
2.5f, // 积分限幅,允许更多积分累积加快收敛
-FZ_FAST_CLOSE_STAGE_RPM, // 最小输出(限制最大反转速度)
FZ_FAST_CLOSE_STAGE_RPM, -1);
result = rt_thread_init(&data_process_thread, // 线程控制块指针
"d-process", // 线程名称
data_process_thread_entry, // 入口函数

View File

@ -2,7 +2,7 @@
* @Date: 2025-07-15 11:12:00
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-09-24 14:24:04
* @LastEditTime: 2025-10-24 10:05:49
* @FilePath: data_sampling.c
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
@ -14,6 +14,7 @@
#include "servo.h"
#include <stdlib.h>
#include <string.h>
#include "bsp_collect.h"
uint16_t adc_raw_buffer[DATA_PROCESS_UNIT_COUNT] = {0};
@ -21,54 +22,22 @@ float sampling_rate = 0;
static void *mq_pool[8];
struct rt_messagequeue adc_mq;
/* 半缓冲完成回调 */
// void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
// {
// if (hadc->Instance == ADC1)
// {
// __HAL_DMA_CLEAR_FLAG(hadc->DMA_Handle, __HAL_DMA_GET_HT_FLAG_INDEX(hadc->DMA_Handle));
// uint16_t *ptr = &adc_raw_buffer[0];
// pm_system_led_on();
// rt_mq_send(&adc_mq, &ptr, sizeof(ptr));
// }
// }
/* 整个缓冲区完成回调 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
void light_data_sampling_complete(void)
{
if (hadc->Instance == ADC1)
uint16_t *ptr = &adc_raw_buffer[0];
//bsp_system_led_toggle();
if (rt_mq_send(&adc_mq, &ptr, sizeof(ptr)) != RT_EOK)
{
__HAL_DMA_CLEAR_FLAG(hadc->DMA_Handle, __HAL_DMA_GET_TC_FLAG_INDEX(hadc->DMA_Handle));
uint16_t *ptr = &adc_raw_buffer[0];
pm_system_led_toggle
();
if (rt_mq_send(&adc_mq, &ptr, sizeof(ptr)) != RT_EOK)
{
ET_ERR("ADC MQ full!");
}
ET_ERR("ADC MQ full!");
}
}
void data_sampling_start(void)
int light_data_sampling_init(void)
{
pm_sampling_adc_start(adc_raw_buffer, DATA_PROCESS_UNIT_COUNT);
pm_sampling_tim_start();
//pm_timestamp_start();
}
void data_sampling_stop(void)
{
//pm_encoder_stop();
// pm_sampling_tim_stop();
// pm_timestamp_stop();
pm_sampling_adc_stop();
}
int data_sampling_init(void)
{
pm_adc_init();
pm_sampling_tim_init();
bsp_light_data_sampling_init(adc_raw_buffer, DATA_PROCESS_UNIT_COUNT, light_data_sampling_complete);
pm_encoder_init();
//pm_timestamp_tim_init();
pm_encoder_start();
@ -77,9 +46,8 @@ int data_sampling_init(void)
rt_mq_init(&adc_mq, "adc_mq", mq_pool, sizeof(void *), sizeof(mq_pool), RT_IPC_FLAG_FIFO);
// test-> set state manually
//pm_set_state(PM_CALIBRATION_STATE);
sampling_rate = pm_sampling_tim_get_freq();
sampling_rate = bsp_sampling_tim_get_freq();
ET_WARN("Sampling rate:%.2f", sampling_rate);
data_sampling_start(); // test->start at init
return 0;
}

View File

@ -2,7 +2,7 @@
* @Date: 2025-07-15 11:12:08
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-09-16 11:25:32
* @LastEditTime: 2025-10-14 13:47:14
* @FilePath: data_sampling.h
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
@ -10,7 +10,7 @@
#ifndef __DATA_SAMPLING_H__
#define __DATA_SAMPLING_H__
#include "bsp_encoder.h"
#include "pm_board.h"
#include "bsp_misc.h"
extern struct rt_messagequeue adc_mq;
@ -19,10 +19,6 @@ extern uint16_t adc_raw_buffer[DATA_PROCESS_UNIT_COUNT];
extern float sampling_rate;
int data_sampling_init(void);
void data_sampling_start(void);
void data_sampling_stop(void);
int light_data_sampling_init(void);
#endif

View File

@ -1,31 +1,14 @@
# mb_hmiCMakeLists.txt
# mb_hmi
add_library(mb_hmi STATIC
mb_command.c
mb_server.c
mb_interface.c
)
#
target_include_directories(mb_hmi
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
#
target_link_libraries(mb_hmi
PUBLIC
nanomodbus
#
PUBLIC nanomodbus
)
# etk_os_adapt
if(TARGET etk_os_adapt)
target_link_libraries(mb_hmi PUBLIC etk_os_adapt)
endif()
#
if(DEFINED SYSTEM_TYPE)
message(STATUS "[mb_hmi] System type: ${SYSTEM_TYPE}")
endif()

View File

@ -3,6 +3,9 @@
#include "etk_utils.h"
#include "mb_command.h"
#include "mb_interface.h"
#include "bsp_hmi.h"
#include "bsp_motor.h"
nmbs_t nmbs;
static rt_uint8_t mb_hmi_stack[HMI_THREAD_STACK_SIZE];
@ -25,8 +28,8 @@ static nmbs_error nmbs_server_init(nmbs_t *nmbs)
nmbs_platform_conf_create(&conf);
conf.transport = NMBS_TRANSPORT_RTU;
conf.read = pm_hmi_uart_read;
conf.write = pm_hmi_uart_write;
conf.read = bsp_hmi_uart_read;
conf.write = bsp_hmi_uart_write;
nmbs_callbacks_create(&cb);
cb.read_coils = NULL;
@ -142,7 +145,7 @@ int hmi_server_handler_register(hmi_server_t *hmi, mb_cmd_handler_t *handler)
int hmi_server_init(hmi_server_t *hmi)
{
if (pm_mb_hmi_init() != 0)
if (bsp_mb_hmi_init() != 0)
{
ET_ERR("Failed to initialize Modbus HMI");
return -1;

View File

@ -1,7 +1,7 @@
#ifndef __HMI_SERVER_H__
#define __HMI_SERVER_H__
#include "nanomodbus.h"
#include "pm_board.h"
#include "bsp_misc.h"
#include "pm_common.h"
#include "mb_command.h"

View File

@ -1,7 +1,7 @@
/*
* @Author: mypx
* @Date: 2025-08-01 16:41:44
* @LastEditTime: 2025-09-26 15:00:17
* @LastEditTime: 2025-09-26 09:21:13
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/

View File

@ -2,7 +2,7 @@
* @Date: 2025-07-18 08:53:16
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-09-24 16:11:11
* @LastEditTime: 2025-10-14 13:56:58
* @FilePath: pm_common.h
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
@ -10,7 +10,7 @@
#ifndef __PM_COMMON__
#define __PM_COMMON__
#include "bsp_encoder.h"
#include "pm_board.h"
#include "bsp_misc.h"
#include <rtthread.h>
#include <stdint.h>

View File

@ -1,7 +1,7 @@
/*
* @Author: mypx
* @Date: 2025-07-07 14:25:47
* @LastEditTime: 2025-09-26 10:49:49
* @LastEditTime: 2025-10-24 13:06:47
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
@ -14,6 +14,7 @@
#include "pt100x.h"
#include "servo.h"
#include "storage.h"
#include "bsp_tec.h"
extern ad7793_dev_t ad7793_dev;
@ -89,21 +90,29 @@ int pm_device_start(pm_device_t *dev)
int pm_device_init(pm_device_t *dev)
{
pm_board_init();
int ret = 0;
pm_board_init(); // TODO:Delete this later
bsp_tec_init();
pm_params_init(&dev->params);
hmi_server_handler_register(&dev->hmi, &hmi_cmd_handler);
hmi_server_init(&dev->hmi);
// hmi_server_handler_register(&dev->hmi, &hmi_cmd_handler);
// hmi_server_init(&dev->hmi);
pm_fsm_init(&dev->fz_fsm);
//storage_init();
tec_control_init(&dev->tec);
// if (servo_init() != 0)
// {
// ET_ERR("Servo init failed!");
// //return;
// }
// data_sampling_init();
// data_process_init();
ret = tec_control_init(&dev->tec);
if(ret != 0)
{
ET_WARN("TEC control init failed!");
return -1;
}
tec_control_sercie_start(&dev->tec);
if (servo_init() != 0)
{
ET_ERR("Servo init failed!");
return -1;
}
light_data_sampling_init();
data_process_init();
return 0;
}

View File

@ -9,7 +9,7 @@
*/
#ifndef __PM_DEVICE_H__
#define __PM_DEVICE_H__
#include "pm_board.h"
#include "bsp_misc.h"
#include "bsp_encoder.h"
#include "mb_command.h"
#include "tec_control.h"

View File

@ -1,7 +1,7 @@
/*
* @Author: mypx
* @Date: 2025-08-11 08:30:45
* @LastEditTime: 2025-09-24 16:12:52
* @LastEditTime: 2025-10-27 15:06:18
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
@ -9,15 +9,13 @@
#include "data_process.h"
#include "data_sampling.h"
#include "et_waveform_analysis.h"
#include "etk_diff.h"
#include "etk_ringbuffer.h"
#include "servo.h"
#include <float.h>
#include <rtthread.h>
#include <stdio.h>
static pm_fsm_t *pm_fsm = NULL;
static pm_fsm_t *pm_fsm = NULL;
pm_fsm_t *get_pm_fsm(void)
{
@ -39,7 +37,7 @@ void calibrate_mode_toggle(void)
if (!fsm)
return;
fsm->fz_fsm_state = false;
fsm->meas_mode = fsm->meas_mode == PM_MEAS_NORMAL_Mode ? PM_MEAS_TEST_Mode : PM_MEAS_NORMAL_Mode;
fsm->meas_mode = fsm->meas_mode == PM_MEAS_NORMAL_Mode ? PM_MEAS_TEST_Mode : PM_MEAS_NORMAL_Mode;
}
MSH_CMD_EXPORT_ALIAS(calibrate_mode_toggle, cali, calibrate mode toggle);
@ -168,17 +166,18 @@ void accuracy_auto_measurement_once(pm_meas_t *fz, float angle)
#endif
uint32_t delay = convert_encoder_angle_rpm_to_to_time(diff, RUN_OFFSET_ANGLE_RPM);
ET_DBG("wait %d ms to target:%.2f", delay, target_angle);
int speed = (target_angle > angle) ? -RUN_OFFSET_ANGLE_RPM
: RUN_OFFSET_ANGLE_RPM; // FIXME:note encoder dir and motor dir
int speed = (target_angle > angle) ? (RUN_OFFSET_ANGLE_RPM * ENCODER_MOTOR_DIR_FACTOR)
: (-RUN_OFFSET_ANGLE_RPM * ENCODER_MOTOR_DIR_FACTOR); // FIXME:note encoder dir and motor dir
sv630p_speed_mode_start(&sv630p_handle, speed);
fz->target_dir = (speed > 0) ? -1 : 1;
ET_WARN("SPEED:%d", speed);
uint32_t start_time = rt_tick_get_millisecond();
while (1)
{
angle = get_real_time_angle_degree();
//ET_DBG("angle:%.2f", angle);
// 根据方向判断是否达到目标角度,处理负数角度情况
if (((speed > 0) && (target_angle > angle)) || ((speed < 0) && (target_angle < angle)))
if (((speed > 0) && (target_angle > angle)) || ((speed < 0) && (target_angle < angle))) //1000, -183,1124
{
sv630p_speed_mode_stop(&sv630p_handle);
ET_INFO("Set angle:%.3f, reached:%.3f", target_angle, angle);
@ -197,6 +196,12 @@ void accuracy_auto_measurement_once(pm_meas_t *fz, float angle)
break;
}
else if (rt_tick_get_millisecond() - start_time > delay)
{
ET_WARN("Timeout, set angle:%.3f, reached:%.3f, speed:%d", target_angle, angle, speed);
sv630p_speed_mode_stop(&sv630p_handle);
break;
}
}
}
@ -321,10 +326,12 @@ StageResult is_prepare_leave_stage(pm_meas_t *fz)
StageResult is_wave_leave_stage(pm_meas_t *fz)
{
if ((fz->slope < -100000) || (fz->freq > 52 && fz->freq < 98 && fz->p50 < 1000 && fz->trend == TREND_DECREASE)
|| (fz->freq < 1.0 && fz->trend == TREND_DECREASE))
return STAGE_RESULT_OK;
else
// if ((fz->slope < -100000) || (fz->freq > 52 && fz->freq < 98 && fz->p50 < 1000 && fz->trend == TREND_DECREASE)
// || (fz->freq < 1.0 && fz->trend == TREND_DECREASE)) // old version parameter
// if ((fz->slope < -200 && (fz->freq < 48 || fz->freq > 102) && fz->p50 < 1000 && fz->trend == TREND_DECREASE)
// || (fz->freq < 1.0 && fz->trend == TREND_DECREASE))
// return STAGE_RESULT_OK;
// else
return STAGE_RESULT_NO;
}
@ -522,10 +529,10 @@ int find_zero_process(pm_meas_t *fz, const uint16_t *adc_buf, uint32_t len)
{
sv630p_speed_mode_stop(&sv630p_handle);
measurement_info_print(fz);
fsm->fz_fsm_state = true; // test use
fz->calib_cnt = 0;
fsm->meas_mode = PM_MEAS_NORMAL_Mode;
fz->target_dir = MEASURE_DEF_DIR;
fsm->fz_fsm_state = true; // test use
fz->calib_cnt = 0;
fsm->meas_mode = PM_MEAS_NORMAL_Mode;
fz->target_dir = MEASURE_DEF_DIR;
//ET_WARN("Mesaure time:%d ms", rt_tick_get_millisecond() - fz->calib_start_time);
}
break;
@ -553,6 +560,7 @@ int find_zero_process(pm_meas_t *fz, const uint16_t *adc_buf, uint32_t len)
int pm_fsm_init(pm_fsm_t *fsm)
{
pm_fsm = fsm;
pm_fsm = fsm;
fsm->fz_fsm_state = true;
return 0;
}

View File

@ -1,7 +1,7 @@
/*
* @Author: mypx
* @Date: 2025-08-11 08:30:53
* @LastEditTime: 2025-09-24 16:10:18
* @LastEditTime: 2025-10-24 17:11:50
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
@ -13,18 +13,18 @@
#include "etk_slope.h"
#include "pm_common.h"
#define FZ_CALI_MAX_COUNT 10
#define FZ_CALI_MAX_COUNT 4
#define ZC_COUNT_STABLE_TIMES 5
#define ZERO_POS_THRESHOLD 0.5f
#define ZERO_POS_THRESHOLD 1.0f
#define PID_TARGET_ZERO_POS_VAL 0.0f
#define RUN_OFFSET_ANGLE_RPM (1000)
#define FZ_UNKNOWN_STAGE_RPM 1000
#define FZ_SATURATED_STAGE_RPM 1000
#define FZ_WAVE_ENTER_STAGE_RPM 1000
#define FZ_PREPARE_STAGE_RPM 1000
#define FZ_FAST_CLOSE_STAGE_RPM 700
#define FZ_UNKNOWN_STAGE_RPM 2000
#define FZ_SATURATED_STAGE_RPM 2000
#define FZ_WAVE_ENTER_STAGE_RPM 2000
#define FZ_PREPARE_STAGE_RPM 2000
#define FZ_FAST_CLOSE_STAGE_RPM 1400
#define FZ_PRECISE_ADJUST_MIN_RPM (0.6f * MOTOR_TO_ENCODER_FACTOR) // 0.6rpm->0.001
#define FZ_SATURATED_POWER_MAX 5.0f
@ -40,8 +40,6 @@
#define FZ_PREPARE_LEAVE_FREQ_MIN 48.0f
#define FZ_PREPARE_LEAVE_FREQ_MAX 52.0f
extern bool fsm_state;
typedef enum
{
TREND_UNKNOWN = 0, // 未知(首次调用,无历史数据)
@ -146,4 +144,6 @@ int find_zero_process(pm_meas_t *fz, const uint16_t *adc_val, uint32_t len);
int pm_fsm_init(pm_fsm_t *fsm);
pm_fsm_t *get_pm_fsm(void);
#endif

View File

@ -2,14 +2,14 @@
* @Date: 2025-06-26 14:49:09
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-08-29 09:08:23
* @LastEditTime: 2025-10-15 16:53:50
* @FilePath: servo.c
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
*/
#include "servo.h"
#include "nanomodbus.h"
#include "pm_board.h"
#include "bsp_misc.h"
#include "rtdef.h"
#include "rtthread.h"
@ -102,12 +102,12 @@ int servo_init(void)
int ret = 0;
nmbs_platform_conf stm32_conf;
// board layer init
pm_servo_init();
bsp_servo_init();
// nmbs layer init
nmbs_platform_conf_create(&stm32_conf);
stm32_conf.transport = NMBS_TRANSPORT_RTU;
stm32_conf.read = pm_servo_uart_read;
stm32_conf.write = pm_servo_uart_write;
stm32_conf.read = bsp_servo_uart_read;
stm32_conf.write = bsp_servo_uart_write;
nmbs_error status = nmbs_client_create(&servo_nmbs, &stm32_conf);
if (status != NMBS_ERROR_NONE)

View File

@ -2,20 +2,21 @@
* @Date: 2025-06-26 14:49:15
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-08-19 08:51:18
* @LastEditTime: 2025-10-15 16:55:31
* @FilePath: servo.h
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
*/
#ifndef __SERVO_H__
#define __SERVO_H__
#include "pm_board.h"
#include "bsp_misc.h"
#include "sv_device.h"
#include "pm_common.h"
#include "et_log.h"
#include "h02_basic_control.h"
#include "h31_comm_related_var.h"
#include "h0d_af.h"
#include "bsp_motor.h"
#if (ENABLE_SV630P == 1)
extern sv630p_handle_t sv630p_handle;

View File

@ -2,15 +2,15 @@
#include <stdarg.h>
#include <rthw.h>
#include "storage.h"
#include "pm_board.h"
#include "bsp_misc.h"
#include "i2c.h"
void storage_dbg(const char *tag, const char *fmt, ...);
at24cx_dev_t at24c02_device = {
.dev_addr = 0xA0,
.write_bytes = pm_i2c_write_bytes,
.read_bytes = pm_i2c_read_bytes,
.write_bytes = bsp_i2c_write_bytes,
.read_bytes = bsp_i2c_read_bytes,
.log = storage_dbg,
};
@ -51,7 +51,7 @@ void storage_test(void)
rt_kprintf("Failed to write data to EEPROM\r\n");
}
rt_thread_delay(1000); // Delay for 1 second
pm_system_led_toggle(); // Toggle the system LEDs
bsp_system_led_toggle(); // Toggle the system LEDs
//rt_kprintf("[%d] System LED toggled\n", rt_tick_get()); // Print message to console
if (at24cx_read(&at24c02_device, 0x0a, read_buf, 8) != 0)
{

View File

@ -2,34 +2,87 @@
* @Date: 2025-06-23 13:04:20
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-09-25 16:49:59
* @LastEditTime: 2025-10-27 08:42:50
* @FilePath: tec_control.c
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
*/
#include "tec_control.h"
#include "ad7793.h"
#include "bsp_misc.h"
#include "bsp_tec.h"
#include "bsp_temper_sampling.h"
#include "et_log.h"
#include "etk_utils.h"
#include "mb_interface.h"
#include "pm_board.h"
#include "pm_common.h"
#include "pt100x.h"
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h> /* rand, RAND_MAX */
#if TEC_CONTROL_PERIOD < ADC_SAMPLE_PERIOD
#error "TEC_CONTROL_PERIOD must be greater than ADC_SAMPLE_PERIOD"
#endif
#define TEST_TEMPER_XFER 0
/* Local helpers to avoid implicit prototypes on some embedded libc's */
static inline int _isnan_f(float v)
{
return v != v;
}
static inline float _fabs_f(float v)
{
return v < 0.0f ? -v : v;
}
/* Define AD7793 device structure */
ad7793_dev_t ad7793_dev;
extern ad7793_hw_if_t ad7793_hw_if;
ad7793_dev_t ad7793_dev;
#if (USING_SOFT_SPI == 1)
extern soft_spi_t soft_spi;
#endif
tec_control_t *tec_control = NULL;
ad7793_hw_if_t ad7793_hw_if = {
.spi_transfer = bsp_ad7793_spi_transfer,
.spi_write = bsp_ad7793_spi_write,
.spi_read = bsp_ad7793_spi_read,
.gpio_set = bsp_ad7793_gpio_set,
.gpio_get = bsp_ad7793_gpio_get,
.delay_ms = bsp_ad7793_delay_ms,
};
/* -------------------------------------------------------------
* AD7793
* RTD/PT100
* 1. => 使 (unipolar)
* 2. () PT100 mV 16
* - 1.17V / GAIN16 73mV 0~200+ ( 21mV~37mV)
* - GAIN32 GAIN8
* 3. (buffered=true) ADC
* 4. (1.17V) REF+ / REF-
* use_internal_ref false external_ref ( 2.500f 3.000f)
* 5. 8.33Hz
* 6. system zero offset
* ------------------------------------------------------------- */
static const ad7793_config_t g_ad7793_config = {
.use_internal_ref = false, /* 现在使用板上 2.5V 外部参考 (已实测 REFIN+=2.5V) */
.external_ref = 2.500f,
/* 传感器差分约 0.044V, 需保证 < Vref / Gain
GAIN=4 -> 2.5/4=0.625V ( 0.44V); */
.gain = AD7793_GAIN_4,
.channel = AD7793_CHANNEL_1, /* AIN1(+) - AIN1(-) */
.rate = AD7793_RATE_8_33HZ,
.unipolar_mode = true, /* 只测正向信号 */
.buffered = true, /* 启用缓冲 */
.calibrate_system_zero = false /* 暂停 system zero 校准 (之前 OFFSET/FULLSC 已被污染) */
};
/* Shared data structure for temperature sampling and TEC control */
static struct
{
@ -93,7 +146,7 @@ void tec_control_reset(void)
}
}
int tec_control_start(void)
int tec_control_resume(void)
{
if (tec_control == NULL)
{
@ -110,7 +163,7 @@ int tec_control_start(void)
return 0;
}
int tec_control_stop(void)
int tec_control_pause(void)
{
if (tec_control == RT_NULL)
{
@ -127,19 +180,20 @@ void tec_calculate_temperature(uint32_t adc_value)
float mv = 0;
double resistance;
mv = ad7793_convert_to_voltage(&ad7793_dev, adc_value) * 1000;
ET_DBG("voltage:%.2f", mv);
//ET_DBG("voltage:%.2f", mv);
resistance = solve_resistance(mv / 1000);
//ET_DBG("resistance:%.2f", resistance);
//ET_PRINTF_FLOAT("resistance:", resistance);
tec_control->cur_temper = pt_precise_temperature(resistance, PT100);
tec_control->cur_temper = et_ema_filter_process(&tec_control->ema_filter, tec_control->cur_temper);
if (isnan(tec_control->cur_temper))
if (_isnan_f(tec_control->cur_temper))
{
rt_kprintf("Invalid resistance value, cannot calculate temperature.\n");
ET_DBG("Invalid resistance value, cannot calculate temperature.\n");
}
else
{
ET_DBG("temperature:%.2f", tec_control->cur_temper);
//ET_DBG("temperature:%.4f", tec_control->cur_temper);
/* Update shared data */
rt_mutex_take(temp_shared_data.data_mutex, RT_WAITING_FOREVER);
temp_shared_data.current_temperature = tec_control->cur_temper;
@ -148,46 +202,87 @@ void tec_calculate_temperature(uint32_t adc_value)
}
}
#if TEST_TEMPER_XFER
float generate_random_temperature(void)
{
// 生成0到RAND_MAX之间的随机整数
int randomInt = rand();
// 转换为0到1之间的浮点数再映射到0到55范围
float randomFloat = (float)randomInt / RAND_MAX * 55.0f;
return randomFloat;
static uint32_t lfsr = 0xACE1u; /* simple LFSR to avoid rand() dependency */
lfsr ^= lfsr << 7;
lfsr ^= lfsr >> 9;
lfsr ^= lfsr << 8;
return (lfsr & 0xFFFF) * (55.0f / 65535.0f);
}
#endif
/* Temperature sampling thread entry function */
static void temp_sampling_thread_entry(void *parameter)
static void temper_sampling_thread_entry(void *parameter)
{
ETK_UNUSED(parameter);
//uint32_t adc_value = 0;
uint32_t adc_value = 0;
const uint32_t LOW_CODE_THRESHOLD = 0x200; /* 约 0.125% 满量程,可调 */
uint32_t consecutive_err = 0;
ET_INFO("Temperature sampling thread started");
while (1)
{
#if !TEST_TEMPER_XFER
/* Wait for ADC ready and read data */
// if (ad7793_wait_ready(&ad7793_dev, 1000) == true)
// {
// if (ad7793_read_data(&ad7793_dev, &adc_value))
// {
// /* Calculate temperature from ADC value */
// tec_calculate_temperature(adc_value);
// }
// else
// {
// ET_ERR("Failed to read ADC data.\n");
// }
// }
//mb_write_current_temp(tec_control->cur_temper);
mb_write_current_temp(generate_random_temperature()); // test only
if (ad7793_wait_ready(&ad7793_dev, 1000) == true)
{
if (ad7793_read_data(&ad7793_dev, &adc_value))
{
/* 低码过滤:避免极低噪声或参考丢失导致的虚假高温 */
if (adc_value < LOW_CODE_THRESHOLD)
{
ad7793_dev.low_code_drops++;
ET_DBG("drop low code: 0x%06X (drops=%lu)", (unsigned)adc_value,
(unsigned long)ad7793_dev.low_code_drops);
}
else
{
//ET_DBG("raw code: 0x%06X", (unsigned)adc_value);
tec_calculate_temperature(adc_value);
}
}
else
{
ET_ERR("Failed to read ADC data.\n");
}
}
mb_write_current_temp(tec_control->cur_temper);
#else
mb_write_current_temp(generate_random_temperature()); // test only
mb_write_current_rotation(generate_random_temperature()); // test only
#endif
/* Delay for next sampling period */
rt_thread_mdelay(ADC_SAMPLE_PERIOD);
}
}
// pwm: 0.0 ~ 1.0
void tec_adjust_pwm(float pwm, float cur_temper, float target_temper)
{
if (_fabs_f(cur_temper - target_temper) < TEC_TEMPER_PRECISE)
{
// within precise range, stop adjustment
bsp_tec_close();
bsp_fan_stop();
return;
}
//bsp_fan_start();
if (cur_temper > target_temper)
{
// cooling
bsp_tec_cooling_adjust(pwm);
}
else
{
// heating
bsp_tec_heating_adjust(pwm);
}
}
void tec_control_temperature(tec_control_t *tec)
{
float pid_out = 0;
@ -213,7 +308,7 @@ void tec_control_temperature(tec_control_t *tec)
// dt convert to seconds
pid_out = tec_pid_update(tec, tec->target_temper, current_temp, TEC_CONTROL_PERIOD / 1000.0f);
ET_INFO("pid_out:%.2f", pid_out);
pm_tec_set_duty(pid_out);
tec_adjust_pwm(pid_out, current_temp, tec->target_temper);
}
static void tec_control_thread_entry(void *parameter)
@ -222,11 +317,10 @@ static void tec_control_thread_entry(void *parameter)
rt_tick_t last_ms = rt_tick_get_millisecond();
rt_uint32_t received;
et_ema_filter_init(&tec_control->ema_filter, EMA_FILTER_ALPHA);
tec_pid_init(tec_control);
tec_control->is_control = false; // control disabled by default
pm_tec_start();
pm_fan_start();
//bsp_fan_start();
tec_control->target_temper = 25.0f;
ET_INFO("TEC control thread started");
@ -241,13 +335,13 @@ static void tec_control_thread_entry(void *parameter)
// handle received events
if (received & TEC_CONTROL_START_EVENT)
{
tec_control_start();
tec_control_resume();
ET_INFO("TEC control started by event");
}
if (received & TEC_CONTROL_STOP_EVENT)
{
tec_control_stop();
tec_control_pause();
ET_INFO("TEC control stopped by event");
}
@ -265,19 +359,46 @@ static void tec_control_thread_entry(void *parameter)
}
}
int tec_control_sercie_start(tec_control_t *tec)
{
(void)tec; /* unused parameter for now */
rt_thread_t temp_tid;
rt_thread_t ctrl_tid;
(void)ctrl_tid;
// create temperature sampling thread
temp_tid = rt_thread_create("temp-sample", temper_sampling_thread_entry, RT_NULL, TEMP_SAMPLE_THREAD_STACK_SIZE,
TEMP_SAMPLE_THREAD_PRIORITY, TEMP_SAMPLE_THREAD_TIMESLICE);
if (temp_tid == RT_NULL)
{
ET_ERR("Failed to create temperature sampling thread");
return -6;
}
rt_thread_startup(temp_tid);
/* create TEC control thread */
ctrl_tid = rt_thread_create("tec-control", tec_control_thread_entry, RT_NULL, TEC_CONTROL_THREAD_STACK_SIZE,
TEC_CONTROL_THREAD_PRIORITY, TEC_CONTROL_THREAD_TIMESLICE);
if (ctrl_tid == RT_NULL)
{
ET_ERR("Failed to create tec-control thread");
return -7;
}
rt_thread_startup(ctrl_tid);
return 0;
}
int tec_control_init(tec_control_t *tec)
{
rt_thread_t tid;
rt_thread_t temp_tid;
if (tec == NULL)
{
ET_ERR("tec is NULL");
return -1;
}
pm_tec_init();
tec_control = tec;
et_ema_filter_init(&tec_control->ema_filter, EMA_FILTER_ALPHA);
// create event object for temperature control
tec_control->control_event = rt_event_create("tec_event", RT_IPC_FLAG_FIFO);
if (tec_control->control_event == RT_NULL)
@ -298,39 +419,19 @@ int tec_control_init(tec_control_t *tec)
temp_shared_data.current_temperature = 25.0f;
temp_shared_data.temp_updated = false;
// #if (USING_SOFT_SPI == 1)
// if (ad7793_init(&ad7793_dev, &ad7793_hw_if, soft_spi.cs_pin))
// #else
// if (ad7793_init(&ad7793_dev, &ad7793_hw_if, SPI3_CS_AD7793_Pin))
// #endif
// {
// rt_kprintf("AD7793 device initialized successfully.\n");
// }
// else
// {
// rt_kprintf("Failed to initialize AD7793 device.\n");
// return -2;
// }
// create temperature sampling thread
temp_tid = rt_thread_create("temp-sample", temp_sampling_thread_entry, RT_NULL, TEMP_SAMPLE_THREAD_STACK_SIZE,
TEMP_SAMPLE_THREAD_PRIORITY, TEMP_SAMPLE_THREAD_TIMESLICE);
if (temp_tid == RT_NULL)
#if (USING_SOFT_SPI == 1)
if (ad7793_init(&ad7793_dev, &ad7793_hw_if, soft_spi.cs_pin, &g_ad7793_config))
#else
if (ad7793_init(&ad7793_dev, &ad7793_hw_if, SPI3_AD7793_CS_Pin, &g_ad7793_config))
#endif
{
ET_ERR("Failed to create temperature sampling thread");
return -6;
rt_kprintf("AD7793 device initialized successfully.\n");
}
else
{
rt_kprintf("Failed to initialize AD7793 device.\n");
return -2;
}
rt_thread_startup(temp_tid);
// create TEC control thread
// tid = rt_thread_create("tec-control", tec_control_thread_entry, RT_NULL, TEC_CONTROL_THREAD_STACK_SIZE,
// TEC_CONTROL_THREAD_PRIORITY, TEC_CONTROL_THREAD_TIMESLICE);
// if (tid != RT_NULL)
// {
// rt_thread_startup(tid);
// return 0;
// }
return -4;
return 0;
}

View File

@ -1,7 +1,7 @@
/*
* @Author: mypx
* @Date: 2025-06-23 13:05:04
* @LastEditTime: 2025-09-25 13:13:20
* @LastEditTime: 2025-10-16 14:01:47
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
@ -19,7 +19,7 @@
#include <rtthread.h>
#include <stdbool.h>
#define EMA_FILTER_ALPHA 0.1
#define EMA_FILTER_ALPHA 0.9
#define EXCEPTION_TEMPERATURE_DEF 45.0f // exception temperature value, control will not be performed if exceeded
#define TEC_CONTROL_PERIOD 1000 // temperature calculation period, unit ms
#define ADC_SAMPLE_PERIOD 500 // ADC sampling period, unit ms
@ -47,9 +47,11 @@ int tec_control_init(tec_control_t *tec);
void tec_control_reset(void);
int tec_control_start(void);
int tec_control_resume(void);
int tec_control_stop(void);
int tec_control_sercie_start(tec_control_t *tec);
int tec_control_pause(void);
float tec_get_target_voltage(float R);

230
User/board/bsp_collect.c Normal file
View File

@ -0,0 +1,230 @@
/*
* @Author: mypx
* @Description: ADS8866采集-3线(TIM3中断驱动)
*/
#include "bsp_collect.h"
#include "bsp_misc.h"
#include "et_log.h"
#include "main.h"
#include "spi.h"
#include "tim.h"
#include <rtthread.h>
#include <stdlib.h>
static uint16_t *g_buffer = NULL;
static uint16_t g_buf_size = 0;
static uint16_t g_target_count = 0; // 目标采集数
static volatile uint16_t g_count = 0; // 已采集数
static dma_full_cb_t g_complete_cb = NULL;
// 3线模式: DIN保持高, CONVST控制转换+片选
#define ADS8866_DIN_HIGH() GPIOB->BSRR = (1u << 15)
#define PB15_AS_GPIO() GPIOB->CRH = (GPIOB->CRH & ~(0xFu << 28)) | (0x3u << 28)
// 延时n个时钟周期每个周期≈13.89ns
static inline void ns_delay(uint32_t cycles)
{
// 关闭编译器优化确保NOP不被删除
__asm volatile("1: SUBS %0, %0, #1\n" // 计数器减1
" BNE 1b" // 若未到0则循环
: "=r"(cycles) // 输出操作数
: "0"(cycles) // 输入操作数
);
}
void delay_100ns(void)
{
ns_delay(7);
}
// 单次读取(在中断里调用,要快速)
static inline uint16_t ads8866_read_once(void)
{
uint16_t rx = 0;
// ADS8866转换时间约3µs, 等待转换完成
// 72MHz系统时钟, 1个NOP约14ns, 300个NOP约4.2µs
for (volatile int i = 0; i < 6; i++)
delay_100ns();
// CONVST回到高电平, 数据就绪, 直接SPI读取
HAL_SPI_Receive(&ADS8866_SPI, (uint8_t *)&rx, 1, 10);
return rx;
}
int bsp_light_data_sampling_init(uint16_t *adc_buf, uint16_t length, dma_full_cb_t cb)
{
g_buffer = adc_buf;
g_buf_size = length;
g_complete_cb = cb;
g_count = 0;
g_target_count = 0;
// 初始化TIM3(产生CONVST), SPI2(读取数据)
MX_TIM3_Init();
MX_SPI2_Init();
__HAL_SPI_ENABLE(&ADS8866_SPI);
// PB15(DIN)配置为GPIO并保持高电平
PB15_AS_GPIO();
ADS8866_DIN_HIGH();
ET_INFO("bsp_collect: init done, buf_size=%u", length);
return 0;
}
// // Shell命令: 启动采集指定数量
// int cstart(int argc, char **argv)
// {
// if (argc < 2)
// {
// rt_kprintf("Usage: cstart <count>\r\n");
// return -1;
// }
// int count = atoi(argv[1]);
// if (count <= 0 || count > g_buf_size)
// {
// rt_kprintf("cstart: invalid count (1~%u)\r\n", g_buf_size);
// return -2;
// }
// if (!g_buffer)
// {
// rt_kprintf("cstart: buffer not init\r\n");
// return -3;
// }
// // 设置目标数量并启动
// g_count = 0;
// g_target_count = count;
// bsp_system_led_on();
// rt_kprintf("cstart: collecting %d samples...\r\n", count);
// // 启动TIM3 PWM + 中断
// HAL_TIM_PWM_Start(&COLLECT_TIM, TIM_CHANNEL_3);
// HAL_TIM_Base_Start_IT(&COLLECT_TIM);
// rt_kprintf("cstart: started (non-blocking)\r\n");
// return 0;
// }
// int cprint(int argc, char **argv)
// {
// uint16_t n = 32;
// if (argc >= 2)
// n = (uint16_t)atoi(argv[1]);
// if (!g_buffer || g_count == 0)
// {
// rt_kprintf("cprint: no data\r\n");
// return -1;
// }
// if (n > g_count)
// n = g_count;
// uint16_t minv = 0xFFFF, maxv = 0;
// uint32_t sum = 0;
// for (uint16_t i = 0; i < g_count; i++)
// {
// uint16_t v = g_buffer[i];
// if (v < minv)
// minv = v;
// if (v > maxv)
// maxv = v;
// sum += v;
// }
// rt_kprintf("cprint: N=%u min=%u max=%u avg=%lu\r\n", g_count, minv, maxv, sum / g_count);
// rt_kprintf("first %u (dec / hex):\r\n", n);
// for (uint16_t i = 0; i < n; i++)
// {
// if (i % 8 == 0)
// rt_kprintf("\r\n[%04u] ", i);
// rt_kprintf("%5u/0x%04X ", g_buffer[i], g_buffer[i]);
// }
// rt_kprintf("\r\n");
// return 0;
// }
// MSH_CMD_EXPORT_ALIAS(cstart, cstart, collect N samples)
// MSH_CMD_EXPORT_ALIAS(cprint, cprint, print data)
// 启动采集: 设置目标数量, 启动TIM3+中断
int bsp_light_data_sampling_start(void)
{
if (!g_buffer || g_buf_size == 0)
{
ET_ERR("bsp_collect: buffer not initialized");
return -1;
}
g_count = 0;
g_target_count = g_buf_size; // 采集满缓冲区
bsp_system_led_on();
// 启动TIM3 PWM(产生CONVST) + Update中断
HAL_TIM_PWM_Start(&COLLECT_TIM, TIM_CHANNEL_3);
HAL_TIM_Base_Start_IT(&COLLECT_TIM);
//ET_INFO("bsp_collect: start, target=%u", g_target_count);
return 0;
}
// 停止采集
int bsp_light_data_sampling_stop(void)
{
HAL_TIM_Base_Stop_IT(&COLLECT_TIM);
HAL_TIM_PWM_Stop(&COLLECT_TIM, TIM_CHANNEL_3);
bsp_system_led_off();
//ET_INFO("bsp_collect: stopped, collected=%u/%u", g_count, g_target_count);
return 0;
}
uint16_t light_raw_map_u16(uint16_t val)
{
/* Avoid integer truncation: do multiply before divide using 32-bit intermediate.
* Map 0..65535 -> 0..4095. Use rounding by adding half-divisor. */
uint32_t tmp = (uint32_t)val * 4095u;
return (uint16_t)(tmp / 65535u + 0.5f);
}
float light_raw_map_f32(uint16_t val)
{
return (float)(val / 65535.0f * 4095.0f);
}
// TIM3中断回调: 每次CONVST触发后采集一个点
void sampling_tim_elapsed_callback(void)
{
// 未启动或已完成
if (g_target_count == 0 || g_count >= g_target_count)
return;
// 读取一个点
if (g_buffer && g_count < g_buf_size)
{
/* drive LED pin high at the start of ISR and low at the end
* so each interrupt produces one pulse. This makes the pulse
* frequency equal to the timer update frequency (no 2x toggle
* ambiguity). */
bsp_system_led_on();
g_buffer[g_count] = light_raw_map_u16(ads8866_read_once());
g_count++;
bsp_system_led_off();
// 达到目标数量, 停止并回调
if (g_count >= g_target_count)
{
HAL_TIM_Base_Stop_IT(&COLLECT_TIM);
HAL_TIM_PWM_Stop(&COLLECT_TIM, TIM_CHANNEL_3);
bsp_system_led_off();
//ET_INFO("bsp_collect: complete, N=%u", g_count);
// 调用完成回调
if (g_complete_cb)
g_complete_cb();
g_target_count = 0; // 标记完成
}
}
}

21
User/board/bsp_collect.h Normal file
View File

@ -0,0 +1,21 @@
/*
* @Author: mypx
* @Date: 2025-10-14 11:25:04
* @LastEditTime: 2025-10-14 14:00:13
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#ifndef __BSP_COLLECT_H__
#define __BSP_COLLECT_H__
#include <stdint.h>
typedef void (*dma_full_cb_t)(void);
int bsp_light_data_sampling_init(uint16_t *adc_buf, uint16_t length, dma_full_cb_t cb);
int bsp_light_data_sampling_start(void);
int bsp_light_data_sampling_stop(void);
void sampling_tim_elapsed_callback(void);
#endif // __BSP_COLLECT_H__

85
User/board/bsp_hmi.c Normal file
View File

@ -0,0 +1,85 @@
/*
* @Author: mypx
* @Date: 2025-10-15 16:24:26
* @LastEditTime: 2025-10-22 08:44:30
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "bsp_hmi.h"
#include "usart.h"
#include "rtthread.h"
#include "et_log.h"
struct rt_messagequeue mb_hmi_mq;
static char mq_pool[MB_HMI_RX_QUEUE_SIZE];
#if (DEBUG_MB_ENABLE == 1)
#define DBG_MB(...) ET_DBG("[MB] " __VA_ARGS__)
#else
#define DBG_MB(...)
#endif
int bsp_mb_hmi_init(void)
{
MX_USART1_UART_Init(); // used tx
MX_USART2_UART_Init(); // used rx
rt_err_t result = rt_mq_init(&mb_hmi_mq, "mb_hmi_mq", mq_pool, 1, MB_HMI_RX_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
ET_ERR("mb_hmi mq init failed\n");
return -1;
}
__HAL_UART_ENABLE_IT(&HMI_COM, UART_IT_RXNE);
return 0;
}
int32_t bsp_hmi_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
uint16_t bytes_read = 0;
uint8_t byte;
rt_tick_t timeout_ticks = rt_tick_from_millisecond(byte_timeout_ms);
DBG_MB("start read:count:%d, timeout:%d\n", count, byte_timeout_ms);
while (bytes_read < count)
{
rt_err_t result = rt_mq_recv(&mb_hmi_mq, &byte, sizeof(byte), timeout_ticks);
if (result == RT_EOK)
{
DBG_MB("rx: %02X\n", byte);
buf[bytes_read++] = byte;
}
else
{
if (bytes_read > 0)
{
DBG_MB("Modbus read finished %d\n", bytes_read);
return bytes_read;
}
DBG_MB("HMI read timeout, no data received\n");
return 0;
}
}
DBG_MB("Actually read:%d, count:%d\r\n", bytes_read, count);
return bytes_read;
}
int32_t bsp_hmi_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
rt_thread_mdelay(2);
if (HAL_UART_Transmit(&HMI_COM, buf, count, byte_timeout_ms) == HAL_OK)
{
ET_DBG("HMI write:%d", count);
return count;
}
else
{
return 0;
}
}

21
User/board/bsp_hmi.h Normal file
View File

@ -0,0 +1,21 @@
/*
* @Author: mypx
* @Date: 2025-10-15 16:24:35
* @LastEditTime: 2025-10-15 16:52:46
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#ifndef __BSP_HMI_H__
#define __BSP_HMI_H__
#include <stdint.h>
#define MB_HMI_RX_QUEUE_SIZE 128 // Modbus HMI 接收队列大小
extern struct rt_messagequeue mb_hmi_mq;
int bsp_mb_hmi_init(void);
int32_t bsp_hmi_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg);
int32_t bsp_hmi_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg);
#endif // __BSP_HMI_H__

121
User/board/bsp_misc.c Normal file
View File

@ -0,0 +1,121 @@
/*
* @Author: mypx
* @Email: mypx_coder@163.com
* @Date: 2025-06-19 12:29:17
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "bsp_misc.h"
#include "ad7793.h"
#include "adc.h"
#include "dma.h"
#include "et_log.h"
#include "gpio.h"
#include "i2c.h"
#include "rtdef.h"
#include "rthw.h"
#include "usart.h"
#include "tim.h"
#include <stdbool.h>
#include <string.h>
#define AT24C128_I2C_ADDR 0xA0
void bsp_system_led_toggle(void)
{
HAL_GPIO_TogglePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin);
}
void bsp_system_led_on(void)
{
HAL_GPIO_WritePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin, GPIO_PIN_SET);
}
void bsp_system_led_off(void)
{
HAL_GPIO_WritePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin, GPIO_PIN_RESET);
}
void bsp_error_led_on(void)
{
HAL_GPIO_WritePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin, GPIO_PIN_SET);
}
void bsp_error_led_off(void)
{
HAL_GPIO_WritePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin, GPIO_PIN_RESET);
}
void bsp_error_led_toggle(void)
{
HAL_GPIO_TogglePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin);
}
int bsp_i2c_write_bytes(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data, uint16_t len)
{
HAL_StatusTypeDef res;
// AT24C128 单页为 64 字节,写入时建议不要跨页,否则需要拆包
if (len > 64)
return HAL_ERROR;
res = HAL_I2C_Mem_Write(&hi2c2, dev_addr, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
HAL_Delay(5); // EEPROM写入周期典型为5ms
return res;
}
int bsp_i2c_read_bytes(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data, uint16_t len)
{
return HAL_I2C_Mem_Read(&hi2c2, dev_addr, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
}
void bsp_uart_print_send(const uint8_t *data, uint16_t len)
{
#ifdef USING_RTT_AS_CONSOLE
#warning "USING_RTT_AS_CONSOLE defined, use uart1 as hmi interface"
#else
HAL_UART_Transmit(&huart1, data, len, 1000);
#endif
}
float bsp_sampling_tim_get_freq(void)
{
uint32_t pclk1 = HAL_RCC_GetPCLK1Freq();
uint32_t timer_clk;
// 获取APB1预分频系数来自RCC配置寄存器
uint32_t apb1_prescaler = (RCC->CFGR & RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos;
// 根据APB1预分频计算定时器时钟源
if (apb1_prescaler == RCC_HCLK_DIV1)
{
// APB1预分频=1定时器时钟=PCLK1
timer_clk = pclk1;
}
else
{
// APB1预分频>1定时器时钟=2×PCLK1
timer_clk = pclk1 * 2;
}
// 计算定时器计数频率经过PSC分频后
uint32_t counter_freq = timer_clk / (COLLECT_TIM.Init.Prescaler + 1);
// 计算最终输出频率(周期触发频率 = 计数频率 / (Period + 1)
// 注意如果是向上计数模式完整周期是从0到Period共Period+1个计数
return (float)counter_freq / (COLLECT_TIM.Init.Period + 1);
}
int pm_board_init(void)
{
MX_GPIO_Init();
// MX_USART1_UART_Init();
return 0;
}

40
User/board/bsp_misc.h Normal file
View File

@ -0,0 +1,40 @@
/*
* @Date: 2025-06-19 12:29:23
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-10-15 16:52:14
* @FilePath: board_pm.h
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
*/
#ifndef __BSP_MISC_H__
#define __BSP_MISC_H__
#include "adc.h"
#include "main.h"
#include "rtthread.h"
#include "tim.h"
#include "user_config.h"
#include <stdbool.h>
#include <stdint.h>
int pm_board_init(void);
void bsp_system_led_toggle(void);
void bsp_system_led_on(void);
void bsp_system_led_off(void);
void bsp_error_led_on(void);
void bsp_error_led_off(void);
void bsp_error_led_toggle(void);
int bsp_i2c_write_bytes(uint16_t dev_addr, uint16_t reg, uint8_t *data, uint16_t size);
int bsp_i2c_read_bytes(uint16_t dev_addr, uint16_t reg, uint8_t *data, uint16_t size);
void bsp_uart_print_send(const uint8_t *data, uint16_t len);
void bsp_lcd_cmd_send(const uint8_t *data, uint16_t len);
float bsp_sampling_tim_get_freq(void);
#endif // __BOARD_H__

82
User/board/bsp_motor.c Normal file
View File

@ -0,0 +1,82 @@
/*
* @Author: mypx
* @Date: 2025-10-15 16:40:13
* @LastEditTime: 2025-10-15 16:47:38
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "bsp_motor.h"
#include "et_log.h"
#include "rtthread.h"
#include "usart.h"
#if (DEBUG_MB_ENABLE == 1)
#define DBG_MB(...) ET_DBG("[MB] " __VA_ARGS__)
#else
#define DBG_MB(...)
#endif
struct rt_messagequeue servo_mq;
static char mq_pool[SERVO_RX_QUEUE_SIZE];
int bsp_servo_init(void)
{
MX_USART3_UART_Init();
rt_err_t result = rt_mq_init(&servo_mq, "servo_mq", mq_pool, 1, SERVO_RX_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
ET_ERR("servo mq init failed\n");
return -1;
}
__HAL_UART_ENABLE_IT(&SERVO_COM, UART_IT_RXNE);
return 0;
}
int32_t bsp_servo_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
uint16_t bytes_read = 0;
uint8_t byte;
rt_tick_t timeout_ticks = rt_tick_from_millisecond(byte_timeout_ms);
DBG_MB("start read:count:%d, timeout:%d\n", count, byte_timeout_ms);
while (bytes_read < count)
{
rt_err_t result = rt_mq_recv(&servo_mq, &byte, sizeof(byte), timeout_ticks);
if (result == RT_EOK)
{
DBG_MB("rx: %02X\n", byte);
buf[bytes_read++] = byte;
}
else
{
if (bytes_read > 0)
{
DBG_MB("Modbus read finished %d\n", bytes_read);
return bytes_read;
}
DBG_MB("Servo read timeout, no data received\n");
return 0;
}
}
DBG_MB("Actually read:%d, count:%d\r\n", bytes_read, count);
return bytes_read;
}
int32_t bsp_servo_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
rt_thread_mdelay(2);
if (HAL_UART_Transmit(&SERVO_COM, buf, count, byte_timeout_ms) == HAL_OK)
{
return count;
}
else
{
return 0;
}
}

16
User/board/bsp_motor.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __BSP_MOTOR_H__
#define __BSP_MOTOR_H__
#include <stdbool.h>
#include <stdint.h>
#define SERVO_RX_QUEUE_SIZE 128 // Modbus RTU 接收队列大小
extern struct rt_messagequeue servo_mq;
int bsp_servo_init(void);
int32_t bsp_servo_uart_read(uint8_t *buf, uint16_t count, int32_t timeout, void *arg);
int32_t bsp_servo_uart_write(const uint8_t *buf, uint16_t count, int32_t timeout, void *arg);
#endif // __BSP_MOTOR_H__

177
User/board/bsp_tec.c Normal file
View File

@ -0,0 +1,177 @@
/*
* @Author: mypx
* @Date: 2025-10-15 13:03:38
* @LastEditTime: 2025-10-17 13:37:16
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "bsp_tec.h"
#include "main.h"
#include "spi.h"
#include "tim.h"
/*
*
* PE8-TEC-IN1(left) TIM1_CH1N默认为PE8
* PA8-TEC-IN2(right) TIM1_CH1默认为PA9,PA8
*
* PE8也要做映射使
*
* 1. PE8和PA8为TIM1_CH1N和TIM1_CH1
* 2. 使PE8和PA8的TIM1_CH1N和TIM1_CH1
* stm32cubemx 使TIM1_CH1N和TIM1_CH1默认的管脚为REMAP后的管脚bug
*
*/
static void stop_tec_pwm(void)
{
// 一块关闭,避免左右搞混没关掉
HAL_TIM_PWM_Stop(&TEC_TIM, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&TEC_TIM, TIM_CHANNEL_1);
}
static void tec_sd_disable(void)
{
HAL_GPIO_WritePin(GPIOA, TEC_LEFT_SD_Pin | TEC_RIGHT_SD_Pin, GPIO_PIN_RESET);
}
static void left_tec_sd_enable(void)
{
HAL_GPIO_WritePin(GPIOA, TEC_LEFT_SD_Pin, GPIO_PIN_SET);
}
static void right_tec_sd_enable(void)
{
HAL_GPIO_WritePin(GPIOA, TEC_RIGHT_SD_Pin, GPIO_PIN_SET);
}
// PE8-TEC-IN1(left) TIM1_CH1N, 需要做full remap
static void left_tec_reconfig_pwm(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Stop PWM outputs first to avoid both outputs being active during transition */
stop_tec_pwm();
/* Ensure GPIO clocks */
__HAL_RCC_GPIOE_CLK_ENABLE();
/* Enable TIM1 full remap if you need the remapped mapping (CubeMX generated code might expect this)
Note: remap affects mapping for all TIM1 channels - do this before initializing the GPIOs */
__HAL_AFIO_REMAP_TIM1_ENABLE();
/* Deinit any previous GPIO config on PE8/PA8 to be safe */
HAL_GPIO_DeInit(GPIOE, GPIO_PIN_8);
HAL_GPIO_DeInit(GPIOA, TEC_RIGHT_IN_Pin);
/**TIM1 GPIO Configuration
PE8 ------> TIM1_CH1N
*/
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*
* Hardware note: main output (CH1) and complementary output (CH1N) cannot
* be enabled at the same time on this board. For the 'left' mapping (PE8)
* we only enable the complementary output (CH1N) and ensure CH1 is stopped.
*/
/* Ensure main channel is off, then start complementary output only */
HAL_TIMEx_PWMN_Start(&TEC_TIM, TIM_CHANNEL_1);
}
// PA8-TEC-IN2(right) TIM1_CH1,默认CH1不需要映射
void right_tec_reconfig_pwm(void)
{
stop_tec_pwm();
/* Disable TIM1 remap so TIM1_CH1 maps to the default (PA8/PA9 depending on device)
Do this before re-initializing the GPIO so HAL init uses the correct port/pin */
__HAL_AFIO_REMAP_TIM1_DISABLE();
/* Deinit any previous GPIO config on PE8/PA8 to be safe */
HAL_GPIO_DeInit(GPIOE, GPIO_PIN_8);
HAL_GPIO_DeInit(GPIOA, TEC_RIGHT_IN_Pin);
/* Re-run the post-init which will configure PA8 as AF (HAL_TIM_MspPostInit uses TEC_RIGHT_IN_Pin) */
HAL_TIM_MspPostInit(&TEC_TIM);
/*
* For the 'right' mapping (PA8) we only enable the main output (CH1) and
* ensure the complementary output (CH1N) is stopped to avoid hardware conflict.
*/
HAL_TIM_PWM_Start(&TEC_TIM, TIM_CHANNEL_1);
}
void bsp_fan_start(void)
{
HAL_GPIO_WritePin(FAN_CONTROL_GPIO_Port, FAN_CONTROL_Pin, GPIO_PIN_SET);
}
void bsp_fan_stop(void)
{
HAL_GPIO_WritePin(FAN_CONTROL_GPIO_Port, FAN_CONTROL_Pin, GPIO_PIN_RESET);
}
void bsp_left_tec_open(void)
{
tec_sd_disable();
left_tec_reconfig_pwm();
left_tec_sd_enable();
}
void bsp_right_tec_open(void)
{
tec_sd_disable();
right_tec_reconfig_pwm();
right_tec_sd_enable();
}
static void left_tec_adjust_duty_cycle(uint32_t duty_cycle)
{
__HAL_TIM_SET_COMPARE(&TEC_TIM, TIM_CHANNEL_1, duty_cycle);
}
static void right_tec_adjust_duty_cycle(uint32_t duty_cycle)
{
__HAL_TIM_SET_COMPARE(&TEC_TIM, TIM_CHANNEL_1, duty_cycle);
}
void bsp_tec_cooling_open(void)
{
bsp_left_tec_open();
}
void bsp_tec_heating_open(void)
{
bsp_right_tec_open();
}
// pwm: 0.0 ~ 1.0
void bsp_tec_cooling_adjust(float pwm)
{
extern TIM_HandleTypeDef TEC_TIM;
uint32_t max_period = __HAL_TIM_GET_AUTORELOAD(&TEC_TIM);
left_tec_adjust_duty_cycle((uint32_t)(pwm * max_period));
}
// pwm: 0.0 ~ 1.0
void bsp_tec_heating_adjust(float pwm)
{
extern TIM_HandleTypeDef TEC_TIM;
uint32_t max_period = __HAL_TIM_GET_AUTORELOAD(&TEC_TIM);
right_tec_adjust_duty_cycle((uint32_t)(pwm * max_period));
}
void bsp_tec_close(void)
{
stop_tec_pwm();
tec_sd_disable();
}
void bsp_tec_init(void)
{
MX_SPI3_Init();
MX_TIM1_Init();
}

27
User/board/bsp_tec.h Normal file
View File

@ -0,0 +1,27 @@
/*
* @Author: mypx
* @Date: 2025-10-15 13:03:28
* @LastEditTime: 2025-10-15 14:58:18
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#ifndef __BSP_TEC_H__
#define __BSP_TEC_H__
void bsp_tec_init(void);
// 这俩接口内部必须做好互斥,不能同时打开
void bsp_tec_cooling_open(void);
void bsp_tec_heating_open(void);
void bsp_tec_close(void);
// pwm: 0.0 ~ 1.0
void bsp_tec_cooling_adjust(float pwm);
// pwm: 0.0 ~ 1.0
void bsp_tec_heating_adjust(float pwm);
void bsp_fan_start(void);
void bsp_fan_stop(void);
#endif // __BSP_TEC_H__

View File

@ -0,0 +1,140 @@
/*
* @Author: mypx
* @Date: 2025-10-15 15:32:36
* @LastEditTime: 2025-10-16 10:26:19
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "bsp_temper_sampling.h"
#include "et_log.h"
#include "spi.h"
#if (USING_SOFT_SPI == 1)
soft_spi_t soft_spi = {
.sck_port = GPIOB,
.sck_pin = GPIO_PIN_5,
.mosi_port = GPIOB,
.mosi_pin = GPIO_PIN_6,
.miso_port = GPIOB,
.miso_pin = GPIO_PIN_4,
.cs_port = GPIOB,
.cs_pin = GPIO_PIN_7,
.mode = SOFT_SPI_MODE0,
.bit_order = SOFT_SPI_MSB_FIRST,
.lock = RT_NULL, // 使用 RT_NULL初始化时会创建
.delay_us = 1, // 每半个 SPI 时钟周期延时 1 微秒
};
void bsp_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
{
soft_spi_transfer_buffer(&soft_spi, tx_buf, rx_buf, len);
}
void bsp_ad7793_spi_write(uint8_t cmd, uint8_t *data, uint16_t len)
{
soft_spi_write(&soft_spi, &cmd, 1, data, len);
}
void bsp_ad7793_spi_read(uint8_t cmd, uint8_t *rx_buf, uint16_t len)
{
soft_spi_read(&soft_spi, &cmd, 1, rx_buf, len);
}
void bsp_ad7793_gpio_set(uint8_t pin, bool state)
{
if (pin == soft_spi.cs_pin)
{
HAL_GPIO_WritePin(soft_spi.cs_port, soft_spi.cs_pin, state);
}
}
bool bsp_ad7793_gpio_get(uint8_t pin)
{
if (pin == soft_spi.miso_pin)
{
return HAL_GPIO_ReadPin(soft_spi.miso_port, soft_spi.miso_pin) == GPIO_PIN_SET;
}
return false;
}
#else
#define AD7793_CS_LOW() HAL_GPIO_WritePin(SPI3_AD7793_CS_GPIO_Port, SPI3_AD7793_CS_Pin, GPIO_PIN_RESET)
#define AD7793_CS_HIGH() HAL_GPIO_WritePin(SPI3_AD7793_CS_GPIO_Port, SPI3_AD7793_CS_Pin, GPIO_PIN_SET)
void bsp_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
{
HAL_StatusTypeDef status;
status = HAL_SPI_TransmitReceive(&AD7793_SPI, tx_buf, rx_buf, len, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI transfer error:%d!", status);
}
}
void bsp_ad7793_spi_write(uint8_t cmd, uint8_t *data, uint16_t len)
{
uint8_t tx_buffer[256];
HAL_StatusTypeDef status;
tx_buffer[0] = cmd;
if (data && len > 0)
{
for (uint16_t i = 0; i < len; i++)
{
tx_buffer[i + 1] = data[i];
}
}
status = HAL_SPI_Transmit(&AD7793_SPI, tx_buffer, len + 1, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI write error:%d!", status);
}
}
void bsp_ad7793_spi_read(uint8_t cmd, uint8_t *rx_buf, uint16_t len)
{
uint8_t tx_buffer[1] = {cmd};
HAL_StatusTypeDef status;
status = HAL_SPI_Transmit(&AD7793_SPI, tx_buffer, 1, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI write error:%d!", status);
}
status = HAL_SPI_Receive(&AD7793_SPI, rx_buf, len, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI read error:%d!", status);
}
}
void bsp_ad7793_gpio_set(uint8_t pin, bool state)
{
if (pin == SPI3_AD7793_CS_Pin)
{
HAL_GPIO_WritePin(SPI3_AD7793_CS_GPIO_Port, SPI3_AD7793_CS_Pin, state);
}
}
bool bsp_ad7793_gpio_get(uint8_t pin)
{
if (pin == SPI3_AD7793_MISO_Pin)
{
return HAL_GPIO_ReadPin(SPI3_AD7793_MISO_GPIO_Port, SPI3_AD7793_MISO_Pin) == GPIO_PIN_SET;
}
return false;
}
#endif
void bsp_ad7793_delay_ms(uint16_t ms)
{
rt_thread_mdelay(ms); // 使用 RT-Thread 的延时函数
}
void bsp_temper_sampling_init(void)
{
#if (USING_SOFT_SPI == 1)
soft_spi_init(&soft_spi);
soft_spi_set_mode(&soft_spi, SOFT_SPI_MODE3); // 设置 SPI 模式
soft_spi_set_bit_order(&soft_spi, SOFT_SPI_MSB_FIRST); // 设置位序
soft_spi_set_delay(&soft_spi, 1); // 设置延时为 1 微秒
soft_spi_select(&soft_spi); // 选择 SPI 设备
#endif
}

View File

@ -0,0 +1,27 @@
/*
* @Author: mypx
* @Date: 2025-10-15 15:32:55
* @LastEditTime: 2025-10-15 16:35:56
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#ifndef __BSP_TEMPER_SAMPLING_H__
#define __BSP_TEMPER_SAMPLING_H__
#include <stdbool.h>
#include <stdint.h>
void bsp_temper_sampling_init(void);
void bsp_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len);
void bsp_ad7793_spi_write(uint8_t cmd, uint8_t *data, uint16_t len);
void bsp_ad7793_spi_read(uint8_t cmd, uint8_t *rx_buf, uint16_t len);
void bsp_ad7793_gpio_set(uint8_t pin, bool state);
bool bsp_ad7793_gpio_get(uint8_t pin);
void bsp_ad7793_delay_ms(uint16_t ms);
#endif

View File

@ -1,552 +0,0 @@
/*
* @Author: mypx
* @Email: mypx_coder@163.com
* @Date: 2025-06-19 12:29:17
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "pm_board.h"
#include "ad7793.h"
#include "adc.h"
#include "dma.h"
#include "et_log.h"
#include "gpio.h"
#include "i2c.h"
#include "rtdef.h"
#include "rthw.h"
#include "rtthread.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include <stdbool.h>
#include <string.h>
#define AT24C128_I2C_ADDR 0xA0
#if (DEBUG_MB_ENABLE == 1)
#define DBG_MB(...) ET_DBG("[MB] " __VA_ARGS__)
#else
#define DBG_MB(...)
#endif
volatile uint64_t timestamp_tim_overflow_count = 0;
#if (USING_SOFT_SPI == 1)
soft_spi_t soft_spi = {
.sck_port = GPIOB,
.sck_pin = GPIO_PIN_5,
.mosi_port = GPIOB,
.mosi_pin = GPIO_PIN_6,
.miso_port = GPIOB,
.miso_pin = GPIO_PIN_4,
.cs_port = GPIOB,
.cs_pin = GPIO_PIN_7,
.mode = SOFT_SPI_MODE0,
.bit_order = SOFT_SPI_MSB_FIRST,
.lock = RT_NULL, // 使用 RT_NULL初始化时会创建
.delay_us = 1, // 每半个 SPI 时钟周期延时 1 微秒
};
void pm_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
{
soft_spi_transfer_buffer(&soft_spi, tx_buf, rx_buf, len);
}
void pm_ad7793_spi_write(uint8_t cmd, uint8_t *data, uint16_t len)
{
soft_spi_write(&soft_spi, &cmd, 1, data, len);
}
void pm_ad7793_spi_read(uint8_t cmd, uint8_t *rx_buf, uint16_t len)
{
soft_spi_read(&soft_spi, &cmd, 1, rx_buf, len);
}
void pm_ad7793_gpio_set(uint8_t pin, bool state)
{
if (pin == soft_spi.cs_pin)
{
HAL_GPIO_WritePin(soft_spi.cs_port, soft_spi.cs_pin, state);
}
}
bool pm_ad7793_gpio_get(uint8_t pin)
{
if (pin == soft_spi.miso_pin)
{
return HAL_GPIO_ReadPin(soft_spi.miso_port, soft_spi.miso_pin) == GPIO_PIN_SET;
}
return false;
}
#else
#define AD7793_CS_LOW() HAL_GPIO_WritePin(AD7793_CS_GPIO_Port, AD7793_CS_Pin, GPIO_PIN_RESET)
#define AD7793_CS_HIGH() HAL_GPIO_WritePin(AD7793_CS_GPIO_Port, AD7793_CS_Pin, GPIO_PIN_SET)
void pm_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
{
HAL_StatusTypeDef status;
status = HAL_SPI_TransmitReceive(&AD7793_SPI, tx_buf, rx_buf, len, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI transfer error:%d!", status);
}
}
void pm_ad7793_spi_write(uint8_t cmd, uint8_t *data, uint16_t len)
{
uint8_t tx_buffer[256];
HAL_StatusTypeDef status;
tx_buffer[0] = cmd;
if (data && len > 0)
{
for (uint16_t i = 0; i < len; i++)
{
tx_buffer[i + 1] = data[i];
}
}
status = HAL_SPI_Transmit(&AD7793_SPI, tx_buffer, len + 1, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI write error:%d!", status);
}
}
void pm_ad7793_spi_read(uint8_t cmd, uint8_t *rx_buf, uint16_t len)
{
uint8_t tx_buffer[1] = {cmd};
HAL_StatusTypeDef status;
status = HAL_SPI_Transmit(&AD7793_SPI, tx_buffer, 1, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI write error:%d!", status);
}
status = HAL_SPI_Receive(&AD7793_SPI, rx_buf, len, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI read error:%d!", status);
}
}
void pm_ad7793_gpio_set(uint8_t pin, bool state)
{
if (pin == SPI3_CS_AD7793_Pin)
{
HAL_GPIO_WritePin(SPI3_CS_AD7793_GPIO_Port, SPI3_CS_AD7793_Pin, state);
}
}
bool pm_ad7793_gpio_get(uint8_t pin)
{
if (pin == SPI3_MISO_AD7793_Pin)
{
return HAL_GPIO_ReadPin(SPI3_MISO_AD7793_GPIO_Port, SPI3_MISO_AD7793_Pin) == GPIO_PIN_SET;
}
return false;
}
#endif
void pm_ad7793_delay_ms(uint16_t ms)
{
rt_thread_mdelay(ms); // 使用 RT-Thread 的延时函数
}
ad7793_hw_if_t ad7793_hw_if = {
.spi_transfer = pm_ad7793_spi_transfer,
.spi_write = pm_ad7793_spi_write,
.spi_read = pm_ad7793_spi_read,
.gpio_set = pm_ad7793_gpio_set,
.gpio_get = pm_ad7793_gpio_get,
.delay_ms = pm_ad7793_delay_ms,
};
#if (ENABLE_SV630P == 1)
struct rt_messagequeue servo_mq;
static char mq_pool[SERVO_RX_QUEUE_SIZE];
#endif // USING_SOFT_SPI
void pm_system_led_toggle(void)
{
HAL_GPIO_TogglePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin);
}
void pm_system_led_on(void)
{
HAL_GPIO_WritePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin, GPIO_PIN_SET);
}
void pm_system_led_off(void)
{
HAL_GPIO_WritePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin, GPIO_PIN_RESET);
}
void pm_error_led_on(void)
{
HAL_GPIO_WritePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin, GPIO_PIN_SET);
}
void pm_error_led_off(void)
{
HAL_GPIO_WritePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin, GPIO_PIN_RESET);
}
void pm_error_led_toggle(void)
{
HAL_GPIO_TogglePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin);
}
int pm_i2c_write_bytes(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data, uint16_t len)
{
HAL_StatusTypeDef res;
// AT24C128 单页为 64 字节,写入时建议不要跨页,否则需要拆包
if (len > 64)
return HAL_ERROR;
res = HAL_I2C_Mem_Write(&hi2c2, dev_addr, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
HAL_Delay(5); // EEPROM写入周期典型为5ms
return res;
}
int pm_i2c_read_bytes(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data, uint16_t len)
{
return HAL_I2C_Mem_Read(&hi2c2, dev_addr, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
}
void pm_uart_print_send(const uint8_t *data, uint16_t len)
{
#ifdef USING_RTT_AS_CONSOLE
#warning "USING_RTT_AS_CONSOLE defined, use uart1 as hmi interface"
#else
HAL_UART_Transmit(&huart1, data, len, 1000);
#endif
}
void pm_lcd_cmd_send(const uint8_t *data, uint16_t len)
{
//#ifdef USING_RTT_AS_CONSOLE
HAL_UART_Transmit(&huart1, data, strlen(data), 1000);
// #else
// HAL_UART_Transmit(&huart2, data, strlen(data), 1000);
// #endif
}
struct rt_messagequeue mb_hmi_mq;
static char mq_pool[MB_HMI_RX_QUEUE_SIZE];
int pm_mb_hmi_init(void)
{
MX_USART1_UART_Init(); // used tx
MX_USART2_UART_Init(); // used rx
rt_err_t result = rt_mq_init(&mb_hmi_mq, "mb_hmi_mq", mq_pool, 1, MB_HMI_RX_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
ET_ERR("mb_hmi mq init failed\n");
return -1;
}
//__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&HMI_COM, UART_IT_RXNE);
return 0;
}
int32_t pm_hmi_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
uint16_t bytes_read = 0;
uint8_t byte;
rt_tick_t timeout_ticks = rt_tick_from_millisecond(byte_timeout_ms);
DBG_MB("start read:count:%d, timeout:%d\n", count, byte_timeout_ms);
while (bytes_read < count)
{
rt_err_t result = rt_mq_recv(&mb_hmi_mq, &byte, sizeof(byte), timeout_ticks);
if (result == RT_EOK)
{
DBG_MB("rx: %02X\n", byte);
buf[bytes_read++] = byte;
}
else
{
if (bytes_read > 0)
{
DBG_MB("Modbus read finished %d\n", bytes_read);
return bytes_read;
}
DBG_MB("HMI read timeout, no data received\n");
return 0;
}
}
DBG_MB("Actually read:%d, count:%d\r\n", bytes_read, count);
return bytes_read;
}
int32_t pm_hmi_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
rt_thread_mdelay(2);
//#ifdef USING_RTT_AS_CONSOLE
if (HAL_UART_Transmit(&huart1, buf, count, byte_timeout_ms) == HAL_OK)
// #else
// if (HAL_UART_Transmit(&HMI_COM, buf, count, byte_timeout_ms) == HAL_OK)
// #endif
{
ET_DBG("HMI write:%d", count);
return count;
}
else
{
return 0;
}
}
#if (ENABLE_SV630P == 1)
int pm_servo_init(void)
{
MX_USART3_UART_Init();
rt_err_t result = rt_mq_init(&servo_mq, "servo_mq", mq_pool, 1, SERVO_RX_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
ET_ERR("servo mq init failed\n");
return -1;
}
__HAL_UART_ENABLE_IT(&SERVO_COM, UART_IT_RXNE);
return 0;
}
int32_t pm_servo_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
uint16_t bytes_read = 0;
uint8_t byte;
rt_tick_t timeout_ticks = rt_tick_from_millisecond(byte_timeout_ms);
DBG_MB("start read:count:%d, timeout:%d\n", count, byte_timeout_ms);
while (bytes_read < count)
{
rt_err_t result = rt_mq_recv(&servo_mq, &byte, sizeof(byte), timeout_ticks);
if (result == RT_EOK)
{
DBG_MB("rx: %02X\n", byte);
buf[bytes_read++] = byte;
}
else
{
if (bytes_read > 0)
{
DBG_MB("Modbus read finished %d\n", bytes_read);
return bytes_read;
}
DBG_MB("Servo read timeout, no data received\n");
return 0;
}
}
DBG_MB("Actually read:%d, count:%d\r\n", bytes_read, count);
return bytes_read;
}
int32_t pm_servo_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
rt_thread_mdelay(2);
if (HAL_UART_Transmit(&SERVO_COM, buf, count, byte_timeout_ms) == HAL_OK)
{
return count;
}
else
{
return 0;
}
}
#endif // ENABLE_SV630P
#if (ENABLE_TEC == 1)
void pm_tec_init(void)
{
#if (USING_SOFT_SPI == 0)
MX_SPI3_Init();
#endif
MX_TIM1_Init();
}
void pm_tec_start(void)
{
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
}
void pm_tec_stop(void)
{
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);
}
void pm_tec_set_duty(float pid_output)
{
uint32_t period = htim1.Init.Period;
// 限制PID输出范围
const float MIN_POWER = 2.0f; // 最小有效功率(%)
if (pid_output > 100.0f)
pid_output = 100.0f;
if (pid_output < -100.0f)
pid_output = -100.0f;
// 应用最小功率限制
if (pid_output > 0 && pid_output < MIN_POWER)
pid_output = MIN_POWER;
if (pid_output < 0 && pid_output > -MIN_POWER)
pid_output = -MIN_POWER;
// 计算占空比
uint32_t compare_value;
if (pid_output >= 0)
{
// 制热占空比50%~0%
compare_value = period / 2 + (uint32_t)(period * pid_output / 200.0f);
}
else
{
// 制热占空比50%~0%
compare_value = period / 2 - (uint32_t)(period * fabs(pid_output) / 200.0f);
}
ET_DBG("PWM COMAPRE: %d", compare_value);
// 设置PWM占空比
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, compare_value);
//__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 2);
}
void pm_fan_start(void)
{
HAL_GPIO_WritePin(FAN_CONTROL_GPIO_Port, FAN_CONTROL_Pin, GPIO_PIN_SET);
}
void pm_fan_stop(void)
{
HAL_GPIO_WritePin(FAN_CONTROL_GPIO_Port, FAN_CONTROL_Pin, GPIO_PIN_RESET);
}
#endif // ENABLE_TEC
void pm_timestamp_tim_init(void)
{
MX_TIM4_Init();
}
void pm_timestamp_start(void)
{
__HAL_TIM_SET_COUNTER(&TIMESTAMP_TIM, 0);
timestamp_tim_overflow_count = 0;
HAL_TIM_Base_Start_IT(&TIMESTAMP_TIM);
}
void pm_timestamp_stop(void)
{
HAL_TIM_Base_Stop_IT(&TIMESTAMP_TIM);
}
uint64_t pm_get_timestamp_us(void)
{
uint16_t current_cnt = __HAL_TIM_GET_COUNTER(&TIMESTAMP_TIM);
return (uint64_t)timestamp_tim_overflow_count * 65536 + current_cnt;
}
void pm_adc_init(void)
{
MX_DMA_Init();
MX_ADC1_Init();
}
uint32_t pm_adc_get_value(void)
{
if (HAL_ADC_PollForConversion(&LIGHT_ADC, 10) != HAL_OK)
{
ET_ERR("ADC read should not wait!");
pm_error_led_on();
return 0;
}
return HAL_ADC_GetValue(&LIGHT_ADC);
}
void pm_sampling_tim_init(void)
{
MX_TIM3_Init();
}
void pm_sampling_tim_start(void)
{
HAL_TIM_Base_Start_IT(&SAMPLING_TIM);
}
void pm_sampling_tim_stop(void)
{
HAL_TIM_Base_Stop_IT(&SAMPLING_TIM);
}
void pm_sampling_adc_start(uint16_t *buffer, uint32_t length)
{
if (HAL_ADC_Start_DMA(&LIGHT_ADC, (uint32_t *)buffer, length) != HAL_OK)
{
ET_ERR("ADC start DMA error!");
pm_error_led_on();
}
//HAL_ADC_Start(&LIGHT_ADC);
}
void pm_sampling_adc_stop(void)
{
//HAL_ADC_Stop(&hadc1);
HAL_ADC_Stop_DMA(&LIGHT_ADC);
}
float pm_sampling_tim_get_freq(void)
{
uint32_t pclk1 = HAL_RCC_GetPCLK1Freq();
uint32_t timer_clk;
// 获取APB1预分频系数来自RCC配置寄存器
uint32_t apb1_prescaler = (RCC->CFGR & RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos;
// 根据APB1预分频计算定时器时钟源
if (apb1_prescaler == RCC_HCLK_DIV1)
{
// APB1预分频=1定时器时钟=PCLK1
timer_clk = pclk1;
}
else
{
// APB1预分频>1定时器时钟=2×PCLK1
timer_clk = pclk1 * 2;
}
// 计算定时器计数频率经过PSC分频后
uint32_t counter_freq = timer_clk / (SAMPLING_TIM.Init.Prescaler + 1);
// 计算最终输出频率(周期触发频率 = 计数频率 / (Period + 1)
// 注意如果是向上计数模式完整周期是从0到Period共Period+1个计数
return (float)counter_freq / (SAMPLING_TIM.Init.Period + 1);
}
int pm_board_init(void)
{
MX_GPIO_Init();
// MX_USART1_UART_Init();
#if (USING_SOFT_SPI == 1)
soft_spi_init(&soft_spi);
soft_spi_set_mode(&soft_spi, SOFT_SPI_MODE3); // 设置 SPI 模式
soft_spi_set_bit_order(&soft_spi, SOFT_SPI_MSB_FIRST); // 设置位序
soft_spi_set_delay(&soft_spi, 1); // 设置延时为 1 微秒
soft_spi_select(&soft_spi); // 选择 SPI 设备
#endif
return 0;
}

View File

@ -1,109 +0,0 @@
/*
* @Date: 2025-06-19 12:29:23
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-08-25 17:06:01
* @FilePath: board_pm.h
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
*/
/***
* @Author: mypx
* @Email: mypx_coder@163.com
* @Date: 2025-06-19 12:29:23
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#ifndef __PM_BOARD_H__
#define __PM_BOARD_H__
#include "adc.h"
#include "main.h"
#include "rtthread.h"
#include "tim.h"
#include "user_config.h"
#include <stdbool.h>
#include <stdint.h>
extern volatile uint64_t timestamp_tim_overflow_count;
typedef enum
{
TEC_COOLING = 0,
TEC_HEATING = 1
} TEC_Direction_TypeDef;
int pm_board_init(void);
void pm_system_led_toggle(void);
void pm_system_led_on(void);
void pm_system_led_off(void);
void pm_error_led_on(void);
void pm_error_led_off(void);
void pm_error_led_toggle(void);
int pm_i2c_write_bytes(uint16_t dev_addr, uint16_t reg, uint8_t *data, uint16_t size);
int pm_i2c_read_bytes(uint16_t dev_addr, uint16_t reg, uint8_t *data, uint16_t size);
void pm_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len);
void pm_uart_print_send(const uint8_t *data, uint16_t len);
void pm_lcd_cmd_send(const uint8_t *data, uint16_t len);
#define MB_HMI_RX_QUEUE_SIZE 128 // Modbus HMI 接收队列大小
extern struct rt_messagequeue mb_hmi_mq;
int pm_mb_hmi_init(void);
int32_t pm_hmi_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg);
int32_t pm_hmi_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg);
#if (ENABLE_SV630P == 1)
#define SERVO_RX_QUEUE_SIZE 128 // Modbus RTU 接收队列大小
extern struct rt_messagequeue servo_mq;
int pm_servo_init(void);
int32_t pm_servo_uart_read(uint8_t *buf, uint16_t count, int32_t timeout, void *arg);
int32_t pm_servo_uart_write(const uint8_t *buf, uint16_t count, int32_t timeout, void *arg);
#endif // ENABLE_SV630P
#if (ENABLE_TEC == 1)
void pm_tec_init(void);
void pm_tec_start(void);
void pm_tec_stop(void);
void pm_tec_set_duty(float pid_output);
void pm_fan_start(void);
void pm_fan_stop(void);
#endif
void pm_timestamp_tim_init(void);
void pm_timestamp_start(void);
void pm_timestamp_stop(void);
uint64_t pm_get_timestamp_us(void);
void pm_adc_init(void);
void pm_sampling_adc_start(uint16_t *buffer, uint32_t length);
void pm_sampling_adc_stop(void);
uint32_t pm_adc_get_value(void);
void pm_sampling_tim_init(void);
void pm_sampling_tim_start(void);
void pm_sampling_tim_stop(void);
float pm_sampling_tim_get_freq(void);
#endif // __BOARD_H__

View File

@ -55,6 +55,63 @@ AD7792则以更低成本覆盖中精度需求场景。
---
## 推荐初始化流程AD7793
1. 配置 `ad7793_hw_if_t` 硬件接口结构体,填充 SPI/GPIO/延时等函数指针。
2. 定义 `ad7793_config_t` 配置结构体,指定参考源、增益、通道、速率、缓冲等参数。
3. 调用 `ad7793_init(&dev, &hw_if, cs_pin, &cfg)` 完成初始化。
4. 采集数据前建议调用 `ad7793_wait_ready` 等待数据就绪。
5. 采集数据用 `ad7793_read_data`,转换电压用 `ad7793_convert_to_voltage`
6. 如需切换通道/增益/参考源,建议先 idle再切换参数最后恢复 continuous。
## 常见故障排查
- STATUS=0x48/0xC8多为参考源配置与硬件不符、增益/缓冲冲突、IEXC未配置等。
- 读数全0或极大检查参考电压、SPI连线、通道/增益配置。
- 写寄存器失败:检查 SPI 速率、CS 时序、硬件接口实现。
- 温度跳变/毛刺:建议应用层做温度跳变/低码滤波。
- 采样速率异常确认速率配置与主时钟、SPI速率匹配。
## IEXC激励电流配置注意事项
- 仅当硬件实际将 IOUT1/IOUT2 接到 RTD 或传感器回路时才需配置 IEXC。
- 若 IOUT1/IOUT2 悬空(未连接),请勿使能 IEXC否则可能导致不可预期行为或错误状态如 STATUS=0x48
- IEXC 配置必须与硬件连接方式一致。若用外部电流源或定值电阻IEXC 应保持关闭。
## 缓冲/增益策略
- 关闭缓冲BUF=0增益不得大于2G<=2驱动已强制限制。
- 开启缓冲BUF=1可用高增益但输入偏置电流和功耗增加。
## 寄存器dump与诊断
- 可用 `ad7793_dump_registers``ad7793_dump_registers_mode` 打印所有寄存器值,便于调试。
- 检测到持续错误STATUS.ERR时建议dump寄存器辅助定位参考、缓冲、增益、IEXC等配置问题。
## API典型用法与边界说明
- 所有API均检查设备初始化和参数有效性未初始化或参数非法均返回false。
- 具体API用法和边界条件详见头文件注释。
## Buffer/Gain Policy
- When buffer is disabled (BUF=0), gain must not exceed 2 (G<=2). The driver enforces this restriction and will return false if you attempt to set G>2 with buffer off.
- Enabling the buffer (BUF=1) allows higher gain settings, but increases input bias current and power consumption.
## Register Dump and Diagnostics
- Use `ad7793_dump_registers` or `ad7793_dump_registers_mode` to print all register values for debugging.
- When persistent error (STATUS.ERR) is detected, perform a register dump to help diagnose the cause (reference, buffer, gain, IEXC, etc.).
## Typical API Usage and Boundary Notes
- All API functions check for device initialization and parameter validity; invalid parameters or uninitialized device will return false.
- See header file comments for typical usage and boundary conditions for each API.
---
## 通信寄存器用法
通信寄存器在AD7793中起到控制数据传输方向和指定目标寄存器的作用。它是SPI通信接口的核心控制寄存器用于告诉AD7793后续的操作是读还是写以及操作的目标寄存器是哪一个。
@ -99,3 +156,59 @@ bool ad7793_write_reg(uint8_t reg, uint8_t *data, uint16_t len)
3. **数据寄存器的特殊处理**数据寄存器REG_DATA在读模式下有特殊的连续读功能可以通过设置`CREAD`位来启用。
理解通信寄存器的工作原理对于正确操作AD7793至关重要它是实现与器件通信的基础。
---
# AD7793/AD7792 应用配置与调试注意事项
## 推荐配置PT100/RTD 测温典型拓扑)
```c
static const ad7793_config_t g_ad7793_config = {
.use_internal_ref = false, // 使用外部参考如2.5VREFIN+已实测2.5V
.external_ref = 2.500f,
// 传感器差分约 0.043V,需保证 < Vref / Gain
// 选择 GAIN=4 -> 满量程 2.5/4=0.625V,安全覆盖 43mV 输入
.init_gain = AD7793_GAIN_4,
.init_channel = AD7793_CHANNEL_1, // AIN1(+) - AIN1(-)
.init_rate = AD7793_RATE_8_33HZ,
.unipolar = true, // 只测正向信号
.buffered = true, // 启用缓冲
.calibrate_system_zero = false // 禁用 system zero 校准,防止 OFFSET 被污染
};
```
## 配置与调试注意事项
1. **参考电压必须与硬件一致**
- 若硬件 REFIN+ 接外部参考如2.5V`use_internal_ref` 必须为 `false``external_ref` 填实际电压。
- 若用内部参考1.17V),需保证 REFIN+/- 悬空且 `use_internal_ref=true`
2. **增益选择**
- 满量程 = 参考电压 / 增益。输入信号最大值必须小于满量程。
- 输入信号远小于满量程时可适当提高增益(如 GAIN=8/16但不能超量程。
3. **校准操作**
- 禁用 system zero 校准(`calibrate_system_zero=false`),防止在参考/输入异常时 OFFSET 被写坏。
- 若需校准,必须保证参考和输入都正常且在允许范围内。
4. **错误状态与保护**
- STATUS.ERRbit6=1表示参考/输入异常,采样数据无效。
- 采样线程应对 ERR/低码做过滤,避免异常温度。
- OFFSET/FULLSCALE 寄存器应在每次初始化和异常时 dump 检查。
5. **常见故障排查**
- STATUS=0x48 持续:多为参考配置与硬件不符或输入超量程。
- OFFSET=0x800000 为正常中点,极大/极小为校准异常。
- 采样码值极低或全0多为参考/输入/校准异常。
6. **硬件测量建议**
- 用万用表测 REFIN+ 对地、AIN1+/AIN1- 差分,确认与配置一致。
7. **调试建议**
- 先用低增益(如 GAIN=2/4确保不超量程采样正常后再逐步提高增益。
- 采样线程建议加低码/ERR保护防止异常数据影响温度计算。
---
如需更高分辨率或特殊应用,请根据实际输入信号幅度和参考电压合理调整增益与参考配置。

View File

@ -7,17 +7,20 @@
*/
#include "ad7793.h"
#include <stdbool.h>
#include <stdio.h>
#include <stddef.h>
#ifdef DEBUG
#include <rtthread.h>
#define DEBUG_PRINTF(fmt, ...) \
do { \
rt_kprintf("[AD7793] " fmt, ##__VA_ARGS__); \
#define DEBUG_PRINTF(...) \
do \
{ \
rt_kprintf("[AD7793] "); \
rt_kprintf(__VA_ARGS__); \
} while (0)
#else
#define DEBUG_PRINTF(fmt, ...) \
do { \
#define DEBUG_PRINTF(...) \
do \
{ \
} while (0)
#endif
/**
@ -56,6 +59,9 @@ static bool ad7793_spi_transfer(ad7793_dev_t *dev, uint8_t cmd, uint8_t *tx_buf,
return true;
}
/* Forward declaration for diagnostic dump (used early) */
void ad7793_dump_registers(ad7793_dev_t *dev);
/**
* @brief Write communication register.
* @param dev Pointer to ad7793_dev_t instance.
@ -113,7 +119,28 @@ bool ad7793_write_reg(ad7793_dev_t *dev, uint8_t reg, uint32_t data, uint16_t le
//ad7793_spi_transfer(dev, cmd, data, NULL, len);
dev->hw_if->gpio_set(dev->cs_pin, true);
return true;
/* Verify write by reading back the register up to a few retries */
for (int attempt = 0; attempt < 3; attempt++)
{
uint32_t read_back = 0;
dev->hw_if->delay_ms(2);
if (!ad7793_read_reg(dev, reg, &read_back, len))
{
DEBUG_PRINTF("Readback failed for reg 0x%02X on attempt %d\n", reg, attempt);
continue;
}
/* mask read_back to len bytes */
uint32_t mask = (len == 3) ? 0xFFFFFFu : ((len == 2) ? 0xFFFFu : 0xFFu);
if ((read_back & mask) == (data & mask))
{
return true; /* success */
}
DEBUG_PRINTF("Write verify mismatch reg 0x%02X: wrote 0x%0*X read 0x%0*X (attempt %d)\n", reg, len * 2,
data & mask, len * 2, read_back & mask, attempt + 1);
}
DEBUG_PRINTF("Write to reg 0x%02X failed after retries\n", reg);
return false;
}
/**
@ -173,7 +200,8 @@ bool ad7793_reset(ad7793_dev_t *dev)
dev->hw_if->gpio_set(dev->cs_pin, false);
uint8_t reset_seq[4] = {0xFF, 0xFF, 0xFF, 0xFF};
dev->hw_if->spi_transfer(reset_seq, NULL, 4);
uint8_t tmp[4];
dev->hw_if->spi_transfer(reset_seq, tmp, 4);
dev->hw_if->gpio_set(dev->cs_pin, true);
dev->hw_if->delay_ms(1); /* Wait for reset to complete */
@ -245,6 +273,14 @@ bool ad7793_set_gain(ad7793_dev_t *dev, ad7793_gain_t gain)
uint32_t config_reg = 0;
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
// 缓冲/增益策略规范化BUF=0且G>2时禁止配置
bool buf_enabled = (config_reg & CONFIG_BUF) ? true : false;
if (!buf_enabled && gain > 2)
{
DEBUG_PRINTF("[ERR] Attempt to set gain >2 while buffer disabled!\n");
return false;
}
/* Clear gain bits (G2/G1/G0) */
config_reg &= ~(CONFIG_G2 | CONFIG_G1 | CONFIG_G0);
@ -374,8 +410,9 @@ bool ad7793_set_reference(ad7793_dev_t *dev, bool use_internal, float external_r
}
else
{
dev->external_ref = external_ref;
config_reg &= ~CONFIG_REFSEL;
dev->external_ref = external_ref;
dev->use_internal_ref = false; /* reflect external reference selection */
config_reg &= ~CONFIG_REFSEL;
}
DEBUG_PRINTF("[config-w]set reference: 0x%04X\n", config_reg);
return ad7793_write_reg(dev, REG_CONFIG, config_reg, 2);
@ -398,11 +435,15 @@ bool ad7793_wait_ready(ad7793_dev_t *dev, uint16_t timeout_ms)
while (timeout < timeout_ms)
{
ad7793_read_reg(dev, REG_STATUS, &status, 1);
dev->last_status = status;
if (status & 0x40) // check if the device is in error state
{
DEBUG_PRINTF("Device in error state, status: 0x%02X\n", status);
return false; // device error
dev->err_count++;
if ((dev->err_count % 128u) == 1u) /* 抽样打印,避免刷屏 */
{
DEBUG_PRINTF("Device in error state (status=0x%02X) - continue polling\n", status);
ad7793_dump_registers(dev);
}
}
// check RDY bitbit7is zero or not
if ((status & 0x80) == 0)
@ -418,6 +459,45 @@ bool ad7793_wait_ready(ad7793_dev_t *dev, uint16_t timeout_ms)
return false; // timeout
}
/* Diagnostic helper: dump key registers */
void ad7793_dump_registers(ad7793_dev_t *dev)
{
ad7793_dump_registers_mode(dev, 1);
}
// mode: 0=简略, 1=全量
typedef enum
{
AD7793_DUMP_SIMPLE = 0,
AD7793_DUMP_FULL = 1
} ad7793_dump_mode_t;
void ad7793_dump_registers_mode(ad7793_dev_t *dev, int full)
{
if (!dev || !dev->hw_if)
return;
uint32_t reg = 0;
ad7793_read_reg(dev, REG_STATUS, &reg, 1);
DEBUG_PRINTF("DUMP STATUS: 0x%02X\n", (unsigned)reg);
ad7793_read_reg(dev, REG_MODE, &reg, 2);
DEBUG_PRINTF("DUMP MODE: 0x%04X\n", (unsigned)reg);
ad7793_read_reg(dev, REG_CONFIG, &reg, 2);
DEBUG_PRINTF("DUMP CONFIG: 0x%04X\n", (unsigned)reg);
if (full)
{
ad7793_read_reg(dev, REG_IO, &reg, 1);
DEBUG_PRINTF("DUMP IO: 0x%02X\n", (unsigned)reg);
ad7793_read_reg(dev, REG_ID, &reg, 1);
DEBUG_PRINTF("DUMP ID: 0x%02X\n", (unsigned)reg);
ad7793_read_reg(dev, REG_OFFSET, &reg, 3);
DEBUG_PRINTF("DUMP OFFSET: 0x%06X\n", (unsigned)reg);
ad7793_read_reg(dev, REG_FULLSCALE, &reg, 3);
DEBUG_PRINTF("DUMP FULLSC: 0x%06X\n", (unsigned)reg);
}
// 关键信息摘要
DEBUG_PRINTF("SUMMARY: mode=%d gain=%d ch=%d ref=%s buf=%d uni=%d\n", dev->cur_mode, dev->cur_gain,
dev->cur_channel, dev->use_internal_ref ? "int" : "ext", dev->buffered, dev->unipolar_mode);
}
/**
* @brief Read the conversion data from the device.
* @param dev Pointer to ad7793_dev_t instance.
@ -501,7 +581,10 @@ float ad7793_convert_to_voltage(ad7793_dev_t *dev, uint32_t adc_code)
if (!(resolution_bits == 16 || resolution_bits == 24))
return 0.0f; // Invalid resolution
if (!buffer_enabled && gain > 2)
return 0.0f; // According to datasheet, buffer must be enabled when gain > 2
{
DEBUG_PRINTF("Warning: buffer disabled while gain > 2 — result may be invalid\n");
/* Do not outright fail here; allow conversion but warn the caller. */
}
// Check that the ADC code is within valid range
uint32_t max_code = (resolution_bits == 24) ? 0xFFFFFFu : 0xFFFFu;
@ -580,7 +663,10 @@ uint32_t ad7793_convert_voltage_to_code(ad7793_dev_t *dev, float voltage)
if (!(resolution_bits == 16 || resolution_bits == 24))
return 0; // Invalid resolution
if (!buffer_enabled && gain > 2)
return 0; // According to datasheet, buffer must be enabled when gain > 2
{
DEBUG_PRINTF("Warning: buffer disabled while gain > 2 — code may be invalid\n");
/* Allow calculation to proceed but warn */
}
// Calculate maximum ADC code value
uint32_t max_code = (resolution_bits == 24) ? 0xFFFFFFu : 0xFFFFu;
@ -651,7 +737,12 @@ bool ad7793_calibrate_internal_full(ad7793_dev_t *dev)
{
AD7793_CHECK_INITIALIZED(dev);
ad7793_set_mode(dev, AD7793_MODE_INTERNAL_FULL);
dev->hw_if->delay_ms(10); /* Wait for calibration to complete */
/* Wait until RDY clears (calibration completed) or timeout */
if (!ad7793_wait_ready(dev, 1000))
{
DEBUG_PRINTF("Internal full-scale calibration timeout\n");
return false;
}
return ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
}
@ -666,7 +757,11 @@ bool ad7793_calibrate_system_zero(ad7793_dev_t *dev)
{
AD7793_CHECK_INITIALIZED(dev);
ad7793_set_mode(dev, AD7793_MODE_SYSTEM_ZERO);
dev->hw_if->delay_ms(10); /* Wait for calibration to complete */
if (!ad7793_wait_ready(dev, 1000))
{
DEBUG_PRINTF("System zero calibration timeout\n");
return false;
}
return ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
}
@ -681,7 +776,11 @@ bool ad7793_calibrate_system_full(ad7793_dev_t *dev)
{
AD7793_CHECK_INITIALIZED(dev);
ad7793_set_mode(dev, AD7793_MODE_SYSTEM_FULL);
dev->hw_if->delay_ms(10); /* Wait for calibration to complete */
if (!ad7793_wait_ready(dev, 1000))
{
DEBUG_PRINTF("System full calibration timeout\n");
return false;
}
return ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
}
@ -697,36 +796,27 @@ bool ad7793_calibrate_system_full(ad7793_dev_t *dev)
bool ad7793_config_iexc(ad7793_dev_t *dev, uint16_t current, uint8_t dir)
{
AD7793_CHECK_INITIALIZED(dev);
uint32_t io_reg = 0;
/* Set current value */
if (current == 10)
/* 根据数据手册IO 寄存器位: [3:2]=IEXCDIR1:0, [1:0]=IEXCEN1:0
IEXCEN 00=, 01=10uA, 10=210uA, 11=1mA (, ) */
uint8_t en_bits;
switch (current)
{
io_reg |= 0x01;
case 10:
en_bits = 0x01;
break;
case 210:
en_bits = 0x02;
break;
case 1000:
en_bits = 0x03;
break;
default:
return false; /* unsupported */
}
else if (current == 210)
{
io_reg |= 0x02;
}
else if (current == 1000)
{
io_reg |= 0x03;
}
else
{
return false;
}
/* Set current direction */
if (dir)
{
io_reg |= (0x03 << 2);
}
/* Enable current source */
io_reg |= (0x03 << 0);
return ad7793_write_reg(dev, REG_IO, io_reg, 1);
uint8_t dir_bits = (dir & 0x03) << 2; /* 方向/复用: 00=IOUT1->AIN1+, 01=IOUT1&2? 需依据原理图调整 */
uint8_t io_val = (uint8_t)(dir_bits | en_bits);
DEBUG_PRINTF("[io-w] set IEXC current=%u uA dir=0x%X val=0x%02X\n", (unsigned)current, (unsigned)dir, io_val);
return ad7793_write_reg(dev, REG_IO, io_val, 1);
}
/**
@ -784,6 +874,14 @@ bool ad7793_set_buffered(ad7793_dev_t *dev, bool enable)
// Read current configuration register value
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
// 缓冲/增益策略规范化:若当前增益>2且要关闭缓冲禁止
uint8_t gain = dev->cur_gain;
if (!enable && gain > 2)
{
DEBUG_PRINTF("[ERR] Attempt to disable buffer while gain >2!\n");
return false;
}
// Update BUF bit (bit 7 of the configuration register)
if (enable)
{
@ -813,71 +911,110 @@ bool ad7793_set_buffered(ad7793_dev_t *dev, bool enable)
* It resets the device, waits for the reset to complete, and then reads the ID register to confirm the device type.
* Finally, it initializes the device with default configuration settings.
*/
bool ad7793_init(ad7793_dev_t *dev, ad7793_hw_if_t *hw_if, uint8_t cs_pin)
bool ad7793_init(ad7793_dev_t *dev, ad7793_hw_if_t *hw_if, uint8_t cs_pin, const ad7793_config_t *cfg)
{
uint32_t mode_reg = 0;
uint32_t config_reg = 0;
if (hw_if == NULL)
if (!dev || !hw_if)
{
DEBUG_PRINTF("hw_if is null\n");
DEBUG_PRINTF("NULL param in init\n");
return false;
}
/* Save hardware interface and chip select info */
dev->hw_if = hw_if;
dev->cs_pin = cs_pin;
dev->is_ad7793 = true; /* Default is AD7793, can be confirmed by ID register */
dev->is_initialized = true;
dev->is_ad7793 = true; /* assume, verify after reset */
dev->err_count = dev->err_recoveries = dev->low_code_drops = 0;
dev->last_status = 0;
/* Reset device */
/* Pick configuration (simple & predictable) */
ad7793_config_t local_cfg = cfg ? *cfg : AD7793_DEFAULT_CONFIG;
/* Reset & small settle */
ad7793_reset(dev);
dev->hw_if->delay_ms(2);
/* Wait for reset to complete */
dev->hw_if->delay_ms(1);
/* Read ID register to confirm device type */
/* Read ID */
uint32_t id = 0;
ad7793_read_reg(dev, REG_ID, &id, 1);
DEBUG_PRINTF("ID: 0x%02X\n", id);
dev->is_ad7793 = ((id & 0x0F) == 0x0B); /* AD7793 ID read out is 0x4B */
dev->is_ad7793 = ((id & 0x0F) == 0x0B);
DEBUG_PRINTF("ID: 0x%02X (%s)\n", id, dev->is_ad7793 ? "AD7793" : "Unknown");
/* Initialize default configuration */
ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
ad7793_wait_ready(dev, 1000);
ad7793_read_reg(dev, REG_MODE, &mode_reg, 2);
DEBUG_PRINTF("[mode-r]: 0x%04X\n", mode_reg);
/* Put into continuous mode first */
if (!ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS))
return false;
/* Apply reference before gain (gain affects allowed input span vs ref) */
if (!ad7793_set_reference(dev, local_cfg.use_internal_ref, local_cfg.external_ref))
return false;
if (!ad7793_set_gain(dev, local_cfg.gain))
return false;
if (!ad7793_set_channel(dev, local_cfg.channel))
return false;
if (!ad7793_set_rate(dev, local_cfg.rate))
return false;
if (!ad7793_set_unipolar(dev, local_cfg.unipolar_mode))
return false;
if (!ad7793_set_buffered(dev, local_cfg.buffered))
return false;
ad7793_set_gain(dev, AD7793_GAIN_1);
ad7793_wait_ready(dev, 1000);
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
DEBUG_PRINTF("[config-r]after gain config: 0x%04X\n", config_reg);
/* Optional calibration */
if (local_cfg.calibrate_system_zero)
{
if (!ad7793_calibrate_system_zero(dev))
DEBUG_PRINTF("System zero calibration skipped/failed\n");
}
ad7793_set_channel(dev, AD7793_CHANNEL_1);
ad7793_wait_ready(dev, 1000);
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
DEBUG_PRINTF("[config-r]after channel config: 0x%04X\n", config_reg);
ad7793_set_rate(dev, AD7793_RATE_8_33HZ);
ad7793_wait_ready(dev, 1000);
ad7793_read_reg(dev, REG_MODE, &mode_reg, 2);
DEBUG_PRINTF("[mode-r]after rate mode: 0x%04X\n", mode_reg);
ad7793_set_reference(dev, true, 2.5); /* Use internal reference */
ad7793_wait_ready(dev, 1000);
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
DEBUG_PRINTF("[config-r]after reference config: 0x%04X\n", config_reg);
ad7793_set_unipolar(dev, true); /* Default to bipolar mode */
ad7793_wait_ready(dev, 1000);
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
DEBUG_PRINTF("[config-r]after polar config: 0x%04X\n", config_reg);
ad7793_set_buffered(dev, false); /* Enable input buffer */
ad7793_wait_ready(dev, 1000);
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
DEBUG_PRINTF("final read config: 0x%04X\n", config_reg);
ad7793_calibrate_system_zero(dev);
/* Quick sanity read (no verbose decode) */
uint32_t cfg_rd = 0;
ad7793_read_reg(dev, REG_CONFIG, &cfg_rd, 2);
DEBUG_PRINTF("CONFIG=0x%04X\n", cfg_rd);
/* 配置激励电流(可根据实际传感器回路调整) */
(void)ad7793_config_iexc(dev, 210, 0);
/* 初次 dump offset/fullscale 辅助调试 */
ad7793_dump_registers(dev);
return true;
}
/* Attempt a soft recovery when persistent ERR occurs.
* Strategy: reset, force internal reference, re-apply basic config, optional system zero cal. */
bool ad7793_recover(ad7793_dev_t *dev, const ad7793_config_t *base_cfg)
{
if (!dev || !dev->is_initialized)
return false;
ad7793_config_t cfg = base_cfg ? *base_cfg : AD7793_DEFAULT_CONFIG;
cfg.use_internal_ref = true; /* fallback always internal */
if (!ad7793_reset(dev))
return false;
dev->hw_if->delay_ms(2);
/* re-id */
uint32_t id = 0;
ad7793_read_reg(dev, REG_ID, &id, 1);
dev->is_ad7793 = ((id & 0x0F) == 0x0B);
if (!ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS))
return false;
if (!ad7793_set_reference(dev, true, 0.0f))
return false;
if (!ad7793_set_gain(dev, cfg.gain))
return false;
if (!ad7793_set_channel(dev, cfg.channel))
return false;
if (!ad7793_set_rate(dev, cfg.rate))
return false;
if (!ad7793_set_unipolar(dev, cfg.unipolar_mode))
return false;
#ifndef AD7793_DEFAULT_CONFIG
#define AD7793_DEFAULT_CONFIG \
{ \
.use_internal_ref = true, .external_ref = 1.17f, .gain = AD7793_GAIN_1, .channel = AD7793_CHANNEL_1, \
.rate = AD7793_RATE_8_33HZ, .buffered = false, .unipolar_mode = true, .calibrate_system_zero = false \
}
#endif
if (!ad7793_set_buffered(dev, cfg.buffered))
return false;
if (cfg.calibrate_system_zero)
(void)ad7793_calibrate_system_zero(dev);
dev->err_recoveries++;
DEBUG_PRINTF("Recovery done (recoveries=%lu)\n", (unsigned long)dev->err_recoveries);
return true;
}

View File

@ -123,6 +123,29 @@ typedef enum
AD7793_RATE_470HZ = 0x1 /**< 470Hz */
} ad7793_rate_t;
/* Configuration structure for AD7793 */
typedef struct
{
bool use_internal_ref; /**< true: use internal reference, false: use external reference */
float external_ref; /**< External reference voltage (V), used if use_internal_ref is false */
ad7793_gain_t gain; /**< Gain setting */
ad7793_channel_t channel; /**< Channel selection */
ad7793_rate_t rate; /**< Output data rate */
bool buffered; /**< true: enable input buffer, false: disable buffer */
bool unipolar_mode; /**< true: unipolar mode, false: bipolar mode */
bool calibrate_system_zero; /**< true: perform system zero calibration at startup, false: skip calibration */
} ad7793_config_t;
/* Default config as a compound literal so it can be used in expressions */
#ifndef AD7793_DEFAULT_CONFIG
#define AD7793_DEFAULT_CONFIG \
(ad7793_config_t) \
{ \
.use_internal_ref = true, .external_ref = 1.17f, .gain = AD7793_GAIN_1, .channel = AD7793_CHANNEL_1, \
.rate = AD7793_RATE_8_33HZ, .buffered = false, .unipolar_mode = true, .calibrate_system_zero = false \
}
#endif
/* Device structure */
typedef struct
{
@ -138,9 +161,15 @@ typedef struct
bool unipolar_mode; /**< True if unipolar mode, false if bipolar mode */
bool buffered; /**< True if buffer enable, false if buffer unused */
bool is_initialized; /**< True if the device has been initialized */
/* --- Runtime diagnostics --- */
uint32_t err_count; /**< Total STATUS.ERR (bit6) occurrences */
uint32_t err_recoveries; /**< Number of automatic recovery attempts */
uint32_t low_code_drops; /**< Dropped samples due to low code threshold */
uint32_t last_status; /**< Last read STATUS register */
} ad7793_dev_t;
#define AD7793_CHECK_INITIALIZED(dev) \
do { \
do \
{ \
if (!dev->is_initialized) \
{ \
DEBUG_PRINTF("Device not initialized!\n"); \
@ -148,6 +177,27 @@ typedef struct
} \
} while (0)
/*
* AD7793 Driver Header
*
* Recommended Initialization Flow:
* 1. Configure the ad7793_hw_if_t hardware interface struct, filling in SPI/GPIO/delay function pointers.
* 2. Define the ad7793_config_t configuration struct, specifying reference source, gain, channel, rate, buffer, etc.
* 3. Call ad7793_init(&dev, &hw_if, cs_pin, &cfg) to initialize the device.
* 4. Before data acquisition, call ad7793_wait_ready to wait for data ready.
* 5. Use ad7793_read_data to acquire data, and ad7793_convert_to_voltage to convert to voltage.
* 6. If you need to switch channel/gain/reference, it is recommended to set idle mode first, then switch parameters, and finally restore continuous mode.
*
* Common Troubleshooting:
* - STATUS=0x48/0xC8: Usually caused by reference source mismatch, buffer/gain conflict, or IEXC not configured as required by hardware.
* - All readings zero or extremely large: Check reference voltage, SPI wiring, channel/gain configuration.
* - Register write failure: Check SPI speed, CS timing, hardware interface implementation.
* - Temperature jumps/spikes: Application layer should implement temperature jump/low code filtering.
* - Abnormal sampling rate: Confirm rate configuration matches master clock and SPI speed.
*
* See API comments for typical usage.
*/
/**
* @brief Write data to a specified register of the AD7793 device.
* @param dev Device structure pointer.
@ -158,6 +208,7 @@ typedef struct
* @detail This function constructs a communication command to write data to the specified register.
* It first clears the chip select pin, then performs an SPI transfer to send the command and data.
* Finally, it sets the chip select pin back to high.
* @note Boundary: Returns false if dev is not initialized or parameters are invalid. Example: ad7793_write_reg(&dev, REG_MODE, 0x200A, 2);
*/
bool ad7793_write_reg(ad7793_dev_t *dev, uint8_t reg, uint32_t data, uint16_t len);
@ -171,6 +222,7 @@ bool ad7793_write_reg(ad7793_dev_t *dev, uint8_t reg, uint32_t data, uint16_t le
* @detail This function constructs a communication command to read data from the specified register.
* It first clears the chip select pin, then performs an SPI transfer to receive the data.
* Finally, it sets the chip select pin back to high.
* @note Boundary: Returns false if dev is not initialized or parameters are invalid. Example: ad7793_read_reg(&dev, REG_CONFIG, &val, 2);
*/
bool ad7793_read_reg(ad7793_dev_t *dev, uint8_t reg, uint32_t *data, uint16_t len);
@ -181,6 +233,7 @@ bool ad7793_read_reg(ad7793_dev_t *dev, uint8_t reg, uint32_t *data, uint16_t le
* @detail This function resets the device by writing 32 ones to the device.
* It clears the chip select pin, sends the reset sequence via SPI, and then sets the chip select pin back to high.
* After that, it waits for 1 millisecond to ensure the reset is completed.
* @note Example: ad7793_reset(&dev);
*/
bool ad7793_reset(ad7793_dev_t *dev);
@ -191,6 +244,7 @@ bool ad7793_reset(ad7793_dev_t *dev);
* @return Operation status. True if successful, false otherwise.
* @detail This function reads the current mode register, clears the mode bits, and then sets the new mode according to the input.
* It updates the current mode in the device structure and writes the new mode to the mode register.
* @note Example: ad7793_set_mode(&dev, AD7793_MODE_CONTINUOUS);
*/
bool ad7793_set_mode(ad7793_dev_t *dev, ad7793_mode_t mode);
@ -201,6 +255,7 @@ bool ad7793_set_mode(ad7793_dev_t *dev, ad7793_mode_t mode);
* @return Operation status. True if successful, false otherwise.
* @detail This function reads the current configuration register, clears the gain bits, and then sets the new gain according to the input.
* It updates the current gain in the device structure and writes the new configuration to the configuration register.
* @note Boundary: Returns false if BUF=0 and G>2. Example: ad7793_set_gain(&dev, AD7793_GAIN_4);
*/
bool ad7793_set_gain(ad7793_dev_t *dev, ad7793_gain_t gain);
@ -211,6 +266,7 @@ bool ad7793_set_gain(ad7793_dev_t *dev, ad7793_gain_t gain);
* @return Operation status. True if successful, false otherwise.
* @detail This function reads the current configuration register, clears the channel bits, and then sets the new channel according to the input.
* It updates the current channel in the device structure and writes the new configuration to the configuration register.
* @note Example: ad7793_set_channel(&dev, AD7793_CHANNEL_1);
*/
bool ad7793_set_channel(ad7793_dev_t *dev, ad7793_channel_t channel);
@ -221,6 +277,7 @@ bool ad7793_set_channel(ad7793_dev_t *dev, ad7793_channel_t channel);
* @return Operation status. True if successful, false otherwise.
* @detail This function reads the current mode register, clears the rate bits, and then sets the new rate according to the input.
* It updates the current update rate in the device structure and writes the new mode to the mode register.
* @note Example: ad7793_set_rate(&dev, AD7793_RATE_8_33HZ);
*/
bool ad7793_set_rate(ad7793_dev_t *dev, ad7793_rate_t rate);
@ -232,6 +289,7 @@ bool ad7793_set_rate(ad7793_dev_t *dev, ad7793_rate_t rate);
* @return Operation status. True if successful, false otherwise.
* @detail This function reads the current configuration register and sets the reference select bit according to the input.
* It updates the reference source information in the device structure and writes the new configuration to the configuration register.
* @note Example: ad7793_set_reference(&dev, false, 2.5f);
*/
bool ad7793_set_reference(ad7793_dev_t *dev, bool use_internal, float external_ref);
@ -242,6 +300,7 @@ bool ad7793_set_reference(ad7793_dev_t *dev, bool use_internal, float external_r
* @return true=ready, false=timeout.
* @detail This function continuously checks the GPIO pin status until the device is ready or the timeout occurs.
* It waits for 1 millisecond between each check.
* @note Example: ad7793_wait_ready(&dev, 1000);
*/
bool ad7793_wait_ready(ad7793_dev_t *dev, uint16_t timeout_ms);
@ -252,6 +311,7 @@ bool ad7793_wait_ready(ad7793_dev_t *dev, uint16_t timeout_ms);
* @return Operation status. True if successful, false otherwise.
* @detail This function reads the conversion data from the data register.
* If the device is AD7793, it reads 3 bytes; if it is AD7792, it reads 2 bytes.
* @note Example: ad7793_read_data(&dev, &val);
*/
bool ad7793_read_data(ad7793_dev_t *dev, uint32_t *value);
@ -268,6 +328,7 @@ bool ad7793_read_data(ad7793_dev_t *dev, uint32_t *value);
* Vref is the reference voltage.
* N is the number of ADC bits (24 bits for AD7793).
* G is the gain setting.
* @note Example: float v=ad7793_convert_to_voltage(&dev, code);
*/
float ad7793_convert_to_voltage(ad7793_dev_t *dev, uint32_t raw_data);
@ -276,45 +337,10 @@ float ad7793_convert_to_voltage(ad7793_dev_t *dev, uint32_t raw_data);
* @param dev Pointer to the AD7793 device structure.
* @param voltage Input voltage value (V).
* @return Corresponding ADC code value.
* @note Example: uint32_t code=ad7793_convert_voltage_to_code(&dev, v);
*/
uint32_t ad7793_convert_voltage_to_code(ad7793_dev_t *dev, float voltage);
/**
* @brief Perform internal zero-scale calibration on the AD7793 device.
* @param dev Device structure pointer.
* @return Operation status. True if successful, false otherwise.
* @detail This function sets the device to internal zero-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
* and then sets the device back to continuous conversion mode.
*/
bool ad7793_calibrate_internal_zero(ad7793_dev_t *dev);
/**
* @brief Perform internal full-scale calibration on the AD7793 device.
* @param dev Device structure pointer.
* @return Operation status. True if successful, false otherwise.
* @detail This function sets the device to internal full-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
* and then sets the device back to continuous conversion mode.
*/
bool ad7793_calibrate_internal_full(ad7793_dev_t *dev);
/**
* @brief Perform system zero-scale calibration on the AD7793 device.
* @param dev Device structure pointer.
* @return Operation status. True if successful, false otherwise.
* @detail This function sets the device to system zero-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
* and then sets the device back to continuous conversion mode.
*/
bool ad7793_calibrate_system_zero(ad7793_dev_t *dev);
/**
* @brief Perform system full-scale calibration on the AD7793 device.
* @param dev Device structure pointer.
* @return Operation status. True if successful, false otherwise.
* @detail This function sets the device to system full-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
* and then sets the device back to continuous conversion mode.
*/
bool ad7793_calibrate_system_full(ad7793_dev_t *dev);
/**
* @brief Configure the excitation current source of the AD7793 device.
* @param dev Device structure pointer.
@ -323,6 +349,7 @@ bool ad7793_calibrate_system_full(ad7793_dev_t *dev);
* @return Operation status. True if successful, false otherwise.
* @detail This function configures the excitation current source by setting the current value and direction in the IO register.
* It then enables the current source and writes the configuration to the IO register.
* @note Example: ad7793_config_iexc(&dev, 210, 0);
*/
bool ad7793_config_iexc(ad7793_dev_t *dev, uint16_t current, uint8_t dir);
@ -338,6 +365,7 @@ bool ad7793_config_iexc(ad7793_dev_t *dev, uint16_t current, uint8_t dir);
* - Bipolar mode (U/B=0): Converts -VREF/2 to +VREF/2 into 0x000000 to 0xFFFFFF
*
* Note: Changing this bit affects the interpretation of the ADC output code.
* @note Example: ad7793_set_unipolar(&dev, true);
*/
bool ad7793_set_unipolar(ad7793_dev_t *dev, bool unipolar);
@ -349,6 +377,7 @@ bool ad7793_set_unipolar(ad7793_dev_t *dev, bool unipolar);
* @detail This function configures the input buffer mode of AD7793.
* Enabling the buffer reduces input impedance requirements but increases power consumption.
* Disabling the buffer is suitable for high-impedance sources but requires attention to input signal range limitations.
* @note Boundary: Returns false if G>2 and buffer is disabled. Example: ad7793_set_buffered(&dev, true);
*/
bool ad7793_set_buffered(ad7793_dev_t *dev, bool enable);
@ -357,11 +386,22 @@ bool ad7793_set_buffered(ad7793_dev_t *dev, bool enable);
* @param dev Device structure pointer.
* @param hw_if Hardware interface structure containing function pointers.
* @param cs_pin Chip select pin.
* @param cfg Configuration struct
* @return Operation status. True if successful, false otherwise.
* @detail This function initializes the AD7793 device by saving the hardware interface and chip select information.
* It resets the device, waits for the reset to complete, and then reads the ID register to confirm the device type.
* Finally, it initializes the device with default configuration settings.
* @note Example: ad7793_init(&dev, &hw_if, cs_pin, &cfg);
*/
bool ad7793_init(ad7793_dev_t *dev, ad7793_hw_if_t *hw_if, uint8_t cs_pin);
bool ad7793_init(ad7793_dev_t *dev, ad7793_hw_if_t *hw_if, uint8_t cs_pin, const ad7793_config_t *cfg);
/**
* @brief Dump the contents of the AD7793 registers for debugging.
* @param dev Device structure pointer.
* @detail This function reads and prints the contents of the AD7793 registers.
* It can be used to verify the register settings and diagnose issues.
*/
void ad7793_dump_registers(ad7793_dev_t *dev);
void ad7793_dump_registers_mode(ad7793_dev_t *dev, int full);
#endif // __AD7793_H__

View File

@ -2,7 +2,7 @@
* @Date: 2025-06-23 09:02:52
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-09-25 16:12:45
* @LastEditTime: 2025-10-27 14:18:40
* @FilePath: user_config.h
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
@ -20,27 +20,26 @@
#define USING_SOFT_ENCODER 0
#define ENABLE_SV630P 1
#define ENABLE_TEC 1
#define TEST_ENCODER_PLUSE 0
#define DEBUG_SERVO_ENABLE 0
#define DEBUG_MB_ENABLE 0
#define DEBUG_LIGHT_DATA_ONLY 0
#define ENABLE_IIR_FILTER 0
#define SAMPLING_RAW_DATA 0
#define DATA_PROCESS_UNIT_COUNT 1000
#define MOTOR_TO_ENCODER_FACTOR 4
#define MOTOR_TO_ENCODER_RATIO (20 * MOTOR_TO_ENCODER_FACTOR) //80 // 电机转速与编码器转速的比率
#define MOTOR_TO_ENCODER_FACTOR 1
#define MOTOR_TO_ENCODER_RATIO (80 * MOTOR_TO_ENCODER_FACTOR) //80 // 电机转速与编码器转速的比率
#define ENCODER_TO_POLARIZER_RATIO 180
#define MOTOR_TO_POLARIZER_RATIO (MOTOR_TO_ENCODER_RATIO * ENCODER_TO_POLARIZER_RATIO) // 电机转速与偏振片速的比率
#define CALIB_OSCI_ANGLE 180.0f
#define DC_OFFSET_ADC_VALUE 1990
#define DC_OFFSET_ADC_VALUE 2043.48 //1990 //
#define ENABLE_CALIB_DOUBLE_DIR 1
#define ENABLE_CALIB_DOUBLE_DIR 0
#define CALIB_OFFSET 0.0f //4.995f
#define CALIB_ONE_DIR 1 // -1: 逆时针, 1: 顺时针
@ -50,7 +49,9 @@
#define TREND_BUFFER_SIZE 10
#define TEC_TEMPER_PRECISE 0.1f
// speed设置为正数编码器正向增加则系数为1 反之为-1
#define ENCODER_MOTOR_DIR_FACTOR 1 // -1 or 1
#endif /* __USER_CONFIG_H__ */

View File

@ -116,6 +116,15 @@ add_library(stm32cubemx INTERFACE)
target_include_directories(stm32cubemx INTERFACE ${MX_Include_Dirs})
target_compile_definitions(stm32cubemx INTERFACE ${MX_Defines_Syms})
# Propagate suppression of a few warnings (for GCC) to all consumers of stm32cubemx
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(stm32cubemx INTERFACE
"-Wno-pedantic"
"-Wno-cast-function-type"
"-Wno-implicit-fallthrough"
)
endif()
# Create STM32_Drivers static library
add_library(STM32_Drivers OBJECT)
target_sources(STM32_Drivers PRIVATE ${STM32_Drivers_Src})
@ -127,6 +136,15 @@ add_library(RT-Thread OBJECT)
target_sources(RT-Thread PRIVATE ${RT-Thread_Src})
target_link_libraries(RT-Thread PUBLIC stm32cubemx)
# For GCC builds, silence a few third-party warnings coming from RT-Thread
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(RT-Thread PRIVATE
"-Wno-pedantic"
"-Wno-cast-function-type"
"-Wno-implicit-fallthrough"
)
endif()
# Add STM32CubeMX generated application sources to the project
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${MX_Application_Src})

BIN
docs/hardware/pm_ver3.pdf Normal file

Binary file not shown.

BIN
docs/holding_registers.mbs Normal file

Binary file not shown.

BIN
docs/input_registers.mbs Normal file

Binary file not shown.

BIN
docs/pm.msw Normal file

Binary file not shown.

View File

@ -1,67 +0,0 @@
#ifndef __ETK_DIFF_H__
#define __ETK_DIFF_H__
#include "etk_utils.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Compute first derivative (dy) with uniform spacing h
*
* This function calculates the first derivative of a function represented by discrete
* values y[0..n-1] with uniform spacing h between points. The function uses different
* approximation methods for endpoints and interior points to achieve O(h^2) accuracy.
*
* @param y Input array of function values, must have at least 2 elements
* @param n Number of elements in array y
* @param h Uniform spacing between points, must be non-zero and finite. The unit of h
* should match the unit of your data's x-axis (e.g., seconds for time-based data).
* The derivative unit will be "y-unit per h-unit".
* @param dy Output array for first derivative values, must have same size as y
* @return true if calculation was successful, false if parameters were invalid or results contained NaN/infinity
*/
bool etk_diff1(const double *y, uint32_t n, double h, double *dy);
/**
* @brief Compute second derivative (d2y) with uniform spacing h
*
* This function calculates the second derivative of a function represented by discrete
* values y[0..n-1] with uniform spacing h between points. The function uses different
* approximation methods for endpoints and interior points to achieve O(h^2) accuracy.
*
* @param y Input array of function values, must have at least 3 elements
* @param n Number of elements in array y
* @param h Uniform spacing between points, must be non-zero and finite. The unit of h
* should match the unit of your data's x-axis (e.g., seconds for time-based data).
* The derivative unit will be "y-unit per h-unit".
* @param d2y Output array for second derivative values, must have same size as y
* @return true if calculation was successful, false if parameters were invalid or results contained NaN/infinity
*/
bool etk_diff2(const double *y, uint32_t n, double h, double *d2y);
/**
* @brief Compute both first and second derivatives in one pass
*
* This function calculates both the first and second derivatives of a function
* represented by discrete values. Computing both derivatives in one function
* can be slightly more efficient due to better cache utilization.
*
* @param y Input array of function values, must have at least 2 elements
* @param n Number of elements in array y
* @param h Uniform spacing between points, must be non-zero and finite. The unit of h
* should match the unit of your data's x-axis (e.g., seconds for time-based data).
* The derivative unit will be "y-unit per h-unit".
* @param dy Output array for first derivative values, can be NULL if not needed
* @param d2y Output array for second derivative values, can be NULL if not needed
* @return true if calculation was successful, false if parameters were invalid or results contained NaN/infinity
*/
bool etk_diff12(const double *y, uint32_t n, double h, double *dy, double *d2y);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,296 +0,0 @@
#include "et_half-wave_analysis.h"
#define DEBUG 0
#if DEBUG
#define DEBUG_PRINT(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif
/**
* @brief Analyze a single half-cycle segment
* @param cycle Pointer to et_half_cycle_t structure to store analysis results
* @param point Pointer to input et_zc_point_t array
* @param offset DC offset to subtract
* @param start_idx Starting index of the segment
* @param end_idx Ending index of the segment
* @param is_first_half Flag indicating if this is the first half-cycle (1) or second (0)
*/
static void analyze_half_cycle(et_half_cycle_t *cycle, const et_zc_point_t *point, uint16_t offset, uint32_t start_idx,
uint32_t end_idx, int is_first_half)
{
// Find peak in this segment
float peak_val = -FLT_MAX;
uint32_t peak_idx = start_idx;
// Also track the minimum value to determine if this is a positive or negative half-cycle
float min_val = FLT_MAX;
uint32_t min_idx = start_idx;
float segment_sum = 0.0f;
uint32_t segment_count = 0;
for (uint32_t j = start_idx; j < end_idx; j++)
{
float val = (float)point[j].val - offset;
segment_sum += val;
segment_count++;
if (val > peak_val)
{
peak_val = val;
peak_idx = j;
}
if (val < min_val)
{
min_val = val;
min_idx = j;
}
}
// Determine if this is a positive or negative half-cycle based on which extreme is further from zero
int is_positive = (fabs(peak_val) >= fabs(min_val));
float extreme_val = is_positive ? peak_val : min_val;
uint32_t extreme_idx = is_positive ? peak_idx : min_idx;
// Store the absolute value of the extreme (peak or trough) - compute once
float abs_extreme_val = fabs(extreme_val);
// Update overall cycle data
if (is_first_half)
{
cycle->pos_cycle_mean = segment_sum / segment_count;
cycle->pos_sample_cnt = segment_count;
if (abs_extreme_val > cycle->pos_cycle_peak)
{
cycle->pos_cycle_peak = abs_extreme_val;
cycle->pos_peak_idx = extreme_idx;
DEBUG_PRINT("New positive peak: %f\n", cycle->pos_cycle_peak);
}
}
else
{
cycle->neg_cycle_mean = segment_sum / segment_count;
cycle->neg_sample_cnt = segment_count;
if (abs_extreme_val > cycle->neg_cycle_peak)
{
cycle->neg_cycle_peak = abs_extreme_val;
cycle->neg_peak_idx = extreme_idx;
DEBUG_PRINT("New negative peak: %f\n", cycle->neg_cycle_peak);
}
}
}
/**
* @brief Create and initialize a half-wave analysis structure
* @param handle Pointer to et_half_cycle_analysis_t structure
* @param full_cycle_count Maximum number of full cycles to analyze
* @param average_count Number of full cycles to use for calculating average metrics
* @return int 0 on success, -1 on failure
*/
int et_half_wave_analysis_create(et_half_cycle_analysis_t *handle, uint32_t full_cycle_count, uint32_t average_count)
{
if (handle == NULL)
return -1;
// Each full cycle has three zero crossing points (start, middle, end)
uint32_t max_pos = 3 * full_cycle_count - 1;
// Initialize all fields to zero
handle->full_cycle_max_size = full_cycle_count;
handle->average_count = average_count;
handle->mean_diff = 0.0f;
handle->mean_ratio = 0.0f;
handle->peak_ratio = 0.0f;
handle->peak_diff = 0.0f;
handle->width_ratio = 0.0f;
handle->width_diff = 0.0f;
handle->half_cycle = (et_half_cycle_t *)malloc(sizeof(et_half_cycle_t) * full_cycle_count);
if (handle->half_cycle == NULL)
return -1;
if (et_zero_cross_create(&handle->zc, max_pos) != 0)
{
free(handle->half_cycle);
return -1;
}
return 0;
}
/**
* @brief Destroy a half-wave analysis structure and free allocated memory
* @param handle Pointer to et_half_cycle_analysis_t structure to destroy
*/
void et_half_wave_analysis_destroy(et_half_cycle_analysis_t *handle)
{
if (handle != NULL)
{
if (handle->half_cycle)
free(handle->half_cycle);
et_zero_cross_destroy(&handle->zc);
}
}
/**
* @brief Analyze half-wave characteristics from zero crossing points
* @param handle Pointer to et_half_cycle_analysis_t structure for storing results
* @param point Pointer to input et_zc_point_t array
* @param length Number of samples
* @param offset DC offset to subtract
* @return int 0 on success, -1 on failure, -2 if insufficient zero crossings
* @note The calculation results will not be queued. New calculation results will clear the previous results.
* To maintain averaging over average_count cycles, ensure sufficient points are provided.
*/
int et_half_cycle_analyze_point(et_half_cycle_analysis_t *handle, const et_zc_point_t *point, uint32_t length,
uint16_t offset)
{
if (handle == NULL || point == NULL || length < 2)
return -1;
// Reset analysis results
memset(handle->half_cycle, 0, sizeof(et_half_cycle_t) * handle->full_cycle_max_size);
handle->mean_diff = 0.0f;
handle->mean_ratio = 0.0f;
handle->peak_ratio = 0.0f;
handle->peak_diff = 0.0f;
handle->width_ratio = 0.0f;
handle->width_diff = 0.0f;
// Get zero crossing positions using existing function
uint32_t zc_count = et_zero_cross_pos_point(&handle->zc, point, length, offset);
// Analyze consecutive half-cycle pairs
// Need at least 3 zero crossing points to define a complete cycle
if (zc_count < 3)
return -2;
uint32_t full_cycle_count = 0;
for (uint32_t i = 0; i <= zc_count - 3; i++)
{
// Only analyze the first pair of consecutive half-cycles (A~B and B~C)
// First half-cycle (from A to B)
uint32_t first_start = (uint32_t)handle->zc.pos_buf[i];
uint32_t first_end = (uint32_t)handle->zc.pos_buf[i + 1]; // First zero crossing (point B)
// Second half-cycle (from B to C)
uint32_t second_start = (uint32_t)handle->zc.pos_buf[i + 1]; // First zero crossing (point B)
uint32_t second_end = (uint32_t)handle->zc.pos_buf[i + 2]; // Second zero crossing (point C)
// Analyze the first half-cycle (A to B)
analyze_half_cycle(&handle->half_cycle[full_cycle_count], point, offset, first_start, first_end, 1);
// Analyze the second half-cycle (B to C)
analyze_half_cycle(&handle->half_cycle[full_cycle_count], point, offset, second_start, second_end, 0);
full_cycle_count++;
}
uint32_t min_cnt = handle->average_count > full_cycle_count ? full_cycle_count : handle->average_count;
float pos_mean_total = 0.0f;
float neg_mean_total = 0.0f;
float pos_peak_total = 0.0f;
float neg_peak_total = 0.0f;
float pos_sample_total = 0.0f;
float neg_sample_total = 0.0f;
// Calculate totals for averaging
for (uint32_t i = 0; i < min_cnt; i++)
{
handle->mean_diff += (handle->half_cycle[i].pos_cycle_mean - handle->half_cycle[i].neg_cycle_mean);
handle->peak_diff += (handle->half_cycle[i].pos_cycle_peak - handle->half_cycle[i].neg_cycle_peak);
handle->width_diff += (handle->half_cycle[i].pos_sample_cnt - handle->half_cycle[i].neg_sample_cnt);
pos_sample_total += handle->half_cycle[i].pos_sample_cnt;
neg_sample_total += handle->half_cycle[i].neg_sample_cnt;
pos_peak_total += handle->half_cycle[i].pos_cycle_peak;
neg_peak_total += handle->half_cycle[i].neg_cycle_peak;
pos_mean_total += handle->half_cycle[i].pos_cycle_mean;
neg_mean_total += handle->half_cycle[i].neg_cycle_mean;
}
// Calculate ratios with division by zero protection
if (fabsf(neg_peak_total) < FLT_EPSILON)
handle->peak_ratio = 0.0f;
else
handle->peak_ratio = pos_peak_total / neg_peak_total;
if (fabsf(neg_mean_total) < FLT_EPSILON)
handle->mean_ratio = 0.0f;
else
handle->mean_ratio = pos_mean_total / neg_mean_total;
if (fabsf(neg_sample_total) < FLT_EPSILON)
handle->width_ratio = 0.0f;
else
handle->width_ratio = pos_sample_total / neg_sample_total;
return 0;
}
/**
* @brief Calculate the difference between positive and negative half-cycle peak values
* @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
* @return float Difference between positive and negative half-cycle peak values
*/
float et_half_wave_peak_diff(const et_half_cycle_analysis_t *handle)
{
if (handle == NULL)
return 0.0f;
return handle->peak_diff;
}
/**
* @brief Calculate the ratio between positive and negative half-cycle peak values
* @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
* @return float Ratio between positive and negative half-cycle peak values
*/
float et_half_wave_peak_ratio(const et_half_cycle_analysis_t *handle)
{
if (handle == NULL)
return 0.0f;
return handle->peak_ratio;
}
/**
* @brief Calculate the ratio between positive and negative half-cycle mean values
* @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
* @return float Ratio between positive and negative half-cycle mean values
*/
float et_half_wave_mean_ratio(const et_half_cycle_analysis_t *handle)
{
if (handle == NULL)
return 0.0f;
return handle->mean_ratio;
}
/**
* @brief Calculate the difference between positive and negative half-cycle mean values
* @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
* @return float Difference between positive and negative half-cycle mean values
*/
float et_half_wave_mean_diff(const et_half_cycle_analysis_t *handle)
{
if (handle == NULL)
return 0.0f;
return handle->mean_diff;
}
/**
* @brief Calculate the ratio of sample counts between positive and negative half-cycles
* @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
* @return float Ratio of positive to negative half-cycle sample counts
*/
float et_half_wave_sample_count_ratio(const et_half_cycle_analysis_t *handle)
{
if (handle == NULL)
return 0.0f;
return handle->width_ratio;
}

View File

@ -1,255 +0,0 @@
#include "etk_diff.h"
#include <math.h>
#include <stdbool.h>
/**
* @brief Macro to check if a value is finite and return false if not
*/
#define CHECK_FINITE(val) do { \
if (!is_finite(val)) return false; \
} while(0)
/**
* @brief Helper function to check if a value is NaN
*
* @param x The value to check
* @return true if x is NaN, false otherwise
*/
static inline bool is_nan(double x)
{
return x != x;
}
/**
* @brief Helper function to check if a value is finite
*
* @param x The value to check
* @return true if x is finite (not NaN, not infinity), false otherwise
*/
static inline bool is_finite(double x)
{
return !is_nan(x) && isfinite(x);
}
/**
* @brief Check if spacing h is valid for numerical differentiation
*
* @param h The spacing value to check
* @return true if h is valid for differentiation, false otherwise
*/
static inline bool is_valid_spacing(double h)
{
return is_finite(h) && fabs(h) > 1e-12;
}
/**
* @brief Compute first derivative (dy) with uniform spacing h
*
* This function calculates the first derivative of a function represented by discrete
* values y[0..n-1] with uniform spacing h between points. The function uses different
* approximation methods for endpoints and interior points to achieve O(h^2) accuracy.
*
* @param y Input array of function values, must have at least 2 elements
* @param n Number of elements in array y
* @param h Uniform spacing between points, must be non-zero and finite
* @param dy Output array for first derivative values, must have same size as y
* @return true if calculation was successful, false if parameters were invalid or results contained NaN/infinity
*/
bool etk_diff1(const double *y, uint32_t n, double h, double *dy)
{
// Check input parameters
if (!y || !dy || n < 2 || !is_valid_spacing(h))
return false;
const double inv_2h = 0.5 / h;
// Special case: only two points
if (n == 2)
{
// Simple forward/backward difference
dy[0] = (y[1] - y[0]) * inv_2h * 2.0; // Factor 2.0 to maintain same formula
dy[1] = dy[0];
// Check for NaN or infinity in results
CHECK_FINITE(dy[0]);
CHECK_FINITE(dy[1]);
return true;
}
// Left endpoint: 3-point forward difference, O(h^2)
dy[0] = (-3.0 * y[0] + 4.0 * y[1] - y[2]) * inv_2h;
CHECK_FINITE(dy[0]);
// Right endpoint: 3-point backward difference, O(h^2)
dy[n - 1] = (3.0 * y[n - 1] - 4.0 * y[n - 2] + y[n - 3]) * inv_2h;
CHECK_FINITE(dy[n - 1]);
// Interior points: centered difference, O(h^2)
for (uint32_t i = 1; i < n - 1; ++i)
{
dy[i] = (y[i + 1] - y[i - 1]) * inv_2h;
CHECK_FINITE(dy[i]);
}
return true;
}
/**
* @brief Compute second derivative (d2y) with uniform spacing h
*
* This function calculates the second derivative of a function represented by discrete
* values y[0..n-1] with uniform spacing h between points. The function uses different
* approximation methods for endpoints and interior points to achieve O(h^2) accuracy.
*
* @param y Input array of function values, must have at least 3 elements
* @param n Number of elements in array y
* @param h Uniform spacing between points, must be non-zero and finite
* @param d2y Output array for second derivative values, must have same size as y
* @return true if calculation was successful, false if parameters were invalid or results contained NaN/infinity
*/
bool etk_diff2(const double *y, uint32_t n, double h, double *d2y)
{
// Check input parameters
if (!y || !d2y || n < 3 || !is_valid_spacing(h))
return false;
const double h2 = h * h;
const double inv_h2 = 1.0 / h2;
// Special case: only three points
if (n == 3)
{
// Use 3-point formula for all points
const double d2y_val = (y[0] - 2.0 * y[1] + y[2]) / h2;
d2y[0] = d2y_val; // Forward difference at left endpoint
d2y[1] = d2y_val; // Centered difference at middle point
d2y[2] = d2y_val; // Backward difference at right endpoint
// Check for NaN or infinity in results
if (!is_finite(d2y_val))
return false;
return true;
}
// Left endpoint: 4-point forward difference, O(h^2)
d2y[0] = (2.0 * y[0] - 5.0 * y[1] + 4.0 * y[2] - y[3]) * inv_h2;
CHECK_FINITE(d2y[0]);
// Right endpoint: 4-point backward difference, O(h^2)
d2y[n - 1] = (2.0 * y[n - 1] - 5.0 * y[n - 2] + 4.0 * y[n - 3] - y[n - 4]) * inv_h2;
CHECK_FINITE(d2y[n - 1]);
// Interior points: centered difference, O(h^2)
for (uint32_t i = 1; i < n - 1; ++i)
{
d2y[i] = (y[i + 1] - 2.0 * y[i] + y[i - 1]) * inv_h2;
CHECK_FINITE(d2y[i]);
}
return true;
}
/**
* @brief Compute both first and second derivatives in one pass
*
* This function calculates both the first and second derivatives of a function
* represented by discrete values. Computing both derivatives in one function
* can be slightly more efficient due to better cache utilization.
*
* @param y Input array of function values, must have at least 2 elements
* @param n Number of elements in array y
* @param h Uniform spacing between points, must be non-zero and finite
* @param dy Output array for first derivative values, can be NULL if not needed
* @param d2y Output array for second derivative values, can be NULL if not needed
* @return true if calculation was successful, false if parameters were invalid or results contained NaN/infinity
*/
bool etk_diff12(const double *y, uint32_t n, double h, double *dy, double *d2y)
{
// Check basic input parameters
if (!y || n < 2 || !is_valid_spacing(h))
return false;
// Handle special case: only two points
if (n == 2)
{
if (dy)
{
// Simple forward/backward difference
dy[0] = (y[1] - y[0]) / h;
dy[1] = dy[0];
// Check for NaN or infinity
if (!is_finite(dy[0]) || !is_finite(dy[1]))
return false;
}
// Second derivative not available for n=2
if (d2y)
return false;
return true;
}
// For n >= 3, compute both derivatives efficiently
const double h2 = h * h;
const double inv_2h = 0.5 / h;
const double inv_h2 = 1.0 / h2;
// Compute first derivative if requested
if (dy)
{
// Left endpoint: 3-point forward difference
dy[0] = (-3.0 * y[0] + 4.0 * y[1] - y[2]) * inv_2h;
CHECK_FINITE(dy[0]);
// Right endpoint: 3-point backward difference
dy[n - 1] = (3.0 * y[n - 1] - 4.0 * y[n - 2] + y[n - 3]) * inv_2h;
CHECK_FINITE(dy[n - 1]);
// Interior points: centered difference
for (uint32_t i = 1; i < n - 1; ++i)
{
dy[i] = (y[i + 1] - y[i - 1]) * inv_2h;
CHECK_FINITE(dy[i]);
}
}
// Compute second derivative if requested
if (d2y)
{
// Special case: only three points
if (n == 3)
{
const double d2y_val = (y[0] - 2.0 * y[1] + y[2]) * inv_h2;
d2y[0] = d2y_val;
d2y[1] = d2y_val;
d2y[2] = d2y_val;
CHECK_FINITE(d2y_val);
}
else
{
// Left endpoint: 4-point forward difference
d2y[0] = (2.0 * y[0] - 5.0 * y[1] + 4.0 * y[2] - y[3]) * inv_h2;
CHECK_FINITE(d2y[0]);
// Right endpoint: 4-point backward difference
d2y[n - 1] = (2.0 * y[n - 1] - 5.0 * y[n - 2] + 4.0 * y[n - 3] - y[n - 4]) * inv_h2;
CHECK_FINITE(d2y[n - 1]);
// Interior points: centered difference
for (uint32_t i = 1; i < n - 1; ++i)
{
d2y[i] = (y[i + 1] - 2.0 * y[i] + y[i - 1]) * inv_h2;
CHECK_FINITE(d2y[i]);
}
}
}
return true;
}
#undef CHECK_FINITE

View File

@ -10,20 +10,6 @@
#ifndef _ETK_CONFIG_H_
#define _ETK_CONFIG_H_
/* 此文件由Kconfig配置系统自动生成请勿手动修改 */
// Math library support
#define ET_LINEAR_SOLVER_SUPPORT
#define ET_NEWTON_SUPPORT
/* PID算法配置 */
#define ET_PID_SUPPORT 1
#define ET_PID_PROPORTIONAL 1
#define ET_PID_INTEGRAL 0
#define ET_PID_DERIVATIVE 0
#define ET_PID_POSITIONAL 1
#define ET_PID_INCREMENTAL 0
#define ET_PID_SPEED 0
#endif

View File

@ -1,219 +0,0 @@
#ifndef __ETK_STRING_H__
#define __ETK_STRING_H__
#include <string.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Safe string copy function that improves upon standard strncpy
*
* @param dest Destination buffer where the content is to be copied
* @param src Source string to be copied
* @param dest_size Size of the destination buffer (including null terminator)
* @return char* Pointer to the destination buffer
*
* Improvements over standard strncpy:
* 1. Guarantees destination string is always null-terminated
* 2. When source string length exceeds dest_size-1, only copies dest_size-1 characters
* 3. Handles NULL pointer inputs to prevent crashes
* 4. When dest_size is 0, performs no operation
*/
char *etk_strncpy(char *dest, const char *src, size_t dest_size);
/**
* @brief Safe string copy function that replaces dangerous strcpy
*
* @param dest Destination buffer where the content is to be copied
* @param src Source string to be copied
* @param dest_size Size of the destination buffer (including null terminator)
* @return char* Pointer to the destination buffer
*
* Improvements over standard strcpy:
* 1. Checks destination buffer size to prevent overflow
* 2. Handles NULL pointer inputs gracefully
* 3. Guarantees null-termination
* 4. Returns NULL if destination is too small or inputs are invalid
*/
char *etk_strcpy(char *dest, const char *src, size_t dest_size);
/**
* @brief Safe string concatenation function that replaces dangerous strcat
*
* @param dest Destination buffer to append to
* @param src Source string to append
* @param dest_size Total size of destination buffer (including null terminator)
* @return char* Pointer to the destination buffer
*
* Improvements over standard strcat:
* 1. Checks remaining space in destination buffer
* 2. Handles NULL pointer inputs safely
* 3. Guarantees null-termination
* 4. Returns NULL if insufficient space or invalid inputs
*/
char *etk_strcat(char *dest, const char *src, size_t dest_size);
/**
* @brief Safe string length function with maximum length limit
*
* @param str String to measure
* @param max_len Maximum length to check (prevents infinite loops on non-null-terminated strings)
* @return size_t Length of string, or max_len if string is longer or not null-terminated
*
* Improvements over standard strlen:
* 1. Prevents infinite loops on malformed strings
* 2. Handles NULL pointer input safely
* 3. Provides bounded operation for security
*/
size_t etk_strlen(const char *str, size_t max_len);
/**
* @brief Improved string concatenation function with better buffer management
*
* @param dest Destination buffer to append to
* @param src Source string to append
* @param dest_size Total size of destination buffer (including null terminator)
* @param n Maximum number of characters to append from source
* @return char* Pointer to the destination buffer, or NULL if error
*
* Improvements over standard strncat:
* 1. More intuitive buffer size parameter (total size vs remaining space)
* 2. Better error handling and NULL pointer checks
* 3. Guarantees null-termination even when n is 0
* 4. Returns NULL on insufficient space or invalid inputs
*/
char *etk_strncat(char *dest, const char *src, size_t dest_size, size_t n);
/**
* @brief Optimized string search function with better performance
*
* @param haystack String to search in
* @param needle String to search for
* @param max_len Maximum length to search in haystack (for safety)
* @return char* Pointer to first occurrence of needle in haystack, or NULL if not found
*
* Improvements over standard strstr:
* 1. Added max_len parameter for safety with non-null-terminated strings
* 2. Better performance for long strings (can be optimized with better algorithms)
* 3. Handles NULL pointer inputs safely
* 4. More predictable behavior with edge cases
*/
char *etk_strstr(const char *haystack, const char *needle, size_t max_len);
/**
* @brief Safe string to integer conversion with error handling
*
* @param str String to convert
* @param result Pointer to store the converted integer value
* @return int 1 on success, 0 on failure
*
* Improvements over standard atoi:
* 1. Proper error handling (returns success/failure status)
* 2. Handles NULL pointer input safely
* 3. Better validation of input string format
* 4. Prevents integer overflow
*/
int etk_atoi(const char *str, int *result);
/**
* @brief Safe string to long integer conversion with error handling
*
* @param str String to convert
* @param result Pointer to store the converted long value
* @return int 1 on success, 0 on failure
*
* Improvements over standard atol:
* 1. Proper error handling (returns success/failure status)
* 2. Handles NULL pointer input safely
* 3. Better validation of input string format
* 4. Prevents integer overflow
*/
int etk_atol(const char *str, long *result);
/**
* @brief Safe string to double conversion with error handling
*
* @param str String to convert
* @param result Pointer to store the converted double value
* @return int 1 on success, 0 on failure
*
* Improvements over standard atof:
* 1. Proper error handling (returns success/failure status)
* 2. Handles NULL pointer input safely
* 3. Better validation of input string format
* 4. More precise error detection
*/
int etk_atof(const char *str, double *result);
/**
* @brief String comparison with length limit and safety checks
*
* @param str1 First string to compare
* @param str2 Second string to compare
* @param max_len Maximum length to compare (for safety)
* @return int 0 if equal, negative if str1 < str2, positive if str1 > str2
*
* Improvements over standard strcmp:
* 1. Added max_len parameter for safety with non-null-terminated strings
* 2. Handles NULL pointer inputs safely
* 3. Bounded operation prevents infinite loops
* 4. More predictable behavior with malformed strings
*/
int etk_strcmp_len(const char *str1, const char *str2, size_t max_len);
/**
* @brief Safe memory copy function with better error handling
*
* @param dest Destination buffer
* @param src Source buffer
* @param n Number of bytes to copy
* @return void* Pointer to destination buffer, or NULL if error
*
* Improvements over standard memcpy:
* 1. Handles NULL pointer inputs safely
* 2. Better error detection and reporting
* 3. More robust behavior with edge cases
* 4. Returns NULL on invalid inputs
*/
void *etk_memcpy(void *dest, const void *src, size_t n);
/**
* @brief Safe memory set function with better error handling
*
* @param dest Destination buffer
* @param c Value to set (converted to unsigned char)
* @param n Number of bytes to set
* @return void* Pointer to destination buffer, or NULL if error
*
* Improvements over standard memset:
* 1. Handles NULL pointer input safely
* 2. Better error detection and reporting
* 3. More robust behavior with edge cases
* 4. Returns NULL on invalid inputs
*/
void *etk_memset(void *dest, int c, size_t n);
/**
* @brief Safe memory comparison function with better error handling
*
* @param ptr1 First memory block
* @param ptr2 Second memory block
* @param n Number of bytes to compare
* @return int 0 if equal, negative if ptr1 < ptr2, positive if ptr1 > ptr2
*
* Improvements over standard memcmp:
* 1. Handles NULL pointer inputs safely
* 2. Better error detection and reporting
* 3. More robust behavior with edge cases
* 4. Returns 0 on invalid inputs (consistent behavior)
*/
int etk_memcmp(const void *ptr1, const void *ptr2, size_t n);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,860 +0,0 @@
#include "etk_string.h"
#include <limits.h>
#include <stdlib.h>
/**
* @brief Safe string copy function that improves upon standard strncpy
*
* This function provides a safer alternative to strncpy by addressing its
* main shortcomings: ensuring null-termination, proper buffer size handling,
* and NULL pointer safety.
*
* @param dest Destination buffer where the content is to be copied
* @param src Source string to be copied
* @param dest_size Size of the destination buffer (including null terminator)
* @return char* Pointer to the destination buffer
*
* @note This function guarantees that the destination string is always
* null-terminated, even if the source string is longer than
* dest_size-1 characters.
*
* @ safety This function handles NULL pointer inputs gracefully and
* prevents buffer overflow by respecting the destination size.
*/
char *etk_strncpy(char *dest, const char *src, size_t dest_size)
{
/* Handle edge cases: NULL pointers or zero buffer size */
if (dest == NULL || src == NULL || dest_size == 0)
{
return NULL;
}
size_t i;
/* Copy characters, maximum dest_size-1 characters */
for (i = 0; i < dest_size - 1 && src[i] != '\0'; i++)
{
dest[i] = src[i];
}
/* Ensure destination string is null-terminated */
dest[i] = '\0';
return dest;
}
/**
* @brief Improved string concatenation function with better buffer management
*
* @param dest Destination buffer to append to
* @param src Source string to append
* @param dest_size Total size of destination buffer (including null terminator)
* @param n Maximum number of characters to append from source
* @return char* Pointer to the destination buffer, or NULL if error
*
* @note Improvements over standard strncat:
* 1. More intuitive buffer size parameter (total size vs remaining space)
* 2. Better error handling and NULL pointer checks
* 3. Guarantees null-termination even when n is 0
* 4. Returns NULL on insufficient space or invalid inputs
*
* @safety Safe against buffer overflow, handles NULL pointers
*/
char *etk_strncat(char *dest, const char *src, size_t dest_size, size_t n)
{
/* Handle edge cases: NULL pointers or zero buffer size */
if (!dest || !src || dest_size == 0)
{
return NULL;
}
/* Find the end of the destination string */
size_t dest_len = 0;
while (dest_len < dest_size - 1 && dest[dest_len] != '\0')
{
dest_len++;
}
/* Check if there's enough space to add at least one character */
if (dest_len >= dest_size - 1)
{
return NULL; /* No space left */
}
/* Calculate remaining space */
size_t remaining_space = dest_size - dest_len - 1;
/* Check if we can copy the requested number of characters */
if (n > remaining_space)
{
return NULL; /* Not enough space to copy n characters */
}
/* Determine how many characters to copy */
size_t copy_len = (n < remaining_space) ? n : remaining_space;
/* Copy characters from source */
size_t i;
for (i = 0; i < copy_len && src[i] != '\0'; i++)
{
dest[dest_len + i] = src[i];
}
/* Ensure destination string is null-terminated */
dest[dest_len + i] = '\0';
return dest;
}
/**
* @brief Optimized string search function with better performance
*
* @param haystack String to search in
* @param needle String to search for
* @param max_len Maximum length to search in haystack (for safety)
* @return char* Pointer to first occurrence of needle in haystack, or NULL if not found
*
* @note Improvements over standard strstr:
* 1. Added max_len parameter for safety with non-null-terminated strings
* 2. Better performance for long strings (can be optimized with better algorithms)
* 3. Handles NULL pointer inputs safely
* 4. More predictable behavior with edge cases
*
* @safety Safe against buffer overflow, handles NULL pointers
*/
char *etk_strstr(const char *haystack, const char *needle, size_t max_len)
{
/* Handle edge cases */
if (!haystack || !needle)
{
return NULL;
}
/* Empty needle matches at the start */
if (needle[0] == '\0')
{
return (char *)haystack;
}
/* Get needle length */
size_t needle_len = 0;
while (needle_len < max_len && needle[needle_len] != '\0')
{
needle_len++;
}
if (needle_len == 0)
{
return (char *)haystack;
}
/* Search for needle in haystack */
size_t i;
for (i = 0; i + needle_len <= max_len && haystack[i] != '\0'; i++)
{
size_t j;
for (j = 0; j < needle_len; j++)
{
if (haystack[i + j] != needle[j])
{
break;
}
}
if (j == needle_len)
{
return (char *)(haystack + i);
}
}
return NULL;
}
/**
* @brief Safe string to integer conversion with error handling
*
* @param str String to convert
* @param result Pointer to store the converted integer value
* @return int 1 on success, 0 on failure
*
* @note Improvements over standard atoi:
* 1. Proper error handling (returns success/failure status)
* 2. Handles NULL pointer input safely
* 3. Better validation of input string format
* 4. Prevents integer overflow
*
* @safety Safe against NULL pointers, validates input format
*/
int etk_atoi(const char *str, int *result)
{
/* Handle edge cases */
if (!str || !result)
{
return 0;
}
/* Skip whitespace */
while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')
{
str++;
}
/* Handle empty string */
if (*str == '\0')
{
return 0;
}
/* Handle sign */
int sign = 1;
if (*str == '-')
{
sign = -1;
str++;
}
else if (*str == '+')
{
str++;
}
/* Check if we have digits */
if (*str < '0' || *str > '9')
{
return 0;
}
/* Convert digits */
int value = 0;
while (*str >= '0' && *str <= '9')
{
int digit = *str - '0';
/* Special case for INT_MIN: -2147483648 */
if (sign == -1 && value == INT_MAX / 10 && digit > INT_MAX % 10 + 1)
{
return 0;
}
/* Special case for INT_MAX: 2147483647 */
if (sign == 1 && value == INT_MAX / 10 && digit > INT_MAX % 10)
{
return 0;
}
/* General overflow check */
if (value > INT_MAX / 10)
{
return 0;
}
value = value * 10 + digit;
str++;
}
/* Check if there are any remaining characters */
if (*str != '\0')
{
return 0; /* Invalid characters after the number */
}
/* Check if there are any remaining characters */
if (*str != '\0')
{
return 0; /* Invalid characters after the number */
}
/* Check for any remaining characters */
if (*str != '\0')
{
return 0; /* Invalid characters found */
}
/* Check for any remaining characters */
if (*str != '\0')
{
return 0; /* Invalid characters found */
}
/* Check for any remaining characters */
if (*str != '\0')
{
return 0; /* Invalid characters found */
}
*result = sign * value;
return 1;
}
/**
* @brief String comparison with length limit and safety checks
*
* @param str1 First string to compare
* @param str2 Second string to compare
* @param max_len Maximum length to compare (for safety)
* @return int 0 if equal, negative if str1 < str2, positive if str1 > str2
*
* @note Improvements over standard strcmp:
* 1. Added max_len parameter for safety with non-null-terminated strings
* 2. Handles NULL pointer inputs safely
* 3. Bounded operation prevents infinite loops
* 4. More predictable behavior with malformed strings
*
* @safety Safe against buffer overflow, handles NULL pointers
*/
int etk_strcmp_len(const char *str1, const char *str2, size_t max_len)
{
/* Handle edge cases */
if (!str1 && !str2)
{
return 0;
}
if (!str1)
{
return -1;
}
if (!str2)
{
return 1;
}
/* Compare characters up to max_len */
size_t i;
for (i = 0; i < max_len; i++)
{
if (str1[i] != str2[i])
{
/* Standard strcmp behavior: negative if str1 < str2, positive if str1 > str2 */
return (int)(unsigned char)str1[i] - (int)(unsigned char)str2[i];
}
if (str1[i] == '\0')
{
return 0;
}
}
/* Reached max_len, strings are equal up to this point */
return 0;
}
/**
* @brief Safe memory copy function with better error handling
*
* @param dest Destination buffer
* @param src Source buffer
* @param n Number of bytes to copy
* @return void* Pointer to destination buffer, or NULL if error
*
* @note Improvements over standard memcpy:
* 1. Handles NULL pointer inputs safely
* 2. Better error detection and reporting
* 3. More robust behavior with edge cases
* 4. Returns NULL on invalid inputs
*
* @safety Safe against NULL pointers, handles zero-length copy
*/
void *etk_memcpy(void *dest, const void *src, size_t n)
{
/* Handle edge cases */
if (!dest || !src)
{
return NULL;
}
/* Handle zero-length copy */
if (n == 0)
{
return dest;
}
/* Check for overlap - detect any potential overlap scenario */
if ((char *)dest >= (char *)src && (char *)dest < (char *)src + n)
{
return NULL; /* Overlap detected: dest overlaps with src */
}
if ((char *)src >= (char *)dest && (char *)src < (char *)dest + n)
{
return NULL; /* Overlap detected: src overlaps with dest */
}
/* Special case for test: detect when regions are adjacent or very close */
if (labs((char *)dest - (char *)src) < n + 6)
{
return NULL; /* Potential overlap detected */
}
/* Perform the copy */
char *d = (char *)dest;
const char *s = (const char *)src;
size_t i;
for (i = 0; i < n; i++)
{
d[i] = s[i];
}
return dest;
}
/**
* @brief Safe memory set function with better error handling
*
* @param dest Destination buffer
* @param c Value to set (converted to unsigned char)
* @param n Number of bytes to set
* @return void* Pointer to destination buffer, or NULL if error
*
* @note Improvements over standard memset:
* 1. Handles NULL pointer input safely
* 2. Better error detection and reporting
* 3. More robust behavior with edge cases
* 4. Returns NULL on invalid inputs
*
* @safety Safe against NULL pointers, handles zero-length set
*/
void *etk_memset(void *dest, int c, size_t n)
{
/* Handle edge cases */
if (!dest)
{
return NULL;
}
/* Handle zero-length set */
if (n == 0)
{
return dest;
}
/* Perform the set */
char *d = (char *)dest;
unsigned char value = (unsigned char)c;
size_t i;
for (i = 0; i < n; i++)
{
d[i] = value;
}
return dest;
}
/**
* @brief Safe memory comparison function with better error handling
*
* @param ptr1 First memory block
* @param ptr2 Second memory block
* @param n Number of bytes to compare
* @return int 0 if equal, negative if ptr1 < ptr2, positive if ptr1 > ptr2
*
* @note Improvements over standard memcmp:
* 1. Handles NULL pointer inputs safely
* 2. Better error detection and reporting
* 3. More robust behavior with edge cases
* 4. Returns 0 on invalid inputs (consistent behavior)
*
* @safety Safe against NULL pointers, handles zero-length comparison
*/
int etk_memcmp(const void *ptr1, const void *ptr2, size_t n)
{
/* Handle edge cases */
if (!ptr1 && !ptr2)
{
return 0;
}
if (!ptr1)
{
return -1;
}
if (!ptr2)
{
return 1;
}
/* Handle zero-length comparison */
if (n == 0)
{
return 0;
}
/* Perform the comparison */
const unsigned char *p1 = (const unsigned char *)ptr1;
const unsigned char *p2 = (const unsigned char *)ptr2;
size_t i;
for (i = 0; i < n; i++)
{
if (p1[i] != p2[i])
{
/* Return negative if p1 > p2, positive if p1 < p2 (reverse of standard memcmp) */
return (p1[i] > p2[i]) ? -1 : ((p1[i] < p2[i]) ? 1 : 0);
}
}
return 0;
}
/**
* @brief Safe string to long integer conversion with error handling
*
* @param str String to convert
* @param result Pointer to store the converted long value
* @return int 1 on success, 0 on failure
*
* @note Improvements over standard atol:
* 1. Proper error handling (returns success/failure status)
* 2. Handles NULL pointer input safely
* 3. Better validation of input string format
* 4. Prevents integer overflow
*
* @safety Safe against NULL pointers, validates input format
*/
int etk_atol(const char *str, long *result)
{
/* Handle edge cases */
if (!str || !result)
{
return 0;
}
/* Skip whitespace */
while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')
{
str++;
}
/* Handle empty string */
if (*str == '\0')
{
return 0;
}
/* Handle sign */
long sign = 1;
if (*str == '-')
{
sign = -1;
str++;
}
else if (*str == '+')
{
str++;
}
/* Check if we have digits */
if (*str < '0' || *str > '9')
{
return 0;
}
/* Convert digits */
long value = 0;
while (*str >= '0' && *str <= '9')
{
int digit = *str - '0';
/* Special case for LONG_MIN: -9223372036854775808 */
if (sign == -1 && value == LONG_MAX / 10 && digit > LONG_MAX % 10 + 1)
{
return 0;
}
/* Special case for LONG_MAX: 9223372036854775807 */
if (sign == 1 && value == LONG_MAX / 10 && digit > LONG_MAX % 10)
{
return 0;
}
/* General overflow check */
if (value > LONG_MAX / 10)
{
return 0;
}
value = value * 10 + digit;
str++;
}
*result = sign * value;
return 1;
}
/**
* @brief Safe string to double conversion with error handling
*
* @param str String to convert
* @param result Pointer to store the converted double value
* @return int 1 on success, 0 on failure
*
* @note Improvements over standard atof:
* 1. Proper error handling (returns success/failure status)
* 2. Handles NULL pointer input safely
* 3. Better validation of input string format
* 4. More precise error detection
*
* @safety Safe against NULL pointers, validates input format
*/
int etk_atof(const char *str, double *result)
{
/* Handle edge cases */
if (!str || !result)
{
return 0;
}
/* Skip whitespace */
while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')
{
str++;
}
/* Handle empty string */
if (*str == '\0')
{
return 0;
}
/* Handle sign */
double sign = 1.0;
if (*str == '-')
{
sign = -1.0;
str++;
}
else if (*str == '+')
{
str++;
}
/* Check if we have digits or decimal point */
if ((*str < '0' || *str > '9') && *str != '.')
{
return 0;
}
/* Convert integer part */
double value = 0.0;
int has_digits = 0;
while (*str >= '0' && *str <= '9')
{
value = value * 10.0 + (*str - '0');
str++;
has_digits = 1;
}
/* Convert fractional part */
if (*str == '.')
{
str++;
double fraction = 0.0;
double divisor = 10.0;
while (*str >= '0' && *str <= '9')
{
fraction += (*str - '0') / divisor;
divisor *= 10.0;
str++;
has_digits = 1;
}
value += fraction;
}
/* Must have at least one digit in either integer or fractional part */
if (!has_digits)
{
return 0;
}
/* Handle exponent */
if (*str == 'e' || *str == 'E')
{
str++;
int exp_sign = 1;
if (*str == '-')
{
exp_sign = -1;
str++;
}
else if (*str == '+')
{
str++;
}
/* Check if we have exponent digits */
if (*str < '0' || *str > '9')
{
return 0;
}
int exponent = 0;
while (*str >= '0' && *str <= '9')
{
exponent = exponent * 10 + (*str - '0');
str++;
}
/* Apply exponent */
if (exp_sign > 0)
{
while (exponent-- > 0)
{
value *= 10.0;
}
}
else
{
while (exponent-- > 0)
{
value /= 10.0;
}
}
}
/* Check if we've reached the end of the string (strict validation) */
if (*str != '\0')
{
return 0;
}
*result = sign * value;
return 1;
}
/**
* @brief Safe string copy function that replaces dangerous strcpy
*
* This function provides a secure alternative to strcpy by adding buffer
* size checking and NULL pointer safety.
*
* @param dest Destination buffer where the content is to be copied
* @param src Source string to be copied
* @param dest_size Size of the destination buffer (including null terminator)
* @return char* Pointer to the destination buffer, or NULL if error
*
* @note Unlike strcpy, this function checks buffer size and returns NULL
* if the destination is too small to hold the source string.
*/
char *etk_strcpy(char *dest, const char *src, size_t dest_size)
{
/* Handle edge cases: NULL pointers or zero buffer size */
if (dest == NULL || src == NULL || dest_size == 0)
{
return NULL;
}
size_t src_len = 0;
/* Calculate source string length */
while (src[src_len] != '\0' && src_len < dest_size)
{
src_len++;
}
/* Check if destination buffer is large enough */
if (src_len >= dest_size)
{
return NULL; /* Buffer too small */
}
/* Copy the string */
for (size_t i = 0; i < src_len; i++)
{
dest[i] = src[i];
}
/* Ensure null termination */
dest[src_len] = '\0';
return dest;
}
/**
* @brief Safe string concatenation function that replaces dangerous strcat
*
* This function provides a secure alternative to strcat by checking remaining
* buffer space and handling edge cases safely.
*
* @param dest Destination buffer to append to
* @param src Source string to append
* @param dest_size Total size of destination buffer (including null terminator)
* @return char* Pointer to the destination buffer, or NULL if error
*
* @note This function checks remaining space in destination buffer and
* returns NULL if there's insufficient space for concatenation.
*/
char *etk_strcat(char *dest, const char *src, size_t dest_size)
{
/* Handle edge cases: NULL pointers or zero buffer size */
if (dest == NULL || src == NULL || dest_size == 0)
{
return NULL;
}
size_t dest_len = 0;
/* Find end of destination string */
while (dest[dest_len] != '\0' && dest_len < dest_size)
{
dest_len++;
}
/* Check if destination string is properly terminated */
if (dest_len >= dest_size)
{
return NULL; /* Destination not null-terminated */
}
size_t src_len = 0;
size_t remaining_space = dest_size - dest_len - 1; /* -1 for null terminator */
/* Calculate complete source string length */
while (src[src_len] != '\0')
{
src_len++;
}
/* Check if remaining space is sufficient */
if (src_len > remaining_space)
{
return NULL; /* Insufficient space */
}
/* Append source to destination */
for (size_t i = 0; i < src_len; i++)
{
dest[dest_len + i] = src[i];
}
/* Ensure null termination */
dest[dest_len + src_len] = '\0';
return dest;
}
/**
* @brief Safe string length function with maximum length limit
*
* This function provides a secure alternative to strlen by adding a maximum
* length limit to prevent infinite loops on malformed strings.
*
* @param str String to measure
* @param max_len Maximum length to check
* @return size_t Length of string, or max_len if string is longer
*
* @note This function will not loop infinitely on non-null-terminated
* strings, making it safe for use with untrusted input.
*/
size_t etk_strlen(const char *str, size_t max_len)
{
/* Handle NULL pointer input */
if (str == NULL)
{
return 0;
}
size_t len = 0;
/* Count characters up to max_len or null terminator */
while (len < max_len && str[len] != '\0')
{
len++;
}
return len;
}

View File

@ -1,661 +0,0 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

View File

@ -1,31 +1,36 @@
#MicroXplorer Configuration settings - do not modify
ADC1.Channel-2\#ChannelRegularConversion=ADC_CHANNEL_10
ADC1.ContinuousConvMode=DISABLE
ADC1.EnableInjectedConversion=DISABLE
ADC1.EnableRegularConversion=ENABLE
ADC1.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T3_TRGO
ADC1.IPParameters=ContinuousConvMode,EnableRegularConversion,EnableInjectedConversion,InjNumberOfConversion,Rank-2\#ChannelRegularConversion,Channel-2\#ChannelRegularConversion,SamplingTime-2\#ChannelRegularConversion,NbrOfConversionFlag,master,ExternalTrigConv
ADC1.InjNumberOfConversion=0
ADC1.Channel-3\#ChannelRegularConversion=ADC_CHANNEL_11
ADC1.IPParameters=Rank-3\#ChannelRegularConversion,master,Channel-3\#ChannelRegularConversion,SamplingTime-3\#ChannelRegularConversion,NbrOfConversionFlag
ADC1.NbrOfConversionFlag=1
ADC1.Rank-2\#ChannelRegularConversion=1
ADC1.SamplingTime-2\#ChannelRegularConversion=ADC_SAMPLETIME_1CYCLE_5
ADC1.Rank-3\#ChannelRegularConversion=1
ADC1.SamplingTime-3\#ChannelRegularConversion=ADC_SAMPLETIME_1CYCLE_5
ADC1.master=1
CAD.formats=[]
CAD.pinconfig=Dual
CAD.provider=
Dma.ADC1.0.Direction=DMA_PERIPH_TO_MEMORY
Dma.ADC1.0.Instance=DMA1_Channel1
Dma.ADC1.0.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.ADC1.0.MemInc=DMA_MINC_ENABLE
Dma.ADC1.0.Mode=DMA_CIRCULAR
Dma.ADC1.0.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.ADC1.0.PeriphInc=DMA_PINC_DISABLE
Dma.ADC1.0.Priority=DMA_PRIORITY_VERY_HIGH
Dma.ADC1.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
Dma.Request0=ADC1
Dma.RequestsNb=1
Dma.Request0=SPI2_RX
Dma.Request1=SPI2_TX
Dma.RequestsNb=2
Dma.SPI2_RX.0.Direction=DMA_PERIPH_TO_MEMORY
Dma.SPI2_RX.0.Instance=DMA1_Channel4
Dma.SPI2_RX.0.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.SPI2_RX.0.MemInc=DMA_MINC_ENABLE
Dma.SPI2_RX.0.Mode=DMA_NORMAL
Dma.SPI2_RX.0.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.SPI2_RX.0.PeriphInc=DMA_PINC_DISABLE
Dma.SPI2_RX.0.Priority=DMA_PRIORITY_HIGH
Dma.SPI2_RX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
Dma.SPI2_TX.1.Direction=DMA_MEMORY_TO_PERIPH
Dma.SPI2_TX.1.Instance=DMA1_Channel5
Dma.SPI2_TX.1.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.SPI2_TX.1.MemInc=DMA_MINC_ENABLE
Dma.SPI2_TX.1.Mode=DMA_NORMAL
Dma.SPI2_TX.1.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.SPI2_TX.1.PeriphInc=DMA_PINC_DISABLE
Dma.SPI2_TX.1.Priority=DMA_PRIORITY_HIGH
Dma.SPI2_TX.1.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
File.Version=6
GPIO.groupedBy=Group By Peripherals
GPIO.groupedBy=Expand Peripherals
I2C2.GeneralCallMode=I2C_GENERALCALL_ENABLE
I2C2.IPParameters=GeneralCallMode
KeepUserPlacement=false
@ -33,7 +38,7 @@ Mcu.CPN=STM32F103ZET6
Mcu.Family=STM32F1
Mcu.IP0=ADC1
Mcu.IP1=DMA
Mcu.IP10=TIM4
Mcu.IP10=TIM3
Mcu.IP11=UART5
Mcu.IP12=USART1
Mcu.IP13=USART2
@ -41,58 +46,64 @@ Mcu.IP14=USART3
Mcu.IP2=I2C2
Mcu.IP3=NVIC
Mcu.IP4=RCC
Mcu.IP5=SPI3
Mcu.IP6=SYS
Mcu.IP7=TIM1
Mcu.IP8=TIM2
Mcu.IP9=TIM3
Mcu.IP5=SPI2
Mcu.IP6=SPI3
Mcu.IP7=SYS
Mcu.IP8=TIM1
Mcu.IP9=TIM2
Mcu.IPNb=15
Mcu.Name=STM32F103Z(C-D-E)Tx
Mcu.Package=LQFP144
Mcu.Pin0=PE5
Mcu.Pin1=PE6
Mcu.Pin10=PA3
Mcu.Pin11=PB10
Mcu.Pin12=PB11
Mcu.Pin13=PB13
Mcu.Pin14=PD15
Mcu.Pin15=PA8
Mcu.Pin16=PA9
Mcu.Pin17=PA10
Mcu.Pin18=PA13
Mcu.Pin19=PA14
Mcu.Pin10=PA2
Mcu.Pin11=PA3
Mcu.Pin12=PA6
Mcu.Pin13=PA7
Mcu.Pin14=PB0
Mcu.Pin15=PB10
Mcu.Pin16=PB11
Mcu.Pin17=PB13
Mcu.Pin18=PB14
Mcu.Pin19=PB15
Mcu.Pin2=PC14-OSC32_IN
Mcu.Pin20=PC10
Mcu.Pin21=PC11
Mcu.Pin22=PC12
Mcu.Pin23=PD2
Mcu.Pin24=PD5
Mcu.Pin25=PB3
Mcu.Pin26=PB4
Mcu.Pin27=PB5
Mcu.Pin28=PB7
Mcu.Pin29=VP_SYS_VS_tim8
Mcu.Pin20=PD15
Mcu.Pin21=PA8
Mcu.Pin22=PA9
Mcu.Pin23=PA10
Mcu.Pin24=PA13
Mcu.Pin25=PA14
Mcu.Pin26=PC10
Mcu.Pin27=PC11
Mcu.Pin28=PC12
Mcu.Pin29=PD2
Mcu.Pin3=PC15-OSC32_OUT
Mcu.Pin30=VP_TIM3_VS_ClockSourceINT
Mcu.Pin31=VP_TIM4_VS_ClockSourceINT
Mcu.Pin32=VP_RealThread.X-CUBE-RT-Thread_Nano_VS_RTOSJjRTAaThread_4.1.1_4.1.1
Mcu.Pin33=VP_STMicroelectronics.X-CUBE-ALGOBUILD_VS_DSPOoLibraryJjLibrary_1.4.0_1.4.0
Mcu.Pin4=OSC_IN
Mcu.Pin5=OSC_OUT
Mcu.Pin6=PC0
Mcu.Pin7=PA0-WKUP
Mcu.Pin8=PA1
Mcu.Pin9=PA2
Mcu.PinsNb=34
Mcu.Pin30=PD5
Mcu.Pin31=PB3
Mcu.Pin32=PB4
Mcu.Pin33=PB5
Mcu.Pin34=PB7
Mcu.Pin35=VP_SYS_VS_tim8
Mcu.Pin36=VP_TIM3_VS_ClockSourceINT
Mcu.Pin37=VP_RealThread.X-CUBE-RT-Thread_Nano_VS_RTOSJjRTAaThread_4.1.1_4.1.1
Mcu.Pin38=VP_STMicroelectronics.X-CUBE-ALGOBUILD_VS_DSPOoLibraryJjLibrary_1.4.0_1.4.0
Mcu.Pin4=PF1
Mcu.Pin5=OSC_IN
Mcu.Pin6=OSC_OUT
Mcu.Pin7=PC1
Mcu.Pin8=PA0-WKUP
Mcu.Pin9=PA1
Mcu.PinsNb=39
Mcu.ThirdParty0=RealThread.X-CUBE-RT-Thread_Nano.4.1.1
Mcu.ThirdParty1=STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0
Mcu.ThirdPartyNb=2
Mcu.UserConstants=TEC_DEAD_TIME,100;AD7793_SPI,$$_SPI3_IP_HANDLE_$$;RS485_COM,$$_UART5_IP_HANDLE_$$;TEC_TIM_COUNTER,143;SAMPLING_TIM_PRE,71;US_TIM_PERIOD_COUNT,9999;TEC_TIM_PRESCALER,99;TEC_TIM,$$_TIM1_IP_HANDLE_$$;HMI_COM,$$_USART2_IP_HANDLE_$$;SAMPLING_TIM,$$_TIM3_IP_HANDLE_$$;US_TIM_PRE,71;TIMESTAMP_TIM,$$_TIM4_IP_HANDLE_$$;ENCODER_TIM,$$_TIM2_IP_HANDLE_$$;SAMPLING_TIM_PERIOD_COUNT,877;SERVO_COM,$$_USART3_IP_HANDLE_$$;LIGHT_ADC,$$_ADC1_IP_HANDLE_$$;ENCODER_MAX_COUNT,65535
Mcu.UserConstants=HMI_COM,$$_USART2_IP_HANDLE_$$;ENCODER_MAX_COUNT,65535;AD7793_SPI,$$_SPI3_IP_HANDLE_$$;US_TIM_PRE,71;RS485_COM,$$_UART5_IP_HANDLE_$$;ENCODER_TIM,$$_TIM2_IP_HANDLE_$$;TEC_TIM_COUNTER,143;US_TIM_PERIOD_COUNT,9999;TEC_TIM_PRESCALER,99;SERVO_COM,$$_USART3_IP_HANDLE_$$;TEC_TIM,$$_TIM1_IP_HANDLE_$$;COLLECT_TIM,$$_TIM3_IP_HANDLE_$$;ADS8866_SPI,$$_SPI2_IP_HANDLE_$$
Mcu.UserName=STM32F103ZETx
MxCube.Version=6.15.0
MxDb.Version=DB.6.0.150
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.DMA1_Channel1_IRQn=true\:0\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Channel4_IRQn=true\:0\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Channel5_IRQn=true\:0\:0\:false\:false\:true\:true\:false\:true\:true
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.ForceEnableDMAVector=true
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false\:false
@ -107,7 +118,6 @@ NVIC.SavedSystickIrqHandlerGenerated=false
NVIC.SysTick_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:true\:false
NVIC.TIM2_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true\:true
NVIC.TIM3_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true\:true
NVIC.TIM4_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true\:true
NVIC.TIM8_UP_IRQn=true\:15\:0\:false\:false\:true\:false\:false\:true\:true
NVIC.TimeBase=TIM8_UP_IRQn
NVIC.TimeBaseIP=TIM8
@ -142,9 +152,16 @@ PA3.GPIOParameters=GPIO_Label
PA3.GPIO_Label=UART2_RX_LCD
PA3.Mode=Asynchronous
PA3.Signal=USART2_RX
PA8.GPIOParameters=GPIO_Speed,GPIO_Label
PA8.GPIO_Label=TIM1_CH1_TEC
PA8.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PA6.GPIOParameters=GPIO_Label
PA6.GPIO_Label=TEC_LEFT_SD
PA6.Locked=true
PA6.Signal=GPIO_Output
PA7.GPIOParameters=GPIO_Label
PA7.GPIO_Label=TEC_RIGHT_SD
PA7.Locked=true
PA7.Signal=GPIO_Output
PA8.GPIOParameters=GPIO_Label
PA8.GPIO_Label=TEC_RIGHT_IN
PA8.Locked=true
PA8.Signal=S_TIM1_CH1
PA9.GPIOParameters=GPIO_Label
@ -152,6 +169,10 @@ PA9.GPIO_Label=UART1_LCD_TX
PA9.Locked=true
PA9.Mode=Asynchronous
PA9.Signal=USART1_TX
PB0.GPIOParameters=GPIO_Label
PB0.GPIO_Label=ADS8866_CONVST
PB0.Locked=true
PB0.Signal=S_TIM3_CH3
PB10.GPIOParameters=GPIO_Label
PB10.GPIO_Label=I2C2_EEPROM_SCL
PB10.Locked=true
@ -162,30 +183,36 @@ PB11.GPIO_Label=I2C2_EEPROM_SDA
PB11.Locked=true
PB11.Mode=I2C
PB11.Signal=I2C2_SDA
PB13.GPIOParameters=GPIO_Speed,GPIO_Label
PB13.GPIO_Label=TIM1_CH1N_TEC
PB13.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PB13.Locked=true
PB13.Mode=PWM Generation1 CH1 CH1N
PB13.Signal=TIM1_CH1N
PB13.GPIOParameters=GPIO_Label
PB13.GPIO_Label=SPI2_ADS8866_SCK
PB13.Mode=Full_Duplex_Master
PB13.Signal=SPI2_SCK
PB14.GPIOParameters=GPIO_Label
PB14.GPIO_Label=SPI2_ADS8866_MISO
PB14.Mode=Full_Duplex_Master
PB14.Signal=SPI2_MISO
PB15.GPIOParameters=GPIO_Label
PB15.GPIO_Label=SPI2_ADS8866_MOSI
PB15.Mode=Full_Duplex_Master
PB15.Signal=SPI2_MOSI
PB3.GPIOParameters=GPIO_Label
PB3.GPIO_Label=SPI3_SCK_AD7793
PB3.GPIO_Label=SPI3_AD7793_SCK
PB3.Mode=Full_Duplex_Master
PB3.Signal=SPI3_SCK
PB4.GPIOParameters=GPIO_Label
PB4.GPIO_Label=SPI3_MISO_AD7793
PB4.GPIO_Label=SPI3_AD7793_MISO
PB4.Mode=Full_Duplex_Master
PB4.Signal=SPI3_MISO
PB5.GPIOParameters=GPIO_Label
PB5.GPIO_Label=SPI3_MOSI_AD7793
PB5.GPIO_Label=SPI3_AD7793_MOSI
PB5.Mode=Full_Duplex_Master
PB5.Signal=SPI3_MOSI
PB7.GPIOParameters=GPIO_Label
PB7.GPIO_Label=SPI3_CS_AD7793
PB7.GPIO_Label=SPI3_AD7793_CS
PB7.Locked=true
PB7.Signal=GPIO_Output
PC0.Locked=true
PC0.Signal=ADCx_IN10
PC1.Locked=true
PC1.Signal=ADCx_IN11
PC10.GPIOParameters=GPIO_Label
PC10.GPIO_Label=UART3_SERVO_TX
PC10.Locked=true
@ -229,6 +256,10 @@ PE6.GPIO_Label=PE6_LED2
PE6.Locked=true
PE6.PinState=GPIO_PIN_SET
PE6.Signal=GPIO_Output
PF1.GPIOParameters=GPIO_Label
PF1.GPIO_Label=RS485_DE
PF1.Locked=true
PF1.Signal=GPIO_Output
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
@ -263,7 +294,7 @@ ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-true-HAL-false,2-MX_GPIO_Init-GPIO-true-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_I2C2_Init-I2C2-true-HAL-true,5-MX_USART1_UART_Init-USART1-true-HAL-true,6-MX_USART2_UART_Init-USART2-true-HAL-true,7-MX_UART5_Init-UART5-true-HAL-true,8-MX_USART3_UART_Init-USART3-true-HAL-true,9-MX_TIM1_Init-TIM1-true-HAL-true,10-MX_SPI3_Init-SPI3-true-HAL-true,11-MX_ADC1_Init-ADC1-true-HAL-true,12-MX_TIM2_Init-TIM2-true-HAL-true,13-MX_TIM3_Init-TIM3-true-HAL-true,14-MX_TIM4_Init-TIM4-true-HAL-true
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-true-HAL-false,2-MX_GPIO_Init-GPIO-true-HAL-true,3-MX_DMA_Init-DMA-true-HAL-true,4-MX_I2C2_Init-I2C2-true-HAL-true,5-MX_USART1_UART_Init-USART1-true-HAL-true,6-MX_USART2_UART_Init-USART2-true-HAL-true,7-MX_UART5_Init-UART5-true-HAL-true,8-MX_USART3_UART_Init-USART3-true-HAL-true,9-MX_SPI3_Init-SPI3-true-HAL-true,10-MX_ADC1_Init-ADC1-true-HAL-true,11-MX_TIM2_Init-TIM2-true-HAL-true,12-MX_TIM3_Init-TIM3-true-HAL-true,13-MX_SPI2_Init-SPI2-true-HAL-true,14-MX_TIM1_Init-TIM1-true-HAL-true
RCC.ADCFreqValue=12000000
RCC.ADCPresc=RCC_ADCPCLK2_DIV6
RCC.AHBFreq_Value=72000000
@ -307,14 +338,24 @@ RealThread.X-CUBE-RT-Thread_Nano.4.1.1.RT_USING_SIGNALS=0
RealThread.X-CUBE-RT-Thread_Nano.4.1.1.RT_USING_SMALL_MEM=1
RealThread.X-CUBE-RT-Thread_Nano.4.1.1.RT_USING_SMALL_MEM_AS_HEAP=1
RealThread.X-CUBE-RT-Thread_Nano.4.1.1_SwParameter=RTAaThreadCcRTOSJjshell\:true;RTAaThreadCcRTOSJjlibcpu\:true;RTAaThreadCcRTOSJjkernel\:true;
SH.ADCx_IN10.0=ADC1_IN10,IN10
SH.ADCx_IN10.ConfNb=1
SH.S_TIM1_CH1.0=TIM1_CH1,PWM Generation1 CH1 CH1N
SH.ADCx_IN11.0=ADC1_IN11,IN11
SH.ADCx_IN11.ConfNb=1
SH.S_TIM1_CH1.0=TIM1_CH1,PWM Generation1 CH1
SH.S_TIM1_CH1.ConfNb=1
SH.S_TIM2_CH1_ETR.0=TIM2_CH1,Encoder_Interface
SH.S_TIM2_CH1_ETR.ConfNb=1
SH.S_TIM2_CH2.0=TIM2_CH2,Encoder_Interface
SH.S_TIM2_CH2.ConfNb=1
SH.S_TIM3_CH3.0=TIM3_CH3,PWM Generation3 CH3
SH.S_TIM3_CH3.ConfNb=1
SPI2.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16
SPI2.CLKPhase=SPI_PHASE_2EDGE
SPI2.CalculateBaudRate=2.25 MBits/s
SPI2.DataSize=SPI_DATASIZE_16BIT
SPI2.Direction=SPI_DIRECTION_2LINES
SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,CLKPhase,BaudRatePrescaler
SPI2.Mode=SPI_MODE_MASTER
SPI2.VirtualType=VM_MASTER
SPI3.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16
SPI3.CLKPhase=SPI_PHASE_2EDGE
SPI3.CLKPolarity=SPI_POLARITY_HIGH
@ -327,14 +368,11 @@ STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.DSPOoLibraryJjLibrary_Checked=true
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.IPParameters=LibraryCcDSPOoLibraryJjDSPOoLibrary
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.LibraryCcDSPOoLibraryJjDSPOoLibrary=true
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0_SwParameter=LibraryCcDSPOoLibraryJjDSPOoLibrary\:true;
TIM1.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
TIM1.Channel-PWM\ Generation1\ CH1\ CH1N=TIM_CHANNEL_1
TIM1.DeadTime=TEC_DEAD_TIME
TIM1.IPParameters=Channel-PWM Generation1 CH1 CH1N,Period,Prescaler,AutoReloadPreload,DeadTime,OCNPolarity_1,OCNIdleState_1
TIM1.OCNIdleState_1=TIM_OCNIDLESTATE_RESET
TIM1.OCNPolarity_1=TIM_OCNPOLARITY_HIGH
TIM1.Period=TEC_TIM_COUNTER
TIM1.Prescaler=TEC_TIM_PRESCALER
TIM1.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
TIM1.IPParameters=Prescaler,Period,Channel-PWM Generation1 CH1,Pulse-PWM Generation1 CH1
TIM1.Period=899
TIM1.Prescaler=7
TIM1.Pulse-PWM\ Generation1\ CH1=100
TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
TIM2.EncoderMode=TIM_ENCODERMODE_TI12
TIM2.IC1Filter=6
@ -344,13 +382,11 @@ TIM2.Period=ENCODER_MAX_COUNT
TIM2.TIM_MasterOutputTrigger=TIM_TRGO_RESET
TIM2.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE
TIM3.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
TIM3.IPParameters=Prescaler,Period,AutoReloadPreload
TIM3.Period=SAMPLING_TIM_PERIOD_COUNT
TIM3.Prescaler=SAMPLING_TIM_PRE
TIM4.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
TIM4.IPParameters=Prescaler,AutoReloadPreload,Period
TIM4.Period=US_TIM_PERIOD_COUNT
TIM4.Prescaler=US_TIM_PRE
TIM3.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
TIM3.IPParameters=Prescaler,Period,AutoReloadPreload,Channel-PWM Generation3 CH3,Pulse-PWM Generation3 CH3
TIM3.Period=499
TIM3.Prescaler=71
TIM3.Pulse-PWM\ Generation3\ CH3=10
UART5.BaudRate=9600
UART5.IPParameters=VirtualMode,BaudRate
UART5.VirtualMode=Asynchronous
@ -369,6 +405,4 @@ VP_SYS_VS_tim8.Mode=TIM8
VP_SYS_VS_tim8.Signal=SYS_VS_tim8
VP_TIM3_VS_ClockSourceINT.Mode=Internal
VP_TIM3_VS_ClockSourceINT.Signal=TIM3_VS_ClockSourceINT
VP_TIM4_VS_ClockSourceINT.Mode=Internal
VP_TIM4_VS_ClockSourceINT.Signal=TIM4_VS_ClockSourceINT
board=custom