DMA进行转运的条件
- 1:开关控制,DMA_CMD必须使能
- 2:传输计数器必须大于0
- 3:触发源必须有触发的信号
DMA存储器到存储器的数据转运
接线图
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
uint8_t Dma_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size){
MyDMA_Size = Size;
// 1: 启rcc时钟,DMA是AHB总线上的外设时钟控制所以要使用的是AHD总线控制
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMAStruture;
// 引出DMA的结构体,外设站点的起始地址,数据宽度是否自增
DMAStruture.DMA_PeripheralBaseAddr = AddrA; // 外设站点的基础地址,要编写的是一个32位的地址
DMAStruture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 设置的是地址,也就是按照字节的方式自增
DMAStruture.DMA_PeripheralInc = DMA_PeripheralInc_Enable; // 指定参数的地址是不是自增,根据上面的内容数组之间的数据转运地址是要自己增加的
// 存储器站点的,起始地址,数据宽度是否自增
DMAStruture.DMA_MemoryBaseAddr = AddrB; // 存储器 站点的基地址
DMAStruture.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte; // 以字节的方式传输
DMAStruture.DMA_MemoryInc = DMA_MemoryInc_Enable; // 自己增加
// 传输方向
DMAStruture.DMA_DIR = DMA_DIR_PeripheralSRC; // 指定是源地址还是起始地址
// 缓存区大小,也就是传输计数器,也就是传输的次数
DMAStruture.DMA_BufferSize = Size;
// DMA的传输模式,也就是是否使用自动重装
DMAStruture.DMA_Mode = DMA_Mode_Normal;
// DMA的,选择触发方式,也就是软件触发还是硬件触发,
DMAStruture.DMA_M2M =DMA_M2M_Enable;
// DMA的优先级
DMAStruture.DMA_Priority = DMA_Priority_Medium;
// 存储器到存储器的数据转运,使用的是软件的触发方式,所以通道的选择可以使用任意的通道
DMA_Init(DMA1_Channel1, &DMAStruture);
// 使能DMA,也就是DMA数据转运
DMA_Cmd(DMA1_Channel1,DISABLE);
}
// 启动DMA转运
void MyDMA_Transfer(void){
// 重新个传输计数器赋值,在重新个传输计数器赋值的同时要给传输计数器失能
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
DMA_Cmd(DMA1_Channel1,ENABLE);
// 等待转运完成
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}
DMA.h
`#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
// 初始化DMA让DMA里面的数据转运到DMB中去
uint8_t DataA[] = {0x01,0x02,0x03,0x04};
uint8_t DataB[] = {0,0,0,0};
int main(void)
{
// 初始化oled
OLED_Init();
// 后面的是转运后的数据,在这个过程中源端数据DATAA的数据是不会变化的
Dma_Init((uint32_t)DataA,(uint32_t)DataB,4);
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);
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);
OLED_ShowHexNum(4, 4, DataB[1], 2);
OLED_ShowHexNum(4, 7, DataB[2], 2);
OLED_ShowHexNum(4, 10,DataB[3], 2);
Delay_ms(1000);
MyDMA_Transfer();
OLED_ShowHexNum(2, 1, DataA[0], 2);
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);
OLED_ShowHexNum(4, 4, DataB[1], 2);
OLED_ShowHexNum(4, 7, DataB[2], 2);
OLED_ShowHexNum(4, 10,DataB[3], 2);
Delay_ms(1000);
}
}
`
DMA+ADC多通道数据转运
接线图
AD.C部分的代码
#include "stm32f10x.h" // Device header
// 1: 开启RCC时钟,包括ADC和GPIO的时钟
// 2:配置GPIO将GPIO配置为模拟输入模式
// 3:配置多路开关将左边的通道接入到规则组中
// 4:配置ADC转换器,单次转换,连续转换,扫描还是非扫描
// 5:开关控制调用ADC_COM参数ADC配置完成就能正常工作
uint16_t AD_Value[4];
void AD_Init(void){
// 开启ADC的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
// 开启GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
// 配置ADC_CLK,72MHz/6 = 12MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// 配置GPIO
GPIO_InitTypeDef GPIO_InitStructre;
// 将GPIO的模式引用出来
GPIO_InitStructre.GPIO_Mode =GPIO_Mode_AIN; // 选择GPIO的模式,设置为AN模拟输入的模式在AIN模式下GPIO口是没有效果的
GPIO_InitStructre.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3; // 选择GPIO的输出模式,选择输出的管脚
GPIO_InitStructre.GPIO_Speed = GPIO_Speed_50MHz; // 选择GPIO的时钟频率
GPIO_Init(GPIOA,&GPIO_InitStructre); // 初始化GPIO
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_55Cycles5);
// 第三步 ---> 使用结构体初始化ADC
ADC_InitTypeDef ADC_InitStructure;
// 引出结构体成员
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 配置扫描的模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 配置ADC的数据对齐方式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 配置ADC的外部触发转换选择:这里使用内部软件触发的方式
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 配置ADC的工作模式为独立模式
ADC_InitStructure.ADC_NbrOfChannel = 4; // 配置通道数目
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 配置扫描的模式
ADC_Init(ADC1,&ADC_InitStructure);
// 以下还可以配置中断和模拟看门狗,根据自己的需求进行配置
DMA_InitTypeDef DMAStruture;
// 引出DMA的结构体,外设站点的起始地址,数据宽度是否自增
DMAStruture.DMA_PeripheralBaseAddr =(uint32_t)&ADC1->DR; // 外设站点的基础地址,要编写的是一个32位的地址
DMAStruture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 设置的是地址,也就是按照字节的方式自增
DMAStruture.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 指定参数的地址是不是自增,根据上面的内容数组之间的数据转运地址是要自己增加的
// 存储器站点的,起始地址,数据宽度是否自增
DMAStruture.DMA_MemoryBaseAddr = (uint32_t)AD_Value; // 存储器 站点的基地址
DMAStruture.DMA_MemoryDataSize =DMA_MemoryDataSize_HalfWord; // 以字节的方式传输
DMAStruture.DMA_MemoryInc = DMA_MemoryInc_Enable; // 自己增加
// 传输方向
DMAStruture.DMA_DIR = DMA_DIR_PeripheralSRC; // 指定是源地址还是起始地址
// 缓存区大小,也就是传输计数器,也就是传输的次数
DMAStruture.DMA_BufferSize = 4;
// DMA的传输模式,也就是是否使用自动重装
DMAStruture.DMA_Mode = DMA_Mode_Normal;
// DMA的,选择触发方式,也就是软件触发还是硬件触发,
DMAStruture.DMA_M2M =DMA_M2M_Disable;
// DMA的优先级
DMAStruture.DMA_Priority = DMA_Priority_Medium;
// 存储器到存储器的数据转运,使用的是软件的触发方式,所以通道的选择可以使用任意的通道
DMA_Init(DMA1_Channel1, &DMAStruture);
// 使能DMA,也就是DMA数据转运
DMA_Cmd(DMA1_Channel1,ENABLE);
// 开启DMA触发信号
ADC_DMACmd(ADC1,ENABLE);
// 开启ADC设置,第一个ENABLE第二个开启ADC的电源
ADC_Cmd(ADC1,ENABLE);
// 对ADC进行校准,这里分别有四个函数可以进行配置
ADC_ResetCalibration(ADC1); // 复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET); // 返回复位校准的状态
ADC_StartCalibration(ADC1); // 启动校准
while(ADC_GetCalibrationStatus(ADC1) == SET); // 获取校准后的状态
// 1: 软件触发转换 2:等待触发完成也就是等待EOC标志位设置为1,3:读取ADC数据寄存器
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
uint16_t AD_GetValue(void){
// 重新个传输计数器赋值,在重新个传输计数器赋值的同时要给传输计数器失能
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,4);
DMA_Cmd(DMA1_Channel1,ENABLE);
// 单次触发模式,在DMA触发之前要重新写入传输计数器
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
// 等待DMA、转换完成的代码
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}
AD.H部分代码
#ifndef __AD_H
#define __AD_H
extern uint16_t AD_Value[4]; // 这个数组作为一个外部可以调用的数组
uint16_t AD_GetValue(void);
void AD_Init(void);
#endif
main.c部分的代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD0,AD1,AD2,AD3;
float Votage;
int main(void)
{
// 初始化oled
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");
OLED_ShowString(2,1,"AD1:");
OLED_ShowString(3,1,"AD3:");
OLED_ShowString(4,1,"AD4:");
while (1)
{
AD_GetValue();
OLED_ShowNum(1, 5, AD_Value[0], 4);
OLED_ShowNum(2, 5, AD_Value[1], 4);
OLED_ShowNum(3, 5, AD_Value[2], 4);
OLED_ShowNum(4, 5, AD_Value[3], 4);
Delay_ms(100);
}
}