STM32学习:ADC/DMA/USART

发布者:hxcp18最新更新时间:2023-07-26 来源: elecfans关键字:STM32  ADC  DMA  USART 手机看文章 扫描二维码
随时随地手机看文章

  学习STM32的ADC转换,在开发板上写程序调试。

  四个任务:

  1.AD以中断方式(单次)采集一路

  2.AD以中断方式连续采集四路

  3.AD以DMA方式采集一路,DMA深度为一级

  4.AD以DMA方式采集四路,每路DMA深度为28级,并滤波,说明滤波原理。

  总结:

  第一个任务:ADC以中断方式采集一路ADC,通过配置ADC_InitStructure结构体中的ADC_ScanConvMode,它规定模数转换工作在扫描模式(多通道)还是单次模式(单通道),

  ADC_InitStructure.ADC_ScanConvMode=DISABLE,为单通道单次模式。

  ADC_ContinuousConvMode,定转换是连续还是单次,ADC_ContinuousConvMode=DISABLE

  为单次,ADC_NbrOfChangnel规定ADC规则转换的通道数。ADC_NbrOfChannel=1;//开启1个通道数。

  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_GetResetCalibrationStatus(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);

  }

  当一次转换结束,DAC产生中断,在中断函数里,读取ADC_DR寄存器中的值,一定清除中断标志位。

  采集出来的数据是16进制数,要经过处理,变成10进制数,具体如下:

  (value*100/4096)*33,value是从寄存器读出来的十六进制的数据,经过此变换后就变成10进制数,是个整数,我们通过串口显示的时候要把小树部分也要显示出来则有:((value*100/4096)*33)/1000,整数部分。

  ((value*100/4096)*33)%1000/100,((value*100/4096)*33)%100/10),小数部分,

  串口配置,我是通过STM32上的串口1与PC机通讯的,具体配置如下:

  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。

  第一个任务大概就是这个过程,在后面的任务有相同之处,就不重复叙述了。

  第二个任务: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;

  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时,表明第一轮转化和传输完成,此时就可以读取数组中的数据,经过处理就可以通过串口显示出来。

  void DMAChannel1_IRQHandler(void)

  {

  ADC_DMA_OK=1;

  DMA_ClearITPendingBit(DMA1_IT_TC1);

  }中断函数。

  第二个任务大概就这样子。

  第三个任务: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中断和任务二的类似。

  第四个任务:AD以DMA方式采集四路,每路DMA深度为128级,并滤波,说明滤波原理。

  这个任务和是个综合性任务,只要弄懂前面三个任务,难点是再如何滤波,开始的时候我也不知道怎么滤波,同事提醒我才知道怎么滤波的,我大概说哈我的理解,把四路通道采集的数据分别放到四个数组中,然后给他来个排序,降序,升序都行,把首位两个数丢掉,然后加起来求平均值。但是我这里因为DMA的深度为128级,也就是四个通道分别采样了128次,大家都知道,数据越多,求平均值就越准确,所以我就没有采用什么排序法了,直接给他们分别求平均值,具体如下:

  #define DR_ADDRESS (u32)0x4001244c ADC的地址

  #define DMA_Count 128 DMA深度,也就是连续传输的次数

  #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("rn当前AD_0值:0x%x,电压值:%d.%d%dVnr",

  Valu1,((Valu1*100/4096)*33)/1000,((Valu1*100/4096)*33)%1000/100,((Valu1*100/4096)*33)%100/10);

  Delay(100000);

  printf("rn当前AD_1值:0x%x,电压值:%d.%d%dVnr",

  Valu2,((Valu2*100/4096)*33)/1000,((Valu2*100/4096)*33)%1000/100,((Valu2*100/4096)*33)%100/10);

  Delay(100000);

  printf("rn当前AD_2值:0x%x,电压值:%d.%d%dVnr",

  Valu3,((Valu3*100/4096)*33)/1000,((Valu3*100/4096)*33)%1000/100,((Valu3*100/4096)*33)%100/10);

  Delay(100000);

  printf("rn当前AD_3值:0x%x,电压值:%d.%d%dVnr",

  Valu4,((Valu4*100/4096)*33)/1000,((Valu4*100/4096)*33)%1000/100,((Valu4*100/4096)*33)%100/10);

  Delay(100000);


关键字:STM32  ADC  DMA  USART 引用地址:STM32学习:ADC/DMA/USART

上一篇:详细图文剖析STM32单片机串口一键下载电路与操作方法
下一篇:STM32CUBEMX开发GD32F303(4)----GPIO输出模式

推荐阅读最新更新时间:2024-11-11 21:37

STM32 RTC与待机模式
1、背景 最近用到低功耗模式-待机模式和RTC唤醒,因此重新梳理RTC和睡眠模式--待机模式。 1.1什么是待机模式 待机模式: 基于CortexTM-M4F深度睡眠模式,其中调压器被禁止,因此1.2V域断电---PLL、HSI振荡器和HSE振荡器也将关闭。 除备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM和寄存器内容都将丢失。 从待机模式唤醒后, 除了备份域+PWR电源控制/状态寄存器(PWR_CSR)外,所有寄存器都将复位。因此程序将按照复位(启动引脚采样、复位向量已获取)后的方式重新执行。 1.2退出待机模式 检测到外部复位(NRST引脚)、IWDG复位、WKU
[单片机]
<font color='red'>STM32</font> RTC与待机模式
关于在stm32中使用printf函数的问题
想在mdk 中用printf,需要同时重定义fputc函数和避免使用semihosting(半主机模式)。 标准库函数的默认输出设备是显示器,要实现在串口或LCD输出,必须重定义标准库函数里调用的与输出设备相关的函数. 例如:printf输出到串口,需要将fputc里面的输出指向串口(重定向),方法如下: #ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker- Libraries- Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR
[单片机]
意法半导体的新STM32探索套件简化移动网至云端连接
中国,2018年3月2日 —— 意法半导体专门配置的两个STM32探索套件让物联网设备能够通过2G/3G或LTE Cat M1/NB1网络快速连接云服务,让大众市场开发人员更自由、更灵活地开发应用。 每款套件都包括一个STM32L496探索板和集成一个Quectel蜂窝移动网络调制解调器的STMod+ 蜂窝扩展板。配套软件包括X-CUBE-CLD-GEN,这是一个移植到STM32L496超低功耗微控制器和扩展板的Espruino嵌入式JavaScript引擎,兼容STM32Cube生态系统。  硬件可直接使用,开发人员可以在JavaScript引擎上快速启动并定制脚本示例,无需额外投资。X-CUBE-CLD-GEN扩展
[半导体设计/制造]
SAR ADC内部结构
前言 用了这么久ADC,从没细看过ADC的内部原理和如何获得最佳精度,今天看到一篇ST的官方文档讲的不错,这里整理分享给大家。 SAR ADC内部结构 STM32微控制器中内置的ADC使用SAR(逐次逼近)原则,分多步执行转换。转换步骤数等 于ADC转换器中的位数。每个步骤均由ADC时钟驱动。每个ADC时钟从结果到输出产生一 位。ADC的内部设计基于切换电容技术。 下面的图介绍了ADC的工作原理。下面的示例仅显示了逼近的前面几步,但是该过程会持续到LSB为止 SAR切换电容ADC的基本原理(10位ADC示例) 带数字输出的ADC基本原理图 采样状态 采样状态:电容充电至电压VIN。Sa切换至VIN,采样期间Sb开关闭合
[单片机]
SAR <font color='red'>ADC</font>内部结构
一文详解STM32的时钟系统
STM32的时钟树 时钟信号推动单片机内各个部分执行相应的指令,时钟就像人的心跳一样。 STM32本身十分复杂,外设非常多,任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,如果都用高速时钟势必造成浪费。同一个电路,时钟越快功耗越大、抗电磁干扰能力越弱。复杂的MCU采用多时钟源的方法来解决这些问题。如下图,是STM32的时钟系统框图。 如上图左边的部分,看到STM32有4个独立时钟源,HSI、HSE、LSI、LSE。 HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。 HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。 LSI是低速内部时钟,RC
[单片机]
一文详解<font color='red'>STM32</font>的时钟系统
STM32产生38KHz红外载波代码
本项目用TIM2通道1产生38KHz频率;对应的MCU管脚是PA0,默认复用功能; 重点是怎样配置TIM2的参数,产生38KHz频率; //应用示例:TIM2_PWM_Init(99,18); void TIM2_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE)
[单片机]
<font color='red'>STM32</font>产生38KHz红外载波代码
STM32学习笔记之GPIO的基本使用
楼学习中主要使用的资料有 战舰V3资料盘——教学视频 《STM32F1开发指南-库函数版本_V3.1》 (PDF) 《零死角玩转STM32》 野火出版 使用的学习板为屹讯电子嵌入式平台v2(MCU为 STM32F103RCT6) **某些IO口带有FT标识,代表其最大工作电压达到5V CPU能直接读取外设电平 上拉电阻的作用????????????????? 下拉电阻 CPU能直接读取外部电压变化(模拟量) 0-3.3v 输出控制电路置1,输出由外部电路决定(上拉或下拉) 输出控制电路置0,输出为0 置1置0的写入由CPU执行 输出控制电路的写入由外设模块决定 输出控制电路置1,输出1;置0,输出0。
[单片机]
<font color='red'>STM32</font>学习笔记之GPIO的基本使用
STM32学习笔记之 DS18B20 SEARCH ROM
使用说明,根据MCU不同 用户只需修改4函数 //单总线复位函数 int OWReset(); 单线总线的复位函数,注意这个要做相应修改,如果期间存在要返回1,期间不存在返回0, 直接从总线上读取的是期间存在返回0,不存在返回1 //向总线发送一个字节 void OWWriteByte(unsigned char dat); //向总线发送一位 void OWWriteBit(unsigned char bit_value); //读取总线一位 unsigned char OWReadBit(); 使用时用 int OWFirst();发现第一个单线器件 如果期间存在返回1,并且把ID存在 unsigned char
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved