打印
[STM32F1]

stm32基础篇——LCDC DAC 实验

[复制链接]
2357|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 aizaixiyuanqian 于 2018-1-24 19:38 编辑

前面的试验我们介绍了 STM32 的 ADC 使用, 本次试验我们将向大家介绍STM32 的 DAC 功能。我们不断将数据发送到 DAC 转换通道 1 内。通过 LCD 显示 DAC 转换的电压值,可以同时使用万用表测量对应 DAC 输出通道管脚PA4,看其电压是否和 LCD 上显示的一致。试验目标:
1. 学会内部 DAC 的操作
沙发
aizaixiyuanqian|  楼主 | 2018-1-24 19:41 | 只看该作者
STM32 DAC  简介
大容量的 STM32F103 具有内部 DAC,我们板子使用的 STM32 是 STM32F103ZET6 属于大容量产品,所以是带有 DAC 模块的。STM32 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输出型的 DAC。 DAC可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合用。 DAC 工作在 12 位模式时,数据可以设置成左对齐或右对齐。 DAC 模块有 2 个输出通道,每个通道都有单独的转换器。在双 DAC 模式下, 2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个通道的输出。 DAC 可以通过引脚输入参考电压 VREF+以获得更精确的转换结果。

使用特权

评论回复
板凳
aizaixiyuanqian|  楼主 | 2018-1-24 19:42 | 只看该作者
STM32 的 DAC 模块主要特点有:
① 2 个 DAC 转换器:每个转换器对应 1 个输出通道
② 8 位或者 12 位单调输出
③ 12 位模式下数据左对齐或者右对齐
④ 同步更新功能
⑤ 噪声波形生成
⑥ 三角波形生成
⑦ 双 DAC 通道同时或者分别转换
⑧ 每个通道都有 DMA 功能

使用特权

评论回复
地板
aizaixiyuanqian|  楼主 | 2018-1-24 19:49 | 只看该作者
单个 DAC 通道的框图如图所示:

使用特权

评论回复
5
aizaixiyuanqian|  楼主 | 2018-1-24 19:50 | 只看该作者
图中 VDDA 和 VSSA 为 DAC 模块模拟部分的供电,而 Vref+则是 DAC 模块的参考电压。DAC_OUTx 就是 DAC 的输出通道了(对应 PA4 或者 PA5 引脚)。从上图可以看出,DAC 输出是受 DORx 寄存器直接控制的,但是我们不能直接往 DORx寄存器写入数据,而是通过DHRx 间接的传给 DORx 寄存器,实现对 DAC 输出的控制。前面我们提到, STM32 的 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]位)。

使用特权

评论回复
6
aizaixiyuanqian|  楼主 | 2018-1-24 19:52 | 只看该作者
我们本次试验使用的就是单 DAC 通道 1,采用 12 位右对齐格式,所以采用第③种情况。如果没有选中硬件触发(寄存器 DAC_CR1 的 TENx 位置’ 0’ ),存入寄存器 DAC_DHRx的数据会在一个 APB1 时钟周期后自动传至寄存器 DAC_DORx。如果选中硬件触发(寄存器DAC_CR1 的 TENx 位置’ 1’ ),数据传输在触发发生以后 3 个 APB1 时钟周期后完成。 一旦数据从 DAC_DHRx 寄存器装入 DAC_DORx 寄存器,在经过时间 Tsetting 之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。 我们可以从STM32F103ZET6 的数据手册查到 Tsetting 的典型值为 3us,最大是 4us。所以 DAC 的转换速度最快是 250K 左右。

使用特权

评论回复
7
aizaixiyuanqian|  楼主 | 2018-1-24 19:55 | 只看该作者
本次试验我们将不使用硬件触发,其转换的时间框图如图所




使用特权

评论回复
8
aizaixiyuanqian|  楼主 | 2018-1-24 19:55 | 只看该作者
当 DAC 的参考电压为 Vref+的时候, DAC 的输出电压是线性的从 0~Vref+, 12 位模式下DAC 输出电压与 Vref+以及 DORx 的计算公式如下:DACx 输出电压=Vref*( DORx/4095)

使用特权

评论回复
9
aizaixiyuanqian|  楼主 | 2018-1-24 20:00 | 只看该作者
要实现 DAC 的通道 1 输出,需要用到的一些寄存器。首先是 DAC控制寄存器 DAC_CR,该寄存器的各位描述如图所示


使用特权

评论回复
10
aizaixiyuanqian|  楼主 | 2018-1-24 20:03 | 只看该作者
DAC_CR 的低 16 位用于控制通道 1,而高 16 位用于控制通道 2,我们这里仅列出比较重要的最低 8 位的详细描述,如图所示


使用特权

评论回复
11
aizaixiyuanqian|  楼主 | 2018-1-24 20:05 | 只看该作者
1 )  开启 PA  口时钟,设置 PA4  为模拟输入。
STM32F103ZET6 的 DAC 通道 1 在 PA4 上,所以,我们先要使能 端口 A 的时钟, 然后设置 PA4 为模拟输入。 DAC 本身是输出,但是为什么端口要设置为模拟输入模式呢?因为一但使能 DACx 通道之后,相应的 GPIO 引脚( PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设置为输入,是为了避免额外的干扰。使能 GPIOA 时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
设置 PA1 为模拟输入只需要设置初始化参数即可:
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟量输入

使用特权

评论回复
12
aizaixiyuanqian|  楼主 | 2018-1-24 20:05 | 只看该作者
本帖最后由 aizaixiyuanqian 于 2018-1-24 20:06 编辑

2 )  使能 DAC1  时钟。
同其他外设一样,要想使用,必须先开启相应的时钟。 STM32 的 DAC 模块时钟是由APB1 提供的,所以我们调用函数 RCC_APB1PeriphClockCmd()设置 DAC 模块的时钟使能。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能 DAC 通道时钟

使用特权

评论回复
13
aizaixiyuanqian|  楼主 | 2018-1-24 20:07 | 只看该作者
3 )  初始化 DAC, 设置 DAC  的工作模式。
该部分设置全部通过 DAC_CR 设置实现,包括: DAC 通道 1 使能、 DAC 通道 1 输出缓存关闭、不使用触发、不使用波形发生器等设置。 这里 DMA 初始化是通过函数 DAC_Init完成的:
void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct)

使用特权

评论回复
14
aizaixiyuanqian|  楼主 | 2018-1-24 20:08 | 只看该作者
首先我们来看看参数设置结构体类型 DAC_InitTypeDef 的定义:
typedef struct
{
uint32_t DAC_Trigger;
uint32_t DAC_WaveGeneration;
uint32_t DAC_LFSRUnmask_TriangleAmplitude;
uint32_t DAC_OutputBuffer;
}DAC_InitTypeDef;
第一个参数 DAC_Trigger 用来设置是否使用触发功能,前面已经讲解过这个的含义,这里我们不是用触发功能,所以值为DAC_Trigger_None。
第二个参数 DAC_WaveGeneratio 用来设置是否使用波形发生,这里我们前面同样讲解过不使用。所以值为 DAC_WaveGeneration_None。
第三个参数 DAC_LFSRUnmask_TriangleAmplitude 用来设置屏蔽/幅值选择器,这个变量只在使用波形发生器的时候才有用,这里我们设置为 0 即可,值为 DAC_LFSRUnmask_Bit0。
第四个参数 DAC_OutputBuffer 是用来设置输出缓存控制位,前面讲解过,我们不使用输出缓存,所以值为 DAC_OutputBuffer_Disable。到此四个参数设置完毕。看看我们的实例代码:
DAC_InitTypeDef DAC_InitStructure;
DAC_InitStructure.DAC_Trigger=DAC_Trigger_None;//不使用触发功能
DAC_InitStructure.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用三角波
//屏蔽 幅值设置
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;
//关闭缓存
DAC_InitStructure.DAC_OutputBuffer=DAC_OutputBuffer_Disable;
DAC_Init(DAC_Channel_1,&DAC_InitStructure);//初始化 DAC 通道 1

使用特权

评论回复
15
aizaixiyuanqian|  楼主 | 2018-1-24 20:09 | 只看该作者
4 )  使能 DAC  转换通道
初始化 DAC 之后,理所当然要使能 DAC 转换通道,库函数方法是:DAC_Cmd(DAC_Channel_1, ENABLE); //使能 DAC1

使用特权

评论回复
16
aizaixiyuanqian|  楼主 | 2018-1-24 20:10 | 只看该作者
5 )  设置 DAC  的输出值。
通过前面 4 个步骤的设置, DAC 就可以开始工作了,我们使用 12 位右对齐数据格式,所以我们通过设置 DHR12R1,就可以在 DAC 输出引脚( PA4)得到不同的电压值了。 库函数是:
DAC_SetChannel1Data(DAC_Align_12b_R,0);//12 位 右对齐 写 0 数据
第一个参数设置对齐方式,可以为 12 位右对齐 DAC_Align_12b_R, 12 位左对齐
DAC_Align_12b_L 以及 8 位右对齐 DAC_Align_8b_R 方式。
第二个参数就是 DAC 的输入值了,这个很好理解,初始化设置为 0。
这里,还可以读出 DAC 的数值,函数是:
DAC_GetDataOutputValue(DAC_Channel_1);

使用特权

评论回复
17
aizaixiyuanqian|  楼主 | 2018-1-24 20:11 | 只看该作者
DAC  初始化
void dac_init() //DAC 初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;//DAC_1
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟量输入
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4);//输出高
DAC_InitStructure.DAC_Trigger=DAC_Trigger_None;//不使用触发功能
DAC_InitStructure.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用三角
//屏蔽 幅值设置
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;
//关闭缓存
DAC_InitStructure.DAC_OutputBuffer=DAC_OutputBuffer_Disable;
DAC_Init(DAC_Channel_1,&DAC_InitStructure);//初始化 DAC 通道 1
DAC_Cmd(DAC_Channel_1,ENABLE);//使能 DAC1
DAC_SetChannel1Data(DAC_Align_12b_R,0);//12 位 右对齐 写 0 数据
}

使用特权

评论回复
18
aizaixiyuanqian|  楼主 | 2018-1-24 20:24 | 只看该作者
主函数
int main()
{
u16 value;
float da;
u8 i=0,j,dat[7];
dac_init();  //dac 配置内部温度初始化
TFT_Init();  //TFT 彩屏初始化
LED_Init();  //端口初始化
TFT_ClearScreen(BLACK);  //清屏
GUI_Show12ASCII(10,10,"This is a DAC-Channel1-PWM Check!",YELLOW,BLACK);
GUI_Show12ASCII(10,100,"The Output Vologe is:",YELLOW,BLACK);
while(1)
{
value=0;
for(i=0;i<=10;i++)
{
value=i*400;
DAC_SetChannel1Data(DAC_Align_12b_R,value);//12 位 右对齐 PA4 端口
输出
da=3.3*value/4095;
value=da*100;
dat[0]=value/100+0x30;
dat[1]='.';
dat[2]=value%100/10+0x30;
dat[3]=value%100%10+0x30;
dat[4]='V';
dat[5]='\0';
GUI_Show12ASCII(180,100,dat,YELLOW,BLACK);
if(j>1)
{
j=0;
GPIO_SetBits(GPIOC,GPIO_Pin_0);
}
else
{
j++;
GPIO_ResetBits(GPIOC,GPIO_Pin_0);
}
delay_ms(1000);//间隔 1 秒输出一个电压
}
}
}

使用特权

评论回复
19
aizaixiyuanqian|  楼主 | 2018-1-24 20:24 | 只看该作者
主程序的效果是,通过不断发送数据到 DAC 通道 1 中进行转换,将转换结果显示在 LCD
上,每次增加幅度是 0.32V,这个幅度大家可以自己修改,只需要将 value=i*400;这个里面的
400 修改即可,i=11 后,value 重新清零,循环开始,整个过程运行 LED 闪烁。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

62

主题

1353

帖子

6

粉丝