目录
- 1. GD32H7xx ADC
- 1.1 ADC外设资源
- 1.2 采样时间
- 1.3 片上硬件过采样
- 2. ADC DMA规则多通道采样程序
- 3. 程序测试
1. GD32H7xx ADC
1.1 ADC外设资源
GD32H7xx 有3个ADC外设:
- ADC0
- 20个外部通道,1个内部通道(DAC0_OUT0通道)
- 32位数据寄存器
- 可配置14位、12位、10位或8位分辨率
- 14位满量程对应转换值:16383
- 12位满量程对应转换值:4096
- 采样值 = 实际电压 / 参考电压 * 16383
- 采样率:分辨率越低转换越快。 MSPs:(Million Samples Per Second)每秒百万次采样
- 14位:4MSPs
- 12位:4.5 MSPs
- 10位:5.14 MSPs
- 8位:6 MSPs
- 时钟最大频率:72MHz
- 过采样:
- 32位的数据寄存器
- 可调整过采样率: 2~1024
- 高达11位的可编程数据移位
- ADC1
- 18个外部通道,3个内部通道(电池电压Vbat、参考电压输入Vrefint、DAC0_OUT1通道)
- 32位数据寄存器
- 可配置14位、12位、10位或8位分辨率
- 采样率与ADC0相同
- 时钟最大频率:72MHz
- 过采样特性与ADC0相同
- ADC2
- 17个外部通道,4个内部通道(电池电压Vbat、参考电压输入Vrefint、内部温度传感通道Vsense、高精度温度传感器通道Vsense2)
- 16位数据寄存器
- 可配置12位、10位、8位或6位分辨率
- 采样率
- 12位:5.3 MSPs
- 10位:6.15 MSPs
- 8位:7.27 MSPs
- 6位:8.89 MSPs
- 时钟最大频率:80MHz
- 过采样:
- 16位的数据寄存器
- 可调整过采样率: 2~256
- 高达8位的可编程数据移位
1.2 采样时间
ADC使用若干个CK_ADC周期对输入电压采样,采样周期数目通过ADC_REQ0~ADC_RSQ8寄存器的RSMPn[9:0]位进行设置。采样时间寄存器如下图所示:
说明:
1.10’d0 : 表示 10位宽的数值0
2.根据寄存器描述的规律:采样时间 = 设置值 + 3.5 周期
从User Manual可知,总转换时间 = 采样时间 + 12.5个CK_ADC周期
举例说明:
当MCU的时钟频率为600MHz, HCLK = 300MHz,ADC时钟 = HCLK/6 = 50MHz
若设置的采样时间值为240
总转换时间 = (240 + 3.5)周期 + 12.5 周期 = 256 周期 = 256/50MHz = 5.12us
采样一次的时间为 5.12us。
1.3 片上硬件过采样
作用:片上硬件过采样单元可以执行数据预处理以减轻CPU负担。能够处理多个转换并将多个转换的结果进行平均。
14位ADC,片上过采样单元执行2个功能:求和、位右移。过采样率: 2~1024倍。除法系数(即右移位数)最大11位。求和单元能够生成一个多达24位的值。
说明:
1.1024 * 2的14次幂(14位ADC)= 2的24次幂 即 24位的值。 这个结果首先右移然后将数据存储到寄存器中。
2.数据每右移1位,相当于除以2。 16倍过采样率即是 2的4次方,即需要右移4bit。
3.和标准的转换模式相比,过采样转换的采样时间不会改变。
接口函数介绍
/*!
\brief configure ADC 过采样
\param[in] adc_periph: ADCx, x=0,1,2
\param[in] mode: ADC 过采样模式
only one parameter can be selected which is shown as below:
\arg ADC_OVERSAMPLING_ALL_CONVERT: 通道的所有过采样转换都是在触发后连续完成的
\arg ADC_OVERSAMPLING_ONE_CONVERT: 通道的每个过采样转换都需要一个触发
\param[in] shift: ADC oversampling shift
only one parameter can be selected which is shown as below:
\arg ADC_OVERSAMPLING_SHIFT_NONE: no oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_1B: 1-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_2B: 2-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_3B: 3-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_4B: 4-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_5B: 5-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_6B: 6-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_7B: 7-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_8B: 8-bit oversampling shift
\arg ADC_OVERSAMPLING_SHIFT_9B: 9-bit oversampling shift, available for ADC0/ADC1
\arg ADC_OVERSAMPLING_SHIFT_10B: 10-bit oversampling shift, available for ADC0/ADC1
\arg ADC_OVERSAMPLING_SHIFT_11B: 11-bit oversampling shift, available for ADC0/ADC1
\param[in] ratio: ADC oversampling ratio, 0..1023 for ADC0/ADC1, 0..255 for ADC2
\param[out] none
\retval none
*/
void adc_oversample_mode_config(uint32_t adc_periph, uint32_t mode, uint16_t shift, uint16_t ratio)
/*!
\brief enable ADC oversample mode
\param[in] adc_periph: ADCx, x=0,1,2
\param[out] none
\retval none
*/
void adc_oversample_mode_enable(uint32_t adc_periph)
{
ADC_OVSAMPCTL(adc_periph) |= (uint32_t)ADC_OVSAMPCTL_OVSEN;
}
/*!
\brief disable ADC oversample mode
\param[in] adc_periph: ADCx, x=0,1,2
\param[out] none
\retval none
*/
void adc_oversample_mode_disable(uint32_t adc_periph)
{
ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)ADC_OVSAMPCTL_OVSEN);
}
2. ADC DMA规则多通道采样程序
usr_adc.h
#ifndef _USR_ADC_H_
#define _USR_ADC_H_
#include "gd32h7xx.h"
#include "cmsis_os2.h"
#include "rtx_os.h"
#include "user_log.h"
// ADC IO、Channel parameter
typedef struct
{
rcu_periph_enum rcu_port; // IO CLK
uint32_t port; // IO port
uint32_t pin; // IO pin
uint32_t gpio_speed; // IO speed
uint8_t adc_channel; // ADC channel
uint32_t sample_time; // 采样周期
}adc_ch_parameter;
typedef struct
{
rcu_periph_enum rcu_dma; // DMA CLK
uint32_t dma_periph; // DMA num
dma_channel_enum dma_channel; // DMA channel
uint32_t request;
uint32_t dma_number; // DMA传输个数
uint32_t dma_priority; // DMA通道优先级
EventStatus dma_circulation_mode; // 循环模式
}adc_dma_parameter;
typedef struct
{
rcu_periph_enum rcu_adc; // ADC CLK
uint32_t adc_psc; // ADC 时钟分频系数
uint32_t adc_port;
uint32_t adc_mode; // ADC work mode:ADC_MODE_FREE,ADC_DAUL_REGULAL_PARALLEL
uint8_t adc_channel_group; // ADC 工作组:规则组或注入组
EventStatus adc_scan_function; // 设置扫描模式
EventStatus adc_continuous_function;// 设置循环模式
uint32_t adc_external_trigger_mode;
uint8_t ch_count; // 设置转换通道个数
adc_dma_parameter dma_parameter; // dma 参数
uint32_t trigger_source; // ADC 触发源
EventStatus DMA_mode; // 是否使用DMA
}adc_ch_general;
/********************************************* Function *********************************************/
void usr_adc_test_thread(void);
void usr_adc_software_trigger_enable(adc_ch_general *ADC);
#endif
usr_adc.c
#include "usr_adc.h"
__attribute__((aligned(32))) uint16_t adc2_value[2];
__attribute__((aligned(32))) uint16_t adc0_value[2];
adc_ch_general adc0= {
.rcu_adc = RCU_ADC0,
.adc_psc = ADC_CLK_SYNC_HCLK_DIV6, // HCLK(300MHz) 6分频
.adc_port = ADC0,
.adc_mode = ADC_SYNC_MODE_INDEPENDENT, // 独立模式
.adc_channel_group = ADC_REGULAR_CHANNEL, // 规则组
.adc_scan_function = ENABLE, // 开启扫描模式
.adc_continuous_function = ENABLE, // 开启循环模式
.ch_count = 2, // 转换通道个数
.adc_external_trigger_mode = EXTERNAL_TRIGGER_DISABLE, // Disable外部触发
.dma_parameter =
{
.rcu_dma = RCU_DMA1, // DMA CLK
.dma_periph = DMA1, // 使用DMA1
.dma_channel = DMA_CH1, // DMA1 通道0
.dma_number = 2, // 传输长度
.request = DMA_REQUEST_ADC0,
.dma_priority = DMA_PRIORITY_HIGH, // 通道优先级
.dma_circulation_mode = ENABLE // DMA循环模式使能
},
.DMA_mode = ENABLE // 使能DMA
};
adc_ch_parameter adc0_ch[2] =
{
{
.rcu_port = RCU_GPIOC,
.port = GPIOC,
.pin = GPIO_PIN_0, // PC0
.gpio_speed = GPIO_OSPEED_12MHZ,
.adc_channel = ADC_CHANNEL_10,
.sample_time = 240 // 采样周期
}
,
{
.rcu_port = RCU_GPIOC,
.port = GPIOC,
.pin = GPIO_PIN_1, // PC1
.gpio_speed = GPIO_OSPEED_12MHZ,
.adc_channel = ADC_CHANNEL_11,
.sample_time = 240
}
};
adc_ch_general adc2= {
.rcu_adc = RCU_ADC2,
.adc_psc = ADC_CLK_SYNC_HCLK_DIV6, // HCLK(300MHz) 6分频
.adc_port = ADC2,
.adc_mode = ADC_SYNC_MODE_INDEPENDENT, // 独立模式
.adc_channel_group = ADC_REGULAR_CHANNEL, // 规则组
.adc_scan_function = ENABLE, // 开启扫描模式
.adc_continuous_function = ENABLE, // 开启循环模式
.ch_count = 2, // 转换通道个数
.adc_external_trigger_mode = EXTERNAL_TRIGGER_DISABLE, // Disable外部触发
.dma_parameter =
{
.rcu_dma = RCU_DMA1, // DMA CLK
.dma_periph = DMA1, // 使用DMA1
.dma_channel = DMA_CH0, // DMA1 通道0
.dma_number = 2, // 传输长度
.request = DMA_REQUEST_ADC2,
.dma_priority = DMA_PRIORITY_HIGH, // 通道优先级
.dma_circulation_mode = ENABLE // DMA循环模式使能
},
.DMA_mode = ENABLE // 使能DMA
};
adc_ch_parameter adc2_ch[2] =
{
{
.rcu_port = RCU_GPIOC,
.port = GPIOC,
.pin = GPIO_PIN_2, // PC2
.gpio_speed = GPIO_OSPEED_12MHZ,
.adc_channel = ADC_CHANNEL_0,
.sample_time = 240// 采样周期
}
,
{
.rcu_port = RCU_GPIOC,
.port = GPIOC,
.pin = GPIO_PIN_3, // PC3
.gpio_speed = GPIO_OSPEED_12MHZ,
.adc_channel = ADC_CHANNEL_1,
.sample_time = 240
}
};
/*
\brief adc_regular_ch_dma_config
\param[in] ADC:ADC para
ADC_CH: channel para
\retval none
*/
void driver_adc_config(adc_ch_general *ADC, adc_ch_parameter *ADC_CH)
{
uint8_t i;
adc_idx_enum idx_adc;
adc_deinit(ADC->adc_port);
/* ADC clock config */
if(ADC->adc_port==ADC0){
idx_adc=IDX_ADC0;
}else if(ADC->adc_port==ADC1){
idx_adc=IDX_ADC1;
}else{
idx_adc=IDX_ADC2;
}
rcu_adc_clock_config(idx_adc, RCU_ADCSRC_PER);
adc_clock_config(ADC->adc_port, ADC->adc_psc);
rcu_periph_clock_enable(ADC->rcu_adc);
for(i=0 ;i<ADC->ch_count; i++)
{
if(ADC_CH[i].adc_channel < ADC_CHANNEL_17)
{
rcu_periph_clock_enable(ADC_CH[i].rcu_port);
gpio_mode_set(ADC_CH[i].port, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, ADC_CH[i].pin);
}
else
{
if(ADC->adc_port==ADC2)
{
if(ADC_CH[i].adc_channel == ADC_CHANNEL_17)
{
adc_internal_channel_config(ADC_CHANNEL_INTERNAL_VBAT, ENABLE);
}
else if(ADC_CH[i].adc_channel == ADC_CHANNEL_18)
{
adc_internal_channel_config(ADC_CHANNEL_INTERNAL_TEMPSENSOR, ENABLE);
}
else if(ADC_CH[i].adc_channel == ADC_CHANNEL_19)
{
adc_internal_channel_config(ADC_CHANNEL_INTERNAL_VREFINT, ENABLE);
}
else if(ADC_CH[i].adc_channel == ADC_CHANNEL_20)
{
adc_internal_channel_config(ADC_CHANNEL_INTERNAL_HP_TEMPSENSOR, ENABLE);
}
}
else
{
rcu_periph_clock_enable(ADC_CH[i].rcu_port);
gpio_mode_set(ADC_CH[i].port, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, ADC_CH[i].pin);
}
}
}
adc_sync_mode_config(ADC->adc_mode);
adc_special_function_config(ADC->adc_port, ADC_SCAN_MODE, ADC->adc_scan_function);
if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL)
{
adc_special_function_config(ADC->adc_port, ADC_CONTINUOUS_MODE, ADC->adc_continuous_function);
}
adc_data_alignment_config(ADC->adc_port, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(ADC->adc_port, ADC->adc_channel_group, ADC->ch_count);
if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL)
{
for(i = 0;i< ADC->ch_count;i++)
{
adc_regular_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);
}
}
else if(ADC->adc_channel_group == ADC_INSERTED_CHANNEL)
{
for(i = 0;i< ADC->ch_count;i++)
{
adc_inserted_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);
}
}
adc_external_trigger_config(ADC->adc_port, ADC->adc_channel_group, ADC->adc_external_trigger_mode);
if(ADC->DMA_mode == ENABLE)
{
adc_dma_request_after_last_enable(ADC->adc_port);
adc_dma_mode_enable(ADC->adc_port);
}
adc_enable(ADC->adc_port);
osDelay(1);
adc_calibration_mode_config(ADC->adc_port, ADC_CALIBRATION_OFFSET_MISMATCH);
/* ADC calibration number config */
adc_calibration_number(ADC->adc_port, ADC_CALIBRATION_NUM32);
adc_calibration_enable(ADC->adc_port);
}
/*
\brief adc_regular_ch_dma_config
\param[in] ADC:ADC para
ADC_CH: channel para
buffer: ADC数据缓存buf
\retval none
*/
void adc_channel_dma_config(adc_ch_general *ADC, adc_ch_parameter *ADC_CH,void *buffer)
{
dma_single_data_parameter_struct dma_single_data_parameter;
rcu_periph_clock_enable(ADC->dma_parameter.rcu_dma);
rcu_periph_clock_enable(RCU_DMAMUX);
dma_deinit(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
dma_single_data_parameter.request = ADC->dma_parameter.request;
dma_single_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC->adc_port));
dma_single_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_single_data_parameter.memory0_addr = (uint32_t)(buffer);
dma_single_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
if(ADC->adc_mode == ADC_DAUL_REGULAL_PARALLEL)
{
dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
}
else
{
dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
}
dma_single_data_parameter.direction = DMA_PERIPH_TO_MEMORY;
dma_single_data_parameter.number = ADC->dma_parameter.dma_number;
dma_single_data_parameter.priority = ADC->dma_parameter.dma_priority;
dma_single_data_mode_init(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel, &dma_single_data_parameter);
if(ADC->dma_parameter.dma_circulation_mode == ENABLE)
{
dma_circulation_enable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
}
else
{
dma_circulation_disable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
}
dma_channel_enable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
driver_adc_config(ADC,ADC_CH);
// 过采样
#if 0
adc_oversample_mode_config(ADC->adc_port, ADC_OVERSAMPLING_ALL_CONVERT, ADC_OVERSAMPLING_SHIFT_4B, 16);
adc_oversample_mode_enable(ADC->adc_port);
#endif
}
/*
\brief adc_software_trigger_enable
\param[in] ADC:ADC para
\retval none
*/
void usr_adc_software_trigger_enable(adc_ch_general *ADC)
{
adc_software_trigger_enable(ADC->adc_port, ADC->adc_channel_group);
}
void usr_adc_init(void)
{
//adc_channel_dma_config(&adc2, adc2_ch, (uint16_t*)adc2_value);
//usr_adc_software_trigger_enable(&adc2);
adc_channel_dma_config(&adc0, adc0_ch, (uint16_t*)adc0_value);
usr_adc_software_trigger_enable(&adc0);
}
void usr_adc_test_thread(void)
{
LogI("%s run...\r\n",__FUNCTION__);
uint32_t ticks;
const uint16_t sleep_ms = 200;
uint32_t Vin1, Vin2;
usr_adc_init();
ticks = osKernelGetTickCount();
while(1)
{
SCB_InvalidateDCache_by_Addr ((uint32_t *)adc0_value, sizeof(adc0_value));
Vin1 = (3300*adc0_value[0]*2)/16383 - 3300;
Vin2 = (3300*adc0_value[1]*2)/16383 - 3300;
LogI("Vin1= [%d]mv, Vin2= [%d]mv, ADC0 val[0]= %d, val[1]= %d\r\n", Vin1, Vin2, adc0_value[0], adc0_value[1]);
ticks += sleep_ms;
osDelayUntil(ticks);
}
}
3. 程序测试
输入电压Vin 为2v,采样成功。
To Be Continue …