• 08:STM32----DMA数据转运


    目录

    1:简历

    2:存储器映像

    3:DMA基本结构

    4: DMA转运的条件 

    5:DMA请求

    A:DMA数据转运

    1:连接图

    2:数据转运+DMA

    3:函数介绍

    4:步骤

    5:代码

     B:DMA+AD多通道

    1:连接图

    2:结构图

     3:函数介绍

    4:代码  


    1:简历

    DMA(Direct Memory Access)直接存储器存取

    DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源

    12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)

    每个通道都支持软件触发和特定的硬件触发  (每隔DMA通道的触发源不一样)

    STM32F103C8T6 DMA资源:DMA1(7个通道)

    外设(外设存储器)  :  一般是外设的数据寄存器DR , 比如ADC的数据寄存器、串口的数据寄存器等等

    存储器  :  这里存储器,指的就是是运行内存SRAM和程序存储器Flash ,  是我们存储变量数组和程序代码的地方

    外设和存储器的数据传输---------使用特定的硬件触发(每隔DMA通道的触发源不一样)

    存储器和存储器之间的数据传输------软件触发

    2:存储器映像

    ROM :  只读存储器,是一种非易失性、掉电不丢失的存储器

    RAM : 随机存储器,是一种易失性、掉电丢失的存储器

            因为CPU是32位的。所以导址范围就是32位的范围

    3:DMA基本结构

    下面的参数在配置DMA时使用

    方向 : 决定了数据的方向    外设--->储存器 或者 储存器---->外设  或者 储存器---->储存器

            Flash是只读储存器,  所以在储存器--->储存器的传输方向的时候 :只能选择 Flash-------------->SRAM方向的传输

            X.DMA_DIR=指定外设站点(外设寄存器)为数据源还是目的地

    传输计数器: 用来指定,我总共需要转运几次的,  这个传输计数器是一个自减计数器

            比如给它写一个5,   那DMA就只能进行5次数据转运 ,  转运过程中,每转运一次,计数器的数就会减1,   当传输计数器减到0之后,DMA就不会再进行数据转运了 ,  它减到0之后,之前自增的地址,也会恢复到起始地址的位置

            X.DMA_BufferSize=传输计数器

    自动重装器 :  传输计数器减到0之后 , 是否要恢复到最初的值

            比如最初传输计数器给5,  如果不使用自动重装器,那转运5次后,DMA就结束了.   如果使用自动重装器,那转运5次,  计数器减到0后,就会立即重装到初始值5,  决定了转运的模式        

            不重装,就是正常的单次模式

            重装,就是循环模式

            X.DMA_Mode=自动重装器

    起始地址 : 决定了数据从那里来到那里去的

    因为stm32是32位的单片机,他的一个内存单元是32位的, 所以起始地址填入的都为32位

    数据宽度 :  指定一次转运要按多大的数据宽度来进行

            字节Byte(uint8_t)、半字HalfWord(uint16_t)和字Word(uint32_t)

    软件触发和循环模式,不能同时用

            软件触发 : 软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,  早日把传输计数器清零,完成这一轮的转换

            软件触发就是尽快把传输计数器清零 .  循环模式是清零后自动重装 ,  如果同时用的话,那DMA就停不下来了

    4: DMA转运的条件 

    1: 开关控制,DMA _Cmd函数必须使能

    2: 传输计数器必须大于0

    3: 触发源,必须有触发信号

            触发一次,转运一次,传输计数器自减一次 ,  当传输计数器等于0,且没有自动重装时 ,   无论是否触发,DMA都不会再进行转运了,   此时就需要DMA_Cmd函数,给DISABLE,关闭DMA.   再为传输计数器写入一个大于0的数,  再DMA_Cmd,给ENABLE,开启DMA才可以正常工作.

            注意,写传输计数器时,必须要先关闭DMA,再进行,   不能在DMA开启时,写传输计数器

    5:DMA请求

    特定的硬件触发 : 每个通道的硬件触发源都是不同的 , 需要用ADC1来触发的话一那就必须选择通道1;   定时器2的更新事件来触发的话,那就必须选择通道2

            因为每个通道的硬件触发源都不同 ,  如果你想使用某个硬件触发源的话,  就必须使用它所在的通道

    软件触发 :  使用软件触发的话,那通道就可以任意选择了.  每个通道的软件触发都是一样的

    A:DMA数据转运

    1:连接图

    2:数据转运+DMA

    3:函数介绍

     在stm32f10x dmah文件中配置----MDA初始化

    void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

     在stm32f10x dmah文件中配置----开启MDA

    void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

     在stm32f10x dmah文件中配置----数据寄存器设置

    void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); 


    uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

    DMA_SetCurrDataCounter : 设置当前数据寄存器 ,  就是给这个传输计数器写数据的,    和配置DMA中X.DMA_Buffersize参数相似

    DMA_GetCurrDataCounter :  获取当前数据寄存器,  这个函数就是返回传输计数器的值 ,     在转运完成后把标志位值1

     在stm32f10x dmah文件中配置----清除中断标志位

    void DMA_ClearFlag(uint32_t DMAy_FLAG)

    DMA_ClearFlag : 清除中断标志位

    4:步骤

    1:开启RCC时钟-----DMA的时钟在AHB总线上

    2:配置DMA

    5:代码

    使用的是存储器和存储器之间的数据传输------软件触发

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "MyDMA.h"
    5. uint16_t MyDMA_Size;
    6. void MyMDA_init(uint32_t AddrA, uint32_t AddrB, uint16_t Size){
    7. MyDMA_Size=Size;
    8. //开启RCC--DMA是AHB总线的设备
    9. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    10. /*
    11. 我们使用的STM32型号为: STMF103
    12. 参数1的第一个选择 : 互连型设备--互联型是STM23F 105/107的型号
    13. 参数1的第一个选择 : 其他设备---103选择这个
    14. */
    15. //配置DMA
    16. DMA_InitTypeDef DMA_initstruct;
    17. //外设备站点的3个参数
    18. //因为stm32是32位的单片机,他的一个内存单元是32位的
    19. DMA_initstruct.DMA_PeripheralBaseAddr=AddrA; //外设的起始地址--要求:32位
    20. DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;外设的数据宽度---我们选择以字节的方式传输(uint8_t)
    21. DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;//外设的地址是否自增--自增
    22. //储存器的3个参数
    23. DMA_initstruct.DMA_MemoryBaseAddr=AddrB;//储存器的起始地址--要求:32位
    24. DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//储存器的数据宽度--我们选择以字节的方式传输(uint8_t)
    25. DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
    26. //传输方向
    27. DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
    28. //缓冲区大小---传输计数器
    29. DMA_initstruct.DMA_BufferSize=Size;//传输几次
    30. //传输模式----是否使用自动重装
    31. DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//正常模式 //传输计数器到0直接停止
    32. //选择触发模式---硬件触发或者软件触发
    33. DMA_initstruct.DMA_M2M=DMA_M2M_Enable; //使用软件触发
    34. //优先级
    35. DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
    36. DMA_Init(DMA1_Channel1,&DMA_initstruct);
    37. //开启MDA
    38. DMA_Cmd(DMA1_Channel1,DISABLE);
    39. }
    40. void MyDMA_Transfer(void)
    41. {
    42. //需要给我传输寄存器重新赋值,首先要使CMD失能
    43. DMA_Cmd(DMA1_Channel1, DISABLE);
    44. /*
    45. 设置当前数据寄存器 ,  就是给这个传输计数器写数据的
    46. 和配置DMA中X.DMA_BufferSize参数相似
    47. */
    48. DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
    49. DMA_Cmd(DMA1_Channel1, ENABLE);
    50. //DMA1_FLAG_TC1---转运完成标志位 转运完成后置1
    51. while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
    52. DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
    53. }
    54. uint8_t dataA[]={0x01,0x02,0x03,0x04};
    55. uint8_t dataB[]={0,0,0,0};
    56. int main(void)
    57. {
    58. OLED_Init();
    59. MyMDA_init((uint32_t)dataA,(uint32_t)dataB,4);
    60. OLED_ShowString(1, 1, "DataA");
    61. OLED_ShowString(3, 1, "DataB");
    62. //数组的函数名就表示地址
    63. OLED_ShowHexNum(1, 8, (uint32_t)dataA, 8);
    64. OLED_ShowHexNum(3, 8, (uint32_t)dataB, 8);
    65. while (1)
    66. { dataA[0]++;
    67. dataA[1]++;
    68. dataA[2]++;
    69. dataA[3]++;
    70. OLED_ShowHexNum(2,1,dataA[0],2);
    71. OLED_ShowHexNum(2,4,dataA[1],2);
    72. OLED_ShowHexNum(2,7,dataA[2],2);
    73. OLED_ShowHexNum(2,10,dataA[3],2);
    74. OLED_ShowHexNum(4,1,dataB[0],2);
    75. OLED_ShowHexNum(4,4,dataB[1],2);
    76. OLED_ShowHexNum(4,7,dataB[2],2);
    77. OLED_ShowHexNum(4,10,dataB[3],2);
    78. Delay_ms(1000);
    79. MyDMA_Transfer();
    80. OLED_ShowHexNum(2,1,dataA[0],2);
    81. OLED_ShowHexNum(2,4,dataA[1],2);
    82. OLED_ShowHexNum(2,7,dataA[2],2);
    83. OLED_ShowHexNum(2,10,dataA[3],2);
    84. OLED_ShowHexNum(4,1,dataB[0],2);
    85. OLED_ShowHexNum(4,4,dataB[1],2);
    86. OLED_ShowHexNum(4,7,dataB[2],2);
    87. OLED_ShowHexNum(4,10,dataB[3],2);
    88. Delay_ms(1000);
    89. }
    90. }

    开启RRC时钟 

            开启RCC--DMA是AHB总线的设备

            RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
        /*
        我们使用的STM32型号为: STMF103
        参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
        参数1的第一个选择  : 其他设备---103选择这个
        */

    字节问题

            因为要参数的数据是dataA和dataB都是uint8_t的大小,  所以在配置DMA时宽度的参数(X.DMA_PeripheralDataSize)选择DMA_PeripheralDataSize_Byte(uint8_t)

            在配置DMA起始地址时选uint32_t:  因为stm32是32位的单片机,他的一个内存单元是32位的

     B:DMA+AD多通道

    1:连接图

    和07:STM32----ADC模数转化器 --- B:AD多通道一致

    2:结构图

     3:函数介绍

    在文件stm32f10x_adc.c中-----开启ADC到DMA的输出

    void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState)

    4:代码  

            在07:STM32----ADC模数转化器中    我们使用了没有使用扫描模式,  是由于ADC的数据覆盖问题,在下面的代码中我们使用DMA进行数据转运,    所以在ADC使用扫描模式的情况下必须使用DMA进行数据转运

            ADC单次扫描+DMA单次转运的模式

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "AD.h"
    5. //ADC单次扫描+DMA单次转运的模式
    6. uint16_t AD_Value[4];
    7. void AD_init(void){
    8. //RCC开启时钟
    9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    11. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    12. /*
    13. 我们使用的STM32型号为: STMF103
    14. 参数1的第一个选择 : 互连型设备--互联型是STM23F 105/107的型号
    15. 参数1的第一个选择 : 其他设备---103选择这个
    16. */
    17. //配置ADCCLK
    18. //APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
    19. RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
    20. //配置GPIO
    21. GPIO_InitTypeDef GPIO_initstruct;
    22. GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN; //模拟输入,可以理解为ADC的专属模式
    23. GPIO_initstruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
    24. GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
    25. GPIO_Init(GPIOA,&GPIO_initstruct);
    26. /*ADC_Channel_0 --通道o
    27. 1----1~16的范围规则组第几个序列
    28. ADC_SampleTime_55Cycles5-----指定通道的采样时间
    29. */
    30. //选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
    31. ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
    32. ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
    33. ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
    34. ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
    35. //初始化ADC
    36. ADC_InitTypeDef ADC_initstruct;
    37. ADC_initstruct.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单次
    38. ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
    39. ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
    40. ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
    41. ADC_initstruct.ADC_NbrOfChannel=4; //通道数目--指定在扫描模式下,总共会用到几个通道
    42. ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
    43. ADC_Init(ADC1,&ADC_initstruct);
    44. //ADC在完成后面数据写在DR寄存器中
    45. //配置DMA
    46. //外设备站点的3个参数
    47. //因为stm32是32位的单片机,他的一个内存单元是32位的
    48. DMA_InitTypeDef DMA_initstruct;
    49. DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
    50. DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)
    51. DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
    52. // //储存器的3个参数
    53. DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位
    54. DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)
    55. DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
    56. //传输方向
    57. DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
    58. //缓冲区大小---传输计数器
    59. DMA_initstruct.DMA_BufferSize=4;//传输几次
    60. //传输模式----是否使用自动重装
    61. DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//普通模式 //传输计数器到0直接停止 //DMA_Mode_Circular--循环模式 DMA_Mode_Normal
    62. //选择触发模式---硬件触发或者软件触发
    63. DMA_initstruct.DMA_M2M=DMA_M2M_Disable; //使用硬件触发
    64. //优先级
    65. DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
    66. DMA_Init(DMA1_Channel1,&DMA_initstruct);
    67. //开启MDA
    68. DMA_Cmd(DMA1_Channel1,ENABLE);
    69. //开启ADC到DMA的输出
    70. ADC_DMACmd(ADC1,ENABLE);
    71. //开启ADC
    72. ADC_Cmd(ADC1,ENABLE);
    73. //校准ADC
    74. //复位校准
    75. ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
    76. //等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
    77. while (ADC_GetResetCalibrationStatus(ADC1)==SET); //SET=1
    78. /*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
    79. 在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
    80. 你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
    81. */
    82. //开始校准
    83. ADC_StartCalibration(ADC1);
    84. //获取校准状态
    85. while(ADC_GetCalibrationStatus(ADC1)==SET);
    86. }
    87. void ad_getvalue(void){
    88. //重新写一下传输寄存器
    89. DMA_Cmd(DMA1_Channel1, DISABLE);
    90. /*
    91. 设置当前数据寄存器 ,  就是给这个传输计数器写数据的
    92. 和配置DMA中X.DMA_BufferSize参数相似
    93. */
    94. DMA_SetCurrDataCounter(DMA1_Channel1, 4);
    95. DMA_Cmd(DMA1_Channel1, ENABLE);
    96. //单次模式-----软件触发转换
    97. ADC_SoftwareStartConvCmd(ADC1,ENABLE);
    98. //这里因为转运(DMA)总是在转换之后的
    99. //等待MDA完成的代码
    100. //DMA1_FLAG_TC1---转运完成标志位 转运完成后置1
    101. while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
    102. DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
    103. }
    104. int main(void)
    105. {
    106. OLED_Init();
    107. AD_init();
    108. OLED_ShowString(1, 1, "AD0:");
    109. OLED_ShowString(2, 1, "AD1:");
    110. OLED_ShowString(3, 1, "AD2:");
    111. OLED_ShowString(4, 1, "AD3:");
    112. while (1)
    113. {
    114. ad_getvalue();
    115. OLED_ShowNum(1, 5, AD_Value[0], 4);
    116. OLED_ShowNum(2, 5, AD_Value[1], 4);
    117. OLED_ShowNum(3, 5, AD_Value[2], 4);
    118. OLED_ShowNum(4, 5, AD_Value[3], 4);
    119. Delay_ms(100);
    120. }
    121. }

     ad_getvalue函数

            因为在这里面的ADC是单次转化,  所以每次转化时都需要软件触发一次,  每转化一次都需要调用一次这个的函数----ADC_SoftwareStartConvCmd(ADC1,ENABLE);

            因为DMA也是正常的单次模式:  所以在触发ADC之前,需要再重新写入一下传输计数器

    1. //重新写一下传输寄存器
    2. DMA_Cmd(DMA1_Channel1, DISABLE);
    3. /*
    4. 设置当前数据寄存器 ,  就是给这个传输计数器写数据的
    5. 和配置DMA中X.DMA_BufferSize参数相似
    6. */
    7. DMA_SetCurrDataCounter(DMA1_Channel1, 4);
    8. DMA_Cmd(DMA1_Channel1, ENABLE)

            最后,等待ADC转换和DMA转运完成,  这里因为转运总是在转换之后的 ,  所以我们只需要写等待DMA完成的代码, 不用写等待ADC完成的代码

    1. //这里因为转运(DMA)总是在转换之后的
    2. //等待MDA完成的代码
    3. //DMA1_FLAG_TC1---转运完成标志位 转运完成后置1
    4. while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
    5. DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位

    启动扫描模式

            就是在选择组的时候多选几个,然后在配置ADC的时候选择扫描模式,  通道要和一引脚对应.  ADC配置的通道数目填入相应的通道数目

    1. /*ADC_Channel_0 --通道o
    2. 1----1~16的范围规则组第几个序列
    3. ADC_SampleTime_55Cycles5-----指定通道的采样时间
    4. */
    5. //选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
    6. ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
    7. ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
    8. ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
    9. ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
    10. ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
    11. ADC_initstruct.ADC_NbrOfChannel=4; //通道数目--指定在扫描模式下,总共会用到几个通道

     DR寄存器

    在ADC会把数据写入到ADC的DR寄存器中,  使用在配置DMA的外设起始地址应该是

    DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
    

     数据源外设地址不自增,  寄存器地址自增

            在这里外面使用的ADC模式为扫描模式,  就是启动了一个组在组中写通道,   如果地址自增就会跑到其他的组中去.        寄存器地址自增 : 外设中组中通道的每个数据对应的是寄存器中不同的地方,  这样才可以实现数据的转移

    其他注意

            在开启ADC前: 要打开ADC到DMA的输出

    DMA_Cmd(DMA1_Channel1,ENABLE);

            在配置DMA的传输计数器是也要改变为相应的传输次数

    	DMA_initstruct.DMA_BufferSize=4;//传输几次
    

            配置DMA的触发方式改为硬件触发

    	DMA_initstruct.DMA_M2M=DMA_M2M_Disable;  //使用硬件触发
    

      ADC连续扫描+DMA循环转运模式

             因为使用的是连续---所以软件触发只需在初始化的时候触发一次即可. 

            当ADC触发之后,ADC连续转换,DMA循环转运 ,  两者一直在工作,始终把最新的转换结果,刷新到SRAM数组里,   我们想要数据的时候,随时去数组里取就行了

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "AD.h"
    5. uint16_t AD_Value[4];
    6. void AD_init(void){
    7. //RCC开启时钟
    8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    10. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    11. /*
    12. 我们使用的STM32型号为: STMF103
    13. 参数1的第一个选择 : 互连型设备--互联型是STM23F 105/107的型号
    14. 参数1的第一个选择 : 其他设备---103选择这个
    15. */
    16. //配置ADCCLK
    17. //APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
    18. RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
    19. //配置GPIO
    20. GPIO_InitTypeDef GPIO_initstruct;
    21. GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN; //模拟输入,可以理解为ADC的专属模式
    22. GPIO_initstruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
    23. GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
    24. GPIO_Init(GPIOA,&GPIO_initstruct);
    25. /*ADC_Channel_0 --通道o
    26. 1----1~16的范围规则组第几个序列
    27. ADC_SampleTime_55Cycles5-----指定通道的采样时间
    28. */
    29. //选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
    30. ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
    31. ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
    32. ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
    33. ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
    34. //初始化ADC
    35. ADC_InitTypeDef ADC_initstruct;
    36. ADC_initstruct.ADC_ContinuousConvMode=ENABLE;//选择是连续转换还是单次转换--连续
    37. ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
    38. ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
    39. ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
    40. ADC_initstruct.ADC_NbrOfChannel=4; //通道数目--指定在扫描模式下,总共会用到几个通道
    41. ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
    42. ADC_Init(ADC1,&ADC_initstruct);
    43. //ADC在完成后面数据写在DR寄存器中
    44. //配置DMA
    45. //外设备站点的3个参数
    46. //因为stm32是32位的单片机,他的一个内存单元是32位的
    47. DMA_InitTypeDef DMA_initstruct;
    48. DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
    49. DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)
    50. DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
    51. // //储存器的3个参数
    52. DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位
    53. DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)
    54. DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
    55. //传输方向
    56. DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
    57. //缓冲区大小---传输计数器
    58. DMA_initstruct.DMA_BufferSize=4;//传输几次
    59. //传输模式----是否使用自动重装
    60. DMA_initstruct.DMA_Mode=DMA_Mode_Circular;//循环模式 //传输计数器到0直接停止 //DMA_Mode_Circular--循环模式 DMA_Mode_Normal
    61. //选择触发模式---硬件触发或者软件触发
    62. DMA_initstruct.DMA_M2M=DMA_M2M_Disable; //使用硬件触发
    63. //优先级
    64. DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
    65. DMA_Init(DMA1_Channel1,&DMA_initstruct);
    66. //开启MDA
    67. DMA_Cmd(DMA1_Channel1,ENABLE);
    68. //开启ADC到DMA的输出
    69. ADC_DMACmd(ADC1,ENABLE);
    70. //开启ADC
    71. ADC_Cmd(ADC1,ENABLE);
    72. //校准ADC
    73. //复位校准
    74. ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
    75. //等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
    76. while (ADC_GetResetCalibrationStatus(ADC1)==SET); //SET=1
    77. /*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
    78. 在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
    79. 你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
    80. */
    81. //开始校准
    82. ADC_StartCalibration(ADC1);
    83. //获取校准状态
    84. while(ADC_GetCalibrationStatus(ADC1)==SET);
    85. ADC_SoftwareStartConvCmd(ADC1,ENABLE);
    86. }
    87. int main(void)
    88. {
    89. OLED_Init();
    90. AD_init();
    91. OLED_ShowString(1, 1, "AD0:");
    92. OLED_ShowString(2, 1, "AD1:");
    93. OLED_ShowString(3, 1, "AD2:");
    94. OLED_ShowString(4, 1, "AD3:");
    95. while (1)
    96. {
    97. OLED_ShowNum(1, 5, AD_Value[0], 4);
    98. OLED_ShowNum(2, 5, AD_Value[1], 4);
    99. OLED_ShowNum(3, 5, AD_Value[2], 4);
    100. OLED_ShowNum(4, 5, AD_Value[3], 4);
    101. Delay_ms(100);
    102. }
    103. }

  • 相关阅读:
    Python 的内置方法
    5G vs 4G
    Open3D FPS最远点下采样
    QT编译Opencv库过程中出现的问题总结
    分享在大疆DJI(深圳总部)工作的日常和福利
    剑指offer 44. 从1到n整数中1出现的次数
    SpringBoot集成腾讯云云点播服务/视频上传
    Redis(三) Redis的java客户端
    [第一篇]——跟我学习Docker 教程
    【AtomicBoolean】常规用法
  • 原文地址:https://blog.csdn.net/m0_74739916/article/details/132571722