一、ADC 介绍
ADC (analog-to-digital converter):模数转换器 - 将模拟信号转换为数字信号
模拟信号 :连续周期变化的信号,在时间和数值上都是连续变化的物理量。
数字信号 :以离散的 0 和 1 组成的信号,在时间和数值上都是离散的物理量。
图 1 模拟信号和数字信号
ADC 一般使用在电路中电流和电压的检测,以及传感器中数据的转换
STM32U575RIT6 可以使用 ADC1 和 ADC4 两个外设控制器进行模数转换
二、ADC1 和 ADC4
2.1 模数转换原理
图 2 ADC1 和 ADC4
ADC 的采样位数:本质是采样精度,是将模拟信号转化为数字信号后的位数;
ADC1 转化后的数字信号是 14 位,ADC4 转化后的数字信号是 12 位
数据寄存器:存储转换后的数字信号
图 3 逐次逼近法原理图
图 4 逐次逼近法原理
ADC 转化使用的是 逐次逼近法
逐次逼近法:循环使用二分查找(折半查找)
AD4 的转化数字信号时 12 位,转换后的数字信号范围时 0 ~ 2¹²-1
2.2 模数转换公式
以 电压模拟信号转换 -> 电压数字信号 为例,使用 12 位的 ADC4
cppADC转换后的数字信号 = 4096 / 3.3v * 1.2v
4096:采样位数的 ADC 最大转化范围(2¹²=4096,2¹⁴=16384)
3.3v:参考电压 / 供电电压
1.2v:待测电压
cpp待测电压的数字信号 = (2^(ADC的采样位数) / 参考电压) * 待测电压的模拟信号
2.3 采样时机和偏移补偿
图 5 采样时机和偏移补偿图示
采样时机指的是ADC的采样保持电路在何时对模拟输入信号进行"采样"(捕获瞬时电压值)的精确时间点。
偏移补偿,也称为失调电压校准,目的是消除ADC输出代码中存在的固定的、系统的直流偏差。如果ADC采样发生偏移,此时采样得到的数据可能不是很准确,就需要使用硬件提供的偏移补偿机制(校准机制)初始化采样时机。
三、ADC 采样模式
3.1 ADC1 的内部框图
图 6 ADC1 连接图
图 7 ADC1 描述
3.2 ADC4 内部框图
图 8 ADC4 连接图
图 9 ADC4 描述
3.3 ADC 的转化模式(采样模式)
图 10 ADC 的五种采样模式
单通道单次转化模式:
只使用 ADC 模数转换器中的一个独立通道,将输入到通道中的模拟信号转换为数字信号,并只转换一次就结束。
单通道连续转换模式:
只使用 ADC 模数转换器中的一个独立通道,将输入到通道中的模拟信号转换为数字信号,结束一次后,自动进行下一次转化。
单通道连续转化模式 = while(1) + 单通道单次转化模式
多通道单次转化模式:
使用 ADC 模数转换器的多个通道,并都输入模拟信号,ADC 转换器根据通道的顺序,依次转换为数字信号,每个通道只转换一次。
多通道连续转换模式:
使用 ADC 模数转换器的多个通道,并都输入模拟信号,ADC 转换器根据通道的顺序,依次转换为数字信号,所有通道转化完后,回到初始通道重新依次转换。
多通道连续转化模式 = while(1) + 多通道单次转化模式
间隔转化模式(间隔触发模式):
给对应通道一个触发信号(触发源)后,ADC 模数转换器开始一次对通道中的模拟信号转换为数字信号。
ADC的转化模式:独立模式(单通道模式)、扫描模式(多通道模式)、触发模式
四、分析电路图
图 11 电路图分析
拓展板电压检测引脚:PB1
五、CubeMX 配置
图 12 配置 PB1 引脚为 ADC4_IN19
图 13 参数设置
- ADCs_Common_Settings(ADC 通用设置)
- Mode(模式):Independent mode(独立模式(每个通道都是独立的))
- ADC_Settings(ADC 设置)
- Clock Prescaler(时钟分频):Asynchronous clock mode divided by 1(时钟分频(时钟频率 -> 影响 ADC 模数转化的速率))
- Resolution(采样精度):ADC 12 - bit resolution(采样精度(转化后的数字信号的位数):12 位)
- Data Alignment(数据对齐方式):Right alignment(数据对齐方式:右对齐)
- Sequencer(序列器):Sequencer set to not fully configurable
- Scan Conversion Mode(输入转化模式):Forward(输入转化模式:标准模式(标准转化速率模式))
- Continuous Conversion Mode(连续转化模式):Disabled
- Discontinuous Conversion Mode(不连续转化模式):Disabled
- DMA Continuous Requests(DMA 连续请求):Disabled
- End Of Conversion Selection(转换结束选择):End of single conversion
- Overrun behaviour(数据保存的机制):Overrun data preserved(数据保存的机制:转化后的数字信号被存储到数据寄存器中可以使用追加 / 覆盖的方式)
- Low Power Auto Wait(低功耗自动等待):Disabled
- Low Power Auto Off(低功耗自动关闭):Low power disabled and auto off disabled
- SamplingTime Common 1(通用采样时间 1):1.5 Cycles(通用的采样时间:1.5 轮转化一次,轮的具体时间需要根据时钟频率来计算 理论上,采样时间越长,采样得到的数据越准确)
- SamplingTime Common 2(通用采样时间 2):1.5 Cycles
- Trigger Frequency(触发频率):Low frequency
- External Trigger Conversion Source(外部触发转换源):Regular Conversion launched by software(使用间隔触发模式时,需要设置)
- External Trigger Conversion Edge(外部触发转换沿):None
- Oversampling Mode(过采样模式):Disabled
- Sampling Time(采样时间):Sampling time common 1(使用采样时间 1)
六、API 接口
ADC 采样和转化流程:
- 开启 ADC 采样校准模式
- 开启 ADC 采样转化功能
- 等待采样+转化结束,数字信号被存放到数据寄存器中
- 从数据寄存器中读取数字信号
- 通过模数转化公式,将数字信号转换为模拟信号,并在串口终端打印
注意:ADC 的校准采样必须在开启采样前或者关闭采样后
6.1 HAL_ADC_Start 函数
cppHAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)
功能:
HAL库提供的用于开启ADC采样转化功能的函数
参数:
hadc:ADC4外设控制器的句柄
返回值:
函数执行成功,返回HAL_OK
函数执行失败,返回错误码
6.2 HAL_ADC_GetValue 函数
cppuint32_t HAL_ADC_GetValue(const ADC_HandleTypeDef *hadc)
功能:
HAL库提供的用于获取转化完毕的数字信号的函数
参数:
hadc:ADC4外设控制器的句柄
返回值:
返回成功获取的数字信号
6.3 HAL_ADC_PollForConversion 函数
cppHAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef *hadc, uint32_t Timeout)
功能:
HAL库提供的用于阻塞等待ADC采样+转化完毕,数据被存放到数据寄存器中的函数
参数:
hadc:ADC4外设控制器的句柄 Timeout:超时检测时间,当前函数的最大阻塞时间
返回值:
当ADC采样+转化数据完毕后,表示这个函数执行成功,函数执行成功,返回HAL_OK
当ADC采样+转化数据没结束时,此时这个函数处于阻塞状态
函数执行失败,返回错误码
HAL_MAX_DELAY 宏
cpp#define HAL_MAX_DELAY 0xFFFFFFFFU
6.4 HAL_ADC_Stop 函数
cppHAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef *hadc)
功能:
HAL库提供的用于关闭ADC采样转化功能的函数
参数:
hadc:ADC4外设控制器的句柄
返回值:
函数执行成功,返回HAL_OK
函数执行失败,返回错误码
6.5 HAL_ADCEx_Calibration_Start 校准采样函数
cppHAL_StatusTypeDef HAL_ADCEx_Calibration_Start (ADC_HandleTypeDef *hadc, uint32_t CalibrationMode, uint32_t SingleDiff)
功能:
HAL库提供的用于校准ADC采样的函数(此函数只需要开启一次即可)
参数:
hadc:ADC4外设控制器的句柄
CalibrationMode:校准模式的设置
ADC_CALIB_OFFSET 偏移量校准方式(最基础的)
ADC_CALIB_OFFSET_LINEARITY 硬件线性化校准(高级的) 线性化校准比偏移量校准更准确,一般使用一些高精度仪器上
SingleDiff:输入的模拟信号的选择
ADC_SINGLE_ENDED:输入单端信号(一个信号就代表一个电信号) ADC_DIFFERENTIAL_ENDED:输入差分信号(两个信号代表一个电信号)
返回值:
函数执行成功,返回HAL_OK
函数执行失败,返回错误码
七、代码编写
7.1 单通道单次转换模式
1)main.c 重写 fputc 函数
cpp
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE* stream)
{
HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,5);
return ch;
}
/* USER CODE END 0 */
八、DMA 介绍
DMA (General purpose direct memory access controller):通用可编程直接内存访问控制器
内存:广义的内存,包括虚拟内存地址和物理内存地址
DMA 本质是硬件缓冲区(FIFO)
DMA 的作用
减轻 CPU 的压力,将简单的数据存储工作交给 DMA 去做,让 CPU 只负责逻辑处理和数据处理。
图 1 DMA 图示
GPDMA 支持16 个独立通道,每个通道都可以用来独立暂存数据
GPDMA 的通道用于数据的暂存和传输
单通道传输模式:使用一个通道进行数据传输
多通道传输模式:使用多个通道进行数据传输
同一时刻,只能使用一个 DMA 通道进行数据传输
- 使用 DMA 通道传输数据前,需要向 DMA 发送使用请求
谁先发送 DMA 通道请求,谁先使用 DMA 通道
- DMA 通道具有优先级等级,优先级等级高的 DMA 通道线传输数据,分为 4 个等级:
(1)低优先级
(2)中优先级
(3)高优先级
(4)超高优先级
使用 DMA 通道传输数据,一端是 Source 源设备(发送端), 一端是 Destination 目标设备(接收端)
使用 DMA 通道传输数据时,有 4 种数据传输方向:
(1)
十一、代码编写
cpp
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t buf[1024];
//中断标志
volatile int flag=0;
int fputc(int ch,FILE* stream)
{
HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,1);
return ch;
}
/* USER CODE END 0 */
cpp
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
//开启串口使用DMA通道接收中断
HAL_UART_Receive_DMA(&huart1,buf,sizeof(buf));
//开启空闲中断
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(flag==1)
{
printf("buf=%s\n",buf);
memset(buf,0,sizeof(buf));
flag=0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
cpp
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
extern volatile int flag;
extern uint8_t buf[1024];
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF);
//手动关闭DMA通道
HAL_UART_DMAStop(&huart1);
//逻辑代码
flag=1;
//重新开启
HAL_UART_Receive_DMA(&huart1,buf,sizeof(buf));
}
/* USER CODE END USART1_IRQn 1 */
}