8.2 DWA数据转运&DWA&AD多通道
1)DWA数据转运
流程:
第一步:RCC开启DMA的时钟
第二步:直接调用DMA_Init,初始化参数(外设和存储器站点的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否自动重装、选择触发源、通道优先级)
第三步:配置开关
主函数:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04}; //源端数组
uint8_t DataB[] = {0, 0, 0, 0}; //目的数组
int main(void)
{
OLED_Init();
MyDMA_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);
}
}
MyDMA.c
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
MyDMA_Size = Size;
//使能DMA1总线的时钟,为使用DMA通道和DMA传输做好准备
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
//配置外设站点
DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA; //外设站点的地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //以字节的方式传输,uint8_t
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //地址自增
//配置存储器站点
DMA_InitStructure.DMA_MemoryBaseAddr = AddrB; //存储器站点的地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //以字节的方式传输,uint8_t
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; //存储器到存储器(Memory to Memory)模式:软件触发
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道的传输优先级,这样选择传输优先级为中等
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
//DMA使能
DMA_Cmd(DMA1_Channel1, DISABLE);
}
//DMA传输函数
void MyDMA_Transfer(void)
{
//赋值前必须先让DMA失能
DMA_Cmd(DMA1_Channel1, DISABLE);
//给DMA重新赋值
DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
//DMA使能
DMA_Cmd(DMA1_Channel1, ENABLE);
//判断转运完成标志位情况
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
//手动清除标志位
DMA_ClearFlag(DMA1_FLAG_TC1);
}
2)DMA+AD多通道(ADC单次扫描+DMA单次转运的模式)
主函数:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
OLED_ShowString(4, 1, "AD3:");
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);
}
}
AD.c
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_Init(void)
{
//第一步:RCC开启时钟,开启GPIO和ADC的时钟,ADCCLK分频器配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//使能DMA1总线的时钟,为使用DMA通道和DMA传输做好准备
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//第二部:配置GPIO,把需要用的GPIO口配置成输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //ADC专属模式
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);
//第三步:配置多路开关,把左边的通道接入到右边的规则组列表里
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); //ADC1上配置一个常规通道,使其可以采集ADC通道0的模拟电压值,并且使用55.5个时钟周期作为采样时间
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转换器,用结构体来配置(AD转换器、AD数据寄存器、触发控制等)
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式,ADC可以单独工作,并不依赖其他STM32内部或外部的时钟或扫描机制。
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集到的ADC数据会在右对齐后被存入ADC转换缓存区中
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ADC外部触发转换配置代码,这里表示不使用外部触发方式来启动ADC转换(这里选择软件触发)
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //ADC连续转换模式配置代码,这里表示非连续转换,即采用单次转换
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //ADC扫描模式配置代码,这里表示扫描模式(四个通道)
ADC_InitStructure.ADC_NbrOfChannel = 4; //ADC转换通道数量配置代码,使用4个ADC通道进行转换
ADC_Init(ADC1,&ADC_InitStructure);
//配置DMA
DMA_InitTypeDef DMA_InitStructure;
//配置外设站点
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设站点的地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //以半字节的方式传输,16位转运
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 = 4; //传输计数器设置
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //正常模式,在该模式下数据传输一次之后即结束,需要重新初始化才能进行下一次传输。
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器(Memory to Memory)模式:不使用软件触发
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道的传输优先级,这样选择传输优先级为中等
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
//DMA使能
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE); //开启DMA触发信号
ADC_Cmd(ADC1, ENABLE); //调用ADC_Cmd(),开启ADC,然后进行校准
ADC_ResetCalibration(ADC1); //在进行ADC模块校准之前,需要先进行复位操作
while (ADC_GetResetCalibrationStatus(ADC1) == SET); //ADC校准复位后,需要等待校准重置完成。使用while语句判断校准复位是否完成
ADC_StartCalibration(ADC1); //校准复位完成后,开始进行ADC模块的校准
while (ADC_GetCalibrationStatus(ADC1) == SET); //ADC校准开始后,需要等待校准完成。使用while语句来判断校准是否完成,完成后继续执行下面的代码。
}
//启动转换,获取结果
void AD_GetValue(void)
{
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1, 4);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC转换开始前需要启动转换操作。这里使用了ADC_SoftwareStartConvCmd(ADC1, ENABLE)函数来启动ADC1的软件转换操作。
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}
3)DMA+AD多通道(ADC连续扫描+DMA循环转运的模式)
主函数:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
OLED_ShowString(4, 1, "AD3:");
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);
}
}
AD.c
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_Init(void)
{
//第一步:RCC开启时钟,开启GPIO和ADC的时钟,ADCCLK分频器配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//使能DMA1总线的时钟,为使用DMA通道和DMA传输做好准备
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//第二部:配置GPIO,把需要用的GPIO口配置成输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //ADC专属模式
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);
//第三步:配置多路开关,把左边的通道接入到右边的规则组列表里
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); //ADC1上配置一个常规通道,使其可以采集ADC通道0的模拟电压值,并且使用55.5个时钟周期作为采样时间
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转换器,用结构体来配置(AD转换器、AD数据寄存器、触发控制等)
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式,ADC可以单独工作,并不依赖其他STM32内部或外部的时钟或扫描机制。
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集到的ADC数据会在右对齐后被存入ADC转换缓存区中
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ADC外部触发转换配置代码,这里表示不使用外部触发方式来启动ADC转换(这里选择软件触发)
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //ADC连续转换模式配置代码,这里表示连续转换
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //ADC扫描模式配置代码,这里表示扫描模式(四个通道)
ADC_InitStructure.ADC_NbrOfChannel = 4; //ADC转换通道数量配置代码,使用4个ADC通道进行转换
ADC_Init(ADC1,&ADC_InitStructure);
//配置DMA
DMA_InitTypeDef DMA_InitStructure;
//配置外设站点
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设站点的地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //以半字节的方式传输,16位转运
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 = 4; //传输计数器设置
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器(Memory to Memory)模式:不使用软件触发
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道的传输优先级,这样选择传输优先级为中等
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE); //DMA使能
ADC_DMACmd(ADC1, ENABLE); //开启DMA触发信号
ADC_Cmd(ADC1, ENABLE); //调用ADC_Cmd(),开启ADC,然后进行校准
ADC_ResetCalibration(ADC1); //在进行ADC模块校准之前,需要先进行复位操作
while (ADC_GetResetCalibrationStatus(ADC1) == SET); //ADC校准复位后,需要等待校准重置完成。使用while语句判断校准复位是否完成
ADC_StartCalibration(ADC1); //校准复位完成后,开始进行ADC模块的校准
while (ADC_GetCalibrationStatus(ADC1) == SET); //ADC校准开始后,需要等待校准完成。使用while语句来判断校准是否完成,完成后继续执行下面的代码。
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC转换开始前需要启动转换操作。这里使用了ADC_SoftwareStartConvCmd(ADC1, ENABLE)函数来启动ADC1的软件转换操作。
}
启动转换,获取结果
//void AD_GetValue(void)
//{
// DMA_Cmd(DMA1_Channel1, DISABLE);
// DMA_SetCurrDataCounter(DMA1_Channel1, 4);
// DMA_Cmd(DMA1_Channel1, ENABLE);
//
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC转换开始前需要启动转换操作。这里使用了ADC_SoftwareStartConvCmd(ADC1, ENABLE)函数来启动ADC1的软件转换操作。
// while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
// DMA_ClearFlag(DMA1_FLAG_TC1);
//}