STMicroelectronics 系列:STM32G0 系列_(11).STM32G0系列的ADC和DAC
STM32G0系列的ADC和DAC
模拟到数字转换器 (ADC)
ADC概述
STM32G0系列微控制器集成了高性能的模拟到数字转换器(ADC),用于将模拟信号转换为数字信号。ADC在各种应用中非常重要,如传感器数据读取、音频处理和环境监测等。STM32G0系列的ADC具有以下特点:
高精度:12位分辨率,确保高精度的模拟信号转换。
多通道:支持多个模拟输入通道,可以同时进行多个信号的采样。
采样率:最高可达5 Msps(每秒百万采样)。
多种触发方式:软件触发、定时器触发、外部事件触发等。
多种工作模式:单次转换、连续转换、扫描模式等。
低功耗:支持多种低功耗模式,适用于电池供电的应用。
ADC硬件结构
STM32G0系列的ADC硬件结构如下:
ADC核心:负责将模拟信号转换为数字信号。
采样通道:支持多个输入通道,每个通道可以连接不同的模拟信号源。
数据寄存器:存储转换后的数字结果。
控制寄存器:用于配置ADC的工作模式、分辨率、触发方式等。
校准寄存器:用于校准ADC以提高精度。
DMA控制器:支持直接内存访问,可以将转换结果自动传输到内存中,减轻CPU负担。
ADC配置
1. ADC初始化
ADC的初始化步骤包括配置时钟、选择分辨率、配置采样时间、设置触发源等。以下是一个简单的初始化示例:
#include "stm32g0xx_hal.h"
// ADC配置结构体
ADC_HandleTypeDef hadc;
void ADC_Init(void) {
// 使能ADC时钟
__HAL_RCC_ADC_CLK_ENABLE();
// ADC初始化结构体
hadc.Instance = ADC1;
hadc.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率
hadc.Init.ScanConvMode = DISABLE; // 单通道模式
hadc.Init.ContinuousConvMode = DISABLE; // 单次转换模式
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 右对齐
hadc.Init.NbrOfConversion = 1; // 1次转换
hadc.Init.DMAContinuousRequests = DISABLE; // 禁用DMA连续请求
// 初始化ADC
if (HAL_ADC_Init(&hadc) != HAL_OK) {
// 初始化错误处理
Error_Handler();
}
// 配置采样时间
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0; // 选择通道0
sConfig.Rank = 1; // 通道1
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 239.5个周期
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
// 配置通道错误处理
Error_Handler();
}
}
2. ADC校准
为了确保ADC的转换精度,需要进行校准。校准可以通过调用HAL_ADCCalibration
函数来完成。
void ADC_Calibrate(void) {
// 校准ADC
if (HAL_ADCEx_Calibration_Start(&hadc, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {
// 校准错误处理
Error_Handler();
}
}
3. ADC启动和停止
在进行ADC转换之前,需要启动ADC。转换完成后,可以停止ADC以节省功耗。
void ADC_Start(void) {
// 启动ADC转换
if (HAL_ADC_Start(&hadc) != HAL_OK) {
// 启动错误处理
Error_Handler();
}
}
void ADC_Stop(void) {
// 停止ADC转换
if (HAL_ADC_Stop(&hadc) != HAL_OK) {
// 停止错误处理
Error_Handler();
}
}
4. ADC数据读取
读取ADC转换结果可以通过HAL_ADC_PollForConversion
函数来实现。该函数会等待ADC转换完成,并返回转换结果。
uint32_t ADC_Read(void) {
uint32_t value;
// 等待ADC转换完成
if (HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY) != HAL_OK) {
// 转换错误处理
Error_Handler();
}
// 读取转换结果
value = HAL_ADC_GetValue(&hadc);
return value;
}
ADC中断和DMA
1. ADC中断配置
使用中断可以实现在ADC转换完成后立即进行数据处理。以下是一个配置ADC中断的示例:
void ADC_Interrupt_Config(void) {
// 使能ADC中断
HAL_NVIC_SetPriority(ADC1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC1_IRQn);
}
void ADC1_IRQHandler(void) {
HAL_ADC_IRQHandler(&hadc);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
// ADC转换完成回调函数
uint32_t value = HAL_ADC_GetValue(hadc);
// 处理转换结果
// ...
}
2. ADC DMA配置
使用DMA可以将ADC转换结果自动传输到内存中,减轻CPU负担。以下是一个配置ADC DMA的示例:
#include "stm32g0xx_hal_dma.h"
// DMA配置结构体
DMA_HandleTypeDef hdma_adc;
// ADC数据缓冲区
uint32_t adc_buffer[10];
void ADC_DMA_Init(void) {
// 使能DMA时钟
__HAL_RCC_DMA1_CLK_ENABLE();
// DMA初始化结构体
hdma_adc.Instance = DMA1_Channel1;
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc.Init.Mode = DMA_CIRCULAR;
hdma_adc.Init.Priority = DMA_PRIORITY_HIGH;
// 初始化DMA
if (HAL_DMA_Init(&hdma_adc) != HAL_OK) {
// 初始化错误处理
Error_Handler();
}
// 将DMA句柄关联到ADC
__HAL_LINKDMA(&hadc, hdmaReg, hdma_adc);
}
void ADC_Start_DMA(void) {
// 启动ADC转换并启用DMA
if (HAL_ADC_Start_DMA(&hadc, (uint32_t *)adc_buffer, 10) != HAL_OK) {
// 启动错误处理
Error_Handler();
}
}
ADC应用示例
1. 读取温度传感器数据
假设我们使用一个温度传感器(如LM35),将其输出的模拟信号通过ADC读取并转换为温度值。
#include "stm32g0xx_hal.h"
// ADC配置结构体
ADC_HandleTypeDef hadc;
// ADC数据缓冲区
uint32_t adc_value;
void ADC_Init(void) {
// 使能ADC时钟
__HAL_RCC_ADC_CLK_ENABLE();
// ADC初始化结构体
hadc.Instance = ADC1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.ScanConvMode = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DMAContinuousRequests = DISABLE;
// 初始化ADC
if (HAL_ADC_Init(&hadc) != HAL_OK) {
Error_Handler();
}
// 配置采样时间
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
void ADC_Calibrate(void) {
if (HAL_ADCEx_Calibration_Start(&hadc, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {
Error_Handler();
}
}
void ADC_Start(void) {
if (HAL_ADC_Start(&hadc) != HAL_OK) {
Error_Handler();
}
}
uint32_t ADC_Read(void) {
if (HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
adc_value = HAL_ADC_GetValue(&hadc);
return adc_value;
}
float GetTemperature(uint32_t adc_value) {
// 假设LM35传感器输出电压与温度的关系为10mV/°C
float voltage = (adc_value * 3.3) / 4096.0; // 3.3V参考电压
float temperature = voltage / 0.01; // 10mV/°C
return temperature;
}
int main(void) {
HAL_Init();
SystemClock_Config(); // 配置系统时钟
ADC_Init();
ADC_Calibrate();
ADC_Start();
while (1) {
uint32_t adc_value = ADC_Read();
float temperature = GetTemperature(adc_value);
// 打印温度值
printf("Temperature: %.2f °C\r\n", temperature);
HAL_Delay(1000); // 1秒延迟
}
}
void SystemClock_Config(void) {
// 配置系统时钟
// ...
}
void Error_Handler(void) {
// 错误处理函数
// ...
}
数字到模拟转换器 (DAC)
DAC概述
STM32G0系列微控制器还集成了数字到模拟转换器(DAC),用于将数字信号转换为模拟信号。DAC在多种应用中非常有用,如音频生成、电压调节和信号生成等。STM32G0系列的DAC具有以下特点:
高精度:12位分辨率,确保高精度的数字信号转换。
多通道:支持多个输出通道。
多种工作模式:波形生成、噪声生成等。
低功耗:支持多种低功耗模式。
DAC硬件结构
STM32G0系列的DAC硬件结构如下:
DAC核心:负责将数字信号转换为模拟信号。
输出通道:支持多个输出通道,每个通道可以连接不同的模拟负载。
数据寄存器:存储要转换的数字值。
控制寄存器:用于配置DAC的工作模式、分辨率等。
波形生成寄存器:支持波形生成功能,如正弦波、三角波等。
DAC配置
1. DAC初始化
DAC的初始化步骤包括配置时钟、选择分辨率、配置输出通道等。以下是一个简单的初始化示例:
#include "stm32g0xx_hal.h"
// DAC配置结构体
DAC_HandleTypeDef hdac;
void DAC_Init(void) {
// 使能DAC时钟
__HAL_RCC_DAC1_CLK_ENABLE();
// DAC初始化结构体
hdac.Instance = DAC1;
hdac.Init.Resolution = DAC_RESOLUTION_12B; // 12位分辨率
hdac.Init.DataAlignment = DAC_DATAALIGN_RIGHT; // 右对齐
hdac.Init.OutPut = DAC_OUTPUT;
hdac.Init.Trigger = DAC_TRIGGER_NONE; // 无触发
// 初始化DAC
if (HAL_DAC_Init(&hdac) != HAL_OK) {
// 初始化错误处理
Error_Handler();
}
// 配置通道
DAC_ChannelConfTypeDef sConfig = {0};
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK) {
// 配置通道错误处理
Error_Handler();
}
}
2. DAC输出
通过HAL_DAC_SetValue
函数可以设置DAC的输出值。以下是一个设置DAC输出值的示例:
void DAC_SetValue(uint32_t value) {
// 设置DAC通道1的输出值
if (HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value) != HAL_OK) {
// 设置值错误处理
Error_Handler();
}
}
3. DAC中断和DMA
使用中断和DMA可以实现更复杂的DAC输出控制,如波形生成。以下是一个配置DAC中断的示例:
void DAC_Interrupt_Config(void) {
// 使能DAC中断
HAL_NVIC_SetPriority(DAC1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DAC1_IRQn);
}
void DAC1_IRQHandler(void) {
HAL_DAC_IRQHandler(&hdac);
}
void HAL_DAC_ConvCpltCallback(DAC_HandleTypeDef *hdac) {
// DAC转换完成回调函数
// 可以在这里处理转换完成后的事件
// ...
}
DAC应用示例
1. 生成正弦波
假设我们使用DAC生成一个正弦波信号。以下是一个生成正弦波的示例:
#include "stm32g0xx_hal.h"
#include <math.h>
// DAC配置结构体
DAC_HandleTypeDef hdac;
// 正弦波数据缓冲区
uint32_t sine_wave[100];
void DAC_Init(void) {
// 使能DAC时钟
__HAL_RCC_DAC1_CLK_ENABLE();
// DAC初始化结构体
hdac.Instance = DAC1;
hdac.Init.Resolution = DAC_RESOLUTION_12B;
hdac.Init.DataAlignment = DAC_DATAALIGN_RIGHT;
hdac.Init.OutPut = DAC_OUTPUT;
hdac.Init.Trigger = DAC_TRIGGER_NONE;
// 初始化DAC
if (HAL_DAC_Init(&hdac) != HAL_OK) {
Error_Handler();
}
// 配置通道
DAC_ChannelConfTypeDef sConfig = {0};
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
}
void GenerateSineWave(void) {
// 生成一个12位分辨率的正弦波
for (int i = 0; i < 100; i++) {
sine_wave[i] = (uint32_t)((sin(2 * M_PI * i / 100) + 1) * 2047.5);
}
}
void DAC_Output_SineWave(void) {
// 输出正弦波
for (int i = 0; i < 100; i++) {
DAC_SetValue(sine_wave[i]);
HAL_Delay(10); // 10ms延迟
}
}
int main(void) {
HAL_Init();
SystemClock_Config(); // 配置系统时钟
DAC_Init();
GenerateSineWave();
while (1) {
DAC_Output_SineWave();
}
}
void SystemClock_Config(void) {
// 配置系统时钟
// ...
}
void Error_Handler(void) {
// 错误处理函数
// ...
}
ADC和DAC的联合应用
1. 读取传感器数据并生成对应的模拟信号
假设我们有一个环境光传感器,读取其输出的模拟信号并通过ADC转换为数字值,然后根据该值生成对应的模拟信号输出到DAC。以下是一个联合应用的示例:
#include "stm32g0xx_hal.h"
#include <math.h>
// ADC配置结构体
ADC_HandleTypeDef hadc;
// DAC配置结构体
DAC_HandleTypeDef hdac;
// ADC数据缓冲区
uint32_t adc_value;
// DAC数据缓冲区
uint32_t dac_value;
void ADC_Init(void) {
// 使能ADC时钟
__HAL_RCC_ADC_CLK_ENABLE();
// ADC初始化结构体
hadc.Instance = ADC1;
hadc.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率
hadc.Init.ScanConvMode = DISABLE; // 单通道模式
hadc.Init.ContinuousConvMode = ENABLE; // 连续转换模式
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 右对齐
hadc.Init.NbrOfConversion = 1; // 1次转换
hadc.Init.DMAContinuousRequests = DISABLE; // 禁用DMA连续请求
// 初始化ADC
if (HAL_ADC_Init(&hadc) != HAL_OK) {
Error_Handler();
}
// 配置采样时间
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0; // 选择通道0
sConfig.Rank = 1; // 通道1
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 239.5个周期
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
void DAC_Init(void) {
// 使能DAC时钟
__HAL_RCC_DAC1_CLK_ENABLE();
// DAC初始化结构体
hdac.Instance = DAC1;
hdac.Init.Resolution = DAC_RESOLUTION_12B; // 12位分辨率
hdac.Init.DataAlignment = DAC_DATAALIGN_RIGHT; // 右对齐
hdac.Init.OutPut = DAC_OUTPUT; // 输出模式
hdac.Init.Trigger = DAC_TRIGGER_NONE; // 无触发
// 初始化DAC
if (HAL_DAC_Init(&hdac) != HAL_OK) {
Error_Handler();
}
// 配置通道
DAC_ChannelConfTypeDef sConfig = {0};
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
}
void ADC_Calibrate(void) {
// 校准ADC
if (HAL_ADCEx_Calibration_Start(&hadc, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {
Error_Handler();
}
}
void ADC_Start(void) {
// 启动ADC转换
if (HAL_ADC_Start(&hadc) != HAL_OK) {
Error_Handler();
}
}
uint32_t ADC_Read(void) {
uint32_t value;
// 等待ADC转换完成
if (HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 读取转换结果
value = HAL_ADC_GetValue(&hadc);
return value;
}
void DAC_SetValue(uint32_t value) {
// 设置DAC通道1的输出值
if (HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value) != HAL_OK) {
// 设置值错误处理
Error_Handler();
}
}
void SystemClock_Config(void) {
// 配置系统时钟
// ...
}
void Error_Handler(void) {
// 错误处理函数
// ...
}
int main(void) {
HAL_Init();
SystemClock_Config(); // 配置系统时钟
ADC_Init();
DAC_Init();
ADC_Calibrate();
ADC_Start();
while (1) {
// 读取ADC值
adc_value = ADC_Read();
// 根据ADC值生成对应的DAC值
// 假设ADC值范围为0-4095,对应0-3.3V
// 将ADC值直接输出到DAC
dac_value = adc_value;
// 设置DAC输出
DAC_SetValue(dac_value);
// 100ms延迟
HAL_Delay(100);
}
}
详细说明
1. ADC初始化
使能ADC时钟:通过__HAL_RCC_ADC_CLK_ENABLE()
函数使能ADC的时钟。
ADC初始化结构体:配置ADC的分辨率、工作模式、触发方式等。
配置采样时间:设置ADC通道的采样时间,确保信号的准确转换。
2. DAC初始化
使能DAC时钟:通过__HAL_RCC_DAC1_CLK_ENABLE()
函数使能DAC的时钟。
DAC初始化结构体:配置DAC的分辨率、输出模式、触发方式等。
配置通道:设置DAC通道的输出缓冲和采样保持模式。
3. 校准ADC
HAL_ADCEx_Calibration_Start()
函数进行ADC校准,提高转换精度。4. 启动ADC
HAL_ADC_Start()
函数启动ADC转换。5. 读取ADC值
等待ADC转换完成:通过HAL_ADC_PollForConversion()
函数等待ADC转换完成。
读取转换结果:通过HAL_ADC_GetValue()
函数读取转换后的数字值。
6. 设置DAC输出
HAL_DAC_SetValue()
函数设置DAC通道的输出值。应用场景
1. 环境光传感器读取
假设我们使用一个环境光传感器,其输出的模拟信号通过ADC读取并转换为数字值。这个数字值可以直接映射到DAC的输出值,从而生成一个对应的模拟信号。
2. 波形生成
除了简单的数字到模拟信号的转换,DAC还可以用于生成复杂的波形,如正弦波、三角波等。这些波形可以通过预计算的数组和定时器触发来实现。
总结
通过联合使用STM32G0系列的ADC和DAC,可以实现从模拟信号的读取到数字信号的处理,再到模拟信号的生成。这种功能在多种应用场景中非常有用,如传感器数据处理、音频信号生成和环境监测等。通过配置中断和DMA,可以进一步优化系统的性能和功耗。
作者:kkchenkx