This commit is contained in:
许晟昊 2025-01-21 11:08:21 +08:00
parent 1e58b0a5d1
commit f0d23f8870
945 changed files with 270180 additions and 0 deletions

416
User/system/bsp/adcs.c Normal file
View File

@ -0,0 +1,416 @@
/**
* @file adcs.c
* @author xxx
* @date 2023-09-04 15:59:16
* @brief LL库ADC驱动 STM32F407
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "adcs.h"
#include "dmas.h"
adcs_t adcs[ADCS_MAX];
static uint8_t adc_get_channels_count(uint32_t channnels); // 通过用户配置的通道号获取通道数量
/**
* @brief ADC初始化
* @param {adcs_e} num ADC编号
* @param {ADC_TypeDef} *adc ADC外设
* @param {DMA_TypeDef} *dma DMA外设
* @param {uint32_t} dma_channel DMA通道
* @param {uint8_t} adc_cct ADC采样次数
* @param {uint32_t} channels
* @return {*}
* @note TCONV() = ( + 12.5 )/(/ADC分频系数)
*/
void adc_init(adcs_e num, ADC_TypeDef *adc, DMA_TypeDef *dma, uint32_t dma_stream, uint32_t dma_channel, uint16_t adc_cct, uint32_t channels)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
DBG_ASSERT(adc != NULL __DBG_LINE);
DBG_ASSERT(dma != NULL __DBG_LINE);
DBG_ASSERT(adc_cct > 0 __DBG_LINE);
adcs_t *p = &adcs[num];
osel_memset((uint8_t *)p, 0, sizeof(adcs_t));
p->adc = adc;
p->dma = dma;
p->dma_stream = dma_stream;
p->dma_channel = dma_channel;
p->channels.data = channels;
p->adc_cct = adc_cct;
p->adc_chans_count = adc_get_channels_count(channels);
p->adc_sum = adc_cct * p->adc_chans_count;
#if defined(SRAM2_BASE) // SRAM2速度更快
p->adc_value = (uint16_t *)osel_mem_alloc2(sizeof(uint16_t) * p->adc_sum);
#else
p->adc_value = (uint16_t *)osel_mem_alloc(sizeof(uint16_t) * p->adc_sum);
#endif
DBG_ASSERT(p->adc_value != NULL __DBG_LINE);
osel_memset((uint8_t *)p->adc_value, 0, sizeof(uint16_t) * p->adc_sum);
LL_DMA_SetChannelSelection(p->dma, p->dma_stream, p->dma_channel);
LL_DMA_ConfigTransfer(p->dma, p->dma_stream,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY |
LL_DMA_MODE_CIRCULAR |
LL_DMA_PERIPH_NOINCREMENT |
LL_DMA_MEMORY_INCREMENT |
LL_DMA_PDATAALIGN_HALFWORD |
LL_DMA_MDATAALIGN_HALFWORD |
LL_DMA_PRIORITY_HIGH);
LL_DMA_ConfigAddresses(p->dma, p->dma_stream,
LL_ADC_DMA_GetRegAddr(p->adc, LL_ADC_DMA_REG_REGULAR_DATA),
(uint32_t)p->adc_value,
LL_DMA_GetDataTransferDirection(p->dma, p->dma_stream));
LL_DMA_SetDataLength(p->dma, p->dma_stream, p->adc_sum);
LL_DMA_EnableStream(p->dma, p->dma_stream);
LL_ADC_REG_SetContinuousMode(p->adc, LL_ADC_REG_CONV_CONTINUOUS);
LL_ADC_REG_SetDMATransfer(p->adc, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
if (BIT_IS_SET(channels, INVREF))
{
// 使能VREFINT
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(p->adc), LL_ADC_PATH_INTERNAL_VREFINT);
}
if (BIT_IS_SET(channels, INTEMP))
{
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(p->adc), LL_ADC_PATH_INTERNAL_TEMPSENSOR);
}
LL_mDelay(10);
// 启动 ADC 转换
LL_ADC_Enable(p->adc);
LL_ADC_REG_StartConversionSWStart(p->adc);
}
/**
* @brief
*
* ADCS ADC
*
* @param num ADCS
*/
void start_sample(adcs_e num)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
// adcs_t *p = &adcs[num];
}
/**
* @brief
*
* ADCS ADCS
*
* @param num ADCS
*/
void stop_sample(adcs_e num)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
// adcs_t *p = &adcs[num];
}
/**
* @brief ADC重新开始转换
* @param {adcs_e} num ADC编号
* @return {*}
* @note
*/
void adc_restart(adcs_e num)
{
stop_sample(num);
start_sample(num);
}
/**
* @brief ADC反初始化
* @param {adcs_e} num ADC编号
* @return {*}
* @note
*/
void adc_dinit(adcs_e num)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
adcs_t *p = &adcs[num];
LL_ADC_Disable(p->adc);
if (p->adc_value != NULL)
{
#if defined(SRAM2_BASE)
osel_mem_free2((uint16_t *)p->adc_value);
#else
osel_mem_free((uint16_t *)p->adc_value);
#endif
}
}
/**
* @brief ADC转换结果,
* @param {adcs_e} num
* @param {uint8_t} chan
* @return {*}
* @note
*/
uint16_t adc_result_only_one(adcs_e num, uint8_t chan)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
adcs_t *p = &adcs[num];
DBG_ASSERT(p != NULL __DBG_LINE);
uint16_t(*gram)[p->adc_chans_count] = (uint16_t(*)[p->adc_chans_count])p->adc_value;
return gram[0][chan];
}
/**
* @brief ,ADC转换结果
* @param {adcs_e} num ADC编号
* @param {uint8_t} chan
* @return {*}
* @note 使O(n^2)
*/
uint16_t adc_result_median_average(adcs_e num, uint8_t chan)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
adcs_t *p = &adcs[num];
DBG_ASSERT(p != NULL __DBG_LINE);
if (p->adc_cct <= 2)
return 0; // 如果adc_cct小于等于2直接返回0
uint16_t adc_temp[p->adc_cct];
uint32_t adc_sum = 0;
uint16_t count = p->adc_cct >> 2; // 使用位移操作计算n的值
// 减少重复计算,计算基础偏移量
uint16_t *adc_values = (uint16_t *)p->adc_value + chan;
for (uint16_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count)
{
adc_temp[i] = *adc_values;
}
insertion_sort(adc_temp, p->adc_cct);
// 计算中间部分的和
for (uint16_t i = count; i < p->adc_cct - count; ++i)
{
adc_sum += adc_temp[i];
}
// 计算平均值确保不会除以0
uint16_t res = adc_sum / (p->adc_cct - (count << 1));
return res;
}
/**
* @brief ,ADC转换结果
* @param {adcs_e} num ADC编号
* @param {uint8_t} chan
* @return {*}
* @note 使O(n^2)
*/
uint16_t adc_result_median(adcs_e num, uint8_t chan)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
uint16_t res = 0;
adcs_t *p = &adcs[num];
DBG_ASSERT(p != NULL __DBG_LINE);
uint16_t adc_temp[p->adc_cct];
// 减少重复计算,计算基础偏移量
uint16_t *adc_values = (uint16_t *)p->adc_value + chan;
for (uint16_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count)
{
adc_temp[i] = *adc_values;
}
insertion_sort(adc_temp, p->adc_cct);
res = adc_temp[p->adc_cct >> 1];
return res;
}
/**
* @brief ,ADC转换结果
* @param {adcs_e} num ADC编号
* @param {uint8_t} chan
* @return {*}
* @note
*/
uint16_t adc_result_average(adcs_e num, uint8_t chan)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
uint16_t res = 0;
uint32_t adc_sum = 0;
adcs_t *p = &adcs[num];
DBG_ASSERT(p != NULL __DBG_LINE);
// 减少重复计算,计算基础偏移量
uint16_t *adc_values = (uint16_t *)p->adc_value + chan;
for (uint16_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count)
{
adc_sum += *adc_values;
}
res = adc_sum / p->adc_cct;
return res;
}
/**
* @brief
* @param {uint16_t} adc_value ADC转换结果
* @return {*}
* @note (measure * VDD_APPLI / VDD_CALIB) - (int32_t)*TEMP30_CAL_ADDR) * (int32_t)(130 - 30) / (int32_t)(*TEMP130_CAL_ADDR - *TEMP30_CAL_ADDR)) + 30
* adc_value ADC读取的温度传感器数据
TS_CAL1 30°C时校准的温度传感器数据TEMPSENSOR_CAL1_ADDR
TS_CAL2 110°C时校准的温度传感器数据TEMPSENSOR_CAL2_ADDR
TEMPSENSOR_CAL1_TEMP 30°C
TEMPSENSOR_CAL2_TEMP 110°C
*/
float32 adc_result_temperature(uint16_t adc_value)
{
uint16_t ts_cal1 = *TEMPSENSOR_CAL1_ADDR;
uint16_t ts_cal2 = *TEMPSENSOR_CAL2_ADDR;
float32 temperature = ((float32)(adc_value - ts_cal1) / (ts_cal2 - ts_cal1)) * (TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP) + TEMPSENSOR_CAL1_TEMP;
return temperature;
}
/**
* @brief
* @param {uint16_t} adc_value ADC转换结果
* @return {*} V
* @note
*/
float32 adc_result_value_local(uint16_t adc_value)
{
float32 vdd = (VREFINT_CAL_VREF * (*VREFINT_CAL_ADDR)) / 4095;
return ((vdd / adc_value) * 4095) / 1000;
}
/**
* @brief ADC DMA转换回调函数
{
* @param {adcs_e} num ADC编号
* @return {*}
* @note
*/
void adc_dma_callback(adcs_e num)
{
adcs_t *p = &adcs[num];
DBG_ASSERT(p != NULL __DBG_LINE);
if (LL_DMA_IsActiveFlag_TC1(p->dma) != 0)
{
LL_DMA_ClearFlag_TC1(p->dma);
}
// 检查DMA1的传输错误标志是否为1如果是则清除该标志
if (LL_DMA_IsActiveFlag_TE1(p->dma) != 0)
{
LL_DMA_ClearFlag_TE1(p->dma);
}
}
/**
* @brief ADC回调函数
* @param {adcs_e} num ADC编号
* @return {*}
* @note
*/
void adc_env_callback(adcs_e num)
{
adcs_t *p = &adcs[num];
DBG_ASSERT(p != NULL __DBG_LINE);
if (LL_ADC_IsActiveFlag_OVR(p->adc) != 0)
{
p->ovr_count++;
LL_ADC_ClearFlag_OVR(p->adc);
}
}
/**
* @brief
* @param {uint32_t} channnels
* @return {*}
* @note
*/
static uint8_t adc_get_channels_count(uint32_t channnels)
{
uint8_t ch_num = 0;
if (BIT_IS_SET(channnels, IN0))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN1))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN2))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN3))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN4))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN5))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN6))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN7))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN8))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN9))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN10))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN11))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN12))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN13))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN14))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN15))
{
ch_num++;
}
if (BIT_IS_SET(channnels, IN16))
{
ch_num++;
}
if (BIT_IS_SET(channnels, INVREF))
{
ch_num++;
}
if (BIT_IS_SET(channnels, INVBAT))
{
ch_num++;
}
if (BIT_IS_SET(channnels, INTEMP))
{
ch_num++;
}
return ch_num;
}

228
User/system/bsp/adcs.h Normal file
View File

@ -0,0 +1,228 @@
/**
* @file adcs.h
* @brief Header file for ADC driver using LL library.
*
* This file contains the declarations and documentation for the ADC driver
* using the LL (Low-Level) library.
*
* @date 2023-09-04 15:59:16
* @author xxx
* @version 1.0
*
* @note This code is proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*
* @note All rights reserved by xxx.
*/
#ifndef __ADCS_H__
#define __ADCS_H__
#include "sys.h"
#define ADC_CHANNEL_MAX 18 ///< Maximum number of ADC channels
typedef enum
{
IN0 = BIT0,
IN1 = BIT1,
IN2 = BIT2,
IN3 = BIT3,
IN4 = BIT4,
IN5 = BIT5,
IN6 = BIT6,
IN7 = BIT7,
IN8 = BIT8,
IN9 = BIT9,
IN10 = BIT10,
IN11 = BIT11,
IN12 = BIT12,
IN13 = BIT13,
IN14 = BIT14,
IN15 = BIT15,
IN16 = BIT16,
INTEMP = BIT17,
INVBAT = BIT18,
INVREF = BIT19,
} adc_num_e; ///< ADC channel number
typedef enum
{
ADCS_1,
ADCS_2,
ADCS_3,
ADCS_MAX,
} adcs_e; ///< ADC number
typedef union
{
uint32_t data;
struct
{
uint32_t in0 : 1;
uint32_t in1 : 1;
uint32_t in2 : 1;
uint32_t in3 : 1;
uint32_t in4 : 1;
uint32_t in5 : 1;
uint32_t in6 : 1;
uint32_t in7 : 1;
uint32_t in8 : 1;
uint32_t in9 : 1;
uint32_t in10 : 1;
uint32_t in11 : 1;
uint32_t in12 : 1;
uint32_t in13 : 1;
uint32_t in14 : 1;
uint32_t in15 : 1;
uint32_t invref : 1;
uint32_t intemp : 1;
};
} adcs_channels_u; ///< ADC channels
typedef struct
{
ADC_TypeDef *adc; ///< ADC peripheral
DMA_TypeDef *dma; ///< DMA peripheral
uint32_t dma_stream; ///< DMA stream
uint32_t dma_channel; ///< DMA channel
adcs_channels_u channels; ///< ADC channels
uint32_t ovr_count; ///< ADC overflow count
uint16_t adc_cct; ///< Channel single acquisition count
uint8_t adc_chans_count; ///< Number of channels
uint16_t adc_sum; ///< Channel acquisition count
__IO uint16_t *adc_value; ///< Address to store ADC conversion results
} adcs_t;
/**
* @brief Initializes the ADC module.
*
* This function initializes the ADC module with the specified parameters.
*
* @param num The ADC number.
* @param adc Pointer to the ADC peripheral.
* @param dma Pointer to the DMA peripheral.
* @param dma_channel The DMA channel number.
* @param adc_cct The ADC continuous conversion mode.
* @param channels The number of ADC channels to be converted.
*/
extern void adc_init(adcs_e num, ADC_TypeDef *adc, DMA_TypeDef *dma, uint32_t dma_stream, uint32_t dma_channel, uint16_t adc_cct, uint32_t channels);
/**
* @brief Starts the ADC conversion.
*
* This function starts the ADC conversion for the specified ADC number.
*
* @param num The ADC number.
*/
extern void adc_restart(adcs_e num);
/**
* @brief Deinitializes the ADC module.
*
* This function deinitializes the ADC module.
*
* @param num The ADC number.
*/
extern void adc_dinit(adcs_e num);
/**
* @brief Gets the ADC conversion result for a single channel.
*
* This function gets the ADC conversion result for a single channel.
* It returns only the first converted value.
*
* @param num The ADC number.
* @param chan The ADC channel number.
* @return The ADC conversion result.
*/
extern uint16_t adc_result_only_one(adcs_e num, uint8_t chan);
/**
* @brief Gets the ADC conversion result using median average filtering.
*
* This function gets the ADC conversion result for a single channel.
* It applies median average filtering to the converted values.
*
* @param num The ADC number.
* @param chan The ADC channel number.
* @return The ADC conversion result.
*/
extern uint16_t adc_result_median_average(adcs_e num, uint8_t chan);
/**
* @brief Gets the ADC conversion result using median filtering.
*
* This function gets the ADC conversion result for a single channel.
* It applies median filtering to the converted values.
*
* @param num The ADC number.
* @param chan The ADC channel number.
* @return The ADC conversion result.
*/
extern uint16_t adc_result_median(adcs_e num, uint8_t chan);
/**
* @brief Gets the ADC conversion result using average filtering.
*
* This function gets the ADC conversion result for a single channel.
* It applies average filtering to the converted values.
*
* @param num The ADC number.
* @param chan The ADC channel number.
* @return The ADC conversion result.
*/
extern uint16_t adc_result_average(adcs_e num, uint8_t chan);
/**
* @brief Gets the ADC conversion result using N-times average filtering.
*
* This function gets the ADC conversion result for a single channel.
* It applies N-times average filtering to the converted values.
*
* @param num The ADC number.
* @param chan The ADC channel number.
* @return The ADC conversion result.
*/
extern uint16_t adc_result_n_average(adcs_e num, uint8_t chan);
/**
* @brief Calculates the temperature from the ADC conversion result.
*
* This function calculates the temperature in degrees Celsius
* from the ADC conversion result of the internal temperature sensor.
*
* @param adc_value The ADC conversion result.
* @return The temperature in degrees Celsius.
*/
extern float32 adc_result_temperature(uint16_t adc_value);
/**
* @brief Calculates the voltage from the ADC conversion result.
*
* This function calculates the voltage in millivolts
* from the ADC conversion result of the internal voltage reference.
*
* @param adc_value The ADC conversion result.
* @return The voltage in millivolts.
*/
extern float32 adc_result_value_local(uint16_t adc_value);
/**
* @brief ADC environment callback function.
*
* This function is called when an ADC conversion is completed.
*
* @param num The ADC number.
*/
extern void adc_env_callback(adcs_e num);
/**
* @brief DMA callback function for ADC.
*
* This function is called when a DMA transfer for ADC is completed.
*
* @param num The ADC number.
*/
extern void adc_dma_callback(adcs_e num);
#endif ///< __ADCS_H__

90
User/system/bsp/bsp.c Normal file
View File

@ -0,0 +1,90 @@
#include "bsp.h"
#define EXIT_LINE LL_EXTI_LINE_16
pvd_irq_handle_cb pvd_irq_handle_cb_func = NULL;
/**
* @brief PVD
*
* PVD
*
* @param pwr_level
* @param call PVD中断处理回调函数
*/
void pvd_configuration(uint32_t pwr_level, pvd_irq_handle_cb call)
{
// pvd_irq_handle_cb_func = call;
// // 启用电源时钟
// LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
// // 设置PVD电平阈值例如设置为2.4V LL_PWR_PVDLEVEL_2
// LL_PWR_SetPVDLevel(pwr_level);
// // 启用PVD
// LL_PWR_EnablePVD();
// // 配置PVD中断
// LL_EXTI_EnableIT_0_31(EXIT_LINE); // PVD连接到EXTI Line
// LL_EXTI_EnableRisingTrig_0_31(EXIT_LINE);
// LL_EXTI_EnableFallingTrig_0_31(EXIT_LINE);
// // 启用PVD中断向量
// NVIC_EnableIRQ(PVD_PVM_IRQn);
// NVIC_SetPriority(PVD_PVM_IRQn, 0);
}
/**
* @brief PVD中断
*
* PVD中断触发时
*
* @note
*/
void pvd_irq_handle(void)
{
// if (LL_EXTI_IsActiveFlag_0_31(EXIT_LINE))
// {
// LL_EXTI_ClearFlag_0_31(EXIT_LINE);
// if (pvd_irq_handle_cb_func != NULL)
// {
// pvd_irq_handle_cb_func();
// }
// }
}
/**
* @brief
*
*
* SWD JTAG GPIO
*
* @note disable_debug_interface调用后 SWD ST-LINK SWD MCU,使 STM32 ST-LINK Utility ST-LINK MCU解除读保护
* :Read Out Protection : Level 0 Level 0 Flash Level 2 Level 2 MCU
*/
void disable_debug_interface(void)
{
// // 使能 SYSCFG 时钟
// LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
// // 关闭调试接口
// LL_DBGMCU_DisableDBGStopMode();
// LL_DBGMCU_DisableDBGStandbyMode();
// LL_DBGMCU_DisableDBGSleepMode();
// // 关闭 SWD 和 JTAG 接口
// LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
// // 配置 SWDIO (PA13) 和 SWCLK (PA14) 引脚为普通 GPIO
// GPIO_InitStruct.Pin = LL_GPIO_PIN_13 | LL_GPIO_PIN_14;
// GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
// LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// // 如果使用的是 JTAG 接口,还需要配置 JTDI (PA15), JTDO (PB3), 和 NJTRST (PB4) 引脚
// GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
// LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// GPIO_InitStruct.Pin = LL_GPIO_PIN_3 | LL_GPIO_PIN_4;
// LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

33
User/system/bsp/bsp.h Normal file
View File

@ -0,0 +1,33 @@
/**
* @file bsp.h
* @brief This file contains the declarations and definitions for the BSP (Board Support Package) module.
*
* The BSP module provides functions and configurations specific to the hardware platform, such as initializing
* peripherals, configuring GPIO pins, and managing interrupts.
*
* @author xxx
* @date 2023-12-27 14:44:03
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __BSP_H__
#define __BSP_H__
#include "gpios.h"
// #include "dmas.h"
// #include "adcs.h"
// #include "dacs.h"
// #include "tims.h"
// #include "pwms.h"
// #include "uarts.h"
// #include "eeprom.h"
// #include "spis.h"
// #include "i2cs.h"
///< 定义回调函数类型
typedef void (*pvd_irq_handle_cb)(void);
extern void pvd_configuration(uint32_t pwr_level, pvd_irq_handle_cb call); ///< Configures the Programmable Voltage Detector (PVD) module
extern void pvd_irq_handle(void); ///< Handles the PVD interrupt
extern void disable_debug_interface(void); ///< Disables the debug interface
#endif ///< __BSP_H__

52
User/system/bsp/dacs.c Normal file
View File

@ -0,0 +1,52 @@
/**
* @file dacs.c
* @brief This file contains the implementation of the DAC module.
* It provides functions to initialize and de-initialize a DAC instance,
* as well as write a 16-bit value to the specified DAC channel.
* @author xxx
* @date 2023-12-27 14:44:03
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#include "dacs.h"
/**
* @brief Writes a 16-bit value to the specified DAC channel
*
* @param dac pointer to the DAC instance
* @param value 16-bit value to write to the DAC
*/
static void _out(dac_t *dac, uint16_t value)
{
DBG_ASSERT(dac != NULL __DBG_LINE);
DAC_OUT(dac->dac, dac->dac_channel, value);
}
/**
* @brief Initializes a DAC instance
*
* @param dac pointer to the DAC instance
* @param dac_channel DAC channel to use
* @return pointer to the initialized DAC instance
*/
dac_t *dac_create(DAC_TypeDef *dac, uint16_t dac_channel)
{
DBG_ASSERT(dac != NULL __DBG_LINE);
dac_t *handle = (dac_t *)osel_mem_alloc(sizeof(dac_t));
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->dac = dac;
handle->dac_channel = dac_channel;
handle->out = _out;
return handle;
}
/**
* @brief De-initializes a DAC instance
*
* @param dac pointer to the DAC instance
*/
void dac_free(dac_t *dac)
{
DBG_ASSERT(dac != NULL __DBG_LINE);
osel_mem_free(dac);
}

55
User/system/bsp/dacs.h Normal file
View File

@ -0,0 +1,55 @@
/**
* @file dacs.h
* @brief Header file for DACs module.
*
* This file contains the declarations and definitions for the DACs module.
* DACs (Digital-to-Analog Converters) are used to convert digital signals into analog voltages.
*
* @author xxx
* @date 2023-12-27 14:44:03
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __DACS_H__
#define __DACS_H__
#include "dac.h"
#include "lib.h"
#include "main.h"
/**
* @brief Set the output value for a specific DAC channel.
* @param dac: pointer to the @ref dac_t structure that contains the configuration information for the specified DAC.
* @param value: the output value to be set.
* @retval None
*/
#define DAC_OUT(DACx, DAC_Channel, Data) \
do \
{ \
LL_DAC_ConvertData12RightAligned(DACx, DAC_Channel, Data); \
LL_DAC_TrigSWConversion(DACx, DAC_Channel); \
} while (__LINE__ == -1)
/**
* @brief Enable the specified DAC channel.
* @param dac: pointer to the @ref dac_t structure that contains the configuration information for the specified DAC.
* @retval None
*/
#define DAC_START(DACx, DAC_Channel) LL_DAC_Enable(DACx, DAC_Channel)
/**
* @brief Disable the specified DAC channel.
* @param dac: pointer to the @ref dac_t structure that contains the configuration information for the specified DAC.
* @retval None
*/
#define DAC_STOP(DACx, DAC_Channel) LL_DAC_Disable(DACx, DAC_Channel)
/**
* @brief Structure definition for the DAC driver.
*/
typedef struct DACS
{
DAC_TypeDef *dac;
uint16_t dac_channel;
void (*out)(struct DACS *dac, uint16_t value);
} dac_t;
#endif

135
User/system/bsp/dmas.h Normal file
View File

@ -0,0 +1,135 @@
#ifndef __DMAS_H__
#define __DMAS_H__
/**
* @brief DMA传输完成标志
* @param {DMA_HandleTypeDef} *DMAX DMA总线句柄
* @param {uint32_t} CHx DMA通道号
* @return {*}
* @note: DMA总线的传输完成标志DMA总线的传输完成标志是否已置位
*/
#define DMA_ClEAR_FLAG_TC(DMAX, CHx) \
do \
{ \
if (LL_DMA_IsActiveFlag_TC##CHx(DMAX)) \
{ \
LL_DMA_ClearFlag_TC##CHx(DMAX); \
} \
} while (__LINE__ == -1)
/**
* @brief DMA传输错误标志
* @param {DMA_HandleTypeDef} *DMAX DMA总线句柄
* @param {uint32_t} CHx DMA通道号
* @return {*}
* @note: DMA总线的传输错误标志DMA总线的传输错误标志是否已置位
*/
#define DMA_ClEAR_FLAG_TE(DMAX, CHx) \
do \
{ \
if (LL_DMA_IsActiveFlag_TE##CHx(DMAX)) \
{ \
LL_DMA_ClearFlag_TE##CHx(DMAX); \
} \
} while (__LINE__ == -1)
/**
* @file uarts.c
* @brief This file contains the implementation of DMA_CLEAR_FLAG_TC_CHANNEL macro.
*/
/**
* @brief Clear the Transfer Complete (TC) flag of a specific DMA channel.
*
* @param dma The DMA peripheral.
* @param channel The DMA channel number.
*/
#define DMA_CLEAR_FLAG_TC_CHANNEL(dma, channel) \
switch (channel) \
{ \
case LL_DMA_CHANNEL_1: \
DMA_ClEAR_FLAG_TC(dma, 1); \
break; \
case LL_DMA_CHANNEL_2: \
DMA_ClEAR_FLAG_TC(dma, 2); \
break; \
case LL_DMA_CHANNEL_3: \
DMA_ClEAR_FLAG_TC(dma, 3); \
break; \
case LL_DMA_CHANNEL_4: \
DMA_ClEAR_FLAG_TC(dma, 4); \
break; \
case LL_DMA_CHANNEL_5: \
DMA_ClEAR_FLAG_TC(dma, 5); \
break; \
case LL_DMA_CHANNEL_6: \
DMA_ClEAR_FLAG_TC(dma, 6); \
break; \
case LL_DMA_CHANNEL_7: \
DMA_ClEAR_FLAG_TC(dma, 7); \
break; \
default: \
break; \
}
/**
* @brief Clear the Transfer Error (TE) flag for the specified DMA channel.
*
* @param dma The DMA peripheral.
* @param channel The DMA channel number.
*/
#define DMA_CLEAR_FLAG_TE_CHANNEL(dma, channel) \
switch (channel) \
{ \
case 1: \
DMA_ClEAR_FLAG_TE(dma, 1); \
break; \
case 2: \
DMA_ClEAR_FLAG_TE(dma, 2); \
break; \
case 3: \
DMA_ClEAR_FLAG_TE(dma, 3); \
break; \
case 4: \
DMA_ClEAR_FLAG_TE(dma, 4); \
break; \
case 5: \
DMA_ClEAR_FLAG_TE(dma, 5); \
break; \
case 6: \
DMA_ClEAR_FLAG_TE(dma, 6); \
break; \
case 7: \
DMA_ClEAR_FLAG_TE(dma, 7); \
break; \
default: \
break; \
}
/**
* @brief Clears the flags of a DMA channel
* @param DMAX DMAx register base
* @param CHx DMA channel number
* @param Flag a boolean variable that indicates if the transfer is complete
* @note This function should be called within the interrupt service routine of the DMA channel
*/
#define DMA_ClEAR_FLAG(DMAX, CHx, Flag) \
do \
{ \
if (LL_DMA_IsActiveFlag_TC##CHx(DMAX)) \
{ \
LL_DMA_ClearFlag_TC##CHx(DMAX); \
LL_DMA_ClearFlag_GI##CHx(DMAX); \
Flag = TRUE; \
} \
if (LL_DMA_IsActiveFlag_TE##CHx(DMAX)) \
{ \
LL_DMA_ClearFlag_TE##CHx(DMAX); \
} \
if (LL_DMA_IsActiveFlag_GI##CHx(DMAX)) \
{ \
LL_DMA_ClearFlag_GI##CHx(DMAX); \
} \
} while (__LINE__ == -1)
#endif // __DMAS_H__

91
User/system/bsp/eeprom.c Normal file
View File

@ -0,0 +1,91 @@
/**
* @file eeprom.c
* @author xxx
* @date 2023-11-16 10:28:47
* @brief STM32L072xx EEPROM
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "eeprom.h"
#include "main.h"
#ifdef STM32L072xx
#define PEKEY1 0x89ABCDEF // FLASH_PEKEYR
#define PEKEY2 0x02030405 // FLASH_PEKEYR
#define LOCK __enable_irq(); // 系统开全局中断
#define UNLOCK __disable_irq(); // 系统关全局中断
/**
* @brief EEPROM的函数
* @param {uint32_t} read_addr -
* @param {uint8_t} *data -
* @param {uint16_t} length -
* @return {*}
* @note: EEPROM中
*/
void chip_eeprom_config_read(uint32_t read_addr, uint8_t *data, uint16_t length)
{
uint8_t *wAddr;
wAddr = (uint8_t *)(read_addr);
while (length--)
{
*data++ = *wAddr++;
}
}
/**
* @brief EEPROM的函数
* @param {uint32_t} write_addr -
* @param {uint8_t} *data -
* @param {uint16_t} length -
* @return {*}
* @note: EEPROM中EEPROMEEPROM
*/
void chip_eeprom_config_write(uint32_t write_addr, uint8_t *data, uint16_t length)
{
uint8_t *addr;
addr = (uint8_t *)(write_addr);
UNLOCK
FLASH->PDKEYR = PEKEY1;
FLASH->PDKEYR = PEKEY2;
while (FLASH->PECR & FLASH_PECR_PELOCK)
;
while (length--)
{
*addr++ = *data++;
while (FLASH->SR & FLASH_SR_BSY)
;
}
FLASH->PECR |= FLASH_PECR_PELOCK;
LOCK
}
#endif // STM32L072xx
#ifdef STM32L476xx
/**
* @brief SRAM2的函数
* @param {uint32_t} read_addr -
* @param {uint8_t} *data -
* @param {uint16_t} length -
* @return {*}
* @note: SRAM2中
*/
void chip_eeprom_config_read(uint32_t read_addr, uint8_t *data, uint16_t length)
{
}
/**
* @brief SRAM2的函数
* @param {uint32_t} write_addr - 0
* @param {uint8_t} *data -
* @param {uint16_t} length -
* @return {*}
* @note: SRAM2中
*/
void chip_eeprom_config_write(uint32_t write_addr, uint8_t *data, uint16_t length)
{
}
#endif // STM32L476xx

7
User/system/bsp/eeprom.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef __EEPROM_H__
#define __EEPROM_H__
#include "lib.h"
extern void chip_eeprom_config_read(uint32_t read_addr, uint8_t *data, uint16_t length); ///< 读取数据
extern void chip_eeprom_config_write(uint32_t write_addr, uint8_t *data, uint16_t length); ///< 写入数据
#endif ///< __EEPROM_H__

550
User/system/bsp/flash.c Normal file
View File

@ -0,0 +1,550 @@
/**
* @file flash.c
* @author xxx
* @date 2024-02-07 11:49:34
* @brief
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
* @attention
*
* ST LL flash
*
* 1. stm32l4xx_ll_system.h FLASH ACR寄存器的处理
*
*
* 2. Main memory
* 1 FLASH_ACR
* 2 FLASH_PDKEYR
* 3 FLASH_KEYR
* 4 FLASH_OPTKEYR
* 5 FLASH_SR
* 6 FLASH_CR
* 7 FLASH_ECCR
* 8 FLASH_OPTR
* 9 FLASH_PCROP1SR
*
* 3. Information block
* - System memory
* - OTP area
* - Option bytes
* 4. HAL
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "flash.h"
#include "stm32l4xx_ll_rcc.h"
#include "stm32l4xx_ll_system.h"
#include "stm32l4xx_ll_pwr.h"
#ifdef USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
#define WHILE_MAX 30000U
/** @addtogroup STM32L4xx_LL_Driver
* @{
*/
/** @addtogroup FLASH_LL
* @{
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/** @addtogroup FLASH_LL_Private_Constants
* @{
*/
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/** @addtogroup FLASH_LL_Private_Macros
* @{
*/
#define IS_LL_FLASH_WRITE_ADDR(__ADDR__) ((__ADDR__) % LL_FLASH_ALIGNMENT_MIN_SIZE == 0)
/**
* @}
*/
/* Private function prototypes -----------------------------------------------*/
/** @defgroup FLASH_LL_Private_Functions FLASH Private functions
* @{
*/
/**
* @}
*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup FLASH_LL_Exported_Functions
* @{
*/
/**
* @brief Clear All Error in SR
* @param FLASHx FLASH Instance
* @retval None
*/
void LL_FLASH_ClearAllErrorFlag(void)
{
LL_FLASH_ClearFlag_OPTVERR(FLASH);
LL_FLASH_ClearFlag_RDERR(FLASH);
LL_FLASH_ClearFlag_FASTERR(FLASH);
LL_FLASH_ClearFlag_MISERR(FLASH);
LL_FLASH_ClearFlag_PGSERR(FLASH);
LL_FLASH_ClearFlag_SIZERR(FLASH);
LL_FLASH_ClearFlag_PGAERR(FLASH);
LL_FLASH_ClearFlag_WRPERR(FLASH);
LL_FLASH_ClearFlag_PROGERR(FLASH);
LL_FLASH_ClearFlag_OPERR(FLASH);
}
/**
* @brief Flush the instruction and data caches.
* @retval None
*/
void LL_FLASH_FlushCaches(void)
{
/* Flush instruction cache */
if (LL_FLASH_IsEnabledInstructionCache(FLASH))
{
LL_FLASH_DisableInstructionCache(FLASH);
/* Reset instruction cache */
LL_FLASH_InstructionCacheReset(FLASH);
/* Enable instruction cache */
LL_FLASH_EnableInstructionCache(FLASH);
}
/* Flush data cache */
if (LL_FLASH_IsEnabledDataCache(FLASH))
{
LL_FLASH_ZCS_DisableDataCache(FLASH);
/* Reset data cache */
LL_FLASH_DataCacheReset(FLASH);
/* Enable data cache */
LL_FLASH_ZCS_EnableDataCache(FLASH);
}
}
/**
* @brief Erase Page
* @param pageno Page number
* @retval None
*/
ErrorStatus LL_FLASH_ErasePage(uint32_t pageno)
{
uint16_t count = 0;
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
LL_FLASH_ClearAllErrorFlag();
/* Set the PER bit and select the page you wish to erase (PNB) with the associated bank (BKER) in the Flash control register (FLASH_CR). */
LL_FLASH_EnablePageErase(FLASH);
if (pageno >= LL_FLASH_BANK1_PAGE_NUM)
{
pageno -= LL_FLASH_BANK1_PAGE_NUM;
LL_FLASH_SetErasePageBank(FLASH, LL_FLASH_BANK2);
}
else
{
LL_FLASH_SetErasePageBank(FLASH, LL_FLASH_BANK1);
}
LL_FLASH_SetErasePageNo(FLASH, pageno);
/* Set the STRT bit in the FLASH_CR register. */
LL_FLASH_EraseStart(FLASH);
/* Wait for the BSY bit to be cleared in the FLASH_SR register. */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* 完成只有需要清除擦除标志. */
LL_FLASH_DisablePageErase(FLASH);
/* Flush the caches to be sure of the data consistency */
LL_FLASH_FlushCaches();
return SUCCESS;
}
/**
* @brief Erase bank
* @param bank This parameter can be one of the following values:
* @arg @ref LL_FLASH_BANK1
* @arg @ref LL_FLASH_BANK2
* @retval None
*/
ErrorStatus LL_FLASH_EraseBank(uint32_t bank)
{
uint16_t count = 0;
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
LL_FLASH_ClearAllErrorFlag();
/* Set the MER1 bit or/and MER2 (depending on the bank) in the Flash control register (FLASH_CR).
Both banks can be selected in the same operation. */
if (bank == LL_FLASH_BANK1)
{
LL_FLASH_EnableBank1Erase(FLASH);
}
else
{
LL_FLASH_EnableBank2Erase(FLASH);
}
/* Set the STRT bit in the FLASH_CR register. */
LL_FLASH_EraseStart(FLASH);
/* Wait for the BSY bit to be cleared in the FLASH_SR register. */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* 完成只有需要清除擦除标志. */
LL_FLASH_DisableBank1Erase(FLASH);
LL_FLASH_DisableBank2Erase(FLASH);
/* Flush the caches to be sure of the data consistency */
LL_FLASH_FlushCaches();
return SUCCESS;
}
/**
* @brief Erase Chip
* @param None
* @retval None
*/
ErrorStatus LL_FLASH_EraseChip(void)
{
uint16_t count = 0;
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
LL_FLASH_ClearAllErrorFlag();
/* Set the MER1 bit or/and MER2 (depending on the bank) in the Flash control register (FLASH_CR).
Both banks can be selected in the same operation. */
LL_FLASH_EnableBank1Erase(FLASH);
LL_FLASH_EnableBank2Erase(FLASH);
/* Set the STRT bit in the FLASH_CR register. */
LL_FLASH_EraseStart(FLASH);
/* Wait for the BSY bit to be cleared in the FLASH_SR register. */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* 完成只有需要清除擦除标志. */
LL_FLASH_DisableBank1Erase(FLASH);
LL_FLASH_DisableBank2Erase(FLASH);
/* Flush the caches to be sure of the data consistency */
LL_FLASH_FlushCaches();
return SUCCESS;
}
/**
* @brief Program Double Word
* @param address specifies the address to be programmed.
* @param data specifies the data to be programmed.
* @retval An ErrorStatus enumeration value:
* - SUCCESS: Write successfully
* - ERROR: error
*/
ErrorStatus LL_FLASH_ProgramDoubleWord(uint32_t address, uint64_t data)
{
assert_param(!IS_LL_FLASH_WRITE_ADDR(address));
uint16_t count = 0;
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
LL_FLASH_ClearAllErrorFlag();
/* Set the PG bit in the Flash control register (FLASH_CR). */
LL_FLASH_EnableProgram(FLASH);
/* Perform the data write operation at the desired memory address, inside main memory
block or OTP area. Only double word can be programmed. */
/* Program the double word */
*(__IO uint32_t *)address = (uint32_t)data;
*(__IO uint32_t *)(address + 4U) = (uint32_t)(data >> 32);
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* Check that EOP flag is set in the FLASH_SR register (meaning that the programming
operation has succeed), and clear it by software. */
if (LL_FLASH_IsActiveFlag_EOP(FLASH))
{
LL_FLASH_ClearFlag_EOP(FLASH);
}
/* Clear the PG bit in the FLASH_CR register if there no more programming request anymore. */
LL_FLASH_DisableProgram(FLASH);
/* Flush the caches to be sure of the data consistency */
LL_FLASH_FlushCaches();
return SUCCESS;
}
/**
* @brief Program
* @param address specifies the address to be programmed.
* @param data specifies the data to be programmed.
* @param num specifies the data number
* @retval An ErrorStatus enumeration value:
* - SUCCESS: Write successfully
* - ERROR: error
*/
ErrorStatus LL_FLASH_Program(uint32_t address, uint8_t data[], uint32_t num)
{
static uint64_t DataT = 0;
uint32_t T = 0, S = 0;
uint16_t count = 0;
assert_param(!IS_LL_FLASH_WRITE_ADDR(address));
if (num == 0)
{
return SUCCESS;
}
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
LL_FLASH_ClearAllErrorFlag();
/* Set the PG bit in the Flash control register (FLASH_CR). */
LL_FLASH_EnableProgram(FLASH);
/* Perform the data write operation at the desired memory address, inside main memory
block or OTP area. Only double word can be programmed. */
T = num;
while (num > 0)
{
DataT = 0;
if (num >= 8)
{
for (int i = 0; i < LL_FLASH_ALIGNMENT_MIN_SIZE; i++)
{
DataT = DataT << 8;
DataT |= data[S + 7 - i];
}
S += LL_FLASH_ALIGNMENT_MIN_SIZE;
num -= LL_FLASH_ALIGNMENT_MIN_SIZE;
}
else
{
for (int i = 0; i < num; i++)
{
DataT = DataT << 8;
DataT |= data[T - 1 - i];
}
num = 0;
}
/* Program the double word */
*(__IO uint32_t *)address = (uint32_t)DataT;
*(__IO uint32_t *)(address + 4U) = (uint32_t)(DataT >> 32);
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
{
if (count++ > WHILE_MAX)
{
return ERROR;
}
}
count = 0;
/* Check that EOP flag is set in the FLASH_SR register (meaning that the programming
operation has succeed), and clear it by software. */
if (LL_FLASH_IsActiveFlag_EOP(FLASH))
{
LL_FLASH_ClearFlag_EOP(FLASH);
}
address += LL_FLASH_ALIGNMENT_MIN_SIZE;
}
/* Clear the PG bit in the FLASH_CR register if there no more programming request anymore. */
LL_FLASH_DisableProgram(FLASH);
/* Flush the caches to be sure of the data consistency */
LL_FLASH_FlushCaches();
return SUCCESS;
}
/**
* @}
*/
/** @addtogroup FLASH_LL_Private_Functions
* @{
*/
/**
* @brief Fast program a row double-word (64-bit) at a specified address.
* @param address: specifies the address to be programmed.
* @param DataAddress: specifies the address where the data are stored.
* @retval None
*/
void LL_FLASH_ProgramFast(uint32_t address, uint32_t DataAddress)
{
uint8_t row_index = (2 * LL_FLASH_ROW_SIZE);
__IO uint32_t *dest_addr = (__IO uint32_t *)address;
__IO uint32_t *src_addr = (__IO uint32_t *)DataAddress;
/* Check the parameters */
assert_param(IS_FLASH_MAIN_MEM_ADDRESS(address));
/* Set FSTPG bit */
LL_FLASH_EnableFastProgram(FLASH);
/* Disable interrupts to avoid any interruption during the loop */
__disable_irq();
/* Program the double word of the row */
do
{
*dest_addr = *src_addr;
dest_addr++;
src_addr++;
row_index--;
} while (row_index != 0U);
/* Re-enable the interrupts */
__enable_irq();
LL_FLASH_DisableFastProgram(FLASH);
}
/**
* @brief Fast program a row double-word (64-bit) at a specified address.
* @param address: specifies the address to be programmed.
* @param data: specifies the data to be programmed.
* @retval None
*/
ErrorStatus LL_FLASH_Read(uint32_t address, uint8_t data[], uint32_t num)
{
if (num == 0)
{
return SUCCESS;
}
for (uint32_t i = 0; i < num; i++)
{
data[i] = *(__IO uint8_t *)(address + i);
}
return SUCCESS;
}
/**
* @brief FLASH
*
* FLASH
*
* @param address FLASH
* @param size
*/
void LL_FLASH_EraseAddress(uint32_t address, uint16_t size)
{
uint16_t start_page = 0, end_page = 0;
start_page = address / LL_FLASH_PAGE_SIZE;
end_page = (address + size) / LL_FLASH_PAGE_SIZE;
// 擦除页
for (uint16_t i = start_page; i < end_page; i++)
{
LL_FLASH_ErasePage(i);
}
}
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

1725
User/system/bsp/flash.h Normal file

File diff suppressed because it is too large Load Diff

76
User/system/bsp/gpios.c Normal file
View File

@ -0,0 +1,76 @@
#include "gpios.h"
#include "gpio.h"
/**
* @brief GPIO引脚为高电平
* @param {gpio_t} gpio - GPIO对象
* @note: GPIO引脚为高电平
*/
static void _set(gpio_t gpio)
{
GPIO_SET(gpio.port, gpio.pin);
}
/**
* @brief GPIO引脚为低电平
* @param {gpio_t} gpio - GPIO对象
* @note: GPIO引脚为低电平
*/
static void _reset(gpio_t gpio)
{
GPIO_RESET(gpio.port, gpio.pin);
}
/**
* @brief GPIO引脚状态
* @param {gpio_t} gpio - GPIO对象
* @note: GPIO引脚的状态
*/
static void _toggle(gpio_t gpio)
{
GPIO_TOGGLE(gpio.port, gpio.pin);
}
/**
* @brief GPIO引脚状态
* @param {gpio_t} gpio - GPIO对象
* @return {*} - GPIO引脚当前状态01
* @note: GPIO引脚的状态01
*/
static uint8_t _read(gpio_t gpio)
{
return (uint8_t)GPIO_READ(gpio.port, gpio.pin);
}
/**
* @brief GPIO对象
* @param {GPIO_TypeDef} *port - GPIO寄存器指针
* @param {uint16_t} pin -
* @return {gpio_t *} - GPIO对象指针
* @note: GPIO对象GPIO功能
*/
gpio_t *gpio_create(GPIO_TypeDef *port, uint16_t pin)
{
gpio_t *gpio = (gpio_t *)osel_mem_alloc(sizeof(gpio_t));
DBG_ASSERT(gpio != NULL __DBG_LINE);
gpio->port = port;
gpio->pin = pin;
gpio->set = _set;
gpio->reset = _reset;
gpio->toggle = _toggle;
gpio->read = _read;
return gpio;
}
/**
* @brief GPIO对象
* @param {gpio_t} *gpio - GPIO对象指针
* @return {*}
* @note: GPIO对象使
*/
void gpio_free(gpio_t *gpio)
{
if (gpio != NULL)
{
osel_mem_free(gpio);
}
}

170
User/system/bsp/gpios.h Normal file
View File

@ -0,0 +1,170 @@
/**
* @file gpios.h
* @brief Header file for GPIO configuration and control.
*
* This file contains the declarations and definitions for GPIO configuration and control functions.
*
* @author xxx
* @date 2023-12-27 14:44:03
* @version 1.0
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __GPIOS_H__
#define __GPIOS_H__
#include "lib.h"
#include "main.h"
/**
* @brief Set the GPIO pin to high.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET(port, pin) (HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET))
/**
* @brief Set the GPIO pin to low.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_RESET(port, pin) (HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET))
/**
* @brief Toggle the state of the GPIO pin.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_TOGGLE(port, pin) (HAL_GPIO_TogglePin(port, pin))
/**
* @brief Read the state of the GPIO pin.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
* @return The state of the GPIO pin (1 if high, 0 if low).
*/
#define GPIO_READ(port, pin) (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET)
/**
* @brief Set the GPIO pin as input.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET_INPUT(port, pin) \
do \
{ \
GPIO_InitTypeDef GPIO_InitStruct = {0}; \
GPIO_InitStruct.Pin = pin; \
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; \
GPIO_InitStruct.Pull = GPIO_NOPULL; \
HAL_GPIO_Init(port, &GPIO_InitStruct); \
} while (0)
/**
* @brief Set the GPIO pin as output.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET_OUTPUT(port, pin) \
do \
{ \
GPIO_InitTypeDef GPIO_InitStruct = {0}; \
GPIO_InitStruct.Pin = pin; \
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; \
GPIO_InitStruct.Pull = GPIO_NOPULL; \
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; \
HAL_GPIO_Init(port, &GPIO_InitStruct); \
} while (0)
/**
* @brief Set the GPIO pin as alternate function.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET_ALTERNATE(port, pin, af) \
do \
{ \
GPIO_InitTypeDef GPIO_InitStruct = {0}; \
GPIO_InitStruct.Pin = pin; \
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; \
GPIO_InitStruct.Pull = GPIO_NOPULL; \
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; \
GPIO_InitStruct.Alternate = af; \
HAL_GPIO_Init(port, &GPIO_InitStruct); \
} while (0)
/**
* @brief Set the GPIO pin as analog.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET_ANALOG(port, pin) \
do \
{ \
GPIO_InitTypeDef GPIO_InitStruct = {0}; \
GPIO_InitStruct.Pin = pin; \
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; \
GPIO_InitStruct.Pull = GPIO_NOPULL; \
HAL_GPIO_Init(port, &GPIO_InitStruct); \
} while (0)
/**
* @brief Structure representing a GPIO pin.
*/
typedef struct GPIO
{
GPIO_TypeDef *port; ///< The GPIO port.
uint16_t pin; ///< The GPIO pin.
/**
* @brief Set the GPIO pin to high.
*
* @param gpio The GPIO pin.
*/
void (*set)(struct GPIO gpio);
/**
* @brief Set the GPIO pin to low.
*
* @param gpio The GPIO pin.
*/
void (*reset)(struct GPIO gpio);
/**
* @brief Toggle the state of the GPIO pin.
*
* @param gpio The GPIO pin.
*/
void (*toggle)(struct GPIO gpio);
/**
* @brief Read the state of the GPIO pin.
*
* @param gpio The GPIO pin.
* @return The state of the GPIO pin (1 if high, 0 if low).
*/
uint8_t (*read)(struct GPIO gpio);
} gpio_t;
/**
* @brief Create a GPIO pin.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
* @return The created GPIO pin.
*/
extern gpio_t *gpio_create(GPIO_TypeDef *port, uint16_t pin);
/**
* @brief Free the memory allocated for a GPIO pin.
*
* @param gpio The GPIO pin to free.
*/
extern void gpio_free(gpio_t *gpio);
#endif ///< __GPIOS_H__

669
User/system/bsp/i2cs.c Normal file
View File

@ -0,0 +1,669 @@
#include "i2cs.h"
#include "main.h"
static inline void delay(i2c_t *handle); // 延时函数
static inline void _ack(i2c_t *handle); // 应答
static inline void _nack(i2c_t *handle); // 非应答
/**
* @brief I2C总线
* @param {i2c_t} *handle - I2C总线句柄
* @note: I2C总线的操作线
*/
static void _start(i2c_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
// 获取gpios指针
i2c_gpio_group_t *gpios = &handle->gpios;
// 获取scl指针
gpio_t *scl = gpios->scl;
// 获取sda指针
gpio_t *sda = gpios->sda;
// 设置sda
gpios->sda->set(*sda);
// 设置scl
gpios->scl->set(*scl);
// 延时
delay(handle);
// 重置sda
gpios->sda->reset(*sda);
// 延时
delay(handle);
// 重置scl
gpios->scl->reset(*scl);
// 延时
delay(handle);
}
/**
* @brief I2C总线
* @param {i2c_t} *handle - I2C总线句柄
* @note: I2C总线的操作线
*/
static void _stop(i2c_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
i2c_gpio_group_t *gpios = &handle->gpios;
gpio_t *scl = gpios->scl;
gpio_t *sda = gpios->sda;
gpios->scl->reset(*scl);
gpios->sda->reset(*sda);
delay(handle);
gpios->scl->set(*scl);
gpios->sda->set(*sda);
delay(handle);
}
/**
* @brief
* @param {i2c_t} *handle - I2C总线句柄
* @return {BOOL} - TRUEFALSE
* @note: I2C总线上发送的应答信号
*/
static BOOL _wait_ack(i2c_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
uint8_t count = 0;
i2c_gpio_group_t *gpios = &handle->gpios;
gpio_t *scl = gpios->scl;
gpio_t *sda = gpios->sda;
gpios->sda->set(*sda);
gpios->scl->set(*scl);
delay(handle);
while (gpios->sda->read(*sda))
{
count++;
if (count > 250)
{
_stop(handle);
return FALSE;
}
}
gpios->scl->reset(*scl);
delay(handle);
return TRUE;
}
/**
* @brief
* @param {i2c_t} *handle - I2C总线句柄
* @param {BOOL} ack -
* @return {uint8_t} -
* @note: I2C总线上读取一个字节
*/
static uint8_t _read_byte(i2c_t *handle, BOOL ack)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
uint8_t i = 0, receive = 0;
i2c_gpio_group_t *gpios = &handle->gpios;
gpio_t *scl = gpios->scl;
gpio_t *sda = gpios->sda;
for (i = 0; i < 8; i++)
{
gpios->sda->set(*sda);
gpios->scl->set(*scl);
receive <<= 1;
delay(handle);
if (gpios->sda->read(*sda))
receive++;
gpios->scl->reset(*scl);
delay(handle);
}
if (TRUE == ack)
{
_ack(handle);
}
else
{
_nack(handle);
}
return receive;
}
/**
* @brief I2C总线上
* @param {i2c_t} *handle I2C总线的句柄
* @param {uint8_t} data
* @return {*}
* @note: I2C总线上发送一个字节的数据1SDA为1SDA为0SCL为11msSCL为01msSDA为1
*/
static void _write_byte(i2c_t *handle, uint8_t data)
{
// 定义变量i
uint8_t i = 0;
// 断言参数handle不为空
DBG_ASSERT(handle != NULL __DBG_LINE);
// 定义变量gpios
i2c_gpio_group_t *gpios = &handle->gpios;
// 定义变量scl
gpio_t *scl = gpios->scl;
// 定义变量sda
gpio_t *sda = gpios->sda;
// 遍历每一位
for (i = 0; i < 8; i++)
{
// 如果data的最低位为1
if (data & 0x80)
{
// 设置sda的状态为1
gpios->sda->set(*sda);
}
// 否则设置sda的状态为0
else
{
// 设置sda的状态为0
gpios->sda->reset(*sda);
}
// 设置scl的状态为1
gpios->scl->set(*scl);
// 延时1ms
delay(handle);
// 将data右移1位
data <<= 1;
// 设置scl的状态为0
gpios->scl->reset(*scl);
// 延时1ms
delay(handle);
// 如果i等于7
if (i == 7)
{
// 设置sda的状态为1
gpios->sda->set(*sda);
}
}
}
/**
* @brief I2C总线上
* @param {i2c_t} *handle I2C总线的句柄
* @param {uint16_t} data
* @return {*}
* @note: I2C总线上发送一个字节的数据1SDA为1SDA为0SCL为11msSCL为01msSDA为1
*/
static void _write_word(i2c_t *handle, uint16_t data)
{
// 循环写入2个字节
for (uint8_t i = 0; i < 2; i++)
{
// 将data的第i个字节写入i2c接口
_write_byte(handle, (uint8_t)(data >> (8 * i)));
// 等待ACK
_wait_ack(handle);
}
}
/**
* @brief I2C总线设备
* @param {i2c_gpio_group_t} gpios I2C总线的GPIO配置
* @param {uint16_t} delay_ticks I2C总线的延时参数
* @return {i2c_t *} I2C总线设备句柄
* @note: I2C总线设备i2c_t结构体gpios和delay_ticks的内存地址复制到handle结构体中handle结构体定义了startstopwait_ackread_bytewrite_byte和write_word函数handle结构体
*/
i2c_t *i2c_create(i2c_gpio_group_t gpios, uint16_t delay_ticks)
{
// 创建一个i2c_t结构体
i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t));
// 将gpios的内存地址复制到handle结构体中
osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(i2c_gpio_group_t));
// 将delay_ticks的内存地址复制到handle结构体中
handle->delay_ticks = delay_ticks;
// 创建一个start函数
handle->interface.start = _start;
// 创建一个stop函数
handle->interface.stop = _stop;
// 创建一个wait_ack函数
handle->interface.wait_ack = _wait_ack;
// 创建一个read_byte函数
handle->interface.read_byte = _read_byte;
// 创建一个write_byte函数
handle->interface.write_byte = _write_byte;
// 创建一个write_word函数
handle->interface.write_word = _write_word;
handle->dead_count = 0;
// 返回handle结构体
return handle;
}
/**
* @brief I2C器件地址
* @param {i2c_t} *handle
* @param {uint8_t} w_address
* @param {uint8_t} r_address
* @return {*}
* @note
*/
void i2c_dma_set_address(i2c_t *handle, uint8_t w_address, uint8_t r_address)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->w_address = w_address;
handle->r_address = r_address;
}
/**
* @brief I2C总线设备
* @param {i2c_t} *handle I2C总线设备句柄
* @return {*}
* @note: I2C总线设备handle是否为空GPIOhandle的内存
*/
void i2c_free(i2c_t *handle)
{
// 如果handle不为空则释放所有的gpio
if (NULL != handle)
{
gpio_free(handle->gpios.scl);
gpio_free(handle->gpios.sda);
osel_mem_free(handle);
}
}
/**
* @brief I2C DMA TX回调函数
* @param {i2c_t} handle
* @return {*}
* @note
*/
void i2c_dma_callback(i2c_t *handle)
{
// 检查输入参数是否为空
DBG_ASSERT(handle != NULL __DBG_LINE);
// 清除传输完成标志位
if (handle->dma_tx_cb != NULL)
{
handle->dma_tx_cb(handle);
}
if (handle->dma_rx_cb != NULL)
{
handle->dma_rx_cb(handle);
}
}
#if defined(STM32L4xx_LL_I2C_H)
static void i2c_reset(i2c_t *handle);
static BOOL _read_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size);
static BOOL _write_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size);
/**
* @brief I2C总线设备 DMA
* @param {I2C_TypeDef} *i2c
* @param {DMA_TypeDef} *dma
* @param {uint16_t} rxsize
* @param {uint32_t} dma_rx_channel
* @param {uint16_t} txsize
* @param {uint32_t} dma_tx_channel
* @return {*}
* @note
*/
i2c_t *i2c_create_dma(I2C_TypeDef *i2c, DMA_TypeDef *dma, uint16_t rxsize, uint32_t dma_rx_channel,
i2cs_dma_callback *dma_rx_cb, uint16_t txsize, uint32_t dma_tx_channel, i2cs_dma_callback *dma_tx_cb)
{
i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t));
handle->i2c = i2c;
handle->dma = dma;
handle->dma_rx_channel = dma_rx_channel;
handle->dma_tx_channel = dma_tx_channel;
handle->rxbuf = (uint8_t *)osel_mem_alloc(rxsize);
handle->txbuf = (uint8_t *)osel_mem_alloc(txsize);
handle->rxsize = rxsize;
handle->txsize = txsize;
handle->tx_dma_ok = TRUE;
handle->interface.write_mem_dma = _write_mem_dma;
handle->interface.read_mem_dma = _read_mem_dma;
if (dma_rx_cb != NULL)
{
handle->dma_rx_cb = dma_rx_cb;
}
if (dma_tx_cb != NULL)
{
handle->dma_tx_cb = dma_tx_cb;
}
LL_DMA_DisableChannel(dma, dma_tx_channel);
LL_DMA_DisableChannel(dma, dma_rx_channel);
// TX
uint8_t *pTransmitBuffer = handle->txbuf;
LL_DMA_ConfigTransfer(dma, dma_tx_channel, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
LL_DMA_ConfigAddresses(dma, dma_tx_channel, (uint32_t)pTransmitBuffer, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_TRANSMIT), LL_DMA_GetDataTransferDirection(dma, dma_tx_channel));
LL_DMA_SetPeriphRequest(dma, dma_tx_channel, LL_DMA_REQUEST_3);
// RX
uint8_t *pReceiveBuffer = handle->rxbuf;
LL_DMA_ConfigTransfer(dma, dma_rx_channel, LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
LL_DMA_ConfigAddresses(dma, dma_rx_channel, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_RECEIVE), (uint32_t)pReceiveBuffer, LL_DMA_GetDataTransferDirection(dma, dma_rx_channel));
LL_DMA_SetPeriphRequest(dma, dma_rx_channel, LL_DMA_REQUEST_3);
LL_DMA_EnableIT_TC(dma, dma_tx_channel);
LL_DMA_EnableIT_TE(dma, dma_tx_channel);
LL_DMA_EnableIT_TC(dma, dma_rx_channel);
LL_DMA_EnableIT_TE(dma, dma_rx_channel);
LL_I2C_EnableDMAReq_TX(i2c);
LL_I2C_EnableDMAReq_RX(i2c);
LL_I2C_Enable(i2c);
return handle;
}
/**
* @brief 使DMA从特定内存地址读取数据
* @param {i2c_t} *handle i2c_t结构体I2C的配置信息
* @param {uint16_t} dev_address 7
* @param {uint16_t} mem_address
* @param {uint16_t} mem_addsize
* @param {uint8_t} *data
* @param {uint16_t} size
* @return {*}
* @note
*/
static BOOL _read_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(data != NULL __DBG_LINE);
if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1)
{
i2c_reset(handle); // xsh:重置I2C,修复一段时间后无法读写的问题
return FALSE;
}
uint16_t count = 2000;
handle->txsize = 0;
handle->tx_dma_ok = FALSE;
handle->rx_dma_ok = FALSE;
for (uint8_t i = mem_addsize; i > 0; i--)
{
handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1)));
}
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize);
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_SOFTEND, LL_I2C_GENERATE_START_WRITE);
count = 2000;
while (!handle->tx_dma_ok)
{
if (count-- == 0)
{
handle->tx_dma_ok = TRUE;
return FALSE;
}
}
count = 2000;
while (LL_I2C_IsActiveFlag_TC(handle->i2c) != 1)
{
if (count-- == 0)
{
handle->tx_dma_ok = TRUE;
return FALSE;
}
}
handle->tx_dma_ok = FALSE;
handle->rx_dma_ok = FALSE;
handle->rxsize = size;
osel_memset(handle->rxbuf, 0, handle->rxsize);
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
LL_DMA_SetDataLength(handle->dma, handle->dma_rx_channel, handle->rxsize);
LL_DMA_EnableChannel(handle->dma, handle->dma_rx_channel);
LL_I2C_HandleTransfer(handle->i2c, handle->r_address, LL_I2C_ADDRSLAVE_7BIT, handle->rxsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_RESTART_7BIT_READ);
count = 2000;
while (!LL_I2C_IsActiveFlag_STOP(handle->i2c))
{
if (count-- == 0)
{
handle->tx_dma_ok = TRUE;
return FALSE;
}
}
LL_DMA_DisableChannel(handle->dma, handle->dma_rx_channel);
LL_I2C_ClearFlag_STOP(handle->i2c);
osel_memcpy(data, handle->rxbuf, handle->rxsize);
return TRUE;
}
/**
* @brief 使DMA将数据写入特定内存地址
* @param {i2c_t} *handle i2c_t结构体I2C的配置信息
* @param {uint16_t} dev_address 7
* @param {uint16_t} mem_address
* @param {uint16_t} mem_addsize
* @param {uint8_t} *data
* @param {uint16_t} size
* @return {*}
* @note
*/
static BOOL _write_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(data != NULL __DBG_LINE);
uint16_t count = 2000;
if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1)
{
i2c_reset(handle); // xsh:重置I2C,修复一段时间后无法读写的问题
return FALSE;
}
handle->txsize = 0;
handle->tx_dma_ok = FALSE;
for (uint8_t i = mem_addsize; i > 0; i--)
{
handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1)));
}
osel_memcpy(&handle->txbuf[handle->txsize], data, size);
handle->txsize += size;
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize);
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE);
count = 2000;
while (!handle->tx_dma_ok)
{
if (count-- == 0)
{
handle->tx_dma_ok = TRUE;
return FALSE;
}
}
count = 2000;
while (!LL_I2C_IsActiveFlag_STOP(handle->i2c))
{
if (count-- == 0)
{
handle->tx_dma_ok = TRUE;
return FALSE;
}
}
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
LL_I2C_ClearFlag_STOP(handle->i2c);
return TRUE;
}
/**
* @brief I2C重置
* @param {I2C_TypeDef} *I2Cx
* @return {*}
* @note I2C总线死锁问题
*/
static void i2c_reset(i2c_t *handle)
{
LL_I2C_Disable(handle->i2c);
LL_I2C_Enable(handle->i2c);
handle->dead_count++;
}
/**
* @brief I2C
* @param {i2c_t} *handle
* @return {*}
* @note
*/
void i2c_ev_callback(i2c_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
if (LL_I2C_IsActiveFlag_ADDR(handle->i2c))
{
/* Verify the Address Match with the OWN Slave address */
if (LL_I2C_GetAddressMatchCode(handle->i2c) == handle->w_address || LL_I2C_GetAddressMatchCode(handle->i2c) == handle->r_address)
{
/* Verify the transfer direction, a write direction, Slave enters receiver mode */
if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_WRITE)
{
/* Clear ADDR flag value in ISR register */
LL_I2C_ClearFlag_ADDR(handle->i2c);
/* Enable Receive Interrupt */
LL_I2C_EnableIT_RX(handle->i2c);
}
else if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_READ)
{
/* Clear ADDR flag value in ISR register */
LL_I2C_ClearFlag_ADDR(handle->i2c);
/* Enable Transmit Interrupt */
LL_I2C_EnableIT_TX(handle->i2c);
}
}
else
{
/* Clear ADDR flag value in ISR register */
LL_I2C_ClearFlag_ADDR(handle->i2c);
/* Call Error function */
DBG_ASSERT(FALSE __DBG_LINE);
}
}
/* Check NACK flag value in ISR register */
else if (LL_I2C_IsActiveFlag_NACK(handle->i2c))
{
/* End of Transfer */
LL_I2C_ClearFlag_NACK(handle->i2c);
}
/* Check RXNE flag value in ISR register */
else if (LL_I2C_IsActiveFlag_RXNE(handle->i2c))
{
/* Call function Slave Reception Callback */
}
/* Check TXIS flag value in ISR register */
else if (LL_I2C_IsActiveFlag_TXIS(handle->i2c))
{
/* Call function Slave Ready to Transmit Callback */
}
/* Check STOP flag value in ISR register */
else if (LL_I2C_IsActiveFlag_STOP(handle->i2c))
{
/* End of Transfer */
LL_I2C_ClearFlag_STOP(handle->i2c);
/* Check TXE flag value in ISR register */
if (!LL_I2C_IsActiveFlag_TXE(handle->i2c))
{
/* Flush TX buffer */
LL_I2C_ClearFlag_TXE(handle->i2c);
}
/* Call function Slave Complete Callback */
}
/* Check TXE flag value in ISR register */
else if (!LL_I2C_IsActiveFlag_TXE(handle->i2c))
{
/* Do nothing */
/* This Flag will be set by hardware when the TXDR register is empty */
/* If needed, use LL_I2C_ClearFlag_TXE() interface to flush the TXDR register */
}
else
{
/* Call Error function */
DBG_ASSERT(FALSE __DBG_LINE);
}
}
#endif
/*下面是内部实现方法*/
/**
* @brief 使NOPcountNOP指令的数量使
* @param {uint16_t} count NOP指令的数量
* @return {*}
*/
static inline void delay(i2c_t *handle)
{
// 断言参数handle不为空
DBG_ASSERT(handle != NULL __DBG_LINE);
// 定义循环计数变量count
uint16_t count = 0;
// 设置循环计数变量count的值为handle->delay_ticks
count = handle->delay_ticks;
// 循环计数变量count的值直到count的值为0
while (count--)
{
// 每次循环调用__NOP()函数
__NOP();
}
}
/**
* @brief ACK信号
* @param {i2c_t} *handle I2C总线的句柄
* @return {*}
* @note: I2C总线上发送ACK信号handle不为空gpios和sclsda的指针sda1msscl为11msscl
*/
static inline void _ack(i2c_t *handle)
{
// 断言handle不为空
DBG_ASSERT(handle != NULL __DBG_LINE);
// 获取gpios指针
i2c_gpio_group_t *gpios = &handle->gpios;
// 获取scl指针
gpio_t *scl = gpios->scl;
// 获取sda指针
gpio_t *sda = gpios->sda;
// 重置sda
gpios->sda->reset(*sda);
// 延时
delay(handle);
// 设置scl
gpios->scl->set(*scl);
// 延时
delay(handle);
// 重置scl
gpios->scl->reset(*scl);
}
/**
* @brief NACK信号
* @param {i2c_t} *handle I2C总线的句柄
* @return {*}
* @note: I2C总线上发送NACK信号handle不为空gpios和sclsda的指针sda为11msscl为11msscl
*/
static inline void _nack(i2c_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
// 获取gpios指针
i2c_gpio_group_t *gpios = &handle->gpios;
// 获取scl指针
gpio_t *scl = gpios->scl;
// 获取sda指针
gpio_t *sda = gpios->sda;
// 设置sda引脚
gpios->sda->set(*sda);
// 等待延时
delay(handle);
// 设置scl引脚
gpios->scl->set(*scl);
// 等待延时
delay(handle);
// 重置scl引脚
gpios->scl->reset(*scl);
}

127
User/system/bsp/i2cs.h Normal file
View File

@ -0,0 +1,127 @@
/**
* @file i2cs.h
* @brief Header file for I2C Slave module.
*
* This file contains the declarations and definitions for the I2C Slave module.
* It provides functions to initialize and configure the I2C peripheral as a slave,
* as well as functions to send and receive data over the I2C bus.
*
* @author xxx
* @date 2023-12-27 14:44:03
* @version 1.0
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __I2CS_H__
#define __I2CS_H__
#include "lib.h"
#include "gpios.h"
/**
* @file i2cs.h
* @brief Header file containing the definition of the I2C slave (I2CS) structure and related functions.
*/
typedef struct I2CS i2c_t;
typedef void i2cs_dma_callback(i2c_t *handle);
typedef struct
{
void (*start)(i2c_t *handle); ///< Function pointer to start the I2C communication.
void (*stop)(i2c_t *handle); ///< Function pointer to stop the I2C communication.
BOOL(*wait_ack)
(i2c_t *handle); ///< Function pointer to wait for the acknowledgment from the I2C bus.
void (*write_byte)(i2c_t *handle, uint8_t data); ///< Function pointer to write a byte of data to the I2C bus.
uint8_t (*read_byte)(i2c_t *handle, BOOL ack); ///< Function pointer to read a byte of data from the I2C bus.
void (*write_word)(i2c_t *handle, uint16_t data); ///< Function pointer to write two bytes of data to the I2C bus.
BOOL(*write_mem_dma)
(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); ///< Function pointer to write multiple bytes of data to a memory address using DMA.
BOOL(*read_mem_dma)
(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); ///< Function pointer to read multiple bytes of data from a memory address using DMA.
} i2c_interface_t;
typedef struct
{
struct GPIO *scl; ///< Pointer to the GPIO pin used for the I2C clock (SCL).
struct GPIO *sda; ///< Pointer to the GPIO pin used for the I2C data (SDA).
} i2c_gpio_group_t;
struct I2CS
{
///< Analog part definition
i2c_gpio_group_t gpios; ///< Structure containing the GPIO pins used for the I2C communication.
uint16_t delay_ticks; ///< Number of NOP instructions to delay the I2C communication.
///< Hardware part definition
I2C_TypeDef *i2c; ///< Pointer to the I2C peripheral.
DMA_TypeDef *dma; ///< Pointer to the DMA peripheral.
uint32_t dma_rx_channel; ///< DMA channel used for receiving data.
uint32_t dma_tx_channel; ///< DMA channel used for transmitting data.
uint8_t *rxbuf; ///< Pointer to the receive buffer.
uint16_t rxsize; ///< Size of the receive buffer.
uint8_t *txbuf; ///< Pointer to the transmit buffer.
uint16_t txsize; ///< Size of the transmit buffer.
uint8_t w_address; ///< 7-bit write address.
uint8_t r_address; ///< 7-bit read address.
__IO BOOL rx_dma_ok; ///< Flag indicating the completion of receive DMA.
__IO BOOL tx_dma_ok; ///< Flag indicating the completion of transmit DMA.
i2cs_dma_callback *dma_rx_cb; ///< Callback function called when receive DMA is completed.
i2cs_dma_callback *dma_tx_cb; ///< Callback function called when transmit DMA is completed.
i2c_interface_t interface; ///< Structure containing the function pointers for the I2C interface.
uint16_t dead_count; ///< Counter for the number of deadlocks.
};
/**
* @brief Creates an I2C slave instance with GPIO pins for clock and data.
* @param gpios The GPIO pins used for the I2C communication.
* @param delay_ticks The number of NOP instructions to delay the I2C communication.
* @return A pointer to the created I2C slave instance.
*/
extern i2c_t *i2c_create(i2c_gpio_group_t gpios, uint16_t delay_ticks);
/**
* @brief Creates an I2C slave instance with DMA support.
* @param i2c Pointer to the I2C peripheral.
* @param dma Pointer to the DMA peripheral.
* @param rxsize Size of the receive buffer.
* @param dma_rx_channel DMA channel used for receiving data.
* @param dma_rx_cb Callback function called when receive DMA is completed.
* @param txsize Size of the transmit buffer.
* @param dma_tx_channel DMA channel used for transmitting data.
* @param dma_tx_cb Callback function called when transmit DMA is completed.
* @return A pointer to the created I2C slave instance.
*/
extern i2c_t *i2c_create_dma(I2C_TypeDef *i2c, DMA_TypeDef *dma, uint16_t rxsize, uint32_t dma_rx_channel,
i2cs_dma_callback *dma_rx_cb, uint16_t txsize, uint32_t dma_tx_channel, i2cs_dma_callback *dma_tx_cb);
/**
* @brief Sets the write and read addresses for the I2C slave instance with DMA support.
* @param handle Pointer to the I2C slave instance.
* @param w_address 7-bit write address.
* @param r_address 7-bit read address.
*/
extern void i2c_dma_set_address(i2c_t *handle, uint8_t w_address, uint8_t r_address);
/**
* @brief Frees the resources used by the I2C slave instance.
* @param handle Pointer to the I2C slave instance.
*/
extern void i2c_free(i2c_t *handle);
/**
* @brief Callback function called when an I2C event occurs.
* @param handle Pointer to the I2C slave instance.
*/
extern void i2c_ev_callback(i2c_t *handle);
/**
* @brief Callback function called when an I2C DMA event occurs.
* @param handle Pointer to the I2C slave instance.
*/
extern void i2c_dma_callback(i2c_t *handle);
#endif ///< __I2CS_H__

39
User/system/bsp/iwdgs.c Normal file
View File

@ -0,0 +1,39 @@
#include "iwdgs.h"
/**
* @brief CPU复位是否是看门狗复位
* @return {BOOL}
* @note
*/
BOOL check_watchdog_reset(void)
{
if (LL_RCC_IsActiveFlag_IWDGRST() == SET) // cpu is reset due to iwdg
{
LL_RCC_ClearResetFlags(); // clear flag
return TRUE;
}
else
{
return FALSE;
}
}
/**
* @brief
* @return {*}
* @note
*/
void debug_freeze_watchdog(void)
{
LL_DBGMCU_APB1_GRP1_FreezePeriph(LL_DBGMCU_APB1_GRP1_IWDG_STOP);
}
/**
* @brief
* @return {*}
* @note
*/
void debug_unfreeze_watchdog(void)
{
LL_DBGMCU_APB1_GRP1_UnFreezePeriph(LL_DBGMCU_APB1_GRP1_IWDG_STOP);
}

45
User/system/bsp/iwdgs.h Normal file
View File

@ -0,0 +1,45 @@
/**
* @file iwdgs.h
* @brief This file contains the declaration of the Independent Watchdog (IWDG) module.
*
* The Independent Watchdog (IWDG) is a hardware module in STM32 microcontrollers that provides a mechanism for system reset in case of software failures or malfunctions. This file declares the functions and constants related to the IWDG module.
*
* @author xxx
* @date 2023-12-27 14:44:03
* @version 1.0
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __IWDGS_H__
#define __IWDGS_H__
#include "main.h"
#include "lib.h"
/**
* @brief Reloads the watchdog counter.
*
* This macro is used to reload the watchdog counter, preventing the system from resetting.
*/
#define WATCHDOG_RESET() LL_IWDG_ReloadCounter(IWDG)
/**
* @brief Checks if the system has been reset by the watchdog.
*
* @return BOOL Returns TRUE if the system has been reset by the watchdog, FALSE otherwise.
*/
extern BOOL check_watchdog_reset(void);
/**
* @brief Freezes the watchdog timer for debugging purposes.
*
* This function freezes the watchdog timer, allowing for debugging without triggering a watchdog reset.
*/
extern void debug_freeze_watchdog(void);
/**
* @brief Unfreezes the watchdog timer after debugging.
*
* This function unfreezes the watchdog timer, allowing it to resume normal operation after debugging.
*/
extern void debug_unfreeze_watchdog(void);
#endif

90
User/system/bsp/pwms.h Normal file
View File

@ -0,0 +1,90 @@
/**
* @file pwms.h
* @brief Header file for PWMs module.
*
* This file contains the declarations and documentation for the PWMs module.
*
* @author xxx
* @date 2023-12-27 14:44:03
* @version 1.0
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __PWMS_H__
#define __PWMS_H__
#include "lib.h"
/**
* @brief Starts the PWM for a specific channel
* @param TIMx: TIM instance
* @param CHx: Channel to be started
* @retval None
*/
#define PWM_START(TIMx, CHx) \
do \
{ \
LL_TIM_EnableCounter(TIMx); \
LL_TIM_CC_EnableChannel(TIMx, CHx); \
} while (__LINE__ == -1)
/**
* @brief Stops the PWM for a specific channel
* @param TIMx: TIM instance
* @param CHx: Channel to be stopped
* @retval None
*/
#define PWM_STOP(TIMx, CHx) \
do \
{ \
LL_TIM_DisableCounter(TIMx); \
LL_TIM_CC_DisableChannel(TIMx, CHx); \
} while (__LINE__ == -1)
/**
* @brief Sets the PWM frequency
* @param TIMx: TIM instance
* @param CHx: Channel to be set
* @param COMPARE: Compare value
* @retval None
*/
static inline void PWM_SET_COMPARE(TIM_TypeDef *TIMx, uint32_t CHx, uint16_t COMPARE)
{
switch (CHx)
{
case LL_TIM_CHANNEL_CH1:
LL_TIM_OC_SetCompareCH1(TIMx, COMPARE);
break;
case LL_TIM_CHANNEL_CH2:
LL_TIM_OC_SetCompareCH2(TIMx, COMPARE);
break;
case LL_TIM_CHANNEL_CH3:
LL_TIM_OC_SetCompareCH3(TIMx, COMPARE);
break;
case LL_TIM_CHANNEL_CH4:
LL_TIM_OC_SetCompareCH4(TIMx, COMPARE);
break;
default:
break;
}
}
/**
* @brief PWM占空比
*
* TIMx的指定通道CHx的PWM占空比
*
* @param TIMx TIM1TIM2等
* @param CHx TIM_CHANNEL_1TIM_CHANNEL_2等
* @param DUTY 0100
*/
static inline void PWM_SET_DUTY(TIM_TypeDef *TIMx, uint32_t CHx, uint16_t DUTY)
{
PWM_SET_COMPARE(TIMx, CHx, DUTY * LL_TIM_GetAutoReload(TIMx) / 100);
}
// 获取当前频率
static inline uint32_t PWM_GET_FREQ(TIM_TypeDef *TIMx)
{
return SystemCoreClock / (LL_TIM_GetPrescaler(TIMx) + 1) / (LL_TIM_GetAutoReload(TIMx) + 1);
}
#endif ///< __PWMS_H__

View File

@ -0,0 +1 @@

811
User/system/bsp/spis.c Normal file
View File

@ -0,0 +1,811 @@
#include "spis.h"
#include "delay.h"
#define SPI_TIMEOUT 2000
#define CMD_RDSR 0x05 /*!< Read Status Register instruction */
#define CMD_WRSR 0x01 /*!< Write Status Register instruction */
#define CMD_WREN 0x06 /*!< Write enable instruction */
#define CMD_WRDI 0x04 /*!< Write disable instruction */
#define CMD_READ 0x03 /*!< Read from Memory instruction */
#define CMD_WRITE 0x02 /*!< Write to Memory instruction */
#define DUMMY_BYTE 0xA5 ///< 虚拟字节
static inline void spi_delay(spi_t *handle); // 延时函数
static inline void spi_rdy_high(spi_t *handle); // RDY高电平
static inline void spi_rdy_low(spi_t *handle); // RDY低电平
static inline void spi_cs_high(spi_t *handle); // CS高电平
static inline void spi_cs_low(spi_t *handle); // CS低电平
static inline void spi_mosi_high(spi_t *handle); // MOSI高电平
static inline void spi_mosi_low(spi_t *handle); // MOSI低电平
static inline void spi_sck_high(spi_t *handle); // SCK高电平
static inline void spi_sck_low(spi_t *handle); // SCK低电平
static inline uint8_t spi_miso_read(spi_t *handle); // 读取MISO电平
static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data); // 读写一个字节
static void spi_reset(spi_t *handle); // 复位
static BOOL spi_write(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); // 写数据
static BOOL spi_read(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); // 读数据
static void spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data); // 写寄存器
static uint8_t spi_read_reg(spi_t *handle, uint8_t reg); // 读寄存器
static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi); // 硬件SPI
static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb); // DMA SPI
static void _spi_dma_callback(spi_t *handle); // DMA发送完成回调
static BOOL _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length); // DMA发送数据
static uint8_t _read_drdy(spi_t *handle); // 读取DRDY电平
static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 写多个寄存器
static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 读多个寄存器
static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data); // 写单个寄存器
static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg); // 读单个寄存器
static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd); // 写命令
static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len); // 写数据
/**
* @brief SPI总线设备
* @param {spi_type_e} spi_type SPI总线的类型
* @param {spi_gpio_group_t} gpios SPI总线的GPIO配置
* @param {uint16_t} delay_ticks SPI总线的延时参数
* @return {*} SPI总线设备句柄
* @note: SPI总线设备spi_type在有效的范围内spi_t结构体gpios和delay_ticks的内存地址复制到handle结构体中spi_type的值handle结构体
*/
spi_t *spi_create(spi_type_e spi_type, spi_gpio_group_t gpios, uint16_t delay_ticks)
{
DBG_ASSERT(spi_type < SPI_TYPE_MAX __DBG_LINE);
spi_t *handle = (spi_t *)osel_mem_alloc(sizeof(spi_t));
osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(spi_gpio_group_t));
handle->delay_ticks = delay_ticks;
handle->spi_type = spi_type;
handle->simualte_gpio = TRUE;
handle->interface.hardware_enable = _hardware_enable;
handle->interface.dma_enable = _dma_enable;
handle->interface.spi_dma_send = _spi_dma_send;
handle->interface.spi_dma_callback = _spi_dma_callback;
if (spi_type == SPI_TYPE_NORMAL)
{
handle->interface.u.normal.write_reg = _spi_write_reg;
handle->interface.u.normal.read_reg = _spi_read_reg;
handle->interface.u.normal.write_regs = _write_regs;
handle->interface.u.normal.read_regs = _read_regs;
handle->interface.u.normal.read_drdy = _read_drdy;
handle->interface.u.normal.spi_send = spi_read_write_byte;
handle->interface.u.normal.spi_reset = spi_reset;
handle->interface.u.normal.spi_read = spi_read;
handle->interface.u.normal.spi_write = spi_write;
handle->interface.u.normal.spi_write_reg = spi_write_reg;
handle->interface.u.normal.spi_read_reg = spi_read_reg;
handle->cfg.cmd_rdsr = CMD_RDSR;
handle->cfg.cmd_wrsr = CMD_WRSR;
handle->cfg.cmd_wren = CMD_WREN;
handle->cfg.cmd_wrdi = CMD_WRDI;
handle->cfg.cmd_read = CMD_READ;
handle->cfg.cmd_write = CMD_WRITE;
handle->cfg.dummy_byte = DUMMY_BYTE;
handle->cfg.continuous_write = FALSE;
}
else if (spi_type == SPI_TYPE_LCD)
{
handle->interface.u.lcd.write_cmd = _spi_write_cmd;
handle->interface.u.lcd.write_data = _spi_write_data;
}
else
{
DBG_ASSERT(FALSE __DBG_LINE);
}
return handle;
}
void spi_free(spi_t *handle)
{
if (handle != NULL)
{
gpio_free(handle->gpios.cs);
gpio_free(handle->gpios.rdy);
gpio_free(handle->gpios.rst);
gpio_free(handle->gpios.mosi);
gpio_free(handle->gpios.miso);
gpio_free(handle->gpios.sck);
osel_mem_free(handle);
}
}
/**
* @brief SPI模式
* @param {spi_t} *handle SPI总线设备句柄
* @param {SPI_TypeDef} *spi SPI总线的寄存器结构体指针
* @return {*}
* @note: SPI总线的硬件模式handle不为空spix不为空handle的simulate_gpio设置为FALSEspix的地址赋值给handle->spixSPI_ENABLE函数启用SPI总线
*/
static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(spi != NULL __DBG_LINE);
handle->simualte_gpio = FALSE;
handle->spi = spi;
SPI_ENABLE(spi);
}
static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(dma != NULL __DBG_LINE);
DBG_ASSERT(handle->spi != NULL __DBG_LINE);
handle->dma = dma;
handle->dma_rx_channel = dma_rx_channel;
handle->dma_tx_channel = dma_tx_channel;
handle->rx_dma_ok = TRUE;
handle->tx_dma_ok = TRUE;
if (dma_rx_cb != NULL)
{
handle->dma_rx_cb = dma_rx_cb;
}
if (dma_tx_cb != NULL)
{
handle->dma_tx_cb = dma_tx_cb;
}
LL_DMA_SetPeriphAddress(dma, dma_tx_channel, LL_SPI_DMA_GetRegAddr(handle->spi));
LL_DMA_ClearFlag_GI1(dma);
LL_DMA_ClearFlag_TC1(dma);
LL_DMA_EnableIT_TC(dma, dma_tx_channel);
LL_SPI_EnableDMAReq_TX(handle->spi);
LL_SPI_Enable(handle->spi);
}
static void _spi_dma_callback(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
if (handle->dma_tx_cb != NULL)
{
handle->dma_tx_cb(handle);
}
if (handle->dma_rx_cb != NULL)
{
handle->dma_rx_cb(handle);
}
}
static BOOL _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length)
{
handle->tx_dma_ok = FALSE;
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
LL_DMA_SetMemoryAddress(handle->dma, handle->dma_tx_channel, (uint32_t)data);
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, length);
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
uint16_t i = 0;
while (handle->tx_dma_ok == FALSE)
{
i++;
if (i > 50000)
{
__NOP();
break;
}
}
return handle->tx_dma_ok == TRUE ? TRUE : FALSE;
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @return {uint8_t}
* @note: SPI总线的数据准备好信号handle不为空handle的gpios.rdy的读取结果
*/
static uint8_t _read_drdy(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
return handle->gpios.rdy->read(*handle->gpios.rdy);
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @param {uint8_t} reg
* @param {uint8_t} data
* @return {*}
* @note: SPI总线的单个寄存器handle不为空data不为空SPI总线的CS引脚设置为低电平SPI总线的CS引脚设置为高电平
*/
static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len)
{
uint8_t status = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(data != NULL __DBG_LINE);
DBG_ASSERT(len != 0 __DBG_LINE);
spi_cs_low(handle);
status = spi_read_write_byte(handle, reg); // 发送寄存器号
while (len--)
{
spi_read_write_byte(handle, *data); // 写入寄存器的值
data++;
}
spi_cs_high(handle);
return status;
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @param {uint8_t} reg
* @param {uint8_t} *data
* @param {uint8_t} len
* @return {uint8_t}
* @note: SPI总线的多个寄存器handle不为空data不为空len大于0SPI总线的CS引脚设置为低电平SPI总线的CS引脚设置为高电平
*/
static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len)
{
uint8_t status = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(data != NULL __DBG_LINE);
DBG_ASSERT(len != 0 __DBG_LINE);
spi_cs_low(handle);
status = spi_read_write_byte(handle, reg);
for (uint8_t i = 0; i < len; i++)
{
data[i] = spi_read_write_byte(handle, 0xff); // 读出数据
}
spi_cs_high(handle);
return status;
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @param {uint8_t} reg
* @param {uint8_t} data
* @return {*}
* @note: SPI总线的单个寄存器handle不为空SPI总线的CS引脚设置为低电平SPI总线的CS引脚设置为高电平
*/
static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data)
{
uint8_t status = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
spi_cs_low(handle);
status = spi_read_write_byte(handle, reg); // 发送寄存器号
spi_read_write_byte(handle, data); // 写入寄存器的值
spi_cs_high(handle);
return status;
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @param {uint8_t} reg
* @return {*}
* @note: SPI总线的单个寄存器handle不为空SPI总线的CS引脚设置为低电平SPI总线的CS引脚设置为高电平
*/
static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg)
{
uint8_t reg_val = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
spi_cs_low(handle);
spi_read_write_byte(handle, reg); // 发送寄存器号
reg_val = spi_read_write_byte(handle, 0);
spi_cs_high(handle);
return reg_val;
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @param {uint8_t} cmd
* @return {*}
* @note: SPI总线的命令handle不为空SPI总线的RDY引脚设置为低电平SPI总线的CS引脚设置为高电平
*/
static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd)
{
uint8_t status = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
spi_rdy_low(handle);
spi_cs_low(handle);
status = spi_read_write_byte(handle, cmd); // 发送命令
spi_cs_high(handle);
return status;
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @param {uint8_t} *data
* @param {uint16_t} len
* @return {*}
* @note: SPI总线的数据handle不为空SPI总线的RDY引脚设置为高电平SPI总线的CS引脚设置为高电平
*/
static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len)
{
uint8_t status = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
spi_rdy_high(handle);
spi_cs_low(handle);
for (uint16_t i = 0; i < len; i++)
{
status = spi_read_write_byte(handle, *data);
data++;
}
spi_cs_high(handle);
return status;
}
/**
* @brief SPI延时函数
* @param {spi_t} *handle SPI总线设备句柄
* @return {*}
* @note: SPI总线的延时handle不为空handle的delay_ticks的值NOP指令
*/
static inline void spi_delay(spi_t *handle)
{
uint16_t count = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
count = handle->delay_ticks;
if (count > 0)
{
while (count--)
{
__NOP();
}
}
}
static BOOL spi_wait_flag(spi_t *handle, uint32_t flag, uint32_t timeout)
{
uint32_t i = 0;
DBG_ASSERT(handle != NULL __DBG_LINE);
if (flag == LL_SPI_SR_TXE)
{
while (LL_SPI_IsActiveFlag_TXE(handle->spi) == 0)
{
if (i++ > timeout)
{
return FALSE; // 超时
}
else
{
__NOP();
}
}
}
else if (flag == LL_SPI_SR_RXNE)
{
while (LL_SPI_IsActiveFlag_RXNE(handle->spi) == 0)
{
if (i++ > timeout)
{
return FALSE; // 超时
}
else
{
__NOP();
}
}
}
else if (flag == LL_SPI_SR_BSY)
{
while (LL_SPI_IsActiveFlag_BSY(handle->spi) == 0)
{
if (i++ > timeout)
{
return FALSE; // 超时
}
else
{
__NOP();
}
}
}
else
{
DBG_ASSERT(FALSE __DBG_LINE);
}
return TRUE; // 成功
}
/**
* @brief
* @param {spi_t} *handle SPI总线设备句柄
* @param {uint8_t} tx_data
* @return {*}
* @note: SPI总线的读写一个字节handle不为空handle的simulate_gpio的值SPI或硬件SPI
*/
static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data)
{
uint8_t bit_ctr;
uint8_t rdata = 0xff;
DBG_ASSERT(handle != NULL __DBG_LINE);
if (handle->simualte_gpio == TRUE)
{
// 模拟SPI
for (bit_ctr = 0; bit_ctr < 8; bit_ctr++)
{
spi_sck_low(handle);
spi_delay(handle);
if (tx_data & 0x80)
spi_mosi_high(handle);
else
spi_mosi_low(handle);
spi_delay(handle);
spi_sck_high(handle);
spi_delay(handle);
tx_data = (tx_data << 1);
rdata = rdata << 1;
if (NULL != handle->gpios.miso->port)
{
if (spi_miso_read(handle) == 1)
rdata += 0x01;
}
}
return (rdata);
}
else
{
LL_SPI_TransmitData8(handle->spi, tx_data);
if (spi_wait_flag(handle, LL_SPI_SR_RXNE, SPI_TIMEOUT) == FALSE)
{
return 0xff;
}
rdata = LL_SPI_ReceiveData8(handle->spi);
return rdata;
}
}
static void _write_enable(spi_t *handle)
{
handle->gpios.cs->reset(*handle->gpios.cs);
handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_wren);
handle->gpios.cs->set(*handle->gpios.cs);
}
static void _write_disable(spi_t *handle)
{
handle->gpios.cs->reset(*handle->gpios.cs);
handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_wrdi);
handle->gpios.cs->set(*handle->gpios.cs);
}
static uint8_t _read_status(spi_t *handle)
{
uint8_t data;
handle->gpios.cs->reset(*handle->gpios.cs);
handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_rdsr);
data = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte);
handle->gpios.cs->set(*handle->gpios.cs);
return data;
}
static void _ready(spi_t *handle)
{
uint16_t count = 0;
while (_read_status(handle) & 0x01)
{
if (count++ > 20000)
{
break;
}
else
{
__NOP();
}
}
}
static BOOL spi_write(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length)
{
BOOL ret = TRUE;
uint8_t cnt = 0; // 返回值检查
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(data != NULL __DBG_LINE);
DBG_ASSERT(length > 0 __DBG_LINE);
DBG_ASSERT(handle->cfg.page_size > 0 __DBG_LINE);
uint32_t page_size = handle->cfg.page_size;
_write_enable(handle); // 写入使能命令
handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平准备开始SPI通信
cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_write); // 发送写入命令
if (cnt == 0)
{
return FALSE;
}
if (handle->cfg.address_bytes == 2)
{
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); // 发送高位地址
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, write_addr); // 发送低位地址
if (cnt == 0)
{
return FALSE;
}
}
else
{
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 16);
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8);
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, write_addr);
if (cnt == 0)
{
return FALSE;
}
}
while (length--)
{
cnt = handle->interface.u.normal.spi_send(handle, *data); // 发送一个字节数据
if (cnt == 0)
{
return FALSE;
}
data++;
if (handle->cfg.continuous_write == FALSE)
{
write_addr++;
if (((write_addr % page_size) == 0) && (length > 0))
{
// 一页写完
handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平完成SPI通信
_ready(handle);
_write_enable(handle); // 写入使能命令
handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平准备开始SPI通信
cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_write); // 发送写入命令
if (cnt == 0)
{
return FALSE;
}
if (handle->cfg.address_bytes == 2)
{
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); // 发送高位地址
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, write_addr); // 发送低位地址
if (cnt == 0)
{
return FALSE;
}
}
else
{
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 16);
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8);
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, write_addr);
if (cnt == 0)
{
return FALSE;
}
}
}
}
}
handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平完成SPI通信
_ready(handle);
_write_disable(handle);
return ret;
}
static void spi_write_reg(spi_t *handle, uint8_t reg, uint8_t value)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
_write_enable(handle); // 写入使能命令
handle->interface.u.normal.write_reg(handle, reg, value);
_ready(handle);
_write_disable(handle); // 写入禁止命令
}
static uint8_t spi_read_reg(spi_t *handle, uint8_t reg)
{
uint8_t data;
handle->gpios.cs->reset(*handle->gpios.cs);
handle->interface.u.normal.spi_send(handle, reg);
data = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte);
handle->gpios.cs->set(*handle->gpios.cs);
return data;
}
static BOOL spi_read(spi_t *handle, uint32_t read_addr, uint8_t *data, uint16_t length)
{
BOOL ret = TRUE;
uint8_t cnt = 0; // 返回值检查
DBG_ASSERT(handle != NULL __DBG_LINE);
DBG_ASSERT(data != NULL __DBG_LINE);
DBG_ASSERT(length > 0 __DBG_LINE);
handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平准备开始SPI通信
cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_read); // 发送读取命令
if (cnt == 0)
{
return FALSE;
}
if (handle->cfg.address_bytes == 2)
{
cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 8); // 发送高位地址
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, read_addr); // 发送低位地址
if (cnt == 0)
{
return FALSE;
}
}
else
{
cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 16);
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 8);
if (cnt == 0)
{
return FALSE;
}
cnt = handle->interface.u.normal.spi_send(handle, read_addr);
if (cnt == 0)
{
return FALSE;
}
}
for (uint16_t i = 0; i < length; i++) // 循环读取数据
{
data[i] = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte); // 发送空字节,读取实际数据
}
handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平完成SPI通信
return ret;
}
static void spi_reset(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.cs->reset(*handle->gpios.cs);
delay_tick(10);
handle->gpios.cs->set(*handle->gpios.cs);
delay_tick(10);
}
/**
* @brief SPIRST
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_rdy_high(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.rdy->set(*handle->gpios.rdy);
}
/**
* @brief SPIRST
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_rdy_low(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.rdy->reset(*handle->gpios.rdy);
}
/**
* @brief SPICS
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_cs_high(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.cs->set(*handle->gpios.cs);
spi_delay(handle);
}
/**
* @brief SPICS
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_cs_low(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.cs->reset(*handle->gpios.cs);
spi_delay(handle);
}
/**
* @brief SPISCK
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_mosi_high(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.mosi->set(*handle->gpios.mosi);
}
/**
* @brief SPIMOSI
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_mosi_low(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.mosi->reset(*handle->gpios.mosi);
}
/**
* @brief SPISCK
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_sck_high(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.sck->set(*handle->gpios.sck);
}
/**
* @brief SPISCK
* @param {spi_id_e} id
* @return {*}
*/
static inline void spi_sck_low(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
handle->gpios.sck->reset(*handle->gpios.sck);
}
/**
* @brief SPIMISO
* @param {spi_id_e} id
* @return {*}
*/
static inline uint8_t spi_miso_read(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
return handle->gpios.miso->read(*handle->gpios.miso);
}

165
User/system/bsp/spis.h Normal file
View File

@ -0,0 +1,165 @@
/**
* @file spis.h
* @brief SPI驱动, SPI设备的读写操作
*
* This file contains the SPI driver used for reading and writing operations on SPI devices.
*
* @date 2023-08-01
* @version 1.0
*
* @note This file is part of the STM32 controller-v2 project.
*
*/
#ifndef __SPIS_H__
#define __SPIS_H__
#include "lib.h"
#include "gpios.h"
#define SPI_ENABLE(SPIX) LL_SPI_Enable(SPIX)
typedef struct SPIS spi_t;
typedef void spis_dma_callback(spi_t *handle);
/**
* @brief SPI type enumeration
*/
typedef enum
{
SPI_TYPE_NORMAL = 0, ///< SPI1:NORMAL
SPI_TYPE_LCD, ///< SPI2:LCD
SPI_TYPE_MAX,
} spi_type_e;
/**
* @brief SPI GPIO group structure
*/
typedef struct
{
gpio_t *mosi; ///< MOSI
gpio_t *miso; ///< MISO
gpio_t *sck; ///< SCK
gpio_t *cs; ///< CS
gpio_t *rst; ///< RST
gpio_t *rdy; ///< DRDY
} spi_gpio_group_t;
/**
* @brief SPI normal interface structure
*/
typedef struct
{
uint8_t (*write_reg)(spi_t *handle, uint8_t reg, uint8_t data); ///< Write a single register via SPI
uint8_t (*read_reg)(spi_t *handle, uint8_t reg); ///< Read the value of a single register via SPI
uint8_t (*read_drdy)(spi_t *handle); ///< Get the value of the SPI DRDY pin
uint8_t (*write_regs)(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); ///< Write multiple registers via SPI
uint8_t (*read_regs)(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); ///< Read multiple registers via SPI
uint8_t (*spi_send)(spi_t *handle, uint8_t data); ///< Send data via SPI
void (*spi_reset)(spi_t *handle); ///< Reset SPI
BOOL(*spi_write)
(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); ///< Write data via SPI
BOOL(*spi_read)
(spi_t *handle, uint32_t read_addr, uint8_t *data, uint16_t length); ///< Read data via SPI
void (*spi_write_reg)(spi_t *handle, uint8_t reg, uint8_t data); ///< Write a single register via SPI
uint8_t (*spi_read_reg)(spi_t *handle, uint8_t reg); ///< Read a single register via SPI
} spi_normal_interface_t;
/**
* @brief SPI LCD interface structure
*/
typedef struct
{
uint8_t (*write_cmd)(spi_t *handle, uint8_t cmd); ///< Write a command via SPI
uint8_t (*write_data)(spi_t *handle, uint8_t *data, uint16_t len); ///< Write data via SPI
} spi_lcd_interface_t;
/**
* @brief SPI interface structure
*/
typedef struct
{
union
{
spi_normal_interface_t normal;
spi_lcd_interface_t lcd;
} u;
void (*hardware_enable)(spi_t *handle, SPI_TypeDef *spi); ///< Enable hardware SPI
void (*dma_enable)(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb); ///< Enable DMA SPI
void (*spi_dma_callback)(spi_t *spi); ///< DMA send completion callback
BOOL(*spi_dma_send)
(spi_t *handle, uint8_t *data, uint16_t length); ///< DMA send
} spi_interface_t;
/**
* @brief SPI structure
*/
typedef struct SPIS spi_t;
/**
* @brief SPI DMA callback function
*/
typedef void spis_dma_callback(spi_t *handle);
typedef struct
{
// CMD
uint8_t cmd_rdsr; ///< Read Status Register instruction
uint8_t cmd_wrsr; ///< Write Status Register instruction
uint8_t cmd_wren; ///< Write enable instruction
uint8_t cmd_wrdi; ///< Write disable instruction
uint8_t cmd_read; ///< Read from Memory instruction
uint8_t cmd_write; ///< Write to Memory instruction
uint8_t dummy_byte; ///< Dummy byte
uint8_t address_bytes;
uint32_t page_size;
uint32_t total_size;
uint8_t ticks; ///< Delay in NOP ticks
BOOL continuous_write; ///< Continuous write
} spi_normal_config_t;
struct SPIS
{
spi_type_e spi_type; ///< SPI type
uint16_t delay_ticks; ///< Delay in NOP ticks
spi_gpio_group_t gpios; ///< SPI GPIOs
spi_interface_t interface; ///< SPI interface
SPI_TypeDef *spi; ///< SPI peripheral
BOOL simualte_gpio; ///< Simulate GPIO
spi_normal_config_t cfg; ///< Normal SPI configuration
///< DMA
DMA_TypeDef *dma; ///< External setting
uint32_t dma_rx_channel; ///< External setting
uint32_t dma_tx_channel; ///< External setting
__IO BOOL rx_dma_ok;
__IO BOOL tx_dma_ok;
spis_dma_callback *dma_rx_cb; ///< DMA receive callback function
spis_dma_callback *dma_tx_cb; ///< DMA send callback function
void *params; ///< 扩展参数
};
/**
* @brief Create a new SPI instance
*
* @param spi_type The type of SPI
* @param gpios The SPI GPIO group
* @param delay_ticks The delay in NOP ticks
* @return spi_t* The created SPI instance
*/
extern spi_t *spi_create(spi_type_e spi_type, spi_gpio_group_t gpios, uint16_t delay_ticks);
/**
* @brief Free the SPI instance
*
* @param spi The SPI instance to free
*/
extern void spi_free(spi_t *spi);
#endif ///< __SPIS_H__

1
User/system/bsp/tims.c Normal file
View File

@ -0,0 +1 @@
#include "tims.h"

128
User/system/bsp/tims.h Normal file
View File

@ -0,0 +1,128 @@
/**
* @file tims.h
* @brief Header file for TIMS module.
*
* This file contains the declarations and definitions for the TIMS module.
* TIMS stands for Timer System and provides functionality related to timers.
*
* @author xxx
* @date 2024-01-16 22:23:43
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __TIMS_H__
#define __TIMS_H__
#include "lib.h"
/**
Timer overflow time calculation formula
Tout = ((ARR + 1)*(PSC + 1)) / Tclk
Given Tclk as 84MHz, we need Tout to be 200ms or 200000us. Let's assume PSC is 839, substituting it into the formula gives ARR = 19999.
With these calculated values, both ARR and PSC are within the range of 0 to 65535, so we can use this parameter set.
*/
#define ENABLE_TIM_COUNT(TIMx) \
do \
{ \
LL_TIM_EnableCounter(TIMx); \
} while (__LINE__ == -1);
#define RESET_TIM_COUNT(TIMx) \
do \
{ \
LL_TIM_DisableCounter(TIMx); \
LL_TIM_SetCounter(TIMx, 0); \
LL_TIM_EnableCounter(TIMx); \
} while (__LINE__ == -1);
/**
* @brief Enables the specified TIMx.
* @param TIMx TIM instance.
*/
#define ENABLE_TIM(TIMx) \
do \
{ \
LL_TIM_EnableCounter(TIMx); \
LL_TIM_EnableIT_UPDATE(TIMx); \
} while (__LINE__ == -1);
/**
* @brief Checks if the specified TIMx is enabled.
* @param TIMx TIM instance.
* @retval The new state of TIMx (1 or 0).
*/
#define IS_ENABLE_TIM(TIMx) LL_TIM_IsEnabledIT_UPDATE(TIMx)
/**
* @brief Disables the specified TIMx.
* @param TIMx TIM instance.
*/
#define DISABLE_TIM(TIMx) \
do \
{ \
LL_TIM_DisableCounter(TIMx); \
LL_TIM_DisableIT_UPDATE(TIMx); \
} while (__LINE__ == -1);
#define ENABLE_TIM_ARR_RELOAD(TIMx) LL_TIM_EnableARRPreload(TIMx)
#define DISABLE_TIM_ARR_RELOAD(TIMx) LL_TIM_DisableARRPreload(TIMx)
/**
* @brief Checks if the specified TIMx interrupt flag is set.
* @param TIMx TIM instance.
* @retval The new state of the specified TIMx interrupt flag (1 or 0).
*/
#define IS_TIM_IT_FLAG(TIMx) (LL_TIM_IsActiveFlag_UPDATE(TIMx) == 1)
/**
* @brief TIM interrupt handler.
* @param TIMx TIM instance.
*/
#define TIM_IRQ_HANDLER(TIMx) \
do \
{ \
if (LL_TIM_IsActiveFlag_CC1(TIMx) == SET) \
{ \
if (LL_TIM_IsEnabledIT_CC1(TIMx) == SET) \
{ \
LL_TIM_ClearFlag_CC1(TIMx); \
} \
} \
if (LL_TIM_IsActiveFlag_CC2(TIMx) == SET) \
{ \
if (LL_TIM_IsEnabledIT_CC2(TIMx) == SET) \
{ \
LL_TIM_ClearFlag_CC2(TIMx); \
} \
} \
if (LL_TIM_IsActiveFlag_CC3(TIMx) == SET) \
{ \
if (LL_TIM_IsEnabledIT_CC3(TIMx) == SET) \
{ \
LL_TIM_ClearFlag_CC3(TIMx); \
} \
} \
if (LL_TIM_IsActiveFlag_CC4(TIMx) == SET) \
{ \
if (LL_TIM_IsEnabledIT_CC4(TIMx) == SET) \
{ \
LL_TIM_ClearFlag_CC4(TIMx); \
} \
} \
if (LL_TIM_IsActiveFlag_UPDATE(TIMx) == SET) \
{ \
if (LL_TIM_IsEnabledIT_UPDATE(TIMx) == SET) \
{ \
LL_TIM_ClearFlag_UPDATE(TIMx); \
} \
} \
} while (__LINE__ == -1)
/**
* @brief
*
* TIMx的周期时间
*
* @param TIMx
*
* @return
*/
#define TIM_CYCLE(TIMx) ((LL_TIM_GetAutoReload(TIMx) + 1) * 0.1)
#endif ///< __TIMS_H__

486
User/system/bsp/uarts.c Normal file
View File

@ -0,0 +1,486 @@
/*
* @Author:
* @Date: 2023-07-31 11:47:35
* @LastEditors: xxx
* @LastEditTime: 2023-08-25 15:30:33
* @Description: LL库的串口驱动
* email:
* Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "uarts.h"
#include "dma.h"
// 清理接收中断错误标志
static void uart_clear_error(uart_t *uart)
{
if (uart == NULL)
{
return;
}
uart->rx_error_count = 0;
if (uart->rx_err_en == TRUE && uart->rx_error != NULL)
{
osel_memset((uint8_t *)uart->rx_error, 0, sizeof(uarts_interupt_error_t) * uart->rxsize);
}
}
/**
* @brief UART设备
* @param {USART_TypeDef} *huart USART总线设备句柄
* @param {BOOL} rx_dma_en DMA使能标志
* @param {uint16_t} rxsize
* @param {rx_interrupt_cb_t} rx_cb
* @param {BOOL} tx_dma_en DMA使能标志
* @param {uint16_t} txsize
* @param {tx_complete_cb_t} tx_complete_cb
* @return {*} UART设备指针
* @note: UART设备huart不为空UART设备指针
*/
uart_t *uart_create(USART_TypeDef *huart, BOOL rx_dma_en, uint16_t rxsize, rx_interupt_cb_t rx_cb,
BOOL tx_dma_en, uint16_t txsize, tx_complete_cb_t tx_complete_cb)
{
DBG_ASSERT(huart != NULL __DBG_LINE);
// 分配内存
uart_t *uart = (uart_t *)osel_mem_alloc(sizeof(uart_t));
DBG_ASSERT(uart != NULL __DBG_LINE);
uart->rx_interupt_timeout = TRUE; // 接收超时标志
uart->rx_interupt_cnt = 0; // 接收中断计数
// 设置接收回调函数
uart->rx_interupt_cb = rx_cb;
// 设置接收数据大小
uart->rxsize = rxsize;
// 设置发送完成回调函数
uart->tx_complete_cb = tx_complete_cb;
// 设置发送数据大小
uart->txsize = txsize;
// 如果接收大小大于0则分配内存
if (rxsize > 0)
{
uart->rxbuf = (uint8_t *)osel_mem_alloc(rxsize);
DBG_ASSERT(uart->rxbuf != NULL __DBG_LINE);
}
// 如果发送大小大于0则分配内存
if (txsize > 0)
{
uart->txbuf = (uint8_t *)osel_mem_alloc(txsize);
DBG_ASSERT(uart->txbuf != NULL __DBG_LINE);
}
// 设置接收DMA禁用
uart->rx_dma_en = rx_dma_en;
// 设置发送DMA禁用
uart->tx_dma_en = tx_dma_en;
// 设置huart
uart->huart = huart;
// 返回uart
return uart;
}
/**
* @brief 使UART接收
* @param {uart_t} *uart UART设备句柄
* @param {BOOL} rx_err_en 使,使
* @return {*}
* @note: 使UART设备的接收功能UART设备的接收DMA使能标志RX DMA并启用RX DMA通道UART设备的发送DMA使能标志TX DMA并启用TX DMA通道
*/
void uart_recv_en(uart_t *uart, BOOL rx_err_en)
{
if (FALSE == uart->rx_dma_en)
{
uart->rx_err_en = rx_err_en;
LL_USART_EnableIT_RXNE(uart->huart); // 使用接收中断处理
}
else
{
uart->rx_err_en = FALSE;
LL_USART_ClearFlag_IDLE(uart->huart);
// 配置RX DMA
LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel);
LL_DMA_DisableChannel(uart->dma, uart->dma_rx_channel);
// 配置RX DMA
LL_DMA_SetPeriphAddress(uart->dma, uart->dma_rx_channel, LL_USART_DMA_GetRegAddr(uart->huart));
LL_DMA_SetMemoryAddress(uart->dma, uart->dma_rx_channel, (uint32_t)uart->rxbuf);
LL_DMA_SetDataLength(uart->dma, uart->dma_rx_channel, uart->rxsize);
LL_DMA_EnableIT_TC(uart->dma, uart->dma_rx_channel);
LL_DMA_EnableChannel(uart->dma, uart->dma_rx_channel);
LL_USART_EnableDMAReq_RX(uart->huart);
LL_USART_EnableIT_IDLE(uart->huart);
// 配置TX DMA
LL_DMA_SetPeriphAddress(uart->dma, uart->dma_tx_channel, LL_USART_DMA_GetRegAddr(uart->huart));
// 配置内存地址
LL_DMA_SetMemoryAddress(uart->dma, uart->dma_tx_channel, (uint32_t)uart->txbuf);
LL_DMA_EnableIT_TC(uart->dma, uart->dma_tx_channel);
LL_USART_EnableDMAReq_TX(uart->huart);
uart->tx_dma_ok = TRUE;
}
if (uart->rx_err_en == TRUE)
{
if (uart->rx_error == NULL)
{
uart->rx_error = (uarts_interupt_error_t *)osel_mem_alloc(sizeof(uarts_interupt_error_t) * uart->rxsize);
DBG_ASSERT(uart->rx_error != NULL __DBG_LINE);
}
LL_USART_EnableIT_PE(uart->huart); // 使能奇偶校验错误中断
// 使能帧错误中断
// 使能帧错误中断
// 使能溢出错误中断
// LL_USART_EnableIT_ERROR 可以使能上面3个中断
/**
* When set, Error Interrupt Enable Bit is enabling interrupt generation in case of a framing
* error, overrun error or noise flag (FE=1 or ORE=1 or NF=1 in the USARTx_ISR register).
*/
LL_USART_EnableIT_ERROR(uart->huart);
}
}
/**
* @brief UART设备资源
* @param {uart_t} *uart UART设备句柄
* @return {*}
* @note: UART设备的接收缓冲区UART设备本身
*/
void uart_free(uart_t *uart)
{
if (uart != NULL)
{
if (uart->rxbuf != NULL)
{
osel_mem_free(uart->rxbuf);
}
if (uart->rx_error != NULL)
{
osel_mem_free(uart->rx_error);
}
if (uart->txbuf != NULL)
{
osel_mem_free(uart->txbuf);
}
osel_mem_free(uart);
}
}
/**
* @brief
* @param {uart_t} *uart
* @param {uint32_t} baudrate
* @return {*}
* @note
*/
void uart_set_baudrate(USART_TypeDef *uart, uint32_t baudrate)
{
LL_USART_SetBaudRate(uart, SystemCoreClock, LL_USART_OVERSAMPLING_16);
}
/**
* @brief
* @param {uart_t} *uart UART设备句柄
* @param {uint8_t} *data
* @param {uint16_t} len
* @return {*}
* @note: UART设备的发送DMA使能标志TX DMA并启用TX DMA通道
*/
void uart_send_data(uart_t *uart, uint8_t *data, uint16_t len)
{
DBG_ASSERT(uart != NULL __DBG_LINE);
DBG_ASSERT(data != NULL __DBG_LINE);
DBG_ASSERT(len > 0 __DBG_LINE);
uint16_t count = 0;
if (TRUE == uart->tx_dma_en)
{
uart->tx_dma_ok = FALSE;
osel_memcpy(uart->txbuf, data, len); // 拷贝数据到发送缓冲区
LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel);
// 配置 DMA 源地址
LL_DMA_SetMemoryAddress(uart->dma, uart->dma_tx_channel, (uint32_t)uart->txbuf);
// 配置数据长度
LL_DMA_SetDataLength(uart->dma, uart->dma_tx_channel, len);
// 使能DMA STREAM 也就是发送数据
LL_DMA_EnableChannel(uart->dma, uart->dma_tx_channel);
// 等待DMA发送完成
while (uart->tx_dma_ok == FALSE)
{
if (count++ >= 2000)
{
return;
}
}
}
else
{
count = 0;
for (uint16_t i = 0; i < len; i++)
{
count = 0;
LL_USART_TransmitData8(uart->huart, data[i]);
while (!LL_USART_IsActiveFlag_TXE(uart->huart))
{
if (count++ >= 0xFE)
{
count = 0;
continue;
}
}
}
while (!LL_USART_IsActiveFlag_TC(uart->huart))
{
if (count++ >= 0xFE)
{
count = 0;
continue;
}
}
if (uart->tx_complete_cb != NULL)
{
uart->tx_complete_cb();
}
LL_USART_ClearFlag_TC(uart->huart);
}
}
/**
* @brief UART接收超时定时器
*
* UART接收超时定时器触发时UART接收超时事件
*
* @param uart UART对象指针
*/
void uart_rx_timeout_timer(uart_t *uart)
{
// DBG_ASSERT(uart != NULL __DBG_LINE);
if (uart == NULL)
{
return;
}
if (uart->rx_dma_en == FALSE && uart->rx_interupt_timeout == FALSE) // 中断方式
{
if (uart->rx_interupt_cnt++ == RX_TIMEOUT_MSEC)
{
uart->rx_interupt_timeout = TRUE;
if (uart->rx_interupt_cb != NULL && uart->rx_index > 0)
{
uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index);
}
uart_data_storage_reset(uart);
}
}
}
/**
* @brief UART接收完成回调函数
*
* UART接收完成时调用此函数
*
* @param uart UART设备指针
*/
void uart_rx_cd_callback(uart_t *uart)
{
if (uart == NULL)
{
return;
}
if (uart->rx_cd_en == FALSE)
{
return;
}
if (uart->rx_dma_en == FALSE) // 中断方式
{
if (uart->rx_interupt_cb != NULL && uart->rx_index > 0)
{
uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index);
}
uart_data_storage_reset(uart);
}
}
/**
* @brief UART通信错误计数
*
* UART设备的接收错误计数
*
* @param uart UART设备指针
*
* @return uint16_t uart为NULL0
*/
uint16_t uart_get_error_count(uart_t *uart)
{
if (uart == NULL)
{
return 0;
}
return uart->rx_error_count;
}
/**
* @brief UART通信中的错误信息
*
* UART设备中获取接收错误信息和错误计数
*
* @param uart UART设备指针
* @param count
*
* @return NULL
*/
uarts_interupt_error_t *uart_get_error(uart_t *uart)
{
if (uart == NULL)
{
return NULL;
}
if (uart->rx_error_count > 0)
{
return uart->rx_error;
}
else
{
return NULL;
}
}
/**
* @brief UART数据存储
*
* UART接收到的数据缓冲区重置
*
* @param uart UART结构体指针
*/
void uart_data_storage_reset(uart_t *uart)
{
if (uart == NULL)
{
return;
}
uart->rx_index = 0;
uart_clear_error(uart);
}
/**
* @brief
* @param {uart_t} *uart UART设备句柄
* @return {*}
* @note: DMA使能标志RX DMA并启用RX DMA通道RX DMA通道并重置接收索引
*/
void uart_reception_callback(uart_t *uart)
{
// DBG_ASSERT(uart != NULL __DBG_LINE);
if (uart == NULL)
{
return;
}
if (LL_USART_IsEnabledIT_RXNE(uart->huart) && LL_USART_IsActiveFlag_RXNE(uart->huart))
{
if (uart->rx_index >= uart->rxsize)
{
uart_data_storage_reset(uart);
}
uart->rxbuf[uart->rx_index++] = LL_USART_ReceiveData8(uart->huart);
uart->rx_interupt_cnt = 0;
uart->rx_interupt_timeout = FALSE;
// 数据交给超时中断处理 uart_rx_timeout_timer
}
else if (LL_USART_IsEnabledIT_IDLE(uart->huart) && LL_USART_IsActiveFlag_IDLE(uart->huart))
{
if (uart->rx_dma_en == TRUE)
{
uart->rx_index = uart->rxsize - LL_DMA_GetDataLength(uart->dma, uart->dma_rx_channel);
if (uart->rx_cd_en == FALSE)
{
LL_DMA_DisableChannel(uart->dma, uart->dma_rx_channel);
if (uart->rx_interupt_cb != NULL && (uart->rx_index > 0 && uart->rx_index <= uart->rxsize))
{
uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index);
osel_memset(uart->rxbuf, 0, uart->rxsize);
}
LL_DMA_SetDataLength(uart->dma, uart->dma_rx_channel, uart->rxsize); // 这个不能少 先关闭DMA才能重新设置长度
LL_DMA_EnableChannel(uart->dma, uart->dma_rx_channel);
}
}
uart->rx_index = 0;
LL_USART_ClearFlag_IDLE(uart->huart);
}
if (LL_USART_IsEnabledIT_TC(uart->huart) && LL_USART_IsActiveFlag_TC(uart->huart))
{
if (uart->tx_complete_cb != NULL)
{
uart->tx_complete_cb();
}
LL_USART_ClearFlag_TC(uart->huart);
}
if (uart->rx_err_en == TRUE)
{
uarts_interupt_error_e err = UART_NO_ERROR;
if (LL_USART_IsEnabledIT_PE(uart->huart) && LL_USART_IsActiveFlag_PE(uart->huart))
{
err = UART_PARITY_ERROR;
LL_USART_ClearFlag_PE(uart->huart); // 清除奇偶校验错误标志
}
if (LL_USART_IsActiveFlag_FE(uart->huart) && LL_USART_IsActiveFlag_FE(uart->huart))
{
err = UART_FRAME_ERROR;
LL_USART_ClearFlag_FE(uart->huart); // 清除帧错误标志
}
if (LL_USART_IsActiveFlag_NE(uart->huart) && LL_USART_IsActiveFlag_NE(uart->huart))
{
// err = UART_NOISE_ERROR;
LL_USART_ClearFlag_NE(uart->huart); // 清除噪声错误标志
}
if (LL_USART_IsActiveFlag_ORE(uart->huart) && LL_USART_IsActiveFlag_ORE(uart->huart))
{
err = UART_OVERRUN_ERROR;
LL_USART_ClearFlag_ORE(uart->huart); // 清除溢出错误标志
}
if (err != UART_NO_ERROR && uart->rx_error != NULL)
{
uint16_t index = uart->rx_index - 1;
uart->rx_error[uart->rx_error_count].index = index;
uart->rx_error[uart->rx_error_count].err = err;
uart->rx_error_count++;
}
}
}
/**
* @brief DMA接收中断的回调函数
* @param {uart_t} *uart -
* @return {*}
* @note:
*/
void uart_dma_reception_callback(uart_t *uart)
{
// 检查输入参数是否为空
DBG_ASSERT(uart != NULL __DBG_LINE);
uart->tx_dma_ok = TRUE;
// 禁用串口DMA的发送通道
LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel);
// 清除发送中断标志位
DMA_CLEAR_FLAG_TC_CHANNEL(uart->dma, uart->dma_tx_channel);
// 使能发送中断,用于关闭发送使能引脚
LL_USART_EnableIT_TC(uart->huart); // 使能发送中断,用于关闭发送使能引脚
// 清除传输错误标志
DMA_CLEAR_FLAG_TE_CHANNEL(uart->dma, uart->dma_tx_channel);
}

218
User/system/bsp/uarts.h Normal file
View File

@ -0,0 +1,218 @@
/**
* @file uarts.h
* @brief Header file for UARTs module.
*
* This file contains the definitions and function prototypes for UARTs module.
* The UARTs module provides functions for creating and managing UART instances,
* enabling reception, sending data, and handling interrupts.
*/
#ifndef __UARTS_H__
#define __UARTS_H__
#include "lib.h"
#include "main.h"
// 串口中断用于接收超时的参数
#define RX_TIMEOUT_TICK (1U) /* 10ms的tick */
#define RX_TIMEOUT_MSEC (20U / RX_TIMEOUT_TICK) /* 20毫秒需要的tick可以根据需求添加其他时间更短的宏 */
/**
* @brief Enumeration for UART status.
*/
typedef enum
{
UART_OK = 0x00u, /**< The action was successful. */
UART_ERROR = 0xFFu /**< Generic error. */
} uart_status_e;
typedef enum
{
// 无错误
UART_NO_ERROR = BIT0,
// 奇偶校验错误中断
UART_PARITY_ERROR = BIT1,
// 帧错误中断
UART_FRAME_ERROR = BIT2,
// 噪声错误中断
UART_NOISE_ERROR = BIT3,
// 溢出错误中断
UART_OVERRUN_ERROR = BIT4,
} uarts_interupt_error_e; ///< UART中断错误枚举
typedef struct
{
uarts_interupt_error_e err; ///< 错误标志
uint16_t index; ///< 接收到的第几个字节
} uarts_interupt_error_t;
/**
* @brief Callback function type for UART receive interrupt.
*
* This function type is used to define the callback function for UART receive interrupt.
* The callback function is called when data is received on the UART.
*
* @param uart_index The index of the UART.
* @param uart_error The error code.
* @param data The received data.
* @param len The length of the received data.
*/
typedef void (*rx_interupt_cb_t)(uint8_t uart_index, uint8_t *data, uint16_t len);
/**
* @brief Callback function type for UART transmit complete.
*
* This function type is used to define the callback function for UART transmit complete.
* The callback function is called when the UART transmission is complete.
*/
typedef void (*tx_complete_cb_t)(void);
/**
* @brief Structure representing a UART instance.
*/
typedef struct
{
uint8_t uart_index; /**< The index of the UART. */
USART_TypeDef *huart; /**< The UART peripheral. */
DMA_TypeDef *dma; /**< The DMA peripheral. */
uint32_t dma_rx_channel; /**< The DMA receive channel. */
uint32_t dma_tx_channel; /**< The DMA transmit channel. */
//*******************RX*************************/
BOOL rx_cd_en; /**< Flag indicating if carrier detect is enabled. */
BOOL rx_dma_en; /**< Flag indicating if DMA reception is enabled. */
BOOL rx_err_en; /**< Flag indicating if error interrupt is enabled. */
__IO BOOL rx_interupt_timeout; /**< Flag indicating if receive interrupt timeout. */
__IO uint8_t rx_interupt_cnt; /**< The receive interrupt count. */
uint8_t *rxbuf; /**< The receive buffer. */
uint16_t rxsize; /**< The size of the receive buffer. */
uarts_interupt_error_t *rx_error; /**< The receive error. */
uint16_t rx_error_count; /**< The receive error count. */
__IO uint16_t rx_index; /**< The receive data index. */
//*******************TX*************************/
BOOL tx_dma_en; /**< Flag indicating if DMA transmission is enabled. */
uint8_t *txbuf; /**< The transmit buffer. */
uint16_t txsize; /**< The size of the transmit buffer. */
uint16_t tx_index; /**< The transmit data index. */
__IO BOOL tx_dma_ok; /**< Flag indicating if DMA transmission is complete. */
rx_interupt_cb_t rx_interupt_cb; /**< The receive interrupt callback function. */
tx_complete_cb_t tx_complete_cb; /**< The transmit complete callback function. */
} uart_t;
/**
* @brief Creates a UART instance.
*
* This function creates a UART instance with the specified parameters.
*
* @param huart The UART peripheral.
* @param rx_dma_en Flag indicating if DMA reception is enabled.
* @param rxsize The size of the receive buffer.
* @param rx_cb The receive interrupt callback function.
* @param tx_dma_en Flag indicating if DMA transmission is enabled.
* @param txsize The size of the transmit buffer.
* @param tx_complete_cb The transmit complete callback function.
* @return The created UART instance.
*/
extern uart_t *uart_create(USART_TypeDef *huart, BOOL rx_dma_en, uint16_t rxsize, rx_interupt_cb_t rx_cb,
BOOL tx_dma_en, uint16_t txsize, tx_complete_cb_t tx_complete_cb);
/**
* @brief Frees the resources of a UART instance.
*
* This function frees the resources allocated for a UART instance.
*
* @param uart The UART instance to free.
*/
extern void uart_free(uart_t *uart);
/**
* @brief Initializes a UART instance.
*
* This function initializes the specified UART instance with the specified parameters.
*
* @param uart The UART instance.
* @param baudrate The baudrate.
*/
extern void uart_set_baudrate(USART_TypeDef *uart, uint32_t baudrate);
/**
* @brief Enables UART reception.
*
* This function enables reception on the specified UART instance.
*
* @param uart The UART instance.
*/
extern void uart_recv_en(uart_t *uart, BOOL rx_err_en);
/**
* @brief Sends data over UART.
*
* This function sends the specified data over the specified UART instance.
*
* @param uart The UART instance.
* @param data The data to send.
* @param len The length of the data.
*/
extern void uart_send_data(uart_t *uart, uint8_t *data, uint16_t len);
/**
* @brief UART receive carrier detect callback.
*
* This function is the callback for UART receive carrier detect.
*
* @param uart The UART instance.
*/
extern void uart_rx_cd_callback(uart_t *uart);
/**
* @brief UART receive timeout timer.
*
* This function is the timer callback for UART receive timeout.
*
* @param uart The UART instance.
*/
extern void uart_rx_timeout_timer(uart_t *uart);
/**
* @brief UART receive interrupt callback.
*
* This function is the interrupt callback for UART receive interrupt.
*
* @param uart The UART instance.
*/
extern void uart_reception_callback(uart_t *uart);
/**
* @brief Get the UART error count.
*
* This function returns the number of errors that have occurred on the specified UART instance.
*
* @param uart The UART instance.
* @return The number of errors.
*/
extern uint16_t uart_get_error_count(uart_t *uart);
/**
* @brief Get UART interrupt error.
*
* This function gets the UART interrupt error.
*
* @param uart The UART instance.
* @param count The error count.
* @return The error.
*/
extern uarts_interupt_error_t *uart_get_error(uart_t *uart);
extern void uart_data_storage_reset(uart_t *uart);
/**
* @brief DMA receive interrupt callback.
*
* This function is the interrupt callback for DMA receive interrupt.
*
* @param uart The UART instance.
*/
extern void uart_dma_reception_callback(uart_t *uart);
#endif ///< __UARTS_H__

248
User/system/btn.c Normal file
View File

@ -0,0 +1,248 @@
/*
* @Author:
* @Date: 2023-07-04 08:25:56
* @LastEditors: xxx
* @LastEditTime: 2023-08-25 11:13:52
* @Description:
* email:
* Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "btn.h"
#define EVENT_CB(ev) \
if (handle->cb[ev]) \
handle->cb[ev]((Button *)handle)
// button handle list head.
static struct Button *head_handle = NULL;
/**
* @brief
* @param handle:
* @param pin_level: HAL GPIOLevel
* @param active_level: GPIOLevel
* @param button_id: ID
* @retval
*/
void button_init(struct Button *handle, uint8_t (*pin_level)(uint8_t), active_level_e active_level, uint8_t button_id, uint8_t button_id_reverse)
{
#ifdef STM32
osel_memset((uint8_t *)handle, 0, sizeof(struct Button));
#else
memset(handle, 0, sizeof(struct Button));
#endif
handle->event = (uint8_t)NONE_PRESS;
handle->hal_button_Level = pin_level;
handle->button_level = handle->hal_button_Level(button_id);
handle->active_level = (uint8_t)active_level;
handle->button_id = button_id;
handle->button_id_reverse = button_id_reverse;
}
/**
* @brief
* @param handle:
* @param event:
* @param cb:
* @retval
*/
void button_attach(struct Button *handle, PressEvent event, BtnCallback cb)
{
handle->cb[event] = cb;
}
/**
* @brief
* @param handle:
* @retval
*/
PressEvent get_button_event(struct Button *handle)
{
return (PressEvent)(handle->event);
}
/**
* @brief
* @param handle:
* @retval
*/
void button_handler(struct Button *handle)
{
uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id);
// ticks counter working..
if ((handle->state) > 0)
handle->ticks++;
/*------------button debounce handle---------------*/
if (read_gpio_level != handle->button_level)
{ // not equal to prev one
// continue read 3 times same new level change
if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS)
{
handle->button_level = read_gpio_level;
handle->debounce_cnt = 0;
}
}
else
{ // leved not change ,counter reset.
handle->debounce_cnt = 0;
}
/*-----------------State machine-------------------*/
switch (handle->state)
{
case 0:
if (handle->button_level == handle->active_level)
{ // start press down
handle->event = (uint8_t)PRESS_DOWN;
EVENT_CB(PRESS_DOWN);
handle->ticks = 0;
handle->repeat = 1;
handle->state = 1;
}
else
{
handle->event = (uint8_t)NONE_PRESS;
}
break;
case 1:
if (handle->button_level != handle->active_level)
{ // released press up
handle->event = (uint8_t)PRESS_UP;
EVENT_CB(PRESS_UP);
handle->ticks = 0;
handle->state = 2;
}
else if (handle->ticks > LONG_TICKS)
{
handle->event = (uint8_t)LONG_PRESS_START;
EVENT_CB(LONG_PRESS_START);
handle->state = 5;
}
break;
case 2:
if (handle->button_level == handle->active_level)
{ // press down again
handle->event = (uint8_t)PRESS_DOWN;
EVENT_CB(PRESS_DOWN);
handle->repeat++;
EVENT_CB(PRESS_REPEAT); // repeat hit
handle->ticks = 0;
handle->state = 3;
}
else if (handle->ticks > SHORT_TICKS)
{ // released timeout
if (handle->repeat == 1)
{
handle->event = (uint8_t)SINGLE_CLICK;
EVENT_CB(SINGLE_CLICK);
}
else if (handle->repeat == 2)
{
handle->event = (uint8_t)DOUBLE_CLICK;
EVENT_CB(DOUBLE_CLICK); // repeat hit
}
handle->state = 0;
}
break;
case 3:
if (handle->button_level != handle->active_level)
{ // released press up
handle->event = (uint8_t)PRESS_UP;
EVENT_CB(PRESS_UP);
if (handle->ticks < SHORT_TICKS)
{
handle->ticks = 0;
handle->state = 2; // repeat press
}
else
{
handle->state = 0;
}
}
else if (handle->ticks > SHORT_TICKS)
{ // long press up
handle->state = 0;
}
break;
case 5:
if (handle->button_level == handle->active_level)
{
// continue hold trigger
handle->event = (uint8_t)LONG_PRESS_HOLD;
EVENT_CB(LONG_PRESS_HOLD);
}
else
{ // releasd
handle->event = (uint8_t)PRESS_UP;
EVENT_CB(PRESS_UP);
handle->state = 0; // reset
}
break;
default:
handle->state = 0; // reset
break;
}
}
/**
* @brief
* @param handle:
* @retval 0: -1:
*/
int button_start(struct Button *handle)
{
struct Button *target = head_handle;
while (target)
{
if (target == handle)
return -1; // already exist.
target = target->next;
}
handle->next = head_handle;
head_handle = handle;
return 0;
}
/**
* @brief
* @param handle:
* @retval None
*/
void button_stop(struct Button *handle)
{
struct Button **curr;
for (curr = &head_handle; *curr;)
{
struct Button *entry = *curr;
if (entry == handle)
{
*curr = entry->next;
// free(entry);
return;
}
else
curr = &entry->next;
}
}
/**
* @brief 5ms
* @param None.
* @retval None
*/
void button_ticks()
{
struct Button *target;
for (target = head_handle; target; target = target->next)
{
button_handler(target);
}
}

164
User/system/btn.h Normal file
View File

@ -0,0 +1,164 @@
/***
* @Author:
* @Date: 2023-07-04 08:26:12
* @LastEditors: xxx
* @LastEditTime: 2023-08-09 22:49:14
* @Description: btn是一个小巧简单易用的事件驱动型按键驱动模块
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
/**
使
1.
struct Button button1;
2.GPIO电平读取接口read_button_pin()
button_init(&button1, read_button_pin, 0, 0);
3.
button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler);
button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler);
...
4.
button_start(&button1);
5.5ms间隔的定时器循环调用后台处理函数
while(1) {
...
if(timer_ticks == 5) {
timer_ticks = 0;
button_ticks();
}
}
*/
#ifndef _BTN_H_
#define _BTN_H_
#ifdef STM32
#include "lib.h"
#else
#include "stdint.h"
#include "string.h"
#endif
// 根据您的需求修改常量。
#define TICKS_INTERVAL 10 // 按钮扫描间隔单位ms
#define DEBOUNCE_TICKS 20 / TICKS_INTERVAL // 按键去抖动时间单位ms
#define SHORT_TICKS (100 / TICKS_INTERVAL) // 短按时间阈值单位ms
#define LONG_TICKS (500 / TICKS_INTERVAL) // 长按时间阈值单位ms
typedef void (*BtnCallback)(void *);
typedef enum
{
ACTIVE_LEVEL_LOW = 0, // 低电平有效
ACTIVE_LEVEL_HIGH, // 高电平有效
} active_level_e;
/***
* @brief
PRESS_DOWN
PRESS_UP
PRESS_REPEAT repeat计数连击次数
SINGLE_CLICK
DOUBLE_CLICK
LONG_PRESS_START
LONG_PRESS_HOLD
*/
typedef enum
{
PRESS_DOWN = 0, // 按下
PRESS_UP, // 弹起
PRESS_REPEAT, // 重复按下
SINGLE_CLICK, // 单击
DOUBLE_CLICK, // 双击
LONG_PRESS_START, // 长按开始
LONG_PRESS_HOLD, // 长按保持
number_of_event, // 事件数量
NONE_PRESS // 无按键
} PressEvent;
/***
* @brief
*/
typedef struct Button
{
uint16_t ticks; // 计时器
uint8_t repeat : 4; // 重复计数
uint8_t event : 4; // 事件类型
uint8_t state : 3; // 状态
uint8_t debounce_cnt : 3; // 去抖计数
uint8_t active_level : 1; // 激活电平
uint8_t button_level : 1; // 按钮电平
uint8_t button_id; // 按钮ID
uint8_t button_id_reverse; // 按钮ID反转
uint8_t (*hal_button_Level)(uint8_t button_id_); // 获取按钮引脚电平函数
BtnCallback cb[number_of_event]; // 回调函数数组
struct Button *next; // 下一个按钮句柄
} Button;
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief
* @param handle:
* @param pin_level:
* @param active_level:
* @param button_id: ID
* @retval 0:
* @retval -1:
*/
void button_init(struct Button *handle, uint8_t (*pin_level)(uint8_t), active_level_e active_level, uint8_t button_id, uint8_t button_id_reverse);
/**
* @brief
* @param handle:
* @param event:
* @param cb:
* @retval 0:
* @retval -1:
*/
void button_attach(struct Button *handle, PressEvent event, BtnCallback cb);
/**
* @brief
* @param handle:
* @retval
*/
PressEvent get_button_event(struct Button *handle);
/**
* @brief
* @param handle:
* @retval 0:
* @retval -1:
*/
int button_start(struct Button *handle);
/**
* @brief
* @param handle:
* @retval None
*/
void button_stop(struct Button *handle);
/**
* @brief 5ms
* @param None
* @retval None
*/
void button_ticks(void);
#ifdef __cplusplus
}
#endif
#endif

188
User/system/delay.c Normal file
View File

@ -0,0 +1,188 @@
/*
* @Author:
* @Date: 2023-04-11 18:31:07
* @LastEditors: xxx
* @LastEditTime: 2023-08-25 11:27:12
* @Description:
* email:
* Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "delay.h"
#if USE_OS == 1
#include "os.h"
// 确保包含了 vPortSetupTimerInterrupt 函数的声明
extern void vPortSetupTimerInterrupt(void);
#endif
// static uint16_t g_fac_ms = 0; // ms延时倍乘数,在os下,代表每个节拍的ms数
static uint32_t g_fac_us = 0; /* us延时倍乘数 */
/**
* @brief
* @param sysclk: , CPU频率(rcc_c_ck)
* @retval
*/
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS /* 如果需要支持OS */
uint32_t reload;
#endif
g_fac_us = sysclk; /* 不论是否使用OS,g_fac_us都需要使用 */
// Remove the redundant assignment statement
#if SYS_SUPPORT_OS /* 如果需要支持OS. */
reload = sysclk; /* 每秒钟的计数次数 单位为M */
reload *= 1000000 / configTICK_RATE_HZ; /* 根据delay_ostickspersec设定溢出时间,reload为24位
* ,:16777216,168M下,0.099s左右
*/
g_fac_ms = 1000 / configTICK_RATE_HZ; // 代表OS可以延时的最少单位
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; /* 开启SYSTICK中断 */
SysTick->LOAD = reload; /* 每1/delay_ostickspersec秒中断一次 */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 开启SYSTICK */
#endif
}
#if USE_OS == 1
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, reload, tcnt = 0;
if ((0x0001 & (SysTick->CTRL)) == 0)
{
// 定时器未工作,初始化定时器
vPortSetupTimerInterrupt();
}
reload = SysTick->LOAD; // 获取重装载寄存器值
ticks = nus * g_fac_us; // 计算延时所需的SysTick计数
vTaskSuspendAll(); // 阻止OS调度防止打断us延时
told = SysTick->VAL; // 获取当前数值寄存器值(开始时数值)
while (1)
{
tnow = SysTick->VAL; // 获取当前数值寄存器值
if (tnow != told)
{ // 当前值不等于开始值说明已在计数
if (tnow < told)
{
tcnt += told - tnow; // 计数值 = 开始值 - 当前值
}
else
{
tcnt += reload - tnow + told; // 计数值 = 重装载值 - 当前值 + 开始值
}
told = tnow; // 更新开始值
if (tcnt >= ticks)
{
break; // 时间超过/等于要延迟的时间,则退出
}
}
}
xTaskResumeAll(); // 恢复OS调度
}
#else
/**
* @brief nus
* @param nus: us数.
* @note : nus的值,34952us(2^24 / g_fac_us @g_fac_us = 168)
* @retval
*/
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD; /* LOAD的值 */
ticks = nus * g_fac_us; /* 需要的节拍数 */
told = SysTick->VAL; /* 刚进入时的计数器值 */
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks)
{
// __NOP();
break; /* 时间超过/等于要延迟的时间,则退出 */
}
}
}
}
#endif
/**
* @brief
*
* 使
*
* @param timer_us
* @param us
*/
void delay_hardware_us(TIM_TypeDef *timer_us, uint32_t us)
{
}
/**
* @brief
*
* 使
*
* @param timer_us
* @param us
*/
void delay_hardware_ms(TIM_TypeDef *timer_us, uint32_t ms)
{
while (ms--)
{
delay_hardware_us(timer_us, 1000); // 每毫秒延时1000微秒
}
}
/**
* @brief nms
* @param nms: ms数 (0< nms <= 65535)
* @retval
*/
void delay_ms(uint16_t nms)
{
uint32_t repeat = nms / 30; /* 这里用30,是考虑到可能有超频应用 */
uint32_t remain = nms % 30;
while (repeat)
{
delay_us(30 * 1000); /* 利用delay_us 实现 1000ms 延时 */
repeat--;
}
if (remain)
{
delay_us(remain * 1000); /* 利用delay_us, 把尾数延时(remain ms)给做了 */
}
}
/**
* @brief
* @param {uint32_t} ticks
* @return {*}
* @note: 使HAL_Delay或rt_delay
*/
void delay_tick(uint32_t ticks)
{
while (ticks--)
{
__NOP();
}
}

26
User/system/delay.h Normal file
View File

@ -0,0 +1,26 @@
/***
* @Author:
* @Date: 2023-04-11 18:31:07
* @LastEditors: xxx
* @LastEditTime: 2023-04-11 18:31:20
* @Description:
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __DELAY_H
#define __DELAY_H
#include "sys.h"
#include "tims.h"
#define USE_OS 0
void delay_init(uint16_t sysclk); /* 初始化延迟函数 */
void delay_ms(uint16_t nms); /* 延时nms */
void delay_us(uint32_t nus); /* 延时nus */
void delay_hardware_us(TIM_TypeDef *timer_us, uint32_t us); /* 硬件延时us */
void delay_hardware_ms(TIM_TypeDef *timer_us, uint32_t ms); /* 硬件延时ms */
void delay_tick(uint32_t ticks); /* 延时ticks */
#endif

View File

@ -0,0 +1,154 @@
#include "dac161p997.h"
#include "delay.h"
static dac161p997_t _handle;
static void _delay_us(uint32_t us)
{
delay_hardware_us(_handle.timer_us, us);
}
static void dac161p997_output_0()
{
_handle.io->set(*_handle.io);
_delay_us(DUTY_CYCLE_25);
_handle.io->reset(*_handle.io);
_delay_us(DUTY_CYCLE_75);
}
static void dac161p997_output_1()
{
_handle.io->set(*_handle.io);
_delay_us(DUTY_CYCLE_75);
_handle.io->reset(*_handle.io);
_delay_us(DUTY_CYCLE_25);
}
static void dac161p997_output_d()
{
_handle.io->set(*_handle.io);
_delay_us(DUTY_CYCLE_50);
_handle.io->reset(*_handle.io);
_delay_us(DUTY_CYCLE_50);
}
/**
* @brief DAC161符号
*
* DAC161输出函数
*
* @param sym ZERO_SYMONE_SYM或其他值
*/
static void dac161p997_output_symbol(uint8_t sym)
{
switch (sym)
{
case ZERO_SYM:
dac161p997_output_0();
break;
case ONE_SYM:
dac161p997_output_1();
break;
default:
dac161p997_output_d();
break;
}
}
/**
* @brief DAC161写入寄存器
*
* DAC161写入一个16位的数据8
*
* @param data 16
* @param tag 8tag = 0DAC寄存器tag = 1
*/
void dac161p997_swif_write_reg(uint16_t data, uint8_t tag)
{
uint8_t plow, phigh;
uint16_t i, tmp;
/* compute parity low */
tmp = data & 0x00ff; // get least significant byte
for (plow = 0; tmp != 0; tmp >>= 1)
{
if (tmp & 0x1) // test if lsb is 1
plow++; // count number of bits equal to 1
}
if (plow & 0x1) // check if number of 1s is odd
plow = 0; // set even parity
else
plow = 1; // else set odd parity
/* compute parity high */
tmp = (data & 0xff00) >> 8; // get most significant byte
for (phigh = 0; tmp != 0; tmp >>= 1)
{
if (tmp & 0x1) // test if lsb is 1
phigh++; // count number of bits equal to 1
}
if (phigh & 0x1) // check if number of 1s is odd
phigh = 0; // set even parity
else
phigh = 1; // set odd parity
phigh = phigh ^ tag; // parity high is for high slice = tag + 1 byte
dac161p997_output_symbol(IDLE_SYM); // Frame start: send an idle symbol first
dac161p997_output_symbol(tag);
tmp = data;
for (i = 0; i < 16; i++) // send 16 data bits msb to lsb
{
if (tmp & 0x8000)
{
dac161p997_output_symbol(ONE_SYM); // send 1
}
else
{
dac161p997_output_symbol(ZERO_SYM); // send 0
}
tmp = tmp << 1; // move next data bit to msb
}
dac161p997_output_symbol(phigh); // send parity high bit
dac161p997_output_symbol(plow); // send parity low bit
dac161p997_output_symbol(IDLE_SYM); // send idle
}
/**
* @brief DAC161输出电流
*
* DAC161写入指定的电流值来设置其输出电流,100ms
*
* @param current
*/
void dac161p997_output_current(float32 current)
{
uint16_t adc = (uint16_t)(current * DAC161P997_CURRENT_SLOPE);
dac161p997_swif_write_reg(adc, DACCODE_WRITE);
}
/**
* @brief DAC161设备
*
* DAC161设备便
*
*
* 1. DAC161的配置寄存器
* 2. DAC161的错误下限寄存器
* 3. DAC161的配置寄存器
*/
void dac161p997_init(TIM_TypeDef *timer_us)
{
DBG_ASSERT(timer_us != NULL __DBG_LINE);
_handle.timer_us = timer_us;
ENABLE_TIM_COUNT(_handle.timer_us);
_handle.io = gpio_create(DAC161P997_IO_PORT, DAC161P997_IO_PIN);
dac161p997_swif_write_reg(DAC161P997_LCK_REG + DAC161P997_LCK_REG_UNLOCK, CONFIG_WRITE);
dac161p997_swif_write_reg(DAC161P997_CONFIG2_REG, CONFIG_WRITE);
dac161p997_swif_write_reg(DAC161P997_LCK_REG + DAC161P997_LCK_REG_LOCK, CONFIG_WRITE);
}

View File

@ -0,0 +1,83 @@
#ifndef __DAC161P997_H__
#define __DAC161P997_H__
#include "main.h"
#include "gpios.h"
#define DAC161P997_IO_PORT (DAC161P997_GPIO_Port)
#define DAC161P997_IO_PIN (DAC161P997_Pin)
#define DAC161P997_CURRENT_SLOPE 2730.625f // adc = (current / 24) * 0xffff
// Symbol Periods
#define DUTY_CYCLE_100 (1000U) // (CPU_CLK / BAUD_RATE)
#define DUTY_CYCLE_75 (DUTY_CYCLE_100 * 0.75f)
#define DUTY_CYCLE_50 (DUTY_CYCLE_100 * 0.50f)
#define DUTY_CYCLE_25 (DUTY_CYCLE_100 * 0.25f)
/************************************************************
* TI DAC161P997 REGISTER SET ADDRESSES
************************************************************/
#define DAC161P997_LCK_REG (0x0000)
#define DAC161P997_CONFIG1_REG (0x0100)
#define DAC161P997_CONFIG2_REG (0x0200)
#define DAC161P997_CONFIG3_REG (0x0300)
#define DAC161P997_ERR_LOW_REG (0x0400)
#define DAC161P997_ERR_HIGH_REG (0x0500)
// TI DAC161P997 Register Bits
#define DAC161P997_LCK_REG_LOCK (0x00AA) // any value other than 0x95
#define DAC161P997_LCK_REG_UNLOCK (0x0095)
#define DAC161P997_CONFIG1_REG_RST (0x0001)
#define DAC161P997_CONFIG1_REG_NOP (0 * 0x08u)
#define DAC161P997_CONFIG1_REG_SET_ERR (1 * 0x08u)
#define DAC161P997_CONFIG1_REG_CLEAR_ERR (2 * 0x08u)
#define DAC161P997_CONFIG1_REG_NOP3 (3 * 0x08u)
#define DAC161P997_CONFIG2_REG_LOOP (0x0001)
#define DAC161P997_CONFIG2_REG_CHANNEL (0x0002)
#define DAC161P997_CONFIG2_REG_PARITY (0x0004)
#define DAC161P997_CONFIG2_REG_FRAME (0x0008)
#define DAC161P997_CONFIG2_REG_ACK_EN (0x0010)
#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_16 (0x000F)
#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_8 (0x0007)
#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_1 (0x0000)
// Tags
#define DACCODE_WRITE (0x00)
#define CONFIG_WRITE (0x01)
// Valid Symbols
#define ZERO_SYM (0x00)
#define ONE_SYM (0x01)
#define IDLE_SYM (0x02)
#define STATIC_LOW_SYM (0x03)
// DAC Codes for different currents
#define DACCODE_0mA (0x0000)
#define DACCODE_4mA (0x2AAA)
#define DACCODE_8mA (0x5555)
#define DACCODE_12mA (0x7FFF)
#define DACCODE_16mA (0xAAAA)
#define DACCODE_20mA (0xD555)
#define DACCODE_24mA (0xFFFF)
typedef enum
{
DAC161P997_IDLE_SYM,
} dac161p997_e;
typedef struct
{
TIM_TypeDef *timer_us;
gpio_t *io;
uint8_t count;
} dac161p997_t;
void dac161p997_output_0(void);
void dac161p997_output_1(void);
void dac161p997_output_d(void);
void dac161p997_output_symbol(uint8_t sym);
void dac161p997_swif_write_reg(uint16_t data, uint8_t tag);
extern void dac161p997_output_current(float32 current);
extern void dac161p997_init(TIM_TypeDef *timer_us);
#endif

View File

@ -0,0 +1,219 @@
/**
* @file eeprom_fm24.c
* @author xxx
* @date 2023-08-29 07:58:27
* @brief FM24 EEPROM相关的读写操作
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "entity.h"
#include "board.h"
#include "eeprom_fm24.h"
#include "delay.h"
#define FM24_SPI SPI1
#define EEPROM_FM24_CS_PORT EE3_CS_GPIO_Port
#define EEPROM_FM24_CS_PIN EE3_CS_Pin
#define EEPROM_FM24_MOSI_PORT SPI_MOSI_GPIO_Port
#define EEPROM_FM24_MOSI_PIN SPI_MOSI_Pin
#define EEPROM_FM24_MISO_PORT SPI_MISO_GPIO_Port
#define EEPROM_FM24_MISO_PIN SPI_MISO_Pin
#define EEPROM_FM24_SCK_PORT SPI_CLK_GPIO_Port
#define EEPROM_FM24_SCK_PIN SPI_CLK_Pin
static fm24_t _eeprom_fm24;
void eeprom_fm24_init(void)
{
spi_gpio_group_t gpios;
spi_normal_config_t cfg;
osel_memset((uint8_t *)&cfg, 0, sizeof(spi_normal_config_t));
// 创建CS引脚
gpios.cs = gpio_create(EEPROM_FM24_CS_PORT, EEPROM_FM24_CS_PIN);
gpios.mosi = gpio_create(EEPROM_FM24_MOSI_PORT, EEPROM_FM24_MOSI_PIN);
gpios.sck = gpio_create(EEPROM_FM24_SCK_PORT, EEPROM_FM24_SCK_PIN);
gpios.miso = gpio_create(EEPROM_FM24_MISO_PORT, EEPROM_FM24_MISO_PIN);
gpios.rst = gpio_create(NULL, 0);
gpios.rdy = gpio_create(NULL, 0);
// 创建SPI对象
eeprom_fm24_get()->spi = spi_create(SPI_TYPE_NORMAL, gpios, 0);
DBG_ASSERT(eeprom_fm24_get() != NULL __DBG_LINE);
cfg.cmd_rdsr = FM24_CMD_RDSR;
cfg.cmd_wrsr = FM24_CMD_WRSR;
cfg.cmd_wren = FM24_CMD_WREN;
cfg.cmd_wrdi = FM24_CMD_WRDI;
cfg.cmd_write = FM24_CMD_WRITE;
cfg.cmd_read = FM24_CMD_READ;
cfg.dummy_byte = FM24_DUMMY_BYTE;
cfg.address_bytes = 2;
cfg.page_size = FM24_PAGE_SIZE;
cfg.total_size = FM24_SIZE;
cfg.continuous_write = FALSE;
osel_memcpy((uint8_t *)&eeprom_fm24_get()->spi->cfg, (uint8_t *)&cfg, sizeof(spi_normal_config_t));
// 使能SPI
eeprom_fm24_get()->spi->interface.hardware_enable(eeprom_fm24_get()->spi, FM24_SPI);
// 这里需要复位下SPI否则读出的数据不对
eeprom_fm24_get()->spi->interface.u.normal.spi_reset(eeprom_fm24_get()->spi);
eeprom_fm24_write_protection_close();
// eeprom_fm24_test();
}
fm24_t *eeprom_fm24_get(void)
{
return &_eeprom_fm24;
}
void eeprom_fm24_dinit(void)
{
LL_SPI_Disable(FM24_SPI);
GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.mosi->port, eeprom_fm24_get()->spi->gpios.mosi->pin);
GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.miso->port, eeprom_fm24_get()->spi->gpios.miso->pin);
GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.sck->port, eeprom_fm24_get()->spi->gpios.sck->pin);
GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.cs->port, eeprom_fm24_get()->spi->gpios.cs->pin);
}
void eeprom_fm24_enable(void)
{
uint16_t count = 100;
LL_SPI_Enable(FM24_SPI);
// 判断SPI是否使能成功
while (LL_SPI_IsEnabled(FM24_SPI) != 1)
{
if (count-- == 0)
{
return;
}
else
{
__NOP();
}
}
}
void eeprom_fm24_disable(void)
{
uint16_t count = 100;
LL_SPI_Disable(FM24_SPI);
// 判断SPI是否关闭成功
while (LL_SPI_IsEnabled(FM24_SPI) != 0)
{
if (count-- == 0)
{
return;
}
else
{
__NOP();
}
}
}
/**
* @brief EEPROM FM24的写保护
*
* EEPROM FM24的写保护功能
*
* EEPROM FM24的SPI接口已正确初始化
*/
void eeprom_fm24_write_protection_close(void)
{
DBG_ASSERT(eeprom_fm24_get()->spi != NULL __DBG_LINE);
eeprom_fm24_enable();
eeprom_fm24_get()->spi->interface.u.normal.spi_write_reg(eeprom_fm24_get()->spi, eeprom_fm24_get()->spi->cfg.cmd_wrsr, 0);
eeprom_fm24_disable();
}
/**
* @brief EEPROM FM24的写保护状态
*
* EEPROM FM24的写保护状态
*
* @return BOOL TRUE表示没有写保护FALSE表示有写保护
*/
BOOL eeprom_fm24_write_protection_state(void)
{
DBG_ASSERT(eeprom_fm24_get()->spi != NULL __DBG_LINE);
eeprom_fm24_enable();
eeprom_fm24_get()->write_protection.data = eeprom_fm24_get()->spi->interface.u.normal.spi_read_reg(eeprom_fm24_get()->spi, eeprom_fm24_get()->spi->cfg.cmd_rdsr);
eeprom_fm24_disable();
if (eeprom_fm24_get()->write_protection.bits.bp0 == 1 ||
eeprom_fm24_get()->write_protection.bits.bp1 == 1 ||
eeprom_fm24_get()->write_protection.bits.wpen == 1)
{
return FALSE;
}
else
{
return TRUE;
}
}
BOOL eeprom_fm24_write(uint32_t write_addr, uint8_t *data, uint16_t length)
{
BOOL ret = FALSE;
if (length == 0)
{
return ret;
}
// 开启和关闭SPI对读写实时性有影响
eeprom_fm24_enable();
ret = eeprom_fm24_get()->spi->interface.u.normal.spi_write(eeprom_fm24_get()->spi, write_addr, data, length);
eeprom_fm24_disable();
return ret;
}
BOOL eeprom_fm24_read(uint32_t read_addr, uint8_t *data, uint16_t length)
{
BOOL ret = FALSE;
if (length == 0)
{
return ret;
}
// 开启和关闭SPI对读写实时性有影响
eeprom_fm24_enable();
ret = eeprom_fm24_get()->spi->interface.u.normal.spi_read(eeprom_fm24_get()->spi, read_addr, data, length);
eeprom_fm24_disable();
return ret;
}
BOOL eeprom_fm24_test(void)
{
const uint8_t buf_size = 5;
uint16_t test_address = FM24_TEST_PAGE * FM24_PAGE_SIZE;
uint8_t buf[buf_size];
uint8_t rbuf[buf_size];
osel_memset(buf, 0, buf_size);
osel_memset(rbuf, 0, buf_size);
buf[0] = 0xD5;
buf[1] = 0xC8;
buf[2] = 0x00;
buf[3] = 0x01;
buf[4] = 0x02;
buf[buf_size - 1] = 0xfe;
eeprom_fm24_write(test_address, buf, buf_size);
__NOP();
eeprom_fm24_read(test_address, rbuf, buf_size);
if (osel_memcmp(buf, rbuf, buf_size) == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* @brief FM24 EEPROM的状态
*
* FM24 EEPROM的当前状态TRUEEEPROM正常工作
*
* @return BOOL TRUE
*/
BOOL eeprom_fm24_status_get(void)
{
return TRUE;
}

View File

@ -0,0 +1,158 @@
/**
* @file eeprom_fm24.h
* @author xxx
* @date 2023-08-30 14:05:55
* @brief FM24系列EEPROM驱动 https://zhuanlan.zhihu.com/p/598934638
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __EEPROM_FM24_H__
#define __EEPROM_FM24_H__
#include "main.h"
#include "spis.h"
// High-endurance 100 trillion (1014) read/writes
//========在此设定芯片地址=============
#define W_ADD_COM 0xa0 // 写字节命令及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 0
#define R_ADD_COM 0xa1 // 读命令字节及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 1
//=======在此设定芯片型号, 1代表24C01; 16代表24C16; 512代表24C512
//=======在此设定芯片型号, 1代表24C01; 16代表24C16; 512代表24C512
#define e2prom 256 //
#if e2prom == 1
#define FM24_PAGE_SIZE 16
#define FM24_SIZE (128 * 8)
#elif e2prom == 2
#define FM24_PAGE_SIZE 16
#define FM24_SIZE (256 * 8)
#elif e2prom == 4
#define FM24_PAGE_SIZE 32
#define FM24_SIZE (512 * 8)
#elif e2prom == 8
#define FM24_PAGE_SIZE 64
#define FM24_SIZE (1024 * 8)
#elif e2prom == 16
#define FM24_PAGE_SIZE 128
#define FM24_SIZE (2048 * 8)
#elif e2prom == 32
#define FM24_PAGE_SIZE 128
#define FM24_SIZE (4096 * 8)
#elif e2prom == 64
#define FM24_PAGE_SIZE 256
#define FM24_SIZE (8192 * 8)
#elif e2prom == 128
#define FM24_PAGE_SIZE 256
#define FM24_SIZE (16384)
#elif e2prom == 256
#define FM24_PAGE_SIZE 256
#define FM24_SIZE (32768) // 32K 128页
#elif e2prom == 512
#define FM24_PAGE_SIZE 512
#define FM24_SIZE (65536)
#endif
#define FM24_CMD_RDSR 0x05 /*!< Read Status Register instruction */
#define FM24_CMD_WRSR 0x01 /*!< Write Status Register instruction */
#define FM24_CMD_WREN 0x06 /*!< Write enable instruction */
#define FM24_CMD_WRDI 0x04 /*!< Write disable instruction */
#define FM24_CMD_READ 0x03 /*!< Read from Memory instruction */
#define FM24_CMD_WRITE 0x02 /*!< Write to Memory instruction */
#define FM24_DUMMY_BYTE 0x00 ///< 无用数据
#define FM24_TEST_PAGE 0 ///< 测试页地址
typedef union
{
uint8_t data;
struct
{
uint8_t reserve1 : 1;
uint8_t wel : 1; ///< Write enable latch
uint8_t bp0 : 1; ///< Block protect 0
uint8_t bp1 : 1; ///< Block protect 1
uint8_t reserve2 : 3;
uint8_t wpen : 1; ///< Write protect enable
} bits;
} fm24_write_protection_u;
typedef struct
{
fm24_write_protection_u write_protection;
spi_t *spi;
} fm24_t;
/**
* @brief Initializes the FM24 EEPROM module.
*/
extern void eeprom_fm24_init(void);
/**
* @brief Deinitializes the FM24 EEPROM module.
*/
extern void eeprom_fm24_dinit(void);
/**
* @brief Gets the fm24_t handle of the FM24 EEPROM module.
* @return The fm24_t handle of the FM24 EEPROM module.
*/
extern fm24_t *eeprom_fm24_get(void);
/**
* @brief Enables the FM24 EEPROM module.
*/
extern void eeprom_fm24_enable(void);
/**
* @brief Disables the FM24 EEPROM module.
*/
extern void eeprom_fm24_disable(void);
/**
* @brief Reads data from the FM24 EEPROM module.
* @param read_addr The starting address to read from.
* @param data Pointer to the buffer to store the read data.
* @param length The number of bytes to read.
* @return TRUE if the read operation is successful, FALSE otherwise.
*/
extern BOOL eeprom_fm24_read(uint32_t read_addr, uint8_t *data, uint16_t length);
/**
* @brief Writes data to the FM24 EEPROM module.
* @param write_addr The starting address to write to.
* @param data Pointer to the data to be written.
* @param length The number of bytes to write.
* @return TRUE if the write operation is successful, FALSE otherwise.
*/
extern BOOL eeprom_fm24_write(uint32_t write_addr, uint8_t *data, uint16_t length);
/**
* @brief Closes the write protection of the FM24 EEPROM module.
*/
extern void eeprom_fm24_write_protection_close(void);
/**
* @brief Gets the write protection state of the FM24 EEPROM module.
* @return TRUE if the FM24 EEPROM module is not write-protected, FALSE otherwise.
*/
extern BOOL eeprom_fm24_write_protection_state(void);
/**
* @brief Performs a test on the FM24 EEPROM module.
*/
extern BOOL eeprom_fm24_test(void);
/**
* @brief Gets the status of the FM24 EEPROM module.
* @return TRUE if the FM24 EEPROM module is ready, FALSE otherwise.
*/
extern BOOL eeprom_fm24_status_get(void);
#endif // __EEPROM_FM24_H__

View File

@ -0,0 +1,171 @@
#include "eeprom_lc02b.h"
#include "delay.h"
#define W_ADD_COM 0xa8 // 写字节命令及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 0
#define R_ADD_COM 0xa9 // 读命令字节及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 1
#define PAGE_SIZE 8U
#define SIZE 256U
#define EEPROM_LC02B_SDA_PORT I2C1_SDA_GPIO_Port
#define EEPROM_LC02B_SDA_PIN I2C1_SDA_Pin
#define EEPROM_LC02B_SCL_PORT I2C1_SCL_GPIO_Port
#define EEPROM_LC02B_SCL_PIN I2C1_SCL_Pin
static i2c_t *_eeprom_24lc028bt_i2c;
static TIM_TypeDef *_timer_us;
static void _delay_us(uint32_t us)
{
if (_timer_us != NULL)
{
delay_hardware_us(_timer_us, us);
}
else
{
delay_us(us);
}
}
void eeprom_lc02b_test(void)
{
#define TEST_SIZE 15
uint16_t test_address = SIZE - TEST_SIZE;
uint8_t buf[TEST_SIZE];
for (uint8_t i = 0; i < TEST_SIZE; i++)
{
buf[i] = i + 1;
}
eeprom_lc02b_write(test_address, buf, TEST_SIZE);
_delay_us(10000);
osel_memset(buf, 0, ARRAY_LEN(buf));
eeprom_lc02b_read(test_address, buf, TEST_SIZE);
__NOP();
}
/**
* @brief EEPROM LC02B的状态
*
* EEPROM LC02B的当前状态
*
* @return EEPROM LC02B处于正常状态TRUEFALSE
* TRUE
*/
BOOL eeprom_lc02b_status_get(void)
{
return TRUE;
}
/**
* @brief EEPROM LC02B初始化
* @return {*}
* @note
*/
void eeprom_lc02b_init(TIM_TypeDef *timer_us)
{
if (timer_us != NULL)
{
_timer_us = timer_us;
ENABLE_TIM_COUNT(_timer_us);
}
i2c_gpio_group_t gpios;
gpios.scl = gpio_create(EEPROM_LC02B_SCL_PORT, EEPROM_LC02B_SCL_PIN);
gpios.sda = gpio_create(EEPROM_LC02B_SDA_PORT, EEPROM_LC02B_SDA_PIN);
_eeprom_24lc028bt_i2c = i2c_create(gpios, 10);
// eeprom_lc02b_test();
}
/**
* @brief EEPROM LC02B反初始化
* @return {*}
* @note
*/
void eeprom_lc02b_dinit(void)
{
GPIO_SET_ANALOG(EEPROM_LC02B_SDA_PORT, EEPROM_LC02B_SDA_PIN);
GPIO_SET_ANALOG(EEPROM_LC02B_SCL_PORT, EEPROM_LC02B_SCL_PIN);
}
/**
* @brief
* @param {uint32_t} write_addr
* @param {uint8_t} *data
* @param {uint16_t} length
* @return {*}
* @note
*/
void eeprom_lc02b_write(uint32_t write_addr, uint8_t *data, uint16_t length)
{
// 发送开始信号
_eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c);
// 发送写入地址命令
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, W_ADD_COM);
// 等待写入地址命令响应
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
// 发送要写入的地址
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, (uint8_t)write_addr);
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
// 循环写入数据
for (uint16_t i = 0; i < length; i++)
{
// 写入一个字节数据
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, *data++);
// 等待响应
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
write_addr++;
if (write_addr % PAGE_SIZE == 0)
{
_eeprom_24lc028bt_i2c->interface.stop(_eeprom_24lc028bt_i2c);
_delay_us(10000); // 延时10ms等待写入完成
_eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c);
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, W_ADD_COM);
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, (uint8_t)write_addr);
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
}
}
// 写入完成停止I2C总线
_eeprom_24lc028bt_i2c->interface.stop(_eeprom_24lc028bt_i2c);
}
/**
* @brief
* @param {uint32_t} read_addr
* @param {uint8_t} *data
* @param {uint16_t} length
* @return {*}
* @note
*/
void eeprom_lc02b_read(uint32_t read_addr, uint8_t *data, uint16_t length)
{
// 发送开始信号
_eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c);
// 发送写入地址命令
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, W_ADD_COM);
// 等待写入地址命令响应
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
// 发送要读取的地址
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, (uint8_t)read_addr);
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
// 发送开始信号
_eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c);
// 发送读取地址命令
_eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, R_ADD_COM);
// 等待读取地址命令响应
_eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c);
// 循环读取数据
for (uint16_t i = 0; i < length - 1; i++)
{
// 读取一个字节数据
*data++ = _eeprom_24lc028bt_i2c->interface.read_byte(_eeprom_24lc028bt_i2c, TRUE);
}
*data++ = _eeprom_24lc028bt_i2c->interface.read_byte(_eeprom_24lc028bt_i2c, FALSE);
// 停止I2C总线
_eeprom_24lc028bt_i2c->interface.stop(_eeprom_24lc028bt_i2c);
}

View File

@ -0,0 +1,56 @@
/**
* @file eeprom_lc02b.h
* @author xxx
* @date 2023-12-27 14:44:02
* @brief
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __EEPROM_LC02B_H
#define __EEPROM_LC02B_H
#include "main.h"
#include "entity.h"
#define PRRESSURE_CALIBRATION_ADDRESS 0x00 ///< 压力校准地址
/**
* @brief Initializes the LC02B EEPROM module.
*/
void eeprom_lc02b_init(TIM_TypeDef *timer_us);
/**
* @brief Deinitializes the LC02B EEPROM module.
*/
void eeprom_lc02b_dinit(void);
/**
* @brief Writes data to the LC02B EEPROM module.
*
* @param write_addr The starting address to write the data.
* @param data The pointer to the data to be written.
* @param length The length of the data to be written.
*/
void eeprom_lc02b_write(uint32_t write_addr, uint8_t *data, uint16_t length);
/**
* @brief Reads data from the LC02B EEPROM module.
*
* @param read_addr The starting address to read the data.
* @param data The pointer to store the read data.
* @param length The length of the data to be read.
*/
void eeprom_lc02b_read(uint32_t read_addr, uint8_t *data, uint16_t length);
/**
* @brief Performs a test on the LC02B EEPROM module.
*/
void eeprom_lc02b_test(void);
/**
* @brief Gets the LC02B EEPROM status.
*
* This function is used to get the current status of the LC02B EEPROM.
*
* @return TRUE if the LC02B EEPROM is in normal status, FALSE otherwise.
*/
BOOL eeprom_lc02b_status_get(void);
#endif ///< !__EEPROM_LC02B_H

View File

@ -0,0 +1,331 @@
/**
* @file eeprom_m95.c
* @author xxx
* @date 2023-08-30 08:58:43
* @brief M95 EEPROM相关的读写操作
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "eeprom_m95.h"
#include "spis.h"
#include "delay.h"
#include "entity.h"
#include "board.h"
#include "diagnosis.h"
#include "storage.h"
#define M95_SPI SPI1
#define EEPROM_M95_1_CS_PORT EE1_CS_GPIO_Port
#define EEPROM_M95_1_CS_PIN EE1_CS_Pin
#define EEPROM_M95_2_CS_PORT EE2_CS_GPIO_Port
#define EEPROM_M95_2_CS_PIN EE2_CS_Pin
// 下面宏定义为2个EEPROM_M95的引脚定义
#define EEPROM_M95_MOSI_PORT SPI_MOSI_GPIO_Port
#define EEPROM_M95_MOSI_PIN SPI_MOSI_Pin
#define EEPROM_M95_MISO_PORT SPI_MISO_GPIO_Port
#define EEPROM_M95_MISO_PIN SPI_MISO_Pin
#define EEPROM_M95_SCK_PORT SPI_CLK_GPIO_Port
#define EEPROM_M95_SCK_PIN SPI_CLK_Pin
m95_number_t eeprom_m95s[M95_MAX];
/**
* @brief EEPROM_M95eeprom_m95s
* @param {m95_number_e} num
* @return {*}
* @note
*/
void eeprom_m95_init(m95_number_e num)
{
DBG_ASSERT(num < M95_MAX __DBG_LINE);
spi_gpio_group_t gpios;
spi_t *eeprom_m95_spi;
spi_normal_config_t cfg;
osel_memset((uint8_t *)&cfg, 0, sizeof(spi_normal_config_t));
cfg.cmd_rdsr = M95_CMD_RDSR;
cfg.cmd_wrsr = M95_CMD_WRSR;
cfg.cmd_wren = M95_CMD_WREN;
cfg.cmd_wrdi = M95_CMD_WRDI;
cfg.cmd_write = M95_CMD_WRITE;
cfg.cmd_read = M95_CMD_READ;
cfg.dummy_byte = M95_DUMMY_BYTE;
cfg.continuous_write = FALSE;
// 128 byte
if (num == M95_1)
{
// 创建CS引脚
gpios.cs = gpio_create(EEPROM_M95_1_CS_PORT, EEPROM_M95_1_CS_PIN);
cfg.address_bytes = 3;
cfg.page_size = M95_PAGE_SIZE_256;
cfg.total_size = _M95M02_;
}
// 256 byte
else if (num == M95_2)
{
// 创建CS引脚
gpios.cs = gpio_create(EEPROM_M95_2_CS_PORT, EEPROM_M95_2_CS_PIN);
cfg.address_bytes = 3;
cfg.page_size = M95_PAGE_SIZE_256;
cfg.total_size = _M95M02_;
}
else
{
DBG_ASSERT(FALSE __DBG_LINE);
}
gpios.mosi = gpio_create(EEPROM_M95_MOSI_PORT, EEPROM_M95_MOSI_PIN);
gpios.sck = gpio_create(EEPROM_M95_SCK_PORT, EEPROM_M95_SCK_PIN);
gpios.miso = gpio_create(EEPROM_M95_MISO_PORT, EEPROM_M95_MISO_PIN);
gpios.rst = gpio_create(NULL, 0);
gpios.rdy = gpio_create(NULL, 0);
// 创建SPI对象
eeprom_m95_spi = spi_create(SPI_TYPE_NORMAL, gpios, 10);
DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE);
osel_memcpy((uint8_t *)&eeprom_m95_spi->cfg, (uint8_t *)&cfg, sizeof(spi_normal_config_t));
// 使能SPI
eeprom_m95_spi->interface.hardware_enable(eeprom_m95_spi, M95_SPI);
eeprom_m95s[num].num = num;
eeprom_m95s[num].spi = eeprom_m95_spi;
// 这里需要设置,否则读出的数据不对
eeprom_m95_spi->interface.u.normal.spi_reset(eeprom_m95_spi);
eeprom_m95_write_protection_close(num); // 关闭写保护
// eeprom_m95_test(num);
}
/**
* @brief EEPROM_M95
* @param {m95_number_e} num
* @return {*}
* @note
*/
void eeprom_m95_dinit(m95_number_e num)
{
LL_SPI_Disable(M95_SPI);
GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.mosi->port, eeprom_m95s[num].spi->gpios.mosi->pin);
GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.miso->port, eeprom_m95s[num].spi->gpios.miso->pin);
GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.sck->port, eeprom_m95s[num].spi->gpios.sck->pin);
GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.cs->port, eeprom_m95s[num].spi->gpios.cs->pin);
}
/**
* @brief M95 EEPROM使能
* @return {*}
* @note
*/
void eeprom_m95_enable(void)
{
uint16_t count = 100;
LL_SPI_Enable(M95_SPI);
// 判断SPI是否使能成功
while (LL_SPI_IsEnabled(M95_SPI) != 1)
{
if (count-- == 0)
{
return;
}
else
{
__NOP();
}
}
}
/**
* @brief M95 EEPROM失能
* @return {*}
* @note
*/
void eeprom_m95_disable(void)
{
uint16_t count = 100;
LL_SPI_Disable(M95_SPI);
// 判断SPI是否关闭成功
while (LL_SPI_IsEnabled(M95_SPI) != 0)
{
if (count-- == 0)
{
return;
}
else
{
__NOP();
}
}
}
/**
* @brief EEPROM M95写保护
*
* M95 EEPROM的写保护功能
*
* @param num M95 EEPROM的编号
*/
void eeprom_m95_write_protection_close(m95_number_e num)
{
spi_t *handle = eeprom_m95s[num].spi;
DBG_ASSERT(handle != NULL __DBG_LINE);
eeprom_m95_enable();
handle->interface.u.normal.spi_write_reg(handle, handle->cfg.cmd_wrsr, 0);
eeprom_m95_disable();
}
/**
* @brief M95 EEPROM写保护状态()
*
* M95编号获取其写保护状态
*
* @param num M95编号
* @return M95 EEPROM处于写保护状态FALSETRUE
*/
BOOL eeprom_m95_write_protection_state(m95_number_e num)
{
spi_t *handle = eeprom_m95s[num].spi;
DBG_ASSERT(handle != NULL __DBG_LINE);
eeprom_m95_enable();
eeprom_m95s[num].write_protection.data = handle->interface.u.normal.spi_read_reg(handle, handle->cfg.cmd_rdsr);
eeprom_m95_disable();
if (eeprom_m95s[num].write_protection.bits.bp0 == 1 ||
eeprom_m95s[num].write_protection.bits.bp1 == 1 ||
eeprom_m95s[num].write_protection.bits.srwd == 1)
{
return FALSE;
}
else
{
return TRUE;
}
}
/**
* @brief M95 EEPROM内存数据
* @param num EEPROM模块编号01
* @param read_addr
* @param data
* @param length
* @return {*}
*/
BOOL eeprom_m95_read(m95_number_e num, uint32_t read_addr, uint8_t *data, uint16_t length)
{
BOOL ret = FALSE;
if (length == 0)
{
return ret;
}
spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; // 获取EEPROM模块的SPI配置
DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE);
// 开启和关闭SPI对读写实时性有影响
eeprom_m95_enable();
ret = eeprom_m95_spi->interface.u.normal.spi_read(eeprom_m95_spi, read_addr, data, length);
eeprom_m95_disable();
return ret;
}
/**
* @brief M95 EEPROM内存写入数据
* @param num EEPROM模块编号01
* @param write_addr
* @param data
* @param length
* @return {*}
*/
BOOL eeprom_m95_write(m95_number_e num, uint32_t write_addr, uint8_t *data, uint16_t length)
{
BOOL ret = FALSE;
if (length == 0)
{
return ret;
}
spi_t *eeprom_m95_spi = eeprom_m95s[num].spi;
DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE);
// 开启和关闭SPI对读写实时性有影响
eeprom_m95_enable();
ret = eeprom_m95_spi->interface.u.normal.spi_write(eeprom_m95_spi, write_addr, data, length);
eeprom_m95_disable();
return ret;
}
BOOL eeprom_m95_1_read(uint32_t addr, uint8_t *buf, uint16_t size)
{
return eeprom_m95_read(M95_1, addr, buf, size);
}
BOOL eeprom_m95_1_write(uint32_t addr, uint8_t *buf, uint16_t size)
{
return eeprom_m95_write(M95_1, addr, (uint8_t *)buf, size);
}
BOOL eeprom_m95_2_read(uint32_t addr, uint8_t *buf, uint16_t size)
{
return eeprom_m95_read(M95_2, addr, buf, size);
}
BOOL eeprom_m95_2_write(uint32_t addr, uint8_t *buf, uint16_t size)
{
return eeprom_m95_write(M95_2, addr, (uint8_t *)buf, size);
}
/**
* @brief M95 EEPROM测试
* @param {m95_number_e} num
* @return {*}
* @note
*/
BOOL eeprom_m95_test(m95_number_e num)
{
const uint8_t buf_size = 5;
storage_t *st = storage_init(M95_TEST_PAGE * M95_PAGE_SIZE_256, M95_PAGE_SIZE_256);
DBG_ASSERT(st != NULL __DBG_LINE);
if (num == M95_1)
{
st->ops.read = eeprom_m95_1_read;
st->ops.write = eeprom_m95_1_write;
}
else
{
st->ops.read = eeprom_m95_2_read;
st->ops.write = eeprom_m95_2_write;
}
uint8_t buf[buf_size];
uint8_t rbuf[buf_size];
storage_add_node(st, 0, buf_size);
osel_memset(buf, 0, buf_size);
buf[0] = 0xD5;
buf[1] = 0xC8;
buf[2] = num;
buf[3] = 0xaa;
buf[4] = 0xbb;
storage_write(st, 0, buf);
__NOP();
storage_read(st, 0, rbuf);
storage_destroy(st);
if (osel_memcmp(buf, rbuf, buf_size) == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* @brief EEPROM M95状态
*
* EEPROM M95设备的工作状态
*
* @param num EEPROM M95设备编号
*
* @return EEPROM M95设备正常工作TRUEFALSE
*/
BOOL eeprom_m95_status_get(m95_number_e num)
{
return TRUE;
}

View File

@ -0,0 +1,166 @@
/**
* @file eeprom_m95.h
* @author xxx
* @date 2023-08-30 14:05:55
* @brief eeprom_m95.h
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __EEPROM_M95_H
#define __EEPROM_M95_H
#include "main.h"
#include "spis.h"
#define _M95010_ 128
#define _M95020_ 256
#define _M95040_ 512
#define _M95080_ 1024
#define _M95160_ 2048
#define _M95320_ 4096
#define _M95640_ 8192
#define _M95128_ 16384
#define _M95256_ 32768
#define _M95512_ 65536 ///< 65K
#define _M95M02_ 262144 ///< 256K
#define _M95_SIZE _M95512_
#define M95_CMD_RDSR 0x05 /*!< Read Status Register instruction */
#define M95_CMD_WRSR 0x01 /*!< Write Status Register instruction */
#define M95_CMD_WREN 0x06 /*!< Write enable instruction */
#define M95_CMD_WRDI 0x04 /*!< Write disable instruction */
#define M95_CMD_READ 0x03 /*!< Read from Memory instruction */
#define M95_CMD_WRITE 0x02 /*!< Write to Memory instruction */
///< Instruction available only for the M95_2-D device.
#define M95_CMD_RDID 0x83 /*!< Read identification page*/
#define M95_CMD_WRID 0x82 /*!< Write identification page*/
#define M95_CMD_RDLS 0x83 /*!< Reads the Identification page lock status*/
#define M95_CMD_LID 0x82 /*!< Locks the Identification page in read-only mode*/
#define M95_DUMMY_BYTE 0xA5 ///< 虚拟字节
#define M95_TEST_PAGE 0 ///< 测试页地址
///< 定义存储器大小(Bytes)
typedef enum
{
M95_PAGE_SIZE_16 = 16, ///< _M95010_ 、_M95020_ 、_M95040_
M95_PAGE_SIZE_32 = 32, ///< _M95080_ 、_M95160_、_M95320_、_M95640_
M95_PAGE_SIZE_64 = 64, ///< _M95128_、_M95256_
M95_PAGE_SIZE_128 = 128, ///< _M95512_
M95_PAGE_SIZE_256 = 256, ///< _M95M02_
} m95_page_size_e;
typedef enum
{
M95_1,
M95_2,
M95_MAX,
} m95_number_e; ///< 板卡上2块m95芯片定义
typedef union
{
uint8_t data;
struct
{
uint8_t wip : 1; ///< Write in progress
uint8_t wel : 1; ///< Write enable latch
uint8_t bp0 : 1; ///< Block protect 0
uint8_t bp1 : 1; ///< Block protect 1
uint8_t reserve : 3;
uint8_t srwd : 1; ///< Status register write protect
} bits;
} m95_write_protection_u;
typedef struct
{
m95_number_e num;
m95_write_protection_u write_protection;
spi_t *spi;
} m95_number_t;
extern m95_number_t eeprom_m95s[M95_MAX]; ///< m95芯片数组
/**
* @brief Initializes the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
*/
extern void eeprom_m95_init(m95_number_e num);
/**
* @brief Deinitializes the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
*/
extern void eeprom_m95_dinit(m95_number_e num);
/**
* @brief Enables the M95 EEPROM module.
*/
extern void eeprom_m95_enable(void);
/**
* @brief Disables the M95 EEPROM module.
*/
extern void eeprom_m95_disable(void);
/**
* @brief Closes the write protection of the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
*/
extern void eeprom_m95_write_protection_close(m95_number_e num);
/**
* @brief write protection state of the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
*/
extern BOOL eeprom_m95_write_protection_state(m95_number_e num);
/**
* @brief Reads data from the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
* @param read_addr The address to read from.
* @param data The buffer to store the read data.
* @param length The number of bytes to read.
*/
extern BOOL eeprom_m95_read(m95_number_e num, uint32_t read_addr, uint8_t *data, uint16_t length);
/**
* @brief Writes data to the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
* @param write_addr The address to write to.
* @param data The data to write.
* @param length The number of bytes to write.
*/
extern BOOL eeprom_m95_write(m95_number_e num, uint32_t write_addr, uint8_t *data, uint16_t length);
/**
* @brief Performs a test on the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
*/
extern BOOL eeprom_m95_test(m95_number_e num);
/**
* @brief Gets the status of the M95 EEPROM module.
*
* @param num The M95 EEPROM number.
*/
extern BOOL eeprom_m95_status_get(m95_number_e num);
extern BOOL eeprom_m95_1_read(uint32_t addr, uint8_t *buf, uint16_t size);
extern BOOL eeprom_m95_1_write(uint32_t addr, uint8_t *buf, uint16_t size);
extern BOOL eeprom_m95_2_read(uint32_t addr, uint8_t *buf, uint16_t size);
extern BOOL eeprom_m95_2_write(uint32_t addr, uint8_t *buf, uint16_t size);
#endif ///< __EEPROM_M95_H

View File

@ -0,0 +1,107 @@
/**
* @file ntc_3950.c
* @author xxx
* @date 2023-08-30 08:58:43
* @brief NTC的应用功能
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "ntc_3950.h"
#define CPU_VREF 2.5
#define NTC_VREF 2.5
#define TABLE_SIZE 185
#define NTC_SERIES_RESISTOR 200000 ///< 200K
#define BASE_TEMP -55
const uint32_t _table[TABLE_SIZE] = {
8989000, 8242680, 7592960, 7021380, 6513750, // -55,-54,-53,-52,-51
6059060, 5648680, 5275800, 4935020, 4621990, 4333220, 4065840, 3817520, 3586310, 3370600, // -50,-49,-48,-47,-46,-45,-44,-43,-42,-41
3169000, 2980330, 2803600, 2637910, 2482470, 2336580, 2199620, 2071020, 1950230, 1836790, // -40,-39,-38,-37,-36,-35,-34,-33,-32,-31
1730230, 1630150, 1536140, 1447840, 1364900, 1287000, 1213820, 1145090, 1080530, 1019890, // -30,-29,-28,-27,-26,-25,-24,-23,-22,-21
962912, 909379, 859074, 811797, 767359, 725581, 686296, 649348, 614590, 581883, // -20,-19,-18,-17,-16,-15,-14,-13,-12,-11
551100, 522117, 494824, 469113, 444886, 422050, 400518, 380209, 361048, 342963, // -10,-9,-8,-7,-6,-5,-4,-3,-2,-1
326560, 309764, 294529, 280131, 266520, 253647, 241470, 229946, 219036, 208706, // 0,1, 2, 3,4,5,6,7,8,9
198920, 189647, 180857, 172523, 164618, 157118, 150000, 143243, 136827, 130731, // 10,11 ,12, 13,14,15,16,17,18,19
124940, 119435, 114202, 109225, 104491, 100000, 95699, 91617, 87731, 84028, // 20,21, 22, 23,24,25,26,27,28,29
80501, 77140, 73936, 70881, 67968, 65188, 62537, 60006, 57590, 55283, // 30,31, 32, 33,34,35,36,37,38,39
53080, 50976, 48965, 47044, 45207, 43451, 41771, 40165, 38628, 37157, // 40,41, 42, 43,34,35,36,37,38,39
35750, 34402, 33112, 31876, 30692, 29558, 28471, 27429, 26430, 25472, // 50,51, 52, 53,54,55,56,57,58,59
24554, 23672, 22827, 22016, 21237, 20489, 19771, 19082, 18420, 17784, // 60,61, 62, 63,64,65,66,67,68,69
17172, 16585, 16020, 15477, 14955, 14453, 13970, 13505, 13058, 12628, // 70,71, 72, 73,74,75,76,77,78,79
12213, 11815, 11431, 11061, 10705, 10362, 10031, 9712, 9405, 9110, // 80,81, 82, 83,84,85,86,87,88,89
8824, 8549, 8284, 8028, 7782, 7544, 7314, 7093, 6879, 6673, // 90,91, 92, 93,94,95,96,97,98,99
6474, 6281, 6096, 5916, 5743, 5576, 5415, 5259, 5108, 4963, // 101,102, 103,104,105,106,107,108,109
4822, 4687, 4555, 4428, 4306, 4187, 4073, 3962, 3855, 3751, // 111,112, 113,114,115,116,117,118,119
3651, 3555, 3461, 3371, 3283, 3199, 3100, 3099, 2899, 2799, // 121,122, 123,124,125,126,127,128,129
};
/**
* @brief
* @param {uint32_t} *list
* @param {uint32_t} rt
* @return {*}
* @note
*/
static uint8_t ntc_lookup(const uint32_t *list, uint32_t rt)
{
uint8_t middle = 0;
uint8_t indexL = 0;
uint8_t indexR = TABLE_SIZE - 1;
if (rt >= *(list + 0))
return 0;
if (rt <= *(list + TABLE_SIZE - 1))
return TABLE_SIZE - 1;
while ((indexR - indexL) > 1)
{
middle = (indexL + indexR) >> 1;
if (rt == *(list + middle))
return middle;
else if (rt > *(list + middle))
indexR = middle;
else if (rt < *(list + middle))
indexL = middle;
}
return indexL;
}
/**
* @brief 0.1
* @param {uint16_t} adc采集值
* @return {float32_u}
* @note
*/
float32_u ntc_get_temp(uint16_t adc)
{
uint8_t index = 0;
int16_t data = 0;
int16_t t = 0;
int16_t result = 0;
uint32_t rt = 0;
const int16_t base = BASE_TEMP * 10;
float32_u res;
res.f = BASE_TEMP;
/**
* ad = (4095*rt)/(rt+10000)
*
* rt = (ad*NTC_SERIES_RESISTOR*CPU_VREF)/(NTC_VREF*4095-CPU_VREF*ad)
*/
rt = (adc * NTC_SERIES_RESISTOR * CPU_VREF) / (NTC_VREF * 4095 - CPU_VREF * adc);
index = ntc_lookup(_table, rt);
if (rt >= _table[0])
return res;
if (rt <= *(_table + TABLE_SIZE - 1))
{
result = (TABLE_SIZE - 1) * 10 + base;
}
else
{
data = _table[index] - _table[index + 1];
t = 10 * (_table[index] - rt) / data;
result = base + index * 10 + t;
}
res.f = result / 10.0;
return res;
}

View File

@ -0,0 +1,15 @@
/**
* @file ntc_3950.h
* @author xxx
* @date 2023-08-30 14:05:55
* @brief ntc_3950.h
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __NTC_B3950_H__
#define __NTC_B3950_H__
#include "main.h"
extern float32_u ntc_get_temp(uint16_t adc); ///< 获取温度值
#endif ///< __NTC_B3950_H__

View File

@ -0,0 +1,560 @@
/**
* @file rtc_rx8010.c
* @author xxx
* @date 2023-08-30 08:58:43
* @brief RTC芯片RX8010的应用功能
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "rtc_rx8010.h"
#include "i2cs.h"
#include "delay.h"
#define RTC_RX8010_SDA_PORT RTC_SDA_GPIO_Port
#define RTC_RX8010_SDA_PIN RTC_SDA_Pin
#define RTC_RX8010_SCL_PORT RTC_SCL_GPIO_Port
#define RTC_RX8010_SCL_PIN RTC_SCL_Pin
static i2c_t *rtc;
static TIM_TypeDef *_timer_us;
static void _delay_us(uint32_t us)
{
if (_timer_us != NULL)
{
delay_hardware_us(_timer_us, us);
}
else
{
delay_us(us);
}
}
/* sec, min, hour, week, day, month, year */
// static uint8_t calendar[7] = {0, 0, 0, 1, 29, 2, 98};
/**
* @brief RTC芯片的指定地址读取一个字节数据
* @param {uint8_t} *read_buf
* @param {uint8_t} addr
* @return {*}
*/
static BOOL rtc_read_byte(uint8_t *read_buf, uint8_t addr)
{
uint8_t *p = read_buf;
/* 发送起始信号 */
rtc->interface.start(rtc);
/* 发送从机地址 + 读写方向 */
rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 发送读地址 */
rtc->interface.write_byte(rtc, addr);
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 重新发送起始信号。前面的代码的目的向RTC传送地址下面开始读取数据 */
rtc->interface.start(rtc);
/* 发送从机地址 + 读写方向 */
rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 读取数据 */
*p = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */
/* 命令执行成功发送I2C总线停止信号 */
rtc->interface.stop(rtc);
return TRUE;
}
/**
* @brief RTC芯片的指定地址读取若干数据
* @param {uint8_t} *read_buf
* @param {uint8_t} addr
* @param {int} size
* @return {*}
*/
static BOOL rtc_read_bytes(uint8_t *read_buf, uint8_t addr, int size)
{
int i = 0;
/* 发送起始信号 */
rtc->interface.start(rtc);
/* 发送从机地址 + 读写方向 */
rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 发送读地址 */
rtc->interface.write_byte(rtc, addr);
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 重新发送起始信号。前面的代码的目的向RTC传送地址下面开始读取数据 */
rtc->interface.start(rtc);
/* 发送从机地址 + 读写方向 */
rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 循环读取数据RTC芯片地址自动自增 */
for (i = 0; i < size; i++)
{
/* 每读完1个字节后需要发送Ack 最后一个字节需要发Nack */
if (i != (size - 1))
{
read_buf[i] = rtc->interface.read_byte(rtc, TRUE); /* 读1个字节 */
}
else
{
read_buf[i] = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */
}
}
/* 命令执行成功发送I2C总线停止信号 */
rtc->interface.stop(rtc);
return TRUE;
}
/**
* @brief RTC芯片的指定地址写入一个数据
* @param {uint8_t} data
* @param {uint8_t} addr
* @return {*}
* @note
*/
static BOOL rtc_write_byte(uint8_t data, uint8_t addr)
{
int retry = 0;
/* 尝试与RTC芯片建立I2C通讯 */
for (retry = 0; retry < 100; retry++)
{
rtc->interface.start(rtc); /* 发送起始信号 */
rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */
if (rtc->interface.wait_ack(rtc) == TRUE)
{
break;
}
}
if (retry == 100)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 发送起始写地址 */
rtc->interface.write_byte(rtc, addr);
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 写入数据 */
rtc->interface.write_byte(rtc, data);
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 命令执行成功发送I2C总线停止信号 */
rtc->interface.stop(rtc);
return TRUE;
}
/**
* @brief RTC芯片的指定地址写入若干数据
* @param {uint8_t} *write_buf
* @param {uint8_t} addr
* @param {int} size
* @return {*}
* @note
*/
static BOOL rtc_write_bytes(uint8_t *write_buf, uint8_t addr, int size)
{
int i = 0;
int retry = 0;
for (i = 0; i < size; i++)
{
if (i == 0)
{
/* 尝试与RTC芯片建立I2C通讯 */
for (retry = 0; retry < 100; retry++)
{
rtc->interface.start(rtc); /* 发送起始信号 */
rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */
if (rtc->interface.wait_ack(rtc) == TRUE)
{
break;
}
}
if (retry == 100)
{
rtc->interface.stop(rtc);
return FALSE;
}
/* 发送起始写地址 */
rtc->interface.write_byte(rtc, addr);
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
}
/* 循环写入数据RTC芯片地址自动自增 */
rtc->interface.write_byte(rtc, write_buf[i]);
if (rtc->interface.wait_ack(rtc) != TRUE)
{
rtc->interface.stop(rtc);
return FALSE;
}
}
/* 命令执行成功发送I2C总线停止信号 */
rtc->interface.stop(rtc);
return TRUE;
}
/**
* @brief RTC芯片RTC响应
* @return {*}
* @note
*/
static void rtc_dummy_read(void)
{
rtc->interface.start(rtc);
rtc->interface.write_byte(rtc, RTC_WR_ADDR);
rtc->interface.write_byte(rtc, 0x20);
rtc->interface.start(rtc);
rtc->interface.write_byte(rtc, RTC_RD_ADDR);
rtc->interface.read_byte(rtc, FALSE);
rtc->interface.stop(rtc);
}
/**
* @brief VLF0x1e bit[1]
* @return {uint8_t} 0 = VLF位为01 = VLF位为1
* @note
*/
static uint8_t rtc_check_vlf(void)
{
uint8_t flag_register = 1;
uint8_t vlf = 0;
rtc_read_byte(&flag_register, RTC_FLAG_ADDR);
vlf = (flag_register & 0x02);
if (vlf == 0)
{
return 0;
}
else
{
return 1;
}
}
/**
* @brief VLF位清除0x1e bit[1]
* @return {uint8_t} 0 = 1 = 2 =
*/
static uint8_t rtc_wait_vlf_clear(void)
{
uint8_t ret = 1;
uint8_t i = 0;
uint8_t vlf;
for (i = 0; i < 10; i++)
{
vlf = rtc_check_vlf();
if (vlf == 0)
{
ret = ((i > 0) ? 0 : 2);
return ret;
}
/* 清除VLF */
rtc_write_byte(0, RTC_FLAG_ADDR);
}
return ret;
}
/**
* @brief
* @return {BOOL} FALSE = TRUE =
*/
static BOOL rtc_soft_reset(void)
{
BOOL ret = FALSE;
ret = rtc_write_byte(0x00, 0x1f);
ret = rtc_write_byte(0x80, 0x1f);
ret = rtc_write_byte(0xd3, 0x60);
ret = rtc_write_byte(0x03, 0x66);
ret = rtc_write_byte(0x02, 0x6b);
ret = rtc_write_byte(0x01, 0x6b);
if (ret == 0)
{
_delay_us(2000);
}
return ret;
}
/**
* @brief
* @return {BOOL} TRUE = FALSE =
*/
static BOOL rtc_clock_reginit(void)
{
BOOL ret = FALSE;
/* set reserve register */
ret = rtc_write_byte(RTC_REG17_DATA, RTC_REG17_ADDR);
ret = rtc_write_byte(RTC_REG30_DATA, RTC_REG30_ADDR);
ret = rtc_write_byte(RTC_REG31_DATA, RTC_REG31_ADDR);
ret = rtc_write_byte(RTC_IRQ_DATA, RTC_IRQ_ADDR);
/* write 0x04 to reg_0x1d */
ret = rtc_write_byte(0x04, 0x1d);
/* write 0x00 to reg_0x1e */
ret = rtc_write_byte(0x00, 0x1e);
/* stop clock */
ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR);
/* set the present time */
// ret = rtc_write_bytes(calendar, RTC_CLOCK_ADDR, sizeof(calendar));
/* start clock */
ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR);
return ret;
}
/**
* @brief RTC芯片初始化
* @return {BOOL} TRUE = FALSE =
* @note
*/
BOOL rtc_init(TIM_TypeDef *timer_us)
{
if (timer_us != NULL)
{
_timer_us = timer_us;
ENABLE_TIM_COUNT(_timer_us);
}
i2c_gpio_group_t gpios;
int ret = 1;
gpios.scl = gpio_create(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN);
gpios.sda = gpio_create(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN);
rtc = i2c_create(gpios, 10);
rtc_dummy_read();
/* wait for VLF bit clear */
ret = rtc_wait_vlf_clear();
if (ret == 0)
{
/* software reset */
ret = rtc_soft_reset();
if (ret == FALSE)
{
return FALSE;
}
}
else if (ret == 1)
{
return FALSE;
}
/* register initialize */
return rtc_clock_reginit();
}
/**
* @brief RTC芯片反初始化
* @return {*}
* @note
*/
BOOL rtc_dinit(void)
{
GPIO_SET_ANALOG(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN);
GPIO_SET_ANALOG(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN);
return TRUE;
}
/**
* @brief RTC芯片读取时间
* @param {uint8_t} *read_buf -
* @return {BOOL} TRUE = FALSE =
* @note
*/
BOOL rtc_get_clock_time(uint8_t *read_buf)
{
return rtc_read_bytes(read_buf, RTC_CLOCK_ADDR, 7);
}
/**
* @brief RTC芯片写入时间
* @param {rtc_date} *data -
* @return {BOOL} TRUE = FALSE =
* @note
*/
BOOL rtc_set_clock_time(rtc_date *data)
{
BOOL ret = FALSE;
uint8_t tmp[7];
tmp[0] = data->second;
tmp[1] = data->minute;
tmp[2] = data->hour;
tmp[3] = data->weekday;
tmp[4] = data->day;
tmp[5] = data->month;
tmp[6] = data->year;
tmp[3] = (rtc_week_e)tmp[3]; // 改成星期几
/* stop clock */
ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR);
/* set the present time */
ret = rtc_write_bytes(tmp, RTC_CLOCK_ADDR, sizeof(tmp));
/* start clock */
ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR);
return ret;
}
/**
* RTC
*
* RTC设备中获取当前的日期和时间197011
* RTC设备中获取时间0
*
* @return 1970110
*/
uint32_t rtc_timestamp(void)
{
BOOL ret = FALSE;
uint8_t tmp[7];
rtc_date_t date;
rtc_time_t time;
ret = rtc_get_clock_time(tmp);
if (ret == FALSE)
{
return 0;
}
date.year = hex_format_dec(tmp[6]);
date.month = hex_format_dec(tmp[5]);
date.day = hex_format_dec(tmp[4]);
time.hour = hex_format_dec(tmp[2]);
time.minute = hex_format_dec(tmp[1]);
time.second = hex_format_dec(tmp[0]);
return time2stamp(&date, &time);
}
/**
* @brief
* @param {uint8_t} *weekday
* @return {*}
* @note
*/
void rtc_weekday_convert(uint8_t *weekday)
{
switch (*weekday)
{
case 1:
*weekday = MON;
break;
case 2:
*weekday = TUE;
break;
case 3:
*weekday = WED;
break;
case 4:
*weekday = THUR;
break;
case 5:
*weekday = FRI;
break;
case 6:
*weekday = SAT;
break;
case 7:
*weekday = SUN;
break;
default:
*weekday = 0;
break;
}
}
/**
* @brief
* @param {uint8_t} *weekday
* @return {*}
* @note
*/
void rtc_weekday_rconvert(uint8_t *weekday)
{
switch (*weekday)
{
case MON:
*weekday = 1;
break;
case TUE:
*weekday = 2;
break;
case WED:
*weekday = 3;
break;
case THUR:
*weekday = 4;
break;
case FRI:
*weekday = 5;
break;
case SAT:
*weekday = 6;
break;
case SUN:
*weekday = 7;
break;
default:
*weekday = 0;
break;
}
}

View File

@ -0,0 +1,99 @@
/**
* @file rtc_rx8010.h
* @author xxx
* @date 2023-08-30 14:05:55
* @brief rtc_rx8010.h
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __RTC_RX8010_H__
#define __RTC_RX8010_H__
#include "main.h"
#define RTC_DEVICE_ADDR 0x32
#define RTC_WR_ADDR ((RTC_DEVICE_ADDR << 1) | 0)
#define RTC_RD_ADDR ((RTC_DEVICE_ADDR << 1) | 1)
#define RTC_FLAG_ADDR 0x1e
#define RTC_CLOCK_ADDR 0x10
#define RTC_CONTROL_ADDR 0x1f
#define RTC_REG17_ADDR 0x17
#define RTC_REG17_DATA 0xd8
#define RTC_REG30_ADDR 0x30
#define RTC_REG30_DATA 0x00
#define RTC_REG31_ADDR 0x31
#define RTC_REG31_DATA 0x08
#define RTC_IRQ_ADDR 0x32
#define RTC_IRQ_DATA 0x00
typedef enum
{
SUN = BIT0,
MON = BIT1,
TUE = BIT2,
WED = BIT3,
THUR = BIT4,
FRI = BIT5,
SAT = BIT6
} rtc_week_e; ///< 星期码
typedef struct
{
uint8_t year; ///< 7 bit - 1 63
uint8_t month; ///< 4 bit
uint8_t day; ///< 5 bit
uint8_t weekday; ///< rtc_week_e
uint8_t hour; ///< 5 bit
uint8_t minute; ///< 6 bit
uint8_t second; ///< 6 bit
} rtc_date;
/**
* @brief Initializes the RTC module.
* @return TRUE if the initialization is successful, FALSE otherwise.
*/
extern BOOL rtc_init(TIM_TypeDef *timer_us);
/**
* @brief Deinitializes the RTC module.
* @return TRUE if the deinitialization is successful, FALSE otherwise.
*/
extern BOOL rtc_dinit(void);
/**
* @brief Retrieves the current clock time from the RTC module.
* @param read_buf Pointer to the buffer to store the clock time.
* @return TRUE if the clock time is successfully retrieved, FALSE otherwise.
*/
extern BOOL rtc_get_clock_time(uint8_t *read_buf);
/**
* @brief Sets the clock time in the RTC module.
* @param data Pointer to the RTC date structure containing the new clock time.
* @return TRUE if the clock time is successfully set, FALSE otherwise.
*/
extern BOOL rtc_set_clock_time(rtc_date *data);
/**
* @brief Retrieves the current alarm time from the RTC module.
* @param read_buf Pointer to the buffer to store the alarm time.
* @return TRUE if the alarm time is successfully retrieved, FALSE otherwise.
*/
extern uint32_t rtc_timestamp(void);
/**
* @brief Converts the weekday value to a human-readable format.
* @param weekday Pointer to the weekday value to be converted.
*/
extern void rtc_weekday_convert(uint8_t *weekday);
/**
* @brief Converts the weekday value from a human-readable format to the RTC format.
* @param weekday Pointer to the weekday value to be converted.
*/
extern void rtc_weekday_rconvert(uint8_t *weekday);
#endif ///< !__RTC_RX8010_H__

181
User/system/driver/sht40.c Normal file
View File

@ -0,0 +1,181 @@
#include "sht40.h"
#include "i2cs.h"
#include "delay.h"
#define SHT40_SDA_PORT I2C1_SDA_GPIO_Port
#define SHT40_SDA_PIN I2C1_SDA_Pin
#define SHT40_SCL_PORT I2C1_SCL_GPIO_Port
#define SHT40_SCL_PIN I2C1_SCL_Pin
#define SHT40_I2C_ADDRESS 0x44
#define SHT40_MEASURE_CMD 0xFD // 2*8-bit T-data:8-bit CRC:2*8-bit RH-data: 8-bit CRC
static i2c_t *_sht40_i2c;
static TIM_TypeDef *_timer_us;
static uint8_t crc8(uint8_t *data, uint8_t len);
static void _delay_us(uint32_t us)
{
if (_timer_us != NULL)
{
delay_hardware_us(_timer_us, us);
}
else
{
delay_us(us);
}
}
void sht40_test(void)
{
float32 temperature = 0;
float32 humidity = 0;
sht40_read(&temperature, &humidity);
__NOP();
}
/**
* @brief SHT40
*
* SHT40 I2C GPIO I2C
*
* sht40_i2c SHT40
*/
void sht40_init(TIM_TypeDef *timer_us)
{
DBG_ASSERT(timer_us != NULL __DBG_LINE);
if (timer_us != NULL)
{
_timer_us = timer_us;
ENABLE_TIM_COUNT(_timer_us);
}
i2c_gpio_group_t gpios;
gpios.scl = gpio_create(SHT40_SCL_PORT, SHT40_SCL_PIN);
gpios.sda = gpio_create(SHT40_SDA_PORT, SHT40_SDA_PIN);
_sht40_i2c = i2c_create(gpios, 10);
DBG_ASSERT(_sht40_i2c != NULL __DBG_LINE);
// sht40_test(); // 测试 SHT40
}
/**
* @brief SHT40
*
* SHT40 湿便湿
*
*
*/
void sht40_dinit(void)
{
GPIO_SET_ANALOG(SHT40_SDA_PORT, SHT40_SDA_PIN);
GPIO_SET_ANALOG(SHT40_SCL_PORT, SHT40_SCL_PIN);
}
/**
* @brief 湿
*
* 湿湿
*
* @param temperature
* @param humidity 湿
*/
BOOL sht40_read(float32 *temperature, float32 *humidity)
{
uint8_t data[6];
osel_memset(data, 0, ARRAY_LEN(data));
// 发送开始信号
_sht40_i2c->interface.start(_sht40_i2c);
// 发送写入地址命令
_sht40_i2c->interface.write_byte(_sht40_i2c, SHT40_I2C_ADDRESS << 1);
// 等待写入地址命令响应
if (_sht40_i2c->interface.wait_ack(_sht40_i2c) == FALSE)
{
return FALSE;
}
// 发送测量命令
_sht40_i2c->interface.write_byte(_sht40_i2c, SHT40_MEASURE_CMD);
// 等待测量命令响应
if (_sht40_i2c->interface.wait_ack(_sht40_i2c) == FALSE)
{
return FALSE;
}
// 停止I2C总线
_sht40_i2c->interface.stop(_sht40_i2c);
_delay_us(10000); // 根据 SHT40 数据手册,等待至少 10ms
// 发送开始信号
_sht40_i2c->interface.start(_sht40_i2c);
// 发送写入地址命令
_sht40_i2c->interface.write_byte(_sht40_i2c, (SHT40_I2C_ADDRESS << 1) | 1);
// 等待写入地址命令响应
if (_sht40_i2c->interface.wait_ack(_sht40_i2c) == FALSE)
{
return FALSE;
}
for (uint8_t i = 0; i < ARRAY_LEN(data); i++)
{
if (i == 5)
{
data[i] = _sht40_i2c->interface.read_byte(_sht40_i2c, FALSE);
}
else
{
data[i] = _sht40_i2c->interface.read_byte(_sht40_i2c, TRUE);
}
}
// 停止I2C总线
_sht40_i2c->interface.stop(_sht40_i2c);
*temperature = 0;
*humidity = 0;
if (crc8(&data[0], 2) != data[2] || crc8(&data[3], 2) != data[5])
{
return FALSE;
}
else
{
*temperature = (float32)((uint16_t)data[0] << 8 | data[1]) * 175 / 65535 - 45;
*humidity = (float32)((uint16_t)data[3] << 8 | data[4]) * 125 / 65535 - 6;
if (*humidity > 100)
{
*humidity = 100;
}
if (*humidity < 0)
{
*humidity = 0;
}
return TRUE;
}
}
/**
* @brief crc8校验函数 x^8 + x^5 + x^4 + 1
* @param data
* @param len
* @retval
* @note SHT3温湿度传感器的数据校验
*/
static uint8_t crc8(uint8_t *data, uint8_t len)
{
const uint8_t polynomial = 0x31;
uint8_t crc = 0xFF;
int i, j;
for (i = 0; i < len; ++i)
{
crc ^= *data++;
for (j = 0; j < 8; ++j)
{
crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1);
}
}
return crc;
}

View File

@ -0,0 +1,9 @@
#ifndef __SHT40_H
#define __SHT40_H
#include "main.h"
void sht40_init(TIM_TypeDef *timer_us); ///< 初始化 SHT40 传感器
void sht40_dinit(void); ///< 反初始化 SHT40 传感器
BOOL sht40_read(float32 *temperature, float32 *humidity); ///< 读取温湿度传感器数据
#endif ///< !__SHT40_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
#ifndef __SSD1306_OLED_H
#define __SSD1306_OLED_H
#include "main.h"
// OLED引脚定义
#define SSD1306_SDA_PORT OLED_SDA_GPIO_Port
#define SSD1306_SDA_PIN OLED_SDA_Pin
#define SSD1306_SCK_PORT OLDE_SCK_GPIO_Port
#define SSD1306_SCK_PIN OLDE_SCK_Pin
// I2C地址
#define SSD1306_I2C_ADDRESS 0x78
// OLED显示参数
#define SSD1306_WIDTH 128
#define SSD1306_HEIGHT 64
// OLED颜色
#define SSD1306_WHITE 1
#define SSD1306_BLACK 0
// OLED命令定义
#define SSD1306_CMD_DISPLAY_OFF 0xAE
#define SSD1306_CMD_DISPLAY_ON 0xAF
#define SSD1306_CMD_SET_CONTRAST 0x81
#define SSD1306_CMD_DISPLAY_ALL_ON_RESUME 0xA4
#define SSD1306_CMD_DISPLAY_ALL_ON 0xA5
#define SSD1306_CMD_NORMAL_DISPLAY 0xA6
#define SSD1306_CMD_INVERT_DISPLAY 0xA7
#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3
#define SSD1306_CMD_SET_COM_PINS 0xDA
#define SSD1306_CMD_SET_VCOM_DETECT 0xDB
#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIV 0xD5
#define SSD1306_CMD_SET_PRECHARGE 0xD9
#define SSD1306_CMD_SET_MULTIPLEX 0xA8
#define SSD1306_CMD_SET_LOW_COLUMN 0x00
#define SSD1306_CMD_SET_HIGH_COLUMN 0x10
#define SSD1306_CMD_SET_START_LINE 0x40
#define SSD1306_CMD_MEMORY_MODE 0x20
#define SSD1306_CMD_COLUMN_ADDR 0x21
#define SSD1306_CMD_PAGE_ADDR 0x22
#define SSD1306_CMD_COM_SCAN_INC 0xC0
#define SSD1306_CMD_COM_SCAN_DEC 0xC8
#define SSD1306_CMD_SEG_REMAP 0xA0
#define SSD1306_CMD_CHARGE_PUMP 0x8D
#define SSD1306_CMD_SET_DC_DC_ENABLE 0x14
#define SDA_OUT() \
{ \
GPIO_SET_OUTPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \
}
#define SDA_IN() \
{ \
GPIO_SET_INPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \
}
// 函数声明
void ssd1306_init(void);
void ssd1306_logo(void);
void ssd1306_display_on(void);
void ssd1306_display_off(void);
void ssd1306_update_screen(void);
void ssd1306_fill(uint8_t color);
void ssd1306_clear(void);
void ssd1306_clear_buffer(void);
void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp);
void ssd1306_f6x8_string(uint8_t x, uint8_t y, const uint8_t *ch);
void ssd1306_f6x8_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num);
void ssd1306_f6x8_string_number(uint8_t x, uint8_t y, const uint8_t *ch, uint8_t unit, float32 num);
void ssd1306_f8x16_string(uint8_t x, uint8_t y, const uint8_t *ch);
void ssd1306_f8x16_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num);
void ssd1306_draw_text_center(uint8_t y, const char *text);
void ssd1306_draw_progress_bar(uint8_t progress);
void ssd1306_fill_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void ssd1306_draw_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void ssd1306_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color);
void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t color);
#endif // __SSD1306_OLED_H

View File

@ -0,0 +1,368 @@
#include "tmc2240.h"
tmc2240_t _tmc2240[TMC2240_MAX];
static void tmc2240_write(tmc2240_index_e index, uint8_t *data)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
DBG_ASSERT(tmc->spi != NULL __DBG_LINE);
tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs);
for (uint16_t i = 0; i < 5; i++)
{
tmc->spi->interface.u.normal.spi_send(tmc->spi, data[i]);
}
tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs);
}
static void tmc2240_read(tmc2240_index_e index, uint8_t *wdata, uint8_t *rdata)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
DBG_ASSERT(tmc->spi != NULL __DBG_LINE);
tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs);
for (uint16_t i = 0; i < 5; i++)
{
rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]);
}
tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs);
__NOP();
__NOP();
__NOP();
tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs);
for (uint16_t i = 0; i < 5; i++)
{
rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]);
}
tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs);
}
static void tmc2240_reg_write(tmc2240_index_e index, uint8_t reg, uint32_t data)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
DBG_ASSERT(tmc->spi != NULL __DBG_LINE);
uint8_t wdata[5] = {0};
wdata[0] = TMC2240_HIGHT_BIT | reg;
wdata[1] = (data >> 24) & 0xFF;
wdata[2] = (data >> 16) & 0xFF;
wdata[3] = (data >> 8) & 0xFF;
wdata[4] = data & 0xFF;
tmc2240_write(index, wdata);
}
static uint32_t tmc2240_reg_read(tmc2240_index_e index, uint8_t reg)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
DBG_ASSERT(tmc->spi != NULL __DBG_LINE);
uint8_t wdata[5] = {0};
uint8_t rdata[5] = {0};
wdata[0] = reg;
tmc2240_read(index, wdata, rdata);
return (rdata[1] << 24) | (rdata[2] << 16) | (rdata[3] << 8) | rdata[4];
}
/**
* @brief TMC2240步进电机驱动器
*
* TMC2240步进电机驱动器的各种参数线
*
* @param index TMC2240步进电机驱动器的索引
*/
static void tmc2240_config_write(tmc2240_index_e index)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
tmc->config.gconf.data = 0x00000000;
tmc->config.chopconf.data = 0x00410153;
tmc->config.chopconf.bits.mres = TMC2240_MRES_8;
tmc->config.drvconf.data = 0x00000021;
tmc->config.global_scaler.data = 0x00000000;
tmc->config.ihold_irun.bits.ihold = 31;
tmc->config.ihold_irun.bits.irun = 31;
tmc->config.ihold_irun.bits.iholddelay = 0;
tmc->config.ihold_irun.bits.irundelay = 0;
tmc->config.pwmconf.data = 0xC44C261E;
tmc->config.gstat.data = 0x00000007;
tmc2240_reg_write(index, TMC2240_GCONF, tmc->config.gconf.data);
tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data);
tmc2240_reg_write(index, TMC2240_DRV_CONF, tmc->config.drvconf.data);
tmc2240_reg_write(index, TMC2240_GLOBAL_SCALER, tmc->config.global_scaler.data);
tmc2240_reg_write(index, TMC2240_IHOLD_IRUN, tmc->config.ihold_irun.data);
tmc2240_reg_write(index, TMC2240_PWMCONF, tmc->config.pwmconf.data);
tmc2240_reg_write(index, TMC2240_GSTAT, tmc->config.gstat.data);
}
static void _tmc2240_motor_update(tmc2240_index_e index)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
switch (tmc->config.chopconf.bits.mres)
{
case TMC2240_MRES_256:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 256;
break;
case TMC2240_MRES_128:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 128;
break;
case TMC2240_MRES_64:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 64;
break;
case TMC2240_MRES_32:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 32;
break;
case TMC2240_MRES_16:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 16;
break;
case TMC2240_MRES_8:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 8;
break;
case TMC2240_MRES_4:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 4;
break;
case TMC2240_MRES_2:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 2;
break;
case TMC2240_MRES_1:
tmc->motor.step_angle = MOTOR_42_STEP_ANGLE;
break;
default:
break;
}
tmc->motor.circle_pulse = 360 / tmc->motor.step_angle;
}
static void _tmc2240_enable(tmc2240_index_e index, BOOL enable)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
BOOL state = tmc->en->read(*tmc->en) == 0 ? TRUE : FALSE;
if (state == enable)
{
return;
}
if (enable == TRUE)
{
PWM_START(tmc->timer, tmc->time_ch);
tmc->en->reset(*tmc->en);
tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data);
}
else
{
PWM_STOP(tmc->timer, tmc->time_ch);
tmc->en->set(*tmc->en);
chopconf_u chopconf;
osel_memset((uint8_t *)&chopconf, 0, sizeof(chopconf_u));
chopconf.bits.mres = TMC2240_MRES_1;
tmc2240_reg_write(index, TMC2240_CHOPCONF, chopconf.data);
}
}
static void _tmc2240_direction(tmc2240_index_e index, tmc2240_direction_e dir)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
if (dir == TMC2240_FORWARD)
{
tmc->dir->reset(*tmc->dir);
}
else
{
tmc->dir->set(*tmc->dir);
}
}
void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
osel_memset((uint8_t *)tmc, 0, sizeof(tmc2240_t));
tmc->timer = timer;
tmc->time_ch = time_ch;
tmc->spi = spi_create(SPI_TYPE_NORMAL, *gpios, 0);
DBG_ASSERT(tmc->spi != NULL __DBG_LINE);
tmc->spi->interface.hardware_enable(tmc->spi, SPIx);
{
tmc->default_tm.sysclk = SystemCoreClock / 1000;
tmc->default_tm.psc = PWM_GET_PSC(tmc->timer);
tmc->default_tm.arr = PWM_GET_ARR(tmc->timer);
tmc->default_tm.freq = PWM_GET_FREQ(tmc->timer);
}
tmc->params.percent = TMC2240_PWM_DUTY_DEFAULT;
tmc->params.arr = 0;
tmc->params.freq = 0;
tmc->params.enable = FALSE;
tmc->params.direction = TMC2240_FORWARD;
tmc2240_config_write(index);
_tmc2240_motor_update(index);
}
tmc2240_t *tmc2240_get(tmc2240_index_e index)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
return &_tmc2240[index];
}
void tmc2240_percent(tmc2240_index_e index, float32 percent)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
PWM_SET_DUTY(tmc->timer, tmc->time_ch, ABS(percent));
}
void tmc2240_config_read(tmc2240_index_e index)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
tmc->read_config.gconf.data = tmc2240_reg_read(index, TMC2240_GCONF);
tmc->read_config.chopconf.data = tmc2240_reg_read(index, TMC2240_CHOPCONF);
tmc->read_config.drvconf.data = tmc2240_reg_read(index, TMC2240_DRV_CONF);
tmc->read_config.global_scaler.data = tmc2240_reg_read(index, TMC2240_GLOBAL_SCALER);
tmc->read_config.ihold_irun.data = tmc2240_reg_read(index, TMC2240_IHOLD_IRUN);
tmc->read_config.pwmconf.data = tmc2240_reg_read(index, TMC2240_PWMCONF);
tmc->read_config.gstat.data = tmc2240_reg_read(index, TMC2240_GSTAT);
tmc->data.tmc2240_adc_temp = tmc2240_reg_read(index, TMC2240_ADC_TEMP);
tmc->data.tmc2240_temperature = (float32)(tmc->data.tmc2240_adc_temp - 2038) / 7.7;
}
void tmc2240_test(tmc2240_index_e index)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
_tmc2240_enable(index, tmc->params.enable);
if (tmc->params.enable == TRUE)
{
_tmc2240_direction(index, tmc->params.direction);
if (PWM_GET_ARR(tmc->timer) != tmc->params.arr)
{
if (tmc->params.arr == 0)
{
tmc->params.arr = tmc->default_tm.arr;
}
PWM_SET_ARR(tmc->timer, tmc->params.arr);
tmc->params.freq = PWM_GET_FREQ(tmc->timer);
}
tmc2240_percent(index, tmc->params.percent);
if (tmc->config.chopconf.bits.mres != tmc->read_config.chopconf.bits.mres)
{
tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data);
_tmc2240_motor_update(index);
}
}
}
void tmc2240_motor_set_angle(tmc2240_index_e index, int32_t angle)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
if (angle == 0)
{
return;
}
tmc->params.enable = FALSE;
_tmc2240_enable(index, tmc->params.enable);
tmc->motor.pulse_count = 0;
if (angle > 0)
{
tmc->params.direction = TMC2240_FORWARD;
}
else
{
tmc->params.direction = TMC2240_BACKWARD;
}
tmc->motor.pulse_count = ABS(angle) / tmc->motor.step_angle;
tmc->motor.step_angle_count = 0;
tmc->params.enable = TRUE;
_tmc2240_direction(index, tmc->params.direction);
if (PWM_GET_ARR(tmc->timer) != tmc->params.arr)
{
if (tmc->params.arr == 0)
{
tmc->params.arr = tmc->default_tm.arr;
}
PWM_SET_ARR(tmc->timer, tmc->params.arr);
tmc->params.freq = PWM_GET_FREQ(tmc->timer);
}
tmc2240_percent(index, tmc->params.percent);
if (tmc->config.chopconf.bits.mres != tmc->read_config.chopconf.bits.mres)
{
tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data);
_tmc2240_motor_update(index);
}
_tmc2240_enable(index, tmc->params.enable);
}
void tmc2240_motor_update(tmc2240_index_e index)
{
DBG_ASSERT(index < TMC2240_MAX __DBG_LINE);
tmc2240_t *tmc = &_tmc2240[index];
DBG_ASSERT(tmc != NULL __DBG_LINE);
if (tmc->motor.pulse_count > 0)
{
tmc->motor.pulse_count--; // 脉冲数
tmc->motor.step_angle_count++; // 步距个数
if (tmc->params.direction == TMC2240_FORWARD)
{
tmc->motor.add_pulse_count++; /* 绝对位置++ */
}
else
{
tmc->motor.add_pulse_count--; /* 绝对位置-- */
}
}
/* 当脉冲数等于0的时候 代表需要发送的脉冲个数已完成,停止输出 */
if (tmc->motor.pulse_count <= 0)
{
tmc->params.enable = FALSE;
_tmc2240_enable(index, tmc->params.enable);
}
}

View File

@ -0,0 +1,372 @@
/**
* @file tmc2240.h
* @author xushenghao
* @brief TMC2240驱动头文件
* @version 0.1
* @note
* 1. VM需要供电SPI无法正常通信
* 2. 421.812001/81=0.2251600
*/
#ifndef __TMC2240_H
#define __TMC2240_H
#include "main.h"
#include "spis.h"
#define MOTOR_42_STEP_ANGLE 1.8f // 42步进电机每步1.8度
#define TMC2240_PWM_DUTY_DEFAULT 50 // PWM默认占空比
/*
0x00 = 0x00002108 ;; writing GCONF @ address 0=0x00 with 0x00002108=8456=0.0
0x03 = 0x00000000 ;; writing SLAVECONF @ address 1=0x03 with 0x00000000=0=0.0
0x04 = 0x4001682C ;; writing IOIN @ address 2=0x04 with 0x4001682C=1073834028=0.0
0x0A = 0x00000021 ;; writing DRV_CONF @ address 3=0x0A with 0x00000021=33=0.0
0x0B = 0x00000000 ;; writing GLOBAL_SCALER @ address 4=0x0B with 0x00000000=0=0.0
0x10 = 0x00001208 ;; writing IHOLD_IRUN @ address 5=0x10 with 0x00001208=4616=0.0
0x11 = 0x00000000 ;; writing TPOWERDOWN @ address 6=0x11 with 0x00000000=0=0.0
0x13 = 0x00000000 ;; writing TPWMTHRS @ address 7=0x13 with 0x00000000=0=0.0
0x14 = 0x000003BE ;; writing TCOOLTHRS @ address 8=0x14 with 0x000003BE=958=0.0
0x15 = 0x00000000 ;; writing THIGH @ address 9=0x15 with 0x00000000=0=0.0
0x2D = 0x00000000 ;; writing DIRECT_MODE @ address 10=0x2D with 0x00000000=0=0.0
0x38 = 0x00000000 ;; writing ENCMODE @ address 11=0x38 with 0x00000000=0=0.0
0x39 = 0x00000000 ;; writing X_ENC @ address 12=0x39 with 0x00000000=0=0.0
0x3A = 0x00010000 ;; writing ENC_CONST @ address 13=0x3A with 0x00010000=65536=0.0
0x52 = 0x0B920F25 ;; writing OTW_OV_VTH @ address 14=0x52 with 0x0B920F25=194121509=0.0
0x60 = 0xAAAAB554 ;; writing MSLUT[0] @ address 15=0x60 with 0xAAAAB554=0=0.0
0x61 = 0x4A9554AA ;; writing MSLUT[1] @ address 16=0x61 with 0x4A9554AA=1251300522=0.0
0x62 = 0x24492929 ;; writing MSLUT[2] @ address 17=0x62 with 0x24492929=608774441=0.0
0x63 = 0x10104222 ;; writing MSLUT[3] @ address 18=0x63 with 0x10104222=269500962=0.0
0x64 = 0xFBFFFFFF ;; writing MSLUT[4] @ address 19=0x64 with 0xFBFFFFFF=0=0.0
0x65 = 0xB5BB777D ;; writing MSLUT[5] @ address 20=0x65 with 0xB5BB777D=0=0.0
0x66 = 0x49295556 ;; writing MSLUT[6] @ address 21=0x66 with 0x49295556=1227445590=0.0
0x67 = 0x00404222 ;; writing MSLUT[7] @ address 22=0x67 with 0x00404222=4211234=0.0
0x68 = 0xFFFF8056 ;; writing MSLUTSEL @ address 23=0x68 with 0xFFFF8056=0=0.0
0x69 = 0x00F70000 ;; writing MSLUTSTART @ address 24=0x69 with 0x00F70000=16187392=0.0
0x6C = 0x00410153 ;; writing CHOPCONF @ address 25=0x6C with 0x00410153=4260179=0.0
0x6D = 0x00040000 ;; writing COOLCONF @ address 26=0x6D with 0x00040000=262144=0.0
0x70 = 0xC44C001E ;; writing PWMCONF @ address 27=0x70 with 0xC44C001E=0=0.0
0x74 = 0x00000000 ;; writing SG4_THRS @ address 28=0x74 with 0x00000000=0=0.0
*/
#define TMC2240_GCONF 0x00
#define TMC2240_GSTAT 0x01
#define TMC2240_IFCNT 0x02
#define TMC2240_SLAVECONF 0x03
#define TMC2240_IOIN 0x04
#define TMC2240_DRV_CONF 0x0A
#define TMC2240_GLOBAL_SCALER 0x0B
#define TMC2240_IHOLD_IRUN 0x10
#define TMC2240_TPOWERDOWN 0x11
#define TMC2240_TSTEP 0x12
#define TMC2240_TPWMTHRS 0x13
#define TMC2240_TCOOLTHRS 0x14
#define TMC2240_THIGH 0x15
#define TMC2240_DIRECT_MODE 0x2D
#define TMC2240_ENCMODE 0x38
#define TMC2240_XENC 0x39
#define TMC2240_ENC_CONST 0x3A
#define TMC2240_ENC_STATUS 0x3B
#define TMC2240_ENC_LATCH 0x3C
#define TMC2240_ADC_VSUPPLY_AIN 0x50
#define TMC2240_ADC_TEMP 0x51
#define TMC2240_OTW_OV_VTH 0x52
#define TMC2240_MSLUT0 0x60
#define TMC2240_MSLUT1 0x61
#define TMC2240_MSLUT2 0x62
#define TMC2240_MSLUT3 0x63
#define TMC2240_MSLUT4 0x64
#define TMC2240_MSLUT5 0x65
#define TMC2240_MSLUT6 0x66
#define TMC2240_MSLUT7 0x67
#define TMC2240_MSLUTSEL 0x68
#define TMC2240_MSLUTSTART 0x69
#define TMC2240_MSCNT 0x6A
#define TMC2240_MSCURACT 0x6B
#define TMC2240_CHOPCONF 0x6C
#define TMC2240_COOLCONF 0x6D
#define TMC2240_DCCTRL 0x6E
#define TMC2240_DRVSTATUS 0x6F
#define TMC2240_PWMCONF 0x70
#define TMC2240_PWMSCALE 0x71
#define TMC2240_PWM_AUTO 0x72
#define TMC2240_SG4_THRS 0x74
#define TMC2240_SG4_RESULT 0x75
#define TMC2240_SG4_IND 0x76
#define TMC2240_HIGHT_BIT 0x80
typedef enum
{
TMC2240_1,
TMC2240_MAX,
} tmc2240_index_e;
typedef enum
{
TMC2240_FORWARD, // 正转
TMC2240_BACKWARD, // 反转
} tmc2240_direction_e;
typedef enum
{
TMC2240_MRES_256,
TMC2240_MRES_128,
TMC2240_MRES_64,
TMC2240_MRES_32,
TMC2240_MRES_16,
TMC2240_MRES_8,
TMC2240_MRES_4,
TMC2240_MRES_2,
TMC2240_MRES_1, // FULL STEP
} tmc2240_mres_e;
// 0x00 GCONF
typedef union
{
uint32_t data;
struct
{
uint32_t reserved1 : 1;
/**
*
* 0x02^20
* 0x12^18
*/
uint32_t fast_standstill : 1;
/**
* StealthChop2模式
* 0x0StealthChop2
* 0x1StealthChop2电压PWM模式使能()
lHOLD =
*/
uint32_t en_pwm_mode : 1;
/**
* StealthChop2的步进输入筛选
* 0x0StealthChop2
* 0x1StealthChop2电压PWM模式使能
()
lHOLD=
*/
uint32_t multistep_filt : 1;
/**
* /
* 0x0
* 0x1
*/
uint32_t shaft : 1;
uint32_t diag0_error : 1;
uint32_t diag0_otpw : 1;
uint32_t diag0_stall : 1;
uint32_t diag1_stall : 1;
uint32_t diag1_index : 1;
uint32_t diag1_onstate : 1;
uint32_t reserved2 : 1;
uint32_t diag0_pushpull : 1;
uint32_t diag1_pushpull : 1;
uint32_t small_hysteresis : 1;
/**
*
* 0x0
* 0x1ENCA停止定序器
(
)
*/
uint32_t stop_enable : 1;
/**
* motpr相电流控制
* 0x0
* 0x1线
(0x2D)线A
(8..0)线B电流(24..16)
lHOLD设置进行定标
StealthChop2电流调节
StealthChop2电流调节仅适用于
*/
uint32_t direct_mode : 1;
} bits;
} gconf_u;
// 0x01 GSTAT
typedef union
{
uint32_t data;
struct
{
uint32_t reset : 1;
uint32_t drv_err : 1;
uint32_t uv_cp : 1;
uint32_t register_reset : 1;
uint32_t vm_uvlo : 1;
} bits;
} gstat_u;
// 0x0A DRVCONF
typedef union
{
uint32_t data;
struct
{
uint32_t current_range : 2; // 0-1
uint32_t reserved1 : 2; // 2-3
uint32_t slope_control : 2; // 4-5
} bits;
} drvconf_u;
// 0x0B GLOBAL_SCALER
typedef union
{
uint32_t data;
struct
{
uint32_t global_scale : 8; // 0-7
} bits;
} global_scaler_u;
// 0x10 IHOLD_IRUN
typedef union
{
uint32_t data;
struct
{
uint32_t ihold : 5; // 0-4
uint32_t reserved1 : 3; // 5-7
uint32_t irun : 5; // 8-12
uint32_t reserved2 : 3; // 13-15
uint32_t iholddelay : 4; // 16-19
uint32_t reserved3 : 4; // 20-23
uint32_t irundelay : 4; // 24-27
} bits;
} ihold_irun_u;
// 0x6C CHOPCONF
typedef union
{
uint32_t data;
struct
{
uint32_t toff : 4; // 0-3
uint32_t hstrt : 3; // 4-6
uint32_t hend : 4; // 7-10
uint32_t fd3 : 1; // 11
uint32_t disfdcc : 1; // 12
uint32_t reserved1 : 1;
uint32_t chm : 1; // 14
uint32_t tbl : 2; // 15-16
uint32_t reserved2 : 1;
uint32_t vhighfs : 1; // 18
uint32_t vhighchm : 1; // 19
uint32_t tpfd : 4; // 20-23
uint32_t mres : 4; // 24-27
uint32_t intpol : 1; // 28
uint32_t dedge : 1; // 29
uint32_t diss2g : 1; // 30
uint32_t diss2vs : 1; // 31
} bits;
} chopconf_u;
// 0x70 PWMCONF
typedef union
{
uint32_t data;
struct
{
/**
* PWM 0-255CS_ACTUAL=31 =30 使 PWM_OFS
* PWM_OFS pwm_autoscale 0 pwm_autoscale 1
* StealthChop2 PWM_OFS = 0 使
* PWM_OFS > 0 PWM
* IHOLD_IRUN
*/
uint32_t pwm_ofs : 8;
/**
* PWM PWM_GRAD x 256 / TSTEP PWM_OFS 使 PWM_GRAD
* PWM_GRAD pwm_autoscale 0 pwm_autoscale 1
* StealthChop2 PWM_GRAD_AUTO
*/
uint32_t pwm_grad : 8;
uint32_t pwm_freq : 2;
uint32_t pwm_autoscale : 1;
uint32_t pwm_autograd : 1;
} bits;
} pwmconf_u;
typedef struct
{
gconf_u gconf; // 0x00 GCONF
gstat_u gstat; // 0x01 GSTAT
drvconf_u drvconf; // 0x0A DRVCONF
global_scaler_u global_scaler; // 0x0B GLOBAL_SCALER
ihold_irun_u ihold_irun; // 0x10 IHOLD_IRUN
chopconf_u chopconf; // 0x6C CHOPCONF
pwmconf_u pwmconf; // 0x70 PWMCONF
} tmc2240_config_t;
typedef struct
{
gpio_t *en; ///< EN_PIN
gpio_t *dir; ///< DIR_PIN
TIM_TypeDef *timer;
uint32_t time_ch;
spi_t *spi;
tmc2240_config_t config;
tmc2240_config_t read_config;
uint32_t step;
__IO uint32_t step_count;
// PRIVATE
struct
{
uint32_t sysclk; // 系统时钟
uint32_t psc; // 预分频系数
uint16_t arr; // 自动重装值 auto reload value
uint32_t freq; // 频率
} default_tm;
struct
{
BOOL enable; // 使能
tmc2240_direction_e direction; // 方向
float32 percent; // 占空比
uint16_t arr; // 自动重装值(改变速度)
uint32_t freq; // 频率
} params;
struct
{
float32 step_angle; // 步进角度
uint16_t circle_pulse; // 一圈脉冲数
__IO int32_t add_pulse_count; /* 脉冲个数累计 */
__IO uint32_t pulse_count; /* 脉冲个数记录 */
__IO uint32_t step_angle_count; /* 步距个数 */
} motor;
struct
{
uint16_t tmc2240_adc_temp; // 温度ADC值
float32 tmc2240_temperature; // 温度
} data;
} tmc2240_t;
void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios);
tmc2240_t *tmc2240_get(tmc2240_index_e index);
void tmc2240_motor_set_angle(tmc2240_index_e index, int32_t angle);
void tmc2240_motor_update(tmc2240_index_e index);
void tmc2240_test(tmc2240_index_e index);
void tmc2240_config_read(tmc2240_index_e index);
#endif // __TMC2240_H

View File

@ -0,0 +1,229 @@
#include "bootload.h"
#include "ymodem.h"
#include "sys.h"
#include "delay.h"
#include "cmac.h"
#define AES_CMAC_DIGEST_LENGTH 16
typedef void (*fnc_ptr)(void); // 用于跳转到应用程序的函数指针
static bootload_transmit_callback transmit_callback = NULL;
static bootload_end_callback end_callback = NULL;
uint8_t upgrade_key[] =
{
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥
static AES_CMAC_CTX upgrade_ctx; /**< AES CMAC context for upgrade process */
static uint8_t pre_upgrade_mic[AES_CMAC_DIGEST_LENGTH]; /**< MIC (Message Integrity Code) before upgrade */
static uint8_t upgrade_mic[AES_CMAC_DIGEST_LENGTH]; /**< MIC (Message Integrity Code) after upgrade */
static uint32_t flashdestination = BOOTLOAD_APP_BACKUP_ADDR_START; /**< Flash destination address for bootloading */
static uint32_t upgrade_size = 0; /**< Size of the upgrade */
static uint8_t read_cache[LL_FLASH_PAGE_SIZE]; /**< Read cache for flash pages */
static uint8_t data_src_from; /**< Data source for bootloading */
/**
* @brief Perform bootload inspection.
*
* This function calls the `rym_process()` function to perform bootload inspection.
*/
void bootload_inspection(void)
{
rym_process();
}
/**
* @brief Checks if the bootload timeout has occurred.
*
* This function calls the `rym_timeout()` function to determine if the bootload timeout has occurred.
*
* @return TRUE if the bootload timeout has occurred, FALSE otherwise.
*/
BOOL bootload_timeout(void)
{
return rym_timeout();
}
/**
* @brief Handles the beginning of the bootloading process.
*
* This function is responsible for handling the initial steps of the bootloading process.
* It checks if the length of the data to be bootloaded is greater than the end address of the application area.
* If the length is greater, it returns an error code indicating that the bootloading cannot proceed.
* Otherwise, it sets the flash destination address to the start address of the backup area,
* erases the bank of the flash memory where the application is stored,
* and initializes the AES-CMAC context for the upgrade process.
*
* @param p Pointer to the data to be bootloaded.
* @param len Length of the data to be bootloaded.
* @return Error code indicating the result of the operation.
* - RYM_CODE_CAN: If the length of the data is greater than the end address of the application area.
* - RYM_CODE_NONE: If the operation is successful.
*/
static rym_code_e on_begin(uint8_t *p, uint32_t len)
{
if (len > BOOTLOAD_APP_END_ADDRESS)
{
return RYM_CODE_CAN;
}
flashdestination = BOOTLOAD_APP_BACKUP_ADDR_START;
LL_FLASH_Unlock(FLASH);
LL_FLASH_EraseBank(LL_FLASH_BANK2);
LL_FLASH_Lock(FLASH);
AES_CMAC_Init(&upgrade_ctx);
AES_CMAC_SetKey(&upgrade_ctx, upgrade_key);
return RYM_CODE_NONE;
}
/**
* @brief Handles the received data and programs it into the flash memory.
*
* This function unlocks the flash memory, programs the received data into the specified flash destination,
* locks the flash memory again, updates the AES-CMAC context with the received data, and increments the flash destination.
*
* @param p Pointer to the data buffer.
* @param len Length of the data buffer.
* @return The result code indicating the success or failure of the operation.
*/
static rym_code_e on_data(uint8_t *p, uint32_t len)
{
LL_FLASH_Unlock(FLASH);
LL_FLASH_Program(flashdestination, p, len);
LL_FLASH_Lock(FLASH);
AES_CMAC_Update(&upgrade_ctx, p, len);
flashdestination += len;
return RYM_CODE_NONE;
}
/**
* @brief Calculate the MIC (Message Integrity Code) for the firmware upgrade.
*
* This function calculates the MIC using AES-CMAC algorithm for the firmware upgrade data.
* It reads the firmware data from flash memory in pages and updates the MIC calculation.
* Finally, it compares the calculated MIC with the pre-upgrade MIC to determine the success of the upgrade.
*
* @param p Pointer to the firmware data.
* @param len Length of the firmware data.
* @return The result code indicating the success or failure of the upgrade process.
*/
static rym_code_e on_end(uint8_t *p, uint32_t len)
{
AES_CMAC_Final(pre_upgrade_mic, &upgrade_ctx);
upgrade_size = flashdestination - BOOTLOAD_APP_BACKUP_ADDR_START;
AES_CMAC_Init(&upgrade_ctx);
AES_CMAC_SetKey(&upgrade_ctx, upgrade_key);
uint32_t start = BOOTLOAD_APP_BACKUP_ADDR_START;
uint16_t num = (upgrade_size / LL_FLASH_PAGE_SIZE);
uint16_t remain = (upgrade_size % LL_FLASH_PAGE_SIZE);
// STM32L476RG的flash页大小为2K,先读取整数页,再读取余数
for (uint16_t i = 0; i < num; i++)
{
LL_FLASH_Read((start + i * (LL_FLASH_PAGE_SIZE)), read_cache, LL_FLASH_PAGE_SIZE);
AES_CMAC_Update(&upgrade_ctx, read_cache, LL_FLASH_PAGE_SIZE);
}
if (remain)
{
osel_memset(read_cache, 0, LL_FLASH_PAGE_SIZE);
LL_FLASH_Read((start + num * (LL_FLASH_PAGE_SIZE)), read_cache, remain);
AES_CMAC_Update(&upgrade_ctx, read_cache, remain);
}
AES_CMAC_Final(upgrade_mic, &upgrade_ctx);
// 比较mic相同可以写入标志位告知应用程序升级成功
if (osel_memcmp(upgrade_mic, pre_upgrade_mic, AES_CMAC_DIGEST_LENGTH) == 0)
{
end_callback(TRUE);
}
else
{
end_callback(FALSE);
}
return RYM_CODE_NONE;
}
/**
* @brief Handles the transmission of data.
*
* This function calls the transmit_callback function to transmit the data from the specified source.
*
* @param p Pointer to the data buffer.
* @param len Length of the data buffer.
* @return The return code indicating the status of the transmission.
*/
static rym_code_e on_transmit(uint8_t *p, uint32_t len)
{
transmit_callback(data_src_from, p, (uint16_t)len);
return RYM_CODE_NONE;
}
/**
* @brief Initializes the bootload module.
*
* This function initializes the bootload module by setting the transmit callback
* and configuring the RYM module. It asserts if the initialization fails.
*
* @param transmit The transmit callback function.
*/
void bootload_init(bootload_transmit_callback transmit, bootload_end_callback end)
{
BOOL res = FALSE;
transmit_callback = transmit;
end_callback = end;
res = rym_init();
DBG_ASSERT(res == TRUE __DBG_LINE);
res = rym_config(on_begin, on_data, on_end, on_transmit, BOOTLOAD_TIMEOUT);
DBG_ASSERT(res == TRUE __DBG_LINE);
}
/**
* @brief Sets the data source index for bootload transmission.
*
* This function sets the data source index for bootload transmission. The data source index
* determines the starting point from which the data will be transmitted.
*
* @param to_index The index of the data source.
*/
void bootload_transmit_from(const uint8_t to_index)
{
data_src_from = to_index;
}
/**
* @brief Jump to the specified address and execute the bootloader.
*
* @param address The entry address of the bootloader.
*/
void bootload_jump(uint32_t address)
{
// Get the entry address of the application program
fnc_ptr jump_to_bootload;
jump_to_bootload = (fnc_ptr)(*(__IO uint32_t *)(address + 4));
// Disable RCC
RCC->APB1ENR1 = 0;
RCC->APB1ENR2 = 0;
RCC->APB2ENR = 0;
RCC->AHB1ENR = 0;
RCC->AHB2ENR = 0;
RCC->AHB3ENR = 0;
// Disable SysTick
SysTick->CTRL = 0;
// 清空SysTick
SysTick->LOAD = 0;
// 清空SysTick
SysTick->VAL = 0;
// 设置向量表偏移地址
SCB->VTOR = address;
// 设置堆栈指针
sys_msr_msp(*(__IO uint32_t *)address);
// 跳转
jump_to_bootload();
}

View File

@ -0,0 +1,102 @@
#ifndef __BOOTLOAD_H
#define __BOOTLOAD_H
#include "lib.h"
#include "flash.h"
#include "flow.h"
#define BOOTLOAD_SET_FLAG 0xbb01
#define BOOTLOAD_UNSET_FLAG 0xbb02
/**
* @brief Defines the start address of the application in flash memory.
*/
#define BOOTLOAD_APP_START_ADDRESS ((uint32_t)0x08000000u)
/**
* @brief Defines the end address of the application in flash memory.
*/
#define BOOTLOAD_APP_END_ADDRESS (BOOTLOAD_APP_START_ADDRESS + 220 * LL_FLASH_PAGE_SIZE) // 220*2048 = 450560
/**
* @brief Defines the start address of the bootloader in flash memory.
*/
#define BOOTLOAD_START_ADDRESS BOOTLOAD_APP_END_ADDRESS
/**
* @brief Defines the start address of the backup area for the application in flash memory.
*/
#define BOOTLOAD_APP_BACKUP_ADDR_START (BOOTLOAD_APP_START_ADDRESS + 256 * LL_FLASH_PAGE_SIZE)
/**
* @brief Defines the timeout for the bootloading process.
*
* This macro defines the timeout for the bootloading process, in seconds. The default value is 10 seconds.
*/
#define BOOTLOAD_TIMEOUT 10
/**
* @brief A function pointer type for a bootload transmit callback function.
*
* This function pointer type is used for a bootload transmit callback function, which is
* called when data needs to be transmitted to the bootloader. The function is passed the
* source of the data (the index of the data packet), a pointer to the data buffer, and the
* length of the data buffer.
*
* @param data_src The index of the data packet that is being transmitted.
* @param buf A pointer to the data buffer.
* @param len The length of the data buffer.
*/
typedef void (*bootload_transmit_callback)(const uint8_t data_src, const uint8_t *buf, const uint16_t len);
/**
* @brief A function pointer type for a bootload end callback function.
*
* This function pointer type is used for a bootload end callback function, which is called
* when the bootloading process is complete. The function takes no parameters and returns no
* value.
*/
typedef void (*bootload_end_callback)(BOOL);
/**
* @brief initializes the bootloader
*
* This function initializes the bootloader, including setting up the communication
* with the host and configuring the flash memory for bootloading.
*
* @param transmit a pointer to the function that will be called to transmit data to the host
*/
void bootload_init(bootload_transmit_callback transmit, bootload_end_callback end);
/**
* @brief Transmits data from the specified index.
*
* This function transmits data from the specified index to the bootloader.
*
* @param to_index The index from which the data should be transmitted.
*/
void bootload_transmit_from(const uint8_t to_index);
/**
* @brief Jumps to the specified address.
*
* This function jumps to the specified address, which is typically the start address of the bootloader.
*
* @param address The address to jump to.
*/
void bootload_jump(uint32_t address);
/**
* @brief Performs inspection of the bootloader.
*
* This function performs inspection of the bootloader, such as checking the version or integrity of the bootloader.
*/
void bootload_inspection(void);
/**
* @brief Checks if a timeout has occurred.
*
* This function checks if a timeout has occurred during the bootloading process.
*
* @return TRUE if a timeout has occurred, FALSE otherwise.
*/
BOOL bootload_timeout(void);
#endif // __BOOTLOAD_H

View File

@ -0,0 +1,50 @@
# BOOTLOADER介绍
STM32的BOOTLOADER是在芯片复位或从停机模式唤醒时执行的一段小程序它负责将用户代码加载到内存中并启动它。STM32F1、F4、H7等不同系列的MCU可能会有不同的BOOTLOADER程序。
BOOTLOADER通常用于以下几种情况
1. 在应用程序无法正常启动时,提供一个后备启动方式。
2. 在系统需要进行固件更新时可以先通过BOOTLOADER加载新的用户代码。
3. 用于在线调试或调试无法通过JTAG/SWD接口访问时可以通过BOOTLOADER加载调试工具。
BOOTLOADER的设计和实现通常依赖于芯片的内部结构和特性以及用户代码存储的介质如内部FLASH外部SPI FLASH等
一个简单的BOOTLOADER示例可能包括以下步骤
1. 复位后芯片开始执行内部的BOOTLOADER程序。
2. 通过某种通信接口如USARTI2CSPI接收新的用户程序代码。
3. 将接收到的代码写入用户代码存储区如内部FLASH
4. 设置启动引脚或者配置BOOT引导模式寄存器选择启动用户代码。
5. 重启芯片这次不再执行BOOTLOADER而是加载并运行新的用户程序代码。
注意实际的BOOTLOADER实现可能会更加复杂包括错误检查和处理、加密解密、固件完整性校验等安全措施。
# bootload.c 文件说明
`bootload.c`是一个实现引导加载程序bootloader功能的源代码文件。引导加载程序是一段在系统启动时运行的代码负责初始化硬件设备、建立内存空间映射图然后加载操作系统内核并将控制权转交给它。
以下是 `bootload.c`文件中可能包含的主要函数和其功能:
1. **系统启动函数** :这个函数是引导加载程序的入口点,它负责启动整个引导加载过程。
2. **硬件初始化函数** 这些函数负责初始化系统的硬件设备包括CPU、内存、IO设备等。
3. **内存映射设置函数** :这些函数负责建立内存空间的映射图,包括物理内存、虚拟内存的映射关系。
4. **操作系统内核加载函数** :这些函数负责加载操作系统内核,包括从存储设备读取内核镜像,加载到内存中,然后跳转到内核的入口点。
5. **错误处理函数** :这些函数负责处理在引导加载过程中可能出现的各种错误,包括硬件错误、内核加载错误等。
# ymodem.c 文件说明
`ymodem.c`是一个实现YMODEM协议的源代码文件。YMODEM是一种用于文件传输的协议它在XMODEM协议的基础上增加了一些新的特性例如支持更大的文件和文件名传输。
以下是 `ymodem.c`文件中可能包含的主要函数和其功能:
1. **初始化和结束传输的函数** :这些函数负责设置传输的开始和结束,包括打开和关闭必要的硬件接口,设置传输参数等。
2. **发送和接收数据包的函数** 这些函数负责实际的数据传输包括将数据打包成YMODEM格式的数据包通过硬件接口发送和接收数据包处理数据包的确认和重传等。
3. **CRC校验的函数** 这些函数负责计算和检查数据包的CRC循环冗余校验以确保数据的完整性。
4. **处理错误和重试的函数** 这些函数负责处理在传输过程中可能出现的各种错误包括数据包丢失、CRC校验失败等并在必要时进行重试。
此外,`ymodem.c`文件还可能包含一些辅助函数,用于处理如超时、缓冲区管理等问题。

View File

@ -0,0 +1,79 @@
#include "unity.h"
#include "ymodem.c"
void setUp(void)
{
// 这里可以进行每个测试用例开始前的设置
}
void tearDown(void)
{
// 这里可以进行每个测试用例结束后的清理
}
void test_CRC16(void)
{
unsigned char data[] = {0x01, 0x02, 0x03, 0x04, 0x05};
TEST_ASSERT_EQUAL_HEX16(EXPECTED_CRC_VALUE, CRC16(data, sizeof(data)));
}
void test_IS_CAP_LETTER(void)
{
TEST_ASSERT_TRUE(IS_CAP_LETTER('A'));
TEST_ASSERT_FALSE(IS_CAP_LETTER('a'));
}
void test_IS_LC_LETTER(void)
{
TEST_ASSERT_TRUE(IS_LC_LETTER('a'));
TEST_ASSERT_FALSE(IS_LC_LETTER('A'));
}
void test_IS_09(void)
{
TEST_ASSERT_TRUE(IS_09('0'));
TEST_ASSERT_FALSE(IS_09('A'));
}
void test_ISVALIDHEX(void)
{
TEST_ASSERT_TRUE(ISVALIDHEX('A'));
TEST_ASSERT_TRUE(ISVALIDHEX('a'));
TEST_ASSERT_TRUE(ISVALIDHEX('0'));
TEST_ASSERT_FALSE(ISVALIDHEX('G'));
}
void test_ISVALIDDEC(void)
{
TEST_ASSERT_TRUE(ISVALIDDEC('0'));
TEST_ASSERT_FALSE(ISVALIDDEC('A'));
}
void test_CONVERTDEC(void)
{
TEST_ASSERT_EQUAL_HEX8(0, CONVERTDEC('0'));
TEST_ASSERT_EQUAL_HEX8(9, CONVERTDEC('9'));
}
void test_CONVERTHEX(void)
{
TEST_ASSERT_EQUAL_HEX8(10, CONVERTHEX('A'));
TEST_ASSERT_EQUAL_HEX8(10, CONVERTHEX('a'));
TEST_ASSERT_EQUAL_HEX8(0, CONVERTHEX('0'));
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_CRC16);
RUN_TEST(test_IS_CAP_LETTER);
RUN_TEST(test_IS_LC_LETTER);
RUN_TEST(test_IS_09);
RUN_TEST(test_ISVALIDHEX);
RUN_TEST(test_ISVALIDDEC);
RUN_TEST(test_CONVERTDEC);
RUN_TEST(test_CONVERTHEX);
return UNITY_END();
}

View File

@ -0,0 +1,633 @@
/**
* @file ymodem.c
* @author xxx
* @date 2024-02-18 19:32:40
* @brief
* YMODEM协议的核心功能
* 使使CRC校验是用来确保数据传输的完整性和准确性
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#include "ymodem.h"
#include "sys.h"
#include "delay.h"
#include "flow.h"
sqqueue_ctrl_t rym_sqqueue; // 一个接收队列控制结构体,用于管理接收到的数据。
static uint32_t tm_sec = 0; // 握手超时时间,用于握手阶段的超时计时
static enum rym_stage stage = RYM_STAGE_NONE; // 当前的阶段
static int32_t rym_tm_sec = 0; // YMODEM超时计时器
static uint8_t aPacketData[_RYM_PKG_SZ]; // 数据包缓冲区
// 回调函数,用于处理不同阶段的事件
static rym_callback rym_on_begin = NULL;
static rym_callback rym_on_data = NULL;
static rym_callback rym_on_end = NULL;
static rym_callback rym_transmit = NULL;
static struct flow handshake_fw; // 握手流程
static struct flow trans_fw; // 传输流程
static struct flow finsh_fw; // 结束流程
static struct flow_sem msg_sem; // 消息信号量,用于同步
static const uint16_t ccitt_table[256] =
{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};
uint16_t CRC16(unsigned char *q, int len)
{
uint16_t crc = 0;
while (len-- > 0)
crc = (crc << 8) ^ ccitt_table[((crc >> 8) ^ *q++) & 0xff];
return crc;
}
/* Exported macro ------------------------------------------------------------*/
#define IS_CAP_LETTER(c) (((c) >= 'A') && ((c) <= 'F'))
#define IS_LC_LETTER(c) (((c) >= 'a') && ((c) <= 'f'))
#define IS_09(c) (((c) >= '0') && ((c) <= '9'))
#define ISVALIDHEX(c) (IS_CAP_LETTER(c) || IS_LC_LETTER(c) || IS_09(c))
#define ISVALIDDEC(c) IS_09(c)
#define CONVERTDEC(c) (c - '0')
#define CONVERTHEX_ALPHA(c) (IS_CAP_LETTER(c) ? ((c) - 'A' + 10) : ((c) - 'a' + 10))
#define CONVERTHEX(c) (IS_09(c) ? ((c) - '0') : CONVERTHEX_ALPHA(c))
/**
* @brief Convert a string to an integer
* @param p_inputstr: The string to be converted
* @param p_intnum: The integer value
* @retval 1: Correct
* 0: Error
*/
uint32_t Str2Int(uint8_t *p_inputstr, uint32_t *p_intnum)
{
uint32_t i = 0, res = 0;
uint32_t val = 0;
if ((p_inputstr[0] == '0') && ((p_inputstr[1] == 'x') || (p_inputstr[1] == 'X')))
{
i = 2;
while ((i < 11) && (p_inputstr[i] != '\0'))
{
if (ISVALIDHEX(p_inputstr[i]))
{
val = (val << 4) + CONVERTHEX(p_inputstr[i]);
}
else
{
/* Return 0, Invalid input */
res = 0;
break;
}
i++;
}
/* valid result */
if (p_inputstr[i] == '\0')
{
*p_intnum = val;
res = 1;
}
}
else /* max 10-digit decimal input */
{
while ((i < 11) && (res != 1))
{
if (p_inputstr[i] == '\0')
{
*p_intnum = val;
/* return 1 */
res = 1;
}
else if (((p_inputstr[i] == 'k') || (p_inputstr[i] == 'K')) && (i > 0))
{
val = val << 10;
*p_intnum = val;
res = 1;
}
else if (((p_inputstr[i] == 'm') || (p_inputstr[i] == 'M')) && (i > 0))
{
val = val << 20;
*p_intnum = val;
res = 1;
}
else if (ISVALIDDEC(p_inputstr[i]))
{
val = val * 10 + CONVERTDEC(p_inputstr[i]);
}
else
{
/* return 0, Invalid input */
res = 0;
break;
}
i++;
}
}
return res;
}
/**
* @brief Read data from the circular queue.
*
* This function reads the specified length of data from the circular queue and stores it in the given buffer.
* If the length of data in the queue is greater than or equal to the specified length, it directly reads the specified length of data.
* If the length of data in the queue is less than the specified length, it reads all the data in the queue.
*
* @param buffer The buffer to store the data.
* @param len The length of data to read.
*
* @return The actual length of data read.
*/
static uint16_t rym_sqqueue_read(void *buffer, uint16_t len)
{
uint16_t i = 0;
uint8_t *buf = buffer;
if (rym_sqqueue.get_len(&rym_sqqueue) >= len)
{
for (i = 0; i < len; i++)
{
buf[i] = *((uint8_t *)rym_sqqueue.del(&rym_sqqueue));
}
}
else
{
while ((rym_sqqueue.get_len(&rym_sqqueue) != 0) && (i < len))
{
buf[i] = *((uint8_t *)rym_sqqueue.del(&rym_sqqueue));
}
}
return i;
}
/**
* @brief Initializes the parameters for the YMODEM process.
*
* This function sets the stage variable to the specified stage and initializes the rym_tm_sec variable with the value of tm_sec.
* If the stage is RYM_STAGE_ESTABLISHING, it also clears the rym_sqqueue.
*
* @param st The stage of the YMODEM process.
*/
static void rym_process_params_init(enum rym_stage st)
{
stage = st;
rym_tm_sec = tm_sec;
if (st == RYM_STAGE_ESTABLISHING)
{
rym_sqqueue.clear_sqq(&rym_sqqueue);
}
}
/**
* @brief Performs the handshake process for the YMODEM protocol.
*
* This function reads packets from the input queue and performs the necessary checks and actions
* to establish a connection and initiate file transfer using the YMODEM protocol.
*
* @param fl The flow structure containing the necessary variables and synchronization mechanisms.
* @return The result of the handshake process.
* - 0: Handshake process completed successfully.
* - Non-zero: Handshake process failed.
*/
static uint8_t rym_do_handshake_process(struct flow *fl)
{
uint8_t index = 0;
FL_HEAD(fl);
static uint16_t rym_recv_len = 0;
static uint16_t recv_crc, cal_crc;
static uint8_t *file_ptr = NULL;
static uint8_t file_name[FILE_NAME_LENGTH];
static uint8_t file_size[FILE_SIZE_LENGTH];
static uint32_t filesize;
for (;;)
{
FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC);
if (FL_SEM_IS_RELEASE(fl, &msg_sem))
{
rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1);
if (rym_recv_len == 1)
{
if (aPacketData[PACKET_START_INDEX] != RYM_CODE_SOH && aPacketData[PACKET_START_INDEX] != RYM_CODE_STX)
continue;
/* SOH/STX + seq + payload + crc */
rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_NUMBER_INDEX],
_RYM_PKG_SZ - 1);
if (rym_recv_len != (_RYM_PKG_SZ - 1))
continue;
/* sanity check */
if ((aPacketData[PACKET_NUMBER_INDEX] != 0) || (aPacketData[PACKET_CNUMBER_INDEX] != 0xFF))
continue;
recv_crc = (uint16_t)(*(&aPacketData[PACKET_START_INDEX] + _RYM_PKG_SZ - 2) << 8) |
*(&aPacketData[PACKET_START_INDEX] + _RYM_PKG_SZ - 1);
cal_crc = CRC16(aPacketData + PACKET_DATA_INDEX, _RYM_PKG_SZ - 5);
if (recv_crc != cal_crc)
continue;
if (rym_on_begin != NULL)
{
file_ptr = aPacketData + PACKET_DATA_INDEX;
while ((*file_ptr != 0) && (index < FILE_NAME_LENGTH))
{
file_name[index++] = *file_ptr++;
}
file_name[index++] = '\0';
index = 0;
file_ptr++;
while ((*file_ptr != ' ') && (index < FILE_SIZE_LENGTH))
{
file_size[index++] = *file_ptr++;
}
file_size[index++] = '\0';
Str2Int(file_size, &filesize);
if (RYM_CODE_NONE != rym_on_begin(file_name, filesize))
{
for (uint8_t i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++)
{
aPacketData[0] = RYM_CODE_CAN;
rym_transmit(aPacketData, 1);
}
}
else
{
aPacketData[0] = RYM_CODE_ACK;
rym_transmit(aPacketData, 1);
FL_LOCK_DELAY(fl, FL_CLOCK_100MSEC * 5);
aPacketData[0] = RYM_CODE_C;
rym_transmit(aPacketData, 1);
rym_process_params_init(RYM_STAGE_TRANSMITTING);
FL_LOCK_DELAY(fl, FL_CLOCK_SEC);
}
}
}
}
else
{
aPacketData[0] = RYM_CODE_C;
rym_transmit(aPacketData, 1);
rym_tm_sec--;
}
}
FL_TAIL(fl);
}
/**
* @brief Transfers data using the YMODEM protocol.
*
* This function is responsible for transferring data using the YMODEM protocol.
* It receives the size of the data to be transferred and a pointer to the code
* that will be returned. It performs various checks on the received data and
* calculates the CRC to ensure data integrity. If all checks pass, it sets the
* code to RYM_CODE_ACK and returns 0. Otherwise, it returns an error code.
*
* @param data_size The size of the data to be transferred.
* @param code Pointer to the code that will be returned.
* @return 0 if successful, otherwise an error code.
*/
static int8_t rym_tran_data(uint16_t data_size, uint8_t *code)
{
DBG_ASSERT(NULL != code __DBG_LINE);
uint16_t recv_len = 0;
uint16_t recv_crc, cal_crc;
const uint16_t tran_size = PACKET_HEADER_SIZE - 1 + data_size + PACKET_TRAILER_SIZE;
/* seq + data + crc */
recv_len = rym_sqqueue_read(&aPacketData[PACKET_NUMBER_INDEX],
tran_size);
if (recv_len != tran_size)
return -RYM_ERR_DSZ;
/* sanity check */
if ((aPacketData[PACKET_NUMBER_INDEX] + aPacketData[PACKET_CNUMBER_INDEX]) != 0xFF)
return -RYM_ERR_SEQ;
/* As we are sending C continuously, there is a chance that the
* sender(remote) receive an C after sending the first handshake package.
* So the sender will interpret it as NAK and re-send the package. So we
* just ignore it and proceed. */
if (stage == RYM_STAGE_ESTABLISHED && aPacketData[PACKET_NUMBER_INDEX] == RYM_CODE_NONE)
{
*code = RYM_CODE_NONE;
return 0;
}
stage = RYM_STAGE_TRANSMITTING;
recv_crc = (uint16_t)(*(&aPacketData[PACKET_START_INDEX] + tran_size - 1) << 8) |
*(&aPacketData[PACKET_START_INDEX] + tran_size);
cal_crc = CRC16(aPacketData + PACKET_DATA_INDEX, data_size);
if (recv_crc != cal_crc)
return -RYM_ERR_CRC;
*code = RYM_CODE_ACK;
return 0;
}
/**
* @brief Performs the YMODEM transmission process.
*
* This function is responsible for handling the YMODEM transmission process.
* It receives packets of data and performs the necessary operations based on the received data.
* It handles timeouts and retransmissions if necessary.
*
* @param fl The flow structure pointer.
* @return The status of the transmission process.
*/
static uint8_t rym_do_trans_process(struct flow *fl)
{
FL_HEAD(fl);
static uint16_t data_size, rym_recv_len;
static uint8_t rym_code;
static uint16_t tran_timeout = 0;
for (;;)
{
FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC);
if (FALSE == FL_SEM_IS_RELEASE(fl, &msg_sem))
{
if (tran_timeout++ >= 5)
{
tran_timeout = 0;
rym_process_params_init(RYM_STAGE_ESTABLISHING);
FL_LOCK_DELAY(fl, FL_CLOCK_SEC);
}
}
else
{
tran_timeout = 0;
rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1);
if (rym_recv_len == 1)
{
if (aPacketData[PACKET_START_INDEX] == RYM_CODE_SOH)
data_size = PACKET_SIZE;
else if (aPacketData[PACKET_START_INDEX] == RYM_CODE_STX)
data_size = PACKET_1K_SIZE;
else if (aPacketData[PACKET_START_INDEX] == RYM_CODE_EOT)
{
aPacketData[0] = RYM_CODE_NAK;
rym_transmit(aPacketData, 1);
rym_process_params_init(RYM_STAGE_FINISHING);
FL_LOCK_DELAY(fl, FL_CLOCK_SEC);
continue;
}
else
{
rym_process_params_init(RYM_STAGE_ESTABLISHING);
FL_LOCK_DELAY(fl, FL_CLOCK_SEC);
continue;
}
if (rym_tran_data(data_size, &rym_code) == 0)
{
if (rym_on_data != NULL)
rym_on_data(aPacketData + PACKET_DATA_INDEX, data_size);
if (rym_code == RYM_CODE_CAN)
{
for (uint8_t i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++)
{
aPacketData[0] = RYM_CODE_CAN;
rym_transmit(aPacketData, 1);
}
}
else if (rym_code == RYM_CODE_ACK)
{
aPacketData[0] = RYM_CODE_ACK;
rym_transmit(aPacketData, 1);
}
}
}
}
}
FL_TAIL(fl);
}
/**
* @brief Performs the finishing process for the YMODEM protocol.
*
* This function is responsible for handling the final stage of the YMODEM protocol,
* where the receiver receives the end of transmission (EOT) signal from the sender.
* It verifies the received EOT signal, sends an acknowledgement (ACK) signal back to
* the sender, and waits for the start of header (SOH) signal to receive the final
* packet containing the payload and checksum. If the received packet is valid, it
* calculates the checksum and compares it with the received checksum. If they match,
* it sets the stage to RYM_STAGE_FINISHED and invokes the callback function if
* available. This function also handles timeout conditions and reinitializes the
* protocol parameters if necessary.
*
* @param fl The flow structure pointer.
* @return The result of the finishing process.
*/
static uint8_t rym_do_finish_process(struct flow *fl)
{
FL_HEAD(fl);
static uint16_t rym_recv_len;
static uint16_t recv_crc, cal_crc;
static uint16_t tran_timeout = 0;
for (;;)
{
FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC);
if (FALSE == FL_SEM_IS_RELEASE(fl, &msg_sem))
{
if (tran_timeout++ >= 5)
{
tran_timeout = 0;
rym_process_params_init(RYM_STAGE_ESTABLISHING);
FL_LOCK_DELAY(fl, FL_CLOCK_SEC);
}
}
else
{
tran_timeout = 0;
/* read the length of the packet */
rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1);
if (rym_recv_len == 1)
{
if (aPacketData[PACKET_START_INDEX] != RYM_CODE_EOT)
continue;
/* send an ACK */
aPacketData[0] = RYM_CODE_ACK;
rym_transmit(aPacketData, 1);
FL_LOCK_DELAY(fl, FL_CLOCK_100MSEC * 5);
/* send a C */
aPacketData[0] = RYM_CODE_C;
rym_transmit(aPacketData, 1);
FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC);
if (FALSE == FL_SEM_IS_RELEASE(fl, &msg_sem))
continue;
/* read the length of the packet */
rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1);
if (rym_recv_len == 1)
{
if (aPacketData[PACKET_START_INDEX] != RYM_CODE_SOH)
continue;
/* SOH/STX + seq + payload + crc */
rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_NUMBER_INDEX],
_RYM_SOH_PKG_SZ - 1);
if (rym_recv_len != (_RYM_SOH_PKG_SZ - 1))
continue;
/* sanity check */
if ((aPacketData[PACKET_NUMBER_INDEX] != 0) || (aPacketData[PACKET_CNUMBER_INDEX] != 0xFF))
continue;
recv_crc = (uint16_t)(*(&aPacketData[PACKET_START_INDEX] + _RYM_SOH_PKG_SZ - 2) << 8) |
*(&aPacketData[PACKET_START_INDEX] + _RYM_SOH_PKG_SZ - 1);
cal_crc = CRC16(aPacketData + PACKET_DATA_INDEX, _RYM_SOH_PKG_SZ - 5);
if (recv_crc != cal_crc)
continue;
/* we got a valid packet. invoke the callback if there is one. */
stage = RYM_STAGE_FINISHED;
aPacketData[0] = RYM_CODE_ACK;
rym_transmit(aPacketData, 1);
/* we already got one EOT in the caller. invoke the callback if there is
* one. */
if (rym_on_end)
rym_on_end(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE);
}
}
}
}
FL_TAIL(fl);
}
/**
* @brief Process the Ymodem protocol stages.
*
* This function is responsible for handling the different stages of the Ymodem protocol.
* It performs the necessary actions based on the current stage.
*
* @note The stages include establishing connection, transmitting data, finishing, and others.
*
* @param None
* @return None
*/
void rym_process(void)
{
switch (stage)
{
case RYM_STAGE_ESTABLISHING: // 建立连接 (Establishing connection)
rym_do_handshake_process(&handshake_fw);
break;
case RYM_STAGE_TRANSMITTING: // 传输中 (Transmitting)
rym_do_trans_process(&trans_fw);
break;
case RYM_STAGE_FINISHING: // 结束 (Finishing)
rym_do_finish_process(&finsh_fw);
break;
case RYM_STAGE_NONE:
rym_process_params_init(RYM_STAGE_ESTABLISHING);
break;
case RYM_STAGE_FINISHED:
rym_tm_sec = 0;
break;
default:
// rym_process_params_init(RYM_STAGE_ESTABLISHING);
break;
}
}
/**
* @brief Checks if the YMODEM receive timeout has occurred.
* @return TRUE if the timeout has occurred, FALSE otherwise.
*/
BOOL rym_timeout(void)
{
return rym_tm_sec == 0;
}
/**
* @brief Initializes the YMODEM protocol for receiving files.
*
* This function initializes the necessary data structures and variables
* for receiving files using the YMODEM protocol.
*
* @return TRUE if initialization is successful, FALSE otherwise.
*/
BOOL rym_init(void)
{
sqqueue_ctrl_init(&rym_sqqueue, sizeof(uint8_t), _RYM_PKG_SZ);
FL_INIT(&handshake_fw);
FL_INIT(&trans_fw);
FL_INIT(&finsh_fw);
return TRUE;
}
/**
* @brief Receive data using the Ymodem protocol.
*
* This function is used to receive data transmitted using the Ymodem protocol and store it in the specified buffer.
*
* @param p Pointer to the data storage buffer.
*/
uint16_t rym_receive(void *p, uint16_t size)
{
rym_sqqueue.string_enter(&rym_sqqueue, p, size);
FLOW_SEM_RELEASE(&msg_sem);
return 0;
}
/**
* @brief Configures the YMODEM protocol with the specified callbacks and handshake timeout.
*
* This function sets the callback functions for the YMODEM protocol, which will be called during different stages of the protocol.
* The `on_begin` callback is called when the YMODEM transfer begins.
* The `on_data` callback is called when a data packet is received during the transfer.
* The `on_end` callback is called when the YMODEM transfer ends.
* The `on_transmit` callback is called when a data packet needs to be transmitted during the transfer.
* The `handshake_timeout` parameter specifies the timeout duration for the handshake phase of the YMODEM protocol.
*
* @param on_begin The callback function to be called when the YMODEM transfer begins.
* @param on_data The callback function to be called when a data packet is received during the transfer.
* @param on_end The callback function to be called when the YMODEM transfer ends.
* @param on_transmit The callback function to be called when a data packet needs to be transmitted during the transfer.
* @param handshake_timeout The timeout duration for the handshake phase of the YMODEM protocol.
*
* @return TRUE if the configuration was successful, FALSE otherwise.
*/
BOOL rym_config(rym_callback on_begin, rym_callback on_data,
rym_callback on_end, rym_callback on_transmit,
int handshake_timeout)
{
rym_on_begin = on_begin;
rym_on_data = on_data;
rym_on_end = on_end;
rym_transmit = on_transmit;
tm_sec = handshake_timeout;
return TRUE;
}

View File

@ -0,0 +1,180 @@
/**
* @file ymodem.h
* @author xsh
* @date 2024-02-18 19:32:46
* @brief
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __YMODEM_H__
#define __YMODEM_H__
#include "lib.h"
enum rym_code
{
RYM_CODE_NONE = 0x00,
RYM_CODE_SOH = 0x01, /* start of 128-byte data packet */
RYM_CODE_STX = 0x02, /* start of 1024-byte data packet */
RYM_CODE_EOT = 0x04, /* end of transmission */
RYM_CODE_ACK = 0x06, /* acknowledge */
RYM_CODE_NAK = 0x15, /* negative acknowledge */
RYM_CODE_CAN = 0x18, /* two of these in succession aborts transfer */
RYM_CODE_C = 0x43, /* 'C' == 0x43, request 16-bit CRC */
};
typedef enum rym_code rym_code_e;
/* RYM error code
*
* We use the rt_err_t to return error values. We take use of current error
* codes available in RTT and append ourselves.
*/
/* timeout on handshake */
#define RYM_ERR_TMO 0x70
/* wrong code, wrong SOH, STX etc. */
#define RYM_ERR_CODE 0x71
/* wrong sequence number */
#define RYM_ERR_SEQ 0x72
/* wrong CRC checksum */
#define RYM_ERR_CRC 0x73
/* not enough data received */
#define RYM_ERR_DSZ 0x74
/* the transmission is aborted by user */
#define RYM_ERR_CAN 0x75
/* how many ticks wait for chars between packet. */
#ifndef RYM_WAIT_CHR_TICK
#define RYM_WAIT_CHR_TICK (OSEL_TICK_RATE_HZ * 3)
#endif
/* how many ticks wait for between packet. */
#ifndef RYM_WAIT_PKG_TICK
#define RYM_WAIT_PKG_TICK (OSEL_TICK_RATE_HZ * 3)
#endif
/* how many ticks between two handshake code. */
#ifndef RYM_CHD_INTV_TICK
#define RYM_CHD_INTV_TICK (OSEL_TICK_RATE_HZ * 3)
#endif
/* how many CAN be sent when user active end the session. */
#ifndef RYM_END_SESSION_SEND_CAN_NUM
#define RYM_END_SESSION_SEND_CAN_NUM 0x03
#endif
/* Exported constants --------------------------------------------------------*/
/* Packet structure defines */
#define PACKET_HEADER_SIZE ((uint32_t)3)
#define PACKET_DATA_INDEX ((uint32_t)4)
#define PACKET_START_INDEX ((uint32_t)1)
#define PACKET_NUMBER_INDEX ((uint32_t)2)
#define PACKET_CNUMBER_INDEX ((uint32_t)3)
#define PACKET_TRAILER_SIZE ((uint32_t)2)
#define PACKET_OVERHEAD_SIZE (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1)
#define PACKET_SIZE ((uint32_t)128)
#define PACKET_1K_SIZE ((uint32_t)1024)
#define _RYM_SOH_PKG_SZ (PACKET_SIZE + PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE)
#define _RYM_STX_PKG_SZ (PACKET_1K_SIZE + PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE)
#define _RYM_PKG_SZ _RYM_STX_PKG_SZ // 这里定义的是数据包的大小
/* 因为data是需要写入到flash里面如果不对齐会出现UNALIGNED异常
* /-------- Packet in IAP memory ------------------------------------------\
* | 0 | 1 | 2 | 3 | 4 | ... | n+4 | n+5 | n+6 |
* |------------------------------------------------------------------------|
* | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 |
* \------------------------------------------------------------------------/
* the first byte is left unused for memory alignment reasons */
#define FILE_NAME_LENGTH ((uint32_t)64)
#define FILE_SIZE_LENGTH ((uint32_t)16)
#define NEGATIVE_BYTE ((uint8_t)0xFF)
#define ABORT1 ((uint8_t)0x41) /* 'A' == 0x41, abort by user */
#define ABORT2 ((uint8_t)0x61) /* 'a' == 0x61, abort by user */
#define MAX_ERRORS ((uint32_t)5)
enum rym_stage
{
RYM_STAGE_NONE,
/* set when C is send */
RYM_STAGE_ESTABLISHING,
/* set when we've got the packet 0 and sent ACK and second C */
RYM_STAGE_ESTABLISHED,
/* set when the sender respond to our second C and recviever got a real
* data packet. */
RYM_STAGE_TRANSMITTING,
/* set when the sender send a EOT */
RYM_STAGE_FINISHING,
/* set when transmission is really finished, i.e., after the NAK, C, final
* NULL packet stuff. */
RYM_STAGE_FINISHED,
};
/* when receiving files, the buf will be the data received from ymodem protocol
* and the len is the data size.
*
* TODO:
* When sending files, the len is the buf size in RYM. The callback need to
* fill the buf with data to send. Returning RYM_CODE_EOT will terminate the
* transfer and the buf will be discarded. Any other return values will cause
* the transfer continue.
*/
typedef enum rym_code (*rym_callback)(uint8_t *buf, uint32_t len);
/** recv a file on device dev with ymodem session ctx.
*
* If an error happens, you can get where it is failed from ctx->stage.
*
* @param on_begin The callback will be invoked when the first packet arrived.
* This packet often contain file names and the size of the file, if the sender
* support it. So if you want to save the data to a file, you may need to
* create the file on need. It is the on_begin's responsibility to parse the
* data content. The on_begin can be NULL, in which case the transmission will
* continue without any side-effects.
*
* @param on_data The callback will be invoked on the packets received. The
* callback should save the data to the destination. The return value will be
* sent to the sender and in turn, only RYM_{ACK,CAN} is valid. When on_data is
* NULL, RYM will barely send ACK on every packet and have no side-effects.
*
* @param on_end The callback will be invoked when one transmission is
* finished. The data should be 128 bytes of NULL. You can do some cleaning job
* in this callback such as closing the file. The return value of this callback
* is ignored. As above, this parameter can be NULL if you don't need such
* function.
*
* @param handshake_timeout the timeout when hand shaking. The unit is in
* second.
*/
BOOL rym_config(rym_callback on_begin, rym_callback on_data,
rym_callback on_end, rym_callback on_transmit,
int handshake_timeout);
/**
* @brief Initializes the YMODEM protocol for receiving data.
*
* @return BOOL Returns TRUE if initialization is successful, FALSE otherwise.
*/
BOOL rym_init(void);
/**
* @brief Receives data using the YMODEM protocol.
*
* @param p Pointer to the buffer where the received data will be stored.
* @param size The size of the buffer.
* @return uint16_t The number of bytes received.
*/
uint16_t rym_receive(void *p, uint16_t size);
/**
* @brief Processes the received data using the YMODEM protocol.
*/
void rym_process(void);
/**
* @brief Checks if a timeout has occurred during the YMODEM protocol.
*
* @return BOOL Returns TRUE if a timeout has occurred, FALSE otherwise.
*/
BOOL rym_timeout(void);
#endif

View File

@ -0,0 +1,38 @@
#include "pid_c.h"
/**
* @brief PID控制器参数
* @param {PID_C} *self - PID控制器结构体指针
* @param {float32} kp -
* @param {float32} ki -
* @param {float32} kd -
* @param {float32} out_min -
* @param {float32} out_max -
* @return {*} -
*/
static void _set_ctrl_prm(struct PID_C *self, float32 kp, float32 ki, float32 kd, float32 out_min, float32 out_max)
{
self->pri.kp = kp; /*比例系数*/
self->pri.ki = ki; /*积分系数*/
self->pri.kd = kd; /*微分系数*/
self->pri.deadband = 0.5; /*死区*/
self->pri.maximum = out_max; /*最大输出*/
self->pri.minimum = out_min; /*最小输出*/
self->pri.last_error = 0; /*上一次误差*/
self->pri.prev_error = 0; /*上上次误差*/
}
static float32 _PID(struct PID_C *self, float32 target, float32 feedback)
{
/**
* PID算法
*/
return 0;
}
void pid_c_constructor(struct PID_C *self)
{
self->set_ctrl_prm = _set_ctrl_prm;
self->PID = _PID;
}

View File

@ -0,0 +1,34 @@
#ifndef __PID_C_H__
#define __PID_C_H__
#include "lib.h"
typedef struct PID_C
{
/* 设置PID三个参数 */
void (*set_ctrl_prm)(struct PID_C *self, float32 kp, float32 ki, float32 kd, float32 out_min, float32 out_max);
/* 控制接口 */
float32 (*PID)(struct PID_C *self, float32 target, float32 feedback);
// 自定义参数
/* 实际值与目标值之间的误差 */
float32 err;
/* 输出值 */
float32 out;
/* private */
struct
{
float32 kp; /*比例学习速度*/
float32 ki; /*积分学习速度*/
float32 kd; /*微分学习速度*/
float32 ki_error; /*积分误差*/
float32 last_error; /*前一拍偏差*/
float32 prev_error; /*前两拍偏差*/
float32 deadband; /*死区*/
float32 maximum; /*输出值的上限*/
float32 minimum; /*输出值的下限*/
} pri;
} pid_c_t;
extern void pid_c_constructor(struct PID_C *self);
#endif // __PID_C_H__

View File

@ -0,0 +1,285 @@
#include "pid_g.h"
#include <math.h>
/**
* @brief PID积分及微分控制数据
* @param {PID_G} *self
* @return {*}
*/
static void _restctrl(struct PID_G *self)
{
self->pri.pre_error = 0;
self->pri.sum_iterm = 0;
}
/**
* @brief
* @param {PID_G} *self
* @param {float32} out_min
* @param {float32} out_max
* @return {*}
* @note
*/
static void _set_range(struct PID_G *self, float32 out_min, float32 out_max)
{
self->pri.out_max = out_max;
self->pri.out_min = out_min;
}
/**
* @brief kp
* @param {PID_G} *self
* @param {float32} kp
* @return {*}
* @note
*/
static void _set_kp(struct PID_G *self, float32 kp)
{
self->pri.kp = kp;
}
/**
* @brief ki
* @param {PID_G} *self
* @param {float32} ki
* @return {*}
* @note
*/
static void _set_ki(struct PID_G *self, float32 ki)
{
self->pri.ki = ki;
}
/**
* @brief kd
* @param {PID_G} *self
* @param {float32} kd
* @return {*}
* @note
*/
static void _set_kd(struct PID_G *self, float32 kd)
{
self->pri.kd = kd;
}
/**
* @brief 使
* @param {PID_G} *self
* @param {BOOL} enable
* @return {*}
* @note
*/
static void _set_ki_enable(struct PID_G *self, BOOL enable)
{
self->pri.ki_enable = enable;
}
/**
* @brief 使
* @param {PID_G} *self
* @param {BOOL} enable
* @return {*}
* @note
*/
static void _set_kd_enable(struct PID_G *self, BOOL enable)
{
self->pri.kd_enable = enable;
}
/**
* @brief
* @return {*}
* @note
*/
static void _set_ctrl_prm(struct PID_G *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 out_min, float32 out_max)
{
g_param_t *pri = &self->pri;
osel_memset((uint8_t *)pri, 0, sizeof(pid_g_t));
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
pri->detach = FALSE;
}
static void _update_ctrl_prm(struct PID_G *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 out_min, float32 out_max)
{
g_param_t *pri = &self->pri;
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
pri->detach = FALSE;
}
/**
* @brief 0+PID,PID
* @param {PID_G} *self
* @param {float32} max_err
* @param {BOOL} mode
* @return {*}
*/
static void _set_cfg(struct PID_G *self, float32 max_err, BOOL mode)
{
self->pri.err_limit = max_err;
self->pri.detach = mode == FALSE ? FALSE : TRUE;
}
/**
* @brief
* @param {PID_G} *self
* @param {float32} max_weight
* @return {*}
* @note
*/
static void _set_weight(struct PID_G *self, float32 max_ratio, BOOL mode)
{
self->pri.ui_ratio = max_ratio;
self->pri.weight = mode == FALSE ? FALSE : TRUE;
}
/**
* @brief PID算法函数
* @param {PID_G} *self
* @param {float32} target
* @param {float32} feedback
* @return {*}
* @note
*/
static float32 _PID(struct PID_G *self, float32 target, float32 feedback)
{
float32 error = 0.0f;
float32 insert = 0.0f; ///< 该值为0时积分不介入计算
float32 temp_iterm = 0.0f;
float32 temp_kd = 0.0f;
g_param_t *pri = &self->pri;
pri->ref = target; ///< 目标位置
pri->feed_back = feedback; ///< 实际位置
pri->error = pri->ref - pri->feed_back; /// 误差
error = pri->error;
if (fabs(pri->error) <= pri->err_dead) ///< 误差小于死区,不计算
{
error = 0;
}
/*根据PID配置的模式,获取积分数据,进行积分累加*/
if (pri->out >= pri->out_max) ///< 到达输出上限
{
if (fabs(error) > pri->err_limit && pri->detach) ///< 误差大于积分介入区间,积分不介入计算
{
insert = 0;
}
else
{
insert = 1;
if (error < 0)
{
temp_iterm = pri->ki * error;
}
}
}
else if (pri->out <= pri->out_min) ///< 到达输出下限
{
if (fabs(error) > pri->err_limit && pri->detach) ///< 误差大于积分介入区间,积分不介入计算
{
insert = 0;
}
else
{
insert = 1;
if (error > 0)
{
temp_iterm = pri->ki * error;
}
}
}
else
{
if (fabs(error) > pri->err_limit && pri->detach)
{
insert = 0;
}
else
{
insert = 1;
temp_iterm = pri->ki * error;
}
}
if (pri->ki_enable == FALSE)
{
temp_iterm = 0;
insert = 0;
}
/* integral */
pri->sum_iterm += temp_iterm;
if (pri->weight == TRUE)
{
if (pri->sum_iterm > pri->ui_ratio)
{
pri->sum_iterm = pri->ui_ratio;
}
else if (pri->sum_iterm < -pri->ui_ratio)
{
pri->sum_iterm = -pri->ui_ratio;
}
}
else
{
if (pri->sum_iterm > pri->out_max)
{
pri->sum_iterm = pri->out_max;
}
else if (pri->sum_iterm < pri->out_min)
{
pri->sum_iterm = pri->out_min;
}
}
/* differential */
if (pri->kd_enable == TRUE)
{
temp_kd = pri->kd;
}
else
{
temp_kd = 0;
}
pri->out = pri->kp * pri->error + pri->sum_iterm * insert + (pri->error - pri->pre_error) * temp_kd;
pri->pre_error = pri->error; ///< 记录这次误差,为下次微分计算做准备
pri->pre_feed_back = pri->feed_back;
/*limt pid output*/
pri->out = RANGE(pri->out, pri->out_min, pri->out_max); ///< 限制输出
return pri->out;
}
/**
* @brief PID接口
* @param {PID_G} *self
* @return {*}
* @note
*/
void pid_g_constructor(struct PID_G *self)
{
self->set_ctrl_prm = _set_ctrl_prm;
self->update_ctrl_prm = _update_ctrl_prm;
self->set_cfg = _set_cfg;
self->set_kp = _set_kp;
self->set_ki_enable = _set_ki_enable;
self->set_ki = _set_ki;
self->set_kd_enable = _set_kd_enable;
self->set_kd = _set_kd;
self->set_range = _set_range;
self->restctrl = _restctrl;
self->PID = _PID;
self->set_weight = _set_weight;
}

View File

@ -0,0 +1,65 @@
#ifndef __PID_G_H__
#define __PID_G_H__
#include "lib.h"
typedef struct
{
float32 ref; /*目标*/
float32 feed_back; /*实际*/
float32 pre_feed_back; /*上一次实际*/
float32 kp; /*比例学习速度*/
float32 ki; /*积分学习速度*/
float32 kd; /*微分学习速度*/
float32 ki_error; /*积分误差*/
float32 error; /*误差*/
float32 pre_error; /*前一拍偏差*/
float32 prev_error; /*前两拍偏差*/
float32 err_dead; /*死区*/
float32 err_limit; /*积分分离上限*/
float32 maximum; /*输出值的上限*/
float32 minimum; /*输出值的下限*/
float32 out; /*输出值*/
float32 sum_iterm; /*积分累加*/
float32 ui_ratio; /*积分权重*/
BOOL ki_enable; /*积分使能*/
BOOL kd_enable; /*微分使能*/
BOOL detach; /*积分分离标志*/
BOOL weight; /*积分权重标志*/
float32 out_max; /*输出最大值*/
float32 out_min; /*输出最小值*/
} g_param_t;
typedef struct PID_G
{
/* 设置PID三个参数 */
void (*set_ctrl_prm)(struct PID_G *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 out_min, float32 out_max);
/* 更新PID参数 */
void (*update_ctrl_prm)(struct PID_G *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 out_min, float32 out_max);
/* 控制接口 */
float32 (*PID)(struct PID_G *self, float32 target, float32 feedback);
/* 更新控制区间 */
void (*set_range)(struct PID_G *self, float32 out_min, float32 out_max);
/* 设置积分分离 */
void (*set_cfg)(struct PID_G *self, float32 max_err, BOOL mode);
/* 设置积分权重 */
void (*set_weight)(struct PID_G *self, float32 max_ratio, BOOL mode);
/* 更新kp */
void (*set_kp)(struct PID_G *self, float32 kp);
/* 使能ki */
void (*set_ki_enable)(struct PID_G *self, BOOL enable);
/* 更新ki */
void (*set_ki)(struct PID_G *self, float32 ki);
/* 使能kd */
void (*set_kd_enable)(struct PID_G *self, BOOL enable);
/* 更新kd */
void (*set_kd)(struct PID_G *self, float32 kd);
/* 复位PID积分及微分控制数据 */
void (*restctrl)(struct PID_G *self);
/* private */
g_param_t pri;
} pid_g_t;
extern void pid_g_constructor(struct PID_G *self);
#endif // __PID_G_H__

View File

@ -0,0 +1,166 @@
#include "pid_hd.h"
#include <math.h>
#include "sys.h"
#include "app.h"
#if INCOMPLETE_DIFFEREN_HD == 1 // 积分分离
/*计算微分项,使用追随误差微分项*/
static float32 td_derivative(struct PID_HD *self, float32 current_err, float32 pre_err, float32 dt)
{
pid_hd_position_t *pri = &self->pri_u.position;
float32 derivative = (current_err - pre_err) / dt; // 计算积分项
derivative = pri->td_alpha * derivative + (1 - pri->td_alpha) * pri->td_beta * pri->pre_derivative; // 追随误差微分器平滑输出
pri->pre_derivative = derivative; // 更新上一次误差
return derivative;
}
#endif
/*杭电设置位置式PID参数*/
static void _set_ctrl_prm_position(struct PID_HD *self, float32 kp, float32 ki, float32 kd)
{
pid_hd_position_t *pri = &self->pri_u.position;
osel_memset((uint8_t *)pri, 0, sizeof(pid_hd_position_t));
/*观测传进来的Kp、Ki、Kd*/
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->ki_limit = 10; // 积分分离界限
pri->err_dead = 0.5; // 控制死区范围
#if INCOMPLETE_DIFFEREN_HD == 1
/*不完全微分系数*/
pri->td_alpha = 0.5;
pri->td_beta = 0.5;
#endif
}
/*杭电:设置输出限幅参数*/
static void _set_out_prm_position(struct PID_HD *self, float32 maximum, float32 minimum)
{
self->pri_u.position.out_max = maximum;
self->pri_u.position.out_min = minimum;
}
/*杭电位置式PID控制算法*/
static float32 _pid_position(struct PID_HD *self, float32 err)
{
/*计算控制的运行时间*/
// sys_millis_reset();
self->pri_u.position.control_time = sys_millis();
self->pri_u.position.tmp_time = 0;
/*测试4.18*/
if (fabs(err) < 0.1)
{
err = 0;
}
float32 x[3];
self->pri_u.position.err = err;
/*抗积分饱和*/
#if INTEGRAL_SEPARATION == 1 // 积分分离
if (self->pri_u.position.out > self->pri_u.position.out_max)
{
if (self->pri_u.position.err > self->pri_u.position.ki_limit) // 积分分离
{
self->pri_u.position.ki_error += 0;
}
else
{
if (self->pri_u.position.err < 0) // 若偏差为负值,执行负偏差的累加
{
self->pri_u.position.ki_error += self->pri_u.position.err;
}
}
}
else if (self->pri_u.position.out < self->pri_u.position.out_min)
{
if (self->pri_u.position.err > self->pri_u.position.ki_limit) // 若偏差为负值,停止积分
{
self->pri_u.position.ki_error += 0;
}
else
{
if (self->pri_u.position.err > 0) // 若偏差为正值,执行正偏差的累加
{
self->pri_u.position.ki_error += self->pri_u.position.err;
}
}
}
else
{
if (fabs(err) > self->pri_u.position.ki_limit || fabs(err) < 0.5)
{
self->pri_u.position.ki_error += 0;
}
else
{
self->pri_u.position.ki_error += self->pri_u.position.err;
}
}
#else /*无积分分离操作*/
if (fabs(err) < 0.4)
{
self->pri_u.position.ki_error += 0;
}
else
{
self->pri_u.position.ki_error += self->pri_u.position.err;
}
#endif
/*输出*/
if (fabs(err) < self->pri_u.position.err_dead)
{
/*输出上一次的值*/
// self->pri_u.position.out = self->pri_u.position.pre_out;
x[0] = self->pri_u.position.err;
x[1] = self->pri_u.position.ki_error;
self->pri_u.position.out = self->pri_u.position.kp * x[0] + self->pri_u.position.ki * x[1] + self->pri_u.position.kd * x[2];
}
else
{
x[0] = self->pri_u.position.err;
x[1] = self->pri_u.position.ki_error;
#if INCOMPLETE_DIFFEREN_HD == 1
/*不完全微分项为了解决普通PID为微分环节容易振荡的问题*/
self->pri_u.position.tmp_time = sys_millis();
self->pri_u.position.control_time -= self->pri_u.position.tmp_time;
self->pri_u.position.control_time /= 1000.0; // 将单位转换为秒
x[2] = td_derivative(&_pid.pid_u.hd, err, self->pri_u.position.pre_error, self->pri_u.position.control_time);
#else
// 普通的微分环节
x[2] = self->pri_u.position.err - self->pri_u.position.pre_error;
#endif
self->pri_u.position.out = self->pri_u.position.kp * x[0] + self->pri_u.position.ki * x[1] + self->pri_u.position.kd * x[2];
}
/*输出限幅*/
if (self->pri_u.position.out > self->pri_u.position.out_max)
{
self->pri_u.position.out = self->pri_u.position.out_max;
}
if (self->pri_u.position.out < self->pri_u.position.out_min)
{
self->pri_u.position.out = self->pri_u.position.out_min;
}
// 更新误差历史
self->pri_u.position.pre_error = self->pri_u.position.err; /*上一次误差值*/
// 更新输出历史
self->pri_u.position.pre_out = self->pri_u.position.out; /*上一次输出值*/
return self->pri_u.position.out;
}
/*杭电:参数控制器*/
void pid_hd_constructor(struct PID_HD *self)
{
self->set_ctrl_prm_position = _set_ctrl_prm_position;
self->set_out_prm_position = _set_out_prm_position;
self->pid_position = _pid_position;
}

View File

@ -0,0 +1,66 @@
#ifndef __PID_HD__
#define __PID_HD__
#include "lib.h"
#define INTEGRAL_SEPARATION 1 // 积分分离
#define INCOMPLETE_DIFFEREN_HD 1 // 不完全微分
typedef struct
{
float32 ref;
float32 feed_back;
float32 pre_feed_back;
float32 pre_error;
float32 ki_error; /*积分误差*/
float32 ki_limit; /*积分分离界限*/
float32 ki_alpha; /*变积分的系数*/
float32 err;
float32 sum_iterm;
float32 kp;
float32 kp_small; /*在接近稳态时的Kp*/
float32 kp_big; /*在大范围时的Kp*/
float32 ki;
float32 kd;
float32 err_limit;
BOOL detach;
float32 err_dead;
#if INCOMPLETE_DIFFEREN_HD == 1
float32 td_alpha; /*不完全微分系数*/
float32 td_beta; /*不完全微分系数beta*/
float32 pre_derivative; /*上一次微分值*/
#endif
float32 out;
float32 pre_out;
float32 out_max;
float32 out_min;
BOOL sm;
float32 sv_range;
uint32_t control_time; /*控制算法运行一次花费的时间*/
uint32_t tmp_time; /*临时用来记录控制的运行时间*/
} pid_hd_position_t; // 位置式PID
typedef struct PID_HD
{
/* 设置PID三个参数 */
void (*set_ctrl_prm_position)(struct PID_HD *self, float32 kp, float32 ki, float32 kd);
/* 设置输出范围 */
void (*set_out_prm_position)(struct PID_HD *self, float32 maximum, float32 minimum);
/* 控制接口 */
float32 (*pid_position)(struct PID_HD *self, float32 err);
// 自定义参数
/* 实际值与目标值之间的误差 */
float32 err;
/* 输出值 */
float32 out;
/* private */
struct
{
pid_hd_position_t position;
} pri_u;
} pid_hd_t;
extern void pid_hd_constructor(struct PID_HD *self);
#endif // __PID_HD__

View File

@ -0,0 +1,481 @@
#include "pid_x.h"
#include "math.h"
#define LAG_PHASE (6) // 迟滞相位,单位:拍
#ifndef PI
#define PI 3.14159265358979f
#endif
// 注1自适应模糊pid最重要的就是论域的选择要和你应该控制的对象相切合
// 注2以下各阀值、限幅值、输出值均需要根据具体的使用情况进行更改
// 注3因为我的控制对象惯性比较大所以以下各部分取值较小
// 论域e:[-5,5] ec:[-0.5,0.5]
// 误差的阀值小于这个数值的时候不做PID调整避免误差较小时频繁调节引起震荡
#define Emin 0.3f
#define Emid 1.0f
#define Emax 5.0f
// 调整值限幅,防止积分饱和
#define Umax 1
#define Umin -1
#define NB 0
#define NM 1
#define NS 2
#define ZO 3
#define PS 4
#define PM 5
#define PB 6
int32_t kp[7][7] = {{PB, PB, PM, PM, PS, ZO, ZO},
{PB, PB, PM, PS, PS, ZO, ZO},
{PM, PM, PM, PS, ZO, NS, NS},
{PM, PM, PS, ZO, NS, NM, NM},
{PS, PS, ZO, NS, NS, NM, NM},
{PS, ZO, NS, NM, NM, NM, NB},
{ZO, ZO, NM, NM, NM, NB, NB}};
int32_t kd[7][7] = {{PS, NS, NB, NB, NB, NM, PS},
{PS, NS, NB, NM, NM, NS, ZO},
{ZO, NS, NM, NM, NS, NS, ZO},
{ZO, NS, NS, NS, NS, NS, ZO},
{ZO, ZO, ZO, ZO, ZO, ZO, ZO},
{PB, NS, PS, PS, PS, PS, PB},
{PB, PM, PM, PM, PS, PS, PB}};
int32_t ki[7][7] = {{NB, NB, NM, NM, NS, ZO, ZO},
{NB, NB, NM, NS, NS, ZO, ZO},
{NB, NM, NS, NS, ZO, PS, PS},
{NM, NM, NS, ZO, PS, PM, PM},
{NM, NS, ZO, PS, PS, PM, PB},
{ZO, ZO, PS, PS, PM, PB, PB},
{ZO, ZO, PS, PM, PM, PB, PB}};
static float32 ec; // 误差变化率
/**************求隶属度(三角形)***************/
float32 FTri(float32 x, float32 a, float32 b, float32 c) // FuzzyTriangle
{
if (x <= a)
return 0;
else if ((a < x) && (x <= b))
return (x - a) / (b - a);
else if ((b < x) && (x <= c))
return (c - x) / (c - b);
else if (x > c)
return 0;
else
return 0;
}
/*****************求隶属度(梯形左)*******************/
float32 FTraL(float32 x, float32 a, float32 b) // FuzzyTrapezoidLeft
{
if (x <= a)
return 1;
else if ((a < x) && (x <= b))
return (b - x) / (b - a);
else if (x > b)
return 0;
else
return 0;
}
/*****************求隶属度(梯形右)*******************/
float32 FTraR(float32 x, float32 a, float32 b) // FuzzyTrapezoidRight
{
if (x <= a)
return 0;
if ((a < x) && (x < b))
return (x - a) / (b - a);
if (x >= b)
return 1;
else
return 1;
}
/****************三角形反模糊化处理**********************/
float32 uFTri(float32 x, float32 a, float32 b, float32 c)
{
float32 y, z;
z = (b - a) * x + a;
y = c - (c - b) * x;
return (y + z) / 2;
}
/*******************梯形(左)反模糊化***********************/
float32 uFTraL(float32 x, float32 a, float32 b)
{
return b - (b - a) * x;
}
/*******************梯形(右)反模糊化***********************/
float32 uFTraR(float32 x, float32 a, float32 b)
{
return (b - a) * x + a;
}
/**************************求交集****************************/
float32 fand(float32 a, float32 b)
{
return (a < b) ? a : b;
}
/**************************求并集****************************/
float32 forr(float32 a, float32 b)
{
return (a < b) ? b : a;
}
static float32 _PID(struct PID_X *self, float32 target, float32 feedback)
{
float32 pwm_var; // pwm调整量
float32 iError; // 当前误差
float32 set, input;
CLASSICPID *pri = &self->pri;
// 计算隶属度表
float32 es[7], ecs[7], e;
float32 form[7][7];
int i = 0, j = 0;
int MaxX = 0, MaxY = 0;
// 记录隶属度最大项及相应推理表的p、i、d值
float32 lsd;
int temp_p, temp_d, temp_i;
float32 detkp, detkd, detki; // 推理后的结果
// 输入格式的转化及偏差计算
set = target;
input = feedback;
iError = set - input; // 偏差
e = iError;
ec = iError - pri->lasterror;
// 当温度差的绝对值小于Emax时对pid的参数进行调整
if (fabs(iError) <= Emax)
{
// 计算iError在es与ecs中各项的隶属度
es[NB] = FTraL(e * 5, -3, -1); // e
es[NM] = FTri(e * 5, -3, -2, 0);
es[NS] = FTri(e * 5, -3, -1, 1);
es[ZO] = FTri(e * 5, -2, 0, 2);
es[PS] = FTri(e * 5, -1, 1, 3);
es[PM] = FTri(e * 5, 0, 2, 3);
es[PB] = FTraR(e * 5, 1, 3);
ecs[NB] = FTraL(ec * 30, -3, -1); // ec
ecs[NM] = FTri(ec * 30, -3, -2, 0);
ecs[NS] = FTri(ec * 30, -3, -1, 1);
ecs[ZO] = FTri(ec * 30, -2, 0, 2);
ecs[PS] = FTri(ec * 30, -1, 1, 3);
ecs[PM] = FTri(ec * 30, 0, 2, 3);
ecs[PB] = FTraR(ec * 30, 1, 3);
// 计算隶属度表确定e和ec相关联后表格各项隶属度的值
for (i = 0; i < 7; i++)
{
for (j = 0; j < 7; j++)
{
form[i][j] = fand(es[i], ecs[j]);
}
}
// 取出具有最大隶属度的那一项
for (i = 0; i < 7; i++)
{
for (j = 0; j < 7; j++)
{
if (form[MaxX][MaxY] < form[i][j])
{
MaxX = i;
MaxY = j;
}
}
}
// 进行模糊推理,并去模糊
lsd = form[MaxX][MaxY];
temp_p = kp[MaxX][MaxY];
temp_d = kd[MaxX][MaxY];
temp_i = ki[MaxX][MaxY];
if (temp_p == NB)
detkp = uFTraL(lsd, -0.3, -0.1);
else if (temp_p == NM)
detkp = uFTri(lsd, -0.3, -0.2, 0);
else if (temp_p == NS)
detkp = uFTri(lsd, -0.3, -0.1, 0.1);
else if (temp_p == ZO)
detkp = uFTri(lsd, -0.2, 0, 0.2);
else if (temp_p == PS)
detkp = uFTri(lsd, -0.1, 0.1, 0.3);
else if (temp_p == PM)
detkp = uFTri(lsd, 0, 0.2, 0.3);
else if (temp_p == PB)
detkp = uFTraR(lsd, 0.1, 0.3);
if (temp_d == NB)
detkd = uFTraL(lsd, -3, -1);
else if (temp_d == NM)
detkd = uFTri(lsd, -3, -2, 0);
else if (temp_d == NS)
detkd = uFTri(lsd, -3, 1, 1);
else if (temp_d == ZO)
detkd = uFTri(lsd, -2, 0, 2);
else if (temp_d == PS)
detkd = uFTri(lsd, -1, 1, 3);
else if (temp_d == PM)
detkd = uFTri(lsd, 0, 2, 3);
else if (temp_d == PB)
detkd = uFTraR(lsd, 1, 3);
if (temp_i == NB)
detki = uFTraL(lsd, -0.06, -0.02);
else if (temp_i == NM)
detki = uFTri(lsd, -0.06, -0.04, 0);
else if (temp_i == NS)
detki = uFTri(lsd, -0.06, -0.02, 0.02);
else if (temp_i == ZO)
detki = uFTri(lsd, -0.04, 0, 0.04);
else if (temp_i == PS)
detki = uFTri(lsd, -0.02, 0.02, 0.06);
else if (temp_i == PM)
detki = uFTri(lsd, 0, 0.04, 0.06);
else if (temp_i == PB)
detki = uFTraR(lsd, 0.02, 0.06);
// pid三项系数的修改
pri->pKp += detkp;
pri->pKi += detki;
if (pri->kd_e)
{
pri->pKd += detkd;
}
else
{
pri->pKd = 0; // 取消微分作用
}
// 对Kp,Ki进行限幅
if (pri->pKp < 0)
{
pri->pKp = 0;
}
if (pri->pKi < 0)
{
pri->pKi = 0;
}
// 计算新的K1,nKi,nKd
pri->nKp = pri->pKp + pri->pKi + pri->pKd;
pri->nKi = -(pri->pKp + 2 * pri->pKd);
pri->nKd = pri->pKd;
}
if (iError > Emax)
{
pri->out = pri->max;
pwm_var = 0;
pri->flag = 1; // 设定标志位,如果误差超过了门限值,则认为当控制量第一次到达给定值时,应该采取下面的 抑制超调 的措施
}
else if (iError < -Emax)
{
pri->out = pri->min;
pwm_var = 0;
}
else if (fabsf(iError) <= Emin)
{
pwm_var = 0;
}
else
{
if (iError < Emid && pri->flag == 1) // 第一次超过(设定值-Emid(-0.08)摄氏度),是输出为零,防止超调,也可以输出其他值,不至于太小而引起震荡
{
pri->out = 0;
pri->flag = 0;
}
else if (-iError > Emid) // 超过(设定+Emid(+0.08)摄氏度)
{
pwm_var = -1;
}
else
{
// 增量计算
pwm_var = (pri->nKp * iError // e[k]
+ pri->nKi * pri->lasterror // e[k-1]
+ pri->nKd * pri->preverror); // e[k-2]
}
if (pwm_var >= Umax)
pwm_var = Umax; // 调整值限幅,防止积分饱和
if (pwm_var <= Umin)
pwm_var = Umin; // 调整值限幅,防止积分饱和
}
pri->preverror = pri->lasterror;
pri->lasterror = iError;
pri->out += pwm_var; // 调整PWM输出
if (pri->out > pri->max)
pri->out = pri->max; // 输出值限幅
if (pri->out < pri->min)
pri->out = pri->min; // 输出值限幅
return pri->out;
}
/*整定开始前的预处理,判断状态及初始化变量*/
static void tune_pretreatment(struct PID_X *self)
{
CLASSIC_AUTOTUNE *tune = &self->tune;
CLASSICPID *vPID = &self->pri;
tune->tuneTimer = 0;
tune->startTime = 0;
tune->endTime = 0;
tune->outputStep = 100;
if (*vPID->pSV >= *vPID->pPV)
{
tune->initialStatus = 1;
tune->outputStatus = 0;
}
else
{
tune->initialStatus = 0;
tune->outputStatus = 1;
}
tune->tuneEnable = 1;
tune->preEnable = 0;
tune->zeroAcrossCounter = 0;
tune->riseLagCounter = 0;
tune->fallLagCounter = 0;
}
/*计算PID参数值*/
static void calculation_parameters(struct PID_X *self)
{
CLASSIC_AUTOTUNE *tune = &self->tune;
CLASSICPID *vPID = &self->pri;
float32 kc = 0.0f;
float32 tc = 0.0f;
float32 zn[3][3] = {{0.5f, 100000.0f, 0.0f}, {0.45f, 0.8f, 0.0f}, {0.6f, 0.5f, 0.125f}};
tc = (tune->endTime - tune->startTime) * tune->tunePeriod / 1000.0;
kc = (8.0f * tune->outputStep) / (PI * (tune->maxPV - tune->minPV));
vPID->pKp = zn[tune->controllerType][0] * kc; // 比例系数
vPID->pKi = vPID->pKp * tune->tunePeriod / (zn[tune->controllerType][1] * tc); // 积分系数
vPID->pKd = vPID->pKp * zn[tune->controllerType][2] * tc / tune->tunePeriod; // 微分系数
}
/**
* @brief
* @param {PID_X} *self
* @return {*}
* @note tuneEnablepreEnable和controllerType需要提前赋值tuneEnable变量值为0时是使用PID控制器tuneEnable变量值为1时是开启整定过程tuneEnable变量值为2时是指示整定失败preEnable变量在整定前赋值为1controllerType则根据所整定的控制器的类型来定
*/
static uint8_t _auto_tune(struct PID_X *self)
{
CLASSIC_AUTOTUNE *tune = &self->tune;
CLASSICPID *vPID = &self->pri;
/*整定开始前的预处理,只执行一次*/
if (tune->preEnable == 1)
{
tune_pretreatment(self);
}
uint32_t tuneDuration = 0;
tune->tuneTimer++;
tuneDuration = (tune->tuneTimer * tune->tunePeriod) / 1000;
if (tuneDuration > (10 * 60)) // 整定过程持续超过10分钟未能形成有效振荡整定失败
{
tune->tuneEnable = 2;
tune->preEnable = 1;
return tune->tuneEnable;
}
if (*vPID->pSV >= *vPID->pPV) // 设定值大于测量值,则开执行单元
{
tune->riseLagCounter++;
tune->fallLagCounter = 0;
if (tune->riseLagCounter > LAG_PHASE)
{
*vPID->pMV = vPID->max;
if (tune->outputStatus == 0)
{
tune->outputStatus = 1;
tune->zeroAcrossCounter++;
if (tune->zeroAcrossCounter == 3)
{
tune->startTime = tune->tuneTimer;
}
}
}
}
else
{
tune->riseLagCounter = 0;
tune->fallLagCounter++;
if (tune->fallLagCounter > LAG_PHASE)
{
*vPID->pMV = vPID->min;
if (tune->outputStatus == 1)
{
tune->outputStatus = 0;
tune->zeroAcrossCounter++;
if (tune->zeroAcrossCounter == 3)
{
tune->startTime = tune->tuneTimer;
}
}
}
}
if (tune->zeroAcrossCounter == 3) // 已经两次过零,可以记录波形数据
{
if (tune->initialStatus == 1) // 初始设定值大于测量值则区域3出现最小值
{
if (*vPID->pPV < tune->minPV)
{
tune->minPV = *vPID->pPV;
}
}
else if (tune->initialStatus == 0) // 初始设定值小于测量值则区域3出现最大值
{
if (*vPID->pPV > tune->maxPV)
{
tune->maxPV = *vPID->pPV;
}
}
}
else if (tune->zeroAcrossCounter == 4) // 已经三次过零,记录另半波的数据
{
if (tune->initialStatus == 1) // 初始设定值大于测量值则区域4出现最大值
{
if (*vPID->pPV > tune->maxPV)
{
tune->maxPV = *vPID->pPV;
}
}
else if (tune->initialStatus == 0) // 初始设定值小于测量值则区域4出现最小值
{
if (*vPID->pPV < tune->minPV)
{
tune->minPV = *vPID->pPV;
}
}
}
else if (tune->zeroAcrossCounter == 5) // 已经四次过零,振荡已形成可以整定参数
{
calculation_parameters(self);
tune->tuneEnable = 0;
tune->preEnable = 1;
}
return tune->tuneEnable;
}
void pid_x_constructor(struct PID_X *self)
{
self->PID = _PID;
self->AUTO_TUNE = _auto_tune;
self->pri.flag = 0;
self->pri.out = 0;
self->tune.preEnable = 1;
}

View File

@ -0,0 +1,71 @@
#ifndef __PID_X_H__
#define __PID_X_H__
#include "lib.h"
/*定义PID对象类型*/
typedef struct CLASSIC
{
float32 *pPV; // 测量值指针
float32 *pSV; // 设定值指针
float32 *pMV; // 输出值指针
BOOL *pMA; // 手自动操作指针
float32 out; // 输出值
float32 setpoint; // 设定值
float32 lasterror; // 前一拍偏差
float32 preverror; // 前两拍偏差
float32 max; // 输出值上限
float32 min; // 输出值下限
uint16_t flag; // 状态标志位
float32 pKp; // 比例系数
float32 pKi; // 积分系数
float32 pKd; // 微分系数
float32 nKp; // 比例系数
float32 nKi; // 积分系数
float32 nKd; // 微分系数
BOOL direct; // 正反作用
BOOL sm; // 设定值平滑
BOOL cas; // 串级设定
BOOL pac; // 输出防陡变
BOOL kd_e; // 微分使能
} CLASSICPID;
// 定义整定参数
typedef struct
{
uint8_t tuneEnable : 2; // 整定与PID控制开关0PID控制1参数整定2整定失败
uint8_t preEnable : 2; // 预处理使能,在开始整定前置位
uint8_t initialStatus : 1; // 记录开始整定前偏差的初始状态
uint8_t outputStatus : 1; // 记录输出的初始状态0允许上升过零计数1允许下降过零计数
uint8_t controllerType : 2; // 控制器类型0P控制器1PI控制器2PID控制器
uint8_t zeroAcrossCounter; // 过零点计数器每次输出改变加1比实际过零次数多1
uint8_t riseLagCounter; // 上升迟滞时间计数器
uint8_t fallLagCounter; // 下降迟滞时间计数器
uint16_t tunePeriod; // 整定采样周期
uint32_t tuneTimer; // 整定计时器
uint32_t startTime; // 记录波形周期起始时间
uint32_t endTime; // 记录波形周期结束时间
float32 outputStep; // 输出阶跃d
float32 maxPV; // 振荡波形中测量值的最大值
float32 minPV; // 振荡波形中测量值的最小值
} CLASSIC_AUTOTUNE;
typedef struct PID_X
{
/* 控制接口 */
float32 (*PID)(struct PID_X *self, float32 target, float32 feedback);
uint8_t (*AUTO_TUNE)(struct PID_X *self);
/* private */
CLASSICPID pri;
CLASSIC_AUTOTUNE tune;
} pid_x_t;
extern void pid_x_constructor(struct PID_X *self);
#endif // __PID_X_H__

View File

@ -0,0 +1,467 @@
#include "pid_zh.h"
#include "sys.h"
#include <math.h>
// 定义输出量比例因子
#ifdef GPS3000
#define KUP 0.0f // #define KUP 3.0f
#define KUI 0.00f
#define KUD 0.0f // #define KUP 3.0f
#else
#define KUP 3.0f
#define KUI 0.0f
#define KUD 0.0f
#endif
// 模糊集合
#define NL -3
#define NM -2
#define NS -1
#define ZE 0
#define PS 1
#define PM 2
#define PL 3
// 定义偏差E的范围因为设置了非线性区间误差在10时才开始进行PID调节这里E的范围为10
#define MAXE (10)
#define MINE (-MAXE)
// 定义EC的范围因为变化非常缓慢每次的EC都非常小这里可以根据实际需求来调整
#define MAXEC (10)
#define MINEC (-MAXEC)
// 定义e,ec的量化因子
#define KE 3 / MAXE
#define KEC 3 / MAXEC
static const float32 fuzzyRuleKp[7][7] = {
PL, PL, PM, PL, PS, PM, PL,
PL, PM, PM, PM, PS, PM, PL,
PM, PS, PS, PS, PS, PS, PM,
PM, PS, ZE, ZE, ZE, PS, PM,
PS, PS, PS, PS, PS, PM, PM,
PM, PM, PM, PM, PL, PL, PL,
PM, PL, PL, PL, PL, PL, PL};
static const float32 fuzzyRuleKi[7][7] = {
PL, PL, PL, PL, PM, PL, PL,
PL, PL, PM, PM, PM, PL, PL,
PM, PM, PS, PS, PS, PM, PM,
PM, PS, ZE, ZE, ZE, PS, PM,
PM, PS, PS, PS, PS, PM, PM,
PM, PM, PS, PM, PM, PL, PL,
PM, PL, PM, PL, PL, PL, PL};
/*
static const float32 fuzzyRuleKi[7][7] = {
NL, NL, NL, NL, NM, NL, NL,
NL, NL, NM, NM, NM, NL, NL,
NM, NM, NS, NS, NS, NM, NM,
NM, NS, ZE, ZE, ZE, NS, NM,
NM, NS, NS, NS, NS, NM, NM,
NM, NM, NS, NM, NM, NL, NL,
NM, NL, NM, NL, NL, NL, NL};
*/
static const float32 fuzzyRuleKd[7][7] = {
PS, PS, ZE, ZE, ZE, PL, PL,
PS, PS, PS, PS, ZE, PS, PM,
PL, PL, PM, PS, ZE, PS, PM,
PL, PM, PM, PS, ZE, PS, PM,
PL, PM, PS, PS, ZE, PS, PS,
PM, PS, PS, PS, ZE, PS, PS,
PS, ZE, ZE, ZE, ZE, PL, PL};
/*
static const float32 fuzzyRuleKp[7][7] = {
PL, PL, PM, PM, PS, ZE, ZE,
PL, PL, PM, PS, PS, ZE, NS,
PM, PM, PM, PS, ZE, NS, NS,
PM, PM, PS, ZE, NS, NM, NM,
PS, PS, ZE, NS, NS, NM, NM,
PS, ZE, NS, NM, NM, NM, NL,
ZE, ZE, NM, NM, NM, NL, NL};
static const float32 fuzzyRuleKi[7][7] = {
NL, NL, NM, NM, NS, ZE, ZE,
NL, NL, NM, NS, NS, ZE, ZE,
NL, NM, NS, NS, ZE, PS, PS,
NM, NM, NS, ZE, PS, PM, PM,
NM, NS, ZE, PS, PS, NM, PL,
ZE, ZE, PS, PS, PM, PL, PL,
ZE, ZE, PS, PM, PM, PL, PL};
static const float32 fuzzyRuleKd[7][7] = {
PS, NS, NL, NL, NL, NM, PS,
PS, NS, NL, NM, NM, NS, ZE,
ZE, NS, NM, NM, NS, NS, ZE,
ZE, NS, NS, NS, NS, NS, ZE,
ZE, ZE, ZE, ZE, ZE, ZE, ZE,
PL, NS, PS, PS, PS, PS, PL,
PL, PM, PM, PM, PS, PS, PL};
*/
static void fuzzy(float32 e, float32 ec, FUZZY_PID_ZH_t *fuzzy_pid)
{
float32 etemp, ectemp;
float32 eLefttemp, ecLefttemp; // ec,e左隶属度
float32 eRighttemp, ecRighttemp;
int eLeftIndex, ecLeftIndex; // 模糊位置标号
int eRightIndex, ecRightIndex;
e = RANGE(e, MINE, MAXE);
ec = RANGE(ec, MINEC, MAXEC);
e = e * KE;
ec = ec * KEC;
etemp = e > 3.0f ? 0.0f : (e < -3.0f ? 0.0f : (e >= 0.0f ? (e >= 2.0f ? 2.5f : (e >= 1.0f ? 1.5f : 0.5f)) : (e >= -1.0f ? -0.5f : (e >= -2.0f ? -1.5f : (e >= -3.0f ? -2.5f : 0.0f)))));
eLeftIndex = (int)((etemp - 0.5f) + 3); //[-3,2] -> [0,5]
eRightIndex = (int)((etemp + 0.5f) + 3);
eLefttemp = etemp == 0.0f ? 0.0f : ((etemp + 0.5f) - e);
eRighttemp = etemp == 0.0f ? 0.0f : (e - (etemp - 0.5f));
ectemp = ec > 3.0f ? 0.0f : (ec < -3.0f ? 0.0f : (ec >= 0.0f ? (ec >= 2.0f ? 2.5f : (ec >= 1.0f ? 1.5f : 0.5f)) : (ec >= -1.0f ? -0.5f : (ec >= -2.0f ? -1.5f : (ec >= -3.0f ? -2.5f : 0.0f)))));
ecLeftIndex = (int)((ectemp - 0.5f) + 3); //[-3,2] -> [0,5]
ecRightIndex = (int)((ectemp + 0.5f) + 3);
ecLefttemp = ectemp == 0.0f ? 0.0f : ((ectemp + 0.5f) - ec);
ecRighttemp = ectemp == 0.0f ? 0.0f : (ec - (ectemp - 0.5f));
/*************************************反模糊*************************************/
fuzzy_pid->kp = (eLefttemp * ecLefttemp * fuzzyRuleKp[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKp[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKp[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKp[eRightIndex][ecRightIndex]);
fuzzy_pid->ki = (eLefttemp * ecLefttemp * fuzzyRuleKi[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKi[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKi[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKi[eRightIndex][ecRightIndex]);
fuzzy_pid->kd = (eLefttemp * ecLefttemp * fuzzyRuleKd[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKd[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKd[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKd[eRightIndex][ecRightIndex]);
// 对解算出的KP,KI,KD进行量化映射
fuzzy_pid->kp = fuzzy_pid->kp * fuzzy_pid->kup;
fuzzy_pid->ki = fuzzy_pid->ki * fuzzy_pid->kui;
fuzzy_pid->kd = fuzzy_pid->kd * fuzzy_pid->kud;
}
static void smoothSetpoint(struct PID_FUZZY_ZH *self, float32 target_sv)
{
#if FUZZY_SUB_TYPE == PID_SUB_TYPE_POSITION
pid_zh_position_t *pri = &self->pri;
#else
pid_common_increment_t *pri = &self->pri;
#endif
float32 stepIn = (pri->sv_range) * 0.1f;
float32 kFactor = 0.0f;
if (fabs(pri->ref - target_sv) <= stepIn)
{
pri->ref = target_sv;
}
else
{
if (pri->ref - target_sv > 0)
{
kFactor = -1.0f;
}
else if (pri->ref - target_sv < 0)
{
kFactor = 1.0f;
}
else
{
kFactor = 0.0f;
}
pri->ref = pri->ref + kFactor * stepIn;
}
}
/*封装模糊接口*/
static void compensate(float32 e, float32 ec, FUZZY_PID_ZH_t *fuzzy_d)
{
fuzzy(e, ec, fuzzy_d);
}
/**
* @brief PID积分及微分控制数据
* @param {PID_FUZZY_ZH} *self
* @return {*}
*/
static void _restctrl(struct PID_FUZZY_ZH *self)
{
self->pri.pre_error = 0;
self->pri.sum_iterm = 0;
#if INCOMPLETE_DIFFEREN == 1
self->pri.lastdev = 0;
#endif
}
/**
* @brief
* @param {PID_FUZZY_ZH} *self
* @param {float32} out_min
* @param {float32} out_max
* @return {*}
* @note
*/
static void _set_range(struct PID_FUZZY_ZH *self, float32 out_min, float32 out_max)
{
self->pri.out_max = out_max;
self->pri.out_min = out_min;
}
/**
* @brief 使
* @param {PID_FUZZY_ZH} *self
* @param {BOOL} enable
* @return {*}
* @note
*/
// static void _set_ki_enable(struct PID_FUZZY_ZH *self, BOOL enable)
// {
// self->pri.ki_enable = enable;
// }
/**
* @brief 使
* @param {PID_FUZZY_ZH} *self
* @param {BOOL} enable
* @return {*}
* @note
*/
static void _set_kd_enable(struct PID_FUZZY_ZH *self, BOOL enable)
{
self->pri.kd_enable = enable;
}
/*
* Function:使
* parameter:*pid需要配PID参数结构指针sv_range控制范围sv的范围
* return:
*/
static void _set_smooth_enable(struct PID_FUZZY_ZH *self, BOOL enable, float32 sv_range)
{
#if FUZZY_SUB_TYPE == PID_SUB_TYPE_POSITION
pid_zh_position_t *pri = &self->pri;
#else
pid_common_increment_t *pri = &self->pri;
#endif
pri->sm = enable;
pri->sv_range = sv_range;
}
// 设置控制参数
static void _set_ctrl_prm(struct PID_FUZZY_ZH *self, float32 kp, float32 ki, float32 kd, float32 err_dead,
float32 out_min, float32 out_max)
{
self->open = TRUE;
self->fuzzy.kup = KUP;
self->fuzzy.kui = KUI;
self->fuzzy.kud = KUD;
#if FUZZY_SUB_TYPE == PID_SUB_TYPE_POSITION
pid_zh_position_t *pri = &self->pri;
osel_memset((uint8_t *)pri, 0, sizeof(pid_zh_position_t));
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
pri->detach = FALSE;
pri->sm = FALSE;
#else
pid_common_increment_t *pri = &self->pri;
osel_memset((uint8_t *)pri, 0, sizeof(pid_common_increment_t));
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
#endif
if (kd > 0)
{
pri->kd_enable = TRUE;
}
else
{
pri->kd_enable = FALSE;
}
}
static void _update_ctrl_prm(struct PID_FUZZY_ZH *self, float32 kp, float32 ki, float32 kd, float32 err_dead,
float32 out_min, float32 out_max)
{
#if FUZZY_SUB_TYPE == PID_SUB_TYPE_POSITION
pid_zh_position_t *pri = &self->pri;
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
pri->detach = FALSE;
pri->sm = FALSE;
#else
pid_common_increment_t *pri = &self->pri;
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
#endif
if (kd > 0)
{
pri->kd_enable = TRUE;
}
else
{
pri->kd_enable = FALSE;
}
}
/**
* @brief 0+PID,PID
* @param {PID_FUZZY_ZH} *self
* @param {float32} max_err
* @param {BOOL} mode
* @return {*}
*/
static void _set_cfg(struct PID_FUZZY_ZH *self, float32 max_err, BOOL mode)
{
self->pri.err_limit = max_err;
self->pri.detach = mode == FALSE ? FALSE : TRUE;
}
#if FUZZY_SUB_TYPE == PID_SUB_TYPE_POSITION
static float32 _PID(struct PID_FUZZY_ZH *self, float32 target, float32 feedback)
{
float32 error = 0;
float32 insert = 0;
float32 ec = 0;
float32 kd = 0;
#if INCOMPLETE_DIFFEREN == 1
float32 thisdev = 0;
#else
// float32 dinput = 0.0f;
#endif
float32 temp_iterm = 0.0f;
pid_zh_position_t *pri = &self->pri;
/*获取期望值与实际值,进行偏差计算*/
if (pri->sm == 1)
{
smoothSetpoint(self, target);
}
else
{
pri->ref = target;
}
pri->feed_back = feedback;
error = pri->ref - pri->feed_back;
if (fabs(error) <= pri->err_dead)
error = 0;
/* fuzzy control caculate */
ec = error - pri->pre_error;
if (self->open == TRUE)
{
compensate(error, ec, &self->fuzzy);
}
/*根据PID配置的模式,获取积分数据,进行积分累加*/
if (pri->out >= pri->out_max)
{
if (fabs(error) > pri->err_limit && pri->detach)
{
insert = 0;
}
else
{
insert = 1;
if (error < 0)
{
temp_iterm = (pri->ki + self->fuzzy.ki) * error;
}
}
}
else if (pri->out <= pri->out_min)
{
if (fabs(error) > pri->err_limit && pri->detach)
{
insert = 0;
}
else
{
insert = 1;
if (error > 0)
{
temp_iterm = (pri->ki + self->fuzzy.ki) * error;
}
}
}
else
{
if (fabs(error) > pri->err_limit && pri->detach)
{
insert = 0;
}
else
{
insert = 1;
temp_iterm = (pri->ki + self->fuzzy.ki) * error;
}
}
pri->sum_iterm += temp_iterm;
/* limt integral */
pri->sum_iterm = RANGE(pri->sum_iterm, pri->out_min, pri->out_max);
/*
if (pri->sum_ref)
pri->sum_iterm = RANGE(pri->sum_iterm, pri->sum_ref - 1.0f, pri->sum_ref + 1.0f);
else
pri->sum_iterm = RANGE(pri->sum_iterm, pri->out_min, pri->out_max);
*/
#if INCOMPLETE_DIFFEREN == 1
/*不完全微分*/
thisdev = (pri->kd + self->fuzzy.kd) * (1.0 - pri->alpha) * (error - pri->pre_error) + pri->alpha * pri->lastdev;
/*caculate pid out*/
pri->out = (pri->kp + self->fuzzy.kp) * error + pri->sum_iterm * insert + thisdev;
/*record last dev result*/
pri->lastdev = thisdev;
#else
if (pri->kd_enable == TRUE)
{
kd = pri->kd + self->fuzzy.kd;
}
else
{
kd = 0;
}
pri->out = (pri->kp + self->fuzzy.kp) * error + pri->sum_iterm * insert + (error - pri->pre_error) * (kd);
// pri->out += pri->sum_ref;
#endif
pri->pre_error = error;
/*record last feedback sensor result*/
pri->pre_feed_back = pri->feed_back;
/*limt pid output*/
pri->out = RANGE(pri->out, pri->out_min, pri->out_max);
return pri->out;
}
#else
#endif
void pid_zh_constructor(struct PID_FUZZY_ZH *self)
{
self->set_ctrl_prm = _set_ctrl_prm;
self->update_ctrl_prm = _update_ctrl_prm;
self->set_cfg = _set_cfg;
self->set_smooth_enable = _set_smooth_enable;
// self->set_ki_enable = _set_ki_enable;
self->set_kd_enable = _set_kd_enable;
self->set_range = _set_range;
self->restctrl = _restctrl;
self->PID = _PID;
}

View File

@ -0,0 +1,73 @@
#ifndef __PID_ZH_H__
#define __PID_ZH_H__
#include "lib.h"
#include "pid_auto_tune.h"
#define GPS2000
typedef struct
{
float32 ref;
float32 feed_back;
float32 pre_feed_back;
float32 pre_error;
float32 sum_ref;
float32 sum_iterm;
float32 kp;
float32 ki;
float32 kd;
float32 err_limit;
BOOL detach;
float32 err_dead;
#if INCOMPLETE_DIFFEREN == 1
float32 alpha;
float32 lastdev;
#endif
float32 out;
float32 out_max;
float32 out_min;
float32 sv_range;
BOOL sm;
BOOL ki_enable;
BOOL kd_enable;
} pid_zh_position_t; // 位置式PID
typedef struct
{
float32 kp;
float32 ki;
float32 kd;
float32 kup;
float32 kui;
float32 kud;
} FUZZY_PID_ZH_t;
// 模糊PID
typedef struct PID_FUZZY_ZH
{
/* 设置PID三个参数 */
void (*set_ctrl_prm)(struct PID_FUZZY_ZH *self, float32 kp, float32 ki, float32 kd, float32 err_dead,
float32 out_min, float32 out_max); // 设置PID参数
void (*update_ctrl_prm)(struct PID_FUZZY_ZH *self, float32 kp, float32 ki, float32 kd, float32 err_dead,
float32 out_min, float32 out_max); // 更新PID参数
void (*set_range)(struct PID_FUZZY_ZH *self, float32 out_min, float32 out_max); // 更新最大最小值
void (*set_cfg)(struct PID_FUZZY_ZH *self, float32 max_err, BOOL mode); // 配置PID模式,默认不使用积分分离
void (*set_smooth_enable)(struct PID_FUZZY_ZH *self, BOOL enable, float32 sv_range); // 设置平滑范围
// void (*set_ki_enable)(struct PID_FUZZY *self, BOOL enable);
// 微分开启使能
void (*set_kd_enable)(struct PID_FUZZY_ZH *self, BOOL enable);
void (*restctrl)(struct PID_FUZZY_ZH *self); // 复位PID积分及微分控制数据
/* 控制接口 */
float32 (*PID)(struct PID_FUZZY_ZH *self, float32 target, float32 feedback);
pid_zh_position_t pri;
BOOL open; // 是否使用模糊PID控制
FUZZY_PID_ZH_t fuzzy;
} pid_zh_t; // 模糊PID
extern void pid_zh_constructor(struct PID_FUZZY_ZH *self);
#endif

View File

@ -0,0 +1,619 @@
#include <math.h>
#include "pid_zh1.h"
// 定义输出量比例因子
#define KUP 1.0f
#define KUI 1.0f
#define KUD 1.0f
// 模糊集合
#define NL -3
#define NM -2
#define NS -1
#define ZE 0
#define PS 1
#define PM 2
#define PL 3
// 定义偏差E的范围因为设置了非线性区间误差在10时才开始进行PID调节这里E的范围为10
#define MAXE (10)
#define MINE (-MAXE)
// 定义EC的范围因为变化非常缓慢每次的EC都非常小这里可以根据实际需求来调整
#define MAXEC (10)
#define MINEC (-MAXEC)
// 定义e,ec的量化因子
#define KE 3 / MAXE
#define KEC 3 / MAXEC
/*
static const float fuzzyRuleKp[7][7] = {
PL, PL, PM, PL, PS, PM, PL,
PL, PM, PM, PM, PS, PM, PL,
PM, PS, PS, PS, PS, PS, PM,
PM, PS, ZE, ZE, ZE, PS, PM,
PS, PS, PS, PS, PS, PM, PM,
PM, PM, PM, PM, PL, PL, PL,
PM, PL, PL, PL, PL, PL, PL};
static const float fuzzyRuleKi[7][7] = {
PL, PL, PL, PL, PM, PL, PL,
PL, PL, PM, PM, PM, PL, PL,
PM, PM, PS, PS, PS, PM, PM,
PM, PS, ZE, ZE, ZE, PS, PM,
PM, PS, PS, PS, PS, PM, PM,
PM, PM, PS, PM, PM, PL, PL,
PM, PL, PM, PL, PL, PL, PL};
static const float fuzzyRuleKd[7][7] = {
PS, PS, ZE, ZE, ZE, PL, PL,
PS, PS, PS, PS, ZE, PS, PM,
PL, PL, PM, PS, ZE, PS, PM,
PL, PM, PM, PS, ZE, PS, PM,
PL, PM, PS, PS, ZE, PS, PS,
PM, PS, PS, PS, ZE, PS, PS,
PS, ZE, ZE, ZE, ZE, PL, PL};
*/
static const float fuzzyRuleKp[7][7] = {
PL, PL, PM, PL, PS, PM, PL,
PL, PM, PM, PM, PS, PM, PL,
PM, PS, PS, PS, PS, PS, PM,
PM, PS, ZE, ZE, ZE, PS, PM,
PS, PS, PS, PS, PS, PM, PM,
PM, PM, PM, PM, PL, PL, PL,
PM, PL, PL, PL, PL, PL, PL};
static const float fuzzyRuleKi[7][7] = {
PL, PL, PL, PL, PM, PL, PL,
PL, PL, PM, PM, PM, PL, PL,
PM, PM, PS, PS, PS, PM, PM,
PM, PS, ZE, ZE, ZE, PS, PM,
PM, PS, PS, PS, PS, PM, PM,
PM, PM, PS, PM, PM, PL, PL,
PM, PL, PM, PL, PL, PL, PL};
static const float fuzzyRuleKd[7][7] = {
PS, PS, ZE, ZE, ZE, PL, PL,
PS, PS, PS, PS, ZE, PS, PM,
PL, PL, PM, PS, ZE, PS, PM,
PL, PM, PM, PS, ZE, PS, PM,
PL, PM, PS, PS, ZE, PS, PS,
PM, PS, PS, PS, ZE, PS, PS,
PS, ZE, ZE, ZE, ZE, PL, PL};
static void fuzzy(float e, float ec, fuzzy_pid_t *fuzzy_pid)
{
float etemp, ectemp;
float eLefttemp, ecLefttemp; // 左隶属度
float eRighttemp, ecRighttemp; // 右隶属度
int eLeftIndex, ecLeftIndex; // 模糊位置标号
int eRightIndex, ecRightIndex;
e = RANGE(e, MINE, MAXE);
ec = RANGE(ec, MINEC, MAXEC);
e = e * KE;
ec = ec * KEC;
etemp = e > 3.0f ? 0.0f : (e < -3.0f ? 0.0f : (e >= 0.0f ? (e >= 2.0f ? 2.5f : (e >= 1.0f ? 1.5f : 0.5f)) : (e >= -1.0f ? -0.5f : (e >= -2.0f ? -1.5f : (e >= -3.0f ? -2.5f : 0.0f)))));
eLeftIndex = (int)((etemp - 0.5f) + 3);
eRightIndex = (int)((etemp + 0.5f) + 3);
eLefttemp = etemp == 0.0f ? 0.0f : ((etemp + 0.5f) - e);
eRighttemp = etemp == 0.0f ? 0.0f : (e - (etemp - 0.5f));
ectemp = ec > 3.0f ? 0.0f : (ec < -3.0f ? 0.0f : (ec >= 0.0f ? (ec >= 2.0f ? 2.5f : (ec >= 1.0f ? 1.5f : 0.5f)) : (ec >= -1.0f ? -0.5f : (ec >= -2.0f ? -1.5f : (ec >= -3.0f ? -2.5f : 0.0f)))));
ecLeftIndex = (int)((ectemp - 0.5f) + 3);
ecRightIndex = (int)((ectemp + 0.5f) + 3);
ecLefttemp = ectemp == 0.0f ? 0.0f : ((ectemp + 0.5f) - ec);
ecRighttemp = ectemp == 0.0f ? 0.0f : (ec - (ectemp - 0.5f));
/*************************************反模糊*************************************/
fuzzy_pid->kp = (eLefttemp * ecLefttemp * fuzzyRuleKp[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKp[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKp[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKp[eRightIndex][ecRightIndex]);
fuzzy_pid->ki = (eLefttemp * ecLefttemp * fuzzyRuleKi[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKi[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKi[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKi[eRightIndex][ecRightIndex]);
fuzzy_pid->kd = (eLefttemp * ecLefttemp * fuzzyRuleKd[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKd[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKd[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKd[eRightIndex][ecRightIndex]);
// 对解算出的KP,KI,KD进行量化映射
fuzzy_pid->kp = fuzzy_pid->kp * fuzzy_pid->kup;
fuzzy_pid->ki = fuzzy_pid->ki * fuzzy_pid->kui;
fuzzy_pid->kd = fuzzy_pid->kd * fuzzy_pid->kud;
}
static void smooth_init(smart_pid_t *pid, BOOL sm_open, float maxTarget)
{
if (!pid)
return;
pid->sm_open = sm_open;
pid->maxTarget = maxTarget;
}
static void smooth_target(smart_pid_t *pid, float *target)
{
if ((!pid) || (!target))
return;
if (!pid->maxTarget)
return;
float sm_step = (pid->maxTarget) * 0.1f;
float k = 0.0f;
if (fabs(pid->target - *target) <= sm_step)
{
pid->target = *target;
}
else
{
if (pid->target - *target > 0)
{
k = -1.0f;
}
else if (pid->target - *target < 0)
{
k = 1.0f;
}
else
{
k = 0.0f;
}
pid->target += k * sm_step;
}
}
static void smart_pid_init(smart_pid_t *pid, float *duty, float *kp, float *ki, float *kd, float *errorDead, float *iDetachCondation, float *maxOut)
{
if ((!pid) || (!duty) || (!kp) || (!ki) || (!kd) || (!iDetachCondation) || (!maxOut))
return;
pid->duty = *duty;
pid->kp = *kp;
pid->ki = *ki;
pid->kd = *kd;
pid->errorDead = *errorDead;
pid->iDetachCondation = *iDetachCondation;
pid->maxOutput = *maxOut;
}
void cascade_pid_init(cascade_pid_t *pid, smart_pid_t *pid_outer, smart_pid_t *pid_inner)
{
smooth_init(&pid->outer, pid_outer->sm_open, pid_outer->maxTarget);
smooth_init(&pid->inner, pid_inner->sm_open, pid_inner->maxTarget);
smart_pid_init(&pid->outer, &pid_outer->duty, &pid_outer->kp, &pid_outer->ki, &pid_outer->kd, &pid_outer->errorDead, &pid_outer->iDetachCondation, &pid_outer->maxOutput);
smart_pid_init(&pid->inner, &pid_inner->duty, &pid_inner->kp, &pid_inner->ki, &pid_inner->kd, &pid_inner->errorDead, &pid_inner->iDetachCondation, &pid_inner->maxOutput);
}
void smart_pid_calc(smart_pid_t *pid, float *target, float *feedback)
{
// 将旧error存起来
pid->lastError = pid->error;
// 平滑处理目标值
if (pid->sm_open == TRUE)
smooth_target(pid, target);
else
pid->target = *target;
// 计算新error
pid->error = pid->target - *feedback;
if (fabs(pid->error) <= pid->errorDead)
pid->error = 0.0f;
// 计算误差变化
pid->dError = pid->error - pid->lastError;
// 选用模糊规则
if (pid->fuzzy_open == TRUE)
{
fuzzy(pid->error, pid->dError, &pid->fuzzy_pid);
}
// 计算微分
float dout = pid->dError * (pid->kd + pid->fuzzy_pid.kd);
// 计算比例
float pout = pid->error * (pid->kp + pid->fuzzy_pid.kp);
// 积分分离
if (fabs(pid->error) <= pid->iDetachCondation)
{
pid->integral += pid->error * (pid->ki + pid->fuzzy_pid.ki);
pid->iDetach = FALSE;
}
else
{
pid->iDetach = TRUE;
pid->integral = 0;
}
// 积分限幅
if (pid->integral > pid->maxOutput)
pid->integral = pid->maxOutput;
else if (pid->integral < -pid->maxOutput)
pid->integral = -pid->maxOutput;
// 计算输出
pid->output = pout + dout + pid->integral * (!pid->iDetach);
// 输出限幅
if (pid->output > pid->maxOutput)
pid->output = pid->maxOutput;
else if (pid->output < -pid->maxOutput)
pid->output = -pid->maxOutput;
}
void cascade_pid_calc(struct CascadePID *pid, float *outerRef, float *outerFdb, float *innerFdb)
{
// 外环位置控制
smart_pid_calc(&pid->cascade_pid.outer, outerRef, outerFdb);
// 内环速度控制
smart_pid_calc(&pid->cascade_pid.inner, &pid->cascade_pid.outer.output, innerFdb);
// 内环输出就是串级PID的输出
// pid->cascade_pid.output = pid->cascade_pid.inner.output;
pid->cascade_pid.output = pid->cascade_pid.outer.output;
}
void pid_zh_constructor1(struct CascadePID *pid)
{
pid->smart_pid_init = smart_pid_init;
pid->smart_pid_calc = smart_pid_calc;
pid->cascade_pid_init = cascade_pid_init;
pid->cascade_pid_calc = cascade_pid_calc;
}
/*
L[1] -> 1/s
L[t] -> 1/s^2
L[1/2t^2] -> 1/s^3
G(s) = k/(TS+1)
G(s) = k/S^i(TS+1)
i = 0 0 i = 1 I I = 2 II
G(s)
G闭(s) = G(s) / 1 + G(s)
R(s) = 1/1+G(s)
limf(t) = lim s*G(s)
t-> s->0
o型系统r/s
e = lim f(t) = lim s*G(s) = G(s) = k/1+G(s) = k/ 1 + (Ts+1+k)..
t-> s->0 s->0 s->0 = s->0
*
*线
*
tr td tp
G(s) = 1/ s^2 *(2ξwn)s + wn^2
ξ:
wn = 1/T:
K T
RC电路
Gs = 1/Ts+1
EPM气压控制等主导环节
G(s) = wn^2/ s*( s + 2ξwn)
G(s) = wn^2/ (s^2 + (2ξwn)s + wn^2)
ξ
Wn
0 < ξ < 1
s^2 + (2ξwn)s + wn^2 = 0
I型系统来消除静差PID控制器
EPM本身的控制特性线性度差
EMP本身的控制线性度差EPM性能的不足
EPM IP开度大小决定EPM本身的不足
:
PID
    u(t) = k*(e(t) +e(t)dt + de(t)dt)
u(n) = k*(e(n) +  e(n)/Ti + Td*(e(n) - e(n-1)))
:PID + PD控制
    [-5%,5%] PID < 0.3%
    [-100%,5%] [5%,100%] PD控制
   
    <= 5%
              > 5%
   
              >= 100%   =  100%
              <= -100% = -100%
    : 60%
    : < 60%
    ->->,t0 -> -> -> ,t1
      <|
    Tu
    : Ku = - / ( - )
    Kp = 0.8 * Ku
          Ti = 0.6 * Tu
          Td = 0.125 * Tu
          Ki = Kp / Ti = Kp / (0.6 * Tu)
          Kd = Kp * Td = Kp * 0.125 * Td
    s1:AD值
    s2:AD值
    v1:
    v2:  
    v:
a
t:
v = (s2 - s1) / t
a = (v2 - v1) / t
   
    PID
    = -
    =
= Kp*  +  Ki *  + Kd *
   
   
      = -
      = -
      = Kp1*  +  Ki1 *  + Kd1 *
      = -
      = -
      = Kp2*  +  Ki2 *  + Kd2 *
???
automatic control theory
control system
linear and nonlinear system
线
modelling
analysis
线
L变换 Z变换
problems
time consuming
nyquist
0
root locus
G(s) = k/s(s+1)
线
p-n/n-m
lambda
lambda
tao/t
PI D
PID
makefile
1->->
#source object target
SRC := *.c
OBJ := *.o
TARGET := mqtt_test
#compile library include
CC := cc
LIB:= -lpthread
LDFLAG := -L. libmosquitto.so
CFLAG :=
CXXFLAG :=
#link
$(TARGET):$(OBJ)
$(CC) -o $(TARGET) $(BOJ) $(LIB) $(LDFLAG)
$(CC) -o $@ $^ $(LIB) $(LDFLAG)
$(OBJ):$(SRC)
$(CC) -c $(SRC)
#all
all:$(TARGET)
$(CC) -o $(TARGET) $(SRC) $(LIB) $(LDFLAG)
#clean
clean:
rm -rf *.o $(TARGET)
*/
// ————————————————————————————————————
/*
typedef struct
{
int argc;
char **argv;
int sock;
}st_app_param;
const char *g_version = "V1.0.0 24.08.16";
int child_mian_ini()
{
int i_re;
i_re = gw_support_init();
if(i_re < 0)
{
printf("解析支持文件错误.\n");
return -1;
}
i_re = gw_config_init();
if(i_re < 0)
{
printf("解析配置文件错误.\n");
return -1;
}
i_re = gw_data_init();
if(i_re < 0)
{
printf("数据初始化错误.\n");
return -1;
}
i_re = gw_modol_init();
if(i_re < 0)
{
printf("解析模型文件错误.\n");
return -1;
}
i_re = gw_stat_init();
if(i_re < 0)
{
printf("数据统计初始化错误.\n");
return -1;
}
i_re = gw_process_init();
if(i_re < 0)
{
printf("规约初始化错误.\n");
return -1;
}
i_re = gw_manage_init();
if(i_re < 0)
{
printf("界面通讯初始化错误.\n");
return -1;
}
pthread_create(tid,thread_task1,p_param);
while(1)
{
get_sys_time();
sleep(1);
}
}
int child_main(int argc,char **argv)
{
if((argc == 2)&&(strcasecmp(argv[1],"-version") == 0))
{
printf("version [%s]",g_version);
}
child_mian_ini();
}
static int run_child_process_thread((void *)param)
{
st_app_param *p_param;
p_param = param;
chlid_main(p_parm->argc,p_param->argv);
}
static int run_child_process(int argc,char **argv,int sock)
{
int i_re;
pthread_t *tid;
st_app_param p_param;
p_param.argc = argc;
p_param.argv = argv;
p_param.sock = sock;
pthread_creat(tid,run_child_process_thread,(void *)p_param);
i_re = recv(sock,..,0);//阻塞接收
send(sock,'q',0);
}
int main(int argc,char**argv)
{
int i_re,fd,pid;
char lock_file[16];
socketpair sockpaire[2];
sprintf(lock_file,"%s.lock",argv[0]);
fd = open(lock_file,_O_WRONLY|_EXC,0777);
if(fd < 0)
{
printf("应用:[%s]已运行.\n",argv[0]);
return 0;
}
sockpaire[0] = -1;
while(1)
{
i_re = socketpaire(sockpaire,AN_UNIX,..);
if(i_re < 0)
{
printf("[%d]创建sockpaire失败.\n",getpid());
return 0;
}
pid = fork();
if(pid < 0)
{
printf("[%d]创建进程失败.\n",getpid());
close(sockpaire[0]);
sockpaire[0] = -1
close(sockpaire[1]);
return 0;
}
if(pid == 0)//child process
{
close(sockpair[0]);
return run_child_process(argc,argv,sockpair[1]);
}
printf("[%d]创建子进程成功.\n",getpid());
close(sockpair[1]);
while(1)//father process
{
i_re = recv(sockpaire[0],....,0);//阻塞接收
if(i_re <= 0)
{
//连接中断,重连
sockpaire[0] = -1;
break;
}
else
{
//正常退出
goto EXIT:
}
}
EXIT:
//退出操作
if(sockpaire[0] == -1)
{
//有子进程,关闭子进程
}
close(fd);
unlink(file_lock);
}
}
*/

View File

@ -0,0 +1,63 @@
/*
* @Author: zxm5337@163.com
* @Date: 2024-06-25 10:26:36
* @LastEditors: DaMingSY zxm5337@163.com
* @LastEditTime: 2024-09-03 09:19:23
* @FilePath: \controller-v2\User\lib\control\custom\cascade_pid_zh.h
* @Description: ,`customMade`, koroFileHeader查看配置 : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#ifndef __CASCADE_PID_ZH__
#define __CASCADE_PID_ZH__
#include "data_type_def.h"
typedef enum
{
TARGET_DIR_ADD,
TARGET_DIR_SUB,
} target_dir_e;
typedef struct
{
float kp;
float ki;
float kd;
float kup;
float kui;
float kud;
} fuzzy_pid_t;
typedef struct
{
BOOL iDetach, sm_open, fuzzy_open;
float feedBack;
float target, maxTarget;
float duty, kp, ki, kd;
float error, dError, lastError, errorDead;
float integral, iDetachCondation;
float output, maxOutput;
fuzzy_pid_t fuzzy_pid;
} smart_pid_t;
typedef struct
{
smart_pid_t inner;
smart_pid_t outer;
float output;
} cascade_pid_t;
typedef struct CascadePID
{
void (*smart_pid_init)(smart_pid_t *pid, float *duty, float *kp, float *ki, float *kd, float *errorDead, float *iDetachCondation, float *maxOut);
void (*cascade_pid_init)(cascade_pid_t *pid, smart_pid_t *pid_outer, smart_pid_t *pid_inner);
void (*smart_pid_calc)(smart_pid_t *pid, float *target, float *feedback);
void (*cascade_pid_calc)(struct CascadePID *pid, float *outerRef, float *outerFdb, float *innerFdb);
smart_pid_t smart_pid;
cascade_pid_t cascade_pid;
} pid_zh1_t; // PID_t;
extern void pid_zh_constructor1(struct CascadePID *pid);
#endif

View File

@ -0,0 +1,26 @@
# 架构
|文件|路径| <div style="width:300px">说明</div>|
|:--|:--|:--|
|pid|User\lib\control\src|PID算法模块|
|execute|User\application\src|执行器|
|app_flow|User|任务流程控制|
## APP_FLOW任务流程控制
> adjust_inspection 中在没有检测到调试信号时执行<b style="color:blue">execute_dac,EXECUTE_PLAN</b>定义了需要执行的算法任务计划
## PID算法模块
文件内容:
|文件| <div style="width:300px">说明</div>|
|:--|:--|
|pid.c|构造算法的入口|
|pid_common.c|普通算法|
|pid_neural.c|神经网络算法|
|pid_fuzzy.c|模糊算法|
<b style="color:blue">custom 目录下为各自算法实现</b>
## EXECUTE执行器
> execute_pid_init中定义了初始化参数
> execute_dac中定义了执行器

View File

@ -0,0 +1,252 @@
#ifndef __PID_H__
#define __PID_H__
#include "lib.h"
#include "pid_auto_tune.h"
#include "pid_c.h"
#include "pid_g.h"
#include "pid_x.h"
#include "pid_zh.h"
#include "pid_zh1.h"
#include "pid_hd.h"
#define INCOMPLETE_DIFFEREN 1 // 不完全微分
typedef enum
{
PID_SUB_TYPE_POSITION = 1, // 位置式
PID_SUB_TYPE_INCREMENT = 2, // 增量式
} pid_sub_type_e;
typedef enum
{
DEAD_ZONE_BOTH = 0, // 正负都可以
DEAD_ZONE_POSITIVE = 1, // 正数
DEAD_ZONE_NEGATIVE = 2, // 负数
} deadzone_e;
typedef enum
{
// PID自整定
PID_TYPE_AUTO_TUNE,
// 通用PID
PID_TYPE_COMMON,
// 神经PID
PID_TYPE_NEURAL,
// 模糊PID
PID_TYPE_FUZZY,
// 以下是自定义PID
// cj PID
PID_TYPE_CUSTOM_CAO,
// gp jPID
PID_TYPE_CUSTOM_GAO,
// xsh PID
PID_TYPE_CUSTOM_XU,
// zxm PID
PID_TYPE_CUSTOM_ZHANG,
// hangdian PID
PID_TYPE_CUSTOM_HANGDIAN,
} pid_type_e;
typedef struct
{
float32 ref;
float32 feedback;
float32 pre_feedback;
float32 e_0; // 当前误差
float32 e_1; // 上一次误差
float32 kp;
float32 ki;
float32 kd;
float32 err_limit;
BOOL detach;
float32 err_dead;
// 不完全微分方式在微分环节采用了低通滤波有效地提高了微分项的特性。其中α的取值是一个0~1之间的数。两个极限值在0时其实就是没有滤波的普通微分环节而取1时则没有微分作用。所以α的取值对不完全微分的效果是至关重要的一般要根据被控对象的特性来确定
float32 alpha;
float32 lastdev;
float32 out;
float32 out_max;
float32 out_min;
float32 sv_range;
float32 iout; // 积分输出
BOOL sm;
BOOL ki_enable;
BOOL kd_enable;
float32 deviation; // 纠正系统误差造成的影响作用于死区大于0需要补偿小于0需要反向补偿
BOOL in_dead_zone; // 是否在死区内
} pid_common_position_t; // 位置式PID
typedef struct
{
float32 ref; // 目标设定值
float32 feedback; // 传感器采集值
float32 out; // PID计算结果
float32 kp;
float32 ki;
float32 kd;
float32 e_0; // 当前误差
float32 e_1; // 上一次误差
float32 e_2; // 上上次误差
float32 err_dead;
float32 deviation; // 纠正系统误差造成的影响,作用于死区
float32 out_max; // 输出限幅
float32 out_min; // 输出限幅
float32 sum_iterm;
float32 iout; // 积分输出
float32 alpha; // 不完全微分参数
float32 lastdev; // 不完全微分参数
BOOL sm;
BOOL ki_enable;
BOOL kd_enable;
float32 sv_range;
// 没有用的
float32 err_limit;
BOOL detach;
} pid_common_increment_t; // 增量式PID
typedef struct PID_COMMON
{
uint8_t type;
/* 设置PID三个参数 */
void (*set_ctrl_prm)(struct PID_COMMON *self, float32 kp, float32 ki, float32 kd);
/* 设置积分范围 */
void (*set_integral_prm)(struct PID_COMMON *self, float32 integral_up, float32 integral_low);
/* 控制接口 */
float32 (*PID)(struct PID_COMMON *self, float32 err);
/* in value */
float32 err;
/* out value */
float32 out;
union
{
pid_common_position_t position;
pid_common_increment_t increment;
} pri_u;
} pid_common_t; // 通用PID
typedef struct PID_NEURAL
{
uint8_t type;
/* 设置PID三个参数 */
void (*set_ctrl_prm)(struct PID_NEURAL *self, float32 minimum, float32 maximum);
/* 设置输出范围 */
void (*set_out_prm)(struct PID_NEURAL *self, float32 minimum, float32 maximum);
/* 控制接口 */
float32 (*PID)(struct PID_NEURAL *self, float32 target, float32 feedback);
struct
{
float32 setpoint; /*设定值*/
float32 kcoef; /*神经元输出比例*/
float32 kp; /*比例学习速度*/
float32 ki; /*积分学习速度*/
float32 kd; /*微分学习速度*/
float32 lasterror; /*前一拍偏差*/
float32 preerror; /*前两拍偏差*/
float32 deadband; /*死区*/
float32 result; /*输出值*/
float32 output; /*百分比输出值*/
float32 maximum; /*输出值的上限*/
float32 minimum; /*输出值的下限*/
float32 wp; /*比例加权系数*/
float32 wi; /*积分加权系数*/
float32 wd; /*微分加权系数*/
} pri;
} pid_neural_t; // 神经PID
typedef struct
{
float32 kp;
float32 ki;
float32 kd;
float32 kup;
float32 kui;
float32 kud;
float32 maxe; // 非线性区间最大值
float32 mine; // 非线性区间最小值
} FUZZY_PID_t;
// 模糊PID
typedef struct PID_FUZZY
{
/* 设置PID三个参数 */
void (*set_ctrl_prm)(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 deviation,
float32 out_min, float32 out_max); // 设置PID参数
void (*set_error_max_min)(struct PID_FUZZY *self, float32 mine, float32 maxe); // 设置非线性区间值
void (*update_ctrl_prm)(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead,
float32 out_min, float32 out_max); // 更新PID参数
void (*set_range)(struct PID_FUZZY *self, float32 out_min, float32 out_max); // 更新最大最小值
void (*set_cfg)(struct PID_FUZZY *self, float32 max_err, BOOL mode); // 配置PID模式,默认不使用积分分离
void (*set_smooth_enable)(struct PID_FUZZY *self, BOOL enable, float32 sv_range); // 设置平滑范围
void (*set_iout)(struct PID_FUZZY *self, float32 iout); // 设置积分输出
void (*set_err_dead)(struct PID_FUZZY *self, float32 err_dead); // 设置死区
void (*set_kp)(struct PID_FUZZY *self, float32 kp);
void (*set_ki_enable)(struct PID_FUZZY *self, BOOL enable);
void (*set_ki)(struct PID_FUZZY *self, float32 ki);
// 微分开启使能
void (*set_kd_enable)(struct PID_FUZZY *self, BOOL enable);
void (*set_kd)(struct PID_FUZZY *self, float32 kd);
void (*set_kd_dev)(struct PID_FUZZY *self, float32 alpha);
void (*restctrl)(struct PID_FUZZY *self, float32 out); // 复位PID积分及微分控制数据
/* 控制接口 */
float32 (*execute)(struct PID_FUZZY *self, float32 target, float32 feedback);
BOOL(*in_dead_zone)
(struct PID_FUZZY *self);
union
{
pid_common_position_t position;
pid_common_increment_t increment;
} pri_u;
pid_sub_type_e sub_type; // 位置式PID增量式PID
BOOL open; // 是否使用模糊PID控制
BOOL speed_integral_enable; // 变速积分,暂时没有验证成功
deadzone_e deadzone_dir;
FUZZY_PID_t pid_params;
} pid_fuzzy_t; // 模糊PID
// PID
typedef struct
{
BOOL is_init; // 是否初始化
pid_type_e type; // 不同的算法类型模糊PID神经PID通用PID
pid_sub_type_e sub_type; // 位置式PID增量式PID
union
{
pid_common_t common;
pid_neural_t neural;
pid_fuzzy_t fuzzy;
// 自定义PID
pid_c_t cao;
pid_g_t gao;
pid_x_t xu;
pid_zh_t zhang;
pid_zh1_t zhang1;
pid_hd_t hd;
} pid_u;
pid_auto_tune_t auto_tune;
} pid_t;
// PID控制
extern void pid_constructor(pid_t *self);
// private
// 神经元PID
extern void pid_neural_constructor(struct PID_NEURAL *self);
// 模糊PID
extern void pid_fuzzy_constructor(struct PID_FUZZY *self);
#endif

View File

@ -0,0 +1,53 @@
/***
* @Author:
* @Date: 2023-07-24 11:17:55
* @LastEditors: xxx
* @LastEditTime: 2023-07-24 11:19:06
* @Description:pid自动调参 KP和震荡周期
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __PID_AUTO_TUNE_H__
#define __PID_AUTO_TUNE_H__
#include "lib.h"
typedef struct PID_AUTO_TUNE
{
// public:
void (*set_ctrl_prm)(struct PID_AUTO_TUNE *self, float32 *input, float32 *output);
int32_t (*runtime)(struct PID_AUTO_TUNE *self);
void (*set_output_step)(struct PID_AUTO_TUNE *self, int32_t step);
void (*set_control_type)(struct PID_AUTO_TUNE *self, int32_t type);
void (*set_noise_band)(struct PID_AUTO_TUNE *self, int32_t band);
void (*set_look_back)(struct PID_AUTO_TUNE *self, int32_t n);
float32 (*get_kp)(struct PID_AUTO_TUNE *self);
float32 (*get_ki)(struct PID_AUTO_TUNE *self);
float32 (*get_kd)(struct PID_AUTO_TUNE *self);
// private:
struct
{
BOOL isMax, isMin; // 运算中出现最大、最小值标志
float32 *input, *output;
float32 setpoint; // 反向控制判断值,这个值需要根据对象的实际工作值确定!是通过第一次启动时对应的输入值带入的。
int32_t noiseBand; // 判断回差,类似于施密特触发器,实际控制反向的比较值是 setpoint + noiseBand 或 setpoint - noiseBand
int32_t controlType; // 计算 PID 参数时,选择 PI 或 PID 模式,输出 Kp Ki或 Kp、Ki、Kd
BOOL running;
uint32_t peak1, peak2, lastTime; // 峰值对应的时间
int32_t sampleTime;
int32_t nLookBack;
int32_t peakType;
int32_t lastInputs[51]; // 保存的历史输入值, 改为 50 次。 by shenghao.xu
int32_t peaks[13]; // 保存的历史峰值,最多存前 12 次,对应 6个最大、6个最小。20221124 by Embedream
int32_t peakCount; // 峰值计数
int32_t peakPeriod[7]; // 保存前 6 次的最大值间隔时间 by shenghao.xu
int32_t peakMaxCount; // 最大峰值计数 by shenghao.xu
BOOL justchanged;
int32_t oStep; // 这个值是用于计算控制高低值的,以 outputStart 为中值,输出高值用 outputStart + oStep 输出低值用 outputStart - oStep
float32 outputStart; // 输出控制的基础值,这个需要结合对象特征确定,此值也是通过第一次启动时对应的输出值带入的。
float32 Ku, Pu;
} pri;
} pid_auto_tune_t;
extern void pid_auto_tune_constructor(struct PID_AUTO_TUNE *self);
#endif // __PID_AUTO_TUNE_H__

View File

@ -0,0 +1,43 @@
#include "pid.h"
#include <math.h>
// 构造函数将接口绑定
void pid_constructor(pid_t *self)
{
switch (self->type)
{
case PID_TYPE_COMMON:
/* code */
break;
case PID_TYPE_NEURAL:
pid_neural_constructor(&self->pid_u.neural);
break;
case PID_TYPE_FUZZY:
DBG_ASSERT(self->sub_type != 0 __DBG_LINE);
self->pid_u.fuzzy.sub_type = self->sub_type;
pid_fuzzy_constructor(&self->pid_u.fuzzy);
break;
case PID_TYPE_AUTO_TUNE:
pid_auto_tune_constructor(&self->auto_tune);
break;
case PID_TYPE_CUSTOM_CAO:
pid_c_constructor(&self->pid_u.cao);
break;
case PID_TYPE_CUSTOM_GAO:
pid_g_constructor(&self->pid_u.gao);
break;
case PID_TYPE_CUSTOM_XU:
pid_x_constructor(&self->pid_u.xu);
break;
case PID_TYPE_CUSTOM_ZHANG:
// pid_zh_constructor(&self->pid_u.zhang);
pid_zh_constructor1(&self->pid_u.zhang1);
break;
case PID_TYPE_CUSTOM_HANGDIAN:
pid_hd_constructor(&self->pid_u.hd);
break;
default:
break;
}
self->is_init = TRUE;
}

View File

@ -0,0 +1,230 @@
#include "pid_auto_tune.h"
#include "sys.h"
/*
, 0.1 0.2 4
*/
static void set_look_backsec(pid_auto_tune_t *self, int32_t value)
{
if (value < 2)
value = 2;
if (value > 40)
value = 40;
if (value < 40)
{
self->pri.nLookBack = 12; // 按目前实际周期约300ms、采样周期 10ms 考虑,一个周期只有 30 点,回溯 12 点即可。
self->pri.sampleTime = value * 10; // 改为 Value*10 ms 20、30、40 ~ 200ms
}
else
{
self->pri.nLookBack = 50 + value;
self->pri.sampleTime = 4000;
}
}
static void _set_ctrl_prm(struct PID_AUTO_TUNE *self, float32 *input, float32 *output)
{
self->pri.input = input;
self->pri.output = output;
self->pri.controlType = 0; // 默认为 PI 模式
self->pri.noiseBand = 1;
self->pri.running = FALSE;
self->pri.oStep = 1;
set_look_backsec(self, 8);
self->pri.lastTime = sys_millis();
}
static void _set_noise_band(struct PID_AUTO_TUNE *self, int32_t value)
{
self->pri.noiseBand = value;
}
static void _set_output_step(struct PID_AUTO_TUNE *self, int32_t value)
{
self->pri.oStep = value;
}
// * Determies if the tuning parameters returned will be PI (D=0)
// or PID. (0=PI, 1=PID)
static void _set_control_type(struct PID_AUTO_TUNE *self, int32_t value)
{
self->pri.controlType = value;
}
static void _set_look_back(struct PID_AUTO_TUNE *self, int32_t value)
{
set_look_backsec(self, value);
}
static float32 _get_kp(struct PID_AUTO_TUNE *self)
{
float32 kp = self->pri.controlType == 1 ? 0.6f * self->pri.Ku : 0.4f * self->pri.Ku;
return kp;
}
static float32 _get_ki(struct PID_AUTO_TUNE *self)
{
float32 ki = self->pri.controlType == 1 ? 1.2f * self->pri.Ku / self->pri.Pu : 0.48f * self->pri.Ku / self->pri.Pu;
return ki;
}
static float32 _get_kd(struct PID_AUTO_TUNE *self)
{
return self->pri.controlType == 1 ? 0.075f * self->pri.Ku * self->pri.Pu : 0;
}
/**
* @brief 0 - 1 - 2 -
* @return {*}
*/
static int32_t _runtime(struct PID_AUTO_TUNE *self)
{
int32_t i, iSum;
uint32_t now = sys_millis();
if ((now - self->pri.lastTime) < ((uint32_t)self->pri.sampleTime))
{
return 2; // 原来返回值为 FALSE 不符合函数定义,也无法区分,改为 2by shenghao.xu
}
// 开始整定计算
self->pri.lastTime = now;
float32 refVal = *(self->pri.input);
if (FALSE == self->pri.running) // 首次进入,初始化参数
{
self->pri.peakType = 0;
self->pri.peakCount = 0;
self->pri.peakMaxCount = 0;
self->pri.peak1 = 0;
self->pri.peak2 = 0;
self->pri.justchanged = FALSE;
self->pri.setpoint = refVal; // 不变
self->pri.running = TRUE;
self->pri.outputStart = *self->pri.output;
*self->pri.output = self->pri.outputStart + self->pri.oStep;
}
// 根据输入与设定点的关系振荡输出
if (refVal > (self->pri.setpoint + self->pri.noiseBand))
*self->pri.output = self->pri.outputStart - self->pri.oStep;
else if (refVal < (self->pri.setpoint - self->pri.noiseBand))
*self->pri.output = self->pri.outputStart + self->pri.oStep;
// bool isMax=TRUE, isMin=TRUE;
self->pri.isMax = TRUE;
self->pri.isMin = TRUE;
// id peaks
/*
isMaxisMin
*/
for (i = self->pri.nLookBack - 1; i >= 0; i--)
{
int32_t val = self->pri.lastInputs[i];
if (self->pri.isMax)
self->pri.isMax = (refVal > val); // 第一次是新输入和缓存最后一个值比较,如果大于,则前面的值均判是否大于
if (self->pri.isMin)
self->pri.isMin = (refVal < val); // 第一次是新输入和缓存最后一个值比较,如果小于,则前面的值均判是否小于
self->pri.lastInputs[i + 1] = self->pri.lastInputs[i]; // 每采样一次,将输入缓存的数据向后挪一次
}
self->pri.lastInputs[0] = refVal; // 新采样的数据放置缓存第一个单元。
/*
nLookBack
nLookBack
1peaks[] peakCount +1
2
3 Ku
1121266
2 6 Pu
*/
if (self->pri.isMax)
{
if (self->pri.peakType == 0)
self->pri.peakType = 1; // 首次最大值,初始化
if (self->pri.peakType == -1) // 如果前一次为最小值,则标识目前进入最大值判断
{
self->pri.peakType = 1; // 开始最大值判断
self->pri.peakCount++; // 峰值计数 by shenghao.xu
self->pri.justchanged = TRUE; // 标识峰值转换
if (self->pri.peak2 != 0) // 已经纪录一次最大峰值对应时间后,开始记录峰值周期 by shenghao.xu
{
self->pri.peakPeriod[self->pri.peakMaxCount] = (int32_t)(self->pri.peak1 - self->pri.peak2); // 最大峰值间隔时间(即峰值周期)
self->pri.peakMaxCount++; // 最大峰值计数
}
self->pri.peak2 = self->pri.peak1; // 刷新上次最大值对应时间
}
self->pri.peak1 = now; // 保存最大值对应时间 peak1
self->pri.peaks[self->pri.peakCount] = refVal; // 保存最大值
} // 此段代码可以保证得到的是真正的最大值因为peakType不变则会不断刷新最大值
else if (self->pri.isMin)
{
if (self->pri.peakType == 0)
self->pri.peakType = -1; // 首次最小值,初始化
if (self->pri.peakType == 1) // 如果前一次是最大值判断,则转入最小值判断
{
self->pri.peakType = -1; // 开始最小值判断
self->pri.peakCount++; // 峰值计数
self->pri.justchanged = TRUE;
}
if (self->pri.peakCount < 10)
self->pri.peaks[self->pri.peakCount] = refVal; // 只要类型不变,就不断刷新最小值
}
/* by shenghao.xu
2
1 12 ( 13 )
2
35
4 10 9 Ku A
5 5 Pu
*/
if (self->pri.justchanged && self->pri.peakCount == 12)
{
// we've transitioned. check if we can autotune based on the last peaks
iSum = 0;
for (i = 2; i <= 10; i++)
iSum += ABS(self->pri.peaks[i] - self->pri.peaks[i + 1]);
iSum /= 9; // 取 9 次峰峰值平均值
self->pri.Ku = (float32)(4 * (2 * self->pri.oStep)) / (iSum * 3.14159); // 用峰峰平均值计算 Ku
iSum = 0;
for (i = 1; i <= 5; i++)
iSum += self->pri.peakPeriod[i];
iSum /= 5; // 计算峰值的所有周期平均值
self->pri.Pu = (float32)(iSum) / 1000; // 用周期平均值作为 Pu单位
*self->pri.output = 0;
self->pri.running = FALSE;
return 1;
}
self->pri.justchanged = FALSE;
return 0;
}
void pid_auto_tune_constructor(struct PID_AUTO_TUNE *self)
{
self->set_ctrl_prm = _set_ctrl_prm;
self->runtime = _runtime;
self->set_output_step = _set_output_step;
self->set_control_type = _set_control_type;
self->set_noise_band = _set_noise_band;
self->set_look_back = _set_look_back;
self->get_kp = _get_kp;
self->get_ki = _get_ki;
self->get_kd = _get_kd;
}

View File

View File

@ -0,0 +1,896 @@
#include "pid.h"
#include <math.h>
// 定义死区枚举
#define DEADZONE DEAD_ZONE_POSITIVE
// 模糊集合
#define NL -3
#define NM -2
#define NS -1
#define ZE 0
#define PS 1
#define PM 2
#define PL 3
// 定义偏差E的范围因为设置了非线性区间误差在10时才开始进行PID调节这里E的范围为10
#define MAXE (30)
#define MINE (-MAXE)
// 定义EC的范围因为变化非常缓慢每次的EC都非常小这里可以根据实际需求来调整
#define MAXEC (30)
#define MINEC (-MAXEC)
// 定义e,ec的量化因子
#define KE 3 / MAXE
#define KEC 3 / MAXEC
// 定义输出量比例因子
#define KUP 3.0f // 这里只使用了模糊PID的比例增益
#define KUI 0.0f
#define KUD 0.0f
static const float32 fuzzyRuleKp[7][7] = {
PL, PL, PM, PL, PS, PM, PL,
PL, PM, PM, PM, PS, PM, PL,
PM, PS, PS, PS, PS, PS, PM,
PM, PS, ZE, ZE, ZE, PS, PM,
PS, PS, PS, PS, PS, PM, PM,
PM, PM, PM, PM, PL, PL, PL,
PM, PL, PL, PL, PL, PL, PL};
static const float32 fuzzyRuleKi[7][7] = {
NL, NL, NL, NL, NM, NL, NL,
NL, NL, NM, NM, NM, NL, NL,
NM, NM, NS, NS, NS, NM, NM,
NM, NS, ZE, ZE, ZE, NS, NM,
NM, NS, NS, NS, NS, NM, NM,
NM, NM, NS, NM, NM, NL, NL,
NM, NL, NM, NL, NL, NL, NL};
static const float32 fuzzyRuleKd[7][7] = {
PS, PS, ZE, ZE, ZE, PL, PL,
NS, NS, NS, NS, ZE, NS, PM,
NL, NL, NM, NS, ZE, PS, PM,
NL, NM, NM, NS, ZE, PS, PM,
NL, NM, NS, NS, ZE, PS, PS,
NM, NS, NS, NS, ZE, PS, PS,
PS, ZE, ZE, ZE, ZE, PL, PL};
static void fuzzy(float32 e, float32 ec, FUZZY_PID_t *fuzzy_pid)
{
float32 etemp, ectemp;
float32 eLefttemp, ecLefttemp; // ec,e左隶属度
float32 eRighttemp, ecRighttemp;
int eLeftIndex, ecLeftIndex; // 模糊位置标号
int eRightIndex, ecRightIndex;
e = RANGE(e, fuzzy_pid->mine, fuzzy_pid->maxe);
ec = RANGE(ec, MINEC, MAXEC);
e = e * KE;
ec = ec * KEC;
etemp = e > 3.0f ? 0.0f : (e < -3.0f ? 0.0f : (e >= 0.0f ? (e >= 2.0f ? 2.5f : (e >= 1.0f ? 1.5f : 0.5f)) : (e >= -1.0f ? -0.5f : (e >= -2.0f ? -1.5f : (e >= -3.0f ? -2.5f : 0.0f)))));
eLeftIndex = (int)((etemp - 0.5f) + 3); //[-3,3] -> [0,6]
eRightIndex = (int)((etemp + 0.5f) + 3);
eLefttemp = etemp == 0.0f ? 0.0f : ((etemp + 0.5f) - e); //
eRighttemp = etemp == 0.0f ? 0.0f : (e - (etemp - 0.5f));
ectemp = ec > 3.0f ? 0.0f : (ec < -3.0f ? 0.0f : (ec >= 0.0f ? (ec >= 2.0f ? 2.5f : (ec >= 1.0f ? 1.5f : 0.5f)) : (ec >= -1.0f ? -0.5f : (ec >= -2.0f ? -1.5f : (ec >= -3.0f ? -2.5f : 0.0f)))));
ecLeftIndex = (int)((ectemp - 0.5f) + 3); //[-3,3] -> [0,6]
ecRightIndex = (int)((ectemp + 0.5f) + 3);
ecLefttemp = ectemp == 0.0f ? 0.0f : ((ectemp + 0.5f) - ec);
ecRighttemp = ectemp == 0.0f ? 0.0f : (ec - (ectemp - 0.5f));
/*************************************反模糊*************************************/
fuzzy_pid->kp = (eLefttemp * ecLefttemp * fuzzyRuleKp[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKp[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKp[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKp[eRightIndex][ecRightIndex]);
fuzzy_pid->ki = (eLefttemp * ecLefttemp * fuzzyRuleKi[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKi[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKi[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKi[eRightIndex][ecRightIndex]);
fuzzy_pid->kd = (eLefttemp * ecLefttemp * fuzzyRuleKd[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKd[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKd[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKd[eRightIndex][ecRightIndex]);
// 对解算出的KP,KI,KD进行量化映射
fuzzy_pid->kp = fuzzy_pid->kp * fuzzy_pid->kup;
fuzzy_pid->ki = fuzzy_pid->ki * fuzzy_pid->kui;
fuzzy_pid->kd = fuzzy_pid->kd * fuzzy_pid->kud;
}
/**
* @brief SV平滑给定,0.10-1
* @param {PID_FUZZY} *self
* @param {float32} target_sv
* @return {*}
* @note
*/
static void smooth_setpoint(struct PID_FUZZY *self, float32 target_sv)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
float32 stepIn = (pri->sv_range) * 0.1f;
float32 kFactor = 0.0f;
if (fabs(pri->ref - target_sv) <= stepIn)
{
pri->ref = target_sv;
}
else
{
if (pri->ref - target_sv > 0)
{
kFactor = -1.0f;
}
else if (pri->ref - target_sv < 0)
{
kFactor = 1.0f;
}
else
{
kFactor = 0.0f;
}
pri->ref = pri->ref + kFactor * stepIn;
}
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
float32 stepIn = (pri->sv_range) * 0.1f;
float32 kFactor = 0.0f;
if (fabs(pri->ref - target_sv) <= stepIn)
{
pri->ref = target_sv;
}
else
{
if (pri->ref - target_sv > 0)
{
kFactor = -1.0f;
}
else if (pri->ref - target_sv < 0)
{
kFactor = 1.0f;
}
else
{
kFactor = 0.0f;
}
pri->ref = pri->ref + kFactor * stepIn;
}
}
}
// 变速积分
static float32 changing_integral_rate(struct PID_FUZZY *self)
{
float32 err = 0, iout = 0;
float32 err_1 = 1, // 误差下限
err_2 = 10; // 误差上限
float32 index = 0;
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
err = pri->e_0;
iout = pri->iout;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
err = pri->e_0;
iout = pri->iout;
}
if (err * iout > 0) // 判断积分是否为积累趋势
{
if (ABS(err) <= err_1)
{
index = 1; // 完整积分
}
else if (ABS(err) <= (err_1 + err_2))
{
// 使用线性函数过渡
index = (float)(err_2 - ABS(err) + err_1) / err_2;
}
else
{
index = 0;
}
}
return index;
}
/*封装模糊接口*/
static void compensate(float32 e, float32 ec, FUZZY_PID_t *fuzzy_d)
{
fuzzy(e, ec, fuzzy_d);
}
/**
* @brief
* @param {PID_FUZZY} *self
* @param {float32} out_min
* @param {float32} out_max
* @return {*}
* @note
*/
static void _set_range(struct PID_FUZZY *self, float32 out_min, float32 out_max)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->out_max = out_max;
pri->out_min = out_min;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->out_max = out_max;
pri->out_min = out_min;
}
}
/**
* @brief
* @param {PID_FUZZY} *self
* @param {float32} err_dead
* @return {*}
* @note
*/
static void _set_err_dead(struct PID_FUZZY *self, float32 err_dead)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->err_dead = err_dead;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->err_dead = err_dead;
}
}
static void _set_iout(struct PID_FUZZY *self, float32 iout)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->iout = iout;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->iout = iout;
}
}
static void _set_kp(struct PID_FUZZY *self, float32 kp)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->kp = kp;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->kp = kp;
}
}
/**
* @brief 使
* @param {PID_FUZZY} *self
* @param {BOOL} enable
* @return {*}
* @note
*/
// static void _set_ki_enable(struct PID_FUZZY *self, BOOL enable)
// {
// pri->ki_enable = enable;
// }
/**
* @brief 使
* @param {PID_FUZZY} *self
* @param {BOOL} enable
* @return {*}
* @note
*/
static void _set_kd_enable(struct PID_FUZZY *self, BOOL enable)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->kd_enable = enable;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->kd_enable = enable;
}
}
static void _set_kd(struct PID_FUZZY *self, float32 kd)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->kd = kd;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->kd = kd;
}
}
/**
* @brief
* @param {PID_FUZZY} *self
* @param {float32} alpha
* @return {*}
* @note alpha范围0-1,
*/
static void _set_kd_dev(struct PID_FUZZY *self, float32 alpha)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->alpha = alpha;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->alpha = alpha;
}
}
static void _set_ki_enable(struct PID_FUZZY *self, BOOL enable)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->ki_enable = enable;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->ki_enable = enable;
}
}
static void _set_ki(struct PID_FUZZY *self, float32 ki)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->ki = ki;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->ki = ki;
}
}
/*
* Function:使
* parameter:*pid需要配PID参数结构指针sv_range控制范围sv的范围
* return:
*/
static void _set_smooth_enable(struct PID_FUZZY *self, BOOL enable, float32 sv_range)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->sm = enable;
pri->sv_range = sv_range;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->sm = enable;
pri->sv_range = sv_range;
}
}
// 设置控制参数
static void _set_ctrl_prm(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 deviation,
float32 out_min, float32 out_max)
{
self->open = TRUE;
self->pid_params.kup = KUP;
self->pid_params.kui = KUI;
self->pid_params.kud = KUD;
self->pid_params.mine = MINE;
self->pid_params.maxe = MAXE;
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
osel_memset((uint8_t *)pri, 0, sizeof(pid_common_position_t));
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->deviation = deviation;
pri->out_max = out_max;
pri->out_min = out_min;
pri->detach = FALSE;
pri->sm = FALSE;
pri->ki_enable = TRUE;
pri->alpha = 0.25;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
osel_memset((uint8_t *)pri, 0, sizeof(pid_common_increment_t));
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->deviation = deviation;
pri->out_max = out_max;
pri->out_min = out_min;
pri->ki_enable = TRUE;
pri->alpha = 0.25;
}
}
static void _set_error_max_min(struct PID_FUZZY *self, float32 mine, float32 maxe)
{
self->pid_params.mine = mine;
self->pid_params.maxe = maxe;
}
static void _update_ctrl_prm(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead,
float32 out_min, float32 out_max)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
pri->detach = FALSE;
pri->sm = FALSE;
if (kd > 0)
{
pri->kd_enable = TRUE;
}
else
{
pri->kd_enable = FALSE;
}
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->kp = kp;
pri->ki = ki;
pri->kd = kd;
pri->err_dead = err_dead;
pri->out_max = out_max;
pri->out_min = out_min;
if (kd > 0)
{
pri->kd_enable = TRUE;
}
else
{
pri->kd_enable = FALSE;
}
}
}
/**
* @brief 0+PID,PID
* @param {PID_FUZZY} *self
* @param {float32} max_err
* @param {BOOL} mode
* @return {*}
*/
static void _set_cfg(struct PID_FUZZY *self, float32 max_err, BOOL mode)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->err_limit = max_err;
pri->detach = mode == FALSE ? FALSE : TRUE;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->err_limit = max_err;
pri->detach = mode == FALSE ? FALSE : TRUE;
}
}
/**
* @brief
*
* PID_FUZZY
*
* @param self PID_FUZZY
*
* @return TRUE FALSE
*/
static BOOL _in_dead_zone(struct PID_FUZZY *self)
{
float32 deviation = 0.0f;
float32 err_dead = 0.0f;
float32 err = 0.0f;
float32 offset = 0.0f;
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
deviation = pri->deviation;
err_dead = pri->err_dead;
err = pri->feedback - pri->ref;
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
deviation = pri->deviation;
err_dead = pri->err_dead;
err = pri->feedback - pri->ref;
}
offset = err + deviation;
if (self->deadzone_dir == DEAD_ZONE_POSITIVE)
{
if (offset >= 0 && offset <= ABS(err_dead))
{
return TRUE;
}
else
{
return FALSE;
}
}
else if (self->deadzone_dir == DEAD_ZONE_NEGATIVE)
{
if (offset <= 0 && offset >= err_dead)
{
return TRUE;
}
else
{
return FALSE;
}
}
else
{
if (ABS(offset) <= ABS(err_dead))
{
return TRUE;
}
else
{
return FALSE;
}
}
}
static float32 position_pid(struct PID_FUZZY *self, float32 target, float32 feedback)
{
float32 error = 0;
float32 ec = 0;
float32 kd = 0;
float32 thisdev = 0;
pid_common_position_t *pri = &self->pri_u.position;
kd = pri->kd;
/*获取期望值与实际值,进行偏差计算*/
if (pri->sm == 1)
{
smooth_setpoint(self, target);
}
else
{
pri->ref = target;
}
pri->feedback = feedback;
error = pri->ref - pri->feedback;
if (self->in_dead_zone(self) == TRUE)
{
error = 0;
pri->in_dead_zone = TRUE;
}
else
{
pri->in_dead_zone = FALSE;
}
pri->e_0 = error;
/* fuzzy control caculate */
ec = error - pri->e_1;
if (self->open == TRUE)
{
compensate(error, ec, &self->pid_params);
}
/*根据PID配置的模式,获取积分数据,进行积分累加*/
if (self->speed_integral_enable == TRUE)
{
pri->iout = (pri->ki + self->pid_params.ki) * error * changing_integral_rate(self);
}
else
{
float32 temp_iterm = 0.0f;
float32 insert = 0;
if (pri->out >= pri->out_max)
{
if (fabs(error) > pri->err_limit && pri->detach)
{
insert = 0;
}
else
{
insert = 1;
if (error < 0)
{
temp_iterm = (pri->ki + self->pid_params.ki) * error;
}
}
}
else if (pri->out <= pri->out_min)
{
if (fabs(error) > pri->err_limit && pri->detach)
{
insert = 0;
}
else
{
insert = 1;
if (error > 0)
{
temp_iterm = (pri->ki + self->pid_params.ki) * error;
}
}
}
else
{
if (fabs(error) > pri->err_limit && pri->detach)
{
insert = 0;
}
else
{
insert = 1;
temp_iterm = (pri->ki + self->pid_params.ki) * error;
}
}
pri->iout += temp_iterm;
/* limt integral */
if (pri->iout > pri->out_max)
{
pri->iout = pri->out_max;
}
else if (pri->iout < pri->out_min)
{
pri->iout = pri->out_min;
}
pri->iout = pri->iout * insert;
}
#if INCOMPLETE_DIFFEREN == 1
/*不完全微分*/
thisdev = kd * (1.0 - pri->alpha) * (error - pri->e_1) + pri->alpha * pri->lastdev;
/*record last dev result*/
pri->lastdev = thisdev;
#else
thisdev = (error - pri->e_1) * (kd);
#endif
if (pri->kd_enable == FALSE)
{
thisdev = 0;
}
if (pri->ki_enable == FALSE)
{
pri->iout = 0;
}
pri->out = (pri->kp + self->pid_params.kp) * error + pri->iout + thisdev;
pri->e_1 = error;
/*record last feedback sensor result*/
pri->pre_feedback = pri->feedback;
/*limt pid output*/
pri->out = RANGE(pri->out, pri->out_min, pri->out_max);
return pri->out;
}
static float32 increment_pid(struct PID_FUZZY *self, float32 target, float32 feedback)
{
float32 ep, ei, ed;
float32 inc_out;
float32 thisdev = 0;
pid_common_increment_t *pri = &self->pri_u.increment;
pri->feedback = feedback;
pri->e_0 = pri->ref - pri->feedback;
if (pri->e_0 >= MAXE)
{
return pri->out_max;
}
else if (pri->e_0 <= MINE)
{
return pri->out_min;
}
if (fabs(pri->e_0) <= pri->err_dead)
{
pri->e_0 = 0;
}
ep = pri->e_0 - pri->e_1;
ei = pri->e_0;
ed = pri->e_0 - 2 * pri->e_1 + pri->e_2;
if (self->open == TRUE)
{
compensate(pri->e_0, ep, &self->pid_params);
}
if (pri->sm == 1)
{
smooth_setpoint(self, target);
}
else
{
pri->ref = target;
}
#if INCOMPLETE_DIFFEREN == 1
/*不完全微分*/
thisdev = (1.0 - pri->alpha) * (pri->kd + self->pid_params.kd) * ed + pri->alpha * pri->lastdev;
#else
ed = ed;
#endif
if (self->speed_integral_enable == TRUE)
{
if (ABS(pri->e_0) > MAXE)
{
pri->iout = (pri->ki + self->pid_params.ki) * ei;
}
else
{
// 变速积分
pri->iout = (pri->ki + self->pid_params.ki) * ei * changing_integral_rate(self);
}
}
else
{
pri->iout = (pri->ki + self->pid_params.ki) * ei;
}
if (pri->kd_enable == FALSE)
{
thisdev = 0;
}
if (pri->ki_enable == FALSE)
{
pri->iout = 0;
}
inc_out = (pri->kp + self->pid_params.kp) * ep + pri->iout + thisdev;
pri->e_2 = pri->e_1;
pri->e_1 = pri->e_0;
pri->lastdev = thisdev;
pri->out = pri->out + inc_out;
pri->out = RANGE(pri->out, pri->out_min, pri->out_max);
return pri->out;
}
static float32 _pid(struct PID_FUZZY *self, float32 target, float32 feedback)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
return position_pid(self, target, feedback);
}
else
{
return increment_pid(self, target, feedback);
}
}
/**
* @brief PID积分及微分控制数据
* @param {PID_FUZZY} *self
* @return {*}
*/
static void _restctrl(struct PID_FUZZY *self, float32 out)
{
if (self->sub_type == PID_SUB_TYPE_POSITION)
{
pid_common_position_t *pri = NULL;
pri = &self->pri_u.position;
pri->e_1 = 0;
pri->iout = 0;
pri->out = out;
pri->iout = out;
#if INCOMPLETE_DIFFEREN == 1
pri->lastdev = 0;
#endif
}
else
{
pid_common_increment_t *pri = NULL;
pri = &self->pri_u.increment;
pri->e_0 = 0;
pri->e_1 = 0;
pri->e_2 = 0;
pri->lastdev = 0;
pri->out = out;
pri->iout = out;
}
}
void pid_fuzzy_constructor(struct PID_FUZZY *self)
{
self->set_ctrl_prm = _set_ctrl_prm;
self->set_error_max_min = _set_error_max_min;
self->update_ctrl_prm = _update_ctrl_prm;
self->set_cfg = _set_cfg;
self->set_smooth_enable = _set_smooth_enable;
self->set_err_dead = _set_err_dead;
self->set_kp = _set_kp;
self->set_ki_enable = _set_ki_enable;
self->set_ki = _set_ki;
self->set_kd_enable = _set_kd_enable;
self->set_kd = _set_kd;
self->set_kd_dev = _set_kd_dev;
self->set_range = _set_range;
self->restctrl = _restctrl;
self->set_iout = _set_iout;
self->in_dead_zone = _in_dead_zone;
self->execute = _pid;
}

View File

@ -0,0 +1,97 @@
#include "pid.h"
#include <math.h>
// 设置控制参数
static void _set_ctrl_prm(struct PID_NEURAL *self, float32 minimum, float32 maximum)
{
self->pri.setpoint = minimum; /*设定值*/
self->pri.kcoef = 0.12; /*神经元输出比例*/
self->pri.kp = 0.45; /*比例学习速度*/
self->pri.ki = 0.05; /*积分学习速度*/
self->pri.kd = 0; /*微分学习速度*/
self->pri.lasterror = 0.0; /*前一拍偏差*/
self->pri.preerror = 0.0; /*前两拍偏差*/
self->pri.result = minimum; /*PID控制器结果*/
self->pri.output = 0.0; /*输出值,百分比*/
self->pri.maximum = maximum; /*输出值上限*/
self->pri.minimum = minimum; /*输出值下限*/
self->pri.deadband = (maximum - minimum) * 0.0005f; /*死区*/
self->pri.wp = 0.10; /*比例加权系数*/
self->pri.wi = 0.10; /*积分加权系数*/
self->pri.wd = 0.10; /*微分加权系数*/
}
// 设置输出参数
static void _set_out_prm(struct PID_NEURAL *self, float32 minimum, float32 maximum)
{
self->pri.maximum = maximum;
self->pri.minimum = minimum;
}
/*单神经元学习规则函数*/
static void NeureLearningRules(struct PID_NEURAL *self, float32 zk, float32 uk, float32 *xi)
{
self->pri.wi = self->pri.wi + self->pri.ki * zk * uk * xi[0];
self->pri.wp = self->pri.wp + self->pri.kp * zk * uk * xi[1];
self->pri.wd = self->pri.wd + self->pri.kd * zk * uk * xi[2];
}
static float32 _PID(struct PID_NEURAL *self, float32 target, float32 feedback)
{
float32 x[3];
float32 w[3];
float32 sabs;
float32 error;
float32 result;
float32 deltaResult;
self->pri.setpoint = target;
error = self->pri.setpoint - feedback;
result = self->pri.result;
if (fabs(error) > self->pri.deadband)
{
x[0] = error;
x[1] = error - self->pri.lasterror;
x[2] = error - self->pri.lasterror * 2 + self->pri.preerror;
sabs = fabs(self->pri.wi) + fabs(self->pri.wp) + fabs(self->pri.wd);
w[0] = self->pri.wi / sabs;
w[1] = self->pri.wp / sabs;
w[2] = self->pri.wd / sabs;
deltaResult = (w[0] * x[0] + w[1] * x[1] + w[2] * x[2]) * self->pri.kcoef;
}
else
{
deltaResult = 0;
}
result = result + deltaResult;
if (result > self->pri.maximum)
{
result = self->pri.maximum;
}
if (result < self->pri.minimum)
{
result = self->pri.minimum;
}
self->pri.result = result;
self->pri.output = self->pri.result;
// 单神经元学习
NeureLearningRules(self, error, result, x);
self->pri.preerror = self->pri.lasterror;
self->pri.lasterror = error;
return self->pri.output;
}
void pid_neural_constructor(struct PID_NEURAL *self)
{
self->set_ctrl_prm = _set_ctrl_prm;
self->set_out_prm = _set_out_prm;
self->PID = _PID;
}

View File

@ -0,0 +1,111 @@
# 模糊PID控制器设计文档
# 模糊PID控制器详细设计文档
## 1. 引言
### 1.1 目的
本文档旨在详细介绍模糊PID控制器的设计理念、实现方法和使用指南为开发者提供一套完整的模糊PID控制解决方案。
### 1.2 背景
PID控制器因其结构简单、稳定性好、易于实现等优点在工业控制系统中得到了广泛应用。然而传统PID控制器在面对复杂或非线性系统时性能表现不佳。模糊PID控制器通过引入模糊逻辑动态调整PID参数以适应系统在不同工作状态下的控制需求从而提高控制性能。
## 2. 设计概述
### 2.1 设计目标
- **适应性**:能够适应不同类型和不同工作状态的控制系统。
- **稳定性**:保证控制系统在各种工作条件下的稳定运行。
- **易用性**:提供简单易懂的接口,便于开发者快速实现和调试。
### 2.2 功能模块
模糊PID控制器主要包括以下几个功能模块
1. **模糊控制模块**负责根据输入的误差和误差变化率通过模糊逻辑计算出PID参数。
2. **SV平滑给定模块**:负责平滑控制目标值,减少控制过程中的突变。
3. **变速积分模块**:根据误差的大小调整积分速率,提高控制效率。
4. **参数设置模块**提供接口函数用于设置和调整PID参数。
## 3. 功能模块详细设计
### 3.1 模糊控制模块
#### 3.1.1 输入处理
- **误差处理**:将实时误差 `e`限制在预定的范围内,并进行模糊化处理。
- **误差变化率处理**:将误差变化率 `ec`进行相同的处理。
#### 3.1.2 模糊规则库
- **规则定义**根据系统的具体需求定义一套模糊规则用于计算PID参数。
- **规则应用**:根据输入的误差和误差变化率的模糊化值,通过模糊规则库计算出 `kp`、`ki`、`kd`。
### 3.2 SV平滑给定模块
- **平滑策略**:根据当前目标值与新目标值之间的差值,动态调整目标值变化的步长,实现平滑过渡。
### 3.3 变速积分模块
- **积分策略**:根据误差的大小,调整积分速率。误差较小时,使用完整积分;误差较大时,减小或停止积分。
### 3.4 参数设置模块
- **接口设计**提供一系列接口函数用于设置PID控制器的参数如输出限制、死区误差等。
## 4. 使用说明
### 4.1 初始化
- **控制器初始化**根据控制对象的特性初始化模糊PID控制器的相关参数和模糊规则库。
### 4.2 实时控制
- **参数调整**在控制循环中根据实时误差和误差变化率动态调整PID参数。
- **控制执行**根据调整后的PID参数执行PID控制算法输出控制信号。
### 4.3 参数调整
- **动态调整**根据系统运行情况通过参数设置模块调整PID参数优化控制效果。
## 5. 结论
模糊PID控制器通过动态调整PID参数提高了控制系统的适应性和稳定性特别适用于复杂或非线性系统的控制。本文档提供了模糊PID控制器的详细设计方案旨在帮助开发者更好地理解和应用模糊PID控制技术
## 概述
本文档旨在详细介绍模糊PID控制器的设计与实现。模糊PID控制器结合了传统PID控制和模糊逻辑控制的优点通过模糊逻辑对PID参数进行动态调整以适应控制系统在不同工作状态下的需求。
## 功能模块
### 1. 模糊控制模块
- **功能描述**:根据误差 `e`和误差变化率 `ec`的模糊化值通过模糊规则库计算出模糊PID控制器的三个参数比例系数 `kp`、积分系数 `ki`、微分系数 `kd`
- **实现方法**:首先将输入的误差 `e`和误差变化率 `ec`限制在预定范围内,然后通过模糊化处理得到其隶属度和模糊位置标号,最后根据模糊规则库计算出 `kp`、`ki`、`kd`的值。
### 2. SV平滑给定模块
- **功能描述**平滑控制目标值Setpoint Value, SV以减少控制过程中的突变提高系统的稳定性。
- **实现方法**:根据当前目标值与新目标值之间的差值,动态调整目标值的变化步长,以实现平滑过渡。
### 3. 变速积分模块
- **功能描述**:根据误差的大小调整积分速率,以提高控制系统的快速性和稳定性。
- **实现方法**:当误差较小时,使用完整积分;当误差在一定范围内变化时,通过线性函数调整积分速率;当误差较大时,减小或停止积分,以避免积分饱和。
### 4. 参数设置模块
- **功能描述**提供接口函数用于设置PID控制器的各项参数包括输出限制范围、死区误差、积分输出值、PID参数等。
- **实现方法**:根据控制器的子类型(位置型或增量型),分别设置相应参数的值。
## 使用说明
1. **初始化**根据控制对象的具体情况初始化模糊PID控制器的结构体包括最大误差、最小误差、PID参数的模糊规则库等。
2. **实时控制**:在控制循环中,根据当前的误差 `e`和误差变化率 `ec`调用模糊控制模块计算出动态调整的PID参数然后根据这些参数进行PID控制。
3. **参数调整**根据系统运行情况通过参数设置模块调整PID控制器的参数以优化控制效果。
## 结论
模糊PID控制器通过引入模糊逻辑使得PID参数能够根据控制系统的实时状态动态调整从而提高了控制系统的适应性和稳定性。通过本文档的设计与实现开发者可以更好地理解和应用模糊PID控制器。

View File

@ -0,0 +1 @@
https://www.cnblogs.com/foxclever/p/16299063.html

View File

@ -0,0 +1,86 @@
# 变量BIN: 给定的是我们想要生成的可执行文件的名称
BIN = run.exe
SO = lib.dll
# 变量SRC中给的是所有的想要编译的.c源文件与makefile在同一目录下可直接写如这里的main.c否则需要写明相对路径如这里的其余源文件都在目录src下
# 多文件时,选择用"\"进行分行处理
SRC = \
../src/malloc.c \
../src/sqqueue.c \
../src/mlist.c \
../src/debug.c \
../src/data_analysis.c \
../src/filter.c \
../src/clist.c \
../src/aes.c \
../src/cmac.c \
../src/lib.c
EXAMPLE = \
./simple_clist.c \
./simple_data_analysis.c \
./simple_sqqueue.c \
./simple_aes.c \
./simple_cmac.c
CPLUS_INCLUDE_PATH= -I ../inc
# 变量CC给定编译器名gcc
# 变量CFLAGS传给编译器的某些编译参数看需求添加
CC = gcc
CFLAGS = -m32 -std=c99
# 变量GDB给定debugger名gdb
# 变量RM给定删除文件方式用于后面删除所有编译所得的.o文件,linux下使用rm -rf
GDB = gdb
RM = rm -rf
# 变量OBJS将变量SRC中所有的.c文件替换成以.o结尾即将.c源文件编译成.o文件
OBJS = $(SRC:%.c=%.o)
EXAPMLES = $(EXAMPLE:%.c=%.o)
$(SO): $(OBJS) $(EXAPMLES)
# pull in dependencies for .o files
-include $(OBJS:.o=.d)
%.o: %.c
$(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) -c $< -o $@
.PHONY: all clean clist data_analysis
all: $(SO)
rm:
$(RM) $(OBJS)
#简单链表
clist: $(SO)
$(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_clist.o -o $(BIN)
$(RM) $(OBJS) $(EXAPMLES)
#数据分析器
data_analysis: $(SO)
$(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_data_analysis.o -o $(BIN)
$(RM) $(OBJS) $(EXAPMLES)
#队列
sqqueue: $(SO)
$(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_sqqueue.o -o $(BIN)
$(RM) $(OBJS) $(EXAPMLES)
#aes加密
aes: $(SO)
$(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_aes.o -o $(BIN)
$(RM) $(OBJS) $(EXAPMLES)
#cmac类CRC
cmac: $(SO)
$(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_cmac.o -o $(BIN)
$(RM) $(OBJS) $(EXAPMLES)
#运行程序
run:
./run.exe
clean:
$(RM) $(OBJS) $(EXAPMLES) $(BIN)

View File

@ -0,0 +1,40 @@
#include "../inc/data_type_def.h"
#include "../inc/log.h"
#include "../inc/osel_arch.h"
#include "../inc/aes.h"
// 全局变量
static aes_context AesContext; // 密钥表
static uint8_t aBlock[] = {0x00, 0x00, 0x00, 0xcc, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 数据块
static uint8_t sBlock[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 存放输出结果
int32_t main(void)
{
uint8_t buf[16] = {0x00};
uint8_t size = ARRAY_LEN(buf);
uint8_t key[] = {
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥
// 初始化密文
for (int i = 0; i < size; i++)
{
buf[i] = i;
}
// 设置预密钥
osel_memset(AesContext.ksch, 0, ARRAY_LEN(AesContext.ksch));
aes_set_key(key, 16, &AesContext);
// 加密
osel_memcpy(aBlock, buf, size);
aes_encrypt(aBlock, sBlock, &AesContext);
LOG_HEX(sBlock, ARRAY_LEN(sBlock)); // 打印加密结果:50 fe 67 cc 99 6d 32 b6 da 09 37 e9 9b af ec 60
// 解密
osel_memcpy(aBlock, sBlock, size);
aes_decrypt(aBlock, sBlock, &AesContext);
LOG_HEX(sBlock, ARRAY_LEN(sBlock)); // 打印解密结果:00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
}

View File

@ -0,0 +1,52 @@
#include "../inc/data_type_def.h"
#include "../inc/clist.h"
int32_t main(void)
{
clist_node_t *head = NULL; // 创建头指针初始化为NULL
clist_init(&head); // 初始化指针(可有可无)
// 1添加数据
for (int32_t i = 0; i < 30; i++)
{
if (i > 10)
clist_push_front(&head, (cnode)i); // 头部插入
else
clist_push_back(&head, (cnode)i); // 尾部插入
}
LOG_PRINT("\n 1: count:%d \n", clist_node_count(head)); // 获取链表节点数,打印
clist_print(head); // 打印链表
// 2删除数据
for (int32_t i = 0; i < 10; i++)
{
if (i > 5)
clist_pop_back(&head); // 删除尾部
else
clist_pop_front(&head); // 头部删除
}
LOG_PRINT("\n 2: count:%d \n", clist_node_count(head));
clist_print(head);
// 3插入数据
clist_insert(&head, 5, (cnode)1111);
clist_insert_for_node(&head, head->Next->Next->Next->Next->Next, (cnode)10000);
clist_insert(&head, 1000, (cnode)2222); // 无效插入
LOG_PRINT("\n 3: count:%d \n", clist_node_count(head));
clist_print(head);
// 4删除指定节点
clist_remove(&head, (cnode)5);
clist_erase_for_node(&head, head->Next->Next);
clist_remove(&head, (cnode)1000); // 无效删除
clist_print(head);
LOG_PRINT("\n 4: count:%d \n", clist_node_count(head));
clist_print(head);
// 5删除所有节点
clist_destroy(&head);
LOG_PRINT("\n 5: count:%d ", clist_node_count(head));
clist_print(head);
return 0;
}

View File

@ -0,0 +1,33 @@
#include "../inc/data_type_def.h"
#include "../inc/log.h"
#include "../inc/osel_arch.h"
#include "../inc/cmac.h"
static uint8_t key[] = {
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥
int32_t main(void)
{
uint8_t *p;
uint8_t buffer[16] = {0x00};
uint32_t size = ARRAY_LEN(buffer);
// 初始化需要校验的数据
for (int i = 0; i < size; i++)
{
buffer[i] = i;
}
uint8_t mic[16]; // 存放生成校验数据的数组
AES_CMAC_CTX AesCmacCtx[1]; // 密钥扩展表
AES_CMAC_Init(AesCmacCtx); // 完成密钥扩展表的初始化
AES_CMAC_SetKey(AesCmacCtx, key); // 完成密钥扩展表数据
AES_CMAC_Update(AesCmacCtx, buffer, size & 0xFF); // 完成数据的奇偶校验
AES_CMAC_Final(mic, AesCmacCtx); // 生成16个字节的校验表
uint32_t xor_vol = (uint32_t)((uint32_t)mic[3] << 24 | (uint32_t)mic[2] << 16 | (uint32_t)mic[1] << 8 | (uint32_t)mic[0]); // 取表4个字节作为校验码
p = (uint8_t *)&xor_vol;
LOG_HEX(p, 4); // 打印结果5c 7e fb 43
}

View File

@ -0,0 +1,26 @@
#include "../inc/data_type_def.h"
#include "../inc/log.h"
#include "../inc/cmd.h"
void at_name_req(void)
{
LOG_PRINT("name:cmd\n");
}
void at_version_req(void)
{
LOG_PRINT("version:1.0\n");
}
REGISTER_CMD(NAME, at_name_req, at name);
REGISTER_CMD(VERSION, at_version_req, at version);
int32_t main(void)
{
cmd_init();
cmd_parsing("TEST");
cmd_parsing("NAME");
cmd_parsing("VERSION");
return 0;
}

View File

@ -0,0 +1,178 @@
#include "../inc/data_type_def.h"
#include "../inc/log.h"
#include "../inc/osel_arch.h"
#include "../inc/data_analysis.h"
#define UART_RXSIZE (254U)
#define UART_DATA_ANALYSIS_PORT_1 DATA_1
#define UART_DATA_ANALYSIS_PORT_2 DATA_2
static data_interupt_cb_t uart_data_analysis_cb = NULL; // 数据源中断回调函数
static void data_analysis_event1(void)
{
uint8_t frame[UART_RXSIZE];
uint8_t data_head[3];
uint8_t crc[2];
uint16_t frame_len, out_frame_len;
data_read(UART_DATA_ANALYSIS_PORT_1, &data_head[0], 3);
osel_memcpy((uint8_t *)&frame_len, &data_head[1], 2);
frame_len = B2S_UINT16(frame_len) - 2; // 报文长度包含帧长这里需要减2
if (frame_len > UART_RXSIZE)
{
lock_data(UART_DATA_ANALYSIS_PORT_1);
unlock_data(UART_DATA_ANALYSIS_PORT_1);
return;
}
out_frame_len = data_read(UART_DATA_ANALYSIS_PORT_1, frame, (uint16_t)frame_len);
if (out_frame_len != frame_len)
{
return;
}
out_frame_len = out_frame_len - 1; // 报文中包含帧尾这里需要减1
// 校验CRC_16
uint16_t crc_16 = 0;
uint16_t crc16 = crc16_compute(&frame[0], out_frame_len - 2);
osel_memcpy(&crc[0], &frame[out_frame_len - 2], 2);
crc_16 = BUILD_UINT16(crc[1], crc[0]);
if (crc16 != crc_16)
{
return;
}
// CRC校验通过后将数据长度-2
out_frame_len -= 2;
LOG_PRINT("data_analysis_event1 ok:");
LOG_HEX(frame, out_frame_len);
}
static void data_analysis_event2(void)
{
uint8_t frame[UART_RXSIZE];
uint8_t data_head[4];
uint8_t crc[2];
uint16_t frame_len, out_frame_len;
data_read(UART_DATA_ANALYSIS_PORT_2, &data_head[0], 4);
osel_memcpy((uint8_t *)&frame_len, &data_head[2], 2);
frame_len = B2S_UINT16(frame_len);
if (frame_len > UART_RXSIZE)
{
lock_data(UART_DATA_ANALYSIS_PORT_2);
unlock_data(UART_DATA_ANALYSIS_PORT_2);
return;
}
out_frame_len = data_read(UART_DATA_ANALYSIS_PORT_2, frame, (uint16_t)frame_len);
if (out_frame_len != frame_len)
{
return;
}
// 校验CRC_16
uint16_t crc_16 = 0;
uint16_t crc16 = crc16_compute(&frame[0], out_frame_len - 2);
osel_memcpy(&crc[0], &frame[out_frame_len - 2], 2);
crc_16 = BUILD_UINT16(crc[1], crc[0]);
if (crc16 != crc_16)
{
LOG_PRINT("crc error crc16:%x, crc_16:%x\n");
return;
}
out_frame_len -= 2; // 去掉CRC_16
LOG_PRINT("data_analysis_event2 ok:");
LOG_HEX(frame, out_frame_len);
}
/**
* @brief
* @return {*}
* @note
*/
static void data_register1(void)
{
/**
*
1 2 2 2 1 n 2 1
*/
#define FRAME_HEAD 0x05 // 帧头
#define FRAME_TAIL 0x1b // 帧尾
// 注册数据解析
data_reg_t reg;
reg.sd.valid = true; // 数据头部验证有效标志位
reg.sd.len = 1; // 数据头部长度
reg.sd.pos = 0; // 数据头部偏移量
reg.sd.data[0] = FRAME_HEAD; // 数据头部数据
reg.ld.len = 2; // 数据长度
reg.ld.pos = 2; // 报文长度包含帧长这里需要设置偏移2
reg.ld.valid = true; // 数据长度有效标志位
reg.ld.little_endian = false; // 数据长度是否小端模式
reg.argu.len_max = UART_RXSIZE; // 数据最大长度
reg.argu.len_min = 2; // 数据最小长度
reg.ed.valid = true; // 数据尾部有效标志位
reg.ed.len = 1; // 数据尾部长度
reg.ed.data[0] = FRAME_TAIL; // 数据尾部数据
reg.echo_en = false; // 是否回显
reg.func_ptr = data_analysis_event1; // 数据解析回调函数 data_analysis模块处理完数据后会调用这个函数继续数据协议的处理
uart_data_analysis_cb = data_fsm_init(UART_DATA_ANALYSIS_PORT_1); // 注册数据处理函数 data_analysis模块会调用这个函数将数据写入到data_analysis模块
data_reg(UART_DATA_ANALYSIS_PORT_1, reg); // 注册数据解析
}
/**
* @brief
* @return {*}
* @note
*/
static void data_register2(void)
{
/**
*
2 2 2 2 1 n 2
*/
#define FRAME_HEAD1 0xD5 // 帧头
#define FRAME_HEAD2 0xC8 // 帧尾
// 注册数据解析
data_reg_t reg;
reg.sd.valid = true; // 数据头部验证有效标志位
reg.sd.len = 2; // 数据头部长度
reg.sd.pos = 0; // 数据头部偏移量
reg.sd.data[0] = FRAME_HEAD1; // 数据头部数据
reg.sd.data[1] = FRAME_HEAD2; // 数据头部数据
reg.ld.len = 2; // 数据长度
reg.ld.pos = 2; // 报文长度包含帧长这里需要设置偏移2
reg.ld.valid = true; // 数据长度有效标志位
reg.ld.little_endian = false; // 数据长度是否小端模式
reg.argu.len_max = UART_RXSIZE; // 数据最大长度
reg.argu.len_min = 2; // 数据最小长度
reg.ed.valid = false; // 数据尾部有效标志位
reg.echo_en = false; // 是否回显
reg.func_ptr = data_analysis_event2; // 数据解析回调函数 data_analysis模块处理完数据后会调用这个函数继续数据协议的处理
uart_data_analysis_cb = data_fsm_init(UART_DATA_ANALYSIS_PORT_2); // 注册数据处理函数 data_analysis模块会调用这个函数将数据写入到data_analysis模块
data_reg(UART_DATA_ANALYSIS_PORT_2, reg); // 注册数据解析
}
int32_t main(void)
{
data_register1();
data_register2();
// 模拟串口数据
uint8_t data1[] = {0x05, 0x00, 0x0a, 0xff, 0xff, 0x00, 0x01, 0x00, 0x55, 0x40, 0x1b};
for (uint16_t i = 0; i < ARRAY_LEN(data1); i++)
{
uart_data_analysis_cb(UART_DATA_ANALYSIS_PORT_1, *(data1 + i));
}
// 模拟串口数据
uint8_t data2[] = {0xD5, 0xC8, 0x00, 0x07, 0xff, 0xff, 0x00, 0x01, 0x00, 0x55, 0x40};
for (uint16_t i = 0; i < ARRAY_LEN(data2); i++)
{
uart_data_analysis_cb(UART_DATA_ANALYSIS_PORT_2, *(data2 + i));
}
return 0;
}

View File

@ -0,0 +1,53 @@
#include "../inc/data_type_def.h"
#include "../inc/log.h"
#include "../inc/osel_arch.h"
#include "../inc/sqqueue.h"
typedef struct
{
uint8_t x;
uint8_t y;
} element_t;
sqqueue_ctrl_t queue; // 创建队列对象
void traverse_cb(const void *e)
{
element_t *p = (element_t *)e;
LOG_PRINT("x = %d, y = %d", p->x, p->y);
}
int32_t main(void)
{
int size = 10;
// 初始化队列
if (FALSE == sqqueue_ctrl_init(&queue, sizeof(element_t), size))
{
LOG_ERR("queue init failed!");
return -1; // 创建失败
}
// 添加测试元素
for (int i = 1; i <= 10; i++)
{
element_t element;
element.x = i * 10;
element.y = i * 10;
queue.enter(&queue, &element); // 将成员插入到队列中
}
LOG_PRINT("add queue len = %d", queue.get_len(&queue)); // 获取队列长度
queue.del(&queue); // 移除首元素
LOG_PRINT("del queue len = %d", queue.get_len(&queue)); // 获取队列长度
queue.revoke(&queue); // 移除尾元素
LOG_PRINT("revoke queue len = %d", queue.get_len(&queue)); // 获取队列长度
queue.remove(&queue, 3); // 删除相对队头指定偏移位置的元素
LOG_PRINT("remove queue len = %d", queue.get_len(&queue)); // 获取队列长度
LOG_PRINT("queue traverse:");
queue.traverse(&queue, traverse_cb); // 遍历队列
queue.clear_sqq(&queue); // 清空队列
LOG_PRINT("clear queue len = %d", queue.get_len(&queue)); // 获取队列长度
return 0;
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief basic KV samples.
*
* basic Key-Value Database KV feature samples
* get and show currnet boot counts
*/
#include <flashdb.h>
#ifdef FDB_USING_KVDB
#define FDB_LOG_TAG "[sample][kvdb][basic]"
void kvdb_basic_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
int boot_count = 0;
FDB_INFO("==================== kvdb_basic_sample ====================\n");
{ /* GET the KV value */
/* get the "boot_count" KV value */
fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
FDB_INFO("get the 'boot_count' value is %d\n", boot_count);
} else {
FDB_INFO("get the 'boot_count' failed\n");
}
}
{ /* CHANGE the KV value */
/* increase the boot count */
boot_count ++;
/* change the "boot_count" KV's value */
fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
FDB_INFO("set the 'boot_count' value to %d\n", boot_count);
}
FDB_INFO("===========================================================\n");
}
#endif /* FDB_USING_KVDB */

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief blob KV samples.
*
* Key-Value Database blob type KV feature samples
*/
#include <flashdb.h>
#ifdef FDB_USING_KVDB
#define FDB_LOG_TAG "[sample][kvdb][blob]"
void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
FDB_INFO("==================== kvdb_type_blob_sample ====================\n");
{ /* CREATE new Key-Value */
int temp_data = 36;
/* It will create new KV node when "temp" KV not in database.
* fdb_blob_make: It's a blob make function, and it will return the blob when make finish.
*/
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data);
}
{ /* GET the KV value */
int temp_data = 0;
/* get the "temp" KV value */
fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
FDB_INFO("get the 'temp' value is: %d\n", temp_data);
}
}
{ /* CHANGE the KV value */
int temp_data = 38;
/* change the "temp" KV's value to 38 */
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
FDB_INFO("set 'temp' value to %d\n", temp_data);
}
{ /* DELETE the KV by name */
fdb_kv_del(kvdb, "temp");
FDB_INFO("delete the 'temp' finish\n");
}
FDB_INFO("===========================================================\n");
}
#endif /* FDB_USING_KVDB */

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief string KV samples.
*
* Key-Value Database string type KV feature samples source file.
*/
#include <flashdb.h>
#include <string.h>
#ifdef FDB_USING_KVDB
#define FDB_LOG_TAG "[sample][kvdb][string]"
void kvdb_type_string_sample(fdb_kvdb_t kvdb)
{
FDB_INFO("==================== kvdb_type_string_sample ====================\n");
{ /* CREATE new Key-Value */
char temp_data[10] = "36C";
/* It will create new KV node when "temp" KV not in database. */
fdb_kv_set(kvdb, "temp", temp_data);
FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data);
}
{ /* GET the KV value */
char *return_value, temp_data[10] = { 0 };
/* Get the "temp" KV value.
* NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible.
*/
return_value = fdb_kv_get(kvdb, "temp");
/* the return value is NULL when get the value failed */
if (return_value != NULL) {
strncpy(temp_data, return_value, sizeof(temp_data));
FDB_INFO("get the 'temp' value is: %s\n", temp_data);
}
}
{ /* CHANGE the KV value */
char temp_data[10] = "38C";
/* change the "temp" KV's value to "38.1" */
fdb_kv_set(kvdb, "temp", temp_data);
FDB_INFO("set 'temp' value to %s\n", temp_data);
}
{ /* DELETE the KV by name */
fdb_kv_del(kvdb, "temp");
FDB_INFO("delete the 'temp' finish\n");
}
FDB_INFO("===========================================================\n");
}
#endif /* FDB_USING_KVDB */

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief TSDB samples.
*
* Time series log (like TSDB) feature samples source file.
*
* TSL is time series log, the TSDB saved many TSLs.
*/
#include <flashdb.h>
#include <string.h>
#ifdef FDB_USING_TSDB
#define FDB_LOG_TAG "[sample][tsdb]"
#ifdef FDB_USING_TIMESTAMP_64BIT
#define __PRITS "ld"
#else
#define __PRITS "d"
#endif
struct env_status {
int temp;
int humi;
};
static bool query_cb(fdb_tsl_t tsl, void *arg);
static bool query_by_time_cb(fdb_tsl_t tsl, void *arg);
static bool set_status_cb(fdb_tsl_t tsl, void *arg);
void tsdb_sample(fdb_tsdb_t tsdb)
{
struct fdb_blob blob;
FDB_INFO("==================== tsdb_sample ====================\n");
{ /* APPEND new TSL (time series log) */
struct env_status status;
/* append new log to TSDB */
status.temp = 36;
status.humi = 85;
fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
status.temp = 38;
status.humi = 90;
fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
}
{ /* QUERY the TSDB */
/* query all TSL in TSDB by iterator */
fdb_tsl_iter(tsdb, query_cb, tsdb);
}
{ /* QUERY the TSDB by time */
/* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */
struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
size_t count;
/* query all TSL in TSDB by time */
fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb);
/* query all FDB_TSL_WRITE status TSL's count in TSDB by time */
count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE);
FDB_INFO("query count is: %zu\n", count);
}
{ /* SET the TSL status */
/* Change the TSL status by iterator or time iterator
* set_status_cb: the change operation will in this callback
*
* NOTE: The actions to modify the state must be in order.
* like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2
* The intermediate states can also be ignored.
* such as: FDB_TSL_WRITE -> FDB_TSL_DELETED
*/
fdb_tsl_iter(tsdb, set_status_cb, tsdb);
}
FDB_INFO("===========================================================\n");
}
static bool query_cb(fdb_tsl_t tsl, void *arg)
{
struct fdb_blob blob;
struct env_status status;
fdb_tsdb_t db = arg;
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
FDB_INFO("[query_cb] queried a TSL: time: %" __PRITS ", temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
return false;
}
static bool query_by_time_cb(fdb_tsl_t tsl, void *arg)
{
struct fdb_blob blob;
struct env_status status;
fdb_tsdb_t db = arg;
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
FDB_INFO("[query_by_time_cb] queried a TSL: time: %" __PRITS ", temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
return false;
}
static bool set_status_cb(fdb_tsl_t tsl, void *arg)
{
fdb_tsdb_t db = arg;
FDB_INFO("set the TSL (time %" __PRITS ") status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1);
fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1);
return false;
}
#endif /* FDB_USING_TSDB */

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-17 armink the first version
*/
#include <fal.h>
static uint8_t init_ok = 0;
/**
* FAL (Flash Abstraction Layer) initialization.
* It will initialize all flash device and all flash partition.
*
* @return >= 0: partitions total number
*/
int fal_init(void)
{
extern int fal_flash_init(void);
extern int fal_partition_init(void);
int result;
/* initialize all flash device on FAL flash table */
result = fal_flash_init();
if (result < 0) {
goto __exit;
}
/* initialize all flash partition on FAL partition table */
result = fal_partition_init();
__exit:
if ((result > 0) && (!init_ok))
{
init_ok = 1;
log_i("Flash Abstraction Layer (V%s) initialize success.", FAL_SW_VERSION);
}
else if(result <= 0)
{
init_ok = 0;
log_e("Flash Abstraction Layer (V%s) initialize failed.", FAL_SW_VERSION);
}
return result;
}
/**
* Check if the FAL is initialized successfully
*
* @return 0: not init or init failed; 1: init success
*/
int fal_init_check(void)
{
return init_ok;
}

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-17 armink the first version
*/
#ifndef _FAL_H_
#define _FAL_H_
#include <fal_cfg.h>
#include "fal_def.h"
/**
* FAL (Flash Abstraction Layer) initialization.
* It will initialize all flash device and all flash partition.
*
* @return >= 0: partitions total number
*/
int fal_init(void);
/* =============== flash device operator API =============== */
/**
* find flash device by name
*
* @param name flash device name
*
* @return != NULL: flash device
* NULL: not found
*/
const struct fal_flash_dev *fal_flash_device_find(const char *name);
/* =============== partition operator API =============== */
/**
* find the partition by name
*
* @param name partition name
*
* @return != NULL: partition
* NULL: not found
*/
const struct fal_partition *fal_partition_find(const char *name);
/**
* get the partition table
*
* @param len return the partition table length
*
* @return partition table
*/
const struct fal_partition *fal_get_partition_table(size_t *len);
/**
* set partition table temporarily
* This setting will modify the partition table temporarily, the setting will be lost after restart.
*
* @param table partition table
* @param len partition table length
*/
void fal_set_partition_table_temp(struct fal_partition *table, size_t len);
/**
* read data from partition
*
* @param part partition
* @param addr relative address for partition
* @param buf read buffer
* @param size read size
*
* @return >= 0: successful read data size
* -1: error
*/
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size);
/**
* write data to partition
*
* @param part partition
* @param addr relative address for partition
* @param buf write buffer
* @param size write size
*
* @return >= 0: successful write data size
* -1: error
*/
int fal_partition_write(const struct fal_partition *part, uint32_t addr, const uint8_t *buf, size_t size);
/**
* erase partition data
*
* @param part partition
* @param addr relative address for partition
* @param size erase size
*
* @return >= 0: successful erased data size
* -1: error
*/
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size);
/**
* erase partition all data
*
* @param part partition
*
* @return >= 0: successful erased data size
* -1: error
*/
int fal_partition_erase_all(const struct fal_partition *part);
/**
* print the partition table
*/
void fal_show_part_table(void);
/* =============== API provided to RT-Thread =============== */
/**
* create RT-Thread block device by specified partition
*
* @param parition_name partition name
*
* @return != NULL: created block device
* NULL: created failed
*/
struct rt_device *fal_blk_device_create(const char *parition_name);
#if defined(RT_USING_MTD_NOR)
/**
* create RT-Thread MTD NOR device by specified partition
*
* @param parition_name partition name
*
* @return != NULL: created MTD NOR device
* NULL: created failed
*/
struct rt_device *fal_mtd_nor_device_create(const char *parition_name);
#endif /* defined(RT_USING_MTD_NOR) */
/**
* create RT-Thread char device by specified partition
*
* @param parition_name partition name
*
* @return != NULL: created char device
* NULL: created failed
*/
struct rt_device *fal_char_device_create(const char *parition_name);
#endif /* _FAL_H_ */

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-17 armink the first version
*/
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_
#include "eeprom_m95.h"
#include "eeprom_fm24.h"
#include "flash.h"
#define FAL_PART_HAS_TABLE_CFG
#define FAL_ERASE_SIZE 100
// 需要块大小为2的N次方
#define EEPROM_M95_1_BLOCK_SIZE M95_PAGE_SIZE_256 * 32
#define EEPROM_M95_2_BLOCK_SIZE M95_PAGE_SIZE_256 * 32
#define EEPROM_FM24_BLOCK_SIZE FM24_PAGE_SIZE * 16
#define EEPROM_M95_1_SIZE _M95M02_
#define EEPROM_M95_2_SIZE _M95M02_
#define EEPROM_FM24_SIZE FM24_SIZE
#define EEPROM_M95_1_DEV_NAME "eeprom_m95_1"
#define EEPROM_M95_2_DEV_NAME "eeprom_m95_2"
#define EEPROM_FM24_DEV_NAME "eeprom_fm24"
/* ===================== Flash device Configuration ========================= */
extern struct fal_flash_dev eeprom_m95_1;
extern struct fal_flash_dev eeprom_m95_2;
extern struct fal_flash_dev eeprom_fm24;
/* flash device table */
#define FAL_FLASH_DEV_TABLE \
{ \
&eeprom_m95_1, \
&eeprom_m95_2, \
}
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
// issues :https://github.com/armink/FlashDB/issues/40 ;epprom的扇区太小
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "KVDB", EEPROM_M95_1_DEV_NAME, 0, EEPROM_M95_1_SIZE, 0}, \
{FAL_PART_MAGIC_WORD, "TSDB", EEPROM_M95_2_DEV_NAME, 0, EEPROM_M95_2_SIZE, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
#endif /* _FAL_CFG_H_ */

View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-17 armink the first version
*/
#ifndef _FAL_DEF_H_
#define _FAL_DEF_H_
#include <stdint.h>
#include <stdio.h>
#ifdef FDB_USING_NATIVE_ASSERT
#include <assert.h>
#endif
#define FAL_SW_VERSION "0.5.99"
#ifdef __RTTHREAD__ /* for RT-Thread platform */
#include <rtthread.h>
#define FAL_PRINTF rt_kprintf
#define FAL_MALLOC rt_malloc
#define FAL_CALLOC rt_calloc
#define FAL_REALLOC rt_realloc
#define FAL_FREE rt_free
#endif
#ifndef FAL_MALLOC
#define FAL_MALLOC malloc
#endif
#ifndef FAL_CALLOC
#define FAL_CALLOC calloc
#endif
#ifndef FAL_REALLOC
#define FAL_REALLOC realloc
#endif
#ifndef FAL_FREE
#define FAL_FREE free
#endif
#ifndef FAL_PRINTF
#define FAL_PRINTF printf
#endif
#ifndef FAL_DEBUG
#define FAL_DEBUG 0
#endif
#if FAL_DEBUG
#ifndef FDB_USING_NATIVE_ASSERT
#ifdef assert
#undef assert
#endif
#define assert(EXPR) \
if (!(EXPR)) \
{ \
FAL_PRINTF("(%s) has assert failed at %s.\n", #EXPR, __func__ ); \
while (1); \
}
#endif
/* debug level log */
#ifdef log_d
#undef log_d
#endif
#include <inttypes.h>
#define log_d(...) FAL_PRINTF("[D/FAL] (%s:%" PRIdLEAST16 ") ", __func__, __LINE__); FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\n")
#else
#ifndef FDB_USING_NATIVE_ASSERT
#ifdef assert
#undef assert
#endif
#define assert(EXPR) ((void)0);
#endif
/* debug level log */
#ifdef log_d
#undef log_d
#endif
#define log_d(...)
#endif /* FAL_DEBUG */
/* error level log */
#ifdef log_e
#undef log_e
#endif
#define log_e(...) FAL_PRINTF("\033[31;22m[E/FAL] (%s:%d) ", __func__, __LINE__);FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\033[0m\n")
/* info level log */
#ifdef log_i
#undef log_i
#endif
#define log_i(...) FAL_PRINTF("\033[32;22m[I/FAL] "); FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\033[0m\n")
/* FAL flash and partition device name max length */
#ifndef FAL_DEV_NAME_MAX
#define FAL_DEV_NAME_MAX 24
#endif
struct fal_flash_dev
{
char name[FAL_DEV_NAME_MAX];
/* flash device start address and len */
uint32_t addr;
size_t len;
/* the block size in the flash for erase minimum granularity */
size_t blk_size;
struct
{
int (*init)(void);
int (*read)(long offset, uint8_t *buf, size_t size);
int (*write)(long offset, const uint8_t *buf, size_t size);
int (*erase)(long offset, size_t size);
} ops;
/* write minimum granularity, unit: bit.
1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1)/ 64(stm32l4)
0 will not take effect. */
size_t write_gran;
};
typedef struct fal_flash_dev *fal_flash_dev_t;
/**
* FAL partition
*/
struct fal_partition
{
uint32_t magic_word;
/* partition name */
char name[FAL_DEV_NAME_MAX];
/* flash device name for partition */
char flash_name[FAL_DEV_NAME_MAX];
/* partition offset address on flash device */
long offset;
size_t len;
uint32_t reserved;
};
typedef struct fal_partition *fal_partition_t;
#endif /* _FAL_DEF_H_ */

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-17 armink the first version
*/
#include <fal.h>
#include <string.h>
/* flash device table, must defined by user */
#if !defined(FAL_FLASH_DEV_TABLE)
#error "You must defined flash device table (FAL_FLASH_DEV_TABLE) on 'fal_cfg.h'"
#endif
static const struct fal_flash_dev * const device_table[] = FAL_FLASH_DEV_TABLE;
static const size_t device_table_len = sizeof(device_table) / sizeof(device_table[0]);
static uint8_t init_ok = 0;
/**
* Initialize all flash device on FAL flash table
*
* @return result
*/
int fal_flash_init(void)
{
size_t i;
if (init_ok)
{
return 0;
}
for (i = 0; i < device_table_len; i++)
{
assert(device_table[i]->ops.read);
assert(device_table[i]->ops.write);
assert(device_table[i]->ops.erase);
/* init flash device on flash table */
if (device_table[i]->ops.init)
{
device_table[i]->ops.init();
}
log_d("Flash device | %*.*s | addr: 0x%08lx | len: 0x%08x | blk_size: 0x%08x |initialized finish.",
FAL_DEV_NAME_MAX, FAL_DEV_NAME_MAX, device_table[i]->name, device_table[i]->addr, device_table[i]->len,
device_table[i]->blk_size);
}
init_ok = 1;
return 0;
}
/**
* find flash device by name
*
* @param name flash device name
*
* @return != NULL: flash device
* NULL: not found
*/
const struct fal_flash_dev *fal_flash_device_find(const char *name)
{
assert(init_ok);
assert(name);
size_t i;
for (i = 0; i < device_table_len; i++)
{
if (!strncmp(name, device_table[i]->name, FAL_DEV_NAME_MAX)) {
return device_table[i];
}
}
return NULL;
}

View File

@ -0,0 +1,525 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-17 armink the first version
*/
#include <fal.h>
#include <string.h>
#include <stdlib.h>
/* partition magic word */
#define FAL_PART_MAGIC_WORD 0x45503130
#define FAL_PART_MAGIC_WORD_H 0x4550L
#define FAL_PART_MAGIC_WORD_L 0x3130L
struct part_flash_info
{
const struct fal_flash_dev *flash_dev;
};
/**
* FAL partition table config has defined on 'fal_cfg.h'.
* When this option is disable, it will auto find the partition table on a specified location in flash partition.
*/
#ifdef FAL_PART_HAS_TABLE_CFG
/* check partition table definition */
#if !defined(FAL_PART_TABLE)
#error "You must defined FAL_PART_TABLE on 'fal_cfg.h'"
#endif
#ifdef __CC_ARM /* ARM Compiler */
#define SECTION(x) __attribute__((section(x)))
#define USED __attribute__((used))
#elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */
#define SECTION(x) @ x
#define USED __root
#elif defined (__GNUC__) /* GNU GCC Compiler */
#define SECTION(x) __attribute__((section(x)))
#define USED __attribute__((used))
#else
#error not supported tool chain
#endif /* __CC_ARM */
//USED static const struct fal_partition partition_table_def[] SECTION("FalPartTable") = FAL_PART_TABLE;
static const struct fal_partition partition_table_def[] = FAL_PART_TABLE;
static const struct fal_partition *partition_table = NULL;
/* partition and flash object information cache table */
static struct part_flash_info part_flash_cache[sizeof(partition_table_def) / sizeof(partition_table_def[0])] = { 0 };
#else /* FAL_PART_HAS_TABLE_CFG */
#if !defined(FAL_PART_TABLE_FLASH_DEV_NAME)
#error "You must defined FAL_PART_TABLE_FLASH_DEV_NAME on 'fal_cfg.h'"
#endif
/* check partition table end offset address definition */
#if !defined(FAL_PART_TABLE_END_OFFSET)
#error "You must defined FAL_PART_TABLE_END_OFFSET on 'fal_cfg.h'"
#endif
static struct fal_partition *partition_table = NULL;
static struct part_flash_info *part_flash_cache = NULL;
#endif /* FAL_PART_HAS_TABLE_CFG */
static uint8_t init_ok = 0;
static size_t partition_table_len = 0;
/**
* print the partition table
*/
void fal_show_part_table(void)
{
char *item1 = "name", *item2 = "flash_dev";
size_t i, part_name_max = strlen(item1), flash_dev_name_max = strlen(item2);
const struct fal_partition *part;
if (partition_table_len)
{
for (i = 0; i < partition_table_len; i++)
{
part = &partition_table[i];
if (strlen(part->name) > part_name_max)
{
part_name_max = strlen(part->name);
}
if (strlen(part->flash_name) > flash_dev_name_max)
{
flash_dev_name_max = strlen(part->flash_name);
}
}
}
log_i("==================== FAL partition table ====================");
log_i("| %-*.*s | %-*.*s | offset | length |", part_name_max, FAL_DEV_NAME_MAX, item1, flash_dev_name_max,
FAL_DEV_NAME_MAX, item2);
log_i("-------------------------------------------------------------");
for (i = 0; i < partition_table_len; i++)
{
#ifdef FAL_PART_HAS_TABLE_CFG
part = &partition_table[i];
#else
part = &partition_table[partition_table_len - i - 1];
#endif
log_i("| %-*.*s | %-*.*s | 0x%08lx | 0x%08x |", part_name_max, FAL_DEV_NAME_MAX, part->name, flash_dev_name_max,
FAL_DEV_NAME_MAX, part->flash_name, part->offset, part->len);
}
log_i("=============================================================");
}
static int check_and_update_part_cache(const struct fal_partition *table, size_t len)
{
const struct fal_flash_dev *flash_dev = NULL;
size_t i;
#ifndef FAL_PART_HAS_TABLE_CFG
if (part_flash_cache)
{
FAL_FREE(part_flash_cache);
}
part_flash_cache = FAL_MALLOC(len * sizeof(struct part_flash_info));
if (part_flash_cache == NULL)
{
log_e("Initialize failed! No memory for partition table cache");
return -2;
}
#endif
for (i = 0; i < len; i++)
{
flash_dev = fal_flash_device_find(table[i].flash_name);
if (flash_dev == NULL)
{
log_d("Warning: Do NOT found the flash device(%s).", table[i].flash_name);
continue;
}
if (table[i].offset >= (long)flash_dev->len)
{
log_e("Initialize failed! Partition(%s) offset address(%ld) out of flash bound(<%d).",
table[i].name, table[i].offset, flash_dev->len);
partition_table_len = 0;
return -1;
}
part_flash_cache[i].flash_dev = flash_dev;
}
return 0;
}
/**
* Initialize all flash partition on FAL partition table
*
* @return partitions total number
*/
int fal_partition_init(void)
{
if (init_ok)
{
return partition_table_len;
}
#ifdef FAL_PART_HAS_TABLE_CFG
partition_table = &partition_table_def[0];
partition_table_len = sizeof(partition_table_def) / sizeof(partition_table_def[0]);
#else
/* load partition table from the end address FAL_PART_TABLE_END_OFFSET, error return 0 */
long part_table_offset = FAL_PART_TABLE_END_OFFSET;
size_t table_num = 0, table_item_size = 0;
uint8_t part_table_find_ok = 0;
uint32_t read_magic_word;
fal_partition_t new_part = NULL;
size_t i;
const struct fal_flash_dev *flash_dev = NULL;
flash_dev = fal_flash_device_find(FAL_PART_TABLE_FLASH_DEV_NAME);
if (flash_dev == NULL)
{
log_e("Initialize failed! Flash device (%s) NOT found.", FAL_PART_TABLE_FLASH_DEV_NAME);
goto _exit;
}
/* check partition table offset address */
if (part_table_offset < 0 || part_table_offset >= (long) flash_dev->len)
{
log_e("Setting partition table end offset address(%ld) out of flash bound(<%d).", part_table_offset, flash_dev->len);
goto _exit;
}
table_item_size = sizeof(struct fal_partition);
new_part = (fal_partition_t)FAL_MALLOC(table_item_size);
if (new_part == NULL)
{
log_e("Initialize failed! No memory for table buffer.");
goto _exit;
}
/* find partition table location */
{
uint8_t read_buf[64];
part_table_offset -= sizeof(read_buf);
while (part_table_offset >= 0)
{
if (flash_dev->ops.read(part_table_offset, read_buf, sizeof(read_buf)) > 0)
{
/* find magic word in read buf */
for (i = 0; i < sizeof(read_buf) - sizeof(read_magic_word) + 1; i++)
{
read_magic_word = read_buf[0 + i] + (read_buf[1 + i] << 8) + (read_buf[2 + i] << 16) + (read_buf[3 + i] << 24);
if (read_magic_word == ((FAL_PART_MAGIC_WORD_H << 16) + FAL_PART_MAGIC_WORD_L))
{
part_table_find_ok = 1;
part_table_offset += i;
log_d("Find the partition table on '%s' offset @0x%08lx.", FAL_PART_TABLE_FLASH_DEV_NAME,
part_table_offset);
break;
}
}
}
else
{
/* read failed */
break;
}
if (part_table_find_ok)
{
break;
}
else
{
/* calculate next read buf position */
if (part_table_offset >= (long)sizeof(read_buf))
{
part_table_offset -= sizeof(read_buf);
part_table_offset += (sizeof(read_magic_word) - 1);
}
else if (part_table_offset != 0)
{
part_table_offset = 0;
}
else
{
/* find failed */
break;
}
}
}
}
/* load partition table */
while (part_table_find_ok)
{
memset(new_part, 0x00, table_num);
if (flash_dev->ops.read(part_table_offset - table_item_size * (table_num), (uint8_t *) new_part,
table_item_size) < 0)
{
log_e("Initialize failed! Flash device (%s) read error!", flash_dev->name);
table_num = 0;
break;
}
if (new_part->magic_word != ((FAL_PART_MAGIC_WORD_H << 16) + FAL_PART_MAGIC_WORD_L))
{
break;
}
partition_table = (fal_partition_t) FAL_REALLOC(partition_table, table_item_size * (table_num + 1));
if (partition_table == NULL)
{
log_e("Initialize failed! No memory for partition table");
table_num = 0;
break;
}
memcpy(partition_table + table_num, new_part, table_item_size);
table_num++;
};
if (table_num == 0)
{
log_e("Partition table NOT found on flash: %s (len: %d) from offset: 0x%08x.", FAL_PART_TABLE_FLASH_DEV_NAME,
FAL_DEV_NAME_MAX, FAL_PART_TABLE_END_OFFSET);
goto _exit;
}
else
{
partition_table_len = table_num;
}
#endif /* FAL_PART_HAS_TABLE_CFG */
/* check the partition table device exists */
if (check_and_update_part_cache(partition_table, partition_table_len) != 0)
{
goto _exit;
}
init_ok = 1;
_exit:
#if FAL_DEBUG
fal_show_part_table();
#endif
#ifndef FAL_PART_HAS_TABLE_CFG
if (new_part)
{
FAL_FREE(new_part);
}
#endif /* !FAL_PART_HAS_TABLE_CFG */
return partition_table_len;
}
/**
* find the partition by name
*
* @param name partition name
*
* @return != NULL: partition
* NULL: not found
*/
const struct fal_partition *fal_partition_find(const char *name)
{
assert(init_ok);
size_t i;
for (i = 0; i < partition_table_len; i++)
{
if (!strcmp(name, partition_table[i].name))
{
return &partition_table[i];
}
}
return NULL;
}
static const struct fal_flash_dev *flash_device_find_by_part(const struct fal_partition *part)
{
assert(part >= partition_table);
assert(part <= &partition_table[partition_table_len - 1]);
return part_flash_cache[part - partition_table].flash_dev;
}
/**
* get the partition table
*
* @param len return the partition table length
*
* @return partition table
*/
const struct fal_partition *fal_get_partition_table(size_t *len)
{
assert(init_ok);
assert(len);
*len = partition_table_len;
return partition_table;
}
/**
* set partition table temporarily
* This setting will modify the partition table temporarily, the setting will be lost after restart.
*
* @param table partition table
* @param len partition table length
*/
void fal_set_partition_table_temp(struct fal_partition *table, size_t len)
{
assert(init_ok);
assert(table);
check_and_update_part_cache(table, len);
partition_table_len = len;
partition_table = table;
}
/**
* read data from partition
*
* @param part partition
* @param addr relative address for partition
* @param buf read buffer
* @param size read size
*
* @return >= 0: successful read data size
* -1: error
*/
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size)
{
int ret = 0;
const struct fal_flash_dev *flash_dev = NULL;
assert(part);
assert(buf);
if (addr + size > part->len)
{
log_e("Partition read error! Partition address out of bound.");
return -1;
}
flash_dev = flash_device_find_by_part(part);
if (flash_dev == NULL)
{
log_e("Partition read error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
return -1;
}
ret = flash_dev->ops.read(part->offset + addr, buf, size);
if (ret < 0)
{
log_e("Partition read error! Flash device(%s) read error!", part->flash_name);
}
return ret;
}
/**
* write data to partition
*
* @param part partition
* @param addr relative address for partition
* @param buf write buffer
* @param size write size
*
* @return >= 0: successful write data size
* -1: error
*/
int fal_partition_write(const struct fal_partition *part, uint32_t addr, const uint8_t *buf, size_t size)
{
int ret = 0;
const struct fal_flash_dev *flash_dev = NULL;
assert(part);
assert(buf);
if (addr + size > part->len)
{
log_e("Partition write error! Partition address out of bound.");
return -1;
}
flash_dev = flash_device_find_by_part(part);
if (flash_dev == NULL)
{
log_e("Partition write error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
return -1;
}
ret = flash_dev->ops.write(part->offset + addr, buf, size);
if (ret < 0)
{
log_e("Partition write error! Flash device(%s) write error!", part->flash_name);
}
return ret;
}
/**
* erase partition data
*
* @param part partition
* @param addr relative address for partition
* @param size erase size
*
* @return >= 0: successful erased data size
* -1: error
*/
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size)
{
int ret = 0;
const struct fal_flash_dev *flash_dev = NULL;
assert(part);
if (addr + size > part->len)
{
log_e("Partition erase error! Partition address out of bound.");
return -1;
}
flash_dev = flash_device_find_by_part(part);
if (flash_dev == NULL)
{
log_e("Partition erase error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
return -1;
}
ret = flash_dev->ops.erase(part->offset + addr, size);
if (ret < 0)
{
log_e("Partition erase error! Flash device(%s) erase error!", part->flash_name);
}
return ret;
}
/**
* erase partition all data
*
* @param part partition
*
* @return >= 0: successful erased data size
* -1: error
*/
int fal_partition_erase_all(const struct fal_partition *part)
{
return fal_partition_erase(part, 0, part->len);
}

View File

@ -0,0 +1,934 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-06-23 armink the first version
* 2019-08-22 MurphyZhao adapt to none rt-thread case
*/
#include <fal.h>
#ifdef RT_VER_NUM
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
/* ========================== block device ======================== */
struct fal_blk_device
{
struct rt_device parent;
struct rt_device_blk_geometry geometry;
const struct fal_partition *fal_part;
};
/* RT-Thread device interface */
#if RTTHREAD_VERSION >= 30000
static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args)
#else
static rt_err_t blk_dev_control(rt_device_t dev, rt_uint8_t cmd, void *args)
#endif
{
struct fal_blk_device *part = (struct fal_blk_device*) dev;
assert(part != RT_NULL);
if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
{
struct rt_device_blk_geometry *geometry;
geometry = (struct rt_device_blk_geometry *) args;
if (geometry == RT_NULL)
{
return -RT_ERROR;
}
memcpy(geometry, &part->geometry, sizeof(struct rt_device_blk_geometry));
}
else if (cmd == RT_DEVICE_CTRL_BLK_ERASE)
{
rt_uint32_t *addrs = (rt_uint32_t *) args, start_addr = addrs[0], end_addr = addrs[1], phy_start_addr;
rt_size_t phy_size;
if (addrs == RT_NULL || start_addr > end_addr)
{
return -RT_ERROR;
}
if (end_addr == start_addr)
{
end_addr++;
}
phy_start_addr = start_addr * part->geometry.bytes_per_sector;
phy_size = (end_addr - start_addr) * part->geometry.bytes_per_sector;
if (fal_partition_erase(part->fal_part, phy_start_addr, phy_size) < 0)
{
return -RT_ERROR;
}
}
return RT_EOK;
}
static rt_size_t blk_dev_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{
int ret = 0;
struct fal_blk_device *part = (struct fal_blk_device*) dev;
assert(part != RT_NULL);
ret = fal_partition_read(part->fal_part, pos * part->geometry.block_size, buffer, size * part->geometry.block_size);
if (ret != (int)(size * part->geometry.block_size))
{
ret = 0;
}
else
{
ret = size;
}
return ret;
}
static rt_size_t blk_dev_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
{
int ret = 0;
struct fal_blk_device *part;
rt_off_t phy_pos;
rt_size_t phy_size;
part = (struct fal_blk_device*) dev;
assert(part != RT_NULL);
/* change the block device's logic address to physical address */
phy_pos = pos * part->geometry.bytes_per_sector;
phy_size = size * part->geometry.bytes_per_sector;
ret = fal_partition_erase(part->fal_part, phy_pos, phy_size);
if (ret == (int) phy_size)
{
ret = fal_partition_write(part->fal_part, phy_pos, buffer, phy_size);
}
if (ret != (int) phy_size)
{
ret = 0;
}
else
{
ret = size;
}
return ret;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops blk_dev_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
blk_dev_read,
blk_dev_write,
blk_dev_control
};
#endif
/**
* create RT-Thread block device by specified partition
*
* @param parition_name partition name
*
* @return != NULL: created block device
* NULL: created failed
*/
struct rt_device *fal_blk_device_create(const char *parition_name)
{
struct fal_blk_device *blk_dev;
const struct fal_partition *fal_part = fal_partition_find(parition_name);
const struct fal_flash_dev *fal_flash = NULL;
if (!fal_part)
{
log_e("Error: the partition name (%s) is not found.", parition_name);
return NULL;
}
if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL)
{
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
return NULL;
}
blk_dev = (struct fal_blk_device*) rt_malloc(sizeof(struct fal_blk_device));
if (blk_dev)
{
blk_dev->fal_part = fal_part;
blk_dev->geometry.bytes_per_sector = fal_flash->blk_size;
blk_dev->geometry.block_size = fal_flash->blk_size;
blk_dev->geometry.sector_count = fal_part->len / fal_flash->blk_size;
/* register device */
blk_dev->parent.type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
blk_dev->parent.ops = &blk_dev_ops;
#else
blk_dev->parent.init = NULL;
blk_dev->parent.open = NULL;
blk_dev->parent.close = NULL;
blk_dev->parent.read = blk_dev_read;
blk_dev->parent.write = blk_dev_write;
blk_dev->parent.control = blk_dev_control;
#endif
/* no private */
blk_dev->parent.user_data = RT_NULL;
log_i("The FAL block device (%s) created successfully", fal_part->name);
rt_device_register(RT_DEVICE(blk_dev), fal_part->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
}
else
{
log_e("Error: no memory for create FAL block device");
}
return RT_DEVICE(blk_dev);
}
/* ========================== MTD nor device ======================== */
#if defined(RT_USING_MTD_NOR)
struct fal_mtd_nor_device
{
struct rt_mtd_nor_device parent;
const struct fal_partition *fal_part;
};
static rt_size_t mtd_nor_dev_read(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint8_t* data, rt_uint32_t length)
{
int ret = 0;
struct fal_mtd_nor_device *part = (struct fal_mtd_nor_device*) device;
assert(part != RT_NULL);
ret = fal_partition_read(part->fal_part, offset, data, length);
if (ret != (int)length)
{
ret = 0;
}
else
{
ret = length;
}
return ret;
}
static rt_size_t mtd_nor_dev_write(struct rt_mtd_nor_device* device, rt_off_t offset, const rt_uint8_t* data, rt_uint32_t length)
{
int ret = 0;
struct fal_mtd_nor_device *part;
part = (struct fal_mtd_nor_device*) device;
assert(part != RT_NULL);
ret = fal_partition_write(part->fal_part, offset, data, length);
if (ret != (int) length)
{
ret = 0;
}
else
{
ret = length;
}
return ret;
}
static rt_err_t mtd_nor_dev_erase(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint32_t length)
{
int ret = 0;
struct fal_mtd_nor_device *part;
part = (struct fal_mtd_nor_device*) device;
assert(part != RT_NULL);
ret = fal_partition_erase(part->fal_part, offset, length);
if (ret != length)
{
return -RT_ERROR;
}
else
{
return RT_EOK;
}
}
static const struct rt_mtd_nor_driver_ops _ops =
{
RT_NULL,
mtd_nor_dev_read,
mtd_nor_dev_write,
mtd_nor_dev_erase,
};
/**
* create RT-Thread MTD NOR device by specified partition
*
* @param parition_name partition name
*
* @return != NULL: created MTD NOR device
* NULL: created failed
*/
struct rt_device *fal_mtd_nor_device_create(const char *parition_name)
{
struct fal_mtd_nor_device *mtd_nor_dev;
const struct fal_partition *fal_part = fal_partition_find(parition_name);
const struct fal_flash_dev *fal_flash = NULL;
if (!fal_part)
{
log_e("Error: the partition name (%s) is not found.", parition_name);
return NULL;
}
if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL)
{
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
return NULL;
}
mtd_nor_dev = (struct fal_mtd_nor_device*) rt_malloc(sizeof(struct fal_mtd_nor_device));
if (mtd_nor_dev)
{
mtd_nor_dev->fal_part = fal_part;
mtd_nor_dev->parent.block_start = 0;
mtd_nor_dev->parent.block_end = fal_part->len / fal_flash->blk_size;
mtd_nor_dev->parent.block_size = fal_flash->blk_size;
/* set ops */
mtd_nor_dev->parent.ops = &_ops;
log_i("The FAL MTD NOR device (%s) created successfully", fal_part->name);
rt_mtd_nor_register_device(fal_part->name, &mtd_nor_dev->parent);
}
else
{
log_e("Error: no memory for create FAL MTD NOR device");
}
return RT_DEVICE(&mtd_nor_dev->parent);
}
#endif /* defined(RT_USING_MTD_NOR) */
/* ========================== char device ======================== */
struct fal_char_device
{
struct rt_device parent;
const struct fal_partition *fal_part;
};
/* RT-Thread device interface */
static rt_size_t char_dev_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
int ret = 0;
struct fal_char_device *part = (struct fal_char_device *) dev;
assert(part != RT_NULL);
if (pos + size > part->fal_part->len)
size = part->fal_part->len - pos;
ret = fal_partition_read(part->fal_part, pos, buffer, size);
if (ret != (int)(size))
ret = 0;
return ret;
}
static rt_size_t char_dev_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
int ret = 0;
struct fal_char_device *part;
part = (struct fal_char_device *) dev;
assert(part != RT_NULL);
if (pos == 0)
{
fal_partition_erase_all(part->fal_part);
}
else if (pos + size > part->fal_part->len)
{
size = part->fal_part->len - pos;
}
ret = fal_partition_write(part->fal_part, pos, buffer, size);
if (ret != (int) size)
ret = 0;
return ret;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops char_dev_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
char_dev_read,
char_dev_write,
RT_NULL
};
#endif
#ifdef RT_USING_POSIX
#include <dfs_posix.h>
/* RT-Thread device filesystem interface */
static int char_dev_fopen(struct dfs_fd *fd)
{
struct fal_char_device *part = (struct fal_char_device *) fd->data;
assert(part != RT_NULL);
switch (fd->flags & O_ACCMODE)
{
case O_RDONLY:
break;
case O_WRONLY:
case O_RDWR:
/* erase partition when device file open */
fal_partition_erase_all(part->fal_part);
break;
default:
break;
}
fd->pos = 0;
return RT_EOK;
}
static int char_dev_fread(struct dfs_fd *fd, void *buf, size_t count)
{
int ret = 0;
struct fal_char_device *part = (struct fal_char_device *) fd->data;
assert(part != RT_NULL);
if (fd->pos + count > part->fal_part->len)
count = part->fal_part->len - fd->pos;
ret = fal_partition_read(part->fal_part, fd->pos, buf, count);
if (ret != (int)(count))
return 0;
fd->pos += ret;
return ret;
}
static int char_dev_fwrite(struct dfs_fd *fd, const void *buf, size_t count)
{
int ret = 0;
struct fal_char_device *part = (struct fal_char_device *) fd->data;
assert(part != RT_NULL);
if (fd->pos + count > part->fal_part->len)
count = part->fal_part->len - fd->pos;
ret = fal_partition_write(part->fal_part, fd->pos, buf, count);
if (ret != (int) count)
return 0;
fd->pos += ret;
return ret;
}
static const struct dfs_file_ops char_dev_fops =
{
char_dev_fopen,
RT_NULL,
RT_NULL,
char_dev_fread,
char_dev_fwrite,
RT_NULL, /* flush */
RT_NULL, /* lseek */
RT_NULL, /* getdents */
RT_NULL,
};
#endif /* defined(RT_USING_POSIX) */
/**
* create RT-Thread char device by specified partition
*
* @param parition_name partition name
*
* @return != NULL: created char device
* NULL: created failed
*/
struct rt_device *fal_char_device_create(const char *parition_name)
{
struct fal_char_device *char_dev;
const struct fal_partition *fal_part = fal_partition_find(parition_name);
if (!fal_part)
{
log_e("Error: the partition name (%s) is not found.", parition_name);
return NULL;
}
if ((fal_flash_device_find(fal_part->flash_name)) == NULL)
{
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
return NULL;
}
char_dev = (struct fal_char_device *) rt_malloc(sizeof(struct fal_char_device));
if (char_dev)
{
char_dev->fal_part = fal_part;
/* register device */
char_dev->parent.type = RT_Device_Class_Char;
#ifdef RT_USING_DEVICE_OPS
char_dev->parent.ops = &char_dev_ops;
#else
char_dev->parent.init = NULL;
char_dev->parent.open = NULL;
char_dev->parent.close = NULL;
char_dev->parent.read = char_dev_read;
char_dev->parent.write = char_dev_write;
char_dev->parent.control = NULL;
/* no private */
char_dev->parent.user_data = NULL;
#endif
rt_device_register(RT_DEVICE(char_dev), fal_part->name, RT_DEVICE_FLAG_RDWR);
log_i("The FAL char device (%s) created successfully", fal_part->name);
#ifdef RT_USING_POSIX
/* set fops */
char_dev->parent.fops = &char_dev_fops;
#endif
}
else
{
log_e("Error: no memory for create FAL char device");
}
return RT_DEVICE(char_dev);
}
#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
#include <finsh.h>
extern int fal_init_check(void);
static void fal(uint8_t argc, char **argv) {
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
#define HEXDUMP_WIDTH 16
#define CMD_PROBE_INDEX 0
#define CMD_READ_INDEX 1
#define CMD_WRITE_INDEX 2
#define CMD_ERASE_INDEX 3
#define CMD_BENCH_INDEX 4
int result;
static const struct fal_flash_dev *flash_dev = NULL;
static const struct fal_partition *part_dev = NULL;
size_t i = 0, j = 0;
const char* help_info[] =
{
[CMD_PROBE_INDEX] = "fal probe [dev_name|part_name] - probe flash device or partition by given name",
[CMD_READ_INDEX] = "fal read addr size - read 'size' bytes starting at 'addr'",
[CMD_WRITE_INDEX] = "fal write addr data1 ... dataN - write some bytes 'data' starting at 'addr'",
[CMD_ERASE_INDEX] = "fal erase addr size - erase 'size' bytes starting at 'addr'",
[CMD_BENCH_INDEX] = "fal bench <blk_size> - benchmark test with per block size",
};
if (fal_init_check() != 1)
{
rt_kprintf("\n[Warning] FAL is not initialized or failed to initialize!\n\n");
return;
}
if (argc < 2)
{
rt_kprintf("Usage:\n");
for (i = 0; i < sizeof(help_info) / sizeof(char*); i++)
{
rt_kprintf("%s\n", help_info[i]);
}
rt_kprintf("\n");
}
else
{
const char *operator = argv[1];
uint32_t addr, size;
if (!strcmp(operator, "probe"))
{
if (argc >= 3)
{
char *dev_name = argv[2];
if ((flash_dev = fal_flash_device_find(dev_name)) != NULL)
{
part_dev = NULL;
}
else if ((part_dev = fal_partition_find(dev_name)) != NULL)
{
flash_dev = NULL;
}
else
{
rt_kprintf("Device %s NOT found. Probe failed.\n", dev_name);
flash_dev = NULL;
part_dev = NULL;
}
}
if (flash_dev)
{
rt_kprintf("Probed a flash device | %s | addr: %ld | len: %d |.\n", flash_dev->name,
flash_dev->addr, flash_dev->len);
}
else if (part_dev)
{
rt_kprintf("Probed a flash partition | %s | flash_dev: %s | offset: %ld | len: %d |.\n",
part_dev->name, part_dev->flash_name, part_dev->offset, part_dev->len);
}
else
{
rt_kprintf("No flash device or partition was probed.\n");
rt_kprintf("Usage: %s.\n", help_info[CMD_PROBE_INDEX]);
fal_show_part_table();
}
}
else
{
if (!flash_dev && !part_dev)
{
rt_kprintf("No flash device or partition was probed. Please run 'fal probe'.\n");
return;
}
if (!rt_strcmp(operator, "read"))
{
if (argc < 4)
{
rt_kprintf("Usage: %s.\n", help_info[CMD_READ_INDEX]);
return;
}
else
{
addr = strtol(argv[2], NULL, 0);
size = strtol(argv[3], NULL, 0);
uint8_t *data = rt_malloc(size);
if (data)
{
if (flash_dev)
{
result = flash_dev->ops.read(addr, data, size);
}
else if (part_dev)
{
result = fal_partition_read(part_dev, addr, data, size);
}
if (result >= 0)
{
rt_kprintf("Read data success. Start from 0x%08X, size is %ld. The data is:\n", addr,
size);
rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
for (i = 0; i < size; i += HEXDUMP_WIDTH)
{
rt_kprintf("[%08X] ", addr + i);
/* dump hex */
for (j = 0; j < HEXDUMP_WIDTH; j++)
{
if (i + j < size)
{
rt_kprintf("%02X ", data[i + j]);
}
else
{
rt_kprintf(" ");
}
}
/* dump char for hex */
for (j = 0; j < HEXDUMP_WIDTH; j++)
{
if (i + j < size)
{
rt_kprintf("%c", __is_print(data[i + j]) ? data[i + j] : '.');
}
}
rt_kprintf("\n");
}
rt_kprintf("\n");
}
rt_free(data);
}
else
{
rt_kprintf("Low memory!\n");
}
}
}
else if (!strcmp(operator, "write"))
{
if (argc < 4)
{
rt_kprintf("Usage: %s.\n", help_info[CMD_WRITE_INDEX]);
return;
}
else
{
addr = strtol(argv[2], NULL, 0);
size = argc - 3;
uint8_t *data = rt_malloc(size);
if (data)
{
for (i = 0; i < size; i++)
{
data[i] = strtol(argv[3 + i], NULL, 0);
}
if (flash_dev)
{
result = flash_dev->ops.write(addr, data, size);
}
else if (part_dev)
{
result = fal_partition_write(part_dev, addr, data, size);
}
if (result >= 0)
{
rt_kprintf("Write data success. Start from 0x%08X, size is %ld.\n", addr, size);
rt_kprintf("Write data: ");
for (i = 0; i < size; i++)
{
rt_kprintf("%d ", data[i]);
}
rt_kprintf(".\n");
}
rt_free(data);
}
else
{
rt_kprintf("Low memory!\n");
}
}
}
else if (!rt_strcmp(operator, "erase"))
{
if (argc < 4)
{
rt_kprintf("Usage: %s.\n", help_info[CMD_ERASE_INDEX]);
return;
}
else
{
addr = strtol(argv[2], NULL, 0);
size = strtol(argv[3], NULL, 0);
if (flash_dev)
{
result = flash_dev->ops.erase(addr, size);
}
else if (part_dev)
{
result = fal_partition_erase(part_dev, addr, size);
}
if (result >= 0)
{
rt_kprintf("Erase data success. Start from 0x%08X, size is %ld.\n", addr, size);
}
}
}
else if (!strcmp(operator, "bench"))
{
if (argc < 3)
{
rt_kprintf("Usage: %s.\n", help_info[CMD_BENCH_INDEX]);
return;
}
else if ((argc > 3 && strcmp(argv[3], "yes")) || argc < 4)
{
rt_kprintf("DANGER: It will erase full chip or partition! Please run 'fal bench %d yes'.\n", strtol(argv[2], NULL, 0));
return;
}
/* full chip benchmark test */
uint32_t start_time, time_cast;
size_t write_size = strtol(argv[2], NULL, 0), read_size = strtol(argv[2], NULL, 0), cur_op_size;
uint8_t *write_data = (uint8_t *)rt_malloc(write_size), *read_data = (uint8_t *)rt_malloc(read_size);
if (write_data && read_data)
{
for (i = 0; i < write_size; i ++) {
write_data[i] = i & 0xFF;
}
if (flash_dev)
{
size = flash_dev->len;
}
else if (part_dev)
{
size = part_dev->len;
}
/* benchmark testing */
rt_kprintf("Erasing %ld bytes data, waiting...\n", size);
start_time = rt_tick_get();
if (flash_dev)
{
result = flash_dev->ops.erase(0, size);
}
else if (part_dev)
{
result = fal_partition_erase(part_dev, 0, size);
}
if (result >= 0)
{
time_cast = rt_tick_get() - start_time;
rt_kprintf("Erase benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
}
else
{
rt_kprintf("Erase benchmark has an error. Error code: %d.\n", result);
}
/* write test */
rt_kprintf("Writing %ld bytes data, waiting...\n", size);
start_time = rt_tick_get();
for (i = 0; i < size; i += write_size)
{
if (i + write_size <= size)
{
cur_op_size = write_size;
}
else
{
cur_op_size = size - i;
}
if (flash_dev)
{
result = flash_dev->ops.write(i, write_data, cur_op_size);
}
else if (part_dev)
{
result = fal_partition_write(part_dev, i, write_data, cur_op_size);
}
if (result < 0)
{
break;
}
}
if (result >= 0)
{
time_cast = rt_tick_get() - start_time;
rt_kprintf("Write benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
}
else
{
rt_kprintf("Write benchmark has an error. Error code: %d.\n", result);
}
/* read test */
rt_kprintf("Reading %ld bytes data, waiting...\n", size);
start_time = rt_tick_get();
for (i = 0; i < size; i += read_size)
{
if (i + read_size <= size)
{
cur_op_size = read_size;
}
else
{
cur_op_size = size - i;
}
if (flash_dev)
{
result = flash_dev->ops.read(i, read_data, cur_op_size);
}
else if (part_dev)
{
result = fal_partition_read(part_dev, i, read_data, cur_op_size);
}
/* data check */
for (int index = 0; index < cur_op_size; index ++)
{
if (write_data[index] != read_data[index])
{
rt_kprintf("%d %d %02x %02x.\n", i, index, write_data[index], read_data[index]);
}
}
if (memcmp(write_data, read_data, cur_op_size))
{
result = -RT_ERROR;
rt_kprintf("Data check ERROR! Please check you flash by other command.\n");
}
/* has an error */
if (result < 0)
{
break;
}
}
if (result >= 0)
{
time_cast = rt_tick_get() - start_time;
rt_kprintf("Read benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
}
else
{
rt_kprintf("Read benchmark has an error. Error code: %d.\n", result);
}
}
else
{
rt_kprintf("Low memory!\n");
}
rt_free(write_data);
rt_free(read_data);
}
else
{
rt_kprintf("Usage:\n");
for (i = 0; i < sizeof(help_info) / sizeof(char*); i++)
{
rt_kprintf("%s\n", help_info[i]);
}
rt_kprintf("\n");
return;
}
if (result < 0) {
rt_kprintf("This operate has an error. Error code: %d.\n", result);
}
}
}
}
MSH_CMD_EXPORT(fal, FAL (Flash Abstraction Layer) operate.);
#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
#endif /* RT_VER_NUM */

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-01-26 armink the first version
*/
#include "eeprom_fm24.h"
#include <fal.h>
static int init(void);
static int erase(long offset, size_t size);
static int read(long offset, uint8_t *buf, size_t size);
static int write(long offset, const uint8_t *buf, size_t size);
// 1.定义 flash 设备
struct fal_flash_dev eeprom_fm24 =
{
.name = EEPROM_FM24_DEV_NAME,
.addr = 0,
.len = FM24_PAGE_SIZE,
.blk_size = EEPROM_FM24_BLOCK_SIZE,
.ops = {init, read, write, erase},
.write_gran = 1};
static int init(void)
{
return 1;
}
static int erase(long offset, size_t size)
{
// uint8_t erase_size = size > FAL_ERASE_SIZE ? FAL_ERASE_SIZE : size;
// uint8_t buf[FAL_ERASE_SIZE];
// osel_memset(buf, 0xFF, FAL_ERASE_SIZE);
// for (uint8_t i = 0; i < (size / FM24_PAGE_SIZE); i++)
// {
// uint32_t addr = eeprom_fm24.addr + offset + i * FM24_PAGE_SIZE;
// eeprom_fm24_write(addr, (uint8_t *)buf, erase_size);
// }
return size;
}
static int read(long offset, uint8_t *buf, size_t size)
{
/* You can add your code under here. */
if (size == 0)
{
return 0;
}
uint32_t addr = eeprom_fm24.addr + offset;
BOOL res = eeprom_fm24_read(addr, buf, size);
return res == TRUE ? size : 0;
}
static int write(long offset, const uint8_t *buf, size_t size)
{
if (size == 0)
{
return 0;
}
uint32_t addr = eeprom_fm24.addr + offset;
BOOL res = eeprom_fm24_write(addr, (uint8_t *)buf, size);
return res == TRUE ? (int)size : -1;
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-01-26 armink the first version
*/
#include "eeprom_m95.h"
#include <fal.h>
static int init(void);
static int erase(long offset, size_t size);
static int read1(long offset, uint8_t *buf, size_t size);
static int read2(long offset, uint8_t *buf, size_t size);
static int write1(long offset, const uint8_t *buf, size_t size);
static int write2(long offset, const uint8_t *buf, size_t size);
// 1.定义 flash 设备
struct fal_flash_dev eeprom_m95_1 =
{
.name = EEPROM_M95_1_DEV_NAME,
.addr = 10 * M95_PAGE_SIZE_256,
.len = EEPROM_M95_1_SIZE - 10 * M95_PAGE_SIZE_256,
.blk_size = EEPROM_M95_1_BLOCK_SIZE,
.ops = {init, read1, write1, erase},
.write_gran = 1}; // 设置写粒度,单位 bitEPPROM写粒度为1bit
struct fal_flash_dev eeprom_m95_2 =
{
.name = EEPROM_M95_2_DEV_NAME,
.addr = 0x000000,
.len = EEPROM_M95_2_SIZE,
.blk_size = EEPROM_M95_2_BLOCK_SIZE,
.ops = {init, read2, write2, erase},
.write_gran = 1};
static int init(void)
{
return 1;
}
static int erase(long offset, size_t size)
{
// uint8_t erase_size = size > FAL_ERASE_SIZE ? FAL_ERASE_SIZE : size;
// uint8_t buf[FAL_ERASE_SIZE];
// osel_memset(buf, 0xFF, FAL_ERASE_SIZE);
// for (uint8_t i = 0; i < (size / M95_PAGE_SIZE_256); i++)
// {
// uint32_t addr = eeprom_m95_1.addr + offset + i * M95_PAGE_SIZE_256;
// eeprom_m95_write(M95_1, addr, (uint8_t *)buf, erase_size);
// }
return size;
}
static int read1(long offset, uint8_t *buf, size_t size)
{
/* You can add your code under here. */
uint32_t addr = eeprom_m95_1.addr + offset;
BOOL res = eeprom_m95_read(M95_1, addr, buf, size);
return res == TRUE ? size : 0;
}
static int write1(long offset, const uint8_t *buf, size_t size)
{
uint32_t addr = eeprom_m95_1.addr + offset;
BOOL res = eeprom_m95_write(M95_1, addr, (uint8_t *)buf, size);
return res == TRUE ? (int)size : -1;
}
static int read2(long offset, uint8_t *buf, size_t size)
{
/* You can add your code under here. */
uint32_t addr = eeprom_m95_2.addr + offset;
BOOL res = eeprom_m95_read(M95_2, addr, buf, size);
return res == TRUE ? size : 0;
}
static int write2(long offset, const uint8_t *buf, size_t size)
{
uint32_t addr = eeprom_m95_2.addr + offset;
BOOL res = eeprom_m95_write(M95_2, addr, (uint8_t *)buf, size);
return res == TRUE ? (int)size : -1;
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Initialize interface.
*
* Some initialize interface for this library.
*/
#include <flashdb.h>
#include <fdb_low_lvl.h>
#include <string.h>
#include <inttypes.h>
#ifdef FDB_USING_FILE_POSIX_MODE
#if !defined(_MSC_VER)
#include <unistd.h>
#endif
#endif /* FDB_USING_FILE_POSIX_MODE */
#define FDB_LOG_TAG ""
#if !defined(FDB_USING_FAL_MODE) && !defined(FDB_USING_FILE_MODE)
#error "Please defined the FDB_USING_FAL_MODE or FDB_USING_FILE_MODE macro"
#endif
fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_type type, void *user_data)
{
FDB_ASSERT(db);
FDB_ASSERT(name);
FDB_ASSERT(path);
if (db->init_ok) {
return FDB_NO_ERR;
}
db->name = name;
db->type = type;
db->user_data = user_data;
if (db->file_mode) {
#ifdef FDB_USING_FILE_MODE
memset(db->cur_file_sec, FDB_FAILED_ADDR, FDB_FILE_CACHE_TABLE_SIZE * sizeof(db->cur_file_sec[0]));
/* must set when using file mode */
FDB_ASSERT(db->sec_size != 0);
FDB_ASSERT(db->max_size != 0);
#ifdef FDB_USING_FILE_POSIX_MODE
memset(db->cur_file, -1, FDB_FILE_CACHE_TABLE_SIZE * sizeof(db->cur_file[0]));
#else
memset(db->cur_file, 0, FDB_FILE_CACHE_TABLE_SIZE * sizeof(db->cur_file[0]));
#endif
db->storage.dir = path;
FDB_ASSERT(strlen(path) != 0)
#endif
} else {
#ifdef FDB_USING_FAL_MODE
size_t block_size;
/* FAL (Flash Abstraction Layer) initialization */
fal_init();
/* check the flash partition */
if ((db->storage.part = fal_partition_find(path)) == NULL) {
FDB_INFO("Error: Partition (%s) not found.\n", path);
return FDB_PART_NOT_FOUND;
}
block_size = fal_flash_device_find(db->storage.part->flash_name)->blk_size;
if (db->sec_size == 0) {
db->sec_size = block_size;
} else {
/* must be aligned with block size */
if (db->sec_size % block_size != 0) {
FDB_INFO("Error: db sector size (%" PRIu32 ") MUST align with block size (%zu).\n", db->sec_size, block_size);
return FDB_INIT_FAILED;
}
}
db->max_size = db->storage.part->len;
#endif /* FDB_USING_FAL_MODE */
}
/* the block size MUST to be the Nth power of 2 */
FDB_ASSERT((db->sec_size & (db->sec_size - 1)) == 0);
/* must align with sector size */
if (db->max_size % db->sec_size != 0) {
FDB_INFO("Error: db total size (%" PRIu32 ") MUST align with sector size (%" PRIu32 ").\n", db->max_size, db->sec_size);
return FDB_INIT_FAILED;
}
/* must has more than or equal 2 sectors */
if (db->max_size / db->sec_size < 2) {
FDB_INFO("Error: db MUST has more than or equal 2 sectors, current has %" PRIu32 " sector(s)\n", db->max_size / db->sec_size);
return FDB_INIT_FAILED;
}
return FDB_NO_ERR;
}
void _fdb_init_finish(fdb_db_t db, fdb_err_t result)
{
static bool log_is_show = false;
if (result == FDB_NO_ERR) {
db->init_ok = true;
if (!log_is_show) {
FDB_INFO("FlashDB V%s is initialize success.\n", FDB_SW_VERSION);
FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n");
log_is_show = true;
}
} else if (!db->not_formatable) {
FDB_INFO("Error: %s (%s@%s) is initialize fail (%d).\n", db->type == FDB_DB_TYPE_KV ? "KVDB" : "TSDB",
db->name, _fdb_db_path(db), (int)result);
}
}
void _fdb_deinit(fdb_db_t db)
{
FDB_ASSERT(db);
if (db->init_ok) {
#ifdef FDB_USING_FILE_MODE
for (int i = 0; i < FDB_FILE_CACHE_TABLE_SIZE; i++) {
#ifdef FDB_USING_FILE_POSIX_MODE
if (db->cur_file[i] > 0) {
close(db->cur_file[i]);
}
#else
if (db->cur_file[i] != 0) {
fclose(db->cur_file[i]);
}
#endif /* FDB_USING_FILE_POSIX_MODE */
}
#endif /* FDB_USING_FILE_MODE */
}
db->init_ok = false;
}
const char *_fdb_db_path(fdb_db_t db)
{
if (db->file_mode) {
#ifdef FDB_USING_FILE_MODE
return db->storage.dir;
#else
return NULL;
#endif
}
else {
#ifdef FDB_USING_FAL_MODE
return db->storage.part->name;
#else
return NULL;
#endif
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief configuration file
*/
#ifndef _FDB_CFG_H_
#define _FDB_CFG_H_
/* using KVDB feature */
#define FDB_USING_KVDB
#ifdef FDB_USING_KVDB
/* Auto update KV to latest default when current KVDB version number is changed. @see fdb_kvdb.ver_num */
#define FDB_KV_AUTO_UPDATE
#endif
/* using TSDB (Time series database) feature */
#define FDB_USING_TSDB
/* Using FAL storage mode */
#define FDB_USING_FAL_MODE
#ifdef FDB_USING_FAL_MODE
/* the flash write granularity, unit: bit
* only support 1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1)/ 64(stm32f7)/ 128(stm32h5) */
#define FDB_WRITE_GRAN 1 /* @note you must define it for a value */
#endif
/* Using file storage mode by LIBC file API, like fopen/fread/fwrte/fclose */
/* #define FDB_USING_FILE_LIBC_MODE */
/* Using file storage mode by POSIX file API, like open/read/write/close */
/* #define FDB_USING_FILE_POSIX_MODE */
/* MCU Endian Configuration, default is Little Endian Order. */
/* #define FDB_BIG_ENDIAN */
/* log print macro. default EF_PRINT macro is printf() */
/* #define FDB_PRINT(...) my_printf(__VA_ARGS__) */
/* print debug information */
// #define FDB_DEBUG_ENABLE
#endif /* _FDB_CFG_H_ */

View File

@ -0,0 +1,385 @@
/*
* Copyright (c) 2020-2023, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public definition.
*/
#ifndef _FDB_DEF_H_
#define _FDB_DEF_H_
#ifdef FDB_USING_NATIVE_ASSERT
#include <assert.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/* software version number */
#define FDB_SW_VERSION "2.1.1"
#define FDB_SW_VERSION_NUM 0x20101
/* the KV max name length must less then it */
#ifndef FDB_KV_NAME_MAX
#define FDB_KV_NAME_MAX 64
#endif
/* the KV cache table size, it will improve KV search speed when using cache */
#ifndef FDB_KV_CACHE_TABLE_SIZE
#define FDB_KV_CACHE_TABLE_SIZE 64
#endif
/* the sector cache table size, it will improve KV save speed when using cache */
#ifndef FDB_SECTOR_CACHE_TABLE_SIZE
#define FDB_SECTOR_CACHE_TABLE_SIZE 8
#endif
#if (FDB_KV_CACHE_TABLE_SIZE > 0) && (FDB_SECTOR_CACHE_TABLE_SIZE > 0)
#define FDB_KV_USING_CACHE
#endif
#if defined(FDB_USING_FILE_LIBC_MODE) || defined(FDB_USING_FILE_POSIX_MODE)
#define FDB_USING_FILE_MODE
#endif
/* the file cache table size, it will improve GC speed in file mode when using cache */
#ifndef FDB_FILE_CACHE_TABLE_SIZE
#define FDB_FILE_CACHE_TABLE_SIZE 4
#endif
#ifndef FDB_WRITE_GRAN
#define FDB_WRITE_GRAN 1
#endif
/* log function. default FDB_PRINT macro is printf() */
// #ifndef FDB_PRINT
// #define FDB_PRINT(...) printf(__VA_ARGS__)
// #endif
#ifndef FDB_PRINT
#define FDB_PRINT(...)
#endif
#define FDB_LOG_PREFIX1() FDB_PRINT("[FlashDB]" FDB_LOG_TAG)
#define FDB_LOG_PREFIX2() FDB_PRINT(" ")
#define FDB_LOG_PREFIX() \
FDB_LOG_PREFIX1(); \
FDB_LOG_PREFIX2()
#ifdef FDB_DEBUG_ENABLE
#define FDB_DEBUG(...) \
FDB_LOG_PREFIX(); \
FDB_PRINT("(%s:%d) ", __FILE__, __LINE__); \
FDB_PRINT(__VA_ARGS__)
#else
#define FDB_DEBUG(...)
#endif
/* routine print function. Must be implement by user. */
#define FDB_INFO(...) \
FDB_LOG_PREFIX(); \
FDB_PRINT(__VA_ARGS__)
/* assert for developer. */
#ifdef FDB_USING_NATIVE_ASSERT
#define FDB_ASSERT(EXPR) assert(EXPR);
#else
#ifndef FDB_ASSERT
#define FDB_ASSERT(EXPR) \
if (!(EXPR)) \
{ \
FDB_INFO("(%s) has assert failed at %s.\n", #EXPR, __func__); \
while (1) \
; \
}
#endif /* FDB_ASSERT */
#endif /* FDB_USING_NATIVE_ASSERT */
#define FDB_KVDB_CTRL_SET_SEC_SIZE 0x00 /**< set sector size control command, this change MUST before database initialization */
#define FDB_KVDB_CTRL_GET_SEC_SIZE 0x01 /**< get sector size control command */
#define FDB_KVDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */
#define FDB_KVDB_CTRL_SET_UNLOCK 0x03 /**< set unlock function control command */
#define FDB_KVDB_CTRL_SET_FILE_MODE 0x09 /**< set file mode control command, this change MUST before database initialization */
#define FDB_KVDB_CTRL_SET_MAX_SIZE 0x0A /**< set database max size in file mode control command, this change MUST before database initialization */
#define FDB_KVDB_CTRL_SET_NOT_FORMAT 0x0B /**< set database NOT format mode control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_SET_SEC_SIZE 0x00 /**< set sector size control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_GET_SEC_SIZE 0x01 /**< get sector size control command */
#define FDB_TSDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */
#define FDB_TSDB_CTRL_SET_UNLOCK 0x03 /**< set unlock function control command */
#define FDB_TSDB_CTRL_SET_ROLLOVER 0x04 /**< set rollover control command, this change MUST after database initialization */
#define FDB_TSDB_CTRL_GET_ROLLOVER 0x05 /**< get rollover control command */
#define FDB_TSDB_CTRL_GET_LAST_TIME 0x06 /**< get last save time control command */
#define FDB_TSDB_CTRL_SET_FILE_MODE 0x09 /**< set file mode control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_SET_MAX_SIZE 0x0A /**< set database max size in file mode control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_SET_NOT_FORMAT 0x0B /**< set database NOT formatable mode control command, this change MUST before database initialization */
#ifdef FDB_USING_TIMESTAMP_64BIT
typedef int64_t fdb_time_t;
#else
typedef int32_t fdb_time_t;
#endif /* FDB_USING_TIMESTAMP_64BIT */
typedef fdb_time_t (*fdb_get_time)(void);
struct fdb_default_kv_node
{
char *key;
void *value;
size_t value_len;
};
struct fdb_default_kv
{
struct fdb_default_kv_node *kvs;
size_t num;
};
/* error code */
typedef enum
{
FDB_NO_ERR,
FDB_ERASE_ERR,
FDB_READ_ERR,
FDB_WRITE_ERR,
FDB_PART_NOT_FOUND,
FDB_KV_NAME_ERR,
FDB_KV_NAME_EXIST,
FDB_SAVED_FULL,
FDB_INIT_FAILED,
} fdb_err_t;
enum fdb_kv_status
{
FDB_KV_UNUSED,
FDB_KV_PRE_WRITE,
FDB_KV_WRITE,
FDB_KV_PRE_DELETE,
FDB_KV_DELETED,
FDB_KV_ERR_HDR,
#define FDB_KV_STATUS_NUM 6
};
typedef enum fdb_kv_status fdb_kv_status_t;
enum fdb_tsl_status
{
FDB_TSL_UNUSED,
FDB_TSL_PRE_WRITE,
FDB_TSL_WRITE,
FDB_TSL_USER_STATUS1,
FDB_TSL_DELETED,
FDB_TSL_USER_STATUS2,
#define FDB_TSL_STATUS_NUM 6
};
typedef enum fdb_tsl_status fdb_tsl_status_t;
/* key-value node object */
struct fdb_kv
{
fdb_kv_status_t status; /**< node status, @see fdb_kv_status_t */
bool crc_is_ok; /**< node CRC32 check is OK */
uint8_t name_len; /**< name length */
uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */
uint32_t len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
uint32_t value_len; /**< value length */
char name[FDB_KV_NAME_MAX]; /**< name */
struct
{
uint32_t start; /**< node start address */
uint32_t value; /**< value start address */
} addr;
};
typedef struct fdb_kv *fdb_kv_t;
struct fdb_kv_iterator
{
struct fdb_kv curr_kv; /**< Current KV we get from the iterator */
uint32_t iterated_cnt; /**< How many KVs have we iterated already */
size_t iterated_obj_bytes; /**< Total storage size of KVs we have iterated. */
size_t iterated_value_bytes; /**< Total value size of KVs we have iterated. */
uint32_t sector_addr; /**< Current sector address we're iterating. DO NOT touch it. */
uint32_t traversed_len; /**< Traversed sector total length. */
};
typedef struct fdb_kv_iterator *fdb_kv_iterator_t;
/* time series log node object */
struct fdb_tsl
{
fdb_tsl_status_t status; /**< node status, @see fdb_log_status_t */
fdb_time_t time; /**< node timestamp */
uint32_t log_len; /**< log length, must align by FDB_WRITE_GRAN */
struct
{
uint32_t index; /**< node index address */
uint32_t log; /**< log data address */
} addr;
};
typedef struct fdb_tsl *fdb_tsl_t;
typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void *arg);
typedef enum
{
FDB_DB_TYPE_KV,
FDB_DB_TYPE_TS,
} fdb_db_type;
/* the flash sector store status */
enum fdb_sector_store_status
{
FDB_SECTOR_STORE_UNUSED,
FDB_SECTOR_STORE_EMPTY,
FDB_SECTOR_STORE_USING,
FDB_SECTOR_STORE_FULL,
#define FDB_SECTOR_STORE_STATUS_NUM 4
};
typedef enum fdb_sector_store_status fdb_sector_store_status_t;
/* the flash sector dirty status */
enum fdb_sector_dirty_status
{
FDB_SECTOR_DIRTY_UNUSED,
FDB_SECTOR_DIRTY_FALSE,
FDB_SECTOR_DIRTY_TRUE,
FDB_SECTOR_DIRTY_GC,
#define FDB_SECTOR_DIRTY_STATUS_NUM 4
};
typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t;
/* KVDB section information */
struct kvdb_sec_info
{
bool check_ok; /**< sector header check is OK */
struct
{
fdb_sector_store_status_t store; /**< sector store status @see fdb_sector_store_status_t */
fdb_sector_dirty_status_t dirty; /**< sector dirty status @see sector_dirty_status_t */
} status;
uint32_t addr; /**< sector start address */
uint32_t magic; /**< magic word(`E`, `F`, `4`, `0`) */
uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not combined */
size_t remain; /**< remain size */
uint32_t empty_kv; /**< the next empty KV node start address */
};
typedef struct kvdb_sec_info *kv_sec_info_t;
/* TSDB section information */
struct tsdb_sec_info
{
bool check_ok; /**< sector header check is OK */
fdb_sector_store_status_t status; /**< sector store status @see fdb_sector_store_status_t */
uint32_t addr; /**< sector start address */
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
fdb_time_t start_time; /**< the first start node's timestamp, 0xFFFFFFFF: unused */
fdb_time_t end_time; /**< the last end node's timestamp, 0xFFFFFFFF: unused */
uint32_t end_idx; /**< the last end node's index, 0xFFFFFFFF: unused */
fdb_tsl_status_t end_info_stat[2]; /**< the last end node's info status */
size_t remain; /**< remain size */
uint32_t empty_idx; /**< the next empty node index address */
uint32_t empty_data; /**< the next empty node's data end address */
};
typedef struct tsdb_sec_info *tsdb_sec_info_t;
struct kv_cache_node
{
uint16_t name_crc; /**< KV name's CRC32 low 16bit value */
uint16_t active; /**< KV node access active degree */
uint32_t addr; /**< KV node address */
};
typedef struct kv_cache_node *kv_cache_node_t;
/* database structure */
typedef struct fdb_db *fdb_db_t;
struct fdb_db
{
const char *name; /**< database name */
fdb_db_type type; /**< database type */
union
{
#ifdef FDB_USING_FAL_MODE
const struct fal_partition *part; /**< flash partition for saving database */
#endif
#ifdef FDB_USING_FILE_MODE
const char *dir; /**< directory path for saving database */
#endif
} storage;
uint32_t sec_size; /**< flash section size. It's a multiple of block size */
uint32_t max_size; /**< database max size. It's a multiple of section size */
uint32_t oldest_addr; /**< the oldest sector start address */
bool init_ok; /**< initialized successfully */
bool file_mode; /**< is file mode, default is false */
bool not_formatable; /**< is can NOT be formated mode, default is false */
#ifdef FDB_USING_FILE_MODE
uint32_t cur_file_sec[FDB_FILE_CACHE_TABLE_SIZE]; /**< last operate sector address */
#if defined(FDB_USING_FILE_POSIX_MODE)
int cur_file[FDB_FILE_CACHE_TABLE_SIZE]; /**< current file object */
#elif defined(FDB_USING_FILE_LIBC_MODE)
FILE *cur_file[FDB_FILE_CACHE_TABLE_SIZE]; /**< current file object */
#endif /* FDB_USING_FILE_MODE */
uint32_t cur_sec; /**< current operate sector address */
#endif
void (*lock)(fdb_db_t db); /**< lock the database operate */
void (*unlock)(fdb_db_t db); /**< unlock the database operate */
void *user_data;
};
/* KVDB structure */
struct fdb_kvdb
{
struct fdb_db parent; /**< inherit from fdb_db */
struct fdb_default_kv default_kvs; /**< default KV */
bool gc_request; /**< request a GC check */
bool in_recovery_check; /**< is in recovery check status when first reboot */
struct fdb_kv cur_kv;
struct kvdb_sec_info cur_sector;
bool last_is_complete_del;
#ifdef FDB_KV_USING_CACHE
/* KV cache table */
struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE];
/* sector cache table, it caching the sector info which status is current using */
struct kvdb_sec_info sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE];
#endif /* FDB_KV_USING_CACHE */
#ifdef FDB_KV_AUTO_UPDATE
uint32_t ver_num; /**< setting version number for update */
#endif
void *user_data;
};
typedef struct fdb_kvdb *fdb_kvdb_t;
/* TSDB structure */
struct fdb_tsdb
{
struct fdb_db parent; /**< inherit from fdb_db */
struct tsdb_sec_info cur_sec; /**< current using sector */
fdb_time_t last_time; /**< last TSL timestamp */
fdb_get_time get_time; /**< the current timestamp get function */
size_t max_len; /**< the maximum length of each log */
bool rollover; /**< the oldest data will rollover by newest data, default is true */
void *user_data;
};
typedef struct fdb_tsdb *fdb_tsdb_t;
/* blob structure */
struct fdb_blob
{
void *buf; /**< blob data buffer */
size_t size; /**< blob data buffer size */
struct
{
uint32_t meta_addr; /**< saved KV or TSL index address */
uint32_t addr; /**< blob data saved address */
size_t len; /**< blob data saved length */
} saved;
};
typedef struct fdb_blob *fdb_blob_t;
#ifdef __cplusplus
}
#endif
#endif /* _FDB_DEF_H_ */

View File

@ -0,0 +1,318 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
* Copyright (c) 2020, enkiller, <462747508@qq.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <flashdb.h>
#include <fdb_low_lvl.h>
#define FDB_LOG_TAG "[file]"
#ifdef FDB_USING_FILE_MODE
#define DB_PATH_MAX 256
static void get_db_file_path(fdb_db_t db, uint32_t addr, char *path, size_t size)
{
#define DB_NAME_MAX 8
/* from db_name.fdb.0 to db_name.fdb.n */
char file_name[DB_NAME_MAX + 4 + 10];
uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
int index = sec_addr / db->sec_size;
snprintf(file_name, sizeof(file_name), "%.*s.fdb.%d", DB_NAME_MAX, db->name, index);
if (strlen(db->storage.dir) + 1 + strlen(file_name) >= size) {
/* path is too long */
FDB_INFO("Error: db (%s) file path (%s) is too log.\n", file_name, db->storage.dir);
FDB_ASSERT(0)
}
snprintf(path, size, "%s/%s", db->storage.dir, file_name);
}
#if defined(FDB_USING_FILE_POSIX_MODE)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if !defined(_MSC_VER)
#include <unistd.h>
#endif
static int get_file_from_cache(fdb_db_t db, uint32_t sec_addr)
{
for (int i = 0; i < FDB_FILE_CACHE_TABLE_SIZE; i++) {
if (db->cur_file_sec[i] == sec_addr)
return db->cur_file[i];
}
return -1;
}
static void update_file_cache(fdb_db_t db, uint32_t sec_addr, int fd)
{
int free_index = FDB_FILE_CACHE_TABLE_SIZE;
for (int i = 0; i < FDB_FILE_CACHE_TABLE_SIZE; i++) {
if (db->cur_file_sec[i] == sec_addr) {
db->cur_file[i] = fd;
return;
} else if (db->cur_file[i] == -1) {
free_index = i;
}
}
if (fd > 0) {
if (free_index < FDB_FILE_CACHE_TABLE_SIZE) {
db->cur_file[free_index] = fd;
db->cur_file_sec[free_index] = sec_addr;
} else {
/* cache is full, move to end */
for (int i = FDB_FILE_CACHE_TABLE_SIZE - 1; i > 0; i--) {
close(db->cur_file[i]);
memcpy(&db->cur_file[i], &db->cur_file[i - 1], sizeof(db->cur_file[0]));
memcpy(&db->cur_file_sec[i], &db->cur_file_sec[i - 1], sizeof(db->cur_file_sec[0]));
}
/* add to head */
db->cur_file[0] = fd;
db->cur_file_sec[0] = sec_addr;
}
}
}
static int open_db_file(fdb_db_t db, uint32_t addr, bool clean)
{
uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
int fd = get_file_from_cache(db, sec_addr);
char path[DB_PATH_MAX];
if (fd <= 0 || clean) {
get_db_file_path(db, addr, path, DB_PATH_MAX);
if (fd > 0) {
close(fd);
fd = -1;
update_file_cache(db, sec_addr, fd);
}
if (clean) {
/* clean the old file */
int clean_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0777);
if (clean_fd <= 0) {
FDB_INFO("Error: open (%s) file failed.\n", path);
}
else {
close(clean_fd);
clean_fd = -1;
}
}
if (get_file_from_cache(db, sec_addr) < 0) {
/* open the database file */
fd = open(path, O_RDWR, 0777);
update_file_cache(db, sec_addr, fd);
}
db->cur_sec = sec_addr;
}
return fd;
}
fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
int fd = open_db_file(db, addr, false);
if (fd > 0) {
/* get the offset address is relative to the start of the current file */
addr = addr % db->sec_size;
if ((lseek(fd, addr, SEEK_SET) != (int32_t)addr) || (read(fd, buf, size) != (ssize_t)size))
result = FDB_READ_ERR;
} else {
result = FDB_READ_ERR;
}
return result;
}
fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
{
fdb_err_t result = FDB_NO_ERR;
int fd = open_db_file(db, addr, false);
if (fd > 0) {
/* get the offset address is relative to the start of the current file */
addr = addr % db->sec_size;
if ((lseek(fd, addr, SEEK_SET) != (int32_t)addr) || (write(fd, buf, size) != (ssize_t)size))
result = FDB_WRITE_ERR;
if(sync) {
fsync(fd);
}
} else {
result = FDB_WRITE_ERR;
}
return result;
}
fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
int fd = open_db_file(db, addr, true);
if (fd > 0) {
#define BUF_SIZE 32
uint8_t buf[BUF_SIZE];
size_t i;
lseek(fd, 0, SEEK_SET);
for (i = 0; i * BUF_SIZE < size; i++)
{
memset(buf, 0xFF, BUF_SIZE);
write(fd, buf, BUF_SIZE);
}
memset(buf, 0xFF, BUF_SIZE);
write(fd, buf, size - i * BUF_SIZE);
fsync(fd);
} else {
result = FDB_ERASE_ERR;
}
return result;
}
#elif defined(FDB_USING_FILE_LIBC_MODE)
static FILE *get_file_from_cache(fdb_db_t db, uint32_t sec_addr)
{
for (int i = 0; i < FDB_FILE_CACHE_TABLE_SIZE; i++) {
if (db->cur_file_sec[i] == sec_addr)
return db->cur_file[i];
}
return NULL;
}
static void update_file_cache(fdb_db_t db, uint32_t sec_addr, FILE *fd)
{
int free_index = FDB_FILE_CACHE_TABLE_SIZE;
for (int i = 0; i < FDB_FILE_CACHE_TABLE_SIZE; i++) {
if (db->cur_file_sec[i] == sec_addr) {
db->cur_file[i] = fd;
return;
}
else if (db->cur_file[i] == 0) {
free_index = i;
}
}
if (fd) {
if (free_index < FDB_FILE_CACHE_TABLE_SIZE) {
db->cur_file[free_index] = fd;
db->cur_file_sec[free_index] = sec_addr;
}
else {
/* cache is full, move to end */
for (int i = FDB_FILE_CACHE_TABLE_SIZE - 1; i > 0; i--) {
fclose(db->cur_file[i]);
memcpy(&db->cur_file[i], &db->cur_file[i - 1], sizeof(db->cur_file[0]));
memcpy(&db->cur_file_sec[i], &db->cur_file_sec[i - 1], sizeof(db->cur_file_sec[0]));
}
/* add to head */
db->cur_file[0] = fd;
db->cur_file_sec[0] = sec_addr;
}
}
}
static FILE *open_db_file(fdb_db_t db, uint32_t addr, bool clean)
{
uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
FILE *fd = get_file_from_cache(db, sec_addr);
char path[DB_PATH_MAX];
if (fd == NULL || clean) {
get_db_file_path(db, addr, path, DB_PATH_MAX);
if (fd) {
fclose(fd);
fd = NULL;
update_file_cache(db, sec_addr, fd);
}
if (clean) {
/* clean the old file */
FILE *clean_fd = fopen(path, "wb+");
if (clean_fd == NULL) {
FDB_INFO("Error: open (%s) file failed.\n", path);
} else {
fclose(clean_fd);
clean_fd = NULL;
}
}
if (get_file_from_cache(db, sec_addr) == NULL) {
/* open the database file */
fd = fopen(path, "rb+");
update_file_cache(db, sec_addr, fd);
}
db->cur_sec = sec_addr;
}
return fd;
}
fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
FILE *fp = open_db_file(db, addr, false);
if (fp) {
addr = addr % db->sec_size;
if ((fseek(fp, addr, SEEK_SET) != 0) || (fread(buf, size, 1, fp) != size))
result = FDB_READ_ERR;
} else {
result = FDB_READ_ERR;
}
return result;
}
fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
{
fdb_err_t result = FDB_NO_ERR;
FILE *fp = open_db_file(db, addr, false);
if (fp) {
addr = addr % db->sec_size;
if ((fseek(fp, addr, SEEK_SET) != 0) || (fwrite(buf, size, 1, fp) != size))
result = FDB_READ_ERR;
if(sync) {
fflush(fp);
}
} else {
result = FDB_READ_ERR;
}
return result;
}
fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
FILE *fp = open_db_file(db, addr, true);
if (fp != NULL) {
#define BUF_SIZE 32
uint8_t buf[BUF_SIZE];
size_t i;
fseek(fp, 0, SEEK_SET);
for (i = 0; i * BUF_SIZE < size; i++)
{
memset(buf, 0xFF, BUF_SIZE);
fwrite(buf, BUF_SIZE, 1, fp);
}
memset(buf, 0xFF, BUF_SIZE);
fwrite(buf, size - i * BUF_SIZE, 1, fp);
fflush(fp);
} else {
result = FDB_ERASE_ERR;
}
return result;
}
#endif /* defined(FDB_USING_FILE_LIBC_MODE) */
#endif /* FDB_USING_FILE_MODE */

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More