图片均来自江科大STM32教学PPT
ADC
简介
- ADC(Analog-Digital Converter)模拟-数字转换器
- ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
- 12位逐次逼近型ADC,1us转换时间
- 输入电压范围:0~3.3V,转换结果范围:0~4095
- 18个输入通道,可测量16个外部和2个内部信号源
- 规则组和注入组两个转换单元
- 模拟看门狗自动监测输入电压范围
STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道
ADC框图
规则组通道:一次性转换16个通道,但是数据寄存器只能保存一个,所以要及时把数据读走,就可以利用DMA。
注入组通道:一次性转换四个通道,数据寄存器也可以存四个数据。
ADC基本结构
ADC输入通道
转换模式
单次转换,非扫描模式
在非扫描模式下,只有序列一有效,就变成了选中单一一个的情况,转换完成后置EOC为1,下一次再转换就需要再次触发。
连续转换,非扫描模式
在一次转换后,可以接着下一次转换
单次转换,扫描模式
可以依次转换多个通道,转换结果放到数据寄存器里,防止数据被覆盖,就需要用到DMA。
连续转换,扫描模式
多个通道加连续转换。
转换时间
- AD转换的步骤:采样,保持,量化,编码
- STM32 ADC的总转换时间为:
TCONV = 采样时间 + 12.5个ADC周期 - 例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs
校准
- ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
- 建议在每次上电后执行一次校准
- 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
代码示例
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
// 1.开启RCC时钟。包括ADC和GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADCCLK=72MHz/6=12MHz
// 2.配置GPIO,把需要用的GPIO配置成模拟输入的模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; // ADC专属模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 3.配置这里的多路开关。把左边的通道接入到右边的规则组列表里
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
// 4.配置ADC转换器
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent; // ADC工作模式
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; // 触发源:软件触发
ADC_InitStruct.ADC_ContinuousConvMode=DISABLE; // 单次转换
ADC_InitStruct.ADC_ScanConvMode=DISABLE; // 非扫描模式
ADC_InitStruct.ADC_NbrOfChannel=1;
ADC_Init(ADC1,&ADC_InitStruct);
// 5.启动ADC
ADC_Cmd(ADC1,ENABLE);
// 6.对ADC进行校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);
}
uint16_t AD_GetValue(void)
{
// 软件触发转换
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
return ADC_GetConversionValue(ADC1);
}
DMA
简介
- DMA(Direct Memory Access)直接存储器存取
- DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
- 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
- 每个通道都支持软件触发和特定的硬件触发
STM32F103C8T6 DMA资源:DMA1(7个通道)
STM32存储器映像
DMA框图
DMA基本结构
代码示例
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
MyDMA_Size=Size;
// 1.RCC开启DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
// 2.初始化DMA参数
DMA_InitTypeDef DMA_InitStruct;
// 外设站点配置
DMA_InitStruct.DMA_PeripheralBaseAddr=AddrA; //外设站点基地址 32位
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;
// 存储器站点配置
DMA_InitStruct.DMA_MemoryBaseAddr=AddrB;
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
// 传输方向配置
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;
// 传输次数配置
DMA_InitStruct.DMA_BufferSize=Size;
// 传输计数器配置
DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;
DMA_InitStruct.DMA_M2M=DMA_M2M_Enable;
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium; //中等优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
// 3.开启DMA
DMA_Cmd(DMA1_Channel1,DISABLE);
}
// 启动DMA转运
void MyDMA_Trandfer(void)
{
// 先失能
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
DMA_Cmd(DMA1_Channel1,ENABLE);
// 等待转运完成,标志位置1
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}
DMA循环转运+ADC连续扫描
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_Init(void)
{
// 1.开启RCC时钟。包括ADC和GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADCCLK=72MHz/6=12MHz
// 2.配置GPIO,把需要用的GPIO配置成模拟输入的模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; // ADC专属模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 3. 配置这里的多路开关。把左边的通道接入到右边的规则组列表里
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
// 4.配置ADC转换器
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent; // ADC工作模式
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; // 触发源:软件触发
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; // 单次转换
ADC_InitStruct.ADC_ScanConvMode=ENABLE; // 非扫描模式
ADC_InitStruct.ADC_NbrOfChannel=4;
ADC_Init(ADC1,&ADC_InitStruct);
// 2.初始化DMA参数
DMA_InitTypeDef DMA_InitStruct;
// 外设站点配置
DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设站点基地址 32位
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
// 存储器站点配置
DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
// 传输方向配置
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;
// 传输次数配置
DMA_InitStruct.DMA_BufferSize=4;
// 传输计数器配置
DMA_InitStruct.DMA_Mode=DMA_Mode_Circular; // 打开DMA循环模式
DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium; //中等优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
// 3.开启DMA
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_DMACmd(ADC1,ENABLE);
// 5.启动ADC
ADC_Cmd(ADC1,ENABLE);
// 6.对ADC进行校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);
// ADC软件触发
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}