STM32F4xx系列一般具有3个ADC,可以相互独立使用,也可以使用 双重 /三重模式(提高采样率)。这里的ADC是12位(最大转换结果为4096)逐次逼近型ADC,具备19个 通道 (可以测量16个外部源,2个内部源和 V b a t V_{bat} Vbat 通道),通道转换可以是 单次、连续、扫描或间断模式 执行,转换又分为 规则通道和注入通道 。转换结果可以 左对齐或右对齐方式 存储在16位数据寄存器中,ADC最大工作频率是36MHz,最大 转换速率 为2.4MHz。
精度(分辨率): 位数越高越好,相应的量化误差越小, Δ = V r e f / 4096 \Delta = V_{ref} / 4096 Δ=Vref/4096。
ADC参考电压: V r e f V_{ref} Vref ,对于STM32F407ZGT6芯片只有 V r e f + V_{ref+} Vref+ 参考电压引脚, V r e f + V_{ref+} Vref+ 输入电压范围是1.8~VDDA(P7端口上通过跳线帽将 V r e f + V_{ref+} Vref+接到VDDA,这样默认参考电压就是3.3V)。
采样时间是ADC记录输入电压模拟量的时间,该时间内要求要求输入电压足够稳定,不会因电压波动造成记录数值不准,越长越精准。
转换时间是指ADC把记录的模拟量转换成数字量的时间
总的转换时间: T c o v n T_{covn} Tcovn=采样时间+12个时钟周期(转换时间)。
(在不同情形下,可能一个名词对应不同概念,个人感觉有些文章将不同概念都当成了同一个名词很有迷惑性,比如把连续转换两个采样点的时间间隔叫做采样时间(间隔)又把模拟量采取时间称为采样时间,个人理解,若有错误感谢指出)
多重模式: 当有多个ADC存在时,可以设置为多个ADC交替采样(比如双重或三重),这样可以提高采样率。
通道: 可以理解为ADC的模拟数据输入端口(来源),可以是外部通道(与GPIO口连接,测量外部电压信号),也可以是内部通道,比如与内部电源或者传感器相连。
转换模式: 顾名思义,单次就是触发转换后,ADC转换一次就结束;而连续模式就是不停的转换;扫描的意思就是顺序读取(多个通道输入时,按照一定顺序依次转换,想象下对多个通道一个一个读取,理解名字为什么这么取)。
规则通道与注入通道: 类比中断可以设置抢占优先级和响应优先级,ADC采样不同通道也可以设置如此:注入通道可以打断规则通道的转换。STM32F4最多可以设置16个规则通道,而注入通道最多4个。
左对齐或右对齐方式: 转换数据在数据寄存器中的存放方式,比如将12位数据0xFFF存放在16位寄存器里面,设置左对齐,则直接读取寄存器得到的数据是0xFFF0。
ADC_CR1: 控制寄存器,如SCAN位用于设置扫描模式,RES位设置ADC分辨率(6、8、10、12位四种)。
ADC_CR2: 控制寄存器,如ADON设置AD的开关,CONT设置连续转换,ALIGN设置数据对齐,EXTEN设置规则通道的外部触发使能(上升沿、下降沿等)。
ADC_CCR: 通用控制寄存器,如ADCPRE位为设置ADC输入时钟分频大小,MULTI用于设置ADC多重模式选择(全0为独立模式)。
ADC_SMPR1和ADC_SMPR2: 采样时间寄存器,选择设置通道0~18的采样时间(每个通道站3位)。
ADC_SQR1~3: 规则序列寄存器,对于规则通道进行配置(多少个规则通道,每个规则通道序列对应的ADC通道编号等)。
ADC_DR: 数据寄存器,规则序列通道的转换结果存在此处(存放的数据可以通过ADC_CR2的ALIGN位设置左对齐还是右对齐);相应的,注入通道转换结果存放在ADC_JDRx里面。
ADC_SR: 状态寄存器,比如EOC位标志AD转换的完成,表示此时可以从ADC_DR 读取转换结果。
考虑到使用ADC读取外部通道模拟量,此处需要配置GPIO为模拟输入。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
有了输入量我们还需要设置ADC的初始化参数。
首先是通用配置:
ADC_CommonInitTypeDef ADC_CommonInitStructure;
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);//初始化
注:里面DMA不管(看后面的文章介绍),ADC_TwoSamplingDelay 的含义理解(设置多重ADC采样间隔时有用,单个ADC无意义,不要与采样时间和采样频率搞混淆了)
初始化使用的ADC1的参数:
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//禁止触发检测,使用软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中 也就是只转换规则序列1
ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
这里的ADC_ExternalTrigConvEdge 触发方式设置设置为软件触发,但是很多时候设置为外部触发比如定时器触发转换,这样可以精确控制采样转换频率。
这样基本参数就设置好了,ADC也配置完成,只需开启使用了:
ADC_Cmd(ADC1, ENABLE);//开启AD转换器
这里我们使用规则序列通道,且需注意读取转换值之前要判断转换是否完成,这里我们直接通过将上述步骤编写为一个ADC转换值读取函数:
//ch: @ref ADC_channels
//通道值 0~16取值范围为:ADC_Channel_0~ADC_Channel_16
//返回值:转换结果
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles ); //ADC1,ADC通道,480个周期,提高采样时间可以提高精确度
ADC_SoftwareStartConv(ADC1); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
DAC即将数字量转换为模拟量(如电压),比如12位DAC,参考电压为 V r e f + V_{ref+} Vref+ ,输出电压线性变化范围0~Vref+, V o u t V_{out} Vout= V r e f + V_{ref+} Vref+*(DORx / 4095),这里的 DORx 存放的就是我们所要转换的目标数字量。
而这是通过写入 DHRx寄存器再间接的传入 DORx 寄存器来控制DA转换的。
STM32F4的DAC模块(数字/模拟转换模块)是12位 数字输入,电压输出型的DAC。DAC 可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合使用。DAC 工作在 12 位模式时, 数据可以设置成左对齐或右对齐。DAC 模块有 2 个输出通道,每个通道都有单独的转换器。 在双 DAC 模式下,2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个 通道的输出。DAC 可以通过引脚输入参考电压 Vref+(通 ADC 共用)以获得更精确的转换结果。
STM32F4 的 DAC 模块主要特点有:
① 2 个 DAC 转换器:每个转换器对应 1 个输出通道
② 8 位或者 12 位单调输出
③ 12 位模式下数据左对齐或者右对齐
④ 同步更新功能
⑤ 噪声波形生成
⑥ 三角波形生成
⑦ 双 DAC 通道同时或者分别转换
⑧ 每个通道都有 DMA 功能
1、DAC_CR (控制寄存器):低 16 位用于控制通道 1,而高 16 位用于控制通道 2。
一些重要的位如下(通道1为例):
EN1:通道1使能位。
BOFF1:通道1输出缓冲器禁止位。
TEN1:通道1触发使能位(决定DHR数据需要延迟几个APB时钟才传入DOR)。
TSEL1:通道1触发器选择(外部触发事件如某个定时器或外部中断线等)。
WAVE1:波形生成使能(噪声/三角波)。
DMAEN1:通道 1 DMA 使能位。
2、DAC_DHR12R1(通道 1 的 12 位右对齐数据保持寄存器):在 DAC_CR 设置好之后,DAC 就可以正常工作了,我们仅需要再设置 DAC 的数据保持寄存器的值,就可以在 DAC 输出 通道得到你想要的电压了(对应 IO 口设置为模拟输入)。
1)开启 PA 口时钟,设置 PA4 为模拟输入。
2)使能 DAC1 时钟。
3)初始化 DAC,设置 DAC 的工作模式。
实际操作对象为DAC_CR寄存器:
void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct);
typedef struct
{
uint32_t DAC_Trigger; //是否使用触发功能
uint32_t DAC_WaveGeneration; //是否使用波形发生
uint32_t DAC_LFSRUnmask_TriangleAmplitude; //用来设置屏蔽/幅值选择器,这个变量只 在使用波形发生器的时候才有用
uint32_t DAC_OutputBuffer; //设置输出缓存控制位
}DAC_InitTypeDef;
4)使能 DAC 转换通道
5)设置 DAC 的输出值
我们使用 12 位右对齐数据格式, 所以我们通过设置 DHR12R1,就可以在 DAC 输出引脚(PA4)得到不同的电压值:
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12 位右对齐数据格式设置 DAC 值
还可以读出 DAC 对应通道最后一次转换的数值:
DAC_GetDataOutputValue(DAC_Channel_1);