目录STM32 ADC单次转换模式、连续转换模式扫描模式不启动SCAN模式在单次转换模式下:在连续转换模式下:启动SCAN模式下在单次转换模式下:在连续转换模式下STM32-ADC多通道数据采集多通道采集模式----轮询:多通道采集模式----DMA:DMA简介:使用DMA的步骤:DMA转运的三个条件:
此随笔出自
对STM32 ADC单次转换模式、连续转换模式扫描模式的理解
STM32-ADC多通道数据采集
STM32 ADC单次转换模式、连续转换模式扫描模式
举例
用ADC1 规则通道的顺序为CH0,CH1,CH2,CH3
不启动SCAN模式
在单次转换模式下:
启动ADC1,则
1.开始转换CHO(ADC SQR的第一通道)
2.转换完成后停止,等待ADC的下一次启动,继续从第一步开始转换
在连续转换模式下:
启动ADC1,则
1.开始转换CHO(ADC SQR的第一通道)
2.转换完成后回到第一步。
启动SCAN模式下
在单次转换模式下:
启动ADC1,则
1.开始转换CHO.
2.转换完成后自动开始转换CH1
3.转换完成后自动开始转换CH2
4.转换完成后自动开始转换CH3
5.转换完成后停止,等待ADC的下一次启动下一次ADC启动后从第一步开始转换
在连续转换模式下
启动ADC1,则
1.开始转换CHO.
2.转换完成后自动开始转换CH1
3.转换完成后自动开始转换CH2
4.转换完成后自动开始转换CH3
5.转换完成后返回第一步
STM32-ADC多通道数据采集
多通道采集模式----轮询:
1.连续转换,扫描模式(手动转换)。在扫描模式下启动列表之后,里面每一个单独的通道转换完成后不会产生任何标志位、和中断。它只有在整个列表都转换完成后才会产生一次EOC标志位,才能触发中断。这种方法容易造成数据覆盖的问题如果想要用扫描模式实现多通道采集,最好要配合DMA来实现。
其他解决方法:
在扫描的时候每转换一个通道就暂停一次,我们可以等手动把数据转运走之后在继续触发,继续下一次的转换。就能避免数据覆盖的问题但是这种方式效率较低(不建议使用)。
2.单次转换,非扫描模式:在每次触发转换前,我们可以手动更改一下列表的第一个位置的通道就行这种我们可以通过代码来实现。
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);//采样不同的通道直接修改第三个参数
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
主函数:
uint16_t AD_GetValue(uint8_t ADC_Channel)
int main(void)
{
AD_Init();
Usart_Init();
while (1)
{
AD0 = AD_GetValue(ADC_Channel_0);
AD1 = AD_GetValue(ADC_Channel_1);
AD2 = AD_GetValue(ADC_Channel_2);
AD3 = AD_GetValue(ADC_Channel_3);
printf("ADC0: %d ADC1:%d ADC2: %d ADC3: %d\r\n",AD0, AD1, AD2, AD3);
Delay_ms(500);
}
}
多通道采集模式----DMA:
DMA简介:
1、DMA(Direct Memory Access,直接存储器访问)是一种硬件机制,它可以在不需要CPU干预的情况下实现外设直接和存储器之间的数据传输。在STM32中,DMA可以用于加速数据传输,减少CPU占用率,提高系统性能。DMA控制器是一个独立的外设,它可以与其他外设相连,如ADC、DAC、USART、SPI、I2C等。DMA控制器可以直接访问存储器,而不必通过CPU,从而大大提高数据传输效率。
2、12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
3、每个通道都支持软件触发和特定的硬件触发(本文采用的是硬件触发方式)
4、STM32F103RCT6 DMA资源:DMA1(6个通道)、DMA2(1个通道)
使用DMA的步骤:
1、初始化DMA控制器,包括设置数据传输方向、数据宽度、传输模式等参数。
2、配置DMA传输的源地址和目的地址。
3、配置传输数据的数量。
4、启动DMA传输。
DMA转运的三个条件:
1.传输计数器不为零。
2.触发源有信号。
3.DMA使能
DMA.c
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
MyDMA_Size = Size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//外设站点的基地址,一般为32位的地址。
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度,这里是一个字节的(宽度为8位)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//地址是否自增(自增)
//存储器站点的三个参数
DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器站点的基地址,一般为32位的地址。
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//数据宽度,这里是一个字节的(宽度为8位)
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//地址是否自增(自增)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//DST: destination(目的地)传输方向为存储器到外设站点。SRC:source(源头),外设站点到存储器站点的存储方向。
DMA_InitStructure.DMA_BufferSize = Size;//以数据单元,指定缓存区的大小。
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//指定传输寄存器是否要自动重装,(DMA_Mode_Circular :循环模式,自动重装。DMA_Mode_Normal:正常模式,不自动重装)
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//DMA是否应用于存储器到存储器的转运(使用软件触发)
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//指定通道的优先级(中等优先级)
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, DISABLE);//DMA失能
}
ADC单次扫描+DMA单次运转:
ADC配置函数:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
DMA配置:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;// 存储器站点的基地址,这为32位的地址。
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//低16位adc1所以未一半
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//存储器站点的基地址,这为32位的地址。
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 3;//以数据单元,指定缓存区的大小未3。
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
整体合并:
#include "stm32f10x.h" // Device header
uint16_t AD_Value[3];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 3;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
}
void Get_Value(void)
{
DMA_Cmd(DMA1_Channel1, DISABLE);//失能
DMA_SetCurrDataCounter(DMA1_Channel1, 3);//给传输计数器负值参数dataNumber:指定给传输计数器写入的值
DMA_Cmd(DMA1_Channel1, ENABLE);//使能
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//查看标志位
DMA_ClearFlag(DMA1_FLAG_TC1);//清除标志位
}
main.c
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
while (1)
{
Get_Value();
OLED_ShowNum(1, 5, AD_Value[0], 3);
OLED_ShowNum(2, 5, AD_Value[1], 3);
OLED_ShowNum(3, 5, AD_Value[2], 3);
Delay_ms(100);
}
}
ADC连续扫描和DMA循环转运模式
#include "stm32f10x.h" // Device header
uint16_t AD_Value[3];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 3;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
main.c
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
while (1)
{
// Get_Value();
OLED_ShowNum(1, 5, AD_Value[0], 4);
OLED_ShowNum(2, 5, AD_Value[1], 4);
OLED_ShowNum(3, 5, AD_Value[2], 4);
Delay_ms(100);
}
}