[其他ST产品] stm32 DMA理论+实践

[复制链接]
2189|82
 楼主| 范德萨发额 发表于 2023-11-25 12:28 | 显示全部楼层
声明两个数组,Data1里面有数据,2里面没有数据

调用MyDMA_Init()里面写入参数,第一个为传输个数,第二个是存储器,第三个是外设,因为我们前面配置的为存储器到外设。

调用之后我们打印一下数组内容。
 楼主| 范德萨发额 发表于 2023-11-25 12:29 | 显示全部楼层
结果(很明显看出,即使这里是吧data1的数据搬移到了data2但是1里面数据依然在,所以说是数据搬移,其实是数据复制了)
5719965617810dc7ba.png
 楼主| 范德萨发额 发表于 2023-11-25 12:29 | 显示全部楼层
4)DMA循环转移代码
首先我们上面说过,软件触发是不能配合重载的,所以转移一次之后就会停止,所以下一次就算数据变动了,他也不会管,这里我们就写个函数,手动来对他进行使能和计数器赋值。
 楼主| 范德萨发额 发表于 2023-11-25 12:29 | 显示全部楼层
代码如下

1 添加函数
  1. void MyMDA_Transfer(void)
  2. {
  3.                 /*重置计数值*/
  4.           DMA_Cmd(DMA1_Channel1,DISABLE);
  5.           DMA_SetCurrDataCounter(DMA1_Channel1,MyMDA_Size);
  6.           DMA_Cmd(DMA1_Channel1,ENABLE);
  7.           /*等待传输完成*/
  8.            while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
  9.                         DMA_ClearFlag(DMA1_FLAG_TC1);
  10. }
 楼主| 范德萨发额 发表于 2023-11-25 12:29 | 显示全部楼层
2 改Init函数 615486561783064db9.png 4329665617837d76e1.png
 楼主| 范德萨发额 发表于 2023-11-25 12:29 | 显示全部楼层
添加全局变量MyMDA_size 方便将传入的形参保存,后面好重新给计数器赋值

之后失能DMA只进行初始化,后面我们手动使能
 楼主| 范德萨发额 发表于 2023-11-25 12:30 | 显示全部楼层
函数分析 7953465617849ad7bb.png
 楼主| 范德萨发额 发表于 2023-11-25 12:30 | 显示全部楼层
重置计数器部分,这里前面说过,DMA在开启的时候是不能更改计数器的值的,所以我们先失能之后更改完值重新使能 2362465617856185f1.png
 楼主| 范德萨发额 发表于 2023-11-25 12:30 | 显示全部楼层
等待传输完成部分:等待传输完成的标志位为1退出循环

这里一般看有没有标志位主要是看参考手册:这里说了标志位名字和是否需要手动清除,他说在ifcr的相应标志写1就能清楚 1687465617869baa5f.png
 楼主| 范德萨发额 发表于 2023-11-25 12:30 | 显示全部楼层
然后看了下中文参考手册,应该是翻译错了,因为的里面是1为清除 14331656178774db5a.png
 楼主| 范德萨发额 发表于 2023-11-25 12:30 | 显示全部楼层
 楼主| 范德萨发额 发表于 2023-11-25 12:31 | 显示全部楼层
之后我们写主函数volatile uint32_t time = 0; // ms 计时变量

uint8_t Data1[]={0x01,0x02,0x03,0x14};
uint8_t Data2[]={0,0,0,0};

int main(void)
{
        Usart_Config();
        TIM6_Init();
        MyDMA_Init(4,(uint32_t)Data2,(uint32_t)Data1);
        MyMDA_Transfer();

  while(1)
        {               

                if(time==1000)
                {
                                 Data1[0]++;
                                Data1[1]++;
                                Data1[2]++;
                                Data1[3]++;
                                MyMDA_Transfer();
                                printf("0x%02x 0x%02x 0x%02x 0x%02x\r\n",Data1[0],Data1[1],Data1[2],Data1[3]);
                                printf("0x%02x 0x%02x 0x%02x 0x%02x\r\n",Data2[0],Data2[1],Data2[2],Data2[3]);
                                time=0;
                }
        }       
}
 楼主| 范德萨发额 发表于 2023-11-25 12:31 | 显示全部楼层
2 ADC扫描模式+DMA
这个是之前写过的一个ADC采集的后续了stm32f103 ADC采集_是小刘不是刘的博客-CSDN博客

因为之前说了扫描模式下循环采集需要ADC来实现,所以吧ADC的多通道扫描采集写到了这里来了,所以没有看ADC的朋友可以先去看看ADC是怎么使用的。
 楼主| 范德萨发额 发表于 2023-11-25 12:31 | 显示全部楼层
首先还是看一下图 33459656178ab85abf.png
 楼主| 范德萨发额 发表于 2023-11-25 12:31 | 显示全部楼层
ADC触发之后,信号给DMA,这时候DMA会帮助ADC进行数据转移,也就不会像之前说的那样,不管转换多少个通道的数据都会只保存最后一个转换数据了
 楼主| 范德萨发额 发表于 2023-11-25 12:32 | 显示全部楼层
DMA多通道单次触发
OK打开我们之前写好的ADC的代码,将我们写的DMA的初始化加进去

整体代码如下,后面开始解释
  1. //adc.c文件
  2. uint16_t addr[2]={0};
  3. static void AD_Init(void)
  4. {
  5.         GPIO_InitTypeDef GPIO_InitStruct;
  6.         ADC_InitTypeDef  ADC_InitStruct;

  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
  8.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

  9.         GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
  10.         GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_2;
  11.         GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
  12.         GPIO_Init(GPIOC,&GPIO_InitStruct);

  13.         RCC_ADCCLKConfig(RCC_PCLK2_Div6);
  14.        
  15.         ADC_RegularChannelConfig(ADC1,ADC_Channel_11,1,ADC_SampleTime_1Cycles5);
  16.         ADC_RegularChannelConfig(ADC1,ADC_Channel_12,2,ADC_SampleTime_1Cycles5);
  17.        
  18.         ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;
  19.         ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
  20.         ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
  21.         ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
  22.         ADC_InitStruct.ADC_NbrOfChannel=2;
  23.         ADC_InitStruct.ADC_ScanConvMode=ENABLE;
  24.         ADC_Init(ADC1,&ADC_InitStruct);

  25.         ADC_DMACmd(ADC1,ENABLE);
  26.         ADC_Cmd(ADC1,ENABLE);
  27.        
  28.         //校验
  29.         ADC_ResetCalibration(ADC1);
  30.         while(ADC_GetResetCalibrationStatus(ADC1) == SET);
  31.         ADC_StartCalibration(ADC1);
  32.         while(ADC_GetCalibrationStatus(ADC1) == SET);
  33. }


  34. static void MyDMA_Init(void)
  35. {
  36.         DMA_InitTypeDef  DMA_InitStruct;

  37.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
  38.                 DMA_InitStruct.DMA_BufferSize=2;
  39.         DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;
  40.         DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;
  41.         DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;
  42.         DMA_InitStruct.DMA_Priority=DMA_Priority_High;
  43.         DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)addr;
  44.         DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
  45.         DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
  46.         DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;
  47.         DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
  48.         DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
  49.         DMA_Init(DMA1_Channel1,&DMA_InitStruct);
  50.                 DMA_Cmd(DMA1_Channel1,ENABLE);
  51. }

  52. void MyAD_Init(void)
  53. {
  54.           AD_Init();
  55.           MyDMA_Init();
  56. }

  57. void AD_GetValue(void)
  58. {
  59.        
  60.                 DMA_Cmd(DMA1_Channel1,DISABLE);
  61.           DMA_SetCurrDataCounter(DMA1_Channel1,2);
  62.           DMA_Cmd(DMA1_Channel1,ENABLE);
  63.        
  64.           ADC_SoftwareStartConvCmd(ADC1,ENABLE);

  65.                  /*等待传输完成*/
  66.            while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
  67.                  DMA_ClearFlag(DMA1_FLAG_TC1);
  68. }
 楼主| 范德萨发额 发表于 2023-11-25 12:32 | 显示全部楼层
//adc.h文件
#ifndef __ADC_H
#define        __ADC_H

#include "stm32f10x.h"

extern uint16_t addr[2];
void AD_GetValue(void);
void MyAD_Init(void);

#endif /*__ADC_H*/
 楼主| 范德萨发额 发表于 2023-11-25 12:32 | 显示全部楼层
这里首先是AD的配置 35533656178d2e4c58.png
 楼主| 范德萨发额 发表于 2023-11-25 12:32 | 显示全部楼层
1  ADC开启两个通道
(和ADC章节我开的两个通道的引脚是一样的是一样的) 我使用的为PC1 PC2 所以通道是11和12, 89040656178dedea05.png
 楼主| 范德萨发额 发表于 2023-11-25 12:32 | 显示全部楼层
2 开启2个通道序列
(就是前面说的序列)之后我们把扫描模式使能。 27281656178ec80d4a.png
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部