完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子
第二十七章 DAC 实验 上几章,我们介绍了 STM32F767 的 ADC 使用,本章我们将向大家介绍 STM32F767 的 DAC 功能。在本章中,我们将利用按键(或 USMART)控制 STM32F767 内部 DAC1 来输 出电压,通过 ADC1 的通道 5 采集 DAC 的输出电压,在 LCD 模块上面显示 ADC 获取到的 电压值以及 DAC 的设定输出电压值等信息。本章将分为如下几个部分: 27.1 STM32F767 DAC 简介 27.2 硬件设计 27.3 软件设计 27.4 下载验证 27.1 STM32F767 DAC 简介 STM32F767 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输出型的 DAC。 DAC 可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合使用。DAC 工作在 12 位模 式时,数据可以设置成左对齐或右对齐。DAC 模块有 2 个输出通道,每个通道都有单独的 转换器。在双 DAC 模式下,2 个通道可以独立地进行转换,也可以同时进行转换并同步地 更新 2 个通道的输出。DAC 可以通过引脚输入参考电压 Vref+(通 ADC 共用)以获得更精 确的转换结果。 STM32F767 的 DAC 模块主要特点有: ① 2 个 DAC 转换器:每个转换器对应 1 个输出通道 ② 8 位或者 12 位单调输出 ③ 12 位模式下数据左对齐或者右对齐 ④ 同步更新功能 ⑤ 噪声波形生成 ⑥ 三角波形生成 ⑦ 双 DAC 通道同时或者分别转换 ⑧ 每个通道都有 DMA 功能 单个 DAC 通道的框图如图 27.1.1 所示: 图 27.1.1 DAC 通道模块框图 图中 VDDA 和 VSSA 为 DAC 模块模拟部分的供电,而 Vref+则是 DAC 模块的参考电 压。DAC_OUTx 就是 DAC 的输出通道了(对应 PA4 或者 PA5 引脚)。 从图 27.1.1 可以看出,DAC 输出是受 DORx 寄存器直接控制的,但是我们不能直接往 DORx 寄存器写入数据,而是通过 DHRx 间接的传给 DORx 寄存器,实现对 DAC 输出的控 制。前面我们提到,STM32F767 的 DAC 支持 8/12 位模式,8 位模式的时候是固定的右对齐 的,而 12 位模式又可以设置左对齐/右对齐。单 DAC 通道 x,总共有 3 种情况: ① 8位数据右对齐:用户将数据写入DAC_DHR8Rx[7:0]位(实际存入DHRx[11:4]位)。 ② 12 位数据左对齐:用户将数据写入 DAC_DHR12Lx[15:4]位(实际存入 DHRx[11:0] 位)。 ③ 12 位数据右对齐:用户将数据写入 DAC_DHR12Rx[11:0]位(实际存入 DHRx[11:0] 位)。 我们本章使用的就是单 DAC 通道 1,采用 12 位右对齐格式,所以采用第③种情况。 如果没有选中硬件触发(寄存器 DAC_CR1 的 TENx 位置’0’ ),存入寄存器 DAC_DHRx 的数据会在一个 APB1 时钟周期后自动传至寄存器 DAC_DORx。如果选中硬件触发(寄存器 DAC_CR1 的 TENx 位置’1’),数据传输在触发发生以后 3 个 APB1 时钟周期后完成。 一 旦数据从 DAC_DHRx 寄存器装入 DAC_DORx 寄存器,在经过时间 之后,输出 即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。我们可以从 STM32F767IGT6 的数据手册查到 的典型值为 3us,最大是 6us。所以 DAC 的转 换速度最快是 333K 左右。 本章我们将不使用硬件触发(TEN=0),其转换的时间框图如图 27.1.2 所示: 图 27.1.2 TEN=0 时 DAC 模块转换时间框图 当 DAC 的参考电压为 Vref+的时候,DAC 的输出电压是线性的从 0~Vref+,12 位模式 下 DAC 输出电压与 Vref+以及 DORx 的计算公式如下: DACx 输出电压=Vref*(DORx/4096) 接下来,我们介绍一下要实现 DAC 的通道 1 输出,需要用到的一些寄存器。首先是 DAC 控制寄存器 DAC_CR,该寄存器的各位描述如图 27.1.3 所示: 图 27.1.3 寄存器 DAC_CR 各位描述 DAC_CR 的低 16 位用于控制通道 1,而高 16 位用于控制通道 2,我们这里仅列出比较 重要的最低 8 位的详细描述,如图 27.1.4 所示: 图 27.1.4 寄存器 DAC_CR 低八位详细描述 首先,我们来看 DAC 通道 1 使能位(EN1),该位用来控制 DAC 通道 1 使能的,本章我 们就是用的 DAC 通道 1,所以该位设置为 1。 再看关闭 DAC 通道 1 输出缓存控制位(BOFF1),这里 STM32F767 的 DAC 输出缓存 做的有些不好,如果使能的话,虽然输出能力强一点,但是输出没法到 0,这是个很严重的 问题。所以本章我们不使用输出缓存。即设置该位为 1。 DAC 通道 1 触发使能位(TEN1),该位用来控制是否使用触发,里我们不使用触发, 所以设置该位为 0。 DAC 通道 1 触发选择位(TSEL1[2:0]),这里我们没用到外部触发,所以设置这几个位 为 0 就行了。 DAC 通道 1 噪声/三角波生成使能位(WAVE1[1:0]),这里我们同样没用到波形发生器, 故也设置为 0 即可。 DAC 通道 1 屏蔽/复制选择器(MAMP[3:0]),这些位仅在使用了波形发生器的时候有 用,本章没有用到波形发生器,故设置为 0 就可以了。 最后是 DAC 通道 1 DMA 使能位(DMAEN1),本章我们没有用到 DMA 功能,故还是 设置为 0。 通道 2 的情况和通道 1 一模一样,这里就不不细说了。在 DAC_CR 设置好之后,DAC 就可以正常工作了,我们仅需要再设置 DAC 的数据保持寄存器的值,就可以在 DAC 输出 通道得到你想要的电压了(对应 IO 口设置为模拟输入)。本章,我们用的是 DAC 通道 1 的 12 位右对齐数据保持寄存器:DAC_DHR12R1,该寄存器各位描述如图 27.1.5 所示: 图 27.1.5 寄存器 DAC_DHR12R1 各位描述 该寄存器用来设置 DAC 输出,通过写入 12 位数据到该寄存器,就可以在 DAC 输出通 道 1(PA4)得到我们所要的结果。 通过以上介绍,我们了解了 STM32F7 实现 DAC 输出的相关设置,本章我们将使用 DAC 模块的通道 1 来输出模拟电压。这里我们用到的库函数以及相关定义分布在文件 stm32f7xx_hal_dac.c 以及头文件 stm32f7xx_hal_dac.h 中。实现上面功能的详细设置步骤如下: 1)开启 DAC 和 PA 口时钟,设置 PA4 为模拟输入。 STM32F7 的 DAC 通道 1 是接在 PA4 上的,所以,我们先要使能 GPIOA 的时钟,然后 设置 PA4 为模拟输入。 这里需要特别说明一下,虽然 DAC 引脚设置为输入,但是 STM32F7 内部会连接在 DAC 模拟输出上,这在我们引脚复用映射章节有讲解。程序如下: __HAL_RCC_DAC_CLK_ENABLE(); //使能 DAC 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); //开启 GPIOA 时钟 GPIO_Initure.Pin=GPIO_PIN_4; //PA4 GPIO_Initure.Mode=GPIO_MODE_ANALOG; //模拟 GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉 HAL_GPIO_Init(GPIOA,&GPIO_Initure); 对于 DAC 通道与引脚对应关系,这在 STM32F7 的数据手册引脚表上有列出,如下图: 图 26.1.6 DAC 通道引脚对应关系 2)初始化 DAC,设置 DAC 的工作模式 HAL 库中提供了一个 DAC 初始化函数 HAL_DAC_Init,该函数声明如下: HAL_StatusTypeDef HAL_DAC_Init(DAC_HandleTypeDef* hdac); 该函数并没有设置任何 DAC 相关寄存器,也就是说没有对 DAC 进行任何配置,它只 是 HAL 库提供用来在软件上初始化 DAC,也就是说,为后面 HAL 库操作 DAC 做好准备。 它有一个很重要的作用就是在函数内部会调用 DAC 的 MSP 初始化函数 HAL_DAC_MspInit, 该函数声明如下: void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac); 一般情况下,步骤 1 中的与 MCU 相关的时钟使能和 IO 口配置都放在该函数中实现。 HAL 库提供了一个很重要的 DAC 配置函数 HAL_DAC_ConfigChannel,该函数用来配 置 DAC 通道的触发类型以及输出缓冲。该函数声明如下: HAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef* hdac, DAC_ChannelConfTypeDef* sConfig, uint32_t Channel); 第一个入口参数非常简单,为 DAC 初始化句柄,和 HAL_DAC_Init 保存一致即可。 第三个入口参数 Channel 用来配置 DAC 通道,比如我们使用 PA4,也就是 DAC 通道 1, 所以配置值为 DAC_CHANNEL_1 即可。 接下来我们看看第二个入口参数 sConfig,该参数是 DAC_ChannelConfTypeDef 结构体 指针类型,结构体 DAC_ChannelConfTypeDef 定义如下: typedef struct { uint32_t DAC_Trigger; // DAC 触发类型 uint32_t DAC_OutputBuffer; //输出缓冲 }DAC_ChannelConfTypeDef; 成员变量DAC_Trigger 用来设置DAC 触发类型,DAC_OutputBuffer 用来设置输出缓冲, 这在我们前面都有讲解。DAC 初始化配置实例代码如下: DAC_HandleTypeDef DAC1_Handler; DAC_ChannelConfTypeDef DACCH1_Config; DAC1_Handler.Instance=DAC; HAL_DAC_Init(&DAC1_Handler); //初始化 DAC DACCH1_Config.DAC_Trigger=DAC_TRIGGER_NONE; //不使用触发功能 DACCH1_Config.DAC_OutputBuffer=DAC_OUTPUTBUFFER_DISABLE; HAL_DAC_ConfigChannel(&DAC1_Handler,&DACCH1_Config,DAC_CHANNEL_1); 3)使能 DAC 转换通道 初始化 DAC 之后,理所当然要使能 DAC 转换通道,HAL 库函数是: HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef* hdac, uint32_t Channel); 该函数非常简单,第一个参数是 DAC 句柄,第二个用来设置 DAC 通道。 4)设置 DAC 的输出值。 通过前面 3 个步骤的设置,DAC 就可以开始工作了,我们使用 12 位右对齐数据格式,, 就可以在 DAC 输出引脚(PA4)得到不同的电压值了,HAL 库函数为: HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data); 该函数从入口参数可以看出,它是配置 DAC 的通道输出值,同时通过第三个入口参数 设置对齐方式。 最后,再提醒一下大家,本例程,我们使用的是 3.3V 的参考电压, 即 Vref+ 连 接 VDDA。 通过以上几个步骤的设置,我们就能正常的使用 STM32F7 的 DAC 通道 1 来输出不同 的模拟电压了。 27.2 硬件设计 本章用到的硬件资源有: 1) 指示灯 DS0 2) KEY_UP 和 KEY1 按键 3) 串口 4) LCD 模块 5) ADC 6) DAC 本章,我们使用 DAC 通道 1 输出模拟电压,然后通过 ADC1 的通道 5 对该输出电压进 行读取,并显示在 LCD 模块上面,DAC 的输出电压,我们通过按键(或 USMART)进行 设置。 我们需要用到 ADC 采集 DAC 的输出电压,所以需要在硬件上把他们短接起来。ADC 和 DAC 的连接原理图如图 27.2.1 所示: 图 27.2.1 ADC、DAC 与 STM32F767 连接原理图 P11 是多功能端口,我们只需要通过跳线帽短接 P11 的 ADC 和 DAC,就可以开始做本 章实验了。如图 27.2.2 所示: 图 27.2.2 硬件连接示意图 27.3 软件设计 打开本章实验工程可以发现,我们相比 ADC 实验,在库函数中主要是添加了 dac 支持 的相关文件 stm32f7xx_hal_dac.c 以及包含头文件 stm32f7xx_hal_dac.h。同时我们在 HARDWARE 分组下面新建了 dac.c 源文件以及包含对应的头文件 dac.h。这两个文件用来存 放我们编写的 ADC 相关函数和定义。打开 dac.c,代码如下: DAC_HandleTypeDef DAC1_Handler;//DAC 句柄 //初始化 DAC void DAC1_Init(void) { DAC_ChannelConfTypeDef DACCH1_Config; DAC1_Handler.Instance=DAC; HAL_DAC_Init(&DAC1_Handler); //初始化 DAC DACCH1_Config.DAC_Trigger=DAC_TRIGGER_NONE; //不使用触发功能 DACCH1_Config.DAC_OutputBuffer=DAC_OUTPUTBUFFER_DISABLE; //DAC1 输出缓冲关闭 HAL_DAC_ConfigChannel(&DAC1_Handler,&DACCH1_Config,DAC_CHANNEL_1); //DAC 通道 1 配置 HAL_DAC_Start(&DAC1_Handler,DAC_CHANNEL_1); //开启 DAC 通道 1 } //DAC 底层驱动,时钟配置,引脚 配置 //此函数会被 HAL_DAC_Init()调用 //hdac:DAC 句柄 void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac) { GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_DAC_CLK_ENABLE(); //使能 DAC 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); //开启 GPIOA 时钟 GPIO_Initure.Pin=GPIO_PIN_4; //PA4 GPIO_Initure.Mode=GPIO_MODE_ANALOG; //模拟 GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉 HAL_GPIO_Init(GPIOA,&GPIO_Initure); } //设置通道 1 输出电压 //vol:0~3300,代表 0~3.3V void DAC1_Set_Vol(u16 vol) { double temp=vol; temp/=1000; temp=temp*4096/3.3; HAL_DAC_SetValue(&DAC1_Handler,DAC_CHANNEL_1, DAC_ALIGN_12B_R,temp);//12 位右对齐数据格式设置DAC值 } 此部分代码就 3 个函数,Dac1_Init 函数用于初始化 DAC 通道 1,并开启 DAC 通道。这 里基本上是按我们上面的步骤 2 和步骤 3 来实现的。函数 HAL_DAC_MspInit 是 DAC 的 MSP 初始化回调函数,内部实现的是时钟使能和 IO 口配置,它和 Dac1_Init 配合使用来初 始化整个 DAC 通道。经过初始化之后,我们就可以正常使用 DAC 通道 1 了。第三个函数 Dac1_Set_Vol,用于设置 DAC 通道 1 的输出电压,实际就是将电压值转换为 DAC 输入值。 其他头文件代码就比较简单,这里我们不做过多讲解,接下来我们来看看主函数代码: int main(void) { u16 adcx; float temp; u8 t=0; u16 dacval=0; u8 key; Cache_Enable(); //打开 L1-Cache Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz delay_init(216); //初始化延时函数 …//此处省略部分初始化代码 MY_ADC_Init(); //初始化 ADC1 DAC1_Init(); //初始化 DAC1 ……//此处省略部分液晶显示代码 HAL_DAC_SetValue(&DAC1_Handler,DAC_CHANNEL_1,DAC_ALIGN_12B_R,0); while(1) { t++; key=KEY_Scan(0); if(key==WKUP_PRES) { if(dacval<4000)dacval+=200; HAL_DAC_SetValue(&DAC1_Handler,DAC_CHANNEL_1, DAC_ALIGN_12B_R,dacval);//设置 DAC 值 }else if(key==2) { if(dacval>200)dacval-=200; else dacval=0; HAL_DAC_SetValue(&DAC1_Handler,DAC_CHANNEL_1, DAC_ALIGN_12B_R,dacval);//设置 DAC 值 } if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1 按下了,或者定时时间到了 { adcx=HAL_DAC_GetValue(&DAC1_Handler,DAC_CHANNEL_1); //读取前面设置 DAC 的值 LCD_ShowxNum(94,150,adcx,4,16,0); //显示 DAC 寄存器值 temp=(float)adcx*(3.3/4096); //得到 DAC 电压值 adcx=temp; LCD_ShowxNum(94,170,temp,1,16,0); //显示电压值整数部分 temp-=adcx; temp*=1000; LCD_ShowxNum(110,170,temp,3,16,0X80); //显示电压值的小数部分 adcx=Get_Adc_Average(ADC_CHANNEL_5,10); //得到 ADC 转换值 temp=(float)adcx*(3.3/4096); //得到 ADC 电压值 adcx=temp; LCD_ShowxNum(94,190,temp,1,16,0); //显示电压值整数部分 temp-=adcx; temp*=1000; LCD_ShowxNum(110,190,temp,3,16,0X80); //显示电压值的小数部分 LED0_Toggle; t=0; } delay_ms(10); } } 此部分代码,我们先对需要用到的模块进行初始化,然后显示一些提示信息,本章我们 通过 KEY_UP(WKUP 按键)和 KEY1(也就是上下键)来实现对 DAC 输出的幅值控制。 按下 KEY_UP 增加,按 KEY1 减小。同时在 LCD 上面显示 DHR12R1 寄存器的值、DAC 设计输出电压以及 ADC 采集到的 DAC 输出电压。 27.4 下载验证 在代码编译成功之后,我们通过下载代码到 ALIENTEK 阿波罗 STM32 开发板上,可以 看到 LCD 显示如图 27.4.1 所示: 图 27.4.1 DAC 实验测试图 同时伴随 DS0 的不停闪烁,提示程序在运行。此时,我们通过按 KEY_UP 按键,可以 看到输出电压增大,按 KEY1 则变小。 |
|
相关推荐
|
|
STM32CUBEMX(12)--IIC,12864OLED(0.96寸)移植
418 浏览 0 评论
STM32CUBEMX(5)--自定义红外NEC解码,定时器TIM捕获方式
522 浏览 0 评论
stm32f407vet6+LAN8720A调试以太网遇到软件复位失败问题
908 浏览 0 评论
使用STM32CubeMX对红外波形进行GPIO输出,并LED显示
440 浏览 0 评论
482 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-9-12 20:14 , Processed in 0.489148 second(s), Total 65, Slave 48 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191