408 lines
11 KiB
C
408 lines
11 KiB
C
/**
|
||
* @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 "sys.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, uint8_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 ADC重新开始转换
|
||
* @param {adcs_e} num ADC编号
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
void adc_restart(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_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 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
|
||
*/
|
||
uint16_t adc_result_median_average(adcs_e num, uint8_t chan)
|
||
{
|
||
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
|
||
adcs_t *p = &adcs[num];
|
||
DBG_ASSERT(p != NULL __DBG_LINE);
|
||
|
||
if (p->adc_cct <= 2)
|
||
return 0; // 如果adc_cct小于等于2,直接返回0
|
||
|
||
uint16_t adc_temp[p->adc_cct];
|
||
uint32_t adc_sum = 0;
|
||
uint8_t count = p->adc_cct >> 2; // 使用位移操作计算n的值
|
||
|
||
// 减少重复计算,计算基础偏移量
|
||
uint16_t *adc_values = (uint16_t *)p->adc_value + chan;
|
||
for (uint8_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 (uint8_t i = count; i < p->adc_cct - count; ++i)
|
||
{
|
||
adc_sum += adc_temp[i];
|
||
}
|
||
|
||
// 计算平均值,确保不会除以0
|
||
uint16_t res = adc_sum / (p->adc_cct - (count << 1));
|
||
return res;
|
||
}
|
||
|
||
/**
|
||
* @brief 中位值滤波,获取ADC转换结果
|
||
* @param {adcs_e} num ADC编号
|
||
* @param {uint8_t} chan 存储数据所在的序列号
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
uint16_t adc_result_median(adcs_e num, uint8_t chan)
|
||
{
|
||
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
|
||
uint16_t res = 0;
|
||
adcs_t *p = &adcs[num];
|
||
DBG_ASSERT(p != NULL __DBG_LINE);
|
||
uint16_t adc_temp[p->adc_cct];
|
||
// 减少重复计算,计算基础偏移量
|
||
uint16_t *adc_values = (uint16_t *)p->adc_value + chan;
|
||
for (uint8_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count)
|
||
{
|
||
adc_temp[i] = *adc_values;
|
||
}
|
||
insertion_sort(adc_temp, p->adc_cct);
|
||
res = adc_temp[p->adc_cct >> 1];
|
||
return res;
|
||
}
|
||
|
||
/**
|
||
* @brief 平均值,获取ADC转换结果
|
||
* @param {adcs_e} num ADC编号
|
||
* @param {uint8_t} chan 存储数据所在的序列号
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
uint16_t adc_result_average(adcs_e num, uint8_t chan)
|
||
{
|
||
DBG_ASSERT(num < ADCS_MAX __DBG_LINE);
|
||
uint32_t adc_sum = 0;
|
||
adcs_t *p = &adcs[num];
|
||
DBG_ASSERT(p != NULL __DBG_LINE);
|
||
// 减少重复计算,计算基础偏移量
|
||
uint16_t *adc_values = (uint16_t *)p->adc_value + chan;
|
||
for (uint8_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count)
|
||
{
|
||
adc_sum += *adc_values;
|
||
}
|
||
|
||
return adc_sum / p->adc_cct;
|
||
}
|
||
|
||
/**
|
||
* @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
|
||
*/
|
||
float32 adc_result_temperature(uint16_t adc_value)
|
||
{
|
||
#define TEMP130_CAL_ADDR ((uint16_t *)((uint32_t)0x1FFF75A8))
|
||
#define TEMP30_CAL_ADDR ((uint16_t *)((uint32_t)0x1FFF75CA))
|
||
#define VDD_CALIB ((uint16_t)(300))
|
||
#define VDD_APPLI ((uint16_t)(330))
|
||
float32 res;
|
||
res = ((adc_value * VDD_APPLI / VDD_CALIB) -
|
||
(int32_t)*TEMP30_CAL_ADDR);
|
||
res = res * (int32_t)(130 - 30);
|
||
res = res / (int32_t)(*TEMP130_CAL_ADDR -
|
||
*TEMP30_CAL_ADDR);
|
||
res = res + 30;
|
||
return res * 0.1f;
|
||
}
|
||
|
||
/**
|
||
* @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, INVREF))
|
||
{
|
||
ch_num++;
|
||
}
|
||
if (BIT_IS_SET(channnels, INVBAT))
|
||
{
|
||
ch_num++;
|
||
}
|
||
if (BIT_IS_SET(channnels, INTEMP))
|
||
{
|
||
ch_num++;
|
||
}
|
||
return ch_num;
|
||
}
|