first commit

This commit is contained in:
许晟昊 2024-12-24 13:06:41 +08:00
commit 5401ebeb61
124 changed files with 28785 additions and 0 deletions

468
bsp/adcs.c Normal file
View File

@ -0,0 +1,468 @@
/**
* @file adcs.c
* @author xxx
* @date 2023-09-04 15:59:16
* @brief LL库ADC驱动
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "adcs.h"
#include "dma.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_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_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);
uint32_t backup_setting_adc_dma_transfer = 0U;
backup_setting_adc_dma_transfer = LL_ADC_REG_GetDMATransfer(p->adc);
LL_ADC_REG_SetDMATransfer(p->adc, LL_ADC_REG_DMA_TRANSFER_NONE);
// ADC开始校准
LL_ADC_StartCalibration(p->adc, LL_ADC_SINGLE_ENDED);
// 等待校准完成
while (LL_ADC_IsCalibrationOnGoing(p->adc))
;
LL_ADC_REG_SetDMATransfer(p->adc, backup_setting_adc_dma_transfer);
LL_mDelay(10);
LL_ADC_EnableIT_OVR(p->adc);
LL_ADC_Enable(p->adc);
LL_mDelay(10);
if (BIT_IS_SET(channels, INVREF))
{
// 使能VREFINT
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(p->adc), LL_ADC_PATH_INTERNAL_VREFINT);
}
LL_DMA_SetDataLength(p->dma, p->dma_channel, p->adc_sum);
LL_DMA_SetPeriphAddress(p->dma, p->dma_channel, LL_ADC_DMA_GetRegAddr(p->adc, LL_ADC_DMA_REG_REGULAR_DATA));
LL_DMA_SetMemoryAddress(p->dma, p->dma_channel, (uint32_t)p->adc_value);
LL_DMA_EnableChannel(p->dma, p->dma_channel);
if (backup_setting_adc_dma_transfer == LL_ADC_REG_DMA_TRANSFER_UNLIMITED)
{
LL_ADC_REG_StartConversion(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];
LL_DMA_SetDataLength(p->dma, p->dma_channel, p->adc_sum);
LL_DMA_SetPeriphAddress(p->dma, p->dma_channel, LL_ADC_DMA_GetRegAddr(p->adc, LL_ADC_DMA_REG_REGULAR_DATA));
LL_DMA_SetMemoryAddress(p->dma, p->dma_channel, (uint32_t)p->adc_value);
LL_DMA_EnableChannel(p->dma, p->dma_channel);
LL_ADC_ClearFlag_OVR(p->adc);
LL_ADC_ClearFlag_EOC(p->adc);
LL_ADC_REG_StartConversion(p->adc); // 开始转换
// ADC开始校准
LL_ADC_StartCalibration(p->adc, LL_ADC_SINGLE_ENDED);
}
/**
* @brief
*
* ADCS ADCS
*
* @param num ADCS
*/
void stop_sample(adcs_e num)
{
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
adcs_t *p = &adcs[num];
LL_ADC_REG_StopConversion(p->adc);
while (LL_ADC_REG_IsConversionOngoing(p->adc) != 0)
;
LL_DMA_DisableChannel(p->dma, p->dma_channel);
DMA_CLEAR_FLAG_TC_CHANNEL(p->dma, p->dma_channel);
DMA_CLEAR_FLAG_TE_CHANNEL(p->dma, p->dma_channel);
}
/**
* @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_REG_StopConversion(p->adc);
LL_DMA_DisableChannel(p->dma, p->dma_channel);
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
p->median_average_ticks.uticks = sys_get_tick();
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));
p->median_average_ticks.ticks_current = sys_get_tick() - p->median_average_ticks.uticks;
if (p->median_average_ticks.ticks_current > p->median_average_ticks.ticks_max)
{
p->median_average_ticks.ticks_max = p->median_average_ticks.ticks_current;
}
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);
p->median_ticks.uticks = sys_get_tick();
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];
p->median_ticks.ticks_current = sys_get_tick() - p->median_ticks.uticks;
if (p->median_ticks.ticks_current > p->median_ticks.ticks_max)
{
p->median_ticks.ticks_max = p->median_ticks.ticks_current;
}
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);
p->average_ticks.uticks = sys_get_tick();
// 减少重复计算,计算基础偏移量
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;
p->average_ticks.ticks_current = sys_get_tick() - p->average_ticks.uticks;
if (p->average_ticks.ticks_current > p->average_ticks.ticks_max)
{
p->average_ticks.ticks_max = p->average_ticks.ticks_current;
}
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);
// 停止ADC转换
LL_ADC_REG_StopConversion(p->adc);
// 关闭ADC,可以不关闭但是校准无法清除
// LL_ADC_Disable(p->adc);
}
// 检查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_REG_StopConversion(p->adc);
LL_DMA_DisableChannel(p->dma, p->dma_channel);
LL_DMA_SetDataLength(p->dma, p->dma_channel, p->adc_sum);
LL_DMA_SetPeriphAddress(p->dma, p->dma_channel, LL_ADC_DMA_GetRegAddr(p->adc, LL_ADC_DMA_REG_REGULAR_DATA));
LL_DMA_SetMemoryAddress(p->dma, p->dma_channel, (uint32_t)p->adc_value);
LL_DMA_EnableChannel(p->dma, p->dma_channel);
LL_ADC_ClearFlag_OVR(p->adc);
LL_ADC_ClearFlag_EOC(p->adc);
LL_ADC_REG_StartConversion(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;
}

233
bsp/adcs.h Normal file
View File

@ -0,0 +1,233 @@
/**
* @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 "lib.h"
#include "main.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_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
utime_ticks_t median_average_ticks; ///< Median average filtering ticks
utime_ticks_t median_ticks; ///< Median filtering ticks
utime_ticks_t average_ticks; ///< Average filtering ticks
} 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_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__

91
bsp/bsp.c Normal file
View File

@ -0,0 +1,91 @@
#include "main.h"
#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);
}

31
bsp/bsp.h Normal file
View File

@ -0,0 +1,31 @@
/**
* @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 "adcs.h"
// #include "dacs.h"
#include "dmas.h"
#include "tims.h"
#include "pwms.h"
#include "uarts.h"
#include "spis.h"
#include "i2cs.h"
// #include "eeprom.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
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
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
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
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
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
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
bsp/flash.h Normal file

File diff suppressed because it is too large Load Diff

76
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 {uint32_t} pin -
* @return {gpio_t *} - GPIO对象指针
* @note: GPIO对象GPIO功能
*/
gpio_t *gpio_create(GPIO_TypeDef *port, uint32_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);
}
}

145
bsp/gpios.h Normal file
View File

@ -0,0 +1,145 @@
/**
* @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) (LL_GPIO_SetOutputPin(port, pin))
/**
* @brief Set the GPIO pin to low.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_RESET(port, pin) (LL_GPIO_ResetOutputPin(port, pin))
/**
* @brief Toggle the state of the GPIO pin.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_TOGGLE(port, pin) (LL_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) (LL_GPIO_IsInputPinSet(port, pin))
/**
* @brief Set the GPIO pin as input.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET_INPUT(port, pin) (LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_INPUT))
/**
* @brief Set the GPIO pin as output.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET_OUTPUT(port, pin) \
do \
{ \
LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_OUTPUT); \
} 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) (LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_ALTERNATE))
/**
* @brief Set the GPIO pin as analog.
*
* @param port The GPIO port.
* @param pin The GPIO pin.
*/
#define GPIO_SET_ANALOG(port, pin) \
do \
{ \
LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_ANALOG); \
} while (0)
/**
* @brief Structure representing a GPIO pin.
*/
typedef struct GPIO
{
GPIO_TypeDef *port; ///< The GPIO port.
uint32_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, uint32_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
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
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 GPIO gpio_t;
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
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
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

1
bsp/pwms.c Normal file
View File

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

121
bsp/pwms.h Normal file
View File

@ -0,0 +1,121 @@
/**
* @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)
#define PWM_GET_ARR(TIMx) LL_TIM_GetAutoReload(TIMx)
#define PWM_SET_ARR(TIMx, ARR) LL_TIM_SetAutoReload(TIMx, ARR - 1)
#define PWM_GET_PSC(TIMx) LL_TIM_GetPrescaler(TIMx)
#define PWM_SET_PSC(TIMx, PSC) LL_TIM_SetPrescaler(TIMx, PSC - 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);
}
/**
* @brief PWM比较值
*
* PWM比较值
*
* @param TIMx TIM_TypeDef结构体
* @param CHx LL_TIM_CHANNEL_CH1到LL_TIM_CHANNEL_CH4
*
* @return uint16_t类型的值PWM比较值0
*/
static inline uint16_t PWM_GET_COMPARE(TIM_TypeDef *TIMx, uint32_t CHx)
{
switch (CHx)
{
case LL_TIM_CHANNEL_CH1:
return LL_TIM_OC_GetCompareCH1(TIMx);
case LL_TIM_CHANNEL_CH2:
return LL_TIM_OC_GetCompareCH2(TIMx);
case LL_TIM_CHANNEL_CH3:
return LL_TIM_OC_GetCompareCH3(TIMx);
case LL_TIM_CHANNEL_CH4:
return LL_TIM_OC_GetCompareCH4(TIMx);
default:
return 0;
}
}
#endif ///< __PWMS_H__

1
bsp/readme.md Normal file
View File

@ -0,0 +1 @@

819
bsp/spis.c Normal file
View File

@ -0,0 +1,819 @@
#include "spis.h"
#include "delay.h"
#define SPI_TIMEOUT 0xffffffff
#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
{
if (spi_wait_flag(handle, LL_SPI_SR_TXE, SPI_TIMEOUT) == FALSE)
{
return 0xff;
}
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 if (handle->cfg.address_bytes == 3)
{
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;
}
}
else
{
DBG_ASSERT(FALSE __DBG_LINE);
}
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);
}

166
bsp/spis.h Normal file
View File

@ -0,0 +1,166 @@
/**
* @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 GPIO gpio_t;
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
bsp/tims.c Normal file
View File

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

116
bsp/tims.h Normal file
View File

@ -0,0 +1,116 @@
/**
* @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.
*/
/**
* @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
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
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__

164
inc/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 30 / TICKS_INTERVAL // 按键去抖动时间单位ms
#define SHORT_TICKS (30 / 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

21
inc/delay.h Normal file
View File

@ -0,0 +1,21 @@
/***
* @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"
void delay_init(uint16_t sysclk); /* 初始化延迟函数 */
void delay_ms(uint16_t nms); /* 延时nms */
void delay_us(uint32_t nus); /* 延时nus */
void delay_tick(uint32_t ticks); /* 延时ticks */
#endif

67
inc/sys.h Normal file
View File

@ -0,0 +1,67 @@
/***
* @Author:
* @Date: 2023-04-11 18:46:58
* @LastEditors: xxx
* @LastEditTime: 2023-04-11 22:16:47
* @Description:
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef _SYS_H
#define _SYS_H
#include "main.h"
#include "lib.h"
#define CLOCK_CHANGE_ENABLE FALSE ///< 时钟切换使能
#define LOCK() __disable_irq() ///< 系统关全局中断
#define UNLOCK() __enable_irq() ///< 系统开全局中断
typedef struct
{
uint32_t pll_source;
uint32_t pll_m;
uint32_t pll_n;
uint32_t pll_r;
uint32_t ahb_div;
uint32_t apb1_div;
uint32_t apb2_div;
uint32_t sysclk;
// private:
uint32_t sysclk_change; // 改变后的系统时钟 这个参数由change_system_clock函数设置作为内部观察使用
} clock_config_t; /* 时钟配置结构体 */
typedef struct
{
uint32_t ticks_max; // 最大ticks
uint32_t ticks_current; // 当前使用的ticks
uint32_t uticks; // 使用的ticks,用于计算
} utime_ticks_t;
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset); /* 设置中断偏移量 */
void sys_standby(void); /* 进入待机模式 */
void sys_soft_reset(void); /* 系统软复位 */
void LL_IncTick(void); /* 系统滴答时钟 */
void sys_millis_reset(void); /* 系统计时器重新开始 */
uint32_t sys_get_tick(void); /* 获取系统滴答时钟 */
uint32_t sys_millis(void); /* 获取系统时间 */
uint32_t sys_to_seconds(uint32_t start_time); /* 将系统时间转换为秒 */
// 以下为汇编函数
void sys_wfi_set(void); /* 执行WFI指令 */
void sys_intx_disable(void); /* 关闭所有中断 */
void sys_intx_enable(void); /* 开启所有中断 */
void sys_msr_msp(uint32_t addr); /* 设置栈顶地址 */
void scheduler_time_start(void);
uint32_t scheduler_time_stop(void);
uint32_t scheduler_time_occupancy_get(uint32_t run_time);
void system_clock_read(void);
void restore_system_clock(void);
clock_config_t *get_original_clock_config(void);
void change_system_clock(clock_config_t *new_config);
#endif

229
lib/bootload/bootload.c Normal file
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();
}

102
lib/bootload/bootload.h Normal file
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

50
lib/bootload/readme.md Normal file
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();
}

633
lib/bootload/ymodem.c Normal file
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;
}

180
lib/bootload/ymodem.h Normal file
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

235
lib/control/inc/pid.h Normal file
View File

@ -0,0 +1,235 @@
#ifndef __PID_H__
#define __PID_H__
#include "lib.h"
#include "pid_auto_tune.h"
#define INCOMPLETE_DIFFEREN 0 // 不完全微分
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;
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_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__

30
lib/control/inc/s_curve.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef __S_CURVE_H
#define __S_CURVE_H
#include "lib.h"
typedef struct
{
// 起始频率
float32 f0;
// 加加速段斜率
float32 faa;
// 减减速段斜率
float32 frr;
// 加加速段时间
float32 taa;
// 匀加速段时间
float32 tua;
// 减加速段时间
float32 tra;
// 匀速段时间
float32 tuu;
// 加减速段时间
float32 tar;
// 匀减速段时间
float32 tur;
// 减减速段时间
float32 trr;
} s_curve_t;
void s_curve_table_gen(uint16_t tmin, uint16_t tmax);
#endif // __S_CURVE_H

27
lib/control/src/pid.c Normal file
View File

@ -0,0 +1,27 @@
#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;
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

894
lib/control/src/pid_fuzzy.c Normal file
View File

@ -0,0 +1,894 @@
#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 (100)
#define MINE (-MAXE)
// 定义EC的范围因为变化非常缓慢每次的EC都非常小这里可以根据实际需求来调整
#define MAXEC (100)
#define MINEC (-MAXEC)
// 定义e,ec的量化因子
#define KE 3 / MAXE
#define KEC 3 / MAXEC
// 定义输出量比例因子
#define KUP 1.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;
}
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;
}
}
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;
}

171
lib/control/src/s_curve.c Normal file
View File

@ -0,0 +1,171 @@
#include "s_curve.h"
// s曲线加速度各段参数定义
// 起始速度
#define F0 0.0f
// 加加速度与减减速度
#define FAA 0.0f
#define FRR 0.0f
// 加速段三个时间
#define TAA 0.1f
#define TUA 0.2f
#define TRA 0.1f
// 匀速段
#define TUU 5.0f
// 减速段
#define TAR 0.1f
#define TUR 0.2f
#define TRR 0.1f
// 表格长度
#define S_CURVE_TABLE_LEN 400
int16_t s_curve_table[S_CURVE_TABLE_LEN] = {0};
static int16_t s_curve_func(s_curve_t *s, float32 t, float32 *freq, float32 *acc)
{
// 辅助常数项
float32 A, B, C, D, E, F;
// 表达式中间值
float32 f3, f4, f5;
// 加速段,匀速段,减速段时间
float32 Ta, Tu, Tr;
// 减加速与加减速段斜率
float32 fra, far;
// 起始频率与加加速频率,减减速频率
float32 f0, faa, frr;
float32 taa, tua, tra, tuu, tar, tur, trr;
// 获取参数
faa = s->faa;
frr = s->frr;
taa = s->taa;
tua = s->tua;
tra = s->tra;
tuu = s->tuu;
tar = s->tar;
tur = s->tur;
trr = s->trr;
f0 = s->f0;
fra = faa * taa / tra;
far = frr * trr / tar;
Ta = taa + tua + tra;
Tu = tuu;
Tr = tar + tur + trr;
A = f0;
B = f0 - 0.5 * faa * taa * taa;
C = f0 + 0.5 * faa * taa * taa + faa * taa * tua + 0.5 * fra * (taa + tua) * (taa + tua) - fra * Ta * (taa + tua);
// f1 = f0 + 0.5 * faa * taa * taa;
// f2 = f0 + 0.5 * faa * taa * taa + faa * taa * tua;
f3 = 0.5 * fra * Ta * Ta + C;
D = f3 - 0.5 * far * (Ta + Tu) * (Ta + Tu);
f4 = -far * 0.5 * (Ta + Tu + tar) * (Ta + Tu + tar) + far * (Ta + Tu) * (Ta + Tu + tar) + D;
E = f4 + far * tar * (Ta + Tu + tar);
f5 = -far * tar * (Ta + Tu + Tr - trr) + E;
F = f5 + frr * (Ta + Tu + Tr) * (Ta + Tu + Tr - trr) - 0.5 * frr * (Ta + Tu + Tr - trr) * (Ta + Tu + Tr - trr);
// 如果时间点在全行程规定的时间段内
if ((t >= 0) && (t <= Ta + Tu + Tr))
{
// 加加速段
if ((t >= 0) && (t <= taa))
{
*freq = 0.5 * faa * t * t + A;
*acc = faa * t;
}
// 匀加速段
else if ((t >= taa) && (t <= taa + tua))
{
*freq = faa * taa * t + B;
*acc = faa * taa;
}
// 加减速段
else if ((t >= taa + tua) && (t <= taa + tua + tra))
{
*freq = -0.5 * fra * t * t + fra * Ta * t + C;
*acc = -fra * t + fra * Ta;
}
// 匀速段
else if ((t >= Ta) && (t <= Ta + tuu))
{
*freq = f3;
*acc = 0;
}
// 加减速段
else if ((t >= Ta + Tu) && (t <= Ta + Tu + tar))
{
*freq = -0.5 * far * t * t + far * (Ta + Tu) * t + D;
*acc = -far * t + far * (Ta + Tu);
}
// 匀减速
else if ((t >= Ta + Tu + tar) && (t <= Ta + Tu + tar + tur))
{
*freq = -far * tar * t + E;
*acc = -far * tar;
}
// 减减速
else if ((t >= Ta + Tu + Tr - trr) && (t <= Ta + Tu + Tr))
{
*freq = 0.5 * frr * t * t - frr * (Ta + Tu + Tr) * t + F;
*acc = frr * t - frr * (Ta + Tu + Tr);
}
}
else
{
return -1;
}
return 0;
}
// S型曲线初始化
void s_curve_table_gen(uint16_t tmin, uint16_t tmax)
{
uint16_t i, tint;
float32 ti;
float32 freq;
float32 acc;
float32 fi;
s_curve_t s;
osel_memset((uint8_t *)&s, 0, sizeof(s_curve_t));
s.f0 = F0;
s.taa = TAA;
s.tua = TUA;
s.tra = TRA;
s.tuu = TUU;
s.tar = TAR;
s.tur = TUR;
s.trr = TRR;
// 根据约束条件求出加加速段与减减速段斜率
s.faa = 2.0 / (s.taa * (s.taa + s.tra + 2 * s.tua));
s.frr = 2.0 / (s.trr * (s.tar + s.trr + 2 * s.tur));
for (i = 0; i < S_CURVE_TABLE_LEN; i++)
{
// 求出每个时间点对应的频率以及加速度
fi = i * (TAA + TUA + TRA + TUU + TAR + TUR + TRR) / S_CURVE_TABLE_LEN;
s_curve_func(&s, fi, &freq, &acc);
// 根据最大与最小装载值确定定时器实际值
ti = tmax - (tmax - tmin) * freq;
// 转换为整数值
tint = (uint16_t)ti;
// 存入s曲线表
s_curve_table[i] = tint;
}
}

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控制器。

1
lib/control/自整定.md Normal file
View File

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

161
lib/driver/dac161p997.c Normal file
View File

@ -0,0 +1,161 @@
#include "dac161p997.h"
#include "delay.h"
static dac161p997_t _handle;
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)
{
uint8_t adc = (uint16_t)(current * DAC161P997_CURRENT_SLOPE) >> 8;
static uint8_t last_adc;
if (adc == last_adc)
{
last_adc = adc;
return;
}
dac161p997_swif_write_reg(DAC161P997_LCK_REG_UNLOCK, CONFIG_WRITE);
dac161p997_swif_write_reg(DAC161P997_ERR_LOW_REG + adc, CONFIG_WRITE);
dac161p997_swif_write_reg(DAC161P997_LCK_REG_LOCK, CONFIG_WRITE);
}
/**
* @brief DAC161设备
*
* DAC161设备便
*
*
* 1. DAC161的配置寄存器
* 2. DAC161的错误下限寄存器
* 3. DAC161的配置寄存器
*/
void dac161p997_init()
{
_handle.io = gpio_create(DAC161P997_IO_PORT, DAC161P997_IO_PIN);
dac161p997_swif_write_reg(DAC161P997_LCK_REG_UNLOCK, CONFIG_WRITE);
dac161p997_swif_write_reg(DAC161P997_CONFIG2_REG +
DAC161P997_CONFIG2_REG_LOOP +
DAC161P997_CONFIG2_REG_CHANNEL +
DAC161P997_CONFIG2_REG_PARITY +
DAC161P997_CONFIG2_REG_FRAME,
CONFIG_WRITE);
dac161p997_swif_write_reg(DAC161P997_LCK_REG_LOCK, CONFIG_WRITE);
}

81
lib/driver/dac161p997.h Normal file
View File

@ -0,0 +1,81 @@
#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)
// cycles
// #define QUARTER_CYCLES (625)
// #define HALF_CYCLES (1250)
// #define THREE_CYCLES (1875)
// #define FULL_CYCLES (2500)
typedef struct
{
gpio_t *io;
} 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(void);
#endif

219
lib/driver/eeprom_fm24.c Normal file
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;
}

158
lib/driver/eeprom_fm24.h Normal file
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__

152
lib/driver/eeprom_lc02b.c Normal file
View File

@ -0,0 +1,152 @@
#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;
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);
LL_mDelay(10);
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(void)
{
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);
LL_mDelay(10); // 延时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);
}

56
lib/driver/eeprom_lc02b.h Normal file
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(void);
/**
* @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

331
lib/driver/eeprom_m95.c Normal file
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;
}

166
lib/driver/eeprom_m95.h Normal file
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

107
lib/driver/ntc_3950.c Normal file
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;
}

15
lib/driver/ntc_3950.h Normal file
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__

541
lib/driver/rtc_rx8010.c Normal file
View File

@ -0,0 +1,541 @@
/**
* @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;
/* 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)
{
LL_mDelay(2);
}
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(void)
{
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;
}
}

99
lib/driver/rtc_rx8010.h Normal file
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(void);
/**
* @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__

160
lib/driver/sht40.c Normal file
View File

@ -0,0 +1,160 @@
#include "sht40.h"
#include "i2cs.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 uint8_t crc8(uint8_t *data, uint8_t len);
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(void)
{
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);
LL_mDelay(10); // 根据 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;
}

9
lib/driver/sht40.h Normal file
View File

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

1011
lib/driver/ssd1306_oled.c Normal file

File diff suppressed because it is too large Load Diff

80
lib/driver/ssd1306_oled.h Normal file
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

219
lib/driver/tmc2240.c Normal file
View File

@ -0,0 +1,219 @@
#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.data = 0x00071f1f;
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_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);
if (enable == TRUE)
{
tmc->en->reset(*tmc->en);
PWM_START(tmc->timer, tmc->time_ch);
}
else
{
tmc->en->set(*tmc->en);
PWM_STOP(tmc->timer, tmc->time_ch);
}
}
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];
tmc->timer = timer;
tmc->time_ch = time_ch;
tmc->spi = spi_create(SPI_TYPE_NORMAL, *gpios, 0);
DBG_ASSERT(tmc->spi != NULL __DBG_LINE);
osel_memset((uint8_t *)&tmc->config, 0, sizeof(tmc2240_config_t));
osel_memset((uint8_t *)&tmc->read_config, 0, sizeof(tmc2240_config_t));
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_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_process(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);
_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);
}

356
lib/driver/tmc2240.h Normal file
View File

@ -0,0 +1,356 @@
/**
* @file tmc2240.h
* @author xushenghao
* @brief TMC2240驱动头文件
* @version 0.1
* @note
* 1. VM需要供电SPI无法正常通信
*/
#ifndef __TMC2240_H
#define __TMC2240_H
#include "main.h"
#include "spis.h"
#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
{
uint16_t tmc2240_adc_temp;
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_process(tmc2240_index_e index);
void tmc2240_config_read(tmc2240_index_e index);
#endif // __TMC2240_H

86
lib/examples/Makefile Normal file
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)

40
lib/examples/simple_aes.c Normal file
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
}

26
lib/examples/simple_cmd.c Normal file
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;
}

18
lib/flow/.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "windows-gcc-x64",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "C:/TDM-GCC-64/bin/gcc.exe",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "windows-gcc-x64",
"compilerArgs": [
""
]
}
],
"version": 4
}

24
lib/flow/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": true,
"cwd": "e:/work/stm32/epm/User/lib/flow",
"program": "e:/work/stm32/epm/User/lib/flow/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

58
lib/flow/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,58 @@
{
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",
"C_Cpp_Runner.debuggerPath": "gdb",
"C_Cpp_Runner.cStandard": "",
"C_Cpp_Runner.cppStandard": "",
"C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat",
"C_Cpp_Runner.useMsvc": false,
"C_Cpp_Runner.warnings": [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wshadow",
"-Wformat=2",
"-Wcast-align",
"-Wconversion",
"-Wsign-conversion",
"-Wnull-dereference"
],
"C_Cpp_Runner.msvcWarnings": [
"/W4",
"/permissive-",
"/w14242",
"/w14287",
"/w14296",
"/w14311",
"/w14826",
"/w44062",
"/w44242",
"/w14905",
"/w14906",
"/w14263",
"/w44265",
"/w14928"
],
"C_Cpp_Runner.enableWarnings": true,
"C_Cpp_Runner.warningsAsError": false,
"C_Cpp_Runner.compilerArgs": [],
"C_Cpp_Runner.linkerArgs": [],
"C_Cpp_Runner.includePaths": [],
"C_Cpp_Runner.includeSearch": [
"*",
"**/*"
],
"C_Cpp_Runner.excludeSearch": [
"**/build",
"**/build/**",
"**/.*",
"**/.*/**",
"**/.vscode",
"**/.vscode/**"
],
"C_Cpp_Runner.useAddressSanitizer": false,
"C_Cpp_Runner.useUndefinedSanitizer": false,
"C_Cpp_Runner.useLeakSanitizer": false,
"C_Cpp_Runner.showCompilationTime": false,
"C_Cpp_Runner.useLinkTimeOptimization": false
}

244
lib/flow/README.md Normal file
View File

@ -0,0 +1,244 @@
# flow_lib
#### 介绍
适用于嵌入式单片机的裸机程序微库只占用你的rom 6个字节是的6个字节。颠覆式的设计思维让你写代码的时候像flow(流水)一样丝滑让你永远不用在为delay时cpu空转而烦恼附加的超轻便的软件定时器让你轻松实现各种定时需求另还有信号量的配方让你任务间的同步像诗一样写意并且能让你裸机程序效率提升百倍以上。
#### 移植说明
移植特别简单flow_def.h有一个全局变量
```
extern unsigned long flow_tick;
```
把这个变量放在你的某个硬件中断里去这个硬件中断一定要是一直运行的推荐RTC半秒中断或者systick中断都可以。
然后在flow.h里的第一行有个宏
```
#define FL_HARD_TICK (500) /* 系统硬件中断一次所需要的时间单位ms */
```
把这里的值改成你的硬件中断一次所需的时间单位是毫秒比如你的flow_tick放在了一个500ms中断一次的rtc里那么这里的宏FL_HARD_TICK的值就是500具体中断设为多少取决于你的系统最短一次的延时的时间。
假如我的最短延时需求是100ms那么我就得给个100ms中断一次的硬件中断源宏FL_HARD_TICK的值就是100我就可以这样使用
```
FL_LOCK_DELAY(fl, FL_CLOCK_SEC /10);
```
来延时100ms。
#### 使用说明
核心文件时flow.h看这里的注释基本就会使用大部分功能。
```
#ifndef __FLOW_
#define __FLOW_
#include <flow_def.h>
#include <flow_core.h>
#include <flow_sem.h>
#define FL_HARD_TICK (500) /* 系统硬件中断一次所需要的时间单位ms */
#define FL_CLOCK_SEC (1000/FL_HARD_TICK) /* 一秒钟需要的tick可以除也可以自行添加其它宏 */
/**
* 初始化一个flow进程
*/
#define FL_INIT(fl) FLOW_INIT(fl)
/**
* flow头必须放在函数内的最前面
*/
#define FL_HEAD(fl) FLOW_HEAD(fl)
/**
* flow尾必须放在函数内的最后面
*/
#define FL_TAIL(fl) FLOW_TAIL(fl)
/**
* 给进程加锁直到judge为真加锁期间一直放开cpu给其他进程使用
*/
#define FL_LOCK_WAIT(fl, judge) FLOW_LOCK_WAIT(fl, judge)
/**
* 如果judge为真就一直给进程加锁加锁期间一直放开cpu给其他进程使用
*/
#define FL_LOCK_WHILE(fl, judge) FLOW_LOCK_WHILE(fl, judge)
/**
* 退出该进程
*/
#define FL_EXIT(fl) FLOW_EXIT(fl)
/**
* 无条件锁住进程一次,下次进来再接着往下运行
*/
#define FL_LOCK_ONCE(fl) FLOW_LOCK_ONCE(fl)
/**
* 等待一个flow进程结束
*/
#define FL_WAIT_PROCESS_END(fl, process) FLOW_WAIT_PROCESS_END(fl, process)
/**
* 等待一个flow子进程结束
*/
#define FL_WAIT_CHILD(fl, cfl, process) FLOW_WAIT_CHILD_PROCESS_END(fl, cfl, process)
/**
* 给进程加锁时长为time加锁期间一直放开cpu给其他进程使用time如果用FL_CLOCK_SEC来乘那么time的单位就是s
* 此处time必须是常数
*/
#define FL_LOCK_DELAY(fl,time) FLOW_LOCK_DELAY(fl,time)
/**
* 给进程加锁时长为time延时期间如果judge为真就直接解锁进程
* 此处time必须是常数
*/
#define FL_LOCK_DELAY_OR_WAIT(fl,judge,time) FLOW_LOCK_DELAY_OR_WAIT(fl,judge,time)
/**
* 初始化一个信号量
*/
#define FL_SEM_INIT(sem, count) FLOW_SEM_INIT(sem, count)
/**
* 给进程加锁,直到有信号释放
*/
#define FL_LOCK_WAIT_SEM(f, sem) FLOW_LOCK_WAIT_SEM(f, sem)
/**
* 给进程加锁直到有信号或者超时此处time可以为变量其他的接口处time必须是常数
*/
#define FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, sem, time) FLOW_LOCK_WAIT_SEM_OR_TIMEOUT(fl, sem, time)
/**
* 释放一个信号量
*/
#define FL_SEM_RELEASE(sem) FLOW_SEM_RELEASE(sem)
/**
* 初始化一个软件定时器
*/
void fl_timer_set(struct flow_timer *t, unsigned long interval);
/**
* 复位一个软件定时器
*/
void fl_timer_reset(struct flow_timer *t);
/**
* 重启一个软件定时器
*/
void fl_timer_restart(struct flow_timer *t);
/**
* 检测一个软件定时器是否超时0为不超时1为超时
*/
char fl_timer_timeout(struct flow_timer *t);
/**
* 检测一个软件定时器还剩多少时间超时单位为硬件tick比如硬件tick 500ms中断一次那么
* 返回的时间单位就是500ms
*/
unsigned long fl_hour_much_time(struct flow_timer *t);
#endif /* __FLOW_ */
```
简单举个例子先从需求说起假如说你现在需要一个函数这个函数的功能是每隔1s让你的led亮一次正常设计的要么起个软件定时器或者硬件定时器甚至状态机可以实现需求但是都太low了让我们看一下如何用flow库来实现这个函数。
该函数格式如下:
```
char led_flash(struct flow *fl)
{}
```
其中char、struct flow *fl是必备的。
再来看看函数里面的内容格式:
```
char led_flash(struct flow *fl)
{
FL_HEAD(fl);
FL_TAIL(fl);
}
```
函数里面的FL_HEAD和FL_TAIL是使用flow库的所必须的宏FL_HEAD(fl)放到函数的最前面如果你的函数内部有变量定义的话放在变量定义的后面。而FL_TAIL(fl)是放在函数最后面一行的。
基本格式有了再来看下如何实现延时一秒呢其实只用一个语句就OK。
```
char led_flash(struct flow *fl)
{
FL_HEAD(fl);
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1);
led_open();
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1);
led_close();
FL_TAIL(fl);
}
```
是的你没看错仅仅只需要FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1)这一个语句就OK当执行到这个语句的时候该函数就会让出CPU权限当延时时间到了之后就会回来接着执行FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1)下面的语句。一直到FL_TAIL(fl),该函数就会结束任务,再也不会执行了,那么如果我们想让它一直循环执行呢?看下面:
```
char led_flash(struct flow *fl)
{
FL_HEAD(fl);
while(1)
{
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1);
led_open();
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1);
led_close();
}
FL_TAIL(fl);
}
```
看起来像不像个进程?其实也有点操作系统的样子了。。。
光有这个函数也不行,还得进行一些额外的操作
比如:
```
static struct flow fl_led; /* 1定义一个struct flow变量给这个函数使用 */
static char led_flash(struct flow *fl)
{
FL_HEAD(fl);
led_init(); /* 这里还能解决你的初始化问题,这里的函数只会在开机时或者说进程第一次进来时运行一次,以后将永远不会运行。注意:如果放在
FL_HEAD(fl)前面,那么就是每次轮到这个进程运行的时侯就会运行一次,总之很灵活 */
while(1)
{
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1);
led_open();
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1);
led_close();
}
FL_TAIL(fl);
}
int main(void)
{
FL_INIT(&fl_led); /* 2初始化struct flow变量 */
while(1)
{
led_flash(&fl_led); /* 3把led_flash进程放在main函数的while循环里 */
...
}
return 0;
}
```
经过以上3步就可以实现进程之间的切换啦。然后想根据某个条件来锁住线程释放CPU的话可以把里面的
```
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1);
```
换成
```
FL_LOCK_WAIT(fl, judge);
```
当里面的judge为假时线程就一直锁住在这一行语句当judge为真时就可以往下执行啦。同理可以完成很多其他的神奇功能让你的cpu再也不空转啦具体请看flow.h文件。。。。
这个版本暂时先写这么多先看看example.c。

28
lib/flow/example.c Normal file
View File

@ -0,0 +1,28 @@
#include "flow.h"
/* 1初始化一个struct flow变量 */
static struct flow fl_led;
static char led_flash(struct flow *fl)
{
FL_HEAD(fl);
for (;;)
{
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1U); /* 延时一秒 */
led_open();
FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1U); /* 延时一秒 */
led_close();
}
FL_TAIL(fl);
}
int main(void)
{
FL_INIT(&fl_led);
for (;;)
{
led_flash(&fl_led);
// other_process();
}
return 0;
}

129
lib/flow/flow.h Normal file
View File

@ -0,0 +1,129 @@
/**
* @file flow.h
* @author: xxx
* @date: 2023-07-21 17:00:15
* @brief
* @copyright: Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __FLOW_
#define __FLOW_
#include "flow_def.h"
#include "flow_core.h"
#include "flow_sem.h"
#define FL_HARD_TICK (10U) /* 系统硬件中断一次所需要的时间单位ms */
#define FL_CLOCK_SEC (1000U / FL_HARD_TICK) /* 一秒钟需要的tick可以根据需求添加其他时间更短的宏 */
#define FL_CLOCK_100MSEC (100U / FL_HARD_TICK)
#define FL_CLOCK_10MSEC (FL_CLOCK_100MSEC / 10U)
/**
* flow进程
*/
#define FL_INIT(fl) FLOW_INIT((fl))
/**
* flow头
*/
#define FL_HEAD(fl) FLOW_HEAD((fl))
/**
* flow尾
*/
#define FL_TAIL(fl) FLOW_TAIL((fl))
/**
* judge为真cpu给其他进程使用
*/
#define FL_LOCK_WAIT(fl, judge) FLOW_LOCK_WAIT((fl), (judge))
/**
* judge为真cpu给其他进程使用
*/
#define FL_LOCK_WHILE(fl, judge) FLOW_LOCK_WHILE((fl), (judge))
/**
* 退
*/
#define FL_EXIT(fl) FLOW_EXIT((fl))
/**
*
*/
#define FL_LOCK_ONCE(fl) FLOW_LOCK_ONCE((fl))
/**
* flow进程结束
*/
#define FL_WAIT_PROCESS_END(fl, process) FLOW_WAIT_PROCESS_END((fl), (process))
/**
* flow子进程结束
*/
#define FL_WAIT_CHILD(fl, cfl, process) FLOW_WAIT_CHILD_PROCESS_END((fl), (cfl), (process))
/**
* timecpu给其他进程使用time如果用FL_CLOCK_SEC来乘time的单位就是s
* time必须是常数
*/
#define FL_LOCK_DELAY(fl, time) FLOW_LOCK_DELAY((fl), (time))
/**
* timejudge为真
* time必须是常数
*/
#define FL_LOCK_DELAY_OR_WAIT(fl, judge, time) FLOW_LOCK_DELAY_OR_WAIT((fl), (judge), (time))
/**
*
*/
#define FL_SEM_INIT(sem, count) FLOW_SEM_INIT((sem), (count))
/**
*
*/
#define FL_LOCK_WAIT_SEM(fl, sem) FLOW_LOCK_WAIT_SEM((fl), (sem))
/**
* time可以为常数或者变量time必须是常数
*/
#define FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, sem, time) FLOW_LOCK_WAIT_SEM_OR_TIMEOUT((fl), (sem), (time))
/**
*
*/
#define FL_SEM_RELEASE(sem) FLOW_SEM_RELEASE((sem))
/**
*
*/
#define FL_SEM_IS_RELEASE(fl, sem) FLOW_SEM_IS_RELEASE((fl), (sem))
/**
*
*/
void fl_timer_set(struct flow_timer *t, unsigned long interval);
/**
*
*/
void fl_timer_reset(struct flow_timer *t);
/**
*
*/
void fl_timer_restart(struct flow_timer *t);
/**
* 01
*/
unsigned char fl_timer_timeout(struct flow_timer *t);
/**
* ticktick 500ms中断一次
* 500ms*n
*/
unsigned long fl_hour_much_time(struct flow_timer *t);
#endif /* __FLOW_ */

83
lib/flow/flow_core.c Normal file
View File

@ -0,0 +1,83 @@
/**
* @file
* @author xxx
* @date 2023-07-21 17:00:15
* @brief
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "flow.h"
unsigned long flow_tick;
/**
* @brief
* @param t
* @param interval
* @return NULL
* @note occurs时开始
*/
void fl_timer_set(struct flow_timer *t, unsigned long interval)
{
if (interval == 0)
{
// 如果间隔时间为零,返回错误
return;
}
t->interval = interval;
t->start = flow_tick;
}
/**
* @brief
* @param t
* @return NULL
* @note occurs时开始
*/
void fl_timer_reset(struct flow_timer *t)
{
t->start += t->interval;
}
/**
* @brief
* @param t
* @return NULL
* @note flow_tick
*/
void fl_timer_restart(struct flow_timer *t)
{
t->start = flow_tick;
}
/**
* @brief flow_timer结构体的超时状态
* @param {flow_timer} *t flow_timer结构体的指针
* @return {unsigned char} 10
* @note flow_timer结构体中的start时间之差是否大于或等于interval
*/
unsigned char fl_timer_timeout(struct flow_timer *t)
{
return ((flow_tick - t->start) >= t->interval) ? 1U : 0U;
}
/**
* @brief flow_timer结构体中的时间长度
* @param {flow_timer} *t flow_timer结构体的指针
* @return {unsigned long}
* @note start时间加上interval与当前时间flow_tick之差flow_ticktime_len - flow_tick0
*/
unsigned long fl_hour_much_time(struct flow_timer *t)
{
unsigned long time_len = t->start + t->interval;
if (time_len >= flow_tick)
{
return (time_len - flow_tick);
}
else
{
return 0U;
}
}

106
lib/flow/flow_core.h Normal file
View File

@ -0,0 +1,106 @@
/**
* @file flow_core.h
* @author: xxx
* @date: 2023-07-21 17:00:15
* @brief
* @copyright: Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __FLOW_CORE_
#define __FLOW_CORE_
#include "flow_def.h"
// 在定时器中断中调用
#define FLOW_TICK_UPDATE() \
do \
{ \
flow_tick++; \
} while (0);
// 初始化一个flow进程
#define FLOW_INIT(f) ((f)->line = 0)
// flow头必须放在函数内的最前面
#define FLOW_HEAD(f) \
{ \
volatile char lock_once_flag = 0; \
switch ((f)->line) \
{ \
case 0:
// flow尾必须放在函数内的最后面
#define FLOW_TAIL(f) \
} \
; \
lock_once_flag = (f)->line = 0; \
return FLOW_END; \
} \
;
// 给进程加锁直到judge为真加锁期间一直放开cpu给其他进程使用
#define FLOW_LOCK_WAIT(f, judge) \
do \
{ \
(f)->line = __LINE__; \
case __LINE__:; \
if (!(judge)) \
return FLOW_WAIT; \
} while (0)
// 如果judge为真就一直给进程加锁加锁期间一直放开cpu给其他进程使用
#define FLOW_LOCK_WHILE(f, judge) \
do \
{ \
(f)->line = __LINE__; \
case __LINE__:; \
if (judge) \
return FLOW_WAIT; \
} while (0)
// 退出该进程
#define FLOW_EXIT(f) \
do \
{ \
(f)->line = 0; \
return FLOW_FINISH; \
} while (0)
// 无条件锁住进程一次,下次进来再接着往下运行
#define FLOW_LOCK_ONCE(f) \
do \
{ \
lock_once_flag = 1; \
(f)->line = __LINE__; \
case __LINE__:; \
if (lock_once_flag) \
return FLOW_LOCK; \
} while (0)
// 等待一个flow进程结束
#define FLOW_WAIT_PROCESS_END(f, process) FLOW_LOCK_WHILE(f, (process) < FLOW_FINISH)
// 等待一个flow子进程结束
#define FLOW_WAIT_CHILD_PROCESS_END(f, cf, process) \
do \
{ \
FLOW_INIT((cf)); \
FLOW_WAIT_PROCESS_END((f), (process)); \
} while (0)
// 给进程加锁时长为time加锁期间一直放开cpu给其他进程使用time如果用FL_CLOCK_SEC来乘那么time的单位就是s
#define FLOW_LOCK_DELAY(f, t) \
do \
{ \
(f)->time = flow_tick; \
FLOW_LOCK_WAIT((f), ((flow_tick - (f)->time) >= (t))); \
} while (0)
// 给进程加锁时长为time延时期间如果judge为真就直接解锁进程
#define FLOW_LOCK_DELAY_OR_WAIT(f, judge, t) \
do \
{ \
(f)->time = flow_tick; \
FLOW_LOCK_WAIT((f), ((judge) || ((flow_tick - (f)->time) >= (t)))); \
} while (0)
#endif /* __FLOW_CORE_ */

37
lib/flow/flow_def.h Normal file
View File

@ -0,0 +1,37 @@
/**
* @file flow_def.h
* @author: xxx
* @date: 2023-07-21 17:00:15
* @brief
* @copyright: Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __FLOW_DEF_
#define __FLOW_DEF_
#define FLOW_WAIT (0)
#define FLOW_LOCK (1)
#define FLOW_FINISH (2)
#define FLOW_END (3)
struct flow
{
unsigned long line;
unsigned long time;
};
struct flow_timer
{
unsigned long start;
unsigned long interval;
};
struct flow_sem
{
unsigned long count;
unsigned long time;
};
extern unsigned long flow_tick;
#endif /* __FLOW_DEF_ */

40
lib/flow/flow_sem.h Normal file
View File

@ -0,0 +1,40 @@
/***
* @file:
* @author: xxx
* @date: 2023-07-21 17:00:15
* @brief
* @copyright: Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __FLOW_SEM_H__
#define __FLOW_SEM_H__
#include "flow_def.h"
#include "flow_core.h"
#define FLOW_SEM_INIT(s, c) ((s)->count = c) // 初始化信号量s的计数值为c
// 等待信号量s的计数值大于0
#define FLOW_LOCK_WAIT_SEM(f, s) \
do \
{ \
FLOW_LOCK_WAIT(f, (s)->count > 0); \
--(s)->count; \
} while (0)
// 等待信号量s的计数值大于0或者当前时间与锁f的时间之差大于等于t
#define FLOW_LOCK_WAIT_SEM_OR_TIMEOUT(f, s, t) \
do \
{ \
(f)->time = flow_tick; \
(s)->time = (t); \
FLOW_LOCK_WAIT(f, (((s)->count > 0) || ((flow_tick - (f)->time) >= ((s)->time)))); \
if (((s)->count > 0) && ((flow_tick - (f)->time) < ((s)->time))) \
--(s)->count; \
} while (0)
#define FLOW_SEM_RELEASE(s) (++(s)->count)
#define FLOW_SEM_IS_RELEASE(f, s) (flow_tick - (f)->time) < ((s)->time)
#endif /* __FLOW_SEM_H__ */

161
lib/inc/aes.h Normal file
View File

@ -0,0 +1,161 @@
/*
---------------------------------------------------------------------------
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
LICENSE TERMS
The redistribution and use of this software (with or without changes)
is allowed without the payment of fees or royalties provided that:
1. source code distributions include the above copyright notice, this
list of conditions and the following disclaimer;
2. binary distributions include the above copyright notice, this list
of conditions and the following disclaimer in their documentation;
3. the name of the copyright holder is not used to endorse products
built using this software without specific written permission.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
Issue 09/09/2006
This is an AES implementation that uses only 8-bit byte operations on the
cipher state.
*/
#ifndef AES_H
#define AES_H
#if 1
#define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */
#endif
#if 1
#define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */
#endif
#if 0
#define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */
#endif
#if 0
#define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */
#endif
#if 0
#define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */
#endif
#if 0
#define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */
#endif
#define N_ROW 4
#define N_COL 4
#define N_BLOCK (N_ROW * N_COL)
#define N_MAX_ROUNDS 14
typedef uint8_t return_type;
/* Warning: The key length for 256 bit keys overflows a byte
(see comment below)
*/
typedef uint8_t length_type;
typedef struct
{
uint8_t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK];
uint8_t rnd;
} aes_context;
/* The following calls are for a precomputed key schedule
NOTE: If the length_type used for the key length is an
unsigned 8-bit character, a key length of 256 bits must
be entered as a length in bytes (valid inputs are hence
128, 192, 16, 24 and 32).
*/
#if defined(AES_ENC_PREKEYED) || defined(AES_DEC_PREKEYED)
return_type aes_set_key(const uint8_t key[],
length_type keylen,
aes_context ctx[1]);
#endif
#if defined(AES_ENC_PREKEYED)
return_type aes_encrypt(const uint8_t in[N_BLOCK],
uint8_t out[N_BLOCK],
const aes_context ctx[1]);
return_type aes_cbc_encrypt(const uint8_t *in,
uint8_t *out,
int32_t n_block,
uint8_t iv[N_BLOCK],
const aes_context ctx[1]);
#endif
#if defined(AES_DEC_PREKEYED)
return_type aes_decrypt(const uint8_t in[N_BLOCK],
uint8_t out[N_BLOCK],
const aes_context ctx[1]);
return_type aes_cbc_decrypt(const uint8_t *in,
uint8_t *out,
int32_t n_block,
uint8_t iv[N_BLOCK],
const aes_context ctx[1]);
#endif
/* The following calls are for 'on the fly' keying. In this case the
encryption and decryption keys are different.
The encryption subroutines take a key in an array of bytes in
key[L] where L is 16, 24 or 32 bytes for key lengths of 128,
192, and 256 bits respectively. They then encrypts the input
data, in[] with this key and put the reult in the output array
out[]. In addition, the second key array, o_key[L], is used
to output the key that is needed by the decryption subroutine
to reverse the encryption operation. The two key arrays can
be the same array but in this case the original key will be
overwritten.
In the same way, the decryption subroutines output keys that
can be used to reverse their effect when used for encryption.
Only 128 and 256 bit keys are supported in these 'on the fly'
modes.
*/
#if defined(AES_ENC_128_OTFK)
void aes_encrypt_128(const uint8_t in[N_BLOCK],
uint8_t out[N_BLOCK],
const uint8_t key[N_BLOCK],
uint8_t o_key[N_BLOCK]);
#endif
#if defined(AES_DEC_128_OTFK)
void aes_decrypt_128(const uint8_t in[N_BLOCK],
uint8_t out[N_BLOCK],
const uint8_t key[N_BLOCK],
uint8_t o_key[N_BLOCK]);
#endif
#if defined(AES_ENC_256_OTFK)
void aes_encrypt_256(const uint8_t in[N_BLOCK],
uint8_t out[N_BLOCK],
const uint8_t key[2 * N_BLOCK],
uint8_t o_key[2 * N_BLOCK]);
#endif
#if defined(AES_DEC_256_OTFK)
void aes_decrypt_256(const uint8_t in[N_BLOCK],
uint8_t out[N_BLOCK],
const uint8_t key[2 * N_BLOCK],
uint8_t o_key[2 * N_BLOCK]);
#endif
#endif

49
lib/inc/clist.h Normal file
View File

@ -0,0 +1,49 @@
/**
* @file clist.h
* @author xxx
* @date 2023-08-08 23:18:15
* @brief 使 lib\examples\simple_clist.c
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __CLIST_H
#define __CLIST_H
#include "lib.h"
typedef void *cnode;
/// 链表中一个节点的结构体
typedef struct CLIST_NODE
{
cnode data; /// 值
struct CLIST_NODE *Next; /// 指向下一个结点
} clist_node_t;
void clist_init(clist_node_t **ppFirst); ///< 初始化 ,构造一条空的链表
void clist_print(clist_node_t *First); ///< 打印链表
uint32_t clist_node_count(clist_node_t *First); ///< 获取链表节点数
void clist_push_back(clist_node_t **ppFirst, cnode data); ///< 尾部插入
void clist_push_front(clist_node_t **ppFirst, cnode data); ///< 头部插入
void clist_pop_back(clist_node_t **ppFirst); ///< 尾部删除
void clist_pop_front(clist_node_t **ppFirst); ///< 头部删除
void clist_insert_for_node(clist_node_t **ppFirst, clist_node_t *pPos, cnode data); ///< 给定结点插入,插入到结点前
int32_t clist_insert(clist_node_t **ppFirst, int32_t Pos, cnode data); ///< 按位置插入
void clist_erase_for_node(clist_node_t **ppFirst, clist_node_t *pPos); ///< 给定结点删除
void clist_remove(clist_node_t **ppFirst, cnode data); ///< 按值删除,只删遇到的第一个
void clist_remove_all(clist_node_t **ppFirst, cnode data); ///< 按值删除,删除所有的
void clist_destroy(clist_node_t **ppFirst); ///< 销毁 ,需要销毁每一个节点
clist_node_t *clist_find(clist_node_t *pFirst, cnode data); ///< 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL
#endif //__CLIST_H

63
lib/inc/cmac.h Normal file
View File

@ -0,0 +1,63 @@
/**************************************************************************
Copyright (C) 2009 Lander Casado, Philippas Tsigas
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files
(the "Software"), to deal with the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers. Redistributions in
binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimers in the documentation and/or
other materials provided with the distribution.
In no event shall the authors or copyright holders be liable for any special,
incidental, indirect or consequential damages of any kind, or any damages
whatsoever resulting from loss of use, data or profits, whether or not
advised of the possibility of damage, and on any theory of liability,
arising out of or in connection with the use or performance of this software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS WITH THE SOFTWARE
*****************************************************************************/
#ifndef _CMAC_H_
#define _CMAC_H_
#include "aes.h"
#define AES_CMAC_KEY_LENGTH 16
#define AES_CMAC_DIGEST_LENGTH 16
typedef struct _AES_CMAC_CTX
{
aes_context rijndael;
uint8_t X[16];
uint8_t M_last[16];
uint32_t M_n;
} AES_CMAC_CTX;
// #include <sys/cdefs.h>
//__BEGIN_DECLS
void AES_CMAC_Init(AES_CMAC_CTX *ctx);
void AES_CMAC_SetKey(AES_CMAC_CTX *ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]);
void AES_CMAC_Update(AES_CMAC_CTX *ctx, const uint8_t *data, uint32_t len);
// __attribute__((__bounded__(__string__,2,3)));
void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX *ctx);
// __attribute__((__bounded__(__minbytes__,1,AES_CMAC_DIGEST_LENGTH)));
//__END_DECLS
#endif /* _CMAC_H_ */

49
lib/inc/cmd.h Normal file
View File

@ -0,0 +1,49 @@
/**
* @file cmd.h
* @author xxx
* @date 2023-06-25 13:07:02
* @brief
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef _CMD_H_
#define _CMD_H_
#define CMD_HASH 0xb433e5c6
#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM Compiler */
#define SECTION(x) __attribute__((section(x)))
#define CMD_USED __attribute__((used))
#elif defined(__IAR_SYSTEMS_ICC__) /* IAR Compiler */
#define SECTION(x) @x
#define CMD_USED __root
#else
#error "not supported tool chain..."
#endif
typedef void (*cmd_handler)(void);
typedef struct cmd
{
const char *cmd;
const char *cmd_mess;
unsigned int hash;
cmd_handler handler;
} cmd_t;
/// 注册命令
#define REGISTER_CMD(cmd, handler, desc) \
const char _register_##cmd##_cmd[] = #cmd; \
const char _register_##cmd##_desc[] = #desc; \
CMD_USED cmd_t _register_##cmd SECTION("CMDS") = \
{ \
_register_##cmd##_cmd, \
_register_##cmd##_desc, \
(unsigned int)CMD_HASH, \
(cmd_handler)&handler};
void cmd_init(void); ///< 初始化命令
void cmd_parsing(char *str); ///< 命令解析
#endif

79
lib/inc/data_analysis.h Normal file
View File

@ -0,0 +1,79 @@
/**
* @file data_analysis.h
* @author xxx
* @date 2023-06-25 13:07:02
* @brief
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef COMPONENTS_COMMON_INCLUDE_DATA_ANALYSIS_H_
#define COMPONENTS_COMMON_INCLUDE_DATA_ANALYSIS_H_
#include "data_type_def.h"
typedef enum
{
DATA_1,
DATA_2,
DATA_MAX,
} DataId_t; // 处理数据模块的个数,请根据实际情况修改
#define DATA_NUM (DATA_MAX)
#define DATA_BUF_RECV_SQQ_LEN 650u
#define DATA_BUF_SEND_SQQ_LEN 0u
#define DATA_SD_LEN_MAX 2
#define DATA_LD_LEN_MAX 2
#define DATA_ED_LEN_MAX 1
typedef struct _data_reg_t_
{
struct
{
uint8_t len;
uint8_t pos;
uint8_t data[DATA_SD_LEN_MAX];
BOOL valid; // 是否有效
} sd; // start delimiter
struct
{
uint8_t len;
uint8_t pos; // 偏移量在wait_end_state中根据帧长去掉固定长度来判断是否是结束符
uint8_t little_endian;
BOOL valid; // 是否有效
} ld; // length describe
struct
{
uint16_t len_max;
uint16_t len_min;
} argu;
struct
{
uint8_t len;
uint8_t data[DATA_ED_LEN_MAX];
BOOL valid;
} ed;
BOOL echo_en;
void (*func_ptr)(void);
} data_reg_t;
typedef void (*data_interupt_cb_t)(uint8_t id, uint8_t ch); ///< 中断回调函数,数据从这里写入
extern uint8_t data_read(uint8_t id, void *buffer, uint16_t len); ///< 读取数据
extern void data_write(uint8_t id, uint8_t *const string, uint16_t len); ///< TODO 写入数据
extern void lock_data(uint8_t data_id); ///< 锁定数据,防止中断写入数据
extern void unlock_data(uint8_t data_id); ///< 解锁数据
extern data_interupt_cb_t data_fsm_init(uint8_t data_id); ///< 初始化数据状态机
extern BOOL data_reg(uint8_t id, data_reg_t reg); ///< 注册数据
extern void data_unreg(uint8_t id); ///< 注销数据
#endif /* COMPONENTS_COMMON_INCLUDE_DATA_ANALYSIS_H_ */

290
lib/inc/data_type_def.h Normal file
View File

@ -0,0 +1,290 @@
/***
* @Author:
* @Date: 2023-03-29 13:16:28
* @LastEditors: xxx
* @LastEditTime: 2023-03-30 00:34:11
* @Description:
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __DATA_TYPE_DEF_H_
#define __DATA_TYPE_DEF_H_
#include <stdint.h>
#ifndef PI
#define PI (3.14159265358979323846f)
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef OK
typedef enum
{
OK = 0,
FAIL = !OK,
} state_e;
#endif
#ifndef __IO
#define __IO volatile
#endif
typedef unsigned char BOOL; /* boolean data */
typedef unsigned char bool_t; /* boolean data */
#if !defined(__stdint_h) && !defined(_GCC_WRAP_STDINT_H)
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned long int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed long int int32_t;
typedef long long int64_t;
#endif
typedef float float32;
typedef double float64;
#ifndef float32_t
typedef float float32_t;
#endif
#ifndef float64_t
typedef double float64_t;
#endif
#pragma pack(1)
typedef struct
{
uint8_t bs[3];
} uint24_t;
typedef struct
{
uint8_t bs[5];
} uint40_t;
typedef union
{
float32 f;
int32_t c;
} float32_u;
#pragma pack()
typedef enum
{
DATA_TYPE_INT8 = 0, // 8-bit signed integer
DATA_TYPE_UINT8, // 8-bit unsigned integer
DATA_TYPE_INT16, // 16-bit signed integer
DATA_TYPE_UINT16, // 16-bit unsigned integer
DATA_TYPE_INT32, // 32-bit signed integer
DATA_TYPE_UINT32, // 32-bit unsigned integer
DATA_TYPE_INT64, // 64-bit signed integer
DATA_TYPE_UINT64, // 64-bit unsigned integer
DATA_TYPE_FLOAT, // 32-bit floating point number
DATA_TYPE_DOUBLE, // 64-bit floating point number
DATA_TYPE_STRING, // string
DATA_TYPE_ARRAY, // array
DATA_TYPE_STRUCT, // structure
DATA_TYPE_UNION, // union
DATA_TYPE_ENUM, // enumeration
DATA_TYPE_POINTER, // pointer
DATA_TYPE_FUNCTION, // function
DATA_TYPE_VOID, // void
DATA_TYPE_MAX,
} data_type_e;
typedef uint16_t nwk_id_t;
/**
* STANDARD BITS
*/
#ifndef BIT0
#define BIT0 (0x01u)
#define BIT1 (0x02u)
#define BIT2 (0x04u)
#define BIT3 (0x08u)
#define BIT4 (0x10u)
#define BIT5 (0x20u)
#define BIT6 (0x40u)
#define BIT7 (0x80u)
#define BIT8 (0x0100u)
#define BIT9 (0x0200u)
#define BIT10 (0x0400u)
#define BIT11 (0x0800u)
#define BIT12 (0x1000u)
#define BIT13 (0x2000u)
#define BIT14 (0x4000u)
#define BIT15 (0x8000u)
#define BIT16 (0x00010000u)
#define BIT17 (0x00020000u)
#define BIT18 (0x00040000u)
#define BIT19 (0x00080000u)
#define BIT20 (0x00100000u)
#define BIT21 (0x00200000u)
#define BIT22 (0x00400000u)
#define BIT23 (0x00800000u)
#define BIT24 (0x01000000u)
#define BIT25 (0x02000000u)
#define BIT26 (0x04000000u)
#define BIT27 (0x08000000u)
#define BIT28 (0x10000000u)
#define BIT29 (0x20000000u)
#define BIT30 (0x40000000u)
#define BIT31 (0x80000000u)
#define BIT_SET(x, b) x |= b // 置位
#define BIT_CLR(x, b) x &= ~b // 清零
#define BIT_GET(x, y) ((x) >> (y) & 1) // 获取某一位
#define BIT_REVERSE(x, y) (x) ^= (1 << y) // 某位取反
#define BIT_IS_SET(x, b) ((x) & (b)) // 判断某一位是否为1
#define BIT_IS_CLR(x, b) (!((x) & (b))) // 判断某一位是否为0
#endif
#ifndef BF
/**
* @brief
* @return {*}
* @note
*> uint8_t num = 0x12; 00010010 <p>
*> uint8_t bit = 2; 20 <p>
*> uint8_t width = 1; 1 <p>
*> uint8_t result = BF(num, bit, width); 1 <p>
*/
#define BF(x, b, s) (((x) & (b)) >> (s))
#endif
#ifndef MIN
/**
* @brief
* @return {*}
* @note
*> int num1 = 10; <p>
*> int num2 = 20; <p>
*> int result = MIN(num1, num2); // 结果为10 <p>
*/
#define MIN(n, m) (((n) < (m)) ? (n) : (m))
#endif
#ifndef MAX
/**
* @brief
* @return {*}
* @note
*> int num1 = 10; <p>
*> int num2 = 20; <p>
*> int result = MAX(num1, num2); // 结果为20 <p>
*/
#define MAX(n, m) (((n) < (m)) ? (m) : (n))
#endif
#ifndef ABS
/**
* @brief
* @return {*}
* @note
*> int num = -10;
*> int result = ABS(num); // 结果为10
*/
#define ABS(n) (((n) < 0) ? -(n) : (n))
#endif
#ifndef RANGE
#define RANGE(x, a, b) (MIN(MAX(x, a), b))
#endif
/**
* @brief Macro to check if a value is between a minimum and maximum value (inclusive).
* @param x The value to check.
* @param min The minimum value.
* @param max The maximum value.
* @return Returns 1 if the value is between the minimum and maximum values (inclusive), 0 otherwise.
*/
#define IS_BETWEEN(x, min, max) ((x) >= min && (x) <= max)
#define ARRAY_LEN(arr) (sizeof(arr)) / (sizeof(arr[0]))
#define HI_UINT16(a) (((uint16_t)(a) >> 8) & 0xFF)
#define LO_UINT16(a) ((uint16_t)(a) & 0xFF)
#define HI_1_UINT32(a) (((uint32_t)(a) >> 24) & 0xFF)
#define HI_2_UINT32(a) (((uint32_t)(a) >> 16) & 0xFF)
#define HI_3_UINT32(a) (((uint32_t)(a) >> 8) & 0xFF)
#define HI_4_UINT32(a) ((uint32_t)(a) & 0xFF)
#define LO_1_UINT8(a) (uint8_t)((a) & 0xFF)
#define LO_2_UINT8(a) (uint8_t)(((a) & 0xFF00) >> 8)
#define LO_3_UINT8(a) (uint8_t)(((a) & 0xFF0000) >> 16)
#define LO_4_UINT8(a) (uint8_t)(((a) & 0xFF000000) >> 24)
// uint32小端转大端
#define S2B_UINT32(a) \
(((uint32_t)(a) & 0xFF000000) >> 24) + (((uint32_t)(a) & 0x00FF0000) >> 8) + (((uint32_t)(a) & 0x0000FF00) << 8) + (((uint32_t)(a) & 0x000000FF) << 24)
// uint32大端转小端
#define B2S_UINT32(a) S2B_UINT32(a)
// uint16小端转大端
#define S2B_UINT16(a) ((((uint16_t)(a) & 0xFF00) >> 8) + (((uint16_t)(a) & 0x00FF) << 8))
// uint16大端转小端
#define B2S_UINT16(a) S2B_UINT16(a)
#define BUILD_UINT16(loByte, hiByte) \
((uint16_t)(((loByte) & 0x00FF) + (((hiByte) & 0x00FF) << 8)))
// float32小端转大端
static inline float32 S2B_FLOAT32(float fv)
{
float32_u _f;
_f.f = fv;
_f.c = S2B_UINT32(_f.c);
return _f.f;
}
// float32大端转小端
#define B2S_FLOAT32(a) S2B_FLOAT32(a)
// 反序数组
#define REVERSE_ARRAY(arr, len) \
do \
{ \
uint8_t _tmp; \
uint16_t _i; \
for (_i = 0; _i < len / 2; _i++) \
{ \
_tmp = arr[_i]; \
arr[_i] = arr[len - _i - 1]; \
arr[len - _i - 1] = _tmp; \
} \
} while (0);
// 比较2个数组是否相等
#define IsEqual(arr1, arr2, n) ({ \
int _equal = 1; \
for (int _i = 0; _i < n; _i++) \
{ \
if (arr1[_i] != arr2[_i]) \
{ \
_equal = 0; \
break; \
} \
} \
_equal; \
})
// ASSIC码转换为数字
#define ASCII_TO_NUM(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : (c) - 'A' + 10)
// 数字转换为ASSIC码
#define NUM_TO_ASCII(x) ((x) < 10 ? (x) + '0' : (x) - 10 + 'A')
#define FLOAT_TO_UINT16(x) (x * 8192 / 100 + 2048) ///> 浮点压缩uint16_t
#define UINT16_TO_FLOAT(x) (100 * (x - 2048) / 8192) ///> uint16转浮点
#endif /* __DATA_TYPE_DEF_H_ */

21
lib/inc/debug.h Normal file
View File

@ -0,0 +1,21 @@
/***
* @Author:
* @Date: 2023-04-04 08:13:11
* @LastEditors: xxx
* @LastEditTime: 2023-04-04 13:21:46
* @Description:
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __DEBUG_H
#define __DEBUG_H
#include "lib.h"
/*形参*/
#define _DBG_LINE_ , uint16_t line
/*实参*/
#define __DBG_LINE , __LINE__
extern BOOL DBG_ASSERT(uint8_t cond _DBG_LINE_);
#endif //__DEBUG_H

53
lib/inc/filter.h Normal file
View File

@ -0,0 +1,53 @@
/**
* @file filter.h
* @author xxx
* @date 2023-08-08 22:59:46
* @brief
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __FILTER_H__
#define __FILTER_H__
#include "lib.h"
typedef struct
{
float32 x; // 卡尔曼滤波器的估计值
float32 a; // 状态转移矩阵1表示没有动态变化
float32 h; // 观测矩阵1表示直接观测
float32 q; // 过程噪声协方差
float32 r; // 观测噪声协方差
float32 p; // 估计误差协方差
float32 gain; // 卡尔曼增益
float32 change_max; // 允许的最大变化量,用于判断观测值是否异常
} kalman_t; // 卡尔曼滤波器结构
typedef struct
{
BOOL fisrt_flag; // 第一次标志位
float32 alpha; // 滤波系数 0~1
float32 last_value; // 上次滤波结果
} lpf_t; // 一阶低通滤波器
typedef struct
{
uint16_t size; // 滑动窗口大小
float32 *window; // 滑动窗口
volatile float32 sum; // 滑动窗口和
volatile float32 out; // 滤波结果
uint16_t index; // 滑动窗口索引
} lpf_window_t; // 滑动窗口滤波器
void kalman_init(kalman_t *cfg, float32 change_max);
float32 kalman_update(kalman_t *cfg, float32 input);
void lpf_init(lpf_t *cfg);
float32 lpf_update(lpf_t *cfg, float32 input);
void lpf_reset(lpf_t *cfg);
void lpf_window_init(lpf_window_t *cfg, uint16_t size);
void lpf_window_dinit(lpf_window_t *cfg);
float32 lpf_window_update(lpf_window_t *cfg, float32 input);
void lpf_window_reset(lpf_window_t *cfg);
#endif // __FILTER_H__

307
lib/inc/fsm.h Normal file
View File

@ -0,0 +1,307 @@
#ifndef __FSM_H__
#define __FSM_H__
#include <stdint.h>
/* ----------------------- Defines ------------------------------------------*/
// 用于快速识别出 STATE与STEP
#define FSM_STATE(name) state_##name
#define FSM_FUNCT(name) funct_##name
// 数据类型定义区
typedef signed char state;
typedef long long step_ret;
typedef void *AS_STEP_RETVAL;
/*!
* @brief
*
*
* @param[in] void*
*
* @return
*/
typedef void *(*Procedure)(void *);
typedef struct
{
state ds; // 默认状态
state cs; // 当前状态
state ns; // 下个状态
} SM_STATE;
// 状态机 属性 定义
typedef struct
{
// 状态管理
SM_STATE st;
// 状态机跳转表
Procedure *procedures;
// 状态机数据区域
void *data;
// 错误处理(用于存放 状态 执行 的结果)
step_ret ret_ptr; // 状态 执行结果
void *err_ptr;
state err_flag;
} FSM;
/* ----------------------- Start function declaration -----------------------------*/
/*!
* @brief
*
* @param[in] fsm
*
* @param[in] err_var
*
* @return /
*/
static inline void set_err_var(FSM *fsm, void *err_var)
{
if (!fsm)
return;
fsm->err_ptr = err_var;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return /
*/
static inline void *get_err_var(FSM *fsm)
{
return fsm->err_ptr;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return /
*/
static inline state is_fsm_error(FSM *fsm)
{
return fsm->err_flag;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return /
*/
static inline state set_fsm_error_flag(FSM *fsm)
{
if (!fsm)
return -1;
fsm->err_flag = 1;
return 0;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return /
*/
static inline state clr_fsm_error_flag(FSM *fsm)
{
if (!fsm)
return -1;
fsm->err_flag = 0;
return 0;
}
/*!
* @brief
*
* @param[in] fsm
*
* @param[in] procedures
*
*/
static inline void set_procedures(FSM *fsm, Procedure *procedures)
{
if (fsm)
{
fsm->procedures = procedures;
fsm->st.cs = -1; // 执行run之前当前状态是未定的
}
}
/*!
* @brief
*
* @param[in] fsm
*
* @param[in] data
*
*/
static inline void set_data_entry(FSM *fsm, void *data)
{
if (fsm)
fsm->data = data;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return
*
*/
static inline void *get_data_entry(FSM *fsm)
{
return fsm->data;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return
* -1 :
*/
static inline state run_state_machine_once(FSM *fsm)
{
if (!fsm)
return -1;
// 切换到新状态
fsm->st.cs = fsm->st.ns;
// 跳转到下一个状态(状态 执行 结果 保存在 ret_ptr 中
fsm->ret_ptr = (step_ret)fsm->procedures[fsm->st.cs](fsm);
return fsm->st.cs;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return /
*/
static inline step_ret *get_step_retval(FSM *fsm)
{
return &fsm->ret_ptr;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return
*/
static inline state get_curr_state(FSM *fsm)
{
return fsm->st.cs;
}
/*!
* @brief
*
* @param[in] fsm
*
* @param[in] st
*
*/
static inline void set_default_state(FSM *fsm, state st)
{
if (!fsm)
return;
fsm->st.ds = st;
fsm->st.ns = st;
}
/*!
* @brief
*
* @param[in] fsm
*
* @param[in] st
*
*/
static inline void set_next_state(FSM *fsm, state st)
{
if (fsm)
fsm->st.ns = st;
}
/*!
* @brief
*
* @param[in] fsm
*
* @return
*/
static inline state get_next_state(FSM *fsm)
{
return fsm->st.ns;
}
/*!
* @brief
*
* @param[in] fsm
*
*/
static inline void init_state_machine(FSM *p)
{
set_next_state(p, p->st.ds);
p->st.cs = -1; // 执行run之前当前状态是未定的
}
/*!
* @brief
*
* @param[in] fsm
*
*/
static inline void reset_state_machine(FSM *p)
{
if (!p)
return;
clr_fsm_error_flag(p);
init_state_machine(p);
}
/*!
* @brief
*
* @param[in] fsm
*
* @param[in] st
*
* @return /
*/
static inline state is_curr_state(FSM *fsm, state st)
{
return fsm->st.cs == st;
}
/*!
* @brief
*
* @param[in] fsm
*
* @param[in] st
*
* @return /
*/
static inline state is_next_state(FSM *fsm, state st)
{
return fsm->st.ns == st;
}
#endif // __FSM_H__

127
lib/inc/lib.h Normal file
View File

@ -0,0 +1,127 @@
/***
* @Author:
* @Date: 2023-04-04 08:13:11
* @LastEditors: xxx
* @LastEditTime: 2023-04-04 10:13:21
* @Description:
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __LIB_H
#define __LIB_H
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "data_type_def.h"
#include "malloc.h"
#include "data_analysis.h"
#include "osel_arch.h"
#include "debug.h"
#include "sqqueue.h"
#include "clist.h"
#define INTERNAL_EXTERN extern
#ifndef STM32
#include "log.h"
#else
#define LOG_PRINT(fmt, ...) \
do \
{ \
} while (0);
#define LOG_ERR(fmt, ...) \
do \
{ \
} while (0);
#define LOG_HEX(data, len) \
do \
{ \
} while (0);
#endif
#define EXIT(x) \
do \
{ \
DBG_ASSERT(FALSE, __DBG_LINE); \
} while (0);
////< 时间结构
typedef union
{
uint8_t data[6];
struct
{
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
} date;
} date_time_t;
typedef struct
{
uint16_t year;
uint8_t month;
uint8_t day;
} rtc_date_t;
typedef struct
{
uint8_t hour;
uint8_t minute;
uint8_t second;
} rtc_time_t;
typedef struct
{
float32 x;
float32 y;
} point_t;
typedef struct
{
float32 a;
float32 b;
} linear_func_param_t;
extern void assic_to_str(uint8_t *assic, uint8_t len, uint8_t *str); // ASCII码转字符串
extern void get_cpu_id(uint32_t *id); // 获取CPU ID
extern uint32_t cpu_encrypt(void); // CPU加密
extern BOOL cpu_judge_encrypt(uint32_t cupid_encrypt); // CPU判断加密
extern void version_split(uint8_t *version, uint8_t *hi, uint8_t *lo); // 版本号1.0拆解成1和0
extern void reverse(uint8_t *buf, uint16_t len); // 反序数组
extern BOOL is_in_array(uint16_t *arr, uint16_t len, uint16_t val); // 判断val是否在数组arr中
extern BOOL is_same_value(uint8_t *buf, uint16_t len, uint8_t value); // 判断数组中的值是否都相等
extern uint16_t crc16_compute(const uint8_t *const data, uint16_t length); // CRC16校验
extern uint32_t crc32_compute(const uint8_t *const data, uint16_t length); // CRC32校验
extern uint64_t crc64_compute(const uint8_t *const data, const uint16_t length); // CRC64校验
extern uint8_t xor_compute(const uint8_t *const data, uint16_t length); // 异或校验
extern uint8_t get_bit_num(uint8_t bit); // 获取bit位的值
extern BOOL is_bit_set(int x, int k); // 判断x的第k位是否为1
extern uint8_t is_leap_year(uint16_t year); // 检查是否是闰年
extern BOOL is_valid_date(uint8_t year, uint8_t month, uint8_t day); // 检查日期是否有效
extern BOOL is_valid_time(uint8_t hour, uint8_t minute, uint8_t second); // 检查时间是否有效
extern BOOL is_valid_datetime(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); // 检查日期和时间是否有效
extern uint32_t days_since_1970(uint16_t year, uint8_t month, uint8_t day); // 计算从1970年1月1日到给定年月日的天数
extern uint32_t date_to_seconds(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); // 将日期转换为秒数
extern void seconds_to_date(uint32_t total_seconds, rtc_date_t *date, rtc_time_t *time); // 将秒数转换为日期
extern uint16_t dayOfyear(uint16_t year, uint8_t month, uint8_t day); // 计算一年中的第几天
extern uint16_t weekOfyear(uint16_t year, uint8_t month, uint8_t day); // 计算一年中的第几周
extern uint8_t get_weekday(uint16_t year, uint8_t month, uint8_t day); // 获取今天星期几
extern uint8_t hex_format_dec(uint8_t hex); // 十六进制转十进制
extern uint8_t dec_format_hex(uint8_t dec); // 十进制转十六进制
extern void quicksort(uint16_t arr[], int low, int high); // 快速排序
extern void insertion_sort(uint16_t arr[], uint16_t n); // 插入排序
extern uint32_t time2stamp(const rtc_date_t *const date, const rtc_time_t *const time); // 日期时间转时间戳
extern void stamp2time(uint32_t stamp, rtc_date_t *date, rtc_time_t *time); // 时间戳转北京时间
extern void convert_seconds(uint32_t total_seconds, char *date); // 秒数转换成时分秒
extern void add_commas(uint32_t num, char *result); // 数字添加逗号
extern linear_func_param_t calculate_linear_regression(const point_t *points, int32_t count); // 计算线性回归
extern float32 get_linearity_value(const point_t *points, int32_t count, linear_func_param_t param); // 获取线性度
#endif //__LIB_H

45
lib/inc/log.h Normal file
View File

@ -0,0 +1,45 @@
/***
* @Author:
* @Date: 2023-03-20 19:27:47
* @LastEditors: xxx
* @LastEditTime: 2023-03-30 00:34:41
* @Description:PC端调试使用
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __LOG_H_
#define __LOG_H_
#include <string.h>
#include <stdio.h>
#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__)
/*调试日志宏定义*/
#define LOG_PRINT(fmt, ...) \
do \
{ \
printf("[DEBUG:%s][%s:%d] " fmt "\n", __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
} while (0);
/*错误日志打印(在日志打印模块还未启动时使用)*/
#define LOG_ERR(fmt, ...) \
do \
{ \
printf("[ERROR:%s][%s:%d] " fmt "\n", __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
} while (0);
// 打印十六进制字符串
#define LOG_HEX(data, len) \
do \
{ \
printf("[DEBUG:%s][%s:%d] ", __FILENAME__, __FUNCTION__, __LINE__); \
for (int i = 0; i < len; i++) \
{ \
printf("%02x ", data[i]); \
} \
printf("\n"); \
} while (0);
#endif //__LOG_H_

45
lib/inc/malloc.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef _MOLLOC_H
#define _MOLLOC_H
#include "../inc/data_type_def.h"
#ifndef NULL
#define NULL 0
#endif
// 定义两个内存池
#define SRAMIN 0 // 内部内存池
#define SRAMEX 1 // 外部内存池(精英STM32开发板不支持外部内存)
// 我们又多少个SRAM可管理
#define SRAMBANK 2 // 定义支持的SRAM块数. 精英版实际上只支持1个内存区域,即内部内存.
// mem1内存参数设定.mem1完全处于内部SRAM里面.(设置内部SARM的内存池和内存表的参数)
#define MEM1_BLOCK_SIZE 8 // 一个内存块大小为32字节
#define MEM1_MAX_SIZE 8 * 1024 // 最大管理内存 1K (我们这个内存管理系统的内部SRAM可控制的内存大小)
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE / MEM1_BLOCK_SIZE // 内存表大小(有多少块内存块)
// mem2内存参数设定.mem2的内存池处于外部SRAM里面
#define MEM2_BLOCK_SIZE 8 // 一个内存块大小为32字节
#define MEM2_MAX_SIZE 0 * 1024 // 因为精英版没有外扩内存,故这里设置一个最小值
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE / MEM2_BLOCK_SIZE // 内存表大小
// 内存管理控制器结构体
// 注意:内存管理由内存池和内存列表组成
// SRAMBANKSARM块数一般有内部SRAM和外部SRAM、CCM
struct _m_mallco_dev
{
void (*init)(uint8_t); // 初始化
uint8_t (*perused)(uint8_t); // 内存使用率
uint8_t *membase[SRAMBANK]; // 内存池 管理SRAMBANK个区域的内存
uint16_t *memmap[SRAMBANK]; // 内存管理状态表
uint8_t memrdy[SRAMBANK]; // 内存管理是否就绪
};
void my_mem_init(uint8_t memx);
uint8_t my_mem_perused(uint8_t memx);
void *mymalloc(uint8_t memx, uint32_t size);
void myfree(uint8_t memx, void *ptr);
void *myrealloc(uint8_t memx, void *ptr, uint32_t size);
#endif

268
lib/inc/mlist.h Normal file
View File

@ -0,0 +1,268 @@
/***
* @Author:
* @Date: 2023-04-04 08:39:32
* @LastEditors: xxx
* @LastEditTime: 2023-04-04 08:46:20
* @Description: Linux内核 (include/linux/list.h)
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __LIST_H
#define __LIST_H
#include <stdbool.h>
#include "data_type_def.h"
typedef struct list_head
{
struct list_head *next;
struct list_head *prev;
} list_head_t;
/**
* ,
*
* @param ptr:
* @param type:
* @param member:
*
* @return
*/
#define list_entry_addr_find(ptr, type, member) \
((type *)((char *)(ptr) - (char *)(&((type *)NULL)->member)))
/**
*
*
* @param head:
* @param type:
* @param member:
*
* @return
*/
#define list_entry_decap(head, type, member) \
( \
(list_empty(head)) ? (type *)NULL \
: (list_entry_addr_find(list_next_elem_get(head), type, member)))
#define list_entry_get_head(head, type, member) \
((list_empty(head)) ? (type *)NULL : (list_entry_addr_find((head)->next, type, member)))
/**
*
*
* @param head:
* @param type:
* @param member:
*
* @return
*/
#define list_entry_curtail(head, type, member) \
((list_empty(head)) ? (type *)NULL \
: (list_entry_addr_find(list_curtail(head), type, member)))
/**
* -- list_for_each
*
* @param pos:
* @param head:
*/
#define list_for_each_forwards(pos, head) \
for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next)
/**
*
*
* @param pos:
* @param head:
*/
#define list_for_each_backwards(pos, head) \
for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev)
/**
*
*
* @param pos:
* @param n:
* @param head:
*/
#define list_for_each_safe(pos, n, head) \
for ((pos) = (head)->next, n = (pos)->next; (pos) != (head); \
(pos) = n, n = (pos)->next)
/**
*
*
* @param pos:
* @param head:
* @param type:
* @param member:
*/
#define list_entry_for_each(pos, head, type, member) \
for ((pos) = list_entry_addr_find((head)->next, type, member); \
&(pos)->member != (head); \
(pos) = list_entry_addr_find((pos)->member.next, type, member))
/**
* ,
*
* @param pos:
* @param n:
* @param head:
* @param type:
* @param member:
*/
#define list_entry_for_each_safe(pos, n, head, type, member) \
for ((pos) = list_entry_addr_find((head)->next, type, member), \
n = list_entry_addr_find((pos)->member.next, type, member); \
&(pos)->member != (head); \
(pos) = n, n = list_entry_addr_find(n->member.next, type, member))
/**
*
*/
#define list_count(head, count) \
do \
{ \
count = 0; \
for (list_head_t *pos = (head)->next; pos != (head); pos = pos->next) \
{ \
count++; \
} \
} while (0)
/**
* funcCompare来决定 -- list_sorted_add
*
* @param new_entry:
* @param head:
* @param type:
* @param member:
* @param func_compare: ,bool func_compare(Node* A, Node* B)
* A < B, true, false
* @param pos:
*/
#define list_entry_sorted_add(new_entry, head, type, member, func_compare, pos) \
do \
{ \
type *entry_a = NULL; \
type *entry_b = NULL; \
for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) \
{ \
entry_a = list_entry_addr_find((new_entry), type, member); \
entry_b = list_entry_addr_find((pos), type, member); \
if (func_compare(entry_a, entry_b)) \
{ \
break; \
} \
} \
if ((pos) != (head)) \
{ \
list_insert_forwards((new_entry), (pos)); \
} \
else \
{ \
list_add_to_tail((new_entry), (head)); \
} \
} while (__LINE__ == -1)
/**
*
*
* @param ptr:
*/
void list_init(list_head_t *const ptr);
/**
*
*
* @param new_entry:
* @param pos:
*/
void list_insert_forwards(list_head_t *const new_entry, list_head_t *const pos);
/**
*
*
* @param new_entry:
* @param pos:
*/
void list_insert_backwards(list_head_t *const new_entry, list_head_t *const pos);
/**
* -- list_append
*
* @param new_entry:
* @param list:
*/
void list_add_to_tail(list_head_t *const new_entry, list_head_t *const list);
/**
*
*
* @param new_entry:
* @param list:
*/
void list_add_to_head(list_head_t *const new_entry, list_head_t *const list);
/**
*
*
* @param elem:
*/
void list_del(list_head_t *const elem);
/**
*
*
* @param head:
*
* @return
*/
list_head_t *list_curtail(const list_head_t *const head);
/**
*
*
* @param head:
*
* @return TRUEFALSE
*/
bool list_empty(const list_head_t *const head);
/**
* -- list_get_head
*
* @param head:
*
* @return
*/
list_head_t *list_first_elem_look(const list_head_t *const head);
/**
*
*
* @param pos:
*
* @return
*/
list_head_t *list_next_elem_get(const list_head_t *const pos);
/**
*
*
* @param elem:
* @param head:
*/
void list_move_to_another_head(list_head_t *const elem, list_head_t *const head);
/**
*
*
* @param elem:
* @param head:
*/
void list_move_to_another_tail(list_head_t *const elem, list_head_t *const head);
#endif
/**
* @}
*/

181
lib/inc/osel_arch.h Normal file
View File

@ -0,0 +1,181 @@
/***
* @Author:
* @Date: 2023-04-04 08:13:11
* @LastEditors: xxx
* @LastEditTime: 2023-04-04 08:16:58
* @Description:
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __OSEL_ARCH_H__
#define __OSEL_ARCH_H__
#include "lib.h"
#define hal_int_state_t char
#ifdef STM32
#include "main.h"
#define HAL_ENTER_CRITICAL(__HANDLE__) \
do \
{ \
if ((__HANDLE__)->Lock == HAL_LOCKED) \
{ \
return HAL_BUSY; \
} \
else \
{ \
(__HANDLE__)->Lock = HAL_LOCKED; \
} \
} while (0U)
#define HAL_EXIT_CRITICAL(__HANDLE__) \
do \
{ \
(__HANDLE__)->Lock = HAL_UNLOCKED; \
} while (0U)
#else
#define HAL_ENTER_CRITICAL(__HANDLE__)
#define HAL_EXIT_CRITICAL(__HANDLE__)
#endif
#define osel_memset _memset
#define osel_memcmp _memcmp
#define osel_memcpy _memcpyL
#define osel_memcpyr _memcpyR
#define osel_reverse _reverse
#define osel_mem_alloc _malloc
#define osel_mem_free _free
#define osel_mem_alloc2 _malloc2
#define osel_mem_free2 _free2
#define osel_mstrlen _mstrlen
static inline void *_malloc(uint32_t size)
{
return mymalloc(SRAMIN, size);
}
static inline void _free(void *ptr)
{
myfree(SRAMIN, ptr);
}
static inline void *_malloc2(uint32_t size)
{
return mymalloc(SRAMEX, size);
}
static inline void _free2(void *ptr)
{
myfree(SRAMEX, ptr);
}
/**
* @brief Fills a block of memory with a given value.
*
* @param dst The destination block of memory.
* @param value The value to fill the memory with.
* @param size The size of the memory block, in bytes.
*/
static inline void _memset(uint8_t *dst, uint8_t value, uint16_t size)
{
while (size--)
{
*dst++ = value;
}
}
/**
* @brief Compares two blocks of memory for equality.
*
* @param[in] dst The first block of memory to compare.
* @param[in] src The second block of memory to compare.
* @param[in] size The number of bytes to compare.
*
* @return 0 if the blocks of memory are equal, -1 otherwise.
*/
static inline int8_t _memcmp(const uint8_t *dst, const uint8_t *src, uint16_t size)
{
while (size--)
{
if (*dst++ != *src++)
{
return -1;
}
}
return 0;
}
/**
* @brief Copies data from a source buffer to a destination buffer in a forward direction.
*
* @param dst The destination buffer.
* @param src The source buffer.
* @param size The number of bytes to copy.
*/
static inline void _memcpyL(uint8_t *dst, const uint8_t *src, uint16_t size)
{
while (size--)
{
*dst++ = *src++;
}
}
/**
* @brief Copies data from a source buffer to a destination buffer in reverse order.
*
* @param dst The destination buffer.
* @param src The source buffer.
* @param size The number of bytes to copy.
*/
static inline void _memcpyR(uint8_t *dst, const uint8_t *src, uint16_t size)
{
// dst is a pointer to the last byte of the destination buffer
// src is a pointer to the first byte of the source buffer
// size is the number of bytes to copy
// decrement the destination pointer by the size, since we want to write to the last byte of the buffer
dst = dst + (size - 1);
// loop through each byte in the buffer, copying from the source to the destination in reverse order
while (size--)
{
// write the next byte from the source to the destination
*dst-- = *src++;
}
}
/**
* @brief Reverses the order of bytes in a buffer
*
* @param buf The buffer to reverse
* @param len The length of the buffer, in bytes
*/
static inline void _reverse(uint8_t *buf, uint16_t len)
{
uint8_t temp = 0;
uint16_t i;
for (i = 0; i < len / 2; i++)
{
temp = buf[i];
buf[i] = buf[len - i - 1];
buf[len - i - 1] = temp;
}
}
/**
* @brief Returns the length of a null-terminated string
*
* @param s The string to measure
* @return The length of the string, not including the null terminator
*/
static inline unsigned int _mstrlen(const unsigned char *s)
{
const unsigned char *ss = s;
while (*ss)
ss++;
return ss - s;
}
#endif // __OSEL_ARCH_H__

169
lib/inc/pbuf.h Normal file
View File

@ -0,0 +1,169 @@
/***
* @Author:
* @Date: 2023-04-04 10:06:40
* @LastEditors: xxx
* @LastEditTime: 2023-04-04 13:21:27
* @Description:
* @email:
* @Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef COMPONENTS_COMMON_INCLUDE_PBUF_H_
#define COMPONENTS_COMMON_INCLUDE_PBUF_H_
#include "../inc/data_type_def.h"
#include "../inc/mlist.h"
#include "../inc/malloc.h"
#define PBUF_DBG_EN (1u)
#define PBUF_TYPE_MAX_NUM (3u)
#define PBUF_NUM_MAX (10u)
// 如果size>254,并使用data_analysis接收数据需要修改data_analysis.c中的DATA_BUF_RECV_SQQ_LEN
#define SMALL_PBUF_BUFFER_SIZE (32)
#define MEDIUM_PBUF_BUFFER_SIZE (32 * 2)
#define LARGE_PBUF_BUFFER_SIZE (32 * 4)
#define SMALL_PBUF_NUM (4u) // 各种PBUF最大个数
#define MEDIUM_PBUF_NUM (4u)
#define LARGE_PBUF_NUM (4u)
#if PBUF_DBG_EN > 0
/*形参*/
#define _PLINE1_ , uint16_t line
#define _PLINE2_ , uint16_t line
/*实参*/
#define __PLINE1 , __LINE__
#define __PLINE2 , __LINE__
#else
#define _PLINE1_
#define _PLINE2_
#define __PLINE1
#define __PLINE2
#endif
enum _PBUF_TYPE
{
SMALL_PBUF,
MEDIUM_PBUF,
LARGE_PBUF,
PBUF_TYPE_INVALID
};
typedef struct __send_times_t
{
uint8_t app_send_times;
uint8_t mac_send_times;
} send_times_t;
typedef struct
{
int8_t rssi_dbm;
uint8_t seq;
nwk_id_t src_id; // 接收到数据帧时,为同步模块提供同步对象信息;
nwk_id_t dst_id; // 填写帧的目的节点网络地址
uint8_t send_mode : 2,
is_ack : 1,
need_ack : 1,
crc_ok : 1,
is_pending : 1,
debug_info : 1,
reserved : 1;
send_times_t already_send_times;
} pkt_attri_t;
typedef struct
{
struct list_head list;
uint8_t *data_p; // 指向数据区
uint8_t *head; // 指向数据区的第一个字节
uint8_t *end; // 指向数据区的最后一个字节
uint16_t data_len; // 该pbuf的实际数据长度
pkt_attri_t attri;
bool used;
#if PBUF_DBG_EN > 0
uint16_t alloc_line;
uint16_t free_line;
#endif
} pbuf_t;
/**
* pbuf_initz: pbuf申请一块内存区域pbuf的大小和数量等
*/
void pbuf_initz(void);
/**
* pbuf
*
* @param size:
* @param _PLINE1_: pbuf_allocz()__PLINE1
*
* @return: pbuf的指针
*/
extern pbuf_t *pbuf_allocz(uint16_t size _PLINE1_);
/**
* 使pbuf
*
* @param pbuf: pbuf的指针的指针
* @param _PLINE2_: pbuf_freez()__PLINE2
*
* @return:
*/
void pbuf_freez(pbuf_t **const pbuf _PLINE2_);
/**
* pbuf->end方向移动pbuf->data_p指针len
*
* @param pbuf: pbuf的指针
* @param len: data_p需要移动的距离
*
* @return: data_p指针NULL
*/
extern uint8_t *pbuf_skip_datap_forward(pbuf_t *const pbuf,
uint8_t len);
/**
* pbuf->head方向移动pbuf->data_p指针len
*
* @param pbuf: pbuf的指针
* @param len: data_p需要移动的距离
*
* @return: data_p指针NULL
*/
extern uint8_t *pbuf_skip_datap_backward(pbuf_t *const pbuf,
uint8_t len);
/**
* pbuf的数据区拷贝数据data_p指针data_len
*
* @param pbuf: pbuf的指针(pbuf->data_p开始拷贝)
* @param src:
* @param len:
*
* @return: TRUE, FALSE
*/
extern bool pbuf_copy_data_in(pbuf_t *const pbuf,
const uint8_t *const src,
uint8_t len);
/**
* pbuf的数据区拷贝数据data_p指针data_len
*
* @param dst:
* @param pbuf: pbuf的指针(pbuf->data_p开始拷贝)
* @param len:
*
* @return: TRUE,
*/
extern bool pbuf_copy_data_out(uint8_t *const dst,
pbuf_t *const pbuf,
uint8_t len);
#endif /* COMPONENTS_COMMON_INCLUDE_PBUF_H_ */

52
lib/inc/sqqueue.h Normal file
View File

@ -0,0 +1,52 @@
/**
* @file sqqueue.h
* @author xxx
* @date 2023-06-25 13:07:02
* @brief
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#ifndef __SQQUEUE_H
#define __SQQUEUE_H
#include "data_type_def.h"
typedef struct _sqqueue_t
{
uint8_t *base; // 队列存储元素的首地址
uint8_t entry_size; // 队列元素的宽度
uint16_t sqq_len; // 队列总长
uint16_t front; // 队列头下标
uint16_t rear; // 队列尾下标
} sqqueue_t;
/**
*
*
* ()
*/
typedef struct _sqqueue_ctrl_t
{
sqqueue_t sqq;
BOOL(*enter)
(struct _sqqueue_ctrl_t *const p_this, const void *const e); // 单元素入队列
BOOL(*string_enter)
(struct _sqqueue_ctrl_t *const p_this, const void *const string, uint16_t len); // 多元素入队列
void *(*del)(struct _sqqueue_ctrl_t *const p_this); // 出队列
void *(*revoke)(struct _sqqueue_ctrl_t *const p_this); // 撤销入队列
uint16_t (*get_len)(const struct _sqqueue_ctrl_t *const p_this); // 取队列长度
BOOL(*full)
(const struct _sqqueue_ctrl_t *const p_this); // 判满
void (*clear_sqq)(struct _sqqueue_ctrl_t *const p_this); // 清空队列
void (*traverse)(struct _sqqueue_ctrl_t *const p_this, void (*vi)(const void *e)); // 遍历
void (*remove)(struct _sqqueue_ctrl_t *const p_this, uint16_t location); // 删除指定位置
} sqqueue_ctrl_t;
BOOL sqqueue_ctrl_init(sqqueue_ctrl_t *const p_this,
uint8_t entry_size,
uint16_t sqq_len); ///< 初始化
void sqqueue_ctrl_dinit(sqqueue_ctrl_t *const p_this); ///< 销毁
#endif
/**
* @}
*/

52
lib/inc/storage.h Normal file
View File

@ -0,0 +1,52 @@
/**
* @file storage.h
* @author xushenghao
* @date 2024-11-15 15:57:02
* @brief
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
#ifndef __STORAGE_H__
#define __STORAGE_H__
#include "lib.h"
typedef struct
{
uint16_t index;
uint16_t size;
uint32_t address;
} storage_node_t;
typedef struct
{
clist_node_t *head;
struct
{
uint32_t base_addr; ///< 存储器基地址
uint16_t page_size; ///< 存储器页大小
uint16_t variable_count; ///< 存储器变量数量
uint16_t variable_size; ///< 存储器变量占用大小,如果变量大小不够一页剩余部分则写入下一页,上一页剩余字节计入变量占用大小
uint16_t page_count; ///< 变量占用存储器页数
} params;
struct
{
BOOL(*read)
(uint32_t addr, uint8_t *buf, uint16_t size);
BOOL(*write)
(uint32_t addr, uint8_t * buf, uint16_t size);
BOOL(*erase_page)
(uint32_t page);
} ops;
} storage_t;
storage_t *storage_init(uint32_t base_addr, uint16_t page_size); ///< 初始化存储器
void storage_destroy(storage_t *storage); ///< 销毁存储器
void storage_add_node(storage_t *storage, uint16_t index, uint16_t size); ///< 添加存储节点
BOOL storage_write(storage_t *storage, uint16_t index, const uint8_t *buf); ///< 存储数据
BOOL storage_read(storage_t *storage, uint16_t index, uint8_t *buf); ///< 读取数据
BOOL storage_write_all(storage_t *storage, const uint8_t *buf); ///< 存储所有数据
BOOL storage_read_all(storage_t *storage, uint8_t *buf); ///< 读取所有数据
BOOL storage_check(storage_t *storage, uint16_t index, uint8_t *buf); ///< 检查存储数据
BOOL storage_check_all(storage_t *storage, uint8_t *buf); ///< 检查所有存储数据
#endif // __STORAGE_H__

108
lib/inc/wl_flash.h Normal file
View File

@ -0,0 +1,108 @@
/**
* @file wl_flash.h
* @author xushenghao
* @date 2024-07-22 13:57:02
* @brief
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
*/
/**
* @brief
*
* 1. block_page大小和当前写入地址
* 2.
* a.
* b.
* i. 使block_pageblock_page继续写入
* ii. block_pageblock_page是否已满
* - block_page
* - block_page是否有有效数据需要迁移
* 3. block_page前Wear Leveling算法选择最佳的block_page进行擦除
* 4.
*/
/**
* 使
*
#define LL_FLASH_PAGE_SIZE (2 * 1024)
#define PVD_RESET_STORAGE_START_ADDRESS (256 * LL_FLASH_PAGE_SIZE)
static void test_wl_flash(void)
{
uint32_t address = 0;
wl_flash_t wf = {
.wl_flag = TRUE,
.addr = PVD_RESET_STORAGE_START_ADDRESS,
.len = 2 * LL_FLASH_PAGE_SIZE,
.page_size = LL_FLASH_PAGE_SIZE,
.data_size = sizeof(device_reset_t),
.write_gran = 8,
.ops.srand = board_srand,
.ops.read = flash_read,
.ops.write = flash_write,
.ops.erase_page = flash_erase_page,
};
wl_flash_init(&wf);
wl_flash_erase(&wf);
address = wl_flash_get_current_address(&wf);
device_reset_t power_on;
power_on.flag = PVD_RESET_FLAG;
// 一页2048/16 = 128个数据,2页 = 256个数据i=255时数据从头开始
for (uint16_t i = 0; i < 1000; i++)
{
wl_flash_write(&wf, (uint8_t *)&power_on, sizeof(device_reset_t));
wl_flash_set_next_address(&wf);
address = wl_flash_get_current_address(&wf);
if (address == wf.addr)
{
__NOP();
}
}
}
*/
#ifndef __WL_FLASH_H__
#define __WL_FLASH_H__
#include "lib.h"
typedef struct
{
BOOL wl_flag; // 开启平衡擦写标志
uint32_t addr; // Flash 起始地址
uint32_t len; // Flash 长度
uint16_t page_size; // Flash 页大小 2^N
uint16_t data_size; // 数据大小
/* write minimum granularity, unit: byte.
1(stm32f2/f4)/ 4(stm32f1)/ 8(stm32l4)
0 will not take effect. */
uint8_t write_gran;
struct
{
void (*srand)(void); // 随机数种子
BOOL(*read)
(uint32_t addr, uint8_t *buf, uint16_t size);
BOOL(*write)
(uint32_t addr, const uint8_t *buf, uint16_t size);
BOOL(*erase_page)
(uint32_t page);
} ops;
struct
{
uint32_t current_address; // 当前准备写入的地址
uint16_t residue_size; // 剩余可写大小
uint16_t start_page; // 起始页
uint16_t page_total; // 总页数
uint16_t block_page; // 擦除块大小
} private;
} wl_flash_t;
extern void wl_flash_init(wl_flash_t *cfg); ///< 初始化 Flash 磨损平衡算法
extern uint32_t wl_flash_get_current_address(wl_flash_t *cfg); ///< 获取当前写入地址
extern void wl_flash_set_next_address(wl_flash_t *cfg); ///< 设置下一个写入地址,下一个写入区域会被擦除
extern void wl_flash_set_current_address(wl_flash_t *cfg, uint32_t address); ///< 设置当前写入地址
extern void wl_flash_erase(wl_flash_t *cfg); ///< 擦除 Flash, 如果开启了平衡擦写标志, 则会随机分配写入地址
extern BOOL wl_flash_write(wl_flash_t *cfg, const uint8_t *buf, uint16_t size); ///< 写入 Flash
extern BOOL wl_flash_read(wl_flash_t *cfg, uint8_t *buf, uint16_t size); ///< 读取 Flash
#endif // __WL_FLASH_H__

905
lib/menu/menu.c Normal file
View File

@ -0,0 +1,905 @@
#include "menu.h"
#include "menus_main.h"
#include "entity.h"
#include <string.h>
#include <stdio.h>
#include "convert.h"
#ifdef _COT_MENU_USE_MALLOC_
#include <malloc.h>
#endif
#ifdef _COT_MENU_USE_SHORTCUT_
#include <stdarg.h>
#endif
/* private typedef ---------------------------------------------------------------------------------------------------*/
typedef struct menu_ctrl
{
uint16_t parent_window_no; /*!< 父菜单的窗口号 */
struct menu_ctrl *p_parent_menu_ctrl; /*!< 父菜单控制处理 */
char *(psz_desc[MENU_SUPPORT_LANGUAGE]); /*!< 当前选项的字符串描述(多语种) */
showmenu_call_fun_f pfn_show_menu_fun; /*!< 当前菜单显示效果函数 */
menu_list_t *p_menu_list; /*!< 当前菜单列表 */
menu_call_fun_f pfn_load_call_fun; /*!< 当前菜单加载函数 */
menu_call_fun_f pfn_run_call_fun; /*!< 当前选项的非菜单功能函数 */
menusize_t items_num; /*!< 当前菜单选项总数目 */
menusize_t show_base_item; /*!< 当前菜单首个显示的选项 */
menusize_t select_item; /*!< 当前菜单选中的选项 */
BOOL is_selected; /*!< 菜单选项是否已经被选择 */
} menu_ctrl_t;
typedef struct
{
menu_ctrl_t *p_menu_ctrl; /*!< 当前菜单控制处理 */
menu_call_fun_f pfn_main_enter_call_fun; /*!< 主菜单进入时(进入菜单)需要执行一次的函数 */
menu_call_fun_f pfn_main_exit_call_fun; /*!< 主菜单进入后退出时(退出菜单)需要执行一次的函数 */
menu_call_fun_f pfn_load_call_fun; /*!< 重加载函数 */
uint8_t language; /*!< 语种选择 */
BOOL is_enter_main_menu : TRUE; /*!< 是否进入了主菜单 */
} menu_manage_t;
/* private define ----------------------------------------------------------------------------------------------------*/
/* private macro -----------------------------------------------------------------------------------------------------*/
/* private variables -------------------------------------------------------------------------------------------------*/
static menu_manage_t sg_t_menu_manage;
#ifndef _menu_use_malloc_
static menu_ctrl_t sg_arr_menu_ctrl[MENU_MAX_DEPTH];
#endif
static uint8_t sg_curr_menu_depth = 0;
/* private function prototypes ---------------------------------------------------------------------------------------*/
static menu_ctrl_t *new_menu(void);
static void delete_menu(menu_ctrl_t *p_menu);
/* private function --------------------------------------------------------------------------------------------------*/
/**
* @brief
*
* @return menu_ctrl_t*
*/
static menu_ctrl_t *new_menu(void)
{
menu_ctrl_t *p_menu_ctrl = NULL;
if (sg_curr_menu_depth < MENU_MAX_DEPTH)
{
#ifdef _menu_use_malloc_
p_menu_ctrl = (menu_ctrl_t *)malloc(sizeof(menu_ctrl_t));
#else
p_menu_ctrl = &sg_arr_menu_ctrl[sg_curr_menu_depth];
#endif
sg_curr_menu_depth++;
}
return p_menu_ctrl;
}
/**
* @brief
*
* @param p_menu
*/
static void delete_menu(menu_ctrl_t *p_menu)
{
#ifdef _menu_use_malloc_
free(p_menu);
#endif
if (sg_curr_menu_depth > 0)
{
sg_curr_menu_depth--;
}
}
/**
* @brief
* @return {*}
* @note
*/
BOOL menu_unbind(void)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL)
{
return FALSE;
}
delete_menu(sg_t_menu_manage.p_menu_ctrl);
return TRUE;
}
/**
* @brief
*
* @param[in] p_main_menu
* @return TRUE:FALSE:
*/
BOOL menu_init(const main_menu_cfg_t *p_main_menu)
{
int i;
menu_ctrl_t *p_new_menu_ctrl = NULL;
if (sg_t_menu_manage.p_menu_ctrl != NULL)
{
return FALSE;
}
#if MENU_MAX_DEPTH != 0
sg_curr_menu_depth = 0;
#endif
if ((p_new_menu_ctrl = new_menu()) == NULL)
{
return FALSE;
}
sg_t_menu_manage.language = 0;
for (i = 0; i < MENU_SUPPORT_LANGUAGE; i++)
{
p_new_menu_ctrl->psz_desc[i] = (char *)p_main_menu->psz_desc[i];
}
p_new_menu_ctrl->p_parent_menu_ctrl = NULL;
p_new_menu_ctrl->pfn_load_call_fun = p_main_menu->pfn_load_call_fun;
p_new_menu_ctrl->pfn_show_menu_fun = NULL;
p_new_menu_ctrl->pfn_run_call_fun = p_main_menu->pfn_run_call_fun;
p_new_menu_ctrl->p_menu_list = NULL;
p_new_menu_ctrl->items_num = 0;
p_new_menu_ctrl->select_item = 0;
p_new_menu_ctrl->show_base_item = 0;
sg_t_menu_manage.p_menu_ctrl = p_new_menu_ctrl;
sg_t_menu_manage.is_enter_main_menu = 0;
sg_t_menu_manage.pfn_main_enter_call_fun = p_main_menu->pfn_enter_call_fun;
sg_t_menu_manage.pfn_main_exit_call_fun = p_main_menu->pfn_exit_call_fun;
sg_t_menu_manage.pfn_load_call_fun = p_new_menu_ctrl->pfn_load_call_fun;
return TRUE;
}
/**
* @brief
*
* @attention 退退退
* @return TRUE:FALSE:
*/
BOOL menu_de_init(void)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL)
{
return FALSE;
}
menu_main_exit();
delete_menu(sg_t_menu_manage.p_menu_ctrl);
sg_t_menu_manage.p_menu_ctrl = NULL;
sg_t_menu_manage.language = 0;
sg_t_menu_manage.is_enter_main_menu = 0;
sg_t_menu_manage.pfn_main_enter_call_fun = NULL;
sg_t_menu_manage.pfn_main_exit_call_fun = NULL;
sg_t_menu_manage.pfn_load_call_fun = NULL;
return TRUE;
}
/**
* @brief
*
* @param parent_window_no
* @param p_menu_list
* @param menu_num
* @param pfn_show_menu_fun , NULL则延续上级菜单显示效果
* @return BOOL
*/
BOOL menu_bind(uint16_t parent_window_no, const menu_list_t *p_menu_list, menusize_t menu_num, showmenu_call_fun_f pfn_show_menu_fun)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL)
{
return FALSE;
}
if (sg_t_menu_manage.p_menu_ctrl->p_menu_list != NULL)
{
return TRUE;
}
sg_t_menu_manage.p_menu_ctrl->p_menu_list = (menu_list_t *)p_menu_list;
sg_t_menu_manage.p_menu_ctrl->items_num = menu_num;
sg_t_menu_manage.p_menu_ctrl->parent_window_no = parent_window_no;
sg_t_menu_manage.p_menu_ctrl->select_item = 0;
if (pfn_show_menu_fun != NULL)
{
sg_t_menu_manage.p_menu_ctrl->pfn_show_menu_fun = pfn_show_menu_fun;
}
return TRUE;
}
/**
* @brief
*
* @param[in] language_idx
* @return TRUE:FALSE:
*/
BOOL menu_select_language(uint8_t language_idx)
{
if (MENU_SUPPORT_LANGUAGE <= language_idx)
{
return FALSE;
}
sg_t_menu_manage.language = language_idx;
return TRUE;
}
/**
* @brief ,
*
* @note 退
* @return TRUE:FALSE:
*/
BOOL menu_reset(void)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
while (sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl != NULL)
{
menu_ctrl_t *p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl;
sg_t_menu_manage.p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl;
delete_menu(p_menu_ctrl);
}
sg_t_menu_manage.p_menu_ctrl->select_item = 0;
return TRUE;
}
/**
* @brief
*
* @return TRUE:FALSE:
*/
BOOL menu_main_enter(void)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 1)
{
return FALSE;
}
if (sg_t_menu_manage.pfn_main_enter_call_fun != NULL)
{
sg_t_menu_manage.pfn_main_enter_call_fun();
}
sg_t_menu_manage.is_enter_main_menu = 1;
sg_t_menu_manage.pfn_load_call_fun = sg_t_menu_manage.p_menu_ctrl->pfn_load_call_fun;
return TRUE;
}
/**
* @brief 退
*
* @attention 退退退
* @return TRUE:FALSE:
*/
BOOL menu_main_exit(void)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
while (menu_exit(1) == 0)
{
}
if (sg_t_menu_manage.pfn_main_exit_call_fun != NULL)
{
sg_t_menu_manage.pfn_main_exit_call_fun();
}
sg_t_menu_manage.is_enter_main_menu = 0;
return TRUE;
}
/**
* @brief
*
* @param[in] p != NULL
* @return TRUE:FALSE:
*/
BOOL menu_enter(void *p)
{
int i;
menu_ctrl_t *p_new_menu_ctrl = NULL;
menu_ctrl_t *p_curr_menu_ctrl = sg_t_menu_manage.p_menu_ctrl;
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
if ((p_new_menu_ctrl = new_menu()) == NULL)
{
return FALSE;
}
for (i = 0; i < MENU_SUPPORT_LANGUAGE; i++)
{
p_new_menu_ctrl->psz_desc[i] = (char *)p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].psz_desc[i];
}
p_new_menu_ctrl->p_menu_list = NULL;
p_new_menu_ctrl->items_num = 0;
p_new_menu_ctrl->pfn_show_menu_fun = p_curr_menu_ctrl->pfn_show_menu_fun;
p_new_menu_ctrl->pfn_load_call_fun = p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_load_call_fun;
p_new_menu_ctrl->pfn_run_call_fun = p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_run_call_fun;
p_new_menu_ctrl->select_item = 0;
p_new_menu_ctrl->is_selected = TRUE;
p_new_menu_ctrl->p_parent_menu_ctrl = p_curr_menu_ctrl;
sg_t_menu_manage.p_menu_ctrl = p_new_menu_ctrl;
sg_t_menu_manage.pfn_load_call_fun = p_new_menu_ctrl->pfn_load_call_fun;
if (p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_enter_call_fun != NULL)
{
p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_enter_call_fun();
}
return TRUE;
}
/**
* @brief 退
*
* @param[in] is_reset
* @return TRUE:FALSE:, ,
*/
BOOL menu_exit(BOOL is_reset)
{
menu_ctrl_t *p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl;
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
if (sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl == NULL)
{
return FALSE;
}
sg_t_menu_manage.p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl;
sg_t_menu_manage.pfn_load_call_fun = sg_t_menu_manage.p_menu_ctrl->pfn_load_call_fun;
delete_menu(p_menu_ctrl);
p_menu_ctrl = NULL;
if (sg_t_menu_manage.p_menu_ctrl->p_menu_list[sg_t_menu_manage.p_menu_ctrl->select_item].pfn_exit_call_fun != NULL)
{
sg_t_menu_manage.p_menu_ctrl->is_selected = FALSE;
sg_t_menu_manage.p_menu_ctrl->p_menu_list[sg_t_menu_manage.p_menu_ctrl->select_item].pfn_exit_call_fun();
}
if (is_reset)
{
sg_t_menu_manage.p_menu_ctrl->select_item = 0;
}
return TRUE;
}
/**
* @brief
*
* @param[in] is_allow_roll
* @return TRUE:FALSE:
*/
BOOL menu_select_previous(BOOL is_allow_roll)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
if (sg_t_menu_manage.p_menu_ctrl->select_item > 0)
{
sg_t_menu_manage.p_menu_ctrl->select_item--;
}
else
{
if (is_allow_roll)
{
sg_t_menu_manage.p_menu_ctrl->select_item = sg_t_menu_manage.p_menu_ctrl->items_num - 1;
}
else
{
sg_t_menu_manage.p_menu_ctrl->select_item = 0;
return FALSE;
}
}
return TRUE;
}
/**
* @brief
* @param[in] show_num
* @return {*}
* @note
*/
BOOL menu_select_previous_page(uint8_t show_num)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
uint8_t page_no = sg_t_menu_manage.p_menu_ctrl->select_item / show_num;
if (page_no > 0)
{
sg_t_menu_manage.p_menu_ctrl->select_item = (page_no - 1) * show_num;
sg_t_menu_manage.p_menu_ctrl->show_base_item = 0;
}
else
{
sg_t_menu_manage.p_menu_ctrl->select_item = ((sg_t_menu_manage.p_menu_ctrl->items_num - 1) / show_num) * show_num;
sg_t_menu_manage.p_menu_ctrl->show_base_item = 0;
}
return TRUE;
}
/**
* @brief
*
* @param[in] is_allow_roll
* @return TRUE:FALSE:
*/
BOOL menu_select_next(BOOL is_allow_roll)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
if (sg_t_menu_manage.p_menu_ctrl->select_item < (sg_t_menu_manage.p_menu_ctrl->items_num - 1))
{
sg_t_menu_manage.p_menu_ctrl->select_item++;
}
else
{
if (is_allow_roll)
{
sg_t_menu_manage.p_menu_ctrl->select_item = 0;
}
else
{
sg_t_menu_manage.p_menu_ctrl->select_item = sg_t_menu_manage.p_menu_ctrl->items_num - 1;
return FALSE;
}
}
return TRUE;
}
/**
* @brief
* @param[in] show_num
* @return {*}
* @note
*/
BOOL menu_select_next_page(uint8_t show_num)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
uint8_t page_no = sg_t_menu_manage.p_menu_ctrl->select_item / show_num;
if (page_no < ((sg_t_menu_manage.p_menu_ctrl->items_num - 1) / show_num))
{
sg_t_menu_manage.p_menu_ctrl->select_item = (page_no + 1) * show_num;
sg_t_menu_manage.p_menu_ctrl->show_base_item = 0;
}
else
{
sg_t_menu_manage.p_menu_ctrl->select_item = 0;
sg_t_menu_manage.p_menu_ctrl->show_base_item = 0;
}
return TRUE;
}
/**
* @brief
*
* @param[in] index
* @return TRUE:FALSE:
*/
BOOL menu_jump_item(uint8_t index)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
sg_t_menu_manage.p_menu_ctrl->select_item = index;
sg_t_menu_manage.p_menu_ctrl->show_base_item = 0;
return TRUE;
}
#ifdef _menu_use_shortcut_
/**
* @brief
*
* @param[in] is_absolute
* @param[in] deep 0
* @param[in] ... (0), deep
* @return TRUE:FALSE:
*/
BOOL menu_shortcut_enter(BOOL is_absolute, uint8_t deep, ...)
{
uint8_t select_deep = 0;
va_list p_item_list;
menusize_t select_item;
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
if (is_absolute)
{
menu_reset();
}
va_start(p_item_list, deep);
while (select_deep < deep)
{
select_item = va_arg(p_item_list, int);
if (select_item >= sg_t_menu_manage.p_menu_ctrl->items_num)
{
va_end(p_item_list);
return FALSE;
}
sg_t_menu_manage.p_menu_ctrl->select_item = select_item;
menu_enter(NULL);
select_deep++;
}
va_end(p_item_list);
return TRUE;
}
#endif
/**
* @brief
*
* @note 使, 使 show_base_item
* @param[in,out] t_menu_show
* @param[in,out] show_num ,
* @return TRUE:FALSE:
*/
BOOL menu_limit_show_list_num(menu_show_t *pt_menu_show, menusize_t *p_show_num)
{
if (pt_menu_show == NULL || p_show_num == NULL)
{
return FALSE;
}
if (*p_show_num > pt_menu_show->items_num)
{
*p_show_num = pt_menu_show->items_num;
}
if (pt_menu_show->select_item < pt_menu_show->show_base_item)
{
pt_menu_show->show_base_item = pt_menu_show->select_item;
}
else if ((pt_menu_show->select_item - pt_menu_show->show_base_item) >= *p_show_num)
{
pt_menu_show->show_base_item = pt_menu_show->select_item - *p_show_num + 1;
}
else
{
// 保持
}
pt_menu_show->page_no = pt_menu_show->select_item / *p_show_num;
return TRUE;
}
/**
* @brief
* level 2
*
* @param[out] pt_menu_show n
* @param[in] level n , 0
* @return int
*/
BOOL menu_query_parent_menu(menu_show_t *pt_menu_show, uint8_t level)
{
int i;
menu_list_t *p_menu;
menu_ctrl_t *p_menu_ctrl = NULL;
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl;
while (level && p_menu_ctrl != NULL)
{
p_menu = p_menu_ctrl->p_menu_list;
pt_menu_show->items_num = p_menu_ctrl->items_num;
pt_menu_show->select_item = p_menu_ctrl->select_item;
pt_menu_show->show_base_item = p_menu_ctrl->show_base_item;
pt_menu_show->psz_desc = sg_t_menu_manage.p_menu_ctrl->psz_desc[sg_t_menu_manage.language];
for (i = 0; i < pt_menu_show->items_num && i < MENU_MAX_NUM; i++)
{
pt_menu_show->psz_items_desc[i] = (char *)p_menu[i].psz_desc[sg_t_menu_manage.language];
pt_menu_show->p_items_ex_data[i] = p_menu[i].p_extend_data;
}
p_menu_ctrl = p_menu_ctrl->p_parent_menu_ctrl;
level--;
}
if (level != 0 && p_menu_ctrl == NULL)
{
return FALSE;
}
return TRUE;
}
/**
* @brief
*
* @param[in] pt_show_info
* @return uint8_t
*/
uint8_t menu_psz_desc_max_size(menu_show_t *pt_show_info)
{
uint8_t i;
uint8_t max_size = 0;
if (pt_show_info == NULL)
{
return 0;
}
for (i = 0; i < pt_show_info->items_num; i++)
{
if (strlen(pt_show_info->psz_items_desc[i]) > max_size)
{
max_size = strlen(pt_show_info->psz_items_desc[i]);
}
}
return max_size;
}
/**
* @brief
* @param {char} *s
* @param {menu_txt_t} *m_txt
* @return {*}
* @note
*/
void menu_txt_show(char *buf, const menu_txt_t *m_txt)
{
DBG_ASSERT(buf != NULL __DBG_LINE);
DBG_ASSERT(m_txt != NULL __DBG_LINE);
sprintf(buf, "%s", m_txt->psz_desc[sg_t_menu_manage.language]);
}
/**
* @brief
*
* @param[in] no
*/
BOOL menu_enter_with_window_no(uint8_t no)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.p_menu_ctrl->items_num == 0)
{
return FALSE;
}
for (uint8_t i = 0; i < sg_t_menu_manage.p_menu_ctrl->items_num; i++)
{
if (sg_t_menu_manage.p_menu_ctrl->p_menu_list[i].window_no == no)
{
sg_t_menu_manage.p_menu_ctrl->select_item = i;
menu_enter(NULL);
return TRUE;
}
else
{
if (sg_t_menu_manage.p_menu_ctrl->p_menu_list[i].window_no != 0 &&
sg_t_menu_manage.p_menu_ctrl->p_menu_list[i].single_page != TRUE)
{
sg_t_menu_manage.p_menu_ctrl->select_item = i;
menu_enter(NULL);
if (menu_enter_with_window_no(no) == FALSE)
{
menu_exit(FALSE);
}
else
{
return TRUE;
}
}
}
}
return FALSE;
}
/**
* @brief
*
* @return uint16_t
*/
uint16_t menu_current_window_no(void)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL)
{
return 0;
}
return sg_t_menu_manage.p_menu_ctrl->p_menu_list[sg_t_menu_manage.p_menu_ctrl->select_item].window_no;
}
/**
* @brief
*
* @return uint16_t
*/
uint16_t menu_current_parent_window_no(void)
{
if (sg_t_menu_manage.p_menu_ctrl == NULL)
{
return 0;
}
return sg_t_menu_manage.p_menu_ctrl->parent_window_no;
}
/**
* @brief
*
* @param[out] info
* @return BOOL
*/
BOOL menu_get_parent_window_info(menu_show_t *info)
{
DBG_ASSERT(info != NULL __DBG_LINE);
int i;
menu_ctrl_t *p;
menu_list_t *p_menu_list;
if (sg_t_menu_manage.p_menu_ctrl != NULL && sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl != NULL)
{
p = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl;
p_menu_list = p->p_menu_list;
info->items_num = p->items_num;
info->select_item = p->select_item;
info->show_base_item = p->show_base_item;
info->psz_desc = p->psz_desc[sg_t_menu_manage.language];
if (p_menu_list != NULL)
{
for (i = 0; i < info->items_num && i < MENU_MAX_NUM; i++)
{
info->psz_items_desc[i] = (char *)p_menu_list[i].psz_desc[sg_t_menu_manage.language];
}
}
return TRUE;
}
else
{
return FALSE;
}
}
/**
* @brief
*
* @param[out] info
* @return BOOL
*/
BOOL menu_get_current_window_info(menu_show_t *info)
{
DBG_ASSERT(info != NULL __DBG_LINE);
int i;
menu_list_t *p_menu_list;
if (sg_t_menu_manage.p_menu_ctrl != NULL)
{
p_menu_list = sg_t_menu_manage.p_menu_ctrl->p_menu_list;
info->items_num = sg_t_menu_manage.p_menu_ctrl->items_num;
info->select_item = sg_t_menu_manage.p_menu_ctrl->select_item;
info->show_base_item = sg_t_menu_manage.p_menu_ctrl->show_base_item;
info->psz_desc = sg_t_menu_manage.p_menu_ctrl->psz_desc[sg_t_menu_manage.language];
if (p_menu_list != NULL)
{
for (i = 0; i < info->items_num && i < MENU_MAX_NUM; i++)
{
info->psz_items_desc[i] = (char *)p_menu_list[i].psz_desc[sg_t_menu_manage.language];
info->p_items_ex_data[i] = p_menu_list[i].p_extend_data;
}
}
return TRUE;
}
else
{
return FALSE;
}
}
/**
* @brief
*
* @return 0,, ; -1,,
*/
BOOL menu_task(void)
{
int i;
menu_list_t *p_menu_list;
menu_show_t t_menu_show;
if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0)
{
return FALSE;
}
if (sg_t_menu_manage.pfn_load_call_fun != NULL)
{
sg_t_menu_manage.pfn_load_call_fun();
sg_t_menu_manage.pfn_load_call_fun = NULL;
}
if (sg_t_menu_manage.p_menu_ctrl->p_menu_list != NULL)
{
p_menu_list = sg_t_menu_manage.p_menu_ctrl->p_menu_list;
t_menu_show.items_num = sg_t_menu_manage.p_menu_ctrl->items_num;
t_menu_show.select_item = sg_t_menu_manage.p_menu_ctrl->select_item;
t_menu_show.show_base_item = sg_t_menu_manage.p_menu_ctrl->show_base_item;
t_menu_show.psz_desc = sg_t_menu_manage.p_menu_ctrl->psz_desc[sg_t_menu_manage.language];
for (i = 0; i < t_menu_show.items_num && i < MENU_MAX_NUM; i++)
{
t_menu_show.psz_items_desc[i] = (char *)p_menu_list[i].psz_desc[sg_t_menu_manage.language];
t_menu_show.p_items_ex_data[i] = p_menu_list[i].p_extend_data;
}
if (sg_t_menu_manage.p_menu_ctrl->pfn_show_menu_fun != NULL)
{
sg_t_menu_manage.p_menu_ctrl->pfn_show_menu_fun(&t_menu_show);
}
sg_t_menu_manage.p_menu_ctrl->show_base_item = t_menu_show.show_base_item;
}
if (sg_t_menu_manage.p_menu_ctrl->pfn_run_call_fun != NULL)
{
sg_t_menu_manage.p_menu_ctrl->pfn_run_call_fun();
}
return TRUE;
}

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