GD32F103VE使用DMA传输读取内部温度和参考电压值

GD32F103VE使用DMA传输读取内部温度和参考电压值。出厂CPU芯片不同,温度误差不同,不要纠结了。

GD32F103的ADC为12位AD转换器,ADC转换结果保存在16位数据寄存器中;

模拟输入通道:
16个外部模拟输入通道;
1个内部温度传感器通道(Vsense)
1个内部参考电压输入通道(VrefInt)

触发转换:
软件触发;
硬件触发;

转换模式:
单通道转换或多通道扫描转换;
单次模式,选择指定的模拟输入通道后,触发一次,则转换一次;
连续模式,选择指定的模拟输入通道后,连续进行AD转换;用于DMA
同步模式,当同时使用ADC0,ADC1和ADC2时,这3个设备就可以同时进行AD转换;
间断模式;

ADC的VDDA电源引脚需要接入3.3V;
ADC输入模拟量的范围:Vref- 至Vref+之间;

ADC_RSQ0~ADC_RSQ2寄存器规定了"规则组通道"的选择;
ADC_RSQ0的RL[3:0]用来指定"规则通道组"转换序列的长度,即"规则通道组"选择;
连续进行AD转换的通道总数为(RL[3:0]+1),在DMA传输中要知道有多少个通道参与AD转换

ADC_ISQ寄存器规定了"注入组通道"的选择;
ADC_ISQ0的IL[3:0]用来指定"整个注入通道组"转换序列的长度,即"规则通道组"选择
连续进行AD转换的通道总数为(IL[3:0]+1),在DMA传输中要知道有多少个通道参与AD转换

注意:ADC同时使用"规则组通道"和"注入组通道",则"注入组通道"的采样周期不能1.5和7.5个采样周期;

GD32F103的ADC0映射到DMA0的通道0
GD32F103的ADC2映射到DMA1的通道4
GD32F103的ADC1没有DMA功能
注意:在多通道AD转换中,DMA使用循环工作模式比较好;

ADC0_CH16为温度传感器通道,Internal temperature sensor output voltage;
ADC0_CH17为内部参考电压通道,Internal voltage reference output voltage;
Vref-引脚必须连接至VSSA引脚,Vref+连接到VDDA,但在64脚以下的CPU(含64脚),其Vref+和Vref-已经在内部连接至VDDA和VSSA;
注意:ST和GD的手册里都写着不同芯片最大偏差45度

#include "ADC.h"
#include "delay.h"
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()

/*
GD32F103的ADC为12位AD转换器,ADC转换结果保存在16位数据寄存器中;

模拟输入通道:
16个外部模拟输入通道;
1个内部温度传感器通道(Vsense)
1个内部参考电压输入通道(VrefInt)

触发转换:
软件触发;
硬件触发;

转换模式:
单通道转换或多通道扫描转换;
单次模式,选择指定的模拟输入通道后,触发一次,则转换一次;
连续模式,选择指定的模拟输入通道后,连续进行AD转换;用于DMA
同步模式,当同时使用ADC0,ADC1和ADC2时,这3个设备就可以同时进行AD转换;
间断模式;

ADC的VDDA电源引脚需要接入3.3V;
ADC输入模拟量的范围:Vref- 至Vref+之间;

ADC_RSQ0~ADC_RSQ2寄存器规定了"规则组通道"的选择;
ADC_RSQ0的RL[3:0]用来指定"规则通道组"转换序列的长度,即"规则通道组"选择;
连续进行AD转换的通道总数为(RL[3:0]+1),在DMA传输中要知道有多少个通道参与AD转换

ADC_ISQ寄存器规定了"注入组通道"的选择;
ADC_ISQ0的IL[3:0]用来指定"整个注入通道组"转换序列的长度,即"规则通道组"选择
连续进行AD转换的通道总数为(IL[3:0]+1),在DMA传输中要知道有多少个通道参与AD转换

注意:ADC同时使用"规则组通道"和"注入组通道",则"注入组通道"的采样周期不能1.5和7.5个采样周期;

GD32F103的ADC0映射到DMA0的通道0
GD32F103的ADC2映射到DMA1的通道4
GD32F103的ADC1没有DMA功能
注意:在多通道AD转换中,DMA使用循环工作模式比较好;

ADC0_CH16为温度传感器通道,Internal temperature sensor output voltage;
ADC0_CH17为内部参考电压通道,Internal voltage reference output voltage;
Vref-引脚必须连接至VSSA引脚,Vref+连接到VDDA,但在64脚以下的CPU(含64脚),其Vref+和Vref-已经在内部连接至VDDA和VSSA;
注意:ST和GD的手册里都写着不同芯片最大偏差45度
*/

#define ADC0_CH_Number   2
#define ADC0_cnt         20
#define ADC0_Buffer_Size 40
uint16_t ADC0_Buffer[ADC0_cnt][ADC0_CH_Number];

void DMA0_DMA_CH0_config(void);
void ADC0_Init(void);
void ADC0_To_DMA0_Init(void);
void Read_TemperatureValue(void);

void DMA0_DMA_CH0_config(void)
{
	dma_parameter_struct dma_init_ADC0;

	dma_deinit(DMA0,DMA_CH0);
	dma_init_ADC0.direction = DMA_PERIPHERAL_TO_MEMORY;//DMA传送方向:从外设到内存
	dma_init_ADC0.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));//源数据块首地址为:ADC0寄存器
	dma_init_ADC0.periph_inc = DMA_PERIPH_INCREASE_DISABLE;  //源数据块地址不递增
	dma_init_ADC0.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; //源数据块的数据宽度为16位

	dma_init_ADC0.memory_addr = (uint32_t)(&ADC0_Buffer);//目的数据首地址为:ADC0_Buffer[]
	dma_init_ADC0.memory_inc = DMA_MEMORY_INCREASE_ENABLE;//目的数据块地址递增
	dma_init_ADC0.memory_width = DMA_MEMORY_WIDTH_16BIT;//目的数据块的数据宽度为16位
	dma_init_ADC0.number = (uint32_t)ADC0_Buffer_Size;//目的数据块的数据长度
	
	dma_init_ADC0.priority = DMA_PRIORITY_ULTRA_HIGH;      //超高的优先级
	dma_init(DMA0,DMA_CH0,&dma_init_ADC0);//使用dma_init_struct数据结构初始化DMA0通道0
	
配置"DMA0通道5工作模式"开始///
	dma_circulation_enable(DMA0,DMA_CH0);
	//使能"DMA0通道0循环工作模式"
	//DMA0触发后,会按照指定的序号,循环触发读取ADC的值
	//在多通道AD转换中,DMA使用循环工作模式比较好
//	dma_circulation_disable(DMA0, DMA_CH0);
	//不使能"DMA0通道0循环工作模式"
	dma_memory_to_memory_disable(DMA0, DMA_CH0); //不使能"DMA0通道0内存到内存传输模式"
配置"DMA0通道5工作模式"结束///
///配置串口DMA接收开结束

  dma_channel_enable(DMA0,DMA_CH0);//使能指定的DMA0通道0	
}

void ADC0_Init(void)
{
	adc_deinit(ADC0);//reset ADC0

	adc_tempsensor_vrefint_enable();
	//channel 16 and 17 enable of ADC0 
	//使能温度传感器通道和内部参考电压通道

	adc_mode_config(ADC_MODE_FREE);//只使用一个ADC,属于独立模式
	adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE);
	//在多通道AD转换,要使用"扫描模式"
	adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,ENABLE);
	//设置AD转换为"连续模式"

	adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
	//使用软件触发AD转换,ADC trigger config

	adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);//ADC data alignment config


	adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL,2);
	//连续进行AD转换的通道总数为2,在DMA传输中要知道有多少个通道参与AD转换
  
	/* ADC regular channel config */
	adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_16, ADC_SAMPLETIME_71POINT5);
	//将ADC0中ADC_CHANNEL_16通道放在第0位置优先进行AD转换,采样时间为71.5个时钟周期
	//配置读取内部温度
	adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_17, ADC_SAMPLETIME_71POINT5);
	//将ADC0中ADC_CHANNEL_17通道放在第1位置优先进行AD转换,采样时间为71.5个时钟周期
	//配置读取内部参考电压

  adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
	//使用软件触发AD转换
	adc_external_trigger_config(ADC0,ADC_REGULAR_CHANNEL,ENABLE);
	//使能ADC规则通道组外部触发,enable ADC regular channel group external trigger

  adc_enable(ADC0);//使能ADC0
	delay_ms(10);     //等待10ms
	adc_calibration_enable(ADC0);//等待ADC0校准完成

	adc_dma_mode_enable(ADC0);//使能ADC转DMA功能,ADC DMA function enable

  adc_software_trigger_enable(ADC0,ADC_REGULAR_CHANNEL);
	//触发ADC0设备执行1次AD转换
	//由于前面配置"ADC0为连续转换模式",所以启动转换后,就可以连续执行AD转换了
}

void ADC0_To_DMA0_Init(void)
{
	rcu_periph_clock_enable(RCU_ADC0);//使能ADC0时钟
  rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
	//配置ADC时钟8分频
	//由于ADC最大时钟频率为14MHz,经过8分频后,变为1.75MHz

//	rcu_periph_clock_enable(RCU_GPIOA);//使能GPIOA端口时钟
//	rcu_periph_clock_enable(RCU_GPIOB);
//	rcu_periph_clock_enable(RCU_GPIOC);//使能GPIOC端口时钟
//	rcu_periph_clock_enable(RCU_GPIOD);
//	rcu_periph_clock_enable(RCU_GPIOE);
//	rcu_periph_clock_enable(RCU_GPIOF);
//	rcu_periph_clock_enable(RCU_GPIOG);

//	rcu_periph_clock_enable(RCU_AF);
	rcu_periph_clock_enable(RCU_DMA0);
//	rcu_periph_clock_enable(RCU_DMA1);
//	rcu_periph_clock_enable(RCU_I2C1);
//	rcu_periph_clock_enable(RCU_ADC0);
//	rcu_periph_clock_enable(RCU_ADC2);
//	rcu_periph_clock_enable(RCU_ADC1);//使能ADC1时钟
//  rcu_periph_clock_enable(RCU_TIMER1);

//	gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);
	//使用SW下载,不使用JTAG下载,管脚用作其它功能

//  gpio_init(GPIOA,GPIO_MODE_AIN,GPIO_OSPEED_50MHZ,GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6);
	//配置PA4,PA5和PA6引脚为模拟输入口
	//PA4复用到ADC01_IN4引脚,即ADC0/ADC1的第4通道
	//PA5复用到ADC01_IN5引脚,即ADC0/ADC1的第5通道
	//PA6复用到ADC01_IN6引脚,即ADC0/ADC1的第6通道
//	gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,GPIO_PIN_4);
//	//配置PA4引脚为模拟输入口,复用到ADC01_IN4引脚,即ADC0/ADC1的第4通道
//	gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,GPIO_PIN_5);
//	//配置PA5引脚为模拟输入口,复用到ADC01_IN5引脚,即ADC0/ADC1的第5通道
//	gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,GPIO_PIN_6);
//	//配置PA6引脚为模拟输入口,复用到ADC01_IN6引脚,即ADC0/ADC1的第6通道

//  gpio_init(GPIOC,GPIO_MODE_AIN,GPIO_OSPEED_50MHZ,GPIO_PIN_3 | GPIO_PIN_4);
	//配置PC3和PC4引脚为模拟输入口
	//PC3复用到ADC012_IN13引脚,即ADC0/ADC1/ADC2的第13通道
	//PC4复用到ADC01_IN14,即ADC0/ADC1的第14通道
//	gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,GPIO_PIN_3);
//	//配置PC3引脚为模拟输入口,复用到ADC012_IN13引脚,即ADC0/ADC1/ADC2的第13通道
//	gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,GPIO_PIN_4);
//	//配置PC4引脚为模拟输入口,复用到ADC01_IN14,即ADC0/ADC1的第14通道

  DMA0_DMA_CH0_config();
  ADC0_Init();
}

/*
温度(°C) = {(V25 - VSENSE) / Avg_Slope} + 25
V25 = VSENSE在25°C时的数值,这里取1.43V
Avg_Slope = 温度与VSENSE曲线的平均斜率,4.3mV/°C
即:(1.43 - voltage) / 0.0043 + 25;
ST和GD的手册里都写着不同芯片最大偏差45度,
*/
void Read_TemperatureValue(void)
{
	uint8_t i;
	uint32_t sum1,sum2;
	uint16_t *p;
	float f;

	sum1=0;sum2=0;
	p=&ADC0_Buffer[0][0];
	for(i=0;i
#ifndef __ADC_H
#define __ADC_H

#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t

#define V25       1.430   //V
#define AvgSlope  0.0043  //V/°C

extern void ADC0_To_DMA0_Init(void);
extern void Read_TemperatureValue(void);
#endif

main.c如下:
 

#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "delay.h"

#include "UART3.h"
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "ADC.h"

uint16_t volValue = 0;

const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
int main(void)
{
	//NVIC_PRIGROUP_PRE4_SUB0:抢占优先级为4bit(取值为0~15),子优先级为0bit(没有响应优先级)
	//NVIC_PRIGROUP_PRE3_SUB1:抢占优先级为3bit(取值为0~7),子优先级为1bit(取值为0~1)
	//NVIC_PRIGROUP_PRE2_SUB2:抢占优先级为2bit(取值为0~3),子优先级为2bit(取值为0~3)
	//NVIC_PRIGROUP_PRE1_SUB3:抢占优先级为1bit(取值为0~1),子优先级为3bit(取值为0~7)
	//NVIC_PRIGROUP_PRE0_SUB4:抢占优先级为0bit(没有抢占优先级),子优先级为3bit(取值为0~15)
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);//设置系统中断优先级"抢占优先级为4bit,子优先级为0bit"

  delay_init();
  INTX_ENABLE();//开启所有中断
	UART3_Init(115200);//初始化UART3
	printf("%s",CPU_Reset_REG);//调试串口输出"\r\nCPU reset!\r\n"

	ADC0_To_DMA0_Init();
	while(1)
	{
    delay_ms(500);
		Read_TemperatureValue();
	}
}

GD32F103VE使用DMA传输读取内部温度和参考电压值_第1张图片

 

你可能感兴趣的:(产品研发,GD32,ADC,DMA,嵌入式硬件)