STM32标准库ADC和DMA知识点总结-2

简介: STM32标准库ADC和DMA知识点总结

STM32标准库ADC和DMA知识点总结-1

https://developer.aliyun.com/article/1508389


二、DMA原理和应用

DMA简介:

存储器映像:

RAM:随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。

ROM:(只读内存(Read-Only Memory)简称)英文简称ROM。ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。

DMA框图:


DMA基本结构:

DMA请求:

数据宽度与对齐:简单来说就是高位补零或者取高位舍低位


数据转运+DMA:

ADC扫描模式+DMA:

(1)DMA数据转运(内存到内存)

DMA.c

#include "stm32f10x.h"                  // Device header
 
uint16_t MyDMA_Size;          //定义全局变量,用于记住Init函数的Size,供Transfer函数使用
 
/**
  * 函    数:DMA初始化
  * 参    数:AddrA 原数组的首地址
  * 参    数:AddrB 目的数组的首地址
  * 参    数:Size 转运的数据大小(转运次数)
  * 返 回 值:无
  */
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
  MyDMA_Size = Size;          //将Size写入到全局变量,记住参数Size
  
  /*开启时钟*/
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);            //开启DMA的时钟
  
  /*DMA初始化*/
  DMA_InitTypeDef DMA_InitStructure;                    //定义结构体变量
  DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;           //外设基地址,给定形参AddrA
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度,选择字节
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;     //外设地址自增,选择使能
  DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;             //存储器基地址,给定形参AddrB
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     //存储器数据宽度,选择字节
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;         //存储器地址自增,选择使能
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;            //数据传输方向,选择由外设到存储器
  DMA_InitStructure.DMA_BufferSize = Size;                //转运的数据大小(转运次数)
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;             //模式,选择正常模式
  DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;               //存储器到存储器,选择使能
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;         //优先级,选择中等
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);              //将结构体变量交给DMA_Init,配置DMA1的通道1
  
  /*DMA使能*/
  DMA_Cmd(DMA1_Channel1, DISABLE);  //这里先不给使能,初始化后不会立刻工作,等后续调用Transfer后,再开始
}
 
/**
  * 函    数:启动DMA数据转运
  * 参    数:无
  * 返 回 值:无
  */
void MyDMA_Transfer(void)
{
  DMA_Cmd(DMA1_Channel1, DISABLE);          //DMA失能,在写入传输计数器之前,需要DMA暂停工作
  DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);  //写入传输计数器,指定将要转运的次数
  DMA_Cmd(DMA1_Channel1, ENABLE);           //DMA使能,开始工作
  
  while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);  //等待DMA工作完成
  DMA_ClearFlag(DMA1_FLAG_TC1);           //清除工作完成标志位
}


DMA.h

#ifndef __MYDMA_H
#define __MYDMA_H
 
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);
 
#endif


main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
 
uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};       //定义测试数组DataA,为数据源
uint8_t DataB[] = {0, 0, 0, 0};             //定义测试数组DataB,为数据目的地
 
int main(void)
{
  /*模块初始化*/
  OLED_Init();        //OLED初始化
  
  MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);  //DMA初始化,把源数组和目的数组的地址传入
  
  /*显示静态字符串*/
  OLED_ShowString(1, 1, "DataA");
  OLED_ShowString(3, 1, "DataB");
  
  /*显示数组的首地址*/
  OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);
  OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);
    
  while (1)
  {
    DataA[0] ++;    //变换测试数据
    DataA[1] ++;
    DataA[2] ++;
    DataA[3] ++;
    
    OLED_ShowHexNum(2, 1, DataA[0], 2);   //显示数组DataA
    OLED_ShowHexNum(2, 4, DataA[1], 2);
    OLED_ShowHexNum(2, 7, DataA[2], 2);
    OLED_ShowHexNum(2, 10, DataA[3], 2);
    OLED_ShowHexNum(4, 1, DataB[0], 2);   //显示数组DataB
    OLED_ShowHexNum(4, 4, DataB[1], 2);
    OLED_ShowHexNum(4, 7, DataB[2], 2);
    OLED_ShowHexNum(4, 10, DataB[3], 2);
    
    Delay_ms(1000);   //延时1s,观察转运前的现象
    
    MyDMA_Transfer(); //使用DMA转运数组,从DataA转运到DataB
    
    OLED_ShowHexNum(2, 1, DataA[0], 2);   //显示数组DataA
    OLED_ShowHexNum(2, 4, DataA[1], 2);
    OLED_ShowHexNum(2, 7, DataA[2], 2);
    OLED_ShowHexNum(2, 10, DataA[3], 2);
    OLED_ShowHexNum(4, 1, DataB[0], 2);   //显示数组DataB
    OLED_ShowHexNum(4, 4, DataB[1], 2);
    OLED_ShowHexNum(4, 7, DataB[2], 2);
    OLED_ShowHexNum(4, 10, DataB[3], 2);
 
    Delay_ms(1000);   //延时1s,观察转运后的现象
  }
}

(2)DMA+AD多同道(外设到内存)

面包板接线:

代码示例:

AD_DMA.c

#include "stm32f10x.h"                  // Device header
 
uint16_t AD_Value[4];         //定义用于存放AD转换结果的全局数组
 
/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
  /*开启时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  //开启ADC1的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);    //开启DMA1的时钟
  
  /*设置ADC时钟*/
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);           //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
  
  /*GPIO初始化*/
  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_Pin_3;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);          //将PA0、PA1、PA2和PA3引脚初始化为模拟输入
  
  /*规则组通道配置*/
  ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0
  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //规则组序列2的位置,配置为通道1
  ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); //规则组序列3的位置,配置为通道2
  ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); //规则组序列4的位置,配置为通道3
  
  /*ADC初始化*/
  ADC_InitTypeDef ADC_InitStructure;                      //定义结构体变量
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;              //模式,选择独立模式,即单独使用ADC1
  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_NbrOfChannel确定
  ADC_InitStructure.ADC_NbrOfChannel = 4;                   //通道数,为4,扫描规则组的前4个通道
  ADC_Init(ADC1, &ADC_InitStructure);                     //将结构体变量交给ADC_Init,配置ADC1
  
  /*DMA初始化*/
  DMA_InitTypeDef DMA_InitStructure;                      //定义结构体变量
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;       //外设基地址,给定形参AddrA
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度,选择半字,对应16为的ADC数据寄存器
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;      //外设地址自增,选择失能,始终以ADC数据寄存器为源
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;          //存储器基地址,给定存放AD转换结果的全局数组AD_Value
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;     //存储器数据宽度,选择半字,与源数据宽度对应
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;           //存储器地址自增,选择使能,每次转运后,数组移到下一个位置
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;              //数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
  DMA_InitStructure.DMA_BufferSize = 4;                   //转运的数据大小(转运次数),与ADC通道数一致
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;               //模式,选择循环模式,与ADC的连续转换一致
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                //存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;           //优先级,选择中等
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);                //将结构体变量交给DMA_Init,配置DMA1的通道1
  
  /*DMA和ADC使能*/
  DMA_Cmd(DMA1_Channel1, ENABLE);             //DMA1的通道1使能
  ADC_DMACmd(ADC1, ENABLE);               //ADC1触发DMA1的信号使能
  ADC_Cmd(ADC1, ENABLE);                  //ADC1使能
  
  /*ADC校准*/
  ADC_ResetCalibration(ADC1);               //固定流程,内部有电路会自动执行校准
  while (ADC_GetResetCalibrationStatus(ADC1) == SET);
  ADC_StartCalibration(ADC1);
  while (ADC_GetCalibrationStatus(ADC1) == SET);
  
  /*ADC触发*/
  ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作
}

AD_DMA.h

#ifndef __AD_H
#define __AD_H
 
extern uint16_t AD_Value[4];
 
void AD_Init(void);
 
#endif


main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
 
int main(void)
{
  /*模块初始化*/
  OLED_Init();        //OLED初始化
  AD_Init();          //AD初始化
  
  /*显示静态字符串*/
  OLED_ShowString(1, 1, "AD0:");
  OLED_ShowString(2, 1, "AD1:");
  OLED_ShowString(3, 1, "AD2:");
  OLED_ShowString(4, 1, "AD3:");
  
  while (1)
  {
    OLED_ShowNum(1, 5, AD_Value[0], 4);   //显示转换结果第0个数据
    OLED_ShowNum(2, 5, AD_Value[1], 4);   //显示转换结果第1个数据
    OLED_ShowNum(3, 5, AD_Value[2], 4);   //显示转换结果第2个数据
    OLED_ShowNum(4, 5, AD_Value[3], 4);   //显示转换结果第3个数据
    
    Delay_ms(100);              //延时100ms,手动增加一些转换的间隔时间
  }
}

HAL库实验可看:STM32DMA原理和应用_stm32dma应用-CSDN博客

相关文章
|
5月前
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
802 0
|
1月前
|
存储 数据管理 数据处理
处理STM32 DMA方式下的HAL_UART_ERROR_ORE错误
通过正确配置UART和DMA、实现有效的错误处理回调函数以及优化数据处理和缓冲区管理,可以有效处理STM32中DMA方式下的 `HAL_UART_ERROR_ORE`错误。这些方法确保了数据的高效传输和处理,避免了因数据溢出导致的通信中断和数据丢失。希望这些解决方案能够帮助您在实际应用中更好地应对和解决此类问题。
233 0
|
5月前
|
传感器 编解码 IDE
STM32CubeMX ADC采集光照和电压
STM32CubeMX ADC采集光照和电压
267 3
|
6月前
|
IDE 开发工具
使用STM32F103标准库实现自定义键盘
通过本文,我们学习了如何使用STM32F103标准库实现一个简单的自定义键盘。我们首先初始化了GPIO引脚,然后实现了一个扫描函数来检测按键状态。这个项目不仅能够帮助我们理解STM32的GPIO配置和按键扫描原理,还可以作为进一步学习中断处理和低功耗设计的基础。希望本文对你有所帮助,祝你在嵌入式开发的道路上不断进步!
539 4
|
6月前
|
存储 数据采集 数据安全/隐私保护
使用STM32F103读取TF卡并模拟U盘:使用标准库实现
通过以上步骤,你可以实现用STM32F103将TF卡内容变成U盘进行读取。这种功能在数据采集、便携式存储设备等应用中非常有用。如果你有更多的需求,可以进一步扩展此项目,例如添加文件管理功能、加密存储等。希望这篇博客能帮到你,如果有任何问题,欢迎在评论区留言讨论!
274 1
|
5月前
|
传感器 编解码 API
【STM32开发入门】温湿度监测系统实战:SPI LCD显示、HAL库应用、GPIO配置、UART中断接收、ADC采集与串口通信全解析
SPI(Serial Peripheral Interface)是一种同步串行通信接口,常用于微控制器与外围设备间的数据传输。SPI LCD是指使用SPI接口与微控制器通信的液晶显示屏。这类LCD通常具有较少的引脚(通常4个:MISO、MOSI、SCK和SS),因此在引脚资源有限的系统中非常有用。通过SPI协议,微控制器可以向LCD发送命令和数据,控制显示内容和模式。
210 0
|
6月前
使用STM32F103标准库实现定时器控制LED点亮和关闭
通过这篇博客,我们学习了如何使用STM32F103标准库,通过定时器来控制LED的点亮和关闭。我们配置了定时器中断,并在中断处理函数中实现了LED状态的切换。这是一个基础且实用的例子,适合初学者了解STM32定时器和中断的使用。 希望这篇博客对你有所帮助。如果有任何问题或建议,欢迎在评论区留言。
481 2
|
7月前
|
传感器
STM32标准库ADC和DMA知识点总结-1
STM32标准库ADC和DMA知识点总结
|
6月前
|
传感器
【经典案例】STM32F407使用HAL库配置I2C详解
STM32F407是一个强大的微控制器,广泛应用于嵌入式系统中。在许多应用中,我们需要使用I2C总线来与传感器、EEPROM、显示屏等外设进行通信。本文将详细介绍如何使用STM32 HAL库来配置和使用I2C接口。
802 2