first commit
This commit is contained in:
commit
5401ebeb61
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -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);
|
||||
}
|
|
@ -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__
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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__
|
|
@ -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中。地址和数据以字节为单位,长度以字节为单位。在写入数据之前,需要先解锁EEPROM,写入数据,然后锁定EEPROM。
|
||||
*/
|
||||
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
|
|
@ -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__
|
|
@ -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****/
|
File diff suppressed because it is too large
Load Diff
|
@ -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引脚当前状态,即0表示低电平,1表示高电平
|
||||
* @note: 用于读取指定GPIO引脚的状态,即返回0或1。
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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__
|
|
@ -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} - 等待成功返回TRUE,否则返回FALSE
|
||||
* @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总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为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总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为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结构体定义了start、stop、wait_ack、read_byte、write_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是否为空,如果不为空,则释放所有的GPIO。最后,释放handle的内存。
|
||||
*/
|
||||
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 此方法是一个简单的延迟函数,它使用循环来执行指定数量的NOP(无操作)指令。这个延迟函数的目的是在程序的执行中引入延迟,这在需要精确定时的某些应用中很有用。延迟时间由输入参数“count”的值决定,该参数指定要执行的NOP指令的数量。延迟时间通常以微秒或毫秒为单位测量,具体取决于使用延迟的环境。
|
||||
* @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和scl、sda的指针。接着重置sda,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。
|
||||
*/
|
||||
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和scl、sda的指针。接着设置sda为1,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。
|
||||
*/
|
||||
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);
|
||||
}
|
|
@ -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__
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
#include "pwms.h"
|
|
@ -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 定时器类型,例如TIM1、TIM2等
|
||||
* @param CHx 通道号,例如TIM_CHANNEL_1、TIM_CHANNEL_2等
|
||||
* @param DUTY 占空比,范围在0到100之间
|
||||
*/
|
||||
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__
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -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设置为FALSE,并将spix的地址赋值给handle->spix。最后,调用SPI_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大于0。接着,将SPI总线的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 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为高
|
||||
* @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 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为低
|
||||
* @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 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为高
|
||||
* @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 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为低
|
||||
* @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 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高
|
||||
* @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 用于将SPI(串行外围接口)设备的输出(MOSI)引脚设置为低
|
||||
* @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 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高
|
||||
* @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 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为低
|
||||
* @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 用于读取SPI(串行外围接口)设备的输入(MISO)引脚的电平
|
||||
* @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);
|
||||
}
|
|
@ -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__
|
|
@ -0,0 +1 @@
|
|||
#include "tims.h"
|
|
@ -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__
|
|
@ -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为NULL,则返回0
|
||||
*/
|
||||
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);
|
||||
}
|
|
@ -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__
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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. 通过某种通信接口(如USART,I2C,SPI)接收新的用户程序代码。
|
||||
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`文件还可能包含一些辅助函数,用于处理如超时、缓冲区管理等问题。
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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__
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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 不符合函数定义,也无法区分,改为 2,by 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
|
||||
/*
|
||||
以下循环完成,对回溯次数的输入缓存进行判断,如果输入值均大于或小于缓存值,则确定此次为峰值。
|
||||
峰值特征根据 isMax、isMin 哪个为真确定。
|
||||
同时完成输入缓存向后平移,腾出第一个单元存放新的输入值。
|
||||
这一段代码完成的噪声所产生的虚假峰值判断,应该没有问题!
|
||||
*/
|
||||
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 次中的最大或最小值。
|
||||
但逐句消化程序后,发现这段处理有几点疑惑:
|
||||
1、peaks[] 的纪录好像不对,在执行最小到最大值转换时,peakCount 也应该+1,否则应该把
|
||||
纪录的最小值覆盖了!所以后面的峰值判断总是满足条件。
|
||||
2、峰值对应时间似乎也应该多次存放,取平均值,因对象没有那么理想化,目前应该是取的最后一组峰值的周期。
|
||||
3、后续计算 Ku 用的是整个整定过程的最大、最小值,这对于非理想的对象而言也不是很合适。
|
||||
|
||||
考虑做如下改进:
|
||||
1)修改峰值纪录,设计12个峰值保存单元,存满12个峰值(6大、6小)后再计算。
|
||||
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)不再判断是否合理,因为对象如果特性好,自然已经稳定,如果不好,再长时间也无效果。
|
||||
3)将后面5次的数据作为素材,去掉第一组数据,因为考虑第一组时对象可能处于过渡过程。
|
||||
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;
|
||||
}
|
|
@ -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.1,范围0-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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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控制器。
|
|
@ -0,0 +1 @@
|
|||
https://www.cnblogs.com/foxclever/p/16299063.html
|
|
@ -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_SYM、ONE_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 附加的标签,8位。tag = 0时,写DAC寄存器;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);
|
||||
}
|
|
@ -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
|
|
@ -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的当前状态。该函数始终返回TRUE,表示EEPROM正常工作。
|
||||
*
|
||||
* @return BOOL 始终返回TRUE
|
||||
*/
|
||||
BOOL eeprom_fm24_status_get(void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
|
@ -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__
|
|
@ -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处于正常状态,则返回TRUE;否则返回FALSE。
|
||||
* 注意:在本实现中,总是返回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);
|
||||
}
|
|
@ -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
|
|
@ -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处于写保护状态,则返回FALSE;否则返回TRUE。
|
||||
*/
|
||||
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模块编号(0或1)
|
||||
* @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模块编号(0或1)
|
||||
* @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设备正常工作,返回TRUE;否则返回FALSE
|
||||
*/
|
||||
BOOL eeprom_m95_status_get(m95_number_e num)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -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 用于检查VLF,寄存器地址:0x1e bit[1]
|
||||
* @return {uint8_t} 0 = VLF位为0,1 = 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设备中获取当前的日期和时间,并将其转换为自1970年1月1日以来的秒数(时间戳)。
|
||||
* 如果无法从RTC设备中获取时间,则返回0。
|
||||
*
|
||||
* @return 返回从1970年1月1日以来的秒数(时间戳),如果无法获取时间则返回0。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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__
|
|
@ -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;
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
/**
|
||||
* 停止之前的步骤执行超时检测。
|
||||
* 0x0:正常时间:2^20个时钟
|
||||
* 0x1:短时间:2^18个时钟
|
||||
*/
|
||||
uint32_t fast_standstill : 1;
|
||||
/**
|
||||
* 启用StealthChop2模式。
|
||||
* 0x0:无StealthChop2
|
||||
* 0x1:StealthChop2电压PWM模式使能(取决于速度阈值)。从关闭状态切换
|
||||
在静止状态下和在lHOLD = 时为开状态 仅限额定电流。
|
||||
*/
|
||||
uint32_t en_pwm_mode : 1;
|
||||
/**
|
||||
* 启用StealthChop2的步进输入筛选
|
||||
* 0x0:无StealthChop2
|
||||
* 0x1:StealthChop2电压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:正常运行
|
||||
* 0x1:紧急停止:ENCA停止定序器
|
||||
当绑得很高时(不执行任何步骤
|
||||
定序器、电机进入停顿状态)。
|
||||
*/
|
||||
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-255)与静止状态下的全电机电流(CS_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
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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。
|
|
@ -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;
|
||||
}
|
|
@ -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))
|
||||
|
||||
/**
|
||||
* 给进程加锁,时长为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(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);
|
||||
|
||||
/**
|
||||
* 检测一个软件定时器是否超时,0为不超时,1为超时
|
||||
*/
|
||||
unsigned char fl_timer_timeout(struct flow_timer *t);
|
||||
|
||||
/**
|
||||
* 检测一个软件定时器还剩多少时间超时,单位为硬件tick,比如硬件tick 500ms中断一次,那么
|
||||
* 返回的剩余时间就是500ms*n
|
||||
*/
|
||||
unsigned long fl_hour_much_time(struct flow_timer *t);
|
||||
|
||||
#endif /* __FLOW_ */
|
|
@ -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} 超时返回1,否则返回0
|
||||
* @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_tick,则返回time_len - flow_tick,否则返回0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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__ */
|
|
@ -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
|
|
@ -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
|
|
@ -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_ */
|
|
@ -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
|
|
@ -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_ */
|
|
@ -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; 提取第2位(从0开始计数) <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_ */
|
|
@ -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
|
|
@ -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__
|
|
@ -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__
|
|
@ -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
|
|
@ -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_
|
|
@ -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 // 内存表大小
|
||||
|
||||
// 内存管理控制器结构体
|
||||
// 注意:内存管理由内存池和内存列表组成
|
||||
// SRAMBANK:SARM块数,一般有内部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
|
|
@ -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 为空时返回TRUE,否则为FALSE
|
||||
*/
|
||||
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
|
||||
/**
|
||||
* @}
|
||||
*/
|
|
@ -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__
|
|
@ -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_ */
|
|
@ -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
|
||||
/**
|
||||
* @}
|
||||
*/
|
|
@ -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__
|
|
@ -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_page,如果有,移动到该block_page继续写入
|
||||
* ii. 如果没有已擦除的block_page,检查当前block_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__
|
|
@ -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
Loading…
Reference in New Issue