打印
[应用相关]

stm32 ADC全解(单次,连续DMA传输)

[复制链接]
765|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hehhehee|  楼主 | 2022-1-25 23:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
学习STM32的ADC转换,在开发板上写程序调试。
四个任务:
四个任务:
1.AD以中断方式(单次)采集一路
2.AD以中断方式连续采集四路
3.ADC多路采集,DMA传输,DMA深度为一级
4. ADC 以DMA 方式采集四路,每路DMA 深度为28 级,并滤波,说明滤波原理




使用特权

评论回复
沙发
hehhehee|  楼主 | 2022-1-25 23:14 | 只看该作者
第一个任务
: ADC以中断方式采集一路ADC,通过配置ADC_InitStructure结构体中的ADC_ScanConvMode,它规定模数转换工作在扫描模式(多通道)还是单次模式(单通道),
ADC_InitStructure.ADC_ScanConvMode=DISABLE,为单通道单次模式。
ADC_ContinuousConvMode,定转换是连续还是单次,ADC_ContinuousConvMode=DISABLE
为单次,ADC_NbrOfChangnel规定ADC规则转换的通道数。ADC_NbrOfChannel=1;//开启1个通道数。

使用特权

评论回复
板凳
hehhehee|  楼主 | 2022-1-25 23:15 | 只看该作者
ADC_RegularChannelConfig(ADC1,ADC_Channel_13, 1,ADC_SampleTime_55Cycles5);设置指定规则组的通道的采样顺序和转换时间。这里以为只有一路通道,采用的是PC3引脚,对应的通道数是13通道,采样顺序也就是1,。
ADC_Cmd(ADC1,ENABLE);使能ADC
ADC_ITConfig(ADC1, ADC_IT_EOC,ENABLE);开启ADC转换结束中断。
ADC_ResetCalibration(ADC1);//重置校验寄存器
while(ADC_GetResetCalibrationStatu s(ADC1));  //等待重置校验成功
ADC_StartCalibration(ADC1);//开始ADC校验
while(ADC_GetCalibrationStatus(ADC1));//等待ADC校验好
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发开始转换
因为ADC有一个16位的规则组数据寄存器(ADC_DR),采用一路转换时可以不用通过DMA传输。这里就没有配置DMA。
void ADC_IRQHandler(void)
{
       ADCConvertedValue=ADC_GetConversionValue(ADC1);
       ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);            
}

使用特权

评论回复
地板
hehhehee|  楼主 | 2022-1-25 23:21 | 只看该作者
当一次转换结束,DAC产生中断,在中断函数里,读取ADC_DR寄存器中的值,一定清除中断标志位。
采集出来的数据是16进制数,要经过处理,变成10进制数,具体如下:
(value*100/4096)*33,value是从寄存器读出来的十六进制的数据,经过此变换后就变成10进制数,是个整数,我们通过串口显示的时候要把小树部分也要显示出来则有:((value*100/4096)*33)/1000,整数部分。
((value*100/4096)*33)00/100,((value*100/4096)*33)0/10),小数部分,
串口配置,我是通过STM32上的串口1与PC机通讯的,具体配置如下:

使用特权

评论回复
5
hehhehee|  楼主 | 2022-1-25 23:22 | 只看该作者
void USART_Configuration(void)
{
       USART_InitTypeDef USART_InitStructure;
       USART_InitStructure.USART_BaudRate=9600;波特率9600
      USART_InitStructure.USART_WordLength=USART_WordLength_8b;//8位数据位
      USART_InitStructure.USART_StopBits=USART_StopBits_1;1个停止位
      USART_InitStructure.USART_Parity=USART_Parity_No;无奇偶校验
      USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;       USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
       USART_Init(USART1,&USART_InitStructure);初始化串口配置
       USART_Cmd(USART1,ENABLE);使能串口
}
int fputc(int ch,FILE *f)
{
       USART_SendData(USART1, (u8)ch);
       while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET)//检查发送是否完成
       {
       }
       return ch;
}此函数,是把printf输出函数定向到USART。

使用特权

评论回复
6
hehhehee|  楼主 | 2022-1-25 23:23 | 只看该作者
第一个任务大概就是这个过程,在后面的任务有相同之处,就不重复叙述了。

使用特权

评论回复
7
hehhehee|  楼主 | 2022-1-25 23:24 | 只看该作者
第二个任务

ADC以中断方式连续采集四路。
首先配置4路模拟输入,我配置的是PC0、PC1、PC2、PC3四个IO口,输入方式为模拟输入,速度采用2M,它们对应的ADC通道分别是10、11、12、13通道。
在第一个任务说了,ADC规则转换多路采样时,ADC的数据寄存器只有一个16位寄存器,所以必须采用DMA来传输数据,DMA配置如下:
DMA_InitStructure.DMA_PeripheralBaseAddr=DR_ADDRESS; //DMA对应的外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr=(u32)&Buf; //内存存储基地址,定义的一个数组
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; //DMA转换模式为SRC模式,由外设搬移到内存
DMA_InitStructure.DMA_BufferSize=4; // DMA缓存大小,4个(设置DMA在传输时缓冲区的长度)
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //接收一次数据后,设备地址禁止后移(设置DMA的外设递增模式)
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //关闭接收一次数据后,目标内存地址后移(设置DMA的内存递增模式)
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//定义外设数据长度
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
//循环模式开启,Buf写满后,自动回到初始地址开始传输
DMA_InitStructure.DMA_Priority=DMA_Priority_High;//优先级高
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;

使用特权

评论回复
8
hehhehee|  楼主 | 2022-1-25 23:32 | 只看该作者
ADC配置:
//ADC配置
       ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//独立转换模式
      ADC_InitStructure.ADC_ScanConvMode=ENABLE;//开启扫描模式
      ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//开启连续转换模式
      ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//ADC外部开关,关闭状态
      ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//对齐方式,右对齐方式
      ADC_InitStructure.ADC_NbrOfChannel=4;//开启通道数,4个
       ADC_Init(ADC1,&ADC_InitStructure);//初始化ADC
       ADC_RegularChannelConfig(ADC1,ADC_Channel_10,1,ADC_SampleTime_55Cycles5);
       ADC_RegularChannelConfig(ADC1,ADC_Channel_11,2,ADC_SampleTime_55Cycles5);
       ADC_RegularChannelConfig(ADC1, ADC_Channel_12,3,ADC_SampleTime_55Cycles5);
       ADC_RegularChannelConfig(ADC1,ADC_Channel_13,4,ADC_SampleTime_55Cycles5);;
       //ADC通道组,第10、11、12、13个通道,采样顺序分别是1,2,3,4转换时间55.5个周期
       ADC_DMACmd(ADC1, ENABLE);//使能ADC1模块DMA
       ADC_Cmd(ADC1, ENABLE);//打开ADC1
       ADC_ResetCalibration(ADC1);//重置ADC1校准寄存器
       while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校准重置完成
       ADC_StartCalibration(ADC1);//开始ADC1校准
       while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校准完成
       ADC_SoftwareStartConvCmd(ADC1,ENABLE);//使能ADC1软件开始转换
中断是采用DMA中断,当DMA第一轮传输结束时,设一个标志位,当标志位为1时,表明第一轮转化和传输完成,此时就可以读取数组中的数据,经过处理就可以通过串口显示出来。

使用特权

评论回复
9
hehhehee|  楼主 | 2022-1-25 23:33 | 只看该作者
void DMAChannel1_IRQHandler(void)
{         
              ADC_DMA_OK=1;
              DMA_ClearITPendingBit(DMA1_IT_TC1);              
}中断函数.

使用特权

评论回复
10
hehhehee|  楼主 | 2022-1-25 23:34 | 只看该作者
第二个任务结束

使用特权

评论回复
11
hehhehee|  楼主 | 2022-1-25 23:36 | 只看该作者
第三个任务:
AD以DMA方式采集一路,DMA深度为一级。
这个任务不难,关键要理解到DMA深度,用自己的语言来理解哈DMA深度吧,当ADC以一路采集时,ADC转换完成就自动把转换结果通过DMA传给目的地址,如果传输一次结束DMA就产生中断的话,DMA的深度就为一级,如果连续传输N次,DMA的深度就位N级,当然这个N是又范围的,因为受目的地址的内存大小控制和数据宽度,这个大家应该豆明白的。
这个任务在第一个任务的基础上我通过DMA传输,意思是AD配置没什么区别。DMA配置和第二个任务的区别就是DMA_BufferSize的宽度不同。
#define  DR_ADDRESS    (u32)0x4001244c ADC的地址
#define  DMA_Count     1   DMA深度,也就是连续传输的次数
#define  ADC_Channle  1  ADC通道
数据处理和串口通讯这里不重复叙述。DMA中断和任务二的类似。

使用特权

评论回复
12
hehhehee|  楼主 | 2022-1-25 23:49 | 只看该作者
第四个任务:
AD以DMA方式采集四路,每路DMA深度为128级,并滤波,说明滤波原理。
这个任务和是个综合性任务,只要弄懂前面三个任务,难点是再如何滤波,开始的时候我也不知道怎么滤波,同事提醒我才知道怎么滤波的,我大概说哈我的理解,把四路通道采集的数据分别放到四个数组中,然后给他来个排序,降序,升序都行,把首位两个数丢掉,然后加起来求平均值。但是我这里因为DMA的深度为128级,也就是四个通道分别采样了128次,大家都知道,数据越多,求平均值就越准确,所以我就没有采用什么排序法了,直接给他们分别求平均值,具体如下:
#define  DR_ADDRESS    (u32)0x4001244c ADC的地址
#define  DMA_Count     128   DMA深度,也就是连续传输的次数

使用特权

评论回复
13
hehhehee|  楼主 | 2022-1-25 23:50 | 只看该作者
#define  ADC_Channle  4  ADC通道
       for(i=0;i<(ADC_Channle*DMA_Count);i+=4)
                     {                  
                            Value1[j]=Buf[i+0];
                            Sum1+=Value1[j];                     
                            Value2[j]=Buf[i+1];
                            Sum2+=Value2[j];                     
                            Value3[j]=Buf[i+2];
                            Sum3+=Value3[j];
                            Value4[j]=Buf[i+3];
                            Sum4+=Value4[j];
                            j++;
                     }           
              Valu1=Sum1/DMA_Count;
              Valu2=Sum2/DMA_Count;
              Valu3=Sum3/DMA_Count;
              Valu4=Sum4/DMA_Count;
              Delay(100000);     
              printf("\r\n当前AD_0值:0x%x,电压值:%d.%d%dV\n\r",
              Valu1,((Valu1*100/4096)*33)/1000,((Valu1*100/4096)*33)00/100,((Valu1*100/4096)*33)0/10);
              Delay(100000);
              printf("\r\n当前AD_1值:0x%x,电压值:%d.%d%dV\n\r",
              Valu2,((Valu2*100/4096)*33)/1000,((Valu2*100/4096)*33)00/100,((Valu2*100/4096)*33)0/10);
              Delay(100000);
              printf("\r\n当前AD_2值:0x%x,电压值:%d.%d%dV\n\r",
              Valu3,((Valu3*100/4096)*33)/1000,((Valu3*100/4096)*33)00/100,((Valu3*100/4096)*33)0/10);
              Delay(100000);
              printf("\r\n当前AD_3值:0x%x,电压值:%d.%d%dV\n\r",
              Valu4,((Valu4*100/4096)*33)/1000,((Valu4*100/4096)*33)00/100,((Valu4*100/4096)*33)0/10);
              Delay(100000);

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

934

帖子

0

粉丝