云计算百科
云计算领域专业知识百科平台

STM32——DMA数据转换、DMA+AD多通道(十五)

一、库函数

1、void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState)

核心功能:配置 DMA 通道的中断使能 / 失能(开启或关闭某个 DMA 中断类型)。

2、void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber)

核心功能:设置 DMA 通道的当前剩余传输数据个数(DMA 的核心计数器,传输 1 个数据,计数器自动减 1)。

3、uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)

核心功能:获取 DMA 通道的当前剩余传输数据个数(读取计数器的实时值),返回值为剩余个数。

4、FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)

核心功能:查询 DMA 的状态标志位是否置 1(即某个事件是否发生,如传输完成、半传输、错误),返回标志位状态。

5、void DMA_ClearFlag(uint32_t DMAy_FLAG)

核心功能:手动清除 DMA 的状态标志位(将置 1 的标志位清零)。

6、ITStatus DMA_GetITStatus(uint32_t DMAy_IT)

核心功能:查询 DMA 的中断挂起位是否置 1(即某个中断事件是否发生,且该中断已开启),返回中断状态。

7、void DMA_ClearITPendingBit(uint32_t DMAy_IT)

核心功能:清除 DMA 的中断挂起位(将置 1 的中断挂起位清零)。

二、数据转换

将数组A中的数据转换到数组B中

uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};
uint8_t DataB[] = {0, 0, 0, 0};

1、初始化配置

void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
MyDMA_Size = Size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_MemoryBaseAddr = 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_Cmd(DMA1_Channel1,DISABLE);
}

站点地址

将要转换数组的地址和数据个数传入。

void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)

打开DMA时钟

 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

配置外设站点

配置外设站点地址,数据宽度:以字节传输,地址是否自增:是。

 DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;

配置存储器站点

配置存储器站点地址,数据宽度:以字节传输,地址是否自增:是。

DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure .DMA_MemoryInc = DMA_MemoryInc_Enable;

配置传输方向和数据个数

配置为从外部站点传输到存储器站点,数据个数Size。

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;

关闭DMA1

    DMA_Cmd(DMA1_Channel1,DISABLE);

2、再次启动DMA

当数组A的数据变化后,再次启动DMA转换数据到数组B。

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要重装计数器,重装计数器要先关闭DMA,MyDMA_Size在前面Init 中有被执行MyDMA_Size = Size;

DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
DMA_Cmd(DMA1_Channel1,ENABLE);

等待DMA通道1转换完成标志位置1,然后手动清除标志位。

while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);

3、调用

让数组A的元素++,显示两个数组的元素值,延时一段时间,然后在显示更新后的值。

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

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

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+AD多通道

1、接线图

2、初始化配置

配置ADC:单ADC工作、数据右对齐、软件触发、关闭连续转换、扫描模式、扫描通道数目4

配置DMA:

外部站点:地址是ADC1的DR寄存器(ADC转化结果存放在这个寄存器)、数据带宽16位、地址不递增。

存储器站点:地址是全局数组AD_Value、数据带宽16位、地址递增。

搬运方向外设→内存、搬运次数4次、单次搬运、ADC硬件触发、DMA优先级:中等(无其他DMA时随意)

void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AIN;//此时GPIO口只受到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);
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_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 4;
ADC_Init(ADC1,&ADC_InitStructure);

DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
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;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1,&DMA_InitStructure);

DMA_Cmd(DMA1_Channel1,ENABLE);

ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1,ENABLE);

ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);

}

打开ADC和DMA的通道

开启ADC1的DMA请求(ADC转换完成后,主动通知DMA搬运)

ADC_DMACmd(ADC1, ENABLE);

3、再启动

需要采集数据时,在主函数中调用一次就执行一次 4 通道扫描,采集完成后结果自动存入AD_Value数组

void AD_GetValue(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,4);
DMA_Cmd(DMA1_Channel1,ENABLE);

ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}

4、调用

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

}
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » STM32——DMA数据转换、DMA+AD多通道(十五)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!