newPtz/drivers/drv_adc.c

378 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/************************************************************
Copyright (C), 2025, cerlink Tech. Co., Ltd.
FileName: drv_adc.c
Author: dufresne Version : 1.0 Date:2025.09.15
Description: // 模块描述
Version: // 版本信息
Function List: // 主要函数及其功能
1. -------
History: // 历史修改记录
<author> <time> <version > <desc>
David 96/10/12 1.0 build this moudle
***********************************************************/
#include "drv_adc.h"
#ifdef DMAX
/*全局声明区*/
uint16_t USER_ADC_DMA_DATA_BUFF[16] = {0};
#endif
float adc1_v[LB_V_TIMES];
float curadc1_out_v;
uint8_t adc1_v_num = 0;
float adc1_i[LB_I_TIMES];
float curadc1_out_i;
uint8_t adc1_i_num = 0;
float tmp75[LB_T_TIMES];
float curtmp75_out;
uint8_t tmp75_num;
bool filter_v_flag = 0;//滤波开启标志位,全局改变一次
bool filter_i_flag = 0;//滤波开启标志位,全局改变一次
bool filter_t_flag = 0;//滤波开启标志位,全局改变一次
/* ---------------------------------------------------配置---------------------------------------------------- */
void adc_init(void)
{
adc_rcu_config();
adc_gpio_config();
#ifdef DMAX
adc_dma_config();
#endif
adc_config();
#ifdef ADCX_IRQn
adc_interrupt_int();
#endif
}
/* 时钟+GPIO */
void adc_rcu_config(void)
{
/* 启用ADC1时钟 */
rcu_periph_clock_enable(RCU_ADC1);
#ifdef DMAX
/* peripheral clock enable */
rcu_periph_clock_enable(RCU_DMA1);//ADC只能用DMA1
#endif
/* 启用GPIOC时钟 */
rcu_periph_clock_enable(RCU_GPIOC);
adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
}
void adc_gpio_config(void)
{
/* 配置ADC引脚为模拟输入模式 */
gpio_mode_set(ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, VOLTAGE_ADC_PIN);
gpio_mode_set(ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, CURRENT_ADC_PIN);
}
#ifdef DMAX
/* DMA配置 */
void adc_dma_config(void)
{
/* ADC_DMA_channel configuration */
dma_single_data_parameter_struct dma_single_data_parameter; //这里使用单数据模式,每次只传输一个数据单元,因为总数据量不大,且数据个数不固定
/* ADC DMA_channel configuration */
dma_deinit (DMA1, USER_DMA_ADC_CHANNEL);
/* initialize DMA single data mode */
dma_single_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADCX)); //配置外设数据源地址宏决定ADCx
dma_single_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; //关闭外设增量,只需要每次对该数据源寄存器访问
dma_single_data_parameter.memory0_addr = (uint32_t)USER_ADC_DMA_DATA_BUFF; //先写死然后外部用的话需要extern
dma_single_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //开启内存增量以存放不同通道的ADC数据
dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT; //ADC数据为16位的寄存器数据目的地为16位
dma_single_data_parameter.circular_mode = DMA_CIRCULAR_MODE_ENABLE; //循环模式由ADC通知DMA进行
dma_single_data_parameter.direction = DMA_PERIPH_TO_MEMORY; //ADC外设到RAM全局变量 DMA外设到内存模式开启
dma_single_data_parameter.number = CHANNEL_LENGTH; //宏决定传输个数
dma_single_data_parameter.priority = DMA_PRIORITY_ULTRA_HIGH; //优先级设为超高,可改用宏
dma_single_data_mode_init (DMAX, USER_DMA_ADC_CHANNEL, &dma_single_data_parameter);
/* 配置 dma 子连接 */
dma_channel_subperipheral_select (DMAX, USER_DMA_ADC_CHANNEL, USER_DMA_ADC_SUBPERI);
/* enable DMA circulation mode */
dma_circulation_enable (DMAX, USER_DMA_ADC_CHANNEL);// 开启循环模式
/* enable DMA it */
dma_interrupt_enable (DMAX,USER_DMA_ADC_CHANNEL,DMA_INT_FTF);//DMA传输完成标志位 打开全部完成中断 4
// dma_interrupt_enable (DMAX,USER_DMA_ADC_CHANNEL,DMA_INT_TAE);//传输错误中断使能位 2
// dma_interrupt_enable (DMAX,USER_DMA_ADC_CHANNEL,DMA_INT_FEE);// 7
nvic_irq_enable (DMA_ADC_IRQn, DMA_ADC_PRIORITY_PRE, DMA_ADC_PRIORITY_SUB);
dma_channel_enable (DMAX, USER_DMA_ADC_CHANNEL);// enable DMA channel
}
/**
* @brief 根据转换序列的序列号获取转换序列中的ADC测量值
* @param 配置ADC通道结构体中的成员序号
* @return 测量并转换后的值
*/
uint16_t BSP_ADCDataAcquire(uint8_t _index)
{
return USER_ADC_DMA_DATA_BUFF[_index];
}
/*
*****************************************************************************
* 函数名 : DMA1_Channel0_IRQHandler
* 功能说明: DMA_ADC中断服务回调函数
* 形 参 : 无
* 返回值 : 无
*****************************************************************************
*/
__weak void DMA1_Channel0_IRQHandler(void)
{
}
#endif
void adc_config(void)
{
adc_deinit();// 复位ADC配置可选但建议初始化时做一次
adc_special_function_config (ADCX, ADC_SCAN_MODE, SCAN_STATUS);// 扫描模式
adc_special_function_config (ADCX, ADC_CONTINUOUS_MODE, CONTINUOUS_STATUS);// 连续模式
adc_data_alignment_config (ADCX, ADC_DATAALIGN_RIGHT);// 配置数据对齐方式为右对齐
adc_resolution_config (ADCX, ADC_RESOLUTION_12B);// 配置ADC分辨率12位
/* 设置转换通道序列 */
adc_channel_length_config (ADCX, SEQUENCE_CHANNEL, CHANNEL_LENGTH);// 配置规则序列的长度2个通道
if (SEQUENCE_CHANNEL == ADC_INSERTED_CHANNEL)
{
//通道注入顺序
/* 配置规则序列序号0是电压通道序号1是电流通道 */
adc_inserted_channel_config (ADCX, 0, VOLTAGE_ADC_CHANNEL, ADC_SAMPLETIME_480);//电压
adc_inserted_channel_config (ADCX, 1, CURRENT_ADC_CHANNEL, ADC_SAMPLETIME_480);//电流
}
#ifdef DMAX
adc_routine_channel_config(ADCX, 0, VOLTAGE_ADC_CHANNEL, ADC_SAMPLETIME_480);
adc_routine_channel_config(ADCX, 1, CURRENT_ADC_CHANNEL, ADC_SAMPLETIME_480);
#endif
/* 使能外部触发:这里使用软件触发,所以先禁用硬件触发 */
// adc_external_trigger_source_config(ADCX, SEQUENCE_CHANNEL, ADC_EXTTRIG_ROUTINE_T0_CH0);
adc_external_trigger_config (ADCX, SEQUENCE_CHANNEL, DISABLE);
#ifdef DMAX
/* ADC dma config */
adc_dma_request_after_last_enable (ADCX);
adc_dma_mode_enable (ADCX);
#endif
adc_enable (ADCX); // 开启ADC
// rt_thread_mdelay(50); // 等待ADC稳定后续接校准
delay_ms(50);
adc_calibration_enable (ADCX); // 执行ADC自校准
#ifdef DMAX
adc_software_trigger_enable (ADCX, SEQUENCE_CHANNEL); // 软件触发使能 后续接读取
#endif
}
#ifdef ADCX_IRQn
void adc_interrupt_int(void)
{
adc_interrupt_enable (ADCX, ADC_INT_EOC);
nvic_irq_enable (ADCX_IRQn, ADCX_PRIORITY_PRE, ADCX_PRIORITY_SUB);
}
#endif
/* ---------------------------------------------------采集---------------------------------------------------- */
// adc电压采集
float ptz_Voltage_collect_adc1_task()
{
uint16_t value_V = 0;
#ifdef ADC_MODE_0
/* 模式1需先配置 */
adc_routine_channel_config(ADCX, 0, VOLTAGE_ADC_CHANNEL, ADC_SAMPLETIME_480);
#endif
#if (defined ADC_MODE_0) || (defined ADC_MODE_1)
/* 模式1 + 2 */
adc_software_trigger_enable(ADCX, SEQUENCE_CHANNEL);// 软件触发使能 后续接读取
#endif
#ifdef ADC_MODE_0
while(adc_flag_get(ADCX, ADC_FLAG_EOC) == RESET); // 等待转换结束
adc_flag_clear(ADCX, ADC_FLAG_EOC);
value_V = adc_routine_data_read(ADCX); // 读取规则组数据寄存器
#endif
#ifdef ADC_MODE_1
while(adc_flag_get(ADCX, ADC_FLAG_EOIC) == RESET); // 等待转换结束
adc_flag_clear(ADCX, ADC_FLAG_EOIC);
value_V = adc_inserted_data_read(ADCX, ADC_INSERTED_CHANNEL_0); // 读取注入组数据寄存器
#endif
#ifdef ADC_MODE_3
value_V = BSP_ADCDataAcquire(0);
#endif
/* 间接测量11倍分压/放大 */
adc1_v[adc1_v_num] = (float)value_V / 4095.0 * 3.3;
adc1_v_num++;
if(adc1_v_num >= LB_V_TIMES)
{
adc1_v_num = 0;
filter_v_flag = 1;
}
if (filter_v_flag)//等读满一组开始滤波
{
return Filtering(adc1_v, LB_V_TIMES, LB_V_DEL);
}
return 0;
}
// adc电流采集
float ptz_Current_collect_adc1_task()
{
uint16_t value_I = 0;
#ifdef ADC_MODE_0
/* 模式1需先配置 */
adc_routine_channel_config(ADCX, 0, CURRENT_ADC_CHANNEL, ADC_SAMPLETIME_480);
#endif
#if (defined ADC_MODE_0) || (defined ADC_MODE_1)
/* 模式1 + 2 */
adc_software_trigger_enable(ADCX, SEQUENCE_CHANNEL);// 软件触发使能 后续接读取
#endif
#ifdef ADC_MODE_0
while(adc_flag_get(ADCX, ADC_FLAG_EOC) == RESET); // 等待转换结束
adc_flag_clear(ADCX, ADC_FLAG_EOC);
value_I = adc_routine_data_read(ADCX); // 读取规则组数据寄存器
#endif
#ifdef ADC_MODE_1
while(adc_flag_get(ADCX, ADC_FLAG_EOIC) == RESET); // 等待转换结束
adc_flag_clear(ADCX, ADC_FLAG_EOIC);
value_I = adc_inserted_data_read(ADCX, ADC_INSERTED_CHANNEL_1); // 读取注入组数据寄存器
#endif
#ifdef ADC_MODE_3
value_I = BSP_ADCDataAcquire(1);
#endif
/* 间接测量11倍分压/放大 */
adc1_i[adc1_i_num] = (((float)value_I / 4095.0 * 3.3) - 3.3 / 2) / 0.132;
adc1_i_num++;
if(adc1_i_num >= LB_I_TIMES)
{
adc1_i_num = 0;
filter_i_flag = 1;
}
if (filter_i_flag)
{
return Filtering(adc1_i, LB_I_TIMES, LB_I_DEL);
}
return 0;
}
/* 温度采集 */
float ptz_temperature_collect_tmp75_task()
{
#ifdef HARDWARE_I2C
uint8_t tempHL[2];
uint16_t tempCode = 0;
float temp = 0;
uint8_t temp_addr[1] = {TEMP_REGISTER_ADDRESS};
i2c_write(temp_addr, 1, TMP75_ADDRESS);
i2c_read(tempHL, 2, TMP75_ADDRESS);
tempCode = (tempHL[0] << 8) | tempHL[1];
tempCode = tempCode >> 6;
if (tempCode & 0x200) // 负温度
{
tempCode &= 0x1ff;
temp = ((float)tempCode - 512) / 4;
}
else
{
temp = (float)tempCode / 4;
}
tmp75[tmp75_num] = temp;
#endif
#ifdef SOFTWARE_I2C
tmp75[tmp75_num] = tmp75_read_temp();
#endif
tmp75_num ++;
if(tmp75_num >= LB_T_TIMES)
{
tmp75_num = 0;
filter_t_flag = 1;
}
if (filter_t_flag)
{
return Filtering(tmp75, LB_T_TIMES, LB_T_DEL);
}
return 0;
}
/* 滤波函数 */
float Filtering(float *filter, uint8_t filterlens, uint8_t filterdel)
{
uint8_t j,k;
float tem;
float curadc = 0;
for(j = 0; j < filterlens - 1; j++)// 采样值由小到大排列
{
for(k = 0; k < filterlens - j - 1; k++)
{
if(filter[k] > filter[k + 1])
{
tem = filter[k];
filter[k] = filter[k + 1];
filter[k + 1] = tem;
}
}
}
for(uint8_t i = filterdel; i < filterlens - filterdel; i++)
{
curadc = curadc + filter[i];
}
curadc = curadc / ((float)(filterlens - filterdel * 2));// 去掉一个最大值和一个最小值求平均值
return curadc;
}