1. 实验目的
使用STM32F4内部 DAC1来输出电压,通过 ADC1的通道5采集DAC的输出电压,并通过串口打印电压值。
2. 实验准备和流程
这里使用DAC的通道1,其对应的引脚是PA4。
注意这里的PA4引脚配置的是模拟输入,但是 STM32F4 内部会连接在 DAC模拟输出上。
这里Vref+连接VDDA,是3.3v,根据计算公式,可以求出输出电压对应的数字量的值。
具体的流程如下:
初始化DAC;
初始化ADC;
编写DAC 输入值函数;
编写ADC转换中断函数;
编写main函数。
2.1 初始化DAC
DAC有三种触发模式:不使用触发、软件触发、外部触发。这里使用的是不使用触发。
触发是指将数据寄存器DORx中的数据转换成模拟信号输出的信号。不使用触发模式,指的是在向DAC数据寄存器DHR写入数据之后,DAC转化模块自动转换一次;软件触发模式,指的是向软件触发寄存器SWTRIGR中写入命令时触发转换,也就是将DOR寄存器中的数据进行转换;外部触发模式,一般是定时器触发。
void DAC_Config_Init(void){ //PA4引脚
DAC_InitTypeDef DAC_InitStruct; //DAC结构体
GPIO_InitTypeDef GPIO_InitStruct; //GPIO结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); //使能DAC时钟
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN; //模拟输入
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
GPIO_Init(GPIOA, &GPIO_InitStruct);
DAC_InitStruct.DAC_Trigger = DAC_Trigger_None; //不使用触发功能
DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生
DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; //用来设置屏蔽/幅值选择器,没有使用波形发生器,这里不使用
DAC_InitStruct.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //不使用输出缓存
DAC_Init(DAC_Channel_1, &DAC_InitStruct); //初始化DAC
DAC_Cmd(DAC_Channel_1,ENABLE); //使能DAC通道1
DAC_SetChannel1Data(DAC_Align_12b_R,0); //12位右对齐数据格式设置DAC值,初始化设置为0
}
2.2 初始化ADC
//初始化GPIO 设置ADC1的通道5来进行AD转换的,是PA5引脚
static void ADCx_Config(void){
GPIO_InitTypeDef GPIO_InitStructure; //GPIO初始化结构体
ADC_CommonInitTypeDef ADC_CommonInitStructure; //初始化CCR寄存器结构体
ADC_InitTypeDef ADC_InitStructure; //初始化ADC1参数
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
//初始化结构体变量
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PA5通道5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不带上下拉
GPIO_Init(GPIOA,&GPIO_InitStructure);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE); //ADC1复位
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); //复位结束
//初始化通用配置
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; //两个采样阶段之间的延迟5个时钟
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA失能
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //预分频4分频。ADCCLK = PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz
ADC_CommonInit(&ADC_CommonInitStructure); //初始化
//初始化ADC1相关参数
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //开启连续转换 ()()()
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁止触发检测,使用软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐方式:右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1; //1个转换在规则序列中
ADC_Init(ADC1, &ADC_InitStructure); //ADC初始化
ADC_Cmd(ADC1, ENABLE);//开启 AD 转换器
ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE); //ADC转换结束产生中断,在中断服务程序中读取转换值,开启中断
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_480Cycles ); //设置指定 ADC 的规则组通道,一个序列,采样时间
ADC_SoftwareStartConv(ADC1); //软件触发
}
static void ADC_NVIC_Config(void){
//NVIC初始化结构体
NVIC_InitTypeDef NVIC_InitStruct;
//设置中断优先级的分组
//就是设置主抢占优先级和子抢占优先级各是几,这里是分组为1,代表主优先级可以是0和1(就是1个位来设置主优先级),子优先级是0-7,是2的3次方
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//配置ADC为中断源
NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn;
//配置抢占优先级
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
//配置子优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
//使能中断
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void ADC1_Init(void){
ADCx_Config();
ADC_NVIC_Config();
}
2.3 编写DAC输入值函数
该函数就是把想输出的电压值换算成对应DAC的输入值,这里value值就是期望的电压值,这里做了10倍处理。
//vaule:0—33,对应的是0-3.3v
void DAC_Set_Valur(uint16_t value){
float temp = value;
temp = value/10;
temp = temp*4095/3.3;
DAC_SetChannel1Data(DAC_Align_12b_R,temp);
}
2.4 编写ADC转换中断函数
uint16_t ADC_Value = 0;
void ADC_IRQHandler(void){
if(ADC_GetITStatus(ADC1, ADC_IT_EOC) == SET){ //表示中断已经来了,EOC=1,代表数据转换结束
ADC_Value = ADC_GetConversionValue(ADC1); //获取ADC数据
}
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
}
2.5 编写main函数
float ADC_ConvertedValueLocal; //存储转换的电压值
extern uint16_t ADC_Value;
int main(void)
{
USART_Config();
ADC1_Init();
DAC_Config_Init();
DAC_Set_Valur(10); //想要输出1v电压
delay_init(168);
while(1){
delay_ms(500);
ADC_ConvertedValueLocal = (float)ADC_Value/4096*3.3;
printf("\r\n The current AD value = 0x%04X \r\n",ADC_Value);
printf("\r\n The current AD value = %.4fV \r\n",ADC_ConvertedValueLocal);
printf("\r\n\r\n");
delay_ms(500);
}
}
3. 实验结果
使用跳线帽把DAC和ADC的引脚连起来,这里把想输出的电压值设置为1v,2v,3v,观察串口输出的结果。
DAC_Set_Valur(10); //想要输出1v电压
DAC_Set_Valur(20); //想要输出2v电压
DAC_Set_Valur(30); //想要输出3v电压